]> Dogcows Code - chaz/openbox/blob - openbox/client.c
focus_client was becoming invalid if the focused client was unmanaged.
[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
2404 /* find the frame's dimensions and move/resize it */
2405 if (self->decorations != fdecor || self->max_horz != fhorz)
2406 moved = resized = TRUE;
2407 if (moved || resized)
2408 frame_adjust_area(self->frame, moved, resized, FALSE);
2409
2410 /* find the client's position relative to the root window */
2411 oldrx = self->root_pos.x;
2412 oldry = self->root_pos.y;
2413 rootmoved = (oldrx != (signed)(self->frame->area.x +
2414 self->frame->size.left -
2415 self->border_width) ||
2416 oldry != (signed)(self->frame->area.y +
2417 self->frame->size.top -
2418 self->border_width));
2419
2420 if (force_reply || ((!user || (user && final)) && rootmoved))
2421 {
2422 XEvent event;
2423
2424 POINT_SET(self->root_pos,
2425 self->frame->area.x + self->frame->size.left -
2426 self->border_width,
2427 self->frame->area.y + self->frame->size.top -
2428 self->border_width);
2429
2430 event.type = ConfigureNotify;
2431 event.xconfigure.display = ob_display;
2432 event.xconfigure.event = self->window;
2433 event.xconfigure.window = self->window;
2434
2435 /* root window real coords */
2436 event.xconfigure.x = self->root_pos.x;
2437 event.xconfigure.y = self->root_pos.y;
2438 event.xconfigure.width = w;
2439 event.xconfigure.height = h;
2440 event.xconfigure.border_width = 0;
2441 event.xconfigure.above = self->frame->plate;
2442 event.xconfigure.override_redirect = FALSE;
2443 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2444 FALSE, StructureNotifyMask, &event);
2445 }
2446
2447 /* if the client is shrinking, then resize the frame before the client */
2448 if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2449 XResizeWindow(ob_display, self->window, w, h);
2450
2451 XFlush(ob_display);
2452 }
2453
2454 void client_fullscreen(ObClient *self, gboolean fs)
2455 {
2456 gint x, y, w, h;
2457
2458 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2459 self->fullscreen == fs) return; /* already done */
2460
2461 self->fullscreen = fs;
2462 client_change_state(self); /* change the state hints on the client */
2463 client_calc_layer(self); /* and adjust out layer/stacking */
2464
2465 if (fs) {
2466 self->pre_fullscreen_area = self->area;
2467 /* if the window is maximized, its area isn't all that meaningful.
2468 save it's premax area instead. */
2469 if (self->max_horz) {
2470 self->pre_fullscreen_area.x = self->pre_max_area.x;
2471 self->pre_fullscreen_area.width = self->pre_max_area.width;
2472 }
2473 if (self->max_vert) {
2474 self->pre_fullscreen_area.y = self->pre_max_area.y;
2475 self->pre_fullscreen_area.height = self->pre_max_area.height;
2476 }
2477
2478 /* these are not actually used cuz client_configure will set them
2479 as appropriate when the window is fullscreened */
2480 x = y = w = h = 0;
2481 } else {
2482 Rect *a;
2483
2484 if (self->pre_fullscreen_area.width > 0 &&
2485 self->pre_fullscreen_area.height > 0)
2486 {
2487 x = self->pre_fullscreen_area.x;
2488 y = self->pre_fullscreen_area.y;
2489 w = self->pre_fullscreen_area.width;
2490 h = self->pre_fullscreen_area.height;
2491 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2492 } else {
2493 /* pick some fallbacks... */
2494 a = screen_area_monitor(self->desktop, 0);
2495 x = a->x + a->width / 4;
2496 y = a->y + a->height / 4;
2497 w = a->width / 2;
2498 h = a->height / 2;
2499 }
2500 }
2501
2502 client_setup_decor_and_functions(self);
2503
2504 client_move_resize(self, x, y, w, h);
2505
2506 /* try focus us when we go into fullscreen mode */
2507 client_focus(self);
2508 }
2509
2510 static void client_iconify_recursive(ObClient *self,
2511 gboolean iconic, gboolean curdesk)
2512 {
2513 GSList *it;
2514 gboolean changed = FALSE;
2515
2516
2517 if (self->iconic != iconic) {
2518 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2519 self->window);
2520
2521 if (iconic) {
2522 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2523 self->iconic = iconic;
2524
2525 /* update the focus lists.. iconic windows go to the bottom of
2526 the list, put the new iconic window at the 'top of the
2527 bottom'. */
2528 focus_order_to_top(self);
2529
2530 changed = TRUE;
2531 }
2532 } else {
2533 self->iconic = iconic;
2534
2535 if (curdesk)
2536 client_set_desktop(self, screen_desktop, FALSE);
2537
2538 /* this puts it after the current focused window */
2539 focus_order_remove(self);
2540 focus_order_add_new(self);
2541
2542 changed = TRUE;
2543 }
2544 }
2545
2546 if (changed) {
2547 client_change_state(self);
2548 client_showhide(self);
2549 if (STRUT_EXISTS(self->strut))
2550 screen_update_areas();
2551 }
2552
2553 /* iconify all direct transients */
2554 for (it = self->transients; it; it = g_slist_next(it))
2555 if (it->data != self)
2556 if (client_is_direct_child(self, it->data))
2557 client_iconify_recursive(it->data, iconic, curdesk);
2558 }
2559
2560 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2561 {
2562 /* move up the transient chain as far as possible first */
2563 self = client_search_top_parent(self);
2564 client_iconify_recursive(self, iconic, curdesk);
2565 }
2566
2567 void client_maximize(ObClient *self, gboolean max, gint dir)
2568 {
2569 gint x, y, w, h;
2570
2571 g_assert(dir == 0 || dir == 1 || dir == 2);
2572 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2573
2574 /* check if already done */
2575 if (max) {
2576 if (dir == 0 && self->max_horz && self->max_vert) return;
2577 if (dir == 1 && self->max_horz) return;
2578 if (dir == 2 && self->max_vert) return;
2579 } else {
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 }
2584
2585 /* we just tell it to configure in the same place and client_configure
2586 worries about filling the screen with the window */
2587 x = self->area.x;
2588 y = self->area.y;
2589 w = self->area.width;
2590 h = self->area.height;
2591
2592 if (max) {
2593 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2594 RECT_SET(self->pre_max_area,
2595 self->area.x, self->pre_max_area.y,
2596 self->area.width, self->pre_max_area.height);
2597 }
2598 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2599 RECT_SET(self->pre_max_area,
2600 self->pre_max_area.x, self->area.y,
2601 self->pre_max_area.width, self->area.height);
2602 }
2603 } else {
2604 Rect *a;
2605
2606 a = screen_area_monitor(self->desktop, 0);
2607 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2608 if (self->pre_max_area.width > 0) {
2609 x = self->pre_max_area.x;
2610 w = self->pre_max_area.width;
2611
2612 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2613 0, self->pre_max_area.height);
2614 } else {
2615 /* pick some fallbacks... */
2616 x = a->x + a->width / 4;
2617 w = a->width / 2;
2618 }
2619 }
2620 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2621 if (self->pre_max_area.height > 0) {
2622 y = self->pre_max_area.y;
2623 h = self->pre_max_area.height;
2624
2625 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2626 self->pre_max_area.width, 0);
2627 } else {
2628 /* pick some fallbacks... */
2629 y = a->y + a->height / 4;
2630 h = a->height / 2;
2631 }
2632 }
2633 }
2634
2635 if (dir == 0 || dir == 1) /* horz */
2636 self->max_horz = max;
2637 if (dir == 0 || dir == 2) /* vert */
2638 self->max_vert = max;
2639
2640 client_change_state(self); /* change the state hints on the client */
2641
2642 client_setup_decor_and_functions(self);
2643
2644 client_move_resize(self, x, y, w, h);
2645 }
2646
2647 void client_shade(ObClient *self, gboolean shade)
2648 {
2649 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2650 shade) || /* can't shade */
2651 self->shaded == shade) return; /* already done */
2652
2653 self->shaded = shade;
2654 client_change_state(self);
2655 client_change_wm_state(self); /* the window is being hidden/shown */
2656 /* resize the frame to just the titlebar */
2657 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2658 }
2659
2660 void client_close(ObClient *self)
2661 {
2662 XEvent ce;
2663
2664 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2665
2666 /* in the case that the client provides no means to requesting that it
2667 close, we just kill it */
2668 if (!self->delete_window)
2669 client_kill(self);
2670
2671 /*
2672 XXX: itd be cool to do timeouts and shit here for killing the client's
2673 process off
2674 like... if the window is around after 5 seconds, then the close button
2675 turns a nice red, and if this function is called again, the client is
2676 explicitly killed.
2677 */
2678
2679 ce.xclient.type = ClientMessage;
2680 ce.xclient.message_type = prop_atoms.wm_protocols;
2681 ce.xclient.display = ob_display;
2682 ce.xclient.window = self->window;
2683 ce.xclient.format = 32;
2684 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2685 ce.xclient.data.l[1] = event_curtime;
2686 ce.xclient.data.l[2] = 0l;
2687 ce.xclient.data.l[3] = 0l;
2688 ce.xclient.data.l[4] = 0l;
2689 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2690 }
2691
2692 void client_kill(ObClient *self)
2693 {
2694 XKillClient(ob_display, self->window);
2695 }
2696
2697 void client_hilite(ObClient *self, gboolean hilite)
2698 {
2699 if (self->demands_attention == hilite)
2700 return; /* no change */
2701
2702 /* don't allow focused windows to hilite */
2703 self->demands_attention = hilite && !client_focused(self);
2704 if (self->demands_attention)
2705 frame_flash_start(self->frame);
2706 else
2707 frame_flash_stop(self->frame);
2708 client_change_state(self);
2709 }
2710
2711 void client_set_desktop_recursive(ObClient *self,
2712 guint target, gboolean donthide)
2713 {
2714 guint old;
2715 GSList *it;
2716
2717 if (target != self->desktop) {
2718
2719 ob_debug("Setting desktop %u\n", target+1);
2720
2721 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2722
2723 /* remove from the old desktop(s) */
2724 focus_order_remove(self);
2725
2726 old = self->desktop;
2727 self->desktop = target;
2728 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2729 /* the frame can display the current desktop state */
2730 frame_adjust_state(self->frame);
2731 /* 'move' the window to the new desktop */
2732 if (!donthide)
2733 client_showhide(self);
2734 /* raise if it was not already on the desktop */
2735 if (old != DESKTOP_ALL)
2736 client_raise(self);
2737 if (STRUT_EXISTS(self->strut))
2738 screen_update_areas();
2739
2740 /* add to the new desktop(s) */
2741 if (config_focus_new)
2742 focus_order_to_top(self);
2743 else
2744 focus_order_to_bottom(self);
2745 }
2746
2747 /* move all transients */
2748 for (it = self->transients; it; it = g_slist_next(it))
2749 if (it->data != self)
2750 if (client_is_direct_child(self, it->data))
2751 client_set_desktop_recursive(it->data, target, donthide);
2752 }
2753
2754 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2755 {
2756 self = client_search_top_parent(self);
2757 client_set_desktop_recursive(self, target, donthide);
2758 }
2759
2760 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
2761 {
2762 while (child != parent &&
2763 child->transient_for && child->transient_for != OB_TRAN_GROUP)
2764 child = child->transient_for;
2765 return child == parent;
2766 }
2767
2768 ObClient *client_search_modal_child(ObClient *self)
2769 {
2770 GSList *it;
2771 ObClient *ret;
2772
2773 for (it = self->transients; it; it = g_slist_next(it)) {
2774 ObClient *c = it->data;
2775 if ((ret = client_search_modal_child(c))) return ret;
2776 if (c->modal) return c;
2777 }
2778 return NULL;
2779 }
2780
2781 gboolean client_validate(ObClient *self)
2782 {
2783 XEvent e;
2784
2785 XSync(ob_display, FALSE); /* get all events on the server */
2786
2787 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2788 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2789 XPutBackEvent(ob_display, &e);
2790 return FALSE;
2791 }
2792
2793 return TRUE;
2794 }
2795
2796 void client_set_wm_state(ObClient *self, glong state)
2797 {
2798 if (state == self->wmstate) return; /* no change */
2799
2800 switch (state) {
2801 case IconicState:
2802 client_iconify(self, TRUE, TRUE);
2803 break;
2804 case NormalState:
2805 client_iconify(self, FALSE, TRUE);
2806 break;
2807 }
2808 }
2809
2810 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
2811 {
2812 gboolean shaded = self->shaded;
2813 gboolean fullscreen = self->fullscreen;
2814 gboolean undecorated = self->undecorated;
2815 gboolean max_horz = self->max_horz;
2816 gboolean max_vert = self->max_vert;
2817 gboolean modal = self->modal;
2818 gboolean iconic = self->iconic;
2819 gboolean demands_attention = self->demands_attention;
2820 gint i;
2821
2822 if (!(action == prop_atoms.net_wm_state_add ||
2823 action == prop_atoms.net_wm_state_remove ||
2824 action == prop_atoms.net_wm_state_toggle))
2825 /* an invalid action was passed to the client message, ignore it */
2826 return;
2827
2828 for (i = 0; i < 2; ++i) {
2829 Atom state = i == 0 ? data1 : data2;
2830
2831 if (!state) continue;
2832
2833 /* if toggling, then pick whether we're adding or removing */
2834 if (action == prop_atoms.net_wm_state_toggle) {
2835 if (state == prop_atoms.net_wm_state_modal)
2836 action = modal ? prop_atoms.net_wm_state_remove :
2837 prop_atoms.net_wm_state_add;
2838 else if (state == prop_atoms.net_wm_state_maximized_vert)
2839 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2840 prop_atoms.net_wm_state_add;
2841 else if (state == prop_atoms.net_wm_state_maximized_horz)
2842 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2843 prop_atoms.net_wm_state_add;
2844 else if (state == prop_atoms.net_wm_state_shaded)
2845 action = shaded ? prop_atoms.net_wm_state_remove :
2846 prop_atoms.net_wm_state_add;
2847 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2848 action = self->skip_taskbar ?
2849 prop_atoms.net_wm_state_remove :
2850 prop_atoms.net_wm_state_add;
2851 else if (state == prop_atoms.net_wm_state_skip_pager)
2852 action = self->skip_pager ?
2853 prop_atoms.net_wm_state_remove :
2854 prop_atoms.net_wm_state_add;
2855 else if (state == prop_atoms.net_wm_state_hidden)
2856 action = self->iconic ?
2857 prop_atoms.net_wm_state_remove :
2858 prop_atoms.net_wm_state_add;
2859 else if (state == prop_atoms.net_wm_state_fullscreen)
2860 action = fullscreen ?
2861 prop_atoms.net_wm_state_remove :
2862 prop_atoms.net_wm_state_add;
2863 else if (state == prop_atoms.net_wm_state_above)
2864 action = self->above ? prop_atoms.net_wm_state_remove :
2865 prop_atoms.net_wm_state_add;
2866 else if (state == prop_atoms.net_wm_state_below)
2867 action = self->below ? prop_atoms.net_wm_state_remove :
2868 prop_atoms.net_wm_state_add;
2869 else if (state == prop_atoms.net_wm_state_demands_attention)
2870 action = self->demands_attention ?
2871 prop_atoms.net_wm_state_remove :
2872 prop_atoms.net_wm_state_add;
2873 else if (state == prop_atoms.ob_wm_state_undecorated)
2874 action = undecorated ? prop_atoms.net_wm_state_remove :
2875 prop_atoms.net_wm_state_add;
2876 }
2877
2878 if (action == prop_atoms.net_wm_state_add) {
2879 if (state == prop_atoms.net_wm_state_modal) {
2880 modal = TRUE;
2881 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2882 max_vert = TRUE;
2883 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2884 max_horz = TRUE;
2885 } else if (state == prop_atoms.net_wm_state_shaded) {
2886 shaded = TRUE;
2887 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2888 self->skip_taskbar = TRUE;
2889 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2890 self->skip_pager = TRUE;
2891 } else if (state == prop_atoms.net_wm_state_hidden) {
2892 iconic = TRUE;
2893 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2894 fullscreen = TRUE;
2895 } else if (state == prop_atoms.net_wm_state_above) {
2896 self->above = TRUE;
2897 self->below = FALSE;
2898 } else if (state == prop_atoms.net_wm_state_below) {
2899 self->above = FALSE;
2900 self->below = TRUE;
2901 } else if (state == prop_atoms.net_wm_state_demands_attention) {
2902 demands_attention = TRUE;
2903 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2904 undecorated = TRUE;
2905 }
2906
2907 } else { /* action == prop_atoms.net_wm_state_remove */
2908 if (state == prop_atoms.net_wm_state_modal) {
2909 modal = FALSE;
2910 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2911 max_vert = FALSE;
2912 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2913 max_horz = FALSE;
2914 } else if (state == prop_atoms.net_wm_state_shaded) {
2915 shaded = FALSE;
2916 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2917 self->skip_taskbar = FALSE;
2918 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2919 self->skip_pager = FALSE;
2920 } else if (state == prop_atoms.net_wm_state_hidden) {
2921 iconic = FALSE;
2922 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2923 fullscreen = FALSE;
2924 } else if (state == prop_atoms.net_wm_state_above) {
2925 self->above = FALSE;
2926 } else if (state == prop_atoms.net_wm_state_below) {
2927 self->below = FALSE;
2928 } else if (state == prop_atoms.net_wm_state_demands_attention) {
2929 demands_attention = FALSE;
2930 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2931 undecorated = FALSE;
2932 }
2933 }
2934 }
2935 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2936 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2937 /* toggling both */
2938 if (max_horz == max_vert) { /* both going the same way */
2939 client_maximize(self, max_horz, 0);
2940 } else {
2941 client_maximize(self, max_horz, 1);
2942 client_maximize(self, max_vert, 2);
2943 }
2944 } else {
2945 /* toggling one */
2946 if (max_horz != self->max_horz)
2947 client_maximize(self, max_horz, 1);
2948 else
2949 client_maximize(self, max_vert, 2);
2950 }
2951 }
2952 /* change fullscreen state before shading, as it will affect if the window
2953 can shade or not */
2954 if (fullscreen != self->fullscreen)
2955 client_fullscreen(self, fullscreen);
2956 if (shaded != self->shaded)
2957 client_shade(self, shaded);
2958 if (undecorated != self->undecorated)
2959 client_set_undecorated(self, undecorated);
2960 if (modal != self->modal) {
2961 self->modal = modal;
2962 /* when a window changes modality, then its stacking order with its
2963 transients needs to change */
2964 client_raise(self);
2965 }
2966 if (iconic != self->iconic)
2967 client_iconify(self, iconic, FALSE);
2968
2969 if (demands_attention != self->demands_attention)
2970 client_hilite(self, demands_attention);
2971
2972 client_change_state(self); /* change the hint to reflect these changes */
2973 }
2974
2975 ObClient *client_focus_target(ObClient *self)
2976 {
2977 ObClient *child = NULL;
2978
2979 child = client_search_modal_child(self);
2980 if (child) return child;
2981 return self;
2982 }
2983
2984 gboolean client_can_focus(ObClient *self)
2985 {
2986 XEvent ev;
2987
2988 /* choose the correct target */
2989 self = client_focus_target(self);
2990
2991 if (!self->frame->visible)
2992 return FALSE;
2993
2994 if (!(self->can_focus || self->focus_notify))
2995 return FALSE;
2996
2997 /* do a check to see if the window has already been unmapped or destroyed
2998 do this intelligently while watching out for unmaps we've generated
2999 (ignore_unmaps > 0) */
3000 if (XCheckTypedWindowEvent(ob_display, self->window,
3001 DestroyNotify, &ev)) {
3002 XPutBackEvent(ob_display, &ev);
3003 return FALSE;
3004 }
3005 while (XCheckTypedWindowEvent(ob_display, self->window,
3006 UnmapNotify, &ev)) {
3007 if (self->ignore_unmaps) {
3008 self->ignore_unmaps--;
3009 } else {
3010 XPutBackEvent(ob_display, &ev);
3011 return FALSE;
3012 }
3013 }
3014
3015 return TRUE;
3016 }
3017
3018 gboolean client_focus(ObClient *self)
3019 {
3020 /* choose the correct target */
3021 self = client_focus_target(self);
3022
3023 if (!client_can_focus(self)) {
3024 if (!self->frame->visible) {
3025 /* update the focus lists */
3026 focus_order_to_top(self);
3027 }
3028 return FALSE;
3029 }
3030
3031 ob_debug_type(OB_DEBUG_FOCUS,
3032 "Focusing client \"%s\" at time %u\n",
3033 self->title, event_curtime);
3034
3035 if (self->can_focus) {
3036 /* This can cause a BadMatch error with CurrentTime, or if an app
3037 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3038 xerror_set_ignore(TRUE);
3039 XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
3040 event_curtime);
3041 xerror_set_ignore(FALSE);
3042 }
3043
3044 if (self->focus_notify) {
3045 XEvent ce;
3046 ce.xclient.type = ClientMessage;
3047 ce.xclient.message_type = prop_atoms.wm_protocols;
3048 ce.xclient.display = ob_display;
3049 ce.xclient.window = self->window;
3050 ce.xclient.format = 32;
3051 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
3052 ce.xclient.data.l[1] = event_curtime;
3053 ce.xclient.data.l[2] = 0l;
3054 ce.xclient.data.l[3] = 0l;
3055 ce.xclient.data.l[4] = 0l;
3056 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
3057 }
3058
3059 #ifdef DEBUG_FOCUS
3060 ob_debug("%sively focusing %lx at %d\n",
3061 (self->can_focus ? "act" : "pass"),
3062 self->window, (gint) event_curtime);
3063 #endif
3064
3065 /* Cause the FocusIn to come back to us. Important for desktop switches,
3066 since otherwise we'll have no FocusIn on the queue and send it off to
3067 the focus_backup. */
3068 XSync(ob_display, FALSE);
3069 return TRUE;
3070 }
3071
3072 void client_activate(ObClient *self, gboolean here, gboolean user)
3073 {
3074 guint32 last_time = focus_client ? focus_client->user_time : CurrentTime;
3075
3076 /* XXX do some stuff here if user is false to determine if we really want
3077 to activate it or not (a parent or group member is currently
3078 active)?
3079 */
3080 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3081 "source=%s\n",
3082 self->window, event_curtime, last_time,
3083 (user ? "user" : "application"));
3084
3085 if (!user && event_curtime && last_time &&
3086 !event_time_after(event_curtime, last_time))
3087 {
3088 client_hilite(self, TRUE);
3089 } else {
3090 if (client_normal(self) && screen_showing_desktop)
3091 screen_show_desktop(FALSE);
3092 if (self->iconic)
3093 client_iconify(self, FALSE, here);
3094 if (self->desktop != DESKTOP_ALL &&
3095 self->desktop != screen_desktop) {
3096 if (here)
3097 client_set_desktop(self, screen_desktop, FALSE);
3098 else
3099 screen_set_desktop(self->desktop);
3100 } else if (!self->frame->visible)
3101 /* if its not visible for other reasons, then don't mess
3102 with it */
3103 return;
3104 if (self->shaded)
3105 client_shade(self, FALSE);
3106
3107 client_focus(self);
3108
3109 /* we do this an action here. this is rather important. this is because
3110 we want the results from the focus change to take place BEFORE we go
3111 about raising the window. when a fullscreen window loses focus, we
3112 need this or else the raise wont be able to raise above the
3113 to-lose-focus fullscreen window. */
3114 client_raise(self);
3115 }
3116 }
3117
3118 void client_raise(ObClient *self)
3119 {
3120 action_run_string("Raise", self, CurrentTime);
3121 }
3122
3123 void client_lower(ObClient *self)
3124 {
3125 action_run_string("Lower", self, CurrentTime);
3126 }
3127
3128 gboolean client_focused(ObClient *self)
3129 {
3130 return self == focus_client;
3131 }
3132
3133 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
3134 {
3135 guint i;
3136 /* si is the smallest image >= req */
3137 /* li is the largest image < req */
3138 gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
3139
3140 if (!self->nicons) {
3141 ObClientIcon *parent = NULL;
3142
3143 if (self->transient_for) {
3144 if (self->transient_for != OB_TRAN_GROUP)
3145 parent = client_icon_recursive(self->transient_for, w, h);
3146 else {
3147 GSList *it;
3148 for (it = self->group->members; it; it = g_slist_next(it)) {
3149 ObClient *c = it->data;
3150 if (c != self && !c->transient_for) {
3151 if ((parent = client_icon_recursive(c, w, h)))
3152 break;
3153 }
3154 }
3155 }
3156 }
3157
3158 return parent;
3159 }
3160
3161 for (i = 0; i < self->nicons; ++i) {
3162 size = self->icons[i].width * self->icons[i].height;
3163 if (size < smallest && size >= (unsigned)(w * h)) {
3164 smallest = size;
3165 si = i;
3166 }
3167 if (size > largest && size <= (unsigned)(w * h)) {
3168 largest = size;
3169 li = i;
3170 }
3171 }
3172 if (largest == 0) /* didnt find one smaller than the requested size */
3173 return &self->icons[si];
3174 return &self->icons[li];
3175 }
3176
3177 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
3178 {
3179 ObClientIcon *ret;
3180 static ObClientIcon deficon;
3181
3182 if (!(ret = client_icon_recursive(self, w, h))) {
3183 deficon.width = deficon.height = 48;
3184 deficon.data = ob_rr_theme->def_win_icon;
3185 ret = &deficon;
3186 }
3187 return ret;
3188 }
3189
3190 void client_set_layer(ObClient *self, gint layer)
3191 {
3192 if (layer < 0) {
3193 self->below = TRUE;
3194 self->above = FALSE;
3195 } else if (layer == 0) {
3196 self->below = self->above = FALSE;
3197 } else {
3198 self->below = FALSE;
3199 self->above = TRUE;
3200 }
3201 client_calc_layer(self);
3202 client_change_state(self); /* reflect this in the state hints */
3203 }
3204
3205 void client_set_undecorated(ObClient *self, gboolean undecorated)
3206 {
3207 if (self->undecorated != undecorated) {
3208 self->undecorated = undecorated;
3209 client_setup_decor_and_functions(self);
3210 /* Make sure the client knows it might have moved. Maybe there is a
3211 * better way of doing this so only one client_configure is sent, but
3212 * since 125 of these are sent per second when moving the window (with
3213 * user = FALSE) i doubt it matters much.
3214 */
3215 client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
3216 self->area.width, self->area.height, TRUE, TRUE);
3217 client_change_state(self); /* reflect this in the state hints */
3218 }
3219 }
3220
3221 guint client_monitor(ObClient *self)
3222 {
3223 return screen_find_monitor(&self->frame->area);
3224 }
3225
3226 ObClient *client_search_top_parent(ObClient *self)
3227 {
3228 while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
3229 self = self->transient_for;
3230 return self;
3231 }
3232
3233 GSList *client_search_all_top_parents(ObClient *self)
3234 {
3235 GSList *ret = NULL;
3236
3237 /* move up the direct transient chain as far as possible */
3238 while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
3239 self = self->transient_for;
3240
3241 if (!self->transient_for)
3242 ret = g_slist_prepend(ret, self);
3243 else {
3244 GSList *it;
3245
3246 g_assert(self->group);
3247
3248 for (it = self->group->members; it; it = g_slist_next(it)) {
3249 ObClient *c = it->data;
3250
3251 if (!c->transient_for && client_normal(c))
3252 ret = g_slist_prepend(ret, c);
3253 }
3254
3255 if (ret == NULL) /* no group parents */
3256 ret = g_slist_prepend(ret, self);
3257 }
3258
3259 return ret;
3260 }
3261
3262 ObClient *client_search_focus_parent(ObClient *self)
3263 {
3264 if (self->transient_for) {
3265 if (self->transient_for != OB_TRAN_GROUP) {
3266 if (client_focused(self->transient_for))
3267 return self->transient_for;
3268 } else {
3269 GSList *it;
3270
3271 for (it = self->group->members; it; it = g_slist_next(it)) {
3272 ObClient *c = it->data;
3273
3274 /* checking transient_for prevents infinate loops! */
3275 if (c != self && !c->transient_for)
3276 if (client_focused(c))
3277 return c;
3278 }
3279 }
3280 }
3281
3282 return NULL;
3283 }
3284
3285 ObClient *client_search_parent(ObClient *self, ObClient *search)
3286 {
3287 if (self->transient_for) {
3288 if (self->transient_for != OB_TRAN_GROUP) {
3289 if (self->transient_for == search)
3290 return search;
3291 } else {
3292 GSList *it;
3293
3294 for (it = self->group->members; it; it = g_slist_next(it)) {
3295 ObClient *c = it->data;
3296
3297 /* checking transient_for prevents infinate loops! */
3298 if (c != self && !c->transient_for)
3299 if (c == search)
3300 return search;
3301 }
3302 }
3303 }
3304
3305 return NULL;
3306 }
3307
3308 ObClient *client_search_transient(ObClient *self, ObClient *search)
3309 {
3310 GSList *sit;
3311
3312 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3313 if (sit->data == search)
3314 return search;
3315 if (client_search_transient(sit->data, search))
3316 return search;
3317 }
3318 return NULL;
3319 }
3320
3321 void client_update_sm_client_id(ObClient *self)
3322 {
3323 g_free(self->sm_client_id);
3324 self->sm_client_id = NULL;
3325
3326 if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
3327 self->group)
3328 PROP_GETS(self->group->leader, sm_client_id, locale,
3329 &self->sm_client_id);
3330 }
3331
3332 #define WANT_EDGE(cur, c) \
3333 if(cur == c) \
3334 continue; \
3335 if(!client_normal(cur)) \
3336 continue; \
3337 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3338 continue; \
3339 if(cur->iconic) \
3340 continue; \
3341 if(cur->layer < c->layer && !config_resist_layers_below) \
3342 continue;
3343
3344 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3345 if ((his_edge_start >= my_edge_start && \
3346 his_edge_start <= my_edge_end) || \
3347 (my_edge_start >= his_edge_start && \
3348 my_edge_start <= his_edge_end)) \
3349 dest = his_offset;
3350
3351 /* finds the nearest edge in the given direction from the current client
3352 * note to self: the edge is the -frame- edge (the actual one), not the
3353 * client edge.
3354 */
3355 gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang)
3356 {
3357 gint dest, monitor_dest;
3358 gint my_edge_start, my_edge_end, my_offset;
3359 GList *it;
3360 Rect *a, *monitor;
3361
3362 if(!client_list)
3363 return -1;
3364
3365 a = screen_area(c->desktop);
3366 monitor = screen_area_monitor(c->desktop, client_monitor(c));
3367
3368 switch(dir) {
3369 case OB_DIRECTION_NORTH:
3370 my_edge_start = c->frame->area.x;
3371 my_edge_end = c->frame->area.x + c->frame->area.width;
3372 my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0);
3373
3374 /* default: top of screen */
3375 dest = a->y + (hang ? c->frame->area.height : 0);
3376 monitor_dest = monitor->y + (hang ? c->frame->area.height : 0);
3377 /* if the monitor edge comes before the screen edge, */
3378 /* use that as the destination instead. (For xinerama) */
3379 if (monitor_dest != dest && my_offset > monitor_dest)
3380 dest = monitor_dest;
3381
3382 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3383 gint his_edge_start, his_edge_end, his_offset;
3384 ObClient *cur = it->data;
3385
3386 WANT_EDGE(cur, c)
3387
3388 his_edge_start = cur->frame->area.x;
3389 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3390 his_offset = cur->frame->area.y +
3391 (hang ? 0 : cur->frame->area.height);
3392
3393 if(his_offset + 1 > my_offset)
3394 continue;
3395
3396 if(his_offset < dest)
3397 continue;
3398
3399 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3400 }
3401 break;
3402 case OB_DIRECTION_SOUTH:
3403 my_edge_start = c->frame->area.x;
3404 my_edge_end = c->frame->area.x + c->frame->area.width;
3405 my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height);
3406
3407 /* default: bottom of screen */
3408 dest = a->y + a->height - (hang ? c->frame->area.height : 0);
3409 monitor_dest = monitor->y + monitor->height -
3410 (hang ? c->frame->area.height : 0);
3411 /* if the monitor edge comes before the screen edge, */
3412 /* use that as the destination instead. (For xinerama) */
3413 if (monitor_dest != dest && my_offset < monitor_dest)
3414 dest = monitor_dest;
3415
3416 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3417 gint his_edge_start, his_edge_end, his_offset;
3418 ObClient *cur = it->data;
3419
3420 WANT_EDGE(cur, c)
3421
3422 his_edge_start = cur->frame->area.x;
3423 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3424 his_offset = cur->frame->area.y +
3425 (hang ? cur->frame->area.height : 0);
3426
3427
3428 if(his_offset - 1 < my_offset)
3429 continue;
3430
3431 if(his_offset > dest)
3432 continue;
3433
3434 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3435 }
3436 break;
3437 case OB_DIRECTION_WEST:
3438 my_edge_start = c->frame->area.y;
3439 my_edge_end = c->frame->area.y + c->frame->area.height;
3440 my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0);
3441
3442 /* default: leftmost egde of screen */
3443 dest = a->x + (hang ? c->frame->area.width : 0);
3444 monitor_dest = monitor->x + (hang ? c->frame->area.width : 0);
3445 /* if the monitor edge comes before the screen edge, */
3446 /* use that as the destination instead. (For xinerama) */
3447 if (monitor_dest != dest && my_offset > monitor_dest)
3448 dest = monitor_dest;
3449
3450 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3451 gint his_edge_start, his_edge_end, his_offset;
3452 ObClient *cur = it->data;
3453
3454 WANT_EDGE(cur, c)
3455
3456 his_edge_start = cur->frame->area.y;
3457 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3458 his_offset = cur->frame->area.x +
3459 (hang ? 0 : cur->frame->area.width);
3460
3461 if(his_offset + 1 > my_offset)
3462 continue;
3463
3464 if(his_offset < dest)
3465 continue;
3466
3467 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3468 }
3469 break;
3470 case OB_DIRECTION_EAST:
3471 my_edge_start = c->frame->area.y;
3472 my_edge_end = c->frame->area.y + c->frame->area.height;
3473 my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width);
3474
3475 /* default: rightmost edge of screen */
3476 dest = a->x + a->width - (hang ? c->frame->area.width : 0);
3477 monitor_dest = monitor->x + monitor->width -
3478 (hang ? c->frame->area.width : 0);
3479 /* if the monitor edge comes before the screen edge, */
3480 /* use that as the destination instead. (For xinerama) */
3481 if (monitor_dest != dest && my_offset < monitor_dest)
3482 dest = monitor_dest;
3483
3484 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3485 gint his_edge_start, his_edge_end, his_offset;
3486 ObClient *cur = it->data;
3487
3488 WANT_EDGE(cur, c)
3489
3490 his_edge_start = cur->frame->area.y;
3491 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3492 his_offset = cur->frame->area.x +
3493 (hang ? cur->frame->area.width : 0);
3494
3495 if(his_offset - 1 < my_offset)
3496 continue;
3497
3498 if(his_offset > dest)
3499 continue;
3500
3501 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3502 }
3503 break;
3504 case OB_DIRECTION_NORTHEAST:
3505 case OB_DIRECTION_SOUTHEAST:
3506 case OB_DIRECTION_NORTHWEST:
3507 case OB_DIRECTION_SOUTHWEST:
3508 /* not implemented */
3509 default:
3510 g_assert_not_reached();
3511 dest = 0; /* suppress warning */
3512 }
3513 return dest;
3514 }
3515
3516 ObClient* client_under_pointer()
3517 {
3518 gint x, y;
3519 GList *it;
3520 ObClient *ret = NULL;
3521
3522 if (screen_pointer_pos(&x, &y)) {
3523 for (it = stacking_list; it; it = g_list_next(it)) {
3524 if (WINDOW_IS_CLIENT(it->data)) {
3525 ObClient *c = WINDOW_AS_CLIENT(it->data);
3526 if (c->frame->visible &&
3527 RECT_CONTAINS(c->frame->area, x, y)) {
3528 ret = c;
3529 break;
3530 }
3531 }
3532 }
3533 }
3534 return ret;
3535 }
3536
3537 gboolean client_has_group_siblings(ObClient *self)
3538 {
3539 return self->group && self->group->members->next;
3540 }
This page took 0.190751 seconds and 5 git commands to generate.