~ctwm/ctwm/trunk

510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
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"
524.1.3 by Matthew Fuller
Pull vscreen.h out of screen.h and #include it directly in the files
22
#include "vscreen.h"
523.1.11 by Matthew Fuller
Inagurate win_ops.c by moving the focus functions from util.c into it.
23
#include "win_ops.h"
523.1.18 by Matthew Fuller
Move the masking funcs into win_util and GC util.h from the files only
24
#include "win_utils.h"
510.1.9 by Matthew Fuller
Only stuff left in workmgr is the actual workspace manager stuff.
25
#include "workspace_manager.h"
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
26
#include "workspace_utils.h"
27
28
#ifdef EWMH
29
#  include "ewmh_atoms.h"
30
#endif
31
32
510.1.16 by Matthew Fuller
Mark up the questionable future of useBackgroundInfo.
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
 */
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
37
bool useBackgroundInfo = false;
38
39
40
/*
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
41
 * Move the display (of a given vs) over to a new workspace.
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
42
 */
510.1.7 by Matthew Fuller
Break these definitions as long as I'm moving the whole contents anyway.
43
void
44
GotoWorkSpace(VirtualScreen *vs, WorkSpace *ws)
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
45
{
46
	TwmWindow            *twmWin;
47
	WorkSpace            *oldws, *newws;
48
	WList                *wl, *wl1;
511.1.16 by Matthew Fuller
For some reason, the WinList structure typedef was typedef'd as a
49
	WinList              *winl;
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
50
	XSetWindowAttributes attr;
511.1.9 by Matthew Fuller
Use mask_out_event{_mask}() and restore_mask() in place of manually
51
	long                 eventMask;
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
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
510.1.42 by Matthew Fuller
Expand a comment about some repeated code that needs factoring out,
73
	/* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() and below */
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
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
				VirtualScreen *tvs;
123
124
				Vanish(vs, twmWin);
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
					for(tvs = Scr->vScreenList; tvs != NULL; tvs = tvs->next) {
133
						if(tvs == vs) { /* no, not back on the old one */
134
							continue;
135
						}
136
						if(OCCUPY(twmWin, tvs->wsw->currentwspc)) {
137
							DisplayWin(tvs, twmWin);
138
							break;
139
						}
140
					}
141
				}
142
			}
143
			else if(twmWin->hasfocusvisible) {
144
				focuswindow = twmWin;
145
				SetFocusVisualAttributes(focuswindow, false);
146
			}
147
		}
148
	}
149
	OtpCheckConsistency();
150
151
	/*
152
	   Reorganize icon manager window lists
153
	*/
154
	for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
155
		wl = twmWin->iconmanagerlist;
156
		if(wl == NULL) {
157
			continue;
158
		}
159
		if(OCCUPY(wl->iconmgr->twm_win, newws)) {
160
			continue;
161
		}
162
		wl1 = wl;
163
		wl  = wl->nextv;
164
		while(wl != NULL) {
165
			if(OCCUPY(wl->iconmgr->twm_win, newws)) {
166
				break;
167
			}
168
			wl1 = wl;
169
			wl  = wl->nextv;
170
		}
171
		if(wl != NULL) {
172
			wl1->nextv = wl->nextv;
173
			wl->nextv  = twmWin->iconmanagerlist;
174
			twmWin->iconmanagerlist = wl;
175
		}
176
	}
177
	wl = NULL;
178
	for(iconmgr = newws->iconmgr; iconmgr; iconmgr = iconmgr->next) {
179
		if(iconmgr->first) {
180
			wl = iconmgr->first;
181
			break;
182
		}
183
	}
184
	CurrentIconManagerEntry(wl);
185
	if(focuswindow) {
186
		SetFocusVisualAttributes(focuswindow, true);
187
	}
188
	vs->wsw->currentwspc = newws;
189
	if(Scr->ReverseCurrentWorkspace && vs->wsw->state == WMS_map) {
190
		MapSubwindow *msw = vs->wsw->mswl [oldws->number];
191
		for(winl = msw->wl; winl != NULL; winl = winl->next) {
192
			WMapRedrawName(vs, winl);
193
		}
194
		msw = vs->wsw->mswl [newws->number];
195
		for(winl = msw->wl; winl != NULL; winl = winl->next) {
196
			WMapRedrawName(vs, winl);
197
		}
198
	}
