- made deiconification not automatically focus window in sloppy focus
[wmaker-crm.git] / src / session.c
blob5339058ba6f44ebd65ca465be79b2e5b5286d85e
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"
94 #include "list.h"
96 #include <proplist.h>
98 /** Global **/
100 extern Atom _XA_WM_SAVE_YOURSELF;
102 extern Time LastTimestamp;
104 #ifdef XSMP_ENABLED
106 extern int wScreenCount;
108 /* requested for SaveYourselfPhase2 */
109 static Bool sWaitingPhase2 = False;
111 static SmcConn sSMCConn = NULL;
113 static WMHandlerID sSMInputHandler = NULL;
115 /* our SM client ID */
116 static char *sClientID = NULL;
117 #endif
120 static proplist_t sApplications = NULL;
121 static proplist_t sCommand;
122 static proplist_t sName;
123 static proplist_t sHost;
124 static proplist_t sWorkspace;
125 static proplist_t sShaded;
126 static proplist_t sMiniaturized;
127 static proplist_t sHidden;
128 static proplist_t sGeometry;
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");
152 sYes = PLMakeString("Yes");
153 sNo = PLMakeString("No");
158 static int
159 getBool(proplist_t value)
161 char *val;
163 if (!PLIsString(value)) {
164 return 0;
166 if (!(val = PLGetString(value))) {
167 return 0;
170 if ((val[1]=='\0' && (val[0]=='y' || val[0]=='Y'))
171 || strcasecmp(val, "YES")==0) {
173 return 1;
174 } else if ((val[1]=='\0' && (val[0]=='n' || val[0]=='N'))
175 || strcasecmp(val, "NO")==0) {
176 return 0;
177 } else {
178 int i;
179 if (sscanf(val, "%i", &i)==1) {
180 return (i!=0);
181 } else {
182 wwarning(_("can't convert \"%s\" to boolean"), val);
183 return 0;
190 static proplist_t
191 makeWindowState(WWindow *wwin, WApplication *wapp)
193 WScreen *scr = wwin->screen_ptr;
194 Window win;
195 int argc;
196 char **argv;
197 char *class, *instance, *command=NULL, buffer[256];
198 proplist_t win_state, cmd, name, workspace;
199 proplist_t shaded, miniaturized, hidden, geometry;
200 proplist_t dock;
202 if (wwin->main_window!=None && wwin->main_window!=wwin->client_win)
203 win = wwin->main_window;
204 else
205 win = wwin->client_win;
207 if (XGetCommand(dpy, win, &argv, &argc) && argc>0) {
208 command = FlattenStringList(argv, argc);
209 XFreeStringList(argv);
211 if (!command)
212 return NULL;
214 if (PropGetWMClass(win, &class, &instance)) {
215 if (class && instance)
216 sprintf(buffer, "%s.%s", instance, class);
217 else if (instance)
218 sprintf(buffer, "%s", instance);
219 else if (class)
220 sprintf(buffer, ".%s", class);
221 else
222 sprintf(buffer, ".");
224 name = PLMakeString(buffer);
225 cmd = PLMakeString(command);
226 /*sprintf(buffer, "%d", wwin->frame->workspace+1);
227 workspace = PLMakeString(buffer);*/
228 workspace = PLMakeString(scr->workspaces[wwin->frame->workspace]->name);
229 shaded = wwin->flags.shaded ? sYes : sNo;
230 miniaturized = wwin->flags.miniaturized ? sYes : sNo;
231 hidden = wwin->flags.hidden ? sYes : sNo;
232 sprintf(buffer, "%ix%i+%i+%i", wwin->client.width, wwin->client.height,
233 wwin->frame_x, wwin->frame_y);
234 geometry = PLMakeString(buffer);
236 win_state = PLMakeDictionaryFromEntries(sName, name,
237 sCommand, cmd,
238 sWorkspace, workspace,
239 sShaded, shaded,
240 sMiniaturized, miniaturized,
241 sHidden, hidden,
242 sGeometry, geometry,
243 NULL);
245 PLRelease(name);
246 PLRelease(cmd);
247 PLRelease(workspace);
248 PLRelease(geometry);
249 if (wapp && wapp->app_icon && wapp->app_icon->dock) {
250 int i;
251 char *name;
252 if (wapp->app_icon->dock == scr->dock) {
253 name="Dock";
254 } else {
255 for(i=0; i<scr->workspace_count; i++)
256 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
257 break;
258 assert( i < scr->workspace_count);
259 /*n = i+1;*/
260 name = scr->workspaces[i]->name;
262 dock = PLMakeString(name);
263 PLInsertDictionaryEntry(win_state, sDock, dock);
264 PLRelease(dock);
266 } else {
267 win_state = NULL;
270 if (instance) XFree(instance);
271 if (class) XFree(class);
272 if (command) free(command);
274 return win_state;
278 void
279 wSessionSaveState(WScreen *scr)
281 WWindow *wwin = scr->focused_window;
282 proplist_t win_info, wks;
283 proplist_t list=NULL;
284 LinkedList *wapp_list=NULL;
287 make_keys();
289 if (!scr->session_state) {
290 scr->session_state = PLMakeDictionaryFromEntries(NULL, NULL, NULL);
291 if (!scr->session_state)
292 return;
295 list = PLMakeArrayFromElements(NULL);
297 while (wwin) {
298 WApplication *wapp=wApplicationOf(wwin->main_window);
300 if (wwin->transient_for==None && list_find(wapp_list, wapp)==NULL
301 && !WFLAGP(wwin, dont_save_session)) {
302 /* A entry for this application was not yet saved. Save one. */
303 if ((win_info = makeWindowState(wwin, wapp))!=NULL) {
304 list = PLAppendArrayElement(list, win_info);
305 PLRelease(win_info);
306 /* If we were succesful in saving the info for this window
307 * add the application the window belongs to, to the
308 * application list, so no multiple entries for the same
309 * application are saved.
311 wapp_list = list_cons(wapp, wapp_list);
314 wwin = wwin->prev;
316 PLRemoveDictionaryEntry(scr->session_state, sApplications);
317 PLInsertDictionaryEntry(scr->session_state, sApplications, list);
318 PLRelease(list);
320 wks = PLMakeString(scr->workspaces[scr->current_workspace]->name);
321 PLInsertDictionaryEntry(scr->session_state, sWorkspace, wks);
322 PLRelease(wks);
324 list_free(wapp_list);
328 void
329 wSessionClearState(WScreen *scr)
331 make_keys();
333 if (!scr->session_state)
334 return;
336 PLRemoveDictionaryEntry(scr->session_state, sApplications);
337 PLRemoveDictionaryEntry(scr->session_state, sWorkspace);
341 static pid_t
342 execCommand(WScreen *scr, char *command, char *host)
344 pid_t pid;
345 char **argv;
346 int argc;
348 ParseCommand(command, &argv, &argc);
350 if (argv==NULL) {
351 return 0;
354 if ((pid=fork())==0) {
355 char **args;
356 int i;
358 SetupEnvironment(scr);
360 args = malloc(sizeof(char*)*(argc+1));
361 if (!args)
362 exit(111);
363 for (i=0; i<argc; i++) {
364 args[i] = argv[i];
366 args[argc] = NULL;
367 execvp(argv[0], args);
368 exit(111);
370 while (argc > 0)
371 free(argv[--argc]);
372 free(argv);
373 return pid;
377 static WSavedState*
378 getWindowState(WScreen *scr, proplist_t win_state)
380 WSavedState *state = wmalloc(sizeof(WSavedState));
381 proplist_t value;
382 char *tmp;
383 int i;
385 memset(state, 0, sizeof(WSavedState));
386 state->workspace = -1;
387 value = PLGetDictionaryEntry(win_state, sWorkspace);
388 if (value && PLIsString(value)) {
389 tmp = PLGetString(value);
390 if (sscanf(tmp, "%i", &state->workspace)!=1) {
391 state->workspace = -1;
392 for (i=0; i < scr->workspace_count; i++) {
393 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
394 state->workspace = i;
395 break;
398 } else {
399 state->workspace--;
402 if ((value = PLGetDictionaryEntry(win_state, sShaded))!=NULL)
403 state->shaded = getBool(value);
404 if ((value = PLGetDictionaryEntry(win_state, sMiniaturized))!=NULL)
405 state->miniaturized = getBool(value);
406 if ((value = PLGetDictionaryEntry(win_state, sHidden))!=NULL)
407 state->hidden = getBool(value);
409 value = PLGetDictionaryEntry(win_state, sGeometry);
410 if (value && PLIsString(value)) {
411 if (sscanf(PLGetString(value), "%ix%i+%i+%i",
412 &state->w, &state->h, &state->x, &state->y)==4 &&
413 (state->w>0 && state->h>0)) {
414 state->use_geometry = 1;
415 } else if (sscanf(PLGetString(value), "%i,%i,%i,%i",
416 &state->x, &state->y, &state->w, &state->h)==4 &&
417 (state->w>0 && state->h>0)) {
418 /* TODO: remove redundant sscanf() in version 0.20.x */
419 state->use_geometry = 1;
424 return state;
428 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
430 void
431 wSessionRestoreState(WScreen *scr)
433 WSavedState *state;
434 char *instance, *class, *command, *host;
435 proplist_t win_info, apps, cmd, value;
436 pid_t pid;
437 int i, count;
438 WDock *dock;
439 WAppIcon *btn=NULL;
440 int j, n, found;
441 char *tmp;
443 make_keys();
445 if (!scr->session_state)
446 return;
448 PLSetStringCmpHook(NULL);
450 apps = PLGetDictionaryEntry(scr->session_state, sApplications);
451 if (!apps)
452 return;
454 count = PLGetNumberOfElements(apps);
455 if (count==0)
456 return;
458 for (i=0; i<count; i++) {
459 win_info = PLGetArrayElement(apps, i);
461 cmd = PLGetDictionaryEntry(win_info, sCommand);
462 if (!cmd || !PLIsString(cmd) || !(command = PLGetString(cmd))) {
463 continue;
466 value = PLGetDictionaryEntry(win_info, sName);
467 if (!value)
468 continue;
470 ParseWindowName(value, &instance, &class, "session");
471 if (!instance && !class)
472 continue;
474 value = PLGetDictionaryEntry(win_info, sHost);
475 if (value && PLIsString(value))
476 host = PLGetString(value);
477 else
478 host = NULL;
480 state = getWindowState(scr, win_info);
482 dock = NULL;
483 value = PLGetDictionaryEntry(win_info, sDock);
484 if (value && PLIsString(value) && (tmp = PLGetString(value))!=NULL) {
485 if (sscanf(tmp, "%i", &n)!=1) {
486 if (!strcasecmp(tmp, "DOCK")) {
487 dock = scr->dock;
488 } else {
489 for (j=0; j < scr->workspace_count; j++) {
490 if (strcmp(scr->workspaces[j]->name, tmp)==0) {
491 dock = scr->workspaces[j]->clip;
492 break;
496 } else {
497 if (n == 0) {
498 dock = scr->dock;
499 } else if (n>0 && n<=scr->workspace_count) {
500 dock = scr->workspaces[n-1]->clip;
505 found = 0;
506 if (dock!=NULL) {
507 for (j=0; j<dock->max_icons; j++) {
508 btn = dock->icon_array[j];
509 if (btn && SAME(instance, btn->wm_instance) &&
510 SAME(class, btn->wm_class) &&
511 SAME(command, btn->command)) {
512 found = 1;
513 break;
518 if (found) {
519 wDockLaunchWithState(dock, btn, state);
520 } else if ((pid = execCommand(scr, command, host)) > 0) {
521 wWindowAddSavedState(instance, class, command, pid, state);
522 } else {
523 free(state);
526 if (instance) free(instance);
527 if (class) free(class);
529 /* clean up */
530 PLSetStringCmpHook(StringCompareHook);
534 void
535 wSessionRestoreLastWorkspace(WScreen *scr)
537 proplist_t wks;
538 int w, i;
539 char *tmp;
541 make_keys();
543 if (!scr->session_state)
544 return;
546 PLSetStringCmpHook(NULL);
548 wks = PLGetDictionaryEntry(scr->session_state, sWorkspace);
549 if (!wks || !PLIsString(wks))
550 return;
552 tmp = PLGetString(wks);
554 /* clean up */
555 PLSetStringCmpHook(StringCompareHook);
557 if (sscanf(tmp, "%i", &w)!=1) {
558 w = -1;
559 for (i=0; i < scr->workspace_count; i++) {
560 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
561 w = i;
562 break;
565 } else {
566 w--;
569 if (w!=scr->current_workspace && w<scr->workspace_count) {
570 wWorkspaceChange(scr, w);
575 static void
576 clearWaitingAckState(WScreen *scr)
578 WWindow *wwin;
579 WApplication *wapp;
581 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
582 wwin->flags.waiting_save_ack = 0;
583 if (wwin->main_window != None) {
584 wapp = wApplicationOf(wwin->main_window);
585 if (wapp)
586 wapp->main_window_desc->flags.waiting_save_ack = 0;
592 void
593 wSessionSaveClients(WScreen *scr)
600 * With XSMP, this job is done by smproxy
602 void
603 wSessionSendSaveYourself(WScreen *scr)
605 WWindow *wwin;
606 int count;
608 /* freeze client interaction with clients */
609 XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync,
610 CurrentTime);
611 XGrabPointer(dpy, scr->root_win, False, ButtonPressMask|ButtonReleaseMask,
612 GrabModeAsync, GrabModeAsync, scr->root_win, None,
613 CurrentTime);
615 clearWaitingAckState(scr);
617 count = 0;
619 /* first send SAVE_YOURSELF for everybody */
620 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
621 WWindow *mainWin;
623 mainWin = wWindowFor(wwin->main_window);
625 if (mainWin) {
626 /* if the client is a multi-window client, only send message
627 * to the main window */
628 wwin = mainWin;
631 /* make sure the SAVE_YOURSELF flag is up-to-date */
632 PropGetProtocols(wwin->client_win, &wwin->protocols);
634 if (wwin->protocols.SAVE_YOURSELF) {
635 if (!wwin->flags.waiting_save_ack) {
636 wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
638 wwin->flags.waiting_save_ack = 1;
639 count++;
641 } else {
642 wwin->flags.waiting_save_ack = 0;
646 /* then wait for acknowledge */
647 while (count > 0) {
651 XUngrabPointer(dpy, CurrentTime);
652 XUngrabKeyboard(dpy, CurrentTime);
653 XFlush(dpy);
657 #ifdef XSMP_ENABLED
659 * With full session management support, the part of WMState
660 * that store client window state will become obsolete (maybe we can reuse
661 * the old code too),
662 * but we still need to store state info like the dock and workspaces.
663 * It is better to keep dock/wspace info in WMState because the user
664 * might want to keep the dock configuration while not wanting to
665 * resume a previously saved session.
666 * So, wmaker specific state info can be saved in
667 * ~/GNUstep/.AppInfo/WindowMaker/statename.state
668 * Its better to not put it in the defaults directory because:
669 * - its not a defaults file (having domain names like wmaker0089504baa
670 * in the defaults directory wouldn't be very neat)
671 * - this state file is not meant to be edited by users
673 * The old session code will become obsolete. When wmaker is
674 * compiled with R6 sm support compiled in, itll be better to
675 * use a totally rewritten state saving code, but we can keep
676 * the current code for when XSMP_ENABLED is not compiled in.
678 * This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
679 * messages), but itll be better.
681 * -readme
685 static char*
686 getWindowRole(Window window)
688 XTextProperty prop;
689 static Atom atom = 0;
691 if (!atom)
692 atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
694 if (XGetTextProperty(dpy, window, &prop, atom)) {
695 if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
696 return prop.value;
699 return NULL;
705 * Saved Info:
707 * WM_WINDOW_ROLE
709 * WM_CLASS.instance
710 * WM_CLASS.class
711 * WM_NAME
712 * WM_COMMAND
714 * geometry
715 * state = (miniaturized, shaded, etc)
716 * attribute
717 * workspace #
718 * app state = (which dock, hidden)
719 * window shortcut #
722 static proplist_t
723 makeAppState(WWindow *wwin)
725 WApplication *wapp;
726 proplist_t state;
727 WScreen *scr = wwin->screen_ptr;
729 state = PLMakeArrayWithElements(NULL, NULL);
731 wapp = wApplicationOf(wwin->main_window);
733 if (wapp) {
734 if (wapp->app_icon && wapp->app_icon->dock) {
736 if (wapp->app_icon->dock == scr->dock) {
737 PLAppendArrayElement(state, PLMakeString("Dock"));
738 } else {
739 int i;
741 for(i=0; i<scr->workspace_count; i++)
742 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
743 break;
745 assert(i < scr->workspace_count);
747 PLAppendArrayElement(state,
748 PLMakeString(scr->workspaces[i]->name));
752 PLAppendArrayElement(state, PLMakeString(wapp->hidden ? "1" : "0"));
755 return state;
760 Bool
761 wSessionGetStateFor(WWindow *wwin, WSessionData *state)
763 char *str;
764 proplist_t slist;
765 proplist_t elem;
766 proplist_t value;
767 int index = 0;
769 index = 3;
771 /* geometry */
772 value = PLGetArrayElement(slist, index++);
773 str = PLGetString(value);
775 sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
776 &state->width, &state->height,
777 &state->user_changed_width, &state->user_changed_height);
780 /* state */
781 value = PLGetArrayElement(slist, index++);
782 str = PLGetString(value);
784 sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded,
785 &state->maximized);
788 /* attributes */
789 value = PLGetArrayElement(slist, index++);
790 str = PLGetString(value);
792 getAttributeState(str, &state->mflags, &state->flags);
795 /* workspace */
796 value = PLGetArrayElement(slist, index++);
797 str = PLGetString(value);
799 sscanf(str, "%i", &state->workspace);
802 /* app state (repeated for all windows of the app) */
803 value = PLGetArrayElement(slist, index++);
804 str = PLGetString(value);
806 /* ???? */
808 /* shortcuts */
809 value = PLGetArrayElement(slist, index++);
810 str = PLGetString(value);
812 sscanf(str, "%i", &state->shortcuts);
817 static proplist_t
818 makeAttributeState(WWindow *wwin)
820 unsigned int data1, data2;
821 char buffer[256];
823 #define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
824 ? (wwin)->user_flags.FLAG : -1)
826 sprintf(buffer,
827 "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
828 W_FLAG(no_titlebar),
829 W_FLAG(no_resizable),
830 W_FLAG(no_closable),
831 W_FLAG(no_miniaturizable),
832 W_FLAG(no_resizebar),
833 W_FLAG(no_close_button),
834 W_FLAG(no_miniaturize_button),
836 W_FLAG(broken_close),
837 W_FLAG(kill_close),
839 W_FLAG(no_shadeable),
840 W_FLAG(omnipresent),
841 W_FLAG(skip_window_list),
842 W_FLAG(floating),
843 W_FLAG(sunken),
844 W_FLAG(no_bind_keys),
845 W_FLAG(no_bind_mouse),
846 W_FLAG(no_hide_others),
847 W_FLAG(no_appicon),
848 W_FLAG(dont_move_off),
849 W_FLAG(no_focusable),
850 W_FLAG(always_user_icon),
851 W_FLAG(start_miniaturized),
852 W_FLAG(start_hidden),
853 W_FLAG(start_maximized),
854 W_FLAG(dont_save_session),
855 W_FLAG(emulate_appicon));
857 return PLMakeString(buffer);
861 static void
862 appendStringInArray(proplist_t array, char *str)
864 proplist_t val;
866 val = PLMakeString(str);
867 PLAppendArrayElement(array, val);
868 PLRelease(val);
872 static proplist_t
873 makeClientState(WWindow *wwin)
875 proplist_t state;
876 proplist_t tmp;
877 char *str;
878 char buffer[256];
879 int i;
880 unsigned shortcuts;
882 state = PLMakeArrayWithElements(NULL, NULL);
884 /* WM_WINDOW_ROLE */
885 str = getWindowRole(wwin->client_win);
886 if (!str)
887 appendStringInArray(state, "");
888 else {
889 appendStringInArray(state, str);
890 XFree(str);
893 /* WM_CLASS.instance */
894 appendStringInArray(state, wwin->wm_instance);
896 /* WM_CLASS.class */
897 appendStringInArray(state, wwin->wm_class);
899 /* WM_NAME */
900 if (wwin->flags.wm_name_changed)
901 appendStringInArray(state, "");
902 else
903 appendStringInArray(state, wwin->frame->name);
905 /* geometry */
906 sprintf(buffer, "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
907 wwin->client.width, wwin->client.height,
908 wwin->flags.user_changed_width, wwin->flags.user_changed_height);
909 appendStringInArray(state, buffer);
911 /* state */
912 sprintf(buffer, "%i %i %i", wwin->flags.miniaturized,
913 wwin->flags.shaded, wwin->flags.maximized);
914 appendStringInArray(state, buffer);
916 /* attributes */
917 tmp = makeAttributeState(wwin);
918 PLAppendArrayElement(state, tmp);
919 PLRelease(tmp);
921 /* workspace */
922 sprintf(buffer, "%i", wwin->frame->workspace);
923 appendStringInArray(state, buffer);
925 /* app state (repeated for all windows of the app) */
926 tmp = makeAppState(wwin);
927 PLAppendArrayElement(state, tmp);
928 PLRelease(tmp);
930 /* shortcuts */
931 shortcuts = 0;
932 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
933 if (scr->shortcutWindow[i] == wwin) {
934 shortcuts |= 1 << i;
937 sprintf(buffer, "%ui", shortcuts);
938 appendStringInArray(tmp, buffer);
940 return state;
944 static void
945 smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
947 SmProp props[4];
948 SmPropValue prop1val, prop2val, prop3val, prop4val;
949 char **argv = (char**)client_data;
950 int argc;
951 int i, j;
952 Bool ok = False;
953 char *statefile = NULL;
954 char *prefix;
955 Bool gsPrefix = False;
956 char *discardCmd = NULL;
957 time_t t;
958 proplist_t state;
960 #ifdef DEBUG1
961 puts("received SaveYourselfPhase2 SM message");
962 #endif
964 /* save session state */
966 /* the file that will contain the state */
967 prefix = getenv("SM_SAVE_DIR");
968 if (!prefix) {
969 prefix = wusergnusteppath();
970 if (prefix)
971 gsPrefix = True;
973 if (!prefix) {
974 prefix = getenv("HOME");
976 if (!prefix)
977 prefix = ".";
979 statefile = malloc(strlen(prefix)+64);
980 if (!statefile) {
981 wwarning(_("out of memory while saving session state"));
982 goto fail;
985 t = time();
986 i = 0;
987 do {
988 if (gsPrefix)
989 sprintf(statefile, "%s/.AppInfo/WindowMaker/wmaker.%l%i.state",
990 prefix, t, i);
991 else
992 sprintf(statefile, "%s/wmaker.%l%i.state", prefix, t, i);
993 i++;
994 } while (access(F_OK, statefile)!=-1);
996 /* save the states of all windows we're managing */
997 state = PLMakeArrayFromElements(NULL, NULL);
1000 * Format:
1002 * state_file ::= dictionary with version_info ; state
1003 * version_info ::= 'version' = '1';
1004 * state ::= 'state' = array of screen_info
1005 * screen_info ::= array of (screen number, window_info, window_info, ...)
1006 * window_info ::=
1008 for (i=0; i<wScreenCount; i++) {
1009 WScreen *scr;
1010 WWindow *wwin;
1011 char buf[32];
1012 proplist_t pscreen;
1014 scr = wScreenWithNumber(i);
1016 sprintf(buf, "%i", scr->screen);
1017 pscreen = PLMakeArrayFromElements(PLMakeString(buf), NULL);
1019 wwin = scr->focused_window;
1020 while (wwin) {
1021 proplist_t pwindow;
1023 pwindow = makeClientState(wwin);
1024 PLAppendArrayElement(pscreen, pwindow);
1026 wwin = wwin->prev;
1029 PLAppendArrayElement(state, pscreen);
1033 proplist_t statefile;
1035 statefile = PLMakeDictionaryFromEntries(PLMakeString("Version"),
1036 PLMakeString("1.0"),
1038 PLMakeString("Screens"),
1039 state,
1041 NULL);
1043 PLSetFilename(statefile, PLMakeString(statefile));
1044 PLSave(statefile, NO);
1046 PLRelease(statefile);
1049 /* set the remaining properties that we didn't set at
1050 * startup time */
1052 for (argc=0, i=0; argv[i]!=NULL; i++) {
1053 if (strcmp(argv[i], "-clientid")==0
1054 || strcmp(argv[i], "-restore")==0) {
1055 i++;
1056 } else {
1057 argc++;
1061 prop[0].name = SmRestartCommand;
1062 prop[0].type = SmLISTofARRAY8;
1063 prop[0].vals = malloc(sizeof(SmPropValue)*(argc+4));
1064 prop[0].num_vals = argc+4;
1066 prop[1].name = SmCloneCommand;
1067 prop[1].type = SmLISTofARRAY8;
1068 prop[1].vals = malloc(sizeof(SmPropValue)*(argc));
1069 prop[1].num_vals = argc;
1071 if (!prop[0].vals || !prop[1].vals) {
1072 wwarning(_("end of memory while saving session state"));
1073 goto fail;
1076 for (j=0, i=0; i<argc+4; i++) {
1077 if (strcmp(argv[i], "-clientid")==0
1078 || strcmp(argv[i], "-restore")==0) {
1079 i++;
1080 } else {
1081 prop[0].vals[j].value = argv[i];
1082 prop[0].vals[j].length = strlen(argv[i]);
1083 prop[1].vals[j].value = argv[i];
1084 prop[1].vals[j].length = strlen(argv[i]);
1085 j++;
1088 prop[0].vals[j].value = "-clientid";
1089 prop[0].vals[j].length = 9;
1090 j++;
1091 prop[0].vals[j].value = sClientID;
1092 prop[0].vals[j].length = strlen(sClientID);
1093 j++;
1094 prop[0].vals[j].value = "-restore";
1095 prop[0].vals[j].length = 11;
1096 j++;
1097 prop[0].vals[j].value = statefile;
1098 prop[0].vals[j].length = strlen(statefile);
1100 discardCmd = malloc(strlen(statefile)+8);
1101 if (!discardCmd)
1102 goto fail;
1103 sprintf(discardCmd, "rm %s", statefile);
1104 prop[2].name = SmDiscardCommand;
1105 prop[2].type = SmARRAY8;
1106 prop[2].vals[0] = discardCmd;
1107 prop[2].num_vals = 1;
1109 SmcSetProperties(sSMCConn, 3, prop);
1111 ok = True;
1112 fail:
1113 SmcSaveYourselfDone(smc_conn, ok);
1115 if (prop[0].vals)
1116 free(prop[0].vals);
1117 if (prop[1].vals)
1118 free(prop[1].vals);
1119 if (discardCmd)
1120 free(discardCmd);
1122 if (!ok) {
1123 remove(statefile);
1125 if (statefile)
1126 free(statefile);
1130 static void
1131 smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
1132 Bool shutdown, int interact_style, Bool fast)
1134 #ifdef DEBUG1
1135 puts("received SaveYourself SM message");
1136 #endif
1138 if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc,
1139 client_data)) {
1141 SmcSaveYourselfDone(smc_conn, False);
1142 sWaitingPhase2 = False;
1143 } else {
1144 #ifdef DEBUG1
1145 puts("successfull request of SYS phase 2");
1146 #endif
1147 sWaitingPhase2 = True;
1152 static void
1153 smDieProc(SmcConn smc_conn, SmPointer client_data)
1155 #ifdef DEBUG1
1156 puts("received Die SM message");
1157 #endif
1159 wSessionDisconnectManager();
1161 Shutdown(WSExitMode, True);
1166 static void
1167 smSaveCompleteProc(SmcConn smc_conn)
1169 /* it means that we can resume doing things that can change our state */
1170 #ifdef DEBUG1
1171 puts("received SaveComplete SM message");
1172 #endif
1176 static void
1177 smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
1179 if (sWaitingPhase2) {
1181 sWaitingPhase2 = False;
1183 SmcSaveYourselfDone(smc_conn, False);
1188 static void
1189 iceMessageProc(int fd, int mask, void *clientData)
1191 IceConn iceConn = (IceConn)clientData;
1193 IceProcessMessages(iceConn, NULL, NULL);
1197 static void
1198 iceIOErrorHandler(IceConnection ice_conn)
1200 /* This is not fatal but can mean the session manager exited.
1201 * If the session manager exited normally we would get a
1202 * Die message, so this probably means an abnormal exit.
1203 * If the sm was the last client of session, then we'll die
1204 * anyway, otherwise we can continue doing our stuff.
1206 wwarning(_("connection to the session manager was lost"));
1207 wSessionDisconnectManager();
1211 void
1212 wSessionConnectManager(char **argv, int argc)
1214 IceConn iceConn;
1215 char *previous_id = NULL;
1216 char buffer[256];
1217 SmcCallbacks callbacks;
1218 unsigned long mask;
1219 char uid[32];
1220 char pid[32];
1221 SmProp props[4];
1222 SmPropValue prop1val, prop2val, prop3val, prop4val;
1223 char restartStyle;
1224 int i;
1226 mask = SmcSaveYourselfProcMask|SmcDieProcMask|SmcSaveCompleteProcMask
1227 |SmcShutdownCancelledProcMask;
1229 callbacks.save_yourself.callback = smSaveYourselfProc;
1230 callbacks.save_yourself.client_data = argv;
1232 callbacks.die.callback = smDieProc;
1233 callbacks.die.client_data = NULL;
1235 callbacks.save_complete.callback = smSaveCompleteProc;
1236 callbacks.save_complete.client_data = NULL;
1238 callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
1239 callbacks.shutdown_cancelled.client_data = NULL;
1241 for (i=0; i<argc; i++) {
1242 if (strcmp(argv[i], "-clientid")==0) {
1243 previous_id = argv[i+1];
1244 break;
1248 /* connect to the session manager */
1249 sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
1250 mask, &callbacks, previous_id,
1251 &sClientID, 255, buffer);
1252 if (!sSMCConn) {
1253 return;
1255 #ifdef DEBUG1
1256 puts("connected to the session manager");
1257 #endif
1259 /* IceSetIOErrorHandler(iceIOErrorHandler);*/
1261 /* check for session manager clients */
1262 iceConn = SmcGetIceConnection(smcConn);
1264 if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
1265 wsyserror("error setting close-on-exec flag for ICE connection");
1268 sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn),
1269 WIReadMask, iceMessageProc, iceConn);
1271 /* setup information about ourselves */
1273 /* program name */
1274 prop1val.value = argv[0];
1275 prop1val.length = strlen(argv[0]);
1276 prop[0].name = SmProgram;
1277 prop[0].type = SmARRAY8;
1278 prop[0].num_vals = 1;
1279 prop[0].vals = &prop1val;
1281 /* The XSMP doc from X11R6.1 says it contains the user name,
1282 * but every client implementation I saw places the uid # */
1283 sprintf(uid, "%i", getuid());
1284 prop2val.value = uid;
1285 prop2val.length = strlen(uid);
1286 prop[1].name = SmUserID;
1287 prop[1].type = SmARRAY8;
1288 prop[1].num_vals = 1;
1289 prop[1].vals = &prop2val;
1291 /* Restart style. We should restart only if we were running when
1292 * the previous session finished. */
1293 restartStyle = SmRestartIfRunning;
1294 prop3val.value = &restartStyle;
1295 prop3val.length = 1;
1296 prop[2].name = SmRestartStyleHint;
1297 prop[2].type = SmCARD8;
1298 prop[2].num_vals = 1;
1299 prop[2].vals = &prop3val;
1301 /* Our PID. Not required but might be usefull */
1302 sprintf(pid, "%i", getpid());
1303 prop4val.value = pid;
1304 prop4val.length = strlen(pid);
1305 prop[3].name = SmProcessID;
1306 prop[3].type = SmARRAY8;
1307 prop[3].num_vals = 1;
1308 prop[3].vals = &prop4val;
1310 /* we'll set the rest of the hints later */
1312 SmcSetProperties(sSMCConn, 4, props);
1317 void
1318 wSessionDisconnectManager(void)
1320 if (sSMCConn) {
1321 WMDeleteInputHandler(sSMInputHandler);
1322 sSMInputHandler = NULL;
1324 SmcCloseConnection(sSMCConn, 0, NULL);
1325 sSMCConn = NULL;
1329 void
1330 wSessionRequestShutdown(void)
1332 /* request a shutdown to the session manager */
1333 if (sSMCConn)
1334 SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny,
1335 False, True);
1339 Bool
1340 wSessionIsManaged(void)
1342 return sSMCConn!=NULL;
1345 #endif /* !XSMP_ENABLED */