win_regions.c revision b18c2d1e
1/*
2 * WindowRegion handling
3 */
4
5#include "ctwm.h"
6
7#include <stdlib.h>
8
9#include "list.h"
10#include "screen.h"
11#include "win_regions.h"
12#include "xparsegeometry.h"
13
14
15static void splitWindowRegionEntry(WindowEntry *we,
16                                   RegGravity grav1, RegGravity grav2,
17                                   int w, int h);
18static WindowEntry *findWindowEntry(WorkSpace *wl,
19                                    TwmWindow *tmp_win, WindowRegion **wrp);
20static WindowEntry *prevWindowEntry(WindowEntry *we, WindowRegion *wr);
21static void mergeWindowEntries(WindowEntry *old, WindowEntry *we);
22
23
24
25/*
26 * Backend for the parser when it hits WindowRegion
27 */
28name_list **
29AddWindowRegion(char *geom, RegGravity grav1, RegGravity grav2)
30{
31	WindowRegion *wr;
32	int mask;
33
34	wr = malloc(sizeof(WindowRegion));
35	wr->next = NULL;
36
37	if(!Scr->FirstWindowRegion) {
38		Scr->FirstWindowRegion = wr;
39	}
40
41	wr->entries    = NULL;
42	wr->clientlist = NULL;
43	wr->grav1      = grav1;
44	wr->grav2      = grav2;
45	wr->x = wr->y = wr->w = wr->h = 0;
46
47	mask = RLayoutXParseGeometry(Scr->Layout, geom, &wr->x, &wr->y,
48	                             (unsigned int *) &wr->w,
49	                             (unsigned int *) &wr->h);
50
51	if(mask & XNegative) {
52		wr->x += Scr->rootw - wr->w;
53	}
54	if(mask & YNegative) {
55		wr->y += Scr->rooth - wr->h;
56	}
57
58	return (&(wr->clientlist));
59}
60
61
62/*
63 * Called during startup after the config parsing (which would hit
64 * AddWindowRegion() above) to do some further setup.
65 */
66void
67CreateWindowRegions(void)
68{
69	WindowRegion  *wr, *wr1 = NULL, *wr2 = NULL;
70	WorkSpace *wl;
71
72	for(wl = Scr->workSpaceMgr.workSpaceList; wl != NULL; wl = wl->next) {
73		wl->FirstWindowRegion = NULL;
74		wr2 = NULL;
75		for(wr = Scr->FirstWindowRegion; wr != NULL; wr = wr->next) {
76			wr1  = malloc(sizeof(WindowRegion));
77			*wr1 = *wr;
78			wr1->entries = calloc(1, sizeof(WindowEntry));
79			wr1->entries->x = wr1->x;
80			wr1->entries->y = wr1->y;
81			wr1->entries->w = wr1->w;
82			wr1->entries->h = wr1->h;
83			if(wr2) {
84				wr2->next = wr1;
85			}
86			else {
87				wl->FirstWindowRegion = wr1;
88			}
89			wr2 = wr1;
90		}
91		if(wr1) {
92			wr1->next = NULL;
93		}
94	}
95}
96
97
98/*
99 * Funcs for putting windows into and taking them out of regions.
100 * Similarly to icons in IconRegion's, this writes the coordinates into
101 * final_[xy] after setting up the Window Region/Entry structures and
102 * stashing them in tmp_win as necessary.  Or it doesn't have anything to
103 * do (like if the user doesn't have WindowRegion's config'd), and it
104 * doesn't touch anything and returns false.
105 */
106bool
107PlaceWindowInRegion(TwmWindow *tmp_win, int *final_x, int *final_y)
108{
109	WindowRegion  *wr;
110	WindowEntry   *we;
111	int           w, h;
112	WorkSpace     *wl;
113
114	if(!Scr->FirstWindowRegion) {
115		return false;
116	}
117	for(wl = Scr->workSpaceMgr.workSpaceList; wl != NULL; wl = wl->next) {
118		if(OCCUPY(tmp_win, wl)) {
119			break;
120		}
121	}
122	if(!wl) {
123		return false;
124	}
125	w = tmp_win->frame_width;
126	h = tmp_win->frame_height;
127	we = NULL;
128	for(wr = wl->FirstWindowRegion; wr; wr = wr->next) {
129		if(LookInList(wr->clientlist, tmp_win->name, &tmp_win->class)) {
130			for(we = wr->entries; we; we = we->next) {
131				if(we->used) {
132					continue;
133				}
134				if(we->w >= w && we->h >= h) {
135					break;
136				}
137			}
138			if(we) {
139				break;
140			}
141		}
142	}
143	tmp_win->wr = NULL;
144	if(!we) {
145		return false;
146	}
147
148	splitWindowRegionEntry(we, wr->grav1, wr->grav2, w, h);
149	we->used = true;
150	we->twm_win = tmp_win;
151	*final_x = we->x;
152	*final_y = we->y;
153	tmp_win->wr = wr;
154	return true;
155}
156
157
158/*
159 * Taking a window out of a region.  Doesn't do anything with the
160 * _window_, just disconnects it from the data structures describing the
161 * regions and entries.
162 */
163void
164RemoveWindowFromRegion(TwmWindow *tmp_win)
165{
166	WindowEntry  *we, *wp, *wn;
167	WindowRegion *wr;
168	WorkSpace    *wl;
169
170	if(!Scr->FirstWindowRegion) {
171		return;
172	}
173	we = NULL;
174	for(wl = Scr->workSpaceMgr.workSpaceList; wl != NULL; wl = wl->next) {
175		we = findWindowEntry(wl, tmp_win, &wr);
176		if(we) {
177			break;
178		}
179	}
180	if(!we) {
181		return;
182	}
183
184	we->twm_win = NULL;
185	we->used = false;
186	wp = prevWindowEntry(we, wr);
187	wn = we->next;
188	for(;;) {
189		if(wp && wp->used == false &&
190		                ((wp->x == we->x && wp->w == we->w) ||
191		                 (wp->y == we->y && wp->h == we->h))) {
192			wp->next = we->next;
193			mergeWindowEntries(we, wp);
194			free(we);
195			we = wp;
196			wp = prevWindowEntry(wp, wr);
197		}
198		else if(wn && wn->used == false &&
199		                ((wn->x == we->x && wn->w == we->w) ||
200		                 (wn->y == we->y && wn->h == we->h))) {
201			we->next = wn->next;
202			mergeWindowEntries(wn, we);
203			free(wn);
204			wn = we->next;
205		}
206		else {
207			break;
208		}
209	}
210}
211
212
213/*
214 * Creating a new space inside a region.
215 *
216 * x-ref comment on splitIconRegionEntry() for grodiness.
217 */
218static void
219splitWindowRegionEntry(WindowEntry *we, RegGravity grav1, RegGravity grav2,
220                       int w, int h)
221{
222	switch(grav1) {
223		case GRAV_NORTH:
224		case GRAV_SOUTH:
225			if(w != we->w) {
226				splitWindowRegionEntry(we, grav2, grav1, w, we->h);
227			}
228			if(h != we->h) {
229				WindowEntry *new = calloc(1, sizeof(WindowEntry));
230				new->next = we->next;
231				we->next  = new;
232				new->x    = we->x;
233				new->h    = (we->h - h);
234				new->w    = we->w;
235				we->h     = h;
236				if(grav1 == GRAV_SOUTH) {
237					new->y = we->y;
238					we->y  = new->y + new->h;
239				}
240				else {
241					new->y = we->y + we->h;
242				}
243			}
244			break;
245		case GRAV_EAST:
246		case GRAV_WEST:
247			if(h != we->h) {
248				splitWindowRegionEntry(we, grav2, grav1, we->w, h);
249			}
250			if(w != we->w) {
251				WindowEntry *new = calloc(1, sizeof(WindowEntry));
252				new->next = we->next;
253				we->next  = new;
254				new->y    = we->y;
255				new->w    = (we->w - w);
256				new->h    = we->h;
257				we->w = w;
258				if(grav1 == GRAV_EAST) {
259					new->x = we->x;
260					we->x  = new->x + new->w;
261				}
262				else {
263					new->x = we->x + we->w;
264				}
265			}
266			break;
267	}
268}
269
270
271/*
272 * Utils for finding and merging various WindowEntry's
273 */
274static WindowEntry *
275findWindowEntry(WorkSpace *wl, TwmWindow *tmp_win, WindowRegion **wrp)
276{
277	WindowRegion *wr;
278	WindowEntry  *we;
279
280	for(wr = wl->FirstWindowRegion; wr; wr = wr->next) {
281		for(we = wr->entries; we; we = we->next) {
282			if(we->twm_win == tmp_win) {
283				if(wrp) {
284					*wrp = wr;
285				}
286				return we;
287			}
288		}
289	}
290	return NULL;
291}
292
293
294static WindowEntry *
295prevWindowEntry(WindowEntry *we, WindowRegion *wr)
296{
297	WindowEntry *wp;
298
299	if(we == wr->entries) {
300		return 0;
301	}
302	for(wp = wr->entries; wp->next != we; wp = wp->next);
303	return wp;
304}
305
306
307static void
308mergeWindowEntries(WindowEntry *old, WindowEntry *we)
309{
310	if(old->y == we->y) {
311		we->w = old->w + we->w;
312		if(old->x < we->x) {
313			we->x = old->x;
314		}
315	}
316	else {
317		we->h = old->h + we->h;
318		if(old->y < we->y) {
319			we->y = old->y;
320		}
321	}
322}
323