occupation.c revision b18c2d1e
10bbfda8aSnia/*
20bbfda8aSnia * Occupation handling bits
30bbfda8aSnia *
40bbfda8aSnia * This is in fact pretty tightly tied and extremely similar to the
50bbfda8aSnia * handling of the WorkSpaceManager in workmgr.c, and used to be there.
60bbfda8aSnia * It makes sense to consider them together (and indeed, many of the
70bbfda8aSnia * configs that affect how this works are really WorkSpaceManager* or
80bbfda8aSnia * WMgr* commands.  But having them crammed together in one file is
90bbfda8aSnia * unwieldy.
100bbfda8aSnia */
110bbfda8aSnia
120bbfda8aSnia#include "ctwm.h"
130bbfda8aSnia
140bbfda8aSnia#include <stdio.h>
150bbfda8aSnia#include <string.h>
160bbfda8aSnia#include <stdlib.h>
170bbfda8aSnia
180bbfda8aSnia#include <X11/Xatom.h>
190bbfda8aSnia
200bbfda8aSnia#include "add_window.h"
210bbfda8aSnia#include "ctwm_atoms.h"
220bbfda8aSnia#include "drawing.h"
230bbfda8aSnia#include "events.h"
240bbfda8aSnia#include "iconmgr.h"
250bbfda8aSnia#include "list.h"
260bbfda8aSnia#include "screen.h"
270bbfda8aSnia#include "occupation.h"
280bbfda8aSnia#include "otp.h"
290bbfda8aSnia#include "util.h"
300bbfda8aSnia#include "vscreen.h"
310bbfda8aSnia#include "win_iconify.h"
320bbfda8aSnia#include "win_regions.h"
330bbfda8aSnia#include "win_utils.h"
340bbfda8aSnia#include "workspace_manager.h"
350bbfda8aSnia#include "workspace_utils.h"
360bbfda8aSnia
370bbfda8aSnia
380bbfda8aSniastatic int GetMaskFromResource(TwmWindow *win, char *res);
390bbfda8aSniastatic char *mk_nullsep_string(const char *prop, int len);
400bbfda8aSnia
410bbfda8aSniastatic bool CanChangeOccupation(TwmWindow **twm_winp);
420bbfda8aSnia
430bbfda8aSniaint fullOccupation = 0;
440bbfda8aSnia
450bbfda8aSnia/*
460bbfda8aSnia * The window whose occupation is currently being manipulated.
470bbfda8aSnia *
480bbfda8aSnia * XXX Should probably be static, but currently needed in
490bbfda8aSnia * WMapRemoveWindow().  Revisit.
500bbfda8aSnia */
510bbfda8aSniaTwmWindow *occupyWin = NULL;
520bbfda8aSnia
530bbfda8aSnia
540bbfda8aSnia/* XXX Share with captive.c? */
550bbfda8aSniastatic XrmOptionDescRec table [] = {
560bbfda8aSnia	{"-xrm",            NULL,           XrmoptionResArg, (XPointer) NULL},
570bbfda8aSnia};
580bbfda8aSnia
590bbfda8aSnia
600bbfda8aSnia
610bbfda8aSnia
620bbfda8aSnia/*
630bbfda8aSnia ****************************************************************
640bbfda8aSnia *
650bbfda8aSnia * First, funcs related to setting and changing a window's occupation.
660bbfda8aSnia *
670bbfda8aSnia ****************************************************************
680bbfda8aSnia */
690bbfda8aSnia
700bbfda8aSnia
710bbfda8aSnia/*
720bbfda8aSnia * Setup the occupation of a TwmWindow.  Called as part of the
730bbfda8aSnia * AddWindow() process.
740bbfda8aSnia *
750bbfda8aSnia * XXX The logic flow in this is kinda weird, and it's not at all clear
760bbfda8aSnia * to what extent it's really doing the right on on what should override
770bbfda8aSnia * what, or which things should expand/contract on others...
780bbfda8aSnia */
790bbfda8aSniavoid
800bbfda8aSniaSetupOccupation(TwmWindow *twm_win, int occupation_hint)
810bbfda8aSnia{
820bbfda8aSnia	char      **cliargv = NULL;
830bbfda8aSnia	int       cliargc;
840bbfda8aSnia	WorkSpace *ws;
850bbfda8aSnia
860bbfda8aSnia	/* If there aren't any config'd workspaces, there's only 0 */
870bbfda8aSnia	if(! Scr->workSpaceManagerActive) {
880bbfda8aSnia		twm_win->occupation = 1 << 0;   /* occupy workspace #0 */
890bbfda8aSnia		/* more?... */
900bbfda8aSnia
910bbfda8aSnia		return;
920bbfda8aSnia	}
930bbfda8aSnia
940bbfda8aSnia	/* Workspace manager window doesn't get futzed with */
950bbfda8aSnia	if(twm_win->iswspmgr) {
960bbfda8aSnia		return;
970bbfda8aSnia	}
980bbfda8aSnia
990bbfda8aSnia	/*twm_win->occupation = twm_win->iswinbox ? fullOccupation : 0;*/
1000bbfda8aSnia	twm_win->occupation = 0;
1010bbfda8aSnia
1020bbfda8aSnia	/* Specified in any Occupy{} config params? */
1030bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
1040bbfda8aSnia		if(LookInList(ws->clientlist, twm_win->name, &twm_win->class)) {
1050bbfda8aSnia			twm_win->occupation |= 1 << ws->number;
1060bbfda8aSnia		}
1070bbfda8aSnia	}
1080bbfda8aSnia
1090bbfda8aSnia	/* OccupyAll{} */
1100bbfda8aSnia	if(LookInList(Scr->OccupyAll, twm_win->name, &twm_win->class)) {
1110bbfda8aSnia		twm_win->occupation = fullOccupation;
1120bbfda8aSnia	}
1130bbfda8aSnia
1140bbfda8aSnia	/* See if it specified in -xrm stuff */
1150bbfda8aSnia	if(XGetCommand(dpy, twm_win->w, &cliargv, &cliargc)) {
1160bbfda8aSnia		Bool status;
1170bbfda8aSnia		char *str_type;
1180bbfda8aSnia		XrmValue value;
1190bbfda8aSnia		XrmDatabase db = NULL;
1200bbfda8aSnia
1210bbfda8aSnia		XrmParseCommand(&db, table, 1, "ctwm", &cliargc, cliargv);
1220bbfda8aSnia		XFreeStringList(cliargv);
1230bbfda8aSnia		status = XrmGetResource(db, "ctwm.workspace", "Ctwm.Workspace",
1240bbfda8aSnia		                        &str_type, &value);
1250bbfda8aSnia		if((status == True) && (value.size != 0)) {
1260bbfda8aSnia			/* Copy the value.addr because it's in XRM memory not ours */
1270bbfda8aSnia			char wrkSpcList[512];
1280bbfda8aSnia			safe_strncpy(wrkSpcList, value.addr, MIN(value.size, 512));
1290bbfda8aSnia
1300bbfda8aSnia			twm_win->occupation = GetMaskFromResource(twm_win, wrkSpcList);
1310bbfda8aSnia		}
1320bbfda8aSnia		XrmDestroyDatabase(db);
1330bbfda8aSnia	}
1340bbfda8aSnia
1350bbfda8aSnia	/* Does it have a property telling us */
1360bbfda8aSnia	if(RestartPreviousState) {
1370bbfda8aSnia		Atom actual_type;
1380bbfda8aSnia		int actual_format;
1390bbfda8aSnia		unsigned long nitems, bytesafter;
1400bbfda8aSnia		unsigned char *prop;
1410bbfda8aSnia
1420bbfda8aSnia		if(XGetWindowProperty(dpy, twm_win->w, XA_WM_OCCUPATION, 0L, 2500, False,
1430bbfda8aSnia		                      XA_STRING, &actual_type, &actual_format, &nitems,
1440bbfda8aSnia		                      &bytesafter, &prop) == Success) {
1450bbfda8aSnia			if(nitems != 0) {
1460bbfda8aSnia				twm_win->occupation = GetMaskFromProperty(prop, nitems);
1470bbfda8aSnia				XFree(prop);
1480bbfda8aSnia			}
1490bbfda8aSnia		}
1500bbfda8aSnia	}
1510bbfda8aSnia
1520bbfda8aSnia#ifdef EWMH
1530bbfda8aSnia	/* Maybe EWMH has something to tell us? */
1540bbfda8aSnia	if(twm_win->occupation == 0) {
1550bbfda8aSnia		twm_win->occupation = EwmhGetOccupation(twm_win);
1560bbfda8aSnia	}
1570bbfda8aSnia#endif /* EWMH */
1580bbfda8aSnia
1590bbfda8aSnia	/* Icon Managers shouldn't get altered */
1600bbfda8aSnia	/* XXX Should this be up near the top? */
1610bbfda8aSnia	if(twm_win->isiconmgr) {
1620bbfda8aSnia		return;        /* someone tried to modify occupation of icon managers */
1630bbfda8aSnia	}
1640bbfda8aSnia
1650bbfda8aSnia
1660bbfda8aSnia	/*
1670bbfda8aSnia	 * Transient-ish things go with their parents unless
1680bbfda8aSnia	 * TransientHasOccupation set in the config.
1690bbfda8aSnia	 */
1700bbfda8aSnia	if(! Scr->TransientHasOccupation) {
1710bbfda8aSnia		TwmWindow *t;
1720bbfda8aSnia
1730bbfda8aSnia		if(twm_win->istransient) {
1740bbfda8aSnia			t = GetTwmWindow(twm_win->transientfor);
1750bbfda8aSnia			if(t != NULL) {
1760bbfda8aSnia				twm_win->occupation = t->occupation;
1770bbfda8aSnia			}
1780bbfda8aSnia		}
1790bbfda8aSnia		else if(twm_win->group != 0) {
1800bbfda8aSnia			t = GetTwmWindow(twm_win->group);
1810bbfda8aSnia			if(t != NULL) {
1820bbfda8aSnia				twm_win->occupation = t->occupation;
1830bbfda8aSnia			}
1840bbfda8aSnia		}
1850bbfda8aSnia	}
1860bbfda8aSnia
1870bbfda8aSnia
1880bbfda8aSnia	/* If we were told something specific, go with that */
1890bbfda8aSnia	if(occupation_hint != 0) {
1900bbfda8aSnia		twm_win->occupation = occupation_hint;
1910bbfda8aSnia	}
1920bbfda8aSnia
1930bbfda8aSnia	/* If it's apparently-nonsensical, put it in its vs's workspace */
1940bbfda8aSnia	if((twm_win->occupation & fullOccupation) == 0) {
1950bbfda8aSnia		twm_win->occupation = 1 << twm_win->vs->wsw->currentwspc->number;
1960bbfda8aSnia	}
1970bbfda8aSnia
1980bbfda8aSnia	/*
1990bbfda8aSnia	 * If the occupation would not show it in the current vscreen,
2000bbfda8aSnia	 * make it vanish.
2010bbfda8aSnia	 *
2020bbfda8aSnia	 * If it could be shown in one of the other vscreens, change the vscreen.
2030bbfda8aSnia	 */
2040bbfda8aSnia	if(!OCCUPY(twm_win, twm_win->vs->wsw->currentwspc)) {
2050bbfda8aSnia
2060bbfda8aSnia		twm_win->vs = NULL;
2070bbfda8aSnia
208b18c2d1eSnia#ifdef VSCREEN
2090bbfda8aSnia		if(Scr->numVscreens > 1) {
210b18c2d1eSnia			VirtualScreen *vs;
2110bbfda8aSnia			for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
2120bbfda8aSnia				if(OCCUPY(twm_win, vs->wsw->currentwspc)) {
2130bbfda8aSnia					twm_win->vs = vs;
2140bbfda8aSnia					twm_win->parent_vs = vs;
2150bbfda8aSnia					break;
2160bbfda8aSnia				}
2170bbfda8aSnia			}
2180bbfda8aSnia		}
219b18c2d1eSnia#endif
2200bbfda8aSnia	}
2210bbfda8aSnia
2220bbfda8aSnia
2230bbfda8aSnia	/* Set the property for the occupation */
2240bbfda8aSnia	{
2250bbfda8aSnia		long eventMask;
2260bbfda8aSnia		char *wsstr;
2270bbfda8aSnia		int  len;
2280bbfda8aSnia
2290bbfda8aSnia		/* Ignore the PropertyChange we're about to do */
2300bbfda8aSnia		if((eventMask = mask_out_event(twm_win->w, PropertyChangeMask)) < 0) {
2310bbfda8aSnia			/* Window is horked, not much we can do */
2320bbfda8aSnia			return;
2330bbfda8aSnia		}
2340bbfda8aSnia
2350bbfda8aSnia		/* Set the property for the occupation */
2360bbfda8aSnia		len = GetPropertyFromMask(twm_win->occupation, &wsstr);
2370bbfda8aSnia		XChangeProperty(dpy, twm_win->w, XA_WM_OCCUPATION, XA_STRING, 8,
2380bbfda8aSnia		                PropModeReplace, (unsigned char *) wsstr, len);
2390bbfda8aSnia		free(wsstr);
2400bbfda8aSnia
2410bbfda8aSnia#ifdef EWMH
2420bbfda8aSnia		EwmhSet_NET_WM_DESKTOP(twm_win);
2430bbfda8aSnia#endif
2440bbfda8aSnia
2450bbfda8aSnia		/* Restore event mask */
2460bbfda8aSnia		restore_mask(twm_win->w, eventMask);
2470bbfda8aSnia	}
2480bbfda8aSnia
2490bbfda8aSnia	/* Set WM_STATE prop */
2500bbfda8aSnia	{
2510bbfda8aSnia		int state = NormalState;
2520bbfda8aSnia		Window icon;
2530bbfda8aSnia
2540bbfda8aSnia		if(!(RestartPreviousState
2550bbfda8aSnia		                && GetWMState(twm_win->w, &state, &icon)
2560bbfda8aSnia		                && (state == NormalState || state == IconicState
2570bbfda8aSnia		                    || state == InactiveState))) {
2580bbfda8aSnia			if(twm_win->wmhints->flags & StateHint) {
2590bbfda8aSnia				state = twm_win->wmhints->initial_state;
2600bbfda8aSnia			}
2610bbfda8aSnia		}
2620bbfda8aSnia		if(visible(twm_win)) {
2630bbfda8aSnia			if(state == InactiveState) {
2640bbfda8aSnia				SetMapStateProp(twm_win, NormalState);
2650bbfda8aSnia			}
2660bbfda8aSnia		}
2670bbfda8aSnia		else {
2680bbfda8aSnia			if(state == NormalState) {
2690bbfda8aSnia				SetMapStateProp(twm_win, InactiveState);
2700bbfda8aSnia			}
2710bbfda8aSnia		}
2720bbfda8aSnia	}
2730bbfda8aSnia}
2740bbfda8aSnia
2750bbfda8aSnia
2760bbfda8aSnia/*
2770bbfda8aSnia * Make sure a window is marked in a given workspace.  f.addtoworkspace.
2780bbfda8aSnia * Also gets called as part of the process of mapping a window; if we're
2790bbfda8aSnia * mapping it here, it should know that it's here.  And Xinerama magic
2800bbfda8aSnia * moves.
2810bbfda8aSnia */
2820bbfda8aSniavoid
2830bbfda8aSniaAddToWorkSpace(char *wname, TwmWindow *twm_win)
2840bbfda8aSnia{
2850bbfda8aSnia	WorkSpace *ws;
2860bbfda8aSnia	int newoccupation;
2870bbfda8aSnia
2880bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
2890bbfda8aSnia		return;
2900bbfda8aSnia	}
2910bbfda8aSnia	ws = GetWorkspace(wname);
2920bbfda8aSnia	if(!ws) {
2930bbfda8aSnia		return;
2940bbfda8aSnia	}
2950bbfda8aSnia
2960bbfda8aSnia	if(twm_win->occupation & (1 << ws->number)) {
2970bbfda8aSnia		return;
2980bbfda8aSnia	}
2990bbfda8aSnia	newoccupation = twm_win->occupation | (1 << ws->number);
3000bbfda8aSnia	ChangeOccupation(twm_win, newoccupation);
3010bbfda8aSnia}
3020bbfda8aSnia
3030bbfda8aSnia
3040bbfda8aSnia/*
3050bbfda8aSnia * Converse of the above.  f.removefromworkspace, also called from
3060bbfda8aSnia * Xinerama-related magic.
3070bbfda8aSnia */
3080bbfda8aSniavoid
3090bbfda8aSniaRemoveFromWorkSpace(char *wname, TwmWindow *twm_win)
3100bbfda8aSnia{
3110bbfda8aSnia	WorkSpace *ws;
3120bbfda8aSnia	int newoccupation;
3130bbfda8aSnia
3140bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
3150bbfda8aSnia		return;
3160bbfda8aSnia	}
3170bbfda8aSnia	ws = GetWorkspace(wname);
3180bbfda8aSnia	if(!ws) {
3190bbfda8aSnia		return;
3200bbfda8aSnia	}
3210bbfda8aSnia
3220bbfda8aSnia	newoccupation = twm_win->occupation & ~(1 << ws->number);
3230bbfda8aSnia	if(!newoccupation) {
3240bbfda8aSnia		return;
3250bbfda8aSnia	}
3260bbfda8aSnia	ChangeOccupation(twm_win, newoccupation);
3270bbfda8aSnia}
3280bbfda8aSnia
3290bbfda8aSnia
3300bbfda8aSnia/* f.toggleoccupation - flip setting for [current] workspace */
3310bbfda8aSniavoid
3320bbfda8aSniaToggleOccupation(char *wname, TwmWindow *twm_win)
3330bbfda8aSnia{
3340bbfda8aSnia	WorkSpace *ws;
3350bbfda8aSnia	int newoccupation;
3360bbfda8aSnia
3370bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
3380bbfda8aSnia		return;
3390bbfda8aSnia	}
3400bbfda8aSnia	ws = GetWorkspace(wname);
3410bbfda8aSnia	if(!ws) {
3420bbfda8aSnia		return;
3430bbfda8aSnia	}
3440bbfda8aSnia
3450bbfda8aSnia	newoccupation = twm_win->occupation ^ (1 << ws->number);
3460bbfda8aSnia	if(!newoccupation) {
3470bbfda8aSnia		/* Don't allow de-occupying _every_ ws */
3480bbfda8aSnia		return;
3490bbfda8aSnia	}
3500bbfda8aSnia	ChangeOccupation(twm_win, newoccupation);
3510bbfda8aSnia}
3520bbfda8aSnia
3530bbfda8aSnia
3540bbfda8aSnia/* f.movetonextworkspace */
3550bbfda8aSniavoid
3560bbfda8aSniaMoveToNextWorkSpace(VirtualScreen *vs, TwmWindow *twm_win)
3570bbfda8aSnia{
3580bbfda8aSnia	WorkSpace *wlist1, *wlist2;
3590bbfda8aSnia	int newoccupation;
3600bbfda8aSnia
3610bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
3620bbfda8aSnia		return;
3630bbfda8aSnia	}
3640bbfda8aSnia
3650bbfda8aSnia	wlist1 = vs->wsw->currentwspc;
3660bbfda8aSnia	wlist2 = wlist1->next;
3670bbfda8aSnia	wlist2 = wlist2 ? wlist2 : Scr->workSpaceMgr.workSpaceList;
3680bbfda8aSnia
3690bbfda8aSnia	/* Out of (here), into (here+1) */
3700bbfda8aSnia	newoccupation = (twm_win->occupation ^ (1 << wlist1->number))
3710bbfda8aSnia	                | (1 << wlist2->number);
3720bbfda8aSnia	ChangeOccupation(twm_win, newoccupation);
3730bbfda8aSnia}
3740bbfda8aSnia
3750bbfda8aSnia
3760bbfda8aSnia/* f.movetonextworkspaceandfollow */
3770bbfda8aSniavoid
3780bbfda8aSniaMoveToNextWorkSpaceAndFollow(VirtualScreen *vs, TwmWindow *twm_win)
3790bbfda8aSnia{
3800bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
3810bbfda8aSnia		return;
3820bbfda8aSnia	}
3830bbfda8aSnia
3840bbfda8aSnia	MoveToNextWorkSpace(vs, twm_win);
3850bbfda8aSnia	GotoNextWorkSpace(vs);
3860bbfda8aSnia#if 0
3870bbfda8aSnia	OtpRaise(twm_win, WinWin);  /* XXX really do this? */
3880bbfda8aSnia#endif
3890bbfda8aSnia}
3900bbfda8aSnia
3910bbfda8aSnia
3920bbfda8aSnia/* f.movetoprevworkspaceand */
3930bbfda8aSniavoid
3940bbfda8aSniaMoveToPrevWorkSpace(VirtualScreen *vs, TwmWindow *twm_win)
3950bbfda8aSnia{
3960bbfda8aSnia	WorkSpace *wlist1, *wlist2;
3970bbfda8aSnia	int newoccupation;
3980bbfda8aSnia
3990bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
4000bbfda8aSnia		return;
4010bbfda8aSnia	}
4020bbfda8aSnia
4030bbfda8aSnia	wlist1 = Scr->workSpaceMgr.workSpaceList;
4040bbfda8aSnia	wlist2 = vs->wsw->currentwspc;
4050bbfda8aSnia	if(wlist1 == NULL) {
4060bbfda8aSnia		return;
4070bbfda8aSnia	}
4080bbfda8aSnia
4090bbfda8aSnia	while(wlist1->next != wlist2 && wlist1->next != NULL) {
4100bbfda8aSnia		wlist1 = wlist1->next;
4110bbfda8aSnia	}
4120bbfda8aSnia
4130bbfda8aSnia	/* Out of (here), into (here-1) */
4140bbfda8aSnia	newoccupation = (twm_win->occupation ^ (1 << wlist2->number))
4150bbfda8aSnia	                | (1 << wlist1->number);
4160bbfda8aSnia	ChangeOccupation(twm_win, newoccupation);
4170bbfda8aSnia}
4180bbfda8aSnia
4190bbfda8aSnia
4200bbfda8aSnia/* f.movetoprevworkspaceandfollow */
4210bbfda8aSniavoid
4220bbfda8aSniaMoveToPrevWorkSpaceAndFollow(VirtualScreen *vs, TwmWindow *twm_win)
4230bbfda8aSnia{
4240bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
4250bbfda8aSnia		return;
4260bbfda8aSnia	}
4270bbfda8aSnia
4280bbfda8aSnia	MoveToPrevWorkSpace(vs, twm_win);
4290bbfda8aSnia	GotoPrevWorkSpace(vs);
4300bbfda8aSnia#if 0
4310bbfda8aSnia	OtpRaise(twm_win, WinWin);          /* XXX really do this? */
4320bbfda8aSnia#endif
4330bbfda8aSnia}
4340bbfda8aSnia
4350bbfda8aSnia
4360bbfda8aSnia/*
4370bbfda8aSnia * Set the occupation based on the window name.  This is called if
4380bbfda8aSnia * AutoOccupy is set, when we get a notification about a window name
4390bbfda8aSnia * change.
4400bbfda8aSnia */
4410bbfda8aSniavoid
4420bbfda8aSniaWmgrRedoOccupation(TwmWindow *win)
4430bbfda8aSnia{
4440bbfda8aSnia	WorkSpace *ws;
4450bbfda8aSnia	int       newoccupation;
4460bbfda8aSnia
4470bbfda8aSnia	if(LookInList(Scr->OccupyAll, win->name, &win->class)) {
4480bbfda8aSnia		newoccupation = fullOccupation;
4490bbfda8aSnia	}
4500bbfda8aSnia	else {
4510bbfda8aSnia		newoccupation = 0;
4520bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
4530bbfda8aSnia			if(LookInList(ws->clientlist, win->name, &win->class)) {
4540bbfda8aSnia				newoccupation |= 1 << ws->number;
4550bbfda8aSnia			}
4560bbfda8aSnia		}
4570bbfda8aSnia	}
4580bbfda8aSnia	if(newoccupation != 0) {
4590bbfda8aSnia		ChangeOccupation(win, newoccupation);
4600bbfda8aSnia	}
4610bbfda8aSnia}
4620bbfda8aSnia
4630bbfda8aSnia
4640bbfda8aSnia/* f.vanish */
4650bbfda8aSniavoid
4660bbfda8aSniaWMgrRemoveFromCurrentWorkSpace(VirtualScreen *vs, TwmWindow *win)
4670bbfda8aSnia{
4680bbfda8aSnia	WorkSpace *ws;
4690bbfda8aSnia	int       newoccupation;
4700bbfda8aSnia
4710bbfda8aSnia	ws = vs->wsw->currentwspc;
4720bbfda8aSnia	if(!ws) {
4730bbfda8aSnia		/* Impossible? */
4740bbfda8aSnia		return;
4750bbfda8aSnia	}
4760bbfda8aSnia	if(! OCCUPY(win, ws)) {
4770bbfda8aSnia		return;
4780bbfda8aSnia	}
4790bbfda8aSnia
4800bbfda8aSnia	newoccupation = win->occupation & ~(1 << ws->number);
4810bbfda8aSnia	if(newoccupation == 0) {
4820bbfda8aSnia		return;
4830bbfda8aSnia	}
4840bbfda8aSnia
4850bbfda8aSnia	ChangeOccupation(win, newoccupation);
4860bbfda8aSnia}
4870bbfda8aSnia
4880bbfda8aSnia
4890bbfda8aSnia/* f.warphere */
4900bbfda8aSniavoid
4910bbfda8aSniaWMgrAddToCurrentWorkSpaceAndWarp(VirtualScreen *vs, char *winname)
4920bbfda8aSnia{
4930bbfda8aSnia	TwmWindow *tw;
4940bbfda8aSnia	int       newoccupation;
4950bbfda8aSnia
4960bbfda8aSnia	/* Find named window on this screen */
4970bbfda8aSnia	for(tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
4980bbfda8aSnia		if(match(winname, tw->name)) {
4990bbfda8aSnia			break;
5000bbfda8aSnia		}
5010bbfda8aSnia	}
5020bbfda8aSnia
5030bbfda8aSnia	/* Didn't find it by name?  Try by class */
5040bbfda8aSnia	if(!tw) {
5050bbfda8aSnia		for(tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
5060bbfda8aSnia			if(match(winname, tw->class.res_name)) {
5070bbfda8aSnia				break;
5080bbfda8aSnia			}
5090bbfda8aSnia		}
5100bbfda8aSnia	}
5110bbfda8aSnia	if(!tw) {
5120bbfda8aSnia		for(tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
5130bbfda8aSnia			if(match(winname, tw->class.res_class)) {
5140bbfda8aSnia				break;
5150bbfda8aSnia			}
5160bbfda8aSnia		}
5170bbfda8aSnia	}
5180bbfda8aSnia
5190bbfda8aSnia	/* Still didn't find?  Beep at the user and bail. */
5200bbfda8aSnia	if(!tw) {
5210bbfda8aSnia		XBell(dpy, 0);
5220bbfda8aSnia		return;
5230bbfda8aSnia	}
5240bbfda8aSnia
5250bbfda8aSnia	/* If WarpUnmapped isn't set and this isn't mapped, beep and bail */
5260bbfda8aSnia	if((! Scr->WarpUnmapped) && (! tw->mapped)) {
5270bbfda8aSnia		XBell(dpy, 0);
5280bbfda8aSnia		return;
5290bbfda8aSnia	}
5300bbfda8aSnia
5310bbfda8aSnia	/* Move it here if it's not */
5320bbfda8aSnia	if(! OCCUPY(tw, vs->wsw->currentwspc)) {
5330bbfda8aSnia		newoccupation = tw->occupation | (1 << vs->wsw->currentwspc->number);
5340bbfda8aSnia		ChangeOccupation(tw, newoccupation);
5350bbfda8aSnia	}
5360bbfda8aSnia
5370bbfda8aSnia	/* If we get here, WarpUnmapped is set, so map it if we need to */
5380bbfda8aSnia	if(! tw->mapped) {
5390bbfda8aSnia		DeIconify(tw);
5400bbfda8aSnia	}
5410bbfda8aSnia
5420bbfda8aSnia	/* And go */
5430bbfda8aSnia	WarpToWindow(tw, Scr->RaiseOnWarp);
5440bbfda8aSnia}
5450bbfda8aSnia
5460bbfda8aSnia
5470bbfda8aSnia/* f.occupyall backend */
5480bbfda8aSniavoid
5490bbfda8aSniaOccupyAll(TwmWindow *twm_win)
5500bbfda8aSnia{
5510bbfda8aSnia	IconMgr *save;
5520bbfda8aSnia
5530bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
5540bbfda8aSnia		return;
5550bbfda8aSnia	}
5560bbfda8aSnia
5570bbfda8aSnia	/*
5580bbfda8aSnia	 * Temporarily alter Scr->iconmgr because stuff down in
5590bbfda8aSnia	 * ChangeOccupation winds up adding/removing bits, and that doesn't
5600bbfda8aSnia	 * work right when we're setting all?  XXX Investigate further.
5610bbfda8aSnia	 */
5620bbfda8aSnia	save = Scr->iconmgr;
5630bbfda8aSnia	Scr->iconmgr = Scr->workSpaceMgr.workSpaceList->iconmgr;
5640bbfda8aSnia	ChangeOccupation(twm_win, fullOccupation);
5650bbfda8aSnia	Scr->iconmgr = save;
5660bbfda8aSnia}
5670bbfda8aSnia
5680bbfda8aSnia
5690bbfda8aSnia
5700bbfda8aSnia/*
5710bbfda8aSnia ****************************************************************
5720bbfda8aSnia *
5730bbfda8aSnia * Pieces related to the Occupy window
5740bbfda8aSnia *
5750bbfda8aSnia ****************************************************************
5760bbfda8aSnia */
5770bbfda8aSnia
5780bbfda8aSniastatic ColorPair occupyButtoncp;
5790bbfda8aSnia
5800bbfda8aSniastatic char *ok_string         = "OK",
5810bbfda8aSnia             *cancel_string     = "Cancel",
5820bbfda8aSnia              *everywhere_string = "All";
5830bbfda8aSnia
5840bbfda8aSnia/*
5850bbfda8aSnia * Create the Occupy window.  Part of startup process.
5860bbfda8aSnia *
5870bbfda8aSnia * Do not do the layout of the parts, only calculate the initial total
5880bbfda8aSnia * size. For the layout, call ResizeOccupyWindow() at the end.
5890bbfda8aSnia *
5900bbfda8aSnia * There is only one Occupy window (per Screen), it is reparented to each
5910bbfda8aSnia * virtual screen as needed.
5920bbfda8aSnia */
5930bbfda8aSniavoid
5940bbfda8aSniaCreateOccupyWindow(void)
5950bbfda8aSnia{
5960bbfda8aSnia	int           width; // Caculated and altered, unlike most others
5970bbfda8aSnia	int           Dummy = 1;
5980bbfda8aSnia	TwmWindow     *tmp_win;
5990bbfda8aSnia	/* Shorthands for the Occupy window */
6000bbfda8aSnia	OccupyWindow  *occwin = Scr->workSpaceMgr.occupyWindow;
6010bbfda8aSnia	Window        w; // occwin->w
6020bbfda8aSnia	/* Misc other shorthands */
6030bbfda8aSnia	const int lines   = Scr->workSpaceMgr.lines;
6040bbfda8aSnia	const int columns = Scr->workSpaceMgr.columns;
6050bbfda8aSnia	const int bwidth  = Scr->vScreenList->wsw->bwidth;
6060bbfda8aSnia	const int bheight = Scr->vScreenList->wsw->bheight;
6070bbfda8aSnia	const int vspace = occwin->vspace;
6080bbfda8aSnia	const int hspace = occwin->hspace;
6090bbfda8aSnia	const int height = ((bheight + vspace) * lines) + bheight + (2 * vspace);
6100bbfda8aSnia
6110bbfda8aSnia	/* Struct embedded in [struct embedded in] Scr, so memory's waiting */
6120bbfda8aSnia
6130bbfda8aSnia	/* There isn't anything we should do without workspaces... */
6140bbfda8aSnia	if(!Scr->workSpaceManagerActive) {
6150bbfda8aSnia		return;
6160bbfda8aSnia	}
6170bbfda8aSnia
6180bbfda8aSnia	/* Initialize font and colorpair bits */
6190bbfda8aSnia	occwin->font     = Scr->IconManagerFont;
6200bbfda8aSnia	occwin->cp       = Scr->IconManagerC;
6210bbfda8aSnia#ifdef COLOR_BLIND_USER
6220bbfda8aSnia	occwin->cp.shadc = Scr->White;
6230bbfda8aSnia	occwin->cp.shadd = Scr->Black;
6240bbfda8aSnia#else
6250bbfda8aSnia	if(!Scr->BeNiceToColormap) {
6260bbfda8aSnia		GetShadeColors(&occwin->cp);
6270bbfda8aSnia	}
6280bbfda8aSnia#endif
6290bbfda8aSnia
6300bbfda8aSnia	/* We already know that these should be too */
6310bbfda8aSnia	occwin->lines   = lines;
6320bbfda8aSnia	occwin->columns = columns;
6330bbfda8aSnia
6340bbfda8aSnia
6350bbfda8aSnia	/*
6360bbfda8aSnia	 * Work out the necessary size of the OK/Cancel/All buttons at the
6370bbfda8aSnia	 * bottom.
6380bbfda8aSnia	 */
6390bbfda8aSnia	{
6400bbfda8aSnia		XRectangle inc_rect;
6410bbfda8aSnia		XRectangle logical_rect;
6420bbfda8aSnia		MyFont font = occwin->font;
6430bbfda8aSnia		int bbwidth;  // Bottom button width
6440bbfda8aSnia		/* Window min width based on bottom vs. workspace btns */
6450bbfda8aSnia		int bb_width, ws_width;
6460bbfda8aSnia
6470bbfda8aSnia		/* Buttons gotta be as wide as the biggest of the three strings */
6480bbfda8aSnia		XmbTextExtents(font.font_set, ok_string, strlen(ok_string),
6490bbfda8aSnia		               &inc_rect, &logical_rect);
6500bbfda8aSnia		bbwidth = logical_rect.width;
6510bbfda8aSnia
6520bbfda8aSnia		XmbTextExtents(font.font_set, cancel_string, strlen(cancel_string),
6530bbfda8aSnia		               &inc_rect, &logical_rect);
6540bbfda8aSnia		bbwidth = MAX(bbwidth, logical_rect.width);
6550bbfda8aSnia
6560bbfda8aSnia		XmbTextExtents(font.font_set, everywhere_string,
6570bbfda8aSnia		               strlen(everywhere_string),
6580bbfda8aSnia		               &inc_rect, &logical_rect);
6590bbfda8aSnia		bbwidth = MAX(bbwidth, logical_rect.width);
6600bbfda8aSnia
6610bbfda8aSnia		/* Plus the padding width */
6620bbfda8aSnia		bbwidth += hspace;
6630bbfda8aSnia
6640bbfda8aSnia		/*
6650bbfda8aSnia		 * So, the final width of those bottom buttons is that, plus the
6660bbfda8aSnia		 * 3d button look extra on both sides, plus a little extra.  I
6670bbfda8aSnia		 * guess that extra + 2 is similar to TitlePadding or
6680bbfda8aSnia		 * ButtonIndent on titlebars, but we don't have a config param
6690bbfda8aSnia		 * for it on the workspace manager (which is the config used for
6700bbfda8aSnia		 * the occupy window), so leave it as a magic constant for now.
6710bbfda8aSnia		 */
6720bbfda8aSnia		occwin->owidth = bbwidth + 2 * Scr->WMgrButtonShadowDepth + 2;
6730bbfda8aSnia
6740bbfda8aSnia		/*
6750bbfda8aSnia		 * The whole thing has to be at least triple the min width of
6760bbfda8aSnia		 * those bottom buttons, since there are three of them.  The
6770bbfda8aSnia		 * layout is "hspace button hspace button [...] hspace", to pad
6780bbfda8aSnia		 * between and on both sides.
6790bbfda8aSnia		 */
6800bbfda8aSnia		bb_width = 3 * (bbwidth + hspace) + hspace;
6810bbfda8aSnia
6820bbfda8aSnia		/*
6830bbfda8aSnia		 * It also has to be the width of our per-WS buttons.  Per-ws
6840bbfda8aSnia		 * buttons are sized the same as in the button-state WSM, and
6850bbfda8aSnia		 * then we add the padding to them as above.
6860bbfda8aSnia		 */
6870bbfda8aSnia		ws_width = columns * (bwidth + hspace) + hspace;
6880bbfda8aSnia
6890bbfda8aSnia		/* So the window has to be as wide as the wider of those */
6900bbfda8aSnia		width = MAX(bb_width, ws_width);
6910bbfda8aSnia	}
6920bbfda8aSnia
6930bbfda8aSnia
6940bbfda8aSnia	/* Now we know the size, so make the window */
6950bbfda8aSnia	w = occwin->w = XCreateSimpleWindow(dpy, Scr->Root, 0, 0, width, height,
6960bbfda8aSnia	                                    1, Scr->Black, occwin->cp.back);
6970bbfda8aSnia
6980bbfda8aSnia	/* Take those base sizes as a minimum */
6990bbfda8aSnia	occwin->minwidth  = width;
7000bbfda8aSnia	occwin->minheight = height;
7010bbfda8aSnia
7020bbfda8aSnia
7030bbfda8aSnia	/*
7040bbfda8aSnia	 * Make subwindows as buttons for the workspaces.  They're laid out
7050bbfda8aSnia	 * in a grid mirroring the workspace manager's.
7060bbfda8aSnia	 */
7070bbfda8aSnia	{
7080bbfda8aSnia		int i = 0, j = 0;
7090bbfda8aSnia		WorkSpace *ws;
7100bbfda8aSnia
7110bbfda8aSnia		occwin->obuttonw = calloc(Scr->workSpaceMgr.count, sizeof(Window));
7120bbfda8aSnia
7130bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
7140bbfda8aSnia			int idx = (j * columns) + i;
7150bbfda8aSnia
7160bbfda8aSnia			/*
7170bbfda8aSnia			 * Make and map.  Note that we're not setting the size or
7180bbfda8aSnia			 * location at all here; ResizeOccupyWindow() does all that.
7190bbfda8aSnia			 * We just make 'em.
7200bbfda8aSnia			 */
7210bbfda8aSnia			occwin->obuttonw[idx] = XCreateSimpleWindow(dpy, w,
7220bbfda8aSnia			                        Dummy /* x */,
7230bbfda8aSnia			                        Dummy /* y */,
7240bbfda8aSnia			                        Dummy /* width */,
7250bbfda8aSnia			                        Dummy /* height */,
7260bbfda8aSnia			                        0, Scr->Black, ws->cp.back);
7270bbfda8aSnia			XMapWindow(dpy, occwin->obuttonw[idx]);
7280bbfda8aSnia
7290bbfda8aSnia			/* Inc around to the next location */
7300bbfda8aSnia			i++;
7310bbfda8aSnia			if(i == columns) {
7320bbfda8aSnia				i = 0;
7330bbfda8aSnia				j++;
7340bbfda8aSnia			}
7350bbfda8aSnia		}
7360bbfda8aSnia	}
7370bbfda8aSnia
7380bbfda8aSnia
7390bbfda8aSnia	/*
7400bbfda8aSnia	 * Now start putting together the OK/Cancel/All buttons
7410bbfda8aSnia	 */
7420bbfda8aSnia
7430bbfda8aSnia	/* Background for them is hardcoded */
7440bbfda8aSnia	GetColor(Scr->Monochrome, &(occupyButtoncp.back), "gray50");
7450bbfda8aSnia
7460bbfda8aSnia	/* Foreground (not used here) is too */
7470bbfda8aSnia	occupyButtoncp.fore = Scr->White;
7480bbfda8aSnia
7490bbfda8aSnia	/* Override (probably historical */
7500bbfda8aSnia	if(!Scr->BeNiceToColormap) {
7510bbfda8aSnia		GetShadeColors(&occupyButtoncp);
7520bbfda8aSnia	}
7530bbfda8aSnia
7540bbfda8aSnia	/* Make 'em */
7550bbfda8aSnia	{
7560bbfda8aSnia		Window tw;
7570bbfda8aSnia
7580bbfda8aSnia		tw = XCreateSimpleWindow(dpy, w, Dummy, Dummy, Dummy, Dummy, 0,
7590bbfda8aSnia		                         Scr->Black, occupyButtoncp.back);
7600bbfda8aSnia		XMapWindow(dpy, tw);
7610bbfda8aSnia		occwin->OK = tw;
7620bbfda8aSnia
7630bbfda8aSnia		tw = XCreateSimpleWindow(dpy, w, Dummy, Dummy, Dummy, Dummy, 0,
7640bbfda8aSnia		                         Scr->Black, occupyButtoncp.back);
7650bbfda8aSnia		XMapWindow(dpy, tw);
7660bbfda8aSnia		occwin->cancel = tw;
7670bbfda8aSnia
7680bbfda8aSnia		tw = XCreateSimpleWindow(dpy, w, Dummy, Dummy, Dummy, Dummy, 0,
7690bbfda8aSnia		                         Scr->Black, occupyButtoncp.back);
7700bbfda8aSnia		XMapWindow(dpy, tw);
7710bbfda8aSnia		occwin->allworkspc = tw;
7720bbfda8aSnia	}
7730bbfda8aSnia
7740bbfda8aSnia
7750bbfda8aSnia	/* Setup various window properties */
7760bbfda8aSnia	{
7770bbfda8aSnia		XSizeHints sizehints;
7780bbfda8aSnia		XWMHints wmhints;
7790bbfda8aSnia
7800bbfda8aSnia		sizehints.flags       = PBaseSize | PMinSize | PMaxSize;
7810bbfda8aSnia		sizehints.min_width   = width;
7820bbfda8aSnia		sizehints.min_height  = height;
7830bbfda8aSnia		sizehints.base_width  = width;
7840bbfda8aSnia		sizehints.base_height = height;
7850bbfda8aSnia		sizehints.max_width   = width;
7860bbfda8aSnia		sizehints.max_height  = height;
7870bbfda8aSnia
7880bbfda8aSnia		wmhints.flags         = InputHint | StateHint;
7890bbfda8aSnia		wmhints.input         = True;
7900bbfda8aSnia		wmhints.initial_state = NormalState;
7910bbfda8aSnia
7920bbfda8aSnia		XmbSetWMProperties(dpy, w, occwin->name, occwin->icon_name,
7930bbfda8aSnia		                   NULL, 0, &sizehints, &wmhints, NULL);
7940bbfda8aSnia	}
7950bbfda8aSnia
7960bbfda8aSnia
7970bbfda8aSnia	/*
7980bbfda8aSnia	 * Create the TwmWindow wrapping around it, with decorations etc.  We
7990bbfda8aSnia	 * do this so early in startup that we're not listening for window
8000bbfda8aSnia	 * creation events yet.
8010bbfda8aSnia	 */
8020bbfda8aSnia	tmp_win = AddWindow(w, AWT_OCCUPY, Scr->iconmgr, Scr->currentvs);
8030bbfda8aSnia	if(! tmp_win) {
8040bbfda8aSnia		fprintf(stderr, "cannot create occupy window, exiting...\n");
8050bbfda8aSnia		exit(1);
8060bbfda8aSnia	}
8070bbfda8aSnia	tmp_win->vs = NULL;
8080bbfda8aSnia	tmp_win->occupation = 0;
8090bbfda8aSnia
8100bbfda8aSnia	/* tmp_win is more convenient the rest of the func, but put in place */
8110bbfda8aSnia	occwin->twm_win = tmp_win;
8120bbfda8aSnia
8130bbfda8aSnia
8140bbfda8aSnia	/*
8150bbfda8aSnia	 * Setup the window to have a button-pushing cursor and listen for
8160bbfda8aSnia	 * clicks.
8170bbfda8aSnia	 */
8180bbfda8aSnia	{
8190bbfda8aSnia		unsigned long attrmask;
8200bbfda8aSnia		XSetWindowAttributes attr;
8210bbfda8aSnia		XWindowAttributes wattr;
8220bbfda8aSnia
8230bbfda8aSnia		attr.cursor = Scr->ButtonCursor;
8240bbfda8aSnia		attrmask = CWCursor;
8250bbfda8aSnia		XChangeWindowAttributes(dpy, w, attrmask, &attr);
8260bbfda8aSnia
8270bbfda8aSnia		XGetWindowAttributes(dpy, w, &wattr);
8280bbfda8aSnia		attrmask = wattr.your_event_mask | KeyPressMask | KeyReleaseMask
8290bbfda8aSnia		           | ExposureMask;
8300bbfda8aSnia		XSelectInput(dpy, w, attrmask);
8310bbfda8aSnia	}
8320bbfda8aSnia
8330bbfda8aSnia
8340bbfda8aSnia	/*
8350bbfda8aSnia	 * Now for each of the buttons (workspaces + OK/Cancel/All), we mark
8360bbfda8aSnia	 * them as listening to click and exposure events.  We also stash
8370bbfda8aSnia	 * away the screen and wrapping TwmWindow in contexts so other code
8380bbfda8aSnia	 * can dredge them up.
8390bbfda8aSnia	 */
8400bbfda8aSnia#define EVT (ButtonPressMask | ButtonReleaseMask | ExposureMask)
8410bbfda8aSnia#define BTN_IPT_CTX(win) \
8420bbfda8aSnia        XSelectInput(dpy, (win), EVT); \
8430bbfda8aSnia        XSaveContext(dpy, (win), TwmContext, (XPointer) tmp_win); \
8440bbfda8aSnia        XSaveContext(dpy, (win), ScreenContext, (XPointer) Scr);
8450bbfda8aSnia
8460bbfda8aSnia	for(WorkSpace *ws = Scr->workSpaceMgr.workSpaceList
8470bbfda8aSnia	                    ; ws != NULL ; ws = ws->next) {
8480bbfda8aSnia		BTN_IPT_CTX(occwin->obuttonw[ws->number]);
8490bbfda8aSnia	}
8500bbfda8aSnia
8510bbfda8aSnia	BTN_IPT_CTX(occwin->OK);
8520bbfda8aSnia	BTN_IPT_CTX(occwin->cancel);
8530bbfda8aSnia	BTN_IPT_CTX(occwin->allworkspc);
8540bbfda8aSnia
8550bbfda8aSnia#undef BTN_IPT_CTX
8560bbfda8aSnia#undef EVT
8570bbfda8aSnia
8580bbfda8aSnia
8590bbfda8aSnia	/* Mark that we're not mapped */
8600bbfda8aSnia	SetMapStateProp(tmp_win, WithdrawnState);
8610bbfda8aSnia
8620bbfda8aSnia	/* Now call that func that sizes all the buttons */
8630bbfda8aSnia	ResizeOccupyWindow(tmp_win);
8640bbfda8aSnia}
8650bbfda8aSnia
8660bbfda8aSnia
8670bbfda8aSnia/*
8680bbfda8aSnia * Slightly misleading name: layout the internals of the Occupy window
8690bbfda8aSnia * based on its current size.  That does happen when it's resized, but
8700bbfda8aSnia * also when it's initially created.  I guess you could call "creation" a
8710bbfda8aSnia * resize of a sort...
8720bbfda8aSnia */
8730bbfda8aSniavoid
8740bbfda8aSniaResizeOccupyWindow(TwmWindow *win)
8750bbfda8aSnia{
8760bbfda8aSnia	int        bwidth, bheight, owidth, oheight;
8770bbfda8aSnia	int        hspace, vspace;
8780bbfda8aSnia	int        lines, columns;
8790bbfda8aSnia	int        neww, newh;
8800bbfda8aSnia	WorkSpace  *ws;
8810bbfda8aSnia	int        i, j, x, y;
8820bbfda8aSnia	OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow;
8830bbfda8aSnia
8840bbfda8aSnia	/* Floor at the original size */
8850bbfda8aSnia	neww = MAX(win->attr.width,  occwin->minwidth);
8860bbfda8aSnia	newh = MAX(win->attr.height, occwin->minheight);
8870bbfda8aSnia	if(occwin->width == neww && occwin->height == newh) {
8880bbfda8aSnia		return;
8890bbfda8aSnia	}
8900bbfda8aSnia
8910bbfda8aSnia	/* Space between WS buttons.  From WMgr{Horiz,Vert}ButtonIndent. */
8920bbfda8aSnia	hspace  = occwin->hspace;
8930bbfda8aSnia	vspace  = occwin->vspace;
8940bbfda8aSnia
8950bbfda8aSnia	/* Lines/cols in the layout.  Same as WorkspaceManager's */
8960bbfda8aSnia	lines   = Scr->workSpaceMgr.lines;
8970bbfda8aSnia	columns = Scr->workSpaceMgr.columns;
8980bbfda8aSnia
8990bbfda8aSnia	/* Width/height of each button, based on the above and window size */
9000bbfda8aSnia	bwidth  = (neww -  columns    * hspace) / columns;
9010bbfda8aSnia	bheight = (newh - (lines + 2) * vspace) / (lines + 1);
9020bbfda8aSnia
9030bbfda8aSnia	/* Width/height of the OK/Cancel/All buttons */
9040bbfda8aSnia	owidth  = occwin->owidth;
9050bbfda8aSnia	oheight = bheight;
9060bbfda8aSnia
9070bbfda8aSnia
9080bbfda8aSnia	/*
9090bbfda8aSnia	 * Lay out the workspace buttons
9100bbfda8aSnia	 */
9110bbfda8aSnia	i = 0;
9120bbfda8aSnia	j = 0;
9130bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
9140bbfda8aSnia		XMoveResizeWindow(dpy, occwin->obuttonw [j * columns + i],
9150bbfda8aSnia		                  i * (bwidth  + hspace) + (hspace / 2),
9160bbfda8aSnia		                  j * (bheight + vspace) + (vspace / 2),
9170bbfda8aSnia		                  bwidth, bheight);
9180bbfda8aSnia		i++;
9190bbfda8aSnia		if(i == columns) {
9200bbfda8aSnia			i = 0;
9210bbfda8aSnia			j++;
9220bbfda8aSnia		}
9230bbfda8aSnia	}
9240bbfda8aSnia
9250bbfda8aSnia
9260bbfda8aSnia	/*
9270bbfda8aSnia	 * Now the action buttons
9280bbfda8aSnia	 */
9290bbfda8aSnia	hspace = (neww - 3 * owidth) / 4;  // Padding between
9300bbfda8aSnia	x = hspace;
9310bbfda8aSnia	y = ((bheight + vspace) * lines) + ((3 * vspace) / 2);
9320bbfda8aSnia	XMoveResizeWindow(dpy, occwin->OK, x, y, owidth, oheight);
9330bbfda8aSnia	x += owidth + hspace;
9340bbfda8aSnia	XMoveResizeWindow(dpy, occwin->cancel, x, y, owidth, oheight);
9350bbfda8aSnia	x += owidth + hspace;
9360bbfda8aSnia	XMoveResizeWindow(dpy, occwin->allworkspc, x, y, owidth, oheight);
9370bbfda8aSnia
9380bbfda8aSnia
9390bbfda8aSnia	/* Save all those dimensions we figured */
9400bbfda8aSnia	occwin->width   = neww;
9410bbfda8aSnia	occwin->height  = newh;
9420bbfda8aSnia	occwin->bwidth  = bwidth;
9430bbfda8aSnia	occwin->bheight = bheight;
9440bbfda8aSnia	occwin->owidth  = owidth;
9450bbfda8aSnia
9460bbfda8aSnia	/* Don't need to repaint it; it'll get expose events */
9470bbfda8aSnia}
9480bbfda8aSnia
9490bbfda8aSnia
9500bbfda8aSnia/*
9510bbfda8aSnia * Draw the window when we need to (e.g., on expose)
9520bbfda8aSnia */
9530bbfda8aSniavoid
9540bbfda8aSniaPaintOccupyWindow(void)
9550bbfda8aSnia{
9560bbfda8aSnia	WorkSpace    *ws;
9570bbfda8aSnia	OccupyWindow *occwin;
9580bbfda8aSnia	int          width, height;
9590bbfda8aSnia
9600bbfda8aSnia	occwin = Scr->workSpaceMgr.occupyWindow;
9610bbfda8aSnia	width  = occwin->width;
9620bbfda8aSnia	height = occwin->height;
9630bbfda8aSnia
9640bbfda8aSnia	Draw3DBorder(occwin->w, 0, 0, width, height, 2, occwin->cp, off, true, false);
9650bbfda8aSnia
9660bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
9670bbfda8aSnia		Window bw = occwin->obuttonw [ws->number];
9680bbfda8aSnia		ButtonState bs = (occwin->tmpOccupation & (1 << ws->number)) ? on : off;
9690bbfda8aSnia
9700bbfda8aSnia		PaintWsButton(OCCUPYWINDOW, NULL, bw, ws->label, ws->cp, bs);
9710bbfda8aSnia	}
9720bbfda8aSnia	PaintWsButton(OCCUPYBUTTON, NULL, occwin->OK,         ok_string,
9730bbfda8aSnia	              occupyButtoncp, off);
9740bbfda8aSnia	PaintWsButton(OCCUPYBUTTON, NULL, occwin->cancel,     cancel_string,
9750bbfda8aSnia	              occupyButtoncp, off);
9760bbfda8aSnia	PaintWsButton(OCCUPYBUTTON, NULL, occwin->allworkspc, everywhere_string,
9770bbfda8aSnia	              occupyButtoncp, off);
9780bbfda8aSnia}
9790bbfda8aSnia
9800bbfda8aSnia
9810bbfda8aSnia/*
9820bbfda8aSnia * Somebody clicked in the Occupy window
9830bbfda8aSnia */
9840bbfda8aSniavoid
9850bbfda8aSniaOccupyHandleButtonEvent(XEvent *event)
9860bbfda8aSnia{
9870bbfda8aSnia	WorkSpace    *ws;
9880bbfda8aSnia	OccupyWindow *occupyW;
9890bbfda8aSnia	Window       buttonW;
9900bbfda8aSnia
9910bbfda8aSnia	/*
9920bbfda8aSnia	 * Doesn't make sense that this can even happen if there are no
9930bbfda8aSnia	 * workspaces...
9940bbfda8aSnia	 */
9950bbfda8aSnia	if(! Scr->workSpaceManagerActive) {
9960bbfda8aSnia		return;
9970bbfda8aSnia	}
9980bbfda8aSnia
9990bbfda8aSnia	/* ... or if there's no Occupy window up for anything */
10000bbfda8aSnia	if(occupyWin == NULL) {
10010bbfda8aSnia		return;
10020bbfda8aSnia	}
10030bbfda8aSnia
10040bbfda8aSnia	/* Which sub-window (button) was clicked */
10050bbfda8aSnia	buttonW = event->xbutton.window;
10060bbfda8aSnia	if(buttonW == 0) {
10070bbfda8aSnia		return;        /* icon */
10080bbfda8aSnia	}
10090bbfda8aSnia
10100bbfda8aSnia	/* Grab onto the pointer for the duration of our action */
10110bbfda8aSnia	XGrabPointer(dpy, Scr->Root, True,
10120bbfda8aSnia	             ButtonPressMask | ButtonReleaseMask,
10130bbfda8aSnia	             GrabModeAsync, GrabModeAsync,
10140bbfda8aSnia	             Scr->Root, None, CurrentTime);
10150bbfda8aSnia
10160bbfda8aSnia	/* Find the workspace button that was clicked */
10170bbfda8aSnia	occupyW = Scr->workSpaceMgr.occupyWindow;
10180bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
10190bbfda8aSnia		if(occupyW->obuttonw [ws->number] == buttonW) {
10200bbfda8aSnia			break;
10210bbfda8aSnia		}
10220bbfda8aSnia	}
10230bbfda8aSnia
10240bbfda8aSnia	if(ws != NULL) {
10250bbfda8aSnia		/* If one was, toggle it */
10260bbfda8aSnia		int mask = 1 << ws->number;
10270bbfda8aSnia		ButtonState bs = (occupyW->tmpOccupation & mask) ? off : on;
10280bbfda8aSnia
10290bbfda8aSnia		PaintWsButton(OCCUPYWINDOW, NULL, occupyW->obuttonw [ws->number],
10300bbfda8aSnia		              ws->label, ws->cp, bs);
10310bbfda8aSnia		occupyW->tmpOccupation ^= mask;
10320bbfda8aSnia	}
10330bbfda8aSnia	else if(buttonW == occupyW->OK) {
10340bbfda8aSnia		/* Else if we clicked OK, set things and close the window */
10350bbfda8aSnia		if(occupyW->tmpOccupation == 0) {
10360bbfda8aSnia			return;
10370bbfda8aSnia		}
10380bbfda8aSnia		ChangeOccupation(occupyWin, occupyW->tmpOccupation);
10390bbfda8aSnia		XUnmapWindow(dpy, occupyW->twm_win->frame);
10400bbfda8aSnia		occupyW->twm_win->mapped = false;
10410bbfda8aSnia		occupyW->twm_win->occupation = 0;
10420bbfda8aSnia		occupyWin = NULL;
10430bbfda8aSnia		XSync(dpy, 0);
10440bbfda8aSnia	}
10450bbfda8aSnia	else if(buttonW == occupyW->cancel) {
10460bbfda8aSnia		/* Or cancel, do nothing and close the window */
10470bbfda8aSnia		XUnmapWindow(dpy, occupyW->twm_win->frame);
10480bbfda8aSnia		occupyW->twm_win->mapped = false;
10490bbfda8aSnia		occupyW->twm_win->occupation = 0;
10500bbfda8aSnia		occupyWin = NULL;
10510bbfda8aSnia		XSync(dpy, 0);
10520bbfda8aSnia	}
10530bbfda8aSnia	else if(buttonW == occupyW->allworkspc) {
10540bbfda8aSnia		/* Or All, set 'em all */
10550bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
10560bbfda8aSnia			PaintWsButton(OCCUPYWINDOW, NULL, occupyW->obuttonw [ws->number],
10570bbfda8aSnia			              ws->label, ws->cp, on);
10580bbfda8aSnia		}
10590bbfda8aSnia		occupyW->tmpOccupation = fullOccupation;
10600bbfda8aSnia	}
10610bbfda8aSnia
10620bbfda8aSnia	/* Release the pointer, if ??? */
10630bbfda8aSnia	if(ButtonPressed == -1) {
10640bbfda8aSnia		XUngrabPointer(dpy, CurrentTime);
10650bbfda8aSnia	}
10660bbfda8aSnia}
10670bbfda8aSnia
10680bbfda8aSnia
10690bbfda8aSnia/*
10700bbfda8aSnia * f.occupy backend - pop up Occupy control for some window
10710bbfda8aSnia */
10720bbfda8aSniavoid
10730bbfda8aSniaOccupy(TwmWindow *twm_win)
10740bbfda8aSnia{
10750bbfda8aSnia	int          x, y;
10760bbfda8aSnia	unsigned int width, height;
10770bbfda8aSnia	Window       w;
10780bbfda8aSnia	struct OccupyWindow    *occupyWindow;
10790bbfda8aSnia	TwmWindow *occupy_twm;
10800bbfda8aSnia
10810bbfda8aSnia	/* Don't pop up on stuff we can't change */
10820bbfda8aSnia	if(!CanChangeOccupation(&twm_win)) {
10830bbfda8aSnia		return;
10840bbfda8aSnia	}
10850bbfda8aSnia
10860bbfda8aSnia	/* Grab our one screen-wide f.occupy window */
10870bbfda8aSnia	occupyWindow = Scr->workSpaceMgr.occupyWindow;
10880bbfda8aSnia	occupyWindow->tmpOccupation = twm_win->occupation;
10890bbfda8aSnia	w = occupyWindow->w;
10900bbfda8aSnia
10910bbfda8aSnia	/* Figure where to put it so it's centered on the cursor */
10920bbfda8aSnia	XGetGeometry(dpy, w, &JunkRoot, &JunkX, &JunkY, &width, &height,
10930bbfda8aSnia	             &JunkBW, &JunkDepth);
10940bbfda8aSnia	XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkRoot, &JunkX, &JunkY,
10950bbfda8aSnia	              &x, &y, &JunkMask);
10960bbfda8aSnia
10970bbfda8aSnia	occupy_twm = occupyWindow->twm_win;
10980bbfda8aSnia	occupy_twm->occupation = twm_win->occupation;
10990bbfda8aSnia
1100b18c2d1eSnia	width += 2 * (occupy_twm->frame_bw3D + occupy_twm->frame_bw);
1101b18c2d1eSnia	height += 2 * (occupy_twm->frame_bw3D + occupy_twm->frame_bw);
1102b18c2d1eSnia	x -= (width  / 2);
1103b18c2d1eSnia	y -= (height / 2);
1104b18c2d1eSnia
1105b18c2d1eSnia	/* Clip to screen */
1106b18c2d1eSnia	ConstrainByLayout(Scr->BorderedLayout, -1, &x, width, &y, height);
1107b18c2d1eSnia
11080bbfda8aSnia	/* Move the occupy window to where it should be */
11090bbfda8aSnia	if(occupy_twm->parent_vs != twm_win->parent_vs) {
11100bbfda8aSnia		occupy_twm->vs = twm_win->parent_vs;
11110bbfda8aSnia		occupy_twm->frame_x = x;
11120bbfda8aSnia		occupy_twm->frame_y = y;
11130bbfda8aSnia		/*
11140bbfda8aSnia		 * XXX Should this be using DisplayWin() like everything else,
11150bbfda8aSnia		 * rather than manually grubbing beneath it?
11160bbfda8aSnia		 */
11170bbfda8aSnia		ReparentFrameAndIcon(occupy_twm);
11180bbfda8aSnia	}
11190bbfda8aSnia	else {
11200bbfda8aSnia		XMoveWindow(dpy, occupyWindow->twm_win->frame, x, y);
11210bbfda8aSnia	}
11220bbfda8aSnia
11230bbfda8aSnia	/* And show it */
11240bbfda8aSnia	SetMapStateProp(occupy_twm, NormalState);
11250bbfda8aSnia	XMapWindow(dpy, occupyWindow->w);
11260bbfda8aSnia	XMapWindow(dpy, occupy_twm->frame);
11270bbfda8aSnia
11280bbfda8aSnia	/* XXX Must be a better way to express "all the way on top" */
11290bbfda8aSnia	OtpSetPriority(occupy_twm, WinWin, 0, Above);
11300bbfda8aSnia
11310bbfda8aSnia	/* Mark it shown, and stash what window we're showing it for */
11320bbfda8aSnia	occupyWindow->twm_win->mapped = true;
11330bbfda8aSnia	occupyWin = twm_win;
11340bbfda8aSnia}
11350bbfda8aSnia
11360bbfda8aSnia
11370bbfda8aSnia
11380bbfda8aSnia
11390bbfda8aSnia/*
11400bbfda8aSnia ****************************************************************
11410bbfda8aSnia *
11420bbfda8aSnia * Backend and misc
11430bbfda8aSnia *
11440bbfda8aSnia ****************************************************************
11450bbfda8aSnia */
11460bbfda8aSnia
11470bbfda8aSnia
11480bbfda8aSnia/*
11490bbfda8aSnia * The actual meat of occupation-changing; [re-]set the occupation for
11500bbfda8aSnia * the window.  This is the func that actually sets and saves the new
11510bbfda8aSnia * occupation, moves the window where it should be, etc.  Should maybe be
11520bbfda8aSnia * called something more like "SetOccupation()".
11530bbfda8aSnia */
11540bbfda8aSniavoid
11550bbfda8aSniaChangeOccupation(TwmWindow *tmp_win, int newoccupation)
11560bbfda8aSnia{
11570bbfda8aSnia	TwmWindow *t;
11580bbfda8aSnia	WorkSpace *ws;
11590bbfda8aSnia	int oldoccupation;
11600bbfda8aSnia	int changedoccupation;
11610bbfda8aSnia
11620bbfda8aSnia	if((newoccupation == 0)
11630bbfda8aSnia	                || (newoccupation == tmp_win->occupation)) {
11640bbfda8aSnia		/*
11650bbfda8aSnia		 * occupation=0 we interpret as "leave it alone".  == current,
11660bbfda8aSnia		 * ditto.  Go ahead and re-set the WM_OCCUPATION property though,
11670bbfda8aSnia		 * in case it's been broken by another client.
11680bbfda8aSnia		 */
11690bbfda8aSnia		char *namelist;
11700bbfda8aSnia		int  len;
11710bbfda8aSnia		long eventMask;
11720bbfda8aSnia
11730bbfda8aSnia		/* Mask out the PropertyChange events while we change the prop */
11740bbfda8aSnia		eventMask = mask_out_event(tmp_win->w, PropertyChangeMask);
11750bbfda8aSnia
11760bbfda8aSnia		len = GetPropertyFromMask(tmp_win->occupation, &namelist);
11770bbfda8aSnia		XChangeProperty(dpy, tmp_win->w, XA_WM_OCCUPATION, XA_STRING, 8,
11780bbfda8aSnia		                PropModeReplace, (unsigned char *) namelist, len);
11790bbfda8aSnia		free(namelist);
11800bbfda8aSnia#ifdef EWMH
11810bbfda8aSnia		EwmhSet_NET_WM_DESKTOP(tmp_win);
11820bbfda8aSnia#endif
11830bbfda8aSnia
11840bbfda8aSnia		/* Reset event mask */
11850bbfda8aSnia		restore_mask(tmp_win->w, eventMask);
11860bbfda8aSnia		return;
11870bbfda8aSnia	}
11880bbfda8aSnia
11890bbfda8aSnia	/*
11900bbfda8aSnia	 * OK, there's something to change.  Stash the current state.
11910bbfda8aSnia	 */
11920bbfda8aSnia	oldoccupation = tmp_win->occupation;
11930bbfda8aSnia
11940bbfda8aSnia	/*
11950bbfda8aSnia	 * Add it to IconManager in the new WS[en], remove from old.  We have
11960bbfda8aSnia	 * to do the rather odd dance because AddIconManager() loops through
11970bbfda8aSnia	 * workspaces, and will add it to any workspaces it occupies (even if
11980bbfda8aSnia	 * it's already there).  RemoveIconManager() goes over the window's
11990bbfda8aSnia	 * list of what icon managers it's on and removes it from any that
12000bbfda8aSnia	 * don't match the current occupation, so it can just be told "here's
12010bbfda8aSnia	 * where I should be".
12020bbfda8aSnia	 */
12030bbfda8aSnia	tmp_win->occupation = newoccupation & ~oldoccupation;
12040bbfda8aSnia	AddIconManager(tmp_win);
12050bbfda8aSnia	tmp_win->occupation = newoccupation;
12060bbfda8aSnia	RemoveIconManager(tmp_win);
12070bbfda8aSnia
12080bbfda8aSnia	/* If it shouldn't be "here", vanish it */
12090bbfda8aSnia	if(tmp_win->vs && !OCCUPY(tmp_win, tmp_win->vs->wsw->currentwspc)) {
12100bbfda8aSnia		Vanish(tmp_win->vs, tmp_win);
12110bbfda8aSnia	}
12120bbfda8aSnia
12130bbfda8aSnia	/*
12140bbfda8aSnia	 * Try to find an(other) virtual screen which shows a workspace
12150bbfda8aSnia	 * where the window has occupation, so that the window can be shown
12160bbfda8aSnia	 * there now.
12170bbfda8aSnia	 */
12180bbfda8aSnia	if(!tmp_win->vs) {
12190bbfda8aSnia		VirtualScreen *vs;
12200bbfda8aSnia		for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
12210bbfda8aSnia			if(OCCUPY(tmp_win, vs->wsw->currentwspc)) {
12220bbfda8aSnia				DisplayWin(vs, tmp_win);
12230bbfda8aSnia				break;
12240bbfda8aSnia			}
12250bbfda8aSnia		}
12260bbfda8aSnia	}
12270bbfda8aSnia
12280bbfda8aSnia	/*
12290bbfda8aSnia	 * Loop over workspaces.  Find the first one that it used to be in.
12300bbfda8aSnia	 * If it's not there anymore, take it out of the WindowRegion there
12310bbfda8aSnia	 * (RWFR() silently returns if we're not using WindowRegion's), and
12320bbfda8aSnia	 * add it the WindowRegion in the first WS it now occupies.
12330bbfda8aSnia	 *
12340bbfda8aSnia	 * XXX I'm not sure this is entirely sensible; it seems like just
12350bbfda8aSnia	 * unconditionally Remove/Place'ing would have the same effect?
12360bbfda8aSnia	 */
12370bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
12380bbfda8aSnia		int mask = 1 << ws->number;
12390bbfda8aSnia		if(oldoccupation & mask) {
12400bbfda8aSnia			if(!(newoccupation & mask)) {
12410bbfda8aSnia				int final_x, final_y;
12420bbfda8aSnia				RemoveWindowFromRegion(tmp_win);
12430bbfda8aSnia				if(PlaceWindowInRegion(tmp_win, &final_x, &final_y)) {
12440bbfda8aSnia					XMoveWindow(dpy, tmp_win->frame, final_x, final_y);
12450bbfda8aSnia				}
12460bbfda8aSnia			}
12470bbfda8aSnia			break;
12480bbfda8aSnia		}
12490bbfda8aSnia	}
12500bbfda8aSnia
12510bbfda8aSnia	/* Now set the WM_OCCUPATION property */
12520bbfda8aSnia	{
12530bbfda8aSnia		char *namelist;
12540bbfda8aSnia		int  len;
12550bbfda8aSnia		long eventMask;
12560bbfda8aSnia
12570bbfda8aSnia		eventMask = mask_out_event(tmp_win->w, PropertyChangeMask);
12580bbfda8aSnia
12590bbfda8aSnia		len = GetPropertyFromMask(newoccupation, &namelist);
12600bbfda8aSnia		XChangeProperty(dpy, tmp_win->w, XA_WM_OCCUPATION, XA_STRING, 8,
12610bbfda8aSnia		                PropModeReplace, (unsigned char *) namelist, len);
12620bbfda8aSnia		free(namelist);
12630bbfda8aSnia#ifdef EWMH
12640bbfda8aSnia		EwmhSet_NET_WM_DESKTOP(tmp_win);
12650bbfda8aSnia#endif
12660bbfda8aSnia
12670bbfda8aSnia		restore_mask(tmp_win->w, eventMask);
12680bbfda8aSnia	}
12690bbfda8aSnia
12700bbfda8aSnia
12710bbfda8aSnia	/*
12720bbfda8aSnia	 * Handle showing it up in the workspace map in the appropriate
12730bbfda8aSnia	 * places.
12740bbfda8aSnia	 *
12750bbfda8aSnia	 * Note that this whole block messes with the {new,old}occupation
12760bbfda8aSnia	 * vars.  That's "safe" because they're no longer used for their
12770bbfda8aSnia	 * original purposes, only for the WSmap changes, but it's still
12780bbfda8aSnia	 * kinda fugly.  Change to local vars at the drop of a hat with later
12790bbfda8aSnia	 * changes...
12800bbfda8aSnia	 */
12810bbfda8aSnia	if(!WMapWindowMayBeAdded(tmp_win)) {
12820bbfda8aSnia		/* Not showing in the map, so pretend it's nowhere */
12830bbfda8aSnia		newoccupation = 0;
12840bbfda8aSnia	}
12850bbfda8aSnia	if(Scr->workSpaceMgr.noshowoccupyall) {
12860bbfda8aSnia		/*
12870bbfda8aSnia		 * Don't show OccupyAll.  Note that this means both OccupyAll
12880bbfda8aSnia		 * window, AND windows manually set to occupy everything.  We
12890bbfda8aSnia		 * don't have to adjust newoccupation, because the above
12900bbfda8aSnia		 * conditional would have caught it, so we only need to edit old.
12910bbfda8aSnia		 */
12920bbfda8aSnia		if(oldoccupation == fullOccupation) {
12930bbfda8aSnia			oldoccupation = 0;
12940bbfda8aSnia		}
12950bbfda8aSnia	}
12960bbfda8aSnia
12970bbfda8aSnia	/* Flip the ones that need flipping */
12980bbfda8aSnia	changedoccupation = oldoccupation ^ newoccupation;
12990bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
13000bbfda8aSnia		int mask = 1 << ws->number;
13010bbfda8aSnia		if(changedoccupation & mask) {
13020bbfda8aSnia			if(newoccupation & mask) {
13030bbfda8aSnia				/* Add to WS */
13040bbfda8aSnia				WMapAddWindowToWorkspace(tmp_win, ws);
13050bbfda8aSnia			}
13060bbfda8aSnia			else {
13070bbfda8aSnia				/*
13080bbfda8aSnia				 * Remove from WS.  We have to take it out of saved focus
13090bbfda8aSnia				 * if it were there.  Maybe there are other places we
13100bbfda8aSnia				 * might need to remove it from (warpring?)?
13110bbfda8aSnia				 */
13120bbfda8aSnia				WMapRemoveWindowFromWorkspace(tmp_win, ws);
13130bbfda8aSnia				if(Scr->SaveWorkspaceFocus && ws->save_focus == tmp_win) {
13140bbfda8aSnia					ws->save_focus = NULL;
13150bbfda8aSnia				}
13160bbfda8aSnia			}
13170bbfda8aSnia		}
13180bbfda8aSnia	}
13190bbfda8aSnia
13200bbfda8aSnia
13210bbfda8aSnia	/*
13220bbfda8aSnia	 * If transients don't have their own occupation, find any transients
13230bbfda8aSnia	 * of this window and move them with it.
13240bbfda8aSnia	 */
13250bbfda8aSnia	if(! Scr->TransientHasOccupation) {
13260bbfda8aSnia		for(t = Scr->FirstWindow; t != NULL; t = t->next) {
13270bbfda8aSnia			if(t != tmp_win &&
13280bbfda8aSnia			                ((t->istransient && t->transientfor == tmp_win->w) ||
13290bbfda8aSnia			                 t->group == tmp_win->w)) {
13300bbfda8aSnia				ChangeOccupation(t, tmp_win->occupation);
13310bbfda8aSnia			}
13320bbfda8aSnia		}
13330bbfda8aSnia	}
13340bbfda8aSnia
13350bbfda8aSnia	/* All done */
13360bbfda8aSnia	return;
13370bbfda8aSnia}
13380bbfda8aSnia
13390bbfda8aSnia
13400bbfda8aSnia/*
13410bbfda8aSnia * There are various reasons you might not be able to change the
13420bbfda8aSnia * occupation of a window (either due to attributes of it, or the state
13430bbfda8aSnia * of your session/WM), so provide a function to check them all when we
13440bbfda8aSnia * try a change.
13450bbfda8aSnia *
13460bbfda8aSnia * Note that this is _not_ called from ChangeOccupation(); only from
13470bbfda8aSnia * other things that wrap it.  Since CO() gets called from states where
13480bbfda8aSnia * this would [falsely] fail, it would be a bad idea to put it there.
13490bbfda8aSnia */
13500bbfda8aSniastatic bool
13510bbfda8aSniaCanChangeOccupation(TwmWindow **twm_winp)
13520bbfda8aSnia{
13530bbfda8aSnia	TwmWindow *twm_win;
13540bbfda8aSnia
13550bbfda8aSnia	/* No workspaces config'd?  Changing is nonsensical. */
13560bbfda8aSnia	if(!Scr->workSpaceManagerActive) {
13570bbfda8aSnia		return false;
13580bbfda8aSnia	}
13590bbfda8aSnia
13600bbfda8aSnia	/*
13610bbfda8aSnia	 * f.occupy window up?  Can't change in the middle of changing.
13620bbfda8aSnia	 * Though if it's not mapped, still pull it up, else iconifying the
13630bbfda8aSnia	 * occupy window breaks it forever.
13640bbfda8aSnia	 */
13650bbfda8aSnia	if(occupyWin != NULL && Scr->workSpaceMgr.occupyWindow->twm_win->mapped) {
13660bbfda8aSnia		return false;
13670bbfda8aSnia	}
13680bbfda8aSnia
13690bbfda8aSnia	/* XXX Can we jut do this in the init?  Check all callers. */
13700bbfda8aSnia	twm_win = *twm_winp;
13710bbfda8aSnia
13720bbfda8aSnia	/* Don't change occupation of icon managers */
13730bbfda8aSnia	if(twm_win->isiconmgr) {
13740bbfda8aSnia		return false;
13750bbfda8aSnia	}
13760bbfda8aSnia
13770bbfda8aSnia	/* XXX Should check iswspmgr here too? */
13780bbfda8aSnia
13790bbfda8aSnia	/*
13800bbfda8aSnia	 * If transients don't have their own occupation, check
13810bbfda8aSnia	 * transient/group bits.
13820bbfda8aSnia	 */
13830bbfda8aSnia	if(!Scr->TransientHasOccupation) {
13840bbfda8aSnia		if(twm_win->istransient) {
13850bbfda8aSnia			return false;
13860bbfda8aSnia		}
13870bbfda8aSnia		if(twm_win->group != (Window) 0 && twm_win->group != twm_win->w) {
13880bbfda8aSnia			/*
13890bbfda8aSnia			 * When trying to modify a group member window,
13900bbfda8aSnia			 * operate on the group leader instead
13910bbfda8aSnia			 * (and thereby on all group member windows as well).
13920bbfda8aSnia			 * If we can't find the group leader, pretend it isn't set.
13930bbfda8aSnia			 */
13940bbfda8aSnia			twm_win = GetTwmWindow(twm_win->group);
13950bbfda8aSnia			if(!twm_win) {
13960bbfda8aSnia				return true;
13970bbfda8aSnia			}
13980bbfda8aSnia			*twm_winp = twm_win;
13990bbfda8aSnia		}
14000bbfda8aSnia	}
14010bbfda8aSnia
14020bbfda8aSnia	/* Sure, go ahead, change it */
14030bbfda8aSnia	return true;
14040bbfda8aSnia}
14050bbfda8aSnia
14060bbfda8aSnia
14070bbfda8aSnia/*
14080bbfda8aSnia * Add a client name to a list determining which workspaces it will
14090bbfda8aSnia * occupy.  Used in handling the Occupy { } block in config file.
14100bbfda8aSnia */
14110bbfda8aSniabool
14120bbfda8aSniaAddToClientsList(char *workspace, char *client)
14130bbfda8aSnia{
14140bbfda8aSnia	WorkSpace *ws;
14150bbfda8aSnia
14160bbfda8aSnia	/* "all" is a magic workspace value which makes it occupy anywhere */
14170bbfda8aSnia	if(strcmp(workspace, "all") == 0) {
14180bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
14190bbfda8aSnia			AddToList(&ws->clientlist, client, "");
14200bbfda8aSnia		}
14210bbfda8aSnia		return true;
14220bbfda8aSnia	}
14230bbfda8aSnia
14240bbfda8aSnia	/* If prefixed with "ws:", strip the prefix and lookup by WS name */
14250bbfda8aSnia	if(strncmp(workspace, "ws:", 3) == 0) {
14260bbfda8aSnia		if((ws = GetWorkspace(workspace + 3)) != NULL) {
14270bbfda8aSnia			AddToList(&ws->clientlist, client, "");
14280bbfda8aSnia			return true;
14290bbfda8aSnia		}
14300bbfda8aSnia	}
14310bbfda8aSnia
14320bbfda8aSnia	/* Else find that named workspace and all this to it */
14330bbfda8aSnia	if((ws = GetWorkspace(workspace)) != NULL) {
14340bbfda8aSnia		AddToList(&ws->clientlist, client, "");
14350bbfda8aSnia		return true;
14360bbfda8aSnia	}
14370bbfda8aSnia
14380bbfda8aSnia	/* Couldn't figure where to put it */
14390bbfda8aSnia	return false;
14400bbfda8aSnia}
14410bbfda8aSnia
14420bbfda8aSnia
14430bbfda8aSnia/*
14440bbfda8aSnia * Turn a ctwm.workspace resource string into an occupation mask.  n.b.;
14450bbfda8aSnia * this changes the 'res' arg in-place.
14460bbfda8aSnia */
14470bbfda8aSniastatic int
14480bbfda8aSniaGetMaskFromResource(TwmWindow *win, char *res)
14490bbfda8aSnia{
14500bbfda8aSnia	WorkSpace *ws;
14510bbfda8aSnia	int       mask;
14520bbfda8aSnia	enum { O_SET, O_ADD, O_REM } mode;
14530bbfda8aSnia	char *wrkSpcName, *tokst;
14540bbfda8aSnia
14550bbfda8aSnia	/*
14560bbfda8aSnia	 * This can set the occupation to a specific set of workspaces ("ws1
14570bbfda8aSnia	 * ws3"), add to the set it woudl have otherwise ("+ws1 ws3"), or
14580bbfda8aSnia	 * remove from the set it would otherwise ("-ws1 ws3").  The +/-
14590bbfda8aSnia	 * apply to the whole expression, not to the individual entries in
14600bbfda8aSnia	 * it.  So first, figure out what we're doing.
14610bbfda8aSnia	 */
14620bbfda8aSnia	mode = O_SET;
14630bbfda8aSnia	if(*res == '+') {
14640bbfda8aSnia		mode = O_ADD;
14650bbfda8aSnia		res++;
14660bbfda8aSnia	}
14670bbfda8aSnia	else if(*res == '-') {
14680bbfda8aSnia		mode = O_REM;
14690bbfda8aSnia		res++;
14700bbfda8aSnia	}
14710bbfda8aSnia
14720bbfda8aSnia	/*
14730bbfda8aSnia	 * Walk through the string adding the workspaces specified into the
14740bbfda8aSnia	 * mask of what we're doing.
14750bbfda8aSnia	 */
14760bbfda8aSnia	mask = 0;
14770bbfda8aSnia	for(wrkSpcName = strtok_r(res, " ", &tokst) ; wrkSpcName
14780bbfda8aSnia	                ; wrkSpcName = strtok_r(NULL, " ", &tokst)) {
14790bbfda8aSnia		if(strcmp(wrkSpcName, "all") == 0) {
14800bbfda8aSnia			mask = fullOccupation;
14810bbfda8aSnia			break;
14820bbfda8aSnia		}
14830bbfda8aSnia		if(strcmp(wrkSpcName, "current") == 0) {
14840bbfda8aSnia			VirtualScreen *vs = Scr->currentvs;
14850bbfda8aSnia			if(vs) {
14860bbfda8aSnia				mask |= (1 << vs->wsw->currentwspc->number);
14870bbfda8aSnia			}
14880bbfda8aSnia			continue;
14890bbfda8aSnia		}
14900bbfda8aSnia
14910bbfda8aSnia		ws = GetWorkspace(wrkSpcName);
14920bbfda8aSnia		if(ws != NULL) {
14930bbfda8aSnia			mask |= (1 << ws->number);
14940bbfda8aSnia		}
14950bbfda8aSnia		else {
14960bbfda8aSnia			fprintf(stderr, "unknown workspace : %s\n", wrkSpcName);
14970bbfda8aSnia		}
14980bbfda8aSnia	}
14990bbfda8aSnia
15000bbfda8aSnia	/*
15010bbfda8aSnia	 * And return that mask, with necessary alterations depending on +/-
15020bbfda8aSnia	 * specified.
15030bbfda8aSnia	 */
15040bbfda8aSnia	switch(mode) {
15050bbfda8aSnia		case O_SET:
15060bbfda8aSnia			return (mask);
15070bbfda8aSnia		case O_ADD:
15080bbfda8aSnia			return (mask | win->occupation);
15090bbfda8aSnia		case O_REM:
15100bbfda8aSnia			return (win->occupation & ~mask);
15110bbfda8aSnia	}
15120bbfda8aSnia
15130bbfda8aSnia	/* Can't get here */
15140bbfda8aSnia	fprintf(stderr, "%s(): Unreachable.\n", __func__);
15150bbfda8aSnia	return 0;
15160bbfda8aSnia}
15170bbfda8aSnia
15180bbfda8aSnia
15190bbfda8aSnia/*
15200bbfda8aSnia * Turns a \0-separated buffer of workspace names into an occupation
15210bbfda8aSnia * bitmask.
15220bbfda8aSnia */
15230bbfda8aSniaunsigned int
15240bbfda8aSniaGetMaskFromProperty(unsigned char *_prop, unsigned long len)
15250bbfda8aSnia{
15260bbfda8aSnia	char         wrkSpcName[256];
15270bbfda8aSnia	WorkSpace    *ws;
15280bbfda8aSnia	unsigned int mask;
15290bbfda8aSnia	int          l;
15300bbfda8aSnia	char         *prop;
15310bbfda8aSnia
15320bbfda8aSnia	mask = 0;
15330bbfda8aSnia	l = 0;
15340bbfda8aSnia	prop = (char *) _prop;
15350bbfda8aSnia	while(l < len) {
15360bbfda8aSnia		/* If you have WS names longer than 256 chars, that's just Too Bad */
15370bbfda8aSnia		safe_strncpy(wrkSpcName, prop, 256);
15380bbfda8aSnia		l    += strlen(prop) + 1;
15390bbfda8aSnia		prop += strlen(prop) + 1;
15400bbfda8aSnia		if(strcmp(wrkSpcName, "all") == 0) {
15410bbfda8aSnia			mask = fullOccupation;
15420bbfda8aSnia			break;
15430bbfda8aSnia		}
15440bbfda8aSnia
15450bbfda8aSnia		ws = GetWorkspace(wrkSpcName);
15460bbfda8aSnia		if(ws != NULL) {
15470bbfda8aSnia			mask |= (1 << ws->number);
15480bbfda8aSnia		}
15490bbfda8aSnia		else {
15500bbfda8aSnia			fprintf(stderr, "unknown workspace : %s\n", wrkSpcName);
15510bbfda8aSnia		}
15520bbfda8aSnia	}
15530bbfda8aSnia
15540bbfda8aSnia#if 0
15550bbfda8aSnia	{
15560bbfda8aSnia		char *dbs = mk_nullsep_string((char *)_prop, len);
15570bbfda8aSnia		fprintf(stderr, "%s('%s') -> 0x%x\n", __func__, dbs, mask);
15580bbfda8aSnia		free(dbs);
15590bbfda8aSnia	}
15600bbfda8aSnia#endif
15610bbfda8aSnia
15620bbfda8aSnia	return (mask);
15630bbfda8aSnia}
15640bbfda8aSnia
15650bbfda8aSnia
15660bbfda8aSnia/*
15670bbfda8aSnia * Turns an occupation mask into a \0-separated buffer (not really a
15680bbfda8aSnia * string) of the workspace names.
15690bbfda8aSnia */
15700bbfda8aSniaint
15710bbfda8aSniaGetPropertyFromMask(unsigned int mask, char **prop)
15720bbfda8aSnia{
15730bbfda8aSnia	WorkSpace *ws;
15740bbfda8aSnia	int       len;
15750bbfda8aSnia	char      *wss[MAXWORKSPACE];
15760bbfda8aSnia	int       i;
15770bbfda8aSnia
15780bbfda8aSnia	/* If it's everything, just say 'all' */
15790bbfda8aSnia	if(mask == fullOccupation) {
15800bbfda8aSnia		*prop = strdup("all");
15810bbfda8aSnia		return 3;
15820bbfda8aSnia	}
15830bbfda8aSnia
15840bbfda8aSnia	/* Stash up pointers to all the labels for WSen it's in */
15850bbfda8aSnia	memset(wss, 0, sizeof(wss));
15860bbfda8aSnia	i = 0;
15870bbfda8aSnia	len = 0;
15880bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
15890bbfda8aSnia		if(mask & (1 << ws->number)) {
15900bbfda8aSnia			wss[i++] = ws->label;
15910bbfda8aSnia			len += strlen(ws->label) + 1;
15920bbfda8aSnia		}
15930bbfda8aSnia	}
15940bbfda8aSnia
15950bbfda8aSnia	/* Assemble them into \0-separated string */
15960bbfda8aSnia	*prop = malloc(len);
15970bbfda8aSnia	len = 0;
15980bbfda8aSnia	for(i = 0 ; wss[i] != NULL ; i++) {
15990bbfda8aSnia		strcpy((*prop + len), wss[i]);
16000bbfda8aSnia		len += strlen(wss[i]) + 1; // Skip past \0
16010bbfda8aSnia	}
16020bbfda8aSnia
16030bbfda8aSnia#if 0
16040bbfda8aSnia	{
16050bbfda8aSnia		char *dbs = mk_nullsep_string(*prop, len);
16060bbfda8aSnia		fprintf(stderr, "%s(0x%x) -> %d:'%s'\n", __func__, mask, len, dbs);
16070bbfda8aSnia		free(dbs);
16080bbfda8aSnia	}
16090bbfda8aSnia#endif
16100bbfda8aSnia
16110bbfda8aSnia	return len;
16120bbfda8aSnia}
16130bbfda8aSnia
16140bbfda8aSnia
16150bbfda8aSnia/*
16160bbfda8aSnia * Generate a printable variant of the null-separated strings we use for
16170bbfda8aSnia * stashing in XA_WM_OCCUPATION.  Used for debugging
16180bbfda8aSnia * Get{Property,Mask}From{Mask,Property}().
16190bbfda8aSnia */
16200bbfda8aSnia#ifdef __GNUC__
16210bbfda8aSnia# pragma GCC diagnostic push
16220bbfda8aSnia# pragma GCC diagnostic ignored "-Wunused-function"
16230bbfda8aSnia#endif
16240bbfda8aSniastatic char *
16250bbfda8aSniamk_nullsep_string(const char *prop, int len)
16260bbfda8aSnia{
16270bbfda8aSnia	char *dbs;
16280bbfda8aSnia	int i, j;
16290bbfda8aSnia
16300bbfda8aSnia	/*
16310bbfda8aSnia	 * '\0' => "\\0" means we need longer than input; *2 is overkill,
16320bbfda8aSnia	 * but always sufficient, and it's cheap.
16330bbfda8aSnia	 */
16340bbfda8aSnia	dbs = malloc(len * 2);
16350bbfda8aSnia	i = j = 0;
16360bbfda8aSnia	while(i < len) {
16370bbfda8aSnia		size_t slen = strlen(prop + i);
16380bbfda8aSnia
16390bbfda8aSnia		strcpy(dbs + j, (prop + i));
16400bbfda8aSnia		i += slen + 1;
16410bbfda8aSnia		strcpy(dbs + j + slen, "\\0");
16420bbfda8aSnia		j += slen + 2;
16430bbfda8aSnia	}
16440bbfda8aSnia
16450bbfda8aSnia	return dbs;
16460bbfda8aSnia}
16470bbfda8aSnia#ifdef __GNUC__
16480bbfda8aSnia# pragma GCC diagnostic pop
16490bbfda8aSnia#endif
1650