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