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