10bbfda8aSnia/*
20bbfda8aSnia * Various workspace handling and utilities.
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 "animate.h"
140bbfda8aSnia#include "clicktofocus.h"
150bbfda8aSnia#include "ctwm_atoms.h"
160bbfda8aSnia#include "drawing.h"
170bbfda8aSnia#include "functions.h"
180bbfda8aSnia#include "iconmgr.h"
190bbfda8aSnia#include "image.h"
200bbfda8aSnia#include "otp.h"
210bbfda8aSnia#include "screen.h"
220bbfda8aSnia#include "vscreen.h"
230bbfda8aSnia#include "win_ops.h"
240bbfda8aSnia#include "win_utils.h"
250bbfda8aSnia#include "workspace_manager.h"
260bbfda8aSnia#include "workspace_utils.h"
270bbfda8aSnia
280bbfda8aSnia#ifdef EWMH
290bbfda8aSnia#  include "ewmh_atoms.h"
300bbfda8aSnia#endif
310bbfda8aSnia
320bbfda8aSnia
330bbfda8aSnia/*
340bbfda8aSnia * XXX I'm not sure this should be here; maybe it's more of a per-screen
350bbfda8aSnia * thing, and so should be in the Screen struct?
360bbfda8aSnia */
370bbfda8aSniabool useBackgroundInfo = false;
380bbfda8aSnia
390bbfda8aSnia
400bbfda8aSnia/*
410bbfda8aSnia * Move the display (of a given vs) over to a new workspace.
420bbfda8aSnia */
430bbfda8aSniavoid
440bbfda8aSniaGotoWorkSpace(VirtualScreen *vs, WorkSpace *ws)
450bbfda8aSnia{
460bbfda8aSnia	TwmWindow            *twmWin;
470bbfda8aSnia	WorkSpace            *oldws, *newws;
480bbfda8aSnia	WList                *wl, *wl1;
490bbfda8aSnia	WinList              *winl;
500bbfda8aSnia	XSetWindowAttributes attr;
510bbfda8aSnia	long                 eventMask;
520bbfda8aSnia	IconMgr              *iconmgr;
530bbfda8aSnia	Window               oldw;
540bbfda8aSnia	Window               neww;
550bbfda8aSnia	TwmWindow            *focuswindow;
560bbfda8aSnia	VirtualScreen        *tmpvs;
570bbfda8aSnia
580bbfda8aSnia	if(! Scr->workSpaceManagerActive) {
590bbfda8aSnia		return;
600bbfda8aSnia	}
610bbfda8aSnia	for(tmpvs = Scr->vScreenList; tmpvs != NULL; tmpvs = tmpvs->next) {
620bbfda8aSnia		if(ws == tmpvs->wsw->currentwspc) {
630bbfda8aSnia			XBell(dpy, 0);
640bbfda8aSnia			return;
650bbfda8aSnia		}
660bbfda8aSnia	}
670bbfda8aSnia	oldws = vs->wsw->currentwspc;
680bbfda8aSnia	newws = ws;
690bbfda8aSnia	if(oldws == newws) {
700bbfda8aSnia		return;
710bbfda8aSnia	}
720bbfda8aSnia
730bbfda8aSnia	/* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() and below */
740bbfda8aSnia	if(useBackgroundInfo && ! Scr->DontPaintRootWindow) {
750bbfda8aSnia		if(newws->image == NULL) {
760bbfda8aSnia			XSetWindowBackground(dpy, vs->window, newws->backcp.back);
770bbfda8aSnia		}
780bbfda8aSnia		else {
790bbfda8aSnia			XSetWindowBackgroundPixmap(dpy, vs->window, newws->image->pixmap);
800bbfda8aSnia		}
810bbfda8aSnia		XClearWindow(dpy, vs->window);
820bbfda8aSnia	}
830bbfda8aSnia
840bbfda8aSnia	/* If SaveWorkspaceFocus is on, save the focus of the last window. */
850bbfda8aSnia	if(Scr->SaveWorkspaceFocus) {
860bbfda8aSnia		oldws->save_focus = Scr->Focus;
870bbfda8aSnia	}
880bbfda8aSnia
890bbfda8aSnia	focuswindow = NULL;
900bbfda8aSnia	/* For better visual effect, the order or map/unmap is important:
910bbfda8aSnia	   - map from top to bottom.
920bbfda8aSnia	   - unmap from bottom to top.
930bbfda8aSnia	   - unmap after mapping.
940bbfda8aSnia	   The guiding factor: at any point during the transition, something
950bbfda8aSnia	   should be visible only if it was visible before the transition or if
960bbfda8aSnia	   it will be visible at the end.  */
970bbfda8aSnia	OtpCheckConsistency();
980bbfda8aSnia
990bbfda8aSnia	for(twmWin = OtpTopWin(); twmWin != NULL;
1000bbfda8aSnia	                twmWin = OtpNextWinDown(twmWin)) {
1010bbfda8aSnia
1020bbfda8aSnia		if(OCCUPY(twmWin, newws)) {
1030bbfda8aSnia			if(!twmWin->vs) {
1040bbfda8aSnia				DisplayWin(vs, twmWin);
1050bbfda8aSnia			}
1060bbfda8aSnia#ifdef EWMH
1070bbfda8aSnia			if(OCCUPY(twmWin, oldws)) {
1080bbfda8aSnia				/*
1090bbfda8aSnia				 * If the window remains visible, re-order the workspace
1100bbfda8aSnia				 * numbers in NET_WM_DESKTOP.
1110bbfda8aSnia				 */
1120bbfda8aSnia				EwmhSet_NET_WM_DESKTOP_ws(twmWin, newws);
1130bbfda8aSnia			}
1140bbfda8aSnia#endif
1150bbfda8aSnia		}
1160bbfda8aSnia	}
1170bbfda8aSnia
1180bbfda8aSnia	for(twmWin = OtpBottomWin(); twmWin != NULL;
1190bbfda8aSnia	                twmWin = OtpNextWinUp(twmWin)) {
1200bbfda8aSnia		if(twmWin->vs == vs) {
1210bbfda8aSnia			if(!OCCUPY(twmWin, newws)) {
1220bbfda8aSnia
1230bbfda8aSnia				Vanish(vs, twmWin);
124b18c2d1eSnia#ifdef VSCREEN
1250bbfda8aSnia				/*
1260bbfda8aSnia				 * Now that the window has Vanished from one virtual screen,
1270bbfda8aSnia				 * check to see if it is wanted on another one.
1280bbfda8aSnia				 * This is relatively rare, so don't bother with the
1290bbfda8aSnia				 * top-to-bottom order here.
1300bbfda8aSnia				 */
1310bbfda8aSnia				if(Scr->numVscreens > 1) {
132b18c2d1eSnia					VirtualScreen *tvs;
1330bbfda8aSnia					for(tvs = Scr->vScreenList; tvs != NULL; tvs = tvs->next) {
1340bbfda8aSnia						if(tvs == vs) { /* no, not back on the old one */
1350bbfda8aSnia							continue;
1360bbfda8aSnia						}
1370bbfda8aSnia						if(OCCUPY(twmWin, tvs->wsw->currentwspc)) {
1380bbfda8aSnia							DisplayWin(tvs, twmWin);
1390bbfda8aSnia							break;
1400bbfda8aSnia						}
1410bbfda8aSnia					}
1420bbfda8aSnia				}
143b18c2d1eSnia#endif
1440bbfda8aSnia			}
1450bbfda8aSnia			else if(twmWin->hasfocusvisible) {
1460bbfda8aSnia				focuswindow = twmWin;
1470bbfda8aSnia				SetFocusVisualAttributes(focuswindow, false);
1480bbfda8aSnia			}
1490bbfda8aSnia		}
1500bbfda8aSnia	}
1510bbfda8aSnia	OtpCheckConsistency();
1520bbfda8aSnia
1530bbfda8aSnia	/*
1540bbfda8aSnia	   Reorganize icon manager window lists
1550bbfda8aSnia	*/
1560bbfda8aSnia	for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
1570bbfda8aSnia		wl = twmWin->iconmanagerlist;
1580bbfda8aSnia		if(wl == NULL) {
1590bbfda8aSnia			continue;
1600bbfda8aSnia		}
1610bbfda8aSnia		if(OCCUPY(wl->iconmgr->twm_win, newws)) {
1620bbfda8aSnia			continue;
1630bbfda8aSnia		}
1640bbfda8aSnia		wl1 = wl;
1650bbfda8aSnia		wl  = wl->nextv;
1660bbfda8aSnia		while(wl != NULL) {
1670bbfda8aSnia			if(OCCUPY(wl->iconmgr->twm_win, newws)) {
1680bbfda8aSnia				break;
1690bbfda8aSnia			}
1700bbfda8aSnia			wl1 = wl;
1710bbfda8aSnia			wl  = wl->nextv;
1720bbfda8aSnia		}
1730bbfda8aSnia		if(wl != NULL) {
1740bbfda8aSnia			wl1->nextv = wl->nextv;
1750bbfda8aSnia			wl->nextv  = twmWin->iconmanagerlist;
1760bbfda8aSnia			twmWin->iconmanagerlist = wl;
1770bbfda8aSnia		}
1780bbfda8aSnia	}
1790bbfda8aSnia	wl = NULL;
1800bbfda8aSnia	for(iconmgr = newws->iconmgr; iconmgr; iconmgr = iconmgr->next) {
1810bbfda8aSnia		if(iconmgr->first) {
1820bbfda8aSnia			wl = iconmgr->first;
1830bbfda8aSnia			break;
1840bbfda8aSnia		}
1850bbfda8aSnia	}
1860bbfda8aSnia	CurrentIconManagerEntry(wl);
1870bbfda8aSnia	if(focuswindow) {
1880bbfda8aSnia		SetFocusVisualAttributes(focuswindow, true);
1890bbfda8aSnia	}
1900bbfda8aSnia	vs->wsw->currentwspc = newws;
1910bbfda8aSnia	if(Scr->ReverseCurrentWorkspace && vs->wsw->state == WMS_map) {
1920bbfda8aSnia		MapSubwindow *msw = vs->wsw->mswl [oldws->number];
1930bbfda8aSnia		for(winl = msw->wl; winl != NULL; winl = winl->next) {
1940bbfda8aSnia			WMapRedrawName(vs, winl);
1950bbfda8aSnia		}
1960bbfda8aSnia		msw = vs->wsw->mswl [newws->number];
1970bbfda8aSnia		for(winl = msw->wl; winl != NULL; winl = winl->next) {
1980bbfda8aSnia			WMapRedrawName(vs, winl);
1990bbfda8aSnia		}
2000bbfda8aSnia	}
2010bbfda8aSnia	else if(vs->wsw->state == WMS_buttons) {
2020bbfda8aSnia		ButtonSubwindow *bsw = vs->wsw->bswl [oldws->number];
2030bbfda8aSnia		PaintWsButton(WSPCWINDOW, vs, bsw->w, oldws->label, oldws->cp, off);
2040bbfda8aSnia		bsw = vs->wsw->bswl [newws->number];
2050bbfda8aSnia		PaintWsButton(WSPCWINDOW, vs, bsw->w, newws->label, newws->cp,  on);
2060bbfda8aSnia	}
2070bbfda8aSnia	oldws->iconmgr = Scr->iconmgr;
2080bbfda8aSnia	Scr->iconmgr = newws->iconmgr;
2090bbfda8aSnia
2100bbfda8aSnia	/* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() and above */
2110bbfda8aSnia	oldw = vs->wsw->mswl [oldws->number]->w;
2120bbfda8aSnia	neww = vs->wsw->mswl [newws->number]->w;
2130bbfda8aSnia	if(useBackgroundInfo) {
2140bbfda8aSnia		if(oldws->image == NULL || Scr->NoImagesInWorkSpaceManager) {
2150bbfda8aSnia			XSetWindowBackground(dpy, oldw, oldws->backcp.back);
2160bbfda8aSnia		}
2170bbfda8aSnia		else {
2180bbfda8aSnia			XSetWindowBackgroundPixmap(dpy, oldw, oldws->image->pixmap);
2190bbfda8aSnia		}
2200bbfda8aSnia	}
2210bbfda8aSnia	else {
2220bbfda8aSnia		if(Scr->workSpaceMgr.defImage == NULL || Scr->NoImagesInWorkSpaceManager) {
2230bbfda8aSnia			XSetWindowBackground(dpy, oldw, Scr->workSpaceMgr.defColors.back);
2240bbfda8aSnia		}
2250bbfda8aSnia		else {
2260bbfda8aSnia			XSetWindowBackgroundPixmap(dpy, oldw, Scr->workSpaceMgr.defImage->pixmap);
2270bbfda8aSnia		}
2280bbfda8aSnia	}
2290bbfda8aSnia	attr.border_pixel = Scr->workSpaceMgr.defBorderColor;
2300bbfda8aSnia	XChangeWindowAttributes(dpy, oldw, CWBorderPixel, &attr);
2310bbfda8aSnia
2320bbfda8aSnia	if(Scr->workSpaceMgr.curImage == NULL) {
2330bbfda8aSnia		if(Scr->workSpaceMgr.curPaint) {
2340bbfda8aSnia			XSetWindowBackground(dpy, neww, Scr->workSpaceMgr.curColors.back);
2350bbfda8aSnia		}
2360bbfda8aSnia	}
2370bbfda8aSnia	else {
2380bbfda8aSnia		XSetWindowBackgroundPixmap(dpy, neww, Scr->workSpaceMgr.curImage->pixmap);
2390bbfda8aSnia	}
2400bbfda8aSnia	attr.border_pixel =  Scr->workSpaceMgr.curBorderColor;
2410bbfda8aSnia	XChangeWindowAttributes(dpy, neww, CWBorderPixel, &attr);
2420bbfda8aSnia
2430bbfda8aSnia	XClearWindow(dpy, oldw);
2440bbfda8aSnia	XClearWindow(dpy, neww);
2450bbfda8aSnia
2460bbfda8aSnia	eventMask = mask_out_event(Scr->Root, PropertyChangeMask);
2470bbfda8aSnia
2480bbfda8aSnia	XChangeProperty(dpy, Scr->Root, XA_WM_CURRENTWORKSPACE, XA_STRING, 8,
2490bbfda8aSnia	                PropModeReplace, (unsigned char *) newws->name, strlen(newws->name));
2500bbfda8aSnia#ifdef EWMH
2510bbfda8aSnia	{
2520bbfda8aSnia		long number = newws->number;
2530bbfda8aSnia		/*
2540bbfda8aSnia		 * TODO: this should probably not use Scr->Root but ->XineramaRoot.
2550bbfda8aSnia		 * That is the real root window if we're using virtual screens.
2560bbfda8aSnia		 * Also, on the real root it would need values for each of the
2570bbfda8aSnia		 * virtual roots, but that doesn't fit in the EWMH ideas.
2580bbfda8aSnia		 */
2590bbfda8aSnia		XChangeProperty(dpy, Scr->Root, XA__NET_CURRENT_DESKTOP,
2600bbfda8aSnia		                XA_CARDINAL, 32,
2610bbfda8aSnia		                PropModeReplace, (unsigned char *) &number, 1);
2620bbfda8aSnia	}
2630bbfda8aSnia#endif /* EWMH */
2640bbfda8aSnia
2650bbfda8aSnia	restore_mask(Scr->Root, eventMask);
2660bbfda8aSnia
2670bbfda8aSnia	/*    XDestroyWindow (dpy, cachew);*/
2680bbfda8aSnia	if(Scr->ChangeWorkspaceFunction.func != 0) {
2690bbfda8aSnia		char *action;
2700bbfda8aSnia		XEvent event;
2710bbfda8aSnia
2720bbfda8aSnia		action = Scr->ChangeWorkspaceFunction.item ?
2730bbfda8aSnia		         Scr->ChangeWorkspaceFunction.item->action : NULL;
2740bbfda8aSnia		ExecuteFunction(Scr->ChangeWorkspaceFunction.func, action,
2750bbfda8aSnia		                (Window) 0, NULL, &event, C_ROOT, false);
2760bbfda8aSnia	}
2770bbfda8aSnia
2780bbfda8aSnia	/* If SaveWorkspaceFocus is on, try to restore the focus to the last
2790bbfda8aSnia	   window which was focused when we left this workspace. */
2800bbfda8aSnia	if(Scr->SaveWorkspaceFocus && newws->save_focus) {
2810bbfda8aSnia		twmWin = newws->save_focus;
2820bbfda8aSnia		if(OCCUPY(twmWin, newws)) {     /* check should not even be needed anymore */
2830bbfda8aSnia			WarpToWindow(twmWin, false);
2840bbfda8aSnia		}
2850bbfda8aSnia		else {
2860bbfda8aSnia			newws->save_focus = NULL;
2870bbfda8aSnia		}
2880bbfda8aSnia	}
2890bbfda8aSnia
2900bbfda8aSnia	/* keep track of the order of the workspaces across restarts */
2910bbfda8aSnia	CtwmSetVScreenMap(dpy, Scr->Root, Scr->vScreenList);
2920bbfda8aSnia
2930bbfda8aSnia	XSync(dpy, 0);
2940bbfda8aSnia	if(Scr->ClickToFocus || Scr->SloppyFocus) {
2950bbfda8aSnia		set_last_window(newws);
2960bbfda8aSnia	}
2970bbfda8aSnia	MaybeAnimate = true;
2980bbfda8aSnia}
2990bbfda8aSnia
3000bbfda8aSnia
3010bbfda8aSnia
3020bbfda8aSnia/*
3030bbfda8aSnia * Various frontends to GotoWorkSpace()
3040bbfda8aSnia */
3050bbfda8aSnia
3060bbfda8aSnia/*
3070bbfda8aSnia * Simplify redundant checks.  If no multiple workspaces, or no vs given
3080bbfda8aSnia * to the func, there's nothing to do.
3090bbfda8aSnia */
3100bbfda8aSnia#define GWS_CHECK do { \
3110bbfda8aSnia                if(! Scr->workSpaceManagerActive) {   \
3120bbfda8aSnia                        return;                       \
3130bbfda8aSnia                }                                     \
3140bbfda8aSnia                if(!vs) {                             \
3150bbfda8aSnia                        return;                       \
3160bbfda8aSnia                }                                     \
3170bbfda8aSnia        } while(0)
3180bbfda8aSnia
3190bbfda8aSniavoid
3200bbfda8aSniaGotoWorkSpaceByName(VirtualScreen *vs, const char *wname)
3210bbfda8aSnia{
3220bbfda8aSnia	WorkSpace *ws;
3230bbfda8aSnia
3240bbfda8aSnia	GWS_CHECK;
3250bbfda8aSnia
3260bbfda8aSnia	ws = GetWorkspace(wname);
3270bbfda8aSnia	if(ws == NULL) {
3280bbfda8aSnia		return;
3290bbfda8aSnia	}
3300bbfda8aSnia	GotoWorkSpace(vs, ws);
3310bbfda8aSnia}
3320bbfda8aSnia
3330bbfda8aSnia
3340bbfda8aSniavoid
3350bbfda8aSniaGotoWorkSpaceByNumber(VirtualScreen *vs, int workspacenum)
3360bbfda8aSnia{
3370bbfda8aSnia	WorkSpace *ws;
3380bbfda8aSnia
3390bbfda8aSnia	GWS_CHECK;
3400bbfda8aSnia
3410bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
3420bbfda8aSnia		if(ws->number == workspacenum) {
3430bbfda8aSnia			break;
3440bbfda8aSnia		}
3450bbfda8aSnia	}
3460bbfda8aSnia	if(ws == NULL) {
3470bbfda8aSnia		return;
3480bbfda8aSnia	}
3490bbfda8aSnia	GotoWorkSpace(vs, ws);
3500bbfda8aSnia}
3510bbfda8aSnia
3520bbfda8aSnia
3530bbfda8aSniavoid
3540bbfda8aSniaGotoPrevWorkSpace(VirtualScreen *vs)
3550bbfda8aSnia{
3560bbfda8aSnia	WorkSpace *ws1, *ws2;
3570bbfda8aSnia
3580bbfda8aSnia	GWS_CHECK;
3590bbfda8aSnia
3600bbfda8aSnia	ws1 = Scr->workSpaceMgr.workSpaceList;
3610bbfda8aSnia	if(ws1 == NULL) {
3620bbfda8aSnia		return;
3630bbfda8aSnia	}
3640bbfda8aSnia	ws2 = ws1->next;
3650bbfda8aSnia
3660bbfda8aSnia	while((ws2 != vs->wsw->currentwspc) && (ws2 != NULL)) {
3670bbfda8aSnia		ws1 = ws2;
3680bbfda8aSnia		ws2 = ws2->next;
3690bbfda8aSnia	}
3700bbfda8aSnia	GotoWorkSpace(vs, ws1);
3710bbfda8aSnia}
3720bbfda8aSnia
3730bbfda8aSnia
3740bbfda8aSniavoid
3750bbfda8aSniaGotoNextWorkSpace(VirtualScreen *vs)
3760bbfda8aSnia{
3770bbfda8aSnia	WorkSpace *ws;
3780bbfda8aSnia
3790bbfda8aSnia	GWS_CHECK;
3800bbfda8aSnia
3810bbfda8aSnia	ws = vs->wsw->currentwspc;
3820bbfda8aSnia	ws = (ws->next != NULL) ? ws->next : Scr->workSpaceMgr.workSpaceList;
3830bbfda8aSnia	GotoWorkSpace(vs, ws);
3840bbfda8aSnia}
3850bbfda8aSnia
3860bbfda8aSnia
3870bbfda8aSniavoid
3880bbfda8aSniaGotoRightWorkSpace(VirtualScreen *vs)
3890bbfda8aSnia{
3900bbfda8aSnia	WorkSpace *ws;
3910bbfda8aSnia	int number, columns, count;
3920bbfda8aSnia
3930bbfda8aSnia	GWS_CHECK;
3940bbfda8aSnia
3950bbfda8aSnia	ws      = vs->wsw->currentwspc;
3960bbfda8aSnia	number  = ws->number;
3970bbfda8aSnia	columns = Scr->workSpaceMgr.columns;
3980bbfda8aSnia	count   = Scr->workSpaceMgr.count;
3990bbfda8aSnia	number++;
4000bbfda8aSnia	if((number % columns) == 0) {
4010bbfda8aSnia		number -= columns;
4020bbfda8aSnia	}
4030bbfda8aSnia	else if(number >= count) {
4040bbfda8aSnia		number = (number / columns) * columns;
4050bbfda8aSnia	}
4060bbfda8aSnia
4070bbfda8aSnia	GotoWorkSpaceByNumber(vs, number);
4080bbfda8aSnia}
4090bbfda8aSnia
4100bbfda8aSnia
4110bbfda8aSniavoid
4120bbfda8aSniaGotoLeftWorkSpace(VirtualScreen *vs)
4130bbfda8aSnia{
4140bbfda8aSnia	WorkSpace *ws;
4150bbfda8aSnia	int number, columns, count;
4160bbfda8aSnia
4170bbfda8aSnia	GWS_CHECK;
4180bbfda8aSnia
4190bbfda8aSnia	ws      = vs->wsw->currentwspc;
4200bbfda8aSnia	number  = ws->number;
4210bbfda8aSnia	columns = Scr->workSpaceMgr.columns;
4220bbfda8aSnia	count   = Scr->workSpaceMgr.count;
4230bbfda8aSnia	number += (number % columns) ? -1 : (columns - 1);
4240bbfda8aSnia	if(number >= count) {
4250bbfda8aSnia		number = count - 1;
4260bbfda8aSnia	}
4270bbfda8aSnia	GotoWorkSpaceByNumber(vs, number);
4280bbfda8aSnia}
4290bbfda8aSnia
4300bbfda8aSnia
4310bbfda8aSniavoid
4320bbfda8aSniaGotoUpWorkSpace(VirtualScreen *vs)
4330bbfda8aSnia{
4340bbfda8aSnia	WorkSpace *ws;
4350bbfda8aSnia	int number, lines, columns, count;
4360bbfda8aSnia
4370bbfda8aSnia	GWS_CHECK;
4380bbfda8aSnia
4390bbfda8aSnia	ws      = vs->wsw->currentwspc;
4400bbfda8aSnia	number  = ws->number;
4410bbfda8aSnia	lines   = Scr->workSpaceMgr.lines;
4420bbfda8aSnia	columns = Scr->workSpaceMgr.columns;
4430bbfda8aSnia	count   = Scr->workSpaceMgr.count;
4440bbfda8aSnia	number -=  columns;
4450bbfda8aSnia	if(number < 0) {
4460bbfda8aSnia		number += lines * columns;
4470bbfda8aSnia		/* If the number of workspaces is not a multiple of nr of columns */
4480bbfda8aSnia		if(number >= count) {
4490bbfda8aSnia			number -= columns;
4500bbfda8aSnia		}
4510bbfda8aSnia	}
4520bbfda8aSnia	GotoWorkSpaceByNumber(vs, number);
4530bbfda8aSnia}
4540bbfda8aSnia
4550bbfda8aSnia
4560bbfda8aSniavoid
4570bbfda8aSniaGotoDownWorkSpace(VirtualScreen *vs)
4580bbfda8aSnia{
4590bbfda8aSnia	WorkSpace *ws;
4600bbfda8aSnia	int number, columns, count;
4610bbfda8aSnia
4620bbfda8aSnia	GWS_CHECK;
4630bbfda8aSnia
4640bbfda8aSnia	ws      = vs->wsw->currentwspc;
4650bbfda8aSnia	number  = ws->number;
4660bbfda8aSnia	columns = Scr->workSpaceMgr.columns;
4670bbfda8aSnia	count   = Scr->workSpaceMgr.count;
4680bbfda8aSnia	number +=  columns;
4690bbfda8aSnia	if(number >= count) {
4700bbfda8aSnia		number %= columns;
4710bbfda8aSnia	}
4720bbfda8aSnia	GotoWorkSpaceByNumber(vs, number);
4730bbfda8aSnia}
4740bbfda8aSnia
4750bbfda8aSnia#undef GWS_CHECK
4760bbfda8aSnia
4770bbfda8aSnia
4780bbfda8aSnia
4790bbfda8aSnia/*
4800bbfda8aSnia * Show the background (by hiding all windows) or undo it.
4810bbfda8aSnia * f.showbackground, also can be called via EWMH bits.
4820bbfda8aSnia *
4830bbfda8aSnia * newstate is the desired showing state.
4840bbfda8aSnia * Pass -1 to toggle, 1 to show the background,
4850bbfda8aSnia * or 0 to re-show the windows.
4860bbfda8aSnia *
4870bbfda8aSnia * XXX Doesn't really belong here; more of a functions.c-ish thing
4880bbfda8aSnia * probably.  But left here for the moment.
4890bbfda8aSnia */
4900bbfda8aSniavoid
4910bbfda8aSniaShowBackground(VirtualScreen *vs, int newstate)
4920bbfda8aSnia{
4930bbfda8aSnia	static int state = 0;
4940bbfda8aSnia	TwmWindow *twmWin;
4950bbfda8aSnia
4960bbfda8aSnia	if(newstate == state) {
4970bbfda8aSnia		return;
4980bbfda8aSnia	}
4990bbfda8aSnia
5000bbfda8aSnia	if(state) {
5010bbfda8aSnia		for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
5020bbfda8aSnia			if(twmWin->savevs == vs) {
5030bbfda8aSnia				DisplayWin(vs, twmWin);
5040bbfda8aSnia			}
5050bbfda8aSnia			twmWin->savevs = NULL;
5060bbfda8aSnia		}
5070bbfda8aSnia		state = 0;
5080bbfda8aSnia	}
5090bbfda8aSnia	else {
5100bbfda8aSnia		for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
5110bbfda8aSnia			if(twmWin->vs == vs
5120bbfda8aSnia#ifdef EWMH
5130bbfda8aSnia			                /* leave wt_Desktop and wt_Dock visible */
5140bbfda8aSnia			                && twmWin->ewmhWindowType == wt_Normal
5150bbfda8aSnia#endif /* EWMH */
5160bbfda8aSnia			  ) {
5170bbfda8aSnia				twmWin->savevs = twmWin->vs;
5180bbfda8aSnia				Vanish(vs, twmWin);
5190bbfda8aSnia			}
5200bbfda8aSnia		}
5210bbfda8aSnia		state = 1;
5220bbfda8aSnia	}
5230bbfda8aSnia#ifdef EWMH
5240bbfda8aSnia	EwmhSet_NET_SHOWING_DESKTOP(state);
5250bbfda8aSnia#endif /* EWMH */
5260bbfda8aSnia}
5270bbfda8aSnia
5280bbfda8aSnia
5290bbfda8aSnia/*
5300bbfda8aSnia * Get the name of the currently active WS.  Used in Execute() for
5310bbfda8aSnia * sub'ing in $currentworkspace in executing commands.
5320bbfda8aSnia */
5330bbfda8aSniachar *
5340bbfda8aSniaGetCurrentWorkSpaceName(VirtualScreen *vs)
5350bbfda8aSnia{
5360bbfda8aSnia	if(! Scr->workSpaceManagerActive) {
5370bbfda8aSnia		return (NULL);
5380bbfda8aSnia	}
5390bbfda8aSnia	if(!vs) {
5400bbfda8aSnia		vs = Scr->vScreenList;
5410bbfda8aSnia	}
5420bbfda8aSnia	return vs->wsw->currentwspc->name;
5430bbfda8aSnia}
5440bbfda8aSnia
5450bbfda8aSnia
5460bbfda8aSnia/*
5470bbfda8aSnia * Find workspace by name
5480bbfda8aSnia */
5490bbfda8aSniaWorkSpace *
5500bbfda8aSniaGetWorkspace(const char *wname)
5510bbfda8aSnia{
5520bbfda8aSnia	WorkSpace *ws;
5530bbfda8aSnia
5540bbfda8aSnia	/* Guard */
5550bbfda8aSnia	if(!wname) {
5560bbfda8aSnia		return (NULL);
5570bbfda8aSnia	}
5580bbfda8aSnia
5590bbfda8aSnia	/* Check by label */
5600bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
5610bbfda8aSnia		if(strcmp(ws->label, wname) == 0) {
5620bbfda8aSnia			return ws;
5630bbfda8aSnia		}
5640bbfda8aSnia	}
5650bbfda8aSnia
5660bbfda8aSnia	/* Check by name */
5670bbfda8aSnia	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
5680bbfda8aSnia		if(strcmp(ws->name, wname) == 0) {
5690bbfda8aSnia			return ws;
5700bbfda8aSnia		}
5710bbfda8aSnia	}
5720bbfda8aSnia
5730bbfda8aSnia	/* Nope */
5740bbfda8aSnia	return NULL;
5750bbfda8aSnia}
576