1/*
2 * Copyright 1992 Claude Lecommandeur.
3 */
4
5#include "ctwm.h"
6
7#include <assert.h>
8#include <stdio.h>
9#include <stdlib.h>
10
11#include <X11/Xatom.h>
12
13#include "ctwm_atoms.h"
14#include "cursor.h"
15#include "icons.h"
16#include "list.h"
17#include "otp.h"
18#include "screen.h"
19#include "vscreen.h"
20#include "win_utils.h"
21
22
23static void DisplayWinUnchecked(VirtualScreen *vs, TwmWindow *tmp_win);
24
25
26static void init_def_vscreen(ScreenInfo *scr)
27{
28	VirtualScreen *vs = malloc(sizeof(VirtualScreen));
29
30	vs->x      = 0;
31	vs->y      = 0;
32	vs->w      = scr->rootw;
33	vs->h      = scr->rooth;
34	vs->window = scr->Root;
35	vs->next   = NULL;
36	vs->wsw    = 0;
37	scr->vScreenList = vs;
38	scr->currentvs   = vs;
39#ifdef VSCREEN
40	scr->numVscreens = 1;
41#endif
42	return;
43}
44
45
46void InitVirtualScreens(ScreenInfo *scr)
47{
48#ifndef VSCREEN
49	// Just do the faking if vscreens are all off anyway.
50	init_def_vscreen(scr);
51	return;
52#else
53
54	// Real implementation
55	Cursor cursor;
56	unsigned long valuemask, attrmask;
57	XSetWindowAttributes attributes;
58	name_list *nptr;
59	VirtualScreen *vs00 = NULL;
60
61	NewFontCursor(&cursor, "X_cursor");
62
63	if(scr->VirtualScreens == NULL) {
64		init_def_vscreen(scr);
65		return;
66	}
67	scr->numVscreens = 0;
68
69	attrmask  = ColormapChangeMask | EnterWindowMask | PropertyChangeMask |
70	            SubstructureRedirectMask | KeyPressMask | ButtonPressMask |
71	            ButtonReleaseMask;
72
73	valuemask = CWBackPixel | CWOverrideRedirect | CWEventMask | CWCursor;
74	attributes.override_redirect = True;
75	attributes.event_mask        = attrmask;
76	attributes.cursor            = cursor;
77	attributes.background_pixel  = Scr->Black;
78
79	scr->vScreenList = NULL;
80	for(nptr = Scr->VirtualScreens; nptr != NULL; nptr = nptr->next) {
81		VirtualScreen *vs;
82		char *geometry = (char *) nptr->name;
83		int x = 0, y = 0;
84		unsigned int w = 0, h = 0;
85
86		XParseGeometry(geometry, &x, &y, &w, &h);
87
88		if((x < 0) || (y < 0) || (w > scr->rootw) || (h > scr->rooth)) {
89			fprintf(stderr, "InitVirtualScreens : invalid geometry : %s\n", geometry);
90			continue;
91		}
92		vs = malloc(sizeof(VirtualScreen));
93		vs->x = x;
94		vs->y = y;
95		vs->w = w;
96		vs->h = h;
97		vs->window = XCreateWindow(dpy, Scr->Root, x, y, w, h,
98		                           0, CopyFromParent, CopyFromParent,
99		                           CopyFromParent, valuemask, &attributes);
100		vs->wsw = 0;
101
102		XSync(dpy, 0);
103		XMapWindow(dpy, vs->window);
104		XChangeProperty(dpy, vs->window, XA_WM_VIRTUALROOT, XA_STRING, 8,
105		                PropModeReplace, (unsigned char *) "Yes", 4);
106
107		vs->next = scr->vScreenList;
108		scr->vScreenList = vs;
109		Scr->numVscreens++;
110
111		/*
112		 * Remember which virtual screen is at (0,0).
113		 */
114		if(x == 0 && y == 0) {
115			vs00 = vs;
116		}
117	}
118
119	if(scr->vScreenList == NULL) {
120		fprintf(stderr, "no valid VirtualScreens found, exiting...\n");
121		exit(1);
122	}
123	/* Setup scr->{currentvs,Root{,x,y,w,h}} as if the
124	 * _correct_ virtual screen is entered with the mouse.
125	 * See HandleEnterNotify().
126	 */
127	if(vs00 == NULL) {
128		vs00 = scr->vScreenList;
129	}
130
131	Scr->Root  = vs00->window;
132#ifdef CAPTIVE
133	Scr->rootx = Scr->crootx + vs00->x;
134	Scr->rooty = Scr->crooty + vs00->y;
135#else
136	Scr->rootx = vs00->x;
137	Scr->rooty = vs00->y;
138#endif
139	Scr->rootw = vs00->w;
140	Scr->rooth = vs00->h;
141	Scr->currentvs = vs00;
142#endif  // VSCREEN
143}
144
145#ifdef VSCREEN
146VirtualScreen *findIfVScreenOf(int x, int y)
147{
148	VirtualScreen *vs;
149	for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
150
151		if((x >= vs->x) && ((x - vs->x) < vs->w) &&
152		                (y >= vs->y) && ((y - vs->y) < vs->h)) {
153			return vs;
154		}
155	}
156	return NULL;
157}
158#endif
159
160/*
161 * Returns the order that virtual screens are displayed for the vscreen
162 * list.  This is stored this way so everything ends up in the right place
163 * on a ctwm restart.
164 */
165char *
166CtwmGetVScreenMap(Display *display, Window rootw)
167{
168	unsigned char       *prop;
169	unsigned long       bytesafter;
170	unsigned long       len;
171	Atom                actual_type;
172	int                 actual_format;
173	char                *ret;
174
175	if(XA_WM_CTWM_VSCREENMAP == None) {
176		return false;
177	}
178	if(XGetWindowProperty(display, rootw, XA_WM_CTWM_VSCREENMAP, 0L, 512,
179	                      False, XA_STRING, &actual_type, &actual_format, &len,
180	                      &bytesafter, &prop) != Success) {
181		return NULL;
182	}
183	if(len == 0) {
184		return NULL;
185	}
186
187	ret = malloc(len + 1);
188	memcpy(ret, prop, len);
189	ret[len] = '\0';
190	XFree(prop);
191
192	return ret;
193}
194
195bool
196CtwmSetVScreenMap(Display *display, Window rootw,
197                  struct VirtualScreen *firstvs)
198{
199	char                        buf[1024];
200	int                         tally = 0;
201	struct VirtualScreen        *vs;
202
203	if(XA_WM_CTWM_VSCREENMAP == None) {
204		return false;
205	}
206
207	memset(buf, 0, sizeof(buf));
208	for(vs = firstvs; vs; vs = vs->next) {
209		if(tally) {
210			strcat(buf, ",");
211		}
212		if(vs->wsw && vs->wsw->currentwspc && vs->wsw->currentwspc->name) {
213			strcat(buf, vs->wsw->currentwspc->name);
214			tally++;
215		}
216	}
217
218	if(! tally) {
219		return false;
220	}
221
222	XChangeProperty(display, rootw, XA_WM_CTWM_VSCREENMAP, XA_STRING, 8,
223	                PropModeReplace, (unsigned char *)buf, strlen(buf));
224	return true;
225}
226
227
228/*
229 * Display a window in a given virtual screen.
230 */
231void
232DisplayWin(VirtualScreen *vs, TwmWindow *tmp_win)
233{
234	OtpCheckConsistency();
235	DisplayWinUnchecked(vs, tmp_win);
236	OtpCheckConsistency();
237}
238
239static void
240DisplayWinUnchecked(VirtualScreen *vs, TwmWindow *tmp_win)
241{
242	/*
243	 * A window cannot be shown in multiple virtual screens, even if
244	 * it occupies both corresponding workspaces.
245	 */
246	if(vs && tmp_win->vs) {
247		return;
248	}
249
250	/* This is where we're moving it */
251	tmp_win->vs = vs;
252
253
254	/* If it's unmapped, RFAI() moves the necessary bits here */
255	if(!tmp_win->mapped) {
256		ReparentFrameAndIcon(tmp_win);
257
258		/* If it's got an icon that should be up, make it up here */
259		if(tmp_win->isicon) {
260			if(tmp_win->icon_on) {
261				if(tmp_win->icon && tmp_win->icon->w) {
262
263					IconUp(tmp_win);
264					XMapWindow(dpy, tmp_win->icon->w);
265				}
266			}
267		}
268
269		/* All there is to do with unmapped wins */
270		return;
271	}
272
273
274	/* If we make it this far, the window is mapped */
275
276	if(tmp_win->UnmapByMovingFarAway) {
277		/*
278		 * XXX I don't believe the handling of UnmapByMovingFarAway is
279		 * quite correct.
280		 */
281		if(vs) {
282			XReparentWindow(dpy, tmp_win->frame, vs->window,
283			                tmp_win->frame_x, tmp_win->frame_y);
284		}
285		else {
286			XMoveWindow(dpy, tmp_win->frame, tmp_win->frame_x, tmp_win->frame_y);
287		}
288	}
289	else {
290		/* Map and move it here */
291		if(!tmp_win->squeezed) {
292			long eventMask;
293
294			eventMask = mask_out_event(tmp_win->w, StructureNotifyMask);
295			XMapWindow(dpy, tmp_win->w);
296			restore_mask(tmp_win->w, eventMask);
297		}
298
299		ReparentFrameAndIcon(tmp_win);
300
301		XMapWindow(dpy, tmp_win->frame);
302		SetMapStateProp(tmp_win, NormalState);
303	}
304}
305
306
307/*
308 * Move a window's frame and icon to a new VS.  This mostly happens as a
309 * backend bit of the DisplayWin() process, but it does get called
310 * directly for the Occupy window.  XXX Should it?
311 */
312void
313ReparentFrameAndIcon(TwmWindow *tmp_win)
314{
315	VirtualScreen *vs = tmp_win->vs; /* which virtual screen we want it in */
316
317	/* parent_vs is the current real parent of the window */
318	if(vs != tmp_win->parent_vs) {
319		struct Icon *icon = tmp_win->icon;
320
321		// This must always be something...
322		assert(vs != NULL);
323
324		tmp_win->parent_vs = vs;
325
326		if(icon && icon->w) {
327			ReparentWindowAndIcon(dpy, tmp_win, vs->window,
328			                      tmp_win->frame_x, tmp_win->frame_y,
329			                      icon->w_x, icon->w_y);
330		}
331		else {
332			ReparentWindow(dpy, tmp_win,  WinWin, vs->window,
333			               tmp_win->frame_x, tmp_win->frame_y);
334		}
335	}
336}
337
338
339/*
340 * Get this window outta here.  Note that despite naming, this is
341 * unrelated to f.vanish.
342 */
343void
344Vanish(VirtualScreen *vs, TwmWindow *tmp_win)
345{
346	/* It's not here?  Nothing to do. */
347	if(vs && tmp_win->vs && tmp_win->vs != vs) {
348		return;
349	}
350
351	/* Unmap (or near-equivalent) all its bits */
352	if(tmp_win->UnmapByMovingFarAway) {
353		/* UnmapByMovingFarAway?  Move it off-screen */
354		XMoveWindow(dpy, tmp_win->frame, Scr->rootw + 1, Scr->rooth + 1);
355	}
356	else if(tmp_win->mapped) {
357		/* It's mapped; unmap it */
358		long eventMask;
359
360		eventMask = mask_out_event(tmp_win->w, StructureNotifyMask);
361		XUnmapWindow(dpy, tmp_win->w);
362		XUnmapWindow(dpy, tmp_win->frame);
363		restore_mask(tmp_win->w, eventMask);
364
365		if(!tmp_win->DontSetInactive) {
366			SetMapStateProp(tmp_win, InactiveState);
367		}
368	}
369	else if(tmp_win->icon_on && tmp_win->icon && tmp_win->icon->w) {
370		/* It's not mapped, but the icon's up; hide it away */
371		XUnmapWindow(dpy, tmp_win->icon->w);
372		IconDown(tmp_win);
373	}
374
375#if 0
376	/*
377	 * The purpose of this is in the event of a ctwm death/restart,
378	 * geometries of windows that were on unmapped workspaces will show
379	 * up where they belong.
380	 * XXX - I doubt its usefulness, since still-mapped windows won't
381	 * enjoy this "protection", making it suboptimal at best.
382	 * XXX - XReparentWindow() messes up the stacking order of windows.
383	 * It should be avoided as much as possible. This already affects
384	 * switching away from and back to a workspace. Therefore do this only
385	 * if there are at least 2 virtual screens AND the new one (firstvs)
386	 * differs from where the window currently is. (Olaf Seibert).
387	 */
388
389	if(Scr->numVscreens > 1) {
390		int x, y;
391		unsigned int junk;
392		Window junkW, w = tmp_win->frame;
393		VirtualScreen *firstvs = NULL;
394
395		for(firstvs = Scr->vScreenList; firstvs; firstvs = firstvs->next)
396			if(firstvs->x == 0 && firstvs->y == 0) {
397				break;
398			}
399		if(firstvs && firstvs != vs) {
400			tmp_win->vs = firstvs;
401			ReparentFrameAndIcon(tmp_win);
402		}
403	}
404#endif
405
406	/* Currently displayed nowhere */
407	tmp_win->vs = NULL;
408}
409