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