- Fixed text in info panel for multibyte (Seiichi SATO <ssato@sh.rim.or.jp>)
[wmaker-crm.git] / src / session.c
blob72c969e0544d62b9159a00156517f17e4616407f
1 /* session.c - session state handling and R6 style session management
3 * Copyright (c) 1998-2002 Dan Pascu
4 * Copyright (c) 1998-2002 Alfredo Kojima
6 * Window Maker window manager
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
27 * If defined(XSMP_ENABLED) and session manager is running then
28 * do normal stuff
29 * else
30 * do pre-R6 session management stuff (save window state and relaunch)
32 * When doing a checkpoint:
34 * = Without XSMP
35 * Open "Stop"/status Dialog
36 * Send SAVE_YOURSELF to clients and wait for reply
37 * Save restart info
38 * Save state of clients
40 * = With XSMP
41 * Send checkpoint request to sm
43 * When exiting:
44 * -------------
46 * = Without XSMP
48 * Open "Exit Now"/status Dialog
49 * Send SAVE_YOURSELF to clients and wait for reply
50 * Save restart info
51 * Save state of clients
52 * Send DELETE to all clients
53 * When no more clients are left or user hit "Exit Now", exit
55 * = With XSMP
57 * Send Shutdown request to session manager
58 * if SaveYourself message received, save state of clients
59 * if the Die message is received, exit.
62 #include "wconfig.h"
64 #include <X11/Xlib.h>
65 #include <X11/Xutil.h>
67 #ifdef XSMP_ENABLED
68 #include <X11/SM/SMlib.h>
69 #endif
71 #include <stdlib.h>
72 #include <stdio.h>
73 #include <string.h>
74 #include <unistd.h>
75 #include <time.h>
78 #include "WindowMaker.h"
79 #include "screen.h"
80 #include "window.h"
81 #include "client.h"
82 #include "session.h"
83 #include "wcore.h"
84 #include "framewin.h"
85 #include "workspace.h"
86 #include "funcs.h"
87 #include "properties.h"
88 #include "application.h"
89 #include "appicon.h"
92 #include "dock.h"
95 #include <WINGs/WUtil.h>
97 /** Global **/
99 extern Atom _XA_WM_SAVE_YOURSELF;
101 extern Time LastTimestamp;
103 #ifdef XSMP_ENABLED
105 extern int wScreenCount;
107 /* requested for SaveYourselfPhase2 */
108 static Bool sWaitingPhase2 = False;
110 static SmcConn sSMCConn = NULL;
112 static WMHandlerID sSMInputHandler = NULL;
114 /* our SM client ID */
115 static char *sClientID = NULL;
116 #endif
119 static WMPropList *sApplications = NULL;
120 static WMPropList *sCommand;
121 static WMPropList *sName;
122 static WMPropList *sHost;
123 static WMPropList *sWorkspace;
124 static WMPropList *sShaded;
125 static WMPropList *sMiniaturized;
126 static WMPropList *sHidden;
127 static WMPropList *sGeometry;
128 static WMPropList *sShortcutMask;
130 static WMPropList *sDock;
132 static WMPropList *sYes, *sNo;
135 static void
136 make_keys()
138 if (sApplications!=NULL)
139 return;
141 sApplications = WMCreatePLString("Applications");
142 sCommand = WMCreatePLString("Command");
143 sName = WMCreatePLString("Name");
144 sHost = WMCreatePLString("Host");
145 sWorkspace = WMCreatePLString("Workspace");
146 sShaded = WMCreatePLString("Shaded");
147 sMiniaturized = WMCreatePLString("Miniaturized");
148 sHidden = WMCreatePLString("Hidden");
149 sGeometry = WMCreatePLString("Geometry");
150 sDock = WMCreatePLString("Dock");
151 sShortcutMask = WMCreatePLString("ShortcutMask");
153 sYes = WMCreatePLString("Yes");
154 sNo = WMCreatePLString("No");
159 static int
160 getBool(WMPropList *value)
162 char *val;
164 if (!WMIsPLString(value)) {
165 return 0;
167 if (!(val = WMGetFromPLString(value))) {
168 return 0;
171 if ((val[1]=='\0' && (val[0]=='y' || val[0]=='Y'))
172 || strcasecmp(val, "YES")==0) {
174 return 1;
175 } else if ((val[1]=='\0' && (val[0]=='n' || val[0]=='N'))
176 || strcasecmp(val, "NO")==0) {
177 return 0;
178 } else {
179 int i;
180 if (sscanf(val, "%i", &i)==1) {
181 return (i!=0);
182 } else {
183 wwarning(_("can't convert \"%s\" to boolean"), val);
184 return 0;
190 static unsigned
191 getInt(WMPropList *value)
193 char *val;
194 unsigned n;
196 if (!WMIsPLString(value))
197 return 0;
198 val = WMGetFromPLString(value);
199 if (!val)
200 return 0;
201 if (sscanf(val, "%u", &n) != 1)
202 return 0;
204 return n;
209 static WMPropList*
210 makeWindowState(WWindow *wwin, WApplication *wapp)
212 WScreen *scr = wwin->screen_ptr;
213 Window win;
214 int argc;
215 char **argv;
216 int i;
217 unsigned mask;
218 char *class, *instance, *command=NULL, buffer[512];
219 WMPropList *win_state, *cmd, *name, *workspace;
220 WMPropList *shaded, *miniaturized, *hidden, *geometry;
221 WMPropList *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 = wtokenjoin(argv, argc);
230 XFreeStringList(argv);
232 if (!command)
233 return NULL;
235 if (PropGetWMClass(win, &class, &instance)) {
236 if (class && instance)
237 snprintf(buffer, sizeof(buffer), "%s.%s", instance, class);
238 else if (instance)
239 snprintf(buffer, sizeof(buffer), "%s", instance);
240 else if (class)
241 snprintf(buffer, sizeof(buffer), ".%s", class);
242 else
243 snprintf(buffer, sizeof(buffer), ".");
245 name = WMCreatePLString(buffer);
246 cmd = WMCreatePLString(command);
247 /*sprintf(buffer, "%d", wwin->frame->workspace+1);
248 workspace = WMCreatePLString(buffer);*/
249 workspace = WMCreatePLString(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 snprintf(buffer, sizeof(buffer), "%ix%i+%i+%i",
254 wwin->client.width, wwin->client.height,
255 wwin->frame_x, wwin->frame_y);
256 geometry = WMCreatePLString(buffer);
258 for (mask = 0, i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
259 if (scr->shortcutWindows[i] != NULL &&
260 WMGetFirstInArray(scr->shortcutWindows[i], wwin) != WANotFound) {
261 mask |= 1<<i;
265 snprintf(buffer, sizeof(buffer), "%u", mask);
266 shortcut = WMCreatePLString(buffer);
268 win_state = WMCreatePLDictionary(sName, name,
269 sCommand, cmd,
270 sWorkspace, workspace,
271 sShaded, shaded,
272 sMiniaturized, miniaturized,
273 sHidden, hidden,
274 sShortcutMask, shortcut,
275 sGeometry, geometry,
276 NULL);
278 WMReleasePropList(name);
279 WMReleasePropList(cmd);
280 WMReleasePropList(workspace);
281 WMReleasePropList(geometry);
282 WMReleasePropList(shortcut);
283 if (wapp && wapp->app_icon && wapp->app_icon->dock) {
284 int i;
285 char *name;
286 if (wapp->app_icon->dock == scr->dock) {
287 name="Dock";
288 } else {
289 for(i=0; i<scr->workspace_count; i++)
290 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
291 break;
292 assert( i < scr->workspace_count);
293 /*n = i+1;*/
294 name = scr->workspaces[i]->name;
296 dock = WMCreatePLString(name);
297 WMPutInPLDictionary(win_state, sDock, dock);
298 WMReleasePropList(dock);
300 } else {
301 win_state = NULL;
304 if (instance) XFree(instance);
305 if (class) XFree(class);
306 if (command) wfree(command);
308 return win_state;
312 void
313 wSessionSaveState(WScreen *scr)
315 WWindow *wwin = scr->focused_window;
316 WMPropList *win_info, *wks;
317 WMPropList *list=NULL;
318 WMArray *wapp_list=NULL;
321 make_keys();
323 if (!scr->session_state) {
324 scr->session_state = WMCreatePLDictionary(NULL, NULL, NULL);
325 if (!scr->session_state)
326 return;
329 list = WMCreatePLArray(NULL);
331 wapp_list = WMCreateArray(16);
333 while (wwin) {
334 WApplication *wapp=wApplicationOf(wwin->main_window);
336 if ((wwin->transient_for==None
337 || wwin->transient_for==wwin->screen_ptr->root_win)
338 && WMGetFirstInArray(wapp_list, wapp)==WANotFound
339 && !WFLAGP(wwin, dont_save_session)) {
340 /* A entry for this application was not yet saved. Save one. */
341 if ((win_info = makeWindowState(wwin, wapp))!=NULL) {
342 WMAddToPLArray(list, win_info);
343 WMReleasePropList(win_info);
344 /* If we were succesful in saving the info for this window
345 * add the application the window belongs to, to the
346 * application list, so no multiple entries for the same
347 * application are saved.
349 WMAddToArray(wapp_list, wapp);
352 wwin = wwin->prev;
354 WMRemoveFromPLDictionary(scr->session_state, sApplications);
355 WMPutInPLDictionary(scr->session_state, sApplications, list);
356 WMReleasePropList(list);
358 wks = WMCreatePLString(scr->workspaces[scr->current_workspace]->name);
359 WMPutInPLDictionary(scr->session_state, sWorkspace, wks);
360 WMReleasePropList(wks);
362 WMFreeArray(wapp_list);
366 void
367 wSessionClearState(WScreen *scr)
369 make_keys();
371 if (!scr->session_state)
372 return;
374 WMRemoveFromPLDictionary(scr->session_state, sApplications);
375 WMRemoveFromPLDictionary(scr->session_state, sWorkspace);
379 static pid_t
380 execCommand(WScreen *scr, char *command, char *host)
382 pid_t pid;
383 char **argv;
384 int argc;
386 wtokensplit(command, &argv, &argc);
388 if (argv==NULL) {
389 return 0;
392 if ((pid=fork())==0) {
393 char **args;
394 int i;
396 SetupEnvironment(scr);
398 args = malloc(sizeof(char*)*(argc+1));
399 if (!args)
400 exit(111);
401 for (i=0; i<argc; i++) {
402 args[i] = argv[i];
404 args[argc] = NULL;
405 execvp(argv[0], args);
406 exit(111);
408 while (argc > 0)
409 wfree(argv[--argc]);
410 wfree(argv);
411 return pid;
415 static WSavedState*
416 getWindowState(WScreen *scr, WMPropList *win_state)
418 WSavedState *state = wmalloc(sizeof(WSavedState));
419 WMPropList *value;
420 char *tmp;
421 unsigned mask;
422 int i;
424 memset(state, 0, sizeof(WSavedState));
425 state->workspace = -1;
426 value = WMGetFromPLDictionary(win_state, sWorkspace);
427 if (value && WMIsPLString(value)) {
428 tmp = WMGetFromPLString(value);
429 if (sscanf(tmp, "%i", &state->workspace)!=1) {
430 state->workspace = -1;
431 for (i=0; i < scr->workspace_count; i++) {
432 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
433 state->workspace = i;
434 break;
437 } else {
438 state->workspace--;
441 if ((value = WMGetFromPLDictionary(win_state, sShaded))!=NULL)
442 state->shaded = getBool(value);
443 if ((value = WMGetFromPLDictionary(win_state, sMiniaturized))!=NULL)
444 state->miniaturized = getBool(value);
445 if ((value = WMGetFromPLDictionary(win_state, sHidden))!=NULL)
446 state->hidden = getBool(value);
447 if ((value = WMGetFromPLDictionary(win_state, sShortcutMask))!=NULL) {
448 mask = getInt(value);
449 state->window_shortcuts = mask;
452 value = WMGetFromPLDictionary(win_state, sGeometry);
453 if (value && WMIsPLString(value)) {
454 if (!(sscanf(WMGetFromPLString(value), "%ix%i+%i+%i",
455 &state->w, &state->h, &state->x, &state->y)==4 &&
456 (state->w>0 && state->h>0))) {
457 state->w = 0;
458 state->h = 0;
462 return state;
466 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
468 void
469 wSessionRestoreState(WScreen *scr)
471 WSavedState *state;
472 char *instance, *class, *command, *host;
473 WMPropList *win_info, *apps, *cmd, *value;
474 pid_t pid;
475 int i, count;
476 WDock *dock;
477 WAppIcon *btn=NULL;
478 int j, n, found;
479 char *tmp;
481 make_keys();
483 if (!scr->session_state)
484 return;
486 WMPLSetCaseSensitive(True);
488 apps = WMGetFromPLDictionary(scr->session_state, sApplications);
489 if (!apps)
490 return;
492 count = WMGetPropListItemCount(apps);
493 if (count==0)
494 return;
496 for (i=0; i<count; i++) {
497 win_info = WMGetFromPLArray(apps, i);
499 cmd = WMGetFromPLDictionary(win_info, sCommand);
500 if (!cmd || !WMIsPLString(cmd) || !(command = WMGetFromPLString(cmd))) {
501 continue;
504 value = WMGetFromPLDictionary(win_info, sName);
505 if (!value)
506 continue;
508 ParseWindowName(value, &instance, &class, "session");
509 if (!instance && !class)
510 continue;
512 value = WMGetFromPLDictionary(win_info, sHost);
513 if (value && WMIsPLString(value))
514 host = WMGetFromPLString(value);
515 else
516 host = NULL;
518 state = getWindowState(scr, win_info);
520 dock = NULL;
521 value = WMGetFromPLDictionary(win_info, sDock);
522 if (value && WMIsPLString(value) && (tmp = WMGetFromPLString(value))!=NULL) {
523 if (sscanf(tmp, "%i", &n)!=1) {
524 if (!strcasecmp(tmp, "DOCK")) {
525 dock = scr->dock;
526 } else {
527 for (j=0; j < scr->workspace_count; j++) {
528 if (strcmp(scr->workspaces[j]->name, tmp)==0) {
529 dock = scr->workspaces[j]->clip;
530 break;
534 } else {
535 if (n == 0) {
536 dock = scr->dock;
537 } else if (n>0 && n<=scr->workspace_count) {
538 dock = scr->workspaces[n-1]->clip;
543 found = 0;
544 if (dock!=NULL) {
545 for (j=0; j<dock->max_icons; j++) {
546 btn = dock->icon_array[j];
547 if (btn && SAME(instance, btn->wm_instance) &&
548 SAME(class, btn->wm_class) &&
549 SAME(command, btn->command) &&
550 !btn->launching) {
551 found = 1;
552 break;
557 if (found) {
558 wDockLaunchWithState(dock, btn, state);
559 } else if ((pid = execCommand(scr, command, host)) > 0) {
560 wWindowAddSavedState(instance, class, command, pid, state);
561 } else {
562 wfree(state);
565 if (instance) wfree(instance);
566 if (class) wfree(class);
568 /* clean up */
569 WMPLSetCaseSensitive(False);
573 void
574 wSessionRestoreLastWorkspace(WScreen *scr)
576 WMPropList *wks;
577 int w, i;
578 char *tmp;
580 make_keys();
582 if (!scr->session_state)
583 return;
585 WMPLSetCaseSensitive(True);
587 wks = WMGetFromPLDictionary(scr->session_state, sWorkspace);
588 if (!wks || !WMIsPLString(wks))
589 return;
591 tmp = WMGetFromPLString(wks);
593 /* clean up */
594 WMPLSetCaseSensitive(False);
596 if (sscanf(tmp, "%i", &w)!=1) {
597 w = -1;
598 for (i=0; i < scr->workspace_count; i++) {
599 if (strcmp(scr->workspaces[i]->name, tmp)==0) {
600 w = i;
601 break;
604 } else {
605 w--;
608 if (w!=scr->current_workspace && w<scr->workspace_count) {
609 wWorkspaceChange(scr, w);
614 static void
615 clearWaitingAckState(WScreen *scr)
617 WWindow *wwin;
618 WApplication *wapp;
620 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
621 wwin->flags.waiting_save_ack = 0;
622 if (wwin->main_window != None) {
623 wapp = wApplicationOf(wwin->main_window);
624 if (wapp)
625 wapp->main_window_desc->flags.waiting_save_ack = 0;
631 void
632 wSessionSaveClients(WScreen *scr)
639 * With XSMP, this job is done by smproxy
641 void
642 wSessionSendSaveYourself(WScreen *scr)
644 WWindow *wwin;
645 int count;
647 /* freeze client interaction with clients */
648 XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync,
649 CurrentTime);
650 XGrabPointer(dpy, scr->root_win, False, ButtonPressMask|ButtonReleaseMask,
651 GrabModeAsync, GrabModeAsync, scr->root_win, None,
652 CurrentTime);
654 clearWaitingAckState(scr);
656 count = 0;
658 /* first send SAVE_YOURSELF for everybody */
659 for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
660 WWindow *mainWin;
662 mainWin = wWindowFor(wwin->main_window);
664 if (mainWin) {
665 /* if the client is a multi-window client, only send message
666 * to the main window */
667 wwin = mainWin;
670 /* make sure the SAVE_YOURSELF flag is up-to-date */
671 PropGetProtocols(wwin->client_win, &wwin->protocols);
673 if (wwin->protocols.SAVE_YOURSELF) {
674 if (!wwin->flags.waiting_save_ack) {
675 wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
677 wwin->flags.waiting_save_ack = 1;
678 count++;
680 } else {
681 wwin->flags.waiting_save_ack = 0;
685 /* then wait for acknowledge */
686 while (count > 0) {
690 XUngrabPointer(dpy, CurrentTime);
691 XUngrabKeyboard(dpy, CurrentTime);
692 XFlush(dpy);
696 #ifdef XSMP_ENABLED
698 * With full session management support, the part of WMState
699 * that store client window state will become obsolete (maybe we can reuse
700 * the old code too),
701 * but we still need to store state info like the dock and workspaces.
702 * It is better to keep dock/wspace info in WMState because the user
703 * might want to keep the dock configuration while not wanting to
704 * resume a previously saved session.
705 * So, wmaker specific state info can be saved in
706 * ~/GNUstep/.AppInfo/WindowMaker/statename.state
707 * Its better to not put it in the defaults directory because:
708 * - its not a defaults file (having domain names like wmaker0089504baa
709 * in the defaults directory wouldn't be very neat)
710 * - this state file is not meant to be edited by users
712 * The old session code will become obsolete. When wmaker is
713 * compiled with R6 sm support compiled in, it'll be better to
714 * use a totally rewritten state saving code, but we can keep
715 * the current code for when XSMP_ENABLED is not compiled in.
717 * This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
718 * messages), but it'll be better.
720 * -readme
724 static char*
725 getWindowRole(Window window)
727 XTextProperty prop;
728 static Atom atom = 0;
730 if (!atom)
731 atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
733 if (XGetTextProperty(dpy, window, &prop, atom)) {
734 if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
735 return prop.value;
738 return NULL;
744 * Saved Info:
746 * WM_WINDOW_ROLE
748 * WM_CLASS.instance
749 * WM_CLASS.class
750 * WM_NAME
751 * WM_COMMAND
753 * geometry
754 * state = (miniaturized, shaded, etc)
755 * attribute
756 * workspace #
757 * app state = (which dock, hidden)
758 * window shortcut #
761 static WMPropList*
762 makeAppState(WWindow *wwin)
764 WApplication *wapp;
765 WMPropList *state;
766 WScreen *scr = wwin->screen_ptr;
768 state = WMCreatePLArray(NULL, NULL);
770 wapp = wApplicationOf(wwin->main_window);
772 if (wapp) {
773 if (wapp->app_icon && wapp->app_icon->dock) {
775 if (wapp->app_icon->dock == scr->dock) {
776 WMAddToPLArray(state, WMCreatePLString("Dock"));
777 } else {
778 int i;
780 for(i=0; i<scr->workspace_count; i++)
781 if(scr->workspaces[i]->clip == wapp->app_icon->dock)
782 break;
784 assert(i < scr->workspace_count);
786 WMAddToPLArray(state,
787 WMCreatePLString(scr->workspaces[i]->name));
791 WMAddToPLArray(state, WMCreatePLString(wapp->hidden ? "1" : "0"));
794 return state;
799 Bool
800 wSessionGetStateFor(WWindow *wwin, WSessionData *state)
802 char *str;
803 WMPropList *slist;
804 WMPropList *elem;
805 WMPropList *value;
806 int index = 0;
808 index = 3;
810 /* geometry */
811 value = WMGetFromPLArray(slist, index++);
812 str = WMGetFromPLString(value);
814 sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
815 &state->width, &state->height,
816 &state->user_changed_width, &state->user_changed_height);
819 /* state */
820 value = WMGetFromPLArray(slist, index++);
821 str = WMGetFromPLString(value);
823 sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded,
824 &state->maximized);
827 /* attributes */
828 value = WMGetFromPLArray(slist, index++);
829 str = WMGetFromPLString(value);
831 getAttributeState(str, &state->mflags, &state->flags);
834 /* workspace */
835 value = WMGetFromPLArray(slist, index++);
836 str = WMGetFromPLString(value);
838 sscanf(str, "%i", &state->workspace);
841 /* app state (repeated for all windows of the app) */
842 value = WMGetFromPLArray(slist, index++);
843 str = WMGetFromPLString(value);
845 /* ???? */
847 /* shortcuts */
848 value = WMGetFromPLArray(slist, index++);
849 str = WMGetFromPLString(value);
851 sscanf(str, "%i", &state->shortcuts);
856 static WMPropList*
857 makeAttributeState(WWindow *wwin)
859 unsigned int data1, data2;
860 char buffer[256];
862 #define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
863 ? (wwin)->user_flags.FLAG : -1)
865 snprintf(buffer, sizeof(buffer),
866 "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
867 W_FLAG(no_titlebar),
868 W_FLAG(no_resizable),
869 W_FLAG(no_closable),
870 W_FLAG(no_miniaturizable),
871 W_FLAG(no_resizebar),
872 W_FLAG(no_close_button),
873 W_FLAG(no_miniaturize_button),
875 W_FLAG(broken_close),
876 W_FLAG(kill_close),
878 W_FLAG(no_shadeable),
879 W_FLAG(omnipresent),
880 W_FLAG(skip_window_list),
881 W_FLAG(floating),
882 W_FLAG(sunken),
883 W_FLAG(no_bind_keys),
884 W_FLAG(no_bind_mouse),
885 W_FLAG(no_hide_others),
886 W_FLAG(no_appicon),
887 W_FLAG(dont_move_off),
888 W_FLAG(no_focusable),
889 W_FLAG(always_user_icon),
890 W_FLAG(start_miniaturized),
891 W_FLAG(start_hidden),
892 W_FLAG(start_maximized),
893 W_FLAG(dont_save_session),
894 W_FLAG(emulate_appicon));
896 return WMCreatePLString(buffer);
900 static void
901 appendStringInArray(WMPropList *array, char *str)
903 WMPropList *val;
905 val = WMCreatePLString(str);
906 WMAddToPLArray(array, val);
907 WMReleasePropList(val);
911 static WMPropList*
912 makeClientState(WWindow *wwin)
914 WMPropList *state;
915 WMPropList *tmp;
916 char *str;
917 char buffer[512];
918 int i;
919 unsigned shortcuts;
921 state = WMCreatePLArray(NULL, NULL);
923 /* WM_WINDOW_ROLE */
924 str = getWindowRole(wwin->client_win);
925 if (!str)
926 appendStringInArray(state, "");
927 else {
928 appendStringInArray(state, str);
929 XFree(str);
932 /* WM_CLASS.instance */
933 appendStringInArray(state, wwin->wm_instance);
935 /* WM_CLASS.class */
936 appendStringInArray(state, wwin->wm_class);
938 /* WM_NAME */
939 if (wwin->flags.wm_name_changed)
940 appendStringInArray(state, "");
941 else
942 appendStringInArray(state, wwin->frame->name);
944 /* geometry */
945 snprintf(buffer, sizeof(buffer), "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
946 wwin->client.width, wwin->client.height,
947 wwin->flags.user_changed_width, wwin->flags.user_changed_height);
948 appendStringInArray(state, buffer);
950 /* state */
951 snprintf(buffer, sizeof(buffer), "%i %i %i", wwin->flags.miniaturized,
952 wwin->flags.shaded, wwin->flags.maximized);
953 appendStringInArray(state, buffer);
955 /* attributes */
956 tmp = makeAttributeState(wwin);
957 WMAddToPLArray(state, tmp);
958 WMReleasePropList(tmp);
960 /* workspace */
961 snprintf(buffer, sizeof(buffer), "%i", wwin->frame->workspace);
962 appendStringInArray(state, buffer);
964 /* app state (repeated for all windows of the app) */
965 tmp = makeAppState(wwin);
966 WMAddToPLArray(state, tmp);
967 WMReleasePropList(tmp);
969 /* shortcuts */
970 shortcuts = 0;
971 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
972 if (scr->shortcutWindow[i] == wwin) {
973 shortcuts |= 1 << i;
976 snprintf(buffer, sizeof(buffer), "%ui", shortcuts);
977 appendStringInArray(tmp, buffer);
979 return state;
983 static void
984 smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
986 SmProp props[4];
987 SmPropValue prop1val, prop2val, prop3val, prop4val;
988 char **argv = (char**)client_data;
989 int argc;
990 int i, j;
991 Bool ok = False;
992 char *statefile = NULL;
993 char *prefix;
994 Bool gsPrefix = False;
995 char *discardCmd = NULL;
996 time_t t;
997 WMPropList *state, *plState;
998 int len;
1000 #ifdef DEBUG1
1001 puts("received SaveYourselfPhase2 SM message");
1002 #endif
1004 /* save session state */
1006 /* the file that will contain the state */
1007 prefix = getenv("SM_SAVE_DIR");
1008 if (!prefix) {
1009 prefix = wusergnusteppath();
1010 if (prefix)
1011 gsPrefix = True;
1013 if (!prefix) {
1014 prefix = getenv("HOME");
1016 if (!prefix)
1017 prefix = ".";
1019 len = strlen(prefix)+64;
1020 statefile = malloc(len);
1021 if (!statefile) {
1022 wwarning(_("out of memory while saving session state"));
1023 goto fail;
1026 t = time();
1027 i = 0;
1028 do {
1029 if (gsPrefix)
1030 snprintf(statefile, len, "%s/.AppInfo/WindowMaker/wmaker.%l%i.state",
1031 prefix, t, i);
1032 else
1033 snprintf(statefile, len, "%s/wmaker.%l%i.state", prefix, t, i);
1034 i++;
1035 } while (access(F_OK, statefile)!=-1);
1037 /* save the states of all windows we're managing */
1038 state = WMCreatePLArray(NULL, NULL);
1041 * Format:
1043 * state_file ::= dictionary with version_info ; state
1044 * version_info ::= 'version' = '1';
1045 * state ::= 'state' = array of screen_info
1046 * screen_info ::= array of (screen number, window_info, window_info, ...)
1047 * window_info ::=
1049 for (i=0; i<wScreenCount; i++) {
1050 WScreen *scr;
1051 WWindow *wwin;
1052 char buf[32];
1053 WMPropList *pscreen;
1055 scr = wScreenWithNumber(i);
1057 snprintf(buf, sizeof(buf), "%i", scr->screen);
1058 pscreen = WMCreatePLArray(WMCreatePLString(buf), NULL);
1060 wwin = scr->focused_window;
1061 while (wwin) {
1062 WMPropList *pwindow;
1064 pwindow = makeClientState(wwin);
1065 WMAddToPLArray(pscreen, pwindow);
1067 wwin = wwin->prev;
1070 WMAddToPLArray(state, pscreen);
1073 plState = WMCreatePLDictionary(WMCreatePLString("Version"),
1074 WMCreatePLString("1.0"),
1075 WMCreatePLString("Screens"),
1076 state, NULL);
1078 WMWritePropListToFile(plState, statefile, False);
1080 WMReleasePropList(plState);
1082 /* set the remaining properties that we didn't set at
1083 * startup time */
1085 for (argc=0, i=0; argv[i]!=NULL; i++) {
1086 if (strcmp(argv[i], "-clientid")==0
1087 || strcmp(argv[i], "-restore")==0) {
1088 i++;
1089 } else {
1090 argc++;
1094 prop[0].name = SmRestartCommand;
1095 prop[0].type = SmLISTofARRAY8;
1096 prop[0].vals = malloc(sizeof(SmPropValue)*(argc+4));
1097 prop[0].num_vals = argc+4;
1099 prop[1].name = SmCloneCommand;
1100 prop[1].type = SmLISTofARRAY8;
1101 prop[1].vals = malloc(sizeof(SmPropValue)*(argc));
1102 prop[1].num_vals = argc;
1104 if (!prop[0].vals || !prop[1].vals) {
1105 wwarning(_("end of memory while saving session state"));
1106 goto fail;
1109 for (j=0, i=0; i<argc+4; i++) {
1110 if (strcmp(argv[i], "-clientid")==0
1111 || strcmp(argv[i], "-restore")==0) {
1112 i++;
1113 } else {
1114 prop[0].vals[j].value = argv[i];
1115 prop[0].vals[j].length = strlen(argv[i]);
1116 prop[1].vals[j].value = argv[i];
1117 prop[1].vals[j].length = strlen(argv[i]);
1118 j++;
1121 prop[0].vals[j].value = "-clientid";
1122 prop[0].vals[j].length = 9;
1123 j++;
1124 prop[0].vals[j].value = sClientID;
1125 prop[0].vals[j].length = strlen(sClientID);
1126 j++;
1127 prop[0].vals[j].value = "-restore";
1128 prop[0].vals[j].length = 11;
1129 j++;
1130 prop[0].vals[j].value = statefile;
1131 prop[0].vals[j].length = strlen(statefile);
1134 int len = strlen(statefile)+8;
1136 discardCmd = malloc(len);
1137 if (!discardCmd)
1138 goto fail;
1139 snprintf(discardCmd, len, "rm %s", statefile);
1141 prop[2].name = SmDiscardCommand;
1142 prop[2].type = SmARRAY8;
1143 prop[2].vals[0] = discardCmd;
1144 prop[2].num_vals = 1;
1146 SmcSetProperties(sSMCConn, 3, prop);
1148 ok = True;
1149 fail:
1150 SmcSaveYourselfDone(smc_conn, ok);
1152 if (prop[0].vals)
1153 wfree(prop[0].vals);
1154 if (prop[1].vals)
1155 wfree(prop[1].vals);
1156 if (discardCmd)
1157 wfree(discardCmd);
1159 if (!ok) {
1160 remove(statefile);
1162 if (statefile)
1163 wfree(statefile);
1167 static void
1168 smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
1169 Bool shutdown, int interact_style, Bool fast)
1171 #ifdef DEBUG1
1172 puts("received SaveYourself SM message");
1173 #endif
1175 if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc,
1176 client_data)) {
1178 SmcSaveYourselfDone(smc_conn, False);
1179 sWaitingPhase2 = False;
1180 } else {
1181 #ifdef DEBUG1
1182 puts("successfull request of SYS phase 2");
1183 #endif
1184 sWaitingPhase2 = True;
1189 static void
1190 smDieProc(SmcConn smc_conn, SmPointer client_data)
1192 #ifdef DEBUG1
1193 puts("received Die SM message");
1194 #endif
1196 wSessionDisconnectManager();
1198 Shutdown(WSExitMode, True);
1203 static void
1204 smSaveCompleteProc(SmcConn smc_conn)
1206 /* it means that we can resume doing things that can change our state */
1207 #ifdef DEBUG1
1208 puts("received SaveComplete SM message");
1209 #endif
1213 static void
1214 smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
1216 if (sWaitingPhase2) {
1218 sWaitingPhase2 = False;
1220 SmcSaveYourselfDone(smc_conn, False);
1225 static void
1226 iceMessageProc(int fd, int mask, void *clientData)
1228 IceConn iceConn = (IceConn)clientData;
1230 IceProcessMessages(iceConn, NULL, NULL);
1234 static void
1235 iceIOErrorHandler(IceConnection ice_conn)
1237 /* This is not fatal but can mean the session manager exited.
1238 * If the session manager exited normally we would get a
1239 * Die message, so this probably means an abnormal exit.
1240 * If the sm was the last client of session, then we'll die
1241 * anyway, otherwise we can continue doing our stuff.
1243 wwarning(_("connection to the session manager was lost"));
1244 wSessionDisconnectManager();
1248 void
1249 wSessionConnectManager(char **argv, int argc)
1251 IceConn iceConn;
1252 char *previous_id = NULL;
1253 char buffer[256];
1254 SmcCallbacks callbacks;
1255 unsigned long mask;
1256 char uid[32];
1257 char pid[32];
1258 SmProp props[4];
1259 SmPropValue prop1val, prop2val, prop3val, prop4val;
1260 char restartStyle;
1261 int i;
1263 mask = SmcSaveYourselfProcMask|SmcDieProcMask|SmcSaveCompleteProcMask
1264 |SmcShutdownCancelledProcMask;
1266 callbacks.save_yourself.callback = smSaveYourselfProc;
1267 callbacks.save_yourself.client_data = argv;
1269 callbacks.die.callback = smDieProc;
1270 callbacks.die.client_data = NULL;
1272 callbacks.save_complete.callback = smSaveCompleteProc;
1273 callbacks.save_complete.client_data = NULL;
1275 callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
1276 callbacks.shutdown_cancelled.client_data = NULL;
1278 for (i=0; i<argc; i++) {
1279 if (strcmp(argv[i], "-clientid")==0) {
1280 previous_id = argv[i+1];
1281 break;
1285 /* connect to the session manager */
1286 sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
1287 mask, &callbacks, previous_id,
1288 &sClientID, 255, buffer);
1289 if (!sSMCConn) {
1290 return;
1292 #ifdef DEBUG1
1293 puts("connected to the session manager");
1294 #endif
1296 /* IceSetIOErrorHandler(iceIOErrorHandler);*/
1298 /* check for session manager clients */
1299 iceConn = SmcGetIceConnection(smcConn);
1301 if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
1302 wsyserror("error setting close-on-exec flag for ICE connection");
1305 sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn),
1306 WIReadMask, iceMessageProc, iceConn);
1308 /* setup information about ourselves */
1310 /* program name */
1311 prop1val.value = argv[0];
1312 prop1val.length = strlen(argv[0]);
1313 prop[0].name = SmProgram;
1314 prop[0].type = SmARRAY8;
1315 prop[0].num_vals = 1;
1316 prop[0].vals = &prop1val;
1318 /* The XSMP doc from X11R6.1 says it contains the user name,
1319 * but every client implementation I saw places the uid # */
1320 snprintf(uid, sizeof(uid), "%i", getuid());
1321 prop2val.value = uid;
1322 prop2val.length = strlen(uid);
1323 prop[1].name = SmUserID;
1324 prop[1].type = SmARRAY8;
1325 prop[1].num_vals = 1;
1326 prop[1].vals = &prop2val;
1328 /* Restart style. We should restart only if we were running when
1329 * the previous session finished. */
1330 restartStyle = SmRestartIfRunning;
1331 prop3val.value = &restartStyle;
1332 prop3val.length = 1;
1333 prop[2].name = SmRestartStyleHint;
1334 prop[2].type = SmCARD8;
1335 prop[2].num_vals = 1;
1336 prop[2].vals = &prop3val;
1338 /* Our PID. Not required but might be usefull */
1339 snprintf(pid, sizeof(pid), "%i", getpid());
1340 prop4val.value = pid;
1341 prop4val.length = strlen(pid);
1342 prop[3].name = SmProcessID;
1343 prop[3].type = SmARRAY8;
1344 prop[3].num_vals = 1;
1345 prop[3].vals = &prop4val;
1347 /* we'll set the rest of the hints later */
1349 SmcSetProperties(sSMCConn, 4, props);
1354 void
1355 wSessionDisconnectManager(void)
1357 if (sSMCConn) {
1358 WMDeleteInputHandler(sSMInputHandler);
1359 sSMInputHandler = NULL;
1361 SmcCloseConnection(sSMCConn, 0, NULL);
1362 sSMCConn = NULL;
1366 void
1367 wSessionRequestShutdown(void)
1369 /* request a shutdown to the session manager */
1370 if (sSMCConn)
1371 SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny,
1372 False, True);
1376 Bool
1377 wSessionIsManaged(void)
1379 return sSMCConn!=NULL;
1382 #endif /* !XSMP_ENABLED */