fixed many bugs related from stacking and old ones too
[wmaker-crm.git] / src / session.c
blob068b512bbdeb8045649159ea34b184f1779853b5
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;
128 static proplist_t sShortcutMask;
130 static proplist_t sDock;
132 static proplist_t sYes, sNo;
135 static void
136 make_keys()
138 if (sApplications!=NULL)
139 return;
141 sApplications = PLMakeString("Applications");
142 sCommand = PLMakeString("Command");
143 sName = PLMakeString("Name");
144 sHost = PLMakeString("Host");
145 sWorkspace = PLMakeString("Workspace");
146 sShaded = PLMakeString("Shaded");
147 sMiniaturized = PLMakeString("Miniaturized");
148 sHidden = PLMakeString("Hidden");
149 sGeometry = PLMakeString("Geometry");
150 sDock = PLMakeString("Dock");
151 sShortcutMask = PLMakeString("ShortcutMask");
153 sYes = PLMakeString("Yes");
154 sNo = PLMakeString("No");
159 static int
160 getBool(proplist_t value)
162 char *val;
164 if (!PLIsString(value)) {
165 return 0;
167 if (!(val = PLGetString(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(proplist_t value)
193 char *val;
194 unsigned n;
196 if (!PLIsString(value))
197 return 0;
198 val = PLGetString(value);
199 if (!val)
200 return 0;
201 if (sscanf(val, "%u", &n) != 1)
202 return 0;
204 return n;
209 static proplist_t
210 makeWindowState(WWindow *wwin, WApplication *wapp)
212 WScreen *scr = wwin->screen_ptr;
213 Window win;
214 int argc;
215 char **argv;
216 int i;
217 unsigned mask;
218 char *class, *instance, *command=NULL, buffer[256];
219 proplist_t win_state, cmd, name, workspace;
220 proplist_t shaded, miniaturized, hidden, geometry;
221 proplist_t dock, shortcut;
223 if (wwin->main_window!=None && wwin->main_window!=wwin->client_win)
224 win = wwin->main_window;
225 else
226 win = wwin->client_win;
228 if (XGetCommand(dpy, win, &argv, &argc) && argc>0) {
229 command = FlattenStringList(argv, argc);
230 XFreeStringList(argv);
232 if (!command)
233 return NULL;
235 if (PropGetWMClass(win, &class, &instance)) {
236 if (class && instance)
237 sprintf(buffer, "%s.%s", instance, class);
238 else if (instance)
239 sprintf(buffer, "%s", instance);
240 else if (class)
241 sprintf(buffer, ".%s", class);
242 else
243 sprintf(buffer, ".");
245 name = PLMakeString(buffer);
246 cmd = PLMakeString(command);
247 /*sprintf(buffer, "%d", wwin->frame->workspace+1);
248 workspace = PLMakeString(buffer);*/
249 workspace = PLMakeString(scr->workspaces[wwin->frame->workspace]->name);
250 shaded = wwin->flags.shaded ? sYes : sNo;
251 miniaturized = wwin->flags.miniaturized ? sYes : sNo;
252 hidden = wwin->flags.hidden ? sYes : sNo;
253 sprintf(buffer, "%ix%i+%i+%i", wwin->client.width, wwin->client.height,
254 wwin->frame_x, wwin->frame_y);
255 geometry = PLMakeString(buffer);
257 for (mask = 0, i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
258 if (scr->shortcutWindows[i] != NULL &&
259 WMGetFirstInBag(scr->shortcutWindows[i], wwin) != WBNotFound) {
260 mask |= 1<<i;
264 sprintf(buffer, "%u", mask);
265 shortcut = PLMakeString(buffer);
267 win_state = PLMakeDictionaryFromEntries(sName, name,
268 sCommand, cmd,
269 sWorkspace, workspace,
270 sShaded, shaded,
271 sMiniaturized, miniaturized,
272 sHidden, hidden,
273 sShortcutMask, shortcut,
274 sGeometry, geometry,
275 NULL);
277 PLRelease(name);
278 PLRelease(cmd);
279 PLRelease(workspace);
280 PLRelease(geometry);
281 PLRelease(shortcut);
282 if (wapp && wapp->app_icon && wapp->app_icon->dock) {
283 int i;
284 char *name;
285 if (wapp->app_icon->dock == scr->dock) {
286 name="Dock";
287 } else {
288 for(i=0; i<scr->workspace_count; i++)
289 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
290 break;
291 assert( i < scr->workspace_count);
292 /*n = i+1;*/
293 name = scr->workspaces[i]->name;
295 dock = PLMakeString(name);
296 PLInsertDictionaryEntry(win_state, sDock, dock);
297 PLRelease(dock);
299 } else {
300 win_state = NULL;
303 if (instance) XFree(instance);
304 if (class) XFree(class);
305 if (command) free(command);
307 return win_state;
311 void
312 wSessionSaveState(WScreen *scr)
314 WWindow *wwin = scr->focused_window;
315 proplist_t win_info, wks;
316 proplist_t list=NULL;
317 WMBag *wapp_list=NULL;
320 make_keys();
322 if (!scr->session_state) {
323 scr->session_state = PLMakeDictionaryFromEntries(NULL, NULL, NULL);
324 if (!scr->session_state)
325 return;
328 list = PLMakeArrayFromElements(NULL);
330 wapp_list = WMCreateBag(16);
332 while (wwin) {
333 WApplication *wapp=wApplicationOf(wwin->main_window);
335 if (wwin->transient_for==None
336 && WMGetFirstInBag(wapp_list, wapp)==WBNotFound
337 && !WFLAGP(wwin, dont_save_session)) {
338 /* A entry for this application was not yet saved. Save one. */
339 if ((win_info = makeWindowState(wwin, wapp))!=NULL) {
340 list = PLAppendArrayElement(list, win_info);
341 PLRelease(win_info);
342 /* If we were succesful in saving the info for this window
343 * add the application the window belongs to, to the
344 * application list, so no multiple entries for the same
345 * application are saved.
347 WMPutInBag(wapp_list, wapp);
350 wwin = wwin->prev;
352 PLRemoveDictionaryEntry(scr->session_state, sApplications);
353 PLInsertDictionaryEntry(scr->session_state, sApplications, list);
354 PLRelease(list);
356 wks = PLMakeString(scr->workspaces[scr->current_workspace]->name);
357 PLInsertDictionaryEntry(scr->session_state, sWorkspace, wks);
358 PLRelease(wks);
360 WMFreeBag(wapp_list);
364 void
365 wSessionClearState(WScreen *scr)
367 make_keys();
369 if (!scr->session_state)
370 return;
372 PLRemoveDictionaryEntry(scr->session_state, sApplications);
373 PLRemoveDictionaryEntry(scr->session_state, sWorkspace);
377 static pid_t
378 execCommand(WScreen *scr, char *command, char *host)
380 pid_t pid;
381 char **argv;
382 int argc;
384 ParseCommand(command, &argv, &argc);
386 if (argv==NULL) {
387 return 0;
390 if ((pid=fork())==0) {
391 char **args;
392 int i;
394 SetupEnvironment(scr);
396 args = malloc(sizeof(char*)*(argc+1));
397 if (!args)
398 exit(111);
399 for (i=0; i<argc; i++) {
400 args[i] = argv[i];
402 args[argc] = NULL;
403 execvp(argv[0], args);
404 exit(111);
406 while (argc > 0)
407 free(argv[--argc]);
408 free(argv);
409 return pid;
413 static WSavedState*
414 getWindowState(WScreen *scr, proplist_t win_state)
416 WSavedState *state = wmalloc(sizeof(WSavedState));
417 proplist_t value;
418 char *tmp;
419 unsigned mask;
420 int i;
422 memset(state, 0, sizeof(WSavedState));
423 state->workspace = -1;
424 value = PLGetDictionaryEntry(win_state, sWorkspace);
425 if (value && PLIsString(value)) {
426 tmp = PLGetString(value);
427 if (sscanf(tmp, "%i", &state->workspace)!=1) {
428 state->workspace = -1;
429 for (i=0; i < scr->workspace_count; i++) {
430 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
431 state->workspace = i;
432 break;
435 } else {
436 state->workspace--;
439 if ((value = PLGetDictionaryEntry(win_state, sShaded))!=NULL)
440 state->shaded = getBool(value);
441 if ((value = PLGetDictionaryEntry(win_state, sMiniaturized))!=NULL)
442 state->miniaturized = getBool(value);
443 if ((value = PLGetDictionaryEntry(win_state, sHidden))!=NULL)
444 state->hidden = getBool(value);
445 if ((value = PLGetDictionaryEntry(win_state, sHidden))!=NULL) {
446 mask = getInt(value);
447 state->window_shortcuts = mask;
450 value = PLGetDictionaryEntry(win_state, sGeometry);
451 if (value && PLIsString(value)) {
452 if (sscanf(PLGetString(value), "%ix%i+%i+%i",
453 &state->w, &state->h, &state->x, &state->y)==4 &&
454 (state->w>0 && state->h>0)) {
455 state->use_geometry = 1;
456 } else if (sscanf(PLGetString(value), "%i,%i,%i,%i",
457 &state->x, &state->y, &state->w, &state->h)==4 &&
458 (state->w>0 && state->h>0)) {
459 /* TODO: remove redundant sscanf() in version 0.20.x */
460 state->use_geometry = 1;
465 return state;
469 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
471 void
472 wSessionRestoreState(WScreen *scr)
474 WSavedState *state;
475 char *instance, *class, *command, *host;
476 proplist_t win_info, apps, cmd, value;
477 pid_t pid;
478 int i, count;
479 WDock *dock;
480 WAppIcon *btn=NULL;
481 int j, n, found;
482 char *tmp;
484 make_keys();
486 if (!scr->session_state)
487 return;
489 PLSetStringCmpHook(NULL);
491 apps = PLGetDictionaryEntry(scr->session_state, sApplications);
492 if (!apps)
493 return;
495 count = PLGetNumberOfElements(apps);
496 if (count==0)
497 return;
499 for (i=0; i<count; i++) {
500 win_info = PLGetArrayElement(apps, i);
502 cmd = PLGetDictionaryEntry(win_info, sCommand);
503 if (!cmd || !PLIsString(cmd) || !(command = PLGetString(cmd))) {
504 continue;
507 value = PLGetDictionaryEntry(win_info, sName);
508 if (!value)
509 continue;
511 ParseWindowName(value, &instance, &class, "session");
512 if (!instance && !class)
513 continue;
515 value = PLGetDictionaryEntry(win_info, sHost);
516 if (value && PLIsString(value))
517 host = PLGetString(value);
518 else
519 host = NULL;
521 state = getWindowState(scr, win_info);
523 dock = NULL;
524 value = PLGetDictionaryEntry(win_info, sDock);
525 if (value && PLIsString(value) && (tmp = PLGetString(value))!=NULL) {
526 if (sscanf(tmp, "%i", &n)!=1) {
527 if (!strcasecmp(tmp, "DOCK")) {
528 dock = scr->dock;
529 } else {
530 for (j=0; j < scr->workspace_count; j++) {
531 if (strcmp(scr->workspaces[j]->name, tmp)==0) {
532 dock = scr->workspaces[j]->clip;
533 break;
537 } else {
538 if (n == 0) {
539 dock = scr->dock;
540 } else if (n>0 && n<=scr->workspace_count) {
541 dock = scr->workspaces[n-1]->clip;
546 found = 0;
547 if (dock!=NULL) {
548 for (j=0; j<dock->max_icons; j++) {
549 btn = dock->icon_array[j];
550 if (btn && SAME(instance, btn->wm_instance) &&
551 SAME(class, btn->wm_class) &&
552 SAME(command, btn->command) &&
553 !btn->launching) {
554 found = 1;
555 break;
560 if (found) {
561 wDockLaunchWithState(dock, btn, state);
562 } else if ((pid = execCommand(scr, command, host)) > 0) {
563 wWindowAddSavedState(instance, class, command, pid, state);
564 } else {
565 free(state);
568 if (instance) free(instance);
569 if (class) free(class);
571 /* clean up */
572 PLSetStringCmpHook(StringCompareHook);
576 void
577 wSessionRestoreLastWorkspace(WScreen *scr)
579 proplist_t wks;
580 int w, i;
581 char *tmp;
583 make_keys();
585 if (!scr->session_state)
586 return;
588 PLSetStringCmpHook(NULL);
590 wks = PLGetDictionaryEntry(scr->session_state, sWorkspace);
591 if (!wks || !PLIsString(wks))
592 return;
594 tmp = PLGetString(wks);
596 /* clean up */
597 PLSetStringCmpHook(StringCompareHook);
599 if (sscanf(tmp, "%i", &w)!=1) {
600 w = -1;
601 for (i=0; i < scr->workspace_count; i++) {
602 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
603 w = i;
604 break;
607 } else {
608 w--;
611 if (w!=scr->current_workspace && w<scr->workspace_count) {
612 wWorkspaceChange(scr, w);
617 static void
618 clearWaitingAckState(WScreen *scr)
620 WWindow *wwin;
621 WApplication *wapp;
623 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
624 wwin->flags.waiting_save_ack = 0;
625 if (wwin->main_window != None) {
626 wapp = wApplicationOf(wwin->main_window);
627 if (wapp)
628 wapp->main_window_desc->flags.waiting_save_ack = 0;
634 void
635 wSessionSaveClients(WScreen *scr)
642 * With XSMP, this job is done by smproxy
644 void
645 wSessionSendSaveYourself(WScreen *scr)
647 WWindow *wwin;
648 int count;
650 /* freeze client interaction with clients */
651 XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync,
652 CurrentTime);
653 XGrabPointer(dpy, scr->root_win, False, ButtonPressMask|ButtonReleaseMask,
654 GrabModeAsync, GrabModeAsync, scr->root_win, None,
655 CurrentTime);
657 clearWaitingAckState(scr);
659 count = 0;
661 /* first send SAVE_YOURSELF for everybody */
662 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
663 WWindow *mainWin;
665 mainWin = wWindowFor(wwin->main_window);
667 if (mainWin) {
668 /* if the client is a multi-window client, only send message
669 * to the main window */
670 wwin = mainWin;
673 /* make sure the SAVE_YOURSELF flag is up-to-date */
674 PropGetProtocols(wwin->client_win, &wwin->protocols);
676 if (wwin->protocols.SAVE_YOURSELF) {
677 if (!wwin->flags.waiting_save_ack) {
678 wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
680 wwin->flags.waiting_save_ack = 1;
681 count++;
683 } else {
684 wwin->flags.waiting_save_ack = 0;
688 /* then wait for acknowledge */
689 while (count > 0) {
693 XUngrabPointer(dpy, CurrentTime);
694 XUngrabKeyboard(dpy, CurrentTime);
695 XFlush(dpy);
699 #ifdef XSMP_ENABLED
701 * With full session management support, the part of WMState
702 * that store client window state will become obsolete (maybe we can reuse
703 * the old code too),
704 * but we still need to store state info like the dock and workspaces.
705 * It is better to keep dock/wspace info in WMState because the user
706 * might want to keep the dock configuration while not wanting to
707 * resume a previously saved session.
708 * So, wmaker specific state info can be saved in
709 * ~/GNUstep/.AppInfo/WindowMaker/statename.state
710 * Its better to not put it in the defaults directory because:
711 * - its not a defaults file (having domain names like wmaker0089504baa
712 * in the defaults directory wouldn't be very neat)
713 * - this state file is not meant to be edited by users
715 * The old session code will become obsolete. When wmaker is
716 * compiled with R6 sm support compiled in, itll be better to
717 * use a totally rewritten state saving code, but we can keep
718 * the current code for when XSMP_ENABLED is not compiled in.
720 * This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
721 * messages), but itll be better.
723 * -readme
727 static char*
728 getWindowRole(Window window)
730 XTextProperty prop;
731 static Atom atom = 0;
733 if (!atom)
734 atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
736 if (XGetTextProperty(dpy, window, &prop, atom)) {
737 if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
738 return prop.value;
741 return NULL;
747 * Saved Info:
749 * WM_WINDOW_ROLE
751 * WM_CLASS.instance
752 * WM_CLASS.class
753 * WM_NAME
754 * WM_COMMAND
756 * geometry
757 * state = (miniaturized, shaded, etc)
758 * attribute
759 * workspace #
760 * app state = (which dock, hidden)
761 * window shortcut #
764 static proplist_t
765 makeAppState(WWindow *wwin)
767 WApplication *wapp;
768 proplist_t state;
769 WScreen *scr = wwin->screen_ptr;
771 state = PLMakeArrayWithElements(NULL, NULL);
773 wapp = wApplicationOf(wwin->main_window);
775 if (wapp) {
776 if (wapp->app_icon && wapp->app_icon->dock) {
778 if (wapp->app_icon->dock == scr->dock) {
779 PLAppendArrayElement(state, PLMakeString("Dock"));
780 } else {
781 int i;
783 for(i=0; i<scr->workspace_count; i++)
784 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
785 break;
787 assert(i < scr->workspace_count);
789 PLAppendArrayElement(state,
790 PLMakeString(scr->workspaces[i]->name));
794 PLAppendArrayElement(state, PLMakeString(wapp->hidden ? "1" : "0"));
797 return state;
802 Bool
803 wSessionGetStateFor(WWindow *wwin, WSessionData *state)
805 char *str;
806 proplist_t slist;
807 proplist_t elem;
808 proplist_t value;
809 int index = 0;
811 index = 3;
813 /* geometry */
814 value = PLGetArrayElement(slist, index++);
815 str = PLGetString(value);
817 sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
818 &state->width, &state->height,
819 &state->user_changed_width, &state->user_changed_height);
822 /* state */
823 value = PLGetArrayElement(slist, index++);
824 str = PLGetString(value);
826 sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded,
827 &state->maximized);
830 /* attributes */
831 value = PLGetArrayElement(slist, index++);
832 str = PLGetString(value);
834 getAttributeState(str, &state->mflags, &state->flags);
837 /* workspace */
838 value = PLGetArrayElement(slist, index++);
839 str = PLGetString(value);
841 sscanf(str, "%i", &state->workspace);
844 /* app state (repeated for all windows of the app) */
845 value = PLGetArrayElement(slist, index++);
846 str = PLGetString(value);
848 /* ???? */
850 /* shortcuts */
851 value = PLGetArrayElement(slist, index++);
852 str = PLGetString(value);
854 sscanf(str, "%i", &state->shortcuts);
859 static proplist_t
860 makeAttributeState(WWindow *wwin)
862 unsigned int data1, data2;
863 char buffer[256];
865 #define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
866 ? (wwin)->user_flags.FLAG : -1)
868 sprintf(buffer,
869 "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
870 W_FLAG(no_titlebar),
871 W_FLAG(no_resizable),
872 W_FLAG(no_closable),
873 W_FLAG(no_miniaturizable),
874 W_FLAG(no_resizebar),
875 W_FLAG(no_close_button),
876 W_FLAG(no_miniaturize_button),
878 W_FLAG(broken_close),
879 W_FLAG(kill_close),
881 W_FLAG(no_shadeable),
882 W_FLAG(omnipresent),
883 W_FLAG(skip_window_list),
884 W_FLAG(floating),
885 W_FLAG(sunken),
886 W_FLAG(no_bind_keys),
887 W_FLAG(no_bind_mouse),
888 W_FLAG(no_hide_others),
889 W_FLAG(no_appicon),
890 W_FLAG(dont_move_off),
891 W_FLAG(no_focusable),
892 W_FLAG(always_user_icon),
893 W_FLAG(start_miniaturized),
894 W_FLAG(start_hidden),
895 W_FLAG(start_maximized),
896 W_FLAG(dont_save_session),
897 W_FLAG(emulate_appicon));
899 return PLMakeString(buffer);
903 static void
904 appendStringInArray(proplist_t array, char *str)
906 proplist_t val;
908 val = PLMakeString(str);
909 PLAppendArrayElement(array, val);
910 PLRelease(val);
914 static proplist_t
915 makeClientState(WWindow *wwin)
917 proplist_t state;
918 proplist_t tmp;
919 char *str;
920 char buffer[256];
921 int i;
922 unsigned shortcuts;
924 state = PLMakeArrayWithElements(NULL, NULL);
926 /* WM_WINDOW_ROLE */
927 str = getWindowRole(wwin->client_win);
928 if (!str)
929 appendStringInArray(state, "");
930 else {
931 appendStringInArray(state, str);
932 XFree(str);
935 /* WM_CLASS.instance */
936 appendStringInArray(state, wwin->wm_instance);
938 /* WM_CLASS.class */
939 appendStringInArray(state, wwin->wm_class);
941 /* WM_NAME */
942 if (wwin->flags.wm_name_changed)
943 appendStringInArray(state, "");
944 else
945 appendStringInArray(state, wwin->frame->name);
947 /* geometry */
948 sprintf(buffer, "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
949 wwin->client.width, wwin->client.height,
950 wwin->flags.user_changed_width, wwin->flags.user_changed_height);
951 appendStringInArray(state, buffer);
953 /* state */
954 sprintf(buffer, "%i %i %i", wwin->flags.miniaturized,
955 wwin->flags.shaded, wwin->flags.maximized);
956 appendStringInArray(state, buffer);
958 /* attributes */
959 tmp = makeAttributeState(wwin);
960 PLAppendArrayElement(state, tmp);
961 PLRelease(tmp);
963 /* workspace */
964 sprintf(buffer, "%i", wwin->frame->workspace);
965 appendStringInArray(state, buffer);
967 /* app state (repeated for all windows of the app) */
968 tmp = makeAppState(wwin);
969 PLAppendArrayElement(state, tmp);
970 PLRelease(tmp);
972 /* shortcuts */
973 shortcuts = 0;
974 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
975 if (scr->shortcutWindow[i] == wwin) {
976 shortcuts |= 1 << i;
979 sprintf(buffer, "%ui", shortcuts);
980 appendStringInArray(tmp, buffer);
982 return state;
986 static void
987 smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
989 SmProp props[4];
990 SmPropValue prop1val, prop2val, prop3val, prop4val;
991 char **argv = (char**)client_data;
992 int argc;
993 int i, j;
994 Bool ok = False;
995 char *statefile = NULL;
996 char *prefix;
997 Bool gsPrefix = False;
998 char *discardCmd = NULL;
999 time_t t;
1000 proplist_t state;
1002 #ifdef DEBUG1
1003 puts("received SaveYourselfPhase2 SM message");
1004 #endif
1006 /* save session state */
1008 /* the file that will contain the state */
1009 prefix = getenv("SM_SAVE_DIR");
1010 if (!prefix) {
1011 prefix = wusergnusteppath();
1012 if (prefix)
1013 gsPrefix = True;
1015 if (!prefix) {
1016 prefix = getenv("HOME");
1018 if (!prefix)
1019 prefix = ".";
1021 statefile = malloc(strlen(prefix)+64);
1022 if (!statefile) {
1023 wwarning(_("out of memory while saving session state"));
1024 goto fail;
1027 t = time();
1028 i = 0;
1029 do {
1030 if (gsPrefix)
1031 sprintf(statefile, "%s/.AppInfo/WindowMaker/wmaker.%l%i.state",
1032 prefix, t, i);
1033 else
1034 sprintf(statefile, "%s/wmaker.%l%i.state", prefix, t, i);
1035 i++;
1036 } while (access(F_OK, statefile)!=-1);
1038 /* save the states of all windows we're managing */
1039 state = PLMakeArrayFromElements(NULL, NULL);
1042 * Format:
1044 * state_file ::= dictionary with version_info ; state
1045 * version_info ::= 'version' = '1';
1046 * state ::= 'state' = array of screen_info
1047 * screen_info ::= array of (screen number, window_info, window_info, ...)
1048 * window_info ::=
1050 for (i=0; i<wScreenCount; i++) {
1051 WScreen *scr;
1052 WWindow *wwin;
1053 char buf[32];
1054 proplist_t pscreen;
1056 scr = wScreenWithNumber(i);
1058 sprintf(buf, "%i", scr->screen);
1059 pscreen = PLMakeArrayFromElements(PLMakeString(buf), NULL);
1061 wwin = scr->focused_window;
1062 while (wwin) {
1063 proplist_t pwindow;
1065 pwindow = makeClientState(wwin);
1066 PLAppendArrayElement(pscreen, pwindow);
1068 wwin = wwin->prev;
1071 PLAppendArrayElement(state, pscreen);
1075 proplist_t statefile;
1077 statefile = PLMakeDictionaryFromEntries(PLMakeString("Version"),
1078 PLMakeString("1.0"),
1080 PLMakeString("Screens"),
1081 state,
1083 NULL);
1085 PLSetFilename(statefile, PLMakeString(statefile));
1086 PLSave(statefile, NO);
1088 PLRelease(statefile);
1091 /* set the remaining properties that we didn't set at
1092 * startup time */
1094 for (argc=0, i=0; argv[i]!=NULL; i++) {
1095 if (strcmp(argv[i], "-clientid")==0
1096 || strcmp(argv[i], "-restore")==0) {
1097 i++;
1098 } else {
1099 argc++;
1103 prop[0].name = SmRestartCommand;
1104 prop[0].type = SmLISTofARRAY8;
1105 prop[0].vals = malloc(sizeof(SmPropValue)*(argc+4));
1106 prop[0].num_vals = argc+4;
1108 prop[1].name = SmCloneCommand;
1109 prop[1].type = SmLISTofARRAY8;
1110 prop[1].vals = malloc(sizeof(SmPropValue)*(argc));
1111 prop[1].num_vals = argc;
1113 if (!prop[0].vals || !prop[1].vals) {
1114 wwarning(_("end of memory while saving session state"));
1115 goto fail;
1118 for (j=0, i=0; i<argc+4; i++) {
1119 if (strcmp(argv[i], "-clientid")==0
1120 || strcmp(argv[i], "-restore")==0) {
1121 i++;
1122 } else {
1123 prop[0].vals[j].value = argv[i];
1124 prop[0].vals[j].length = strlen(argv[i]);
1125 prop[1].vals[j].value = argv[i];
1126 prop[1].vals[j].length = strlen(argv[i]);
1127 j++;
1130 prop[0].vals[j].value = "-clientid";
1131 prop[0].vals[j].length = 9;
1132 j++;
1133 prop[0].vals[j].value = sClientID;
1134 prop[0].vals[j].length = strlen(sClientID);
1135 j++;
1136 prop[0].vals[j].value = "-restore";
1137 prop[0].vals[j].length = 11;
1138 j++;
1139 prop[0].vals[j].value = statefile;
1140 prop[0].vals[j].length = strlen(statefile);
1142 discardCmd = malloc(strlen(statefile)+8);
1143 if (!discardCmd)
1144 goto fail;
1145 sprintf(discardCmd, "rm %s", statefile);
1146 prop[2].name = SmDiscardCommand;
1147 prop[2].type = SmARRAY8;
1148 prop[2].vals[0] = discardCmd;
1149 prop[2].num_vals = 1;
1151 SmcSetProperties(sSMCConn, 3, prop);
1153 ok = True;
1154 fail:
1155 SmcSaveYourselfDone(smc_conn, ok);
1157 if (prop[0].vals)
1158 free(prop[0].vals);
1159 if (prop[1].vals)
1160 free(prop[1].vals);
1161 if (discardCmd)
1162 free(discardCmd);
1164 if (!ok) {
1165 remove(statefile);
1167 if (statefile)
1168 free(statefile);
1172 static void
1173 smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
1174 Bool shutdown, int interact_style, Bool fast)
1176 #ifdef DEBUG1
1177 puts("received SaveYourself SM message");
1178 #endif
1180 if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc,
1181 client_data)) {
1183 SmcSaveYourselfDone(smc_conn, False);
1184 sWaitingPhase2 = False;
1185 } else {
1186 #ifdef DEBUG1
1187 puts("successfull request of SYS phase 2");
1188 #endif
1189 sWaitingPhase2 = True;
1194 static void
1195 smDieProc(SmcConn smc_conn, SmPointer client_data)
1197 #ifdef DEBUG1
1198 puts("received Die SM message");
1199 #endif
1201 wSessionDisconnectManager();
1203 Shutdown(WSExitMode, True);
1208 static void
1209 smSaveCompleteProc(SmcConn smc_conn)
1211 /* it means that we can resume doing things that can change our state */
1212 #ifdef DEBUG1
1213 puts("received SaveComplete SM message");
1214 #endif
1218 static void
1219 smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
1221 if (sWaitingPhase2) {
1223 sWaitingPhase2 = False;
1225 SmcSaveYourselfDone(smc_conn, False);
1230 static void
1231 iceMessageProc(int fd, int mask, void *clientData)
1233 IceConn iceConn = (IceConn)clientData;
1235 IceProcessMessages(iceConn, NULL, NULL);
1239 static void
1240 iceIOErrorHandler(IceConnection ice_conn)
1242 /* This is not fatal but can mean the session manager exited.
1243 * If the session manager exited normally we would get a
1244 * Die message, so this probably means an abnormal exit.
1245 * If the sm was the last client of session, then we'll die
1246 * anyway, otherwise we can continue doing our stuff.
1248 wwarning(_("connection to the session manager was lost"));
1249 wSessionDisconnectManager();
1253 void
1254 wSessionConnectManager(char **argv, int argc)
1256 IceConn iceConn;
1257 char *previous_id = NULL;
1258 char buffer[256];
1259 SmcCallbacks callbacks;
1260 unsigned long mask;
1261 char uid[32];
1262 char pid[32];
1263 SmProp props[4];
1264 SmPropValue prop1val, prop2val, prop3val, prop4val;
1265 char restartStyle;
1266 int i;
1268 mask = SmcSaveYourselfProcMask|SmcDieProcMask|SmcSaveCompleteProcMask
1269 |SmcShutdownCancelledProcMask;
1271 callbacks.save_yourself.callback = smSaveYourselfProc;
1272 callbacks.save_yourself.client_data = argv;
1274 callbacks.die.callback = smDieProc;
1275 callbacks.die.client_data = NULL;
1277 callbacks.save_complete.callback = smSaveCompleteProc;
1278 callbacks.save_complete.client_data = NULL;
1280 callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
1281 callbacks.shutdown_cancelled.client_data = NULL;
1283 for (i=0; i<argc; i++) {
1284 if (strcmp(argv[i], "-clientid")==0) {
1285 previous_id = argv[i+1];
1286 break;
1290 /* connect to the session manager */
1291 sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
1292 mask, &callbacks, previous_id,
1293 &sClientID, 255, buffer);
1294 if (!sSMCConn) {
1295 return;
1297 #ifdef DEBUG1
1298 puts("connected to the session manager");
1299 #endif
1301 /* IceSetIOErrorHandler(iceIOErrorHandler);*/
1303 /* check for session manager clients */
1304 iceConn = SmcGetIceConnection(smcConn);
1306 if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
1307 wsyserror("error setting close-on-exec flag for ICE connection");
1310 sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn),
1311 WIReadMask, iceMessageProc, iceConn);
1313 /* setup information about ourselves */
1315 /* program name */
1316 prop1val.value = argv[0];
1317 prop1val.length = strlen(argv[0]);
1318 prop[0].name = SmProgram;
1319 prop[0].type = SmARRAY8;
1320 prop[0].num_vals = 1;
1321 prop[0].vals = &prop1val;
1323 /* The XSMP doc from X11R6.1 says it contains the user name,
1324 * but every client implementation I saw places the uid # */
1325 sprintf(uid, "%i", getuid());
1326 prop2val.value = uid;
1327 prop2val.length = strlen(uid);
1328 prop[1].name = SmUserID;
1329 prop[1].type = SmARRAY8;
1330 prop[1].num_vals = 1;
1331 prop[1].vals = &prop2val;
1333 /* Restart style. We should restart only if we were running when
1334 * the previous session finished. */
1335 restartStyle = SmRestartIfRunning;
1336 prop3val.value = &restartStyle;
1337 prop3val.length = 1;
1338 prop[2].name = SmRestartStyleHint;
1339 prop[2].type = SmCARD8;
1340 prop[2].num_vals = 1;
1341 prop[2].vals = &prop3val;
1343 /* Our PID. Not required but might be usefull */
1344 sprintf(pid, "%i", getpid());
1345 prop4val.value = pid;
1346 prop4val.length = strlen(pid);
1347 prop[3].name = SmProcessID;
1348 prop[3].type = SmARRAY8;
1349 prop[3].num_vals = 1;
1350 prop[3].vals = &prop4val;
1352 /* we'll set the rest of the hints later */
1354 SmcSetProperties(sSMCConn, 4, props);
1359 void
1360 wSessionDisconnectManager(void)
1362 if (sSMCConn) {
1363 WMDeleteInputHandler(sSMInputHandler);
1364 sSMInputHandler = NULL;
1366 SmcCloseConnection(sSMCConn, 0, NULL);
1367 sSMCConn = NULL;
1371 void
1372 wSessionRequestShutdown(void)
1374 /* request a shutdown to the session manager */
1375 if (sSMCConn)
1376 SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny,
1377 False, True);
1381 Bool
1382 wSessionIsManaged(void)
1384 return sSMCConn!=NULL;
1387 #endif /* !XSMP_ENABLED */