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