Fix "smart" placement bug when there are shaded windows in other workspaces
[wmaker-crm.git] / src / session.c
blob90f50405a19e645e54a265f9ba15dcceb6f90e03
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, dont_save_session)) {
317 /* A entry for this application was not yet saved. Save one. */
318 if ((win_info = makeWindowState(wwin, wapp)) != NULL) {
319 WMAddToPLArray(list, win_info);
320 WMReleasePropList(win_info);
321 /* If we were succesful in saving the info for this window
322 * add the application the window belongs to, to the
323 * application list, so no multiple entries for the same
324 * application are saved.
326 WMAddToArray(wapp_list, (void *)appId);
329 wwin = wwin->prev;
331 WMRemoveFromPLDictionary(scr->session_state, sApplications);
332 WMPutInPLDictionary(scr->session_state, sApplications, list);
333 WMReleasePropList(list);
335 wks = WMCreatePLString(scr->workspaces[scr->current_workspace]->name);
336 WMPutInPLDictionary(scr->session_state, sWorkspace, wks);
337 WMReleasePropList(wks);
339 WMFreeArray(wapp_list);
342 void wSessionClearState(WScreen * scr)
344 make_keys();
346 if (!scr->session_state)
347 return;
349 WMRemoveFromPLDictionary(scr->session_state, sApplications);
350 WMRemoveFromPLDictionary(scr->session_state, sWorkspace);
353 static pid_t execCommand(WScreen * scr, char *command, char *host)
355 pid_t pid;
356 char **argv;
357 int argc;
359 wtokensplit(command, &argv, &argc);
361 if (!argc) {
362 return 0;
365 if ((pid = fork()) == 0) {
366 char **args;
367 int i;
369 SetupEnvironment(scr);
371 args = malloc(sizeof(char *) * (argc + 1));
372 if (!args)
373 exit(111);
374 for (i = 0; i < argc; i++) {
375 args[i] = argv[i];
377 args[argc] = NULL;
378 execvp(argv[0], args);
379 exit(111);
381 while (argc > 0)
382 wfree(argv[--argc]);
383 wfree(argv);
384 return pid;
387 static WSavedState *getWindowState(WScreen * scr, WMPropList * win_state)
389 WSavedState *state = wmalloc(sizeof(WSavedState));
390 WMPropList *value;
391 char *tmp;
392 unsigned mask;
393 int i;
395 memset(state, 0, sizeof(WSavedState));
396 state->workspace = -1;
397 value = WMGetFromPLDictionary(win_state, sWorkspace);
398 if (value && WMIsPLString(value)) {
399 tmp = WMGetFromPLString(value);
400 if (sscanf(tmp, "%i", &state->workspace) != 1) {
401 state->workspace = -1;
402 for (i = 0; i < scr->workspace_count; i++) {
403 if (strcmp(scr->workspaces[i]->name, tmp) == 0) {
404 state->workspace = i;
405 break;
408 } else {
409 state->workspace--;
412 if ((value = WMGetFromPLDictionary(win_state, sShaded)) != NULL)
413 state->shaded = getBool(value);
414 if ((value = WMGetFromPLDictionary(win_state, sMiniaturized)) != NULL)
415 state->miniaturized = getBool(value);
416 if ((value = WMGetFromPLDictionary(win_state, sHidden)) != NULL)
417 state->hidden = getBool(value);
418 if ((value = WMGetFromPLDictionary(win_state, sShortcutMask)) != NULL) {
419 mask = getInt(value);
420 state->window_shortcuts = mask;
423 value = WMGetFromPLDictionary(win_state, sGeometry);
424 if (value && WMIsPLString(value)) {
425 if (!(sscanf(WMGetFromPLString(value), "%ix%i+%i+%i",
426 &state->w, &state->h, &state->x, &state->y) == 4 && (state->w > 0 && state->h > 0))) {
427 state->w = 0;
428 state->h = 0;
432 return state;
435 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
437 void wSessionRestoreState(WScreen * scr)
439 WSavedState *state;
440 char *instance, *class, *command, *host;
441 WMPropList *win_info, *apps, *cmd, *value;
442 pid_t pid;
443 int i, count;
444 WDock *dock;
445 WAppIcon *btn = NULL;
446 int j, n, found;
447 char *tmp;
449 make_keys();
451 if (!scr->session_state)
452 return;
454 WMPLSetCaseSensitive(True);
456 apps = WMGetFromPLDictionary(scr->session_state, sApplications);
457 if (!apps)
458 return;
460 count = WMGetPropListItemCount(apps);
461 if (count == 0)
462 return;
464 for (i = 0; i < count; i++) {
465 win_info = WMGetFromPLArray(apps, i);
467 cmd = WMGetFromPLDictionary(win_info, sCommand);
468 if (!cmd || !WMIsPLString(cmd) || !(command = WMGetFromPLString(cmd))) {
469 continue;
472 value = WMGetFromPLDictionary(win_info, sName);
473 if (!value)
474 continue;
476 ParseWindowName(value, &instance, &class, "session");
477 if (!instance && !class)
478 continue;
480 value = WMGetFromPLDictionary(win_info, sHost);
481 if (value && WMIsPLString(value))
482 host = WMGetFromPLString(value);
483 else
484 host = NULL;
486 state = getWindowState(scr, win_info);
488 dock = NULL;
489 value = WMGetFromPLDictionary(win_info, sDock);
490 if (value && WMIsPLString(value) && (tmp = WMGetFromPLString(value)) != NULL) {
491 if (sscanf(tmp, "%i", &n) != 1) {
492 if (!strcasecmp(tmp, "DOCK")) {
493 dock = scr->dock;
494 } else {
495 for (j = 0; j < scr->workspace_count; j++) {
496 if (strcmp(scr->workspaces[j]->name, tmp) == 0) {
497 dock = scr->workspaces[j]->clip;
498 break;
502 } else {
503 if (n == 0) {
504 dock = scr->dock;
505 } else if (n > 0 && n <= scr->workspace_count) {
506 dock = scr->workspaces[n - 1]->clip;
511 found = 0;
512 if (dock != NULL) {
513 for (j = 0; j < dock->max_icons; j++) {
514 btn = dock->icon_array[j];
515 if (btn && SAME(instance, btn->wm_instance) &&
516 SAME(class, btn->wm_class) && SAME(command, btn->command) && !btn->launching) {
517 found = 1;
518 break;
523 if (found) {
524 wDockLaunchWithState(dock, btn, state);
525 } else if ((pid = execCommand(scr, command, host)) > 0) {
526 wWindowAddSavedState(instance, class, command, pid, state);
527 } else {
528 wfree(state);
531 if (instance)
532 wfree(instance);
533 if (class)
534 wfree(class);
536 /* clean up */
537 WMPLSetCaseSensitive(False);
540 void wSessionRestoreLastWorkspace(WScreen * scr)
542 WMPropList *wks;
543 int w, i;
544 char *tmp;
546 make_keys();
548 if (!scr->session_state)
549 return;
551 WMPLSetCaseSensitive(True);
553 wks = WMGetFromPLDictionary(scr->session_state, sWorkspace);
554 if (!wks || !WMIsPLString(wks))
555 return;
557 tmp = WMGetFromPLString(wks);
559 /* clean up */
560 WMPLSetCaseSensitive(False);
562 if (sscanf(tmp, "%i", &w) != 1) {
563 w = -1;
564 for (i = 0; i < scr->workspace_count; i++) {
565 if (strcmp(scr->workspaces[i]->name, tmp) == 0) {
566 w = i;
567 break;
570 } else {
571 w--;
574 if (w != scr->current_workspace && w < scr->workspace_count) {
575 wWorkspaceChange(scr, w);
579 static void clearWaitingAckState(WScreen * scr)
581 WWindow *wwin;
582 WApplication *wapp;
584 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
585 wwin->flags.waiting_save_ack = 0;
586 if (wwin->main_window != None) {
587 wapp = wApplicationOf(wwin->main_window);
588 if (wapp)
589 wapp->main_window_desc->flags.waiting_save_ack = 0;
594 void wSessionSaveClients(WScreen * scr)
600 * With XSMP, this job is done by smproxy
602 void wSessionSendSaveYourself(WScreen * scr)
604 WWindow *wwin;
605 int count;
607 /* freeze client interaction with clients */
608 XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
609 XGrabPointer(dpy, scr->root_win, False, ButtonPressMask | ButtonReleaseMask,
610 GrabModeAsync, GrabModeAsync, scr->root_win, None, CurrentTime);
612 clearWaitingAckState(scr);
614 count = 0;
616 /* first send SAVE_YOURSELF for everybody */
617 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
618 WWindow *mainWin;
620 mainWin = wWindowFor(wwin->main_window);
622 if (mainWin) {
623 /* if the client is a multi-window client, only send message
624 * to the main window */
625 wwin = mainWin;
628 /* make sure the SAVE_YOURSELF flag is up-to-date */
629 PropGetProtocols(wwin->client_win, &wwin->protocols);
631 if (wwin->protocols.SAVE_YOURSELF) {
632 if (!wwin->flags.waiting_save_ack) {
633 wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
635 wwin->flags.waiting_save_ack = 1;
636 count++;
638 } else {
639 wwin->flags.waiting_save_ack = 0;
643 /* then wait for acknowledge */
644 while (count > 0) {
648 XUngrabPointer(dpy, CurrentTime);
649 XUngrabKeyboard(dpy, CurrentTime);
650 XFlush(dpy);
653 #ifdef XSMP_ENABLED
655 * With full session management support, the part of WMState
656 * that store client window state will become obsolete (maybe we can reuse
657 * the old code too),
658 * but we still need to store state info like the dock and workspaces.
659 * It is better to keep dock/wspace info in WMState because the user
660 * might want to keep the dock configuration while not wanting to
661 * resume a previously saved session.
662 * So, wmaker specific state info can be saved in
663 * ~/GNUstep/Library/WindowMaker/statename.state
664 * Its better to not put it in the defaults directory because:
665 * - its not a defaults file (having domain names like wmaker0089504baa
666 * in the defaults directory wouldn't be very neat)
667 * - this state file is not meant to be edited by users
669 * The old session code will become obsolete. When wmaker is
670 * compiled with R6 sm support compiled in, it'll be better to
671 * use a totally rewritten state saving code, but we can keep
672 * the current code for when XSMP_ENABLED is not compiled in.
674 * This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
675 * messages), but it'll be better.
677 * -readme
680 static char *getWindowRole(Window window)
682 XTextProperty prop;
683 static Atom atom = 0;
685 if (!atom)
686 atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
688 if (XGetTextProperty(dpy, window, &prop, atom)) {
689 if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
690 return prop.value;
693 return NULL;
698 * Saved Info:
700 * WM_WINDOW_ROLE
702 * WM_CLASS.instance
703 * WM_CLASS.class
704 * WM_NAME
705 * WM_COMMAND
707 * geometry
708 * state = (miniaturized, shaded, etc)
709 * attribute
710 * workspace #
711 * app state = (which dock, hidden)
712 * window shortcut #
715 static WMPropList *makeAppState(WWindow * wwin)
717 WApplication *wapp;
718 WMPropList *state;
719 WScreen *scr = wwin->screen_ptr;
721 state = WMCreatePLArray(NULL, NULL);
723 wapp = wApplicationOf(wwin->main_window);
725 if (wapp) {
726 if (wapp->app_icon && wapp->app_icon->dock) {
728 if (wapp->app_icon->dock == scr->dock) {
729 WMAddToPLArray(state, WMCreatePLString("Dock"));
730 } else {
731 int i;
733 for (i = 0; i < scr->workspace_count; i++)
734 if (scr->workspaces[i]->clip == wapp->app_icon->dock)
735 break;
737 assert(i < scr->workspace_count);
739 WMAddToPLArray(state, WMCreatePLString(scr->workspaces[i]->name));
743 WMAddToPLArray(state, WMCreatePLString(wapp->hidden ? "1" : "0"));
746 return state;
749 Bool wSessionGetStateFor(WWindow * wwin, WSessionData * state)
751 char *str;
752 WMPropList *slist;
753 WMPropList *elem;
754 WMPropList *value;
755 int index = 0;
757 index = 3;
759 /* geometry */
760 value = WMGetFromPLArray(slist, index++);
761 str = WMGetFromPLString(value);
763 sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
764 &state->width, &state->height, &state->user_changed_width, &state->user_changed_height);
766 /* state */
767 value = WMGetFromPLArray(slist, index++);
768 str = WMGetFromPLString(value);
770 sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded, &state->maximized);
772 /* attributes */
773 value = WMGetFromPLArray(slist, index++);
774 str = WMGetFromPLString(value);
776 getAttributeState(str, &state->mflags, &state->flags);
778 /* workspace */
779 value = WMGetFromPLArray(slist, index++);
780 str = WMGetFromPLString(value);
782 sscanf(str, "%i", &state->workspace);
784 /* app state (repeated for all windows of the app) */
785 value = WMGetFromPLArray(slist, index++);
786 str = WMGetFromPLString(value);
788 /* ???? */
790 /* shortcuts */
791 value = WMGetFromPLArray(slist, index++);
792 str = WMGetFromPLString(value);
794 sscanf(str, "%i", &state->shortcuts);
797 static WMPropList *makeAttributeState(WWindow * wwin)
799 unsigned int data1, data2;
800 char buffer[256];
802 #define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
803 ? (wwin)->user_flags.FLAG : -1)
805 snprintf(buffer, sizeof(buffer),
806 "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
807 W_FLAG(no_titlebar),
808 W_FLAG(no_resizable),
809 W_FLAG(no_closable),
810 W_FLAG(no_miniaturizable),
811 W_FLAG(no_resizebar), W_FLAG(no_close_button), W_FLAG(no_miniaturize_button),
813 W_FLAG(broken_close),
814 W_FLAG(kill_close),
816 W_FLAG(no_shadeable),
817 W_FLAG(omnipresent),
818 W_FLAG(skip_window_list),
819 W_FLAG(floating),
820 W_FLAG(sunken),
821 W_FLAG(no_bind_keys),
822 W_FLAG(no_bind_mouse),
823 W_FLAG(no_hide_others),
824 W_FLAG(no_appicon),
825 W_FLAG(dont_move_off),
826 W_FLAG(no_focusable),
827 W_FLAG(always_user_icon),
828 W_FLAG(start_miniaturized),
829 W_FLAG(start_hidden),
830 W_FLAG(start_maximized), W_FLAG(dont_save_session), W_FLAG(emulate_appicon));
832 return WMCreatePLString(buffer);
835 static void appendStringInArray(WMPropList * array, char *str)
837 WMPropList *val;
839 val = WMCreatePLString(str);
840 WMAddToPLArray(array, val);
841 WMReleasePropList(val);
844 static WMPropList *makeClientState(WWindow * wwin)
846 WMPropList *state;
847 WMPropList *tmp;
848 char *str;
849 char buffer[512];
850 int i;
851 unsigned shortcuts;
853 state = WMCreatePLArray(NULL, NULL);
855 /* WM_WINDOW_ROLE */
856 str = getWindowRole(wwin->client_win);
857 if (!str)
858 appendStringInArray(state, "");
859 else {
860 appendStringInArray(state, str);
861 XFree(str);
864 /* WM_CLASS.instance */
865 appendStringInArray(state, wwin->wm_instance);
867 /* WM_CLASS.class */
868 appendStringInArray(state, wwin->wm_class);
870 /* WM_NAME */
871 if (wwin->flags.wm_name_changed)
872 appendStringInArray(state, "");
873 else
874 appendStringInArray(state, wwin->frame->name);
876 /* geometry */
877 snprintf(buffer, sizeof(buffer), "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
878 wwin->client.width, wwin->client.height,
879 wwin->flags.user_changed_width, wwin->flags.user_changed_height);
880 appendStringInArray(state, buffer);
882 /* state */
883 snprintf(buffer, sizeof(buffer), "%i %i %i", wwin->flags.miniaturized,
884 wwin->flags.shaded, wwin->flags.maximized);
885 appendStringInArray(state, buffer);
887 /* attributes */
888 tmp = makeAttributeState(wwin);
889 WMAddToPLArray(state, tmp);
890 WMReleasePropList(tmp);
892 /* workspace */
893 snprintf(buffer, sizeof(buffer), "%i", wwin->frame->workspace);
894 appendStringInArray(state, buffer);
896 /* app state (repeated for all windows of the app) */
897 tmp = makeAppState(wwin);
898 WMAddToPLArray(state, tmp);
899 WMReleasePropList(tmp);
901 /* shortcuts */
902 shortcuts = 0;
903 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
904 if (scr->shortcutWindow[i] == wwin) {
905 shortcuts |= 1 << i;
908 snprintf(buffer, sizeof(buffer), "%ui", shortcuts);
909 appendStringInArray(tmp, buffer);
911 return state;
914 static void smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
916 SmProp props[4];
917 SmPropValue prop1val, prop2val, prop3val, prop4val;
918 char **argv = (char **)client_data;
919 int argc;
920 int i, j;
921 Bool ok = False;
922 char *statefile = NULL;
923 char *prefix;
924 Bool gsPrefix = False;
925 char *discardCmd = NULL;
926 time_t t;
927 WMPropList *state, *plState;
928 int len;
930 #ifdef DEBUG1
931 puts("received SaveYourselfPhase2 SM message");
932 #endif
934 /* save session state */
936 /* the file that will contain the state */
937 prefix = getenv("SM_SAVE_DIR");
938 if (!prefix) {
939 prefix = wusergnusteppath();
940 if (prefix)
941 gsPrefix = True;
943 if (!prefix) {
944 prefix = getenv("HOME");
946 if (!prefix)
947 prefix = ".";
949 len = strlen(prefix) + 64;
950 statefile = malloc(len);
951 if (!statefile) {
952 wwarning(_("out of memory while saving session state"));
953 goto fail;
956 t = time();
957 i = 0;
958 do {
959 if (gsPrefix)
960 snprintf(statefile, len, "%s/Library/WindowMaker/wmaker.%l%i.state", prefix, t, i);
961 else
962 snprintf(statefile, len, "%s/wmaker.%l%i.state", prefix, t, i);
963 i++;
964 } while (access(F_OK, statefile) != -1);
966 /* save the states of all windows we're managing */
967 state = WMCreatePLArray(NULL, NULL);
970 * Format:
972 * state_file ::= dictionary with version_info ; state
973 * version_info ::= 'version' = '1';
974 * state ::= 'state' = array of screen_info
975 * screen_info ::= array of (screen number, window_info, window_info, ...)
976 * window_info ::=
978 for (i = 0; i < wScreenCount; i++) {
979 WScreen *scr;
980 WWindow *wwin;
981 char buf[32];
982 WMPropList *pscreen;
984 scr = wScreenWithNumber(i);
986 snprintf(buf, sizeof(buf), "%i", scr->screen);
987 pscreen = WMCreatePLArray(WMCreatePLString(buf), NULL);
989 wwin = scr->focused_window;
990 while (wwin) {
991 WMPropList *pwindow;
993 pwindow = makeClientState(wwin);
994 WMAddToPLArray(pscreen, pwindow);
996 wwin = wwin->prev;
999 WMAddToPLArray(state, pscreen);
1002 plState = WMCreatePLDictionary(WMCreatePLString("Version"),
1003 WMCreatePLString("1.0"), WMCreatePLString("Screens"), state, NULL);
1005 WMWritePropListToFile(plState, statefile, False);
1007 WMReleasePropList(plState);
1009 /* set the remaining properties that we didn't set at
1010 * startup time */
1012 for (argc = 0, i = 0; argv[i] != NULL; i++) {
1013 if (strcmp(argv[i], "-clientid") == 0 || strcmp(argv[i], "-restore") == 0) {
1014 i++;
1015 } else {
1016 argc++;
1020 prop[0].name = SmRestartCommand;
1021 prop[0].type = SmLISTofARRAY8;
1022 prop[0].vals = malloc(sizeof(SmPropValue) * (argc + 4));
1023 prop[0].num_vals = argc + 4;
1025 prop[1].name = SmCloneCommand;
1026 prop[1].type = SmLISTofARRAY8;
1027 prop[1].vals = malloc(sizeof(SmPropValue) * (argc));
1028 prop[1].num_vals = argc;
1030 if (!prop[0].vals || !prop[1].vals) {
1031 wwarning(_("end of memory while saving session state"));
1032 goto fail;
1035 for (j = 0, i = 0; i < argc + 4; i++) {
1036 if (strcmp(argv[i], "-clientid") == 0 || strcmp(argv[i], "-restore") == 0) {
1037 i++;
1038 } else {
1039 prop[0].vals[j].value = argv[i];
1040 prop[0].vals[j].length = strlen(argv[i]);
1041 prop[1].vals[j].value = argv[i];
1042 prop[1].vals[j].length = strlen(argv[i]);
1043 j++;
1046 prop[0].vals[j].value = "-clientid";
1047 prop[0].vals[j].length = 9;
1048 j++;
1049 prop[0].vals[j].value = sClientID;
1050 prop[0].vals[j].length = strlen(sClientID);
1051 j++;
1052 prop[0].vals[j].value = "-restore";
1053 prop[0].vals[j].length = 11;
1054 j++;
1055 prop[0].vals[j].value = statefile;
1056 prop[0].vals[j].length = strlen(statefile);
1059 int len = strlen(statefile) + 8;
1061 discardCmd = malloc(len);
1062 if (!discardCmd)
1063 goto fail;
1064 snprintf(discardCmd, len, "rm %s", statefile);
1066 prop[2].name = SmDiscardCommand;
1067 prop[2].type = SmARRAY8;
1068 prop[2].vals[0] = discardCmd;
1069 prop[2].num_vals = 1;
1071 SmcSetProperties(sSMCConn, 3, prop);
1073 ok = True;
1074 fail:
1075 SmcSaveYourselfDone(smc_conn, ok);
1077 if (prop[0].vals)
1078 wfree(prop[0].vals);
1079 if (prop[1].vals)
1080 wfree(prop[1].vals);
1081 if (discardCmd)
1082 wfree(discardCmd);
1084 if (!ok) {
1085 remove(statefile);
1087 if (statefile)
1088 wfree(statefile);
1091 static void
1092 smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
1093 Bool shutdown, int interact_style, Bool fast)
1095 #ifdef DEBUG1
1096 puts("received SaveYourself SM message");
1097 #endif
1099 if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc, client_data)) {
1101 SmcSaveYourselfDone(smc_conn, False);
1102 sWaitingPhase2 = False;
1103 } else {
1104 #ifdef DEBUG1
1105 puts("successfull request of SYS phase 2");
1106 #endif
1107 sWaitingPhase2 = True;
1111 static void smDieProc(SmcConn smc_conn, SmPointer client_data)
1113 #ifdef DEBUG1
1114 puts("received Die SM message");
1115 #endif
1117 wSessionDisconnectManager();
1119 Shutdown(WSExitMode, True);
1122 static void smSaveCompleteProc(SmcConn smc_conn)
1124 /* it means that we can resume doing things that can change our state */
1125 #ifdef DEBUG1
1126 puts("received SaveComplete SM message");
1127 #endif
1130 static void smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
1132 if (sWaitingPhase2) {
1134 sWaitingPhase2 = False;
1136 SmcSaveYourselfDone(smc_conn, False);
1140 static void iceMessageProc(int fd, int mask, void *clientData)
1142 IceConn iceConn = (IceConn) clientData;
1144 IceProcessMessages(iceConn, NULL, NULL);
1147 static void iceIOErrorHandler(IceConnection ice_conn)
1149 /* This is not fatal but can mean the session manager exited.
1150 * If the session manager exited normally we would get a
1151 * Die message, so this probably means an abnormal exit.
1152 * If the sm was the last client of session, then we'll die
1153 * anyway, otherwise we can continue doing our stuff.
1155 wwarning(_("connection to the session manager was lost"));
1156 wSessionDisconnectManager();
1159 void wSessionConnectManager(char **argv, int argc)
1161 IceConn iceConn;
1162 char *previous_id = NULL;
1163 char buffer[256];
1164 SmcCallbacks callbacks;
1165 unsigned long mask;
1166 char uid[32];
1167 char pid[32];
1168 SmProp props[4];
1169 SmPropValue prop1val, prop2val, prop3val, prop4val;
1170 char restartStyle;
1171 int i;
1173 mask = SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
1175 callbacks.save_yourself.callback = smSaveYourselfProc;
1176 callbacks.save_yourself.client_data = argv;
1178 callbacks.die.callback = smDieProc;
1179 callbacks.die.client_data = NULL;
1181 callbacks.save_complete.callback = smSaveCompleteProc;
1182 callbacks.save_complete.client_data = NULL;
1184 callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
1185 callbacks.shutdown_cancelled.client_data = NULL;
1187 for (i = 0; i < argc; i++) {
1188 if (strcmp(argv[i], "-clientid") == 0) {
1189 previous_id = argv[i + 1];
1190 break;
1194 /* connect to the session manager */
1195 sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
1196 mask, &callbacks, previous_id, &sClientID, 255, buffer);
1197 if (!sSMCConn) {
1198 return;
1200 #ifdef DEBUG1
1201 puts("connected to the session manager");
1202 #endif
1204 /* IceSetIOErrorHandler(iceIOErrorHandler); */
1206 /* check for session manager clients */
1207 iceConn = SmcGetIceConnection(smcConn);
1209 if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
1210 wsyserror("error setting close-on-exec flag for ICE connection");
1213 sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn), WIReadMask, iceMessageProc, iceConn);
1215 /* setup information about ourselves */
1217 /* program name */
1218 prop1val.value = argv[0];
1219 prop1val.length = strlen(argv[0]);
1220 prop[0].name = SmProgram;
1221 prop[0].type = SmARRAY8;
1222 prop[0].num_vals = 1;
1223 prop[0].vals = &prop1val;
1225 /* The XSMP doc from X11R6.1 says it contains the user name,
1226 * but every client implementation I saw places the uid # */
1227 snprintf(uid, sizeof(uid), "%i", getuid());
1228 prop2val.value = uid;
1229 prop2val.length = strlen(uid);
1230 prop[1].name = SmUserID;
1231 prop[1].type = SmARRAY8;
1232 prop[1].num_vals = 1;
1233 prop[1].vals = &prop2val;
1235 /* Restart style. We should restart only if we were running when
1236 * the previous session finished. */
1237 restartStyle = SmRestartIfRunning;
1238 prop3val.value = &restartStyle;
1239 prop3val.length = 1;
1240 prop[2].name = SmRestartStyleHint;
1241 prop[2].type = SmCARD8;
1242 prop[2].num_vals = 1;
1243 prop[2].vals = &prop3val;
1245 /* Our PID. Not required but might be usefull */
1246 snprintf(pid, sizeof(pid), "%i", getpid());
1247 prop4val.value = pid;
1248 prop4val.length = strlen(pid);
1249 prop[3].name = SmProcessID;
1250 prop[3].type = SmARRAY8;
1251 prop[3].num_vals = 1;
1252 prop[3].vals = &prop4val;
1254 /* we'll set the rest of the hints later */
1256 SmcSetProperties(sSMCConn, 4, props);
1260 void wSessionDisconnectManager(void)
1262 if (sSMCConn) {
1263 WMDeleteInputHandler(sSMInputHandler);
1264 sSMInputHandler = NULL;
1266 SmcCloseConnection(sSMCConn, 0, NULL);
1267 sSMCConn = NULL;
1271 void wSessionRequestShutdown(void)
1273 /* request a shutdown to the session manager */
1274 if (sSMCConn)
1275 SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny, False, True);
1278 Bool wSessionIsManaged(void)
1280 return sSMCConn != NULL;
1283 #endif /* !XSMP_ENABLED */