199
	else if(vs->wsw->state == WMS_buttons) {
200
		ButtonSubwindow *bsw = vs->wsw->bswl [oldws->number];
201
		PaintWsButton(WSPCWINDOW, vs, bsw->w, oldws->label, oldws->cp, off);
202
		bsw = vs->wsw->bswl [newws->number];
203
		PaintWsButton(WSPCWINDOW, vs, bsw->w, newws->label, newws->cp,  on);
204
	}
205
	oldws->iconmgr = Scr->iconmgr;
206
	Scr->iconmgr = newws->iconmgr;
207
510.1.42 by Matthew Fuller
Expand a comment about some repeated code that needs factoring out,
208
	/* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() and above */
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
209
	oldw = vs->wsw->mswl [oldws->number]->w;
210
	neww = vs->wsw->mswl [newws->number]->w;
211
	if(useBackgroundInfo) {
212
		if(oldws->image == NULL || Scr->NoImagesInWorkSpaceManager) {
213
			XSetWindowBackground(dpy, oldw, oldws->backcp.back);
214
		}
215
		else {
216
			XSetWindowBackgroundPixmap(dpy, oldw, oldws->image->pixmap);
217
		}
218
	}
219
	else {
220
		if(Scr->workSpaceMgr.defImage == NULL || Scr->NoImagesInWorkSpaceManager) {
221
			XSetWindowBackground(dpy, oldw, Scr->workSpaceMgr.defColors.back);
222
		}
223
		else {
224
			XSetWindowBackgroundPixmap(dpy, oldw, Scr->workSpaceMgr.defImage->pixmap);
225
		}
226
	}
227
	attr.border_pixel = Scr->workSpaceMgr.defBorderColor;
228
	XChangeWindowAttributes(dpy, oldw, CWBorderPixel, &attr);
229
230
	if(Scr->workSpaceMgr.curImage == NULL) {
231
		if(Scr->workSpaceMgr.curPaint) {
232
			XSetWindowBackground(dpy, neww, Scr->workSpaceMgr.curColors.back);
233
		}
234
	}
235
	else {
236
		XSetWindowBackgroundPixmap(dpy, neww, Scr->workSpaceMgr.curImage->pixmap);
237
	}
238
	attr.border_pixel =  Scr->workSpaceMgr.curBorderColor;
239
	XChangeWindowAttributes(dpy, neww, CWBorderPixel, &attr);
240
241
	XClearWindow(dpy, oldw);
242
	XClearWindow(dpy, neww);
243
511.1.9 by Matthew Fuller
Use mask_out_event{_mask}() and restore_mask() in place of manually
244
	eventMask = mask_out_event(Scr->Root, PropertyChangeMask);
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
245
246
	XChangeProperty(dpy, Scr->Root, XA_WM_CURRENTWORKSPACE, XA_STRING, 8,
247
	                PropModeReplace, (unsigned char *) newws->name, strlen(newws->name));
248
#ifdef EWMH
249
	{
250
		long number = newws->number;
251
		/*
252
		 * TODO: this should probably not use Scr->Root but ->XineramaRoot.
253
		 * That is the real root window if we're using virtual screens.
254
		 * Also, on the real root it would need values for each of the
255
		 * virtual roots, but that doesn't fit in the EWMH ideas.
256
		 */
257
		XChangeProperty(dpy, Scr->Root, XA__NET_CURRENT_DESKTOP,
258
		                XA_CARDINAL, 32,
259
		                PropModeReplace, (unsigned char *) &number, 1);
260
	}
261
#endif /* EWMH */
511.1.9 by Matthew Fuller
Use mask_out_event{_mask}() and restore_mask() in place of manually
262
263
	restore_mask(Scr->Root, eventMask);
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
264
265
	/*    XDestroyWindow (dpy, cachew);*/
266
	if(Scr->ChangeWorkspaceFunction.func != 0) {
267
		char *action;
268
		XEvent event;
269
270
		action = Scr->ChangeWorkspaceFunction.item ?
271
		         Scr->ChangeWorkspaceFunction.item->action : NULL;
272
		ExecuteFunction(Scr->ChangeWorkspaceFunction.func, action,
273
		                (Window) 0, NULL, &event, C_ROOT, false);
274
	}
275
276
	/* If SaveWorkspaceFocus is on, try to restore the focus to the last
277
	   window which was focused when we left this workspace. */
278
	if(Scr->SaveWorkspaceFocus && newws->save_focus) {
279
		twmWin = newws->save_focus;
280
		if(OCCUPY(twmWin, newws)) {     /* check should not even be needed anymore */
281
			WarpToWindow(twmWin, false);
282
		}
283
		else {
284
			newws->save_focus = NULL;
285
		}
286
	}
