00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "main.h"
00023
00024
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
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 {
00091 XGetErrorText( dpy, event.error_code, tmp, 255 );
00092
00093
00094 if( char* paren = strchr( tmp, '(' ))
00095 *paren = '\0';
00096
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
00105 {
00106
00107
00108
00109
00110
00111
00112
00113
00114
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 {
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' )
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;
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
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
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
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 {
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 {
00293 kDebug( 1212 ) << "Too many crashes recently, disabling compositing";
00294 KConfigGroup compgroup( config, "Compositing" );
00295 compgroup.writeEntry( "Enabled", false );
00296 }
00297
00298 QTimer::singleShot( 15*1000, this, SLOT( resetCrashesCount() ));
00299
00300
00301 config->reparseConfiguration();
00302
00303 initting = true;
00304
00305
00306 XSetErrorHandler( x11ErrorHandler );
00307
00308
00309 XSelectInput( display(), rootWindow(), SubstructureRedirectMask );
00310 syncX();
00311
00312 atoms = new Atoms;
00313
00314 initting = false;
00315
00316
00317
00318
00319 options = new Options;
00320
00321
00322 (void) new Workspace( isSessionRestored() );
00323
00324 syncX();
00325
00326 initting = false;
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 )
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
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 }
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 {
00413
00414
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;
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 );
00435
00436 QString envir;
00437 if( number_of_screens != 1 )
00438 {
00439 for( int i = 0; i < number_of_screens; i++ )
00440 {
00441
00442
00443 if( i != KWin::screen_number && fork() == 0 )
00444 {
00445 KWin::screen_number = i;
00446
00447
00448 break;
00449 }
00450 }
00451
00452
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",
00466 0,
00467 ki18n( "KWin" ),
00468 version,
00469 ki18n( description ),
00470 KAboutData::License_GPL,
00471 ki18n( "(c) 1999-2008, The KDE Developers" ));
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
00493 if( qstrcmp( getenv( "KWIN_DIRECT_GL" ), "1" ) != 0 )
00494 setenv( "LIBGL_ALWAYS_INDIRECT","1", true );
00495
00496
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
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"