mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-20 12:58:08 +01:00
1384 lines
34 KiB
C
1384 lines
34 KiB
C
/* session.c - session state handling and R6 style session management
|
|
*
|
|
* Copyright (c) 1998-2002 Dan Pascu
|
|
* Copyright (c) 1998-2002 Alfredo Kojima
|
|
*
|
|
* Window Maker window manager
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
* USA.
|
|
*/
|
|
|
|
|
|
/*
|
|
*
|
|
* If defined(XSMP_ENABLED) and session manager is running then
|
|
* do normal stuff
|
|
* else
|
|
* do pre-R6 session management stuff (save window state and relaunch)
|
|
*
|
|
* When doing a checkpoint:
|
|
*
|
|
* = Without XSMP
|
|
* Open "Stop"/status Dialog
|
|
* Send SAVE_YOURSELF to clients and wait for reply
|
|
* Save restart info
|
|
* Save state of clients
|
|
*
|
|
* = With XSMP
|
|
* Send checkpoint request to sm
|
|
*
|
|
* When exiting:
|
|
* -------------
|
|
*
|
|
* = Without XSMP
|
|
*
|
|
* Open "Exit Now"/status Dialog
|
|
* Send SAVE_YOURSELF to clients and wait for reply
|
|
* Save restart info
|
|
* Save state of clients
|
|
* Send DELETE to all clients
|
|
* When no more clients are left or user hit "Exit Now", exit
|
|
*
|
|
* = With XSMP
|
|
*
|
|
* Send Shutdown request to session manager
|
|
* if SaveYourself message received, save state of clients
|
|
* if the Die message is received, exit.
|
|
*/
|
|
|
|
#include "wconfig.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
|
|
#ifdef XSMP_ENABLED
|
|
#include <X11/SM/SMlib.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
|
|
#include "WindowMaker.h"
|
|
#include "screen.h"
|
|
#include "window.h"
|
|
#include "client.h"
|
|
#include "session.h"
|
|
#include "wcore.h"
|
|
#include "framewin.h"
|
|
#include "workspace.h"
|
|
#include "funcs.h"
|
|
#include "properties.h"
|
|
#include "application.h"
|
|
#include "appicon.h"
|
|
|
|
|
|
#include "dock.h"
|
|
|
|
|
|
#include <WINGs/WUtil.h>
|
|
|
|
/** Global **/
|
|
|
|
extern Atom _XA_WM_SAVE_YOURSELF;
|
|
|
|
extern Time LastTimestamp;
|
|
|
|
#ifdef XSMP_ENABLED
|
|
|
|
extern int wScreenCount;
|
|
|
|
/* requested for SaveYourselfPhase2 */
|
|
static Bool sWaitingPhase2 = False;
|
|
|
|
static SmcConn sSMCConn = NULL;
|
|
|
|
static WMHandlerID sSMInputHandler = NULL;
|
|
|
|
/* our SM client ID */
|
|
static char *sClientID = NULL;
|
|
#endif
|
|
|
|
|
|
static WMPropList *sApplications = NULL;
|
|
static WMPropList *sCommand;
|
|
static WMPropList *sName;
|
|
static WMPropList *sHost;
|
|
static WMPropList *sWorkspace;
|
|
static WMPropList *sShaded;
|
|
static WMPropList *sMiniaturized;
|
|
static WMPropList *sHidden;
|
|
static WMPropList *sGeometry;
|
|
static WMPropList *sShortcutMask;
|
|
|
|
static WMPropList *sDock;
|
|
|
|
static WMPropList *sYes, *sNo;
|
|
|
|
|
|
static void
|
|
make_keys()
|
|
{
|
|
if (sApplications!=NULL)
|
|
return;
|
|
|
|
sApplications = WMCreatePLString("Applications");
|
|
sCommand = WMCreatePLString("Command");
|
|
sName = WMCreatePLString("Name");
|
|
sHost = WMCreatePLString("Host");
|
|
sWorkspace = WMCreatePLString("Workspace");
|
|
sShaded = WMCreatePLString("Shaded");
|
|
sMiniaturized = WMCreatePLString("Miniaturized");
|
|
sHidden = WMCreatePLString("Hidden");
|
|
sGeometry = WMCreatePLString("Geometry");
|
|
sDock = WMCreatePLString("Dock");
|
|
sShortcutMask = WMCreatePLString("ShortcutMask");
|
|
|
|
sYes = WMCreatePLString("Yes");
|
|
sNo = WMCreatePLString("No");
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
getBool(WMPropList *value)
|
|
{
|
|
char *val;
|
|
|
|
if (!WMIsPLString(value)) {
|
|
return 0;
|
|
}
|
|
if (!(val = WMGetFromPLString(value))) {
|
|
return 0;
|
|
}
|
|
|
|
if ((val[1]=='\0' && (val[0]=='y' || val[0]=='Y'))
|
|
|| strcasecmp(val, "YES")==0) {
|
|
|
|
return 1;
|
|
} else if ((val[1]=='\0' && (val[0]=='n' || val[0]=='N'))
|
|
|| strcasecmp(val, "NO")==0) {
|
|
return 0;
|
|
} else {
|
|
int i;
|
|
if (sscanf(val, "%i", &i)==1) {
|
|
return (i!=0);
|
|
} else {
|
|
wwarning(_("can't convert \"%s\" to boolean"), val);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static unsigned
|
|
getInt(WMPropList *value)
|
|
{
|
|
char *val;
|
|
unsigned n;
|
|
|
|
if (!WMIsPLString(value))
|
|
return 0;
|
|
val = WMGetFromPLString(value);
|
|
if (!val)
|
|
return 0;
|
|
if (sscanf(val, "%u", &n) != 1)
|
|
return 0;
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
|
|
static WMPropList*
|
|
makeWindowState(WWindow *wwin, WApplication *wapp)
|
|
{
|
|
WScreen *scr = wwin->screen_ptr;
|
|
Window win;
|
|
int argc;
|
|
char **argv;
|
|
int i;
|
|
unsigned mask;
|
|
char *class, *instance, *command=NULL, buffer[512];
|
|
WMPropList *win_state, *cmd, *name, *workspace;
|
|
WMPropList *shaded, *miniaturized, *hidden, *geometry;
|
|
WMPropList *dock, *shortcut;
|
|
|
|
if (wwin->orig_main_window!=None && wwin->orig_main_window!=wwin->client_win)
|
|
win = wwin->orig_main_window;
|
|
else
|
|
win = wwin->client_win;
|
|
|
|
if (XGetCommand(dpy, win, &argv, &argc) && argc>0) {
|
|
command = wtokenjoin(argv, argc);
|
|
XFreeStringList(argv);
|
|
}
|
|
if (!command)
|
|
return NULL;
|
|
|
|
if (PropGetWMClass(win, &class, &instance)) {
|
|
if (class && instance)
|
|
snprintf(buffer, sizeof(buffer), "%s.%s", instance, class);
|
|
else if (instance)
|
|
snprintf(buffer, sizeof(buffer), "%s", instance);
|
|
else if (class)
|
|
snprintf(buffer, sizeof(buffer), ".%s", class);
|
|
else
|
|
snprintf(buffer, sizeof(buffer), ".");
|
|
|
|
name = WMCreatePLString(buffer);
|
|
cmd = WMCreatePLString(command);
|
|
/*sprintf(buffer, "%d", wwin->frame->workspace+1);
|
|
workspace = WMCreatePLString(buffer);*/
|
|
workspace = WMCreatePLString(scr->workspaces[wwin->frame->workspace]->name);
|
|
shaded = wwin->flags.shaded ? sYes : sNo;
|
|
miniaturized = wwin->flags.miniaturized ? sYes : sNo;
|
|
hidden = wwin->flags.hidden ? sYes : sNo;
|
|
snprintf(buffer, sizeof(buffer), "%ix%i+%i+%i",
|
|
wwin->client.width, wwin->client.height,
|
|
wwin->frame_x, wwin->frame_y);
|
|
geometry = WMCreatePLString(buffer);
|
|
|
|
for (mask = 0, i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
|
|
if (scr->shortcutWindows[i] != NULL &&
|
|
WMGetFirstInArray(scr->shortcutWindows[i], wwin) != WANotFound) {
|
|
mask |= 1<<i;
|
|
}
|
|
}
|
|
|
|
snprintf(buffer, sizeof(buffer), "%u", mask);
|
|
shortcut = WMCreatePLString(buffer);
|
|
|
|
win_state = WMCreatePLDictionary(sName, name,
|
|
sCommand, cmd,
|
|
sWorkspace, workspace,
|
|
sShaded, shaded,
|
|
sMiniaturized, miniaturized,
|
|
sHidden, hidden,
|
|
sShortcutMask, shortcut,
|
|
sGeometry, geometry,
|
|
NULL);
|
|
|
|
WMReleasePropList(name);
|
|
WMReleasePropList(cmd);
|
|
WMReleasePropList(workspace);
|
|
WMReleasePropList(geometry);
|
|
WMReleasePropList(shortcut);
|
|
if (wapp && wapp->app_icon && wapp->app_icon->dock) {
|
|
int i;
|
|
char *name;
|
|
if (wapp->app_icon->dock == scr->dock) {
|
|
name="Dock";
|
|
} else {
|
|
for(i=0; i<scr->workspace_count; i++)
|
|
if(scr->workspaces[i]->clip == wapp->app_icon->dock)
|
|
break;
|
|
assert( i < scr->workspace_count);
|
|
/*n = i+1;*/
|
|
name = scr->workspaces[i]->name;
|
|
}
|
|
dock = WMCreatePLString(name);
|
|
WMPutInPLDictionary(win_state, sDock, dock);
|
|
WMReleasePropList(dock);
|
|
}
|
|
} else {
|
|
win_state = NULL;
|
|
}
|
|
|
|
if (instance) XFree(instance);
|
|
if (class) XFree(class);
|
|
if (command) wfree(command);
|
|
|
|
return win_state;
|
|
}
|
|
|
|
|
|
void
|
|
wSessionSaveState(WScreen *scr)
|
|
{
|
|
WWindow *wwin = scr->focused_window;
|
|
WMPropList *win_info, *wks;
|
|
WMPropList *list=NULL;
|
|
WMArray *wapp_list=NULL;
|
|
|
|
|
|
make_keys();
|
|
|
|
if (!scr->session_state) {
|
|
scr->session_state = WMCreatePLDictionary(NULL, NULL, NULL);
|
|
if (!scr->session_state)
|
|
return;
|
|
}
|
|
|
|
list = WMCreatePLArray(NULL);
|
|
|
|
wapp_list = WMCreateArray(16);
|
|
|
|
while (wwin) {
|
|
WApplication *wapp=wApplicationOf(wwin->main_window);
|
|
Window appId = wwin->orig_main_window;
|
|
|
|
if ((wwin->transient_for==None
|
|
|| wwin->transient_for==wwin->screen_ptr->root_win)
|
|
&& WMGetFirstInArray(wapp_list, (void*)appId)==WANotFound
|
|
&& !WFLAGP(wwin, dont_save_session)) {
|
|
/* A entry for this application was not yet saved. Save one. */
|
|
if ((win_info = makeWindowState(wwin, wapp))!=NULL) {
|
|
WMAddToPLArray(list, win_info);
|
|
WMReleasePropList(win_info);
|
|
/* If we were succesful in saving the info for this window
|
|
* add the application the window belongs to, to the
|
|
* application list, so no multiple entries for the same
|
|
* application are saved.
|
|
*/
|
|
WMAddToArray(wapp_list, (void*)appId);
|
|
}
|
|
}
|
|
wwin = wwin->prev;
|
|
}
|
|
WMRemoveFromPLDictionary(scr->session_state, sApplications);
|
|
WMPutInPLDictionary(scr->session_state, sApplications, list);
|
|
WMReleasePropList(list);
|
|
|
|
wks = WMCreatePLString(scr->workspaces[scr->current_workspace]->name);
|
|
WMPutInPLDictionary(scr->session_state, sWorkspace, wks);
|
|
WMReleasePropList(wks);
|
|
|
|
WMFreeArray(wapp_list);
|
|
}
|
|
|
|
|
|
void
|
|
wSessionClearState(WScreen *scr)
|
|
{
|
|
make_keys();
|
|
|
|
if (!scr->session_state)
|
|
return;
|
|
|
|
WMRemoveFromPLDictionary(scr->session_state, sApplications);
|
|
WMRemoveFromPLDictionary(scr->session_state, sWorkspace);
|
|
}
|
|
|
|
|
|
static pid_t
|
|
execCommand(WScreen *scr, char *command, char *host)
|
|
{
|
|
pid_t pid;
|
|
char **argv;
|
|
int argc;
|
|
|
|
wtokensplit(command, &argv, &argc);
|
|
|
|
if (argv==NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if ((pid=fork())==0) {
|
|
char **args;
|
|
int i;
|
|
|
|
SetupEnvironment(scr);
|
|
|
|
args = malloc(sizeof(char*)*(argc+1));
|
|
if (!args)
|
|
exit(111);
|
|
for (i=0; i<argc; i++) {
|
|
args[i] = argv[i];
|
|
}
|
|
args[argc] = NULL;
|
|
execvp(argv[0], args);
|
|
exit(111);
|
|
}
|
|
while (argc > 0)
|
|
wfree(argv[--argc]);
|
|
wfree(argv);
|
|
return pid;
|
|
}
|
|
|
|
|
|
static WSavedState*
|
|
getWindowState(WScreen *scr, WMPropList *win_state)
|
|
{
|
|
WSavedState *state = wmalloc(sizeof(WSavedState));
|
|
WMPropList *value;
|
|
char *tmp;
|
|
unsigned mask;
|
|
int i;
|
|
|
|
memset(state, 0, sizeof(WSavedState));
|
|
state->workspace = -1;
|
|
value = WMGetFromPLDictionary(win_state, sWorkspace);
|
|
if (value && WMIsPLString(value)) {
|
|
tmp = WMGetFromPLString(value);
|
|
if (sscanf(tmp, "%i", &state->workspace)!=1) {
|
|
state->workspace = -1;
|
|
for (i=0; i < scr->workspace_count; i++) {
|
|
if (strcmp(scr->workspaces[i]->name, tmp)==0) {
|
|
state->workspace = i;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
state->workspace--;
|
|
}
|
|
}
|
|
if ((value = WMGetFromPLDictionary(win_state, sShaded))!=NULL)
|
|
state->shaded = getBool(value);
|
|
if ((value = WMGetFromPLDictionary(win_state, sMiniaturized))!=NULL)
|
|
state->miniaturized = getBool(value);
|
|
if ((value = WMGetFromPLDictionary(win_state, sHidden))!=NULL)
|
|
state->hidden = getBool(value);
|
|
if ((value = WMGetFromPLDictionary(win_state, sShortcutMask))!=NULL) {
|
|
mask = getInt(value);
|
|
state->window_shortcuts = mask;
|
|
}
|
|
|
|
value = WMGetFromPLDictionary(win_state, sGeometry);
|
|
if (value && WMIsPLString(value)) {
|
|
if (!(sscanf(WMGetFromPLString(value), "%ix%i+%i+%i",
|
|
&state->w, &state->h, &state->x, &state->y)==4 &&
|
|
(state->w>0 && state->h>0))) {
|
|
state->w = 0;
|
|
state->h = 0;
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
#define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
|
|
|
|
void
|
|
wSessionRestoreState(WScreen *scr)
|
|
{
|
|
WSavedState *state;
|
|
char *instance, *class, *command, *host;
|
|
WMPropList *win_info, *apps, *cmd, *value;
|
|
pid_t pid;
|
|
int i, count;
|
|
WDock *dock;
|
|
WAppIcon *btn=NULL;
|
|
int j, n, found;
|
|
char *tmp;
|
|
|
|
make_keys();
|
|
|
|
if (!scr->session_state)
|
|
return;
|
|
|
|
WMPLSetCaseSensitive(True);
|
|
|
|
apps = WMGetFromPLDictionary(scr->session_state, sApplications);
|
|
if (!apps)
|
|
return;
|
|
|
|
count = WMGetPropListItemCount(apps);
|
|
if (count==0)
|
|
return;
|
|
|
|
for (i=0; i<count; i++) {
|
|
win_info = WMGetFromPLArray(apps, i);
|
|
|
|
cmd = WMGetFromPLDictionary(win_info, sCommand);
|
|
if (!cmd || !WMIsPLString(cmd) || !(command = WMGetFromPLString(cmd))) {
|
|
continue;
|
|
}
|
|
|
|
value = WMGetFromPLDictionary(win_info, sName);
|
|
if (!value)
|
|
continue;
|
|
|
|
ParseWindowName(value, &instance, &class, "session");
|
|
if (!instance && !class)
|
|
continue;
|
|
|
|
value = WMGetFromPLDictionary(win_info, sHost);
|
|
if (value && WMIsPLString(value))
|
|
host = WMGetFromPLString(value);
|
|
else
|
|
host = NULL;
|
|
|
|
state = getWindowState(scr, win_info);
|
|
|
|
dock = NULL;
|
|
value = WMGetFromPLDictionary(win_info, sDock);
|
|
if (value && WMIsPLString(value) && (tmp = WMGetFromPLString(value))!=NULL) {
|
|
if (sscanf(tmp, "%i", &n)!=1) {
|
|
if (!strcasecmp(tmp, "DOCK")) {
|
|
dock = scr->dock;
|
|
} else {
|
|
for (j=0; j < scr->workspace_count; j++) {
|
|
if (strcmp(scr->workspaces[j]->name, tmp)==0) {
|
|
dock = scr->workspaces[j]->clip;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (n == 0) {
|
|
dock = scr->dock;
|
|
} else if (n>0 && n<=scr->workspace_count) {
|
|
dock = scr->workspaces[n-1]->clip;
|
|
}
|
|
}
|
|
}
|
|
|
|
found = 0;
|
|
if (dock!=NULL) {
|
|
for (j=0; j<dock->max_icons; j++) {
|
|
btn = dock->icon_array[j];
|
|
if (btn && SAME(instance, btn->wm_instance) &&
|
|
SAME(class, btn->wm_class) &&
|
|
SAME(command, btn->command) &&
|
|
!btn->launching) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
wDockLaunchWithState(dock, btn, state);
|
|
} else if ((pid = execCommand(scr, command, host)) > 0) {
|
|
wWindowAddSavedState(instance, class, command, pid, state);
|
|
} else {
|
|
wfree(state);
|
|
}
|
|
|
|
if (instance) wfree(instance);
|
|
if (class) wfree(class);
|
|
}
|
|
/* clean up */
|
|
WMPLSetCaseSensitive(False);
|
|
}
|
|
|
|
|
|
void
|
|
wSessionRestoreLastWorkspace(WScreen *scr)
|
|
{
|
|
WMPropList *wks;
|
|
int w, i;
|
|
char *tmp;
|
|
|
|
make_keys();
|
|
|
|
if (!scr->session_state)
|
|
return;
|
|
|
|
WMPLSetCaseSensitive(True);
|
|
|
|
wks = WMGetFromPLDictionary(scr->session_state, sWorkspace);
|
|
if (!wks || !WMIsPLString(wks))
|
|
return;
|
|
|
|
tmp = WMGetFromPLString(wks);
|
|
|
|
/* clean up */
|
|
WMPLSetCaseSensitive(False);
|
|
|
|
if (sscanf(tmp, "%i", &w)!=1) {
|
|
w = -1;
|
|
for (i=0; i < scr->workspace_count; i++) {
|
|
if (strcmp(scr->workspaces[i]->name, tmp)==0) {
|
|
w = i;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
w--;
|
|
}
|
|
|
|
if (w!=scr->current_workspace && w<scr->workspace_count) {
|
|
wWorkspaceChange(scr, w);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
clearWaitingAckState(WScreen *scr)
|
|
{
|
|
WWindow *wwin;
|
|
WApplication *wapp;
|
|
|
|
for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
|
|
wwin->flags.waiting_save_ack = 0;
|
|
if (wwin->main_window != None) {
|
|
wapp = wApplicationOf(wwin->main_window);
|
|
if (wapp)
|
|
wapp->main_window_desc->flags.waiting_save_ack = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
wSessionSaveClients(WScreen *scr)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* With XSMP, this job is done by smproxy
|
|
*/
|
|
void
|
|
wSessionSendSaveYourself(WScreen *scr)
|
|
{
|
|
WWindow *wwin;
|
|
int count;
|
|
|
|
/* freeze client interaction with clients */
|
|
XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync,
|
|
CurrentTime);
|
|
XGrabPointer(dpy, scr->root_win, False, ButtonPressMask|ButtonReleaseMask,
|
|
GrabModeAsync, GrabModeAsync, scr->root_win, None,
|
|
CurrentTime);
|
|
|
|
clearWaitingAckState(scr);
|
|
|
|
count = 0;
|
|
|
|
/* first send SAVE_YOURSELF for everybody */
|
|
for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
|
|
WWindow *mainWin;
|
|
|
|
mainWin = wWindowFor(wwin->main_window);
|
|
|
|
if (mainWin) {
|
|
/* if the client is a multi-window client, only send message
|
|
* to the main window */
|
|
wwin = mainWin;
|
|
}
|
|
|
|
/* make sure the SAVE_YOURSELF flag is up-to-date */
|
|
PropGetProtocols(wwin->client_win, &wwin->protocols);
|
|
|
|
if (wwin->protocols.SAVE_YOURSELF) {
|
|
if (!wwin->flags.waiting_save_ack) {
|
|
wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
|
|
|
|
wwin->flags.waiting_save_ack = 1;
|
|
count++;
|
|
}
|
|
} else {
|
|
wwin->flags.waiting_save_ack = 0;
|
|
}
|
|
}
|
|
|
|
/* then wait for acknowledge */
|
|
while (count > 0) {
|
|
|
|
}
|
|
|
|
XUngrabPointer(dpy, CurrentTime);
|
|
XUngrabKeyboard(dpy, CurrentTime);
|
|
XFlush(dpy);
|
|
}
|
|
|
|
|
|
#ifdef XSMP_ENABLED
|
|
/*
|
|
* With full session management support, the part of WMState
|
|
* that store client window state will become obsolete (maybe we can reuse
|
|
* the old code too),
|
|
* but we still need to store state info like the dock and workspaces.
|
|
* It is better to keep dock/wspace info in WMState because the user
|
|
* might want to keep the dock configuration while not wanting to
|
|
* resume a previously saved session.
|
|
* So, wmaker specific state info can be saved in
|
|
* ~/GNUstep/.AppInfo/WindowMaker/statename.state
|
|
* Its better to not put it in the defaults directory because:
|
|
* - its not a defaults file (having domain names like wmaker0089504baa
|
|
* in the defaults directory wouldn't be very neat)
|
|
* - this state file is not meant to be edited by users
|
|
*
|
|
* The old session code will become obsolete. When wmaker is
|
|
* compiled with R6 sm support compiled in, it'll be better to
|
|
* use a totally rewritten state saving code, but we can keep
|
|
* the current code for when XSMP_ENABLED is not compiled in.
|
|
*
|
|
* This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
|
|
* messages), but it'll be better.
|
|
*
|
|
* -readme
|
|
*/
|
|
|
|
|
|
static char*
|
|
getWindowRole(Window window)
|
|
{
|
|
XTextProperty prop;
|
|
static Atom atom = 0;
|
|
|
|
if (!atom)
|
|
atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
|
|
|
|
if (XGetTextProperty(dpy, window, &prop, atom)) {
|
|
if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
|
|
return prop.value;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* Saved Info:
|
|
*
|
|
* WM_WINDOW_ROLE
|
|
*
|
|
* WM_CLASS.instance
|
|
* WM_CLASS.class
|
|
* WM_NAME
|
|
* WM_COMMAND
|
|
*
|
|
* geometry
|
|
* state = (miniaturized, shaded, etc)
|
|
* attribute
|
|
* workspace #
|
|
* app state = (which dock, hidden)
|
|
* window shortcut #
|
|
*/
|
|
|
|
static WMPropList*
|
|
makeAppState(WWindow *wwin)
|
|
{
|
|
WApplication *wapp;
|
|
WMPropList *state;
|
|
WScreen *scr = wwin->screen_ptr;
|
|
|
|
state = WMCreatePLArray(NULL, NULL);
|
|
|
|
wapp = wApplicationOf(wwin->main_window);
|
|
|
|
if (wapp) {
|
|
if (wapp->app_icon && wapp->app_icon->dock) {
|
|
|
|
if (wapp->app_icon->dock == scr->dock) {
|
|
WMAddToPLArray(state, WMCreatePLString("Dock"));
|
|
} else {
|
|
int i;
|
|
|
|
for(i=0; i<scr->workspace_count; i++)
|
|
if(scr->workspaces[i]->clip == wapp->app_icon->dock)
|
|
break;
|
|
|
|
assert(i < scr->workspace_count);
|
|
|
|
WMAddToPLArray(state,
|
|
WMCreatePLString(scr->workspaces[i]->name));
|
|
}
|
|
}
|
|
|
|
WMAddToPLArray(state, WMCreatePLString(wapp->hidden ? "1" : "0"));
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
|
|
Bool
|
|
wSessionGetStateFor(WWindow *wwin, WSessionData *state)
|
|
{
|
|
char *str;
|
|
WMPropList *slist;
|
|
WMPropList *elem;
|
|
WMPropList *value;
|
|
int index = 0;
|
|
|
|
index = 3;
|
|
|
|
/* geometry */
|
|
value = WMGetFromPLArray(slist, index++);
|
|
str = WMGetFromPLString(value);
|
|
|
|
sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
|
|
&state->width, &state->height,
|
|
&state->user_changed_width, &state->user_changed_height);
|
|
|
|
|
|
/* state */
|
|
value = WMGetFromPLArray(slist, index++);
|
|
str = WMGetFromPLString(value);
|
|
|
|
sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded,
|
|
&state->maximized);
|
|
|
|
|
|
/* attributes */
|
|
value = WMGetFromPLArray(slist, index++);
|
|
str = WMGetFromPLString(value);
|
|
|
|
getAttributeState(str, &state->mflags, &state->flags);
|
|
|
|
|
|
/* workspace */
|
|
value = WMGetFromPLArray(slist, index++);
|
|
str = WMGetFromPLString(value);
|
|
|
|
sscanf(str, "%i", &state->workspace);
|
|
|
|
|
|
/* app state (repeated for all windows of the app) */
|
|
value = WMGetFromPLArray(slist, index++);
|
|
str = WMGetFromPLString(value);
|
|
|
|
/* ???? */
|
|
|
|
/* shortcuts */
|
|
value = WMGetFromPLArray(slist, index++);
|
|
str = WMGetFromPLString(value);
|
|
|
|
sscanf(str, "%i", &state->shortcuts);
|
|
}
|
|
|
|
|
|
|
|
static WMPropList*
|
|
makeAttributeState(WWindow *wwin)
|
|
{
|
|
unsigned int data1, data2;
|
|
char buffer[256];
|
|
|
|
#define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
|
|
? (wwin)->user_flags.FLAG : -1)
|
|
|
|
snprintf(buffer, sizeof(buffer),
|
|
"%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
|
|
W_FLAG(no_titlebar),
|
|
W_FLAG(no_resizable),
|
|
W_FLAG(no_closable),
|
|
W_FLAG(no_miniaturizable),
|
|
W_FLAG(no_resizebar),
|
|
W_FLAG(no_close_button),
|
|
W_FLAG(no_miniaturize_button),
|
|
/*
|
|
W_FLAG(broken_close),
|
|
W_FLAG(kill_close),
|
|
*/
|
|
W_FLAG(no_shadeable),
|
|
W_FLAG(omnipresent),
|
|
W_FLAG(skip_window_list),
|
|
W_FLAG(floating),
|
|
W_FLAG(sunken),
|
|
W_FLAG(no_bind_keys),
|
|
W_FLAG(no_bind_mouse),
|
|
W_FLAG(no_hide_others),
|
|
W_FLAG(no_appicon),
|
|
W_FLAG(dont_move_off),
|
|
W_FLAG(no_focusable),
|
|
W_FLAG(always_user_icon),
|
|
W_FLAG(start_miniaturized),
|
|
W_FLAG(start_hidden),
|
|
W_FLAG(start_maximized),
|
|
W_FLAG(dont_save_session),
|
|
W_FLAG(emulate_appicon));
|
|
|
|
return WMCreatePLString(buffer);
|
|
}
|
|
|
|
|
|
static void
|
|
appendStringInArray(WMPropList *array, char *str)
|
|
{
|
|
WMPropList *val;
|
|
|
|
val = WMCreatePLString(str);
|
|
WMAddToPLArray(array, val);
|
|
WMReleasePropList(val);
|
|
}
|
|
|
|
|
|
static WMPropList*
|
|
makeClientState(WWindow *wwin)
|
|
{
|
|
WMPropList *state;
|
|
WMPropList *tmp;
|
|
char *str;
|
|
char buffer[512];
|
|
int i;
|
|
unsigned shortcuts;
|
|
|
|
state = WMCreatePLArray(NULL, NULL);
|
|
|
|
/* WM_WINDOW_ROLE */
|
|
str = getWindowRole(wwin->client_win);
|
|
if (!str)
|
|
appendStringInArray(state, "");
|
|
else {
|
|
appendStringInArray(state, str);
|
|
XFree(str);
|
|
}
|
|
|
|
/* WM_CLASS.instance */
|
|
appendStringInArray(state, wwin->wm_instance);
|
|
|
|
/* WM_CLASS.class */
|
|
appendStringInArray(state, wwin->wm_class);
|
|
|
|
/* WM_NAME */
|
|
if (wwin->flags.wm_name_changed)
|
|
appendStringInArray(state, "");
|
|
else
|
|
appendStringInArray(state, wwin->frame->name);
|
|
|
|
/* geometry */
|
|
snprintf(buffer, sizeof(buffer), "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
|
|
wwin->client.width, wwin->client.height,
|
|
wwin->flags.user_changed_width, wwin->flags.user_changed_height);
|
|
appendStringInArray(state, buffer);
|
|
|
|
/* state */
|
|
snprintf(buffer, sizeof(buffer), "%i %i %i", wwin->flags.miniaturized,
|
|
wwin->flags.shaded, wwin->flags.maximized);
|
|
appendStringInArray(state, buffer);
|
|
|
|
/* attributes */
|
|
tmp = makeAttributeState(wwin);
|
|
WMAddToPLArray(state, tmp);
|
|
WMReleasePropList(tmp);
|
|
|
|
/* workspace */
|
|
snprintf(buffer, sizeof(buffer), "%i", wwin->frame->workspace);
|
|
appendStringInArray(state, buffer);
|
|
|
|
/* app state (repeated for all windows of the app) */
|
|
tmp = makeAppState(wwin);
|
|
WMAddToPLArray(state, tmp);
|
|
WMReleasePropList(tmp);
|
|
|
|
/* shortcuts */
|
|
shortcuts = 0;
|
|
for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
|
|
if (scr->shortcutWindow[i] == wwin) {
|
|
shortcuts |= 1 << i;
|
|
}
|
|
}
|
|
snprintf(buffer, sizeof(buffer), "%ui", shortcuts);
|
|
appendStringInArray(tmp, buffer);
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
static void
|
|
smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
|
|
{
|
|
SmProp props[4];
|
|
SmPropValue prop1val, prop2val, prop3val, prop4val;
|
|
char **argv = (char**)client_data;
|
|
int argc;
|
|
int i, j;
|
|
Bool ok = False;
|
|
char *statefile = NULL;
|
|
char *prefix;
|
|
Bool gsPrefix = False;
|
|
char *discardCmd = NULL;
|
|
time_t t;
|
|
WMPropList *state, *plState;
|
|
int len;
|
|
|
|
#ifdef DEBUG1
|
|
puts("received SaveYourselfPhase2 SM message");
|
|
#endif
|
|
|
|
/* save session state */
|
|
|
|
/* the file that will contain the state */
|
|
prefix = getenv("SM_SAVE_DIR");
|
|
if (!prefix) {
|
|
prefix = wusergnusteppath();
|
|
if (prefix)
|
|
gsPrefix = True;
|
|
}
|
|
if (!prefix) {
|
|
prefix = getenv("HOME");
|
|
}
|
|
if (!prefix)
|
|
prefix = ".";
|
|
|
|
len = strlen(prefix)+64;
|
|
statefile = malloc(len);
|
|
if (!statefile) {
|
|
wwarning(_("out of memory while saving session state"));
|
|
goto fail;
|
|
}
|
|
|
|
t = time();
|
|
i = 0;
|
|
do {
|
|
if (gsPrefix)
|
|
snprintf(statefile, len, "%s/.AppInfo/WindowMaker/wmaker.%l%i.state",
|
|
prefix, t, i);
|
|
else
|
|
snprintf(statefile, len, "%s/wmaker.%l%i.state", prefix, t, i);
|
|
i++;
|
|
} while (access(F_OK, statefile)!=-1);
|
|
|
|
/* save the states of all windows we're managing */
|
|
state = WMCreatePLArray(NULL, NULL);
|
|
|
|
/*
|
|
* Format:
|
|
*
|
|
* state_file ::= dictionary with version_info ; state
|
|
* version_info ::= 'version' = '1';
|
|
* state ::= 'state' = array of screen_info
|
|
* screen_info ::= array of (screen number, window_info, window_info, ...)
|
|
* window_info ::=
|
|
*/
|
|
for (i=0; i<wScreenCount; i++) {
|
|
WScreen *scr;
|
|
WWindow *wwin;
|
|
char buf[32];
|
|
WMPropList *pscreen;
|
|
|
|
scr = wScreenWithNumber(i);
|
|
|
|
snprintf(buf, sizeof(buf), "%i", scr->screen);
|
|
pscreen = WMCreatePLArray(WMCreatePLString(buf), NULL);
|
|
|
|
wwin = scr->focused_window;
|
|
while (wwin) {
|
|
WMPropList *pwindow;
|
|
|
|
pwindow = makeClientState(wwin);
|
|
WMAddToPLArray(pscreen, pwindow);
|
|
|
|
wwin = wwin->prev;
|
|
}
|
|
|
|
WMAddToPLArray(state, pscreen);
|
|
}
|
|
|
|
plState = WMCreatePLDictionary(WMCreatePLString("Version"),
|
|
WMCreatePLString("1.0"),
|
|
WMCreatePLString("Screens"),
|
|
state, NULL);
|
|
|
|
WMWritePropListToFile(plState, statefile, False);
|
|
|
|
WMReleasePropList(plState);
|
|
|
|
/* set the remaining properties that we didn't set at
|
|
* startup time */
|
|
|
|
for (argc=0, i=0; argv[i]!=NULL; i++) {
|
|
if (strcmp(argv[i], "-clientid")==0
|
|
|| strcmp(argv[i], "-restore")==0) {
|
|
i++;
|
|
} else {
|
|
argc++;
|
|
}
|
|
}
|
|
|
|
prop[0].name = SmRestartCommand;
|
|
prop[0].type = SmLISTofARRAY8;
|
|
prop[0].vals = malloc(sizeof(SmPropValue)*(argc+4));
|
|
prop[0].num_vals = argc+4;
|
|
|
|
prop[1].name = SmCloneCommand;
|
|
prop[1].type = SmLISTofARRAY8;
|
|
prop[1].vals = malloc(sizeof(SmPropValue)*(argc));
|
|
prop[1].num_vals = argc;
|
|
|
|
if (!prop[0].vals || !prop[1].vals) {
|
|
wwarning(_("end of memory while saving session state"));
|
|
goto fail;
|
|
}
|
|
|
|
for (j=0, i=0; i<argc+4; i++) {
|
|
if (strcmp(argv[i], "-clientid")==0
|
|
|| strcmp(argv[i], "-restore")==0) {
|
|
i++;
|
|
} else {
|
|
prop[0].vals[j].value = argv[i];
|
|
prop[0].vals[j].length = strlen(argv[i]);
|
|
prop[1].vals[j].value = argv[i];
|
|
prop[1].vals[j].length = strlen(argv[i]);
|
|
j++;
|
|
}
|
|
}
|
|
prop[0].vals[j].value = "-clientid";
|
|
prop[0].vals[j].length = 9;
|
|
j++;
|
|
prop[0].vals[j].value = sClientID;
|
|
prop[0].vals[j].length = strlen(sClientID);
|
|
j++;
|
|
prop[0].vals[j].value = "-restore";
|
|
prop[0].vals[j].length = 11;
|
|
j++;
|
|
prop[0].vals[j].value = statefile;
|
|
prop[0].vals[j].length = strlen(statefile);
|
|
|
|
{
|
|
int len = strlen(statefile)+8;
|
|
|
|
discardCmd = malloc(len);
|
|
if (!discardCmd)
|
|
goto fail;
|
|
snprintf(discardCmd, len, "rm %s", statefile);
|
|
}
|
|
prop[2].name = SmDiscardCommand;
|
|
prop[2].type = SmARRAY8;
|
|
prop[2].vals[0] = discardCmd;
|
|
prop[2].num_vals = 1;
|
|
|
|
SmcSetProperties(sSMCConn, 3, prop);
|
|
|
|
ok = True;
|
|
fail:
|
|
SmcSaveYourselfDone(smc_conn, ok);
|
|
|
|
if (prop[0].vals)
|
|
wfree(prop[0].vals);
|
|
if (prop[1].vals)
|
|
wfree(prop[1].vals);
|
|
if (discardCmd)
|
|
wfree(discardCmd);
|
|
|
|
if (!ok) {
|
|
remove(statefile);
|
|
}
|
|
if (statefile)
|
|
wfree(statefile);
|
|
}
|
|
|
|
|
|
static void
|
|
smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
|
|
Bool shutdown, int interact_style, Bool fast)
|
|
{
|
|
#ifdef DEBUG1
|
|
puts("received SaveYourself SM message");
|
|
#endif
|
|
|
|
if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc,
|
|
client_data)) {
|
|
|
|
SmcSaveYourselfDone(smc_conn, False);
|
|
sWaitingPhase2 = False;
|
|
} else {
|
|
#ifdef DEBUG1
|
|
puts("successfull request of SYS phase 2");
|
|
#endif
|
|
sWaitingPhase2 = True;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
smDieProc(SmcConn smc_conn, SmPointer client_data)
|
|
{
|
|
#ifdef DEBUG1
|
|
puts("received Die SM message");
|
|
#endif
|
|
|
|
wSessionDisconnectManager();
|
|
|
|
Shutdown(WSExitMode, True);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
smSaveCompleteProc(SmcConn smc_conn)
|
|
{
|
|
/* it means that we can resume doing things that can change our state */
|
|
#ifdef DEBUG1
|
|
puts("received SaveComplete SM message");
|
|
#endif
|
|
}
|
|
|
|
|
|
static void
|
|
smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
|
|
{
|
|
if (sWaitingPhase2) {
|
|
|
|
sWaitingPhase2 = False;
|
|
|
|
SmcSaveYourselfDone(smc_conn, False);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
iceMessageProc(int fd, int mask, void *clientData)
|
|
{
|
|
IceConn iceConn = (IceConn)clientData;
|
|
|
|
IceProcessMessages(iceConn, NULL, NULL);
|
|
}
|
|
|
|
|
|
static void
|
|
iceIOErrorHandler(IceConnection ice_conn)
|
|
{
|
|
/* This is not fatal but can mean the session manager exited.
|
|
* If the session manager exited normally we would get a
|
|
* Die message, so this probably means an abnormal exit.
|
|
* If the sm was the last client of session, then we'll die
|
|
* anyway, otherwise we can continue doing our stuff.
|
|
*/
|
|
wwarning(_("connection to the session manager was lost"));
|
|
wSessionDisconnectManager();
|
|
}
|
|
|
|
|
|
void
|
|
wSessionConnectManager(char **argv, int argc)
|
|
{
|
|
IceConn iceConn;
|
|
char *previous_id = NULL;
|
|
char buffer[256];
|
|
SmcCallbacks callbacks;
|
|
unsigned long mask;
|
|
char uid[32];
|
|
char pid[32];
|
|
SmProp props[4];
|
|
SmPropValue prop1val, prop2val, prop3val, prop4val;
|
|
char restartStyle;
|
|
int i;
|
|
|
|
mask = SmcSaveYourselfProcMask|SmcDieProcMask|SmcSaveCompleteProcMask
|
|
|SmcShutdownCancelledProcMask;
|
|
|
|
callbacks.save_yourself.callback = smSaveYourselfProc;
|
|
callbacks.save_yourself.client_data = argv;
|
|
|
|
callbacks.die.callback = smDieProc;
|
|
callbacks.die.client_data = NULL;
|
|
|
|
callbacks.save_complete.callback = smSaveCompleteProc;
|
|
callbacks.save_complete.client_data = NULL;
|
|
|
|
callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
|
|
callbacks.shutdown_cancelled.client_data = NULL;
|
|
|
|
for (i=0; i<argc; i++) {
|
|
if (strcmp(argv[i], "-clientid")==0) {
|
|
previous_id = argv[i+1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* connect to the session manager */
|
|
sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
|
|
mask, &callbacks, previous_id,
|
|
&sClientID, 255, buffer);
|
|
if (!sSMCConn) {
|
|
return;
|
|
}
|
|
#ifdef DEBUG1
|
|
puts("connected to the session manager");
|
|
#endif
|
|
|
|
/* IceSetIOErrorHandler(iceIOErrorHandler);*/
|
|
|
|
/* check for session manager clients */
|
|
iceConn = SmcGetIceConnection(smcConn);
|
|
|
|
if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
|
|
wsyserror("error setting close-on-exec flag for ICE connection");
|
|
}
|
|
|
|
sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn),
|
|
WIReadMask, iceMessageProc, iceConn);
|
|
|
|
/* setup information about ourselves */
|
|
|
|
/* program name */
|
|
prop1val.value = argv[0];
|
|
prop1val.length = strlen(argv[0]);
|
|
prop[0].name = SmProgram;
|
|
prop[0].type = SmARRAY8;
|
|
prop[0].num_vals = 1;
|
|
prop[0].vals = &prop1val;
|
|
|
|
/* The XSMP doc from X11R6.1 says it contains the user name,
|
|
* but every client implementation I saw places the uid # */
|
|
snprintf(uid, sizeof(uid), "%i", getuid());
|
|
prop2val.value = uid;
|
|
prop2val.length = strlen(uid);
|
|
prop[1].name = SmUserID;
|
|
prop[1].type = SmARRAY8;
|
|
prop[1].num_vals = 1;
|
|
prop[1].vals = &prop2val;
|
|
|
|
/* Restart style. We should restart only if we were running when
|
|
* the previous session finished. */
|
|
restartStyle = SmRestartIfRunning;
|
|
prop3val.value = &restartStyle;
|
|
prop3val.length = 1;
|
|
prop[2].name = SmRestartStyleHint;
|
|
prop[2].type = SmCARD8;
|
|
prop[2].num_vals = 1;
|
|
prop[2].vals = &prop3val;
|
|
|
|
/* Our PID. Not required but might be usefull */
|
|
snprintf(pid, sizeof(pid), "%i", getpid());
|
|
prop4val.value = pid;
|
|
prop4val.length = strlen(pid);
|
|
prop[3].name = SmProcessID;
|
|
prop[3].type = SmARRAY8;
|
|
prop[3].num_vals = 1;
|
|
prop[3].vals = &prop4val;
|
|
|
|
/* we'll set the rest of the hints later */
|
|
|
|
SmcSetProperties(sSMCConn, 4, props);
|
|
|
|
}
|
|
|
|
|
|
void
|
|
wSessionDisconnectManager(void)
|
|
{
|
|
if (sSMCConn) {
|
|
WMDeleteInputHandler(sSMInputHandler);
|
|
sSMInputHandler = NULL;
|
|
|
|
SmcCloseConnection(sSMCConn, 0, NULL);
|
|
sSMCConn = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
wSessionRequestShutdown(void)
|
|
{
|
|
/* request a shutdown to the session manager */
|
|
if (sSMCConn)
|
|
SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny,
|
|
False, True);
|
|
}
|
|
|
|
|
|
Bool
|
|
wSessionIsManaged(void)
|
|
{
|
|
return sSMCConn!=NULL;
|
|
}
|
|
|
|
#endif /* !XSMP_ENABLED */
|