287
288
	/* keep track of the order of the workspaces across restarts */
289
	CtwmSetVScreenMap(dpy, Scr->Root, Scr->vScreenList);
290
291
	XSync(dpy, 0);
292
	if(Scr->ClickToFocus || Scr->SloppyFocus) {
293
		set_last_window(newws);
294
	}
295
	MaybeAnimate = true;
296
}
297
298
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
299
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
300
/*
301
 * Various frontends to GotoWorkSpace()
302
 */
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
303
304
/*
305
 * Simplify redundant checks.  If no multiple workspaces, or no vs given
306
 * to the func, there's nothing to do.
307
 */
308
#define GWS_CHECK do { \
510.1.13 by Matthew Fuller
make indent
309
                if(! Scr->workSpaceManagerActive) {   \
310
                        return;                       \
311
                }                                     \
312
                if(!vs) {                             \
313
                        return;                       \
314
                }                                     \
315
        } while(0)
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
316
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
317
void
510.1.14 by Matthew Fuller
const-ify these char * args.
318
GotoWorkSpaceByName(VirtualScreen *vs, const char *wname)
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
319
{
320
	WorkSpace *ws;
321
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
322
	GWS_CHECK;
323
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
324
	ws = GetWorkspace(wname);
325
	if(ws == NULL) {
326
		return;
327
	}
328
	GotoWorkSpace(vs, ws);
329
}
330
331
332
void
333
GotoWorkSpaceByNumber(VirtualScreen *vs, int workspacenum)
334
{
335
	WorkSpace *ws;
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
336
337
	GWS_CHECK;
338
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
339
	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
340
		if(ws->number == workspacenum) {
341
			break;
342
		}
343
	}
344
	if(ws == NULL) {
345
		return;
346
	}
347
	GotoWorkSpace(vs, ws);
348
}
349
350
351
void
352
GotoPrevWorkSpace(VirtualScreen *vs)
353
{
354
	WorkSpace *ws1, *ws2;
355
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
356
	GWS_CHECK;
357
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
358
	ws1 = Scr->workSpaceMgr.workSpaceList;
359
	if(ws1 == NULL) {
360
		return;
361
	}
362
	ws2 = ws1->next;
363
364
	while((ws2 != vs->wsw->currentwspc) && (ws2 != NULL)) {
365
		ws1 = ws2;
366
		ws2 = ws2->next;
367
	}
368
	GotoWorkSpace(vs, ws1);
369
}
370
371
372
void
373
GotoNextWorkSpace(VirtualScreen *vs)
374
{
375
	WorkSpace *ws;
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
376
377
	GWS_CHECK;
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
378
379
	ws = vs->wsw->currentwspc;
380
	ws = (ws->next != NULL) ? ws->next : Scr->workSpaceMgr.workSpaceList;
381
	GotoWorkSpace(vs, ws);
382
}
383
384
385
void
386
GotoRightWorkSpace(VirtualScreen *vs)
387
{
388
	WorkSpace *ws;
389
	int number, columns, count;
390
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
391
	GWS_CHECK;
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
392
393
	ws      = vs->wsw->currentwspc;
394
	number  = ws->number;
395
	columns = Scr->workSpaceMgr.columns;
396
	count   = Scr->workSpaceMgr.count;
397
	number++;
398
	if((number % columns) == 0) {
399
		number -= columns;
400
	}
401
	else if(number >= count) {
402
		number = (number / columns) * columns;
403
	}
404
405
	GotoWorkSpaceByNumber(vs, number);
406
}
407
408
409
void
410
GotoLeftWorkSpace(VirtualScreen *vs)
411
{
412
	WorkSpace *ws;
413
	int number, columns, count;
414
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
415
	GWS_CHECK;
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
416
417
	ws      = vs->wsw->currentwspc;
418
	number  = ws->number;
419
	columns = Scr->workSpaceMgr.columns;
420
	count   = Scr->workSpaceMgr.count;
421
	number += (number % columns) ? -1 : (columns - 1);
422
	if(number >= count) {
423
		number = count - 1;
424
	}
425
	GotoWorkSpaceByNumber(vs, number);
426
}
427
428
429
void
430
GotoUpWorkSpace(VirtualScreen *vs)
431
{
432
	WorkSpace *ws;
433
	int number, lines, columns, count;
434
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
435
	GWS_CHECK;
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
436
437
	ws      = vs->wsw->currentwspc;
438
	number  = ws->number;
439
	lines   = Scr->workSpaceMgr.lines;
440
	columns = Scr->workSpaceMgr.columns;
441
	count   = Scr->workSpaceMgr.count;
442
	number -=  columns;
443
	if(number < 0) {
444
		number += lines * columns;
445
		/* If the number of workspaces is not a multiple of nr of columns */
446
		if(number >= count) {
447
			number -= columns;
448
		}
449
	}
450
	GotoWorkSpaceByNumber(vs, number);
451
}
452
453
454
void
455
GotoDownWorkSpace(VirtualScreen *vs)
456
{
457
	WorkSpace *ws;
458
	int number, columns, count;
459
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
460
	GWS_CHECK;
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
461
462
	ws      = vs->wsw->currentwspc;
463
	number  = ws->number;
464
	columns = Scr->workSpaceMgr.columns;
465
	count   = Scr->workSpaceMgr.count;
466
	number +=  columns;
467
	if(number >= count) {
468
		number %= columns;
469
	}
470
	GotoWorkSpaceByNumber(vs, number);
471
}
472
510.1.12 by Matthew Fuller
Factor out simple checks at the start of all these funcs into a macro.
473
#undef GWS_CHECK
474
510.1.11 by Matthew Fuller
Better yet, it should probably go way up at the top of the file.
475
510.1.10 by Matthew Fuller
Move GotoWorkSpace up to alongside the other frontends that call it.
476
477
/*
478
 * Show the background (by hiding all windows) or undo it.
479
 * f.showbackground, also can be called via EWMH bits.
480
 *
481
 * newstate is the desired showing state.
482
 * Pass -1 to toggle, 1 to show the background,
483
 * or 0 to re-show the windows.
484
 *
485
 * XXX Doesn't really belong here; more of a functions.c-ish thing
486
 * probably.  But left here for the moment.
487
 */
