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