Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Evergreen:11.2:Test
kdebase4-wallpapers
randr12.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File randr12.diff of Package kdebase4-wallpapers
--- kcontrol/randr/BRANCH +++ kcontrol/randr/BRANCH @@ -0,0 +1,7 @@ +last sync: +r1012444 (4.3) + + +pending: +r861620 +Key_Display --- kcontrol/randr/CMakeLists.txt +++ kcontrol/randr/CMakeLists.txt @@ -1,4 +1,8 @@ +if( XRANDR_1_2_FOUND ) + macro_optional_add_subdirectory(module) +endif( XRANDR_1_2_FOUND ) + configure_file (config-randr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-randr.h ) --- kcontrol/randr/krandrtray.desktop +++ kcontrol/randr/krandrtray.desktop @@ -1,86 +1,20 @@ [Desktop Entry] Name=KRandRTray -Name[af]=KRandRTray Name[ar]=ضبط شاشة كيدي Name[be]=Змена параметраў манітора -Name[be@latin]=KRandRTray -Name[bg]=KRandRTray -Name[bn]=KRandRTray -Name[bn_IN]=KRandRTray -Name[br]=KRandRTray -Name[ca]=KRandRTray -Name[ca@valencia]=KRandRTray -Name[cs]=KRandRTray -Name[csb]=KRandRTray -Name[da]=KRandRTray -Name[de]=KRandRTray -Name[el]=KRandRTray -Name[en_GB]=KRandRTray -Name[eo]=KRandRTray -Name[es]=KRandRTray -Name[et]=KRandRTray -Name[eu]=KRandRTray -Name[fi]=KRandRTray -Name[fr]=KRandRTray -Name[fy]=KRandRTray -Name[ga]=KRandRTray -Name[gl]=KRandRTray -Name[gu]=KRandRTray -Name[he]=KRandRTray Name[hi]=के-रैँड-आर-ट्रे Name[hne]=के-रैँड-आर-ट्रे -Name[hr]=KRandRTray -Name[hsb]=KRandRTray Name[hu]=Képernyőfelbontás -Name[is]=KRandRTray -Name[it]=KRandRTray -Name[ja]=KRandRTray -Name[ka]=KRandRTray -Name[kk]=KRandRTray -Name[km]=KRandRTray Name[kn]=ಕೆರಾಂಡರ್ಟ್ರೇ -Name[ko]=KRandRTray -Name[ku]=KRandRTray -Name[lt]=KRandRTray -Name[lv]=KRandRTray Name[mai]=के-रैँड-आर-ट्रे -Name[mk]=KRandRTray -Name[ml]=KRandRTray Name[mr]=केरैँडआरट्रे -Name[ms]=KRandRTray -Name[nb]=KRandRTray -Name[nds]=KRandRTray Name[ne]=KRandR ट्रे -Name[nl]=KRandRTray -Name[nn]=KRandRTray -Name[oc]=KRandRTray -Name[or]=KRandRTray -Name[pa]=KRandRTray -Name[pl]=KRandRTray -Name[pt]=KRandRTray Name[pt_BR]=Ícone do KRandR -Name[ro]=KRandRTray -Name[ru]=KRandRTray -Name[se]=KRandRTray -Name[si]=KRandRTray -Name[sk]=KRandRTray -Name[sl]=KRandRTray Name[sr]=К‑рандр-касета Name[sr@latin]=K‑RandR-kaseta Name[sv]=Krandrtray -Name[ta]=KRandRTray -Name[te]=KRandRTray -Name[tg]=KRandRTray -Name[th]=KRandRTray -Name[tr]=KRandRTray -Name[uk]=KRandRTray -Name[uz]=KRandRTray -Name[uz@cyrillic]=KRandRTray Name[vi]=Khay KRandR -Name[wa]=KRandRTray Name[x-test]=xxKRandRTrayxx -Name[zh_CN]=KRandRTray -Name[zh_TW]=KRandRTray GenericName=Screen Resize & Rotate GenericName[af]=Skerm Hervergroot & Roteer GenericName[ar]=إعادة تحجيم ودوران الشاشة @@ -88,6 +22,7 @@ GenericName[be@latin]=Aplet dla źmieny pamieraŭ i pavarotu ekrana GenericName[bg]=Големина и въртене на екрана GenericName[bn]=পর্দা মাপবদল ও আবর্তন +GenericName[bn_IN]=পর্দা মাপবদল ও আবর্তন GenericName[br]=Adventañ ha treiñ ar skramm GenericName[ca]=Amida i gira la pantalla GenericName[ca@valencia]=Amida i gira la pantalla @@ -97,7 +32,6 @@ GenericName[da]=Ændr størrelse på skærm og Rotér GenericName[de]=Bildschirmgröße & -ausrichtung ändern GenericName[el]=Αλλαγή μεγέθους & Περιστροφή οθόνης -GenericName[en_GB]=Screen Resize & Rotate GenericName[eo]=Regrandigi kaj turni ekranon GenericName[es]=Redimensionar y rotar pantalla GenericName[et]=Ekraani suuruse muutmine ja pööramine @@ -144,7 +78,6 @@ GenericName[ro]=Redimensionare și rotire ecran GenericName[ru]=Изменение размера и ориентации экрана GenericName[se]=Rievdat šearbmagova sturrodaga ja jorat dan -GenericName[si]=Screen Resize & Rotate GenericName[sk]=Zmena veľkosti a otočenia obrazovky GenericName[sl]=Velikost in obračanje zaslonov GenericName[sr]=Промена величине и ротација екрана @@ -174,7 +107,6 @@ Comment[da]=Et panelprogram til at ændre størrelse og orientering for X-skærme. Comment[de]=Ein Werkzeug zum Ändern der Bildschirmgröße und -ausrichtung Comment[el]=Μια μικροεφαρμογή αλλαγής μεγέθους και προσανατολισμού οθονών Χ. -Comment[en_GB]=A panel applet for resizing and reorientating X screens. Comment[eo]=Panela aplikaĵeto por regrandigi kaj reorienti la X ekranojn. Comment[es]=Una miniaplicación del panel para redimensionar y reorientar pantallas X. Comment[et]=Paneeliaplett X'i ekraanide suuruse muutmiseks ja pööramiseks @@ -218,7 +150,6 @@ Comment[ro]=O miniaplicație de panou care redimensionează și reorientează ecranele X. Comment[ru]=Виджет панели для изменения размера и ориентации экрана. Comment[se]=Panelaprográmmaš mii sáhttá rievdadit šearpma čoakkisčuohkku ja jorahit dan. -Comment[si]=A panel applet for resizing and reorientating X screens. Comment[sk]=Applet pre panel na zmenu veľkosti a orientácie obrazoviek X Comment[sl]=Orodje za spreminjanje velikosti in usmeritve zaslonov. Comment[sr]=Панелски аплет за промену величине и оријентације икс екрана --- kcontrol/randr/module/CMakeLists.txt +++ kcontrol/randr/module/CMakeLists.txt @@ -0,0 +1,25 @@ +include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/.. ) + +########### next target ############### + +set(kded_randrmonitor_PART_SRCS + randrmonitor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../randrdisplay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../randrscreen.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../randroutput.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../randrcrtc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../randrmode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../randr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../legacyrandrscreen.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ktimerdialog.cpp + ) + +kde4_add_plugin(kded_randrmonitor ${kded_randrmonitor_PART_SRCS}) + +target_link_libraries(kded_randrmonitor ${KDE4_KDEUI_LIBS} ${X11_Xrandr_LIB} ${X11_LIBRARIES}) + +install(TARGETS kded_randrmonitor DESTINATION ${PLUGIN_INSTALL_DIR} ) + +########### install files ############### + +install( FILES randrmonitor.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kded ) --- kcontrol/randr/module/randrmonitor.cpp +++ kcontrol/randr/module/randrmonitor.cpp @@ -0,0 +1,210 @@ +/******************************************************************** + +Copyright (C) 2008 Lubos Lunak <l.lunak@suse.cz> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*********************************************************************/ + +#include "randrmonitor.h" + +#include <kaction.h> +#include <kactioncollection.h> +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpluginfactory.h> +#include <kpluginloader.h> +#include <ktoolinvocation.h> + +#include <qdbusconnection.h> +#include <qdbusconnectioninterface.h> +#include <qtimer.h> +#include <qx11info_x11.h> + +#include <randrdisplay.h> +#include <randrscreen.h> +#include <randroutput.h> + +K_PLUGIN_FACTORY(RandrMonitorModuleFactory, + registerPlugin<RandrMonitorModule>(); + ) +K_EXPORT_PLUGIN(RandrMonitorModuleFactory("randrmonitor")) + +RandrMonitorModule::RandrMonitorModule( QObject* parent, const QList<QVariant>& ) + : KDEDModule( parent ) + , have_randr( false ) + { + setModuleName( "randrmonitor" ); + initRandr(); + } + +RandrMonitorModule::~RandrMonitorModule() + { + if( have_randr ) + { + Display* dpy = QX11Info::display(); + XDestroyWindow( dpy, window ); + delete helper; + delete dialog; + have_randr = false; + } + } + +void RandrMonitorModule::initRandr() + { + Display* dpy = QX11Info::display(); + if( !XRRQueryExtension( dpy, &randr_base, &randr_error )) + return; + int major = 1; + int minor = 2; + if( !XRRQueryVersion( dpy, &major, &minor ) || major < 1 || (major == 1 && minor < 2 )) + return; + have_randr = true; + // It looks like we need a separate window for getting the events, so that we don't + // change e.g. Qt's event mask. + window = XCreateSimpleWindow( dpy, DefaultRootWindow( dpy ), 0, 0, 1, 1, 0, 0, 0 ); + XRRSelectInput( dpy, window, RROutputChangeNotifyMask ); +#if 0 // xrandr apparently can't detect hw changes and on some systems polling freezes X :( + // HACK: see poll() + QTimer* timer = new QTimer( this ); + timer->start( 10000 ); // 10 s + connect( timer, SIGNAL( timeout()), this, SLOT( poll())); +#endif + helper = new RandrMonitorHelper( this ); + kapp->installX11EventFilter( helper ); + dialog = NULL; + currentMonitors = connectedMonitors(); + KActionCollection* coll = new KActionCollection( this ); + KAction* act = coll->addAction( "display" ); + act->setText( i18n( "Switch Display" )); +// TODO act->setGlobalShortcut( KShortcut( Qt::Key_Display )); + connect( act, SIGNAL( triggered( bool )), SLOT( switchDisplay())); + } + +void RandrMonitorModule::poll() + { + // HACK: It seems that RRNotify/RRNotify_OutputChange event (i.e. detecting a newly + // plugged or unplugged monitor) does not work without polling some randr functionality. + int dummy; + XRRGetScreenSizeRange( QX11Info::display(), window, &dummy, &dummy, &dummy, &dummy ); + } + +void RandrMonitorModule::processX11Event( XEvent* e ) + { + if( e->xany.type == randr_base + RRNotify ) + { + XRRNotifyEvent* e2 = reinterpret_cast< XRRNotifyEvent* >( e ); + if( e2->subtype == RRNotify_OutputChange ) // TODO && e2->window == window ) + { + kDebug() << "Monitor change detected"; + QStringList newMonitors = connectedMonitors(); + if( newMonitors == currentMonitors ) + return; + if( QDBusConnection::sessionBus().interface()->isServiceRegistered( + "org.kde.internal.KSettingsWidget-kcm_display" )) + { // already running + return; + } + kapp->updateUserTimestamp(); // well, let's say plugging in a monitor is a user activity +#warning Modal dialog, stupid, fix. + QString change; + QString question = + ( newMonitors.count() < currentMonitors.count() + ? i18n( "A monitor output has been disconnected." ) + : i18n( "A new monitor output has been connected." )) + + "\n\n" + i18n( "Do you wish to run a configuration tool to adjust the monitor setup?" ); + currentMonitors = newMonitors; + if( KMessageBox::questionYesNo( NULL, question, i18n( "Monitor setup has changed" ), + KGuiItem( "Con&figure" ), KGuiItem( "&Ignore" ), "randrmonitorchange" ) + == KMessageBox::Yes ) + { + KToolInvocation::kdeinitExec( "kcmshell4", QStringList() << "display" ); + } + } + } + } + +QStringList RandrMonitorModule::connectedMonitors() const + { + QStringList ret; + Display* dpy = QX11Info::display(); + XRRScreenResources* resources = XRRGetScreenResources( dpy, window ); + for( int i = 0; + i < resources->noutput; + ++i ) + { + XRROutputInfo* info = XRRGetOutputInfo( dpy, resources, resources->outputs[ i ] ); + QString name = QString::fromUtf8( info->name ); + if( info->connection == RR_Connected ) + ret.append( name ); + XRRFreeOutputInfo( info ); + } + XRRFreeScreenResources( resources ); + return ret; + } + +void RandrMonitorModule::switchDisplay() + { + QList< RandROutput* > outputs; + RandRDisplay display; + for( int scr = 0; + scr < display.numScreens(); + ++scr ) + { + foreach( RandROutput* output, display.screen( scr )->outputs()) + { + if( !output->isConnected()) + continue; + if( !outputs.contains( output )) + outputs.append( output ); + } + } + if( outputs.count() <= 1 ) // just one, do nothing + return; + if( outputs.count() == 2 ) // alternative between one, second, both + { + if( outputs[ 0 ]->isActive() && !outputs[ 1 ]->isActive()) + { + enableOutput( outputs[ 1 ], true ); + enableOutput( outputs[ 0 ], false ); + } + else if( !outputs[ 0 ]->isActive() && outputs[ 1 ]->isActive()) + { + enableOutput( outputs[ 1 ], true ); + enableOutput( outputs[ 0 ], true ); + } + else + { + enableOutput( outputs[ 0 ], true ); + enableOutput( outputs[ 1 ], false ); + } + return; + } + // no idea what to do here + KToolInvocation::kdeinitExec( "kcmshell4", QStringList() << "display" ); + } + +void RandrMonitorModule::enableOutput( RandROutput* output, bool enable ) + { // a bit lame, but I don't know how to do this easily with this codebase :-/ + KProcess::execute( QStringList() << "xrandr" << "--output" << output->name() << ( enable ? "--auto" : "--off" )); + } + +bool RandrMonitorHelper::x11Event( XEvent* e ) + { + module->processX11Event( e ); + return QWidget::x11Event( e ); + } + +#include "randrmonitor.moc" --- kcontrol/randr/module/randrmonitor.desktop +++ kcontrol/randr/module/randrmonitor.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=Detecting RANDR (monitor) changes +Type=Service +X-KDE-ServiceTypes=KDEDModule +X-KDE-Library=randrmonitor +X-KDE-DBus-ModuleName=randrmonitor +X-KDE-Kded-autoload=true +OnlyShowIn=KDE; --- kcontrol/randr/module/randrmonitor.h +++ kcontrol/randr/module/randrmonitor.h @@ -0,0 +1,78 @@ +/******************************************************************** + +Copyright (C) 2008 Lubos Lunak <l.lunak@suse.cz> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*********************************************************************/ + +#ifndef RANDRMONITOR_H +#define RANDRMONITOR_H + +#include <kdedmodule.h> +#include <kprocess.h> +#include <qwidget.h> + +#include <X11/Xlib.h> +#include <X11/extensions/Xrandr.h> +#include <fixx11h.h> + +class RandROutput; + +class RandrMonitorHelper; + +class RandrMonitorModule + : public KDEDModule + { + Q_OBJECT + public: + RandrMonitorModule(QObject* parent, const QList<QVariant>&); + virtual ~RandrMonitorModule(); + void processX11Event( XEvent* e ); + private slots: + void poll(); + void switchDisplay(); + private: + void initRandr(); + void getRandrInfo( XRROutputChangeNotifyEvent* e, QString* change, QRect* rect ); + QStringList connectedMonitors() const; + void enableOutput( RandROutput* output, bool enable ); + bool have_randr; + int randr_base; + int randr_error; + Window window; + QStringList currentMonitors; + RandrMonitorHelper* helper; + QDialog* dialog; + }; + +class RandrMonitorHelper + : public QWidget + { + Q_OBJECT + public: + RandrMonitorHelper( RandrMonitorModule* module ); + protected: + virtual bool x11Event( XEvent* e ); + private: + RandrMonitorModule* module; + }; + + +inline +RandrMonitorHelper::RandrMonitorHelper( RandrMonitorModule* m ) + : module( m ) + { + } + +#endif --- kcontrol/randr/module/randrpolltest.cpp +++ kcontrol/randr/module/randrpolltest.cpp @@ -0,0 +1,63 @@ +#include <X11/Xlib.h> +#include <X11/extensions/Xrandr.h> +#include <stdio.h> +#include <unistd.h> + +int main( int argc, char* argv[] ) + { + Display* dpy = XOpenDisplay( NULL ); + XSetWindowAttributes attrs; + Window w = XCreateWindow( dpy, DefaultRootWindow( dpy ), 0, 0, 100, 100, 0, CopyFromParent, CopyFromParent, + CopyFromParent, 0, &attrs ); +// XMapWindow( dpy, w ); + int base, error; + if( !XRRQueryExtension( dpy, &base, &error )) + return 1; + int major = 1; + int minor = 2; + if( !XRRQueryVersion( dpy, &major, &minor ) || major < 1 || (major == 1 && minor < 2 )) + return 2; + XRRSelectInput( dpy, w, + RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask | RROutputPropertyNotifyMask ); + for(;;) + { + XEvent ev; + int a, b, c, d; + static int cnt = 0; + if( ++cnt % 30 == 0 ) + { +// XRRFreeScreenResources(XRRGetScreenResources( dpy, w )); + XRRGetScreenSizeRange( dpy, w, &a, &b, &c, &d ); +// XSync( dpy, False ); + printf( "Poll\n" ); + } + sleep( 1 ); + if( !XPending( dpy )) + continue; + XNextEvent( dpy, &ev ); + if( ev.xany.type == base + RRScreenChangeNotify ) + { + printf( "Screen Change\n" ); + } + if( ev.xany.type == base + RRNotify ) + { + XRRNotifyEvent* e = reinterpret_cast< XRRNotifyEvent* >( &ev ); + switch( e->subtype ) + { + case RRNotify_CrtcChange: + printf( "Crtc Change\n" ); + break; + case RRNotify_OutputChange: + printf( "Output Change\n" ); + break; + case RRNotify_OutputProperty: + printf( "Output Property Change\n" ); + break; + default: + printf( "Unknown Notify\n" ); + break; + } + } + } + XCloseDisplay( dpy ); + } --- kcontrol/randr/module/TODO +++ kcontrol/randr/module/TODO @@ -0,0 +1,7 @@ +- zrusit ten modalni dialog +- zkontrolovat, ze tohle opravdu nerusi randr eventmask pro Qt + - plus zkontrolovat, jak se tedy pouziva to window pro events +- musi se dialog zobrazit na spravnem monitoru (tj. ne na vypnutem) +- Hidden[$e]= v .desktop nefunguje +- kdyz se detekuje zmena, kcm sam o sobe nic(?) neudela, takze musi byt nejake 'suggest'? +- zkontrolovat nastaveni po startu KDE --- kcontrol/randr/outputconfig.cpp +++ kcontrol/randr/outputconfig.cpp @@ -24,15 +24,13 @@ #include "randrmode.h" #include <kdebug.h> -OutputConfig::OutputConfig(QWidget *parent, RandROutput *output, OutputGraphicsItem *item) +OutputConfig::OutputConfig(QWidget *parent, RandROutput *output, OutputConfigList preceding) : QWidget(parent) + , precedingOutputConfigs( preceding ) { m_output = output; Q_ASSERT(output); - m_item = item; - Q_ASSERT(item); - setupUi(this); // connect signals @@ -40,6 +38,10 @@ this, SLOT(positionComboChanged(int))); connect(sizeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateRateList(int))); + connect(sizeCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(updatePositionList())); + connect(sizeCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(updateRotationList())); connect(m_output, SIGNAL(outputChanged(RROutput, int)), this, SLOT(outputChanged(RROutput, int))); @@ -48,7 +50,19 @@ connect(orientationCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setConfigDirty())); connect(positionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setConfigDirty())); connect(positionOutputCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setConfigDirty())); - + connect(sizeCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateView())); + connect(orientationCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateView())); + connect(positionCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateView())); + connect(positionOutputCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateView())); + connect(absolutePosX, SIGNAL(textChanged(const QString&)), this, SIGNAL(updateView())); + connect(absolutePosY, SIGNAL(textChanged(const QString&)), this, SIGNAL(updateView())); + // make sure to update option for relative position when other outputs get enabled/disabled + foreach( OutputConfig* config, precedingOutputConfigs ) + connect( config, SIGNAL( updateView()), this, SLOT( updatePositionList())); + + updatePositionListTimer.setSingleShot( true ); + connect( &updatePositionListTimer, SIGNAL( timeout()), SLOT( updatePositionListDelayed())); + load(); } @@ -63,20 +77,56 @@ QPoint OutputConfig::position(void) const { + if( !isActive()) + return QPoint(); int index = positionCombo->currentIndex(); if((Relation)positionCombo->itemData(index).toInt() == Absolute) return QPoint(absolutePosX->text().toInt(), absolutePosY->text().toInt()); + foreach(OutputConfig *config, precedingOutputConfigs) { + if( config->output()->id() + == positionOutputCombo->itemData( positionOutputCombo->currentIndex()).toUInt()) { + QPoint pos = config->position(); + switch( (Relation)positionCombo->itemData(index).toInt()) { + case LeftOf: + return QPoint( pos.x() - resolution().width(), pos.y()); + case RightOf: + return QPoint( pos.x() + config->resolution().width(), pos.y()); + case Over: + return QPoint( pos.x(), pos.y() - resolution().height()); + case Under: + return QPoint( pos.x(), pos.y() + config->resolution().height()); + case SameAs: + return pos; + default: + abort(); + } + } + } return QPoint(0, 0); } QSize OutputConfig::resolution(void) const { + if( sizeCombo->count() == 0 ) + return QSize(); return sizeCombo->itemData(sizeCombo->currentIndex()).toSize(); } +QRect OutputConfig::rect() const +{ + return QRect( position(), resolution()); +} + +bool OutputConfig::isActive() const +{ + return sizeCombo->count() != 0 && !resolution().isEmpty(); +} + float OutputConfig::refreshRate(void) const { + if( !isActive()) + return 0; float rate = float(refreshCombo->itemData(refreshCombo->currentIndex()).toDouble()); if(rate == 0.0f) { RateList rates = m_output->refreshRates(resolution()); @@ -87,12 +137,14 @@ int OutputConfig::rotation(void) const { + if( !isActive()) + return 0; return orientationCombo->itemData(orientationCombo->currentIndex()).toInt(); } -bool OutputConfig::hasPendingChanges(void) const +bool OutputConfig::hasPendingChanges( const QPoint& normalizePos ) const { - if (m_output->rect() != QRect(position(), resolution())) { + if (m_output->rect().translated( -normalizePos ) != QRect(position(), resolution())) { return true; } else if (m_output->rotation() != rotation()) { @@ -124,8 +176,6 @@ if(changes & RandR::ChangeRect) { QRect r = m_output->rect(); kDebug() << "Output rect changed:" << r; - m_item->setRect(r); - emit updateView(); } if(changes & RandR::ChangeRotation) { @@ -151,11 +201,6 @@ //QSize modeSize = m_output->screen()->mode(m_output->mode()).size(); QSize modeSize = m_output->mode().size(); updateRateList(sizeCombo->findData(modeSize)); - - //Update the text inside the OutputGraphicsItem - m_item->updateText(); - m_item->setRect(m_output->rect()); - emit updateView(); } } @@ -180,7 +225,6 @@ orientationCombo->clear(); - m_item->setVisible(m_output->isActive()); if (!m_output->isConnected()) return; @@ -191,11 +235,6 @@ updateRotationList(); updatePositionList(); - // update the item - m_item->setRect( 0, 0, m_output->rect().width(), m_output->rect().height()); - kDebug() << " Setting graphic rect pos: " << m_output->rect().topLeft(); - m_item->setPos( m_output->rect().topLeft() ); - emit updateView(); } @@ -205,6 +244,25 @@ emit optionChanged(); } +bool OutputConfig::isRelativeTo( QRect rect, QRect to, Relation rel ) +{ + switch( rel ) { + case LeftOf: + return rect.x() + rect.width() == to.x() && rect.y() == to.y(); + case RightOf: + return rect.x() == to.x() + to.width() && rect.y() == to.y(); + case Over: + return rect.x() == to.x() && rect.y() + rect.height() == to.y(); + case Under: + return rect.x() == to.x() && rect.y() == to.y() + to.height(); + case SameAs: + return rect.topLeft() == to.topLeft(); + case Absolute: + default: + return false; + } +} + void OutputConfig::positionComboChanged(int item) { Relation rel; @@ -227,10 +285,27 @@ void OutputConfig::updatePositionList(void) { + // Delay because + // a) this is an optimization + // b) this can be called in the middle of changing configuration and can + // lead to the comboboxes being setup to wrong values + updatePositionListTimer.start( 0 ); +} + +void OutputConfig::updatePositionListDelayed() +{ + bool enable = !resolution().isEmpty(); + positionCombo->setEnabled( enable ); + positionLabel->setEnabled( enable ); + positionOutputCombo->setEnabled( enable ); + absolutePosX->setEnabled( enable ); + absolutePosY->setEnabled( enable ); + // when updating, use previously set value, otherwise read it from the output + QRect rect = positionCombo->count() > 0 ? QRect( position(), resolution()) : m_output->rect(); positionCombo->clear(); positionOutputCombo->clear(); - Relation rel = SameAs; + Relation rel = Absolute; // FIXME: get default value from KConfig for(int i = -1; i < 5; i++) positionCombo->addItem(OutputConfig::positionName((Relation)i), i); @@ -240,9 +315,22 @@ positionCombo->setCurrentIndex(index); /* Relative Output Name Configuration */ - OutputMap outputs = m_output->screen()->outputs(); - foreach(RandROutput *output, outputs) + foreach(OutputConfig *config, precedingOutputConfigs) { + RandROutput* output = config->output(); + if( config->resolution().isEmpty()) + continue; // ignore disabled outputs positionOutputCombo->addItem(QIcon(output->icon()), output->name(), (int)output->id()); + for( int rel = -1; rel < 5; ++rel ) { + if( isRelativeTo( rect, QRect( config->position(), config->resolution()), (Relation) rel )) { + positionCombo->setCurrentIndex( positionCombo->findData( rel )); + } + } + } + if( positionOutputCombo->count() == 0 ) { + positionOutputCombo->setEnabled( false ); + while( positionCombo->count() > 1 ) // keep only 'Absolute' + positionCombo->removeItem( positionCombo->count() - 1 ); + } // FIXME: get this from Kconfig again /*if(m_output->relation(0) != m_output) { @@ -250,21 +338,13 @@ if(index != -1) positionOutputCombo->setCurrentIndex(index); }*/ - bool enabled = (m_output->screen()->activeCount() >= 2); - positionLabel->setEnabled(enabled); - positionCombo->setEnabled(enabled); - positionOutputCombo->setEnabled(enabled); - - // FIXME: RandRScreen::applyProposed() has a bit of code for positioning - // outputs, but it's commented out and might not work as is. Until someone - // gets this to work, it's probably better not to show these controls. - positionLabel->setVisible(false); - positionCombo->setVisible(false); - positionOutputCombo->setVisible(false); } void OutputConfig::updateRotationList(void) { + bool enable = !resolution().isEmpty(); + orientationCombo->setEnabled( enable ); + orientationLabel->setEnabled( enable ); orientationCombo->clear(); int rotations = m_output->rotations(); for(int i =0; i < 6; ++i) { @@ -310,6 +390,7 @@ QSize resolution = sizeCombo->itemData(resolutionIndex).toSize(); if((resolution == QSize(0, 0)) || !resolution.isValid()) { refreshCombo->setEnabled(false); + rateLabel->setEnabled(false); return; } @@ -318,6 +399,7 @@ refreshCombo->clear(); refreshCombo->addItem(i18nc("Automatic configuration", "Auto"), 0.0f); refreshCombo->setEnabled(true); + rateLabel->setEnabled(true); foreach(RRMode m, modeList) { RandRMode outMode = m_output->screen()->mode(m); if(outMode.isValid() && outMode.size() == resolution) { --- kcontrol/randr/outputconfig.h +++ kcontrol/randr/outputconfig.h @@ -22,18 +22,21 @@ #include <QWidget> #include <QTextStream> +#include <QTimer> #include "ui_outputconfigbase.h" #include "randr.h" #include "randroutput.h" class RandROutput; -class OutputGraphicsItem; + +class OutputConfig; +typedef QList<OutputConfig*> OutputConfigList; class OutputConfig : public QWidget, public Ui::OutputConfigBase { Q_OBJECT public: - OutputConfig(QWidget *parent, RandROutput *output, OutputGraphicsItem *item); + OutputConfig(QWidget *parent, RandROutput *output, OutputConfigList preceding); ~OutputConfig(); /** Enumeration describing two related outputs (i.e. VGA LeftOf TMDS) */ @@ -48,15 +51,17 @@ // NOTE: I'd love to have used Above and Below but Xlib already defines them // and that confuses GCC. + bool isActive() const; QPoint position(void) const; QSize resolution(void) const; + QRect rect() const; float refreshRate(void) const; int rotation(void) const; static QString positionName(Relation position); RandROutput *output(void) const; - bool hasPendingChanges(void) const; + bool hasPendingChanges( const QPoint& normalizePos ) const; public slots: void load(); @@ -65,6 +70,7 @@ void setConfigDirty(void); void updatePositionList(void); + void updatePositionListDelayed(void); void updateRotationList(void); void updateSizeList(void); void updateRateList(void); @@ -79,12 +85,16 @@ private: + static bool isRelativeTo( QRect rect, QRect to, Relation rel ); int m_changes; bool m_changed; QPoint m_pos; + QTimer updatePositionListTimer; RandROutput *m_output; - OutputGraphicsItem *m_item; + // List of configs shown before this one. Relative positions may be given only + // relative to these in order to avoid cycles. + OutputConfigList precedingOutputConfigs; }; --- kcontrol/randr/outputgraphicsitem.cpp +++ kcontrol/randr/outputgraphicsitem.cpp @@ -17,7 +17,7 @@ */ #include "outputgraphicsitem.h" -#include "randroutput.h" +#include "outputconfig.h" #include "randr.h" #include <QPen> @@ -26,29 +26,24 @@ #include <QGraphicsScene> #include <KGlobalSettings> -OutputGraphicsItem::OutputGraphicsItem(RandROutput *output) - : QGraphicsRectItem(output->rect()) +OutputGraphicsItem::OutputGraphicsItem(OutputConfig *config) + : QGraphicsRectItem(config->rect()) + , m_config( config ) { - m_output = output; - m_left = m_right = m_top = m_bottom = NULL; setPen(QPen(Qt::black)); - if(output->isActive()) - setBrush(QColor(0, 255, 0, 128)); - else setBrush(QColor(128, 128, 128, 0)); setFlag(QGraphicsItem::ItemIsMovable, false); - setFlag(QGraphicsItem::ItemIsSelectable, true); +// FIXME not implemented yet setFlag(QGraphicsItem::ItemIsSelectable, true); + + m_text = new QGraphicsTextItem(QString(), this); - m_text = new QGraphicsTextItem( this ); - QFont font = KGlobalSettings::generalFont(); font.setPixelSize(72); m_text->setFont(font); - - updateText(); - + setVisible( false ); + m_text->setVisible( false ); } OutputGraphicsItem::~OutputGraphicsItem() @@ -56,6 +51,30 @@ disconnect(); } +void OutputGraphicsItem::configUpdated() +{ + if( !m_config->isActive()) { + setVisible( false ); + m_text->setVisible( false ); + return; + } + setVisible( true ); + m_text->setVisible( true ); + setRect( m_config->rect()); + setBrush(QColor(0, 255, 0, 128)); + // An example of this description text with radeonhd on randr 1.2: + // DVI-I_2/digital + // 1680x1050 (60.0 Hz) + QString refresh = QString::number(m_config->refreshRate(), 'f', 1); + QString desc = m_config->output()->name() + '\n' + + QString("%1x%2 (%3 Hz)").arg(rect().width()).arg(rect().height()).arg(refresh); + m_text->setPlainText( desc ); + // more accurate text centering + QRectF textRect = m_text->boundingRect(); + m_text->setPos( rect().x() + (rect().width() - textRect.width()) / 2, + rect().y() + (rect().height() - textRect.height()) / 2); +} + OutputGraphicsItem *OutputGraphicsItem::left() const { return m_left; @@ -216,28 +235,6 @@ } } -void OutputGraphicsItem::updateText() -{ - // An example of this description text with radeonhd on randr 1.2: - // DVI-I_2/digital - // 1680x1050 (60.0 Hz) - - int w = m_output->rect().width(), h = m_output->rect().height(); - - QString refresh = QString::number(m_output->refreshRate(), 'f', 1); - QString desc = m_output->name() + '\n' + - QString("%1x%2 (%3 Hz)").arg(w).arg(h).arg(refresh); - - m_text->setPlainText(desc); - - QRectF textRect = m_text->boundingRect(); - QRect outRect = m_output->rect(); - // more accurate text centering - m_text->setPos((outRect.width() - textRect.width()) / 2, - (outRect.height() - textRect.height()) / 2); - -} - void OutputGraphicsItem::disconnect() { // for now just disconnect everything --- kcontrol/randr/outputgraphicsitem.h +++ kcontrol/randr/outputgraphicsitem.h @@ -24,15 +24,17 @@ #include "randr.h" -class RandROutput; +class OutputConfig; class OutputGraphicsItem : public QObject, public QGraphicsRectItem { Q_OBJECT public: - OutputGraphicsItem(RandROutput *output); + OutputGraphicsItem(OutputConfig *config); ~OutputGraphicsItem(); + void configUpdated(); // updates from OutputConfig + OutputGraphicsItem *left() const; OutputGraphicsItem *right() const; OutputGraphicsItem *top() const; @@ -45,8 +47,6 @@ bool isConnected(); - void updateText(); - protected: void disconnect(); virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); @@ -61,7 +61,7 @@ OutputGraphicsItem *m_top; OutputGraphicsItem *m_bottom; - RandROutput *m_output; + OutputConfig *m_config; QGraphicsTextItem *m_text; --- kcontrol/randr/randrconfig.cpp +++ kcontrol/randr/randrconfig.cpp @@ -29,6 +29,8 @@ #include "randrscreen.h" #include <kglobalsettings.h> +#include <kmessagebox.h> +#include <kprocess.h> RandRConfig::RandRConfig(QWidget *parent, RandRDisplay *display) : QWidget(parent), Ui::RandRConfigBase() @@ -37,7 +39,6 @@ Q_ASSERT(m_display); m_changed = false; - m_firstLoad = true; if (!m_display->isValid()) { // FIXME: this needs much better handling of this error... @@ -48,7 +49,9 @@ connect( identifyOutputsButton, SIGNAL( clicked()), SLOT( identifyOutputs())); connect( &identifyTimer, SIGNAL( timeout()), SLOT( clearIndicators())); + connect( &compressUpdateViewTimer, SIGNAL( timeout()), SLOT( slotDelayedUpdateView())); identifyTimer.setSingleShot( true ); + compressUpdateViewTimer.setSingleShot( true ); // create the container for the settings widget QHBoxLayout *layout = new QHBoxLayout(outputList); @@ -70,7 +73,6 @@ RandRConfig::~RandRConfig() { clearIndicators(); - delete m_scene; } void RandRConfig::load(void) @@ -79,35 +81,24 @@ kDebug() << "Invalid display! Aborting config load."; return; } - - if(!m_firstLoad) { - qDeleteAll(m_outputList); - m_outputList.clear(); - - QList<QGraphicsItem*> items = m_scene->items(); - foreach(QGraphicsItem *i, items) { - if(i->scene() == m_scene) - m_scene->removeItem(i); - } - } - - m_firstLoad = false; - + + m_scene->clear(); + qDeleteAll(m_outputList); + m_outputList.clear(); + m_configs.clear(); // objects deleted above + OutputMap outputs = m_display->currentScreen()->outputs(); // FIXME: adjust it to run on a multi screen system CollapsibleWidget *w; OutputGraphicsItem *o; + OutputConfigList preceding; foreach(RandROutput *output, outputs) { - o = new OutputGraphicsItem(output); - m_scene->addItem(o); - - connect(o, SIGNAL(itemChanged(OutputGraphicsItem*)), - this, SLOT(slotAdjustOutput(OutputGraphicsItem*))); + OutputConfig *config = new OutputConfig(this, output, preceding); + m_configs.append( config ); + preceding.append( config ); - OutputConfig *config = new OutputConfig(0, output, o); - QString description = output->isConnected() ? i18n("%1 (Connected)", output->name()) : output->name(); @@ -118,6 +109,12 @@ } m_outputList.append(w); + o = new OutputGraphicsItem(config); + m_scene->addItem(o); + + connect(o, SIGNAL(itemChanged(OutputGraphicsItem*)), + this, SLOT(slotAdjustOutput(OutputGraphicsItem*))); + connect(config, SIGNAL(updateView()), this, SLOT(slotUpdateView())); connect(config, SIGNAL(optionChanged()), this, SLOT(slotChanged())); } @@ -140,6 +137,28 @@ void RandRConfig::apply() { kDebug() << "Applying settings..."; + + // normalize positions so that the coordinate system starts at (0,0) + QPoint normalizePos; + bool first = true; + foreach(CollapsibleWidget *w, m_outputList) { + OutputConfig *config = static_cast<OutputConfig *>(w->innerWidget()); + if( config->isActive()) { + QPoint pos = config->position(); + if( first ) { + normalizePos = pos; + first = false; + } else { + if( pos.x() < normalizePos.x()) + normalizePos.setX( pos.x()); + if( pos.y() < normalizePos.y()) + normalizePos.setY( pos.y()); + } + } + } + normalizePos = -normalizePos; + kDebug() << "Normalizing positions by" << normalizePos; + foreach(CollapsibleWidget *w, m_outputList) { OutputConfig *config = static_cast<OutputConfig *>(w->innerWidget()); RandROutput *output = config->output(); @@ -150,7 +169,7 @@ QSize res = config->resolution(); if(!res.isNull()) { - if(!config->hasPendingChanges()) { + if(!config->hasPendingChanges( normalizePos )) { kDebug() << "Ignoring identical config for" << output->name(); continue; } @@ -161,7 +180,13 @@ << ", rot =" << config->rotation() << ", rate =" << config->refreshRate(); - output->proposeRect(configuredRect); + // Break the connection with the previous CRTC for changed outputs, since + // otherwise the code could try to use the same CRTC for two different outputs. + // This is probably rather hackish and may not always work, but I don't see + // a better way with this codebase, definitely not with the time I have now. + output->disconnectFromCrtc(); + + output->proposeRect(configuredRect.translated( normalizePos )); output->proposeRotation(config->rotation()); output->proposeRefreshRate(config->refreshRate()); } else { // user wants to disable this output @@ -207,20 +232,26 @@ void RandRConfig::slotUpdateView() { + compressUpdateViewTimer.start( 0 ); +} + +#include <typeinfo> + +void RandRConfig::slotDelayedUpdateView() +{ QRect r; bool first = true; // updates the graphics view so that all outputs fit inside of it - OutputMap outputs = m_display->currentScreen()->outputs(); - foreach(RandROutput *output, outputs) + foreach(OutputConfig *config, m_configs) { if (first) { first = false; - r = output->rect(); + r = config->rect(); } else - r = r.united(output->rect()); + r = r.united(config->rect()); } // scale the total bounding rectangle for all outputs to fit // 80% of the containing QGraphicsView @@ -233,6 +264,12 @@ screenView->scale(scale,scale); screenView->ensureVisible(r); screenView->setSceneRect(r); + + foreach( QGraphicsItem* item, m_scene->items()) { + if( OutputGraphicsItem* itemo = dynamic_cast< OutputGraphicsItem* >( item )) + itemo->configUpdated(); + } + screenView->update(); } uint qHash( const QPoint& p ) @@ -248,7 +285,7 @@ OutputMap outputs = m_display->currentScreen()->outputs(); foreach(RandROutput *output, outputs) { - if( !output->isConnected()) + if( !output->isConnected() || output->rect().isEmpty()) continue; ids[ output->rect().center() ].append( output->name()); } @@ -278,5 +315,23 @@ m_indicators.clear(); } +void RandRConfig::insufficientVirtualSize() +{ + if( KMessageBox::questionYesNo( this, + i18n( "Insufficient virtual size for the total screen size.\n" + "The configured virtual size of your X server is insufficient for this setup. " + "This configuration needs to be adjusted.\n" + "Do you wish to run a tool to adjust the configuration?" )) == KMessageBox::Yes ) + { + KProcess proc; + // TODO + if( proc.execute() == 0 ) + KMessageBox::information( this, i18n( "Configuration has been adjusted. Please restart " + "your session for this change to take effect." )); + else + KMessageBox::sorry( this, i18n( "Changing configuration failed. Please adjust your xorg.conf manually." )); + } +} + #include "randrconfig.moc" --- kcontrol/randr/randrconfig.h +++ kcontrol/randr/randrconfig.h @@ -22,6 +22,7 @@ #include "ui_randrconfigbase.h" #include "randr.h" +#include "outputconfig.h" #include <QWidget> #include <QTimer> @@ -51,6 +52,7 @@ public slots: void slotUpdateView(); + void slotDelayedUpdateView(); protected slots: void slotChanged(void); @@ -65,6 +67,7 @@ virtual bool eventFilter(QObject *obj, QEvent *event); private: + void insufficientVirtualSize(); RandRDisplay *m_display; bool m_changed; bool m_firstLoad; @@ -75,6 +78,8 @@ LayoutManager *m_layoutManager; QList<QWidget*> m_indicators; QTimer identifyTimer; + OutputConfigList m_configs; + QTimer compressUpdateViewTimer; }; --- kcontrol/randr/randr.desktop +++ kcontrol/randr/randr.desktop @@ -28,7 +28,6 @@ Name[da]=Størrelse og orientering Name[de]=Größe & Orientierung Name[el]=Μέγεθος & Προσανατολισμός -Name[en_GB]=Size & Orientation Name[eo]=Grandeco kaj orientiĝo Name[es]=Tamaño y orientación Name[et]=Suurus ja orientatsioon @@ -75,7 +74,6 @@ Name[ro]=Mărime și orientare Name[ru]=Размер и ориентация Name[se]=Sturrodat ja joraheapmi -Name[si]=Size & Orientation Name[sk]=Veľkosť a orientácia Name[sl]=Velikost in orientacija Name[sr]=Величина и оријентација @@ -102,6 +100,7 @@ Comment[be@latin]=Źmianieńnie pamieraŭ i pavarot ekrana Comment[bg]=Настройване на големината и завъртането на екрана Comment[bn]=আপনার ডিসপ্লের আকৃতি এবং দিশা পরিবর্তন করুন +Comment[bn_IN]=আপনার ডিসপ্লের আকৃতি এবং দিশা পরিবর্তন করুন Comment[br]=Adventañ ha treiñ ho skramm Comment[ca]=Amida i gira la vostra pantalla Comment[ca@valencia]=Amida i gira la vostra pantalla @@ -111,7 +110,6 @@ Comment[da]=Ændrer størrelse og roterer din visning Comment[de]=Die Größe und Ausrichtung der Anzeige ändern Comment[el]=Αλλαγή μεγέθους και Περιστροφή της οθόνης σας -Comment[en_GB]=Resize and Rotate your display Comment[eo]=Regrandigi kaj turni vian ekranon Comment[es]=Ajustar el tamaño y rotar la pantalla Comment[et]=Oma kuva suuruse muutmine ja pööramine @@ -158,7 +156,6 @@ Comment[ro]=Redimensionează și rotește ecranul dumneavoastră Comment[ru]=Изменение размера и ориентации экрана Comment[se]=Rievdat šearpma sturrodaga ja joraheami -Comment[si]=Resize and Rotate your display Comment[sk]=Zmení veľkosť a otočí váš displej Comment[sl]=Spremenite velikost in obrnite zaslon Comment[sr]=Промените величину и оријентацију екрана --- kcontrol/randr/randrdisplay.h +++ kcontrol/randr/randrdisplay.h @@ -25,6 +25,7 @@ #include "randr.h" #include <X11/Xlib.h> +#include <fixx11h.h> class RandRDisplay { --- kcontrol/randr/randr.h +++ kcontrol/randr/randr.h @@ -39,6 +39,8 @@ #include <X11/extensions/Xrandr.h> } +#include <fixx11h.h> + #ifdef HAS_RANDR_1_2 class RandRScreen; class RandRCrtc; --- kcontrol/randr/randroutput.cpp +++ kcontrol/randr/randroutput.cpp @@ -602,7 +602,6 @@ bool RandROutput::setCrtc(RandRCrtc *crtc, bool applyNow) { - Q_UNUSED(applyNow); if( !crtc || (m_crtc && crtc->id() == m_crtc->id()) ) return false; @@ -615,7 +614,8 @@ this, SLOT(slotCrtcChanged(RRCrtc, int))); m_crtc->removeOutput(m_id); - m_crtc->applyProposed(); + if( applyNow ) + m_crtc->applyProposed(); } m_crtc = crtc; if (!m_crtc->isValid()) @@ -628,6 +628,11 @@ return true; } +void RandROutput::disconnectFromCrtc() +{ // but don't apply now + setCrtc(m_screen->crtc(None), false); +} + void RandROutput::slotCrtcChanged(RRCrtc c, int changes) { Q_UNUSED(c); --- kcontrol/randr/randroutput.h +++ kcontrol/randr/randroutput.h @@ -69,6 +69,8 @@ /** Returns the current CRTC for this output. */ RandRCrtc *crtc() const; + void disconnectFromCrtc(); + /** Returns a list of all RRModes supported by this output. */ ModeList modes() const; --- kcontrol/randr/randrscreen.cpp +++ kcontrol/randr/randrscreen.cpp @@ -395,7 +395,7 @@ : group.readEntry("UnifiedRect", QRect()); m_unifiedRotation = group.readEntry("UnifiedRotation", (int) RandR::Rotate0); - slotUnifyOutputs(m_outputsUnified); +// slotUnifyOutputs(m_outputsUnified); foreach(RandROutput *output, m_outputs) {
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor