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