1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
45 #include <X11/Xutil.h>
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask)
50 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
55 ObClientDestructor func;
59 GList *client_list = NULL;
61 static GSList *client_destructors = NULL;
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);
81 void client_startup(gboolean reconfig)
88 void client_shutdown(gboolean reconfig)
92 void client_add_destructor(ObClientDestructor func, gpointer data)
94 Destructor *d = g_new(Destructor, 1);
97 client_destructors = g_slist_prepend(client_destructors, d);
100 void client_remove_destructor(ObClientDestructor func)
104 for (it = client_destructors; it; it = g_slist_next(it)) {
105 Destructor *d = it->data;
106 if (d->func == func) {
108 client_destructors = g_slist_delete_link(client_destructors, it);
114 void client_set_list()
116 Window *windows, *win_it;
118 guint size = g_list_length(client_list);
120 /* create an array of the window ids */
122 windows = g_new(Window, size);
124 for (it = client_list; it; it = g_list_next(it), ++win_it)
125 *win_it = ((ObClient*)it->data)->window;
129 PROP_SETA32(RootWindow(ob_display, ob_screen),
130 net_client_list, window, (gulong*)windows, size);
139 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
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);
149 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
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);
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);
169 void client_manage_all()
174 XWindowAttributes attrib;
176 XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
177 &w, &w, &children, &nchild);
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]);
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) {
195 for (i = 0; i < nchild; ++i) {
196 if (children[i] == None)
198 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
199 if (attrib.override_redirect) continue;
201 if (attrib.map_state != IsUnmapped)
202 client_manage(children[i]);
208 void client_manage(Window window)
212 XWindowAttributes attrib;
213 XSetWindowAttributes attrib_set;
215 gboolean activate = FALSE;
216 ObAppSettings *settings;
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))
226 XPutBackEvent(ob_display, &e);
228 ob_debug("Trying to manage unmapped window. Aborting that.\n");
230 return; /* don't manage it */
233 /* make sure it isn't an override-redirect window */
234 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
235 attrib.override_redirect)
238 return; /* don't manage it */
241 /* is the window a docking app */
242 if ((wmhint = XGetWMHints(ob_display, window))) {
243 if ((wmhint->flags & StateHint) &&
244 wmhint->initial_state == WithdrawnState)
246 dock_add(window, wmhint);
254 ob_debug("Managing window: %lx\n", window);
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);
263 /* create the ObClient struct, and populate it from the hints on the
265 self = g_new0(ObClient, 1);
266 self->obwin.type = Window_Client;
267 self->window = window;
269 /* non-zero defaults */
270 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
272 self->desktop = screen_num_desktops; /* always an invalid value */
273 self->user_time = CurrentTime;
275 client_get_all(self);
276 /* per-app settings override stuff, and return the settings for other
278 settings = client_get_settings_state(self);
279 /* the session should get the last say */
280 client_restore_session_state(self);
282 client_calc_layer(self);
285 Time t = sn_app_started(self->startup_id, self->class);
286 if (t) self->user_time = t;
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);
293 /* remove the client's border (and adjust re gravity) */
294 client_toggle_border(self, FALSE);
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);
300 /* create the decoration frame for the client window */
301 self->frame = frame_new(self);
303 frame_grab_client(self->frame, self);
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);
311 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
312 client_restore_session_stacking(self);
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))
329 if (self->desktop != screen_desktop) {
330 /* activate the window */
333 gboolean group_foc = FALSE;
338 for (it = self->group->members; it; it = g_slist_next(it))
340 if (client_focused(it->data))
348 (!self->transient_for && (!self->group ||
349 !self->group->members->next))) ||
350 client_search_focus_tree_full(self) ||
352 !client_normal(focus_client))
354 /* activate the window */
361 /* get the current position */
365 /* figure out placement for the window */
366 if (ob_state() == OB_STATE_RUNNING) {
369 transient = place_client(self, &newx, &newy, settings);
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) */
386 (((self->positioned & PPosition) &&
387 !(self->positioned & USPosition)) &&
388 client_normal(self) &&
392 /* do this after the window is placed, so the premax/prefullscreen numbers
394 also, this moves the window to the position where it has been placed
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);
400 keyboard_grab_for_client(self, TRUE);
401 mouse_grab_for_client(self, TRUE);
404 guint32 last_time = focus_client ?
405 focus_client->user_time : CurrentTime;
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);
411 /* If a nothing at all, or a parent was focused, then focus this
414 if (!focus_client || client_search_focus_parent(self) != NULL)
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))
424 /* Don't steal focus from globally active clients.
425 I stole this idea from KWin. It seems nice.
427 if (!(focus_client->can_focus || focus_client->focus_notify))
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 */
438 ob_debug("Focus stealing prevention activated for %s with time %u "
440 self->title, self->user_time, last_time);
441 /* if the client isn't focused, then hilite it so the user
443 client_hilite(self, TRUE);
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?
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
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
468 /* if using focus_delay, stop the timer now so that focus doesn't
470 event_halt_focus_delay();
474 /* client_activate does this but we aren't using it so we have to do it
476 if (screen_showing_desktop)
477 screen_show_desktop(FALSE);
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);
483 /* this has to happen after we're in the client_list */
484 if (STRUT_EXISTS(self->strut))
485 screen_update_areas();
487 /* update the list hints */
490 ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
493 void client_unmanage_all()
495 while (client_list != NULL)
496 client_unmanage(client_list->data);
499 void client_unmanage(ObClient *self)
504 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, self->class,
505 self->title ? self->title : "");
507 g_assert(self != NULL);
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);
513 frame_hide(self->frame);
514 /* sync to send the hide to the server quickly, and to get back the enter
516 XSync(ob_display, FALSE);
518 if (focus_client == self) {
519 /* ignore enter events from the unmap so it doesnt mess with the focus
521 event_ignore_queued_enters();
525 keyboard_grab_for_client(self, FALSE);
526 mouse_grab_for_client(self, FALSE);
528 /* remove the window from our save set */
529 XChangeSaveSet(ob_display, self->window, SetModeDelete);
531 /* update the focus lists */
532 focus_order_remove(self);
534 client_list = g_list_remove(client_list, self);
535 stacking_remove(self);
536 g_hash_table_remove(window_map, &self->window);
538 /* once the client is out of the list, update the struts to remove its
540 if (STRUT_EXISTS(self->strut))
541 screen_update_areas();
543 for (it = client_destructors; it; it = g_slist_next(it)) {
544 Destructor *d = it->data;
545 d->func(self, d->data);
548 /* tell our parent(s) that we're gone */
549 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
550 for (it = self->group->members; it; it = g_slist_next(it))
551 if (it->data != self)
552 ((ObClient*)it->data)->transients =
553 g_slist_remove(((ObClient*)it->data)->transients, self);
554 } else if (self->transient_for) { /* transient of window */
555 self->transient_for->transients =
556 g_slist_remove(self->transient_for->transients, self);
559 /* tell our transients that we're gone */
560 for (it = self->transients; it; it = g_slist_next(it)) {
561 if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
562 ((ObClient*)it->data)->transient_for = NULL;
563 client_calc_layer(it->data);
567 /* remove from its group */
569 group_remove(self->group, self);
573 /* restore the window's original geometry so it is not lost */
577 if (self->fullscreen)
578 a = self->pre_fullscreen_area;
579 else if (self->max_horz || self->max_vert) {
580 if (self->max_horz) {
581 a.x = self->pre_max_area.x;
582 a.width = self->pre_max_area.width;
584 if (self->max_vert) {
585 a.y = self->pre_max_area.y;
586 a.height = self->pre_max_area.height;
590 /* give the client its border back */
591 client_toggle_border(self, TRUE);
593 self->fullscreen = self->max_horz = self->max_vert = FALSE;
594 self->decorations = 0; /* unmanaged windows have no decor */
596 client_move_resize(self, a.x, a.y, a.width, a.height);
599 /* reparent the window out of the frame, and free the frame */
600 frame_release_client(self->frame, self);
603 if (ob_state() != OB_STATE_EXITING) {
604 /* these values should not be persisted across a window
606 PROP_ERASE(self->window, net_wm_desktop);
607 PROP_ERASE(self->window, net_wm_state);
608 PROP_ERASE(self->window, wm_state);
610 /* if we're left in an unmapped state, the client wont be mapped. this
611 is bad, since we will no longer be managing the window on restart */
612 XMapWindow(ob_display, self->window);
615 ob_debug("Unmanaged window 0x%lx\n", self->window);
617 /* free all data allocated in the client struct */
618 g_slist_free(self->transients);
619 for (j = 0; j < self->nicons; ++j)
620 g_free(self->icons[j].data);
621 if (self->nicons > 0)
624 g_free(self->icon_title);
628 g_free(self->sm_client_id);
631 /* update the list hints */
635 static ObAppSettings *client_get_settings_state(ObClient *self)
637 ObAppSettings *settings = NULL;
640 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
641 ObAppSettings *app = it->data;
643 if ((app->name && !app->class && !strcmp(app->name, self->name))
644 || (app->class && !app->name && !strcmp(app->class, self->class))
645 || (app->class && app->name && !strcmp(app->class, self->class)
646 && !strcmp(app->name, self->name)))
648 ob_debug("Window matching: %s\n", app->name);
649 /* Match if no role was specified in the per app setting, or if the
650 * string matches the beginning of the role, since apps like to set
651 * the role to things like browser-window-23c4b2f */
653 || !strncmp(app->role, self->role, strlen(app->role)))
663 if (settings->shade != -1)
664 self->shaded = !!settings->shade;
665 if (settings->decor != -1)
666 self->undecorated = !settings->decor;
667 if (settings->iconic != -1)
668 self->iconic = !!settings->iconic;
669 if (settings->skip_pager != -1)
670 self->skip_pager = !!settings->skip_pager;
671 if (settings->skip_taskbar != -1)
672 self->skip_taskbar = !!settings->skip_taskbar;
674 if (settings->max_vert != -1)
675 self->max_vert = !!settings->max_vert;
676 if (settings->max_horz != -1)
677 self->max_vert = !!settings->max_horz;
679 if (settings->fullscreen != -1)
680 self->fullscreen = !!settings->fullscreen;
682 if (settings->desktop < screen_num_desktops
683 || settings->desktop == DESKTOP_ALL)
684 self->desktop = settings->desktop;
686 if (settings->layer == -1) {
690 else if (settings->layer == 0) {
694 else if (settings->layer == 1) {
702 static void client_restore_session_state(ObClient *self)
706 if (!(it = session_state_find(self)))
709 self->session = it->data;
711 RECT_SET_POINT(self->area, self->session->x, self->session->y);
712 self->positioned = PPosition;
713 if (self->session->w > 0)
714 self->area.width = self->session->w;
715 if (self->session->h > 0)
716 self->area.height = self->session->h;
717 XResizeWindow(ob_display, self->window,
718 self->area.width, self->area.height);
720 self->desktop = (self->session->desktop == DESKTOP_ALL ?
721 self->session->desktop :
722 MIN(screen_num_desktops - 1, self->session->desktop));
723 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
725 self->shaded = self->session->shaded;
726 self->iconic = self->session->iconic;
727 self->skip_pager = self->session->skip_pager;
728 self->skip_taskbar = self->session->skip_taskbar;
729 self->fullscreen = self->session->fullscreen;
730 self->above = self->session->above;
731 self->below = self->session->below;
732 self->max_horz = self->session->max_horz;
733 self->max_vert = self->session->max_vert;
736 static void client_restore_session_stacking(ObClient *self)
740 if (!self->session) return;
742 it = g_list_find(session_saved_state, self->session);
743 for (it = g_list_previous(it); it; it = g_list_previous(it)) {
746 for (cit = client_list; cit; cit = g_list_next(cit))
747 if (session_state_cmp(it->data, cit->data))
750 client_calc_layer(self);
751 stacking_below(CLIENT_AS_WINDOW(self),
752 CLIENT_AS_WINDOW(cit->data));
758 void client_move_onscreen(ObClient *self, gboolean rude)
760 gint x = self->area.x;
761 gint y = self->area.y;
762 if (client_find_onscreen(self, &x, &y,
763 self->frame->area.width,
764 self->frame->area.height, rude)) {
765 client_move(self, x, y);
769 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
773 gint ox = *x, oy = *y;
775 frame_client_gravity(self->frame, x, y); /* get where the frame
778 /* XXX watch for xinerama dead areas */
779 /* This makes sure windows aren't entirely outside of the screen so you
780 can't see them at all.
781 It makes sure 10% of the window is on the screen at least. At don't let
782 it move itself off the top of the screen, which would hide the titlebar
783 on you. (The user can still do this if they want too, it's only limiting
786 if (client_normal(self)) {
787 a = screen_area(self->desktop);
788 if (!self->strut.right &&
789 *x + self->frame->area.width/10 >= a->x + a->width - 1)
790 *x = a->x + a->width - self->frame->area.width/10;
791 if (!self->strut.bottom &&
792 *y + self->frame->area.height/10 >= a->y + a->height - 1)
793 *y = a->y + a->height - self->frame->area.height/10;
794 if (!self->strut.left && *x + self->frame->area.width*9/10 - 1 < a->x)
795 *x = a->x - self->frame->area.width*9/10;
796 if (!self->strut.top && *y + self->frame->area.height*9/10 - 1 < a->y)
797 *y = a->y - self->frame->area.width*9/10;
800 /* This here doesn't let windows even a pixel outside the screen,
801 * when called from client_manage, programs placing themselves are
802 * forced completely onscreen, while things like
803 * xterm -geometry resolution-width/2 will work fine. Trying to
804 * place it completely offscreen will be handled in the above code.
805 * Sorry for this confused comment, i am tired. */
807 /* avoid the xinerama monitor divide while we're at it,
808 * remember to fix the placement stuff to avoid it also and
809 * then remove this XXX */
810 a = screen_area_monitor(self->desktop, client_monitor(self));
811 /* dont let windows map into the strut unless they
812 are bigger than the available area */
814 if (!self->strut.left && *x < a->x) *x = a->x;
815 if (!self->strut.right && *x + w > a->x + a->width)
816 *x = a->x + a->width - w;
818 if (h <= a->height) {
819 if (!self->strut.top && *y < a->y) *y = a->y;
820 if (!self->strut.bottom && *y + h > a->y + a->height)
821 *y = a->y + a->height - h;
825 frame_frame_gravity(self->frame, x, y); /* get where the client
828 return ox != *x || oy != *y;
831 static void client_toggle_border(ObClient *self, gboolean show)
833 /* adjust our idea of where the client is, based on its border. When the
834 border is removed, the client should now be considered to be in a
836 when re-adding the border to the client, the same operation needs to be
838 gint oldx = self->area.x, oldy = self->area.y;
839 gint x = oldx, y = oldy;
840 switch(self->gravity) {
842 case NorthWestGravity:
844 case SouthWestGravity:
846 case NorthEastGravity:
848 case SouthEastGravity:
849 if (show) x -= self->border_width * 2;
850 else x += self->border_width * 2;
857 if (show) x -= self->border_width;
858 else x += self->border_width;
861 switch(self->gravity) {
863 case NorthWestGravity:
865 case NorthEastGravity:
867 case SouthWestGravity:
869 case SouthEastGravity:
870 if (show) y -= self->border_width * 2;
871 else y += self->border_width * 2;
878 if (show) y -= self->border_width;
879 else y += self->border_width;
886 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
888 /* set border_width to 0 because there is no border to add into
889 calculations anymore */
890 self->border_width = 0;
892 XSetWindowBorderWidth(ob_display, self->window, 0);
896 static void client_get_all(ObClient *self)
898 client_get_area(self);
899 client_get_mwm_hints(self);
901 /* The transient hint is used to pick a type, but the type can also affect
902 transiency (dialogs are always made transients of their group if they
903 have one). This is Havoc's idea, but it is needed to make some apps
904 work right (eg tsclient). */
905 client_update_transient_for(self);
906 client_get_type(self);/* this can change the mwmhints for special cases */
907 client_get_state(self);
908 client_update_transient_for(self);
910 client_update_wmhints(self);
911 client_get_startup_id(self);
912 client_get_desktop(self);/* uses transient data/group/startup id if a
913 desktop is not specified */
914 client_get_shaped(self);
916 client_get_layer(self); /* if layer hasn't been specified, get it from
917 other sources if possible */
920 /* a couple type-based defaults for new windows */
922 /* this makes sure that these windows appear on all desktops */
923 if (self->type == OB_CLIENT_TYPE_DESKTOP)
924 self->desktop = DESKTOP_ALL;
927 client_update_protocols(self);
929 client_get_gravity(self); /* get the attribute gravity */
930 client_update_normal_hints(self); /* this may override the attribute
933 /* got the type, the mwmhints, the protocols, and the normal hints
934 (min/max sizes), so we're ready to set up the decorations/functions */
935 client_setup_decor_and_functions(self);
937 client_update_title(self);
938 client_update_class(self);
939 client_update_sm_client_id(self);
940 client_update_strut(self);
941 client_update_icons(self);
942 client_update_user_time(self);
945 static void client_get_startup_id(ObClient *self)
947 if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
949 PROP_GETS(self->group->leader,
950 net_startup_id, utf8, &self->startup_id);
953 static void client_get_area(ObClient *self)
955 XWindowAttributes wattrib;
958 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
959 g_assert(ret != BadWindow);
961 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
962 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
963 self->border_width = wattrib.border_width;
965 ob_debug("client area: %d %d %d %d\n", wattrib.x, wattrib.y,
966 wattrib.width, wattrib.height);
969 static void client_get_desktop(ObClient *self)
971 guint32 d = screen_num_desktops; /* an always-invalid value */
973 if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
974 if (d >= screen_num_desktops && d != DESKTOP_ALL)
975 self->desktop = screen_num_desktops - 1;
979 gboolean trdesk = FALSE;
981 if (self->transient_for) {
982 if (self->transient_for != OB_TRAN_GROUP) {
983 self->desktop = self->transient_for->desktop;
988 for (it = self->group->members; it; it = g_slist_next(it))
989 if (it->data != self &&
990 !((ObClient*)it->data)->transient_for) {
991 self->desktop = ((ObClient*)it->data)->desktop;
998 /* try get from the startup-notification protocol */
999 if (sn_get_desktop(self->startup_id, &self->desktop)) {
1000 if (self->desktop >= screen_num_desktops &&
1001 self->desktop != DESKTOP_ALL)
1002 self->desktop = screen_num_desktops - 1;
1004 /* defaults to the current desktop */
1005 self->desktop = screen_desktop;
1008 if (self->desktop != d) {
1009 /* set the desktop hint, to make sure that it always exists */
1010 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
1014 static void client_get_layer(ObClient *self)
1016 if (!(self->above || self->below)) {
1018 /* apply stuff from the group */
1022 for (it = self->group->members; it; it = g_slist_next(it)) {
1023 ObClient *c = it->data;
1024 if (c != self && !client_search_transient(self, c) &&
1025 client_normal(self) && client_normal(c))
1028 (c->above ? 1 : (c->below ? -1 : 0)));
1042 g_assert_not_reached();
1049 static void client_get_state(ObClient *self)
1054 if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
1056 for (i = 0; i < num; ++i) {
1057 if (state[i] == prop_atoms.net_wm_state_modal)
1059 else if (state[i] == prop_atoms.net_wm_state_shaded)
1060 self->shaded = TRUE;
1061 else if (state[i] == prop_atoms.net_wm_state_hidden)
1062 self->iconic = TRUE;
1063 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
1064 self->skip_taskbar = TRUE;
1065 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
1066 self->skip_pager = TRUE;
1067 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
1068 self->fullscreen = TRUE;
1069 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
1070 self->max_vert = TRUE;
1071 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
1072 self->max_horz = TRUE;
1073 else if (state[i] == prop_atoms.net_wm_state_above)
1075 else if (state[i] == prop_atoms.net_wm_state_below)
1077 else if (state[i] == prop_atoms.net_wm_state_demands_attention)
1078 self->demands_attention = TRUE;
1079 else if (state[i] == prop_atoms.ob_wm_state_undecorated)
1080 self->undecorated = TRUE;
1087 static void client_get_shaped(ObClient *self)
1089 self->shaped = FALSE;
1091 if (extensions_shape) {
1096 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
1098 XShapeQueryExtents(ob_display, self->window, &s, &foo,
1099 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1101 self->shaped = (s != 0);
1106 void client_update_transient_for(ObClient *self)
1109 ObClient *target = NULL;
1111 if (XGetTransientForHint(ob_display, self->window, &t)) {
1112 self->transient = TRUE;
1113 if (t != self->window) { /* cant be transient to itself! */
1114 target = g_hash_table_lookup(window_map, &t);
1115 /* if this happens then we need to check for it*/
1116 g_assert(target != self);
1117 if (target && !WINDOW_IS_CLIENT(target)) {
1118 /* this can happen when a dialog is a child of
1119 a dockapp, for example */
1123 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1125 Setting the transient_for to Root is actually illegal, however
1126 applications from time have done this to specify transient for
1129 Now you can do that by being a TYPE_DIALOG and not setting
1130 the transient_for hint at all on your window. But people still
1131 use Root, and Kwin is very strange in this regard.
1133 KWin 3.0 will not consider windows with transient_for set to
1134 Root as transient for their group *UNLESS* they are also modal.
1135 In that case, it will make them transient for the group. This
1136 leads to all sorts of weird behavior from KDE apps which are
1137 only tested in KWin. I'd like to follow their behavior just to
1138 make this work right with KDE stuff, but that seems wrong.
1140 if (!target && self->group) {
1141 /* not transient to a client, see if it is transient for a
1143 if (t == RootWindow(ob_display, ob_screen)) {
1144 /* window is a transient for its group! */
1145 target = OB_TRAN_GROUP;
1149 } else if (self->group) {
1150 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1151 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1152 self->type == OB_CLIENT_TYPE_MENU ||
1153 self->type == OB_CLIENT_TYPE_UTILITY)
1155 self->transient = TRUE;
1156 target = OB_TRAN_GROUP;
1159 self->transient = FALSE;
1161 /* if anything has changed... */
1162 if (target != self->transient_for) {
1163 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
1166 /* remove from old parents */
1167 for (it = self->group->members; it; it = g_slist_next(it)) {
1168 ObClient *c = it->data;
1169 if (c != self && !c->transient_for)
1170 c->transients = g_slist_remove(c->transients, self);
1172 } else if (self->transient_for != NULL) { /* transient of window */
1173 /* remove from old parent */
1174 self->transient_for->transients =
1175 g_slist_remove(self->transient_for->transients, self);
1177 self->transient_for = target;
1178 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
1181 /* add to new parents */
1182 for (it = self->group->members; it; it = g_slist_next(it)) {
1183 ObClient *c = it->data;
1184 if (c != self && !c->transient_for)
1185 c->transients = g_slist_append(c->transients, self);
1188 /* remove all transients which are in the group, that causes
1189 circlular pointer hell of doom */
1190 for (it = self->group->members; it; it = g_slist_next(it)) {
1192 for (sit = self->transients; sit; sit = next) {
1193 next = g_slist_next(sit);
1194 if (sit->data == it->data)
1196 g_slist_delete_link(self->transients, sit);
1199 } else if (self->transient_for != NULL) { /* transient of window */
1200 /* add to new parent */
1201 self->transient_for->transients =
1202 g_slist_append(self->transient_for->transients, self);
1207 static void client_get_mwm_hints(ObClient *self)
1212 self->mwmhints.flags = 0; /* default to none */
1214 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
1216 if (num >= OB_MWM_ELEMENTS) {
1217 self->mwmhints.flags = hints[0];
1218 self->mwmhints.functions = hints[1];
1219 self->mwmhints.decorations = hints[2];
1225 void client_get_type(ObClient *self)
1232 if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1233 /* use the first value that we know about in the array */
1234 for (i = 0; i < num; ++i) {
1235 if (val[i] == prop_atoms.net_wm_window_type_desktop)
1236 self->type = OB_CLIENT_TYPE_DESKTOP;
1237 else if (val[i] == prop_atoms.net_wm_window_type_dock)
1238 self->type = OB_CLIENT_TYPE_DOCK;
1239 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1240 self->type = OB_CLIENT_TYPE_TOOLBAR;
1241 else if (val[i] == prop_atoms.net_wm_window_type_menu)
1242 self->type = OB_CLIENT_TYPE_MENU;
1243 else if (val[i] == prop_atoms.net_wm_window_type_utility)
1244 self->type = OB_CLIENT_TYPE_UTILITY;
1245 else if (val[i] == prop_atoms.net_wm_window_type_splash)
1246 self->type = OB_CLIENT_TYPE_SPLASH;
1247 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1248 self->type = OB_CLIENT_TYPE_DIALOG;
1249 else if (val[i] == prop_atoms.net_wm_window_type_normal)
1250 self->type = OB_CLIENT_TYPE_NORMAL;
1251 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1252 /* prevent this window from getting any decor or
1254 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1255 OB_MWM_FLAG_DECORATIONS);
1256 self->mwmhints.decorations = 0;
1257 self->mwmhints.functions = 0;
1259 if (self->type != (ObClientType) -1)
1260 break; /* grab the first legit type */
1265 if (self->type == (ObClientType) -1) {
1266 /*the window type hint was not set, which means we either classify
1267 ourself as a normal window or a dialog, depending on if we are a
1269 if (self->transient)
1270 self->type = OB_CLIENT_TYPE_DIALOG;
1272 self->type = OB_CLIENT_TYPE_NORMAL;
1276 void client_update_protocols(ObClient *self)
1279 guint num_return, i;
1281 self->focus_notify = FALSE;
1282 self->delete_window = FALSE;
1284 if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1285 for (i = 0; i < num_return; ++i) {
1286 if (proto[i] == prop_atoms.wm_delete_window) {
1287 /* this means we can request the window to close */
1288 self->delete_window = TRUE;
1289 } else if (proto[i] == prop_atoms.wm_take_focus)
1290 /* if this protocol is requested, then the window will be
1291 notified whenever we want it to receive focus */
1292 self->focus_notify = TRUE;
1298 static void client_get_gravity(ObClient *self)
1300 XWindowAttributes wattrib;
1303 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1304 g_assert(ret != BadWindow);
1305 self->gravity = wattrib.win_gravity;
1308 void client_update_normal_hints(ObClient *self)
1312 gint oldgravity = self->gravity;
1315 self->min_ratio = 0.0f;
1316 self->max_ratio = 0.0f;
1317 SIZE_SET(self->size_inc, 1, 1);
1318 SIZE_SET(self->base_size, 0, 0);
1319 SIZE_SET(self->min_size, 0, 0);
1320 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1322 /* get the hints from the window */
1323 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1324 /* normal windows can't request placement! har har
1325 if (!client_normal(self))
1327 self->positioned = (size.flags & (PPosition|USPosition));
1329 if (size.flags & PWinGravity) {
1330 self->gravity = size.win_gravity;
1332 /* if the client has a frame, i.e. has already been mapped and
1333 is changing its gravity */
1334 if (self->frame && self->gravity != oldgravity) {
1335 /* move our idea of the client's position based on its new
1337 self->area.x = self->frame->area.x;
1338 self->area.y = self->frame->area.y;
1339 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
1343 if (size.flags & PAspect) {
1344 if (size.min_aspect.y)
1346 (gfloat) size.min_aspect.x / size.min_aspect.y;
1347 if (size.max_aspect.y)
1349 (gfloat) size.max_aspect.x / size.max_aspect.y;
1352 if (size.flags & PMinSize)
1353 SIZE_SET(self->min_size, size.min_width, size.min_height);
1355 if (size.flags & PMaxSize)
1356 SIZE_SET(self->max_size, size.max_width, size.max_height);
1358 if (size.flags & PBaseSize)
1359 SIZE_SET(self->base_size, size.base_width, size.base_height);
1361 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1362 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1366 void client_setup_decor_and_functions(ObClient *self)
1368 /* start with everything (cept fullscreen) */
1370 (OB_FRAME_DECOR_TITLEBAR |
1371 OB_FRAME_DECOR_HANDLE |
1372 OB_FRAME_DECOR_GRIPS |
1373 OB_FRAME_DECOR_BORDER |
1374 OB_FRAME_DECOR_ICON |
1375 OB_FRAME_DECOR_ALLDESKTOPS |
1376 OB_FRAME_DECOR_ICONIFY |
1377 OB_FRAME_DECOR_MAXIMIZE |
1378 OB_FRAME_DECOR_SHADE |
1379 OB_FRAME_DECOR_CLOSE);
1381 (OB_CLIENT_FUNC_RESIZE |
1382 OB_CLIENT_FUNC_MOVE |
1383 OB_CLIENT_FUNC_ICONIFY |
1384 OB_CLIENT_FUNC_MAXIMIZE |
1385 OB_CLIENT_FUNC_SHADE |
1386 OB_CLIENT_FUNC_CLOSE);
1388 if (!(self->min_size.width < self->max_size.width ||
1389 self->min_size.height < self->max_size.height))
1390 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1392 switch (self->type) {
1393 case OB_CLIENT_TYPE_NORMAL:
1394 /* normal windows retain all of the possible decorations and
1395 functionality, and are the only windows that you can fullscreen */
1396 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1399 case OB_CLIENT_TYPE_DIALOG:
1400 case OB_CLIENT_TYPE_UTILITY:
1401 /* these windows cannot be maximized */
1402 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1405 case OB_CLIENT_TYPE_MENU:
1406 case OB_CLIENT_TYPE_TOOLBAR:
1407 /* these windows get less functionality */
1408 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1411 case OB_CLIENT_TYPE_DESKTOP:
1412 case OB_CLIENT_TYPE_DOCK:
1413 case OB_CLIENT_TYPE_SPLASH:
1414 /* none of these windows are manipulated by the window manager */
1415 self->decorations = 0;
1416 self->functions = 0;
1420 /* Mwm Hints are applied subtractively to what has already been chosen for
1421 decor and functionality */
1422 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1423 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1424 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1425 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1427 /* if the mwm hints request no handle or title, then all
1428 decorations are disabled, but keep the border if that's
1430 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1431 self->decorations = OB_FRAME_DECOR_BORDER;
1433 self->decorations = 0;
1438 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1439 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1440 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1441 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1442 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1443 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1444 /* dont let mwm hints kill any buttons
1445 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1446 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1447 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1448 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1450 /* dont let mwm hints kill the close button
1451 if (! (self->mwmhints.functions & MwmFunc_Close))
1452 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1456 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1457 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1458 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1459 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1460 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1461 self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1463 /* can't maximize without moving/resizing */
1464 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1465 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1466 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1467 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1468 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1471 /* kill the handle on fully maxed windows */
1472 if (self->max_vert && self->max_horz)
1473 self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1475 /* finally, the user can have requested no decorations, which overrides
1476 everything (but doesnt give it a border if it doesnt have one) */
1477 if (self->undecorated) {
1478 if (config_theme_keepborder)
1479 self->decorations &= OB_FRAME_DECOR_BORDER;
1481 self->decorations = 0;
1484 /* if we don't have a titlebar, then we cannot shade! */
1485 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1486 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1488 /* now we need to check against rules for the client's current state */
1489 if (self->fullscreen) {
1490 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1491 OB_CLIENT_FUNC_FULLSCREEN |
1492 OB_CLIENT_FUNC_ICONIFY);
1493 self->decorations = 0;
1496 client_change_allowed_actions(self);
1499 /* adjust the client's decorations, etc. */
1500 client_reconfigure(self);
1504 static void client_change_allowed_actions(ObClient *self)
1509 /* desktop windows are kept on all desktops */
1510 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1511 actions[num++] = prop_atoms.net_wm_action_change_desktop;
1513 if (self->functions & OB_CLIENT_FUNC_SHADE)
1514 actions[num++] = prop_atoms.net_wm_action_shade;
1515 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1516 actions[num++] = prop_atoms.net_wm_action_close;
1517 if (self->functions & OB_CLIENT_FUNC_MOVE)
1518 actions[num++] = prop_atoms.net_wm_action_move;
1519 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1520 actions[num++] = prop_atoms.net_wm_action_minimize;
1521 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1522 actions[num++] = prop_atoms.net_wm_action_resize;
1523 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1524 actions[num++] = prop_atoms.net_wm_action_fullscreen;
1525 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1526 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1527 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1530 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1532 /* make sure the window isn't breaking any rules now */
1534 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1535 if (self->frame) client_shade(self, FALSE);
1536 else self->shaded = FALSE;
1538 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1539 if (self->frame) client_iconify(self, FALSE, TRUE);
1540 else self->iconic = FALSE;
1542 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1543 if (self->frame) client_fullscreen(self, FALSE);
1544 else self->fullscreen = FALSE;
1546 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1548 if (self->frame) client_maximize(self, FALSE, 0);
1549 else self->max_vert = self->max_horz = FALSE;
1553 void client_reconfigure(ObClient *self)
1555 /* by making this pass FALSE for user, we avoid the emacs event storm where
1556 every configurenotify causes an update in its normal hints, i think this
1557 is generally what we want anyways... */
1558 client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
1559 self->area.width, self->area.height, FALSE, TRUE);
1562 void client_update_wmhints(ObClient *self)
1567 /* assume a window takes input if it doesnt specify */
1568 self->can_focus = TRUE;
1570 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1571 if (hints->flags & InputHint)
1572 self->can_focus = hints->input;
1574 /* only do this when first managing the window *AND* when we aren't
1576 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1577 if (hints->flags & StateHint)
1578 self->iconic = hints->initial_state == IconicState;
1580 if (!(hints->flags & WindowGroupHint))
1581 hints->window_group = None;
1583 /* did the group state change? */
1584 if (hints->window_group !=
1585 (self->group ? self->group->leader : None)) {
1586 /* remove from the old group if there was one */
1587 if (self->group != NULL) {
1588 /* remove transients of the group */
1589 for (it = self->group->members; it; it = g_slist_next(it))
1590 self->transients = g_slist_remove(self->transients,
1593 /* remove myself from parents in the group */
1594 if (self->transient_for == OB_TRAN_GROUP) {
1595 for (it = self->group->members; it;
1596 it = g_slist_next(it))
1598 ObClient *c = it->data;
1600 if (c != self && !c->transient_for)
1601 c->transients = g_slist_remove(c->transients,
1606 group_remove(self->group, self);
1609 if (hints->window_group != None) {
1610 self->group = group_add(hints->window_group, self);
1612 /* i can only have transients from the group if i am not
1614 if (!self->transient_for) {
1615 /* add other transients of the group that are already
1617 for (it = self->group->members; it;
1618 it = g_slist_next(it))
1620 ObClient *c = it->data;
1621 if (c != self && c->transient_for == OB_TRAN_GROUP)
1623 g_slist_append(self->transients, c);
1628 /* because the self->transient flag wont change from this call,
1629 we don't need to update the window's type and such, only its
1630 transient_for, and the transients lists of other windows in
1631 the group may be affected */
1632 client_update_transient_for(self);
1635 /* the WM_HINTS can contain an icon */
1636 client_update_icons(self);
1642 void client_update_title(ObClient *self)
1646 g_free(self->title);
1649 if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
1650 /* try old x stuff */
1651 if (!(PROP_GETS(self->window, wm_name, locale, &data)
1652 || PROP_GETS(self->window, wm_name, utf8, &data))) {
1653 if (self->transient) {
1655 GNOME alert windows are not given titles:
1656 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1658 data = g_strdup("");
1660 data = g_strdup("Unnamed Window");
1664 PROP_SETS(self->window, net_wm_visible_name, data);
1668 frame_adjust_title(self->frame);
1670 /* update the icon title */
1672 g_free(self->icon_title);
1675 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1676 /* try old x stuff */
1677 if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) ||
1678 PROP_GETS(self->window, wm_icon_name, utf8, &data)))
1679 data = g_strdup(self->title);
1681 PROP_SETS(self->window, net_wm_visible_icon_name, data);
1682 self->icon_title = data;
1685 void client_update_class(ObClient *self)
1690 if (self->name) g_free(self->name);
1691 if (self->class) g_free(self->class);
1692 if (self->role) g_free(self->role);
1694 self->name = self->class = self->role = NULL;
1696 if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1698 self->name = g_strdup(data[0]);
1700 self->class = g_strdup(data[1]);
1705 if (PROP_GETS(self->window, wm_window_role, locale, &s))
1708 if (self->name == NULL) self->name = g_strdup("");
1709 if (self->class == NULL) self->class = g_strdup("");
1710 if (self->role == NULL) self->role = g_strdup("");
1713 void client_update_strut(ObClient *self)
1717 gboolean got = FALSE;
1720 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1724 STRUT_PARTIAL_SET(strut,
1725 data[0], data[2], data[1], data[3],
1726 data[4], data[5], data[8], data[9],
1727 data[6], data[7], data[10], data[11]);
1733 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1739 /* use the screen's width/height */
1740 a = screen_physical_area();
1742 STRUT_PARTIAL_SET(strut,
1743 data[0], data[2], data[1], data[3],
1744 a->y, a->y + a->height - 1,
1745 a->x, a->x + a->width - 1,
1746 a->y, a->y + a->height - 1,
1747 a->x, a->x + a->width - 1);
1753 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1754 0, 0, 0, 0, 0, 0, 0, 0);
1756 if (!STRUT_EQUAL(strut, self->strut)) {
1757 self->strut = strut;
1759 /* updating here is pointless while we're being mapped cuz we're not in
1760 the client list yet */
1762 screen_update_areas();
1766 void client_update_icons(ObClient *self)
1772 for (i = 0; i < self->nicons; ++i)
1773 g_free(self->icons[i].data);
1774 if (self->nicons > 0)
1775 g_free(self->icons);
1778 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1779 /* figure out how many valid icons are in here */
1781 while (num - i > 2) {
1785 if (i > num || w*h == 0) break;
1789 self->icons = g_new(ObClientIcon, self->nicons);
1791 /* store the icons */
1793 for (j = 0; j < self->nicons; ++j) {
1796 w = self->icons[j].width = data[i++];
1797 h = self->icons[j].height = data[i++];
1799 if (w*h == 0) continue;
1801 self->icons[j].data = g_new(RrPixel32, w * h);
1802 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1807 self->icons[j].data[t] =
1808 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1809 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1810 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1811 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1820 if ((hints = XGetWMHints(ob_display, self->window))) {
1821 if (hints->flags & IconPixmapHint) {
1823 self->icons = g_new(ObClientIcon, self->nicons);
1824 xerror_set_ignore(TRUE);
1825 if (!RrPixmapToRGBA(ob_rr_inst,
1827 (hints->flags & IconMaskHint ?
1828 hints->icon_mask : None),
1829 &self->icons[self->nicons-1].width,
1830 &self->icons[self->nicons-1].height,
1831 &self->icons[self->nicons-1].data)){
1832 g_free(&self->icons[self->nicons-1]);
1835 xerror_set_ignore(FALSE);
1842 frame_adjust_icon(self->frame);
1845 void client_update_user_time(ObClient *self)
1849 if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) {
1850 /* we set this every time, not just when it grows, because in practice
1851 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1852 backward we don't want all windows to stop focusing. we'll just
1853 assume noone is setting times older than the last one, cuz that
1854 would be pretty stupid anyways
1856 self->user_time = time;
1859 ob_debug("window %s user time %u\n", self->title, time);
1864 static void client_change_wm_state(ObClient *self)
1869 old = self->wmstate;
1871 if (self->shaded || self->iconic || !self->frame->visible)
1872 self->wmstate = IconicState;
1874 self->wmstate = NormalState;
1876 if (old != self->wmstate) {
1877 PROP_MSG(self->window, kde_wm_change_state,
1878 self->wmstate, 1, 0, 0);
1880 state[0] = self->wmstate;
1882 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1886 static void client_change_state(ObClient *self)
1888 gulong netstate[11];
1893 netstate[num++] = prop_atoms.net_wm_state_modal;
1895 netstate[num++] = prop_atoms.net_wm_state_shaded;
1897 netstate[num++] = prop_atoms.net_wm_state_hidden;
1898 if (self->skip_taskbar)
1899 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1900 if (self->skip_pager)
1901 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1902 if (self->fullscreen)
1903 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1905 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1907 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1909 netstate[num++] = prop_atoms.net_wm_state_above;
1911 netstate[num++] = prop_atoms.net_wm_state_below;
1912 if (self->demands_attention)
1913 netstate[num++] = prop_atoms.net_wm_state_demands_attention;
1914 if (self->undecorated)
1915 netstate[num++] = prop_atoms.ob_wm_state_undecorated;
1916 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1919 frame_adjust_state(self->frame);
1922 ObClient *client_search_focus_tree(ObClient *self)
1927 for (it = self->transients; it; it = g_slist_next(it)) {
1928 if (client_focused(it->data)) return it->data;
1929 if ((ret = client_search_focus_tree(it->data))) return ret;
1934 ObClient *client_search_focus_tree_full(ObClient *self)
1936 if (self->transient_for) {
1937 if (self->transient_for != OB_TRAN_GROUP) {
1938 return client_search_focus_tree_full(self->transient_for);
1941 gboolean recursed = FALSE;
1943 for (it = self->group->members; it; it = g_slist_next(it))
1944 if (!((ObClient*)it->data)->transient_for) {
1946 if ((c = client_search_focus_tree_full(it->data)))
1955 /* this function checks the whole tree, the client_search_focus_tree~
1956 does not, so we need to check this window */
1957 if (client_focused(self))
1959 return client_search_focus_tree(self);
1962 static ObStackingLayer calc_layer(ObClient *self)
1966 if (self->fullscreen &&
1967 (client_focused(self) || client_search_focus_tree(self)))
1968 l = OB_STACKING_LAYER_FULLSCREEN;
1969 else if (self->type == OB_CLIENT_TYPE_DESKTOP)
1970 l = OB_STACKING_LAYER_DESKTOP;
1971 else if (self->type == OB_CLIENT_TYPE_DOCK) {
1972 if (self->below) l = OB_STACKING_LAYER_NORMAL;
1973 else l = OB_STACKING_LAYER_ABOVE;
1975 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1976 else if (self->below) l = OB_STACKING_LAYER_BELOW;
1977 else l = OB_STACKING_LAYER_NORMAL;
1982 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1983 ObStackingLayer min, gboolean raised)
1985 ObStackingLayer old, own;
1989 own = calc_layer(self);
1990 self->layer = MAX(own, min);
1992 for (it = self->transients; it; it = g_slist_next(it))
1993 client_calc_layer_recursive(it->data, orig,
1995 raised ? raised : self->layer != old);
1997 if (!raised && self->layer != old)
1998 if (orig->frame) { /* only restack if the original window is managed */
1999 stacking_remove(CLIENT_AS_WINDOW(self));
2000 stacking_add(CLIENT_AS_WINDOW(self));
2004 void client_calc_layer(ObClient *self)
2011 /* transients take on the layer of their parents */
2012 it = client_search_all_top_parents(self);
2014 for (; it; it = g_slist_next(it))
2015 client_calc_layer_recursive(it->data, orig, 0, FALSE);
2018 gboolean client_should_show(ObClient *self)
2022 if (client_normal(self) && screen_showing_desktop)
2025 if (self->transient_for) {
2026 if (self->transient_for != OB_TRAN_GROUP)
2027 return client_should_show(self->transient_for);
2031 for (it = self->group->members; it; it = g_slist_next(it)) {
2032 ObClient *c = it->data;
2033 if (c != self && !c->transient_for) {
2034 if (client_should_show(c))
2041 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2047 void client_show(ObClient *self)
2050 if (client_should_show(self)) {
2051 frame_show(self->frame);
2054 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2055 needs to be in IconicState. This includes when it is on another
2058 client_change_wm_state(self);
2061 void client_hide(ObClient *self)
2063 if (!client_should_show(self)) {
2064 frame_hide(self->frame);
2067 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2068 needs to be in IconicState. This includes when it is on another
2071 client_change_wm_state(self);
2074 void client_showhide(ObClient *self)
2077 if (client_should_show(self)) {
2078 frame_show(self->frame);
2081 frame_hide(self->frame);
2084 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2085 needs to be in IconicState. This includes when it is on another
2088 client_change_wm_state(self);
2091 gboolean client_normal(ObClient *self) {
2092 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2093 self->type == OB_CLIENT_TYPE_DOCK ||
2094 self->type == OB_CLIENT_TYPE_SPLASH);
2097 static void client_apply_startup_state(ObClient *self, gint x, gint y)
2099 gboolean pos = FALSE; /* has the window's position been configured? */
2102 /* save the position, and set self->area for these to use */
2108 /* these are in a carefully crafted order.. */
2111 self->iconic = FALSE;
2112 client_iconify(self, TRUE, FALSE);
2114 if (self->fullscreen) {
2115 self->fullscreen = FALSE;
2116 client_fullscreen(self, TRUE);
2119 if (self->undecorated) {
2120 self->undecorated = FALSE;
2121 client_set_undecorated(self, TRUE);
2124 self->shaded = FALSE;
2125 client_shade(self, TRUE);
2127 if (self->demands_attention) {
2128 self->demands_attention = FALSE;
2129 client_hilite(self, TRUE);
2132 if (self->max_vert && self->max_horz) {
2133 self->max_vert = self->max_horz = FALSE;
2134 client_maximize(self, TRUE, 0);
2136 } else if (self->max_vert) {
2137 self->max_vert = FALSE;
2138 client_maximize(self, TRUE, 2);
2140 } else if (self->max_horz) {
2141 self->max_horz = FALSE;
2142 client_maximize(self, TRUE, 1);
2146 /* if the client didn't get positioned yet, then do so now
2147 call client_move even if the window is not being moved anywhere, because
2148 when we reparent it and decorate it, it is getting moved and we need to
2149 be telling it so with a ConfigureNotify event.
2152 /* use the saved position */
2155 client_move(self, x, y);
2158 /* nothing to do for the other states:
2167 void client_try_configure(ObClient *self, ObCorner anchor,
2168 gint *x, gint *y, gint *w, gint *h,
2169 gint *logicalw, gint *logicalh,
2172 Rect desired_area = {*x, *y, *w, *h};
2174 /* make the frame recalculate its dimentions n shit without changing
2175 anything visible for real, this way the constraints below can work with
2176 the updated frame dimensions. */
2177 frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
2179 /* work within the prefered sizes given by the window */
2180 if (!(*w == self->area.width && *h == self->area.height)) {
2181 gint basew, baseh, minw, minh;
2183 /* base size is substituted with min size if not specified */
2184 if (self->base_size.width || self->base_size.height) {
2185 basew = self->base_size.width;
2186 baseh = self->base_size.height;
2188 basew = self->min_size.width;
2189 baseh = self->min_size.height;
2191 /* min size is substituted with base size if not specified */
2192 if (self->min_size.width || self->min_size.height) {
2193 minw = self->min_size.width;
2194 minh = self->min_size.height;
2196 minw = self->base_size.width;
2197 minh = self->base_size.height;
2200 /* if this is a user-requested resize, then check against min/max
2203 /* smaller than min size or bigger than max size? */
2204 if (*w > self->max_size.width) *w = self->max_size.width;
2205 if (*w < minw) *w = minw;
2206 if (*h > self->max_size.height) *h = self->max_size.height;
2207 if (*h < minh) *h = minh;
2212 /* keep to the increments */
2213 *w /= self->size_inc.width;
2214 *h /= self->size_inc.height;
2216 /* you cannot resize to nothing */
2217 if (basew + *w < 1) *w = 1 - basew;
2218 if (baseh + *h < 1) *h = 1 - baseh;
2220 /* save the logical size */
2221 *logicalw = self->size_inc.width > 1 ? *w : *w + basew;
2222 *logicalh = self->size_inc.height > 1 ? *h : *h + baseh;
2224 *w *= self->size_inc.width;
2225 *h *= self->size_inc.height;
2230 /* adjust the height to match the width for the aspect ratios.
2231 for this, min size is not substituted for base size ever. */
2232 *w -= self->base_size.width;
2233 *h -= self->base_size.height;
2235 if (!self->fullscreen) {
2236 if (self->min_ratio)
2237 if (*h * self->min_ratio > *w) {
2238 *h = (gint)(*w / self->min_ratio);
2240 /* you cannot resize to nothing */
2243 *w = (gint)(*h * self->min_ratio);
2246 if (self->max_ratio)
2247 if (*h * self->max_ratio < *w) {
2248 *h = (gint)(*w / self->max_ratio);
2250 /* you cannot resize to nothing */
2253 *w = (gint)(*h * self->min_ratio);
2258 *w += self->base_size.width;
2259 *h += self->base_size.height;
2262 /* gets the frame's position */
2263 frame_client_gravity(self->frame, x, y);
2265 /* these positions are frame positions, not client positions */
2267 /* set the size and position if fullscreen */
2268 if (self->fullscreen) {
2272 i = screen_find_monitor(&desired_area);
2273 a = screen_physical_area_monitor(i);
2280 user = FALSE; /* ignore if the client can't be moved/resized when it
2281 is entering fullscreen */
2282 } else if (self->max_horz || self->max_vert) {
2286 i = screen_find_monitor(&desired_area);
2287 a = screen_area_monitor(self->desktop, i);
2289 /* set the size and position if maximized */
2290 if (self->max_horz) {
2292 *w = a->width - self->frame->size.left - self->frame->size.right;
2294 if (self->max_vert) {
2296 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2299 /* maximizing is not allowed if the user can't move+resize the window
2303 /* gets the client's position */
2304 frame_frame_gravity(self->frame, x, y);
2306 /* these override the above states! if you cant move you can't move! */
2308 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2312 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2313 *w = self->area.width;
2314 *h = self->area.height;
2322 case OB_CORNER_TOPLEFT:
2324 case OB_CORNER_TOPRIGHT:
2325 *x -= *w - self->area.width;
2327 case OB_CORNER_BOTTOMLEFT:
2328 *y -= *h - self->area.height;
2330 case OB_CORNER_BOTTOMRIGHT:
2331 *x -= *w - self->area.width;
2332 *y -= *h - self->area.height;
2338 void client_configure_full(ObClient *self, ObCorner anchor,
2339 gint x, gint y, gint w, gint h,
2340 gboolean user, gboolean final,
2341 gboolean force_reply)
2343 gint oldw, oldh, oldrx, oldry;
2344 gboolean send_resize_client;
2345 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
2346 guint fdecor = self->frame->decorations;
2347 gboolean fhorz = self->frame->max_horz;
2348 gint logicalw, logicalh;
2350 /* find the new x, y, width, and height (and logical size) */
2351 client_try_configure(self, anchor, &x, &y, &w, &h,
2352 &logicalw, &logicalh, user);
2354 /* set the logical size if things changed */
2355 if (!(w == self->area.width && h == self->area.height))
2356 SIZE_SET(self->logical_size, logicalw, logicalh);
2358 /* figure out if we moved or resized or what */
2359 moved = x != self->area.x || y != self->area.y;
2360 resized = w != self->area.width || h != self->area.height;
2362 oldw = self->area.width;
2363 oldh = self->area.height;
2364 RECT_SET(self->area, x, y, w, h);
2366 /* for app-requested resizes, always resize if 'resized' is true.
2367 for user-requested ones, only resize if final is true, or when
2368 resizing in redraw mode */
2369 send_resize_client = ((!user && resized) ||
2371 (resized && config_resize_redraw))));
2373 /* if the client is enlarging, then resize the client before the frame */
2374 if (send_resize_client && user && (w > oldw || h > oldh))
2375 XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
2377 /* find the frame's dimensions and move/resize it */
2378 if (self->decorations != fdecor || self->max_horz != fhorz)
2379 moved = resized = TRUE;
2380 if (moved || resized)
2381 frame_adjust_area(self->frame, moved, resized, FALSE);
2383 /* find the client's position relative to the root window */
2384 oldrx = self->root_pos.x;
2385 oldry = self->root_pos.y;
2386 rootmoved = (oldrx != (signed)(self->frame->area.x +
2387 self->frame->size.left -
2388 self->border_width) ||
2389 oldry != (signed)(self->frame->area.y +
2390 self->frame->size.top -
2391 self->border_width));
2393 if (force_reply || ((!user || (user && final)) && rootmoved))
2397 POINT_SET(self->root_pos,
2398 self->frame->area.x + self->frame->size.left -
2400 self->frame->area.y + self->frame->size.top -
2401 self->border_width);
2403 event.type = ConfigureNotify;
2404 event.xconfigure.display = ob_display;
2405 event.xconfigure.event = self->window;
2406 event.xconfigure.window = self->window;
2408 /* root window real coords */
2409 event.xconfigure.x = self->root_pos.x;
2410 event.xconfigure.y = self->root_pos.y;
2411 event.xconfigure.width = w;
2412 event.xconfigure.height = h;
2413 event.xconfigure.border_width = 0;
2414 event.xconfigure.above = self->frame->plate;
2415 event.xconfigure.override_redirect = FALSE;
2416 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2417 FALSE, StructureNotifyMask, &event);
2420 /* if the client is shrinking, then resize the frame before the client */
2421 if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2422 XResizeWindow(ob_display, self->window, w, h);
2427 void client_fullscreen(ObClient *self, gboolean fs)
2431 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2432 self->fullscreen == fs) return; /* already done */
2434 self->fullscreen = fs;
2435 client_change_state(self); /* change the state hints on the client */
2436 client_calc_layer(self); /* and adjust out layer/stacking */
2439 self->pre_fullscreen_area = self->area;
2440 /* if the window is maximized, its area isn't all that meaningful.
2441 save it's premax area instead. */
2442 if (self->max_horz) {
2443 self->pre_fullscreen_area.x = self->pre_max_area.x;
2444 self->pre_fullscreen_area.width = self->pre_max_area.width;
2446 if (self->max_vert) {
2447 self->pre_fullscreen_area.y = self->pre_max_area.y;
2448 self->pre_fullscreen_area.height = self->pre_max_area.height;
2451 /* these are not actually used cuz client_configure will set them
2452 as appropriate when the window is fullscreened */
2457 if (self->pre_fullscreen_area.width > 0 &&
2458 self->pre_fullscreen_area.height > 0)
2460 x = self->pre_fullscreen_area.x;
2461 y = self->pre_fullscreen_area.y;
2462 w = self->pre_fullscreen_area.width;
2463 h = self->pre_fullscreen_area.height;
2464 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2466 /* pick some fallbacks... */
2467 a = screen_area_monitor(self->desktop, 0);
2468 x = a->x + a->width / 4;
2469 y = a->y + a->height / 4;
2475 client_setup_decor_and_functions(self);
2477 client_move_resize(self, x, y, w, h);
2479 /* try focus us when we go into fullscreen mode */
2483 static void client_iconify_recursive(ObClient *self,
2484 gboolean iconic, gboolean curdesk)
2487 gboolean changed = FALSE;
2490 if (self->iconic != iconic) {
2491 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2495 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2496 self->iconic = iconic;
2498 /* update the focus lists.. iconic windows go to the bottom of
2499 the list, put the new iconic window at the 'top of the
2501 focus_order_to_top(self);
2506 self->iconic = iconic;
2509 client_set_desktop(self, screen_desktop, FALSE);
2511 /* this puts it after the current focused window */
2512 focus_order_remove(self);
2513 focus_order_add_new(self);
2520 client_change_state(self);
2521 client_showhide(self);
2522 if (STRUT_EXISTS(self->strut))
2523 screen_update_areas();
2526 /* iconify all direct transients */
2527 for (it = self->transients; it; it = g_slist_next(it))
2528 if (it->data != self)
2529 if (client_is_direct_child(self, it->data))
2530 client_iconify_recursive(it->data, iconic, curdesk);
2533 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2535 /* move up the transient chain as far as possible first */
2536 self = client_search_top_parent(self);
2537 client_iconify_recursive(self, iconic, curdesk);
2540 void client_maximize(ObClient *self, gboolean max, gint dir)
2544 g_assert(dir == 0 || dir == 1 || dir == 2);
2545 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2547 /* check if already done */
2549 if (dir == 0 && self->max_horz && self->max_vert) return;
2550 if (dir == 1 && self->max_horz) return;
2551 if (dir == 2 && self->max_vert) return;
2553 if (dir == 0 && !self->max_horz && !self->max_vert) return;
2554 if (dir == 1 && !self->max_horz) return;
2555 if (dir == 2 && !self->max_vert) return;
2558 /* we just tell it to configure in the same place and client_configure
2559 worries about filling the screen with the window */
2562 w = self->area.width;
2563 h = self->area.height;
2566 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2567 RECT_SET(self->pre_max_area,
2568 self->area.x, self->pre_max_area.y,
2569 self->area.width, self->pre_max_area.height);
2571 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2572 RECT_SET(self->pre_max_area,
2573 self->pre_max_area.x, self->area.y,
2574 self->pre_max_area.width, self->area.height);
2579 a = screen_area_monitor(self->desktop, 0);
2580 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2581 if (self->pre_max_area.width > 0) {
2582 x = self->pre_max_area.x;
2583 w = self->pre_max_area.width;
2585 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2586 0, self->pre_max_area.height);
2588 /* pick some fallbacks... */
2589 x = a->x + a->width / 4;
2593 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2594 if (self->pre_max_area.height > 0) {
2595 y = self->pre_max_area.y;
2596 h = self->pre_max_area.height;
2598 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2599 self->pre_max_area.width, 0);
2601 /* pick some fallbacks... */
2602 y = a->y + a->height / 4;
2608 if (dir == 0 || dir == 1) /* horz */
2609 self->max_horz = max;
2610 if (dir == 0 || dir == 2) /* vert */
2611 self->max_vert = max;
2613 client_change_state(self); /* change the state hints on the client */
2615 client_setup_decor_and_functions(self);
2617 client_move_resize(self, x, y, w, h);
2620 void client_shade(ObClient *self, gboolean shade)
2622 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2623 shade) || /* can't shade */
2624 self->shaded == shade) return; /* already done */
2626 self->shaded = shade;
2627 client_change_state(self);
2628 client_change_wm_state(self); /* the window is being hidden/shown */
2629 /* resize the frame to just the titlebar */
2630 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2633 void client_close(ObClient *self)
2637 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2639 /* in the case that the client provides no means to requesting that it
2640 close, we just kill it */
2641 if (!self->delete_window)
2645 XXX: itd be cool to do timeouts and shit here for killing the client's
2647 like... if the window is around after 5 seconds, then the close button
2648 turns a nice red, and if this function is called again, the client is
2652 ce.xclient.type = ClientMessage;
2653 ce.xclient.message_type = prop_atoms.wm_protocols;
2654 ce.xclient.display = ob_display;
2655 ce.xclient.window = self->window;
2656 ce.xclient.format = 32;
2657 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2658 ce.xclient.data.l[1] = event_curtime;
2659 ce.xclient.data.l[2] = 0l;
2660 ce.xclient.data.l[3] = 0l;
2661 ce.xclient.data.l[4] = 0l;
2662 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2665 void client_kill(ObClient *self)
2667 XKillClient(ob_display, self->window);
2670 void client_hilite(ObClient *self, gboolean hilite)
2672 if (self->demands_attention == hilite)
2673 return; /* no change */
2675 /* don't allow focused windows to hilite */
2676 self->demands_attention = hilite && !client_focused(self);
2677 if (self->demands_attention)
2678 frame_flash_start(self->frame);
2680 frame_flash_stop(self->frame);
2681 client_change_state(self);
2684 void client_set_desktop_recursive(ObClient *self,
2685 guint target, gboolean donthide)
2690 if (target != self->desktop) {
2692 ob_debug("Setting desktop %u\n", target+1);
2694 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2696 /* remove from the old desktop(s) */
2697 focus_order_remove(self);
2699 old = self->desktop;
2700 self->desktop = target;
2701 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2702 /* the frame can display the current desktop state */
2703 frame_adjust_state(self->frame);
2704 /* 'move' the window to the new desktop */
2706 client_showhide(self);
2707 /* raise if it was not already on the desktop */
2708 if (old != DESKTOP_ALL)
2710 if (STRUT_EXISTS(self->strut))
2711 screen_update_areas();
2713 /* add to the new desktop(s) */
2714 if (config_focus_new)
2715 focus_order_to_top(self);
2717 focus_order_to_bottom(self);
2720 /* move all transients */
2721 for (it = self->transients; it; it = g_slist_next(it))
2722 if (it->data != self)
2723 if (client_is_direct_child(self, it->data))
2724 client_set_desktop_recursive(it->data, target, donthide);
2727 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2729 self = client_search_top_parent(self);
2730 client_set_desktop_recursive(self, target, donthide);
2733 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
2735 while (child != parent &&
2736 child->transient_for && child->transient_for != OB_TRAN_GROUP)
2737 child = child->transient_for;
2738 return child == parent;
2741 ObClient *client_search_modal_child(ObClient *self)
2746 for (it = self->transients; it; it = g_slist_next(it)) {
2747 ObClient *c = it->data;
2748 if ((ret = client_search_modal_child(c))) return ret;
2749 if (c->modal) return c;
2754 gboolean client_validate(ObClient *self)
2758 XSync(ob_display, FALSE); /* get all events on the server */
2760 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2761 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2762 XPutBackEvent(ob_display, &e);
2769 void client_set_wm_state(ObClient *self, glong state)
2771 if (state == self->wmstate) return; /* no change */
2775 client_iconify(self, TRUE, TRUE);
2778 client_iconify(self, FALSE, TRUE);
2783 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
2785 gboolean shaded = self->shaded;
2786 gboolean fullscreen = self->fullscreen;
2787 gboolean undecorated = self->undecorated;
2788 gboolean max_horz = self->max_horz;
2789 gboolean max_vert = self->max_vert;
2790 gboolean modal = self->modal;
2791 gboolean iconic = self->iconic;
2792 gboolean demands_attention = self->demands_attention;
2795 if (!(action == prop_atoms.net_wm_state_add ||
2796 action == prop_atoms.net_wm_state_remove ||
2797 action == prop_atoms.net_wm_state_toggle))
2798 /* an invalid action was passed to the client message, ignore it */
2801 for (i = 0; i < 2; ++i) {
2802 Atom state = i == 0 ? data1 : data2;
2804 if (!state) continue;
2806 /* if toggling, then pick whether we're adding or removing */
2807 if (action == prop_atoms.net_wm_state_toggle) {
2808 if (state == prop_atoms.net_wm_state_modal)
2809 action = modal ? prop_atoms.net_wm_state_remove :
2810 prop_atoms.net_wm_state_add;
2811 else if (state == prop_atoms.net_wm_state_maximized_vert)
2812 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2813 prop_atoms.net_wm_state_add;
2814 else if (state == prop_atoms.net_wm_state_maximized_horz)
2815 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2816 prop_atoms.net_wm_state_add;
2817 else if (state == prop_atoms.net_wm_state_shaded)
2818 action = shaded ? prop_atoms.net_wm_state_remove :
2819 prop_atoms.net_wm_state_add;
2820 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2821 action = self->skip_taskbar ?
2822 prop_atoms.net_wm_state_remove :
2823 prop_atoms.net_wm_state_add;
2824 else if (state == prop_atoms.net_wm_state_skip_pager)
2825 action = self->skip_pager ?
2826 prop_atoms.net_wm_state_remove :
2827 prop_atoms.net_wm_state_add;
2828 else if (state == prop_atoms.net_wm_state_hidden)
2829 action = self->iconic ?
2830 prop_atoms.net_wm_state_remove :
2831 prop_atoms.net_wm_state_add;
2832 else if (state == prop_atoms.net_wm_state_fullscreen)
2833 action = fullscreen ?
2834 prop_atoms.net_wm_state_remove :
2835 prop_atoms.net_wm_state_add;
2836 else if (state == prop_atoms.net_wm_state_above)
2837 action = self->above ? prop_atoms.net_wm_state_remove :
2838 prop_atoms.net_wm_state_add;
2839 else if (state == prop_atoms.net_wm_state_below)
2840 action = self->below ? prop_atoms.net_wm_state_remove :
2841 prop_atoms.net_wm_state_add;
2842 else if (state == prop_atoms.net_wm_state_demands_attention)
2843 action = self->demands_attention ?
2844 prop_atoms.net_wm_state_remove :
2845 prop_atoms.net_wm_state_add;
2846 else if (state == prop_atoms.ob_wm_state_undecorated)
2847 action = undecorated ? prop_atoms.net_wm_state_remove :
2848 prop_atoms.net_wm_state_add;
2851 if (action == prop_atoms.net_wm_state_add) {
2852 if (state == prop_atoms.net_wm_state_modal) {
2854 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2856 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2858 } else if (state == prop_atoms.net_wm_state_shaded) {
2860 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2861 self->skip_taskbar = TRUE;
2862 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2863 self->skip_pager = TRUE;
2864 } else if (state == prop_atoms.net_wm_state_hidden) {
2866 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2868 } else if (state == prop_atoms.net_wm_state_above) {
2870 self->below = FALSE;
2871 } else if (state == prop_atoms.net_wm_state_below) {
2872 self->above = FALSE;
2874 } else if (state == prop_atoms.net_wm_state_demands_attention) {
2875 demands_attention = TRUE;
2876 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2880 } else { /* action == prop_atoms.net_wm_state_remove */
2881 if (state == prop_atoms.net_wm_state_modal) {
2883 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2885 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2887 } else if (state == prop_atoms.net_wm_state_shaded) {
2889 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2890 self->skip_taskbar = FALSE;
2891 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2892 self->skip_pager = FALSE;
2893 } else if (state == prop_atoms.net_wm_state_hidden) {
2895 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2897 } else if (state == prop_atoms.net_wm_state_above) {
2898 self->above = FALSE;
2899 } else if (state == prop_atoms.net_wm_state_below) {
2900 self->below = FALSE;
2901 } else if (state == prop_atoms.net_wm_state_demands_attention) {
2902 demands_attention = FALSE;
2903 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2904 undecorated = FALSE;
2908 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2909 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2911 if (max_horz == max_vert) { /* both going the same way */
2912 client_maximize(self, max_horz, 0);
2914 client_maximize(self, max_horz, 1);
2915 client_maximize(self, max_vert, 2);
2919 if (max_horz != self->max_horz)
2920 client_maximize(self, max_horz, 1);
2922 client_maximize(self, max_vert, 2);
2925 /* change fullscreen state before shading, as it will affect if the window
2927 if (fullscreen != self->fullscreen)
2928 client_fullscreen(self, fullscreen);
2929 if (shaded != self->shaded)
2930 client_shade(self, shaded);
2931 if (undecorated != self->undecorated)
2932 client_set_undecorated(self, undecorated);
2933 if (modal != self->modal) {
2934 self->modal = modal;
2935 /* when a window changes modality, then its stacking order with its
2936 transients needs to change */
2939 if (iconic != self->iconic)
2940 client_iconify(self, iconic, FALSE);
2942 if (demands_attention != self->demands_attention)
2943 client_hilite(self, demands_attention);
2945 client_change_state(self); /* change the hint to reflect these changes */
2948 ObClient *client_focus_target(ObClient *self)
2950 ObClient *child = NULL;
2952 child = client_search_modal_child(self);
2953 if (child) return child;
2957 gboolean client_can_focus(ObClient *self)
2961 /* choose the correct target */
2962 self = client_focus_target(self);
2964 if (!self->frame->visible)
2967 if (!(self->can_focus || self->focus_notify))
2970 /* do a check to see if the window has already been unmapped or destroyed
2971 do this intelligently while watching out for unmaps we've generated
2972 (ignore_unmaps > 0) */
2973 if (XCheckTypedWindowEvent(ob_display, self->window,
2974 DestroyNotify, &ev)) {
2975 XPutBackEvent(ob_display, &ev);
2978 while (XCheckTypedWindowEvent(ob_display, self->window,
2979 UnmapNotify, &ev)) {
2980 if (self->ignore_unmaps) {
2981 self->ignore_unmaps--;
2983 XPutBackEvent(ob_display, &ev);
2991 gboolean client_focus(ObClient *self)
2993 /* choose the correct target */
2994 self = client_focus_target(self);
2996 if (!client_can_focus(self)) {
2997 if (!self->frame->visible) {
2998 /* update the focus lists */
2999 focus_order_to_top(self);
3004 ob_debug_type(OB_DEBUG_FOCUS,
3005 "Focusing client \"%s\" at time %u\n",
3006 self->title, event_curtime);
3008 if (self->can_focus) {
3009 /* This can cause a BadMatch error with CurrentTime, or if an app
3010 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3011 xerror_set_ignore(TRUE);
3012 XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
3014 xerror_set_ignore(FALSE);
3017 if (self->focus_notify) {
3019 ce.xclient.type = ClientMessage;
3020 ce.xclient.message_type = prop_atoms.wm_protocols;
3021 ce.xclient.display = ob_display;
3022 ce.xclient.window = self->window;
3023 ce.xclient.format = 32;
3024 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
3025 ce.xclient.data.l[1] = event_curtime;
3026 ce.xclient.data.l[2] = 0l;
3027 ce.xclient.data.l[3] = 0l;
3028 ce.xclient.data.l[4] = 0l;
3029 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
3033 ob_debug("%sively focusing %lx at %d\n",
3034 (self->can_focus ? "act" : "pass"),
3035 self->window, (gint) event_curtime);
3038 /* Cause the FocusIn to come back to us. Important for desktop switches,
3039 since otherwise we'll have no FocusIn on the queue and send it off to
3040 the focus_backup. */
3041 XSync(ob_display, FALSE);
3045 void client_activate(ObClient *self, gboolean here, gboolean user)
3047 guint32 last_time = focus_client ? focus_client->user_time : CurrentTime;
3049 /* XXX do some stuff here if user is false to determine if we really want
3050 to activate it or not (a parent or group member is currently
3053 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3055 self->window, event_curtime, last_time,
3056 (user ? "user" : "application"));
3058 if (!user && event_curtime && last_time &&
3059 !event_time_after(event_curtime, last_time))
3061 client_hilite(self, TRUE);
3063 if (client_normal(self) && screen_showing_desktop)
3064 screen_show_desktop(FALSE);
3066 client_iconify(self, FALSE, here);
3067 if (self->desktop != DESKTOP_ALL &&
3068 self->desktop != screen_desktop) {
3070 client_set_desktop(self, screen_desktop, FALSE);
3072 screen_set_desktop(self->desktop);
3073 } else if (!self->frame->visible)
3074 /* if its not visible for other reasons, then don't mess
3078 client_shade(self, FALSE);
3082 /* we do this an action here. this is rather important. this is because
3083 we want the results from the focus change to take place BEFORE we go
3084 about raising the window. when a fullscreen window loses focus, we
3085 need this or else the raise wont be able to raise above the
3086 to-lose-focus fullscreen window. */
3091 void client_raise(ObClient *self)
3093 action_run_string("Raise", self, CurrentTime);
3096 void client_lower(ObClient *self)
3098 action_run_string("Lower", self, CurrentTime);
3101 gboolean client_focused(ObClient *self)
3103 return self == focus_client;
3106 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
3109 /* si is the smallest image >= req */
3110 /* li is the largest image < req */
3111 gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
3113 if (!self->nicons) {
3114 ObClientIcon *parent = NULL;
3116 if (self->transient_for) {
3117 if (self->transient_for != OB_TRAN_GROUP)
3118 parent = client_icon_recursive(self->transient_for, w, h);
3121 for (it = self->group->members; it; it = g_slist_next(it)) {
3122 ObClient *c = it->data;
3123 if (c != self && !c->transient_for) {
3124 if ((parent = client_icon_recursive(c, w, h)))
3134 for (i = 0; i < self->nicons; ++i) {
3135 size = self->icons[i].width * self->icons[i].height;
3136 if (size < smallest && size >= (unsigned)(w * h)) {
3140 if (size > largest && size <= (unsigned)(w * h)) {
3145 if (largest == 0) /* didnt find one smaller than the requested size */
3146 return &self->icons[si];
3147 return &self->icons[li];
3150 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
3153 static ObClientIcon deficon;
3155 if (!(ret = client_icon_recursive(self, w, h))) {
3156 deficon.width = deficon.height = 48;
3157 deficon.data = ob_rr_theme->def_win_icon;
3163 /* this be mostly ripped from fvwm */
3164 ObClient *client_find_directional(ObClient *c, ObDirection dir)
3166 gint my_cx, my_cy, his_cx, his_cy;
3169 gint score, best_score;
3170 ObClient *best_client, *cur;
3176 /* first, find the centre coords of the currently focused window */
3177 my_cx = c->frame->area.x + c->frame->area.width / 2;
3178 my_cy = c->frame->area.y + c->frame->area.height / 2;
3183 for(it = g_list_first(client_list); it; it = g_list_next(it)) {
3186 /* the currently selected window isn't interesting */
3189 if (!client_normal(cur))
3191 /* using c->desktop instead of screen_desktop doesn't work if the
3192 * current window was omnipresent, hope this doesn't have any other
3194 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3198 if(!(client_focus_target(cur) == cur &&
3199 client_can_focus(cur)))
3202 /* find the centre coords of this window, from the
3203 * currently focused window's point of view */
3204 his_cx = (cur->frame->area.x - my_cx)
3205 + cur->frame->area.width / 2;
3206 his_cy = (cur->frame->area.y - my_cy)
3207 + cur->frame->area.height / 2;
3209 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
3210 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
3212 /* Rotate the diagonals 45 degrees counterclockwise.
3213 * To do this, multiply the matrix /+h +h\ with the
3214 * vector (x y). \-h +h/
3215 * h = sqrt(0.5). We can set h := 1 since absolute
3216 * distance doesn't matter here. */
3217 tx = his_cx + his_cy;
3218 his_cy = -his_cx + his_cy;
3223 case OB_DIRECTION_NORTH:
3224 case OB_DIRECTION_SOUTH:
3225 case OB_DIRECTION_NORTHEAST:
3226 case OB_DIRECTION_SOUTHWEST:
3227 offset = (his_cx < 0) ? -his_cx : his_cx;
3228 distance = ((dir == OB_DIRECTION_NORTH ||
3229 dir == OB_DIRECTION_NORTHEAST) ?
3232 case OB_DIRECTION_EAST:
3233 case OB_DIRECTION_WEST:
3234 case OB_DIRECTION_SOUTHEAST:
3235 case OB_DIRECTION_NORTHWEST:
3236 offset = (his_cy < 0) ? -his_cy : his_cy;
3237 distance = ((dir == OB_DIRECTION_WEST ||
3238 dir == OB_DIRECTION_NORTHWEST) ?
3243 /* the target must be in the requested direction */
3247 /* Calculate score for this window. The smaller the better. */
3248 score = distance + offset;
3250 /* windows more than 45 degrees off the direction are
3251 * heavily penalized and will only be chosen if nothing
3252 * else within a million pixels */
3253 if(offset > distance)
3256 if(best_score == -1 || score < best_score)
3264 void client_set_layer(ObClient *self, gint layer)
3268 self->above = FALSE;
3269 } else if (layer == 0) {
3270 self->below = self->above = FALSE;
3272 self->below = FALSE;
3275 client_calc_layer(self);
3276 client_change_state(self); /* reflect this in the state hints */
3279 void client_set_undecorated(ObClient *self, gboolean undecorated)
3281 if (self->undecorated != undecorated) {
3282 self->undecorated = undecorated;
3283 client_setup_decor_and_functions(self);
3284 /* Make sure the client knows it might have moved. Maybe there is a
3285 * better way of doing this so only one client_configure is sent, but
3286 * since 125 of these are sent per second when moving the window (with
3287 * user = FALSE) i doubt it matters much.
3289 client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
3290 self->area.width, self->area.height, TRUE, TRUE);
3291 client_change_state(self); /* reflect this in the state hints */
3295 guint client_monitor(ObClient *self)
3297 return screen_find_monitor(&self->frame->area);
3300 ObClient *client_search_top_parent(ObClient *self)
3302 while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
3303 self = self->transient_for;
3307 GSList *client_search_all_top_parents(ObClient *self)
3311 /* move up the direct transient chain as far as possible */
3312 while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
3313 self = self->transient_for;
3315 if (!self->transient_for)
3316 ret = g_slist_prepend(ret, self);
3320 g_assert(self->group);
3322 for (it = self->group->members; it; it = g_slist_next(it)) {
3323 ObClient *c = it->data;
3325 if (!c->transient_for && client_normal(c))
3326 ret = g_slist_prepend(ret, c);
3329 if (ret == NULL) /* no group parents */
3330 ret = g_slist_prepend(ret, self);
3336 ObClient *client_search_focus_parent(ObClient *self)
3338 if (self->transient_for) {
3339 if (self->transient_for != OB_TRAN_GROUP) {
3340 if (client_focused(self->transient_for))
3341 return self->transient_for;
3345 for (it = self->group->members; it; it = g_slist_next(it)) {
3346 ObClient *c = it->data;
3348 /* checking transient_for prevents infinate loops! */
3349 if (c != self && !c->transient_for)
3350 if (client_focused(c))
3359 ObClient *client_search_parent(ObClient *self, ObClient *search)
3361 if (self->transient_for) {
3362 if (self->transient_for != OB_TRAN_GROUP) {
3363 if (self->transient_for == search)
3368 for (it = self->group->members; it; it = g_slist_next(it)) {
3369 ObClient *c = it->data;
3371 /* checking transient_for prevents infinate loops! */
3372 if (c != self && !c->transient_for)
3382 ObClient *client_search_transient(ObClient *self, ObClient *search)
3386 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3387 if (sit->data == search)
3389 if (client_search_transient(sit->data, search))
3395 void client_update_sm_client_id(ObClient *self)
3397 g_free(self->sm_client_id);
3398 self->sm_client_id = NULL;
3400 if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
3402 PROP_GETS(self->group->leader, sm_client_id, locale,
3403 &self->sm_client_id);
3406 #define WANT_EDGE(cur, c) \
3409 if(!client_normal(cur)) \
3411 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3415 if(cur->layer < c->layer && !config_resist_layers_below) \
3418 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3419 if ((his_edge_start >= my_edge_start && \
3420 his_edge_start <= my_edge_end) || \
3421 (my_edge_start >= his_edge_start && \
3422 my_edge_start <= his_edge_end)) \
3425 /* finds the nearest edge in the given direction from the current client
3426 * note to self: the edge is the -frame- edge (the actual one), not the
3429 gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang)
3431 gint dest, monitor_dest;
3432 gint my_edge_start, my_edge_end, my_offset;
3439 a = screen_area(c->desktop);
3440 monitor = screen_area_monitor(c->desktop, client_monitor(c));
3443 case OB_DIRECTION_NORTH:
3444 my_edge_start = c->frame->area.x;
3445 my_edge_end = c->frame->area.x + c->frame->area.width;
3446 my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0);
3448 /* default: top of screen */
3449 dest = a->y + (hang ? c->frame->area.height : 0);
3450 monitor_dest = monitor->y + (hang ? c->frame->area.height : 0);
3451 /* if the monitor edge comes before the screen edge, */
3452 /* use that as the destination instead. (For xinerama) */
3453 if (monitor_dest != dest && my_offset > monitor_dest)
3454 dest = monitor_dest;
3456 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3457 gint his_edge_start, his_edge_end, his_offset;
3458 ObClient *cur = it->data;
3462 his_edge_start = cur->frame->area.x;
3463 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3464 his_offset = cur->frame->area.y +
3465 (hang ? 0 : cur->frame->area.height);
3467 if(his_offset + 1 > my_offset)
3470 if(his_offset < dest)
3473 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3476 case OB_DIRECTION_SOUTH:
3477 my_edge_start = c->frame->area.x;
3478 my_edge_end = c->frame->area.x + c->frame->area.width;
3479 my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height);
3481 /* default: bottom of screen */
3482 dest = a->y + a->height - (hang ? c->frame->area.height : 0);
3483 monitor_dest = monitor->y + monitor->height -
3484 (hang ? c->frame->area.height : 0);
3485 /* if the monitor edge comes before the screen edge, */
3486 /* use that as the destination instead. (For xinerama) */
3487 if (monitor_dest != dest && my_offset < monitor_dest)
3488 dest = monitor_dest;
3490 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3491 gint his_edge_start, his_edge_end, his_offset;
3492 ObClient *cur = it->data;
3496 his_edge_start = cur->frame->area.x;
3497 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3498 his_offset = cur->frame->area.y +
3499 (hang ? cur->frame->area.height : 0);
3502 if(his_offset - 1 < my_offset)
3505 if(his_offset > dest)
3508 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3511 case OB_DIRECTION_WEST:
3512 my_edge_start = c->frame->area.y;
3513 my_edge_end = c->frame->area.y + c->frame->area.height;
3514 my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0);
3516 /* default: leftmost egde of screen */
3517 dest = a->x + (hang ? c->frame->area.width : 0);
3518 monitor_dest = monitor->x + (hang ? c->frame->area.width : 0);
3519 /* if the monitor edge comes before the screen edge, */
3520 /* use that as the destination instead. (For xinerama) */
3521 if (monitor_dest != dest && my_offset > monitor_dest)
3522 dest = monitor_dest;
3524 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3525 gint his_edge_start, his_edge_end, his_offset;
3526 ObClient *cur = it->data;
3530 his_edge_start = cur->frame->area.y;
3531 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3532 his_offset = cur->frame->area.x +
3533 (hang ? 0 : cur->frame->area.width);
3535 if(his_offset + 1 > my_offset)
3538 if(his_offset < dest)
3541 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3544 case OB_DIRECTION_EAST:
3545 my_edge_start = c->frame->area.y;
3546 my_edge_end = c->frame->area.y + c->frame->area.height;
3547 my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width);
3549 /* default: rightmost edge of screen */
3550 dest = a->x + a->width - (hang ? c->frame->area.width : 0);
3551 monitor_dest = monitor->x + monitor->width -
3552 (hang ? c->frame->area.width : 0);
3553 /* if the monitor edge comes before the screen edge, */
3554 /* use that as the destination instead. (For xinerama) */
3555 if (monitor_dest != dest && my_offset < monitor_dest)
3556 dest = monitor_dest;
3558 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3559 gint his_edge_start, his_edge_end, his_offset;
3560 ObClient *cur = it->data;
3564 his_edge_start = cur->frame->area.y;
3565 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3566 his_offset = cur->frame->area.x +
3567 (hang ? cur->frame->area.width : 0);
3569 if(his_offset - 1 < my_offset)
3572 if(his_offset > dest)
3575 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3578 case OB_DIRECTION_NORTHEAST:
3579 case OB_DIRECTION_SOUTHEAST:
3580 case OB_DIRECTION_NORTHWEST:
3581 case OB_DIRECTION_SOUTHWEST:
3582 /* not implemented */
3584 g_assert_not_reached();
3585 dest = 0; /* suppress warning */
3590 ObClient* client_under_pointer()
3594 ObClient *ret = NULL;
3596 if (screen_pointer_pos(&x, &y)) {
3597 for (it = stacking_list; it; it = g_list_next(it)) {
3598 if (WINDOW_IS_CLIENT(it->data)) {
3599 ObClient *c = WINDOW_AS_CLIENT(it->data);
3600 if (c->frame->visible &&
3601 RECT_CONTAINS(c->frame->area, x, y)) {
3611 gboolean client_has_group_siblings(ObClient *self)
3613 return self->group && self->group->members->next;