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