/* Greeter module for xdm Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org> Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org> 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <config.h> #include "tdm_greet.h" #include "tdmshutdown.h" #include "tdmconfig.h" #include "kgapp.h" #include "kgreeter.h" #ifdef XDMCP # include "kchooser.h" #endif #include "sakdlg.h" #include <kprocess.h> #include <kcmdlineargs.h> #include <kcrash.h> #include <kstandarddirs.h> #include <ksimpleconfig.h> #include <klocale.h> #include <kdebug.h> #ifdef WITH_XRANDR #include <libkrandr/libkrandr.h> #endif #include <tqtimer.h> #include <tqstring.h> #include <tqcursor.h> #include <tqpalette.h> #include <stdlib.h> // free(), exit() #include <unistd.h> // alarm() #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/keysym.h> #include <X11/cursorfont.h> #ifdef HAVE_XCOMPOSITE #include <X11/extensions/Xrender.h> #include <X11/extensions/Xcomposite.h> #endif #include <pwd.h> #define TSAK_FIFO_FILE "/tmp/tdesocket-global/tsak" bool argb_visual_available = false; bool has_twin = false; bool is_themed = false; bool trinity_desktop_lock_use_sak = TRUE; static int ignoreXError( Display *dpy ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED ) { return 0; } extern "C" { static void sigAlarm( int ) { exit( EX_RESERVER_DPY ); } } GreeterApp::GreeterApp() { pingInterval = _isLocal ? 0 : _pingInterval; if (pingInterval) { struct sigaction sa; sigemptyset( &sa.sa_mask ); sa.sa_flags = 0; sa.sa_handler = sigAlarm; sigaction( SIGALRM, &sa, 0 ); alarm( pingInterval * 70 ); // sic! give the "proper" pinger enough time startTimer( pingInterval * 60000 ); } } GreeterApp::GreeterApp(Display *dpy, Qt::HANDLE visual, Qt::HANDLE colormap) : KApplication(dpy, visual, colormap) { pingInterval = _isLocal ? 0 : _pingInterval; if (pingInterval) { struct sigaction sa; sigemptyset( &sa.sa_mask ); sa.sa_flags = 0; sa.sa_handler = sigAlarm; sigaction( SIGALRM, &sa, 0 ); alarm( pingInterval * 70 ); // sic! give the "proper" pinger enough time startTimer( pingInterval * 60000 ); } } void GreeterApp::timerEvent( TQTimerEvent * ) { alarm( 0 ); if (!PingServer( tqt_xdisplay() )) ::exit( EX_RESERVER_DPY ); alarm( pingInterval * 70 ); // sic! give the "proper" pinger enough time } bool GreeterApp::x11EventFilter( XEvent * ev ) { KeySym sym; switch (ev->type) { case FocusIn: case FocusOut: // Hack to tell dialogs to take focus when the keyboard is grabbed ev->xfocus.mode = NotifyNormal; break; case KeyPress: sym = XLookupKeysym( &ev->xkey, 0 ); if (sym != XK_Return && !IsModifierKey( sym )) emit activity(); break; case ButtonPress: emit activity(); /* fall through */ case ButtonRelease: // Hack to let the RMB work as LMB if (ev->xbutton.button == 3) ev->xbutton.button = 1; /* fall through */ case MotionNotify: if (ev->xbutton.state & Button3Mask) ev->xbutton.state = (ev->xbutton.state & ~Button3Mask) | Button1Mask; break; } return false; } extern bool kde_have_kipc; extern "C" { static int xIOErr( Display * ) { exit( EX_RESERVER_DPY ); } //KSimpleConfig *iccconfig; void checkSAK(GreeterApp* app) { app->restoreOverrideCursor(); SAKDlg sak(0); sak.exec(); app->setOverrideCursor( Qt::WaitCursor ); } void kg_main( const char *argv0 ) { static char *argv[] = { (char *)"tdmgreet", 0 }; KCmdLineArgs::init( 1, argv, *argv, 0, 0, 0, true ); kdDebug() << timestamp() << "start" << endl; kde_have_kipc = false; KApplication::disableAutoDcopRegistration(); KCrash::setSafer( true ); KProcess *tsak = 0; KProcess *proc = 0; KProcess *comp = 0; KProcess *dcop = 0; KProcess *twin = 0; trinity_desktop_lock_use_sak = _useSAK; if (trinity_desktop_lock_use_sak) { tsak = new KProcess; *tsak << TQCString( argv0, strrchr( argv0, '/' ) - argv0 + 2 ) + "tsak"; tsak->start(KProcess::Block, KProcess::AllOutput); } else { remove(TSAK_FIFO_FILE); } if (tsak) { tsak->closeStdin(); tsak->closeStdout(); tsak->detach(); delete tsak; } #ifdef HAVE_XCOMPOSITE // Begin ARGB initialization XSetErrorHandler( ignoreXError ); argb_visual_available = false; char *display = 0; Display *dpyi = XOpenDisplay( display ); if ( !dpyi ) { kdError() << "cannot connect to X server " << display << endl; exit( 1 ); } int screen = DefaultScreen( dpyi ); Colormap colormap = 0; Visual *visual = 0; int event_base, error_base; if ( XRenderQueryExtension( dpyi, &event_base, &error_base ) ) { int nvi; XVisualInfo templ; templ.screen = screen; templ.depth = 32; templ.c_class = TrueColor; XVisualInfo *xvi = XGetVisualInfo( dpyi, VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi ); for ( int i = 0; i < nvi; i++ ) { XRenderPictFormat *format = XRenderFindVisualFormat( dpyi, xvi[i].visual ); if ( format->type == PictTypeDirect && format->direct.alphaMask ) { visual = xvi[i].visual; colormap = XCreateColormap( dpyi, RootWindow( dpyi, screen ), visual, AllocNone ); kdDebug() << "found visual with alpha support" << endl; argb_visual_available = true; break; } } } XSetErrorHandler( (XErrorHandler)0 ); GreeterApp *app; if ( (argb_visual_available == true) && (!_compositor.isEmpty()) ) { app = new GreeterApp(dpyi, Qt::HANDLE( visual ), Qt::HANDLE( colormap )); } else { app = new GreeterApp(); } // End ARGB initialization #else GreeterApp *app = new GreeterApp(); #endif XSetIOErrorHandler( xIOErr ); TQString login_user; TQString login_session_wm; Display *dpy = tqt_xdisplay(); if (!_GUIStyle.isEmpty()) app->setStyle( _GUIStyle ); // Load up systemwide display settings #ifdef WITH_XRANDR KRandrSimpleAPI *randrsimple = new KRandrSimpleAPI(); TQPoint primaryScreenPosition = randrsimple->applySystemwideDisplayConfiguration("", KDE_CONFDIR); delete randrsimple; #endif // Load up the systemwide ICC profile TQString iccConfigFile = TQString(KDE_CONFDIR); iccConfigFile += "/kicc/kiccconfigrc"; KSimpleConfig iccconfig(iccConfigFile, true); if (iccconfig.readBoolEntry("EnableICC", false) == true) { TQString iccCommand = TQString("/usr/bin/xcalib "); iccCommand += iccconfig.readEntry("ICCFile"); iccCommand += TQString(" &"); system(iccCommand.ascii()); } _colorScheme = locate( "data", "kdisplay/color-schemes/" + _colorScheme + ".kcsrc" ); if (!_colorScheme.isEmpty()) { KSimpleConfig config( _colorScheme, true ); config.setGroup( "Color Scheme" ); app->setPalette( app->createApplicationPalette( &config, 7 ) ); } app->setFont( _normalFont ); setup_modifiers( dpy, _numLockStatus ); SecureDisplay( dpy ); if (!_grabServer) { if (_useBackground) { proc = new KProcess; *proc << TQCString( argv0, strrchr( argv0, '/' ) - argv0 + 2 ) + "krootimage"; *proc << _backgroundCfg; proc->start(); } GSendInt( G_SetupDpy ); GRecvInt(); } if (!_compositor.isEmpty()) { comp = new KProcess; *comp << TQCString( argv0, strrchr( argv0, '/' ) - argv0 + 2 ) + _compositor.ascii(); comp->start(KProcess::NotifyOnExit, KProcess::Stdin); } if (!_windowManager.isEmpty()) { if (_windowManager == "twin") { // Special case // Start DCOP... dcop = new KProcess; *dcop << TQCString( argv0, strrchr( argv0, '/' ) - argv0 + 2 ) + "dcopserver" << TQCString("--suicide"); dcop->start(); } twin = new KProcess; *twin << TQCString( argv0, strrchr( argv0, '/' ) - argv0 + 2 ) + _windowManager.ascii(); twin->start(); has_twin = true; } GSendInt( G_Ready ); kdDebug() << timestamp() << " main1" << endl; setCursor( dpy, app->desktop()->winId(), XC_left_ptr ); for (;;) { int rslt, cmd = GRecvInt(); if (cmd == G_ConfShutdown) { int how = GRecvInt(), uid = GRecvInt(); char *os = GRecvStr(); TDMSlimShutdown::externShutdown( how, os, uid ); if (os) free( os ); GSendInt( G_Ready ); _autoLoginDelay = 0; continue; } if (cmd == G_ErrorGreet) { if (KGVerify::handleFailVerify( TQT_TQWIDGET(tqApp->desktop()->screen( _greeterScreen )) )) break; _autoLoginDelay = 0; cmd = G_Greet; } KProcess *proc2 = 0; app->setOverrideCursor( Qt::WaitCursor ); FDialog *dialog = NULL; #ifdef XDMCP if (cmd == G_Choose) { dialog = new ChooserDlg; GSendInt( G_Ready ); /* tell chooser to go into async mode */ GRecvInt(); /* ack */ } else #endif { if ((cmd != G_GreetTimed && !_autoLoginAgain) || _autoLoginUser.isEmpty()) _autoLoginDelay = 0; if (_useTheme && !_theme.isEmpty()) { // Qt4 has a nasty habit of generating BadWindow errors in normal operation, so we simply ignore them // This also prevents the user from being dropped to a console login if Xorg glitches or is buggy XSetErrorHandler( ignoreXError ); KThemedGreeter *tgrt; bool has_twin_bkp = has_twin; is_themed = true; has_twin = false; // [FIXME] The themed greeter is built on the assumption that there is no window manager available (i.e. it keeps stealing focus) and needs to be repaired. dialog = tgrt = new KThemedGreeter; kdDebug() << timestamp() << " themed" << endl; if (!tgrt->isOK()) { is_themed = false; has_twin = has_twin_bkp; delete tgrt; checkSAK(app); dialog = new KStdGreeter; #ifdef WITH_XRANDR dialog->move(dialog->x() + primaryScreenPosition.x(), dialog->y() + primaryScreenPosition.y()); #endif } else { #ifdef WITH_XRANDR dialog->move(primaryScreenPosition.x(), primaryScreenPosition.y()); #endif } XSetErrorHandler( (XErrorHandler)0 ); } else { checkSAK(app); dialog = new KStdGreeter; #ifdef WITH_XRANDR dialog->move(dialog->x() + primaryScreenPosition.x(), dialog->y() + primaryScreenPosition.y()); #endif } TQPoint oldCursorPos = TQCursor::pos(); #ifdef WITH_XRANDR TQCursor::setPos(oldCursorPos.x() + primaryScreenPosition.x(), oldCursorPos.y() + primaryScreenPosition.y()); #endif if (*_preloader) { proc2 = new KProcess; *proc2 << _preloader; proc2->start(); } } app->restoreOverrideCursor(); Debug( "entering event loop\n" ); // Qt4 has a nasty habit of generating BadWindow errors in normal operation, so we simply ignore them // This also prevents the user from being dropped to a console login if Xorg glitches or is buggy XSetErrorHandler( ignoreXError ); rslt = dialog->exec(); XSetErrorHandler( (XErrorHandler)0 ); Debug( "left event loop\n" ); login_user = static_cast<KGreeter*>(dialog)->curUser; login_session_wm = static_cast<KGreeter*>(dialog)->curWMSession; if (rslt != ex_greet) { delete dialog; } delete proc2; #ifdef XDMCP switch (rslt) { case ex_greet: GSendInt( G_DGreet ); continue; case ex_choose: GSendInt( G_DChoose ); continue; default: break; } #endif break; } KGVerify::done(); if (comp) { if (_compositor == "kompmgr") { // Change process UID // Get user UID passwd* userinfo = getpwnam(login_user.ascii()); if (userinfo) { TQString newuid = TQString("%1").arg(userinfo->pw_uid); // kompmgr allows us to change its uid in this manner: // 1.) Send SIGUSER1 // 2.) Send the new UID to it on the command line comp->kill(SIGUSR1); comp->writeStdin(newuid.ascii(), newuid.length()); usleep(50000); // Give the above function some time to execute. Note that on REALLY slow systems this could fail, leaving kompmgr running as root. TODO: Look into ways to make this more robust. } } comp->closeStdin(); comp->detach(); delete comp; } if (twin) { if (login_session_wm.endsWith("/starttde") || (login_session_wm == "failsafe")) { twin->closeStdin(); twin->detach(); dcop->detach(); } else { twin->kill(); dcop->kill(); } delete twin; delete dcop; } delete proc; UnsecureDisplay( dpy ); restore_modifiers(); XSetInputFocus( tqt_xdisplay(), PointerRoot, PointerRoot, CurrentTime ); delete app; } } // extern "C" #include "kgapp.moc"