wmaker: replace and be replaced (ICCCM protocol)
[wmaker-crm.git] / src / session.c
blob90109a3f7e97392aba0a5b304c805261f57f20c2
1 /* session.c - session state handling and R6 style session management
3 * Copyright (c) 1998-2003 Dan Pascu
4 * Copyright (c) 1998-2003 Alfredo Kojima
6 * Window Maker window manager
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If defined(XSMP_ENABLED) and session manager is running then
26 * do normal stuff
27 * else
28 * do pre-R6 session management stuff (save window state and relaunch)
30 * When doing a checkpoint:
32 * = Without XSMP
33 * Open "Stop"/status Dialog
34 * Send SAVE_YOURSELF to clients and wait for reply
35 * Save restart info
36 * Save state of clients
38 * = With XSMP
39 * Send checkpoint request to sm
41 * When exiting:
42 * -------------
44 * = Without XSMP
46 * Open "Exit Now"/status Dialog
47 * Send SAVE_YOURSELF to clients and wait for reply
48 * Save restart info
49 * Save state of clients
50 * Send DELETE to all clients
51 * When no more clients are left or user hit "Exit Now", exit
53 * = With XSMP
55 * Send Shutdown request to session manager
56 * if SaveYourself message received, save state of clients
57 * if the Die message is received, exit.
60 #include "wconfig.h"
62 #include <X11/Xlib.h>
63 #include <X11/Xutil.h>
65 #include <stdlib.h>
66 #include <stdio.h>
67 #include <string.h>
68 #include <strings.h>
69 #include <unistd.h>
70 #include <time.h>
72 #include "WindowMaker.h"
73 #include "screen.h"
74 #include "window.h"
75 #include "client.h"
76 #include "session.h"
77 #include "framewin.h"
78 #include "workspace.h"
79 #include "main.h"
80 #include "properties.h"
81 #include "application.h"
82 #include "appicon.h"
83 #include "dock.h"
84 #include "misc.h"
86 #include <WINGs/WUtil.h>
89 static WMPropList *sApplications = NULL;
90 static WMPropList *sCommand;
91 static WMPropList *sName;
92 static WMPropList *sHost;
93 static WMPropList *sWorkspace;
94 static WMPropList *sShaded;
95 static WMPropList *sMiniaturized;
96 static WMPropList *sHidden;
97 static WMPropList *sGeometry;
98 static WMPropList *sShortcutMask;
100 static WMPropList *sDock;
101 static WMPropList *sYes, *sNo;
103 static void make_keys(void)
105 if (sApplications != NULL)
106 return;
108 sApplications = WMCreatePLString("Applications");
109 sCommand = WMCreatePLString("Command");
110 sName = WMCreatePLString("Name");
111 sHost = WMCreatePLString("Host");
112 sWorkspace = WMCreatePLString("Workspace");
113 sShaded = WMCreatePLString("Shaded");
114 sMiniaturized = WMCreatePLString("Miniaturized");
115 sHidden = WMCreatePLString("Hidden");
116 sGeometry = WMCreatePLString("Geometry");
117 sDock = WMCreatePLString("Dock");
118 sShortcutMask = WMCreatePLString("ShortcutMask");
120 sYes = WMCreatePLString("Yes");
121 sNo = WMCreatePLString("No");
124 static int getBool(WMPropList * value)
126 char *val;
128 if (!WMIsPLString(value)) {
129 return 0;
131 val = WMGetFromPLString(value);
132 if (val == NULL)
133 return 0;
135 if ((val[1] == '\0' && (val[0] == 'y' || val[0] == 'Y'))
136 || strcasecmp(val, "YES") == 0) {
138 return 1;
139 } else if ((val[1] == '\0' && (val[0] == 'n' || val[0] == 'N'))
140 || strcasecmp(val, "NO") == 0) {
141 return 0;
142 } else {
143 int i;
144 if (sscanf(val, "%i", &i) == 1) {
145 return (i != 0);
146 } else {
147 wwarning(_("can't convert \"%s\" to boolean"), val);
148 return 0;
153 static unsigned getInt(WMPropList * value)
155 char *val;
156 unsigned n;
158 if (!WMIsPLString(value))
159 return 0;
160 val = WMGetFromPLString(value);
161 if (!val)
162 return 0;
163 if (sscanf(val, "%u", &n) != 1)
164 return 0;
166 return n;
169 static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp)
171 WScreen *scr = wwin->screen_ptr;
172 Window win;
173 int i;
174 unsigned mask;
175 char *class, *instance, *command = NULL, buffer[512];
176 WMPropList *win_state, *cmd, *name, *workspace;
177 WMPropList *shaded, *miniaturized, *hidden, *geometry;
178 WMPropList *dock, *shortcut;
180 if (wwin->orig_main_window != None && wwin->orig_main_window != wwin->client_win)
181 win = wwin->orig_main_window;
182 else
183 win = wwin->client_win;
185 command = GetCommandForWindow(win);
186 if (!command)
187 return NULL;
189 if (PropGetWMClass(win, &class, &instance)) {
190 if (class && instance)
191 snprintf(buffer, sizeof(buffer), "%s.%s", instance, class);
192 else if (instance)
193 snprintf(buffer, sizeof(buffer), "%s", instance);
194 else if (class)
195 snprintf(buffer, sizeof(buffer), ".%s", class);
196 else
197 snprintf(buffer, sizeof(buffer), ".");
199 name = WMCreatePLString(buffer);
200 cmd = WMCreatePLString(command);
201 workspace = WMCreatePLString(scr->workspaces[wwin->frame->workspace]->name);
203 shaded = wwin->flags.shaded ? sYes : sNo;
204 miniaturized = wwin->flags.miniaturized ? sYes : sNo;
205 hidden = wwin->flags.hidden ? sYes : sNo;
206 snprintf(buffer, sizeof(buffer), "%ix%i+%i+%i",
207 wwin->client.width, wwin->client.height, wwin->frame_x, wwin->frame_y);
208 geometry = WMCreatePLString(buffer);
210 for (mask = 0, i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
211 if (scr->shortcutWindows[i] != NULL &&
212 WMGetFirstInArray(scr->shortcutWindows[i], wwin) != WANotFound)
213 mask |= 1 << i;
216 snprintf(buffer, sizeof(buffer), "%u", mask);
217 shortcut = WMCreatePLString(buffer);
219 win_state = WMCreatePLDictionary(sName, name,
220 sCommand, cmd,
221 sWorkspace, workspace,
222 sShaded, shaded,
223 sMiniaturized, miniaturized,
224 sHidden, hidden,
225 sShortcutMask, shortcut, sGeometry, geometry, NULL);
227 WMReleasePropList(name);
228 WMReleasePropList(cmd);
229 WMReleasePropList(workspace);
230 WMReleasePropList(geometry);
231 WMReleasePropList(shortcut);
232 if (wapp && wapp->app_icon && wapp->app_icon->dock) {
233 int i;
234 char *name = NULL;
235 if (wapp->app_icon->dock == scr->dock)
236 name = "Dock";
238 /* Try the clips */
239 if (name == NULL) {
240 for (i = 0; i < scr->workspace_count; i++)
241 if (scr->workspaces[i]->clip == wapp->app_icon->dock)
242 break;
243 if (i < scr->workspace_count)
244 name = scr->workspaces[i]->name;
246 /* Try the drawers */
247 if (name == NULL) {
248 WDrawerChain *dc;
249 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
250 if (dc->adrawer == wapp->app_icon->dock)
251 break;
253 assert(dc != NULL);
254 name = dc->adrawer->icon_array[0]->wm_instance;
256 dock = WMCreatePLString(name);
257 WMPutInPLDictionary(win_state, sDock, dock);
258 WMReleasePropList(dock);
260 } else {
261 win_state = NULL;
264 if (instance)
265 free(instance);
266 if (class)
267 free(class);
268 if (command)
269 wfree(command);
271 return win_state;
274 void wSessionSaveState(WScreen * scr)
276 WWindow *wwin = scr->focused_window;
277 WMPropList *win_info, *wks;
278 WMPropList *list = NULL;
279 WMArray *wapp_list = NULL;
281 make_keys();
283 if (!scr->session_state) {
284 scr->session_state = WMCreatePLDictionary(NULL, NULL);
285 if (!scr->session_state)
286 return;
289 list = WMCreatePLArray(NULL);
291 wapp_list = WMCreateArray(16);
293 while (wwin) {
294 WApplication *wapp = wApplicationOf(wwin->main_window);
295 Window appId = wwin->orig_main_window;
297 if ((wwin->transient_for == None || wwin->transient_for == wwin->screen_ptr->root_win)
298 && (WMGetFirstInArray(wapp_list, (void *)appId) == WANotFound
299 || WFLAGP(wwin, shared_appicon))
300 && !WFLAGP(wwin, dont_save_session)) {
301 /* A entry for this application was not yet saved. Save one. */
302 win_info = makeWindowState(wwin, wapp);
303 if (win_info != NULL) {
304 WMAddToPLArray(list, win_info);
305 WMReleasePropList(win_info);
306 /* If we were succesful in saving the info for this window
307 * add the application the window belongs to, to the
308 * application list, so no multiple entries for the same
309 * application are saved.
311 WMAddToArray(wapp_list, (void *)appId);
314 wwin = wwin->prev;
316 WMRemoveFromPLDictionary(scr->session_state, sApplications);
317 WMPutInPLDictionary(scr->session_state, sApplications, list);
318 WMReleasePropList(list);
320 wks = WMCreatePLString(scr->workspaces[scr->current_workspace]->name);
321 WMPutInPLDictionary(scr->session_state, sWorkspace, wks);
322 WMReleasePropList(wks);
324 WMFreeArray(wapp_list);
327 void wSessionClearState(WScreen * scr)
329 make_keys();
331 if (!scr->session_state)
332 return;
334 WMRemoveFromPLDictionary(scr->session_state, sApplications);
335 WMRemoveFromPLDictionary(scr->session_state, sWorkspace);
338 static pid_t execCommand(WScreen *scr, char *command)
340 pid_t pid;
341 char **argv;
342 int argc;
344 wtokensplit(command, &argv, &argc);
346 if (!argc) {
347 return 0;
350 pid = fork();
351 if (pid == 0) {
352 char **args;
353 int i;
355 SetupEnvironment(scr);
357 args = malloc(sizeof(char *) * (argc + 1));
358 if (!args)
359 exit(111);
360 for (i = 0; i < argc; i++) {
361 args[i] = argv[i];
363 args[argc] = NULL;
364 execvp(argv[0], args);
365 exit(111);
367 while (argc > 0)
368 wfree(argv[--argc]);
369 wfree(argv);
370 return pid;
373 static WSavedState *getWindowState(WScreen * scr, WMPropList * win_state)
375 WSavedState *state = wmalloc(sizeof(WSavedState));
376 WMPropList *value;
377 char *tmp;
378 unsigned mask;
379 int i;
381 state->workspace = -1;
382 value = WMGetFromPLDictionary(win_state, sWorkspace);
383 if (value && WMIsPLString(value)) {
384 tmp = WMGetFromPLString(value);
385 if (sscanf(tmp, "%i", &state->workspace) != 1) {
386 state->workspace = -1;
387 for (i = 0; i < scr->workspace_count; i++) {
388 if (strcmp(scr->workspaces[i]->name, tmp) == 0) {
389 state->workspace = i;
390 break;
393 } else {
394 state->workspace--;
398 value = WMGetFromPLDictionary(win_state, sShaded);
399 if (value != NULL)
400 state->shaded = getBool(value);
402 value = WMGetFromPLDictionary(win_state, sMiniaturized);
403 if (value != NULL)
404 state->miniaturized = getBool(value);
406 value = WMGetFromPLDictionary(win_state, sHidden);
407 if (value != NULL)
408 state->hidden = getBool(value);
410 value = WMGetFromPLDictionary(win_state, sShortcutMask);
411 if (value != NULL) {
412 mask = getInt(value);
413 state->window_shortcuts = mask;
416 value = WMGetFromPLDictionary(win_state, sGeometry);
417 if (value && WMIsPLString(value)) {
418 if (!(sscanf(WMGetFromPLString(value), "%ix%i+%i+%i",
419 &state->w, &state->h, &state->x, &state->y) == 4 && (state->w > 0 && state->h > 0))) {
420 state->w = 0;
421 state->h = 0;
425 return state;
428 static inline int is_same(const char *x, const char *y)
430 if ((x == NULL) && (y == NULL))
431 return 1;
433 if ((x == NULL) || (y == NULL))
434 return 0;
436 if (strcmp(x, y) == 0)
437 return 1;
438 else
439 return 0;
442 void wSessionRestoreState(WScreen *scr)
444 WSavedState *state;
445 char *instance, *class, *command;
446 WMPropList *win_info, *apps, *cmd, *value;
447 pid_t pid;
448 int i, count;
449 WDock *dock;
450 WAppIcon *btn = NULL;
451 int j, n, found;
452 char *tmp;
454 make_keys();
456 if (!scr->session_state)
457 return;
459 WMPLSetCaseSensitive(True);
461 apps = WMGetFromPLDictionary(scr->session_state, sApplications);
462 if (!apps)
463 return;
465 count = WMGetPropListItemCount(apps);
466 if (count == 0)
467 return;
469 for (i = 0; i < count; i++) {
470 win_info = WMGetFromPLArray(apps, i);
472 cmd = WMGetFromPLDictionary(win_info, sCommand);
473 if (!cmd || !WMIsPLString(cmd) || !(command = WMGetFromPLString(cmd))) {
474 continue;
477 value = WMGetFromPLDictionary(win_info, sName);
478 if (!value)
479 continue;
481 ParseWindowName(value, &instance, &class, "session");
482 if (!instance && !class)
483 continue;
485 state = getWindowState(scr, win_info);
487 dock = NULL;
488 value = WMGetFromPLDictionary(win_info, sDock);
489 if (value && WMIsPLString(value) && (tmp = WMGetFromPLString(value)) != NULL) {
490 if (sscanf(tmp, "%i", &n) != 1) {
491 if (!strcasecmp(tmp, "DOCK"))
492 dock = scr->dock;
494 /* Try the clips */
495 if (dock == NULL) {
496 for (j = 0; j < scr->workspace_count; j++) {
497 if (strcmp(scr->workspaces[j]->name, tmp) == 0) {
498 dock = scr->workspaces[j]->clip;
499 break;
503 if (dock == NULL) // Try the drawers
505 WDrawerChain *dc;
506 for (dc = scr->drawers; dc != NULL; dc = dc->next)
508 if (strcmp(dc->adrawer->icon_array[0]->wm_instance, tmp) == 0)
510 dock = dc->adrawer;
511 break;
515 } else {
516 if (n == 0) {
517 dock = scr->dock;
518 } else if (n > 0 && n <= scr->workspace_count) {
519 dock = scr->workspaces[n - 1]->clip;
524 found = 0;
525 if (dock != NULL) {
526 for (j = 0; j < dock->max_icons; j++) {
527 btn = dock->icon_array[j];
528 if (btn && is_same(instance, btn->wm_instance) &&
529 is_same(class, btn->wm_class) &&
530 is_same(command, btn->command) &&
531 !btn->launching) {
532 found = 1;
533 break;
538 if (found) {
539 wDockLaunchWithState(btn, state);
540 } else if ((pid = execCommand(scr, command)) > 0) {
541 wWindowAddSavedState(instance, class, command, pid, state);
542 } else {
543 wfree(state);
546 if (instance)
547 wfree(instance);
548 if (class)
549 wfree(class);
551 /* clean up */
552 WMPLSetCaseSensitive(False);
555 void wSessionRestoreLastWorkspace(WScreen * scr)
557 WMPropList *wks;
558 int w;
559 char *value;
561 make_keys();
563 if (!scr->session_state)
564 return;
566 WMPLSetCaseSensitive(True);
568 wks = WMGetFromPLDictionary(scr->session_state, sWorkspace);
569 if (!wks || !WMIsPLString(wks))
570 return;
572 value = WMGetFromPLString(wks);
574 if (!value)
575 return;
577 /* clean up */
578 WMPLSetCaseSensitive(False);
580 /* Get the workspace number for the workspace name */
581 w = wGetWorkspaceNumber(scr, value);
583 if (w != scr->current_workspace && w < scr->workspace_count)
584 wWorkspaceChange(scr, w);