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