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