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 along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If defined(XSMP_ENABLED) and session manager is running then
28 * do pre-R6 session management stuff (save window state and relaunch)
30 * When doing a checkpoint:
33 * Open "Stop"/status Dialog
34 * Send SAVE_YOURSELF to clients and wait for reply
36 * Save state of clients
39 * Send checkpoint request to sm
46 * Open "Exit Now"/status Dialog
47 * Send SAVE_YOURSELF to clients and wait for reply
49 * Save state of clients
50 * Send DELETE to all clients
51 * When no more clients are left or user hit "Exit Now", exit
55 * Send Shutdown request to session manager
56 * if SaveYourself message received, save state of clients
57 * if the Die message is received, exit.
63 #include <X11/Xutil.h>
71 #include "WindowMaker.h"
77 #include "workspace.h"
79 #include "properties.h"
80 #include "application.h"
84 #include <WINGs/WUtil.h>
88 extern Atom _XA_WM_SAVE_YOURSELF
;
90 extern Time LastTimestamp
;
92 static WMPropList
*sApplications
= NULL
;
93 static WMPropList
*sCommand
;
94 static WMPropList
*sName
;
95 static WMPropList
*sHost
;
96 static WMPropList
*sWorkspace
;
97 static WMPropList
*sShaded
;
98 static WMPropList
*sMiniaturized
;
99 static WMPropList
*sHidden
;
100 static WMPropList
*sGeometry
;
101 static WMPropList
*sShortcutMask
;
103 static WMPropList
*sDock
;
104 static WMPropList
*sYes
, *sNo
;
106 static void make_keys(void)
108 if (sApplications
!= NULL
)
111 sApplications
= WMCreatePLString("Applications");
112 sCommand
= WMCreatePLString("Command");
113 sName
= WMCreatePLString("Name");
114 sHost
= WMCreatePLString("Host");
115 sWorkspace
= WMCreatePLString("Workspace");
116 sShaded
= WMCreatePLString("Shaded");
117 sMiniaturized
= WMCreatePLString("Miniaturized");
118 sHidden
= WMCreatePLString("Hidden");
119 sGeometry
= WMCreatePLString("Geometry");
120 sDock
= WMCreatePLString("Dock");
121 sShortcutMask
= WMCreatePLString("ShortcutMask");
123 sYes
= WMCreatePLString("Yes");
124 sNo
= WMCreatePLString("No");
127 static int getBool(WMPropList
* value
)
131 if (!WMIsPLString(value
)) {
134 if (!(val
= WMGetFromPLString(value
))) {
138 if ((val
[1] == '\0' && (val
[0] == 'y' || val
[0] == 'Y'))
139 || strcasecmp(val
, "YES") == 0) {
142 } else if ((val
[1] == '\0' && (val
[0] == 'n' || val
[0] == 'N'))
143 || strcasecmp(val
, "NO") == 0) {
147 if (sscanf(val
, "%i", &i
) == 1) {
150 wwarning(_("can't convert \"%s\" to boolean"), val
);
156 static unsigned getInt(WMPropList
* value
)
161 if (!WMIsPLString(value
))
163 val
= WMGetFromPLString(value
);
166 if (sscanf(val
, "%u", &n
) != 1)
172 static WMPropList
*makeWindowState(WWindow
* wwin
, WApplication
* wapp
)
174 WScreen
*scr
= wwin
->screen_ptr
;
178 char *class, *instance
, *command
= NULL
, buffer
[512];
179 WMPropList
*win_state
, *cmd
, *name
, *workspace
;
180 WMPropList
*shaded
, *miniaturized
, *hidden
, *geometry
;
181 WMPropList
*dock
, *shortcut
;
183 if (wwin
->orig_main_window
!= None
&& wwin
->orig_main_window
!= wwin
->client_win
)
184 win
= wwin
->orig_main_window
;
186 win
= wwin
->client_win
;
188 command
= GetCommandForWindow(win
);
192 if (PropGetWMClass(win
, &class, &instance
)) {
193 if (class && instance
)
194 snprintf(buffer
, sizeof(buffer
), "%s.%s", instance
, class);
196 snprintf(buffer
, sizeof(buffer
), "%s", instance
);
198 snprintf(buffer
, sizeof(buffer
), ".%s", class);
200 snprintf(buffer
, sizeof(buffer
), ".");
202 name
= WMCreatePLString(buffer
);
203 cmd
= WMCreatePLString(command
);
204 /*sprintf(buffer, "%d", wwin->frame->workspace+1);
205 workspace = WMCreatePLString(buffer); */
206 workspace
= WMCreatePLString(scr
->workspaces
[wwin
->frame
->workspace
]->name
);
207 shaded
= wwin
->flags
.shaded
? sYes
: sNo
;
208 miniaturized
= wwin
->flags
.miniaturized
? sYes
: sNo
;
209 hidden
= wwin
->flags
.hidden
? sYes
: sNo
;
210 snprintf(buffer
, sizeof(buffer
), "%ix%i+%i+%i",
211 wwin
->client
.width
, wwin
->client
.height
, wwin
->frame_x
, wwin
->frame_y
);
212 geometry
= WMCreatePLString(buffer
);
214 for (mask
= 0, i
= 0; i
< MAX_WINDOW_SHORTCUTS
; i
++) {
215 if (scr
->shortcutWindows
[i
] != NULL
&&
216 WMGetFirstInArray(scr
->shortcutWindows
[i
], wwin
) != WANotFound
) {
221 snprintf(buffer
, sizeof(buffer
), "%u", mask
);
222 shortcut
= WMCreatePLString(buffer
);
224 win_state
= WMCreatePLDictionary(sName
, name
,
226 sWorkspace
, workspace
,
228 sMiniaturized
, miniaturized
,
230 sShortcutMask
, shortcut
, sGeometry
, geometry
, NULL
);
232 WMReleasePropList(name
);
233 WMReleasePropList(cmd
);
234 WMReleasePropList(workspace
);
235 WMReleasePropList(geometry
);
236 WMReleasePropList(shortcut
);
237 if (wapp
&& wapp
->app_icon
&& wapp
->app_icon
->dock
) {
240 if (wapp
->app_icon
->dock
== scr
->dock
) {
243 for (i
= 0; i
< scr
->workspace_count
; i
++)
244 if (scr
->workspaces
[i
]->clip
== wapp
->app_icon
->dock
)
246 assert(i
< scr
->workspace_count
);
248 name
= scr
->workspaces
[i
]->name
;
250 dock
= WMCreatePLString(name
);
251 WMPutInPLDictionary(win_state
, sDock
, dock
);
252 WMReleasePropList(dock
);
268 void wSessionSaveState(WScreen
* scr
)
270 WWindow
*wwin
= scr
->focused_window
;
271 WMPropList
*win_info
, *wks
;
272 WMPropList
*list
= NULL
;
273 WMArray
*wapp_list
= NULL
;
277 if (!scr
->session_state
) {
278 scr
->session_state
= WMCreatePLDictionary(NULL
, NULL
);
279 if (!scr
->session_state
)
283 list
= WMCreatePLArray(NULL
);
285 wapp_list
= WMCreateArray(16);
288 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
289 Window appId
= wwin
->orig_main_window
;
291 if ((wwin
->transient_for
== None
|| wwin
->transient_for
== wwin
->screen_ptr
->root_win
)
292 && (WMGetFirstInArray(wapp_list
, (void *)appId
) == WANotFound
293 || WFLAGP(wwin
, shared_appicon
))
294 && !WFLAGP(wwin
, dont_save_session
)) {
295 /* A entry for this application was not yet saved. Save one. */
296 if ((win_info
= makeWindowState(wwin
, wapp
)) != NULL
) {
297 WMAddToPLArray(list
, win_info
);
298 WMReleasePropList(win_info
);
299 /* If we were succesful in saving the info for this window
300 * add the application the window belongs to, to the
301 * application list, so no multiple entries for the same
302 * application are saved.
304 WMAddToArray(wapp_list
, (void *)appId
);
309 WMRemoveFromPLDictionary(scr
->session_state
, sApplications
);
310 WMPutInPLDictionary(scr
->session_state
, sApplications
, list
);
311 WMReleasePropList(list
);
313 wks
= WMCreatePLString(scr
->workspaces
[scr
->current_workspace
]->name
);
314 WMPutInPLDictionary(scr
->session_state
, sWorkspace
, wks
);
315 WMReleasePropList(wks
);
317 WMFreeArray(wapp_list
);
320 void wSessionClearState(WScreen
* scr
)
324 if (!scr
->session_state
)
327 WMRemoveFromPLDictionary(scr
->session_state
, sApplications
);
328 WMRemoveFromPLDictionary(scr
->session_state
, sWorkspace
);
331 static pid_t
execCommand(WScreen
* scr
, char *command
, char *host
)
337 wtokensplit(command
, &argv
, &argc
);
343 if ((pid
= fork()) == 0) {
347 SetupEnvironment(scr
);
349 args
= malloc(sizeof(char *) * (argc
+ 1));
352 for (i
= 0; i
< argc
; i
++) {
356 execvp(argv
[0], args
);
365 static WSavedState
*getWindowState(WScreen
* scr
, WMPropList
* win_state
)
367 WSavedState
*state
= wmalloc(sizeof(WSavedState
));
373 memset(state
, 0, sizeof(WSavedState
));
374 state
->workspace
= -1;
375 value
= WMGetFromPLDictionary(win_state
, sWorkspace
);
376 if (value
&& WMIsPLString(value
)) {
377 tmp
= WMGetFromPLString(value
);
378 if (sscanf(tmp
, "%i", &state
->workspace
) != 1) {
379 state
->workspace
= -1;
380 for (i
= 0; i
< scr
->workspace_count
; i
++) {
381 if (strcmp(scr
->workspaces
[i
]->name
, tmp
) == 0) {
382 state
->workspace
= i
;
390 if ((value
= WMGetFromPLDictionary(win_state
, sShaded
)) != NULL
)
391 state
->shaded
= getBool(value
);
392 if ((value
= WMGetFromPLDictionary(win_state
, sMiniaturized
)) != NULL
)
393 state
->miniaturized
= getBool(value
);
394 if ((value
= WMGetFromPLDictionary(win_state
, sHidden
)) != NULL
)
395 state
->hidden
= getBool(value
);
396 if ((value
= WMGetFromPLDictionary(win_state
, sShortcutMask
)) != NULL
) {
397 mask
= getInt(value
);
398 state
->window_shortcuts
= mask
;
401 value
= WMGetFromPLDictionary(win_state
, sGeometry
);
402 if (value
&& WMIsPLString(value
)) {
403 if (!(sscanf(WMGetFromPLString(value
), "%ix%i+%i+%i",
404 &state
->w
, &state
->h
, &state
->x
, &state
->y
) == 4 && (state
->w
> 0 && state
->h
> 0))) {
413 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
415 void wSessionRestoreState(WScreen
* scr
)
418 char *instance
, *class, *command
, *host
;
419 WMPropList
*win_info
, *apps
, *cmd
, *value
;
423 WAppIcon
*btn
= NULL
;
429 if (!scr
->session_state
)
432 WMPLSetCaseSensitive(True
);
434 apps
= WMGetFromPLDictionary(scr
->session_state
, sApplications
);
438 count
= WMGetPropListItemCount(apps
);
442 for (i
= 0; i
< count
; i
++) {
443 win_info
= WMGetFromPLArray(apps
, i
);
445 cmd
= WMGetFromPLDictionary(win_info
, sCommand
);
446 if (!cmd
|| !WMIsPLString(cmd
) || !(command
= WMGetFromPLString(cmd
))) {
450 value
= WMGetFromPLDictionary(win_info
, sName
);
454 ParseWindowName(value
, &instance
, &class, "session");
455 if (!instance
&& !class)
458 value
= WMGetFromPLDictionary(win_info
, sHost
);
459 if (value
&& WMIsPLString(value
))
460 host
= WMGetFromPLString(value
);
464 state
= getWindowState(scr
, win_info
);
467 value
= WMGetFromPLDictionary(win_info
, sDock
);
468 if (value
&& WMIsPLString(value
) && (tmp
= WMGetFromPLString(value
)) != NULL
) {
469 if (sscanf(tmp
, "%i", &n
) != 1) {
470 if (!strcasecmp(tmp
, "DOCK")) {
473 for (j
= 0; j
< scr
->workspace_count
; j
++) {
474 if (strcmp(scr
->workspaces
[j
]->name
, tmp
) == 0) {
475 dock
= scr
->workspaces
[j
]->clip
;
483 } else if (n
> 0 && n
<= scr
->workspace_count
) {
484 dock
= scr
->workspaces
[n
- 1]->clip
;
491 for (j
= 0; j
< dock
->max_icons
; j
++) {
492 btn
= dock
->icon_array
[j
];
493 if (btn
&& SAME(instance
, btn
->wm_instance
) &&
494 SAME(class, btn
->wm_class
) && SAME(command
, btn
->command
) && !btn
->launching
) {
502 wDockLaunchWithState(dock
, btn
, state
);
503 } else if ((pid
= execCommand(scr
, command
, host
)) > 0) {
504 wWindowAddSavedState(instance
, class, command
, pid
, state
);
515 WMPLSetCaseSensitive(False
);
518 void wSessionRestoreLastWorkspace(WScreen
* scr
)
526 if (!scr
->session_state
)
529 WMPLSetCaseSensitive(True
);
531 wks
= WMGetFromPLDictionary(scr
->session_state
, sWorkspace
);
532 if (!wks
|| !WMIsPLString(wks
))
535 tmp
= WMGetFromPLString(wks
);
538 WMPLSetCaseSensitive(False
);
540 if (sscanf(tmp
, "%i", &w
) != 1) {
542 for (i
= 0; i
< scr
->workspace_count
; i
++) {
543 if (strcmp(scr
->workspaces
[i
]->name
, tmp
) == 0) {
552 if (w
!= scr
->current_workspace
&& w
< scr
->workspace_count
) {
553 wWorkspaceChange(scr
, w
);
557 static void clearWaitingAckState(WScreen
* scr
)
562 for (wwin
= scr
->focused_window
; wwin
!= NULL
; wwin
= wwin
->prev
) {
563 wwin
->flags
.waiting_save_ack
= 0;
564 if (wwin
->main_window
!= None
) {
565 wapp
= wApplicationOf(wwin
->main_window
);
567 wapp
->main_window_desc
->flags
.waiting_save_ack
= 0;
572 void wSessionSaveClients(WScreen
* scr
)
578 * With XSMP, this job is done by smproxy
580 void wSessionSendSaveYourself(WScreen
* scr
)
585 /* freeze client interaction with clients */
586 XGrabKeyboard(dpy
, scr
->root_win
, False
, GrabModeAsync
, GrabModeAsync
, CurrentTime
);
587 XGrabPointer(dpy
, scr
->root_win
, False
, ButtonPressMask
| ButtonReleaseMask
,
588 GrabModeAsync
, GrabModeAsync
, scr
->root_win
, None
, CurrentTime
);
590 clearWaitingAckState(scr
);
594 /* first send SAVE_YOURSELF for everybody */
595 for (wwin
= scr
->focused_window
; wwin
!= NULL
; wwin
= wwin
->prev
) {
598 mainWin
= wWindowFor(wwin
->main_window
);
601 /* if the client is a multi-window client, only send message
602 * to the main window */
606 /* make sure the SAVE_YOURSELF flag is up-to-date */
607 PropGetProtocols(wwin
->client_win
, &wwin
->protocols
);
609 if (wwin
->protocols
.SAVE_YOURSELF
) {
610 if (!wwin
->flags
.waiting_save_ack
) {
611 wClientSendProtocol(wwin
, _XA_WM_SAVE_YOURSELF
, LastTimestamp
);
613 wwin
->flags
.waiting_save_ack
= 1;
617 wwin
->flags
.waiting_save_ack
= 0;
621 /* then wait for acknowledge */
626 XUngrabPointer(dpy
, CurrentTime
);
627 XUngrabKeyboard(dpy
, CurrentTime
);