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