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