diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 4aed2c8219774f5d797760606b8489a92ddc5163 (patch) | |
tree | 3f8c130f7d269626bf6a9447407ef6c35954426a /ksmserver/legacy.cpp | |
download | tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'ksmserver/legacy.cpp')
-rw-r--r-- | ksmserver/legacy.cpp | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/ksmserver/legacy.cpp b/ksmserver/legacy.cpp new file mode 100644 index 000000000..ca198a212 --- /dev/null +++ b/ksmserver/legacy.cpp @@ -0,0 +1,402 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org> + +relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de> + +some code taken from the dcopserver (part of the KDE libraries), which is +Copyright (c) 1999 Matthias Ettrich <ettrich@kde.org> +Copyright (c) 1999 Preston Brown <pbrown@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#include "server.h" + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#include <unistd.h> + +#include <qtimer.h> + +#include <kconfig.h> +#include <kdebug.h> +#include <kwinmodule.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + + +/* + * Legacy session management + */ +const int WM_SAVE_YOURSELF_TIMEOUT = 4000; + +static WindowMap* windowMapPtr = 0; + +static Atom wm_save_yourself = None; +static Atom wm_protocols = None; +static Atom wm_client_leader = None; + +extern Time qt_x_time; + +static int winsErrorHandler(Display *, XErrorEvent *ev) +{ + if (windowMapPtr) { + WindowMap::Iterator it = windowMapPtr->find(ev->resourceid); + if (it != windowMapPtr->end()) + (*it).type = SM_ERROR; + } + return 0; +} + +void KSMServer::performLegacySessionSave() +{ + kdDebug( 1218 ) << "Saving legacy session apps" << endl; + // Setup error handler + legacyWindows.clear(); + windowMapPtr = &legacyWindows; + XErrorHandler oldHandler = XSetErrorHandler(winsErrorHandler); + // Compute set of leader windows that need legacy session management + // and determine which style (WM_COMMAND or WM_SAVE_YOURSELF) + KWinModule module; + if( wm_save_yourself == (Atom)None ) { + Atom atoms[ 3 ]; + const char* const names[] + = { "WM_SAVE_YOURSELF", "WM_PROTOCOLS", "WM_CLIENT_LEADER" }; + XInternAtoms( qt_xdisplay(), const_cast< char** >( names ), 3, + False, atoms ); + wm_save_yourself = atoms[ 0 ]; + wm_protocols = atoms[ 1 ]; + wm_client_leader = atoms[ 2 ]; + } + for ( QValueList<WId>::ConstIterator it = module.windows().begin(); + it != module.windows().end(); ++it) { + WId leader = windowWmClientLeader( *it ); + if (!legacyWindows.contains(leader) && windowSessionId( *it, leader ).isEmpty()) { + SMType wtype = SM_WMCOMMAND; + int nprotocols = 0; + Atom *protocols = 0; + if( XGetWMProtocols(qt_xdisplay(), leader, &protocols, &nprotocols)) { + for (int i=0; i<nprotocols; i++) + if (protocols[i] == wm_save_yourself) { + wtype = SM_WMSAVEYOURSELF; + break; + } + XFree((void*) protocols); + } + SMData data; + data.type = wtype; + XClassHint classHint; + if( XGetClassHint( qt_xdisplay(), leader, &classHint ) ) { + data.wmclass1 = classHint.res_name; + data.wmclass2 = classHint.res_class; + XFree( classHint.res_name ); + XFree( classHint.res_class ); + } + legacyWindows.insert(leader, data); + } + } + // Open fresh display for sending WM_SAVE_YOURSELF + XSync(qt_xdisplay(), False); + Display *newdisplay = XOpenDisplay(DisplayString(qt_xdisplay())); + if (!newdisplay) { + windowMapPtr = NULL; + XSetErrorHandler(oldHandler); + return; + } + WId root = DefaultRootWindow(newdisplay); + XGrabKeyboard(newdisplay, root, False, + GrabModeAsync, GrabModeAsync, CurrentTime); + XGrabPointer(newdisplay, root, False, Button1Mask|Button2Mask|Button3Mask, + GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + // Send WM_SAVE_YOURSELF messages + XEvent ev; + int awaiting_replies = 0; + for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) { + if ( (*it).type == SM_WMSAVEYOURSELF ) { + WId w = it.key(); + awaiting_replies += 1; + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = w; + ev.xclient.message_type = wm_protocols; + ev.xclient.format = 32; + ev.xclient.data.l[0] = wm_save_yourself; + ev.xclient.data.l[1] = qt_x_time; + XSelectInput(newdisplay, w, PropertyChangeMask|StructureNotifyMask); + XSendEvent(newdisplay, w, False, 0, &ev); + } + } + // Wait for change in WM_COMMAND with timeout + XFlush(newdisplay); + QTime start = QTime::currentTime(); + while (awaiting_replies > 0) { + if (XPending(newdisplay)) { + /* Process pending event */ + XNextEvent(newdisplay, &ev); + if ( ( ev.xany.type == UnmapNotify ) || + ( ev.xany.type == PropertyNotify && ev.xproperty.atom == XA_WM_COMMAND ) ) { + WindowMap::Iterator it = legacyWindows.find( ev.xany.window ); + if ( it != legacyWindows.end() && (*it).type != SM_WMCOMMAND ) { + awaiting_replies -= 1; + if ( (*it).type != SM_ERROR ) + (*it).type = SM_WMCOMMAND; + } + } + } else { + /* Check timeout */ + int msecs = start.elapsed(); + if (msecs >= WM_SAVE_YOURSELF_TIMEOUT) + break; + /* Wait for more events */ + fd_set fds; + FD_ZERO(&fds); + int fd = ConnectionNumber(newdisplay); + FD_SET(fd, &fds); + struct timeval tmwait; + tmwait.tv_sec = (WM_SAVE_YOURSELF_TIMEOUT - msecs) / 1000; + tmwait.tv_usec = ((WM_SAVE_YOURSELF_TIMEOUT - msecs) % 1000) * 1000; + ::select(fd+1, &fds, NULL, &fds, &tmwait); + } + } + // Terminate work in new display + XAllowEvents(newdisplay, ReplayPointer, CurrentTime); + XAllowEvents(newdisplay, ReplayKeyboard, CurrentTime); + XSync(newdisplay, False); + XCloseDisplay(newdisplay); + // Restore old error handler + XSync(qt_xdisplay(), False); + XSetErrorHandler(oldHandler); + for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) { + if ( (*it).type != SM_ERROR) { + WId w = it.key(); + (*it).wmCommand = windowWmCommand(w); + (*it).wmClientMachine = windowWmClientMachine(w); + } + } + kdDebug( 1218 ) << "Done saving " << legacyWindows.count() << " legacy session apps" << endl; +} + +/*! + Stores legacy session management data +*/ +void KSMServer::storeLegacySession( KConfig* config ) +{ + // Write LegacySession data + config->deleteGroup( "Legacy" + sessionGroup ); + KConfigGroupSaver saver( config, "Legacy" + sessionGroup ); + int count = 0; + for (WindowMap::ConstIterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) { + if ( (*it).type != SM_ERROR) { + if( excludeApps.contains( (*it).wmclass1.lower()) + || excludeApps.contains( (*it).wmclass2.lower())) + continue; + if ( !(*it).wmCommand.isEmpty() && !(*it).wmClientMachine.isEmpty() ) { + count++; + QString n = QString::number(count); + config->writeEntry( QString("command")+n, (*it).wmCommand ); + config->writeEntry( QString("clientMachine")+n, (*it).wmClientMachine ); + } + } + } + config->writeEntry( "count", count ); +} + +/*! + Restores legacy session management data (i.e. restart applications) +*/ +void KSMServer::restoreLegacySession( KConfig* config ) +{ + if( config->hasGroup( "Legacy" + sessionGroup )) { + KConfigGroupSaver saver( config, "Legacy" + sessionGroup ); + restoreLegacySessionInternal( config ); + } else if( wm == "kwin" ) { // backwards comp. - get it from kwinrc + KConfigGroupSaver saver( config, sessionGroup ); + int count = config->readNumEntry( "count", 0 ); + for ( int i = 1; i <= count; i++ ) { + QString n = QString::number(i); + if ( config->readEntry( QString("program")+n ) != wm ) + continue; + QStringList restartCommand = + config->readListEntry( QString("restartCommand")+n ); + for( QStringList::ConstIterator it = restartCommand.begin(); + it != restartCommand.end(); + ++it ) { + if( (*it) == "-session" ) { + ++it; + if( it != restartCommand.end()) { + KConfig cfg( "session/" + wm + "_" + (*it), true ); + cfg.setGroup( "LegacySession" ); + restoreLegacySessionInternal( &cfg, ' ' ); + } + } + } + } + } +} + +void KSMServer::restoreLegacySessionInternal( KConfig* config, char sep ) +{ + int count = config->readNumEntry( "count" ); + for ( int i = 1; i <= count; i++ ) { + QString n = QString::number(i); + QStringList wmCommand = config->readListEntry( QString("command")+n, sep ); + if( wmCommand.isEmpty()) + continue; + if( isWM( wmCommand.first())) + continue; + startApplication( wmCommand, + config->readEntry( QString("clientMachine")+n ), + config->readEntry( QString("userId")+n )); + } +} + +static QCString getQCStringProperty(WId w, Atom prop) +{ + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + QCString result = ""; + status = XGetWindowProperty( qt_xdisplay(), w, prop, 0, 10000, + FALSE, XA_STRING, &type, &format, + &nitems, &extra, &data ); + if ( status == Success) { + if( data ) + result = (char*)data; + XFree(data); + } + return result; +} + +static QStringList getQStringListProperty(WId w, Atom prop) +{ + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + QStringList result; + + status = XGetWindowProperty( qt_xdisplay(), w, prop, 0, 10000, + FALSE, XA_STRING, &type, &format, + &nitems, &extra, &data ); + if ( status == Success) { + if (!data) + return result; + for (int i=0; i<(int)nitems; i++) { + result << QString::fromLatin1( (const char*)data + i ); + while(data[i]) i++; + } + XFree(data); + } + return result; +} + +QStringList KSMServer::windowWmCommand(WId w) +{ + QStringList ret = getQStringListProperty(w, XA_WM_COMMAND); + // hacks here + if( ret.count() == 1 ) { + QString command = ret.first(); + // Mozilla is launched using wrapper scripts, so it's launched using "mozilla", + // but the actual binary is "mozilla-bin" or "<path>/mozilla-bin", and that's what + // will be also in WM_COMMAND - using this "mozilla-bin" doesn't work at all though + if( command.endsWith( "mozilla-bin" )) + return QStringList() << "mozilla"; + if( command.endsWith( "firefox-bin" )) + return QStringList() << "firefox"; + if( command.endsWith( "thunderbird-bin" )) + return QStringList() << "thunderbird"; + if( command.endsWith( "sunbird-bin" )) + return QStringList() << "sunbird"; + } + return ret; +} + +QString KSMServer::windowWmClientMachine(WId w) +{ + QCString result = getQCStringProperty(w, XA_WM_CLIENT_MACHINE); + if (result.isEmpty()) { + result = "localhost"; + } else { + // special name for the local machine (localhost) + char hostnamebuf[80]; + if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) { + hostnamebuf[sizeof(hostnamebuf)-1] = 0; + if (result == hostnamebuf) + result = "localhost"; + if(char *dot = strchr(hostnamebuf, '.')) { + *dot = '\0'; + if(result == hostnamebuf) + result = "localhost"; + } + } + } + return QString::fromLatin1(result); +} + +WId KSMServer::windowWmClientLeader(WId w) +{ + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + Window result = w; + status = XGetWindowProperty( qt_xdisplay(), w, wm_client_leader, 0, 10000, + FALSE, XA_WINDOW, &type, &format, + &nitems, &extra, &data ); + if (status == Success ) { + if (data && nitems > 0) + result = *((Window*) data); + XFree(data); + } + return result; +} + + +/* + Returns sessionId for this client, + taken either from its window or from the leader window. + */ +extern Atom qt_sm_client_id; +QCString KSMServer::windowSessionId(WId w, WId leader) +{ + QCString result = getQCStringProperty(w, qt_sm_client_id); + if (result.isEmpty() && leader != (WId)None && leader != w) + result = getQCStringProperty(leader, qt_sm_client_id); + return result; +} |