1 /* session.c - session state handling and R6 style session management
3 * Copyright (c) 1998-2003 Dan Pascu
4 * Copyright (c) 1998-2003 Alfredo Kojima
6 * Window Maker window manager
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
26 * If defined(XSMP_ENABLED) and session manager is running then
29 * do pre-R6 session management stuff (save window state and relaunch)
31 * When doing a checkpoint:
34 * Open "Stop"/status Dialog
35 * Send SAVE_YOURSELF to clients and wait for reply
37 * Save state of clients
40 * Send checkpoint request to sm
47 * Open "Exit Now"/status Dialog
48 * Send SAVE_YOURSELF to clients and wait for reply
50 * Save state of clients
51 * Send DELETE to all clients
52 * When no more clients are left or user hit "Exit Now", exit
56 * Send Shutdown request to session manager
57 * if SaveYourself message received, save state of clients
58 * if the Die message is received, exit.
64 #include <X11/Xutil.h>
67 #include <X11/SM/SMlib.h>
76 #include "WindowMaker.h"
83 #include "workspace.h"
85 #include "properties.h"
86 #include "application.h"
91 #include <WINGs/WUtil.h>
95 extern Atom _XA_WM_SAVE_YOURSELF
;
97 extern Time LastTimestamp
;
101 extern int wScreenCount
;
103 /* requested for SaveYourselfPhase2 */
104 static Bool sWaitingPhase2
= False
;
106 static SmcConn sSMCConn
= NULL
;
108 static WMHandlerID sSMInputHandler
= NULL
;
110 /* our SM client ID */
111 static char *sClientID
= NULL
;
114 static WMPropList
*sApplications
= NULL
;
115 static WMPropList
*sCommand
;
116 static WMPropList
*sName
;
117 static WMPropList
*sHost
;
118 static WMPropList
*sWorkspace
;
119 static WMPropList
*sShaded
;
120 static WMPropList
*sMiniaturized
;
121 static WMPropList
*sHidden
;
122 static WMPropList
*sGeometry
;
123 static WMPropList
*sShortcutMask
;
125 static WMPropList
*sDock
;
127 static WMPropList
*sYes
, *sNo
;
129 static void make_keys()
131 if (sApplications
!= NULL
)
134 sApplications
= WMCreatePLString("Applications");
135 sCommand
= WMCreatePLString("Command");
136 sName
= WMCreatePLString("Name");
137 sHost
= WMCreatePLString("Host");
138 sWorkspace
= WMCreatePLString("Workspace");
139 sShaded
= WMCreatePLString("Shaded");
140 sMiniaturized
= WMCreatePLString("Miniaturized");
141 sHidden
= WMCreatePLString("Hidden");
142 sGeometry
= WMCreatePLString("Geometry");
143 sDock
= WMCreatePLString("Dock");
144 sShortcutMask
= WMCreatePLString("ShortcutMask");
146 sYes
= WMCreatePLString("Yes");
147 sNo
= WMCreatePLString("No");
150 static int getBool(WMPropList
* value
)
154 if (!WMIsPLString(value
)) {
157 if (!(val
= WMGetFromPLString(value
))) {
161 if ((val
[1] == '\0' && (val
[0] == 'y' || val
[0] == 'Y'))
162 || strcasecmp(val
, "YES") == 0) {
165 } else if ((val
[1] == '\0' && (val
[0] == 'n' || val
[0] == 'N'))
166 || strcasecmp(val
, "NO") == 0) {
170 if (sscanf(val
, "%i", &i
) == 1) {
173 wwarning(_("can't convert \"%s\" to boolean"), val
);
179 static unsigned getInt(WMPropList
* value
)
184 if (!WMIsPLString(value
))
186 val
= WMGetFromPLString(value
);
189 if (sscanf(val
, "%u", &n
) != 1)
195 static WMPropList
*makeWindowState(WWindow
* wwin
, WApplication
* wapp
)
197 WScreen
*scr
= wwin
->screen_ptr
;
201 char *class, *instance
, *command
= NULL
, buffer
[512];
202 WMPropList
*win_state
, *cmd
, *name
, *workspace
;
203 WMPropList
*shaded
, *miniaturized
, *hidden
, *geometry
;
204 WMPropList
*dock
, *shortcut
;
206 if (wwin
->orig_main_window
!= None
&& wwin
->orig_main_window
!= wwin
->client_win
)
207 win
= wwin
->orig_main_window
;
209 win
= wwin
->client_win
;
211 command
= GetCommandForWindow(win
);
215 if (PropGetWMClass(win
, &class, &instance
)) {
216 if (class && instance
)
217 snprintf(buffer
, sizeof(buffer
), "%s.%s", instance
, class);
219 snprintf(buffer
, sizeof(buffer
), "%s", instance
);
221 snprintf(buffer
, sizeof(buffer
), ".%s", class);
223 snprintf(buffer
, sizeof(buffer
), ".");
225 name
= WMCreatePLString(buffer
);
226 cmd
= WMCreatePLString(command
);
227 /*sprintf(buffer, "%d", wwin->frame->workspace+1);
228 workspace = WMCreatePLString(buffer); */
229 workspace
= WMCreatePLString(scr
->workspaces
[wwin
->frame
->workspace
]->name
);
230 shaded
= wwin
->flags
.shaded
? sYes
: sNo
;
231 miniaturized
= wwin
->flags
.miniaturized
? sYes
: sNo
;
232 hidden
= wwin
->flags
.hidden
? sYes
: sNo
;
233 snprintf(buffer
, sizeof(buffer
), "%ix%i+%i+%i",
234 wwin
->client
.width
, wwin
->client
.height
, wwin
->frame_x
, wwin
->frame_y
);
235 geometry
= WMCreatePLString(buffer
);
237 for (mask
= 0, i
= 0; i
< MAX_WINDOW_SHORTCUTS
; i
++) {
238 if (scr
->shortcutWindows
[i
] != NULL
&&
239 WMGetFirstInArray(scr
->shortcutWindows
[i
], wwin
) != WANotFound
) {
244 snprintf(buffer
, sizeof(buffer
), "%u", mask
);
245 shortcut
= WMCreatePLString(buffer
);
247 win_state
= WMCreatePLDictionary(sName
, name
,
249 sWorkspace
, workspace
,
251 sMiniaturized
, miniaturized
,
253 sShortcutMask
, shortcut
, sGeometry
, geometry
, NULL
);
255 WMReleasePropList(name
);
256 WMReleasePropList(cmd
);
257 WMReleasePropList(workspace
);
258 WMReleasePropList(geometry
);
259 WMReleasePropList(shortcut
);
260 if (wapp
&& wapp
->app_icon
&& wapp
->app_icon
->dock
) {
263 if (wapp
->app_icon
->dock
== scr
->dock
) {
266 for (i
= 0; i
< scr
->workspace_count
; i
++)
267 if (scr
->workspaces
[i
]->clip
== wapp
->app_icon
->dock
)
269 assert(i
< scr
->workspace_count
);
271 name
= scr
->workspaces
[i
]->name
;
273 dock
= WMCreatePLString(name
);
274 WMPutInPLDictionary(win_state
, sDock
, dock
);
275 WMReleasePropList(dock
);
291 void wSessionSaveState(WScreen
* scr
)
293 WWindow
*wwin
= scr
->focused_window
;
294 WMPropList
*win_info
, *wks
;
295 WMPropList
*list
= NULL
;
296 WMArray
*wapp_list
= NULL
;
300 if (!scr
->session_state
) {
301 scr
->session_state
= WMCreatePLDictionary(NULL
, NULL
);
302 if (!scr
->session_state
)
306 list
= WMCreatePLArray(NULL
);
308 wapp_list
= WMCreateArray(16);
311 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
312 Window appId
= wwin
->orig_main_window
;
314 if ((wwin
->transient_for
== None
|| wwin
->transient_for
== wwin
->screen_ptr
->root_win
)
315 && (WMGetFirstInArray(wapp_list
, (void *)appId
) == WANotFound
316 || WFLAGP(wwin
, shared_appicon
))
317 && !WFLAGP(wwin
, dont_save_session
)) {
318 /* A entry for this application was not yet saved. Save one. */
319 if ((win_info
= makeWindowState(wwin
, wapp
)) != NULL
) {
320 WMAddToPLArray(list
, win_info
);
321 WMReleasePropList(win_info
);
322 /* If we were succesful in saving the info for this window
323 * add the application the window belongs to, to the
324 * application list, so no multiple entries for the same
325 * application are saved.
327 WMAddToArray(wapp_list
, (void *)appId
);
332 WMRemoveFromPLDictionary(scr
->session_state
, sApplications
);
333 WMPutInPLDictionary(scr
->session_state
, sApplications
, list
);
334 WMReleasePropList(list
);
336 wks
= WMCreatePLString(scr
->workspaces
[scr
->current_workspace
]->name
);
337 WMPutInPLDictionary(scr
->session_state
, sWorkspace
, wks
);
338 WMReleasePropList(wks
);
340 WMFreeArray(wapp_list
);
343 void wSessionClearState(WScreen
* scr
)
347 if (!scr
->session_state
)
350 WMRemoveFromPLDictionary(scr
->session_state
, sApplications
);
351 WMRemoveFromPLDictionary(scr
->session_state
, sWorkspace
);
354 static pid_t
execCommand(WScreen
* scr
, char *command
, char *host
)
360 wtokensplit(command
, &argv
, &argc
);
366 if ((pid
= fork()) == 0) {
370 SetupEnvironment(scr
);
372 args
= malloc(sizeof(char *) * (argc
+ 1));
375 for (i
= 0; i
< argc
; i
++) {
379 execvp(argv
[0], args
);
388 static WSavedState
*getWindowState(WScreen
* scr
, WMPropList
* win_state
)
390 WSavedState
*state
= wmalloc(sizeof(WSavedState
));
396 memset(state
, 0, sizeof(WSavedState
));
397 state
->workspace
= -1;
398 value
= WMGetFromPLDictionary(win_state
, sWorkspace
);
399 if (value
&& WMIsPLString(value
)) {
400 tmp
= WMGetFromPLString(value
);
401 if (sscanf(tmp
, "%i", &state
->workspace
) != 1) {
402 state
->workspace
= -1;
403 for (i
= 0; i
< scr
->workspace_count
; i
++) {
404 if (strcmp(scr
->workspaces
[i
]->name
, tmp
) == 0) {
405 state
->workspace
= i
;
413 if ((value
= WMGetFromPLDictionary(win_state
, sShaded
)) != NULL
)
414 state
->shaded
= getBool(value
);
415 if ((value
= WMGetFromPLDictionary(win_state
, sMiniaturized
)) != NULL
)
416 state
->miniaturized
= getBool(value
);
417 if ((value
= WMGetFromPLDictionary(win_state
, sHidden
)) != NULL
)
418 state
->hidden
= getBool(value
);
419 if ((value
= WMGetFromPLDictionary(win_state
, sShortcutMask
)) != NULL
) {
420 mask
= getInt(value
);
421 state
->window_shortcuts
= mask
;
424 value
= WMGetFromPLDictionary(win_state
, sGeometry
);
425 if (value
&& WMIsPLString(value
)) {
426 if (!(sscanf(WMGetFromPLString(value
), "%ix%i+%i+%i",
427 &state
->w
, &state
->h
, &state
->x
, &state
->y
) == 4 && (state
->w
> 0 && state
->h
> 0))) {
436 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
438 void wSessionRestoreState(WScreen
* scr
)
441 char *instance
, *class, *command
, *host
;
442 WMPropList
*win_info
, *apps
, *cmd
, *value
;
446 WAppIcon
*btn
= NULL
;
452 if (!scr
->session_state
)
455 WMPLSetCaseSensitive(True
);
457 apps
= WMGetFromPLDictionary(scr
->session_state
, sApplications
);
461 count
= WMGetPropListItemCount(apps
);
465 for (i
= 0; i
< count
; i
++) {
466 win_info
= WMGetFromPLArray(apps
, i
);
468 cmd
= WMGetFromPLDictionary(win_info
, sCommand
);
469 if (!cmd
|| !WMIsPLString(cmd
) || !(command
= WMGetFromPLString(cmd
))) {
473 value
= WMGetFromPLDictionary(win_info
, sName
);
477 ParseWindowName(value
, &instance
, &class, "session");
478 if (!instance
&& !class)
481 value
= WMGetFromPLDictionary(win_info
, sHost
);
482 if (value
&& WMIsPLString(value
))
483 host
= WMGetFromPLString(value
);
487 state
= getWindowState(scr
, win_info
);
490 value
= WMGetFromPLDictionary(win_info
, sDock
);
491 if (value
&& WMIsPLString(value
) && (tmp
= WMGetFromPLString(value
)) != NULL
) {
492 if (sscanf(tmp
, "%i", &n
) != 1) {
493 if (!strcasecmp(tmp
, "DOCK")) {
496 for (j
= 0; j
< scr
->workspace_count
; j
++) {
497 if (strcmp(scr
->workspaces
[j
]->name
, tmp
) == 0) {
498 dock
= scr
->workspaces
[j
]->clip
;
506 } else if (n
> 0 && n
<= scr
->workspace_count
) {
507 dock
= scr
->workspaces
[n
- 1]->clip
;
514 for (j
= 0; j
< dock
->max_icons
; j
++) {
515 btn
= dock
->icon_array
[j
];
516 if (btn
&& SAME(instance
, btn
->wm_instance
) &&
517 SAME(class, btn
->wm_class
) && SAME(command
, btn
->command
) && !btn
->launching
) {
525 wDockLaunchWithState(dock
, btn
, state
);
526 } else if ((pid
= execCommand(scr
, command
, host
)) > 0) {
527 wWindowAddSavedState(instance
, class, command
, pid
, state
);
538 WMPLSetCaseSensitive(False
);
541 void wSessionRestoreLastWorkspace(WScreen
* scr
)
549 if (!scr
->session_state
)
552 WMPLSetCaseSensitive(True
);
554 wks
= WMGetFromPLDictionary(scr
->session_state
, sWorkspace
);
555 if (!wks
|| !WMIsPLString(wks
))
558 tmp
= WMGetFromPLString(wks
);
561 WMPLSetCaseSensitive(False
);
563 if (sscanf(tmp
, "%i", &w
) != 1) {
565 for (i
= 0; i
< scr
->workspace_count
; i
++) {
566 if (strcmp(scr
->workspaces
[i
]->name
, tmp
) == 0) {
575 if (w
!= scr
->current_workspace
&& w
< scr
->workspace_count
) {
576 wWorkspaceChange(scr
, w
);
580 static void clearWaitingAckState(WScreen
* scr
)
585 for (wwin
= scr
->focused_window
; wwin
!= NULL
; wwin
= wwin
->prev
) {
586 wwin
->flags
.waiting_save_ack
= 0;
587 if (wwin
->main_window
!= None
) {
588 wapp
= wApplicationOf(wwin
->main_window
);
590 wapp
->main_window_desc
->flags
.waiting_save_ack
= 0;
595 void wSessionSaveClients(WScreen
* scr
)
601 * With XSMP, this job is done by smproxy
603 void wSessionSendSaveYourself(WScreen
* scr
)
608 /* freeze client interaction with clients */
609 XGrabKeyboard(dpy
, scr
->root_win
, False
, GrabModeAsync
, GrabModeAsync
, CurrentTime
);
610 XGrabPointer(dpy
, scr
->root_win
, False
, ButtonPressMask
| ButtonReleaseMask
,
611 GrabModeAsync
, GrabModeAsync
, scr
->root_win
, None
, CurrentTime
);
613 clearWaitingAckState(scr
);
617 /* first send SAVE_YOURSELF for everybody */
618 for (wwin
= scr
->focused_window
; wwin
!= NULL
; wwin
= wwin
->prev
) {
621 mainWin
= wWindowFor(wwin
->main_window
);
624 /* if the client is a multi-window client, only send message
625 * to the main window */
629 /* make sure the SAVE_YOURSELF flag is up-to-date */
630 PropGetProtocols(wwin
->client_win
, &wwin
->protocols
);
632 if (wwin
->protocols
.SAVE_YOURSELF
) {
633 if (!wwin
->flags
.waiting_save_ack
) {
634 wClientSendProtocol(wwin
, _XA_WM_SAVE_YOURSELF
, LastTimestamp
);
636 wwin
->flags
.waiting_save_ack
= 1;
640 wwin
->flags
.waiting_save_ack
= 0;
644 /* then wait for acknowledge */
649 XUngrabPointer(dpy
, CurrentTime
);
650 XUngrabKeyboard(dpy
, CurrentTime
);
656 * With full session management support, the part of WMState
657 * that store client window state will become obsolete (maybe we can reuse
659 * but we still need to store state info like the dock and workspaces.
660 * It is better to keep dock/wspace info in WMState because the user
661 * might want to keep the dock configuration while not wanting to
662 * resume a previously saved session.
663 * So, wmaker specific state info can be saved in
664 * ~/GNUstep/Library/WindowMaker/statename.state
665 * Its better to not put it in the defaults directory because:
666 * - its not a defaults file (having domain names like wmaker0089504baa
667 * in the defaults directory wouldn't be very neat)
668 * - this state file is not meant to be edited by users
670 * The old session code will become obsolete. When wmaker is
671 * compiled with R6 sm support compiled in, it'll be better to
672 * use a totally rewritten state saving code, but we can keep
673 * the current code for when XSMP_ENABLED is not compiled in.
675 * This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
676 * messages), but it'll be better.
681 static char *getWindowRole(Window window
)
684 static Atom atom
= 0;
687 atom
= XInternAtom(dpy
, "WM_WINDOW_ROLE", False
);
689 if (XGetTextProperty(dpy
, window
, &prop
, atom
)) {
690 if (prop
.encoding
== XA_STRING
&& prop
.format
== 8 && prop
.nitems
> 0)
709 * state = (miniaturized, shaded, etc)
712 * app state = (which dock, hidden)
716 static WMPropList
*makeAppState(WWindow
* wwin
)
720 WScreen
*scr
= wwin
->screen_ptr
;
722 state
= WMCreatePLArray(NULL
, NULL
);
724 wapp
= wApplicationOf(wwin
->main_window
);
727 if (wapp
->app_icon
&& wapp
->app_icon
->dock
) {
729 if (wapp
->app_icon
->dock
== scr
->dock
) {
730 WMAddToPLArray(state
, WMCreatePLString("Dock"));
734 for (i
= 0; i
< scr
->workspace_count
; i
++)
735 if (scr
->workspaces
[i
]->clip
== wapp
->app_icon
->dock
)
738 assert(i
< scr
->workspace_count
);
740 WMAddToPLArray(state
, WMCreatePLString(scr
->workspaces
[i
]->name
));
744 WMAddToPLArray(state
, WMCreatePLString(wapp
->hidden
? "1" : "0"));
750 Bool
wSessionGetStateFor(WWindow
* wwin
, WSessionData
* state
)
761 value
= WMGetFromPLArray(slist
, index
++);
762 str
= WMGetFromPLString(value
);
764 sscanf(str
, "%i %i %i %i %i %i", &state
->x
, &state
->y
,
765 &state
->width
, &state
->height
, &state
->user_changed_width
, &state
->user_changed_height
);
768 value
= WMGetFromPLArray(slist
, index
++);
769 str
= WMGetFromPLString(value
);
771 sscanf(str
, "%i %i %i", &state
->miniaturized
, &state
->shaded
, &state
->maximized
);
774 value
= WMGetFromPLArray(slist
, index
++);
775 str
= WMGetFromPLString(value
);
777 getAttributeState(str
, &state
->mflags
, &state
->flags
);
780 value
= WMGetFromPLArray(slist
, index
++);
781 str
= WMGetFromPLString(value
);
783 sscanf(str
, "%i", &state
->workspace
);
785 /* app state (repeated for all windows of the app) */
786 value
= WMGetFromPLArray(slist
, index
++);
787 str
= WMGetFromPLString(value
);
792 value
= WMGetFromPLArray(slist
, index
++);
793 str
= WMGetFromPLString(value
);
795 sscanf(str
, "%i", &state
->shortcuts
);
798 static WMPropList
*makeAttributeState(WWindow
* wwin
)
800 unsigned int data1
, data2
;
803 #define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
804 ? (wwin)->user_flags.FLAG : -1)
806 snprintf(buffer
, sizeof(buffer
),
807 "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
809 W_FLAG(no_resizable
),
811 W_FLAG(no_miniaturizable
),
812 W_FLAG(no_resizebar
), W_FLAG(no_close_button
), W_FLAG(no_miniaturize_button
),
814 W_FLAG(broken_close),
817 W_FLAG(no_shadeable
),
819 W_FLAG(skip_window_list
),
822 W_FLAG(no_bind_keys
),
823 W_FLAG(no_bind_mouse
),
824 W_FLAG(no_hide_others
),
826 W_FLAG(dont_move_off
),
827 W_FLAG(no_focusable
),
828 W_FLAG(always_user_icon
),
829 W_FLAG(start_miniaturized
),
830 W_FLAG(start_hidden
),
831 W_FLAG(start_maximized
),
832 W_FLAG(dont_save_session
), W_FLAG(dont_focus_across_wksp
), W_FLAG(emulate_appicon
));
834 return WMCreatePLString(buffer
);
837 static void appendStringInArray(WMPropList
* array
, char *str
)
841 val
= WMCreatePLString(str
);
842 WMAddToPLArray(array
, val
);
843 WMReleasePropList(val
);
846 static WMPropList
*makeClientState(WWindow
* wwin
)
855 state
= WMCreatePLArray(NULL
, NULL
);
858 str
= getWindowRole(wwin
->client_win
);
860 appendStringInArray(state
, "");
862 appendStringInArray(state
, str
);
866 /* WM_CLASS.instance */
867 appendStringInArray(state
, wwin
->wm_instance
);
870 appendStringInArray(state
, wwin
->wm_class
);
873 if (wwin
->flags
.wm_name_changed
)
874 appendStringInArray(state
, "");
876 appendStringInArray(state
, wwin
->frame
->name
);
879 snprintf(buffer
, sizeof(buffer
), "%i %i %i %i %i %i", wwin
->frame_x
, wwin
->frame_y
,
880 wwin
->client
.width
, wwin
->client
.height
,
881 wwin
->flags
.user_changed_width
, wwin
->flags
.user_changed_height
);
882 appendStringInArray(state
, buffer
);
885 snprintf(buffer
, sizeof(buffer
), "%i %i %i", wwin
->flags
.miniaturized
,
886 wwin
->flags
.shaded
, wwin
->flags
.maximized
);
887 appendStringInArray(state
, buffer
);
890 tmp
= makeAttributeState(wwin
);
891 WMAddToPLArray(state
, tmp
);
892 WMReleasePropList(tmp
);
895 snprintf(buffer
, sizeof(buffer
), "%i", wwin
->frame
->workspace
);
896 appendStringInArray(state
, buffer
);
898 /* app state (repeated for all windows of the app) */
899 tmp
= makeAppState(wwin
);
900 WMAddToPLArray(state
, tmp
);
901 WMReleasePropList(tmp
);
905 for (i
= 0; i
< MAX_WINDOW_SHORTCUTS
; i
++) {
906 if (scr
->shortcutWindow
[i
] == wwin
) {
910 snprintf(buffer
, sizeof(buffer
), "%ui", shortcuts
);
911 appendStringInArray(tmp
, buffer
);
916 static void smSaveYourselfPhase2Proc(SmcConn smc_conn
, SmPointer client_data
)
919 SmPropValue prop1val
, prop2val
, prop3val
, prop4val
;
920 char **argv
= (char **)client_data
;
924 char *statefile
= NULL
;
926 Bool gsPrefix
= False
;
927 char *discardCmd
= NULL
;
929 WMPropList
*state
, *plState
;
933 puts("received SaveYourselfPhase2 SM message");
936 /* save session state */
938 /* the file that will contain the state */
939 prefix
= getenv("SM_SAVE_DIR");
941 prefix
= wusergnusteppath();
946 prefix
= getenv("HOME");
951 len
= strlen(prefix
) + 64;
952 statefile
= malloc(len
);
954 wwarning(_("out of memory while saving session state"));
962 snprintf(statefile
, len
, "%s/Library/WindowMaker/wmaker.%l%i.state", prefix
, t
, i
);
964 snprintf(statefile
, len
, "%s/wmaker.%l%i.state", prefix
, t
, i
);
966 } while (access(F_OK
, statefile
) != -1);
968 /* save the states of all windows we're managing */
969 state
= WMCreatePLArray(NULL
, NULL
);
974 * state_file ::= dictionary with version_info ; state
975 * version_info ::= 'version' = '1';
976 * state ::= 'state' = array of screen_info
977 * screen_info ::= array of (screen number, window_info, window_info, ...)
980 for (i
= 0; i
< wScreenCount
; i
++) {
986 scr
= wScreenWithNumber(i
);
988 snprintf(buf
, sizeof(buf
), "%i", scr
->screen
);
989 pscreen
= WMCreatePLArray(WMCreatePLString(buf
), NULL
);
991 wwin
= scr
->focused_window
;
995 pwindow
= makeClientState(wwin
);
996 WMAddToPLArray(pscreen
, pwindow
);
1001 WMAddToPLArray(state
, pscreen
);
1004 plState
= WMCreatePLDictionary(WMCreatePLString("Version"),
1005 WMCreatePLString("1.0"), WMCreatePLString("Screens"), state
, NULL
);
1007 if (!WMWritePropListToFile(plState
, statefile
, True
)) {
1008 wwarning(_("error while saving session state"));
1009 WMReleasePropList(plState
);
1013 WMReleasePropList(plState
);
1015 /* set the remaining properties that we didn't set at
1018 for (argc
= 0, i
= 0; argv
[i
] != NULL
; i
++) {
1019 if (strcmp(argv
[i
], "-clientid") == 0 || strcmp(argv
[i
], "-restore") == 0) {
1026 prop
[0].name
= SmRestartCommand
;
1027 prop
[0].type
= SmLISTofARRAY8
;
1028 prop
[0].vals
= malloc(sizeof(SmPropValue
) * (argc
+ 4));
1029 prop
[0].num_vals
= argc
+ 4;
1031 prop
[1].name
= SmCloneCommand
;
1032 prop
[1].type
= SmLISTofARRAY8
;
1033 prop
[1].vals
= malloc(sizeof(SmPropValue
) * (argc
));
1034 prop
[1].num_vals
= argc
;
1036 if (!prop
[0].vals
|| !prop
[1].vals
) {
1037 wwarning(_("end of memory while saving session state"));
1041 for (j
= 0, i
= 0; i
< argc
+ 4; i
++) {
1042 if (strcmp(argv
[i
], "-clientid") == 0 || strcmp(argv
[i
], "-restore") == 0) {
1045 prop
[0].vals
[j
].value
= argv
[i
];
1046 prop
[0].vals
[j
].length
= strlen(argv
[i
]);
1047 prop
[1].vals
[j
].value
= argv
[i
];
1048 prop
[1].vals
[j
].length
= strlen(argv
[i
]);
1052 prop
[0].vals
[j
].value
= "-clientid";
1053 prop
[0].vals
[j
].length
= 9;
1055 prop
[0].vals
[j
].value
= sClientID
;
1056 prop
[0].vals
[j
].length
= strlen(sClientID
);
1058 prop
[0].vals
[j
].value
= "-restore";
1059 prop
[0].vals
[j
].length
= 11;
1061 prop
[0].vals
[j
].value
= statefile
;
1062 prop
[0].vals
[j
].length
= strlen(statefile
);
1065 int len
= strlen(statefile
) + 8;
1067 discardCmd
= malloc(len
);
1070 snprintf(discardCmd
, len
, "rm %s", statefile
);
1072 prop
[2].name
= SmDiscardCommand
;
1073 prop
[2].type
= SmARRAY8
;
1074 prop
[2].vals
[0] = discardCmd
;
1075 prop
[2].num_vals
= 1;
1077 SmcSetProperties(sSMCConn
, 3, prop
);
1081 SmcSaveYourselfDone(smc_conn
, ok
);
1084 wfree(prop
[0].vals
);
1086 wfree(prop
[1].vals
);
1098 smSaveYourselfProc(SmcConn smc_conn
, SmPointer client_data
, int save_type
,
1099 Bool shutdown
, int interact_style
, Bool fast
)
1102 puts("received SaveYourself SM message");
1105 if (!SmcRequestSaveYourselfPhase2(smc_conn
, smSaveYourselfPhase2Proc
, client_data
)) {
1107 SmcSaveYourselfDone(smc_conn
, False
);
1108 sWaitingPhase2
= False
;
1111 puts("successfull request of SYS phase 2");
1113 sWaitingPhase2
= True
;
1117 static void smDieProc(SmcConn smc_conn
, SmPointer client_data
)
1120 puts("received Die SM message");
1123 wSessionDisconnectManager();
1125 Shutdown(WSExitMode
, True
);
1128 static void smSaveCompleteProc(SmcConn smc_conn
)
1130 /* it means that we can resume doing things that can change our state */
1132 puts("received SaveComplete SM message");
1136 static void smShutdownCancelledProc(SmcConn smc_conn
, SmPointer client_data
)
1138 if (sWaitingPhase2
) {
1140 sWaitingPhase2
= False
;
1142 SmcSaveYourselfDone(smc_conn
, False
);
1146 static void iceMessageProc(int fd
, int mask
, void *clientData
)
1148 IceConn iceConn
= (IceConn
) clientData
;
1150 IceProcessMessages(iceConn
, NULL
, NULL
);
1153 static void iceIOErrorHandler(IceConnection ice_conn
)
1155 /* This is not fatal but can mean the session manager exited.
1156 * If the session manager exited normally we would get a
1157 * Die message, so this probably means an abnormal exit.
1158 * If the sm was the last client of session, then we'll die
1159 * anyway, otherwise we can continue doing our stuff.
1161 wwarning(_("connection to the session manager was lost"));
1162 wSessionDisconnectManager();
1165 void wSessionConnectManager(char **argv
, int argc
)
1168 char *previous_id
= NULL
;
1170 SmcCallbacks callbacks
;
1175 SmPropValue prop1val
, prop2val
, prop3val
, prop4val
;
1179 mask
= SmcSaveYourselfProcMask
| SmcDieProcMask
| SmcSaveCompleteProcMask
| SmcShutdownCancelledProcMask
;
1181 callbacks
.save_yourself
.callback
= smSaveYourselfProc
;
1182 callbacks
.save_yourself
.client_data
= argv
;
1184 callbacks
.die
.callback
= smDieProc
;
1185 callbacks
.die
.client_data
= NULL
;
1187 callbacks
.save_complete
.callback
= smSaveCompleteProc
;
1188 callbacks
.save_complete
.client_data
= NULL
;
1190 callbacks
.shutdown_cancelled
.callback
= smShutdownCancelledProc
;
1191 callbacks
.shutdown_cancelled
.client_data
= NULL
;
1193 for (i
= 0; i
< argc
; i
++) {
1194 if (strcmp(argv
[i
], "-clientid") == 0) {
1195 previous_id
= argv
[i
+ 1];
1200 /* connect to the session manager */
1201 sSMCConn
= SmcOpenConnection(NULL
, NULL
, SmProtoMajor
, SmProtoMinor
,
1202 mask
, &callbacks
, previous_id
, &sClientID
, 255, buffer
);
1207 puts("connected to the session manager");
1210 /* IceSetIOErrorHandler(iceIOErrorHandler); */
1212 /* check for session manager clients */
1213 iceConn
= SmcGetIceConnection(smcConn
);
1215 if (fcntl(IceConnectionNumber(iceConn
), F_SETFD
, FD_CLOEXEC
) < 0) {
1216 wsyserror("error setting close-on-exec flag for ICE connection");
1219 sSMInputHandler
= WMAddInputHandler(IceConnectionNumber(iceConn
), WIReadMask
, iceMessageProc
, iceConn
);
1221 /* setup information about ourselves */
1224 prop1val
.value
= argv
[0];
1225 prop1val
.length
= strlen(argv
[0]);
1226 prop
[0].name
= SmProgram
;
1227 prop
[0].type
= SmARRAY8
;
1228 prop
[0].num_vals
= 1;
1229 prop
[0].vals
= &prop1val
;
1231 /* The XSMP doc from X11R6.1 says it contains the user name,
1232 * but every client implementation I saw places the uid # */
1233 snprintf(uid
, sizeof(uid
), "%i", getuid());
1234 prop2val
.value
= uid
;
1235 prop2val
.length
= strlen(uid
);
1236 prop
[1].name
= SmUserID
;
1237 prop
[1].type
= SmARRAY8
;
1238 prop
[1].num_vals
= 1;
1239 prop
[1].vals
= &prop2val
;
1241 /* Restart style. We should restart only if we were running when
1242 * the previous session finished. */
1243 restartStyle
= SmRestartIfRunning
;
1244 prop3val
.value
= &restartStyle
;
1245 prop3val
.length
= 1;
1246 prop
[2].name
= SmRestartStyleHint
;
1247 prop
[2].type
= SmCARD8
;
1248 prop
[2].num_vals
= 1;
1249 prop
[2].vals
= &prop3val
;
1251 /* Our PID. Not required but might be usefull */
1252 snprintf(pid
, sizeof(pid
), "%i", getpid());
1253 prop4val
.value
= pid
;
1254 prop4val
.length
= strlen(pid
);
1255 prop
[3].name
= SmProcessID
;
1256 prop
[3].type
= SmARRAY8
;
1257 prop
[3].num_vals
= 1;
1258 prop
[3].vals
= &prop4val
;
1260 /* we'll set the rest of the hints later */
1262 SmcSetProperties(sSMCConn
, 4, props
);
1266 void wSessionDisconnectManager(void)
1269 WMDeleteInputHandler(sSMInputHandler
);
1270 sSMInputHandler
= NULL
;
1272 SmcCloseConnection(sSMCConn
, 0, NULL
);
1277 void wSessionRequestShutdown(void)
1279 /* request a shutdown to the session manager */
1281 SmcRequestSaveYourself(sSMCConn
, SmSaveBoth
, True
, SmInteractStyleAny
, False
, True
);
1284 Bool
wSessionIsManaged(void)
1286 return sSMCConn
!= NULL
;
1289 #endif /* !XSMP_ENABLED */