1 /*
2  * calmwm - the calm window manager
3  *
4  * Copyright (c) 2004 Andy Adamson <dros@monkey.org>
5  * Copyright (c) 2004,2005 Marius Aamodt Eriksen <marius@monkey.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  *
19  * $OpenBSD$
20  */
21 
22 #include <sys/types.h>
23 #include "queue.h"
24 
25 #include <err.h>
26 #include <errno.h>
27 #include <limits.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "calmwm.h"
34 
35 static struct group_ctx	*group_next(struct group_ctx *);
36 static struct group_ctx	*group_prev(struct group_ctx *);
37 static void		 group_restack(struct group_ctx *);
38 static void		 group_set_active(struct group_ctx *);
39 
40 void
group_assign(struct group_ctx * gc,struct client_ctx * cc)41 group_assign(struct group_ctx *gc, struct client_ctx *cc)
42 {
43 	if ((gc != NULL) && (gc->num == 0))
44 		gc = NULL;
45 
46 	cc->gc = gc;
47 
48 	xu_ewmh_set_net_wm_desktop(cc);
49 }
50 
51 void
group_hide(struct group_ctx * gc)52 group_hide(struct group_ctx *gc)
53 {
54 	struct screen_ctx	*sc = gc->sc;
55 	struct client_ctx	*cc;
56 
57 	screen_updatestackingorder(gc->sc);
58 
59 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
60 		if (cc->gc != gc)
61 			continue;
62 		if (!(cc->flags & CLIENT_STICKY) &&
63 		    !(cc->flags & CLIENT_HIDDEN))
64 			client_hide(cc);
65 	}
66 }
67 
68 void
group_show(struct group_ctx * gc)69 group_show(struct group_ctx *gc)
70 {
71 	struct screen_ctx	*sc = gc->sc;
72 	struct client_ctx	*cc;
73 
74 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
75 		if (cc->gc != gc)
76 			continue;
77 		if (!(cc->flags & CLIENT_STICKY) &&
78 		     (cc->flags & CLIENT_HIDDEN))
79 			client_show(cc);
80 	}
81 	group_restack(gc);
82 	group_set_active(gc);
83 }
84 
85 static void
group_restack(struct group_ctx * gc)86 group_restack(struct group_ctx *gc)
87 {
88 	struct screen_ctx	*sc = gc->sc;
89 	struct client_ctx	*cc;
90 	Window			*winlist;
91 	int			 i, lastempty = -1;
92 	int			 nwins = 0, highstack = 0;
93 
94 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
95 		if (cc->gc != gc)
96 			continue;
97 		if (cc->stackingorder > highstack)
98 			highstack = cc->stackingorder;
99 	}
100 	winlist = xreallocarray(NULL, (highstack + 1), sizeof(*winlist));
101 
102 	/* Invert the stacking order for XRestackWindows(). */
103 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
104 		if (cc->gc != gc)
105 			continue;
106 		winlist[highstack - cc->stackingorder] = cc->win;
107 		nwins++;
108 	}
109 
110 	/* Un-sparseify */
111 	for (i = 0; i <= highstack; i++) {
112 		if (!winlist[i] && lastempty == -1)
113 			lastempty = i;
114 		else if (winlist[i] && lastempty != -1) {
115 			winlist[lastempty] = winlist[i];
116 			if (++lastempty == i)
117 				lastempty = -1;
118 		}
119 	}
120 
121 	XRestackWindows(X_Dpy, winlist, nwins);
122 	free(winlist);
123 }
124 
125 void
group_init(struct screen_ctx * sc,int num,const char * name)126 group_init(struct screen_ctx *sc, int num, const char *name)
127 {
128 	struct group_ctx	*gc;
129 
130 	gc = xmalloc(sizeof(*gc));
131 	gc->sc = sc;
132 	gc->name = xstrdup(name);
133 	gc->num = num;
134 	TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
135 
136 	if (num == 1)
137 		group_set_active(gc);
138 }
139 
140 void
group_set_active(struct group_ctx * gc)141 group_set_active(struct group_ctx *gc)
142 {
143 	struct screen_ctx	*sc = gc->sc;
144 
145 	sc->group_active = gc;
146 
147 	xu_ewmh_net_current_desktop(sc);
148 }
149 
150 void
group_movetogroup(struct client_ctx * cc,int idx)151 group_movetogroup(struct client_ctx *cc, int idx)
152 {
153 	struct screen_ctx	*sc = cc->sc;
154 	struct group_ctx	*gc;
155 
156 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
157 		if (gc->num == idx) {
158 			if (cc->gc == gc)
159 				return;
160 			if (gc->num != 0 && group_holds_only_hidden(gc))
161 				client_hide(cc);
162 			group_assign(gc, cc);
163 		}
164 	}
165 }
166 
167 void
group_toggle_membership(struct client_ctx * cc)168 group_toggle_membership(struct client_ctx *cc)
169 {
170 	struct screen_ctx	*sc = cc->sc;
171 	struct group_ctx	*gc = sc->group_active;
172 
173 	if (cc->gc == gc) {
174 		group_assign(NULL, cc);
175 		cc->flags |= CLIENT_UNGROUP;
176 	} else {
177 		group_assign(gc, cc);
178 		cc->flags |= CLIENT_GROUP;
179 	}
180 	client_draw_border(cc);
181 }
182 
183 int
group_holds_only_sticky(struct group_ctx * gc)184 group_holds_only_sticky(struct group_ctx *gc)
185 {
186 	struct screen_ctx	*sc = gc->sc;
187 	struct client_ctx	*cc;
188 
189 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
190 		if (cc->gc != gc)
191 			continue;
192 		if (!(cc->flags & CLIENT_STICKY))
193 			return 0;
194 	}
195 	return 1;
196 }
197 
198 int
group_holds_only_hidden(struct group_ctx * gc)199 group_holds_only_hidden(struct group_ctx *gc)
200 {
201 	struct screen_ctx	*sc = gc->sc;
202 	struct client_ctx	*cc;
203 
204 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
205 		if (cc->gc != gc)
206 			continue;
207 		if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY)))
208 			return 0;
209 	}
210 	return 1;
211 }
212 
213 void
group_only(struct screen_ctx * sc,int idx)214 group_only(struct screen_ctx *sc, int idx)
215 {
216 	struct group_ctx	*gc;
217 
218 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
219 		if (gc->num == idx)
220 			group_show(gc);
221 		else
222 			group_hide(gc);
223 	}
224 }
225 
226 void
group_toggle(struct screen_ctx * sc,int idx)227 group_toggle(struct screen_ctx *sc, int idx)
228 {
229 	struct group_ctx	*gc;
230 
231 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
232 		if (gc->num == idx) {
233 			if (group_holds_only_hidden(gc))
234 				group_show(gc);
235 			else
236 				group_hide(gc);
237 		}
238 	}
239 }
240 
241 void
group_toggle_all(struct screen_ctx * sc)242 group_toggle_all(struct screen_ctx *sc)
243 {
244 	struct group_ctx	*gc;
245 
246 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
247 		if (sc->hideall)
248 			group_show(gc);
249 		else
250 			group_hide(gc);
251 	}
252 	sc->hideall = !sc->hideall;
253 }
254 
255 void
group_close(struct screen_ctx * sc,int idx)256 group_close(struct screen_ctx *sc, int idx)
257 {
258 	struct group_ctx	*gc;
259 	struct client_ctx	*cc;
260 
261 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
262 		if (gc->num == idx) {
263 			TAILQ_FOREACH(cc, &sc->clientq, entry) {
264 				if (cc->gc != gc)
265 					continue;
266 				client_close(cc);
267 			}
268 		}
269 	}
270 }
271 
272 void
group_cycle(struct screen_ctx * sc,int flags)273 group_cycle(struct screen_ctx *sc, int flags)
274 {
275 	struct group_ctx	*newgc, *oldgc, *showgroup = NULL;
276 
277 	oldgc = sc->group_active;
278 
279 	newgc = oldgc;
280 	for (;;) {
281 		newgc = (flags & CWM_CYCLE_REVERSE) ? group_prev(newgc) :
282 		    group_next(newgc);
283 
284 		if (newgc == oldgc)
285 			break;
286 
287 		if (!group_holds_only_sticky(newgc) && showgroup == NULL)
288 			showgroup = newgc;
289 		else if (!group_holds_only_hidden(newgc))
290 			group_hide(newgc);
291 	}
292 	if (showgroup == NULL)
293 		return;
294 
295 	group_hide(oldgc);
296 
297 	if (group_holds_only_hidden(showgroup))
298 		group_show(showgroup);
299 	else
300 		group_set_active(showgroup);
301 }
302 
303 static struct group_ctx *
group_next(struct group_ctx * gc)304 group_next(struct group_ctx *gc)
305 {
306 	struct screen_ctx	*sc = gc->sc;
307 	struct group_ctx	*newgc;
308 
309 	return(((newgc = TAILQ_NEXT(gc, entry)) != NULL) ?
310 	    newgc : TAILQ_FIRST(&sc->groupq));
311 }
312 
313 static struct group_ctx *
group_prev(struct group_ctx * gc)314 group_prev(struct group_ctx *gc)
315 {
316 	struct screen_ctx	*sc = gc->sc;
317 	struct group_ctx	*newgc;
318 
319 	return(((newgc = TAILQ_PREV(gc, group_q, entry)) != NULL) ?
320 	    newgc : TAILQ_LAST(&sc->groupq, group_q));
321 }
322 
323 int
group_restore(struct client_ctx * cc)324 group_restore(struct client_ctx *cc)
325 {
326 	struct screen_ctx	*sc = cc->sc;
327 	struct group_ctx	*gc;
328 	int			 num;
329 	long			 grpnum;
330 
331 	if (!xu_ewmh_get_net_wm_desktop(cc, &grpnum))
332 		return 0;
333 
334 	num = (grpnum == -1) ? 0 : grpnum;
335 	num = MIN(num, (Conf.ngroups - 1));
336 
337 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
338 		if (gc->num == num) {
339 			group_assign(gc, cc);
340 			return 1;
341 		}
342 	}
343 	return 0;
344 }
345 
346 int
group_autogroup(struct client_ctx * cc)347 group_autogroup(struct client_ctx *cc)
348 {
349 	struct screen_ctx	*sc = cc->sc;
350 	struct autogroup	*ag;
351 	struct group_ctx	*gc;
352 	int			 num = -1, both_match = 0;
353 
354 	if (cc->res_class == NULL || cc->res_name == NULL)
355 		return 0;
356 
357 	TAILQ_FOREACH(ag, &Conf.autogroupq, entry) {
358 		if (strcmp(ag->class, cc->res_class) == 0) {
359 			if ((ag->name != NULL) &&
360 			    (strcmp(ag->name, cc->res_name) == 0)) {
361 				num = ag->num;
362 				both_match = 1;
363 			} else if (ag->name == NULL && !both_match)
364 				num = ag->num;
365 		}
366 	}
367 
368 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
369 		if (gc->num == num) {
370 			group_assign(gc, cc);
371 			return 1;
372 		}
373 	}
374 	return 0;
375 }
376