1
/* $Id: xfsm-legacy.c 4751 2004-12-27 17:25:51Z benny $ */
3
* Copyright (c) 2004 Benedikt Meurer <benny@xfce.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2, or (at your option)
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22
* Most of the code in this file is borred from ksmserver, the KDE session
24
* Copyright (C) 2000 Matthias Ettrich <ettrich@kde.org>
26
* Permission is hereby granted, free of charge, to any person obtaining a copy
27
* of this software and associated documentation files (the "Software"), to deal
28
* in the Software without restriction, including without limitation the rights
29
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30
* copies of the Software, and to permit persons to whom the Software is
31
* furnished to do so, subject to the following conditions:
33
* The above copyright notice and this permission notice shall be included in
34
* all copies or substantial portions of the Software.
36
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39
* AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
40
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
41
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43
/* After all, this code is mostly a hack now, one should cleanup the
51
#ifdef HAVE_SYS_TYPES_H
52
#include <sys/types.h>
54
#ifdef HAVE_SYS_TIME_H
68
#include <X11/Xatom.h>
70
#include <X11/Xutil.h>
74
#include <libxfcegui4/libxfcegui4.h>
76
#include <libxfsm/xfsm-util.h>
79
#ifdef LEGACY_SESSION_MANAGEMENT
81
#define WM_SAVE_YOURSELF_TIMEOUT 4000
91
typedef struct _SmWindow SmWindow;
96
gchar *wm_client_machine;
102
#define SM_WINDOW(window) ((SmWindow *) (window))
109
#define SM_RESTART_APP(a) ((SmRestartApp *) (a))
112
static GList *restart_apps = NULL;
113
static GList *window_list = NULL;
117
static Atom _XA_WM_PROTOCOLS = None;
118
static Atom _XA_WM_SAVE_YOURSELF = None;
119
static Atom _XA_WM_CLIENT_LEADER = None;
120
static Atom _XA_SM_CLIENT_ID = None;
124
sm_window_new (Window wid, gint screen_num, gint type,
125
gchar *wm_class1, gchar *wm_class2)
129
smw = g_new0 (SmWindow, 1);
132
smw->wm_class1 = wm_class1;
133
smw->wm_class2 = wm_class2;
134
smw->screen_num = screen_num;
141
sm_window_free (SmWindow *window)
143
g_return_if_fail (window != NULL);
145
if (window->wm_command != NULL)
146
g_strfreev (window->wm_command);
147
if (window->wm_client_machine != NULL)
148
g_free (window->wm_client_machine);
149
if (window->wm_class1)
150
g_free (window->wm_class1);
151
if (window->wm_class2)
152
g_free (window->wm_class2);
158
sm_window_list_clear (void)
162
for (lp = window_list; lp != NULL; lp = lp->next)
163
sm_window_free (SM_WINDOW (lp->data));
165
g_list_free (window_list);
171
sm_window_list_contains (Window window)
175
for (lp = window_list; lp != NULL; lp = lp->next)
176
if (SM_WINDOW (lp->data)->wid == window)
184
wins_error_handler (Display *display, XErrorEvent *event)
187
for (lp = window_list; lp != NULL; lp = lp->next)
188
if (SM_WINDOW (lp->data)->wid == event->resourceid)
189
SM_WINDOW (lp->data)->type = SM_ERROR;
195
has_xsmp_support (Window window)
198
gboolean has_it = FALSE;
200
if (XGetTextProperty (gdk_display, window, &tp, _XA_SM_CLIENT_ID))
202
if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
205
if (tp.value != NULL)
206
XFree ((char *) tp.value);
214
get_wmcommand (Window window)
216
gchar **result = NULL;
221
gdk_error_trap_push ();
222
status = XGetCommand (gdk_display, window, &argv, &argc);
223
if (gdk_error_trap_pop ())
226
if (status && argv && argc > 0)
228
result = g_new0 (gchar *, argc + 1);
229
for (i = 0; i < argc; ++i)
230
result[i] = g_strdup (argv[i]);
231
XFreeStringList (argv);
239
get_wmclientmachine (Window window)
242
gchar *result = NULL;
245
gdk_error_trap_push ();
246
status = XGetWMClientMachine (gdk_display, window, &tp);
247
if (gdk_error_trap_pop ())
252
if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
253
result = g_strdup ((char *) tp.value);
255
if (tp.value != NULL)
256
XFree ((char *) tp.value);
260
result = g_strdup ("localhost");
266
get_clientleader (Window window)
270
unsigned long nitems = 0;
271
unsigned long extra = 0;
272
unsigned char *data = 0;
273
Window result = window;
275
status = XGetWindowProperty (gdk_display, window, _XA_WM_CLIENT_LEADER,
276
0, 10000, FALSE, XA_WINDOW, &type, &format,
277
&nitems, &extra, &data);
278
if (status == Success)
280
if (data != NULL && nitems > 0)
281
result = *((Window *) data);
292
xfsm_legacy_perform_session_save (void)
294
#ifdef LEGACY_SESSION_MANAGEMENT
295
XErrorHandler old_handler;
303
XClassHint class_hint;
311
int awaiting_replies = 0;
314
/* clear window list */
315
sm_window_list_clear ();
318
if (_XA_WM_SAVE_YOURSELF == None)
320
_XA_SM_CLIENT_ID = XInternAtom (gdk_display, "SM_CLIENT_ID", False);
321
_XA_WM_PROTOCOLS = XInternAtom (gdk_display, "WM_PROTOCOLS", False);
322
_XA_WM_SAVE_YOURSELF = XInternAtom (gdk_display, "WM_SAVE_YOURSELF",
324
_XA_WM_CLIENT_LEADER = XInternAtom (gdk_display, "WM_CLIENT_LEADER",
328
/* install custom X error handler */
329
old_handler = XSetErrorHandler (wins_error_handler);
331
/* query mapped windows on all screens */
332
for (n = 0; n < ScreenCount (gdk_display); ++n)
334
screen = netk_screen_get (n);
335
netk_screen_force_update (screen);
337
windows = netk_screen_get_windows (screen);
339
for (lp = windows; lp != NULL; lp = lp->next)
341
window = netk_window_get_xid (NETK_WINDOW (lp->data));
342
leader = get_clientleader (window);
343
if (leader == None || sm_window_list_contains (leader)
344
|| has_xsmp_support (window) || has_xsmp_support (leader))
354
if (XGetWMProtocols (gdk_display, leader, &protocols, &nprotocols))
356
for (i = 0; i < nprotocols; ++i)
357
if (protocols[i] == _XA_WM_SAVE_YOURSELF)
359
type = SM_WMSAVEYOURSELF;
362
XFree ((void *) protocols);
365
if (XGetClassHint (gdk_display, leader, &class_hint))
367
wmclass2 = g_strdup (class_hint.res_class);
368
wmclass1 = g_strdup (class_hint.res_name);
369
XFree (class_hint.res_class);
370
XFree (class_hint.res_name);
373
sm_window = sm_window_new (leader, n, type, wmclass1, wmclass2);
374
window_list = g_list_append (window_list, sm_window);
378
/* open fresh display for sending WM_SAVE_YOURSELF commands */
379
XSync (gdk_display, False);
380
display = XOpenDisplay (DisplayString (gdk_display));
383
XSetErrorHandler (old_handler);
387
/* grab keyboard/pointer (XXX - check pointer pos first?) */
388
root = DefaultRootWindow (display);
389
XGrabKeyboard (display, root, False, GrabModeAsync, GrabModeAsync,
391
XGrabPointer (display, root, False, Button1Mask | Button2Mask | Button3Mask,
392
GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
393
for (lp = window_list; lp != NULL; lp = lp->next)
395
sm_window = SM_WINDOW (lp->data);
396
if (sm_window->type == SM_WMSAVEYOURSELF)
400
bzero (&ev, sizeof (ev));
402
ev.xclient.type = ClientMessage;
403
ev.xclient.window = sm_window->wid;
404
ev.xclient.message_type = _XA_WM_PROTOCOLS;
405
ev.xclient.format = 32;
406
ev.xclient.data.l[0] = _XA_WM_SAVE_YOURSELF;
407
ev.xclient.data.l[1] = CurrentTime;
408
XSelectInput (display, sm_window->wid,
409
PropertyChangeMask | StructureNotifyMask);
410
XSendEvent (display, sm_window->wid, False, 0, &ev);
414
/* wait for change in WM_COMMAND with timeout */
416
timer = g_timer_new ();
417
while (awaiting_replies > 0)
419
if (XPending (display))
421
XNextEvent (display, &ev);
422
if (ev.type == UnmapNotify || (ev.type == PropertyNotify
423
&& ev.xproperty.atom == XA_WM_COMMAND))
425
for (lp = window_list; lp != NULL; lp = lp->next)
427
if (SM_WINDOW (lp->data)->wid == ev.xany.window
428
&& SM_WINDOW (lp->data)->type != SM_WMCOMMAND)
431
if (SM_WINDOW (lp->data)->type != SM_ERROR)
432
SM_WINDOW (lp->data)->type = SM_WMCOMMAND;
444
msecs = (int)(g_timer_elapsed (timer, NULL) * 1000);
445
if (msecs >= WM_SAVE_YOURSELF_TIMEOUT)
448
fd = ConnectionNumber (display);
451
tv.tv_sec = (WM_SAVE_YOURSELF_TIMEOUT - msecs) / 1000;
452
tv.tv_usec = ((WM_SAVE_YOURSELF_TIMEOUT - msecs) % 1000) * 1000;
453
select (fd + 1, &fds, NULL, &fds, &tv);
456
g_timer_destroy (timer);
458
/* Terminate work in new display */
459
XAllowEvents (display, ReplayPointer, CurrentTime);
460
XAllowEvents (display, ReplayKeyboard, CurrentTime);
461
XSync (display, False);
462
XCloseDisplay (display);
463
XSetErrorHandler (old_handler);
465
for (lp = window_list; lp != NULL; lp = lp->next)
467
sm_window = SM_WINDOW (lp->data);
468
if (sm_window->type != SM_ERROR)
470
sm_window->wm_command = get_wmcommand (sm_window->wid);
471
sm_window->wm_client_machine = get_wmclientmachine (sm_window->wid);
472
if (sm_window->wm_command == NULL || sm_window->wm_client_machine == NULL)
473
sm_window->type = SM_ERROR;
481
xfsm_legacy_store_session (XfceRc *rc)
483
#ifdef LEGACY_SESSION_MANAGEMENT
489
for (lp = window_list; lp != NULL; lp = lp->next)
491
sm_window = SM_WINDOW (lp->data);
492
if (sm_window->type != SM_ERROR)
494
/* xmms is b0rked, surprise, surprise */
495
if ((sm_window->wm_class1 != NULL
496
&& strcmp (sm_window->wm_class1, "xmms") == 0)
497
|| (sm_window->wm_class2 != NULL
498
&& strcmp (sm_window->wm_class2, "xmms") == 0))
501
if (sm_window->wm_command == NULL
502
|| sm_window->wm_client_machine == NULL)
505
g_snprintf (buffer, 256, "Legacy%d_Screen", count);
506
xfce_rc_write_int_entry (rc, buffer, sm_window->screen_num);
508
g_snprintf (buffer, 256, "Legacy%d_Command", count);
509
xfce_rc_write_list_entry (rc, buffer, sm_window->wm_command, NULL);
511
g_snprintf (buffer, 256, "Legacy%d_ClientMachine", count);
512
xfce_rc_write_entry (rc, buffer, sm_window->wm_client_machine);
518
xfce_rc_write_int_entry (rc, "LegacyCount", count);
524
xfsm_legacy_load_session (XfceRc *rc)
526
#ifdef LEGACY_SESSION_MANAGEMENT
534
count = xfce_rc_read_int_entry (rc, "LegacyCount", 0);
535
for (i = 0; i < count; ++i)
537
g_snprintf (buffer, 256, "Legacy%d_Screen", i);
538
screen_num = xfce_rc_read_int_entry (rc, buffer, 0);
540
g_snprintf (buffer, 256, "Legacy%d_Command", i);
541
command = xfce_rc_read_list_entry (rc, buffer, NULL);
545
app = g_new0 (SmRestartApp, 1);
546
app->screen_num = screen_num;
547
app->command = command;
549
restart_apps = g_list_append (restart_apps, app);
556
xfsm_legacy_init (void)
558
#ifdef LEGACY_SESSION_MANAGEMENT
560
Atom dt_restore_mode;
567
/* Some CDE apps are broken (thanks again to Craig for the Sun Box :).
568
* Bugfix found on http://bugzilla.gnome.org/long_list.cgi?buglist=81343
569
* and implemented in gnome-session. The following code extends what
570
* gnome does, since it seems to be necessary to set both properties
571
* per screen, not just for the first screen. Anybody with access to
572
* CDE source code to enlighten me?
575
dt_save_mode = XInternAtom (dpy, "_DT_SAVE_MODE", False);
576
dt_restore_mode = XInternAtom (dpy, "_DT_RESTORE_MODE", False);
577
for (n = 0; n < ScreenCount (dpy); ++n)
579
root = RootWindow (dpy, n);
580
XChangeProperty (dpy, root, dt_save_mode, XA_STRING, 8,
581
PropModeReplace, "xfce4", sizeof ("xfce4"));
582
XChangeProperty (dpy, root, dt_restore_mode, XA_STRING, 8,
583
PropModeReplace, "xfce4", sizeof ("xfce4"));
590
xfsm_legacy_startup (void)
592
#ifdef LEGACY_SESSION_MANAGEMENT
596
for (lp = restart_apps; lp != NULL; lp = lp->next)
598
screen = gdk_display_get_screen (gdk_display_get_default (),
599
SM_RESTART_APP (lp->data)->screen_num);
600
xfsm_start_application (SM_RESTART_APP (lp->data)->command, NULL,
601
screen, NULL, NULL, NULL);
602
g_strfreev (SM_RESTART_APP (lp->data)->command);
606
g_list_free (restart_apps);
613
xfsm_legacy_shutdown (void)
615
#ifdef LEGACY_SESSION_MANAGEMENT
619
gdk_error_trap_push ();
622
for (lp = window_list; lp != NULL; lp = lp->next)
624
sm_window = SM_WINDOW (lp->data);
625
if (sm_window->wid != None)
626
XKillClient (gdk_display, sm_window->wid);
629
sm_window_list_clear ();
633
gdk_error_trap_pop ();