added nana stuff
[wmaker-crm.git] / src / session.c
blob5c842d1f3dffe3604b406fac1ac6cbfc6c08fc44
1 /* session.c - session state handling and R6 style session management
3 * Copyright (c) 1998 Dan Pascu
4 * Copyright (c) 1998, 1999 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 <proplist.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 proplist_t sApplications = NULL;
120 static proplist_t sCommand;
121 static proplist_t sName;
122 static proplist_t sHost;
123 static proplist_t sWorkspace;
124 static proplist_t sShaded;
125 static proplist_t sMiniaturized;
126 static proplist_t sHidden;
127 static proplist_t sGeometry;
129 static proplist_t sDock;
131 static proplist_t sYes, sNo;
134 static void
135 make_keys()
137 if (sApplications!=NULL)
138 return;
140 sApplications = PLMakeString("Applications");
141 sCommand = PLMakeString("Command");
142 sName = PLMakeString("Name");
143 sHost = PLMakeString("Host");
144 sWorkspace = PLMakeString("Workspace");
145 sShaded = PLMakeString("Shaded");
146 sMiniaturized = PLMakeString("Miniaturized");
147 sHidden = PLMakeString("Hidden");
148 sGeometry = PLMakeString("Geometry");
149 sDock = PLMakeString("Dock");
151 sYes = PLMakeString("Yes");
152 sNo = PLMakeString("No");
157 static int
158 getBool(proplist_t value)
160 char *val;
162 if (!PLIsString(value)) {
163 return 0;
165 if (!(val = PLGetString(value))) {
166 return 0;
169 if ((val[1]=='\0' && (val[0]=='y' || val[0]=='Y'))
170 || strcasecmp(val, "YES")==0) {
172 return 1;
173 } else if ((val[1]=='\0' && (val[0]=='n' || val[0]=='N'))
174 || strcasecmp(val, "NO")==0) {
175 return 0;
176 } else {
177 int i;
178 if (sscanf(val, "%i", &i)==1) {
179 return (i!=0);
180 } else {
181 wwarning(_("can't convert \"%s\" to boolean"), val);
182 return 0;
189 static proplist_t
190 makeWindowState(WWindow *wwin, WApplication *wapp)
192 WScreen *scr = wwin->screen_ptr;
193 Window win;
194 int argc;
195 char **argv;
196 char *class, *instance, *command=NULL, buffer[256];
197 proplist_t win_state, cmd, name, workspace;
198 proplist_t shaded, miniaturized, hidden, geometry;
199 proplist_t dock;
201 if (wwin->main_window!=None && wwin->main_window!=wwin->client_win)
202 win = wwin->main_window;
203 else
204 win = wwin->client_win;
206 if (XGetCommand(dpy, win, &argv, &argc) && argc>0) {
207 command = FlattenStringList(argv, argc);
208 XFreeStringList(argv);
210 if (!command)
211 return NULL;
213 if (PropGetWMClass(win, &class, &instance)) {
214 if (class && instance)
215 sprintf(buffer, "%s.%s", instance, class);
216 else if (instance)
217 sprintf(buffer, "%s", instance);
218 else if (class)
219 sprintf(buffer, ".%s", class);
220 else
221 sprintf(buffer, ".");
223 name = PLMakeString(buffer);
224 cmd = PLMakeString(command);
225 /*sprintf(buffer, "%d", wwin->frame->workspace+1);
226 workspace = PLMakeString(buffer);*/
227 workspace = PLMakeString(scr->workspaces[wwin->frame->workspace]->name);
228 shaded = wwin->flags.shaded ? sYes : sNo;
229 miniaturized = wwin->flags.miniaturized ? sYes : sNo;
230 hidden = wwin->flags.hidden ? sYes : sNo;
231 sprintf(buffer, "%ix%i+%i+%i", wwin->client.width, wwin->client.height,
232 wwin->frame_x, wwin->frame_y);
233 geometry = PLMakeString(buffer);
235 win_state = PLMakeDictionaryFromEntries(sName, name,
236 sCommand, cmd,
237 sWorkspace, workspace,
238 sShaded, shaded,
239 sMiniaturized, miniaturized,
240 sHidden, hidden,
241 sGeometry, geometry,
242 NULL);
244 PLRelease(name);
245 PLRelease(cmd);
246 PLRelease(workspace);
247 PLRelease(geometry);
248 if (wapp && wapp->app_icon && wapp->app_icon->dock) {
249 int i;
250 char *name;
251 if (wapp->app_icon->dock == scr->dock) {
252 name="Dock";
253 } else {
254 for(i=0; i<scr->workspace_count; i++)
255 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
256 break;
257 assert( i < scr->workspace_count);
258 /*n = i+1;*/
259 name = scr->workspaces[i]->name;
261 dock = PLMakeString(name);
262 PLInsertDictionaryEntry(win_state, sDock, dock);
263 PLRelease(dock);
265 } else {
266 win_state = NULL;
269 if (instance) XFree(instance);
270 if (class) XFree(class);
271 if (command) free(command);
273 return win_state;
277 void
278 wSessionSaveState(WScreen *scr)
280 WWindow *wwin = scr->focused_window;
281 proplist_t win_info, wks;
282 proplist_t list=NULL;
283 WMBag *wapp_list=NULL;
286 make_keys();
288 if (!scr->session_state) {
289 scr->session_state = PLMakeDictionaryFromEntries(NULL, NULL, NULL);
290 if (!scr->session_state)
291 return;
294 list = PLMakeArrayFromElements(NULL);
296 wapp_list = WMCreateBag(16);
298 while (wwin) {
299 WApplication *wapp=wApplicationOf(wwin->main_window);
301 if (wwin->transient_for==None && WMGetFirstInBag(wapp_list, wapp)<0
302 && !WFLAGP(wwin, dont_save_session)) {
303 /* A entry for this application was not yet saved. Save one. */
304 if ((win_info = makeWindowState(wwin, wapp))!=NULL) {
305 list = PLAppendArrayElement(list, win_info);
306 PLRelease(win_info);
307 /* If we were succesful in saving the info for this window
308 * add the application the window belongs to, to the
309 * application list, so no multiple entries for the same
310 * application are saved.
312 WMPutInBag(wapp_list, wapp);
315 wwin = wwin->prev;
317 PLRemoveDictionaryEntry(scr->session_state, sApplications);
318 PLInsertDictionaryEntry(scr->session_state, sApplications, list);
319 PLRelease(list);
321 wks = PLMakeString(scr->workspaces[scr->current_workspace]->name);
322 PLInsertDictionaryEntry(scr->session_state, sWorkspace, wks);
323 PLRelease(wks);
325 WMFreeBag(wapp_list);
329 void
330 wSessionClearState(WScreen *scr)
332 make_keys();
334 if (!scr->session_state)
335 return;
337 PLRemoveDictionaryEntry(scr->session_state, sApplications);
338 PLRemoveDictionaryEntry(scr->session_state, sWorkspace);
342 static pid_t
343 execCommand(WScreen *scr, char *command, char *host)
345 pid_t pid;
346 char **argv;
347 int argc;
349 ParseCommand(command, &argv, &argc);
351 if (argv==NULL) {
352 return 0;
355 if ((pid=fork())==0) {
356 char **args;
357 int i;
359 SetupEnvironment(scr);
361 args = malloc(sizeof(char*)*(argc+1));
362 if (!args)
363 exit(111);
364 for (i=0; i<argc; i++) {
365 args[i] = argv[i];
367 args[argc] = NULL;
368 execvp(argv[0], args);
369 exit(111);
371 while (argc > 0)
372 free(argv[--argc]);
373 free(argv);
374 return pid;
378 static WSavedState*
379 getWindowState(WScreen *scr, proplist_t win_state)
381 WSavedState *state = wmalloc(sizeof(WSavedState));
382 proplist_t value;
383 char *tmp;
384 int i;
386 memset(state, 0, sizeof(WSavedState));
387 state->workspace = -1;
388 value = PLGetDictionaryEntry(win_state, sWorkspace);
389 if (value && PLIsString(value)) {
390 tmp = PLGetString(value);
391 if (sscanf(tmp, "%i", &state->workspace)!=1) {
392 state->workspace = -1;
393 for (i=0; i < scr->workspace_count; i++) {
394 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
395 state->workspace = i;
396 break;
399 } else {
400 state->workspace--;
403 if ((value = PLGetDictionaryEntry(win_state, sShaded))!=NULL)
404 state->shaded = getBool(value);
405 if ((value = PLGetDictionaryEntry(win_state, sMiniaturized))!=NULL)
406 state->miniaturized = getBool(value);
407 if ((value = PLGetDictionaryEntry(win_state, sHidden))!=NULL)
408 state->hidden = getBool(value);
410 value = PLGetDictionaryEntry(win_state, sGeometry);
411 if (value && PLIsString(value)) {
412 if (sscanf(PLGetString(value), "%ix%i+%i+%i",
413 &state->w, &state->h, &state->x, &state->y)==4 &&
414 (state->w>0 && state->h>0)) {
415 state->use_geometry = 1;
416 } else if (sscanf(PLGetString(value), "%i,%i,%i,%i",
417 &state->x, &state->y, &state->w, &state->h)==4 &&
418 (state->w>0 && state->h>0)) {
419 /* TODO: remove redundant sscanf() in version 0.20.x */
420 state->use_geometry = 1;
425 return state;
429 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
431 void
432 wSessionRestoreState(WScreen *scr)
434 WSavedState *state;
435 char *instance, *class, *command, *host;
436 proplist_t win_info, apps, cmd, value;
437 pid_t pid;
438 int i, count;
439 WDock *dock;
440 WAppIcon *btn=NULL;
441 int j, n, found;
442 char *tmp;
444 make_keys();
446 if (!scr->session_state)
447 return;
449 PLSetStringCmpHook(NULL);
451 apps = PLGetDictionaryEntry(scr->session_state, sApplications);
452 if (!apps)
453 return;
455 count = PLGetNumberOfElements(apps);
456 if (count==0)
457 return;
459 for (i=0; i<count; i++) {
460 win_info = PLGetArrayElement(apps, i);
462 cmd = PLGetDictionaryEntry(win_info, sCommand);
463 if (!cmd || !PLIsString(cmd) || !(command = PLGetString(cmd))) {
464 continue;
467 value = PLGetDictionaryEntry(win_info, sName);
468 if (!value)
469 continue;
471 ParseWindowName(value, &instance, &class, "session");
472 if (!instance && !class)
473 continue;
475 value = PLGetDictionaryEntry(win_info, sHost);
476 if (value && PLIsString(value))
477 host = PLGetString(value);
478 else
479 host = NULL;
481 state = getWindowState(scr, win_info);
483 dock = NULL;
484 value = PLGetDictionaryEntry(win_info, sDock);
485 if (value && PLIsString(value) && (tmp = PLGetString(value))!=NULL) {
486 if (sscanf(tmp, "%i", &n)!=1) {
487 if (!strcasecmp(tmp, "DOCK")) {
488 dock = scr->dock;
489 } else {
490 for (j=0; j < scr->workspace_count; j++) {
491 if (strcmp(scr->workspaces[j]->name, tmp)==0) {
492 dock = scr->workspaces[j]->clip;
493 break;
497 } else {
498 if (n == 0) {
499 dock = scr->dock;
500 } else if (n>0 && n<=scr->workspace_count) {
501 dock = scr->workspaces[n-1]->clip;
506 found = 0;
507 if (dock!=NULL) {
508 for (j=0; j<dock->max_icons; j++) {
509 btn = dock->icon_array[j];
510 if (btn && SAME(instance, btn->wm_instance) &&
511 SAME(class, btn->wm_class) &&
512 SAME(command, btn->command) &&
513 !btn->launching) {
514 found = 1;
515 break;
520 if (found) {
521 wDockLaunchWithState(dock, btn, state);
522 } else if ((pid = execCommand(scr, command, host)) > 0) {
523 wWindowAddSavedState(instance, class, command, pid, state);
524 } else {
525 free(state);
528 if (instance) free(instance);
529 if (class) free(class);
531 /* clean up */
532 PLSetStringCmpHook(StringCompareHook);
536 void
537 wSessionRestoreLastWorkspace(WScreen *scr)
539 proplist_t wks;
540 int w, i;
541 char *tmp;
543 make_keys();
545 if (!scr->session_state)
546 return;
548 PLSetStringCmpHook(NULL);
550 wks = PLGetDictionaryEntry(scr->session_state, sWorkspace);
551 if (!wks || !PLIsString(wks))
552 return;
554 tmp = PLGetString(wks);
556 /* clean up */
557 PLSetStringCmpHook(StringCompareHook);
559 if (sscanf(tmp, "%i", &w)!=1) {
560 w = -1;
561 for (i=0; i < scr->workspace_count; i++) {
562 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
563 w = i;
564 break;
567 } else {
568 w--;
571 if (w!=scr->current_workspace && w<scr->workspace_count) {
572 wWorkspaceChange(scr, w);
577 static void
578 clearWaitingAckState(WScreen *scr)
580 WWindow *wwin;
581 WApplication *wapp;
583 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
584 wwin->flags.waiting_save_ack = 0;
585 if (wwin->main_window != None) {
586 wapp = wApplicationOf(wwin->main_window);
587 if (wapp)
588 wapp->main_window_desc->flags.waiting_save_ack = 0;
594 void
595 wSessionSaveClients(WScreen *scr)
602 * With XSMP, this job is done by smproxy
604 void
605 wSessionSendSaveYourself(WScreen *scr)
607 WWindow *wwin;
608 int count;
610 /* freeze client interaction with clients */
611 XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync,
612 CurrentTime);
613 XGrabPointer(dpy, scr->root_win, False, ButtonPressMask|ButtonReleaseMask,
614 GrabModeAsync, GrabModeAsync, scr->root_win, None,
615 CurrentTime);
617 clearWaitingAckState(scr);
619 count = 0;
621 /* first send SAVE_YOURSELF for everybody */
622 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
623 WWindow *mainWin;
625 mainWin = wWindowFor(wwin->main_window);
627 if (mainWin) {
628 /* if the client is a multi-window client, only send message
629 * to the main window */
630 wwin = mainWin;
633 /* make sure the SAVE_YOURSELF flag is up-to-date */
634 PropGetProtocols(wwin->client_win, &wwin->protocols);
636 if (wwin->protocols.SAVE_YOURSELF) {
637 if (!wwin->flags.waiting_save_ack) {
638 wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
640 wwin->flags.waiting_save_ack = 1;
641 count++;
643 } else {
644 wwin->flags.waiting_save_ack = 0;
648 /* then wait for acknowledge */
649 while (count > 0) {
653 XUngrabPointer(dpy, CurrentTime);
654 XUngrabKeyboard(dpy, CurrentTime);
655 XFlush(dpy);
659 #ifdef XSMP_ENABLED
661 * With full session management support, the part of WMState
662 * that store client window state will become obsolete (maybe we can reuse
663 * the old code too),
664 * but we still need to store state info like the dock and workspaces.
665 * It is better to keep dock/wspace info in WMState because the user
666 * might want to keep the dock configuration while not wanting to
667 * resume a previously saved session.
668 * So, wmaker specific state info can be saved in
669 * ~/GNUstep/.AppInfo/WindowMaker/statename.state
670 * Its better to not put it in the defaults directory because:
671 * - its not a defaults file (having domain names like wmaker0089504baa
672 * in the defaults directory wouldn't be very neat)
673 * - this state file is not meant to be edited by users
675 * The old session code will become obsolete. When wmaker is
676 * compiled with R6 sm support compiled in, itll be better to
677 * use a totally rewritten state saving code, but we can keep
678 * the current code for when XSMP_ENABLED is not compiled in.
680 * This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
681 * messages), but itll be better.
683 * -readme
687 static char*
688 getWindowRole(Window window)
690 XTextProperty prop;
691 static Atom atom = 0;
693 if (!atom)
694 atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
696 if (XGetTextProperty(dpy, window, &prop, atom)) {
697 if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
698 return prop.value;
701 return NULL;
707 * Saved Info:
709 * WM_WINDOW_ROLE
711 * WM_CLASS.instance
712 * WM_CLASS.class
713 * WM_NAME
714 * WM_COMMAND
716 * geometry
717 * state = (miniaturized, shaded, etc)
718 * attribute
719 * workspace #
720 * app state = (which dock, hidden)
721 * window shortcut #
724 static proplist_t
725 makeAppState(WWindow *wwin)
727 WApplication *wapp;
728 proplist_t state;
729 WScreen *scr = wwin->screen_ptr;
731 state = PLMakeArrayWithElements(NULL, NULL);
733 wapp = wApplicationOf(wwin->main_window);
735 if (wapp) {
736 if (wapp->app_icon && wapp->app_icon->dock) {
738 if (wapp->app_icon->dock == scr->dock) {
739 PLAppendArrayElement(state, PLMakeString("Dock"));
740 } else {
741 int i;
743 for(i=0; i<scr->workspace_count; i++)
744 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
745 break;
747 assert(i < scr->workspace_count);
749 PLAppendArrayElement(state,
750 PLMakeString(scr->workspaces[i]->name));
754 PLAppendArrayElement(state, PLMakeString(wapp->hidden ? "1" : "0"));
757 return state;
762 Bool
763 wSessionGetStateFor(WWindow *wwin, WSessionData *state)
765 char *str;
766 proplist_t slist;
767 proplist_t elem;
768 proplist_t value;
769 int index = 0;
771 index = 3;
773 /* geometry */
774 value = PLGetArrayElement(slist, index++);
775 str = PLGetString(value);
777 sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
778 &state->width, &state->height,
779 &state->user_changed_width, &state->user_changed_height);
782 /* state */
783 value = PLGetArrayElement(slist, index++);
784 str = PLGetString(value);
786 sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded,
787 &state->maximized);
790 /* attributes */
791 value = PLGetArrayElement(slist, index++);
792 str = PLGetString(value);
794 getAttributeState(str, &state->mflags, &state->flags);
797 /* workspace */
798 value = PLGetArrayElement(slist, index++);
799 str = PLGetString(value);
801 sscanf(str, "%i", &state->workspace);
804 /* app state (repeated for all windows of the app) */
805 value = PLGetArrayElement(slist, index++);
806 str = PLGetString(value);
808 /* ???? */
810 /* shortcuts */
811 value = PLGetArrayElement(slist, index++);
812 str = PLGetString(value);
814 sscanf(str, "%i", &state->shortcuts);
819 static proplist_t
820 makeAttributeState(WWindow *wwin)
822 unsigned int data1, data2;
823 char buffer[256];
825 #define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
826 ? (wwin)->user_flags.FLAG : -1)
828 sprintf(buffer,
829 "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
830 W_FLAG(no_titlebar),
831 W_FLAG(no_resizable),
832 W_FLAG(no_closable),
833 W_FLAG(no_miniaturizable),
834 W_FLAG(no_resizebar),
835 W_FLAG(no_close_button),
836 W_FLAG(no_miniaturize_button),
838 W_FLAG(broken_close),
839 W_FLAG(kill_close),
841 W_FLAG(no_shadeable),
842 W_FLAG(omnipresent),
843 W_FLAG(skip_window_list),
844 W_FLAG(floating),
845 W_FLAG(sunken),
846 W_FLAG(no_bind_keys),
847 W_FLAG(no_bind_mouse),
848 W_FLAG(no_hide_others),
849 W_FLAG(no_appicon),
850 W_FLAG(dont_move_off),
851 W_FLAG(no_focusable),
852 W_FLAG(always_user_icon),
853 W_FLAG(start_miniaturized),
854 W_FLAG(start_hidden),
855 W_FLAG(start_maximized),
856 W_FLAG(dont_save_session),
857 W_FLAG(emulate_appicon));
859 return PLMakeString(buffer);
863 static void
864 appendStringInArray(proplist_t array, char *str)
866 proplist_t val;
868 val = PLMakeString(str);
869 PLAppendArrayElement(array, val);
870 PLRelease(val);
874 static proplist_t
875 makeClientState(WWindow *wwin)
877 proplist_t state;
878 proplist_t tmp;
879 char *str;
880 char buffer[256];
881 int i;
882 unsigned shortcuts;
884 state = PLMakeArrayWithElements(NULL, NULL);
886 /* WM_WINDOW_ROLE */
887 str = getWindowRole(wwin->client_win);
888 if (!str)
889 appendStringInArray(state, "");
890 else {
891 appendStringInArray(state, str);
892 XFree(str);
895 /* WM_CLASS.instance */
896 appendStringInArray(state, wwin->wm_instance);
898 /* WM_CLASS.class */
899 appendStringInArray(state, wwin->wm_class);
901 /* WM_NAME */
902 if (wwin->flags.wm_name_changed)
903 appendStringInArray(state, "");
904 else
905 appendStringInArray(state, wwin->frame->name);
907 /* geometry */
908 sprintf(buffer, "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
909 wwin->client.width, wwin->client.height,
910 wwin->flags.user_changed_width, wwin->flags.user_changed_height);
911 appendStringInArray(state, buffer);
913 /* state */
914 sprintf(buffer, "%i %i %i", wwin->flags.miniaturized,
915 wwin->flags.shaded, wwin->flags.maximized);
916 appendStringInArray(state, buffer);
918 /* attributes */
919 tmp = makeAttributeState(wwin);
920 PLAppendArrayElement(state, tmp);
921 PLRelease(tmp);
923 /* workspace */
924 sprintf(buffer, "%i", wwin->frame->workspace);
925 appendStringInArray(state, buffer);
927 /* app state (repeated for all windows of the app) */
928 tmp = makeAppState(wwin);
929 PLAppendArrayElement(state, tmp);
930 PLRelease(tmp);
932 /* shortcuts */
933 shortcuts = 0;
934 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
935 if (scr->shortcutWindow[i] == wwin) {
936 shortcuts |= 1 << i;
939 sprintf(buffer, "%ui", shortcuts);
940 appendStringInArray(tmp, buffer);
942 return state;
946 static void
947 smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
949 SmProp props[4];
950 SmPropValue prop1val, prop2val, prop3val, prop4val;
951 char **argv = (char**)client_data;
952 int argc;
953 int i, j;
954 Bool ok = False;
955 char *statefile = NULL;
956 char *prefix;
957 Bool gsPrefix = False;
958 char *discardCmd = NULL;
959 time_t t;
960 proplist_t state;
962 #ifdef DEBUG1
963 puts("received SaveYourselfPhase2 SM message");
964 #endif
966 /* save session state */
968 /* the file that will contain the state */
969 prefix = getenv("SM_SAVE_DIR");
970 if (!prefix) {
971 prefix = wusergnusteppath();
972 if (prefix)
973 gsPrefix = True;
975 if (!prefix) {
976 prefix = getenv("HOME");
978 if (!prefix)
979 prefix = ".";
981 statefile = malloc(strlen(prefix)+64);
982 if (!statefile) {
983 wwarning(_("out of memory while saving session state"));
984 goto fail;
987 t = time();
988 i = 0;
989 do {
990 if (gsPrefix)
991 sprintf(statefile, "%s/.AppInfo/WindowMaker/wmaker.%l%i.state",
992 prefix, t, i);
993 else
994 sprintf(statefile, "%s/wmaker.%l%i.state", prefix, t, i);
995 i++;
996 } while (access(F_OK, statefile)!=-1);
998 /* save the states of all windows we're managing */
999 state = PLMakeArrayFromElements(NULL, NULL);
1002 * Format:
1004 * state_file ::= dictionary with version_info ; state
1005 * version_info ::= 'version' = '1';
1006 * state ::= 'state' = array of screen_info
1007 * screen_info ::= array of (screen number, window_info, window_info, ...)
1008 * window_info ::=
1010 for (i=0; i<wScreenCount; i++) {
1011 WScreen *scr;
1012 WWindow *wwin;
1013 char buf[32];
1014 proplist_t pscreen;
1016 scr = wScreenWithNumber(i);
1018 sprintf(buf, "%i", scr->screen);
1019 pscreen = PLMakeArrayFromElements(PLMakeString(buf), NULL);
1021 wwin = scr->focused_window;
1022 while (wwin) {
1023 proplist_t pwindow;
1025 pwindow = makeClientState(wwin);
1026 PLAppendArrayElement(pscreen, pwindow);
1028 wwin = wwin->prev;
1031 PLAppendArrayElement(state, pscreen);
1035 proplist_t statefile;
1037 statefile = PLMakeDictionaryFromEntries(PLMakeString("Version"),
1038 PLMakeString("1.0"),
1040 PLMakeString("Screens"),
1041 state,
1043 NULL);
1045 PLSetFilename(statefile, PLMakeString(statefile));
1046 PLSave(statefile, NO);
1048 PLRelease(statefile);
1051 /* set the remaining properties that we didn't set at
1052 * startup time */
1054 for (argc=0, i=0; argv[i]!=NULL; i++) {
1055 if (strcmp(argv[i], "-clientid")==0
1056 || strcmp(argv[i], "-restore")==0) {
1057 i++;
1058 } else {
1059 argc++;
1063 prop[0].name = SmRestartCommand;
1064 prop[0].type = SmLISTofARRAY8;
1065 prop[0].vals = malloc(sizeof(SmPropValue)*(argc+4));
1066 prop[0].num_vals = argc+4;
1068 prop[1].name = SmCloneCommand;
1069 prop[1].type = SmLISTofARRAY8;
1070 prop[1].vals = malloc(sizeof(SmPropValue)*(argc));
1071 prop[1].num_vals = argc;
1073 if (!prop[0].vals || !prop[1].vals) {
1074 wwarning(_("end of memory while saving session state"));
1075 goto fail;
1078 for (j=0, i=0; i<argc+4; i++) {
1079 if (strcmp(argv[i], "-clientid")==0
1080 || strcmp(argv[i], "-restore")==0) {
1081 i++;
1082 } else {
1083 prop[0].vals[j].value = argv[i];
1084 prop[0].vals[j].length = strlen(argv[i]);
1085 prop[1].vals[j].value = argv[i];
1086 prop[1].vals[j].length = strlen(argv[i]);
1087 j++;
1090 prop[0].vals[j].value = "-clientid";
1091 prop[0].vals[j].length = 9;
1092 j++;
1093 prop[0].vals[j].value = sClientID;
1094 prop[0].vals[j].length = strlen(sClientID);
1095 j++;
1096 prop[0].vals[j].value = "-restore";
1097 prop[0].vals[j].length = 11;
1098 j++;
1099 prop[0].vals[j].value = statefile;
1100 prop[0].vals[j].length = strlen(statefile);
1102 discardCmd = malloc(strlen(statefile)+8);
1103 if (!discardCmd)
1104 goto fail;
1105 sprintf(discardCmd, "rm %s", statefile);
1106 prop[2].name = SmDiscardCommand;
1107 prop[2].type = SmARRAY8;
1108 prop[2].vals[0] = discardCmd;
1109 prop[2].num_vals = 1;
1111 SmcSetProperties(sSMCConn, 3, prop);
1113 ok = True;
1114 fail:
1115 SmcSaveYourselfDone(smc_conn, ok);
1117 if (prop[0].vals)
1118 free(prop[0].vals);
1119 if (prop[1].vals)
1120 free(prop[1].vals);
1121 if (discardCmd)
1122 free(discardCmd);
1124 if (!ok) {
1125 remove(statefile);
1127 if (statefile)
1128 free(statefile);
1132 static void
1133 smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
1134 Bool shutdown, int interact_style, Bool fast)
1136 #ifdef DEBUG1
1137 puts("received SaveYourself SM message");
1138 #endif
1140 if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc,
1141 client_data)) {
1143 SmcSaveYourselfDone(smc_conn, False);
1144 sWaitingPhase2 = False;
1145 } else {
1146 #ifdef DEBUG1
1147 puts("successfull request of SYS phase 2");
1148 #endif
1149 sWaitingPhase2 = True;
1154 static void
1155 smDieProc(SmcConn smc_conn, SmPointer client_data)
1157 #ifdef DEBUG1
1158 puts("received Die SM message");
1159 #endif
1161 wSessionDisconnectManager();
1163 Shutdown(WSExitMode, True);
1168 static void
1169 smSaveCompleteProc(SmcConn smc_conn)
1171 /* it means that we can resume doing things that can change our state */
1172 #ifdef DEBUG1
1173 puts("received SaveComplete SM message");
1174 #endif
1178 static void
1179 smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
1181 if (sWaitingPhase2) {
1183 sWaitingPhase2 = False;
1185 SmcSaveYourselfDone(smc_conn, False);
1190 static void
1191 iceMessageProc(int fd, int mask, void *clientData)
1193 IceConn iceConn = (IceConn)clientData;
1195 IceProcessMessages(iceConn, NULL, NULL);
1199 static void
1200 iceIOErrorHandler(IceConnection ice_conn)
1202 /* This is not fatal but can mean the session manager exited.
1203 * If the session manager exited normally we would get a
1204 * Die message, so this probably means an abnormal exit.
1205 * If the sm was the last client of session, then we'll die
1206 * anyway, otherwise we can continue doing our stuff.
1208 wwarning(_("connection to the session manager was lost"));
1209 wSessionDisconnectManager();
1213 void
1214 wSessionConnectManager(char **argv, int argc)
1216 IceConn iceConn;
1217 char *previous_id = NULL;
1218 char buffer[256];
1219 SmcCallbacks callbacks;
1220 unsigned long mask;
1221 char uid[32];
1222 char pid[32];
1223 SmProp props[4];
1224 SmPropValue prop1val, prop2val, prop3val, prop4val;
1225 char restartStyle;
1226 int i;
1228 mask = SmcSaveYourselfProcMask|SmcDieProcMask|SmcSaveCompleteProcMask
1229 |SmcShutdownCancelledProcMask;
1231 callbacks.save_yourself.callback = smSaveYourselfProc;
1232 callbacks.save_yourself.client_data = argv;
1234 callbacks.die.callback = smDieProc;
1235 callbacks.die.client_data = NULL;
1237 callbacks.save_complete.callback = smSaveCompleteProc;
1238 callbacks.save_complete.client_data = NULL;
1240 callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
1241 callbacks.shutdown_cancelled.client_data = NULL;
1243 for (i=0; i<argc; i++) {
1244 if (strcmp(argv[i], "-clientid")==0) {
1245 previous_id = argv[i+1];
1246 break;
1250 /* connect to the session manager */
1251 sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
1252 mask, &callbacks, previous_id,
1253 &sClientID, 255, buffer);
1254 if (!sSMCConn) {
1255 return;
1257 #ifdef DEBUG1
1258 puts("connected to the session manager");
1259 #endif
1261 /* IceSetIOErrorHandler(iceIOErrorHandler);*/
1263 /* check for session manager clients */
1264 iceConn = SmcGetIceConnection(smcConn);
1266 if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
1267 wsyserror("error setting close-on-exec flag for ICE connection");
1270 sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn),
1271 WIReadMask, iceMessageProc, iceConn);
1273 /* setup information about ourselves */
1275 /* program name */
1276 prop1val.value = argv[0];
1277 prop1val.length = strlen(argv[0]);
1278 prop[0].name = SmProgram;
1279 prop[0].type = SmARRAY8;
1280 prop[0].num_vals = 1;
1281 prop[0].vals = &prop1val;
1283 /* The XSMP doc from X11R6.1 says it contains the user name,
1284 * but every client implementation I saw places the uid # */
1285 sprintf(uid, "%i", getuid());
1286 prop2val.value = uid;
1287 prop2val.length = strlen(uid);
1288 prop[1].name = SmUserID;
1289 prop[1].type = SmARRAY8;
1290 prop[1].num_vals = 1;
1291 prop[1].vals = &prop2val;
1293 /* Restart style. We should restart only if we were running when
1294 * the previous session finished. */
1295 restartStyle = SmRestartIfRunning;
1296 prop3val.value = &restartStyle;
1297 prop3val.length = 1;
1298 prop[2].name = SmRestartStyleHint;
1299 prop[2].type = SmCARD8;
1300 prop[2].num_vals = 1;
1301 prop[2].vals = &prop3val;
1303 /* Our PID. Not required but might be usefull */
1304 sprintf(pid, "%i", getpid());
1305 prop4val.value = pid;
1306 prop4val.length = strlen(pid);
1307 prop[3].name = SmProcessID;
1308 prop[3].type = SmARRAY8;
1309 prop[3].num_vals = 1;
1310 prop[3].vals = &prop4val;
1312 /* we'll set the rest of the hints later */
1314 SmcSetProperties(sSMCConn, 4, props);
1319 void
1320 wSessionDisconnectManager(void)
1322 if (sSMCConn) {
1323 WMDeleteInputHandler(sSMInputHandler);
1324 sSMInputHandler = NULL;
1326 SmcCloseConnection(sSMCConn, 0, NULL);
1327 sSMCConn = NULL;
1331 void
1332 wSessionRequestShutdown(void)
1334 /* request a shutdown to the session manager */
1335 if (sSMCConn)
1336 SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny,
1337 False, True);
1341 Bool
1342 wSessionIsManaged(void)
1344 return sSMCConn!=NULL;
1347 #endif /* !XSMP_ENABLED */