changed indentation to use spaces only
[wmaker-crm.git] / src / session.c
blobcbb38a7b87dc1208359d0bba24602267607df201
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.
27 * If defined(XSMP_ENABLED) and session manager is running then
28 * do normal stuff
29 * else
30 * do pre-R6 session management stuff (save window state and relaunch)
32 * When doing a checkpoint:
34 * = Without XSMP
35 * Open "Stop"/status Dialog
36 * Send SAVE_YOURSELF to clients and wait for reply
37 * Save restart info
38 * Save state of clients
40 * = With XSMP
41 * Send checkpoint request to sm
43 * When exiting:
44 * -------------
46 * = Without XSMP
48 * Open "Exit Now"/status Dialog
49 * Send SAVE_YOURSELF to clients and wait for reply
50 * Save restart info
51 * Save state of clients
52 * Send DELETE to all clients
53 * When no more clients are left or user hit "Exit Now", exit
55 * = With XSMP
57 * Send Shutdown request to session manager
58 * if SaveYourself message received, save state of clients
59 * if the Die message is received, exit.
62 #include "wconfig.h"
64 #include <X11/Xlib.h>
65 #include <X11/Xutil.h>
67 #ifdef XSMP_ENABLED
68 #include <X11/SM/SMlib.h>
69 #endif
71 #include <stdlib.h>
72 #include <stdio.h>
73 #include <string.h>
74 #include <unistd.h>
75 #include <time.h>
78 #include "WindowMaker.h"
79 #include "screen.h"
80 #include "window.h"
81 #include "client.h"
82 #include "session.h"
83 #include "wcore.h"
84 #include "framewin.h"
85 #include "workspace.h"
86 #include "funcs.h"
87 #include "properties.h"
88 #include "application.h"
89 #include "appicon.h"
92 #include "dock.h"
95 #include <WINGs/WUtil.h>
97 /** Global **/
99 extern Atom _XA_WM_SAVE_YOURSELF;
101 extern Time LastTimestamp;
103 #ifdef XSMP_ENABLED
105 extern int wScreenCount;
107 /* requested for SaveYourselfPhase2 */
108 static Bool sWaitingPhase2 = False;
110 static SmcConn sSMCConn = NULL;
112 static WMHandlerID sSMInputHandler = NULL;
114 /* our SM client ID */
115 static char *sClientID = NULL;
116 #endif
119 static WMPropList *sApplications = NULL;
120 static WMPropList *sCommand;
121 static WMPropList *sName;
122 static WMPropList *sHost;
123 static WMPropList *sWorkspace;
124 static WMPropList *sShaded;
125 static WMPropList *sMiniaturized;
126 static WMPropList *sHidden;
127 static WMPropList *sGeometry;
128 static WMPropList *sShortcutMask;
130 static WMPropList *sDock;
132 static WMPropList *sYes, *sNo;
135 static void
136 make_keys()
138 if (sApplications!=NULL)
139 return;
141 sApplications = WMCreatePLString("Applications");
142 sCommand = WMCreatePLString("Command");
143 sName = WMCreatePLString("Name");
144 sHost = WMCreatePLString("Host");
145 sWorkspace = WMCreatePLString("Workspace");
146 sShaded = WMCreatePLString("Shaded");
147 sMiniaturized = WMCreatePLString("Miniaturized");
148 sHidden = WMCreatePLString("Hidden");
149 sGeometry = WMCreatePLString("Geometry");
150 sDock = WMCreatePLString("Dock");
151 sShortcutMask = WMCreatePLString("ShortcutMask");
153 sYes = WMCreatePLString("Yes");
154 sNo = WMCreatePLString("No");
159 static int
160 getBool(WMPropList *value)
162 char *val;
164 if (!WMIsPLString(value)) {
165 return 0;
167 if (!(val = WMGetFromPLString(value))) {
168 return 0;
171 if ((val[1]=='\0' && (val[0]=='y' || val[0]=='Y'))
172 || strcasecmp(val, "YES")==0) {
174 return 1;
175 } else if ((val[1]=='\0' && (val[0]=='n' || val[0]=='N'))
176 || strcasecmp(val, "NO")==0) {
177 return 0;
178 } else {
179 int i;
180 if (sscanf(val, "%i", &i)==1) {
181 return (i!=0);
182 } else {
183 wwarning(_("can't convert \"%s\" to boolean"), val);
184 return 0;
190 static unsigned
191 getInt(WMPropList *value)
193 char *val;
194 unsigned n;
196 if (!WMIsPLString(value))
197 return 0;
198 val = WMGetFromPLString(value);
199 if (!val)
200 return 0;
201 if (sscanf(val, "%u", &n) != 1)
202 return 0;
204 return n;
209 static WMPropList*
210 makeWindowState(WWindow *wwin, WApplication *wapp)
212 WScreen *scr = wwin->screen_ptr;
213 Window win;
214 int i;
215 unsigned mask;
216 char *class, *instance, *command=NULL, buffer[512];
217 WMPropList *win_state, *cmd, *name, *workspace;
218 WMPropList *shaded, *miniaturized, *hidden, *geometry;
219 WMPropList *dock, *shortcut;
221 if (wwin->orig_main_window!=None && wwin->orig_main_window!=wwin->client_win)
222 win = wwin->orig_main_window;
223 else
224 win = wwin->client_win;
226 command = GetCommandForWindow(win);
227 if (!command)
228 return NULL;
230 if (PropGetWMClass(win, &class, &instance)) {
231 if (class && instance)
232 snprintf(buffer, sizeof(buffer), "%s.%s", instance, class);
233 else if (instance)
234 snprintf(buffer, sizeof(buffer), "%s", instance);
235 else if (class)
236 snprintf(buffer, sizeof(buffer), ".%s", class);
237 else
238 snprintf(buffer, sizeof(buffer), ".");
240 name = WMCreatePLString(buffer);
241 cmd = WMCreatePLString(command);
242 /*sprintf(buffer, "%d", wwin->frame->workspace+1);
243 workspace = WMCreatePLString(buffer);*/
244 workspace = WMCreatePLString(scr->workspaces[wwin->frame->workspace]->name);
245 shaded = wwin->flags.shaded ? sYes : sNo;
246 miniaturized = wwin->flags.miniaturized ? sYes : sNo;
247 hidden = wwin->flags.hidden ? sYes : sNo;
248 snprintf(buffer, sizeof(buffer), "%ix%i+%i+%i",
249 wwin->client.width, wwin->client.height,
250 wwin->frame_x, wwin->frame_y);
251 geometry = WMCreatePLString(buffer);
253 for (mask = 0, i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
254 if (scr->shortcutWindows[i] != NULL &&
255 WMGetFirstInArray(scr->shortcutWindows[i], wwin) != WANotFound) {
256 mask |= 1<<i;
260 snprintf(buffer, sizeof(buffer), "%u", mask);
261 shortcut = WMCreatePLString(buffer);
263 win_state = WMCreatePLDictionary(sName, name,
264 sCommand, cmd,
265 sWorkspace, workspace,
266 sShaded, shaded,
267 sMiniaturized, miniaturized,
268 sHidden, hidden,
269 sShortcutMask, shortcut,
270 sGeometry, geometry,
271 NULL);
273 WMReleasePropList(name);
274 WMReleasePropList(cmd);
275 WMReleasePropList(workspace);
276 WMReleasePropList(geometry);
277 WMReleasePropList(shortcut);
278 if (wapp && wapp->app_icon && wapp->app_icon->dock) {
279 int i;
280 char *name;
281 if (wapp->app_icon->dock == scr->dock) {
282 name="Dock";
283 } else {
284 for(i=0; i<scr->workspace_count; i++)
285 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
286 break;
287 assert(i < scr->workspace_count);
288 /*n = i+1;*/
289 name = scr->workspaces[i]->name;
291 dock = WMCreatePLString(name);
292 WMPutInPLDictionary(win_state, sDock, dock);
293 WMReleasePropList(dock);
295 } else {
296 win_state = NULL;
299 if (instance) XFree(instance);
300 if (class) XFree(class);
301 if (command) wfree(command);
303 return win_state;
307 void
308 wSessionSaveState(WScreen *scr)
310 WWindow *wwin = scr->focused_window;
311 WMPropList *win_info, *wks;
312 WMPropList *list=NULL;
313 WMArray *wapp_list=NULL;
316 make_keys();
318 if (!scr->session_state) {
319 scr->session_state = WMCreatePLDictionary(NULL, NULL);
320 if (!scr->session_state)
321 return;
324 list = WMCreatePLArray(NULL);
326 wapp_list = WMCreateArray(16);
328 while (wwin) {
329 WApplication *wapp=wApplicationOf(wwin->main_window);
330 Window appId = wwin->orig_main_window;
332 if ((wwin->transient_for==None
333 || wwin->transient_for==wwin->screen_ptr->root_win)
334 && WMGetFirstInArray(wapp_list, (void*)appId)==WANotFound
335 && !WFLAGP(wwin, dont_save_session)) {
336 /* A entry for this application was not yet saved. Save one. */
337 if ((win_info = makeWindowState(wwin, wapp))!=NULL) {
338 WMAddToPLArray(list, win_info);
339 WMReleasePropList(win_info);
340 /* If we were succesful in saving the info for this window
341 * add the application the window belongs to, to the
342 * application list, so no multiple entries for the same
343 * application are saved.
345 WMAddToArray(wapp_list, (void*)appId);
348 wwin = wwin->prev;
350 WMRemoveFromPLDictionary(scr->session_state, sApplications);
351 WMPutInPLDictionary(scr->session_state, sApplications, list);
352 WMReleasePropList(list);
354 wks = WMCreatePLString(scr->workspaces[scr->current_workspace]->name);
355 WMPutInPLDictionary(scr->session_state, sWorkspace, wks);
356 WMReleasePropList(wks);
358 WMFreeArray(wapp_list);
362 void
363 wSessionClearState(WScreen *scr)
365 make_keys();
367 if (!scr->session_state)
368 return;
370 WMRemoveFromPLDictionary(scr->session_state, sApplications);
371 WMRemoveFromPLDictionary(scr->session_state, sWorkspace);
375 static pid_t
376 execCommand(WScreen *scr, char *command, char *host)
378 pid_t pid;
379 char **argv;
380 int argc;
382 wtokensplit(command, &argv, &argc);
384 if (argv==NULL) {
385 return 0;
388 if ((pid=fork())==0) {
389 char **args;
390 int i;
392 SetupEnvironment(scr);
394 args = malloc(sizeof(char*)*(argc+1));
395 if (!args)
396 exit(111);
397 for (i=0; i<argc; i++) {
398 args[i] = argv[i];
400 args[argc] = NULL;
401 execvp(argv[0], args);
402 exit(111);
404 while (argc > 0)
405 wfree(argv[--argc]);
406 wfree(argv);
407 return pid;
411 static WSavedState*
412 getWindowState(WScreen *scr, WMPropList *win_state)
414 WSavedState *state = wmalloc(sizeof(WSavedState));
415 WMPropList *value;
416 char *tmp;
417 unsigned mask;
418 int i;
420 memset(state, 0, sizeof(WSavedState));
421 state->workspace = -1;
422 value = WMGetFromPLDictionary(win_state, sWorkspace);
423 if (value && WMIsPLString(value)) {
424 tmp = WMGetFromPLString(value);
425 if (sscanf(tmp, "%i", &state->workspace)!=1) {
426 state->workspace = -1;
427 for (i=0; i < scr->workspace_count; i++) {
428 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
429 state->workspace = i;
430 break;
433 } else {
434 state->workspace--;
437 if ((value = WMGetFromPLDictionary(win_state, sShaded))!=NULL)
438 state->shaded = getBool(value);
439 if ((value = WMGetFromPLDictionary(win_state, sMiniaturized))!=NULL)
440 state->miniaturized = getBool(value);
441 if ((value = WMGetFromPLDictionary(win_state, sHidden))!=NULL)
442 state->hidden = getBool(value);
443 if ((value = WMGetFromPLDictionary(win_state, sShortcutMask))!=NULL) {
444 mask = getInt(value);
445 state->window_shortcuts = mask;
448 value = WMGetFromPLDictionary(win_state, sGeometry);
449 if (value && WMIsPLString(value)) {
450 if (!(sscanf(WMGetFromPLString(value), "%ix%i+%i+%i",
451 &state->w, &state->h, &state->x, &state->y)==4 &&
452 (state->w>0 && state->h>0))) {
453 state->w = 0;
454 state->h = 0;
458 return state;
462 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
464 void
465 wSessionRestoreState(WScreen *scr)
467 WSavedState *state;
468 char *instance, *class, *command, *host;
469 WMPropList *win_info, *apps, *cmd, *value;
470 pid_t pid;
471 int i, count;
472 WDock *dock;
473 WAppIcon *btn=NULL;
474 int j, n, found;
475 char *tmp;
477 make_keys();
479 if (!scr->session_state)
480 return;
482 WMPLSetCaseSensitive(True);
484 apps = WMGetFromPLDictionary(scr->session_state, sApplications);
485 if (!apps)
486 return;
488 count = WMGetPropListItemCount(apps);
489 if (count==0)
490 return;
492 for (i=0; i<count; i++) {
493 win_info = WMGetFromPLArray(apps, i);
495 cmd = WMGetFromPLDictionary(win_info, sCommand);
496 if (!cmd || !WMIsPLString(cmd) || !(command = WMGetFromPLString(cmd))) {
497 continue;
500 value = WMGetFromPLDictionary(win_info, sName);
501 if (!value)
502 continue;
504 ParseWindowName(value, &instance, &class, "session");
505 if (!instance && !class)
506 continue;
508 value = WMGetFromPLDictionary(win_info, sHost);
509 if (value && WMIsPLString(value))
510 host = WMGetFromPLString(value);
511 else
512 host = NULL;
514 state = getWindowState(scr, win_info);
516 dock = NULL;
517 value = WMGetFromPLDictionary(win_info, sDock);
518 if (value && WMIsPLString(value) && (tmp = WMGetFromPLString(value))!=NULL) {
519 if (sscanf(tmp, "%i", &n)!=1) {
520 if (!strcasecmp(tmp, "DOCK")) {
521 dock = scr->dock;
522 } else {
523 for (j=0; j < scr->workspace_count; j++) {
524 if (strcmp(scr->workspaces[j]->name, tmp)==0) {
525 dock = scr->workspaces[j]->clip;
526 break;
530 } else {
531 if (n == 0) {
532 dock = scr->dock;
533 } else if (n>0 && n<=scr->workspace_count) {
534 dock = scr->workspaces[n-1]->clip;
539 found = 0;
540 if (dock!=NULL) {
541 for (j=0; j<dock->max_icons; j++) {
542 btn = dock->icon_array[j];
543 if (btn && SAME(instance, btn->wm_instance) &&
544 SAME(class, btn->wm_class) &&
545 SAME(command, btn->command) &&
546 !btn->launching) {
547 found = 1;
548 break;
553 if (found) {
554 wDockLaunchWithState(dock, btn, state);
555 } else if ((pid = execCommand(scr, command, host)) > 0) {
556 wWindowAddSavedState(instance, class, command, pid, state);
557 } else {
558 wfree(state);
561 if (instance) wfree(instance);
562 if (class) wfree(class);
564 /* clean up */
565 WMPLSetCaseSensitive(False);
569 void
570 wSessionRestoreLastWorkspace(WScreen *scr)
572 WMPropList *wks;
573 int w, i;
574 char *tmp;
576 make_keys();
578 if (!scr->session_state)
579 return;
581 WMPLSetCaseSensitive(True);
583 wks = WMGetFromPLDictionary(scr->session_state, sWorkspace);
584 if (!wks || !WMIsPLString(wks))
585 return;
587 tmp = WMGetFromPLString(wks);
589 /* clean up */
590 WMPLSetCaseSensitive(False);
592 if (sscanf(tmp, "%i", &w)!=1) {
593 w = -1;
594 for (i=0; i < scr->workspace_count; i++) {
595 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
596 w = i;
597 break;
600 } else {
601 w--;
604 if (w!=scr->current_workspace && w<scr->workspace_count) {
605 wWorkspaceChange(scr, w);
610 static void
611 clearWaitingAckState(WScreen *scr)
613 WWindow *wwin;
614 WApplication *wapp;
616 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
617 wwin->flags.waiting_save_ack = 0;
618 if (wwin->main_window != None) {
619 wapp = wApplicationOf(wwin->main_window);
620 if (wapp)
621 wapp->main_window_desc->flags.waiting_save_ack = 0;
627 void
628 wSessionSaveClients(WScreen *scr)
635 * With XSMP, this job is done by smproxy
637 void
638 wSessionSendSaveYourself(WScreen *scr)
640 WWindow *wwin;
641 int count;
643 /* freeze client interaction with clients */
644 XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync,
645 CurrentTime);
646 XGrabPointer(dpy, scr->root_win, False, ButtonPressMask|ButtonReleaseMask,
647 GrabModeAsync, GrabModeAsync, scr->root_win, None,
648 CurrentTime);
650 clearWaitingAckState(scr);
652 count = 0;
654 /* first send SAVE_YOURSELF for everybody */
655 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
656 WWindow *mainWin;
658 mainWin = wWindowFor(wwin->main_window);
660 if (mainWin) {
661 /* if the client is a multi-window client, only send message
662 * to the main window */
663 wwin = mainWin;
666 /* make sure the SAVE_YOURSELF flag is up-to-date */
667 PropGetProtocols(wwin->client_win, &wwin->protocols);
669 if (wwin->protocols.SAVE_YOURSELF) {
670 if (!wwin->flags.waiting_save_ack) {
671 wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
673 wwin->flags.waiting_save_ack = 1;
674 count++;
676 } else {
677 wwin->flags.waiting_save_ack = 0;
681 /* then wait for acknowledge */
682 while (count > 0) {
686 XUngrabPointer(dpy, CurrentTime);
687 XUngrabKeyboard(dpy, CurrentTime);
688 XFlush(dpy);
692 #ifdef XSMP_ENABLED
694 * With full session management support, the part of WMState
695 * that store client window state will become obsolete (maybe we can reuse
696 * the old code too),
697 * but we still need to store state info like the dock and workspaces.
698 * It is better to keep dock/wspace info in WMState because the user
699 * might want to keep the dock configuration while not wanting to
700 * resume a previously saved session.
701 * So, wmaker specific state info can be saved in
702 * ~/GNUstep/.AppInfo/WindowMaker/statename.state
703 * Its better to not put it in the defaults directory because:
704 * - its not a defaults file (having domain names like wmaker0089504baa
705 * in the defaults directory wouldn't be very neat)
706 * - this state file is not meant to be edited by users
708 * The old session code will become obsolete. When wmaker is
709 * compiled with R6 sm support compiled in, it'll be better to
710 * use a totally rewritten state saving code, but we can keep
711 * the current code for when XSMP_ENABLED is not compiled in.
713 * This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
714 * messages), but it'll be better.
716 * -readme
720 static char*
721 getWindowRole(Window window)
723 XTextProperty prop;
724 static Atom atom = 0;
726 if (!atom)
727 atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
729 if (XGetTextProperty(dpy, window, &prop, atom)) {
730 if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
731 return prop.value;
734 return NULL;
740 * Saved Info:
742 * WM_WINDOW_ROLE
744 * WM_CLASS.instance
745 * WM_CLASS.class
746 * WM_NAME
747 * WM_COMMAND
749 * geometry
750 * state = (miniaturized, shaded, etc)
751 * attribute
752 * workspace #
753 * app state = (which dock, hidden)
754 * window shortcut #
757 static WMPropList*
758 makeAppState(WWindow *wwin)
760 WApplication *wapp;
761 WMPropList *state;
762 WScreen *scr = wwin->screen_ptr;
764 state = WMCreatePLArray(NULL, NULL);
766 wapp = wApplicationOf(wwin->main_window);
768 if (wapp) {
769 if (wapp->app_icon && wapp->app_icon->dock) {
771 if (wapp->app_icon->dock == scr->dock) {
772 WMAddToPLArray(state, WMCreatePLString("Dock"));
773 } else {
774 int i;
776 for(i=0; i<scr->workspace_count; i++)
777 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
778 break;
780 assert(i < scr->workspace_count);
782 WMAddToPLArray(state,
783 WMCreatePLString(scr->workspaces[i]->name));
787 WMAddToPLArray(state, WMCreatePLString(wapp->hidden ? "1" : "0"));
790 return state;
795 Bool
796 wSessionGetStateFor(WWindow *wwin, WSessionData *state)
798 char *str;
799 WMPropList *slist;
800 WMPropList *elem;
801 WMPropList *value;
802 int index = 0;
804 index = 3;
806 /* geometry */
807 value = WMGetFromPLArray(slist, index++);
808 str = WMGetFromPLString(value);
810 sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
811 &state->width, &state->height,
812 &state->user_changed_width, &state->user_changed_height);
815 /* state */
816 value = WMGetFromPLArray(slist, index++);
817 str = WMGetFromPLString(value);
819 sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded,
820 &state->maximized);
823 /* attributes */
824 value = WMGetFromPLArray(slist, index++);
825 str = WMGetFromPLString(value);
827 getAttributeState(str, &state->mflags, &state->flags);
830 /* workspace */
831 value = WMGetFromPLArray(slist, index++);
832 str = WMGetFromPLString(value);
834 sscanf(str, "%i", &state->workspace);
837 /* app state (repeated for all windows of the app) */
838 value = WMGetFromPLArray(slist, index++);
839 str = WMGetFromPLString(value);
841 /* ???? */
843 /* shortcuts */
844 value = WMGetFromPLArray(slist, index++);
845 str = WMGetFromPLString(value);
847 sscanf(str, "%i", &state->shortcuts);
852 static WMPropList*
853 makeAttributeState(WWindow *wwin)
855 unsigned int data1, data2;
856 char buffer[256];
858 #define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
859 ? (wwin)->user_flags.FLAG : -1)
861 snprintf(buffer, sizeof(buffer),
862 "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
863 W_FLAG(no_titlebar),
864 W_FLAG(no_resizable),
865 W_FLAG(no_closable),
866 W_FLAG(no_miniaturizable),
867 W_FLAG(no_resizebar),
868 W_FLAG(no_close_button),
869 W_FLAG(no_miniaturize_button),
871 W_FLAG(broken_close),
872 W_FLAG(kill_close),
874 W_FLAG(no_shadeable),
875 W_FLAG(omnipresent),
876 W_FLAG(skip_window_list),
877 W_FLAG(floating),
878 W_FLAG(sunken),
879 W_FLAG(no_bind_keys),
880 W_FLAG(no_bind_mouse),
881 W_FLAG(no_hide_others),
882 W_FLAG(no_appicon),
883 W_FLAG(dont_move_off),
884 W_FLAG(no_focusable),
885 W_FLAG(always_user_icon),
886 W_FLAG(start_miniaturized),
887 W_FLAG(start_hidden),
888 W_FLAG(start_maximized),
889 W_FLAG(dont_save_session),
890 W_FLAG(emulate_appicon));
892 return WMCreatePLString(buffer);
896 static void
897 appendStringInArray(WMPropList *array, char *str)
899 WMPropList *val;
901 val = WMCreatePLString(str);
902 WMAddToPLArray(array, val);
903 WMReleasePropList(val);
907 static WMPropList*
908 makeClientState(WWindow *wwin)
910 WMPropList *state;
911 WMPropList *tmp;
912 char *str;
913 char buffer[512];
914 int i;
915 unsigned shortcuts;
917 state = WMCreatePLArray(NULL, NULL);
919 /* WM_WINDOW_ROLE */
920 str = getWindowRole(wwin->client_win);
921 if (!str)
922 appendStringInArray(state, "");
923 else {
924 appendStringInArray(state, str);
925 XFree(str);
928 /* WM_CLASS.instance */
929 appendStringInArray(state, wwin->wm_instance);
931 /* WM_CLASS.class */
932 appendStringInArray(state, wwin->wm_class);
934 /* WM_NAME */
935 if (wwin->flags.wm_name_changed)
936 appendStringInArray(state, "");
937 else
938 appendStringInArray(state, wwin->frame->name);
940 /* geometry */
941 snprintf(buffer, sizeof(buffer), "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
942 wwin->client.width, wwin->client.height,
943 wwin->flags.user_changed_width, wwin->flags.user_changed_height);
944 appendStringInArray(state, buffer);
946 /* state */
947 snprintf(buffer, sizeof(buffer), "%i %i %i", wwin->flags.miniaturized,
948 wwin->flags.shaded, wwin->flags.maximized);
949 appendStringInArray(state, buffer);
951 /* attributes */
952 tmp = makeAttributeState(wwin);
953 WMAddToPLArray(state, tmp);
954 WMReleasePropList(tmp);
956 /* workspace */
957 snprintf(buffer, sizeof(buffer), "%i", wwin->frame->workspace);
958 appendStringInArray(state, buffer);
960 /* app state (repeated for all windows of the app) */
961 tmp = makeAppState(wwin);
962 WMAddToPLArray(state, tmp);
963 WMReleasePropList(tmp);
965 /* shortcuts */
966 shortcuts = 0;
967 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
968 if (scr->shortcutWindow[i] == wwin) {
969 shortcuts |= 1 << i;
972 snprintf(buffer, sizeof(buffer), "%ui", shortcuts);
973 appendStringInArray(tmp, buffer);
975 return state;
979 static void
980 smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
982 SmProp props[4];
983 SmPropValue prop1val, prop2val, prop3val, prop4val;
984 char **argv = (char**)client_data;
985 int argc;
986 int i, j;
987 Bool ok = False;
988 char *statefile = NULL;
989 char *prefix;
990 Bool gsPrefix = False;
991 char *discardCmd = NULL;
992 time_t t;
993 WMPropList *state, *plState;
994 int len;
996 #ifdef DEBUG1
997 puts("received SaveYourselfPhase2 SM message");
998 #endif
1000 /* save session state */
1002 /* the file that will contain the state */
1003 prefix = getenv("SM_SAVE_DIR");
1004 if (!prefix) {
1005 prefix = wusergnusteppath();
1006 if (prefix)
1007 gsPrefix = True;
1009 if (!prefix) {
1010 prefix = getenv("HOME");
1012 if (!prefix)
1013 prefix = ".";
1015 len = strlen(prefix)+64;
1016 statefile = malloc(len);
1017 if (!statefile) {
1018 wwarning(_("out of memory while saving session state"));
1019 goto fail;
1022 t = time();
1023 i = 0;
1024 do {
1025 if (gsPrefix)
1026 snprintf(statefile, len, "%s/.AppInfo/WindowMaker/wmaker.%l%i.state",
1027 prefix, t, i);
1028 else
1029 snprintf(statefile, len, "%s/wmaker.%l%i.state", prefix, t, i);
1030 i++;
1031 } while (access(F_OK, statefile)!=-1);
1033 /* save the states of all windows we're managing */
1034 state = WMCreatePLArray(NULL, NULL);
1037 * Format:
1039 * state_file ::= dictionary with version_info ; state
1040 * version_info ::= 'version' = '1';
1041 * state ::= 'state' = array of screen_info
1042 * screen_info ::= array of (screen number, window_info, window_info, ...)
1043 * window_info ::=
1045 for (i=0; i<wScreenCount; i++) {
1046 WScreen *scr;
1047 WWindow *wwin;
1048 char buf[32];
1049 WMPropList *pscreen;
1051 scr = wScreenWithNumber(i);
1053 snprintf(buf, sizeof(buf), "%i", scr->screen);
1054 pscreen = WMCreatePLArray(WMCreatePLString(buf), NULL);
1056 wwin = scr->focused_window;
1057 while (wwin) {
1058 WMPropList *pwindow;
1060 pwindow = makeClientState(wwin);
1061 WMAddToPLArray(pscreen, pwindow);
1063 wwin = wwin->prev;
1066 WMAddToPLArray(state, pscreen);
1069 plState = WMCreatePLDictionary(WMCreatePLString("Version"),
1070 WMCreatePLString("1.0"),
1071 WMCreatePLString("Screens"),
1072 state, NULL);
1074 WMWritePropListToFile(plState, statefile, False);
1076 WMReleasePropList(plState);
1078 /* set the remaining properties that we didn't set at
1079 * startup time */
1081 for (argc=0, i=0; argv[i]!=NULL; i++) {
1082 if (strcmp(argv[i], "-clientid")==0
1083 || strcmp(argv[i], "-restore")==0) {
1084 i++;
1085 } else {
1086 argc++;
1090 prop[0].name = SmRestartCommand;
1091 prop[0].type = SmLISTofARRAY8;
1092 prop[0].vals = malloc(sizeof(SmPropValue)*(argc+4));
1093 prop[0].num_vals = argc+4;
1095 prop[1].name = SmCloneCommand;
1096 prop[1].type = SmLISTofARRAY8;
1097 prop[1].vals = malloc(sizeof(SmPropValue)*(argc));
1098 prop[1].num_vals = argc;
1100 if (!prop[0].vals || !prop[1].vals) {
1101 wwarning(_("end of memory while saving session state"));
1102 goto fail;
1105 for (j=0, i=0; i<argc+4; i++) {
1106 if (strcmp(argv[i], "-clientid")==0
1107 || strcmp(argv[i], "-restore")==0) {
1108 i++;
1109 } else {
1110 prop[0].vals[j].value = argv[i];
1111 prop[0].vals[j].length = strlen(argv[i]);
1112 prop[1].vals[j].value = argv[i];
1113 prop[1].vals[j].length = strlen(argv[i]);
1114 j++;
1117 prop[0].vals[j].value = "-clientid";
1118 prop[0].vals[j].length = 9;
1119 j++;
1120 prop[0].vals[j].value = sClientID;
1121 prop[0].vals[j].length = strlen(sClientID);
1122 j++;
1123 prop[0].vals[j].value = "-restore";
1124 prop[0].vals[j].length = 11;
1125 j++;
1126 prop[0].vals[j].value = statefile;
1127 prop[0].vals[j].length = strlen(statefile);
1130 int len = strlen(statefile)+8;
1132 discardCmd = malloc(len);
1133 if (!discardCmd)
1134 goto fail;
1135 snprintf(discardCmd, len, "rm %s", statefile);
1137 prop[2].name = SmDiscardCommand;
1138 prop[2].type = SmARRAY8;
1139 prop[2].vals[0] = discardCmd;
1140 prop[2].num_vals = 1;
1142 SmcSetProperties(sSMCConn, 3, prop);
1144 ok = True;
1145 fail:
1146 SmcSaveYourselfDone(smc_conn, ok);
1148 if (prop[0].vals)
1149 wfree(prop[0].vals);
1150 if (prop[1].vals)
1151 wfree(prop[1].vals);
1152 if (discardCmd)
1153 wfree(discardCmd);
1155 if (!ok) {
1156 remove(statefile);
1158 if (statefile)
1159 wfree(statefile);
1163 static void
1164 smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
1165 Bool shutdown, int interact_style, Bool fast)
1167 #ifdef DEBUG1
1168 puts("received SaveYourself SM message");
1169 #endif
1171 if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc,
1172 client_data)) {
1174 SmcSaveYourselfDone(smc_conn, False);
1175 sWaitingPhase2 = False;
1176 } else {
1177 #ifdef DEBUG1
1178 puts("successfull request of SYS phase 2");
1179 #endif
1180 sWaitingPhase2 = True;
1185 static void
1186 smDieProc(SmcConn smc_conn, SmPointer client_data)
1188 #ifdef DEBUG1
1189 puts("received Die SM message");
1190 #endif
1192 wSessionDisconnectManager();
1194 Shutdown(WSExitMode, True);
1199 static void
1200 smSaveCompleteProc(SmcConn smc_conn)
1202 /* it means that we can resume doing things that can change our state */
1203 #ifdef DEBUG1
1204 puts("received SaveComplete SM message");
1205 #endif
1209 static void
1210 smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
1212 if (sWaitingPhase2) {
1214 sWaitingPhase2 = False;
1216 SmcSaveYourselfDone(smc_conn, False);
1221 static void
1222 iceMessageProc(int fd, int mask, void *clientData)
1224 IceConn iceConn = (IceConn)clientData;
1226 IceProcessMessages(iceConn, NULL, NULL);
1230 static void
1231 iceIOErrorHandler(IceConnection ice_conn)
1233 /* This is not fatal but can mean the session manager exited.
1234 * If the session manager exited normally we would get a
1235 * Die message, so this probably means an abnormal exit.
1236 * If the sm was the last client of session, then we'll die
1237 * anyway, otherwise we can continue doing our stuff.
1239 wwarning(_("connection to the session manager was lost"));
1240 wSessionDisconnectManager();
1244 void
1245 wSessionConnectManager(char **argv, int argc)
1247 IceConn iceConn;
1248 char *previous_id = NULL;
1249 char buffer[256];
1250 SmcCallbacks callbacks;
1251 unsigned long mask;
1252 char uid[32];
1253 char pid[32];
1254 SmProp props[4];
1255 SmPropValue prop1val, prop2val, prop3val, prop4val;
1256 char restartStyle;
1257 int i;
1259 mask = SmcSaveYourselfProcMask|SmcDieProcMask|SmcSaveCompleteProcMask
1260 |SmcShutdownCancelledProcMask;
1262 callbacks.save_yourself.callback = smSaveYourselfProc;
1263 callbacks.save_yourself.client_data = argv;
1265 callbacks.die.callback = smDieProc;
1266 callbacks.die.client_data = NULL;
1268 callbacks.save_complete.callback = smSaveCompleteProc;
1269 callbacks.save_complete.client_data = NULL;
1271 callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
1272 callbacks.shutdown_cancelled.client_data = NULL;
1274 for (i=0; i<argc; i++) {
1275 if (strcmp(argv[i], "-clientid")==0) {
1276 previous_id = argv[i+1];
1277 break;
1281 /* connect to the session manager */
1282 sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
1283 mask, &callbacks, previous_id,
1284 &sClientID, 255, buffer);
1285 if (!sSMCConn) {
1286 return;
1288 #ifdef DEBUG1
1289 puts("connected to the session manager");
1290 #endif
1292 /* IceSetIOErrorHandler(iceIOErrorHandler);*/
1294 /* check for session manager clients */
1295 iceConn = SmcGetIceConnection(smcConn);
1297 if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
1298 wsyserror("error setting close-on-exec flag for ICE connection");
1301 sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn),
1302 WIReadMask, iceMessageProc, iceConn);
1304 /* setup information about ourselves */
1306 /* program name */
1307 prop1val.value = argv[0];
1308 prop1val.length = strlen(argv[0]);
1309 prop[0].name = SmProgram;
1310 prop[0].type = SmARRAY8;
1311 prop[0].num_vals = 1;
1312 prop[0].vals = &prop1val;
1314 /* The XSMP doc from X11R6.1 says it contains the user name,
1315 * but every client implementation I saw places the uid # */
1316 snprintf(uid, sizeof(uid), "%i", getuid());
1317 prop2val.value = uid;
1318 prop2val.length = strlen(uid);
1319 prop[1].name = SmUserID;
1320 prop[1].type = SmARRAY8;
1321 prop[1].num_vals = 1;
1322 prop[1].vals = &prop2val;
1324 /* Restart style. We should restart only if we were running when
1325 * the previous session finished. */
1326 restartStyle = SmRestartIfRunning;
1327 prop3val.value = &restartStyle;
1328 prop3val.length = 1;
1329 prop[2].name = SmRestartStyleHint;
1330 prop[2].type = SmCARD8;
1331 prop[2].num_vals = 1;
1332 prop[2].vals = &prop3val;
1334 /* Our PID. Not required but might be usefull */
1335 snprintf(pid, sizeof(pid), "%i", getpid());
1336 prop4val.value = pid;
1337 prop4val.length = strlen(pid);
1338 prop[3].name = SmProcessID;
1339 prop[3].type = SmARRAY8;
1340 prop[3].num_vals = 1;
1341 prop[3].vals = &prop4val;
1343 /* we'll set the rest of the hints later */
1345 SmcSetProperties(sSMCConn, 4, props);
1350 void
1351 wSessionDisconnectManager(void)
1353 if (sSMCConn) {
1354 WMDeleteInputHandler(sSMInputHandler);
1355 sSMInputHandler = NULL;
1357 SmcCloseConnection(sSMCConn, 0, NULL);
1358 sSMCConn = NULL;
1362 void
1363 wSessionRequestShutdown(void)
1365 /* request a shutdown to the session manager */
1366 if (sSMCConn)
1367 SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny,
1368 False, True);
1372 Bool
1373 wSessionIsManaged(void)
1375 return sSMCConn!=NULL;
1378 #endif /* !XSMP_ENABLED */