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