10bbfda8aSnia/*
20bbfda8aSnia * Copyright 1992 Claude Lecommandeur.
30bbfda8aSnia */
40bbfda8aSnia
50bbfda8aSnia#include "ctwm.h"
60bbfda8aSnia
70bbfda8aSnia#include <stdio.h>
80bbfda8aSnia#include <string.h>
90bbfda8aSnia#include <stdlib.h>
100bbfda8aSnia
110bbfda8aSnia#include <X11/Xatom.h>
120bbfda8aSnia
130bbfda8aSnia#include "ctwm_atoms.h"
140bbfda8aSnia#include "util.h"
150bbfda8aSnia#include "animate.h"
160bbfda8aSnia#include "screen.h"
170bbfda8aSnia#include "add_window.h"
180bbfda8aSnia#include "events.h"
190bbfda8aSnia#include "otp.h"
200bbfda8aSnia#include "cursor.h"
210bbfda8aSnia#include "image.h"
220bbfda8aSnia#include "drawing.h"
230bbfda8aSnia#include "list.h"
240bbfda8aSnia#include "occupation.h"
250bbfda8aSnia#include "vscreen.h"
260bbfda8aSnia#include "win_decorations.h"
270bbfda8aSnia#include "win_iconify.h"
280bbfda8aSnia#include "win_ops.h"
290bbfda8aSnia#include "win_utils.h"
300bbfda8aSnia#include "workspace_manager.h"
310bbfda8aSnia#include "workspace_utils.h"
32b18c2d1eSnia#include "xparsegeometry.h"
33b18c2d1eSnia
340bbfda8aSnia
350bbfda8aSnia#include "gram.tab.h"
360bbfda8aSnia
370bbfda8aSnia
380bbfda8aSnia// Temp; x-ref desc in workspace_utils
390bbfda8aSniaextern bool useBackgroundInfo;
400bbfda8aSnia
410bbfda8aSnia
420bbfda8aSniastatic void CreateWorkSpaceManagerWindow(VirtualScreen *vs);
430bbfda8aSniastatic void ResizeWorkSpaceManager(VirtualScreen *vs, TwmWindow *win);
440bbfda8aSniastatic void PaintWorkSpaceManagerBorder(VirtualScreen *vs);
450bbfda8aSnia
460bbfda8aSniastatic void wmap_mapwin_backend(TwmWindow *win, bool handleraise);
470bbfda8aSnia
480bbfda8aSniastatic void WMapRedrawWindow(Window window, int width, int height,
490bbfda8aSnia                             ColorPair cp, const char *label);
500bbfda8aSnia
510bbfda8aSniastatic void InvertColorPair(ColorPair *cp);
520bbfda8aSnia
530bbfda8aSnia
540bbfda8aSniastatic XContext MapWListContext = None;
550bbfda8aSniastatic Cursor handCursor = None;
560bbfda8aSnia
570bbfda8aSnia
580bbfda8aSnia
590bbfda8aSnia/*
600bbfda8aSnia ****************************************************************
610bbfda8aSnia *
620bbfda8aSnia * First, functions related to general creation and drawing of the WSM
630bbfda8aSnia * window and its backing structs
640bbfda8aSnia *
650bbfda8aSnia ****************************************************************
660bbfda8aSnia */
670bbfda8aSnia
680bbfda8aSnia/*
690bbfda8aSnia * Allocate an X Context for WSM stuff.
700bbfda8aSnia */
710bbfda8aSniavoid
720bbfda8aSniaInitWorkSpaceManagerContext(void)
730bbfda8aSnia{
740bbfda8aSnia	if(MapWListContext == None) {
750bbfda8aSnia		MapWListContext = XUniqueContext();
760bbfda8aSnia	}
770bbfda8aSnia}
780bbfda8aSnia
790bbfda8aSnia
80b18c2d1eSnia/**
810bbfda8aSnia * Prep up structures for WSM windows in each VS.  Called (for each
82b18c2d1eSnia * Screen) in startup after InitVirtualScreens() has setup the VS stuff
83b18c2d1eSnia * (and after config file processing).  This also retrieves info for each
84b18c2d1eSnia * vs about which workspace is active, if available (from restarting
85b18c2d1eSnia * ourself, etc).
86b18c2d1eSnia *
87b18c2d1eSnia * XXX Passed param isn't quite complete, as we call some funcs that use
88b18c2d1eSnia * global Scr...
890bbfda8aSnia */
900bbfda8aSniavoid
91b18c2d1eSniaConfigureWorkSpaceManager(ScreenInfo *scr)
920bbfda8aSnia{
93b18c2d1eSnia	WorkSpace *ws = Scr->workSpaceMgr.workSpaceList;
94b18c2d1eSnia	char *vsmapbuf, *vsmap;
95b18c2d1eSnia
96b18c2d1eSnia	// Get the workspace name that's up on this vscreen.  This is
97b18c2d1eSnia	// where the startup process actually sets what workspace we're
98b18c2d1eSnia	// [re]starting in.
99b18c2d1eSnia	vsmapbuf = CtwmGetVScreenMap(dpy, Scr->Root);
100b18c2d1eSnia	if(vsmapbuf != NULL) {
101b18c2d1eSnia		// Property is a comma-separate list of the workspaces for
102b18c2d1eSnia		// each vscreen, in magic order.  So we start by chopping off
103b18c2d1eSnia		// the first and then re-chop in the loop below.
104b18c2d1eSnia		vsmap = strtok(vsmapbuf, ",");
105b18c2d1eSnia	}
106b18c2d1eSnia	else {
107b18c2d1eSnia		vsmap = NULL;
108b18c2d1eSnia	}
109b18c2d1eSnia
110b18c2d1eSnia
111b18c2d1eSnia	// Setup each vs
112b18c2d1eSnia	for(VirtualScreen *vs = scr->vScreenList; vs != NULL; vs = vs->next) {
113b18c2d1eSnia		WorkSpace *fws;
1140bbfda8aSnia
1150bbfda8aSnia		/*
1160bbfda8aSnia		 * Make sure this is all properly initialized to nothing.  Otherwise
1170bbfda8aSnia		 * bad and undefined behavior can show up in certain cases (e.g.,
1180bbfda8aSnia		 * with no Workspaces {} defined in .ctwmrc, the only defined
1190bbfda8aSnia		 * workspace will be random memory bytes, which can causes crashes on
1200bbfda8aSnia		 * e.g.  f.menu "TwmWindows".)
1210bbfda8aSnia		 */
1220bbfda8aSnia		WorkSpaceWindow *wsw = calloc(1, sizeof(WorkSpaceWindow));
123b18c2d1eSnia		wsw->state = scr->workSpaceMgr.initialstate;
124b18c2d1eSnia
125b18c2d1eSnia		// If we have a current ws for this vs, assign it in, and
126b18c2d1eSnia		// loop onward to the ws for the next vs.  For any we don't
127b18c2d1eSnia		// have a default for, the default ws is the first one we haven't
128b18c2d1eSnia		// used yet.
129b18c2d1eSnia		if((fws = GetWorkspace(vsmap)) != NULL) {
130b18c2d1eSnia			wsw->currentwspc = fws;
131b18c2d1eSnia			vsmap = strtok(NULL, ",");
132b18c2d1eSnia		}
133b18c2d1eSnia		else {
134b18c2d1eSnia			wsw->currentwspc = ws;
135b18c2d1eSnia			if(ws) {
136b18c2d1eSnia				ws = ws->next;
137b18c2d1eSnia			}
138b18c2d1eSnia		}
139b18c2d1eSnia
1400bbfda8aSnia		vs->wsw = wsw;
1410bbfda8aSnia	}
142b18c2d1eSnia
143b18c2d1eSnia	free(vsmapbuf);
1440bbfda8aSnia}
1450bbfda8aSnia
1460bbfda8aSnia
147b18c2d1eSnia
148b18c2d1eSnia
1490bbfda8aSnia/*
1500bbfda8aSnia * Create workspace manager windows for each vscreen.  Called (for each
1510bbfda8aSnia * screen) late in startup, after the preceeding funcs have run their
1520bbfda8aSnia * course.
1530bbfda8aSnia */
1540bbfda8aSniavoid
1550bbfda8aSniaCreateWorkSpaceManager(void)
1560bbfda8aSnia{
1570bbfda8aSnia	if(! Scr->workSpaceManagerActive) {
1580bbfda8aSnia		return;
1590bbfda8aSnia	}
1600bbfda8aSnia
1610bbfda8aSnia	/* Setup basic fonts/colors/cursors */
1620bbfda8aSnia	Scr->workSpaceMgr.windowFont.basename =
1630bbfda8aSnia	        "-adobe-courier-medium-r-normal--10-100-75-75-m-60-iso8859-1";
1640bbfda8aSnia	Scr->workSpaceMgr.buttonFont = Scr->IconManagerFont;
1650bbfda8aSnia	Scr->workSpaceMgr.cp         = Scr->IconManagerC;
1660bbfda8aSnia	if(!Scr->BeNiceToColormap) {
1670bbfda8aSnia		GetShadeColors(&Scr->workSpaceMgr.cp);
1680bbfda8aSnia	}
1690bbfda8aSnia	if(handCursor == None) {
1700bbfda8aSnia		NewFontCursor(&handCursor, "top_left_arrow");
1710bbfda8aSnia	}
1720bbfda8aSnia
1730bbfda8aSnia
1740bbfda8aSnia	/*
1750bbfda8aSnia	 * Create a WSM window for each vscreen.  We don't need one for each
1760bbfda8aSnia	 * workspace (we just reuse the same one), but we do need one for
1770bbfda8aSnia	 * each vscreen (since they have to be displayed simultaneously).
1780bbfda8aSnia	 */
179b18c2d1eSnia	for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
180b18c2d1eSnia		CreateWorkSpaceManagerWindow(vs);
1810bbfda8aSnia	}
1820bbfda8aSnia
1830bbfda8aSnia
1840bbfda8aSnia	/*
1850bbfda8aSnia	 * Init background in the WSM workspace subwindow and potentially the
1860bbfda8aSnia	 * root window to the settings for the active workspace
1870bbfda8aSnia	 *
1880bbfda8aSnia	 * XXX CTAG_BGDRAW This process is also done in similar fashion
1890bbfda8aSnia	 * during CreateWorkSpaceManagerWindow(), and the two parts are done
1900bbfda8aSnia	 * split well apart during GotoWorkSpace().  The details of the
1910bbfda8aSnia	 * process should be factored out into helper functions instead of
1920bbfda8aSnia	 * being reimplemented in each place.  That will require a little
1930bbfda8aSnia	 * shuffling of code, and careful thinking on the apparent
1940bbfda8aSnia	 * differences (which seem like they may be cosmetic).  Todo.
1950bbfda8aSnia	 */
1960bbfda8aSnia	for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
1970bbfda8aSnia		WorkSpaceWindow *wsw = vs->wsw;    // Our WSW
1980bbfda8aSnia		WorkSpace *ws2 = wsw->currentwspc; // Active WS
1990bbfda8aSnia		MapSubwindow *msw = wsw->mswl[ws2->number]; // Active WS's subwin
2000bbfda8aSnia
2010bbfda8aSnia		/* Setup the background/border on the active workspace */
2020bbfda8aSnia		if(Scr->workSpaceMgr.curImage == NULL) {
2030bbfda8aSnia			if(Scr->workSpaceMgr.curPaint) {
2040bbfda8aSnia				XSetWindowBackground(dpy, msw->w, Scr->workSpaceMgr.curColors.back);
2050bbfda8aSnia			}
2060bbfda8aSnia		}
2070bbfda8aSnia		else {
2080bbfda8aSnia			XSetWindowBackgroundPixmap(dpy, msw->w, Scr->workSpaceMgr.curImage->pixmap);
2090bbfda8aSnia		}
2100bbfda8aSnia		XSetWindowBorder(dpy, msw->w, Scr->workSpaceMgr.curBorderColor);
2110bbfda8aSnia		XClearWindow(dpy, msw->w);
2120bbfda8aSnia
2130bbfda8aSnia		/* Set the root window to the color/image of that WS if we should */
2140bbfda8aSnia		if(useBackgroundInfo && ! Scr->DontPaintRootWindow) {
2150bbfda8aSnia			if(ws2->image == NULL) {
2160bbfda8aSnia				XSetWindowBackground(dpy, vs->window, ws2->backcp.back);
2170bbfda8aSnia			}
2180bbfda8aSnia			else {
2190bbfda8aSnia				XSetWindowBackgroundPixmap(dpy, vs->window, ws2->image->pixmap);
2200bbfda8aSnia			}
2210bbfda8aSnia			XClearWindow(dpy, vs->window);
2220bbfda8aSnia		}
2230bbfda8aSnia	}
2240bbfda8aSnia
2250bbfda8aSnia
2260bbfda8aSnia	/*
2270bbfda8aSnia	 * Set the property we use to store the full list of workspaces.
2280bbfda8aSnia	 *
2290bbfda8aSnia	 * XXX This isn't really part of creating the WSM windows, so doesn't
2300bbfda8aSnia	 * strictly belong here.  It does need to happen after the config
2310bbfda8aSnia	 * file parsing setup the workspaces, so couldn't go into
2320bbfda8aSnia	 * InitWorkSpaceManager().  It could probably move into
2330bbfda8aSnia	 * ConfigureWorkSpaceManager() though, or could move into a separate
2340bbfda8aSnia	 * hypotehtical ConfigureWorkSpaces() sort of thing...
2350bbfda8aSnia	 */
2360bbfda8aSnia	{
2370bbfda8aSnia		char *wrkSpcList;
2380bbfda8aSnia		int  len;
2390bbfda8aSnia
2400bbfda8aSnia		len = GetPropertyFromMask(0xFFFFFFFFu, &wrkSpcList);
2410bbfda8aSnia		XChangeProperty(dpy, Scr->Root, XA_WM_WORKSPACESLIST, XA_STRING, 8,
2420bbfda8aSnia		                PropModeReplace, (unsigned char *) wrkSpcList, len);
2430bbfda8aSnia		free(wrkSpcList);
2440bbfda8aSnia	}
2450bbfda8aSnia}
2460bbfda8aSnia
2470bbfda8aSnia
2480bbfda8aSnia/*
2490bbfda8aSnia * Put together the actual window for the workspace manager.  Called as
2500bbfda8aSnia * part of CreateWorkSpaceManager() during startup, once per vscreen
2510bbfda8aSnia * (since there's a separate window for each).
2520bbfda8aSnia */
2530bbfda8aSniastatic void
2540bbfda8aSniaCreateWorkSpaceManagerWindow(VirtualScreen *vs)
2550bbfda8aSnia{
2560bbfda8aSnia	unsigned int width, height;
2570bbfda8aSnia	TwmWindow *tmp_win;
2580bbfda8aSnia	int x, y, gravity;
2590bbfda8aSnia	/* Shortcuts */
2600bbfda8aSnia	const int vspace = Scr->workSpaceMgr.vspace;
2610bbfda8aSnia	const int hspace = Scr->workSpaceMgr.hspace;
2620bbfda8aSnia	const long count = Scr->workSpaceMgr.count;
2630bbfda8aSnia
2640bbfda8aSnia	/* No workspaces?  Nothing to do. */
2650bbfda8aSnia	if(count == 0) {
2660bbfda8aSnia		return;
2670bbfda8aSnia	}
2680bbfda8aSnia
2690bbfda8aSnia	/*
2700bbfda8aSnia	 * Work out grid.  wSM.columns will be filled if specified in
2710bbfda8aSnia	 * WorkSpaceManageGeometry, or uninitialized (0) if not.
2720bbfda8aSnia	 */
2730bbfda8aSnia	{
2740bbfda8aSnia		int lines, columns;
2750bbfda8aSnia		columns = Scr->workSpaceMgr.columns;
2760bbfda8aSnia		if(columns == 0) {
2770bbfda8aSnia			lines = 2;
2780bbfda8aSnia			columns = ((count - 1) / lines) + 1;
2790bbfda8aSnia		}
2800bbfda8aSnia		else {
2810bbfda8aSnia			lines = ((count - 1) / columns) + 1;
2820bbfda8aSnia		}
2830bbfda8aSnia		Scr->workSpaceMgr.lines   = lines;
2840bbfda8aSnia		Scr->workSpaceMgr.columns = columns;
2850bbfda8aSnia	}
2860bbfda8aSnia
2870bbfda8aSnia
2880bbfda8aSnia	/* Work out dimensions of stuff */
2890bbfda8aSnia	{
2900bbfda8aSnia		unsigned int bwidth, bheight;
2910bbfda8aSnia		unsigned short strWid;
2920bbfda8aSnia		WorkSpace *ws;
2930bbfda8aSnia		const char *geometry = Scr->workSpaceMgr.geometry;
2940bbfda8aSnia		const int lines      = Scr->workSpaceMgr.lines;
2950bbfda8aSnia		const int columns    = Scr->workSpaceMgr.columns;
2960bbfda8aSnia
2970bbfda8aSnia		/* Figure longest label */
2980bbfda8aSnia		strWid = 0;
2990bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
3000bbfda8aSnia			XRectangle inc_rect;
3010bbfda8aSnia			XRectangle logical_rect;
3020bbfda8aSnia			unsigned short wid;
3030bbfda8aSnia			const MyFont font = Scr->workSpaceMgr.buttonFont;
3040bbfda8aSnia
3050bbfda8aSnia			XmbTextExtents(font.font_set, ws->label, strlen(ws->label),
3060bbfda8aSnia			               &inc_rect, &logical_rect);
3070bbfda8aSnia			wid = logical_rect.width;
3080bbfda8aSnia			if(wid > strWid) {
3090bbfda8aSnia				strWid = wid;
3100bbfda8aSnia			}
3110bbfda8aSnia		}
3120bbfda8aSnia
3130bbfda8aSnia		/*
3140bbfda8aSnia		 * If WorkSpaceManagerGeometry is given, work from that.  Else,
3150bbfda8aSnia		 * create a workable minimum ourselves.
3160bbfda8aSnia		 * */
3170bbfda8aSnia		if(geometry != NULL) {
3180bbfda8aSnia			int mask;
3190bbfda8aSnia
3200bbfda8aSnia			/* Base button/subwindow sizes */
3210bbfda8aSnia			bwidth = strWid + 10;
3220bbfda8aSnia			bheight = 22;
3230bbfda8aSnia
3240bbfda8aSnia			/* Adjust to WSMGeometry if specified */
325b18c2d1eSnia			mask = RLayoutXParseGeometry(Scr->Layout, geometry, &x, &y, &width, &height);
3260bbfda8aSnia			if(mask & WidthValue) {
3270bbfda8aSnia				bwidth = (width - (columns * hspace)) / columns;
3280bbfda8aSnia			}
3290bbfda8aSnia			if(mask & HeightValue) {
3300bbfda8aSnia				bheight = (height - (lines * vspace)) / lines;
3310bbfda8aSnia			}
3320bbfda8aSnia
3330bbfda8aSnia			/* Size of the whole thing is based off those */
3340bbfda8aSnia			width  = columns * (bwidth  + hspace);
3350bbfda8aSnia			height = lines   * (bheight + vspace);
3360bbfda8aSnia
3370bbfda8aSnia			/*
3380bbfda8aSnia			 * If no Y given, put it at the bottom of the screen.  If one
3390bbfda8aSnia			 * is, just accept it.  If it's a negative, we have to figure
3400bbfda8aSnia			 * out where that actually is on this vscreen.
3410bbfda8aSnia			 */
3420bbfda8aSnia			if(!(mask & YValue)) {
3430bbfda8aSnia				y = 0;
3440bbfda8aSnia				mask |= YNegative;
3450bbfda8aSnia			}
3460bbfda8aSnia			if(mask & YNegative) {
3470bbfda8aSnia				y += vs->h - height;
3480bbfda8aSnia			}
3490bbfda8aSnia
3500bbfda8aSnia			/*
3510bbfda8aSnia			 * If X is given, tweak as necessary for the vscreen
3520bbfda8aSnia			 * location.  Otherwise, put it in in something like the
3530bbfda8aSnia			 * middle.
3540bbfda8aSnia			 */
3550bbfda8aSnia			if(mask & XValue) {
3560bbfda8aSnia				if(mask & XNegative) {
3570bbfda8aSnia					x += vs->w - width;
3580bbfda8aSnia					gravity = (mask & YNegative) ? SouthEastGravity : NorthEastGravity;
3590bbfda8aSnia				}
3600bbfda8aSnia				else {
3610bbfda8aSnia					gravity = (mask & YNegative) ? SouthWestGravity : NorthWestGravity;
3620bbfda8aSnia				}
3630bbfda8aSnia			}
3640bbfda8aSnia			else {
3650bbfda8aSnia				x = (vs->w - width) / 2;
3660bbfda8aSnia				gravity = (mask & YValue) ? ((mask & YNegative) ?
3670bbfda8aSnia				                             SouthGravity : NorthGravity) : SouthGravity;
3680bbfda8aSnia			}
3690bbfda8aSnia		}
3700bbfda8aSnia		else {
3710bbfda8aSnia			/* No geom specified, come up with one */
3720bbfda8aSnia			bwidth  = strWid + 2 * Scr->WMgrButtonShadowDepth + 6;
3730bbfda8aSnia			bheight = 22;
3740bbfda8aSnia			width   = columns * (bwidth  + hspace);
3750bbfda8aSnia			height  = lines   * (bheight + vspace);
3760bbfda8aSnia			x       = (vs->w - width) / 2;
3770bbfda8aSnia			y       = vs->h - height;
3780bbfda8aSnia			gravity = NorthWestGravity;
3790bbfda8aSnia		}
3800bbfda8aSnia	}
3810bbfda8aSnia
3820bbfda8aSnia	/* Set w/h to dummy values; ResizeWorkSpaceManager() writes real ones */
3830bbfda8aSnia	vs->wsw->width  = 1;
3840bbfda8aSnia	vs->wsw->height = 1;
3850bbfda8aSnia
3860bbfda8aSnia	/* Allocate structs for map/button subwindows */
3870bbfda8aSnia	vs->wsw->bswl = calloc(count, sizeof(ButtonSubwindow *));
3880bbfda8aSnia	vs->wsw->mswl = calloc(count, sizeof(MapSubwindow *));
3890bbfda8aSnia
3900bbfda8aSnia	/* Create main window */
3910bbfda8aSnia	vs->wsw->w = XCreateSimpleWindow(dpy, Scr->Root, x, y, width, height, 0,
3920bbfda8aSnia	                                 Scr->Black, Scr->workSpaceMgr.cp.back);
3930bbfda8aSnia
3940bbfda8aSnia
3950bbfda8aSnia	/*
3960bbfda8aSnia	 * Create the map and button subwindows for each workspace
3970bbfda8aSnia	 */
3980bbfda8aSnia	{
3990bbfda8aSnia		WorkSpace *ws;
4000bbfda8aSnia
4010bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
4020bbfda8aSnia			MapSubwindow *msw;
4030bbfda8aSnia			ButtonSubwindow *bsw;
4040bbfda8aSnia			const int Dummy = 1;
4050bbfda8aSnia			const unsigned long border = Scr->workSpaceMgr.defBorderColor;
4060bbfda8aSnia
4070bbfda8aSnia			/* Alloc structs */
4080bbfda8aSnia			vs->wsw->bswl[ws->number] = bsw
4090bbfda8aSnia			                            = calloc(1, sizeof(ButtonSubwindow));
4100bbfda8aSnia			vs->wsw->mswl[ws->number] = msw = calloc(1, sizeof(MapSubwindow));
4110bbfda8aSnia
4120bbfda8aSnia			/*
4130bbfda8aSnia			 * Create windows for button/map.  ResizeWorkSpaceManager()
4140bbfda8aSnia			 * sets the real sizes and positions, so we dummy 'em.
4150bbfda8aSnia			 */
4160bbfda8aSnia			bsw->w = XCreateSimpleWindow(dpy, vs->wsw->w,
4170bbfda8aSnia			                             Dummy, Dummy, Dummy, Dummy,
4180bbfda8aSnia			                             0, Scr->Black, ws->cp.back);
4190bbfda8aSnia
4200bbfda8aSnia			msw->w = XCreateSimpleWindow(dpy, vs->wsw->w,
4210bbfda8aSnia			                             Dummy, Dummy, Dummy, Dummy,
4220bbfda8aSnia			                             1, border, ws->cp.back);
4230bbfda8aSnia
4240bbfda8aSnia			/* Map whichever is up by default */
4250bbfda8aSnia			if(vs->wsw->state == WMS_buttons) {
4260bbfda8aSnia				XMapWindow(dpy, bsw->w);
4270bbfda8aSnia			}
4280bbfda8aSnia			else {
4290bbfda8aSnia				XMapWindow(dpy, msw->w);
4300bbfda8aSnia			}
4310bbfda8aSnia
4320bbfda8aSnia			/* Setup background on map-state window */
4330bbfda8aSnia			/* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() */
4340bbfda8aSnia			if(useBackgroundInfo) {
4350bbfda8aSnia				if(ws->image == NULL || Scr->NoImagesInWorkSpaceManager) {
4360bbfda8aSnia					XSetWindowBackground(dpy, msw->w, ws->backcp.back);
4370bbfda8aSnia				}
4380bbfda8aSnia				else {
4390bbfda8aSnia					XSetWindowBackgroundPixmap(dpy, msw->w, ws->image->pixmap);
4400bbfda8aSnia				}
4410bbfda8aSnia			}
4420bbfda8aSnia			else {
4430bbfda8aSnia				if(Scr->workSpaceMgr.defImage == NULL || Scr->NoImagesInWorkSpaceManager) {
4440bbfda8aSnia					XSetWindowBackground(dpy, msw->w, Scr->workSpaceMgr.defColors.back);
4450bbfda8aSnia				}
4460bbfda8aSnia				else {
4470bbfda8aSnia					XSetWindowBackgroundPixmap(dpy, msw->w, Scr->workSpaceMgr.defImage->pixmap);
4480bbfda8aSnia				}
4490bbfda8aSnia			}
4500bbfda8aSnia
4510bbfda8aSnia			/*
4520bbfda8aSnia			 * Clear out button subwin; PaintWorkSpaceManager() fills it
4530bbfda8aSnia			 * in.  Is this really necessary?
4540bbfda8aSnia			 */
4550bbfda8aSnia			XClearWindow(dpy, bsw->w);
4560bbfda8aSnia		}
4570bbfda8aSnia	}
4580bbfda8aSnia
4590bbfda8aSnia
4600bbfda8aSnia	/* Set WM properties */
4610bbfda8aSnia	{
4620bbfda8aSnia		XSizeHints sizehints;
4630bbfda8aSnia		XWMHints   wmhints;
4640bbfda8aSnia		const int lines   = Scr->workSpaceMgr.lines;
4650bbfda8aSnia		const int columns = Scr->workSpaceMgr.columns;
4660bbfda8aSnia		const char *name      = Scr->workSpaceMgr.name;
4670bbfda8aSnia		const char *icon_name = Scr->workSpaceMgr.icon_name;
4680bbfda8aSnia
4690bbfda8aSnia		sizehints.flags       = USPosition | PBaseSize | PMinSize | PResizeInc
4700bbfda8aSnia		                        | PWinGravity;
4710bbfda8aSnia		sizehints.x           = x;
4720bbfda8aSnia		sizehints.y           = y;
4730bbfda8aSnia		sizehints.base_width  = columns * hspace;
4740bbfda8aSnia		sizehints.base_height = lines   * vspace;
4750bbfda8aSnia		sizehints.width_inc   = columns;
4760bbfda8aSnia		sizehints.height_inc  = lines;
4770bbfda8aSnia		sizehints.min_width   = columns  * (hspace + 2);
4780bbfda8aSnia		sizehints.min_height  = lines    * (vspace + 2);
4790bbfda8aSnia		sizehints.win_gravity = gravity;
4800bbfda8aSnia
4810bbfda8aSnia		wmhints.flags         = InputHint | StateHint;
4820bbfda8aSnia		wmhints.input         = True;
4830bbfda8aSnia		wmhints.initial_state = NormalState;
4840bbfda8aSnia
4850bbfda8aSnia		XmbSetWMProperties(dpy, vs->wsw->w, name, icon_name, NULL, 0,
4860bbfda8aSnia		                   &sizehints, &wmhints, NULL);
4870bbfda8aSnia	}
4880bbfda8aSnia
4890bbfda8aSnia
4900bbfda8aSnia	/* Create our TwmWindow wrapping around it */
4910bbfda8aSnia	tmp_win = AddWindow(vs->wsw->w, AWT_WORKSPACE_MANAGER,
4920bbfda8aSnia	                    Scr->iconmgr, vs);
4930bbfda8aSnia	if(! tmp_win) {
4940bbfda8aSnia		fprintf(stderr, "cannot create workspace manager window, exiting...\n");
4950bbfda8aSnia		exit(1);
4960bbfda8aSnia	}
4970bbfda8aSnia	tmp_win->occupation = fullOccupation;
4980bbfda8aSnia	tmp_win->attr.width = width;
4990bbfda8aSnia	tmp_win->attr.height = height;
5000bbfda8aSnia	vs->wsw->twm_win = tmp_win;
5010bbfda8aSnia
5020bbfda8aSnia
5030bbfda8aSnia	/* Do the figuring to size and internal-layout it */
5040bbfda8aSnia	ResizeWorkSpaceManager(vs, tmp_win);
5050bbfda8aSnia
5060bbfda8aSnia
5070bbfda8aSnia	/* Setup cursor/gravity and listen for events */
5080bbfda8aSnia	{
5090bbfda8aSnia		XWindowAttributes wattr;
5100bbfda8aSnia		XSetWindowAttributes attr;
5110bbfda8aSnia		unsigned long attrmask;
5120bbfda8aSnia
5130bbfda8aSnia		attr.cursor = Scr->ButtonCursor;
5140bbfda8aSnia		attr.win_gravity = gravity;
5150bbfda8aSnia		attrmask = CWCursor | CWWinGravity;
5160bbfda8aSnia		XChangeWindowAttributes(dpy, vs->wsw->w, attrmask, &attr);
5170bbfda8aSnia
5180bbfda8aSnia		XGetWindowAttributes(dpy, vs->wsw->w, &wattr);
5190bbfda8aSnia		attrmask = wattr.your_event_mask | KeyPressMask | KeyReleaseMask
5200bbfda8aSnia		           | ExposureMask;
5210bbfda8aSnia		XSelectInput(dpy, vs->wsw->w, attrmask);
5220bbfda8aSnia	}
5230bbfda8aSnia
5240bbfda8aSnia
5250bbfda8aSnia	/*
5260bbfda8aSnia	 * Mark the buttons as listening to click and exposure events, and
5270bbfda8aSnia	 * stash away some pointers in contexts.  We stash the overall WSM
5280bbfda8aSnia	 * window in TwmContext, which means that when an event looks up the
5290bbfda8aSnia	 * window, it finds the WSM rather than the subwindow, and then falls
5300bbfda8aSnia	 * into the WMgrHandle*Event()'s, which then dig down into the event
5310bbfda8aSnia	 * to find where it happened in there.
5320bbfda8aSnia	 *
5330bbfda8aSnia	 * The map window doesn't listen to expose events; it's just empty
5340bbfda8aSnia	 * and background colored.  The individual subwindows in the map
5350bbfda8aSnia	 * listen for exposes for drawing themselves.
5360bbfda8aSnia	 *
5370bbfda8aSnia	 * Dragging windows around to move or re-occupy in the map window
5380bbfda8aSnia	 * does rely on motion events, but we don't listen for them here.
5390bbfda8aSnia	 * That happens in WMgrHandleButtonEvent() after getting the initial
5400bbfda8aSnia	 * click.  It changes the listen and runs through the action
5410bbfda8aSnia	 * internally; those motions never run through our main event loop.
5420bbfda8aSnia	 */
5430bbfda8aSnia	for(WorkSpace *ws = Scr->workSpaceMgr.workSpaceList; ws != NULL;
5440bbfda8aSnia	                ws = ws->next) {
5450bbfda8aSnia		Window buttonw = vs->wsw->bswl[ws->number]->w;
5460bbfda8aSnia		Window mapsubw = vs->wsw->mswl[ws->number]->w;
5470bbfda8aSnia
5480bbfda8aSnia		XSelectInput(dpy, buttonw, ButtonPressMask | ButtonReleaseMask
5490bbfda8aSnia		             | ExposureMask);
5500bbfda8aSnia		XSaveContext(dpy, buttonw, TwmContext, (XPointer) tmp_win);
5510bbfda8aSnia		XSaveContext(dpy, buttonw, ScreenContext, (XPointer) Scr);
5520bbfda8aSnia
5530bbfda8aSnia		XSelectInput(dpy, mapsubw, ButtonPressMask | ButtonReleaseMask);
5540bbfda8aSnia		XSaveContext(dpy, mapsubw, TwmContext, (XPointer) tmp_win);
5550bbfda8aSnia		XSaveContext(dpy, mapsubw, ScreenContext, (XPointer) Scr);
5560bbfda8aSnia	}
5570bbfda8aSnia
5580bbfda8aSnia
5590bbfda8aSnia	/* Set WM_STATE prop */
5600bbfda8aSnia	SetMapStateProp(tmp_win, WithdrawnState);
5610bbfda8aSnia
5620bbfda8aSnia
5630bbfda8aSnia	/* Setup root window if necessary */
5640bbfda8aSnia	/* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() */
5650bbfda8aSnia	if(useBackgroundInfo && ! Scr->DontPaintRootWindow) {
5660bbfda8aSnia		WorkSpace *ws = Scr->workSpaceMgr.workSpaceList;
5670bbfda8aSnia		if(ws->image == NULL) {
5680bbfda8aSnia			XSetWindowBackground(dpy, Scr->Root, ws->backcp.back);
5690bbfda8aSnia		}
5700bbfda8aSnia		else {
5710bbfda8aSnia			XSetWindowBackgroundPixmap(dpy, Scr->Root, ws->image->pixmap);
5720bbfda8aSnia		}
5730bbfda8aSnia		XClearWindow(dpy, Scr->Root);
5740bbfda8aSnia	}
5750bbfda8aSnia
5760bbfda8aSnia
5770bbfda8aSnia	/*
5780bbfda8aSnia	 * Don't have to PaintWorkSpaceManager(vs) here, because
5790bbfda8aSnia	 * ResizeWorkSpaceManager() already called it for us.
5800bbfda8aSnia	 */
5810bbfda8aSnia}
5820bbfda8aSnia
5830bbfda8aSnia
5840bbfda8aSnia/*
5850bbfda8aSnia * Size and layout a WSM.  Mostly an internal bit in the process of
5860bbfda8aSnia * setting it up.
5870bbfda8aSnia */
5880bbfda8aSniastatic void
5890bbfda8aSniaResizeWorkSpaceManager(VirtualScreen *vs, TwmWindow *win)
5900bbfda8aSnia{
5910bbfda8aSnia	WorkSpace *ws;
5920bbfda8aSnia	int       i, j;
5930bbfda8aSnia	/* Lots of shortcuts to ease reading */
5940bbfda8aSnia	const int neww    = win->attr.width;
5950bbfda8aSnia	const int newh    = win->attr.height;
5960bbfda8aSnia	const int hspace  = Scr->workSpaceMgr.hspace;
5970bbfda8aSnia	const int vspace  = Scr->workSpaceMgr.vspace;
5980bbfda8aSnia	const int lines   = Scr->workSpaceMgr.lines;
5990bbfda8aSnia	const int columns = Scr->workSpaceMgr.columns;
6000bbfda8aSnia	const int bwidth  = (neww - (columns * hspace)) / columns;
6010bbfda8aSnia	const int bheight = (newh - (lines   * vspace)) / lines;
6020bbfda8aSnia	const int wwidth  = neww / columns;
6030bbfda8aSnia	const int wheight = newh / lines;
6040bbfda8aSnia	const float wf    = (float)(wwidth  - 2) / (float) vs->w;
6050bbfda8aSnia	const float hf    = (float)(wheight - 2) / (float) vs->h;
6060bbfda8aSnia
6070bbfda8aSnia	/* If nothing's changed since our last run, there's nothing to change */
6080bbfda8aSnia	if(neww == vs->wsw->width && newh == vs->wsw->height) {
6090bbfda8aSnia		return;
6100bbfda8aSnia	}
6110bbfda8aSnia
6120bbfda8aSnia	/* Set the new overall vals */
6130bbfda8aSnia	vs->wsw->bwidth  = bwidth;
6140bbfda8aSnia	vs->wsw->bheight = bheight;
6150bbfda8aSnia	vs->wsw->width   = neww;
6160bbfda8aSnia	vs->wsw->height  = newh;
6170bbfda8aSnia	vs->wsw->wwidth  = wwidth;
6180bbfda8aSnia	vs->wsw->wheight = wheight;
6190bbfda8aSnia
6200bbfda8aSnia	/* Iterate over the WS's */
6210bbfda8aSnia	i = 0;
6220bbfda8aSnia	j = 0;
6230bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
6240bbfda8aSnia		MapSubwindow    *msw = vs->wsw->mswl[ws->number];
6250bbfda8aSnia		ButtonSubwindow *bsw = vs->wsw->bswl[ws->number];
6260bbfda8aSnia
6270bbfda8aSnia		/* Move button window to its place in the grid and size appropriately */
6280bbfda8aSnia		XMoveResizeWindow(dpy, bsw->w,
6290bbfda8aSnia		                  i * (bwidth  + hspace) + (hspace / 2),
6300bbfda8aSnia		                  j * (bheight + vspace) + (vspace / 2),
6310bbfda8aSnia		                  bwidth, bheight);
6320bbfda8aSnia
6330bbfda8aSnia		/* Move the map window as well */
6340bbfda8aSnia		msw->x = i * wwidth;
6350bbfda8aSnia		msw->y = j * wheight;
6360bbfda8aSnia		XMoveResizeWindow(dpy, msw->w, msw->x, msw->y, wwidth - 2, wheight - 2);
6370bbfda8aSnia
6380bbfda8aSnia		/*
6390bbfda8aSnia		 * Redo interior sizing and placement of all the windows in the
6400bbfda8aSnia		 * WS in the map window
6410bbfda8aSnia		 */
6420bbfda8aSnia		for(WinList *wl = msw->wl; wl != NULL; wl = wl->next) {
6430bbfda8aSnia			TwmWindow *tmp_win = wl->twm_win;
6440bbfda8aSnia			wl->x      = (int)(tmp_win->frame_x * wf);
6450bbfda8aSnia			wl->y      = (int)(tmp_win->frame_y * hf);
6460bbfda8aSnia			wl->width  = (unsigned int)((tmp_win->frame_width  * wf) + 0.5);
6470bbfda8aSnia			wl->height = (unsigned int)((tmp_win->frame_height * hf) + 0.5);
6480bbfda8aSnia			XMoveResizeWindow(dpy, wl->w, wl->x, wl->y, wl->width, wl->height);
6490bbfda8aSnia		}
6500bbfda8aSnia
6510bbfda8aSnia		/* And around to the next WS */
6520bbfda8aSnia		i++;
6530bbfda8aSnia		if(i == columns) {
6540bbfda8aSnia			i = 0;
6550bbfda8aSnia			j++;
6560bbfda8aSnia		};
6570bbfda8aSnia	}
6580bbfda8aSnia
6590bbfda8aSnia
6600bbfda8aSnia	/* Draw it */
6610bbfda8aSnia	PaintWorkSpaceManager(vs);
6620bbfda8aSnia}
6630bbfda8aSnia
6640bbfda8aSnia
6650bbfda8aSnia/*
6660bbfda8aSnia * Draw up the button-state pieces of a WSM window.
6670bbfda8aSnia *
6680bbfda8aSnia * Note: this is currently stubbed out and does nothing.  Historically
6690bbfda8aSnia * it's been called during startup when the WSM window is put together,
6700bbfda8aSnia * and when the screen is unmasked.  However, the only apparent result is
6710bbfda8aSnia * that the border and buttons get drawn a little earlier; they already
6720bbfda8aSnia * get expose events that get picked up when we start the event loop.
6730bbfda8aSnia *
6740bbfda8aSnia * If we don't find any reason to reinstate it, we should remove this in
6750bbfda8aSnia * the future.
6760bbfda8aSnia */
6770bbfda8aSniavoid
6780bbfda8aSniaPaintWorkSpaceManager(VirtualScreen *vs)
6790bbfda8aSnia{
6800bbfda8aSnia	WorkSpace *ws;
6810bbfda8aSnia
6820bbfda8aSnia	/* x-ref header comment */
6830bbfda8aSnia	return;
6840bbfda8aSnia
6850bbfda8aSnia	PaintWorkSpaceManagerBorder(vs);
6860bbfda8aSnia
6870bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
6880bbfda8aSnia		Window buttonw = vs->wsw->bswl[ws->number]->w;
6890bbfda8aSnia		ButtonState bs = (ws == vs->wsw->currentwspc) ? on : off;
6900bbfda8aSnia
6910bbfda8aSnia		PaintWsButton(WSPCWINDOW, vs, buttonw, ws->label, ws->cp, bs);
6920bbfda8aSnia	}
6930bbfda8aSnia}
6940bbfda8aSnia
6950bbfda8aSnia
6960bbfda8aSnia/*
6970bbfda8aSnia * Border around the WSM
6980bbfda8aSnia */
6990bbfda8aSniastatic void
7000bbfda8aSniaPaintWorkSpaceManagerBorder(VirtualScreen *vs)
7010bbfda8aSnia{
7020bbfda8aSnia	int width, height;
7030bbfda8aSnia
7040bbfda8aSnia	width  = vs->wsw->width;
7050bbfda8aSnia	height = vs->wsw->height;
7060bbfda8aSnia	Draw3DBorder(vs->wsw->w, 0, 0, width, height, 2, Scr->workSpaceMgr.cp, off,
7070bbfda8aSnia	             true, false);
7080bbfda8aSnia}
7090bbfda8aSnia
7100bbfda8aSnia
7110bbfda8aSnia/*
7120bbfda8aSnia * Draw a workspace manager window on expose.  X-ref comment on
7130bbfda8aSnia * PaintWorkSpaceManager().
7140bbfda8aSnia */
7150bbfda8aSniavoid
7160bbfda8aSniaWMgrHandleExposeEvent(VirtualScreen *vs, XEvent *event)
7170bbfda8aSnia{
7180bbfda8aSnia	if(vs->wsw->state == WMS_buttons) {
7190bbfda8aSnia		Window buttonw;
7200bbfda8aSnia		WorkSpace *ws;
7210bbfda8aSnia
7220bbfda8aSnia		/* Find the button we're exposing */
7230bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
7240bbfda8aSnia			buttonw = vs->wsw->bswl[ws->number]->w;
7250bbfda8aSnia			if(event->xexpose.window == buttonw) {
7260bbfda8aSnia				break;
7270bbfda8aSnia			}
7280bbfda8aSnia		}
7290bbfda8aSnia
7300bbfda8aSnia		/* If none, just paint the border.  Else paint the button. */
7310bbfda8aSnia		if(ws == NULL) {
7320bbfda8aSnia			PaintWorkSpaceManagerBorder(vs);
7330bbfda8aSnia		}
7340bbfda8aSnia		else {
7350bbfda8aSnia			ButtonState bs = (ws == vs->wsw->currentwspc) ? on : off;
7360bbfda8aSnia			PaintWsButton(WSPCWINDOW, vs, buttonw, ws->label, ws->cp, bs);
7370bbfda8aSnia		}
7380bbfda8aSnia	}
7390bbfda8aSnia	else {
7400bbfda8aSnia		WinList *wl;
7410bbfda8aSnia
7420bbfda8aSnia		/*
7430bbfda8aSnia		 * This is presumably exposing some individual window in the WS
7440bbfda8aSnia		 * subwindow; find it from the stashed context on the window, and
7450bbfda8aSnia		 * redraw it.
7460bbfda8aSnia		 */
7470bbfda8aSnia		if(XFindContext(dpy, event->xexpose.window, MapWListContext,
7480bbfda8aSnia		                (XPointer *) &wl) == XCNOENT) {
7490bbfda8aSnia			return;
7500bbfda8aSnia		}
7510bbfda8aSnia		if(wl && wl->twm_win && wl->twm_win->mapped) {
7520bbfda8aSnia			WMapRedrawName(vs, wl);
7530bbfda8aSnia		}
7540bbfda8aSnia	}
7550bbfda8aSnia}
7560bbfda8aSnia
7570bbfda8aSnia
7580bbfda8aSnia
7590bbfda8aSnia/*
7600bbfda8aSnia * Moving the WSM between button and map state
7610bbfda8aSnia */
7620bbfda8aSniavoid
7630bbfda8aSniaWMgrToggleState(VirtualScreen *vs)
7640bbfda8aSnia{
7650bbfda8aSnia	if(vs->wsw->state == WMS_buttons) {
7660bbfda8aSnia		WMgrSetMapState(vs);
7670bbfda8aSnia	}
7680bbfda8aSnia	else {
7690bbfda8aSnia		WMgrSetButtonsState(vs);
7700bbfda8aSnia	}
7710bbfda8aSnia}
7720bbfda8aSnia
7730bbfda8aSniavoid
7740bbfda8aSniaWMgrSetMapState(VirtualScreen *vs)
7750bbfda8aSnia{
7760bbfda8aSnia	WorkSpace *ws;
7770bbfda8aSnia
7780bbfda8aSnia	if(vs->wsw->state == WMS_map) {
7790bbfda8aSnia		return;
7800bbfda8aSnia	}
7810bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
7820bbfda8aSnia		XUnmapWindow(dpy, vs->wsw->bswl [ws->number]->w);
7830bbfda8aSnia		XMapWindow(dpy, vs->wsw->mswl [ws->number]->w);
7840bbfda8aSnia	}
7850bbfda8aSnia	vs->wsw->state = WMS_map;
7860bbfda8aSnia	MaybeAnimate = true;
7870bbfda8aSnia}
7880bbfda8aSnia
7890bbfda8aSniavoid
7900bbfda8aSniaWMgrSetButtonsState(VirtualScreen *vs)
7910bbfda8aSnia{
7920bbfda8aSnia	WorkSpace *ws;
7930bbfda8aSnia
7940bbfda8aSnia	if(vs->wsw->state == WMS_buttons) {
7950bbfda8aSnia		return;
7960bbfda8aSnia	}
7970bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
7980bbfda8aSnia		XUnmapWindow(dpy, vs->wsw->mswl [ws->number]->w);
7990bbfda8aSnia		XMapWindow(dpy, vs->wsw->bswl [ws->number]->w);
8000bbfda8aSnia	}
8010bbfda8aSnia	vs->wsw->state = WMS_buttons;
8020bbfda8aSnia}
8030bbfda8aSnia
8040bbfda8aSnia
8050bbfda8aSnia
8060bbfda8aSnia
8070bbfda8aSnia/*
8080bbfda8aSnia ****************************************************************
8090bbfda8aSnia *
8100bbfda8aSnia * Handlers for mouse/key actions in the WSM
8110bbfda8aSnia *
8120bbfda8aSnia ****************************************************************
8130bbfda8aSnia */
8140bbfda8aSnia
8150bbfda8aSnia/*
8160bbfda8aSnia * Key press/release events in the WSM.  A major use (and only for
8170bbfda8aSnia * release) is the Ctrl-key switching between map and button state.  The
8180bbfda8aSnia * other use is on-the-fly renaming of workspaces by typing in the
8190bbfda8aSnia * button-state WSM.
8200bbfda8aSnia */
8210bbfda8aSniavoid
8220bbfda8aSniaWMgrHandleKeyReleaseEvent(VirtualScreen *vs, XEvent *event)
8230bbfda8aSnia{
8240bbfda8aSnia	KeySym keysym;
8250bbfda8aSnia
8260bbfda8aSnia	keysym = XLookupKeysym((XKeyEvent *) event, 0);
8270bbfda8aSnia	if(! keysym) {
8280bbfda8aSnia		return;
8290bbfda8aSnia	}
8300bbfda8aSnia	if(keysym == XK_Control_L || keysym == XK_Control_R) {
8310bbfda8aSnia		/* DontToggleWorkSpaceManagerState added 20040607 by dl*/
8320bbfda8aSnia		if(!Scr->DontToggleWorkspaceManagerState) {
8330bbfda8aSnia			WMgrToggleState(vs);
8340bbfda8aSnia		}
8350bbfda8aSnia		return;
8360bbfda8aSnia	}
8370bbfda8aSnia}
8380bbfda8aSnia
8390bbfda8aSniavoid
8400bbfda8aSniaWMgrHandleKeyPressEvent(VirtualScreen *vs, XEvent *event)
8410bbfda8aSnia{
8420bbfda8aSnia	WorkSpace *ws;
8430bbfda8aSnia
8440bbfda8aSnia	/* Check if we're using Control to toggle the state */
8450bbfda8aSnia	{
8460bbfda8aSnia		KeySym keysym = XLookupKeysym((XKeyEvent *) event, 0);
8470bbfda8aSnia		if(! keysym) {
8480bbfda8aSnia			return;
8490bbfda8aSnia		}
8500bbfda8aSnia		if(keysym == XK_Control_L || keysym == XK_Control_R) {
8510bbfda8aSnia			/* DontToggleWorkSpaceManagerState added 20040607 by dl*/
8520bbfda8aSnia			if(!Scr->DontToggleWorkspaceManagerState) {
8530bbfda8aSnia				WMgrToggleState(vs);
8540bbfda8aSnia			}
8550bbfda8aSnia			return;
8560bbfda8aSnia		}
8570bbfda8aSnia	}
8580bbfda8aSnia
8590bbfda8aSnia	/* Otherwise, key presses do nothing in map state */
8600bbfda8aSnia	if(vs->wsw->state == WMS_map) {
8610bbfda8aSnia		return;
8620bbfda8aSnia	}
8630bbfda8aSnia
8640bbfda8aSnia	/*
8650bbfda8aSnia	 * If we're typing in a button-state WSM, and the mouse is on one of
8660bbfda8aSnia	 * the buttons, that means we're changing the name, so do that dance.
8670bbfda8aSnia	 */
8680bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
8690bbfda8aSnia		if(vs->wsw->bswl[ws->number]->w == event->xkey.subwindow) {
8700bbfda8aSnia			break;
8710bbfda8aSnia		}
8720bbfda8aSnia	}
8730bbfda8aSnia	if(ws == NULL) {
8740bbfda8aSnia		/* Not on a button, nothing to do */
8750bbfda8aSnia		return;
8760bbfda8aSnia	}
8770bbfda8aSnia
8780bbfda8aSnia
8790bbfda8aSnia	/*
8800bbfda8aSnia	 * Edit the label.
8810bbfda8aSnia	 */
8820bbfda8aSnia	{
8830bbfda8aSnia		int    nkeys;
8840bbfda8aSnia		char   keys[16];
8850bbfda8aSnia		size_t nlen;
8860bbfda8aSnia		char   *newname;
8870bbfda8aSnia
8880bbfda8aSnia		/* Look up what keystrokes are queued.  Arbitrary buf size */
8890bbfda8aSnia		nkeys = XLookupString(&(event->xkey), keys, 16, NULL, NULL);
8900bbfda8aSnia
8910bbfda8aSnia		/* Label length can't grow to more than cur+nkeys */
8920bbfda8aSnia		nlen = strlen(ws->label);
8930bbfda8aSnia		newname = malloc(nlen + nkeys + 1);
8940bbfda8aSnia		strcpy(newname, ws->label);
8950bbfda8aSnia
8960bbfda8aSnia		/* Iterate over the passed keystrokes */
8970bbfda8aSnia		for(int i = 0 ; i < nkeys ; i++) {
8980bbfda8aSnia			unsigned char k = keys[i];
8990bbfda8aSnia
9000bbfda8aSnia			if(isprint(k)) {
9010bbfda8aSnia				/* Printable chars append to the string */
9020bbfda8aSnia				newname[nlen++] = k;
9030bbfda8aSnia			}
9040bbfda8aSnia			else if((k == 127) || (k == 8)) {
9050bbfda8aSnia				/*
9060bbfda8aSnia				 * DEL or BS back up a char.
9070bbfda8aSnia				 *
9080bbfda8aSnia				 * XXX Would it be more generally correct to do this via
9090bbfda8aSnia				 * keysyms, in the face of changed keyboard mappings or
9100bbfda8aSnia				 * significantly differing locales?
9110bbfda8aSnia				 */
9120bbfda8aSnia				if(nlen != 0) {
9130bbfda8aSnia					nlen--;
9140bbfda8aSnia				}
9150bbfda8aSnia			}
9160bbfda8aSnia			else {
9170bbfda8aSnia				/* Any other char stops the process dead */
9180bbfda8aSnia				break;
9190bbfda8aSnia			}
9200bbfda8aSnia		}
9210bbfda8aSnia		/* Now ends where it ends */
9220bbfda8aSnia		newname[nlen] = '\0';
9230bbfda8aSnia
9240bbfda8aSnia		/* Swap it in */
9250bbfda8aSnia		free(ws->label);
9260bbfda8aSnia		ws->label = newname;
9270bbfda8aSnia	}
9280bbfda8aSnia
9290bbfda8aSnia
9300bbfda8aSnia	/* Redraw the button with the new label */
9310bbfda8aSnia	{
9320bbfda8aSnia		ButtonState bs = (ws == vs->wsw->currentwspc) ? on : off;
9330bbfda8aSnia
9340bbfda8aSnia		PaintWsButton(WSPCWINDOW, vs, vs->wsw->bswl[ws->number]->w, ws->label,
9350bbfda8aSnia		              ws->cp, bs);
9360bbfda8aSnia	}
9370bbfda8aSnia}
9380bbfda8aSnia
9390bbfda8aSnia
9400bbfda8aSnia/*
9410bbfda8aSnia * Mouse clicking in WSM.  Gets called on button press (not release).  In
9420bbfda8aSnia * the simple case, that's just switching workspaces.  In the more
9430bbfda8aSnia * complex, it's changing window occupation in various different ways, or
9440bbfda8aSnia * even moving windows.  When that's happening, it internally hijacks
9450bbfda8aSnia * button/motion/exposure events and implements them for the moving, with
9460bbfda8aSnia * magic escapes if it gets them for something else.  Ew.
9470bbfda8aSnia */
9480bbfda8aSniavoid
9490bbfda8aSniaWMgrHandleButtonEvent(VirtualScreen *vs, XEvent *event)
9500bbfda8aSnia{
9510bbfda8aSnia	WorkSpace    *oldws, *newws;
9520bbfda8aSnia	WinList      *wl;
9530bbfda8aSnia	TwmWindow    *win;
9540bbfda8aSnia	unsigned int W0, H0;
9550bbfda8aSnia	XEvent       lastev;
9560bbfda8aSnia	Window       w = 0;
9570bbfda8aSnia	Position     newX = 0, newY = 0, winX = 0, winY = 0;
9580bbfda8aSnia	bool         alreadyvivible, realmovemode;
9590bbfda8aSnia	const WorkSpaceWindow *mw = vs->wsw;
9600bbfda8aSnia
9610bbfda8aSnia	/* Shortcuts into the event */
9620bbfda8aSnia	const Window parent = event->xbutton.window;    // Map/button for WS
9630bbfda8aSnia	const Window sw     = event->xbutton.subwindow; // Map mini-win
9640bbfda8aSnia	const Time etime    = event->xbutton.time;
9650bbfda8aSnia	const unsigned int button   = event->xbutton.button;
9660bbfda8aSnia	const unsigned int modifier = event->xbutton.state;
9670bbfda8aSnia
9680bbfda8aSnia
9690bbfda8aSnia	/* If we're in button state, we're just clicking to change */
9700bbfda8aSnia	if(vs->wsw->state == WMS_buttons) {
9710bbfda8aSnia		WorkSpace *ws;
9720bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
9730bbfda8aSnia			if(vs->wsw->bswl[ws->number]->w == parent) {
9740bbfda8aSnia				GotoWorkSpace(vs, ws);
9750bbfda8aSnia				break;
9760bbfda8aSnia			}
9770bbfda8aSnia		}
9780bbfda8aSnia		return;
9790bbfda8aSnia	}
9800bbfda8aSnia
9810bbfda8aSnia	/*
9820bbfda8aSnia	 * If we get this far, we're in map state, where things are more
9830bbfda8aSnia	 * complicated.  A simple click/release means change here too, but
9840bbfda8aSnia	 * there's also the possibility of dragging a subwindow around to
9850bbfda8aSnia	 * change its window's occupation.
9860bbfda8aSnia	 */
9870bbfda8aSnia
9880bbfda8aSnia	/* Find what workspace we're clicking in */
9890bbfda8aSnia	for(oldws = Scr->workSpaceMgr.workSpaceList ; oldws != NULL ;
9900bbfda8aSnia	                oldws = oldws->next) {
9910bbfda8aSnia		if(vs->wsw->mswl[oldws->number]->w == parent) {
9920bbfda8aSnia			break;
9930bbfda8aSnia		}
9940bbfda8aSnia	}
9950bbfda8aSnia	if(oldws == NULL) {
9960bbfda8aSnia		/* None?  We're done here. */
9970bbfda8aSnia		return;
9980bbfda8aSnia	}
9990bbfda8aSnia
10000bbfda8aSnia	/*
10010bbfda8aSnia	 * If clicked in the workspace but outside a window, we can only be
10020bbfda8aSnia	 * switching workspaces.  So just do that, and we're done.
10030bbfda8aSnia	 */
10040bbfda8aSnia	if(sw == (Window) 0) {
10050bbfda8aSnia		GotoWorkSpace(vs, oldws);
10060bbfda8aSnia		return;
10070bbfda8aSnia	}
10080bbfda8aSnia
10090bbfda8aSnia	/* Use the context to find the winlist entry for this window */
10100bbfda8aSnia	if(XFindContext(dpy, sw, MapWListContext, (XPointer *) &wl) == XCNOENT) {
10110bbfda8aSnia		return;
10120bbfda8aSnia	}
10130bbfda8aSnia	win = wl->twm_win;
10140bbfda8aSnia
10150bbfda8aSnia	/*
10160bbfda8aSnia	 * Sometimes we skip transients, so do so.  XXX Should this
10170bbfda8aSnia	 * GotoWorkSpace()?
10180bbfda8aSnia	 */
10190bbfda8aSnia	if((! Scr->TransientHasOccupation) && win->istransient) {
10200bbfda8aSnia		return;
10210bbfda8aSnia	}
10220bbfda8aSnia
10230bbfda8aSnia	/*
10240bbfda8aSnia	 * Are we trying to actually move the window, by moving its avatar in
10250bbfda8aSnia	 * the WSM?  If ReallyMoveInWorkspaceManager is set, we're moving on
10260bbfda8aSnia	 * click, but not in shift-click.  If it's not, it's the reverse.
10270bbfda8aSnia	 *
10280bbfda8aSnia	 * XXX This interacts really oddly and badly when you're also moving
10290bbfda8aSnia	 * the window from WS to WS.
10300bbfda8aSnia	 */
10310bbfda8aSnia	realmovemode = false;
10320bbfda8aSnia	if(Scr->ReallyMoveInWorkspaceManager) {
10330bbfda8aSnia		if(!(modifier & ShiftMask)) {
10340bbfda8aSnia			realmovemode = true;
10350bbfda8aSnia		}
10360bbfda8aSnia	}
10370bbfda8aSnia	else if(modifier & ShiftMask) {
10380bbfda8aSnia		realmovemode = true;
10390bbfda8aSnia	}
10400bbfda8aSnia
10410bbfda8aSnia	/*
10420bbfda8aSnia	 * Frob screen-wide OpaqueMove as necessary for this window's
10430bbfda8aSnia	 * details.
10440bbfda8aSnia	 * XXX Really?
10450bbfda8aSnia	 */
10460bbfda8aSnia	if(win->OpaqueMove) {
10470bbfda8aSnia		if(Scr->OpaqueMoveThreshold >= 200) {
10480bbfda8aSnia			Scr->OpaqueMove = true;
10490bbfda8aSnia		}
10500bbfda8aSnia		else {
10510bbfda8aSnia			const unsigned long winsz = win->frame_width * win->frame_height;
10520bbfda8aSnia			const unsigned long scrsz = vs->w * vs->h;
10530bbfda8aSnia			const float sf = Scr->OpaqueMoveThreshold / 100.0;
10540bbfda8aSnia			if(winsz > (scrsz * sf)) {
10550bbfda8aSnia				Scr->OpaqueMove = false;
10560bbfda8aSnia			}
10570bbfda8aSnia			else {
10580bbfda8aSnia				Scr->OpaqueMove = true;
10590bbfda8aSnia			}
10600bbfda8aSnia		}
10610bbfda8aSnia	}
10620bbfda8aSnia	else {
10630bbfda8aSnia		Scr->OpaqueMove = false;
10640bbfda8aSnia	}
10650bbfda8aSnia
10660bbfda8aSnia	/*
10670bbfda8aSnia	 * Buttons inside the workspace manager, when clicking on the
10680bbfda8aSnia	 * representation of a window:
10690bbfda8aSnia	 * 1: drag window to a different workspace
10700bbfda8aSnia	 * 2: drag a copy of the window to a different workspace
10710bbfda8aSnia	 *    (show it in both workspaces)
10720bbfda8aSnia	 * 3: remove the window from this workspace (if it isn't the last).
10730bbfda8aSnia	 *
10740bbfda8aSnia	 * XXX If you move between workspaces while also doing realmovemode,
10750bbfda8aSnia	 * really messed up things happen.
10760bbfda8aSnia	 */
10770bbfda8aSnia	switch(button) {
10780bbfda8aSnia		case 1 : {
10790bbfda8aSnia			/*
10800bbfda8aSnia			 * Moving from one to another; get rid of the old location,
10810bbfda8aSnia			 * then fall through to the "duplicating" case below.
10820bbfda8aSnia			 */
10830bbfda8aSnia			XUnmapWindow(dpy, sw);
10840bbfda8aSnia			/* FALLTHRU */
10850bbfda8aSnia		}
10860bbfda8aSnia
10870bbfda8aSnia		case 2 : {
10880bbfda8aSnia			/*
10890bbfda8aSnia			 * Duplicating window to another WS.  We create a copy of the
10900bbfda8aSnia			 * window, and start moving that.  The 'moving' case above
10910bbfda8aSnia			 * falls through to us after unmapping that old window,
10920bbfda8aSnia			 * leaving just our copy, which is good enough.  This is
10930bbfda8aSnia			 * really just setting up the visual stuff for the move; the
10940bbfda8aSnia			 * actual occupation changes etc. come at the end of the
10950bbfda8aSnia			 * motion, much lower down.
10960bbfda8aSnia			 */
10970bbfda8aSnia			int X0, Y0, X1, Y1;
10980bbfda8aSnia			unsigned int bw;
10990bbfda8aSnia			XSetWindowAttributes attrs;
11000bbfda8aSnia			Window junkW;
11010bbfda8aSnia
11020bbfda8aSnia			/* [XYWH]0 = size/location of the avatar in the map */
11030bbfda8aSnia			XGetGeometry(dpy, sw, &junkW, &X0, &Y0, &W0, &H0, &bw, &JunkDepth);
11040bbfda8aSnia
11050bbfda8aSnia			/*
11060bbfda8aSnia			 * [XY]0 are the coordinates of the avatar subwindow inside
11070bbfda8aSnia			 * the individual workspace window in the map.  Turn those
11080bbfda8aSnia			 * into [XY]1 as the coordinates of it relative to the whole
11090bbfda8aSnia			 * WSM window.
11100bbfda8aSnia			 */
11110bbfda8aSnia			XTranslateCoordinates(dpy, vs->wsw->mswl[oldws->number]->w,
11120bbfda8aSnia			                      mw->w, X0, Y0, &X1, &Y1, &junkW);
11130bbfda8aSnia
11140bbfda8aSnia			/*
11150bbfda8aSnia			 * Create the copy window to drag around, as a child of the
11160bbfda8aSnia			 * whole WSM (so we can move it between workspaces), and map
11170bbfda8aSnia			 * it.
11180bbfda8aSnia			 */
11190bbfda8aSnia			attrs.event_mask       = ExposureMask;
11200bbfda8aSnia			attrs.background_pixel = wl->cp.back;
11210bbfda8aSnia			attrs.border_pixel     = wl->cp.back;
11220bbfda8aSnia			w = XCreateWindow(dpy, mw->w, X1, Y1, W0, H0, bw,
11230bbfda8aSnia			                  CopyFromParent, CopyFromParent, CopyFromParent,
11240bbfda8aSnia			                  CWEventMask | CWBackPixel | CWBorderPixel,
11250bbfda8aSnia			                  &attrs);
11260bbfda8aSnia			XMapRaised(dpy, w);
11270bbfda8aSnia
11280bbfda8aSnia			/* Do our dance on it to draw the name/color/etc */
11290bbfda8aSnia			WMapRedrawWindow(w, W0, H0, wl->cp, wl->twm_win->icon_name);
11300bbfda8aSnia
11310bbfda8aSnia			/*
11320bbfda8aSnia			 * If we're moving the real window and
11330bbfda8aSnia			 * AlwaysShowWindowWhenMovingFromWorkspaceManager is set in
11340bbfda8aSnia			 * config, we need to be sure the real window is visible
11350bbfda8aSnia			 * while we move it.
11360bbfda8aSnia			 */
11370bbfda8aSnia			if(realmovemode && Scr->ShowWinWhenMovingInWmgr) {
11380bbfda8aSnia				if(Scr->OpaqueMove) {
11390bbfda8aSnia					DisplayWin(vs, win);
11400bbfda8aSnia				}
11410bbfda8aSnia				else {
11420bbfda8aSnia					MoveOutline(Scr->Root,
11430bbfda8aSnia					            win->frame_x - win->frame_bw,
11440bbfda8aSnia					            win->frame_y - win->frame_bw,
11450bbfda8aSnia					            win->frame_width  + 2 * win->frame_bw,
11460bbfda8aSnia					            win->frame_height + 2 * win->frame_bw,
11470bbfda8aSnia					            win->frame_bw,
11480bbfda8aSnia					            win->title_height + win->frame_bw3D);
11490bbfda8aSnia				}
11500bbfda8aSnia			}
11510bbfda8aSnia
11520bbfda8aSnia			/* Move onward */
11530bbfda8aSnia			break;
11540bbfda8aSnia		}
11550bbfda8aSnia
11560bbfda8aSnia		/*
11570bbfda8aSnia		 * For the button 3 or anything else case, there's no dragging or
11580bbfda8aSnia		 * anything, so they do their thing and just immediately return.
11590bbfda8aSnia		 */
11600bbfda8aSnia		case 3 : {
11610bbfda8aSnia			int newocc = win->occupation & ~(1 << oldws->number);
11620bbfda8aSnia			if(newocc != 0) {
11630bbfda8aSnia				ChangeOccupation(win, newocc);
11640bbfda8aSnia			}
11650bbfda8aSnia			return;
11660bbfda8aSnia		}
11670bbfda8aSnia
11680bbfda8aSnia		default :
11690bbfda8aSnia			return;
11700bbfda8aSnia	}
11710bbfda8aSnia
11720bbfda8aSnia	/*
11730bbfda8aSnia	 * Keep dragging the representation of the window
11740bbfda8aSnia	 *
11750bbfda8aSnia	 * XXX Look back at this and see if we can move it to an inner
11760bbfda8aSnia	 * function for readability...
11770bbfda8aSnia	 */
11780bbfda8aSnia	{
11790bbfda8aSnia		const float wf = (float)(mw->wwidth  - 1) / (float) vs->w;
11800bbfda8aSnia		const float hf = (float)(mw->wheight - 1) / (float) vs->h;
11810bbfda8aSnia		int XW, YW;
11820bbfda8aSnia		bool cont;
11830bbfda8aSnia		Window junkW;
11840bbfda8aSnia
11850bbfda8aSnia		/* Figure where in the subwindow the click was, and stash in XW/YW */
11860bbfda8aSnia		XTranslateCoordinates(dpy, Scr->Root, sw, event->xbutton.x_root,
11870bbfda8aSnia		                      event->xbutton.y_root,
11880bbfda8aSnia		                      &XW, &YW, &junkW);
11890bbfda8aSnia
11900bbfda8aSnia		/*
11910bbfda8aSnia		 * Grab the pointer, lock it into the WSM, and get the events
11920bbfda8aSnia		 * related to buttons and movement.  We don't need
11930bbfda8aSnia		 * PointerMotionMask, since that would only happen after buttons
11940bbfda8aSnia		 * are released, and we'll be done by then.
11950bbfda8aSnia		 */
11960bbfda8aSnia		XGrabPointer(dpy, mw->w, False,
11970bbfda8aSnia		             ButtonPressMask | ButtonMotionMask | ButtonReleaseMask,
11980bbfda8aSnia		             GrabModeAsync, GrabModeAsync, mw->w, Scr->MoveCursor,
11990bbfda8aSnia		             CurrentTime);
12000bbfda8aSnia
12010bbfda8aSnia		/* Start handling events until we're done */
12020bbfda8aSnia		alreadyvivible = false;
12030bbfda8aSnia		cont = true;
12040bbfda8aSnia		while(cont) {
12050bbfda8aSnia			XEvent ev;
12060bbfda8aSnia
12070bbfda8aSnia			/* Grab the next event and handle */
12080bbfda8aSnia			XMaskEvent(dpy, ButtonPressMask | ButtonMotionMask |
12090bbfda8aSnia			           ButtonReleaseMask | ExposureMask, &ev);
12100bbfda8aSnia			switch(ev.xany.type) {
12110bbfda8aSnia				case Expose : {
12120bbfda8aSnia					/* Something got exposed */
12130bbfda8aSnia					if(ev.xexpose.window == w) {
12140bbfda8aSnia						/*
12150bbfda8aSnia						 * The win we're working with?  We know how to do
12160bbfda8aSnia						 * that.
12170bbfda8aSnia						 */
12180bbfda8aSnia						WMapRedrawWindow(w, W0, H0, wl->cp,
12190bbfda8aSnia						                 wl->twm_win->icon_name);
12200bbfda8aSnia						break;
12210bbfda8aSnia					}
12220bbfda8aSnia
12230bbfda8aSnia					/* Else, delegate to our global dispatcher */
12240bbfda8aSnia					Event = ev;
12250bbfda8aSnia					DispatchEvent();
12260bbfda8aSnia					break;
12270bbfda8aSnia				}
12280bbfda8aSnia
12290bbfda8aSnia				case ButtonPress :
12300bbfda8aSnia				case ButtonRelease : {
12310bbfda8aSnia					/*
12320bbfda8aSnia					 * Events for buttons other than the one whose press
12330bbfda8aSnia					 * started this activity are totally ignored.
12340bbfda8aSnia					 */
12350bbfda8aSnia					if(ev.xbutton.button != button) {
12360bbfda8aSnia						break;
12370bbfda8aSnia					}
12380bbfda8aSnia
12390bbfda8aSnia					/*
12400bbfda8aSnia					 * Otherwise, this is a press/release of the button
12410bbfda8aSnia					 * that started things.  Though I'm not sure how it
12420bbfda8aSnia					 * could be a press, since it was already pressed.
12430bbfda8aSnia					 * Regardless, this is our exit condition.  Fall
12440bbfda8aSnia					 * through into the Motion case to handle any
12450bbfda8aSnia					 * remaining movement.
12460bbfda8aSnia					 */
12470bbfda8aSnia					lastev = ev;
12480bbfda8aSnia					cont = false;
12490bbfda8aSnia					newX = ev.xbutton.x;
12500bbfda8aSnia					newY = ev.xbutton.y;
12510bbfda8aSnia				}
12520bbfda8aSnia
12530bbfda8aSnia				/* Everything remaining is motion handling */
12540bbfda8aSnia				case MotionNotify : {
12550bbfda8aSnia					/* If we fell through from above, no new movement */
12560bbfda8aSnia					if(cont) {
12570bbfda8aSnia						newX = ev.xmotion.x;
12580bbfda8aSnia						newY = ev.xmotion.y;
12590bbfda8aSnia					}
12600bbfda8aSnia
12610bbfda8aSnia					/* Lots to do if we're moving the window for real */
12620bbfda8aSnia					if(realmovemode) {
12630bbfda8aSnia						int XSW, YSW;
12640bbfda8aSnia						WorkSpace *cws;
12650bbfda8aSnia						MapSubwindow *msw;
12660bbfda8aSnia
12670bbfda8aSnia						/* Did the move start in the currently visible WS? */
12680bbfda8aSnia						bool startincurrent = (oldws == vs->wsw->currentwspc);
12690bbfda8aSnia
12700bbfda8aSnia						/* Find the workspace we wound up in */
12710bbfda8aSnia						for(cws = Scr->workSpaceMgr.workSpaceList ;
12720bbfda8aSnia						                cws != NULL ;
12730bbfda8aSnia						                cws = cws->next) {
12740bbfda8aSnia							msw = vs->wsw->mswl [cws->number];
12750bbfda8aSnia							if((newX >= msw->x)
12760bbfda8aSnia							                && (newX <  msw->x + mw->wwidth)
12770bbfda8aSnia							                && (newY >= msw->y)
12780bbfda8aSnia							                && (newY <  msw->y + mw->wheight)) {
12790bbfda8aSnia								break;
12800bbfda8aSnia							}
12810bbfda8aSnia						}
12820bbfda8aSnia						if(!cws) {
12830bbfda8aSnia							/* None?  Ignore. */
12840bbfda8aSnia							break;
12850bbfda8aSnia						}
12860bbfda8aSnia
12870bbfda8aSnia						/*
12880bbfda8aSnia						 * Mouse is wherever it started inside the
12890bbfda8aSnia						 * subwindow, so figure the X/Y of the top left
12900bbfda8aSnia						 * of the subwindow from there.  (coords relative
12910bbfda8aSnia						 * to the whole WSM because of grab)
12920bbfda8aSnia						 */
12930bbfda8aSnia						winX = newX - XW;
12940bbfda8aSnia						winY = newY - YW;
12950bbfda8aSnia
12960bbfda8aSnia						/* XXX redundant w/previous? */
12970bbfda8aSnia						msw = vs->wsw->mswl[cws->number];
12980bbfda8aSnia
12990bbfda8aSnia						/*
13000bbfda8aSnia						 * Figure where those coords are relative to the
13010bbfda8aSnia						 * per-workspace window.
13020bbfda8aSnia						 */
13030bbfda8aSnia						XTranslateCoordinates(dpy, mw->w, msw->w,
13040bbfda8aSnia						                      winX, winY, &XSW, &YSW, &junkW);
13050bbfda8aSnia
13060bbfda8aSnia						/*
13070bbfda8aSnia						 * Now rework the win[XY] to be the coordinates
13080bbfda8aSnia						 * the window would be in the whole screen, based
13090bbfda8aSnia						 * on the [XY]SW inside the map, using our scale
13100bbfda8aSnia						 * factors.
13110bbfda8aSnia						 */
13120bbfda8aSnia						winX = (int)(XSW / wf);
13130bbfda8aSnia						winY = (int)(YSW / hf);
13140bbfda8aSnia
13150bbfda8aSnia						/*
13160bbfda8aSnia						 * Clip to the screen if DontMoveOff is set.
13170bbfda8aSnia						 *
13180bbfda8aSnia						 * XXX Can we use the Constrain*() functions for
13190bbfda8aSnia						 * this, instead of implementing icky magic
13200bbfda8aSnia						 * internally?
13210bbfda8aSnia						 */
13220bbfda8aSnia						if(Scr->DontMoveOff) {
13230bbfda8aSnia							const int width = win->frame_width;
13240bbfda8aSnia							const int height = win->frame_height;
13250bbfda8aSnia
13260bbfda8aSnia							if((winX < Scr->BorderLeft) && ((Scr->MoveOffResistance < 0) ||
13270bbfda8aSnia							                                (winX > Scr->BorderLeft - Scr->MoveOffResistance))) {
13280bbfda8aSnia								winX = Scr->BorderLeft;
13290bbfda8aSnia								newX = msw->x + XW + Scr->BorderLeft * mw->wwidth / vs->w;
13300bbfda8aSnia							}
13310bbfda8aSnia							if(((winX + width) > vs->w - Scr->BorderRight) &&
13320bbfda8aSnia							                ((Scr->MoveOffResistance < 0) ||
13330bbfda8aSnia							                 ((winX + width) < vs->w - Scr->BorderRight + Scr->MoveOffResistance))) {
13340bbfda8aSnia								winX = vs->w - Scr->BorderRight - width;
13350bbfda8aSnia								newX = msw->x + mw->wwidth *
13360bbfda8aSnia								       (1 - Scr->BorderRight / (double) vs->w) - wl->width + XW - 2;
13370bbfda8aSnia							}
13380bbfda8aSnia							if((winY < Scr->BorderTop) && ((Scr->MoveOffResistance < 0) ||
13390bbfda8aSnia							                               (winY > Scr->BorderTop - Scr->MoveOffResistance))) {
13400bbfda8aSnia								winY = Scr->BorderTop;
13410bbfda8aSnia								newY = msw->y + YW + Scr->BorderTop * mw->height / vs->h;
13420bbfda8aSnia							}
13430bbfda8aSnia							if(((winY + height) > vs->h - Scr->BorderBottom) &&
13440bbfda8aSnia							                ((Scr->MoveOffResistance < 0) ||
13450bbfda8aSnia							                 ((winY + height) < vs->h - Scr->BorderBottom + Scr->MoveOffResistance))) {
13460bbfda8aSnia								winY = vs->h - Scr->BorderBottom - height;
13470bbfda8aSnia								newY = msw->y + mw->wheight
13480bbfda8aSnia								       * (1 - Scr->BorderBottom / (double) vs->h)
13490bbfda8aSnia								       * - wl->height + YW - 2;
13500bbfda8aSnia							}
13510bbfda8aSnia						}
13520bbfda8aSnia
13530bbfda8aSnia
13540bbfda8aSnia						/* Setup the avatar for the new position of win */
13550bbfda8aSnia						WMapSetupWindow(win, winX, winY, -1, -1);
13560bbfda8aSnia
13570bbfda8aSnia						/*
13580bbfda8aSnia						 * If SWWMIW, we already made sure above the
13590bbfda8aSnia						 * window is always visible on the screen.  So we
13600bbfda8aSnia						 * don't need to do any of the following steps to
13610bbfda8aSnia						 * maybe show it or maybe un-show it, since we
13620bbfda8aSnia						 * know it's already always shown.
13630bbfda8aSnia						 */
13640bbfda8aSnia						if(Scr->ShowWinWhenMovingInWmgr) {
13650bbfda8aSnia							goto movewin;
13660bbfda8aSnia						}
13670bbfda8aSnia
13680bbfda8aSnia						/*
13690bbfda8aSnia						 * If we wound up in the same workspace as is
13700bbfda8aSnia						 * being displayed on the screen, we need to make
13710bbfda8aSnia						 * sure that window is showing up on the screen
13720bbfda8aSnia						 * as a "about to be occupied here if you release
13730bbfda8aSnia						 * the button" indication.
13740bbfda8aSnia						 */
13750bbfda8aSnia						if(cws == vs->wsw->currentwspc) {
13760bbfda8aSnia							if(alreadyvivible) {
13770bbfda8aSnia								/* Unless it's already there */
13780bbfda8aSnia								goto movewin;
13790bbfda8aSnia							}
13800bbfda8aSnia							if(Scr->OpaqueMove) {
13810bbfda8aSnia								XMoveWindow(dpy, win->frame, winX, winY);
13820bbfda8aSnia								DisplayWin(vs, win);
13830bbfda8aSnia							}
13840bbfda8aSnia							else {
13850bbfda8aSnia								MoveOutline(Scr->Root,
13860bbfda8aSnia								            winX - win->frame_bw,
13870bbfda8aSnia								            winY - win->frame_bw,
13880bbfda8aSnia								            win->frame_width  + 2 * win->frame_bw,
13890bbfda8aSnia								            win->frame_height + 2 * win->frame_bw,
13900bbfda8aSnia								            win->frame_bw,
13910bbfda8aSnia								            win->title_height + win->frame_bw3D);
13920bbfda8aSnia							}
13930bbfda8aSnia							alreadyvivible = true;
13940bbfda8aSnia
13950bbfda8aSnia							/*
13960bbfda8aSnia							 * We already moved it, skip past the other
13970bbfda8aSnia							 * code trying to move it in other cases.
13980bbfda8aSnia							 */
13990bbfda8aSnia							goto move;
14000bbfda8aSnia						}
14010bbfda8aSnia
14020bbfda8aSnia						/*
14030bbfda8aSnia						 * If it's for whatever reason not being shown on
14040bbfda8aSnia						 * the screen, then we don't need to move it; hop
14050bbfda8aSnia						 * straight to moving the avatar.
14060bbfda8aSnia						 */
14070bbfda8aSnia						if(!alreadyvivible) {
14080bbfda8aSnia							goto move;
14090bbfda8aSnia						}
14100bbfda8aSnia
14110bbfda8aSnia						/*
14120bbfda8aSnia						 * So it _is_ being shown.  If it's not supposed
14130bbfda8aSnia						 * to be here, hide it away (don't need to worry
14140bbfda8aSnia						 * about AlwaysShow, it would have skipped past
14150bbfda8aSnia						 * us from ab0ve).  Also, if we started moving
14160bbfda8aSnia						 * the window out of the visible workspace with
14170bbfda8aSnia						 * button 1, it's gonna not be here if you
14180bbfda8aSnia						 * release, so hide it away.
14190bbfda8aSnia						 */
14200bbfda8aSnia						if(!OCCUPY(win, vs->wsw->currentwspc) ||
14210bbfda8aSnia						                (startincurrent && (button == 1))) {
14220bbfda8aSnia							if(Scr->OpaqueMove) {
14230bbfda8aSnia								Vanish(vs, win);
14240bbfda8aSnia								XMoveWindow(dpy, win->frame, winX, winY);
14250bbfda8aSnia							}
14260bbfda8aSnia							else {
14270bbfda8aSnia								MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
14280bbfda8aSnia							}
14290bbfda8aSnia							alreadyvivible = false;
14300bbfda8aSnia							goto move;
14310bbfda8aSnia						}
14320bbfda8aSnia
14330bbfda8aSniamovewin:
14340bbfda8aSnia						/*
14350bbfda8aSnia						 * However we get here, the real window is
14360bbfda8aSnia						 * visible and needs to be moved.  So move it.
14370bbfda8aSnia						 */
14380bbfda8aSnia						if(Scr->OpaqueMove) {
14390bbfda8aSnia							XMoveWindow(dpy, win->frame, winX, winY);
14400bbfda8aSnia						}
14410bbfda8aSnia						else {
14420bbfda8aSnia							MoveOutline(Scr->Root,
14430bbfda8aSnia							            winX - win->frame_bw,
14440bbfda8aSnia							            winY - win->frame_bw,
14450bbfda8aSnia							            win->frame_width  + 2 * win->frame_bw,
14460bbfda8aSnia							            win->frame_height + 2 * win->frame_bw,
14470bbfda8aSnia							            win->frame_bw,
14480bbfda8aSnia							            win->title_height + win->frame_bw3D);
14490bbfda8aSnia						}
14500bbfda8aSnia					}
14510bbfda8aSnia
14520bbfda8aSniamove:
14530bbfda8aSnia					/*
14540bbfda8aSnia					 * Just move the subwindow in the map to the new
14550bbfda8aSnia					 * location.
14560bbfda8aSnia					 */
14570bbfda8aSnia					XMoveWindow(dpy, w, newX - XW, newY - YW);
14580bbfda8aSnia
14590bbfda8aSnia					/* And we're done.  Next event! */
14600bbfda8aSnia					break;
14610bbfda8aSnia				}
14620bbfda8aSnia			}
14630bbfda8aSnia		}
14640bbfda8aSnia	}
14650bbfda8aSnia
14660bbfda8aSnia	/*
14670bbfda8aSnia	 * Finished with dragging (button released).
14680bbfda8aSnia	 */
14690bbfda8aSnia	if(realmovemode) {
14700bbfda8aSnia		/* Moving the real window?  Move the real window. */
14710bbfda8aSnia		if(Scr->ShowWinWhenMovingInWmgr || alreadyvivible) {
14720bbfda8aSnia			if(Scr->OpaqueMove && !OCCUPY(win, vs->wsw->currentwspc)) {
14730bbfda8aSnia				Vanish(vs, win);
14740bbfda8aSnia			}
14750bbfda8aSnia			if(!Scr->OpaqueMove) {
14760bbfda8aSnia				MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
14770bbfda8aSnia				WMapRedrawName(vs, wl);
14780bbfda8aSnia			}
14790bbfda8aSnia		}
14800bbfda8aSnia		SetupWindow(win, winX, winY, win->frame_width, win->frame_height, -1);
14810bbfda8aSnia	}
14820bbfda8aSnia
14830bbfda8aSnia	/*
14840bbfda8aSnia	 * Last event that caused us to escape is other code's
14850bbfda8aSnia	 * responsibility, put it back in the queue.
14860bbfda8aSnia	 */
14870bbfda8aSnia	lastev.xbutton.subwindow = (Window) 0;
14880bbfda8aSnia	lastev.xbutton.window = parent;
14890bbfda8aSnia	XPutBackEvent(dpy, &lastev);
14900bbfda8aSnia
14910bbfda8aSnia	/* End our grab */
14920bbfda8aSnia	XUngrabPointer(dpy, CurrentTime);
14930bbfda8aSnia
14940bbfda8aSnia	/*
14950bbfda8aSnia	 * If you wanted to change workspaces, and clicked _outside_ a
14960bbfda8aSnia	 * window, it would have just switched way up near the top of the
14970bbfda8aSnia	 * function.  But if you clicked _in_ a window [in the WSM map], it
14980bbfda8aSnia	 * would have to go through the whole fun to find out whether you
14990bbfda8aSnia	 * wanted to move/reoccupy the window, or were just wanting to change
15000bbfda8aSnia	 * WSen.
15010bbfda8aSnia	 *
15020bbfda8aSnia	 * So if it's been <250ms (completely arbitrary and non-configgable,
15030bbfda8aSnia	 * probably should be rethought) since you started, assume you just
15040bbfda8aSnia	 * wanted to switch workspaces.  Don't do any occupation change, And
15050bbfda8aSnia	 * just switch.  Also do some magic related to the map/button
15060bbfda8aSnia	 * toggling.
15070bbfda8aSnia	 *
15080bbfda8aSnia	 * XXX This still leaves any window _moves_ done.  It seems like it
15090bbfda8aSnia	 * should probably revert those too?
15100bbfda8aSnia	 */
15110bbfda8aSnia	if((lastev.xbutton.time - etime) < 250) {
15120bbfda8aSnia		KeyCode control_L_code, control_R_code;
15130bbfda8aSnia		char keys [32];
15140bbfda8aSnia
15150bbfda8aSnia		/* Re-show old miniwindow, destroy the temp, and warp to WS */
15160bbfda8aSnia		XMapWindow(dpy, sw);
15170bbfda8aSnia		XDestroyWindow(dpy, w);
15180bbfda8aSnia		GotoWorkSpace(vs, oldws);
15190bbfda8aSnia		if(!Scr->DontWarpCursorInWMap) {
15200bbfda8aSnia			WarpToWindow(win, Scr->RaiseOnWarp);
15210bbfda8aSnia		}
15220bbfda8aSnia
15230bbfda8aSnia		/*
15240bbfda8aSnia		 * The control keys toggle between map and button state.  If we
15250bbfda8aSnia		 * did a short move, and ctrl is being held at the end, flip the
15260bbfda8aSnia		 * state.  This has several possible causes and effects.
15270bbfda8aSnia		 *
15280bbfda8aSnia		 * One is that _during_ a move, we don't do look through KeyPress
15290bbfda8aSnia		 * events, so we wouldn't see it happen yet.  But after we're
15300bbfda8aSnia		 * done and return back into the event loop, that press will come
15310bbfda8aSnia		 * in and cause the state to change.  It may be weird to the user
15320bbfda8aSnia		 * to see that happen, not when they hit ctrl, but _later_, after
15330bbfda8aSnia		 * they release the mouse button.  The "best" solution may be to
15340bbfda8aSnia		 * find that press in the event queue and empty it out, but a
15350bbfda8aSnia		 * cheap solution is just to pre-flip it and then let the event
15360bbfda8aSnia		 * code flip it back.  Flickery maybe, but easy.  Now, _should_ we be
15370bbfda8aSnia		 * doing that?  I'm a little doubtful...
15380bbfda8aSnia		 *
15390bbfda8aSnia		 * A second is that if the WSM is "naturally" in button mode, and
15400bbfda8aSnia		 * you temporarily ctrl-flip it into map mode and then click in a
15410bbfda8aSnia		 * window.  This code will cause it to automatically flip back
15420bbfda8aSnia		 * after you release the mouse if you haven't released ctrl yet.
15430bbfda8aSnia		 * This is apparently needed because, unless you have
15440bbfda8aSnia		 * DontWarpCursorInWMap set, the previous few lines of code would
15450bbfda8aSnia		 * have shifted the cursor to the window you clicked, which means
15460bbfda8aSnia		 * you don't get a chance to release it in the WSM and flip the
15470bbfda8aSnia		 * state back.  It seems a reasonable assumption that the user
15480bbfda8aSnia		 * wanted a temporary change of state just for the purposes of
15490bbfda8aSnia		 * the change.
15500bbfda8aSnia		 *
15510bbfda8aSnia		 * (if you had released the ctrl before the mouse, the release
15520bbfda8aSnia		 * event would already be queued up up on the WSM, so would flip
15530bbfda8aSnia		 * it back after we return)
15540bbfda8aSnia		 *
15550bbfda8aSnia		 * XXX Should we be checking DontToggleWorkSpaceManagerState
15560bbfda8aSnia		 * here?
15570bbfda8aSnia		 */
15580bbfda8aSnia		control_L_code = XKeysymToKeycode(dpy, XStringToKeysym("Control_L"));
15590bbfda8aSnia		control_R_code = XKeysymToKeycode(dpy, XStringToKeysym("Control_R"));
15600bbfda8aSnia		XQueryKeymap(dpy, keys);
15610bbfda8aSnia		if((keys [control_L_code / 8] & ((char) 0x80 >> (control_L_code % 8))) ||
15620bbfda8aSnia		                keys [control_R_code / 8] & ((char) 0x80 >> (control_R_code % 8))) {
15630bbfda8aSnia			WMgrToggleState(vs);
15640bbfda8aSnia		}
15650bbfda8aSnia		return;
15660bbfda8aSnia	}
15670bbfda8aSnia
15680bbfda8aSnia
15690bbfda8aSnia	/* Find the workspace we finally found up in */
15700bbfda8aSnia	for(newws = Scr->workSpaceMgr.workSpaceList ; newws != NULL ;
15710bbfda8aSnia	                newws = newws->next) {
15720bbfda8aSnia		MapSubwindow *msw = vs->wsw->mswl[newws->number];
15730bbfda8aSnia		if((newX >= msw->x) && (newX < msw->x + mw->wwidth) &&
15740bbfda8aSnia		                (newY >= msw->y) && (newY < msw->y + mw->wheight)) {
15750bbfda8aSnia			break;
15760bbfda8aSnia		}
15770bbfda8aSnia	}
15780bbfda8aSnia
15790bbfda8aSnia	/* And finish off whatever we're supposed to be doing */
15800bbfda8aSnia	switch(button) {
15810bbfda8aSnia		case 1 : { /* moving to another workspace */
15820bbfda8aSnia			int occupation;
15830bbfda8aSnia
15840bbfda8aSnia			/* If nothing to change, re-map avatar and we're done */
15850bbfda8aSnia			if((newws == NULL) || (newws == oldws) ||
15860bbfda8aSnia			                OCCUPY(wl->twm_win, newws)) {
15870bbfda8aSnia				XMapWindow(dpy, sw);
15880bbfda8aSnia				break;
15890bbfda8aSnia			}
15900bbfda8aSnia
15910bbfda8aSnia			/* Out of the old, into the new */
15920bbfda8aSnia			occupation = win->occupation;
15930bbfda8aSnia			occupation |= (1 << newws->number);
15940bbfda8aSnia			occupation &= ~(1 << oldws->number);
15950bbfda8aSnia			ChangeOccupation(win, occupation);
15960bbfda8aSnia
15970bbfda8aSnia			/*
15980bbfda8aSnia			 * Raise it to the top if it's in our current ws, and
15990bbfda8aSnia			 * properly slot it into the WSM map stack if not.
16000bbfda8aSnia			 */
16010bbfda8aSnia			if(newws == vs->wsw->currentwspc) {
16020bbfda8aSnia				OtpRaise(win, WinWin);
16030bbfda8aSnia				WMapRaise(win);
16040bbfda8aSnia			}
16050bbfda8aSnia			else {
16060bbfda8aSnia				WMapRestack(newws);
16070bbfda8aSnia			}
16080bbfda8aSnia
16090bbfda8aSnia			break;
16100bbfda8aSnia		}
16110bbfda8aSnia
16120bbfda8aSnia		case 2 : { /* putting in extra workspace */
16130bbfda8aSnia			int occupation;
16140bbfda8aSnia
16150bbfda8aSnia			/* Nothing to do if it's going nowhere or places it already is */
16160bbfda8aSnia			if((newws == NULL) || (newws == oldws) ||
16170bbfda8aSnia			                OCCUPY(wl->twm_win, newws)) {
16180bbfda8aSnia				break;
16190bbfda8aSnia			}
16200bbfda8aSnia
16210bbfda8aSnia			/* Move */
16220bbfda8aSnia			occupation = win->occupation | (1 << newws->number);
16230bbfda8aSnia			ChangeOccupation(win, occupation);
16240bbfda8aSnia
16250bbfda8aSnia			/* Raise/stack */
16260bbfda8aSnia			if(newws == vs->wsw->currentwspc) {
16270bbfda8aSnia				OtpRaise(win, WinWin);
16280bbfda8aSnia				WMapRaise(win);
16290bbfda8aSnia			}
16300bbfda8aSnia			else {
16310bbfda8aSnia				WMapRestack(newws);
16320bbfda8aSnia			}
16330bbfda8aSnia			break;
16340bbfda8aSnia		}
16350bbfda8aSnia
16360bbfda8aSnia		/*
16370bbfda8aSnia		 * Should actually never hit this state; only 1/2 would have
16380bbfda8aSnia		 * gotten this far into the code...
16390bbfda8aSnia		 */
16400bbfda8aSnia		default :
16410bbfda8aSnia			return;
16420bbfda8aSnia	}
16430bbfda8aSnia
16440bbfda8aSnia	/* Clean up our temporary moving-around-in the WSM window */
16450bbfda8aSnia	XDestroyWindow(dpy, w);
16460bbfda8aSnia}
16470bbfda8aSnia
16480bbfda8aSnia
16490bbfda8aSnia
16500bbfda8aSnia
16510bbfda8aSnia/*
16520bbfda8aSnia ****************************************************************
16530bbfda8aSnia *
16540bbfda8aSnia * Functions for doing things with subwindows in the WSM in map state
16550bbfda8aSnia *
16560bbfda8aSnia ****************************************************************
16570bbfda8aSnia */
16580bbfda8aSnia
16590bbfda8aSnia/*
16600bbfda8aSnia * Backend for mapping up windows in the WSM map.
16610bbfda8aSnia */
16620bbfda8aSniastatic void
16630bbfda8aSniawmap_mapwin_backend(TwmWindow *win, bool handleraise)
16640bbfda8aSnia{
16650bbfda8aSnia	VirtualScreen *vs;
16660bbfda8aSnia	WorkSpace *ws;
16670bbfda8aSnia	WinList *wl;
16680bbfda8aSnia
16690bbfda8aSnia	for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
16700bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
16710bbfda8aSnia			for(wl = vs->wsw->mswl[ws->number]->wl; wl != NULL; wl = wl->next) {
16720bbfda8aSnia				if(win == wl->twm_win) {
16730bbfda8aSnia					/*
16740bbfda8aSnia					 * When called via deiconify, we might have to do
16750bbfda8aSnia					 * stuff related to auto-raising the window while we
16760bbfda8aSnia					 * de-iconify.  When called via a map request, the
16770bbfda8aSnia					 * window is always wherever it previously was in the
16780bbfda8aSnia					 * stack.
16790bbfda8aSnia					 */
16800bbfda8aSnia					if(!handleraise || Scr->NoRaiseDeicon) {
16810bbfda8aSnia						XMapWindow(dpy, wl->w);
16820bbfda8aSnia					}
16830bbfda8aSnia					else {
16840bbfda8aSnia						XMapRaised(dpy, wl->w);
16850bbfda8aSnia					}
16860bbfda8aSnia					WMapRedrawName(vs, wl);
16870bbfda8aSnia					break;
16880bbfda8aSnia				}
16890bbfda8aSnia			}
16900bbfda8aSnia		}
16910bbfda8aSnia	}
16920bbfda8aSnia}
16930bbfda8aSnia
16940bbfda8aSnia/*
16950bbfda8aSnia * Map up a window's subwindow in the map-mode WSM.  Happens as a result
16960bbfda8aSnia * of getting (or faking) a Map request event.  Notably, _not_ in the
16970bbfda8aSnia * process of de-iconifying a window; mostly as a result of _creating_
16980bbfda8aSnia * windows, or when a client maps itself without a request from us.
16990bbfda8aSnia *
17000bbfda8aSnia * x-ref comment on WMapDeIconify() for some subtle distinctions between
17010bbfda8aSnia * the two...
17020bbfda8aSnia */
17030bbfda8aSniavoid
17040bbfda8aSniaWMapMapWindow(TwmWindow *win)
17050bbfda8aSnia{
17060bbfda8aSnia	/* We don't do raise handling */
17070bbfda8aSnia	wmap_mapwin_backend(win, false);
17080bbfda8aSnia}
17090bbfda8aSnia
17100bbfda8aSnia
17110bbfda8aSnia/*
17120bbfda8aSnia * De-iconify a window in the WSM map.  The opposite of WMapIconify(),
17130bbfda8aSnia * and different from WMapMapWindow() in complicated ways.  This function
17140bbfda8aSnia * winds up getting called when a window is de-iconified via a ctwm
17150bbfda8aSnia * function.
17160bbfda8aSnia */
17170bbfda8aSniavoid
17180bbfda8aSniaWMapDeIconify(TwmWindow *win)
17190bbfda8aSnia{
17200bbfda8aSnia	/* If it's not showing anywhere, nothing to do.  Is this possible? */
17210bbfda8aSnia	if(!win->vs) {
17220bbfda8aSnia		return;
17230bbfda8aSnia	}
17240bbfda8aSnia
17250bbfda8aSnia	/* Loop and map, handling raises if necessary */
17260bbfda8aSnia	wmap_mapwin_backend(win, true);
17270bbfda8aSnia}
17280bbfda8aSnia
17290bbfda8aSnia
17300bbfda8aSnia/*
17310bbfda8aSnia * Hide away a window in the WSM map.  Happens when win is iconified;
17320bbfda8aSnia * different from destruction.
17330bbfda8aSnia */
17340bbfda8aSniavoid
17350bbfda8aSniaWMapIconify(TwmWindow *win)
17360bbfda8aSnia{
17370bbfda8aSnia	VirtualScreen *vs;
17380bbfda8aSnia	WorkSpace *ws;
17390bbfda8aSnia	WinList *wl;
17400bbfda8aSnia
17410bbfda8aSnia	if(!win->vs) {
17420bbfda8aSnia		return;
17430bbfda8aSnia	}
17440bbfda8aSnia
17450bbfda8aSnia	for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
17460bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
17470bbfda8aSnia			for(wl = vs->wsw->mswl[ws->number]->wl; wl != NULL; wl = wl->next) {
17480bbfda8aSnia				if(win == wl->twm_win) {
17490bbfda8aSnia					XUnmapWindow(dpy, wl->w);
17500bbfda8aSnia					break;
17510bbfda8aSnia				}
17520bbfda8aSnia			}
17530bbfda8aSnia		}
17540bbfda8aSnia	}
17550bbfda8aSnia}
17560bbfda8aSnia
17570bbfda8aSnia
17580bbfda8aSnia/*
17590bbfda8aSnia * Position a window in the WSM.  Happens as a result of moving things.
17600bbfda8aSnia */
17610bbfda8aSniavoid
17620bbfda8aSniaWMapSetupWindow(TwmWindow *win, int x, int y, int w, int h)
17630bbfda8aSnia{
17640bbfda8aSnia	VirtualScreen *vs;
17650bbfda8aSnia
17660bbfda8aSnia	/* If it's an icon manager, or not on a vscreen, nothing to do */
17670bbfda8aSnia	if(win->isiconmgr || !win->vs) {
17680bbfda8aSnia		return;
17690bbfda8aSnia	}
17700bbfda8aSnia
17710bbfda8aSnia	/* If it's a WSM, we may be resetting size/position, but that's it */
17720bbfda8aSnia	if(win->iswspmgr) {
17730bbfda8aSnia		if(w == -1) {
17740bbfda8aSnia			return;
17750bbfda8aSnia		}
17760bbfda8aSnia		ResizeWorkSpaceManager(win->vs, win);
17770bbfda8aSnia		return;
17780bbfda8aSnia	}
17790bbfda8aSnia
17800bbfda8aSnia	/* If it's an occupy window, ditto */
17810bbfda8aSnia	if(win == Scr->workSpaceMgr.occupyWindow->twm_win) {
17820bbfda8aSnia		if(w == -1) {
17830bbfda8aSnia			return;
17840bbfda8aSnia		}
17850bbfda8aSnia		ResizeOccupyWindow(win);
17860bbfda8aSnia		return;
17870bbfda8aSnia	}
17880bbfda8aSnia
17890bbfda8aSnia
17900bbfda8aSnia	/* For anything else, we're potentially showing something */
17910bbfda8aSnia	for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
17920bbfda8aSnia		WorkSpaceWindow *wsw = vs->wsw;
17930bbfda8aSnia		WorkSpace *ws;
17940bbfda8aSnia
17950bbfda8aSnia		/* Scale factors for windows in the map */
17960bbfda8aSnia		float wf = (float)(wsw->wwidth  - 2) / (float) vs->w;
17970bbfda8aSnia		float hf = (float)(wsw->wheight - 2) / (float) vs->h;
17980bbfda8aSnia
17990bbfda8aSnia		/*
18000bbfda8aSnia		 * Loop around windows in each WS to find all the places the
18010bbfda8aSnia		 * requested window should show up.
18020bbfda8aSnia		 */
18030bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
18040bbfda8aSnia			MapSubwindow *msw = wsw->mswl[ws->number];
18050bbfda8aSnia
18060bbfda8aSnia			for(WinList *wl = msw->wl; wl != NULL; wl = wl->next) {
18070bbfda8aSnia				if(win == wl->twm_win) {
18080bbfda8aSnia					/* New positions */
18090bbfda8aSnia					wl->x = (int)(x * wf);
18100bbfda8aSnia					wl->y = (int)(y * hf);
18110bbfda8aSnia
18120bbfda8aSnia					/* Rescale if necessary and move */
18130bbfda8aSnia					if(w == -1) {
18140bbfda8aSnia						XMoveWindow(dpy, wl->w, wl->x, wl->y);
18150bbfda8aSnia					}
18160bbfda8aSnia					else {
18170bbfda8aSnia						wl->width  = (unsigned int)((w * wf) + 0.5);
18180bbfda8aSnia						wl->height = (unsigned int)((h * hf) + 0.5);
18190bbfda8aSnia						if(!Scr->use3Dwmap) {
18200bbfda8aSnia							wl->width  -= 2;
18210bbfda8aSnia							wl->height -= 2;
18220bbfda8aSnia						}
18230bbfda8aSnia						if(wl->width < 1) {
18240bbfda8aSnia							wl->width = 1;
18250bbfda8aSnia						}
18260bbfda8aSnia						if(wl->height < 1) {
18270bbfda8aSnia							wl->height = 1;
18280bbfda8aSnia						}
18290bbfda8aSnia						XMoveResizeWindow(dpy, wl->w, wl->x, wl->y,
18300bbfda8aSnia						                  wl->width, wl->height);
18310bbfda8aSnia					}
18320bbfda8aSnia					break;
18330bbfda8aSnia				}
18340bbfda8aSnia			}
18350bbfda8aSnia		}
18360bbfda8aSnia	}
18370bbfda8aSnia}
18380bbfda8aSnia
18390bbfda8aSnia
18400bbfda8aSnia/*
18410bbfda8aSnia * Frontends for changing the stacking of windows in the WSM.
18420bbfda8aSnia *
18430bbfda8aSnia * Strictly speaker, we have no ability to raise or lower a window in the
18440bbfda8aSnia * map; we only draw the whole stack.  And these functions don't actually
18450bbfda8aSnia * change the stacking of a window, they're called as a _result_ of
18460bbfda8aSnia * changing the stacking, to notify the WSM to re-check and update
18470bbfda8aSnia * itself.  So while conceptually we maintain a separation for our
18480bbfda8aSnia * callers between various reasons this is being called, the
18490bbfda8aSnia * implementations are identical.
18500bbfda8aSnia */
18510bbfda8aSniavoid
18520bbfda8aSniaWMapRaiseLower(TwmWindow *win)
18530bbfda8aSnia{
18540bbfda8aSnia	WorkSpace *ws;
18550bbfda8aSnia
18560bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
18570bbfda8aSnia		if(OCCUPY(win, ws)) {
18580bbfda8aSnia			WMapRestack(ws);
18590bbfda8aSnia		}
18600bbfda8aSnia	}
18610bbfda8aSnia}
18620bbfda8aSnia
18630bbfda8aSniavoid
18640bbfda8aSniaWMapLower(TwmWindow *win)
18650bbfda8aSnia{
18660bbfda8aSnia	WMapRaiseLower(win);
18670bbfda8aSnia}
18680bbfda8aSnia
18690bbfda8aSniavoid
18700bbfda8aSniaWMapRaise(TwmWindow *win)
18710bbfda8aSnia{
18720bbfda8aSnia	WMapRaiseLower(win);
18730bbfda8aSnia}
18740bbfda8aSnia
18750bbfda8aSnia
18760bbfda8aSnia/*
18770bbfda8aSnia * Backend for redoing the stacking of a window in the WSM.
18780bbfda8aSnia *
18790bbfda8aSnia * XXX Since this tends to get called iteratively, there's probably
18800bbfda8aSnia * something better we can do than doing all this relatively expensive
18810bbfda8aSnia * stuff over and over...
18820bbfda8aSnia */
18830bbfda8aSniavoid
18840bbfda8aSniaWMapRestack(WorkSpace *ws)
18850bbfda8aSnia{
18860bbfda8aSnia	WinList *wl;
18870bbfda8aSnia	Window  root, parent; // Dummy
18880bbfda8aSnia	Window  *children, *smallws;
18890bbfda8aSnia	unsigned int nchildren;
18900bbfda8aSnia
18910bbfda8aSnia	/* Get a whole list of the windows on the screen */
18920bbfda8aSnia	nchildren = 0;
18930bbfda8aSnia	XQueryTree(dpy, Scr->Root, &root, &parent, &children, &nchildren);
18940bbfda8aSnia
18950bbfda8aSnia	/*
18960bbfda8aSnia	 * We're presumably dealing with a [often very small] subset of them,
18970bbfda8aSnia	 * but just allocate space for the whole list; easier than trying to
18980bbfda8aSnia	 * shrink down, and really, how big can it possibly be?
18990bbfda8aSnia	 */
19000bbfda8aSnia	smallws = calloc(nchildren, sizeof(Window));
19010bbfda8aSnia
19020bbfda8aSnia	/* Work it up per vscreen */
19030bbfda8aSnia	for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
19040bbfda8aSnia		int j = 0;
19050bbfda8aSnia		const MapSubwindow *msw = vs->wsw->mswl[ws->number];
19060bbfda8aSnia
19070bbfda8aSnia		/* Loop backward (from top to bottom of stack) */
19080bbfda8aSnia		for(int i = nchildren - 1; i >= 0; i--) {
19090bbfda8aSnia			TwmWindow *win = GetTwmWindow(children[i]);
19100bbfda8aSnia
19110bbfda8aSnia			/*
19120bbfda8aSnia			 * Only care about certain windows.  If it's not a window we
19130bbfda8aSnia			 * know about, or not a frame (e.g., an icon), or doesn't
19140bbfda8aSnia			 * occupy this workspace, skip it.
19150bbfda8aSnia			 */
19160bbfda8aSnia			if(win == NULL || win->frame != children[i] || !OCCUPY(win, ws)) {
19170bbfda8aSnia				continue;
19180bbfda8aSnia			}
19190bbfda8aSnia
19200bbfda8aSnia			/* Debug */
19210bbfda8aSnia			if(tracefile) {
19220bbfda8aSnia				fprintf(tracefile, "WMapRestack : w = %lx, win = %p\n",
19230bbfda8aSnia				        children[i], (void *)win);
19240bbfda8aSnia				fflush(tracefile);
19250bbfda8aSnia			}
19260bbfda8aSnia
19270bbfda8aSnia			/* Find this window in the list for the map of this WS */
19280bbfda8aSnia			for(wl = msw->wl; wl != NULL; wl = wl->next) {
19290bbfda8aSnia				/* Debug */
19300bbfda8aSnia				if(tracefile) {
19310bbfda8aSnia					fprintf(tracefile, "WMapRestack : wl = %p, twm_win = %p\n",
19320bbfda8aSnia					        (void *)wl, (void *)wl->twm_win);
19330bbfda8aSnia					fflush(tracefile);
19340bbfda8aSnia				}
19350bbfda8aSnia
19360bbfda8aSnia				if(win == wl->twm_win) {
19370bbfda8aSnia					/* There you are.  Add into our list to restack. */
19380bbfda8aSnia					smallws[j++] = wl->w;
19390bbfda8aSnia					break;
19400bbfda8aSnia				}
19410bbfda8aSnia			}
19420bbfda8aSnia		}
19430bbfda8aSnia
19440bbfda8aSnia		/*
19450bbfda8aSnia		 * Restack the windows in the map.  Note that the order is
19460bbfda8aSnia		 * reversed from earlier; XQueryTree() returns bottom->top,
19470bbfda8aSnia		 * XRestackWindows() is passed top->bottom.
19480bbfda8aSnia		 */
19490bbfda8aSnia		XRestackWindows(dpy, smallws, j);
19500bbfda8aSnia	}
19510bbfda8aSnia
19520bbfda8aSnia	/* Cleanup */
19530bbfda8aSnia	XFree(children);
19540bbfda8aSnia	free(smallws);
19550bbfda8aSnia	return;
19560bbfda8aSnia}
19570bbfda8aSnia
19580bbfda8aSnia
19590bbfda8aSnia
19600bbfda8aSnia
19610bbfda8aSnia/*
19620bbfda8aSnia ****************************************************************
19630bbfda8aSnia *
19640bbfda8aSnia * Bits related to the actual drawing of the windows in the map.
19650bbfda8aSnia *
19660bbfda8aSnia ****************************************************************
19670bbfda8aSnia */
19680bbfda8aSnia
19690bbfda8aSnia/*
19700bbfda8aSnia * Update stuff in the WSM when win's icon name changes
19710bbfda8aSnia */
19720bbfda8aSniavoid
19730bbfda8aSniaWMapUpdateIconName(TwmWindow *win)
19740bbfda8aSnia{
19750bbfda8aSnia	VirtualScreen *vs;
19760bbfda8aSnia	WorkSpace *ws;
19770bbfda8aSnia	WinList *wl;
19780bbfda8aSnia
19790bbfda8aSnia	for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
19800bbfda8aSnia		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
19810bbfda8aSnia			for(wl = vs->wsw->mswl[ws->number]->wl; wl != NULL; wl = wl->next) {
19820bbfda8aSnia				if(win == wl->twm_win) {
19830bbfda8aSnia					WMapRedrawName(vs, wl);
19840bbfda8aSnia					break;
19850bbfda8aSnia				}
19860bbfda8aSnia			}
19870bbfda8aSnia		}
19880bbfda8aSnia	}
19890bbfda8aSnia}
19900bbfda8aSnia
19910bbfda8aSnia
19920bbfda8aSnia/*
19930bbfda8aSnia * Draw a window name into the window's representation in the map-state
19940bbfda8aSnia * WSM.
19950bbfda8aSnia */
19960bbfda8aSniavoid
19970bbfda8aSniaWMapRedrawName(VirtualScreen *vs, WinList *wl)
19980bbfda8aSnia{
19990bbfda8aSnia	ColorPair cp = wl->cp;
20000bbfda8aSnia
20010bbfda8aSnia	if(Scr->ReverseCurrentWorkspace && wl->wlist == vs->wsw->currentwspc) {
20020bbfda8aSnia		InvertColorPair(&cp);
20030bbfda8aSnia	}
20040bbfda8aSnia	WMapRedrawWindow(wl->w, wl->width, wl->height, cp, wl->twm_win->icon_name);
20050bbfda8aSnia}
20060bbfda8aSnia
20070bbfda8aSnia
20080bbfda8aSnia/*
20090bbfda8aSnia * Draw up a window's representation in the map-state WSM, with the
20100bbfda8aSnia * window name.
20110bbfda8aSnia *
20120bbfda8aSnia * The drawing of the window name could probably be done a bit better.
20130bbfda8aSnia * The font size is based on a tiny fraction of the window's height, so
20140bbfda8aSnia * is probably usually too small to be useful, and often appears just as
20150bbfda8aSnia * some odd colored pixels at the top of the window.
20160bbfda8aSnia */
20170bbfda8aSniastatic void
20180bbfda8aSniaWMapRedrawWindow(Window window, int width, int height,
20190bbfda8aSnia                 ColorPair cp, const char *label)
20200bbfda8aSnia{
20210bbfda8aSnia	int x, y;
20220bbfda8aSnia	const MyFont font = Scr->workSpaceMgr.windowFont;
20230bbfda8aSnia
20240bbfda8aSnia	/* Blank out window background color */
20250bbfda8aSnia	XClearWindow(dpy, window);
20260bbfda8aSnia
20270bbfda8aSnia	/* Figure out where to position the name */
20280bbfda8aSnia	{
20290bbfda8aSnia		XRectangle inc_rect;
20300bbfda8aSnia		XRectangle logical_rect;
20310bbfda8aSnia		int strhei, strwid;
20320bbfda8aSnia		int i, descent;
20330bbfda8aSnia		XFontStruct **xfonts;
20340bbfda8aSnia		char **font_names;
20350bbfda8aSnia		int fnum;
20360bbfda8aSnia
20370bbfda8aSnia		XmbTextExtents(font.font_set, label, strlen(label),
20380bbfda8aSnia		               &inc_rect, &logical_rect);
20390bbfda8aSnia		strwid = logical_rect.width;
20400bbfda8aSnia		strhei = logical_rect.height;
20410bbfda8aSnia
20420bbfda8aSnia		/*
20430bbfda8aSnia		 * If it's too tall to fit, just give up now.
20440bbfda8aSnia		 * XXX Probably should still do border stuff below...
20450bbfda8aSnia		 */
20460bbfda8aSnia		if(strhei > height) {
20470bbfda8aSnia			return;
20480bbfda8aSnia		}
20490bbfda8aSnia
20500bbfda8aSnia		x = (width - strwid) / 2;
20510bbfda8aSnia		if(x < 1) {
20520bbfda8aSnia			x = 1;
20530bbfda8aSnia		}
20540bbfda8aSnia
20550bbfda8aSnia		fnum = XFontsOfFontSet(font.font_set, &xfonts, &font_names);
20560bbfda8aSnia		for(i = 0, descent = 0; i < fnum; i++) {
20570bbfda8aSnia			/* xf = xfonts[i]; */
20580bbfda8aSnia			descent = ((descent < (xfonts[i]->max_bounds.descent)) ?
20590bbfda8aSnia			           (xfonts[i]->max_bounds.descent) : descent);
20600bbfda8aSnia		}
20610bbfda8aSnia
20620bbfda8aSnia		y = ((height + strhei) / 2) - descent;
20630bbfda8aSnia	}
20640bbfda8aSnia
20650bbfda8aSnia	/* Draw up borders around the win */
20660bbfda8aSnia	if(Scr->use3Dwmap) {
20670bbfda8aSnia		Draw3DBorder(window, 0, 0, width, height, 1, cp, off, true, false);
20680bbfda8aSnia		FB(cp.fore, cp.back);
20690bbfda8aSnia	}
20700bbfda8aSnia	else {
20710bbfda8aSnia		FB(cp.back, cp.fore);
20720bbfda8aSnia		XFillRectangle(dpy, window, Scr->NormalGC, 0, 0, width, height);
20730bbfda8aSnia		FB(cp.fore, cp.back);
20740bbfda8aSnia	}
20750bbfda8aSnia
20760bbfda8aSnia	/* Write in the name */
20770bbfda8aSnia	if(Scr->Monochrome != COLOR) {
20780bbfda8aSnia		XmbDrawImageString(dpy, window, font.font_set, Scr->NormalGC, x, y,
20790bbfda8aSnia		                   label, strlen(label));
20800bbfda8aSnia	}
20810bbfda8aSnia	else {
20820bbfda8aSnia		XmbDrawString(dpy, window, font.font_set, Scr->NormalGC, x, y,
20830bbfda8aSnia		              label, strlen(label));
20840bbfda8aSnia	}
20850bbfda8aSnia}
20860bbfda8aSnia
20870bbfda8aSnia
20880bbfda8aSnia
20890bbfda8aSnia
20900bbfda8aSnia/*
20910bbfda8aSnia * Processes for adding/removing windows from the WSM
20920bbfda8aSnia */
20930bbfda8aSnia
20940bbfda8aSnia/*
20950bbfda8aSnia * Add a window into any appropriate WSMs' maps.  Called during
20960bbfda8aSnia * AddWindow().
20970bbfda8aSnia */
20980bbfda8aSniavoid
20990bbfda8aSniaWMapAddWindow(TwmWindow *win)
21000bbfda8aSnia{
21010bbfda8aSnia	WorkSpace *ws;
21020bbfda8aSnia
21030bbfda8aSnia	if(!WMapWindowMayBeAdded(win)) {
21040bbfda8aSnia		return;
21050bbfda8aSnia	}
21060bbfda8aSnia
21070bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
21080bbfda8aSnia		if(OCCUPY(win, ws)) {
21090bbfda8aSnia			WMapAddWindowToWorkspace(win, ws);
21100bbfda8aSnia		}
21110bbfda8aSnia	}
21120bbfda8aSnia}
21130bbfda8aSnia
21140bbfda8aSnia
21150bbfda8aSnia/*
21160bbfda8aSnia * Create WSM representation of a given window in a given WS.  Called
21170bbfda8aSnia * when windows get added to a workspace, either via WMapAddWindow()
21180bbfda8aSnia * during the AddWindow() process, or via an occupation change.
21190bbfda8aSnia *
21200bbfda8aSnia * (previously: WMapAddToList())
21210bbfda8aSnia */
21220bbfda8aSniavoid
21230bbfda8aSniaWMapAddWindowToWorkspace(TwmWindow *win, WorkSpace *ws)
21240bbfda8aSnia{
21250bbfda8aSnia	ColorPair cp;
21260bbfda8aSnia
21270bbfda8aSnia	/* Setup coloring for the window */
21280bbfda8aSnia	cp.back = win->title.back;
21290bbfda8aSnia	cp.fore = win->title.fore;
21300bbfda8aSnia	if(Scr->workSpaceMgr.windowcpgiven) {
21310bbfda8aSnia		cp.back = Scr->workSpaceMgr.windowcp.back;
21320bbfda8aSnia		GetColorFromList(Scr->workSpaceMgr.windowBackgroundL,
21330bbfda8aSnia		                 win->name, &win->class, &cp.back);
21340bbfda8aSnia		cp.fore = Scr->workSpaceMgr.windowcp.fore;
21350bbfda8aSnia		GetColorFromList(Scr->workSpaceMgr.windowForegroundL,
21360bbfda8aSnia		                 win->name, &win->class, &cp.fore);
21370bbfda8aSnia	}
21380bbfda8aSnia	if(Scr->use3Dwmap && !Scr->BeNiceToColormap) {
21390bbfda8aSnia		GetShadeColors(&cp);
21400bbfda8aSnia	}
21410bbfda8aSnia
21420bbfda8aSnia	/* We need a copy in each VS */
21430bbfda8aSnia	for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
21440bbfda8aSnia		unsigned int bw;
21450bbfda8aSnia		WinList *wl;
21460bbfda8aSnia		const float wf = (float)(vs->wsw->wwidth  - 2) / (float) vs->w;
21470bbfda8aSnia		const float hf = (float)(vs->wsw->wheight - 2) / (float) vs->h;
21480bbfda8aSnia		MapSubwindow *msw = vs->wsw->mswl[ws->number];
21490bbfda8aSnia
21500bbfda8aSnia		/* Put together its winlist entry */
21510bbfda8aSnia		wl = malloc(sizeof(struct winList));
21520bbfda8aSnia		wl->wlist  = ws;
21530bbfda8aSnia		wl->x      = (int)(win->frame_x * wf);
21540bbfda8aSnia		wl->y      = (int)(win->frame_y * hf);
21550bbfda8aSnia		wl->width  = (unsigned int)((win->frame_width  * wf) + 0.5);
21560bbfda8aSnia		wl->height = (unsigned int)((win->frame_height * hf) + 0.5);
21570bbfda8aSnia		wl->cp     = cp;
21580bbfda8aSnia		wl->twm_win = win;
21590bbfda8aSnia
21600bbfda8aSnia		/* Size the window bits */
21610bbfda8aSnia		bw = 0;
21620bbfda8aSnia		if(!Scr->use3Dwmap) {
21630bbfda8aSnia			bw = 1;
21640bbfda8aSnia			wl->width  -= 2;
21650bbfda8aSnia			wl->height -= 2;
21660bbfda8aSnia		}
21670bbfda8aSnia		if(wl->width < 1) {
21680bbfda8aSnia			wl->width  = 1;
21690bbfda8aSnia		}
21700bbfda8aSnia		if(wl->height < 1) {
21710bbfda8aSnia			wl->height = 1;
21720bbfda8aSnia		}
21730bbfda8aSnia
21740bbfda8aSnia		/* Create its little window */
21750bbfda8aSnia		wl->w = XCreateSimpleWindow(dpy, msw->w, wl->x, wl->y,
21760bbfda8aSnia		                            wl->width, wl->height, bw,
21770bbfda8aSnia		                            Scr->Black, cp.back);
21780bbfda8aSnia
21790bbfda8aSnia		/* Setup cursor and attributes for it */
21800bbfda8aSnia		{
21810bbfda8aSnia			XSetWindowAttributes attr;
21820bbfda8aSnia			unsigned long attrmask;
21830bbfda8aSnia
21840bbfda8aSnia			attr.cursor = handCursor;
21850bbfda8aSnia			attrmask = CWCursor;
21860bbfda8aSnia
21870bbfda8aSnia			if(Scr->BackingStore) {
21880bbfda8aSnia				attr.backing_store = WhenMapped;
21890bbfda8aSnia				attrmask |= CWBackingStore;
21900bbfda8aSnia			}
21910bbfda8aSnia
21920bbfda8aSnia			XChangeWindowAttributes(dpy, wl->w, attrmask, &attr);
21930bbfda8aSnia		}
21940bbfda8aSnia
21950bbfda8aSnia		/* Setup events and stash context bits */
21960bbfda8aSnia		XSelectInput(dpy, wl->w, ExposureMask);
21970bbfda8aSnia		XSaveContext(dpy, wl->w, TwmContext, (XPointer) vs->wsw->twm_win);
21980bbfda8aSnia		XSaveContext(dpy, wl->w, ScreenContext, (XPointer) Scr);
21990bbfda8aSnia		XSaveContext(dpy, wl->w, MapWListContext, (XPointer) wl);
22000bbfda8aSnia
22010bbfda8aSnia		/* Link it onto the front of the list */
22020bbfda8aSnia		wl->next = msw->wl;
22030bbfda8aSnia		msw->wl  = wl;
22040bbfda8aSnia
22050bbfda8aSnia		/*
22060bbfda8aSnia		 * And map it, if its window is mapped.  That'll kick an expose
22070bbfda8aSnia		 * event, which will work its way down to WMapRedrawWindow(), and
22080bbfda8aSnia		 * fill things in.
22090bbfda8aSnia		 */
22100bbfda8aSnia		if(win->mapped) {
22110bbfda8aSnia			XMapWindow(dpy, wl->w);
22120bbfda8aSnia		}
22130bbfda8aSnia	} // And around to the next vscreen
22140bbfda8aSnia}
22150bbfda8aSnia
22160bbfda8aSnia
22170bbfda8aSnia/*
22180bbfda8aSnia * Remove a window from any WSM maps it's in.  Called during window
22190bbfda8aSnia * destruction process.
22200bbfda8aSnia *
22210bbfda8aSnia * (previously: WMapDestroyWindow())
22220bbfda8aSnia */
22230bbfda8aSniavoid
22240bbfda8aSniaWMapRemoveWindow(TwmWindow *win)
22250bbfda8aSnia{
22260bbfda8aSnia	WorkSpace *ws;
22270bbfda8aSnia
22280bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
22290bbfda8aSnia		if(OCCUPY(win, ws)) {
22300bbfda8aSnia			WMapRemoveWindowFromWorkspace(win, ws);
22310bbfda8aSnia		}
22320bbfda8aSnia	}
22330bbfda8aSnia
22340bbfda8aSnia	/*
22350bbfda8aSnia	 * If it's a mapped occupy window, manually hide aways its bits in
22360bbfda8aSnia	 * here.
22370bbfda8aSnia	 *
22380bbfda8aSnia	 * XXX Better belongs inline in caller or separate func?  This is the
22390bbfda8aSnia	 * only thing exposing occupyWin out of occupation.c.
22400bbfda8aSnia	 */
22410bbfda8aSnia	if(win == occupyWin) {
22420bbfda8aSnia		OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow;
22430bbfda8aSnia		XUnmapWindow(dpy, occwin->twm_win->frame);
22440bbfda8aSnia		occwin->twm_win->mapped = false;
22450bbfda8aSnia		occwin->twm_win->occupation = 0;
22460bbfda8aSnia		occupyWin = NULL;
22470bbfda8aSnia	}
22480bbfda8aSnia}
22490bbfda8aSnia
22500bbfda8aSnia
22510bbfda8aSnia/*
22520bbfda8aSnia * Remove window's WSM representation.  Happens from WMapRemoveWindow()
22530bbfda8aSnia * as part of the window destruction process, and in the occupation
22540bbfda8aSnia * change process.
22550bbfda8aSnia *
22560bbfda8aSnia * (previously: WMapRemoveFromList())
22570bbfda8aSnia */
22580bbfda8aSniavoid
22590bbfda8aSniaWMapRemoveWindowFromWorkspace(TwmWindow *win, WorkSpace *ws)
22600bbfda8aSnia{
22610bbfda8aSnia	VirtualScreen *vs;
22620bbfda8aSnia
22630bbfda8aSnia	for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
22640bbfda8aSnia		WinList **prev = &vs->wsw->mswl[ws->number]->wl;
22650bbfda8aSnia
22660bbfda8aSnia		/* Pull it out of the list and destroy it */
22670bbfda8aSnia		for(WinList *wl = *prev ; wl != NULL ; wl = wl->next) {
22680bbfda8aSnia			if(win != wl->twm_win) {
22690bbfda8aSnia				/* Not it */
22700bbfda8aSnia				prev = &wl->next;
22710bbfda8aSnia				continue;
22720bbfda8aSnia			}
22730bbfda8aSnia
22740bbfda8aSnia			/* There you are.  Unlink and kill */
22750bbfda8aSnia			*prev = wl->next;
22760bbfda8aSnia
22770bbfda8aSnia			XDeleteContext(dpy, wl->w, TwmContext);
22780bbfda8aSnia			XDeleteContext(dpy, wl->w, ScreenContext);
22790bbfda8aSnia			XDeleteContext(dpy, wl->w, MapWListContext);
22800bbfda8aSnia			XDestroyWindow(dpy, wl->w);
22810bbfda8aSnia			free(wl);
22820bbfda8aSnia
22830bbfda8aSnia			/* Around to the next vscreen */
22840bbfda8aSnia			break;
22850bbfda8aSnia		}
22860bbfda8aSnia	}
22870bbfda8aSnia}
22880bbfda8aSnia
22890bbfda8aSnia
22900bbfda8aSnia
22910bbfda8aSnia
22920bbfda8aSnia/*
22930bbfda8aSnia ****************************************************************
22940bbfda8aSnia *
22950bbfda8aSnia * Utils-ish funcs
22960bbfda8aSnia *
22970bbfda8aSnia ****************************************************************
22980bbfda8aSnia */
22990bbfda8aSnia
23000bbfda8aSnia/*
23010bbfda8aSnia * This is really more util.c fodder, but leaving it here for now because
23020bbfda8aSnia * it's only used once in WMapRedrawName().  If we start finding external
23030bbfda8aSnia * uses for it, it should be moved.
23040bbfda8aSnia */
23050bbfda8aSniastatic void
23060bbfda8aSniaInvertColorPair(ColorPair *cp)
23070bbfda8aSnia{
23080bbfda8aSnia	Pixel save;
23090bbfda8aSnia
23100bbfda8aSnia	save = cp->fore;
23110bbfda8aSnia	cp->fore = cp->back;
23120bbfda8aSnia	cp->back = save;
23130bbfda8aSnia	save = cp->shadc;
23140bbfda8aSnia	cp->shadc = cp->shadd;
23150bbfda8aSnia	cp->shadd = save;
23160bbfda8aSnia}
23170bbfda8aSnia
23180bbfda8aSnia
23190bbfda8aSnia/*
23200bbfda8aSnia * Verify if a window may be added to the workspace map.
23210bbfda8aSnia * This is not allowed for
23220bbfda8aSnia * - icon managers
23230bbfda8aSnia * - the occupy window
23240bbfda8aSnia * - workspace manager windows
23250bbfda8aSnia * - or, optionally, windows with full occupation.
23260bbfda8aSnia */
23270bbfda8aSniabool
23280bbfda8aSniaWMapWindowMayBeAdded(TwmWindow *win)
23290bbfda8aSnia{
23300bbfda8aSnia	if(win->isiconmgr) {
23310bbfda8aSnia		return false;
23320bbfda8aSnia	}
23330bbfda8aSnia	if(win == Scr->workSpaceMgr.occupyWindow->twm_win) {
23340bbfda8aSnia		return false;
23350bbfda8aSnia	}
23360bbfda8aSnia	if(win->iswspmgr) {
23370bbfda8aSnia		return false;
23380bbfda8aSnia	}
23390bbfda8aSnia	if(Scr->workSpaceMgr.noshowoccupyall &&
23400bbfda8aSnia	                win->occupation == fullOccupation) {
23410bbfda8aSnia		return false;
23420bbfda8aSnia	}
23430bbfda8aSnia	return true;
23440bbfda8aSnia}
2345