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