• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

main.cpp

Go to the documentation of this file.
00001 /********************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
00007 
00008 This program is free software; you can redistribute it and/or modify
00009 it under the terms of the GNU General Public License as published by
00010 the Free Software Foundation; either version 2 of the License, or
00011 (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 GNU General Public License for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 *********************************************************************/
00021 
00022 #include "main.h"
00023 
00024 //#define QT_CLEAN_NAMESPACE
00025 #include <ksharedconfig.h>
00026 
00027 #include <kglobal.h>
00028 #include <klocale.h>
00029 #include <stdlib.h>
00030 #include <kcmdlineargs.h>
00031 #include <kaboutdata.h>
00032 #include <kcrash.h>
00033 #include <unistd.h>
00034 #include <signal.h>
00035 #include <fcntl.h>
00036 #include <QX11Info>
00037 #include <stdio.h>
00038 #include <fixx11h.h>
00039 #include <kxerrorhandler.h>
00040 #include <kdefakes.h>
00041 #include <QtDBus/QtDBus>
00042 
00043 #include <kdialog.h>
00044 #include <kstandarddirs.h>
00045 #include <kdebug.h>
00046 #include <QLabel>
00047 #include <QComboBox>
00048 #include <QVBoxLayout>
00049 
00050 #include "atoms.h"
00051 #include "options.h"
00052 #include "sm.h"
00053 #include "utils.h"
00054 #include "effects.h"
00055 
00056 #define INT8 _X11INT8
00057 #define INT32 _X11INT32
00058 #include <X11/Xproto.h>
00059 #undef INT8
00060 #undef INT32
00061 
00062 namespace KWin
00063 {
00064 
00065 Options* options;
00066 
00067 Atoms* atoms;
00068 
00069 int screen_number = -1;
00070 
00071 static bool initting = false;
00072 
00078 static bool kwin_sync = false;
00079 
00083 // This is copied from KXErrorHandler and modified to explicitly use known extensions
00084 static QByteArray errorMessage( const XErrorEvent& event, Display* dpy )
00085     {
00086     QByteArray ret;
00087     char tmp[256];
00088     char num[256];
00089     if( event.request_code < 128 )
00090         { // Core request
00091         XGetErrorText( dpy, event.error_code, tmp, 255 );
00092         // The explanation in parentheses just makes
00093         // it more verbose and is not really useful
00094         if( char* paren = strchr( tmp, '(' ))
00095             *paren = '\0';
00096         // The various casts are to get overloads non-ambiguous :-/
00097         ret = QByteArray( "error: " ) + (const char*)( tmp ) + '[' + QByteArray::number( event.error_code ) + ']';
00098         sprintf( num, "%d", event.request_code );
00099         XGetErrorDatabaseText( dpy, "XRequest", num, "<unknown>", tmp, 256 );
00100         ret += QByteArray( ", request: " ) + (const char*)( tmp ) + '[' + QByteArray::number( event.request_code ) + ']';
00101         if( event.resourceid != 0 )
00102             ret += QByteArray( ", resource: 0x" ) + QByteArray::number( qlonglong( event.resourceid ), 16 );
00103         }
00104     else // Extensions
00105         {
00106         // XGetErrorText() currently has a bug that makes it fail to find text
00107         // for some errors (when error==error_base), also XGetErrorDatabaseText()
00108         // requires the right extension name, so it is needed to get info about
00109         // all extensions. However that is almost impossible:
00110         // - Xlib itself has it, but in internal data.
00111         // - Opening another X connection now can cause deadlock with server grabs.
00112         // - Fetching it at startup means a bunch of roundtrips.
00113 
00114         // KWin here explicitly uses known extensions.
00115         int nextensions;
00116         const char** extensions;
00117         int* majors;
00118         int* error_bases;
00119         Extensions::fillExtensionsData( extensions, nextensions, majors, error_bases );
00120         XGetErrorText( dpy, event.error_code, tmp, 255 );
00121         int index = -1;
00122         int base = 0;
00123         for( int i = 0; i < nextensions; ++i )
00124             if( error_bases[i] != 0 &&
00125                 event.error_code >= error_bases[i] && ( index == -1 || error_bases[i] > base ))
00126                 {
00127                 index = i;
00128                 base = error_bases[i];
00129                 }
00130         if( tmp == QString::number( event.error_code ))
00131             { // XGetErrorText() failed or it has a bug that causes not finding all errors, check ourselves
00132             if( index != -1 )
00133                 {
00134                 snprintf( num, 255, "%s.%d", extensions[index], event.error_code - base );
00135                 XGetErrorDatabaseText( dpy, "XProtoError", num, "<unknown>", tmp, 255 );
00136                 }
00137             else
00138                 strcpy( tmp, "<unknown>" );
00139             }
00140         if( char* paren = strchr( tmp, '(' ))
00141             *paren = '\0';
00142         if( index != -1 )
00143             ret = QByteArray( "error: " ) + (const char*)( tmp ) + '[' + (const char*)( extensions[index] ) +
00144                 '+' + QByteArray::number( event.error_code - base ) + ']';
00145         else
00146             ret = QByteArray( "error: " ) + (const char*)( tmp ) + '[' + QByteArray::number( event.error_code ) + ']';
00147         tmp[0] = '\0';
00148         for( int i = 0; i < nextensions; ++i )
00149             if( majors[i] == event.request_code )
00150                 {
00151                 snprintf( num, 255, "%s.%d", extensions[i], event.minor_code );
00152                 XGetErrorDatabaseText( dpy, "XRequest", num, "<unknown>", tmp, 255 );
00153                 ret += QByteArray( ", request: " ) + (const char*)( tmp ) + '[' +
00154                     (const char*)( extensions[i] ) + '+' + QByteArray::number( event.minor_code ) + ']';
00155                 }
00156         if( tmp[0] == '\0' ) // Not found?
00157             ret += QByteArray( ", request <unknown> [" ) + QByteArray::number( event.request_code ) + ':'
00158                 + QByteArray::number( event.minor_code ) + ']';
00159         if( event.resourceid != 0 )
00160             ret += QByteArray( ", resource: 0x" ) + QByteArray::number( qlonglong( event.resourceid ), 16 );
00161         }
00162     return ret;
00163     }
00164 
00165 static int x11ErrorHandler( Display* d, XErrorEvent* e )
00166     {
00167     bool ignore_badwindow = true; // Might be temporary
00168 
00169     if( initting && ( e->request_code == X_ChangeWindowAttributes || e->request_code == X_GrabKey ) &&
00170         e->error_code == BadAccess )
00171         {
00172         fputs( i18n( "kwin: it looks like there's already a window manager running. kwin not started.\n" ).toLocal8Bit(), stderr );
00173         exit(1);
00174         }
00175 
00176     if( ignore_badwindow && ( e->error_code == BadWindow || e->error_code == BadColor ))
00177         return 0;
00178 
00179     //fprintf( stderr, "kwin: X Error (%s)\n", KXErrorHandler::errorMessage( *e, d ).data());
00180     fprintf( stderr, "kwin: X Error (%s)\n", errorMessage( *e, d ).data() );
00181 
00182     if( kwin_sync )
00183     fprintf( stderr, "%s\n", kBacktrace().toLocal8Bit().data() );
00184 
00185     if( initting )
00186         {
00187         fputs( i18n( "kwin: failure during initialization; aborting").toLocal8Bit(), stderr );
00188         exit( 1 );
00189         }
00190     return 0;
00191     }
00192 
00193 class AlternativeWMDialog : public KDialog
00194 {
00195     public:
00196         AlternativeWMDialog()
00197             : KDialog()
00198             {
00199             setButtons( KDialog::Ok | KDialog::Cancel );
00200 
00201             QWidget* mainWidget = new QWidget( this );
00202             QVBoxLayout* layout = new QVBoxLayout( mainWidget );
00203             QString text = i18n(
00204                 "KWin is unstable.\n"
00205                 "It seems to have crashed several times in a row.\n"
00206                 "You can select another window manager to run:" );
00207             QLabel* textLabel = new QLabel( text, mainWidget );
00208             layout->addWidget( textLabel );
00209             wmList = new QComboBox( mainWidget );
00210             wmList->setEditable( true );
00211             layout->addWidget( wmList );
00212 
00213             addWM( "metacity" );
00214             addWM( "openbox" );
00215             addWM( "fvwm2" );
00216             addWM( "kwin" );
00217 
00218             setMainWidget( mainWidget );
00219 
00220             raise();
00221             centerOnScreen( this );
00222             }
00223 
00224         void addWM( const QString& wm )
00225             {
00226             // TODO: Check if WM is installed
00227             if( !KStandardDirs::findExe( wm ).isEmpty() )
00228                 wmList->addItem( wm );
00229             }
00230         QString selectedWM() const
00231             { return wmList->currentText(); }
00232 
00233     private:
00234         QComboBox* wmList;
00235 };
00236 
00237 int Application::crashes = 0;
00238 
00239 Application::Application()
00240     : KApplication()
00241     , owner( screen_number )
00242     {
00243     if( KCmdLineArgs::parsedArgs( "qt" )->isSet( "sync" ))
00244         {
00245         kwin_sync = true;
00246         XSynchronize( display(), True );
00247         kDebug( 1212 ) << "Running KWin in sync mode";
00248         }
00249     KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
00250     KSharedConfig::Ptr config = KGlobal::config();
00251     if( !config->isImmutable() && args->isSet( "lock" ))
00252         {
00253 #ifdef __GNUC__
00254 #warning this shouldn not be necessary
00255 #endif
00256         //config->setReadOnly( true );
00257         config->reparseConfiguration();
00258         }
00259 
00260     if( screen_number == -1 )
00261         screen_number = DefaultScreen( display() );
00262 
00263     if( !owner.claim( args->isSet( "replace" ), true ))
00264         {
00265         fputs( i18n( "kwin: unable to claim manager selection, another wm running? (try using --replace)\n" ).toLocal8Bit(), stderr );
00266         ::exit( 1 );
00267         }
00268     connect( &owner, SIGNAL( lostOwnership() ), SLOT( lostSelection() ));
00269 
00270     KCrash::setEmergencySaveFunction( Application::crashHandler );
00271     crashes = args->getOption( "crashes" ).toInt();
00272     if( crashes >= 4 )
00273         { // Something has gone seriously wrong
00274         AlternativeWMDialog dialog;
00275         QString cmd = "kwin";
00276         if( dialog.exec() == QDialog::Accepted )
00277             cmd = dialog.selectedWM();
00278         else
00279             ::exit( 1 );
00280         if( cmd.length() > 500 )
00281             {
00282             kDebug( 1212 ) << "Command is too long, truncating";
00283             cmd = cmd.left( 500 );
00284             }
00285         kDebug( 1212 ) << "Starting" << cmd << "and exiting";
00286         char buf[1024];
00287         sprintf( buf, "%s &", cmd.toAscii().data() );
00288         system( buf );
00289         ::exit( 1 );
00290         }
00291     if( crashes >= 2 )
00292         { // Disable compositing if we have had too many crashes
00293         kDebug( 1212 ) << "Too many crashes recently, disabling compositing";
00294         KConfigGroup compgroup( config, "Compositing" );
00295         compgroup.writeEntry( "Enabled", false );
00296         }
00297     // Reset crashes count if we stay up for more that 15 seconds
00298     QTimer::singleShot( 15*1000, this, SLOT( resetCrashesCount() ));
00299 
00300     // If KWin was already running it saved its configuration after loosing the selection -> Reread
00301     config->reparseConfiguration();
00302 
00303     initting = true; // Startup...
00304 
00305     // Install X11 error handler
00306     XSetErrorHandler( x11ErrorHandler );
00307 
00308     // Check  whether another windowmanager is running
00309     XSelectInput( display(), rootWindow(), SubstructureRedirectMask );
00310     syncX(); // Trigger error now
00311 
00312     atoms = new Atoms;
00313 
00314     initting = false; // TODO
00315 
00316     // This tries to detect compositing options and can use GLX. GLX problems
00317     // (X errors) shouldn't cause kwin to abort, so this is out of the
00318     // critical startup section where x errors cause kwin to abort.
00319     options = new Options;
00320 
00321     // create workspace.
00322     (void) new Workspace( isSessionRestored() );
00323 
00324     syncX(); // Trigger possible errors, there's still a chance to abort
00325 
00326     initting = false; // Startup done, we are up and running now.
00327 
00328     XEvent e;
00329     e.xclient.type = ClientMessage;
00330     e.xclient.message_type = XInternAtom( display(), "_KDE_SPLASH_PROGRESS", False );
00331     e.xclient.display = display();
00332     e.xclient.window = rootWindow();
00333     e.xclient.format = 8;
00334     strcpy( e.xclient.data.b, "wm" );
00335     XSendEvent( display(), rootWindow(), False, SubstructureNotifyMask, &e );
00336     }
00337 
00338 Application::~Application()
00339     {
00340     delete Workspace::self();
00341     if( owner.ownerWindow() != None ) // If there was no --replace (no new WM)
00342         XSetInputFocus( display(), PointerRoot, RevertToPointerRoot, xTime() );
00343     delete options;
00344     delete effects;
00345     delete atoms;
00346     }
00347 
00348 void Application::lostSelection()
00349     {
00350     sendPostedEvents();
00351     delete Workspace::self();
00352     // Remove windowmanager privileges
00353     XSelectInput( display(), rootWindow(), PropertyChangeMask );
00354     quit();
00355     }
00356 
00357 bool Application::x11EventFilter( XEvent* e )
00358     {
00359     if( Workspace::self() && Workspace::self()->workspaceEvent( e ))
00360         return true;
00361     return KApplication::x11EventFilter( e );
00362     }
00363 
00364 bool Application::notify( QObject* o, QEvent* e )
00365     {
00366     if( Workspace::self()->workspaceEvent( e ))
00367         return true;
00368     return KApplication::notify( o, e );
00369     }
00370 
00371 static void sighandler( int )
00372     {
00373     QApplication::exit();
00374     }
00375 
00376 void Application::crashHandler( int signal )
00377     {
00378     crashes++;
00379 
00380     fprintf( stderr, "Application::crashHandler() called with signal %d; recent crashes: %d\n", signal, crashes );
00381     char cmd[1024];
00382     sprintf( cmd, "kwin --crashes %d &", crashes );
00383 
00384     sleep( 1 );
00385     system( cmd );
00386     }
00387 
00388 void Application::resetCrashesCount()
00389     {
00390     crashes = 0;
00391     }
00392 
00393 } // namespace
00394 
00395 static const char version[] = KDE_VERSION_STRING;
00396 static const char description[] = I18N_NOOP( "KDE window manager" );
00397 
00398 extern "C"
00399 KDE_EXPORT int kdemain( int argc, char * argv[] )
00400     {
00401     bool restored = false;
00402     for( int arg = 1; arg < argc; arg++ )
00403         {
00404         if( !qstrcmp( argv[arg], "-session" ))
00405             {
00406             restored = true;
00407             break;
00408             }
00409         }
00410 
00411     if( !restored )
00412         { // We only do the multihead fork if we are not restored by the session
00413           // manager, since the session manager will register multiple kwins,
00414           // one for each screen...
00415         QByteArray multiHead = getenv( "KDE_MULTIHEAD" );
00416         if( multiHead.toLower() == "true" )
00417             {
00418             Display* dpy = XOpenDisplay( NULL );
00419             if( !dpy )
00420                 {
00421                 fprintf( stderr, "%s: FATAL ERROR while trying to open display %s\n",
00422                     argv[0], XDisplayName( NULL ));
00423                 exit( 1 );
00424                 }
00425 
00426             int number_of_screens = ScreenCount( dpy );
00427             KWin::screen_number = DefaultScreen( dpy );
00428             int pos; // Temporarily needed to reconstruct DISPLAY var if multi-head
00429             QByteArray display_name = XDisplayString( dpy );
00430             XCloseDisplay( dpy );
00431             dpy = 0;
00432 
00433             if(( pos = display_name.lastIndexOf( '.' )) != -1 )
00434                 display_name.remove( pos, 10 ); // 10 is enough to be sure we removed ".s"
00435 
00436             QString envir;
00437             if( number_of_screens != 1 )
00438                 {
00439                 for( int i = 0; i < number_of_screens; i++ )
00440                     {
00441                     // If execution doesn't pass by here, then kwin
00442                     // acts exactly as previously
00443                     if( i != KWin::screen_number && fork() == 0 )
00444                         {
00445                         KWin::screen_number = i;
00446                         // Break here because we are the child process, we don't
00447                         // want to fork() anymore
00448                         break;
00449                         }
00450                     }
00451                 // In the next statement, display_name shouldn't contain a screen
00452                 // number. If it had it, it was removed at the "pos" check
00453                 envir.sprintf( "DISPLAY=%s.%d", display_name.data(), KWin::screen_number );
00454 
00455                 if( putenv( strdup( envir.toAscii() )))
00456                     {
00457                     fprintf( stderr, "%s: WARNING: unable to set DISPLAY environment variable\n", argv[0] );
00458                     perror("putenv()");
00459                     }
00460                 }
00461             }
00462         }
00463 
00464     KAboutData aboutData(
00465         "kwin",                     // The program name used internally
00466         0,                          // The message catalog name. If null, program name is used instead
00467         ki18n( "KWin" ),            // A displayable program name string
00468         version,                    // The program version string
00469         ki18n( description ),       // Short description of what the app does
00470         KAboutData::License_GPL,    // The license this code is released under
00471         ki18n( "(c) 1999-2008, The KDE Developers" )); // Copyright Statement
00472     aboutData.addAuthor( ki18n( "Matthias Ettrich" ),KLocalizedString(), "ettrich@kde.org" );
00473     aboutData.addAuthor( ki18n( "Cristian Tibirna" ),KLocalizedString(), "tibirna@kde.org" );
00474     aboutData.addAuthor( ki18n( "Daniel M. Duley" ),KLocalizedString(), "mosfet@kde.org" );
00475     aboutData.addAuthor( ki18n( "Luboš Luňák" ), ki18n( "Maintainer" ), "l.lunak@kde.org" );
00476 
00477     KCmdLineArgs::init( argc, argv, &aboutData );
00478 
00479     KCmdLineOptions args;
00480     args.add( "lock", ki18n( "Disable configuration options" ));
00481     args.add( "replace", ki18n( "Replace already-running ICCCM2.0-compliant window manager" ));
00482     args.add( "crashes <n>", ki18n( "Indicate that KWin has recently crashed n times" ));
00483     KCmdLineArgs::addCmdLineOptions( args );
00484 
00485     if( signal( SIGTERM, KWin::sighandler ) == SIG_IGN )
00486         signal( SIGTERM, SIG_IGN );
00487     if( signal( SIGINT, KWin::sighandler ) == SIG_IGN )
00488         signal( SIGINT, SIG_IGN );
00489     if( signal( SIGHUP, KWin::sighandler ) == SIG_IGN )
00490         signal( SIGHUP, SIG_IGN );
00491 
00492     // HACK: This is needed for AIGLX
00493     if( qstrcmp( getenv( "KWIN_DIRECT_GL" ), "1" ) != 0 )
00494         setenv( "LIBGL_ALWAYS_INDIRECT","1", true );
00495 
00496     // HACK: this is needed to work around a Qt4.4.0RC1 bug (#157659)
00497     setenv( "QT_SLOW_TOPLEVEL_RESIZE", "1", true );
00498 
00499     KWin::Application a;
00500     KWin::SessionManager weAreIndeed;
00501     KWin::SessionSaveDoneHelper helper;
00502     KGlobal::locale()->insertCatalog( "kwin_effects" );
00503 
00504     // Announce when KWIN_DIRECT_GL is set for above HACK
00505     if( qstrcmp( getenv( "KWIN_DIRECT_GL" ), "1" ) == 0 )
00506         kDebug( 1212 ) << "KWIN_DIRECT_GL set, not forcing LIBGL_ALWAYS_INDIRECT=1";
00507 
00508     fcntl( XConnectionNumber( KWin::display() ), F_SETFD, 1 );
00509 
00510     QString appname;
00511     if( KWin::screen_number == 0 )
00512         appname = "org.kde.kwin";
00513     else
00514         appname.sprintf( "org.kde.kwin-screen-%d", KWin::screen_number );
00515 
00516     QDBusConnection::sessionBus().interface()->registerService(
00517         appname, QDBusConnectionInterface::DontQueueService );
00518 
00519     return a.exec();
00520     }
00521 
00522 #include "main.moc"

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal