Fix session saving for apps with shared appicons
[wmaker-crm.git] / src / session.c
blobdac83cbc95a796d26b5d0e3a652f525af88aca74
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
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
26 * If defined(XSMP_ENABLED) and session manager is running then
27 * do normal stuff
28 * else
29 * do pre-R6 session management stuff (save window state and relaunch)
31 * When doing a checkpoint:
33 * = Without XSMP
34 * Open "Stop"/status Dialog
35 * Send SAVE_YOURSELF to clients and wait for reply
36 * Save restart info
37 * Save state of clients
39 * = With XSMP
40 * Send checkpoint request to sm
42 * When exiting:
43 * -------------
45 * = Without XSMP
47 * Open "Exit Now"/status Dialog
48 * Send SAVE_YOURSELF to clients and wait for reply
49 * Save restart info
50 * Save state of clients
51 * Send DELETE to all clients
52 * When no more clients are left or user hit "Exit Now", exit
54 * = With XSMP
56 * Send Shutdown request to session manager
57 * if SaveYourself message received, save state of clients
58 * if the Die message is received, exit.
61 #include "wconfig.h"
63 #include <X11/Xlib.h>
64 #include <X11/Xutil.h>
66 #ifdef XSMP_ENABLED
67 #include <X11/SM/SMlib.h>
68 #endif
70 #include <stdlib.h>
71 #include <stdio.h>
72 #include <string.h>
73 #include <unistd.h>
74 #include <time.h>
76 #include "WindowMaker.h"
77 #include "screen.h"
78 #include "window.h"
79 #include "client.h"
80 #include "session.h"
81 #include "wcore.h"
82 #include "framewin.h"
83 #include "workspace.h"
84 #include "funcs.h"
85 #include "properties.h"
86 #include "application.h"
87 #include "appicon.h"
89 #include "dock.h"
91 #include <WINGs/WUtil.h>
93 /** Global **/
95 extern Atom _XA_WM_SAVE_YOURSELF;
97 extern Time LastTimestamp;
99 #ifdef XSMP_ENABLED
101 extern int wScreenCount;
103 /* requested for SaveYourselfPhase2 */
104 static Bool sWaitingPhase2 = False;
106 static SmcConn sSMCConn = NULL;
108 static WMHandlerID sSMInputHandler = NULL;
110 /* our SM client ID */
111 static char *sClientID = NULL;
112 #endif
114 static WMPropList *sApplications = NULL;
115 static WMPropList *sCommand;
116 static WMPropList *sName;
117 static WMPropList *sHost;
118 static WMPropList *sWorkspace;
119 static WMPropList *sShaded;
120 static WMPropList *sMiniaturized;
121 static WMPropList *sHidden;
122 static WMPropList *sGeometry;
123 static WMPropList *sShortcutMask;
125 static WMPropList *sDock;
127 static WMPropList *sYes, *sNo;
129 static void make_keys()
131 if (sApplications != NULL)
132 return;
134 sApplications = WMCreatePLString("Applications");
135 sCommand = WMCreatePLString("Command");
136 sName = WMCreatePLString("Name");
137 sHost = WMCreatePLString("Host");
138 sWorkspace = WMCreatePLString("Workspace");
139 sShaded = WMCreatePLString("Shaded");
140 sMiniaturized = WMCreatePLString("Miniaturized");
141 sHidden = WMCreatePLString("Hidden");
142 sGeometry = WMCreatePLString("Geometry");
143 sDock = WMCreatePLString("Dock");
144 sShortcutMask = WMCreatePLString("ShortcutMask");
146 sYes = WMCreatePLString("Yes");
147 sNo = WMCreatePLString("No");
150 static int getBool(WMPropList * value)
152 char *val;
154 if (!WMIsPLString(value)) {
155 return 0;
157 if (!(val = WMGetFromPLString(value))) {
158 return 0;
161 if ((val[1] == '\0' && (val[0] == 'y' || val[0] == 'Y'))
162 || strcasecmp(val, "YES") == 0) {
164 return 1;
165 } else if ((val[1] == '\0' && (val[0] == 'n' || val[0] == 'N'))
166 || strcasecmp(val, "NO") == 0) {
167 return 0;
168 } else {
169 int i;
170 if (sscanf(val, "%i", &i) == 1) {
171 return (i != 0);
172 } else {
173 wwarning(_("can't convert \"%s\" to boolean"), val);
174 return 0;
179 static unsigned getInt(WMPropList * value)
181 char *val;
182 unsigned n;
184 if (!WMIsPLString(value))
185 return 0;
186 val = WMGetFromPLString(value);
187 if (!val)
188 return 0;
189 if (sscanf(val, "%u", &n) != 1)
190 return 0;
192 return n;
195 static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp)
197 WScreen *scr = wwin->screen_ptr;
198 Window win;
199 int i;
200 unsigned mask;
201 char *class, *instance, *command = NULL, buffer[512];
202 WMPropList *win_state, *cmd, *name, *workspace;
203 WMPropList *shaded, *miniaturized, *hidden, *geometry;
204 WMPropList *dock, *shortcut;
206 if (wwin->orig_main_window != None && wwin->orig_main_window != wwin->client_win)
207 win = wwin->orig_main_window;
208 else
209 win = wwin->client_win;
211 command = GetCommandForWindow(win);
212 if (!command)
213 return NULL;
215 if (PropGetWMClass(win, &class, &instance)) {
216 if (class && instance)
217 snprintf(buffer, sizeof(buffer), "%s.%s", instance, class);
218 else if (instance)
219 snprintf(buffer, sizeof(buffer), "%s", instance);
220 else if (class)
221 snprintf(buffer, sizeof(buffer), ".%s", class);
222 else
223 snprintf(buffer, sizeof(buffer), ".");
225 name = WMCreatePLString(buffer);
226 cmd = WMCreatePLString(command);
227 /*sprintf(buffer, "%d", wwin->frame->workspace+1);
228 workspace = WMCreatePLString(buffer); */
229 workspace = WMCreatePLString(scr->workspaces[wwin->frame->workspace]->name);
230 shaded = wwin->flags.shaded ? sYes : sNo;
231 miniaturized = wwin->flags.miniaturized ? sYes : sNo;
232 hidden = wwin->flags.hidden ? sYes : sNo;
233 snprintf(buffer, sizeof(buffer), "%ix%i+%i+%i",
234 wwin->client.width, wwin->client.height, wwin->frame_x, wwin->frame_y);
235 geometry = WMCreatePLString(buffer);
237 for (mask = 0, i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
238 if (scr->shortcutWindows[i] != NULL &&
239 WMGetFirstInArray(scr->shortcutWindows[i], wwin) != WANotFound) {
240 mask |= 1 << i;
244 snprintf(buffer, sizeof(buffer), "%u", mask);
245 shortcut = WMCreatePLString(buffer);
247 win_state = WMCreatePLDictionary(sName, name,
248 sCommand, cmd,
249 sWorkspace, workspace,
250 sShaded, shaded,
251 sMiniaturized, miniaturized,
252 sHidden, hidden,
253 sShortcutMask, shortcut, sGeometry, geometry, NULL);
255 WMReleasePropList(name);
256 WMReleasePropList(cmd);
257 WMReleasePropList(workspace);
258 WMReleasePropList(geometry);
259 WMReleasePropList(shortcut);
260 if (wapp && wapp->app_icon && wapp->app_icon->dock) {
261 int i;
262 char *name;
263 if (wapp->app_icon->dock == scr->dock) {
264 name = "Dock";
265 } else {
266 for (i = 0; i < scr->workspace_count; i++)
267 if (scr->workspaces[i]->clip == wapp->app_icon->dock)
268 break;
269 assert(i < scr->workspace_count);
270 /*n = i+1; */
271 name = scr->workspaces[i]->name;
273 dock = WMCreatePLString(name);
274 WMPutInPLDictionary(win_state, sDock, dock);
275 WMReleasePropList(dock);
277 } else {
278 win_state = NULL;
281 if (instance)
282 XFree(instance);
283 if (class)
284 XFree(class);
285 if (command)
286 wfree(command);
288 return win_state;
291 void wSessionSaveState(WScreen * scr)
293 WWindow *wwin = scr->focused_window;
294 WMPropList *win_info, *wks;
295 WMPropList *list = NULL;
296 WMArray *wapp_list = NULL;
298 make_keys();
300 if (!scr->session_state) {
301 scr->session_state = WMCreatePLDictionary(NULL, NULL);
302 if (!scr->session_state)
303 return;
306 list = WMCreatePLArray(NULL);
308 wapp_list = WMCreateArray(16);
310 while (wwin) {
311 WApplication *wapp = wApplicationOf(wwin->main_window);
312 Window appId = wwin->orig_main_window;
314 if ((wwin->transient_for == None || wwin->transient_for == wwin->screen_ptr->root_win)
315 && (WMGetFirstInArray(wapp_list, (void *)appId) == WANotFound
316 || WFLAGP(wwin, shared_appicon))
317 && !WFLAGP(wwin, dont_save_session)) {
318 /* A entry for this application was not yet saved. Save one. */
319 if ((win_info = makeWindowState(wwin, wapp)) != NULL) {
320 WMAddToPLArray(list, win_info);
321 WMReleasePropList(win_info);
322 /* If we were succesful in saving the info for this window
323 * add the application the window belongs to, to the
324 * application list, so no multiple entries for the same
325 * application are saved.
327 WMAddToArray(wapp_list, (void *)appId);
330 wwin = wwin->prev;
332 WMRemoveFromPLDictionary(scr->session_state, sApplications);
333 WMPutInPLDictionary(scr->session_state, sApplications, list);
334 WMReleasePropList(list);
336 wks = WMCreatePLString(scr->workspaces[scr->current_workspace]->name);
337 WMPutInPLDictionary(scr->session_state, sWorkspace, wks);
338 WMReleasePropList(wks);
340 WMFreeArray(wapp_list);
343 void wSessionClearState(WScreen * scr)
345 make_keys();
347 if (!scr->session_state)
348 return;
350 WMRemoveFromPLDictionary(scr->session_state, sApplications);
351 WMRemoveFromPLDictionary(scr->session_state, sWorkspace);
354 static pid_t execCommand(WScreen * scr, char *command, char *host)
356 pid_t pid;
357 char **argv;
358 int argc;
360 wtokensplit(command, &argv, &argc);
362 if (!argc) {
363 return 0;
366 if ((pid = fork()) == 0) {
367 char **args;
368 int i;
370 SetupEnvironment(scr);
372 args = malloc(sizeof(char *) * (argc + 1));
373 if (!args)
374 exit(111);
375 for (i = 0; i < argc; i++) {
376 args[i] = argv[i];
378 args[argc] = NULL;
379 execvp(argv[0], args);
380 exit(111);
382 while (argc > 0)
383 wfree(argv[--argc]);
384 wfree(argv);
385 return pid;
388 static WSavedState *getWindowState(WScreen * scr, WMPropList * win_state)
390 WSavedState *state = wmalloc(sizeof(WSavedState));
391 WMPropList *value;
392 char *tmp;
393 unsigned mask;
394 int i;
396 memset(state, 0, sizeof(WSavedState));
397 state->workspace = -1;
398 value = WMGetFromPLDictionary(win_state, sWorkspace);
399 if (value && WMIsPLString(value)) {
400 tmp = WMGetFromPLString(value);
401 if (sscanf(tmp, "%i", &state->workspace) != 1) {
402 state->workspace = -1;
403 for (i = 0; i < scr->workspace_count; i++) {
404 if (strcmp(scr->workspaces[i]->name, tmp) == 0) {
405 state->workspace = i;
406 break;
409 } else {
410 state->workspace--;
413 if ((value = WMGetFromPLDictionary(win_state, sShaded)) != NULL)
414 state->shaded = getBool(value);
415 if ((value = WMGetFromPLDictionary(win_state, sMiniaturized)) != NULL)
416 state->miniaturized = getBool(value);
417 if ((value = WMGetFromPLDictionary(win_state, sHidden)) != NULL)
418 state->hidden = getBool(value);
419 if ((value = WMGetFromPLDictionary(win_state, sShortcutMask)) != NULL) {
420 mask = getInt(value);
421 state->window_shortcuts = mask;
424 value = WMGetFromPLDictionary(win_state, sGeometry);
425 if (value && WMIsPLString(value)) {
426 if (!(sscanf(WMGetFromPLString(value), "%ix%i+%i+%i",
427 &state->w, &state->h, &state->x, &state->y) == 4 && (state->w > 0 && state->h > 0))) {
428 state->w = 0;
429 state->h = 0;
433 return state;
436 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
438 void wSessionRestoreState(WScreen * scr)
440 WSavedState *state;
441 char *instance, *class, *command, *host;
442 WMPropList *win_info, *apps, *cmd, *value;
443 pid_t pid;
444 int i, count;
445 WDock *dock;
446 WAppIcon *btn = NULL;
447 int j, n, found;
448 char *tmp;
450 make_keys();
452 if (!scr->session_state)
453 return;
455 WMPLSetCaseSensitive(True);
457 apps = WMGetFromPLDictionary(scr->session_state, sApplications);
458 if (!apps)
459 return;
461 count = WMGetPropListItemCount(apps);
462 if (count == 0)
463 return;
465 for (i = 0; i < count; i++) {
466 win_info = WMGetFromPLArray(apps, i);
468 cmd = WMGetFromPLDictionary(win_info, sCommand);
469 if (!cmd || !WMIsPLString(cmd) || !(command = WMGetFromPLString(cmd))) {
470 continue;
473 value = WMGetFromPLDictionary(win_info, sName);
474 if (!value)
475 continue;
477 ParseWindowName(value, &instance, &class, "session");
478 if (!instance && !class)
479 continue;
481 value = WMGetFromPLDictionary(win_info, sHost);
482 if (value && WMIsPLString(value))
483 host = WMGetFromPLString(value);
484 else
485 host = NULL;
487 state = getWindowState(scr, win_info);
489 dock = NULL;
490 value = WMGetFromPLDictionary(win_info, sDock);
491 if (value && WMIsPLString(value) && (tmp = WMGetFromPLString(value)) != NULL) {
492 if (sscanf(tmp, "%i", &n) != 1) {
493 if (!strcasecmp(tmp, "DOCK")) {
494 dock = scr->dock;
495 } else {
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 } else {
504 if (n == 0) {
505 dock = scr->dock;
506 } else if (n > 0 && n <= scr->workspace_count) {
507 dock = scr->workspaces[n - 1]->clip;
512 found = 0;
513 if (dock != NULL) {
514 for (j = 0; j < dock->max_icons; j++) {
515 btn = dock->icon_array[j];
516 if (btn && SAME(instance, btn->wm_instance) &&
517 SAME(class, btn->wm_class) && SAME(command, btn->command) && !btn->launching) {
518 found = 1;
519 break;
524 if (found) {
525 wDockLaunchWithState(dock, btn, state);
526 } else if ((pid = execCommand(scr, command, host)) > 0) {
527 wWindowAddSavedState(instance, class, command, pid, state);
528 } else {
529 wfree(state);
532 if (instance)
533 wfree(instance);
534 if (class)
535 wfree(class);
537 /* clean up */
538 WMPLSetCaseSensitive(False);
541 void wSessionRestoreLastWorkspace(WScreen * scr)
543 WMPropList *wks;
544 int w, i;
545 char *tmp;
547 make_keys();
549 if (!scr->session_state)
550 return;
552 WMPLSetCaseSensitive(True);
554 wks = WMGetFromPLDictionary(scr->session_state, sWorkspace);
555 if (!wks || !WMIsPLString(wks))
556 return;
558 tmp = WMGetFromPLString(wks);
560 /* clean up */
561 WMPLSetCaseSensitive(False);
563 if (sscanf(tmp, "%i", &w) != 1) {
564 w = -1;
565 for (i = 0; i < scr->workspace_count; i++) {
566 if (strcmp(scr->workspaces[i]->name, tmp) == 0) {
567 w = i;
568 break;
571 } else {
572 w--;
575 if (w != scr->current_workspace && w < scr->workspace_count) {
576 wWorkspaceChange(scr, w);
580 static void clearWaitingAckState(WScreen * scr)
582 WWindow *wwin;
583 WApplication *wapp;
585 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
586 wwin->flags.waiting_save_ack = 0;
587 if (wwin->main_window != None) {
588 wapp = wApplicationOf(wwin->main_window);
589 if (wapp)
590 wapp->main_window_desc->flags.waiting_save_ack = 0;
595 void wSessionSaveClients(WScreen * scr)
601 * With XSMP, this job is done by smproxy
603 void wSessionSendSaveYourself(WScreen * scr)
605 WWindow *wwin;
606 int count;
608 /* freeze client interaction with clients */
609 XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
610 XGrabPointer(dpy, scr->root_win, False, ButtonPressMask | ButtonReleaseMask,
611 GrabModeAsync, GrabModeAsync, scr->root_win, None, CurrentTime);
613 clearWaitingAckState(scr);
615 count = 0;
617 /* first send SAVE_YOURSELF for everybody */
618 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
619 WWindow *mainWin;
621 mainWin = wWindowFor(wwin->main_window);
623 if (mainWin) {
624 /* if the client is a multi-window client, only send message
625 * to the main window */
626 wwin = mainWin;
629 /* make sure the SAVE_YOURSELF flag is up-to-date */
630 PropGetProtocols(wwin->client_win, &wwin->protocols);
632 if (wwin->protocols.SAVE_YOURSELF) {
633 if (!wwin->flags.waiting_save_ack) {
634 wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
636 wwin->flags.waiting_save_ack = 1;
637 count++;
639 } else {
640 wwin->flags.waiting_save_ack = 0;
644 /* then wait for acknowledge */
645 while (count > 0) {
649 XUngrabPointer(dpy, CurrentTime);
650 XUngrabKeyboard(dpy, CurrentTime);
651 XFlush(dpy);
654 #ifdef XSMP_ENABLED
656 * With full session management support, the part of WMState
657 * that store client window state will become obsolete (maybe we can reuse
658 * the old code too),
659 * but we still need to store state info like the dock and workspaces.
660 * It is better to keep dock/wspace info in WMState because the user
661 * might want to keep the dock configuration while not wanting to
662 * resume a previously saved session.
663 * So, wmaker specific state info can be saved in
664 * ~/GNUstep/Library/WindowMaker/statename.state
665 * Its better to not put it in the defaults directory because:
666 * - its not a defaults file (having domain names like wmaker0089504baa
667 * in the defaults directory wouldn't be very neat)
668 * - this state file is not meant to be edited by users
670 * The old session code will become obsolete. When wmaker is
671 * compiled with R6 sm support compiled in, it'll be better to
672 * use a totally rewritten state saving code, but we can keep
673 * the current code for when XSMP_ENABLED is not compiled in.
675 * This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
676 * messages), but it'll be better.
678 * -readme
681 static char *getWindowRole(Window window)
683 XTextProperty prop;
684 static Atom atom = 0;
686 if (!atom)
687 atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
689 if (XGetTextProperty(dpy, window, &prop, atom)) {
690 if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
691 return prop.value;
694 return NULL;
699 * Saved Info:
701 * WM_WINDOW_ROLE
703 * WM_CLASS.instance
704 * WM_CLASS.class
705 * WM_NAME
706 * WM_COMMAND
708 * geometry
709 * state = (miniaturized, shaded, etc)
710 * attribute
711 * workspace #
712 * app state = (which dock, hidden)
713 * window shortcut #
716 static WMPropList *makeAppState(WWindow * wwin)
718 WApplication *wapp;
719 WMPropList *state;
720 WScreen *scr = wwin->screen_ptr;
722 state = WMCreatePLArray(NULL, NULL);
724 wapp = wApplicationOf(wwin->main_window);
726 if (wapp) {
727 if (wapp->app_icon && wapp->app_icon->dock) {
729 if (wapp->app_icon->dock == scr->dock) {
730 WMAddToPLArray(state, WMCreatePLString("Dock"));
731 } else {
732 int i;
734 for (i = 0; i < scr->workspace_count; i++)
735 if (scr->workspaces[i]->clip == wapp->app_icon->dock)
736 break;
738 assert(i < scr->workspace_count);
740 WMAddToPLArray(state, WMCreatePLString(scr->workspaces[i]->name));
744 WMAddToPLArray(state, WMCreatePLString(wapp->hidden ? "1" : "0"));
747 return state;
750 Bool wSessionGetStateFor(WWindow * wwin, WSessionData * state)
752 char *str;
753 WMPropList *slist;
754 WMPropList *elem;
755 WMPropList *value;
756 int index = 0;
758 index = 3;
760 /* geometry */
761 value = WMGetFromPLArray(slist, index++);
762 str = WMGetFromPLString(value);
764 sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
765 &state->width, &state->height, &state->user_changed_width, &state->user_changed_height);
767 /* state */
768 value = WMGetFromPLArray(slist, index++);
769 str = WMGetFromPLString(value);
771 sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded, &state->maximized);
773 /* attributes */
774 value = WMGetFromPLArray(slist, index++);
775 str = WMGetFromPLString(value);
777 getAttributeState(str, &state->mflags, &state->flags);
779 /* workspace */
780 value = WMGetFromPLArray(slist, index++);
781 str = WMGetFromPLString(value);
783 sscanf(str, "%i", &state->workspace);
785 /* app state (repeated for all windows of the app) */
786 value = WMGetFromPLArray(slist, index++);
787 str = WMGetFromPLString(value);
789 /* ???? */
791 /* shortcuts */
792 value = WMGetFromPLArray(slist, index++);
793 str = WMGetFromPLString(value);
795 sscanf(str, "%i", &state->shortcuts);
798 static WMPropList *makeAttributeState(WWindow * wwin)
800 unsigned int data1, data2;
801 char buffer[256];
803 #define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
804 ? (wwin)->user_flags.FLAG : -1)
806 snprintf(buffer, sizeof(buffer),
807 "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
808 W_FLAG(no_titlebar),
809 W_FLAG(no_resizable),
810 W_FLAG(no_closable),
811 W_FLAG(no_miniaturizable),
812 W_FLAG(no_resizebar), W_FLAG(no_close_button), W_FLAG(no_miniaturize_button),
814 W_FLAG(broken_close),
815 W_FLAG(kill_close),
817 W_FLAG(no_shadeable),
818 W_FLAG(omnipresent),
819 W_FLAG(skip_window_list),
820 W_FLAG(floating),
821 W_FLAG(sunken),
822 W_FLAG(no_bind_keys),
823 W_FLAG(no_bind_mouse),
824 W_FLAG(no_hide_others),
825 W_FLAG(no_appicon),
826 W_FLAG(dont_move_off),
827 W_FLAG(no_focusable),
828 W_FLAG(always_user_icon),
829 W_FLAG(start_miniaturized),
830 W_FLAG(start_hidden),
831 W_FLAG(start_maximized),
832 W_FLAG(dont_save_session), W_FLAG(dont_focus_across_wksp), W_FLAG(emulate_appicon));
834 return WMCreatePLString(buffer);
837 static void appendStringInArray(WMPropList * array, char *str)
839 WMPropList *val;
841 val = WMCreatePLString(str);
842 WMAddToPLArray(array, val);
843 WMReleasePropList(val);
846 static WMPropList *makeClientState(WWindow * wwin)
848 WMPropList *state;
849 WMPropList *tmp;
850 char *str;
851 char buffer[512];
852 int i;
853 unsigned shortcuts;
855 state = WMCreatePLArray(NULL, NULL);
857 /* WM_WINDOW_ROLE */
858 str = getWindowRole(wwin->client_win);
859 if (!str)
860 appendStringInArray(state, "");
861 else {
862 appendStringInArray(state, str);
863 XFree(str);
866 /* WM_CLASS.instance */
867 appendStringInArray(state, wwin->wm_instance);
869 /* WM_CLASS.class */
870 appendStringInArray(state, wwin->wm_class);
872 /* WM_NAME */
873 if (wwin->flags.wm_name_changed)
874 appendStringInArray(state, "");
875 else
876 appendStringInArray(state, wwin->frame->name);
878 /* geometry */
879 snprintf(buffer, sizeof(buffer), "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
880 wwin->client.width, wwin->client.height,
881 wwin->flags.user_changed_width, wwin->flags.user_changed_height);
882 appendStringInArray(state, buffer);
884 /* state */
885 snprintf(buffer, sizeof(buffer), "%i %i %i", wwin->flags.miniaturized,
886 wwin->flags.shaded, wwin->flags.maximized);
887 appendStringInArray(state, buffer);
889 /* attributes */
890 tmp = makeAttributeState(wwin);
891 WMAddToPLArray(state, tmp);
892 WMReleasePropList(tmp);
894 /* workspace */
895 snprintf(buffer, sizeof(buffer), "%i", wwin->frame->workspace);
896 appendStringInArray(state, buffer);
898 /* app state (repeated for all windows of the app) */
899 tmp = makeAppState(wwin);
900 WMAddToPLArray(state, tmp);
901 WMReleasePropList(tmp);
903 /* shortcuts */
904 shortcuts = 0;
905 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
906 if (scr->shortcutWindow[i] == wwin) {
907 shortcuts |= 1 << i;
910 snprintf(buffer, sizeof(buffer), "%ui", shortcuts);
911 appendStringInArray(tmp, buffer);
913 return state;
916 static void smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
918 SmProp props[4];
919 SmPropValue prop1val, prop2val, prop3val, prop4val;
920 char **argv = (char **)client_data;
921 int argc;
922 int i, j;
923 Bool ok = False;
924 char *statefile = NULL;
925 char *prefix;
926 Bool gsPrefix = False;
927 char *discardCmd = NULL;
928 time_t t;
929 WMPropList *state, *plState;
930 int len;
932 #ifdef DEBUG1
933 puts("received SaveYourselfPhase2 SM message");
934 #endif
936 /* save session state */
938 /* the file that will contain the state */
939 prefix = getenv("SM_SAVE_DIR");
940 if (!prefix) {
941 prefix = wusergnusteppath();
942 if (prefix)
943 gsPrefix = True;
945 if (!prefix) {
946 prefix = getenv("HOME");
948 if (!prefix)
949 prefix = ".";
951 len = strlen(prefix) + 64;
952 statefile = malloc(len);
953 if (!statefile) {
954 wwarning(_("out of memory while saving session state"));
955 goto fail;
958 t = time();
959 i = 0;
960 do {
961 if (gsPrefix)
962 snprintf(statefile, len, "%s/Library/WindowMaker/wmaker.%l%i.state", prefix, t, i);
963 else
964 snprintf(statefile, len, "%s/wmaker.%l%i.state", prefix, t, i);
965 i++;
966 } while (access(F_OK, statefile) != -1);
968 /* save the states of all windows we're managing */
969 state = WMCreatePLArray(NULL, NULL);
972 * Format:
974 * state_file ::= dictionary with version_info ; state
975 * version_info ::= 'version' = '1';
976 * state ::= 'state' = array of screen_info
977 * screen_info ::= array of (screen number, window_info, window_info, ...)
978 * window_info ::=
980 for (i = 0; i < wScreenCount; i++) {
981 WScreen *scr;
982 WWindow *wwin;
983 char buf[32];
984 WMPropList *pscreen;
986 scr = wScreenWithNumber(i);
988 snprintf(buf, sizeof(buf), "%i", scr->screen);
989 pscreen = WMCreatePLArray(WMCreatePLString(buf), NULL);
991 wwin = scr->focused_window;
992 while (wwin) {
993 WMPropList *pwindow;
995 pwindow = makeClientState(wwin);
996 WMAddToPLArray(pscreen, pwindow);
998 wwin = wwin->prev;
1001 WMAddToPLArray(state, pscreen);
1004 plState = WMCreatePLDictionary(WMCreatePLString("Version"),
1005 WMCreatePLString("1.0"), WMCreatePLString("Screens"), state, NULL);
1007 if (!WMWritePropListToFile(plState, statefile, True)) {
1008 wwarning(_("error while saving session state"));
1009 WMReleasePropList(plState);
1010 goto fail;
1013 WMReleasePropList(plState);
1015 /* set the remaining properties that we didn't set at
1016 * startup time */
1018 for (argc = 0, i = 0; argv[i] != NULL; i++) {
1019 if (strcmp(argv[i], "-clientid") == 0 || strcmp(argv[i], "-restore") == 0) {
1020 i++;
1021 } else {
1022 argc++;
1026 prop[0].name = SmRestartCommand;
1027 prop[0].type = SmLISTofARRAY8;
1028 prop[0].vals = malloc(sizeof(SmPropValue) * (argc + 4));
1029 prop[0].num_vals = argc + 4;
1031 prop[1].name = SmCloneCommand;
1032 prop[1].type = SmLISTofARRAY8;
1033 prop[1].vals = malloc(sizeof(SmPropValue) * (argc));
1034 prop[1].num_vals = argc;
1036 if (!prop[0].vals || !prop[1].vals) {
1037 wwarning(_("end of memory while saving session state"));
1038 goto fail;
1041 for (j = 0, i = 0; i < argc + 4; i++) {
1042 if (strcmp(argv[i], "-clientid") == 0 || strcmp(argv[i], "-restore") == 0) {
1043 i++;
1044 } else {
1045 prop[0].vals[j].value = argv[i];
1046 prop[0].vals[j].length = strlen(argv[i]);
1047 prop[1].vals[j].value = argv[i];
1048 prop[1].vals[j].length = strlen(argv[i]);
1049 j++;
1052 prop[0].vals[j].value = "-clientid";
1053 prop[0].vals[j].length = 9;
1054 j++;
1055 prop[0].vals[j].value = sClientID;
1056 prop[0].vals[j].length = strlen(sClientID);
1057 j++;
1058 prop[0].vals[j].value = "-restore";
1059 prop[0].vals[j].length = 11;
1060 j++;
1061 prop[0].vals[j].value = statefile;
1062 prop[0].vals[j].length = strlen(statefile);
1065 int len = strlen(statefile) + 8;
1067 discardCmd = malloc(len);
1068 if (!discardCmd)
1069 goto fail;
1070 snprintf(discardCmd, len, "rm %s", statefile);
1072 prop[2].name = SmDiscardCommand;
1073 prop[2].type = SmARRAY8;
1074 prop[2].vals[0] = discardCmd;
1075 prop[2].num_vals = 1;
1077 SmcSetProperties(sSMCConn, 3, prop);
1079 ok = True;
1080 fail:
1081 SmcSaveYourselfDone(smc_conn, ok);
1083 if (prop[0].vals)
1084 wfree(prop[0].vals);
1085 if (prop[1].vals)
1086 wfree(prop[1].vals);
1087 if (discardCmd)
1088 wfree(discardCmd);
1090 if (!ok) {
1091 remove(statefile);
1093 if (statefile)
1094 wfree(statefile);
1097 static void
1098 smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
1099 Bool shutdown, int interact_style, Bool fast)
1101 #ifdef DEBUG1
1102 puts("received SaveYourself SM message");
1103 #endif
1105 if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc, client_data)) {
1107 SmcSaveYourselfDone(smc_conn, False);
1108 sWaitingPhase2 = False;
1109 } else {
1110 #ifdef DEBUG1
1111 puts("successfull request of SYS phase 2");
1112 #endif
1113 sWaitingPhase2 = True;
1117 static void smDieProc(SmcConn smc_conn, SmPointer client_data)
1119 #ifdef DEBUG1
1120 puts("received Die SM message");
1121 #endif
1123 wSessionDisconnectManager();
1125 Shutdown(WSExitMode, True);
1128 static void smSaveCompleteProc(SmcConn smc_conn)
1130 /* it means that we can resume doing things that can change our state */
1131 #ifdef DEBUG1
1132 puts("received SaveComplete SM message");
1133 #endif
1136 static void smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
1138 if (sWaitingPhase2) {
1140 sWaitingPhase2 = False;
1142 SmcSaveYourselfDone(smc_conn, False);
1146 static void iceMessageProc(int fd, int mask, void *clientData)
1148 IceConn iceConn = (IceConn) clientData;
1150 IceProcessMessages(iceConn, NULL, NULL);
1153 static void iceIOErrorHandler(IceConnection ice_conn)
1155 /* This is not fatal but can mean the session manager exited.
1156 * If the session manager exited normally we would get a
1157 * Die message, so this probably means an abnormal exit.
1158 * If the sm was the last client of session, then we'll die
1159 * anyway, otherwise we can continue doing our stuff.
1161 wwarning(_("connection to the session manager was lost"));
1162 wSessionDisconnectManager();
1165 void wSessionConnectManager(char **argv, int argc)
1167 IceConn iceConn;
1168 char *previous_id = NULL;
1169 char buffer[256];
1170 SmcCallbacks callbacks;
1171 unsigned long mask;
1172 char uid[32];
1173 char pid[32];
1174 SmProp props[4];
1175 SmPropValue prop1val, prop2val, prop3val, prop4val;
1176 char restartStyle;
1177 int i;
1179 mask = SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
1181 callbacks.save_yourself.callback = smSaveYourselfProc;
1182 callbacks.save_yourself.client_data = argv;
1184 callbacks.die.callback = smDieProc;
1185 callbacks.die.client_data = NULL;
1187 callbacks.save_complete.callback = smSaveCompleteProc;
1188 callbacks.save_complete.client_data = NULL;
1190 callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
1191 callbacks.shutdown_cancelled.client_data = NULL;
1193 for (i = 0; i < argc; i++) {
1194 if (strcmp(argv[i], "-clientid") == 0) {
1195 previous_id = argv[i + 1];
1196 break;
1200 /* connect to the session manager */
1201 sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
1202 mask, &callbacks, previous_id, &sClientID, 255, buffer);
1203 if (!sSMCConn) {
1204 return;
1206 #ifdef DEBUG1
1207 puts("connected to the session manager");
1208 #endif
1210 /* IceSetIOErrorHandler(iceIOErrorHandler); */
1212 /* check for session manager clients */
1213 iceConn = SmcGetIceConnection(smcConn);
1215 if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
1216 wsyserror("error setting close-on-exec flag for ICE connection");
1219 sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn), WIReadMask, iceMessageProc, iceConn);
1221 /* setup information about ourselves */
1223 /* program name */
1224 prop1val.value = argv[0];
1225 prop1val.length = strlen(argv[0]);
1226 prop[0].name = SmProgram;
1227 prop[0].type = SmARRAY8;
1228 prop[0].num_vals = 1;
1229 prop[0].vals = &prop1val;
1231 /* The XSMP doc from X11R6.1 says it contains the user name,
1232 * but every client implementation I saw places the uid # */
1233 snprintf(uid, sizeof(uid), "%i", getuid());
1234 prop2val.value = uid;
1235 prop2val.length = strlen(uid);
1236 prop[1].name = SmUserID;
1237 prop[1].type = SmARRAY8;
1238 prop[1].num_vals = 1;
1239 prop[1].vals = &prop2val;
1241 /* Restart style. We should restart only if we were running when
1242 * the previous session finished. */
1243 restartStyle = SmRestartIfRunning;
1244 prop3val.value = &restartStyle;
1245 prop3val.length = 1;
1246 prop[2].name = SmRestartStyleHint;
1247 prop[2].type = SmCARD8;
1248 prop[2].num_vals = 1;
1249 prop[2].vals = &prop3val;
1251 /* Our PID. Not required but might be usefull */
1252 snprintf(pid, sizeof(pid), "%i", getpid());
1253 prop4val.value = pid;
1254 prop4val.length = strlen(pid);
1255 prop[3].name = SmProcessID;
1256 prop[3].type = SmARRAY8;
1257 prop[3].num_vals = 1;
1258 prop[3].vals = &prop4val;
1260 /* we'll set the rest of the hints later */
1262 SmcSetProperties(sSMCConn, 4, props);
1266 void wSessionDisconnectManager(void)
1268 if (sSMCConn) {
1269 WMDeleteInputHandler(sSMInputHandler);
1270 sSMInputHandler = NULL;
1272 SmcCloseConnection(sSMCConn, 0, NULL);
1273 sSMCConn = NULL;
1277 void wSessionRequestShutdown(void)
1279 /* request a shutdown to the session manager */
1280 if (sSMCConn)
1281 SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny, False, True);
1284 Bool wSessionIsManaged(void)
1286 return sSMCConn != NULL;
1289 #endif /* !XSMP_ENABLED */