488
void
489
ShowBackground(VirtualScreen *vs, int newstate)
490
{
491
	static int state = 0;
492
	TwmWindow *twmWin;
493
494
	if(newstate == state) {
495
		return;
496
	}
497
498
	if(state) {
499
		for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
500
			if(twmWin->savevs == vs) {
501
				DisplayWin(vs, twmWin);
502
			}
503
			twmWin->savevs = NULL;
504
		}
505
		state = 0;
506
	}
507
	else {
508
		for(twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
509
			if(twmWin->vs == vs
510
#ifdef EWMH
511
			                /* leave wt_Desktop and wt_Dock visible */
512
			                && twmWin->ewmhWindowType == wt_Normal
513
#endif /* EWMH */
514
			  ) {
515
				twmWin->savevs = twmWin->vs;
516
				Vanish(vs, twmWin);
517
			}
518
		}
519
		state = 1;
520
	}
521
#ifdef EWMH
522
	EwmhSet_NET_SHOWING_DESKTOP(state);
523
#endif /* EWMH */
524
}
525
526
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
527
/*
528
 * Get the name of the currently active WS.  Used in Execute() for
529
 * sub'ing in $currentworkspace in executing commands.
530
 */
510.1.7 by Matthew Fuller
Break these definitions as long as I'm moving the whole contents anyway.
531
char *
532
GetCurrentWorkSpaceName(VirtualScreen *vs)
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
533
{
534
	if(! Scr->workSpaceManagerActive) {
535
		return (NULL);
536
	}
537
	if(!vs) {
538
		vs = Scr->vScreenList;
539
	}
540
	return vs->wsw->currentwspc->name;
541
}
542
543
544
/*
545
 * Find workspace by name
546
 */
547
WorkSpace *
510.1.14 by Matthew Fuller
const-ify these char * args.
548
GetWorkspace(const char *wname)
510.1.5 by Matthew Fuller
Break the general/utility workspace functions out into their own file.
549
{
550
	WorkSpace *ws;
551
552
	/* Guard */
553
	if(!wname) {
554
		return (NULL);
555
	}
556
557
	/* Check by label */
558
	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
559
		if(strcmp(ws->label, wname) == 0) {
560
			return ws;
561
		}
562
	}
563
564
	/* Check by name */
565
	for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
566
		if(strcmp(ws->name, wname) == 0) {
567
			return ws;
568
		}
569
	}
570
571
	/* Nope */
572
	return NULL;
573
}