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