not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / ksmserver / legacy.cpp
blobd7e61cc2161286fda63dda381ad11bd7dced3bdb
1 /*****************************************************************
2 ksmserver - the KDE session management server
4 Copyright 2000 Matthias Ettrich <ettrich@kde.org>
5 Copyright 2005 Lubos Lunak <l.lunak@kde.org>
7 relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
9 some code taken from the dcopserver (part of the KDE libraries), which is
10 Copyright 1999 Matthias Ettrich <ettrich@kde.org>
11 Copyright 1999 Preston Brown <pbrown@kde.org>
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
20 The above copyright notice and this permission notice shall be included in
21 all copies or substantial portions of the Software.
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
27 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 ******************************************************************/
32 #include <config-workspace.h>
34 #ifdef HAVE_SYS_TIME_H
35 #include <sys/time.h>
36 #endif
38 #include "server.h"
40 #include <unistd.h>
43 #include <kconfig.h>
44 #include <kconfiggroup.h>
45 #include <kshell.h>
46 #include <kdebug.h>
47 #include <kwindowsystem.h>
49 #include <X11/Xlib.h>
50 #include <X11/Xutil.h>
51 #include <X11/Xatom.h>
54 * Legacy session management
57 #ifndef NO_LEGACY_SESSION_MANAGEMENT
58 const int WM_SAVE_YOURSELF_TIMEOUT = 4000;
60 static WindowMap* windowMapPtr = 0;
62 static Atom wm_save_yourself = XNone;
63 static Atom wm_protocols = XNone;
64 static Atom wm_client_leader = XNone;
65 static Atom sm_client_id = XNone;
67 static int winsErrorHandler(Display *, XErrorEvent *ev)
69 if (windowMapPtr) {
70 WindowMap::Iterator it = windowMapPtr->find(ev->resourceid);
71 if (it != windowMapPtr->end())
72 (*it).type = SM_ERROR;
74 return 0;
77 void KSMServer::performLegacySessionSave()
79 kDebug( 1218 ) << "Saving legacy session apps";
80 // Setup error handler
81 legacyWindows.clear();
82 windowMapPtr = &legacyWindows;
83 XErrorHandler oldHandler = XSetErrorHandler(winsErrorHandler);
84 // Compute set of leader windows that need legacy session management
85 // and determine which style (WM_COMMAND or WM_SAVE_YOURSELF)
86 if( wm_save_yourself == (Atom)XNone ) {
87 Atom atoms[ 4 ];
88 const char* const names[]
89 = { "WM_SAVE_YOURSELF", "WM_PROTOCOLS", "WM_CLIENT_LEADER", "SM_CLIENT_ID" };
90 XInternAtoms( QX11Info::display(), const_cast< char** >( names ), 4,
91 False, atoms );
92 wm_save_yourself = atoms[ 0 ];
93 wm_protocols = atoms[ 1 ];
94 wm_client_leader = atoms[ 2 ];
95 sm_client_id = atoms[ 3 ];
97 for ( QList<WId>::ConstIterator it = KWindowSystem::windows().begin();
98 it != KWindowSystem::windows().end(); ++it) {
99 WId leader = windowWmClientLeader( *it );
100 if (!legacyWindows.contains(leader) && windowSessionId( *it, leader ).isEmpty()) {
101 SMType wtype = SM_WMCOMMAND;
102 int nprotocols = 0;
103 Atom *protocols = 0;
104 if( XGetWMProtocols(QX11Info::display(), leader, &protocols, &nprotocols)) {
105 for (int i=0; i<nprotocols; i++)
106 if (protocols[i] == wm_save_yourself) {
107 wtype = SM_WMSAVEYOURSELF;
108 break;
110 XFree((void*) protocols);
112 SMData data;
113 data.type = wtype;
114 XClassHint classHint;
115 if( XGetClassHint( QX11Info::display(), leader, &classHint ) ) {
116 data.wmclass1 = classHint.res_name;
117 data.wmclass2 = classHint.res_class;
118 XFree( classHint.res_name );
119 XFree( classHint.res_class );
121 legacyWindows.insert(leader, data);
124 // Open fresh display for sending WM_SAVE_YOURSELF
125 XSync(QX11Info::display(), False);
126 Display *newdisplay = XOpenDisplay(DisplayString(QX11Info::display()));
127 if (!newdisplay) {
128 windowMapPtr = NULL;
129 XSetErrorHandler(oldHandler);
130 return;
132 WId root = DefaultRootWindow(newdisplay);
133 XGrabKeyboard(newdisplay, root, False,
134 GrabModeAsync, GrabModeAsync, CurrentTime);
135 XGrabPointer(newdisplay, root, False, Button1Mask|Button2Mask|Button3Mask,
136 GrabModeAsync, GrabModeAsync, XNone, XNone, CurrentTime);
137 // Send WM_SAVE_YOURSELF messages
138 XEvent ev;
139 int awaiting_replies = 0;
140 for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) {
141 if ( (*it).type == SM_WMSAVEYOURSELF ) {
142 WId w = it.key();
143 awaiting_replies += 1;
144 memset(&ev, 0, sizeof(ev));
145 ev.xclient.type = ClientMessage;
146 ev.xclient.window = w;
147 ev.xclient.message_type = wm_protocols;
148 ev.xclient.format = 32;
149 ev.xclient.data.l[0] = wm_save_yourself;
150 ev.xclient.data.l[1] = QX11Info::appTime();
151 XSelectInput(newdisplay, w, PropertyChangeMask|StructureNotifyMask);
152 XSendEvent(newdisplay, w, False, 0, &ev);
155 // Wait for change in WM_COMMAND with timeout
156 XFlush(newdisplay);
157 QTime start = QTime::currentTime();
158 while (awaiting_replies > 0) {
159 if (XPending(newdisplay)) {
160 /* Process pending event */
161 XNextEvent(newdisplay, &ev);
162 if ( ( ev.xany.type == UnmapNotify ) ||
163 ( ev.xany.type == PropertyNotify && ev.xproperty.atom == XA_WM_COMMAND ) ) {
164 WindowMap::Iterator it = legacyWindows.find( ev.xany.window );
165 if ( it != legacyWindows.end() && (*it).type != SM_WMCOMMAND ) {
166 awaiting_replies -= 1;
167 if ( (*it).type != SM_ERROR )
168 (*it).type = SM_WMCOMMAND;
171 } else {
172 /* Check timeout */
173 int msecs = start.elapsed();
174 if (msecs >= WM_SAVE_YOURSELF_TIMEOUT)
175 break;
176 /* Wait for more events */
177 fd_set fds;
178 FD_ZERO(&fds);
179 int fd = ConnectionNumber(newdisplay);
180 FD_SET(fd, &fds);
181 struct timeval tmwait;
182 tmwait.tv_sec = (WM_SAVE_YOURSELF_TIMEOUT - msecs) / 1000;
183 tmwait.tv_usec = ((WM_SAVE_YOURSELF_TIMEOUT - msecs) % 1000) * 1000;
184 ::select(fd+1, &fds, NULL, &fds, &tmwait);
187 // Terminate work in new display
188 XAllowEvents(newdisplay, ReplayPointer, CurrentTime);
189 XAllowEvents(newdisplay, ReplayKeyboard, CurrentTime);
190 XSync(newdisplay, False);
191 XCloseDisplay(newdisplay);
192 // Restore old error handler
193 XSync(QX11Info::display(), False);
194 XSetErrorHandler(oldHandler);
195 for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) {
196 if ( (*it).type != SM_ERROR) {
197 WId w = it.key();
198 (*it).wmCommand = windowWmCommand(w);
199 (*it).wmClientMachine = windowWmClientMachine(w);
202 kDebug( 1218 ) << "Done saving " << legacyWindows.count() << " legacy session apps";
206 Stores legacy session management data
208 void KSMServer::storeLegacySession( KConfig* config )
210 // Write LegacySession data
211 config->deleteGroup( "Legacy" + sessionGroup );
212 KConfigGroup group( config, "Legacy" + sessionGroup );
213 int count = 0;
214 for (WindowMap::ConstIterator it = legacyWindows.constBegin(); it != legacyWindows.constEnd(); ++it) {
215 if ( (*it).type != SM_ERROR) {
216 if( excludeApps.contains( (*it).wmclass1.toLower())
217 || excludeApps.contains( (*it).wmclass2.toLower()))
218 continue;
219 if ( !(*it).wmCommand.isEmpty() && !(*it).wmClientMachine.isEmpty() ) {
220 count++;
221 QString n = QString::number(count);
222 group.writeEntry( QString("command")+n, (*it).wmCommand );
223 group.writeEntry( QString("clientMachine")+n, (*it).wmClientMachine );
227 group.writeEntry( "count", count );
231 Restores legacy session management data (i.e. restart applications)
233 void KSMServer::restoreLegacySession( KConfig* config )
235 if( config->hasGroup( "Legacy" + sessionGroup )) {
236 KConfigGroup group( config, "Legacy" + sessionGroup );
237 restoreLegacySessionInternal( &group );
238 } else if( wm == "kwin" ) { // backwards comp. - get it from kwinrc
239 KConfigGroup group( config, sessionGroup );
240 int count = group.readEntry( "count", 0 );
241 for ( int i = 1; i <= count; i++ ) {
242 QString n = QString::number(i);
243 if ( group.readEntry( QString("program")+n, QString() ) != wm )
244 continue;
245 QStringList restartCommand =
246 group.readEntry( QString("restartCommand")+n, QStringList() );
247 for( QStringList::ConstIterator it = restartCommand.constBegin();
248 it != restartCommand.constEnd();
249 ++it ) {
250 if( (*it) == "-session" ) {
251 ++it;
252 if( it != restartCommand.constEnd()) {
253 KConfig cfg( "session/" + wm + '_' + (*it) );
254 KConfigGroup group(&cfg, "LegacySession");
255 restoreLegacySessionInternal( &group, ' ' );
263 void KSMServer::restoreLegacySessionInternal( KConfigGroup* config, char sep )
265 int count = config->readEntry( "count",0 );
266 for ( int i = 1; i <= count; i++ ) {
267 QString n = QString::number(i);
268 QStringList wmCommand = (sep == ',') ?
269 config->readEntry( QString("command")+n, QStringList() ) :
270 KShell::splitArgs( config->readEntry( QString("command")+n, QString() ) ); // close enough(?)
271 if( wmCommand.isEmpty())
272 continue;
273 if( isWM( wmCommand.first()))
274 continue;
275 startApplication( wmCommand,
276 config->readEntry( QString("clientMachine")+n, QString() ),
277 config->readEntry( QString("userId")+n, QString() ));
281 static QByteArray getQCStringProperty(WId w, Atom prop)
283 Atom type;
284 int format, status;
285 unsigned long nitems = 0;
286 unsigned long extra = 0;
287 unsigned char *data = 0;
288 QByteArray result = "";
289 status = XGetWindowProperty( QX11Info::display(), w, prop, 0, 10000,
290 false, XA_STRING, &type, &format,
291 &nitems, &extra, &data );
292 if ( status == Success) {
293 if( data )
294 result = (char*)data;
295 XFree(data);
297 return result;
300 static QStringList getQStringListProperty(WId w, Atom prop)
302 Atom type;
303 int format, status;
304 unsigned long nitems = 0;
305 unsigned long extra = 0;
306 unsigned char *data = 0;
307 QStringList result;
309 status = XGetWindowProperty( QX11Info::display(), w, prop, 0, 10000,
310 false, XA_STRING, &type, &format,
311 &nitems, &extra, &data );
312 if ( status == Success) {
313 if (!data)
314 return result;
315 for (int i=0; i<(int)nitems; i++) {
316 result << QLatin1String( (const char*)data + i );
317 while(data[i]) i++;
319 XFree(data);
321 return result;
324 QStringList KSMServer::windowWmCommand(WId w)
326 QStringList ret = getQStringListProperty(w, XA_WM_COMMAND);
327 // hacks here
328 if( ret.count() == 1 ) {
329 QString command = ret.first();
330 // Mozilla is launched using wrapper scripts, so it's launched using "mozilla",
331 // but the actual binary is "mozilla-bin" or "<path>/mozilla-bin", and that's what
332 // will be also in WM_COMMAND - using this "mozilla-bin" doesn't work at all though
333 if( command.endsWith( "mozilla-bin" ))
334 return QStringList() << "mozilla";
335 if( command.endsWith( "firefox-bin" ))
336 return QStringList() << "firefox";
337 if( command.endsWith( "thunderbird-bin" ))
338 return QStringList() << "thunderbird";
339 if( command.endsWith( "sunbird-bin" ))
340 return QStringList() << "sunbird";
342 return ret;
345 QString KSMServer::windowWmClientMachine(WId w)
347 QByteArray result = getQCStringProperty(w, XA_WM_CLIENT_MACHINE);
348 if (result.isEmpty()) {
349 result = "localhost";
350 } else {
351 // special name for the local machine (localhost)
352 char hostnamebuf[80];
353 if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) {
354 hostnamebuf[sizeof(hostnamebuf)-1] = 0;
355 if (result == hostnamebuf)
356 result = "localhost";
357 if(char *dot = strchr(hostnamebuf, '.')) {
358 *dot = '\0';
359 if(result == hostnamebuf)
360 result = "localhost";
364 return QLatin1String(result);
367 WId KSMServer::windowWmClientLeader(WId w)
369 Atom type;
370 int format, status;
371 unsigned long nitems = 0;
372 unsigned long extra = 0;
373 unsigned char *data = 0;
374 Window result = w;
375 status = XGetWindowProperty( QX11Info::display(), w, wm_client_leader, 0, 10000,
376 false, XA_WINDOW, &type, &format,
377 &nitems, &extra, &data );
378 if (status == Success ) {
379 if (data && nitems > 0)
380 result = *((Window*) data);
381 XFree(data);
383 return result;
388 Returns sessionId for this client,
389 taken either from its window or from the leader window.
391 QByteArray KSMServer::windowSessionId(WId w, WId leader)
393 QByteArray result = getQCStringProperty(w, sm_client_id);
394 if (result.isEmpty() && leader != (WId)None && leader != w)
395 result = getQCStringProperty(leader, sm_client_id);
396 return result;
398 #endif