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