Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Various workspace handling and utilities.
      3  */
      4 
      5 #include "ctwm.h"
      6 
      7 #include <stdio.h>
      8 #include <string.h>
      9 #include <stdlib.h>
     10 
     11 #include <X11/Xatom.h>
     12 
     13 #include "animate.h"
     14 #include "clicktofocus.h"
     15 #include "ctwm_atoms.h"
     16 #include "drawing.h"
     17 #include "functions.h"
     18 #include "iconmgr.h"
     19 #include "image.h"
     20 #include "otp.h"
     21 #include "screen.h"
     22 #include "vscreen.h"
     23 #include "win_ops.h"
     24 #include "win_utils.h"
     25 #include "workspace_manager.h"
     26 #include "workspace_utils.h"
     27 
     28 #ifdef EWMH
     29 #  include "ewmh_atoms.h"
     30 #endif
     31 
     32 
     33 /*
     34  * XXX I'm not sure this should be here; maybe it's more of a per-screen
     35  * thing, and so should be in the Screen struct?
     36  */
     37 bool useBackgroundInfo = false;
     38 
     39 
     40 /*
     41  * Move the display (of a given vs) over to a new workspace.
     42  */
     43 void
     44 GotoWorkSpace(VirtualScreen *vs, WorkSpace *ws)
     45 {
     46 	TwmWindow            *twmWin;
     47 	WorkSpace            *oldws, *newws;
     48 	WList                *wl, *wl1;
     49 	WinList              *winl;
     50 	XSetWindowAttributes attr;
     51 	long                 eventMask;
     52 	IconMgr              *iconmgr;
     53 	Window               oldw;
     54 	Window               neww;
     55 	TwmWindow            *focuswindow;
     56 	VirtualScreen        *tmpvs;
     57 
     58 	if(! Scr->workSpaceManagerActive) {
     59 		return;
     60 	}
     61 	for(tmpvs = Scr->vScreenList; tmpvs != NULL; tmpvs = tmpvs->next) {
     62 		if(ws == tmpvs->wsw->currentwspc) {
     63 			XBell(dpy, 0);
     64 			return;
     65 		}
     66 	}
     67 	oldws = vs->wsw->currentwspc;
     68 	newws = ws;
     69 	if(oldws == newws) {
     70 		return;
     71 	}
     72 
     73 	/* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() and below */
     74 	if(useBackgroundInfo && ! Scr->DontPaintRootWindow) {
     75 		if(newws->image == NULL) {
     76 			XSetWindowBackground(dpy, vs->window, newws->backcp.back);
     77 		}
     78 		else {
     79 			XSetWindowBackgroundPixmap(dpy, vs->window, newws->image->pixmap);
     80 		}
     81 		XClearWindow(dpy, vs->window);
     82 	}
     83 
     84 	/* If SaveWorkspaceFocus is on, save the focus of the last window. */
     85 	if(Scr->SaveWorkspaceFocus) {
     86 		oldws->save_focus = Scr->Focus;
     87 	}
     88 
     89 	focuswindow = NULL;
     90 	/* For better visual effect, the order or map/unmap is important:
     91 	   - map from top to bottom.
     92 	   - unmap from bottom to top.
     93 	   - unmap after mapping.
     94 	   The guiding factor: at any point during the transition, something
     95 	   should be visible only if it was visible before the transition or if
     96 	   it will be visible at the end.  */
     97 	OtpCheckConsistency();
     98 
     99 	for(twmWin = OtpTopWin(); twmWin != NULL;
    100 	                twmWin = OtpNextWinDown(twmWin)) {
    101 
    102 		if(OCCUPY(twmWin, newws)) {
    103 			if(!twmWin->vs) {
    104 				DisplayWin(vs, twmWin);
    105 			}
    106 #ifdef EWMH
    107 			if(OCCUPY(twmWin, oldws)) {
    108 				/*
    109 				 * If the window remains visible, re-order the workspace
    110 				 * numbers in NET_WM_DESKTOP.
    111 				 */
    112 				EwmhSet_NET_WM_DESKTOP_ws(twmWin, newws);
    113 			}
    114 #endif
    115 		}
    116 	}
    117 
    118 	for(twmWin = OtpBottomWin(); twmWin != NULL;
    119 	                twmWin = OtpNextWinUp(twmWin)) {
    120 		if(twmWin->vs == vs) {
    121 			if(!OCCUPY(twmWin, newws)) {
    122 
    123 				Vanish(vs, twmWin);
    124 #ifdef VSCREEN
    125 				/*
    126 				 * Now that the window has Vanished from one virtual screen,
    127 				 * check to see if it is wanted on another one.
    128 				 * This is relatively rare, so don't bother with the
    129 				 * top-to-bottom order here.
    130 				 */
    131 				if(Scr->numVscreens > 1) {
    132 					VirtualScreen *tvs;
    133 					for(tvs = Scr->vScreenList; tvs != NULL; tvs = tvs->next) {
    134 						if(tvs == vs) { /* no, not back on the old one */
    135 							continue;
    136 						}
    137 						if(OCCUPY(twmWin, tvs->wsw->currentwspc)) {
    138 							DisplayWin(tvs, twmWin);
    139 							break;
    140 						}
    141 					}
    142 				}
    143 #endif
    144 			}
    145 			else if(twmWin->hasfocusvisible) {
    146 				focuswindow = twmWin;
    147 				SetFocusVisualAttributes(focuswindow, false);
    148 			}
    149 		}
    150 	}
    151 	OtpCheckConsistency();
    152 
    153 	/*
    154 	   Reorganize icon manager window lists
    155 	*/
    156 	for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
    157 		wl = twmWin->iconmanagerlist;
    158 		if(wl == NULL) {
    159 			continue;
    160 		}
    161 		if(OCCUPY(wl->iconmgr->twm_win, newws)) {
    162 			continue;
    163 		}
    164 		wl1 = wl;
    165 		wl  = wl->nextv;
    166 		while(wl != NULL) {
    167 			if(OCCUPY(wl->iconmgr->twm_win, newws)) {
    168 				break;
    169 			}
    170 			wl1 = wl;
    171 			wl  = wl->nextv;
    172 		}
    173 		if(wl != NULL) {
    174 			wl1->nextv = wl->nextv;
    175 			wl->nextv  = twmWin->iconmanagerlist;
    176 			twmWin->iconmanagerlist = wl;
    177 		}
    178 	}
    179 	wl = NULL;
    180 	for(iconmgr = newws->iconmgr; iconmgr; iconmgr = iconmgr->next) {
    181 		if(iconmgr->first) {
    182 			wl = iconmgr->first;
    183 			break;
    184 		}
    185 	}
    186 	CurrentIconManagerEntry(wl);
    187 	if(focuswindow) {
    188 		SetFocusVisualAttributes(focuswindow, true);
    189 	}
    190 	vs->wsw->currentwspc = newws;
    191 	if(Scr->ReverseCurrentWorkspace && vs->wsw->state == WMS_map) {
    192 		MapSubwindow *msw = vs->wsw->mswl [oldws->number];
    193 		for(winl = msw->wl; winl != NULL; winl = winl->next) {
    194 			WMapRedrawName(vs, winl);
    195 		}
    196 		msw = vs->wsw->mswl [newws->number];
    197 		for(winl = msw->wl; winl != NULL; winl = winl->next) {
    198 			WMapRedrawName(vs, winl);
    199 		}
    200 	}
    201 	else if(vs->wsw->state == WMS_buttons) {
    202 		ButtonSubwindow *bsw = vs->wsw->bswl [oldws->number];
    203 		PaintWsButton(WSPCWINDOW, vs, bsw->w, oldws->label, oldws->cp, off);
    204 		bsw = vs->wsw->bswl [newws->number];
    205 		PaintWsButton(WSPCWINDOW, vs, bsw->w, newws->label, newws->cp,  on);
    206 	}
    207 	oldws->iconmgr = Scr->iconmgr;
    208 	Scr->iconmgr = newws->iconmgr;
    209 
    210 	/* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() and above */
    211 	oldw = vs->wsw->mswl [oldws->number]->w;
    212 	neww = vs->wsw->mswl [newws->number]->w;
    213 	if(useBackgroundInfo) {
    214 		if(oldws->image == NULL || Scr->NoImagesInWorkSpaceManager) {
    215 			XSetWindowBackground(dpy, oldw, oldws->backcp.back);
    216 		}
    217 		else {
    218 			XSetWindowBackgroundPixmap(dpy, oldw, oldws->image->pixmap);
    219 		}
    220 	}
    221 	else {
    222 		if(Scr->workSpaceMgr.defImage == NULL || Scr->NoImagesInWorkSpaceManager) {
    223 			XSetWindowBackground(dpy, oldw, Scr->workSpaceMgr.defColors.back);
    224 		}
    225 		else {
    226 			XSetWindowBackgroundPixmap(dpy, oldw, Scr->workSpaceMgr.defImage->pixmap);
    227 		}
    228 	}
    229 	attr.border_pixel = Scr->workSpaceMgr.defBorderColor;
    230 	XChangeWindowAttributes(dpy, oldw, CWBorderPixel, &attr);
    231 
    232 	if(Scr->workSpaceMgr.curImage == NULL) {
    233 		if(Scr->workSpaceMgr.curPaint) {
    234 			XSetWindowBackground(dpy, neww, Scr->workSpaceMgr.curColors.back);
    235 		}
    236 	}
    237 	else {
    238 		XSetWindowBackgroundPixmap(dpy, neww, Scr->workSpaceMgr.curImage->pixmap);
    239 	}
    240 	attr.border_pixel =  Scr->workSpaceMgr.curBorderColor;
    241 	XChangeWindowAttributes(dpy, neww, CWBorderPixel, &attr);
    242 
    243 	XClearWindow(dpy, oldw);
    244 	XClearWindow(dpy, neww);
    245 
    246 	eventMask = mask_out_event(Scr->Root, PropertyChangeMask);
    247 
    248 	XChangeProperty(dpy, Scr->Root, XA_WM_CURRENTWORKSPACE, XA_STRING, 8,
    249 	                PropModeReplace, (unsigned char *) newws->name, strlen(newws->name));
    250 #ifdef EWMH
    251 	{
    252 		long number = newws->number;
    253 		/*
    254 		 * TODO: this should probably not use Scr->Root but ->XineramaRoot.
    255 		 * That is the real root window if we're using virtual screens.
    256 		 * Also, on the real root it would need values for each of the
    257 		 * virtual roots, but that doesn't fit in the EWMH ideas.
    258 		 */
    259 		XChangeProperty(dpy, Scr->Root, XA__NET_CURRENT_DESKTOP,
    260 		                XA_CARDINAL, 32,
    261 		                PropModeReplace, (unsigned char *) &number, 1);
    262 	}
    263 #endif /* EWMH */
    264 
    265 	restore_mask(Scr->Root, eventMask);
    266 
    267 	/*    XDestroyWindow (dpy, cachew);*/
    268 	if(Scr->ChangeWorkspaceFunction.func != 0) {
    269 		char *action;
    270 		XEvent event;
    271 
    272 		action = Scr->ChangeWorkspaceFunction.item ?
    273 		         Scr->ChangeWorkspaceFunction.item->action : NULL;
    274 		ExecuteFunction(Scr->ChangeWorkspaceFunction.func, action,
    275 		                (Window) 0, NULL, &event, C_ROOT, false);
    276 	}
    277 
    278 	/* If SaveWorkspaceFocus is on, try to restore the focus to the last
    279 	   window which was focused when we left this workspace. */
    280 	if(Scr->SaveWorkspaceFocus && newws->save_focus) {
    281 		twmWin = newws->save_focus;
    282 		if(OCCUPY(twmWin, newws)) {     /* check should not even be needed anymore */
    283 			WarpToWindow(twmWin, false);
    284 		}
    285 		else {
    286 			newws->save_focus = NULL;
    287 		}
    288 	}
    289 
    290 	/* keep track of the order of the workspaces across restarts */
    291 	CtwmSetVScreenMap(dpy, Scr->Root, Scr->vScreenList);
    292 
    293 	XSync(dpy, 0);
    294 	if(Scr->ClickToFocus || Scr->SloppyFocus) {
    295 		set_last_window(newws);
    296 	}
    297 	MaybeAnimate = true;
    298 }
    299 
    300 
    301 
    302 /*
    303  * Various frontends to GotoWorkSpace()
    304  */
    305 
    306 /*
    307  * Simplify redundant checks.  If no multiple workspaces, or no vs given
    308  * to the func, there's nothing to do.
    309  */
    310 #define GWS_CHECK do { \
    311                 if(! Scr->workSpaceManagerActive) {   \
    312                         return;                       \
    313                 }                                     \
    314                 if(!vs) {                             \
    315                         return;                       \
    316                 }                                     \
    317         } while(0)
    318 
    319 void
    320 GotoWorkSpaceByName(VirtualScreen *vs, const char *wname)
    321 {
    322 	WorkSpace *ws;
    323 
    324 	GWS_CHECK;
    325 
    326 	ws = GetWorkspace(wname);
    327 	if(ws == NULL) {
    328 		return;
    329 	}
    330 	GotoWorkSpace(vs, ws);
    331 }
    332 
    333 
    334 void
    335 GotoWorkSpaceByNumber(VirtualScreen *vs, int workspacenum)
    336 {
    337 	WorkSpace *ws;
    338 
    339 	GWS_CHECK;
    340 
    341 	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
    342 		if(ws->number == workspacenum) {
    343 			break;
    344 		}
    345 	}
    346 	if(ws == NULL) {
    347 		return;
    348 	}
    349 	GotoWorkSpace(vs, ws);
    350 }
    351 
    352 
    353 void
    354 GotoPrevWorkSpace(VirtualScreen *vs)
    355 {
    356 	WorkSpace *ws1, *ws2;
    357 
    358 	GWS_CHECK;
    359 
    360 	ws1 = Scr->workSpaceMgr.workSpaceList;
    361 	if(ws1 == NULL) {
    362 		return;
    363 	}
    364 	ws2 = ws1->next;
    365 
    366 	while((ws2 != vs->wsw->currentwspc) && (ws2 != NULL)) {
    367 		ws1 = ws2;
    368 		ws2 = ws2->next;
    369 	}
    370 	GotoWorkSpace(vs, ws1);
    371 }
    372 
    373 
    374 void
    375 GotoNextWorkSpace(VirtualScreen *vs)
    376 {
    377 	WorkSpace *ws;
    378 
    379 	GWS_CHECK;
    380 
    381 	ws = vs->wsw->currentwspc;
    382 	ws = (ws->next != NULL) ? ws->next : Scr->workSpaceMgr.workSpaceList;
    383 	GotoWorkSpace(vs, ws);
    384 }
    385 
    386 
    387 void
    388 GotoRightWorkSpace(VirtualScreen *vs)
    389 {
    390 	WorkSpace *ws;
    391 	int number, columns, count;
    392 
    393 	GWS_CHECK;
    394 
    395 	ws      = vs->wsw->currentwspc;
    396 	number  = ws->number;
    397 	columns = Scr->workSpaceMgr.columns;
    398 	count   = Scr->workSpaceMgr.count;
    399 	number++;
    400 	if((number % columns) == 0) {
    401 		number -= columns;
    402 	}
    403 	else if(number >= count) {
    404 		number = (number / columns) * columns;
    405 	}
    406 
    407 	GotoWorkSpaceByNumber(vs, number);
    408 }
    409 
    410 
    411 void
    412 GotoLeftWorkSpace(VirtualScreen *vs)
    413 {
    414 	WorkSpace *ws;
    415 	int number, columns, count;
    416 
    417 	GWS_CHECK;
    418 
    419 	ws      = vs->wsw->currentwspc;
    420 	number  = ws->number;
    421 	columns = Scr->workSpaceMgr.columns;
    422 	count   = Scr->workSpaceMgr.count;
    423 	number += (number % columns) ? -1 : (columns - 1);
    424 	if(number >= count) {
    425 		number = count - 1;
    426 	}
    427 	GotoWorkSpaceByNumber(vs, number);
    428 }
    429 
    430 
    431 void
    432 GotoUpWorkSpace(VirtualScreen *vs)
    433 {
    434 	WorkSpace *ws;
    435 	int number, lines, columns, count;
    436 
    437 	GWS_CHECK;
    438 
    439 	ws      = vs->wsw->currentwspc;
    440 	number  = ws->number;
    441 	lines   = Scr->workSpaceMgr.lines;
    442 	columns = Scr->workSpaceMgr.columns;
    443 	count   = Scr->workSpaceMgr.count;
    444 	number -=  columns;
    445 	if(number < 0) {
    446 		number += lines * columns;
    447 		/* If the number of workspaces is not a multiple of nr of columns */
    448 		if(number >= count) {
    449 			number -= columns;
    450 		}
    451 	}
    452 	GotoWorkSpaceByNumber(vs, number);
    453 }
    454 
    455 
    456 void
    457 GotoDownWorkSpace(VirtualScreen *vs)
    458 {
    459 	WorkSpace *ws;
    460 	int number, columns, count;
    461 
    462 	GWS_CHECK;
    463 
    464 	ws      = vs->wsw->currentwspc;
    465 	number  = ws->number;
    466 	columns = Scr->workSpaceMgr.columns;
    467 	count   = Scr->workSpaceMgr.count;
    468 	number +=  columns;
    469 	if(number >= count) {
    470 		number %= columns;
    471 	}
    472 	GotoWorkSpaceByNumber(vs, number);
    473 }
    474 
    475 #undef GWS_CHECK
    476 
    477 
    478 
    479 /*
    480  * Show the background (by hiding all windows) or undo it.
    481  * f.showbackground, also can be called via EWMH bits.
    482  *
    483  * newstate is the desired showing state.
    484  * Pass -1 to toggle, 1 to show the background,
    485  * or 0 to re-show the windows.
    486  *
    487  * XXX Doesn't really belong here; more of a functions.c-ish thing
    488  * probably.  But left here for the moment.
    489  */
    490 void
    491 ShowBackground(VirtualScreen *vs, int newstate)
    492 {
    493 	static int state = 0;
    494 	TwmWindow *twmWin;
    495 
    496 	if(newstate == state) {
    497 		return;
    498 	}
    499 
    500 	if(state) {
    501 		for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
    502 			if(twmWin->savevs == vs) {
    503 				DisplayWin(vs, twmWin);
    504 			}
    505 			twmWin->savevs = NULL;
    506 		}
    507 		state = 0;
    508 	}
    509 	else {
    510 		for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
    511 			if(twmWin->vs == vs
    512 #ifdef EWMH
    513 			                /* leave wt_Desktop and wt_Dock visible */
    514 			                && twmWin->ewmhWindowType == wt_Normal
    515 #endif /* EWMH */
    516 			  ) {
    517 				twmWin->savevs = twmWin->vs;
    518 				Vanish(vs, twmWin);
    519 			}
    520 		}
    521 		state = 1;
    522 	}
    523 #ifdef EWMH
    524 	EwmhSet_NET_SHOWING_DESKTOP(state);
    525 #endif /* EWMH */
    526 }
    527 
    528 
    529 /*
    530  * Get the name of the currently active WS.  Used in Execute() for
    531  * sub'ing in $currentworkspace in executing commands.
    532  */
    533 char *
    534 GetCurrentWorkSpaceName(VirtualScreen *vs)
    535 {
    536 	if(! Scr->workSpaceManagerActive) {
    537 		return (NULL);
    538 	}
    539 	if(!vs) {
    540 		vs = Scr->vScreenList;
    541 	}
    542 	return vs->wsw->currentwspc->name;
    543 }
    544 
    545 
    546 /*
    547  * Find workspace by name
    548  */
    549 WorkSpace *
    550 GetWorkspace(const char *wname)
    551 {
    552 	WorkSpace *ws;
    553 
    554 	/* Guard */
    555 	if(!wname) {
    556 		return (NULL);
    557 	}
    558 
    559 	/* Check by label */
    560 	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
    561 		if(strcmp(ws->label, wname) == 0) {
    562 			return ws;
    563 		}
    564 	}
    565 
    566 	/* Check by name */
    567 	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
    568 		if(strcmp(ws->name, wname) == 0) {
    569 			return ws;
    570 		}
    571 	}
    572 
    573 	/* Nope */
    574 	return NULL;
    575 }
    576