]> Dogcows Code - chaz/openbox/blob - openbox/client.c
fix
[chaz/openbox] / openbox / client.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 client.c for the Openbox window manager
4 Copyright (c) 2004 Mikael Magnusson
5 Copyright (c) 2003 Ben Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "client.h"
21 #include "debug.h"
22 #include "startupnotify.h"
23 #include "dock.h"
24 #include "xerror.h"
25 #include "screen.h"
26 #include "moveresize.h"
27 #include "place.h"
28 #include "prop.h"
29 #include "extensions.h"
30 #include "frame.h"
31 #include "session.h"
32 #include "event.h"
33 #include "grab.h"
34 #include "focus.h"
35 #include "stacking.h"
36 #include "openbox.h"
37 #include "group.h"
38 #include "config.h"
39 #include "menuframe.h"
40 #include "keyboard.h"
41 #include "mouse.h"
42 #include "render/render.h"
43
44 #include <glib.h>
45 #include <X11/Xutil.h>
46
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
49 StructureNotifyMask)
50
51 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
52 ButtonMotionMask)
53
54 typedef struct
55 {
56 ObClientDestructor func;
57 gpointer data;
58 } Destructor;
59
60 GList *client_list = NULL;
61 GSList *client_destructors = NULL;
62
63 static void client_get_all(ObClient *self);
64 static void client_toggle_border(ObClient *self, gboolean show);
65 static void client_get_startup_id(ObClient *self);
66 static void client_get_area(ObClient *self);
67 static void client_get_desktop(ObClient *self);
68 static void client_get_state(ObClient *self);
69 static void client_get_shaped(ObClient *self);
70 static void client_get_mwm_hints(ObClient *self);
71 static void client_get_gravity(ObClient *self);
72 static void client_showhide(ObClient *self);
73 static void client_change_allowed_actions(ObClient *self);
74 static void client_change_state(ObClient *self);
75 static void client_apply_startup_state(ObClient *self);
76 static void client_restore_session_state(ObClient *self);
77 static void client_restore_session_stacking(ObClient *self);
78 static void client_urgent_notify(ObClient *self);
79
80 void client_startup(gboolean reconfig)
81 {
82 if (reconfig) return;
83
84 client_set_list();
85 }
86
87 void client_shutdown(gboolean reconfig)
88 {
89 }
90
91 void client_add_destructor(ObClientDestructor func, gpointer data)
92 {
93 Destructor *d = g_new(Destructor, 1);
94 d->func = func;
95 d->data = data;
96 client_destructors = g_slist_prepend(client_destructors, d);
97 }
98
99 void client_remove_destructor(ObClientDestructor func)
100 {
101 GSList *it;
102
103 for (it = client_destructors; it; it = g_slist_next(it)) {
104 Destructor *d = it->data;
105 if (d->func == func) {
106 g_free(d);
107 client_destructors = g_slist_delete_link(client_destructors, it);
108 break;
109 }
110 }
111 }
112
113 void client_set_list()
114 {
115 Window *windows, *win_it;
116 GList *it;
117 guint size = g_list_length(client_list);
118
119 /* create an array of the window ids */
120 if (size > 0) {
121 windows = g_new(Window, size);
122 win_it = windows;
123 for (it = client_list; it; it = g_list_next(it), ++win_it)
124 *win_it = ((ObClient*)it->data)->window;
125 } else
126 windows = NULL;
127
128 PROP_SETA32(RootWindow(ob_display, ob_screen),
129 net_client_list, window, (gulong*)windows, size);
130
131 if (windows)
132 g_free(windows);
133
134 stacking_set_list();
135 }
136
137 /*
138 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
139 {
140 GSList *it;
141
142 for (it = self->transients; it; it = g_slist_next(it)) {
143 if (!func(it->data, data)) return;
144 client_foreach_transient(it->data, func, data);
145 }
146 }
147
148 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
149 {
150 if (self->transient_for) {
151 if (self->transient_for != OB_TRAN_GROUP) {
152 if (!func(self->transient_for, data)) return;
153 client_foreach_ancestor(self->transient_for, func, data);
154 } else {
155 GSList *it;
156
157 for (it = self->group->members; it; it = g_slist_next(it))
158 if (it->data != self &&
159 !((ObClient*)it->data)->transient_for) {
160 if (!func(it->data, data)) return;
161 client_foreach_ancestor(it->data, func, data);
162 }
163 }
164 }
165 }
166 */
167
168 void client_manage_all()
169 {
170 guint i, j, nchild;
171 Window w, *children;
172 XWMHints *wmhints;
173 XWindowAttributes attrib;
174
175 XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
176 &w, &w, &children, &nchild);
177
178 /* remove all icon windows from the list */
179 for (i = 0; i < nchild; i++) {
180 if (children[i] == None) continue;
181 wmhints = XGetWMHints(ob_display, children[i]);
182 if (wmhints) {
183 if ((wmhints->flags & IconWindowHint) &&
184 (wmhints->icon_window != children[i]))
185 for (j = 0; j < nchild; j++)
186 if (children[j] == wmhints->icon_window) {
187 children[j] = None;
188 break;
189 }
190 XFree(wmhints);
191 }
192 }
193
194 for (i = 0; i < nchild; ++i) {
195 if (children[i] == None)
196 continue;
197 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
198 if (attrib.override_redirect) continue;
199
200 if (attrib.map_state != IsUnmapped)
201 client_manage(children[i]);
202 }
203 }
204 XFree(children);
205 }
206
207 /* This should possibly do something more interesting than just match
208 * against WM_CLASS literally. */
209 static ObAppSettings *get_settings(ObClient *client)
210 {
211 GSList *a = config_per_app_settings;
212
213 while (a) {
214 ObAppSettings *app = (ObAppSettings *) a->data;
215
216 if (!strcmp(app->name, client->name)) {
217 ob_debug("Window matching: %s\n", app->name);
218 /* Match if no role was specified in the per app setting, or if the string
219 * matches the beginning of the role, since apps like to set the role to
220 * things like browser-window-23c4b2f */
221 if (!app->role || !strncmp(app->role, client->role, strlen(app->role)))
222 return app;
223 }
224
225 a = a->next;
226 }
227 return NULL;
228 }
229
230 void client_manage(Window window)
231 {
232 ObClient *self;
233 XEvent e;
234 XWindowAttributes attrib;
235 XSetWindowAttributes attrib_set;
236 XWMHints *wmhint;
237 gboolean activate = FALSE;
238 ObAppSettings *settings;
239
240 grab_server(TRUE);
241
242 /* check if it has already been unmapped by the time we started mapping
243 the grab does a sync so we don't have to here */
244 if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
245 XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
246 XPutBackEvent(ob_display, &e);
247
248 grab_server(FALSE);
249 return; /* don't manage it */
250 }
251
252 /* make sure it isn't an override-redirect window */
253 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
254 attrib.override_redirect) {
255 grab_server(FALSE);
256 return; /* don't manage it */
257 }
258
259 /* is the window a docking app */
260 if ((wmhint = XGetWMHints(ob_display, window))) {
261 if ((wmhint->flags & StateHint) &&
262 wmhint->initial_state == WithdrawnState) {
263 dock_add(window, wmhint);
264 grab_server(FALSE);
265 XFree(wmhint);
266 return;
267 }
268 XFree(wmhint);
269 }
270
271 ob_debug("Managing window: %lx\n", window);
272
273 /* choose the events we want to receive on the CLIENT window */
274 attrib_set.event_mask = CLIENT_EVENTMASK;
275 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
276 XChangeWindowAttributes(ob_display, window,
277 CWEventMask|CWDontPropagate, &attrib_set);
278
279
280 /* create the ObClient struct, and populate it from the hints on the
281 window */
282 self = g_new0(ObClient, 1);
283 self->obwin.type = Window_Client;
284 self->window = window;
285
286 /* non-zero defaults */
287 self->title_count = 1;
288 self->wmstate = NormalState;
289 self->layer = -1;
290 self->desktop = screen_num_desktops; /* always an invalid value */
291
292 client_get_all(self);
293 client_restore_session_state(self);
294
295 sn_app_started(self->class);
296
297 /* update the focus lists, do this before the call to change_state or
298 it can end up in the list twice! */
299 focus_order_add_new(self);
300
301 client_change_state(self);
302
303 /* remove the client's border (and adjust re gravity) */
304 client_toggle_border(self, FALSE);
305
306 /* specify that if we exit, the window should not be destroyed and should
307 be reparented back to root automatically */
308 XChangeSaveSet(ob_display, window, SetModeInsert);
309
310 /* create the decoration frame for the client window */
311 self->frame = frame_new();
312
313 frame_grab_client(self->frame, self);
314
315 grab_server(FALSE);
316
317 client_apply_startup_state(self);
318
319 /* get and set application level settings */
320 settings = get_settings(self);
321
322 if (settings) {
323 /* Don't worry, we won't actually both shade and undecorate the
324 * window when push comes to shove. */
325 if (settings->shade != -1)
326 client_shade(self, settings->shade);
327 if (settings->decor != -1)
328 client_set_undecorated(self, !settings->decor);
329 if (settings->iconic != -1)
330 client_iconify(self, settings->iconic, FALSE);
331 if (settings->skip_pager != -1)
332 client->skip_pager = !!settings->skip_pager;
333 if (settings->skip_taskbar != -1)
334 client->skip_taskbar = !!settings->skip_taskbar;
335
336 /* 1 && -1 shouldn't be possible by the code in config.c */
337 if (settings->max_vert == 1 && self->max_horz == 1)
338 client_maximize(self, TRUE, 0, TRUE);
339 else if (settings->max_vert == 0 && self->max_horz == 0)
340 client_maximize(self, FALSE, 0, TRUE);
341 else if (settings->max_vert == 1 && self->max_horz == 0) {
342 client_maximize(self, TRUE, 2, TRUE);
343 client_maximize(self, FALSE, 1, TRUE);
344 } else if (settings->max_vert == 0 && self->max_horz == 1) {
345 client_maximize(self, TRUE, 1, TRUE);
346 client_maximize(self, FALSE, 2, TRUE);
347 }
348
349 if (settings->fullscreen != -1)
350 client_fullscreen(self, !!settings->fullscreen, TRUE);
351
352 if (settings->desktop < screen_num_desktops)
353 client_set_desktop(self, settings->desktop, FALSE);
354
355 if (settings->layer > -2 && settings->layer < 2)
356 client_set_layer(self, settings->layer);
357
358 }
359
360 stacking_add(CLIENT_AS_WINDOW(self));
361 client_restore_session_stacking(self);
362
363 /* focus the new window? */
364 if (ob_state() != OB_STATE_STARTING &&
365 (config_focus_new || client_search_focus_parent(self)) ||
366 (settings && settings->focus == TRUE) &&
367 /* note the check against Type_Normal/Dialog, not client_normal(self),
368 which would also include other types. in this case we want more
369 strict rules for focus */
370 (self->type == OB_CLIENT_TYPE_NORMAL ||
371 self->type == OB_CLIENT_TYPE_DIALOG))
372 {
373 activate = TRUE;
374 #if 0
375 if (self->desktop != screen_desktop) {
376 /* activate the window */
377 activate = TRUE;
378 } else {
379 gboolean group_foc = FALSE;
380
381 if (self->group) {
382 GSList *it;
383
384 for (it = self->group->members; it; it = g_slist_next(it))
385 {
386 if (client_focused(it->data))
387 {
388 group_foc = TRUE;
389 break;
390 }
391 }
392 }
393 if ((group_foc ||
394 (!self->transient_for && (!self->group ||
395 !self->group->members->next))) ||
396 client_search_focus_tree_full(self) ||
397 !focus_client ||
398 !client_normal(focus_client))
399 {
400 /* activate the window */
401 activate = TRUE;
402 }
403 }
404 #endif
405 }
406
407 if (ob_state() == OB_STATE_RUNNING) {
408 gint x = self->area.x, ox = x;
409 gint y = self->area.y, oy = y;
410
411 place_client(self, &x, &y, settings);
412
413 /* make sure the window is visible. */
414 client_find_onscreen(self, &x, &y,
415 self->frame->area.width,
416 self->frame->area.height,
417 /* non-normal clients has less rules, and
418 windows that are being restored from a
419 session do also. we can assume you want
420 it back where you saved it. Clients saying
421 they placed themselves are subjected to
422 harder rules, ones that are placed by
423 place.c or by the user are allowed partially
424 off-screen and on xinerama divides (ie,
425 it is up to the placement routines to avoid
426 the xinerama divides) */
427 ((self->positioned & PPosition) &&
428 !(self->positioned & USPosition)) &&
429 client_normal(self) &&
430 !self->session);
431 if (x != ox || y != oy)
432 client_move(self, x, y);
433 }
434
435 keyboard_grab_for_client(self, TRUE);
436 mouse_grab_for_client(self, TRUE);
437
438 client_showhide(self);
439
440 /* use client_focus instead of client_activate cuz client_activate does
441 stuff like switch desktops etc and I'm not interested in all that when
442 a window maps since its not based on an action from the user like
443 clicking a window to activate is. so keep the new window out of the way
444 but do focus it. */
445 if (activate) {
446 /* if using focus_delay, stop the timer now so that focus doesn't go
447 moving on us */
448 event_halt_focus_delay();
449
450 client_focus(self);
451 /* since focus can change the stacking orders, if we focus the window
452 then the standard raise it gets is not enough, we need to queue one
453 for after the focus change takes place */
454 client_raise(self);
455 }
456
457 /* client_activate does this but we aret using it so we have to do it
458 here as well */
459 if (screen_showing_desktop)
460 screen_show_desktop(FALSE);
461
462 /* add to client list/map */
463 client_list = g_list_append(client_list, self);
464 g_hash_table_insert(window_map, &self->window, self);
465
466 /* this has to happen after we're in the client_list */
467 screen_update_areas();
468
469 /* update the list hints */
470 client_set_list();
471
472 ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
473 }
474
475 void client_unmanage_all()
476 {
477 while (client_list != NULL)
478 client_unmanage(client_list->data);
479 }
480
481 void client_unmanage(ObClient *self)
482 {
483 guint j;
484 GSList *it;
485
486 ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
487
488 g_assert(self != NULL);
489
490 keyboard_grab_for_client(self, FALSE);
491 mouse_grab_for_client(self, FALSE);
492
493 /* potentially fix focusLast */
494 if (config_focus_last)
495 grab_pointer(TRUE, OB_CURSOR_NONE);
496
497 /* remove the window from our save set */
498 XChangeSaveSet(ob_display, self->window, SetModeDelete);
499
500 /* we dont want events no more */
501 XSelectInput(ob_display, self->window, NoEventMask);
502
503 frame_hide(self->frame);
504
505 client_list = g_list_remove(client_list, self);
506 stacking_remove(self);
507 g_hash_table_remove(window_map, &self->window);
508
509 /* update the focus lists */
510 focus_order_remove(self);
511
512 /* once the client is out of the list, update the struts to remove it's
513 influence */
514 screen_update_areas();
515
516 for (it = client_destructors; it; it = g_slist_next(it)) {
517 Destructor *d = it->data;
518 d->func(self, d->data);
519 }
520
521 if (focus_client == self) {
522 XEvent e;
523
524 /* focus the last focused window on the desktop, and ignore enter
525 events from the unmap so it doesnt mess with the focus */
526 while (XCheckTypedEvent(ob_display, EnterNotify, &e));
527 /* remove these flags so we don't end up getting focused in the
528 fallback! */
529 self->can_focus = FALSE;
530 self->focus_notify = FALSE;
531 self->modal = FALSE;
532 client_unfocus(self);
533 }
534
535 /* tell our parent(s) that we're gone */
536 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
537 for (it = self->group->members; it; it = g_slist_next(it))
538 if (it->data != self)
539 ((ObClient*)it->data)->transients =
540 g_slist_remove(((ObClient*)it->data)->transients, self);
541 } else if (self->transient_for) { /* transient of window */
542 self->transient_for->transients =
543 g_slist_remove(self->transient_for->transients, self);
544 }
545
546 /* tell our transients that we're gone */
547 for (it = self->transients; it; it = g_slist_next(it)) {
548 if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
549 ((ObClient*)it->data)->transient_for = NULL;
550 client_calc_layer(it->data);
551 }
552 }
553
554 /* remove from its group */
555 if (self->group) {
556 group_remove(self->group, self);
557 self->group = NULL;
558 }
559
560 /* give the client its border back */
561 client_toggle_border(self, TRUE);
562
563 /* reparent the window out of the frame, and free the frame */
564 frame_release_client(self->frame, self);
565 self->frame = NULL;
566
567 if (ob_state() != OB_STATE_EXITING) {
568 /* these values should not be persisted across a window
569 unmapping/mapping */
570 PROP_ERASE(self->window, net_wm_desktop);
571 PROP_ERASE(self->window, net_wm_state);
572 PROP_ERASE(self->window, wm_state);
573 } else {
574 /* if we're left in an unmapped state, the client wont be mapped. this
575 is bad, since we will no longer be managing the window on restart */
576 XMapWindow(ob_display, self->window);
577 }
578
579
580 ob_debug("Unmanaged window 0x%lx\n", self->window);
581
582 /* free all data allocated in the client struct */
583 g_slist_free(self->transients);
584 for (j = 0; j < self->nicons; ++j)
585 g_free(self->icons[j].data);
586 if (self->nicons > 0)
587 g_free(self->icons);
588 g_free(self->title);
589 g_free(self->icon_title);
590 g_free(self->name);
591 g_free(self->class);
592 g_free(self->role);
593 g_free(self->sm_client_id);
594 g_free(self);
595
596 /* update the list hints */
597 client_set_list();
598
599 if (config_focus_last)
600 grab_pointer(FALSE, OB_CURSOR_NONE);
601 }
602
603 static void client_urgent_notify(ObClient *self)
604 {
605 if (self->urgent)
606 frame_flash_start(self->frame);
607 else
608 frame_flash_stop(self->frame);
609 }
610
611 static void client_restore_session_state(ObClient *self)
612 {
613 GList *it;
614
615 if (!(it = session_state_find(self)))
616 return;
617
618 self->session = it->data;
619
620 RECT_SET_POINT(self->area, self->session->x, self->session->y);
621 self->positioned = PPosition;
622 if (self->session->w > 0)
623 self->area.width = self->session->w;
624 if (self->session->h > 0)
625 self->area.height = self->session->h;
626 XResizeWindow(ob_display, self->window,
627 self->area.width, self->area.height);
628
629 self->desktop = (self->session->desktop == DESKTOP_ALL ?
630 self->session->desktop :
631 MIN(screen_num_desktops - 1, self->session->desktop));
632 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
633
634 self->shaded = self->session->shaded;
635 self->iconic = self->session->iconic;
636 self->skip_pager = self->session->skip_pager;
637 self->skip_taskbar = self->session->skip_taskbar;
638 self->fullscreen = self->session->fullscreen;
639 self->above = self->session->above;
640 self->below = self->session->below;
641 self->max_horz = self->session->max_horz;
642 self->max_vert = self->session->max_vert;
643 }
644
645 static void client_restore_session_stacking(ObClient *self)
646 {
647 GList *it;
648
649 if (!self->session) return;
650
651 it = g_list_find(session_saved_state, self->session);
652 for (it = g_list_previous(it); it; it = g_list_previous(it)) {
653 GList *cit;
654
655 for (cit = client_list; cit; cit = g_list_next(cit))
656 if (session_state_cmp(it->data, cit->data))
657 break;
658 if (cit) {
659 client_calc_layer(self);
660 stacking_below(CLIENT_AS_WINDOW(self),
661 CLIENT_AS_WINDOW(cit->data));
662 break;
663 }
664 }
665 }
666
667 void client_move_onscreen(ObClient *self, gboolean rude)
668 {
669 gint x = self->area.x;
670 gint y = self->area.y;
671 if (client_find_onscreen(self, &x, &y,
672 self->frame->area.width,
673 self->frame->area.height, rude)) {
674 client_move(self, x, y);
675 }
676 }
677
678 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
679 gboolean rude)
680 {
681 Rect *a;
682 gint ox = *x, oy = *y;
683
684 frame_client_gravity(self->frame, x, y); /* get where the frame
685 would be */
686
687 /* XXX watch for xinerama dead areas */
688 /* This makes sure windows aren't entirely outside of the screen so you
689 * can't see them at all */
690 if (client_normal(self)) {
691 a = screen_area(self->desktop);
692 if (!self->strut.right && *x >= a->x + a->width - 1)
693 *x = a->x + a->width - self->frame->area.width;
694 if (!self->strut.bottom && *y >= a->y + a->height - 1)
695 *y = a->y + a->height - self->frame->area.height;
696 if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
697 *x = a->x;
698 if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
699 *y = a->y;
700 }
701
702 /* This here doesn't let windows even a pixel outside the screen,
703 * when called from client_manage, programs placing themselves are
704 * forced completely onscreen, while things like
705 * xterm -geometry resolution-width/2 will work fine. Trying to
706 * place it completely offscreen will be handled in the above code.
707 * Sorry for this confused comment, i am tired. */
708 if (rude) {
709 /* avoid the xinerama monitor divide while we're at it,
710 * remember to fix the placement stuff to avoid it also and
711 * then remove this XXX */
712 a = screen_physical_area_monitor(client_monitor(self));
713 /* dont let windows map/move into the strut unless they
714 are bigger than the available area */
715 if (w <= a->width) {
716 if (!self->strut.left && *x < a->x) *x = a->x;
717 if (!self->strut.right && *x + w > a->x + a->width)
718 *x = a->x + a->width - w;
719 }
720 if (h <= a->height) {
721 if (!self->strut.top && *y < a->y) *y = a->y;
722 if (!self->strut.bottom && *y + h > a->y + a->height)
723 *y = a->y + a->height - h;
724 }
725 }
726
727 frame_frame_gravity(self->frame, x, y); /* get where the client
728 should be */
729
730 return ox != *x || oy != *y;
731 }
732
733 static void client_toggle_border(ObClient *self, gboolean show)
734 {
735 /* adjust our idea of where the client is, based on its border. When the
736 border is removed, the client should now be considered to be in a
737 different position.
738 when re-adding the border to the client, the same operation needs to be
739 reversed. */
740 gint oldx = self->area.x, oldy = self->area.y;
741 gint x = oldx, y = oldy;
742 switch(self->gravity) {
743 default:
744 case NorthWestGravity:
745 case WestGravity:
746 case SouthWestGravity:
747 break;
748 case NorthEastGravity:
749 case EastGravity:
750 case SouthEastGravity:
751 if (show) x -= self->border_width * 2;
752 else x += self->border_width * 2;
753 break;
754 case NorthGravity:
755 case SouthGravity:
756 case CenterGravity:
757 case ForgetGravity:
758 case StaticGravity:
759 if (show) x -= self->border_width;
760 else x += self->border_width;
761 break;
762 }
763 switch(self->gravity) {
764 default:
765 case NorthWestGravity:
766 case NorthGravity:
767 case NorthEastGravity:
768 break;
769 case SouthWestGravity:
770 case SouthGravity:
771 case SouthEastGravity:
772 if (show) y -= self->border_width * 2;
773 else y += self->border_width * 2;
774 break;
775 case WestGravity:
776 case EastGravity:
777 case CenterGravity:
778 case ForgetGravity:
779 case StaticGravity:
780 if (show) y -= self->border_width;
781 else y += self->border_width;
782 break;
783 }
784 self->area.x = x;
785 self->area.y = y;
786
787 if (show) {
788 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
789
790 /* move the client so it is back it the right spot _with_ its
791 border! */
792 if (x != oldx || y != oldy)
793 XMoveWindow(ob_display, self->window, x, y);
794 } else
795 XSetWindowBorderWidth(ob_display, self->window, 0);
796 }
797
798
799 static void client_get_all(ObClient *self)
800 {
801 client_get_area(self);
802 client_update_transient_for(self);
803 client_update_wmhints(self);
804 client_get_startup_id(self);
805 client_get_desktop(self);
806 client_get_shaped(self);
807
808 client_get_mwm_hints(self);
809 client_get_type(self);/* this can change the mwmhints for special cases */
810
811 /* The transient hint is used to pick a type, but the type can also affect
812 transiency (dialogs are always made transients). This is Havoc's idea,
813 but it is needed to make some apps work right (eg tsclient). */
814 client_update_transient_for(self);
815
816 client_get_state(self);
817
818 {
819 /* a couple type-based defaults for new windows */
820
821 /* this makes sure that these windows appear on all desktops */
822 if (self->type == OB_CLIENT_TYPE_DESKTOP)
823 self->desktop = DESKTOP_ALL;
824 }
825
826 client_update_protocols(self);
827
828 client_get_gravity(self); /* get the attribute gravity */
829 client_update_normal_hints(self); /* this may override the attribute
830 gravity */
831
832 /* got the type, the mwmhints, the protocols, and the normal hints
833 (min/max sizes), so we're ready to set up the decorations/functions */
834 client_setup_decor_and_functions(self);
835
836 client_update_title(self);
837 client_update_class(self);
838 client_update_sm_client_id(self);
839 client_update_strut(self);
840 client_update_icons(self);
841 }
842
843 static void client_get_startup_id(ObClient *self)
844 {
845 if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
846 if (self->group)
847 PROP_GETS(self->group->leader,
848 net_startup_id, utf8, &self->startup_id);
849 }
850
851 static void client_get_area(ObClient *self)
852 {
853 XWindowAttributes wattrib;
854 Status ret;
855
856 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
857 g_assert(ret != BadWindow);
858
859 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
860 self->border_width = wattrib.border_width;
861 }
862
863 static void client_get_desktop(ObClient *self)
864 {
865 guint32 d = screen_num_desktops; /* an always-invalid value */
866
867 if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
868 if (d >= screen_num_desktops && d != DESKTOP_ALL)
869 self->desktop = screen_num_desktops - 1;
870 else
871 self->desktop = d;
872 } else {
873 gboolean trdesk = FALSE;
874
875 if (self->transient_for) {
876 if (self->transient_for != OB_TRAN_GROUP) {
877 self->desktop = self->transient_for->desktop;
878 trdesk = TRUE;
879 } else {
880 GSList *it;
881
882 for (it = self->group->members; it; it = g_slist_next(it))
883 if (it->data != self &&
884 !((ObClient*)it->data)->transient_for) {
885 self->desktop = ((ObClient*)it->data)->desktop;
886 trdesk = TRUE;
887 break;
888 }
889 }
890 }
891 if (!trdesk) {
892 /* try get from the startup-notification protocol */
893 if (sn_get_desktop(self->startup_id, &self->desktop)) {
894 if (self->desktop >= screen_num_desktops &&
895 self->desktop != DESKTOP_ALL)
896 self->desktop = screen_num_desktops - 1;
897 } else
898 /* defaults to the current desktop */
899 self->desktop = screen_desktop;
900 }
901 }
902 if (self->desktop != d) {
903 /* set the desktop hint, to make sure that it always exists */
904 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
905 }
906 }
907
908 static void client_get_state(ObClient *self)
909 {
910 guint32 *state;
911 guint num;
912
913 if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
914 gulong i;
915 for (i = 0; i < num; ++i) {
916 if (state[i] == prop_atoms.net_wm_state_modal)
917 self->modal = TRUE;
918 else if (state[i] == prop_atoms.net_wm_state_shaded)
919 self->shaded = TRUE;
920 else if (state[i] == prop_atoms.net_wm_state_hidden)
921 self->iconic = TRUE;
922 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
923 self->skip_taskbar = TRUE;
924 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
925 self->skip_pager = TRUE;
926 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
927 self->fullscreen = TRUE;
928 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
929 self->max_vert = TRUE;
930 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
931 self->max_horz = TRUE;
932 else if (state[i] == prop_atoms.net_wm_state_above)
933 self->above = TRUE;
934 else if (state[i] == prop_atoms.net_wm_state_below)
935 self->below = TRUE;
936 else if (state[i] == prop_atoms.ob_wm_state_undecorated)
937 self->undecorated = TRUE;
938 }
939
940 g_free(state);
941 }
942
943 if (!(self->above || self->below)) {
944 if (self->group) {
945 /* apply stuff from the group */
946 GSList *it;
947 gint layer = -2;
948
949 for (it = self->group->members; it; it = g_slist_next(it)) {
950 ObClient *c = it->data;
951 if (c != self && !client_search_transient(self, c) &&
952 client_normal(self) && client_normal(c))
953 {
954 layer = MAX(layer,
955 (c->above ? 1 : (c->below ? -1 : 0)));
956 }
957 }
958 switch (layer) {
959 case -1:
960 self->below = TRUE;
961 break;
962 case -2:
963 case 0:
964 break;
965 case 1:
966 self->above = TRUE;
967 break;
968 default:
969 g_assert_not_reached();
970 break;
971 }
972 }
973 }
974 }
975
976 static void client_get_shaped(ObClient *self)
977 {
978 self->shaped = FALSE;
979 #ifdef SHAPE
980 if (extensions_shape) {
981 gint foo;
982 guint ufoo;
983 gint s;
984
985 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
986
987 XShapeQueryExtents(ob_display, self->window, &s, &foo,
988 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
989 &ufoo);
990 self->shaped = (s != 0);
991 }
992 #endif
993 }
994
995 void client_update_transient_for(ObClient *self)
996 {
997 Window t = None;
998 ObClient *target = NULL;
999
1000 if (XGetTransientForHint(ob_display, self->window, &t)) {
1001 self->transient = TRUE;
1002 if (t != self->window) { /* cant be transient to itself! */
1003 target = g_hash_table_lookup(window_map, &t);
1004 /* if this happens then we need to check for it*/
1005 g_assert(target != self);
1006 if (target && !WINDOW_IS_CLIENT(target)) {
1007 /* this can happen when a dialog is a child of
1008 a dockapp, for example */
1009 target = NULL;
1010 }
1011
1012 if (!target && self->group) {
1013 /* not transient to a client, see if it is transient for a
1014 group */
1015 if (t == self->group->leader ||
1016 t == None ||
1017 t == RootWindow(ob_display, ob_screen))
1018 {
1019 /* window is a transient for its group! */
1020 target = OB_TRAN_GROUP;
1021 }
1022 }
1023 }
1024 } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) {
1025 self->transient = TRUE;
1026 target = OB_TRAN_GROUP;
1027 } else
1028 self->transient = FALSE;
1029
1030 /* if anything has changed... */
1031 if (target != self->transient_for) {
1032 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
1033 GSList *it;
1034
1035 /* remove from old parents */
1036 for (it = self->group->members; it; it = g_slist_next(it)) {
1037 ObClient *c = it->data;
1038 if (c != self && !c->transient_for)
1039 c->transients = g_slist_remove(c->transients, self);
1040 }
1041 } else if (self->transient_for != NULL) { /* transient of window */
1042 /* remove from old parent */
1043 self->transient_for->transients =
1044 g_slist_remove(self->transient_for->transients, self);
1045 }
1046 self->transient_for = target;
1047 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
1048 GSList *it;
1049
1050 /* add to new parents */
1051 for (it = self->group->members; it; it = g_slist_next(it)) {
1052 ObClient *c = it->data;
1053 if (c != self && !c->transient_for)
1054 c->transients = g_slist_append(c->transients, self);
1055 }
1056
1057 /* remove all transients which are in the group, that causes
1058 circlular pointer hell of doom */
1059 for (it = self->group->members; it; it = g_slist_next(it)) {
1060 GSList *sit, *next;
1061 for (sit = self->transients; sit; sit = next) {
1062 next = g_slist_next(sit);
1063 if (sit->data == it->data)
1064 self->transients =
1065 g_slist_delete_link(self->transients, sit);
1066 }
1067 }
1068 } else if (self->transient_for != NULL) { /* transient of window */
1069 /* add to new parent */
1070 self->transient_for->transients =
1071 g_slist_append(self->transient_for->transients, self);
1072 }
1073 }
1074 }
1075
1076 static void client_get_mwm_hints(ObClient *self)
1077 {
1078 guint num;
1079 guint32 *hints;
1080
1081 self->mwmhints.flags = 0; /* default to none */
1082
1083 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
1084 &hints, &num)) {
1085 if (num >= OB_MWM_ELEMENTS) {
1086 self->mwmhints.flags = hints[0];
1087 self->mwmhints.functions = hints[1];
1088 self->mwmhints.decorations = hints[2];
1089 }
1090 g_free(hints);
1091 }
1092 }
1093
1094 void client_get_type(ObClient *self)
1095 {
1096 guint num, i;
1097 guint32 *val;
1098
1099 self->type = -1;
1100
1101 if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1102 /* use the first value that we know about in the array */
1103 for (i = 0; i < num; ++i) {
1104 if (val[i] == prop_atoms.net_wm_window_type_desktop)
1105 self->type = OB_CLIENT_TYPE_DESKTOP;
1106 else if (val[i] == prop_atoms.net_wm_window_type_dock)
1107 self->type = OB_CLIENT_TYPE_DOCK;
1108 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1109 self->type = OB_CLIENT_TYPE_TOOLBAR;
1110 else if (val[i] == prop_atoms.net_wm_window_type_menu)
1111 self->type = OB_CLIENT_TYPE_MENU;
1112 else if (val[i] == prop_atoms.net_wm_window_type_utility)
1113 self->type = OB_CLIENT_TYPE_UTILITY;
1114 else if (val[i] == prop_atoms.net_wm_window_type_splash)
1115 self->type = OB_CLIENT_TYPE_SPLASH;
1116 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1117 self->type = OB_CLIENT_TYPE_DIALOG;
1118 else if (val[i] == prop_atoms.net_wm_window_type_normal)
1119 self->type = OB_CLIENT_TYPE_NORMAL;
1120 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1121 /* prevent this window from getting any decor or
1122 functionality */
1123 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1124 OB_MWM_FLAG_DECORATIONS);
1125 self->mwmhints.decorations = 0;
1126 self->mwmhints.functions = 0;
1127 }
1128 if (self->type != (ObClientType) -1)
1129 break; /* grab the first legit type */
1130 }
1131 g_free(val);
1132 }
1133
1134 if (self->type == (ObClientType) -1) {
1135 /*the window type hint was not set, which means we either classify
1136 ourself as a normal window or a dialog, depending on if we are a
1137 transient. */
1138 if (self->transient)
1139 self->type = OB_CLIENT_TYPE_DIALOG;
1140 else
1141 self->type = OB_CLIENT_TYPE_NORMAL;
1142 }
1143 }
1144
1145 void client_update_protocols(ObClient *self)
1146 {
1147 guint32 *proto;
1148 guint num_return, i;
1149
1150 self->focus_notify = FALSE;
1151 self->delete_window = FALSE;
1152
1153 if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1154 for (i = 0; i < num_return; ++i) {
1155 if (proto[i] == prop_atoms.wm_delete_window) {
1156 /* this means we can request the window to close */
1157 self->delete_window = TRUE;
1158 } else if (proto[i] == prop_atoms.wm_take_focus)
1159 /* if this protocol is requested, then the window will be
1160 notified whenever we want it to receive focus */
1161 self->focus_notify = TRUE;
1162 }
1163 g_free(proto);
1164 }
1165 }
1166
1167 static void client_get_gravity(ObClient *self)
1168 {
1169 XWindowAttributes wattrib;
1170 Status ret;
1171
1172 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1173 g_assert(ret != BadWindow);
1174 self->gravity = wattrib.win_gravity;
1175 }
1176
1177 void client_update_normal_hints(ObClient *self)
1178 {
1179 XSizeHints size;
1180 glong ret;
1181 gint oldgravity = self->gravity;
1182
1183 /* defaults */
1184 self->min_ratio = 0.0f;
1185 self->max_ratio = 0.0f;
1186 SIZE_SET(self->size_inc, 1, 1);
1187 SIZE_SET(self->base_size, 0, 0);
1188 SIZE_SET(self->min_size, 0, 0);
1189 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1190
1191 /* get the hints from the window */
1192 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1193 /* normal windows can't request placement! har har
1194 if (!client_normal(self))
1195 */
1196 self->positioned = (size.flags & (PPosition|USPosition));
1197
1198 if (size.flags & PWinGravity) {
1199 self->gravity = size.win_gravity;
1200
1201 /* if the client has a frame, i.e. has already been mapped and
1202 is changing its gravity */
1203 if (self->frame && self->gravity != oldgravity) {
1204 /* move our idea of the client's position based on its new
1205 gravity */
1206 self->area.x = self->frame->area.x;
1207 self->area.y = self->frame->area.y;
1208 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
1209 }
1210 }
1211
1212 if (size.flags & PAspect) {
1213 if (size.min_aspect.y)
1214 self->min_ratio =
1215 (gfloat) size.min_aspect.x / size.min_aspect.y;
1216 if (size.max_aspect.y)
1217 self->max_ratio =
1218 (gfloat) size.max_aspect.x / size.max_aspect.y;
1219 }
1220
1221 if (size.flags & PMinSize)
1222 SIZE_SET(self->min_size, size.min_width, size.min_height);
1223
1224 if (size.flags & PMaxSize)
1225 SIZE_SET(self->max_size, size.max_width, size.max_height);
1226
1227 if (size.flags & PBaseSize)
1228 SIZE_SET(self->base_size, size.base_width, size.base_height);
1229
1230 if (size.flags & PResizeInc)
1231 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1232 }
1233 }
1234
1235 void client_setup_decor_and_functions(ObClient *self)
1236 {
1237 /* start with everything (cept fullscreen) */
1238 self->decorations =
1239 (OB_FRAME_DECOR_TITLEBAR |
1240 (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) |
1241 OB_FRAME_DECOR_GRIPS |
1242 OB_FRAME_DECOR_BORDER |
1243 OB_FRAME_DECOR_ICON |
1244 OB_FRAME_DECOR_ALLDESKTOPS |
1245 OB_FRAME_DECOR_ICONIFY |
1246 OB_FRAME_DECOR_MAXIMIZE |
1247 OB_FRAME_DECOR_SHADE |
1248 OB_FRAME_DECOR_CLOSE);
1249 self->functions =
1250 (OB_CLIENT_FUNC_RESIZE |
1251 OB_CLIENT_FUNC_MOVE |
1252 OB_CLIENT_FUNC_ICONIFY |
1253 OB_CLIENT_FUNC_MAXIMIZE |
1254 OB_CLIENT_FUNC_SHADE |
1255 OB_CLIENT_FUNC_CLOSE);
1256
1257 if (!(self->min_size.width < self->max_size.width ||
1258 self->min_size.height < self->max_size.height))
1259 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1260
1261 switch (self->type) {
1262 case OB_CLIENT_TYPE_NORMAL:
1263 /* normal windows retain all of the possible decorations and
1264 functionality, and are the only windows that you can fullscreen */
1265 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1266 break;
1267
1268 case OB_CLIENT_TYPE_DIALOG:
1269 case OB_CLIENT_TYPE_UTILITY:
1270 /* these windows cannot be maximized */
1271 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1272 break;
1273
1274 case OB_CLIENT_TYPE_MENU:
1275 case OB_CLIENT_TYPE_TOOLBAR:
1276 /* these windows get less functionality */
1277 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1278 break;
1279
1280 case OB_CLIENT_TYPE_DESKTOP:
1281 case OB_CLIENT_TYPE_DOCK:
1282 case OB_CLIENT_TYPE_SPLASH:
1283 /* none of these windows are manipulated by the window manager */
1284 self->decorations = 0;
1285 self->functions = 0;
1286 break;
1287 }
1288
1289 /* Mwm Hints are applied subtractively to what has already been chosen for
1290 decor and functionality */
1291 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1292 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1293 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1294 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1295 /* if the mwm hints request no handle or title, then all
1296 decorations are disabled */
1297 self->decorations = config_theme_keepborder ? OB_FRAME_DECOR_BORDER : 0;
1298 }
1299 }
1300
1301 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1302 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1303 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1304 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1305 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1306 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1307 /* dont let mwm hints kill any buttons
1308 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1309 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1310 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1311 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1312 */
1313 /* dont let mwm hints kill the close button
1314 if (! (self->mwmhints.functions & MwmFunc_Close))
1315 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1316 }
1317 }
1318
1319 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1320 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1321 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1322 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1323 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1324 self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1325
1326 /* can't maximize without moving/resizing */
1327 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1328 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1329 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1330 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1331 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1332 }
1333
1334 /* kill the handle on fully maxed windows */
1335 if (self->max_vert && self->max_horz)
1336 self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1337
1338 /* finally, the user can have requested no decorations, which overrides
1339 everything (but doesnt give it a border if it doesnt have one) */
1340 if (self->undecorated) {
1341 if (config_theme_keepborder)
1342 self->decorations &= OB_FRAME_DECOR_BORDER;
1343 else
1344 self->decorations = 0;
1345 }
1346
1347 /* if we don't have a titlebar, then we cannot shade! */
1348 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1349 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1350
1351 /* now we need to check against rules for the client's current state */
1352 if (self->fullscreen) {
1353 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1354 OB_CLIENT_FUNC_FULLSCREEN |
1355 OB_CLIENT_FUNC_ICONIFY);
1356 self->decorations = 0;
1357 }
1358
1359 client_change_allowed_actions(self);
1360
1361 if (self->frame) {
1362 /* adjust the client's decorations, etc. */
1363 client_reconfigure(self);
1364 }
1365 }
1366
1367 static void client_change_allowed_actions(ObClient *self)
1368 {
1369 gulong actions[9];
1370 gint num = 0;
1371
1372 /* desktop windows are kept on all desktops */
1373 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1374 actions[num++] = prop_atoms.net_wm_action_change_desktop;
1375
1376 if (self->functions & OB_CLIENT_FUNC_SHADE)
1377 actions[num++] = prop_atoms.net_wm_action_shade;
1378 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1379 actions[num++] = prop_atoms.net_wm_action_close;
1380 if (self->functions & OB_CLIENT_FUNC_MOVE)
1381 actions[num++] = prop_atoms.net_wm_action_move;
1382 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1383 actions[num++] = prop_atoms.net_wm_action_minimize;
1384 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1385 actions[num++] = prop_atoms.net_wm_action_resize;
1386 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1387 actions[num++] = prop_atoms.net_wm_action_fullscreen;
1388 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1389 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1390 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1391 }
1392
1393 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1394
1395 /* make sure the window isn't breaking any rules now */
1396
1397 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1398 if (self->frame) client_shade(self, FALSE);
1399 else self->shaded = FALSE;
1400 }
1401 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1402 if (self->frame) client_iconify(self, FALSE, TRUE);
1403 else self->iconic = FALSE;
1404 }
1405 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1406 if (self->frame) client_fullscreen(self, FALSE, TRUE);
1407 else self->fullscreen = FALSE;
1408 }
1409 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1410 self->max_vert)) {
1411 if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1412 else self->max_vert = self->max_horz = FALSE;
1413 }
1414 }
1415
1416 void client_reconfigure(ObClient *self)
1417 {
1418 /* by making this pass FALSE for user, we avoid the emacs event storm where
1419 every configurenotify causes an update in its normal hints, i think this
1420 is generally what we want anyways... */
1421 client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
1422 self->area.width, self->area.height, FALSE, TRUE);
1423 }
1424
1425 void client_update_wmhints(ObClient *self)
1426 {
1427 XWMHints *hints;
1428 gboolean ur = FALSE;
1429 GSList *it;
1430
1431 /* assume a window takes input if it doesnt specify */
1432 self->can_focus = TRUE;
1433
1434 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1435 if (hints->flags & InputHint)
1436 self->can_focus = hints->input;
1437
1438 /* only do this when first managing the window *AND* when we aren't
1439 starting up! */
1440 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1441 if (hints->flags & StateHint)
1442 self->iconic = hints->initial_state == IconicState;
1443
1444 if (hints->flags & XUrgencyHint)
1445 ur = TRUE;
1446
1447 if (!(hints->flags & WindowGroupHint))
1448 hints->window_group = None;
1449
1450 /* did the group state change? */
1451 if (hints->window_group !=
1452 (self->group ? self->group->leader : None)) {
1453 /* remove from the old group if there was one */
1454 if (self->group != NULL) {
1455 /* remove transients of the group */
1456 for (it = self->group->members; it; it = g_slist_next(it))
1457 self->transients = g_slist_remove(self->transients,
1458 it->data);
1459
1460 /* remove myself from parents in the group */
1461 if (self->transient_for == OB_TRAN_GROUP) {
1462 for (it = self->group->members; it;
1463 it = g_slist_next(it))
1464 {
1465 ObClient *c = it->data;
1466
1467 if (c != self && !c->transient_for)
1468 c->transients = g_slist_remove(c->transients,
1469 self);
1470 }
1471 }
1472
1473 group_remove(self->group, self);
1474 self->group = NULL;
1475 }
1476 if (hints->window_group != None) {
1477 self->group = group_add(hints->window_group, self);
1478
1479 /* i can only have transients from the group if i am not
1480 transient myself */
1481 if (!self->transient_for) {
1482 /* add other transients of the group that are already
1483 set up */
1484 for (it = self->group->members; it;
1485 it = g_slist_next(it))
1486 {
1487 ObClient *c = it->data;
1488 if (c != self && c->transient_for == OB_TRAN_GROUP)
1489 self->transients =
1490 g_slist_append(self->transients, c);
1491 }
1492 }
1493 }
1494
1495 /* because the self->transient flag wont change from this call,
1496 we don't need to update the window's type and such, only its
1497 transient_for, and the transients lists of other windows in
1498 the group may be affected */
1499 client_update_transient_for(self);
1500 }
1501
1502 /* the WM_HINTS can contain an icon */
1503 client_update_icons(self);
1504
1505 XFree(hints);
1506 }
1507
1508 if (ur != self->urgent) {
1509 self->urgent = ur;
1510 /* fire the urgent callback if we're mapped, otherwise, wait until
1511 after we're mapped */
1512 if (self->frame)
1513 client_urgent_notify(self);
1514 }
1515 }
1516
1517 void client_update_title(ObClient *self)
1518 {
1519 GList *it;
1520 guint32 nums;
1521 guint i;
1522 gchar *data = NULL;
1523 gboolean read_title;
1524 gchar *old_title;
1525
1526 old_title = self->title;
1527
1528 /* try netwm */
1529 if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
1530 /* try old x stuff */
1531 if (!PROP_GETS(self->window, wm_name, locale, &data)) {
1532 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1533 if (self->transient) {
1534 data = g_strdup("");
1535 goto no_number;
1536 } else
1537 data = g_strdup("Unnamed Window");
1538 }
1539 }
1540
1541 /* did the title change? then reset the title_count */
1542 if (old_title && 0 != strncmp(old_title, data, strlen(data)))
1543 self->title_count = 1;
1544
1545 /* look for duplicates and append a number */
1546 nums = 0;
1547 for (it = client_list; it; it = g_list_next(it))
1548 if (it->data != self) {
1549 ObClient *c = it->data;
1550 if (0 == strncmp(c->title, data, strlen(data)))
1551 nums |= 1 << c->title_count;
1552 }
1553 /* find first free number */
1554 for (i = 1; i <= 32; ++i)
1555 if (!(nums & (1 << i))) {
1556 if (self->title_count == 1 || i == 1)
1557 self->title_count = i;
1558 break;
1559 }
1560 /* dont display the number for the first window */
1561 if (self->title_count > 1) {
1562 gchar *ndata;
1563 ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
1564 g_free(data);
1565 data = ndata;
1566 }
1567
1568 PROP_SETS(self->window, net_wm_visible_name, data);
1569 no_number:
1570 self->title = data;
1571
1572 if (self->frame)
1573 frame_adjust_title(self->frame);
1574
1575 g_free(old_title);
1576
1577 /* update the icon title */
1578 data = NULL;
1579 g_free(self->icon_title);
1580
1581 read_title = TRUE;
1582 /* try netwm */
1583 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1584 /* try old x stuff */
1585 if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) {
1586 data = g_strdup(self->title);
1587 read_title = FALSE;
1588 }
1589
1590 /* append the title count, dont display the number for the first window */
1591 if (read_title && self->title_count > 1) {
1592 gchar *vdata, *ndata;
1593 ndata = g_strdup_printf(" - [%u]", self->title_count);
1594 vdata = g_strconcat(data, ndata, NULL);
1595 g_free(ndata);
1596 g_free(data);
1597 data = vdata;
1598 }
1599
1600 PROP_SETS(self->window, net_wm_visible_icon_name, data);
1601
1602 self->icon_title = data;
1603 }
1604
1605 void client_update_class(ObClient *self)
1606 {
1607 gchar **data;
1608 gchar *s;
1609
1610 if (self->name) g_free(self->name);
1611 if (self->class) g_free(self->class);
1612 if (self->role) g_free(self->role);
1613
1614 self->name = self->class = self->role = NULL;
1615
1616 if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1617 if (data[0]) {
1618 self->name = g_strdup(data[0]);
1619 if (data[1])
1620 self->class = g_strdup(data[1]);
1621 }
1622 g_strfreev(data);
1623 }
1624
1625 if (PROP_GETS(self->window, wm_window_role, locale, &s))
1626 self->role = s;
1627
1628 if (self->name == NULL) self->name = g_strdup("");
1629 if (self->class == NULL) self->class = g_strdup("");
1630 if (self->role == NULL) self->role = g_strdup("");
1631 }
1632
1633 void client_update_strut(ObClient *self)
1634 {
1635 guint num;
1636 guint32 *data;
1637 gboolean got = FALSE;
1638 StrutPartial strut;
1639
1640 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1641 &data, &num)) {
1642 if (num == 12) {
1643 got = TRUE;
1644 STRUT_PARTIAL_SET(strut,
1645 data[0], data[2], data[1], data[3],
1646 data[4], data[5], data[8], data[9],
1647 data[6], data[7], data[10], data[11]);
1648 }
1649 g_free(data);
1650 }
1651
1652 if (!got &&
1653 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1654 if (num == 4) {
1655 const Rect *a;
1656
1657 got = TRUE;
1658
1659 /* use the screen's width/height */
1660 a = screen_physical_area();
1661
1662 STRUT_PARTIAL_SET(strut,
1663 data[0], data[2], data[1], data[3],
1664 a->y, a->y + a->height - 1,
1665 a->x, a->x + a->width - 1,
1666 a->y, a->y + a->height - 1,
1667 a->x, a->x + a->width - 1);
1668 }
1669 g_free(data);
1670 }
1671
1672 if (!got)
1673 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1674 0, 0, 0, 0, 0, 0, 0, 0);
1675
1676 if (!STRUT_EQUAL(strut, self->strut)) {
1677 self->strut = strut;
1678
1679 /* updating here is pointless while we're being mapped cuz we're not in
1680 the client list yet */
1681 if (self->frame)
1682 screen_update_areas();
1683 }
1684 }
1685
1686 void client_update_icons(ObClient *self)
1687 {
1688 guint num;
1689 guint32 *data;
1690 guint w, h, i, j;
1691
1692 for (i = 0; i < self->nicons; ++i)
1693 g_free(self->icons[i].data);
1694 if (self->nicons > 0)
1695 g_free(self->icons);
1696 self->nicons = 0;
1697
1698 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1699 /* figure out how many valid icons are in here */
1700 i = 0;
1701 while (num - i > 2) {
1702 w = data[i++];
1703 h = data[i++];
1704 i += w * h;
1705 if (i > num || w*h == 0) break;
1706 ++self->nicons;
1707 }
1708
1709 self->icons = g_new(ObClientIcon, self->nicons);
1710
1711 /* store the icons */
1712 i = 0;
1713 for (j = 0; j < self->nicons; ++j) {
1714 guint x, y, t;
1715
1716 w = self->icons[j].width = data[i++];
1717 h = self->icons[j].height = data[i++];
1718
1719 if (w*h == 0) continue;
1720
1721 self->icons[j].data = g_new(RrPixel32, w * h);
1722 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1723 if (x >= w) {
1724 x = 0;
1725 ++y;
1726 }
1727 self->icons[j].data[t] =
1728 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1729 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1730 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1731 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1732 }
1733 g_assert(i <= num);
1734 }
1735
1736 g_free(data);
1737 } else if (PROP_GETA32(self->window, kwm_win_icon,
1738 kwm_win_icon, &data, &num)) {
1739 if (num == 2) {
1740 self->nicons++;
1741 self->icons = g_new(ObClientIcon, self->nicons);
1742 xerror_set_ignore(TRUE);
1743 if (!RrPixmapToRGBA(ob_rr_inst,
1744 data[0], data[1],
1745 &self->icons[self->nicons-1].width,
1746 &self->icons[self->nicons-1].height,
1747 &self->icons[self->nicons-1].data)) {
1748 g_free(&self->icons[self->nicons-1]);
1749 self->nicons--;
1750 }
1751 xerror_set_ignore(FALSE);
1752 }
1753 g_free(data);
1754 } else {
1755 XWMHints *hints;
1756
1757 if ((hints = XGetWMHints(ob_display, self->window))) {
1758 if (hints->flags & IconPixmapHint) {
1759 self->nicons++;
1760 self->icons = g_new(ObClientIcon, self->nicons);
1761 xerror_set_ignore(TRUE);
1762 if (!RrPixmapToRGBA(ob_rr_inst,
1763 hints->icon_pixmap,
1764 (hints->flags & IconMaskHint ?
1765 hints->icon_mask : None),
1766 &self->icons[self->nicons-1].width,
1767 &self->icons[self->nicons-1].height,
1768 &self->icons[self->nicons-1].data)){
1769 g_free(&self->icons[self->nicons-1]);
1770 self->nicons--;
1771 }
1772 xerror_set_ignore(FALSE);
1773 }
1774 XFree(hints);
1775 }
1776 }
1777
1778 if (self->frame)
1779 frame_adjust_icon(self->frame);
1780 }
1781
1782 static void client_change_state(ObClient *self)
1783 {
1784 gulong state[2];
1785 gulong netstate[11];
1786 guint num;
1787
1788 state[0] = self->wmstate;
1789 state[1] = None;
1790 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1791
1792 num = 0;
1793 if (self->modal)
1794 netstate[num++] = prop_atoms.net_wm_state_modal;
1795 if (self->shaded)
1796 netstate[num++] = prop_atoms.net_wm_state_shaded;
1797 if (self->iconic)
1798 netstate[num++] = prop_atoms.net_wm_state_hidden;
1799 if (self->skip_taskbar)
1800 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1801 if (self->skip_pager)
1802 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1803 if (self->fullscreen)
1804 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1805 if (self->max_vert)
1806 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1807 if (self->max_horz)
1808 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1809 if (self->above)
1810 netstate[num++] = prop_atoms.net_wm_state_above;
1811 if (self->below)
1812 netstate[num++] = prop_atoms.net_wm_state_below;
1813 if (self->undecorated)
1814 netstate[num++] = prop_atoms.ob_wm_state_undecorated;
1815 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1816
1817 client_calc_layer(self);
1818
1819 if (self->frame)
1820 frame_adjust_state(self->frame);
1821 }
1822
1823 ObClient *client_search_focus_tree(ObClient *self)
1824 {
1825 GSList *it;
1826 ObClient *ret;
1827
1828 for (it = self->transients; it; it = g_slist_next(it)) {
1829 if (client_focused(it->data)) return it->data;
1830 if ((ret = client_search_focus_tree(it->data))) return ret;
1831 }
1832 return NULL;
1833 }
1834
1835 ObClient *client_search_focus_tree_full(ObClient *self)
1836 {
1837 if (self->transient_for) {
1838 if (self->transient_for != OB_TRAN_GROUP) {
1839 return client_search_focus_tree_full(self->transient_for);
1840 } else {
1841 GSList *it;
1842 gboolean recursed = FALSE;
1843
1844 for (it = self->group->members; it; it = g_slist_next(it))
1845 if (!((ObClient*)it->data)->transient_for) {
1846 ObClient *c;
1847 if ((c = client_search_focus_tree_full(it->data)))
1848 return c;
1849 recursed = TRUE;
1850 }
1851 if (recursed)
1852 return NULL;
1853 }
1854 }
1855
1856 /* this function checks the whole tree, the client_search_focus_tree~
1857 does not, so we need to check this window */
1858 if (client_focused(self))
1859 return self;
1860 return client_search_focus_tree(self);
1861 }
1862
1863 static ObStackingLayer calc_layer(ObClient *self)
1864 {
1865 ObStackingLayer l;
1866
1867 if (self->fullscreen &&
1868 (client_focused(self) || client_search_focus_tree(self)))
1869 l = OB_STACKING_LAYER_FULLSCREEN;
1870 else if (self->type == OB_CLIENT_TYPE_DESKTOP)
1871 l = OB_STACKING_LAYER_DESKTOP;
1872 else if (self->type == OB_CLIENT_TYPE_DOCK) {
1873 if (self->below) l = OB_STACKING_LAYER_NORMAL;
1874 else l = OB_STACKING_LAYER_ABOVE;
1875 }
1876 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1877 else if (self->below) l = OB_STACKING_LAYER_BELOW;
1878 else l = OB_STACKING_LAYER_NORMAL;
1879
1880 return l;
1881 }
1882
1883 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1884 ObStackingLayer l, gboolean raised)
1885 {
1886 ObStackingLayer old, own;
1887 GSList *it;
1888
1889 old = self->layer;
1890 own = calc_layer(self);
1891 self->layer = l > own ? l : own;
1892
1893 for (it = self->transients; it; it = g_slist_next(it))
1894 client_calc_layer_recursive(it->data, orig,
1895 l, raised ? raised : l != old);
1896
1897 if (!raised && l != old)
1898 if (orig->frame) { /* only restack if the original window is managed */
1899 stacking_remove(CLIENT_AS_WINDOW(self));
1900 stacking_add(CLIENT_AS_WINDOW(self));
1901 }
1902 }
1903
1904 void client_calc_layer(ObClient *self)
1905 {
1906 ObStackingLayer l;
1907 ObClient *orig;
1908
1909 orig = self;
1910
1911 /* transients take on the layer of their parents */
1912 self = client_search_top_transient(self);
1913
1914 l = calc_layer(self);
1915
1916 client_calc_layer_recursive(self, orig, l, FALSE);
1917 }
1918
1919 gboolean client_should_show(ObClient *self)
1920 {
1921 if (self->iconic)
1922 return FALSE;
1923 if (client_normal(self) && screen_showing_desktop)
1924 return FALSE;
1925 /*
1926 if (self->transient_for) {
1927 if (self->transient_for != OB_TRAN_GROUP)
1928 return client_should_show(self->transient_for);
1929 else {
1930 GSList *it;
1931
1932 for (it = self->group->members; it; it = g_slist_next(it)) {
1933 ObClient *c = it->data;
1934 if (c != self && !c->transient_for) {
1935 if (client_should_show(c))
1936 return TRUE;
1937 }
1938 }
1939 }
1940 }
1941 */
1942 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
1943 return TRUE;
1944
1945 return FALSE;
1946 }
1947
1948 static void client_showhide(ObClient *self)
1949 {
1950
1951 if (client_should_show(self))
1952 frame_show(self->frame);
1953 else
1954 frame_hide(self->frame);
1955 }
1956
1957 gboolean client_normal(ObClient *self) {
1958 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
1959 self->type == OB_CLIENT_TYPE_DOCK ||
1960 self->type == OB_CLIENT_TYPE_SPLASH);
1961 }
1962
1963 static void client_apply_startup_state(ObClient *self)
1964 {
1965 /* these are in a carefully crafted order.. */
1966
1967 if (self->iconic) {
1968 self->iconic = FALSE;
1969 client_iconify(self, TRUE, FALSE);
1970 }
1971 if (self->fullscreen) {
1972 self->fullscreen = FALSE;
1973 client_fullscreen(self, TRUE, FALSE);
1974 }
1975 if (self->undecorated) {
1976 self->undecorated = FALSE;
1977 client_set_undecorated(self, TRUE);
1978 }
1979 if (self->shaded) {
1980 self->shaded = FALSE;
1981 client_shade(self, TRUE);
1982 }
1983 if (self->urgent)
1984 client_urgent_notify(self);
1985
1986 if (self->max_vert && self->max_horz) {
1987 self->max_vert = self->max_horz = FALSE;
1988 client_maximize(self, TRUE, 0, FALSE);
1989 } else if (self->max_vert) {
1990 self->max_vert = FALSE;
1991 client_maximize(self, TRUE, 2, FALSE);
1992 } else if (self->max_horz) {
1993 self->max_horz = FALSE;
1994 client_maximize(self, TRUE, 1, FALSE);
1995 }
1996
1997 /* nothing to do for the other states:
1998 skip_taskbar
1999 skip_pager
2000 modal
2001 above
2002 below
2003 */
2004 }
2005
2006 void client_configure_full(ObClient *self, ObCorner anchor,
2007 gint x, gint y, gint w, gint h,
2008 gboolean user, gboolean final,
2009 gboolean force_reply)
2010 {
2011 gint oldw, oldh;
2012 gboolean send_resize_client;
2013 gboolean moved = FALSE, resized = FALSE;
2014 guint fdecor = self->frame->decorations;
2015 gboolean fhorz = self->frame->max_horz;
2016
2017 /* make the frame recalculate its dimentions n shit without changing
2018 anything visible for real, this way the constraints below can work with
2019 the updated frame dimensions. */
2020 frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
2021
2022 /* gets the frame's position */
2023 frame_client_gravity(self->frame, &x, &y);
2024
2025 /* these positions are frame positions, not client positions */
2026
2027 /* set the size and position if fullscreen */
2028 if (self->fullscreen) {
2029 #ifdef VIDMODE
2030 gint dot;
2031 XF86VidModeModeLine mode;
2032 #endif
2033 Rect *a;
2034 guint i;
2035
2036 i = client_monitor(self);
2037 a = screen_physical_area_monitor(i);
2038
2039 #ifdef VIDMODE
2040 if (i == 0 && /* primary head */
2041 extensions_vidmode &&
2042 XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
2043 /* get the mode last so the mode.privsize isnt freed incorrectly */
2044 XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
2045 x += a->x;
2046 y += a->y;
2047 w = mode.hdisplay;
2048 h = mode.vdisplay;
2049 if (mode.privsize) XFree(mode.private);
2050 } else
2051 #endif
2052 {
2053 x = a->x;
2054 y = a->y;
2055 w = a->width;
2056 h = a->height;
2057 }
2058
2059 user = FALSE; /* ignore that increment etc shit when in fullscreen */
2060 } else {
2061 Rect *a;
2062
2063 a = screen_area_monitor(self->desktop, client_monitor(self));
2064
2065 /* set the size and position if maximized */
2066 if (self->max_horz) {
2067 x = a->x;
2068 w = a->width - self->frame->size.left - self->frame->size.right;
2069 }
2070 if (self->max_vert) {
2071 y = a->y;
2072 h = a->height - self->frame->size.top - self->frame->size.bottom;
2073 }
2074 }
2075
2076 /* gets the client's position */
2077 frame_frame_gravity(self->frame, &x, &y);
2078
2079 /* these override the above states! if you cant move you can't move! */
2080 if (user) {
2081 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2082 x = self->area.x;
2083 y = self->area.y;
2084 }
2085 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2086 w = self->area.width;
2087 h = self->area.height;
2088 }
2089 }
2090
2091 if (!(w == self->area.width && h == self->area.height)) {
2092 gint basew, baseh, minw, minh;
2093
2094 /* base size is substituted with min size if not specified */
2095 if (self->base_size.width || self->base_size.height) {
2096 basew = self->base_size.width;
2097 baseh = self->base_size.height;
2098 } else {
2099 basew = self->min_size.width;
2100 baseh = self->min_size.height;
2101 }
2102 /* min size is substituted with base size if not specified */
2103 if (self->min_size.width || self->min_size.height) {
2104 minw = self->min_size.width;
2105 minh = self->min_size.height;
2106 } else {
2107 minw = self->base_size.width;
2108 minh = self->base_size.height;
2109 }
2110
2111 /* if this is a user-requested resize, then check against min/max
2112 sizes */
2113
2114 /* smaller than min size or bigger than max size? */
2115 if (w > self->max_size.width) w = self->max_size.width;
2116 if (w < minw) w = minw;
2117 if (h > self->max_size.height) h = self->max_size.height;
2118 if (h < minh) h = minh;
2119
2120 w -= basew;
2121 h -= baseh;
2122
2123 /* keep to the increments */
2124 w /= self->size_inc.width;
2125 h /= self->size_inc.height;
2126
2127 /* you cannot resize to nothing */
2128 if (basew + w < 1) w = 1 - basew;
2129 if (baseh + h < 1) h = 1 - baseh;
2130
2131 /* store the logical size */
2132 SIZE_SET(self->logical_size,
2133 self->size_inc.width > 1 ? w : w + basew,
2134 self->size_inc.height > 1 ? h : h + baseh);
2135
2136 w *= self->size_inc.width;
2137 h *= self->size_inc.height;
2138
2139 w += basew;
2140 h += baseh;
2141
2142 /* adjust the height to match the width for the aspect ratios.
2143 for this, min size is not substituted for base size ever. */
2144 w -= self->base_size.width;
2145 h -= self->base_size.height;
2146
2147 if (!self->fullscreen) {
2148 if (self->min_ratio)
2149 if (h * self->min_ratio > w) {
2150 h = (gint)(w / self->min_ratio);
2151
2152 /* you cannot resize to nothing */
2153 if (h < 1) {
2154 h = 1;
2155 w = (gint)(h * self->min_ratio);
2156 }
2157 }
2158 if (self->max_ratio)
2159 if (h * self->max_ratio < w) {
2160 h = (gint)(w / self->max_ratio);
2161
2162 /* you cannot resize to nothing */
2163 if (h < 1) {
2164 h = 1;
2165 w = (gint)(h * self->min_ratio);
2166 }
2167 }
2168 }
2169
2170 w += self->base_size.width;
2171 h += self->base_size.height;
2172 }
2173
2174 g_assert(w > 0);
2175 g_assert(h > 0);
2176
2177 switch (anchor) {
2178 case OB_CORNER_TOPLEFT:
2179 break;
2180 case OB_CORNER_TOPRIGHT:
2181 x -= w - self->area.width;
2182 break;
2183 case OB_CORNER_BOTTOMLEFT:
2184 y -= h - self->area.height;
2185 break;
2186 case OB_CORNER_BOTTOMRIGHT:
2187 x -= w - self->area.width;
2188 y -= h - self->area.height;
2189 break;
2190 }
2191
2192 moved = x != self->area.x || y != self->area.y;
2193 resized = w != self->area.width || h != self->area.height;
2194
2195 oldw = self->area.width;
2196 oldh = self->area.height;
2197 RECT_SET(self->area, x, y, w, h);
2198
2199 /* for app-requested resizes, always resize if 'resized' is true.
2200 for user-requested ones, only resize if final is true, or when
2201 resizing in redraw mode */
2202 send_resize_client = ((!user && resized) ||
2203 (user && (final ||
2204 (resized && config_resize_redraw))));
2205
2206 /* if the client is enlarging, then resize the client before the frame */
2207 if (send_resize_client && user && (w > oldw || h > oldh))
2208 XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
2209
2210 /* move/resize the frame to match the request */
2211 if (self->frame) {
2212 if (self->decorations != fdecor || self->max_horz != fhorz)
2213 moved = resized = TRUE;
2214
2215 if (moved || resized)
2216 frame_adjust_area(self->frame, moved, resized, FALSE);
2217
2218 if (!resized && (force_reply || ((!user && moved) || (user && final))))
2219 {
2220 XEvent event;
2221 event.type = ConfigureNotify;
2222 event.xconfigure.display = ob_display;
2223 event.xconfigure.event = self->window;
2224 event.xconfigure.window = self->window;
2225
2226 /* root window real coords */
2227 event.xconfigure.x = self->frame->area.x + self->frame->size.left -
2228 self->border_width;
2229 event.xconfigure.y = self->frame->area.y + self->frame->size.top -
2230 self->border_width;
2231 event.xconfigure.width = w;
2232 event.xconfigure.height = h;
2233 event.xconfigure.border_width = 0;
2234 event.xconfigure.above = self->frame->plate;
2235 event.xconfigure.override_redirect = FALSE;
2236 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2237 FALSE, StructureNotifyMask, &event);
2238 }
2239 }
2240
2241 /* if the client is shrinking, then resize the frame before the client */
2242 if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2243 XResizeWindow(ob_display, self->window, w, h);
2244
2245 XFlush(ob_display);
2246 }
2247
2248 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
2249 {
2250 gint x, y, w, h;
2251
2252 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2253 self->fullscreen == fs) return; /* already done */
2254
2255 self->fullscreen = fs;
2256 client_change_state(self); /* change the state hints on the client,
2257 and adjust out layer/stacking */
2258
2259 if (fs) {
2260 if (savearea)
2261 self->pre_fullscreen_area = self->area;
2262
2263 /* these are not actually used cuz client_configure will set them
2264 as appropriate when the window is fullscreened */
2265 x = y = w = h = 0;
2266 } else {
2267 Rect *a;
2268
2269 if (self->pre_fullscreen_area.width > 0 &&
2270 self->pre_fullscreen_area.height > 0)
2271 {
2272 x = self->pre_fullscreen_area.x;
2273 y = self->pre_fullscreen_area.y;
2274 w = self->pre_fullscreen_area.width;
2275 h = self->pre_fullscreen_area.height;
2276 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2277 } else {
2278 /* pick some fallbacks... */
2279 a = screen_area_monitor(self->desktop, 0);
2280 x = a->x + a->width / 4;
2281 y = a->y + a->height / 4;
2282 w = a->width / 2;
2283 h = a->height / 2;
2284 }
2285 }
2286
2287 client_setup_decor_and_functions(self);
2288
2289 client_move_resize(self, x, y, w, h);
2290
2291 /* try focus us when we go into fullscreen mode */
2292 client_focus(self);
2293 }
2294
2295 static void client_iconify_recursive(ObClient *self,
2296 gboolean iconic, gboolean curdesk)
2297 {
2298 GSList *it;
2299 gboolean changed = FALSE;
2300
2301
2302 if (self->iconic != iconic) {
2303 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2304 self->window);
2305
2306 self->iconic = iconic;
2307
2308 if (iconic) {
2309 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2310 glong old;
2311
2312 old = self->wmstate;
2313 self->wmstate = IconicState;
2314 if (old != self->wmstate)
2315 PROP_MSG(self->window, kde_wm_change_state,
2316 self->wmstate, 1, 0, 0);
2317
2318 /* update the focus lists.. iconic windows go to the bottom of
2319 the list, put the new iconic window at the 'top of the
2320 bottom'. */
2321 focus_order_to_top(self);
2322
2323 changed = TRUE;
2324 }
2325 } else {
2326 glong old;
2327
2328 if (curdesk)
2329 client_set_desktop(self, screen_desktop, FALSE);
2330
2331 old = self->wmstate;
2332 self->wmstate = self->shaded ? IconicState : NormalState;
2333 if (old != self->wmstate)
2334 PROP_MSG(self->window, kde_wm_change_state,
2335 self->wmstate, 1, 0, 0);
2336
2337 /* this puts it after the current focused window */
2338 focus_order_remove(self);
2339 focus_order_add_new(self);
2340
2341 /* this is here cuz with the VIDMODE extension, the viewport can
2342 change while a fullscreen window is iconic, and when it
2343 uniconifies, it would be nice if it did so to the new position
2344 of the viewport */
2345 client_reconfigure(self);
2346
2347 changed = TRUE;
2348 }
2349 }
2350
2351 if (changed) {
2352 client_change_state(self);
2353 client_showhide(self);
2354 screen_update_areas();
2355 }
2356
2357 /* iconify all transients */
2358 for (it = self->transients; it; it = g_slist_next(it))
2359 if (it->data != self) client_iconify_recursive(it->data,
2360 iconic, curdesk);
2361 }
2362
2363 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2364 {
2365 /* move up the transient chain as far as possible first */
2366 self = client_search_top_transient(self);
2367
2368 client_iconify_recursive(client_search_top_transient(self),
2369 iconic, curdesk);
2370 }
2371
2372 void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
2373 {
2374 gint x, y, w, h;
2375
2376 g_assert(dir == 0 || dir == 1 || dir == 2);
2377 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2378
2379 /* check if already done */
2380 if (max) {
2381 if (dir == 0 && self->max_horz && self->max_vert) return;
2382 if (dir == 1 && self->max_horz) return;
2383 if (dir == 2 && self->max_vert) return;
2384 } else {
2385 if (dir == 0 && !self->max_horz && !self->max_vert) return;
2386 if (dir == 1 && !self->max_horz) return;
2387 if (dir == 2 && !self->max_vert) return;
2388 }
2389
2390 /* we just tell it to configure in the same place and client_configure
2391 worries about filling the screen with the window */
2392 x = self->area.x;
2393 y = self->area.y;
2394 w = self->area.width;
2395 h = self->area.height;
2396
2397 if (max) {
2398 if (savearea) {
2399 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2400 RECT_SET(self->pre_max_area,
2401 self->area.x, self->pre_max_area.y,
2402 self->area.width, self->pre_max_area.height);
2403 }
2404 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2405 RECT_SET(self->pre_max_area,
2406 self->pre_max_area.x, self->area.y,
2407 self->pre_max_area.width, self->area.height);
2408 }
2409 }
2410 } else {
2411 Rect *a;
2412
2413 a = screen_area_monitor(self->desktop, 0);
2414 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2415 if (self->pre_max_area.width > 0) {
2416 x = self->pre_max_area.x;
2417 w = self->pre_max_area.width;
2418
2419 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2420 0, self->pre_max_area.height);
2421 } else {
2422 /* pick some fallbacks... */
2423 x = a->x + a->width / 4;
2424 w = a->width / 2;
2425 }
2426 }
2427 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2428 if (self->pre_max_area.height > 0) {
2429 y = self->pre_max_area.y;
2430 h = self->pre_max_area.height;
2431
2432 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2433 self->pre_max_area.width, 0);
2434 } else {
2435 /* pick some fallbacks... */
2436 y = a->y + a->height / 4;
2437 h = a->height / 2;
2438 }
2439 }
2440 }
2441
2442 if (dir == 0 || dir == 1) /* horz */
2443 self->max_horz = max;
2444 if (dir == 0 || dir == 2) /* vert */
2445 self->max_vert = max;
2446
2447 client_change_state(self); /* change the state hints on the client */
2448
2449 client_setup_decor_and_functions(self);
2450
2451 client_move_resize(self, x, y, w, h);
2452 }
2453
2454 void client_shade(ObClient *self, gboolean shade)
2455 {
2456 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2457 shade) || /* can't shade */
2458 self->shaded == shade) return; /* already done */
2459
2460 /* when we're iconic, don't change the wmstate */
2461 if (!self->iconic) {
2462 glong old;
2463
2464 old = self->wmstate;
2465 self->wmstate = shade ? IconicState : NormalState;
2466 if (old != self->wmstate)
2467 PROP_MSG(self->window, kde_wm_change_state,
2468 self->wmstate, 1, 0, 0);
2469 }
2470
2471 self->shaded = shade;
2472 client_change_state(self);
2473 /* resize the frame to just the titlebar */
2474 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2475 }
2476
2477 void client_close(ObClient *self)
2478 {
2479 XEvent ce;
2480
2481 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2482
2483 /* in the case that the client provides no means to requesting that it
2484 close, we just kill it */
2485 if (!self->delete_window)
2486 client_kill(self);
2487
2488 /*
2489 XXX: itd be cool to do timeouts and shit here for killing the client's
2490 process off
2491 like... if the window is around after 5 seconds, then the close button
2492 turns a nice red, and if this function is called again, the client is
2493 explicitly killed.
2494 */
2495
2496 ce.xclient.type = ClientMessage;
2497 ce.xclient.message_type = prop_atoms.wm_protocols;
2498 ce.xclient.display = ob_display;
2499 ce.xclient.window = self->window;
2500 ce.xclient.format = 32;
2501 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2502 ce.xclient.data.l[1] = event_lasttime;
2503 ce.xclient.data.l[2] = 0l;
2504 ce.xclient.data.l[3] = 0l;
2505 ce.xclient.data.l[4] = 0l;
2506 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2507 }
2508
2509 void client_kill(ObClient *self)
2510 {
2511 XKillClient(ob_display, self->window);
2512 }
2513
2514 void client_set_desktop_recursive(ObClient *self,
2515 guint target, gboolean donthide)
2516 {
2517 guint old;
2518 GSList *it;
2519
2520 if (target != self->desktop) {
2521
2522 ob_debug("Setting desktop %u\n", target+1);
2523
2524 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2525
2526 /* remove from the old desktop(s) */
2527 focus_order_remove(self);
2528
2529 old = self->desktop;
2530 self->desktop = target;
2531 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2532 /* the frame can display the current desktop state */
2533 frame_adjust_state(self->frame);
2534 /* 'move' the window to the new desktop */
2535 if (!donthide)
2536 client_showhide(self);
2537 /* raise if it was not already on the desktop */
2538 if (old != DESKTOP_ALL)
2539 client_raise(self);
2540 screen_update_areas();
2541
2542 /* add to the new desktop(s) */
2543 if (config_focus_new)
2544 focus_order_to_top(self);
2545 else
2546 focus_order_to_bottom(self);
2547 }
2548
2549 /* move all transients */
2550 for (it = self->transients; it; it = g_slist_next(it))
2551 if (it->data != self) client_set_desktop_recursive(it->data,
2552 target, donthide);
2553 }
2554
2555 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2556 {
2557 client_set_desktop_recursive(client_search_top_transient(self),
2558 target, donthide);
2559 }
2560
2561 ObClient *client_search_modal_child(ObClient *self)
2562 {
2563 GSList *it;
2564 ObClient *ret;
2565
2566 for (it = self->transients; it; it = g_slist_next(it)) {
2567 ObClient *c = it->data;
2568 if ((ret = client_search_modal_child(c))) return ret;
2569 if (c->modal) return c;
2570 }
2571 return NULL;
2572 }
2573
2574 gboolean client_validate(ObClient *self)
2575 {
2576 XEvent e;
2577
2578 XSync(ob_display, FALSE); /* get all events on the server */
2579
2580 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2581 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2582 XPutBackEvent(ob_display, &e);
2583 return FALSE;
2584 }
2585
2586 return TRUE;
2587 }
2588
2589 void client_set_wm_state(ObClient *self, glong state)
2590 {
2591 if (state == self->wmstate) return; /* no change */
2592
2593 switch (state) {
2594 case IconicState:
2595 client_iconify(self, TRUE, TRUE);
2596 break;
2597 case NormalState:
2598 client_iconify(self, FALSE, TRUE);
2599 break;
2600 }
2601 }
2602
2603 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
2604 {
2605 gboolean shaded = self->shaded;
2606 gboolean fullscreen = self->fullscreen;
2607 gboolean undecorated = self->undecorated;
2608 gboolean max_horz = self->max_horz;
2609 gboolean max_vert = self->max_vert;
2610 gboolean modal = self->modal;
2611 gboolean iconic = self->iconic;
2612 gint i;
2613
2614 if (!(action == prop_atoms.net_wm_state_add ||
2615 action == prop_atoms.net_wm_state_remove ||
2616 action == prop_atoms.net_wm_state_toggle))
2617 /* an invalid action was passed to the client message, ignore it */
2618 return;
2619
2620 for (i = 0; i < 2; ++i) {
2621 Atom state = i == 0 ? data1 : data2;
2622
2623 if (!state) continue;
2624
2625 /* if toggling, then pick whether we're adding or removing */
2626 if (action == prop_atoms.net_wm_state_toggle) {
2627 if (state == prop_atoms.net_wm_state_modal)
2628 action = modal ? prop_atoms.net_wm_state_remove :
2629 prop_atoms.net_wm_state_add;
2630 else if (state == prop_atoms.net_wm_state_maximized_vert)
2631 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2632 prop_atoms.net_wm_state_add;
2633 else if (state == prop_atoms.net_wm_state_maximized_horz)
2634 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2635 prop_atoms.net_wm_state_add;
2636 else if (state == prop_atoms.net_wm_state_shaded)
2637 action = shaded ? prop_atoms.net_wm_state_remove :
2638 prop_atoms.net_wm_state_add;
2639 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2640 action = self->skip_taskbar ?
2641 prop_atoms.net_wm_state_remove :
2642 prop_atoms.net_wm_state_add;
2643 else if (state == prop_atoms.net_wm_state_skip_pager)
2644 action = self->skip_pager ?
2645 prop_atoms.net_wm_state_remove :
2646 prop_atoms.net_wm_state_add;
2647 else if (state == prop_atoms.net_wm_state_hidden)
2648 action = self->iconic ?
2649 prop_atoms.net_wm_state_remove :
2650 prop_atoms.net_wm_state_add;
2651 else if (state == prop_atoms.net_wm_state_fullscreen)
2652 action = fullscreen ?
2653 prop_atoms.net_wm_state_remove :
2654 prop_atoms.net_wm_state_add;
2655 else if (state == prop_atoms.net_wm_state_above)
2656 action = self->above ? prop_atoms.net_wm_state_remove :
2657 prop_atoms.net_wm_state_add;
2658 else if (state == prop_atoms.net_wm_state_below)
2659 action = self->below ? prop_atoms.net_wm_state_remove :
2660 prop_atoms.net_wm_state_add;
2661 else if (state == prop_atoms.ob_wm_state_undecorated)
2662 action = undecorated ? prop_atoms.net_wm_state_remove :
2663 prop_atoms.net_wm_state_add;
2664 }
2665
2666 if (action == prop_atoms.net_wm_state_add) {
2667 if (state == prop_atoms.net_wm_state_modal) {
2668 modal = TRUE;
2669 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2670 max_vert = TRUE;
2671 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2672 max_horz = TRUE;
2673 } else if (state == prop_atoms.net_wm_state_shaded) {
2674 shaded = TRUE;
2675 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2676 self->skip_taskbar = TRUE;
2677 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2678 self->skip_pager = TRUE;
2679 } else if (state == prop_atoms.net_wm_state_hidden) {
2680 iconic = TRUE;
2681 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2682 fullscreen = TRUE;
2683 } else if (state == prop_atoms.net_wm_state_above) {
2684 self->above = TRUE;
2685 self->below = FALSE;
2686 } else if (state == prop_atoms.net_wm_state_below) {
2687 self->above = FALSE;
2688 self->below = TRUE;
2689 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2690 undecorated = TRUE;
2691 }
2692
2693 } else { /* action == prop_atoms.net_wm_state_remove */
2694 if (state == prop_atoms.net_wm_state_modal) {
2695 modal = FALSE;
2696 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2697 max_vert = FALSE;
2698 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2699 max_horz = FALSE;
2700 } else if (state == prop_atoms.net_wm_state_shaded) {
2701 shaded = FALSE;
2702 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2703 self->skip_taskbar = FALSE;
2704 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2705 self->skip_pager = FALSE;
2706 } else if (state == prop_atoms.net_wm_state_hidden) {
2707 iconic = FALSE;
2708 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2709 fullscreen = FALSE;
2710 } else if (state == prop_atoms.net_wm_state_above) {
2711 self->above = FALSE;
2712 } else if (state == prop_atoms.net_wm_state_below) {
2713 self->below = FALSE;
2714 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2715 undecorated = FALSE;
2716 }
2717 }
2718 }
2719 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2720 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2721 /* toggling both */
2722 if (max_horz == max_vert) { /* both going the same way */
2723 client_maximize(self, max_horz, 0, TRUE);
2724 } else {
2725 client_maximize(self, max_horz, 1, TRUE);
2726 client_maximize(self, max_vert, 2, TRUE);
2727 }
2728 } else {
2729 /* toggling one */
2730 if (max_horz != self->max_horz)
2731 client_maximize(self, max_horz, 1, TRUE);
2732 else
2733 client_maximize(self, max_vert, 2, TRUE);
2734 }
2735 }
2736 /* change fullscreen state before shading, as it will affect if the window
2737 can shade or not */
2738 if (fullscreen != self->fullscreen)
2739 client_fullscreen(self, fullscreen, TRUE);
2740 if (shaded != self->shaded)
2741 client_shade(self, shaded);
2742 if (undecorated != self->undecorated)
2743 client_set_undecorated(self, undecorated);
2744 if (modal != self->modal) {
2745 self->modal = modal;
2746 /* when a window changes modality, then its stacking order with its
2747 transients needs to change */
2748 client_raise(self);
2749 }
2750 if (iconic != self->iconic)
2751 client_iconify(self, iconic, FALSE);
2752
2753 client_calc_layer(self);
2754 client_change_state(self); /* change the hint to reflect these changes */
2755 }
2756
2757 ObClient *client_focus_target(ObClient *self)
2758 {
2759 ObClient *child;
2760
2761 /* if we have a modal child, then focus it, not us */
2762 child = client_search_modal_child(client_search_top_transient(self));
2763 if (child) return child;
2764 return self;
2765 }
2766
2767 gboolean client_can_focus(ObClient *self)
2768 {
2769 XEvent ev;
2770
2771 /* choose the correct target */
2772 self = client_focus_target(self);
2773
2774 if (!self->frame->visible)
2775 return FALSE;
2776
2777 if (!(self->can_focus || self->focus_notify))
2778 return FALSE;
2779
2780 /* do a check to see if the window has already been unmapped or destroyed
2781 do this intelligently while watching out for unmaps we've generated
2782 (ignore_unmaps > 0) */
2783 if (XCheckTypedWindowEvent(ob_display, self->window,
2784 DestroyNotify, &ev)) {
2785 XPutBackEvent(ob_display, &ev);
2786 return FALSE;
2787 }
2788 while (XCheckTypedWindowEvent(ob_display, self->window,
2789 UnmapNotify, &ev)) {
2790 if (self->ignore_unmaps) {
2791 self->ignore_unmaps--;
2792 } else {
2793 XPutBackEvent(ob_display, &ev);
2794 return FALSE;
2795 }
2796 }
2797
2798 return TRUE;
2799 }
2800
2801 gboolean client_focus(ObClient *self)
2802 {
2803 /* choose the correct target */
2804 self = client_focus_target(self);
2805
2806 if (!client_can_focus(self)) {
2807 if (!self->frame->visible) {
2808 /* update the focus lists */
2809 focus_order_to_top(self);
2810 }
2811 return FALSE;
2812 }
2813
2814 if (self->can_focus) {
2815 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2816 I choose to use it always, hopefully to find errors quicker, if any
2817 are left. (I hate X. I hate focus events.)
2818
2819 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2820 #799. So now it is RevertToNone again.
2821 */
2822 XSetInputFocus(ob_display, self->window, RevertToNone,
2823 event_lasttime);
2824 }
2825
2826 if (self->focus_notify) {
2827 XEvent ce;
2828 ce.xclient.type = ClientMessage;
2829 ce.xclient.message_type = prop_atoms.wm_protocols;
2830 ce.xclient.display = ob_display;
2831 ce.xclient.window = self->window;
2832 ce.xclient.format = 32;
2833 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2834 ce.xclient.data.l[1] = event_lasttime;
2835 ce.xclient.data.l[2] = 0l;
2836 ce.xclient.data.l[3] = 0l;
2837 ce.xclient.data.l[4] = 0l;
2838 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2839 }
2840
2841 #ifdef DEBUG_FOCUS
2842 ob_debug("%sively focusing %lx at %d\n",
2843 (self->can_focus ? "act" : "pass"),
2844 self->window, (gint) event_lasttime);
2845 #endif
2846
2847 /* Cause the FocusIn to come back to us. Important for desktop switches,
2848 since otherwise we'll have no FocusIn on the queue and send it off to
2849 the focus_backup. */
2850 XSync(ob_display, FALSE);
2851 return TRUE;
2852 }
2853
2854 void client_unfocus(ObClient *self)
2855 {
2856 if (focus_client == self) {
2857 #ifdef DEBUG_FOCUS
2858 ob_debug("client_unfocus for %lx\n", self->window);
2859 #endif
2860 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2861 }
2862 }
2863
2864 void client_activate(ObClient *self, gboolean here)
2865 {
2866 /* This check is for the client_list_menu trying to activate
2867 * a closed client. */
2868 if (!g_list_find(client_list, self)) return;
2869 if (client_normal(self) && screen_showing_desktop)
2870 screen_show_desktop(FALSE);
2871 if (self->iconic)
2872 client_iconify(self, FALSE, here);
2873 if (self->desktop != DESKTOP_ALL &&
2874 self->desktop != screen_desktop) {
2875 if (here)
2876 client_set_desktop(self, screen_desktop, FALSE);
2877 else
2878 screen_set_desktop(self->desktop);
2879 } else if (!self->frame->visible)
2880 /* if its not visible for other reasons, then don't mess
2881 with it */
2882 return;
2883 if (self->shaded)
2884 client_shade(self, FALSE);
2885
2886 client_focus(self);
2887
2888 /* we do this an action here. this is rather important. this is because
2889 we want the results from the focus change to take place BEFORE we go
2890 about raising the window. when a fullscreen window loses focus, we need
2891 this or else the raise wont be able to raise above the to-lose-focus
2892 fullscreen window. */
2893 client_raise(self);
2894 }
2895
2896 void client_raise(ObClient *self)
2897 {
2898 action_run_string("Raise", self);
2899 }
2900
2901 void client_lower(ObClient *self)
2902 {
2903 action_run_string("Lower", self);
2904 }
2905
2906 gboolean client_focused(ObClient *self)
2907 {
2908 return self == focus_client;
2909 }
2910
2911 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
2912 {
2913 guint i;
2914 /* si is the smallest image >= req */
2915 /* li is the largest image < req */
2916 gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2917
2918 if (!self->nicons) {
2919 ObClientIcon *parent = NULL;
2920
2921 if (self->transient_for) {
2922 if (self->transient_for != OB_TRAN_GROUP)
2923 parent = client_icon_recursive(self->transient_for, w, h);
2924 else {
2925 GSList *it;
2926 for (it = self->group->members; it; it = g_slist_next(it)) {
2927 ObClient *c = it->data;
2928 if (c != self && !c->transient_for) {
2929 if ((parent = client_icon_recursive(c, w, h)))
2930 break;
2931 }
2932 }
2933 }
2934 }
2935
2936 return parent;
2937 }
2938
2939 for (i = 0; i < self->nicons; ++i) {
2940 size = self->icons[i].width * self->icons[i].height;
2941 if (size < smallest && size >= (unsigned)(w * h)) {
2942 smallest = size;
2943 si = i;
2944 }
2945 if (size > largest && size <= (unsigned)(w * h)) {
2946 largest = size;
2947 li = i;
2948 }
2949 }
2950 if (largest == 0) /* didnt find one smaller than the requested size */
2951 return &self->icons[si];
2952 return &self->icons[li];
2953 }
2954
2955 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
2956 {
2957 ObClientIcon *ret;
2958 static ObClientIcon deficon;
2959
2960 if (!(ret = client_icon_recursive(self, w, h))) {
2961 deficon.width = deficon.height = 48;
2962 deficon.data = ob_rr_theme->def_win_icon;
2963 ret = &deficon;
2964 }
2965 return ret;
2966 }
2967
2968 /* this be mostly ripped from fvwm */
2969 ObClient *client_find_directional(ObClient *c, ObDirection dir)
2970 {
2971 gint my_cx, my_cy, his_cx, his_cy;
2972 gint offset = 0;
2973 gint distance = 0;
2974 gint score, best_score;
2975 ObClient *best_client, *cur;
2976 GList *it;
2977
2978 if(!client_list)
2979 return NULL;
2980
2981 /* first, find the centre coords of the currently focused window */
2982 my_cx = c->frame->area.x + c->frame->area.width / 2;
2983 my_cy = c->frame->area.y + c->frame->area.height / 2;
2984
2985 best_score = -1;
2986 best_client = NULL;
2987
2988 for(it = g_list_first(client_list); it; it = g_list_next(it)) {
2989 cur = it->data;
2990
2991 /* the currently selected window isn't interesting */
2992 if(cur == c)
2993 continue;
2994 if (!client_normal(cur))
2995 continue;
2996 /* using c->desktop instead of screen_desktop doesn't work if the
2997 * current window was omnipresent, hope this doesn't have any other
2998 * side effects */
2999 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3000 continue;
3001 if(cur->iconic)
3002 continue;
3003 if(client_focus_target(cur) == cur &&
3004 !(cur->can_focus || cur->focus_notify))
3005 continue;
3006
3007 /* find the centre coords of this window, from the
3008 * currently focused window's point of view */
3009 his_cx = (cur->frame->area.x - my_cx)
3010 + cur->frame->area.width / 2;
3011 his_cy = (cur->frame->area.y - my_cy)
3012 + cur->frame->area.height / 2;
3013
3014 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
3015 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
3016 gint tx;
3017 /* Rotate the diagonals 45 degrees counterclockwise.
3018 * To do this, multiply the matrix /+h +h\ with the
3019 * vector (x y). \-h +h/
3020 * h = sqrt(0.5). We can set h := 1 since absolute
3021 * distance doesn't matter here. */
3022 tx = his_cx + his_cy;
3023 his_cy = -his_cx + his_cy;
3024 his_cx = tx;
3025 }
3026
3027 switch(dir) {
3028 case OB_DIRECTION_NORTH:
3029 case OB_DIRECTION_SOUTH:
3030 case OB_DIRECTION_NORTHEAST:
3031 case OB_DIRECTION_SOUTHWEST:
3032 offset = (his_cx < 0) ? -his_cx : his_cx;
3033 distance = ((dir == OB_DIRECTION_NORTH ||
3034 dir == OB_DIRECTION_NORTHEAST) ?
3035 -his_cy : his_cy);
3036 break;
3037 case OB_DIRECTION_EAST:
3038 case OB_DIRECTION_WEST:
3039 case OB_DIRECTION_SOUTHEAST:
3040 case OB_DIRECTION_NORTHWEST:
3041 offset = (his_cy < 0) ? -his_cy : his_cy;
3042 distance = ((dir == OB_DIRECTION_WEST ||
3043 dir == OB_DIRECTION_NORTHWEST) ?
3044 -his_cx : his_cx);
3045 break;
3046 }
3047
3048 /* the target must be in the requested direction */
3049 if(distance <= 0)
3050 continue;
3051
3052 /* Calculate score for this window. The smaller the better. */
3053 score = distance + offset;
3054
3055 /* windows more than 45 degrees off the direction are
3056 * heavily penalized and will only be chosen if nothing
3057 * else within a million pixels */
3058 if(offset > distance)
3059 score += 1000000;
3060
3061 if(best_score == -1 || score < best_score)
3062 best_client = cur,
3063 best_score = score;
3064 }
3065
3066 return best_client;
3067 }
3068
3069 void client_set_layer(ObClient *self, gint layer)
3070 {
3071 if (layer < 0) {
3072 self->below = TRUE;
3073 self->above = FALSE;
3074 } else if (layer == 0) {
3075 self->below = self->above = FALSE;
3076 } else {
3077 self->below = FALSE;
3078 self->above = TRUE;
3079 }
3080 client_calc_layer(self);
3081 client_change_state(self); /* reflect this in the state hints */
3082 }
3083
3084 void client_set_undecorated(ObClient *self, gboolean undecorated)
3085 {
3086 if (self->undecorated != undecorated) {
3087 self->undecorated = undecorated;
3088 client_setup_decor_and_functions(self);
3089 /* Make sure the client knows it might have moved. Maybe there is a
3090 * better way of doing this so only one client_configure is sent, but
3091 * since 125 of these are sent per second when moving the window (with
3092 * user = FALSE) i doubt it matters much.
3093 */
3094 client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
3095 self->area.width, self->area.height, TRUE, TRUE);
3096 client_change_state(self); /* reflect this in the state hints */
3097 }
3098 }
3099
3100 /* Determines which physical monitor a client is on by calculating the
3101 area of the part of the client on each monitor. The number of the
3102 monitor containing the greatest area of the client is returned.*/
3103 guint client_monitor(ObClient *self)
3104 {
3105 guint i;
3106 guint most = 0;
3107 guint mostv = 0;
3108
3109 for (i = 0; i < screen_num_monitors; ++i) {
3110 Rect *area = screen_physical_area_monitor(i);
3111 if (RECT_INTERSECTS_RECT(*area, self->frame->area)) {
3112 Rect r;
3113 guint v;
3114
3115 RECT_SET_INTERSECTION(r, *area, self->frame->area);
3116 v = r.width * r.height;
3117
3118 if (v > mostv) {
3119 mostv = v;
3120 most = i;
3121 }
3122 }
3123 }
3124 return most;
3125 }
3126
3127 ObClient *client_search_top_transient(ObClient *self)
3128 {
3129 /* move up the transient chain as far as possible */
3130 if (self->transient_for) {
3131 if (self->transient_for != OB_TRAN_GROUP) {
3132 return client_search_top_transient(self->transient_for);
3133 } else {
3134 GSList *it;
3135
3136 g_assert(self->group);
3137
3138 for (it = self->group->members; it; it = g_slist_next(it)) {
3139 ObClient *c = it->data;
3140
3141 /* checking transient_for prevents infinate loops! */
3142 if (c != self && !c->transient_for)
3143 break;
3144 }
3145 if (it)
3146 return it->data;
3147 }
3148 }
3149
3150 return self;
3151 }
3152
3153 ObClient *client_search_focus_parent(ObClient *self)
3154 {
3155 if (self->transient_for) {
3156 if (self->transient_for != OB_TRAN_GROUP) {
3157 if (client_focused(self->transient_for))
3158 return self->transient_for;
3159 } else {
3160 GSList *it;
3161
3162 for (it = self->group->members; it; it = g_slist_next(it)) {
3163 ObClient *c = it->data;
3164
3165 /* checking transient_for prevents infinate loops! */
3166 if (c != self && !c->transient_for)
3167 if (client_focused(c))
3168 return c;
3169 }
3170 }
3171 }
3172
3173 return NULL;
3174 }
3175
3176 ObClient *client_search_parent(ObClient *self, ObClient *search)
3177 {
3178 if (self->transient_for) {
3179 if (self->transient_for != OB_TRAN_GROUP) {
3180 if (self->transient_for == search)
3181 return search;
3182 } else {
3183 GSList *it;
3184
3185 for (it = self->group->members; it; it = g_slist_next(it)) {
3186 ObClient *c = it->data;
3187
3188 /* checking transient_for prevents infinate loops! */
3189 if (c != self && !c->transient_for)
3190 if (c == search)
3191 return search;
3192 }
3193 }
3194 }
3195
3196 return NULL;
3197 }
3198
3199 ObClient *client_search_transient(ObClient *self, ObClient *search)
3200 {
3201 GSList *sit;
3202
3203 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3204 if (sit->data == search)
3205 return search;
3206 if (client_search_transient(sit->data, search))
3207 return search;
3208 }
3209 return NULL;
3210 }
3211
3212 void client_update_sm_client_id(ObClient *self)
3213 {
3214 g_free(self->sm_client_id);
3215 self->sm_client_id = NULL;
3216
3217 if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
3218 self->group)
3219 PROP_GETS(self->group->leader, sm_client_id, locale,
3220 &self->sm_client_id);
3221 }
3222
3223 /* finds the nearest edge in the given direction from the current client
3224 * note to self: the edge is the -frame- edge (the actual one), not the
3225 * client edge.
3226 */
3227 gint client_directional_edge_search(ObClient *c, ObDirection dir)
3228 {
3229 gint dest, monitor_dest;
3230 gint my_edge_start, my_edge_end, my_offset;
3231 GList *it;
3232 Rect *a, *monitor;
3233
3234 if(!client_list)
3235 return -1;
3236
3237 a = screen_area(c->desktop);
3238 monitor = screen_area_monitor(c->desktop, client_monitor(c));
3239
3240 switch(dir) {
3241 case OB_DIRECTION_NORTH:
3242 my_edge_start = c->frame->area.x;
3243 my_edge_end = c->frame->area.x + c->frame->area.width;
3244 my_offset = c->frame->area.y;
3245
3246 /* default: top of screen */
3247 dest = a->y;
3248 monitor_dest = monitor->y;
3249 /* if the monitor edge comes before the screen edge, */
3250 /* use that as the destination instead. (For xinerama) */
3251 if (monitor_dest != dest && my_offset > monitor_dest)
3252 dest = monitor_dest;
3253
3254 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3255 gint his_edge_start, his_edge_end, his_offset;
3256 ObClient *cur = it->data;
3257
3258 if(cur == c)
3259 continue;
3260 if(!client_normal(cur))
3261 continue;
3262 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3263 continue;
3264 if(cur->iconic)
3265 continue;
3266 if(cur->layer < c->layer && !config_resist_layers_below)
3267 continue;
3268
3269 his_edge_start = cur->frame->area.x;
3270 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3271 his_offset = cur->frame->area.y + cur->frame->area.height;
3272
3273 if(his_offset + 1 > my_offset)
3274 continue;
3275
3276 if(his_offset < dest)
3277 continue;
3278
3279 if(his_edge_start >= my_edge_start &&
3280 his_edge_start <= my_edge_end)
3281 dest = his_offset;
3282
3283 if(my_edge_start >= his_edge_start &&
3284 my_edge_start <= his_edge_end)
3285 dest = his_offset;
3286
3287 }
3288 break;
3289 case OB_DIRECTION_SOUTH:
3290 my_edge_start = c->frame->area.x;
3291 my_edge_end = c->frame->area.x + c->frame->area.width;
3292 my_offset = c->frame->area.y + c->frame->area.height;
3293
3294 /* default: bottom of screen */
3295 dest = a->y + a->height;
3296 monitor_dest = monitor->y + monitor->height;
3297 /* if the monitor edge comes before the screen edge, */
3298 /* use that as the destination instead. (For xinerama) */
3299 if (monitor_dest != dest && my_offset < monitor_dest)
3300 dest = monitor_dest;
3301
3302 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3303 gint his_edge_start, his_edge_end, his_offset;
3304 ObClient *cur = it->data;
3305
3306 if(cur == c)
3307 continue;
3308 if(!client_normal(cur))
3309 continue;
3310 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3311 continue;
3312 if(cur->iconic)
3313 continue;
3314 if(cur->layer < c->layer && !config_resist_layers_below)
3315 continue;
3316
3317 his_edge_start = cur->frame->area.x;
3318 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3319 his_offset = cur->frame->area.y;
3320
3321
3322 if(his_offset - 1 < my_offset)
3323 continue;
3324
3325 if(his_offset > dest)
3326 continue;
3327
3328 if(his_edge_start >= my_edge_start &&
3329 his_edge_start <= my_edge_end)
3330 dest = his_offset;
3331
3332 if(my_edge_start >= his_edge_start &&
3333 my_edge_start <= his_edge_end)
3334 dest = his_offset;
3335
3336 }
3337 break;
3338 case OB_DIRECTION_WEST:
3339 my_edge_start = c->frame->area.y;
3340 my_edge_end = c->frame->area.y + c->frame->area.height;
3341 my_offset = c->frame->area.x;
3342
3343 /* default: leftmost egde of screen */
3344 dest = a->x;
3345 monitor_dest = monitor->x;
3346 /* if the monitor edge comes before the screen edge, */
3347 /* use that as the destination instead. (For xinerama) */
3348 if (monitor_dest != dest && my_offset > monitor_dest)
3349 dest = monitor_dest;
3350
3351 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3352 gint his_edge_start, his_edge_end, his_offset;
3353 ObClient *cur = it->data;
3354
3355 if(cur == c)
3356 continue;
3357 if(!client_normal(cur))
3358 continue;
3359 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3360 continue;
3361 if(cur->iconic)
3362 continue;
3363 if(cur->layer < c->layer && !config_resist_layers_below)
3364 continue;
3365
3366 his_edge_start = cur->frame->area.y;
3367 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3368 his_offset = cur->frame->area.x + cur->frame->area.width;
3369
3370 if(his_offset + 1 > my_offset)
3371 continue;
3372
3373 if(his_offset < dest)
3374 continue;
3375
3376 if(his_edge_start >= my_edge_start &&
3377 his_edge_start <= my_edge_end)
3378 dest = his_offset;
3379
3380 if(my_edge_start >= his_edge_start &&
3381 my_edge_start <= his_edge_end)
3382 dest = his_offset;
3383
3384
3385 }
3386 break;
3387 case OB_DIRECTION_EAST:
3388 my_edge_start = c->frame->area.y;
3389 my_edge_end = c->frame->area.y + c->frame->area.height;
3390 my_offset = c->frame->area.x + c->frame->area.width;
3391
3392 /* default: rightmost edge of screen */
3393 dest = a->x + a->width;
3394 monitor_dest = monitor->x + monitor->width;
3395 /* if the monitor edge comes before the screen edge, */
3396 /* use that as the destination instead. (For xinerama) */
3397 if (monitor_dest != dest && my_offset < monitor_dest)
3398 dest = monitor_dest;
3399
3400 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3401 gint his_edge_start, his_edge_end, his_offset;
3402 ObClient *cur = it->data;
3403
3404 if(cur == c)
3405 continue;
3406 if(!client_normal(cur))
3407 continue;
3408 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3409 continue;
3410 if(cur->iconic)
3411 continue;
3412 if(cur->layer < c->layer && !config_resist_layers_below)
3413 continue;
3414
3415 his_edge_start = cur->frame->area.y;
3416 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3417 his_offset = cur->frame->area.x;
3418
3419 if(his_offset - 1 < my_offset)
3420 continue;
3421
3422 if(his_offset > dest)
3423 continue;
3424
3425 if(his_edge_start >= my_edge_start &&
3426 his_edge_start <= my_edge_end)
3427 dest = his_offset;
3428
3429 if(my_edge_start >= his_edge_start &&
3430 my_edge_start <= his_edge_end)
3431 dest = his_offset;
3432
3433 }
3434 break;
3435 case OB_DIRECTION_NORTHEAST:
3436 case OB_DIRECTION_SOUTHEAST:
3437 case OB_DIRECTION_NORTHWEST:
3438 case OB_DIRECTION_SOUTHWEST:
3439 /* not implemented */
3440 default:
3441 g_assert_not_reached();
3442 dest = 0; /* suppress warning */
3443 }
3444 return dest;
3445 }
3446
3447 ObClient* client_under_pointer()
3448 {
3449 gint x, y;
3450 GList *it;
3451 ObClient *ret = NULL;
3452
3453 if (screen_pointer_pos(&x, &y)) {
3454 for (it = stacking_list; it; it = g_list_next(it)) {
3455 if (WINDOW_IS_CLIENT(it->data)) {
3456 ObClient *c = WINDOW_AS_CLIENT(it->data);
3457 if (c->frame->visible &&
3458 RECT_CONTAINS(c->frame->area, x, y)) {
3459 ret = c;
3460 break;
3461 }
3462 }
3463 }
3464 }
3465 return ret;
3466 }
3467
3468 gboolean client_has_group_siblings(ObClient *self)
3469 {
3470 return self->group && self->group->members->next;
3471 }
This page took 0.191671 seconds and 5 git commands to generate.