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