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
00073
00074
00075 static bool kwin_sync = false;
00076
00077
00078 static QByteArray errorMessage( const XErrorEvent& event, Display* dpy )
00079 {
00080 QByteArray ret;
00081 char tmp[ 256 ];
00082 char num[ 256 ];
00083 if( event.request_code < 128 )
00084 {
00085 XGetErrorText( dpy, event.error_code, tmp, 255 );
00086 if( char* paren = strchr( tmp, '(' ))
00087 *paren = '\0';
00088
00089 ret = QByteArray( "error: " ) + (const char*)tmp + '[' + QByteArray::number( event.error_code ) + ']';
00090 sprintf( num, "%d", event.request_code );
00091 XGetErrorDatabaseText( dpy, "XRequest", num, "<unknown>", tmp, 256 );
00092 ret += QByteArray( ", request: " ) + (const char*)tmp + '[' + QByteArray::number( event.request_code ) + ']';
00093 if( event.resourceid != 0 )
00094 ret += QByteArray( ", resource: 0x" ) + QByteArray::number( (qlonglong)event.resourceid, 16 );
00095 }
00096 else
00097 {
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107 int nextensions;
00108 const char** extensions;
00109 int* majors;
00110 int* error_bases;
00111 Extensions::fillExtensionsData( extensions, nextensions, majors, error_bases );
00112 XGetErrorText( dpy, event.error_code, tmp, 255 );
00113 int index = -1;
00114 int base = 0;
00115 for( int i = 0;
00116 i < nextensions;
00117 ++i )
00118 if( error_bases[ i ] != 0
00119 && event.error_code >= error_bases[ i ] && ( index == -1 || error_bases[ i ] > base ))
00120 {
00121 index = i;
00122 base = error_bases[ i ];
00123 }
00124 if( tmp == QString::number( event.error_code ))
00125 {
00126 if( index != -1 )
00127 {
00128 snprintf( num, 255, "%s.%d", extensions[ index ], event.error_code - base );
00129 XGetErrorDatabaseText( dpy, "XProtoError", num, "<unknown>", tmp, 255 );
00130 }
00131 else
00132 strcpy( tmp, "<unknown>" );
00133 }
00134 if( char* paren = strchr( tmp, '(' ))
00135 *paren = '\0';
00136 if( index != -1 )
00137 ret = QByteArray( "error: " ) + (const char*)tmp + '[' + (const char*)extensions[ index ]
00138 + '+' + QByteArray::number( event.error_code - base ) + ']';
00139 else
00140 ret = QByteArray( "error: " ) + (const char*)tmp + '[' + QByteArray::number( event.error_code ) + ']';
00141 tmp[ 0 ] = '\0';
00142 for( int i = 0;
00143 i < nextensions;
00144 ++i )
00145 if( majors[ i ] == event.request_code )
00146 {
00147 snprintf( num, 255, "%s.%d", extensions[ i ], event.minor_code );
00148 XGetErrorDatabaseText( dpy, "XRequest", num, "<unknown>", tmp, 255 );
00149 ret += QByteArray( ", request: " ) + (const char*)tmp + '[' + (const char*)extensions[ i ] + '+'
00150 + QByteArray::number( event.minor_code ) + ']';
00151 }
00152 if( tmp[ 0 ] == '\0' )
00153 ret += QByteArray( ", request <unknown> [" ) + QByteArray::number( event.request_code ) + ':'
00154 + QByteArray::number( event.minor_code ) + ']';
00155 if( event.resourceid != 0 )
00156 ret += QByteArray( ", resource: 0x" ) + QByteArray::number( (qlonglong)event.resourceid, 16 );
00157 }
00158 return ret;
00159 }
00160
00161 static
00162 int x11ErrorHandler(Display *d, XErrorEvent *e)
00163 {
00164 bool ignore_badwindow = true;
00165
00166 if (initting &&
00167 (
00168 e->request_code == X_ChangeWindowAttributes
00169 || e->request_code == X_GrabKey
00170 )
00171 && (e->error_code == BadAccess))
00172 {
00173 fputs(i18n("kwin: it looks like there's already a window manager running. kwin not started.\n").toLocal8Bit(), stderr);
00174 exit(1);
00175 }
00176
00177 if (ignore_badwindow && (e->error_code == BadWindow || e->error_code == BadColor))
00178 return 0;
00179
00180
00181 fprintf( stderr, "kwin: X Error (%s)\n", errorMessage( *e, d ).data());
00182
00183 if( kwin_sync )
00184 fprintf( stderr, "%s\n", kBacktrace().toLocal8Bit().data());
00185
00186 if (initting)
00187 {
00188 fputs(i18n("kwin: failure during initialization; aborting").toLocal8Bit(), stderr);
00189 exit(1);
00190 }
00191 return 0;
00192 }
00193
00194
00195 class AlternativeWMDialog : public KDialog
00196 {
00197 public:
00198 AlternativeWMDialog() : KDialog()
00199 {
00200 setButtons( KDialog::Ok | KDialog::Cancel );
00201
00202 QWidget* mainWidget = new QWidget( this );
00203 QVBoxLayout* layout = new QVBoxLayout( mainWidget );
00204 QString text = i18n("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 void addWM( const QString& wm )
00224 {
00225
00226 if( !KStandardDirs::findExe( wm ).isEmpty() )
00227 {
00228 wmList->addItem( wm );
00229 }
00230 }
00231 QString selectedWM() const { return wmList->currentText(); }
00232
00233 private:
00234 QComboBox* wmList;
00235 };
00236
00237 int Application::crashes = 0;
00238
00239 Application::Application( )
00240 : KApplication( ), owner( screen_number )
00241 {
00242 if( KCmdLineArgs::parsedArgs( "qt" )->isSet( "sync" ))
00243 {
00244 kwin_sync = true;
00245 XSynchronize( display(), True );
00246 kDebug() << "Running KWin in sync mode";
00247 }
00248 KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
00249 KSharedConfig::Ptr config = KGlobal::config();
00250 if (!config->isImmutable() && args->isSet("lock"))
00251 {
00252 #warning this shouldn not be necessary
00253
00254 config->reparseConfiguration();
00255 }
00256
00257 if (screen_number == -1)
00258 screen_number = DefaultScreen(display());
00259
00260 if( !owner.claim( args->isSet( "replace" ), true ))
00261 {
00262 fputs(i18n("kwin: unable to claim manager selection, another wm running? (try using --replace)\n").toLocal8Bit(), stderr);
00263 ::exit(1);
00264 }
00265 connect( &owner, SIGNAL( lostOwnership()), SLOT( lostSelection()));
00266
00267 KCrash::setEmergencySaveFunction( Application::crashHandler );
00268 crashes = args->getOption("crashes").toInt();
00269 if(crashes >= 4)
00270 {
00271
00272 AlternativeWMDialog dialog;
00273 QString cmd = "kwin";
00274 if( dialog.exec() == QDialog::Accepted )
00275 {
00276 cmd = dialog.selectedWM();
00277 }
00278 else
00279 ::exit(1);
00280 if( cmd.length() > 500 )
00281 {
00282 kDebug() << "Command is too long, truncating";
00283 cmd = cmd.left(500);
00284 }
00285 kDebug() << "Starting" << cmd << "and exiting";
00286 char buf[1024];
00287 sprintf(buf, "%s &", cmd.toAscii().data());
00288 system(buf);
00289 ::exit(1);
00290 }
00291
00292 if( crashes >= 2 )
00293 {
00294 kDebug() << "Too many crashes recently, disabling compositing";
00295 KConfigGroup compgroup( config, "Compositing" );
00296 compgroup.writeEntry( "Enabled", false );
00297 }
00298
00299 QTimer::singleShot( 15*1000, this, SLOT( resetCrashesCount() ));
00300
00301
00302 config->reparseConfiguration();
00303
00304 initting = true;
00305
00306
00307 XSetErrorHandler( x11ErrorHandler );
00308
00309
00310 XSelectInput(display(), rootWindow(), SubstructureRedirectMask );
00311 syncX();
00312
00313 atoms = new Atoms;
00314
00315 initting = false;
00316
00317
00318
00319
00320 options = new Options;
00321
00322
00323 (void) new Workspace( isSessionRestored() );
00324
00325 syncX();
00326
00327 initting = false;
00328
00329 XEvent e;
00330 e.xclient.type = ClientMessage;
00331 e.xclient.message_type = XInternAtom( display(), "_KDE_SPLASH_PROGRESS", False );
00332 e.xclient.display = display();
00333 e.xclient.window = rootWindow();
00334 e.xclient.format = 8;
00335 strcpy( e.xclient.data.b, "wm" );
00336 XSendEvent( display(), rootWindow(), False, SubstructureNotifyMask, &e );
00337 }
00338
00339 Application::~Application()
00340 {
00341 delete Workspace::self();
00342 if( owner.ownerWindow() != None )
00343 XSetInputFocus( display(), PointerRoot, RevertToPointerRoot, xTime() );
00344 delete options;
00345 delete effects;
00346 delete atoms;
00347 }
00348
00349 void Application::lostSelection()
00350 {
00351 sendPostedEvents();
00352 delete Workspace::self();
00353
00354 XSelectInput(display(), rootWindow(), PropertyChangeMask );
00355 quit();
00356 }
00357
00358 bool Application::x11EventFilter( XEvent *e )
00359 {
00360 if ( Workspace::self() && Workspace::self()->workspaceEvent( e ) )
00361 return true;
00362 return KApplication::x11EventFilter( e );
00363 }
00364
00365 bool Application::notify( QObject* o, QEvent* e )
00366 {
00367 if( Workspace::self()->workspaceEvent( e ))
00368 return true;
00369 return KApplication::notify( o, e );
00370 }
00371
00372 static void sighandler(int)
00373 {
00374 QApplication::exit();
00375 }
00376
00377 void Application::crashHandler(int signal)
00378 {
00379 crashes++;
00380
00381 fprintf( stderr, "Application::crashHandler() called with signal %d; recent crashes: %d\n", signal, crashes );
00382 char cmd[1024];
00383 sprintf( cmd, "kwin --crashes %d &", crashes );
00384
00385 sleep( 1 );
00386 system( cmd );
00387 }
00388
00389 void Application::resetCrashesCount()
00390 {
00391 crashes = 0;
00392 }
00393
00394
00395 }
00396
00397 static const char version[] = "3.0";
00398 static const char description[] = I18N_NOOP( "KDE window manager" );
00399
00400 extern "C"
00401 KDE_EXPORT int kdemain( int argc, char * argv[] )
00402 {
00403 bool restored = false;
00404 for (int arg = 1; arg < argc; arg++)
00405 {
00406 if (! qstrcmp(argv[arg], "-session"))
00407 {
00408 restored = true;
00409 break;
00410 }
00411 }
00412
00413 if (! restored)
00414 {
00415
00416
00417
00418 QByteArray multiHead = getenv("KDE_MULTIHEAD");
00419 if (multiHead.toLower() == "true")
00420 {
00421
00422 Display* dpy = XOpenDisplay( NULL );
00423 if ( !dpy )
00424 {
00425 fprintf(stderr, "%s: FATAL ERROR while trying to open display %s\n",
00426 argv[0], XDisplayName(NULL ) );
00427 exit (1);
00428 }
00429
00430 int number_of_screens = ScreenCount( dpy );
00431 KWin::screen_number = DefaultScreen( dpy );
00432 int pos;
00433 QByteArray display_name = XDisplayString( dpy );
00434 XCloseDisplay( dpy );
00435 dpy = 0;
00436
00437 if ((pos = display_name.lastIndexOf('.')) != -1 )
00438 display_name.remove(pos,10);
00439
00440 QString envir;
00441 if (number_of_screens != 1)
00442 {
00443 for (int i = 0; i < number_of_screens; i++ )
00444 {
00445
00446
00447 if ( i != KWin::screen_number && fork() == 0 )
00448 {
00449 KWin::screen_number = i;
00450
00451
00452 break;
00453 }
00454 }
00455
00456
00457 envir.sprintf("DISPLAY=%s.%d", display_name.data(), KWin::screen_number);
00458
00459 if (putenv( strdup(envir.toAscii())) )
00460 {
00461 fprintf(stderr,
00462 "%s: WARNING: unable to set DISPLAY environment variable\n",
00463 argv[0]);
00464 perror("putenv()");
00465 }
00466 }
00467 }
00468 }
00469
00470 KAboutData aboutData( "kwin", 0, ki18n("KWin"),
00471 version, ki18n(description), KAboutData::License_GPL,
00472 ki18n("(c) 1999-2005, The KDE Developers"));
00473 aboutData.addAuthor(ki18n("Matthias Ettrich"),KLocalizedString(), "ettrich@kde.org");
00474 aboutData.addAuthor(ki18n("Cristian Tibirna"),KLocalizedString(), "tibirna@kde.org");
00475 aboutData.addAuthor(ki18n("Daniel M. Duley"),KLocalizedString(), "mosfet@kde.org");
00476 aboutData.addAuthor(ki18n("Luboš Luňák"), ki18n( "Maintainer" ), "l.lunak@kde.org");
00477
00478 KCmdLineArgs::init(argc, argv, &aboutData);
00479
00480 KCmdLineOptions args;
00481 args.add("lock", ki18n("Disable configuration options"));
00482 args.add("replace", ki18n("Replace already-running ICCCM2.0-compliant window manager"));
00483 args.add("crashes <n>", ki18n("Indicate that KWin has recently crashed n times"));
00484 KCmdLineArgs::addCmdLineOptions( args );
00485
00486 if (signal(SIGTERM, KWin::sighandler) == SIG_IGN)
00487 signal(SIGTERM, SIG_IGN);
00488 if (signal(SIGINT, KWin::sighandler) == SIG_IGN)
00489 signal(SIGINT, SIG_IGN);
00490 if (signal(SIGHUP, KWin::sighandler) == SIG_IGN)
00491 signal(SIGHUP, SIG_IGN);
00492
00493 setenv( "LIBGL_ALWAYS_INDIRECT","1", true );
00494
00495 setenv( "QT_SLOW_TOPLEVEL_RESIZE", "1", true );
00496 KWin::Application a;
00497 KWin::SessionManager weAreIndeed;
00498 KWin::SessionSaveDoneHelper helper;
00499 KGlobal::locale()->insertCatalog( "kwin_effects" );
00500
00501 fcntl(XConnectionNumber(KWin::display()), F_SETFD, 1);
00502
00503 QString appname;
00504 if (KWin::screen_number == 0)
00505 appname = "org.kde.kwin";
00506 else
00507 appname.sprintf("org.kde.kwin-screen-%d", KWin::screen_number);
00508
00509 QDBusConnection::sessionBus().interface()->registerService( appname, QDBusConnectionInterface::DontQueueService );
00510
00511 return a.exec();
00512 }
00513
00514 #include "main.moc"