1 /*
2  * calmwm - the calm window manager
3  *
4  * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * $OpenBSD$
19  */
20 
21 #include <sys/types.h>
22 #include "queue.h"
23 
24 #include <err.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "calmwm.h"
33 
34 static void			 client_class_hint(struct client_ctx *);
35 static void			 client_placement(struct client_ctx *);
36 static void			 client_mwm_hints(struct client_ctx *);
37 static void			 client_wm_protocols(struct client_ctx *);
38 
39 struct client_ctx *
client_init(Window win,struct screen_ctx * sc)40 client_init(Window win, struct screen_ctx *sc)
41 {
42 	struct client_ctx	*cc;
43 	XWindowAttributes	 wattr;
44 	int			 mapped;
45 	long			 state;
46 
47 	if (win == None)
48 		return NULL;
49 	if (!XGetWindowAttributes(X_Dpy, win, &wattr))
50 		return NULL;
51 
52 	if (sc == NULL) {
53 		if ((sc = screen_find(wattr.root)) == NULL)
54 			return NULL;
55 		mapped = 1;
56 	} else {
57 		if (wattr.override_redirect || wattr.map_state != IsViewable)
58 			return NULL;
59 		mapped = wattr.map_state != IsUnmapped;
60 	}
61 
62 	XGrabServer(X_Dpy);
63 
64 	cc = xmalloc(sizeof(*cc));
65 	cc->sc = sc;
66 	cc->win = win;
67 	cc->name = NULL;
68 	cc->label = NULL;
69 	cc->gc = NULL;
70 	cc->res_class = NULL;
71 	cc->res_name = NULL;
72 	cc->flags = 0;
73 	cc->stackingorder = 0;
74 	cc->initial_state = 0;
75 	memset(&cc->hint, 0, sizeof(cc->hint));
76 	TAILQ_INIT(&cc->nameq);
77 
78 	cc->geom.x = wattr.x;
79 	cc->geom.y = wattr.y;
80 	cc->geom.w = wattr.width;
81 	cc->geom.h = wattr.height;
82 	cc->colormap = wattr.colormap;
83 	cc->obwidth = wattr.border_width;
84 	cc->bwidth = Conf.bwidth;
85 
86 	client_set_name(cc);
87 	conf_client(cc);
88 
89 	client_wm_hints(cc);
90 	client_class_hint(cc);
91 	client_wm_protocols(cc);
92 	client_get_sizehints(cc);
93 	client_transient(cc);
94 	client_mwm_hints(cc);
95 
96 	if ((cc->flags & CLIENT_IGNORE))
97 		cc->bwidth = 0;
98 	cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw;
99 	cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch;
100 	cc->ptr.x = cc->geom.w / 2;
101 	cc->ptr.y = cc->geom.h / 2;
102 
103 	if (wattr.map_state != IsViewable) {
104 		client_placement(cc);
105 		client_resize(cc, 0);
106 		if (cc->initial_state)
107 			xu_set_wm_state(cc->win, cc->initial_state);
108 	}
109 
110 	XSelectInput(X_Dpy, cc->win,
111 	    EnterWindowMask | PropertyChangeMask | KeyReleaseMask);
112 
113 	XAddToSaveSet(X_Dpy, cc->win);
114 
115 	/* Notify client of its configuration. */
116 	client_config(cc);
117 
118 	TAILQ_INSERT_TAIL(&sc->clientq, cc, entry);
119 
120 	xu_ewmh_net_client_list(sc);
121 	xu_ewmh_net_client_list_stacking(sc);
122 	xu_ewmh_restore_net_wm_state(cc);
123 
124 	xu_get_wm_state(cc->win, &state);
125 	if (state == IconicState)
126 		client_hide(cc);
127 	else
128 		client_show(cc);
129 
130 	if (mapped) {
131 		if (cc->gc) {
132 			group_movetogroup(cc, cc->gc->num);
133 			goto out;
134 		}
135 		if (group_restore(cc))
136 			goto out;
137 		if (group_autogroup(cc))
138 			goto out;
139 		if (Conf.stickygroups)
140 			group_assign(sc->group_active, cc);
141 		else
142 			group_assign(NULL, cc);
143 	}
144 out:
145 	XSync(X_Dpy, False);
146 	XUngrabServer(X_Dpy);
147 
148 	return cc;
149 }
150 
151 struct client_ctx *
client_current(struct screen_ctx * sc)152 client_current(struct screen_ctx *sc)
153 {
154 	struct screen_ctx	*_sc;
155 	struct client_ctx	*cc;
156 
157 	if (sc) {
158 		TAILQ_FOREACH(cc, &sc->clientq, entry) {
159 			if (cc->flags & CLIENT_ACTIVE)
160 				return cc;
161 		}
162 	} else {
163 		TAILQ_FOREACH(_sc, &Screenq, entry) {
164 			TAILQ_FOREACH(cc, &_sc->clientq, entry) {
165 				if (cc->flags & CLIENT_ACTIVE)
166 					return cc;
167 			}
168 		}
169 	}
170 	return NULL;
171 }
172 
173 struct client_ctx *
client_find(Window win)174 client_find(Window win)
175 {
176 	struct screen_ctx	*sc;
177 	struct client_ctx	*cc;
178 
179 	TAILQ_FOREACH(sc, &Screenq, entry) {
180 		TAILQ_FOREACH(cc, &sc->clientq, entry) {
181 			if (cc->win == win)
182 				return cc;
183 		}
184 	}
185 	return NULL;
186 }
187 
188 struct client_ctx *
client_next(struct client_ctx * cc)189 client_next(struct client_ctx *cc)
190 {
191 	struct screen_ctx	*sc = cc->sc;
192 	struct client_ctx	*newcc;
193 
194 	return(((newcc = TAILQ_NEXT(cc, entry)) != NULL) ?
195 	    newcc : TAILQ_FIRST(&sc->clientq));
196 }
197 
198 struct client_ctx *
client_prev(struct client_ctx * cc)199 client_prev(struct client_ctx *cc)
200 {
201 	struct screen_ctx	*sc = cc->sc;
202 	struct client_ctx	*newcc;
203 
204 	return(((newcc = TAILQ_PREV(cc, client_q, entry)) != NULL) ?
205 	    newcc : TAILQ_LAST(&sc->clientq, client_q));
206 }
207 
208 void
client_remove(struct client_ctx * cc)209 client_remove(struct client_ctx *cc)
210 {
211 	struct screen_ctx	*sc = cc->sc;
212 	struct winname		*wn;
213 
214 	TAILQ_REMOVE(&sc->clientq, cc, entry);
215 
216 	xu_ewmh_net_client_list(sc);
217 	xu_ewmh_net_client_list_stacking(sc);
218 
219 	if (cc->flags & CLIENT_ACTIVE)
220 		xu_ewmh_net_active_window(sc, None);
221 
222 	while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) {
223 		TAILQ_REMOVE(&cc->nameq, wn, entry);
224 		free(wn->name);
225 		free(wn);
226 	}
227 
228 	free(cc->name);
229 	free(cc->label);
230 	free(cc->res_class);
231 	free(cc->res_name);
232 	free(cc);
233 }
234 
235 void
client_set_active(struct client_ctx * cc)236 client_set_active(struct client_ctx *cc)
237 {
238 	struct screen_ctx	*sc = cc->sc;
239 	struct client_ctx	*oldcc;
240 
241 	if (cc->flags & CLIENT_HIDDEN)
242 		return;
243 
244 	XInstallColormap(X_Dpy, cc->colormap);
245 
246 	if ((cc->flags & CLIENT_INPUT) ||
247 	    (!(cc->flags & CLIENT_WM_TAKE_FOCUS))) {
248 		XSetInputFocus(X_Dpy, cc->win,
249 		    RevertToPointerRoot, CurrentTime);
250 	}
251 	if (cc->flags & CLIENT_WM_TAKE_FOCUS)
252 		xu_send_clientmsg(cc->win, cwmh[WM_TAKE_FOCUS], Last_Event_Time);
253 
254 	if ((oldcc = client_current(sc)) != NULL) {
255 		oldcc->flags &= ~CLIENT_ACTIVE;
256 		client_draw_border(oldcc);
257 	}
258 
259 	/* If we're in the middle of cycling, don't change the order. */
260 	if (!sc->cycling)
261 		client_mtf(cc);
262 
263 	cc->flags |= CLIENT_ACTIVE;
264 	cc->flags &= ~CLIENT_URGENCY;
265 	client_draw_border(cc);
266 	conf_grab_mouse(cc->win);
267 	xu_ewmh_net_active_window(sc, cc->win);
268 }
269 
270 void
client_toggle_freeze(struct client_ctx * cc)271 client_toggle_freeze(struct client_ctx *cc)
272 {
273 	if (cc->flags & CLIENT_FULLSCREEN)
274 		return;
275 
276 	cc->flags ^= CLIENT_FREEZE;
277 	xu_ewmh_set_net_wm_state(cc);
278 }
279 
280 void
client_toggle_hidden(struct client_ctx * cc)281 client_toggle_hidden(struct client_ctx *cc)
282 {
283 	cc->flags ^= CLIENT_HIDDEN;
284 	xu_ewmh_set_net_wm_state(cc);
285 }
286 
287 void
client_toggle_skip_pager(struct client_ctx * cc)288 client_toggle_skip_pager(struct client_ctx *cc)
289 {
290 	cc->flags ^= CLIENT_SKIP_PAGER;
291 	xu_ewmh_set_net_wm_state(cc);
292 }
293 
294 void
client_toggle_skip_taskbar(struct client_ctx * cc)295 client_toggle_skip_taskbar(struct client_ctx *cc)
296 {
297 	cc->flags ^= CLIENT_SKIP_TASKBAR;
298 	xu_ewmh_set_net_wm_state(cc);
299 }
300 
301 void
client_toggle_sticky(struct client_ctx * cc)302 client_toggle_sticky(struct client_ctx *cc)
303 {
304 	cc->flags ^= CLIENT_STICKY;
305 	xu_ewmh_set_net_wm_state(cc);
306 }
307 
308 void
client_toggle_fullscreen(struct client_ctx * cc)309 client_toggle_fullscreen(struct client_ctx *cc)
310 {
311 	struct screen_ctx	*sc = cc->sc;
312 	struct geom		 area;
313 
314 	if ((cc->flags & CLIENT_FREEZE) &&
315 	    !(cc->flags & CLIENT_FULLSCREEN))
316 		return;
317 
318 	if (cc->flags & CLIENT_FULLSCREEN) {
319 		if (!(cc->flags & CLIENT_IGNORE))
320 			cc->bwidth = Conf.bwidth;
321 		cc->geom = cc->fullgeom;
322 		cc->flags &= ~(CLIENT_FULLSCREEN | CLIENT_FREEZE);
323 		goto resize;
324 	}
325 
326 	cc->fullgeom = cc->geom;
327 
328 	area = screen_area(sc,
329 	    cc->geom.x + cc->geom.w / 2,
330 	    cc->geom.y + cc->geom.h / 2, 0);
331 
332 	cc->bwidth = 0;
333 	cc->geom = area;
334 	cc->flags |= (CLIENT_FULLSCREEN | CLIENT_FREEZE);
335 
336 resize:
337 	client_resize(cc, 0);
338 	xu_ewmh_set_net_wm_state(cc);
339 }
340 
341 void
client_toggle_maximize(struct client_ctx * cc)342 client_toggle_maximize(struct client_ctx *cc)
343 {
344 	struct screen_ctx	*sc = cc->sc;
345 	struct geom		 area;
346 
347 	if (cc->flags & CLIENT_FREEZE)
348 		return;
349 
350 	if ((cc->flags & CLIENT_MAXFLAGS) == CLIENT_MAXIMIZED) {
351 		cc->geom = cc->savegeom;
352 		cc->flags &= ~CLIENT_MAXIMIZED;
353 		goto resize;
354 	}
355 
356 	if (!(cc->flags & CLIENT_VMAXIMIZED)) {
357 		cc->savegeom.h = cc->geom.h;
358 		cc->savegeom.y = cc->geom.y;
359 	}
360 
361 	if (!(cc->flags & CLIENT_HMAXIMIZED)) {
362 		cc->savegeom.w = cc->geom.w;
363 		cc->savegeom.x = cc->geom.x;
364 	}
365 
366 	area = screen_area(sc,
367 	    cc->geom.x + cc->geom.w / 2,
368 	    cc->geom.y + cc->geom.h / 2, 1);
369 
370 	cc->geom.x = area.x;
371 	cc->geom.y = area.y;
372 	cc->geom.w = area.w - (cc->bwidth * 2);
373 	cc->geom.h = area.h - (cc->bwidth * 2);
374 	cc->flags |= CLIENT_MAXIMIZED;
375 
376 resize:
377 	client_resize(cc, 0);
378 	xu_ewmh_set_net_wm_state(cc);
379 }
380 
381 void
client_toggle_vmaximize(struct client_ctx * cc)382 client_toggle_vmaximize(struct client_ctx *cc)
383 {
384 	struct screen_ctx	*sc = cc->sc;
385 	struct geom		 area;
386 
387 	if (cc->flags & CLIENT_FREEZE)
388 		return;
389 
390 	if (cc->flags & CLIENT_VMAXIMIZED) {
391 		cc->geom.y = cc->savegeom.y;
392 		cc->geom.h = cc->savegeom.h;
393 		cc->flags &= ~CLIENT_VMAXIMIZED;
394 		goto resize;
395 	}
396 
397 	cc->savegeom.y = cc->geom.y;
398 	cc->savegeom.h = cc->geom.h;
399 
400 	area = screen_area(sc,
401 	    cc->geom.x + cc->geom.w / 2,
402 	    cc->geom.y + cc->geom.h / 2, 1);
403 
404 	cc->geom.y = area.y;
405 	cc->geom.h = area.h - (cc->bwidth * 2);
406 	cc->flags |= CLIENT_VMAXIMIZED;
407 
408 resize:
409 	client_resize(cc, 0);
410 	xu_ewmh_set_net_wm_state(cc);
411 }
412 
413 void
client_toggle_hmaximize(struct client_ctx * cc)414 client_toggle_hmaximize(struct client_ctx *cc)
415 {
416 	struct screen_ctx	*sc = cc->sc;
417 	struct geom		 area;
418 
419 	if (cc->flags & CLIENT_FREEZE)
420 		return;
421 
422 	if (cc->flags & CLIENT_HMAXIMIZED) {
423 		cc->geom.x = cc->savegeom.x;
424 		cc->geom.w = cc->savegeom.w;
425 		cc->flags &= ~CLIENT_HMAXIMIZED;
426 		goto resize;
427 	}
428 
429 	cc->savegeom.x = cc->geom.x;
430 	cc->savegeom.w = cc->geom.w;
431 
432 	area = screen_area(sc,
433 	    cc->geom.x + cc->geom.w / 2,
434 	    cc->geom.y + cc->geom.h / 2, 1);
435 
436 	cc->geom.x = area.x;
437 	cc->geom.w = area.w - (cc->bwidth * 2);
438 	cc->flags |= CLIENT_HMAXIMIZED;
439 
440 resize:
441 	client_resize(cc, 0);
442 	xu_ewmh_set_net_wm_state(cc);
443 }
444 
445 void
client_resize(struct client_ctx * cc,int reset)446 client_resize(struct client_ctx *cc, int reset)
447 {
448 	if (reset) {
449 		cc->flags &= ~CLIENT_MAXIMIZED;
450 		xu_ewmh_set_net_wm_state(cc);
451 	}
452 
453 	client_draw_border(cc);
454 
455 	XMoveResizeWindow(X_Dpy, cc->win, cc->geom.x,
456 	    cc->geom.y, cc->geom.w, cc->geom.h);
457 	cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw;
458 	cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch;
459 	client_config(cc);
460 }
461 
462 void
client_move(struct client_ctx * cc)463 client_move(struct client_ctx *cc)
464 {
465 	XMoveWindow(X_Dpy, cc->win, cc->geom.x, cc->geom.y);
466 	client_config(cc);
467 }
468 
469 void
client_lower(struct client_ctx * cc)470 client_lower(struct client_ctx *cc)
471 {
472 	XLowerWindow(X_Dpy, cc->win);
473 }
474 
475 void
client_raise(struct client_ctx * cc)476 client_raise(struct client_ctx *cc)
477 {
478 	XRaiseWindow(X_Dpy, cc->win);
479 }
480 
481 void
client_config(struct client_ctx * cc)482 client_config(struct client_ctx *cc)
483 {
484 	XConfigureEvent	 cn;
485 
486 	(void)memset(&cn, 0, sizeof(cn));
487 	cn.type = ConfigureNotify;
488 	cn.event = cc->win;
489 	cn.window = cc->win;
490 	cn.x = cc->geom.x;
491 	cn.y = cc->geom.y;
492 	cn.width = cc->geom.w;
493 	cn.height = cc->geom.h;
494 	cn.border_width = cc->bwidth;
495 	cn.above = None;
496 	cn.override_redirect = 0;
497 
498 	XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&cn);
499 }
500 
501 void
client_ptr_inbound(struct client_ctx * cc,int getpos)502 client_ptr_inbound(struct client_ctx *cc, int getpos)
503 {
504 	if (getpos)
505 		xu_ptr_get(cc->win, &cc->ptr.x, &cc->ptr.y);
506 
507 	if (cc->ptr.x < 0)
508 		cc->ptr.x = 0;
509 	else if (cc->ptr.x > cc->geom.w - 1)
510 		cc->ptr.x = cc->geom.w - 1;
511 	if (cc->ptr.y < 0)
512 		cc->ptr.y = 0;
513 	else if (cc->ptr.y > cc->geom.h - 1)
514 		cc->ptr.y = cc->geom.h - 1;
515 
516 	client_ptr_warp(cc);
517 }
518 
519 void
client_ptr_warp(struct client_ctx * cc)520 client_ptr_warp(struct client_ctx *cc)
521 {
522 	xu_ptr_set(cc->win, cc->ptr.x, cc->ptr.y);
523 }
524 
525 void
client_ptr_save(struct client_ctx * cc)526 client_ptr_save(struct client_ctx *cc)
527 {
528 	int	 x, y;
529 
530 	xu_ptr_get(cc->win, &x, &y);
531 	if (client_inbound(cc, x, y)) {
532 		cc->ptr.x = x;
533 		cc->ptr.y = y;
534 	} else {
535 		cc->ptr.x = cc->geom.w / 2;
536 		cc->ptr.y = cc->geom.h / 2;
537 	}
538 }
539 
540 void
client_hide(struct client_ctx * cc)541 client_hide(struct client_ctx *cc)
542 {
543 	XUnmapWindow(X_Dpy, cc->win);
544 
545 	if (cc->flags & CLIENT_ACTIVE) {
546 		cc->flags &= ~CLIENT_ACTIVE;
547 		xu_ewmh_net_active_window(cc->sc, None);
548 	}
549 	cc->flags |= CLIENT_HIDDEN;
550 	xu_set_wm_state(cc->win, IconicState);
551 }
552 
553 void
client_show(struct client_ctx * cc)554 client_show(struct client_ctx *cc)
555 {
556 	XMapRaised(X_Dpy, cc->win);
557 
558 	cc->flags &= ~CLIENT_HIDDEN;
559 	xu_set_wm_state(cc->win, NormalState);
560 	client_draw_border(cc);
561 }
562 
563 void
client_urgency(struct client_ctx * cc)564 client_urgency(struct client_ctx *cc)
565 {
566 	if (!(cc->flags & CLIENT_ACTIVE))
567 		cc->flags |= CLIENT_URGENCY;
568 }
569 
570 void
client_draw_border(struct client_ctx * cc)571 client_draw_border(struct client_ctx *cc)
572 {
573 	struct screen_ctx	*sc = cc->sc;
574 	unsigned long		 pixel;
575 
576 	if (cc->flags & CLIENT_ACTIVE)
577 		switch (cc->flags & CLIENT_HIGHLIGHT) {
578 		case CLIENT_GROUP:
579 			pixel = sc->xftcolor[CWM_COLOR_BORDER_GROUP].pixel;
580 			break;
581 		case CLIENT_UNGROUP:
582 			pixel = sc->xftcolor[CWM_COLOR_BORDER_UNGROUP].pixel;
583 			break;
584 		default:
585 			pixel = sc->xftcolor[CWM_COLOR_BORDER_ACTIVE].pixel;
586 			break;
587 		}
588 	else
589 		pixel = sc->xftcolor[CWM_COLOR_BORDER_INACTIVE].pixel;
590 
591 	if (cc->flags & CLIENT_URGENCY)
592 		pixel = sc->xftcolor[CWM_COLOR_BORDER_URGENCY].pixel;
593 
594 	XSetWindowBorderWidth(X_Dpy, cc->win, (unsigned int)cc->bwidth);
595 	XSetWindowBorder(X_Dpy, cc->win, pixel);
596 }
597 
598 static void
client_class_hint(struct client_ctx * cc)599 client_class_hint(struct client_ctx *cc)
600 {
601 	XClassHint	ch;
602 
603 	if (XGetClassHint(X_Dpy, cc->win, &ch)) {
604 		if (ch.res_class) {
605 			cc->res_class = xstrdup(ch.res_class);
606 			XFree(ch.res_class);
607 		}
608 		if (ch.res_name) {
609 			cc->res_name = xstrdup(ch.res_name);
610 			XFree(ch.res_name);
611 		}
612 	}
613 }
614 
615 static void
client_wm_protocols(struct client_ctx * cc)616 client_wm_protocols(struct client_ctx *cc)
617 {
618 	Atom	*p;
619 	int	 i, j;
620 
621 	if (XGetWMProtocols(X_Dpy, cc->win, &p, &j)) {
622 		for (i = 0; i < j; i++) {
623 			if (p[i] == cwmh[WM_DELETE_WINDOW])
624 				cc->flags |= CLIENT_WM_DELETE_WINDOW;
625 			else if (p[i] == cwmh[WM_TAKE_FOCUS])
626 				cc->flags |= CLIENT_WM_TAKE_FOCUS;
627 		}
628 		XFree(p);
629 	}
630 }
631 
632 void
client_wm_hints(struct client_ctx * cc)633 client_wm_hints(struct client_ctx *cc)
634 {
635 	XWMHints	*wmh;
636 
637 	if ((wmh = XGetWMHints(X_Dpy, cc->win)) != NULL) {
638 		if ((wmh->flags & InputHint) && (wmh->input))
639 			cc->flags |= CLIENT_INPUT;
640 		if ((wmh->flags & XUrgencyHint))
641 			client_urgency(cc);
642 		if ((wmh->flags & StateHint))
643 			cc->initial_state = wmh->initial_state;
644 		XFree(wmh);
645 	}
646 }
647 
648 void
client_close(struct client_ctx * cc)649 client_close(struct client_ctx *cc)
650 {
651 	if (cc->flags & CLIENT_WM_DELETE_WINDOW)
652 		xu_send_clientmsg(cc->win, cwmh[WM_DELETE_WINDOW], CurrentTime);
653 	else
654 		XKillClient(X_Dpy, cc->win);
655 }
656 
657 void
client_set_name(struct client_ctx * cc)658 client_set_name(struct client_ctx *cc)
659 {
660 	struct winname	*wn, *wnnxt;
661 	int		 i = 0;
662 
663 	free(cc->name);
664 	if (!xu_get_strprop(cc->win, ewmh[_NET_WM_NAME], &cc->name))
665 		if (!xu_get_strprop(cc->win, XA_WM_NAME, &cc->name))
666 			cc->name = xstrdup("");
667 
668 	TAILQ_FOREACH_SAFE(wn, &cc->nameq, entry, wnnxt) {
669 		if (strcmp(wn->name, cc->name) == 0) {
670 			TAILQ_REMOVE(&cc->nameq, wn, entry);
671 			free(wn->name);
672 			free(wn);
673 		}
674 		i++;
675 	}
676 	wn = xmalloc(sizeof(*wn));
677 	wn->name = xstrdup(cc->name);
678 	TAILQ_INSERT_TAIL(&cc->nameq, wn, entry);
679 
680 	/* Garbage collection. */
681 	if ((i + 1) > Conf.nameqlen) {
682 		wn = TAILQ_FIRST(&cc->nameq);
683 		TAILQ_REMOVE(&cc->nameq, wn, entry);
684 		free(wn->name);
685 		free(wn);
686 	}
687 }
688 
689 static void
client_placement(struct client_ctx * cc)690 client_placement(struct client_ctx *cc)
691 {
692 	struct screen_ctx	*sc = cc->sc;
693 
694 	if (cc->hint.flags & (USPosition | PPosition)) {
695 		if (cc->geom.x >= sc->view.w)
696 			cc->geom.x = sc->view.w - cc->bwidth - 1;
697 		if (cc->geom.x + cc->geom.w + cc->bwidth <= 0)
698 			cc->geom.x = -(cc->geom.w + cc->bwidth - 1);
699 		if (cc->geom.y >= sc->view.h)
700 			cc->geom.x = sc->view.h - cc->bwidth - 1;
701 		if (cc->geom.y + cc->geom.h + cc->bwidth <= 0)
702 			cc->geom.y = -(cc->geom.h + cc->bwidth - 1);
703 		if (cc->flags & CLIENT_IGNORE) {
704 			if (((cc->obwidth * 2) + cc->geom.x + cc->geom.w) == sc->view.w)
705 				cc->geom.x += cc->obwidth * 2;
706 			if (((cc->obwidth * 2) + cc->geom.y + cc->geom.h) == sc->view.h)
707 				cc->geom.y += cc->obwidth * 2;
708 		}
709 	} else {
710 		struct geom	 area;
711 		int		 xmouse, ymouse, xslack, yslack;
712 
713 		xu_ptr_get(sc->rootwin, &xmouse, &ymouse);
714 		area = screen_area(sc, xmouse, ymouse, 1);
715 
716 		xmouse = MAX(MAX(xmouse, area.x) - cc->geom.w / 2, area.x);
717 		ymouse = MAX(MAX(ymouse, area.y) - cc->geom.h / 2, area.y);
718 
719 		xslack = area.x + area.w - cc->geom.w - cc->bwidth * 2;
720 		yslack = area.y + area.h - cc->geom.h - cc->bwidth * 2;
721 
722 		if (xslack >= area.x) {
723 			cc->geom.x = MAX(MIN(xmouse, xslack), area.x);
724 		} else {
725 			cc->geom.x = area.x;
726 			cc->geom.w = area.x + area.w;
727 		}
728 		if (yslack >= area.y) {
729 			cc->geom.y = MAX(MIN(ymouse, yslack), area.y);
730 		} else {
731 			cc->geom.y = area.y;
732 			cc->geom.h = area.y + area.h;
733 		}
734 	}
735 }
736 
737 void
client_mtf(struct client_ctx * cc)738 client_mtf(struct client_ctx *cc)
739 {
740 	struct screen_ctx	*sc = cc->sc;
741 
742 	TAILQ_REMOVE(&sc->clientq, cc, entry);
743 	TAILQ_INSERT_HEAD(&sc->clientq, cc, entry);
744 }
745 
746 void
client_get_sizehints(struct client_ctx * cc)747 client_get_sizehints(struct client_ctx *cc)
748 {
749 	long		 tmp;
750 	XSizeHints	 size;
751 
752 	if (!XGetWMNormalHints(X_Dpy, cc->win, &size, &tmp))
753 		size.flags = 0;
754 
755 	cc->hint.flags = size.flags;
756 
757 	if (size.flags & PBaseSize) {
758 		cc->hint.basew = size.base_width;
759 		cc->hint.baseh = size.base_height;
760 	} else if (size.flags & PMinSize) {
761 		cc->hint.basew = size.min_width;
762 		cc->hint.baseh = size.min_height;
763 	}
764 	if (size.flags & PMinSize) {
765 		cc->hint.minw = size.min_width;
766 		cc->hint.minh = size.min_height;
767 	} else if (size.flags & PBaseSize) {
768 		cc->hint.minw = size.base_width;
769 		cc->hint.minh = size.base_height;
770 	}
771 	if (size.flags & PMaxSize) {
772 		cc->hint.maxw = size.max_width;
773 		cc->hint.maxh = size.max_height;
774 	}
775 	if (size.flags & PResizeInc) {
776 		cc->hint.incw = size.width_inc;
777 		cc->hint.inch = size.height_inc;
778 	}
779 	cc->hint.incw = MAX(1, cc->hint.incw);
780 	cc->hint.inch = MAX(1, cc->hint.inch);
781 	cc->hint.minw = MAX(1, cc->hint.minw);
782 	cc->hint.minh = MAX(1, cc->hint.minh);
783 
784 	if (size.flags & PAspect) {
785 		if (size.min_aspect.x > 0)
786 			cc->hint.mina = (float)size.min_aspect.y /
787 			    size.min_aspect.x;
788 		if (size.max_aspect.y > 0)
789 			cc->hint.maxa = (float)size.max_aspect.x /
790 			    size.max_aspect.y;
791 	}
792 }
793 
794 void
client_apply_sizehints(struct client_ctx * cc)795 client_apply_sizehints(struct client_ctx *cc)
796 {
797 	Bool		 baseismin;
798 
799 	baseismin = (cc->hint.basew == cc->hint.minw) &&
800 	    (cc->hint.baseh == cc->hint.minh);
801 
802 	/* temporarily remove base dimensions, ICCCM 4.1.2.3 */
803 	if (!baseismin) {
804 		cc->geom.w -= cc->hint.basew;
805 		cc->geom.h -= cc->hint.baseh;
806 	}
807 
808 	/* adjust for aspect limits */
809 	if (cc->hint.mina && cc->hint.maxa) {
810 		if (cc->hint.maxa < (float)cc->geom.w / cc->geom.h)
811 			cc->geom.w = cc->geom.h * cc->hint.maxa;
812 		else if (cc->hint.mina < (float)cc->geom.h / cc->geom.w)
813 			cc->geom.h = cc->geom.w * cc->hint.mina;
814 	}
815 
816 	/* remove base dimensions for increment */
817 	if (baseismin) {
818 		cc->geom.w -= cc->hint.basew;
819 		cc->geom.h -= cc->hint.baseh;
820 	}
821 
822 	/* adjust for increment value */
823 	cc->geom.w -= cc->geom.w % cc->hint.incw;
824 	cc->geom.h -= cc->geom.h % cc->hint.inch;
825 
826 	/* restore base dimensions */
827 	cc->geom.w += cc->hint.basew;
828 	cc->geom.h += cc->hint.baseh;
829 
830 	/* adjust for min width/height */
831 	cc->geom.w = MAX(cc->geom.w, cc->hint.minw);
832 	cc->geom.h = MAX(cc->geom.h, cc->hint.minh);
833 
834 	/* adjust for max width/height */
835 	if (cc->hint.maxw)
836 		cc->geom.w = MIN(cc->geom.w, cc->hint.maxw);
837 	if (cc->hint.maxh)
838 		cc->geom.h = MIN(cc->geom.h, cc->hint.maxh);
839 }
840 
841 static void
client_mwm_hints(struct client_ctx * cc)842 client_mwm_hints(struct client_ctx *cc)
843 {
844 	struct mwm_hints	*mwmh;
845 
846 	if (xu_get_prop(cc->win, cwmh[_MOTIF_WM_HINTS],
847 	    cwmh[_MOTIF_WM_HINTS], MWM_HINTS_ELEMENTS,
848 	    (unsigned char **)&mwmh) == MWM_HINTS_ELEMENTS) {
849 		if (mwmh->flags & MWM_FLAGS_DECORATIONS &&
850 		    !(mwmh->decorations & MWM_DECOR_ALL) &&
851 		    !(mwmh->decorations & MWM_DECOR_BORDER))
852 			cc->bwidth = 0;
853 		XFree(mwmh);
854 	}
855 }
856 
857 void
client_transient(struct client_ctx * cc)858 client_transient(struct client_ctx *cc)
859 {
860 	struct client_ctx	*tc;
861 	Window			 trans;
862 
863 	if (XGetTransientForHint(X_Dpy, cc->win, &trans)) {
864 		if ((tc = client_find(trans)) != NULL) {
865 			if (tc->flags & CLIENT_IGNORE) {
866 				cc->flags |= CLIENT_IGNORE;
867 				cc->bwidth = tc->bwidth;
868 			}
869 		}
870 	}
871 }
872 
873 int
client_inbound(struct client_ctx * cc,int x,int y)874 client_inbound(struct client_ctx *cc, int x, int y)
875 {
876 	return(x < cc->geom.w && x >= 0 &&
877 	    y < cc->geom.h && y >= 0);
878 }
879 
880 int
client_snapcalc(int n0,int n1,int e0,int e1,int snapdist)881 client_snapcalc(int n0, int n1, int e0, int e1, int snapdist)
882 {
883 	int	 s0, s1;
884 
885 	s0 = s1 = 0;
886 
887 	if (abs(e0 - n0) <= snapdist)
888 		s0 = e0 - n0;
889 
890 	if (abs(e1 - n1) <= snapdist)
891 		s1 = e1 - n1;
892 
893 	/* possible to snap in both directions */
894 	if (s0 != 0 && s1 != 0)
895 		if (abs(s0) < abs(s1))
896 			return s0;
897 		else
898 			return s1;
899 	else if (s0 != 0)
900 		return s0;
901 	else if (s1 != 0)
902 		return s1;
903 	else
904 		return 0;
905 }
906 
907 void
client_htile(struct client_ctx * cc)908 client_htile(struct client_ctx *cc)
909 {
910 	struct client_ctx	*ci;
911 	struct screen_ctx 	*sc = cc->sc;
912 	struct geom 		 area;
913 	int 			 i, n, mh, x, w, h;
914 
915 	i = n = 0;
916 	area = screen_area(sc,
917 	    cc->geom.x + cc->geom.w / 2,
918 	    cc->geom.y + cc->geom.h / 2, 1);
919 
920 	TAILQ_FOREACH(ci, &sc->clientq, entry) {
921 		if (ci->gc != cc->gc)
922 			continue;
923 		if (ci->flags & CLIENT_HIDDEN ||
924 		    ci->flags & CLIENT_IGNORE || (ci == cc) ||
925 		    ci->geom.x < area.x ||
926 		    ci->geom.x > (area.x + area.w) ||
927 		    ci->geom.y < area.y ||
928 		    ci->geom.y > (area.y + area.h))
929 			continue;
930 		n++;
931 	}
932 	if (n == 0)
933 		return;
934 
935 	if (cc->flags & CLIENT_VMAXIMIZED ||
936 	    cc->geom.h + (cc->bwidth * 2) >= area.h)
937 		return;
938 
939 	cc->flags &= ~CLIENT_HMAXIMIZED;
940 	cc->geom.x = area.x;
941 	cc->geom.y = area.y;
942 	cc->geom.w = area.w - (cc->bwidth * 2);
943 	if (Conf.htile > 0)
944 		cc->geom.h = ((area.h - (cc->bwidth * 2)) * Conf.htile) / 100;
945 	client_resize(cc, 1);
946 	client_ptr_warp(cc);
947 
948 	mh = cc->geom.h + (cc->bwidth * 2);
949 	x = area.x;
950 	w = area.w / n;
951 	h = area.h - mh;
952 	TAILQ_FOREACH(ci, &sc->clientq, entry) {
953 		if (ci->gc != cc->gc)
954 			continue;
955 		if (ci->flags & CLIENT_HIDDEN ||
956 		    ci->flags & CLIENT_IGNORE || (ci == cc) ||
957 		    ci->geom.x < area.x ||
958 		    ci->geom.x > (area.x + area.w) ||
959 		    ci->geom.y < area.y ||
960 		    ci->geom.y > (area.y + area.h))
961 			continue;
962 		ci->bwidth = Conf.bwidth;
963 		ci->geom.x = x;
964 		ci->geom.y = area.y + mh;
965 		ci->geom.w = w - (ci->bwidth * 2);
966 		ci->geom.h = h - (ci->bwidth * 2);
967 		if (i + 1 == n)
968 			ci->geom.w = area.x + area.w -
969 			    ci->geom.x - (ci->bwidth * 2);
970 		x += w;
971 		i++;
972 		client_resize(ci, 1);
973 	}
974 }
975 
976 void
client_vtile(struct client_ctx * cc)977 client_vtile(struct client_ctx *cc)
978 {
979 	struct client_ctx	*ci;
980 	struct screen_ctx 	*sc = cc->sc;
981 	struct geom 		 area;
982 	int 			 i, n, mw, y, w, h;
983 
984 	i = n = 0;
985 	area = screen_area(sc,
986 	    cc->geom.x + cc->geom.w / 2,
987 	    cc->geom.y + cc->geom.h / 2, 1);
988 
989 	TAILQ_FOREACH(ci, &sc->clientq, entry) {
990 		if (ci->gc != cc->gc)
991 			continue;
992 		if (ci->flags & CLIENT_HIDDEN ||
993 		    ci->flags & CLIENT_IGNORE || (ci == cc) ||
994 		    ci->geom.x < area.x ||
995 		    ci->geom.x > (area.x + area.w) ||
996 		    ci->geom.y < area.y ||
997 		    ci->geom.y > (area.y + area.h))
998 			continue;
999 		n++;
1000 	}
1001 	if (n == 0)
1002 		return;
1003 
1004 	if (cc->flags & CLIENT_HMAXIMIZED ||
1005 	    cc->geom.w + (cc->bwidth * 2) >= area.w)
1006 		return;
1007 
1008 	cc->flags &= ~CLIENT_VMAXIMIZED;
1009 	cc->geom.x = area.x;
1010 	cc->geom.y = area.y;
1011 	if (Conf.vtile > 0)
1012 		cc->geom.w = ((area.w - (cc->bwidth * 2)) * Conf.vtile) / 100;
1013 	cc->geom.h = area.h - (cc->bwidth * 2);
1014 	client_resize(cc, 1);
1015 	client_ptr_warp(cc);
1016 
1017 	mw = cc->geom.w + (cc->bwidth * 2);
1018 	y = area.y;
1019 	h = area.h / n;
1020 	w = area.w - mw;
1021 	TAILQ_FOREACH(ci, &sc->clientq, entry) {
1022 		if (ci->gc != cc->gc)
1023 			continue;
1024 		if (ci->flags & CLIENT_HIDDEN ||
1025 		    ci->flags & CLIENT_IGNORE || (ci == cc) ||
1026 		    ci->geom.x < area.x ||
1027 		    ci->geom.x > (area.x + area.w) ||
1028 		    ci->geom.y < area.y ||
1029 		    ci->geom.y > (area.y + area.h))
1030 			continue;
1031 		ci->bwidth = Conf.bwidth;
1032 		ci->geom.x = area.x + mw;
1033 		ci->geom.y = y;
1034 		ci->geom.w = w - (ci->bwidth * 2);
1035 		ci->geom.h = h - (ci->bwidth * 2);
1036 		if (i + 1 == n)
1037 			ci->geom.h = area.y + area.h -
1038 			    ci->geom.y - (ci->bwidth * 2);
1039 		y += h;
1040 		i++;
1041 		client_resize(ci, 1);
1042 	}
1043 }
1044