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