/***************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak You can Freely distribute this program under the GNU General Public License. See the file "COPYING" for the exact licensing terms. ******************************************************************/ #include "main.h" //#define QT_CLEAN_NAMESPACE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atoms.h" #include "options.h" #include "sm.h" #include "utils.h" #include "effects.h" #define INT8 _X11INT8 #define INT32 _X11INT32 #include #undef INT8 #undef INT32 namespace KWin { Options* options; Atoms* atoms; int screen_number = -1; static bool initting = false; // Whether to run Xlib in synchronous mode and print backtraces for X errors. // Note that you most probably need to configure cmake with "-D__KDE_HAVE_GCC_VISIBILITY=0" // and -rdynamic in CXXFLAGS for kBacktrace() to work. static bool kwin_sync = false; static int x11ErrorHandler(Display *d, XErrorEvent *e) { char msg[80], req[80], number[80]; bool ignore_badwindow = true; //maybe temporary if (initting && ( e->request_code == X_ChangeWindowAttributes || e->request_code == X_GrabKey ) && (e->error_code == BadAccess)) { fputs(i18n("kwin: it looks like there's already a window manager running. kwin not started.\n").toLocal8Bit(), stderr); exit(1); } if (ignore_badwindow && (e->error_code == BadWindow || e->error_code == BadColor)) return 0; XGetErrorText(d, e->error_code, msg, sizeof(msg)); sprintf(number, "%d", e->request_code); XGetErrorDatabaseText(d, "XRequest", number, "", req, sizeof(req)); fprintf(stderr, "kwin: %s(0x%lx): %s\n", req, e->resourceid, msg); if( kwin_sync ) kDebug() << kBacktrace(); if (initting) { fputs(i18n("kwin: failure during initialization; aborting").toLocal8Bit(), stderr); exit(1); } return 0; } class AlternativeWMDialog : public KDialog { public: AlternativeWMDialog() : KDialog() { setButtons( KDialog::Ok | KDialog::Cancel ); QWidget* mainWidget = new QWidget( this ); QVBoxLayout* layout = new QVBoxLayout( mainWidget ); QString text = i18n("KWin is unstable.\n" "It seems to have crashed several times in a row.\n" "You can select another window manager to run:"); QLabel* textLabel = new QLabel( text, mainWidget ); layout->addWidget( textLabel ); wmList = new QComboBox( mainWidget ); wmList->setEditable( true ); layout->addWidget( wmList ); addWM( "metacity" ); addWM( "openbox" ); addWM( "fvwm2" ); addWM( "kwin" ); setMainWidget(mainWidget); raise(); centerOnScreen( this ); } void addWM( const QString& wm ) { // TODO: check if wm is installed if( !KStandardDirs::findExe( wm ).isEmpty() ) { wmList->addItem( wm ); } } QString selectedWM() const { return wmList->currentText(); } private: QComboBox* wmList; }; int Application::crashes = 0; Application::Application( ) : KApplication( ), owner( screen_number ) { if( KCmdLineArgs::parsedArgs( "qt" )->isSet( "sync" )) { kwin_sync = true; XSynchronize( display(), True ); kDebug() << "Running KWin in sync mode"; } KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); KSharedConfig::Ptr config = KGlobal::config(); if (!config->isImmutable() && args->isSet("lock")) { #warning this shouldn not be necessary //config->setReadOnly(true); config->reparseConfiguration(); } if (screen_number == -1) screen_number = DefaultScreen(display()); if( !owner.claim( args->isSet( "replace" ), true )) { fputs(i18n("kwin: unable to claim manager selection, another wm running? (try using --replace)\n").toLocal8Bit(), stderr); ::exit(1); } connect( &owner, SIGNAL( lostOwnership()), SLOT( lostSelection())); KCrash::setEmergencySaveFunction( Application::crashHandler ); crashes = args->getOption("crashes").toInt(); if(crashes >= 4) { // Something has gone seriously wrong AlternativeWMDialog dialog; QString cmd = "kwin"; if( dialog.exec() == QDialog::Accepted ) { cmd = dialog.selectedWM(); } if( cmd.length() > 500 ) { kDebug() << "Command is too long, truncating"; cmd = cmd.left(500); } kDebug() << "Starting" << cmd << "and exiting"; char buf[1024]; sprintf(buf, "%s &", cmd.toAscii().data()); system(buf); ::exit(1); } // Disable compositing if we have had too many crashes if( crashes >= 2 ) { kDebug() << "Too many crashes recently, disabling compositing"; KConfigGroup compgroup( config, "Compositing" ); compgroup.writeEntry( "Enabled", false ); } // Reset crashes count if we stay up for more that 15 seconds QTimer::singleShot( 15*1000, this, SLOT( resetCrashesCount() )); // if there was already kwin running, it saved its configuration after loosing the selection -> reread config->reparseConfiguration(); initting = true; // startup.... // install X11 error handler XSetErrorHandler( x11ErrorHandler ); // check whether another windowmanager is running XSelectInput(display(), rootWindow(), SubstructureRedirectMask ); syncX(); // trigger error now atoms = new Atoms; initting = false; // TODO // This tries to detect compositing options and can use GLX. GLX problems // (X errors) shouldn't cause kwin to abort, so this is out of the // critical startup section where x errors cause kwin to abort. options = new Options; // create workspace. (void) new Workspace( isSessionRestored() ); syncX(); // trigger possible errors, there's still a chance to abort initting = false; // startup done, we are up and running now. XEvent e; e.xclient.type = ClientMessage; e.xclient.message_type = XInternAtom( display(), "_KDE_SPLASH_PROGRESS", False ); e.xclient.display = display(); e.xclient.window = rootWindow(); e.xclient.format = 8; strcpy( e.xclient.data.b, "wm" ); XSendEvent( display(), rootWindow(), False, SubstructureNotifyMask, &e ); } Application::~Application() { delete Workspace::self(); if( owner.ownerWindow() != None ) // if there was no --replace (no new WM) XSetInputFocus( display(), PointerRoot, RevertToPointerRoot, xTime() ); delete options; delete effects; delete atoms; } void Application::lostSelection() { sendPostedEvents(); delete Workspace::self(); // remove windowmanager privileges XSelectInput(display(), rootWindow(), PropertyChangeMask ); quit(); } bool Application::x11EventFilter( XEvent *e ) { if ( Workspace::self() && Workspace::self()->workspaceEvent( e ) ) return true; return KApplication::x11EventFilter( e ); } bool Application::notify( QObject* o, QEvent* e ) { if( Workspace::self()->workspaceEvent( e )) return true; return KApplication::notify( o, e ); } static void sighandler(int) { QApplication::exit(); } void Application::crashHandler(int signal) { crashes++; fprintf( stderr, "Application::crashHandler() called with signal %d; recent crashes: %d\n", signal, crashes ); char cmd[1024]; sprintf( cmd, "kwin --crashes %d &", crashes ); sleep( 1 ); system( cmd ); } void Application::resetCrashesCount() { crashes = 0; } } // namespace static const char version[] = "3.0"; static const char description[] = I18N_NOOP( "KDE window manager" ); extern "C" KDE_EXPORT int kdemain( int argc, char * argv[] ) { bool restored = false; for (int arg = 1; arg < argc; arg++) { if (! qstrcmp(argv[arg], "-session")) { restored = true; break; } } if (! restored) { // we only do the multihead fork if we are not restored by the session // manager, since the session manager will register multiple kwins, // one for each screen... QByteArray multiHead = getenv("KDE_MULTIHEAD"); if (multiHead.toLower() == "true") { Display* dpy = XOpenDisplay( NULL ); if ( !dpy ) { fprintf(stderr, "%s: FATAL ERROR while trying to open display %s\n", argv[0], XDisplayName(NULL ) ); exit (1); } int number_of_screens = ScreenCount( dpy ); KWin::screen_number = DefaultScreen( dpy ); int pos; // temporarily needed to reconstruct DISPLAY var if multi-head QByteArray display_name = XDisplayString( dpy ); XCloseDisplay( dpy ); dpy = 0; if ((pos = display_name.lastIndexOf('.')) != -1 ) display_name.remove(pos,10); // 10 is enough to be sure we removed ".s" QString envir; if (number_of_screens != 1) { for (int i = 0; i < number_of_screens; i++ ) { // if execution doesn't pass by here, then kwin // acts exactly as previously if ( i != KWin::screen_number && fork() == 0 ) { KWin::screen_number = i; // break here because we are the child process, we don't // want to fork() anymore break; } } // in the next statement, display_name shouldn't contain a screen // number. If it had it, it was removed at the "pos" check envir.sprintf("DISPLAY=%s.%d", display_name.data(), KWin::screen_number); if (putenv( strdup(envir.toAscii())) ) { fprintf(stderr, "%s: WARNING: unable to set DISPLAY environment variable\n", argv[0]); perror("putenv()"); } } } } KAboutData aboutData( "kwin", 0, ki18n("KWin"), version, ki18n(description), KAboutData::License_GPL, ki18n("(c) 1999-2005, The KDE Developers")); aboutData.addAuthor(ki18n("Matthias Ettrich"),KLocalizedString(), "ettrich@kde.org"); aboutData.addAuthor(ki18n("Cristian Tibirna"),KLocalizedString(), "tibirna@kde.org"); aboutData.addAuthor(ki18n("Daniel M. Duley"),KLocalizedString(), "mosfet@kde.org"); aboutData.addAuthor(ki18n("Luboš Luňák"), ki18n( "Maintainer" ), "l.lunak@kde.org"); KCmdLineArgs::init(argc, argv, &aboutData); KCmdLineOptions args; args.add("lock", ki18n("Disable configuration options")); args.add("replace", ki18n("Replace already-running ICCCM2.0-compliant window manager")); args.add("crashes ", ki18n("Indicate that KWin has recently crashed n times")); KCmdLineArgs::addCmdLineOptions( args ); if (signal(SIGTERM, KWin::sighandler) == SIG_IGN) signal(SIGTERM, SIG_IGN); if (signal(SIGINT, KWin::sighandler) == SIG_IGN) signal(SIGINT, SIG_IGN); if (signal(SIGHUP, KWin::sighandler) == SIG_IGN) signal(SIGHUP, SIG_IGN); KWin::Application a; KWin::SessionManager weAreIndeed; KWin::SessionSaveDoneHelper helper; fcntl(XConnectionNumber(KWin::display()), F_SETFD, 1); QString appname; if (KWin::screen_number == 0) appname = "org.kde.kwin"; else appname.sprintf("org.kde.kwin-screen-%d", KWin::screen_number); QDBusConnection::sessionBus().interface()->registerService( appname, QDBusConnectionInterface::DontQueueService ); return a.exec(); } #include "main.moc"