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