1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2003 Ben Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
21 #include "startupnotify.h"
25 #include "moveresize.h"
28 #include "extensions.h"
38 #include "menuframe.h"
41 #include "render/render.h"
44 #include <X11/Xutil.h>
46 /*! The event mask to grab on client windows */
47 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
50 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
53 GList
*client_list
= NULL
;
54 GSList
*client_destructors
= NULL
;
56 static void client_get_all(ObClient
*self
);
57 static void client_toggle_border(ObClient
*self
, gboolean show
);
58 static void client_get_startup_id(ObClient
*self
);
59 static void client_get_area(ObClient
*self
);
60 static void client_get_desktop(ObClient
*self
);
61 static void client_get_state(ObClient
*self
);
62 static void client_get_shaped(ObClient
*self
);
63 static void client_get_mwm_hints(ObClient
*self
);
64 static void client_get_gravity(ObClient
*self
);
65 static void client_showhide(ObClient
*self
);
66 static void client_change_allowed_actions(ObClient
*self
);
67 static void client_change_state(ObClient
*self
);
68 static void client_apply_startup_state(ObClient
*self
);
69 static void client_restore_session_state(ObClient
*self
);
70 static void client_restore_session_stacking(ObClient
*self
);
71 static void client_urgent_notify(ObClient
*self
);
73 void client_startup(gboolean reconfig
)
80 void client_shutdown(gboolean reconfig
)
84 void client_add_destructor(GDestroyNotify func
)
86 client_destructors
= g_slist_prepend(client_destructors
, (gpointer
)func
);
89 void client_remove_destructor(GDestroyNotify func
)
91 client_destructors
= g_slist_remove(client_destructors
, (gpointer
)func
);
94 void client_set_list()
96 Window
*windows
, *win_it
;
98 guint size
= g_list_length(client_list
);
100 /* create an array of the window ids */
102 windows
= g_new(Window
, size
);
104 for (it
= client_list
; it
!= NULL
; it
= it
->next
, ++win_it
)
105 *win_it
= ((ObClient
*)it
->data
)->window
;
109 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
110 net_client_list
, window
, (guint32
*)windows
, size
);
119 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, void *data)
123 for (it = self->transients; it; it = it->next) {
124 if (!func(it->data, data)) return;
125 client_foreach_transient(it->data, func, data);
129 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, void *data)
131 if (self->transient_for) {
132 if (self->transient_for != OB_TRAN_GROUP) {
133 if (!func(self->transient_for, data)) return;
134 client_foreach_ancestor(self->transient_for, func, data);
138 for (it = self->group->members; it; it = it->next)
139 if (it->data != self &&
140 !((ObClient*)it->data)->transient_for) {
141 if (!func(it->data, data)) return;
142 client_foreach_ancestor(it->data, func, data);
149 void client_manage_all()
151 unsigned int i
, j
, nchild
;
154 XWindowAttributes attrib
;
156 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
157 &w
, &w
, &children
, &nchild
);
159 /* remove all icon windows from the list */
160 for (i
= 0; i
< nchild
; i
++) {
161 if (children
[i
] == None
) continue;
162 wmhints
= XGetWMHints(ob_display
, children
[i
]);
164 if ((wmhints
->flags
& IconWindowHint
) &&
165 (wmhints
->icon_window
!= children
[i
]))
166 for (j
= 0; j
< nchild
; j
++)
167 if (children
[j
] == wmhints
->icon_window
) {
175 for (i
= 0; i
< nchild
; ++i
) {
176 if (children
[i
] == None
)
178 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
179 if (attrib
.override_redirect
) continue;
181 if (attrib
.map_state
!= IsUnmapped
)
182 client_manage(children
[i
]);
188 void client_manage(Window window
)
192 XWindowAttributes attrib
;
193 XSetWindowAttributes attrib_set
;
195 gboolean activate
= FALSE
;
199 /* check if it has already been unmapped by the time we started mapping
200 the grab does a sync so we don't have to here */
201 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
202 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
)) {
203 XPutBackEvent(ob_display
, &e
);
206 return; /* don't manage it */
209 /* make sure it isn't an override-redirect window */
210 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
211 attrib
.override_redirect
) {
213 return; /* don't manage it */
216 /* is the window a docking app */
217 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
218 if ((wmhint
->flags
& StateHint
) &&
219 wmhint
->initial_state
== WithdrawnState
) {
220 dock_add(window
, wmhint
);
228 ob_debug("Managing window: %lx\n", window
);
230 /* choose the events we want to receive on the CLIENT window */
231 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
232 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
233 XChangeWindowAttributes(ob_display
, window
,
234 CWEventMask
|CWDontPropagate
, &attrib_set
);
237 /* create the ObClient struct, and populate it from the hints on the
239 self
= g_new0(ObClient
, 1);
240 self
->obwin
.type
= Window_Client
;
241 self
->window
= window
;
243 /* non-zero defaults */
244 self
->title_count
= 1;
245 self
->wmstate
= NormalState
;
247 self
->desktop
= screen_num_desktops
; /* always an invalid value */
249 client_get_all(self
);
250 client_restore_session_state(self
);
252 sn_app_started(self
->class);
254 client_change_state(self
);
256 /* remove the client's border (and adjust re gravity) */
257 client_toggle_border(self
, FALSE
);
259 /* specify that if we exit, the window should not be destroyed and should
260 be reparented back to root automatically */
261 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
263 /* create the decoration frame for the client window */
264 self
->frame
= frame_new();
266 frame_grab_client(self
->frame
, self
);
270 client_apply_startup_state(self
);
272 /* update the focus lists */
273 focus_order_add_new(self
);
275 stacking_add(CLIENT_AS_WINDOW(self
));
276 client_restore_session_stacking(self
);
278 /* focus the new window? */
279 if (ob_state() != OB_STATE_STARTING
&&
280 (config_focus_new
|| client_search_focus_parent(self
)) &&
281 /* note the check against Type_Normal/Dialog, not client_normal(self),
282 which would also include other types. in this case we want more
283 strict rules for focus */
284 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
285 self
->type
== OB_CLIENT_TYPE_DIALOG
))
289 if (self
->desktop
!= screen_desktop
) {
290 /* activate the window */
293 gboolean group_foc
= FALSE
;
298 for (it
= self
->group
->members
; it
; it
= it
->next
)
300 if (client_focused(it
->data
))
308 (!self
->transient_for
&& (!self
->group
||
309 !self
->group
->members
->next
))) ||
310 client_search_focus_tree_full(self
) ||
312 !client_normal(focus_client
))
314 /* activate the window */
321 if (ob_state() == OB_STATE_RUNNING
) {
322 int x
= self
->area
.x
, ox
= x
;
323 int y
= self
->area
.y
, oy
= y
;
325 place_client(self
, &x
, &y
);
327 /* make sure the window is visible */
328 client_find_onscreen(self
, &x
, &y
,
329 self
->frame
->area
.width
,
330 self
->frame
->area
.height
,
331 client_normal(self
));
333 if (x
!= ox
|| y
!= oy
)
334 client_move(self
, x
, y
);
337 client_showhide(self
);
339 /* use client_focus instead of client_activate cuz client_activate does
340 stuff like switch desktops etc and I'm not interested in all that when
341 a window maps since its not based on an action from the user like
342 clicking a window to activate is. so keep the new window out of the way
344 if (activate
) client_focus(self
);
346 /* client_activate does this but we aret using it so we have to do it
348 if (screen_showing_desktop
)
349 screen_show_desktop(FALSE
);
351 /* add to client list/map */
352 client_list
= g_list_append(client_list
, self
);
353 g_hash_table_insert(window_map
, &self
->window
, self
);
355 /* this has to happen after we're in the client_list */
356 screen_update_areas();
358 /* update the list hints */
361 keyboard_grab_for_client(self
, TRUE
);
362 mouse_grab_for_client(self
, TRUE
);
364 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
367 void client_unmanage_all()
369 while (client_list
!= NULL
)
370 client_unmanage(client_list
->data
);
373 void client_unmanage(ObClient
*self
)
378 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
380 g_assert(self
!= NULL
);
382 keyboard_grab_for_client(self
, FALSE
);
383 mouse_grab_for_client(self
, FALSE
);
385 /* remove the window from our save set */
386 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
388 /* we dont want events no more */
389 XSelectInput(ob_display
, self
->window
, NoEventMask
);
391 frame_hide(self
->frame
);
393 client_list
= g_list_remove(client_list
, self
);
394 stacking_remove(self
);
395 g_hash_table_remove(window_map
, &self
->window
);
397 /* update the focus lists */
398 focus_order_remove(self
);
400 /* once the client is out of the list, update the struts to remove it's
402 screen_update_areas();
404 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
405 GDestroyNotify func
= (GDestroyNotify
) it
->data
;
409 if (focus_client
== self
) {
412 /* focus the last focused window on the desktop, and ignore enter
413 events from the unmap so it doesnt mess with the focus */
414 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
415 /* remove these flags so we don't end up getting focused in the
417 self
->can_focus
= FALSE
;
418 self
->focus_notify
= FALSE
;
420 client_unfocus(self
);
423 /* tell our parent(s) that we're gone */
424 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
427 for (it
= self
->group
->members
; it
; it
= it
->next
)
428 if (it
->data
!= self
)
429 ((ObClient
*)it
->data
)->transients
=
430 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
431 } else if (self
->transient_for
) { /* transient of window */
432 self
->transient_for
->transients
=
433 g_slist_remove(self
->transient_for
->transients
, self
);
436 /* tell our transients that we're gone */
437 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
438 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
439 ((ObClient
*)it
->data
)->transient_for
= NULL
;
440 client_calc_layer(it
->data
);
444 /* remove from its group */
446 group_remove(self
->group
, self
);
450 /* give the client its border back */
451 client_toggle_border(self
, TRUE
);
453 /* reparent the window out of the frame, and free the frame */
454 frame_release_client(self
->frame
, self
);
457 if (ob_state() != OB_STATE_EXITING
) {
458 /* these values should not be persisted across a window
460 PROP_ERASE(self
->window
, net_wm_desktop
);
461 PROP_ERASE(self
->window
, net_wm_state
);
462 PROP_ERASE(self
->window
, wm_state
);
464 /* if we're left in an iconic state, the client wont be mapped. this is
465 bad, since we will no longer be managing the window on restart */
467 XMapWindow(ob_display
, self
->window
);
471 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
473 /* free all data allocated in the client struct */
474 g_slist_free(self
->transients
);
475 for (j
= 0; j
< self
->nicons
; ++j
)
476 g_free(self
->icons
[j
].data
);
477 if (self
->nicons
> 0)
480 g_free(self
->icon_title
);
484 g_free(self
->sm_client_id
);
487 /* update the list hints */
491 static void client_urgent_notify(ObClient
*self
)
494 frame_flash_start(self
->frame
);
496 frame_flash_stop(self
->frame
);
499 static void client_restore_session_state(ObClient
*self
)
503 if (!(it
= session_state_find(self
)))
506 self
->session
= it
->data
;
508 RECT_SET(self
->area
, self
->session
->x
, self
->session
->y
,
509 self
->session
->w
, self
->session
->h
);
510 self
->positioned
= TRUE
;
511 XResizeWindow(ob_display
, self
->window
,
512 self
->session
->w
, self
->session
->h
);
514 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
515 self
->session
->desktop
:
516 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
517 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
519 self
->shaded
= self
->session
->shaded
;
520 self
->iconic
= self
->session
->iconic
;
521 self
->skip_pager
= self
->session
->skip_pager
;
522 self
->skip_taskbar
= self
->session
->skip_taskbar
;
523 self
->fullscreen
= self
->session
->fullscreen
;
524 self
->above
= self
->session
->above
;
525 self
->below
= self
->session
->below
;
526 self
->max_horz
= self
->session
->max_horz
;
527 self
->max_vert
= self
->session
->max_vert
;
530 static void client_restore_session_stacking(ObClient
*self
)
534 if (!self
->session
) return;
536 it
= g_list_find(session_saved_state
, self
->session
);
537 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
540 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
541 if (session_state_cmp(it
->data
, cit
->data
))
544 client_calc_layer(self
);
545 stacking_below(CLIENT_AS_WINDOW(self
),
546 CLIENT_AS_WINDOW(cit
->data
));
552 void client_move_onscreen(ObClient
*self
, gboolean rude
)
554 int x
= self
->area
.x
;
555 int y
= self
->area
.y
;
556 if (client_find_onscreen(self
, &x
, &y
,
557 self
->frame
->area
.width
,
558 self
->frame
->area
.height
, rude
)) {
559 client_move(self
, x
, y
);
563 gboolean
client_find_onscreen(ObClient
*self
, int *x
, int *y
, int w
, int h
,
567 int ox
= *x
, oy
= *y
;
569 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
572 /* XXX watch for xinerama dead areas */
574 a
= screen_area(self
->desktop
);
575 if (client_normal(self
)) {
576 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
577 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
578 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
579 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
580 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
582 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
587 /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
588 Java can suck it too. */
590 /* dont let windows map/move into the strut unless they
591 are bigger than the available area */
593 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
594 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
595 *x
= a
->x
+ a
->width
- w
;
597 if (h
<= a
->height
) {
598 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
599 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
600 *y
= a
->y
+ a
->height
- h
;
604 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
607 return ox
!= *x
|| oy
!= *y
;
610 static void client_toggle_border(ObClient
*self
, gboolean show
)
612 /* adjust our idea of where the client is, based on its border. When the
613 border is removed, the client should now be considered to be in a
615 when re-adding the border to the client, the same operation needs to be
617 int oldx
= self
->area
.x
, oldy
= self
->area
.y
;
618 int x
= oldx
, y
= oldy
;
619 switch(self
->gravity
) {
621 case NorthWestGravity
:
623 case SouthWestGravity
:
625 case NorthEastGravity
:
627 case SouthEastGravity
:
628 if (show
) x
-= self
->border_width
* 2;
629 else x
+= self
->border_width
* 2;
636 if (show
) x
-= self
->border_width
;
637 else x
+= self
->border_width
;
640 switch(self
->gravity
) {
642 case NorthWestGravity
:
644 case NorthEastGravity
:
646 case SouthWestGravity
:
648 case SouthEastGravity
:
649 if (show
) y
-= self
->border_width
* 2;
650 else y
+= self
->border_width
* 2;
657 if (show
) y
-= self
->border_width
;
658 else y
+= self
->border_width
;
665 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
667 /* move the client so it is back it the right spot _with_ its
669 if (x
!= oldx
|| y
!= oldy
)
670 XMoveWindow(ob_display
, self
->window
, x
, y
);
672 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
676 static void client_get_all(ObClient
*self
)
678 client_get_area(self
);
679 client_update_transient_for(self
);
680 client_update_wmhints(self
);
681 client_get_startup_id(self
);
682 client_get_desktop(self
);
683 client_get_state(self
);
684 client_get_shaped(self
);
686 client_get_mwm_hints(self
);
687 client_get_type(self
);/* this can change the mwmhints for special cases */
689 client_update_protocols(self
);
691 client_get_gravity(self
); /* get the attribute gravity */
692 client_update_normal_hints(self
); /* this may override the attribute
695 /* got the type, the mwmhints, the protocols, and the normal hints
696 (min/max sizes), so we're ready to set up the decorations/functions */
697 client_setup_decor_and_functions(self
);
699 client_update_title(self
);
700 client_update_class(self
);
701 client_update_sm_client_id(self
);
702 client_update_strut(self
);
703 client_update_icons(self
);
706 static void client_get_startup_id(ObClient
*self
)
708 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
710 PROP_GETS(self
->group
->leader
,
711 net_startup_id
, utf8
, &self
->startup_id
);
714 static void client_get_area(ObClient
*self
)
716 XWindowAttributes wattrib
;
719 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
720 g_assert(ret
!= BadWindow
);
722 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
723 self
->border_width
= wattrib
.border_width
;
726 static void client_get_desktop(ObClient
*self
)
728 guint32 d
= screen_num_desktops
; /* an always-invalid value */
730 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
731 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
732 self
->desktop
= screen_num_desktops
- 1;
736 gboolean trdesk
= FALSE
;
738 if (self
->transient_for
) {
739 if (self
->transient_for
!= OB_TRAN_GROUP
) {
740 self
->desktop
= self
->transient_for
->desktop
;
745 for (it
= self
->group
->members
; it
; it
= it
->next
)
746 if (it
->data
!= self
&&
747 !((ObClient
*)it
->data
)->transient_for
) {
748 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
755 /* try get from the startup-notification protocol */
756 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
757 if (self
->desktop
>= screen_num_desktops
&&
758 self
->desktop
!= DESKTOP_ALL
)
759 self
->desktop
= screen_num_desktops
- 1;
761 /* defaults to the current desktop */
762 self
->desktop
= screen_desktop
;
765 if (self
->desktop
!= d
) {
766 /* set the desktop hint, to make sure that it always exists */
767 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
771 static void client_get_state(ObClient
*self
)
776 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
778 for (i
= 0; i
< num
; ++i
) {
779 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
781 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
783 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
785 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
786 self
->skip_taskbar
= TRUE
;
787 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
788 self
->skip_pager
= TRUE
;
789 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
790 self
->fullscreen
= TRUE
;
791 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
792 self
->max_vert
= TRUE
;
793 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
794 self
->max_horz
= TRUE
;
795 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
797 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
799 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
800 self
->undecorated
= TRUE
;
807 static void client_get_shaped(ObClient
*self
)
809 self
->shaped
= FALSE
;
811 if (extensions_shape
) {
816 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
818 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
819 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
821 self
->shaped
= (s
!= 0);
826 void client_update_transient_for(ObClient
*self
)
829 ObClient
*target
= NULL
;
831 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
832 self
->transient
= TRUE
;
833 if (t
!= self
->window
) { /* cant be transient to itself! */
834 target
= g_hash_table_lookup(window_map
, &t
);
835 /* if this happens then we need to check for it*/
836 g_assert(target
!= self
);
837 if (target
&& !WINDOW_IS_CLIENT(target
)) {
838 /* this can happen when a dialog is a child of
839 a dockapp, for example */
843 if (!target
&& self
->group
) {
844 /* not transient to a client, see if it is transient for a
846 if (t
== self
->group
->leader
||
848 t
== RootWindow(ob_display
, ob_screen
))
850 /* window is a transient for its group! */
851 target
= OB_TRAN_GROUP
;
856 self
->transient
= FALSE
;
858 /* if anything has changed... */
859 if (target
!= self
->transient_for
) {
860 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
863 /* remove from old parents */
864 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
865 ObClient
*c
= it
->data
;
866 if (c
!= self
&& !c
->transient_for
)
867 c
->transients
= g_slist_remove(c
->transients
, self
);
869 } else if (self
->transient_for
!= NULL
) { /* transient of window */
870 /* remove from old parent */
871 self
->transient_for
->transients
=
872 g_slist_remove(self
->transient_for
->transients
, self
);
874 self
->transient_for
= target
;
875 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
878 /* add to new parents */
879 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
880 ObClient
*c
= it
->data
;
881 if (c
!= self
&& !c
->transient_for
)
882 c
->transients
= g_slist_append(c
->transients
, self
);
885 /* remove all transients which are in the group, that causes
886 circlular pointer hell of doom */
887 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
889 for (sit
= self
->transients
; sit
; sit
= next
) {
890 next
= g_slist_next(sit
);
891 if (sit
->data
== it
->data
)
893 g_slist_delete_link(self
->transients
, sit
);
896 } else if (self
->transient_for
!= NULL
) { /* transient of window */
897 /* add to new parent */
898 self
->transient_for
->transients
=
899 g_slist_append(self
->transient_for
->transients
, self
);
904 static void client_get_mwm_hints(ObClient
*self
)
909 self
->mwmhints
.flags
= 0; /* default to none */
911 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
913 if (num
>= OB_MWM_ELEMENTS
) {
914 self
->mwmhints
.flags
= hints
[0];
915 self
->mwmhints
.functions
= hints
[1];
916 self
->mwmhints
.decorations
= hints
[2];
922 void client_get_type(ObClient
*self
)
929 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
930 /* use the first value that we know about in the array */
931 for (i
= 0; i
< num
; ++i
) {
932 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
933 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
934 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
935 self
->type
= OB_CLIENT_TYPE_DOCK
;
936 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
937 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
938 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
939 self
->type
= OB_CLIENT_TYPE_MENU
;
940 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
941 self
->type
= OB_CLIENT_TYPE_UTILITY
;
942 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
943 self
->type
= OB_CLIENT_TYPE_SPLASH
;
944 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
945 self
->type
= OB_CLIENT_TYPE_DIALOG
;
946 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
947 self
->type
= OB_CLIENT_TYPE_NORMAL
;
948 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
949 /* prevent this window from getting any decor or
951 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
952 OB_MWM_FLAG_DECORATIONS
);
953 self
->mwmhints
.decorations
= 0;
954 self
->mwmhints
.functions
= 0;
956 if (self
->type
!= (ObClientType
) -1)
957 break; /* grab the first legit type */
962 if (self
->type
== (ObClientType
) -1) {
963 /*the window type hint was not set, which means we either classify
964 ourself as a normal window or a dialog, depending on if we are a
967 self
->type
= OB_CLIENT_TYPE_DIALOG
;
969 self
->type
= OB_CLIENT_TYPE_NORMAL
;
973 void client_update_protocols(ObClient
*self
)
978 self
->focus_notify
= FALSE
;
979 self
->delete_window
= FALSE
;
981 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
982 for (i
= 0; i
< num_return
; ++i
) {
983 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
984 /* this means we can request the window to close */
985 self
->delete_window
= TRUE
;
986 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
987 /* if this protocol is requested, then the window will be
988 notified whenever we want it to receive focus */
989 self
->focus_notify
= TRUE
;
995 static void client_get_gravity(ObClient
*self
)
997 XWindowAttributes wattrib
;
1000 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1001 g_assert(ret
!= BadWindow
);
1002 self
->gravity
= wattrib
.win_gravity
;
1005 void client_update_normal_hints(ObClient
*self
)
1009 int oldgravity
= self
->gravity
;
1012 self
->min_ratio
= 0.0f
;
1013 self
->max_ratio
= 0.0f
;
1014 SIZE_SET(self
->size_inc
, 1, 1);
1015 SIZE_SET(self
->base_size
, 0, 0);
1016 SIZE_SET(self
->min_size
, 0, 0);
1017 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1019 /* get the hints from the window */
1020 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1021 self
->positioned
= !!(size
.flags
& (PPosition
|USPosition
));
1023 if (size
.flags
& PWinGravity
) {
1024 self
->gravity
= size
.win_gravity
;
1026 /* if the client has a frame, i.e. has already been mapped and
1027 is changing its gravity */
1028 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1029 /* move our idea of the client's position based on its new
1031 self
->area
.x
= self
->frame
->area
.x
;
1032 self
->area
.y
= self
->frame
->area
.y
;
1033 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1037 if (size
.flags
& PAspect
) {
1038 if (size
.min_aspect
.y
)
1039 self
->min_ratio
= (float)size
.min_aspect
.x
/ size
.min_aspect
.y
;
1040 if (size
.max_aspect
.y
)
1041 self
->max_ratio
= (float)size
.max_aspect
.x
/ size
.max_aspect
.y
;
1044 if (size
.flags
& PMinSize
)
1045 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1047 if (size
.flags
& PMaxSize
)
1048 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1050 if (size
.flags
& PBaseSize
)
1051 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1053 if (size
.flags
& PResizeInc
)
1054 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1058 void client_setup_decor_and_functions(ObClient
*self
)
1060 /* start with everything (cept fullscreen) */
1062 (OB_FRAME_DECOR_TITLEBAR
|
1063 (ob_rr_theme
->show_handle
? OB_FRAME_DECOR_HANDLE
: 0) |
1064 OB_FRAME_DECOR_GRIPS
|
1065 OB_FRAME_DECOR_BORDER
|
1066 OB_FRAME_DECOR_ICON
|
1067 OB_FRAME_DECOR_ALLDESKTOPS
|
1068 OB_FRAME_DECOR_ICONIFY
|
1069 OB_FRAME_DECOR_MAXIMIZE
|
1070 OB_FRAME_DECOR_SHADE
);
1072 (OB_CLIENT_FUNC_RESIZE
|
1073 OB_CLIENT_FUNC_MOVE
|
1074 OB_CLIENT_FUNC_ICONIFY
|
1075 OB_CLIENT_FUNC_MAXIMIZE
|
1076 OB_CLIENT_FUNC_SHADE
);
1077 if (self
->delete_window
) {
1078 self
->functions
|= OB_CLIENT_FUNC_CLOSE
;
1079 self
->decorations
|= OB_FRAME_DECOR_CLOSE
;
1082 if (!(self
->min_size
.width
< self
->max_size
.width
||
1083 self
->min_size
.height
< self
->max_size
.height
))
1084 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1086 switch (self
->type
) {
1087 case OB_CLIENT_TYPE_NORMAL
:
1088 /* normal windows retain all of the possible decorations and
1089 functionality, and are the only windows that you can fullscreen */
1090 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1093 case OB_CLIENT_TYPE_DIALOG
:
1094 case OB_CLIENT_TYPE_UTILITY
:
1095 /* these windows cannot be maximized */
1096 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1099 case OB_CLIENT_TYPE_MENU
:
1100 case OB_CLIENT_TYPE_TOOLBAR
:
1101 /* these windows get less functionality */
1102 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1105 case OB_CLIENT_TYPE_DESKTOP
:
1106 case OB_CLIENT_TYPE_DOCK
:
1107 case OB_CLIENT_TYPE_SPLASH
:
1108 /* none of these windows are manipulated by the window manager */
1109 self
->decorations
= 0;
1110 self
->functions
= 0;
1114 /* Mwm Hints are applied subtractively to what has already been chosen for
1115 decor and functionality */
1116 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1117 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1118 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1119 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1120 /* if the mwm hints request no handle or title, then all
1121 decorations are disabled */
1122 self
->decorations
= 0;
1126 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1127 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1128 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1129 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1130 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1131 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1132 /* dont let mwm hints kill any buttons
1133 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1134 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1135 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1136 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1138 /* dont let mwm hints kill the close button
1139 if (! (self->mwmhints.functions & MwmFunc_Close))
1140 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1144 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1145 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1146 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1147 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1148 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1149 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1151 /* can't maximize without moving/resizing */
1152 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1153 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1154 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1155 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1156 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1159 /* kill the handle on fully maxed windows */
1160 if (self
->max_vert
&& self
->max_horz
)
1161 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1163 /* finally, the user can have requested no decorations, which overrides
1165 if (self
->undecorated
)
1166 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1168 /* if we don't have a titlebar, then we cannot shade! */
1169 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1170 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1172 /* now we need to check against rules for the client's current state */
1173 if (self
->fullscreen
) {
1174 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1175 OB_CLIENT_FUNC_FULLSCREEN
|
1176 OB_CLIENT_FUNC_ICONIFY
);
1177 self
->decorations
= 0;
1180 client_change_allowed_actions(self
);
1183 /* adjust the client's decorations, etc. */
1184 client_reconfigure(self
);
1186 /* this makes sure that these windows appear on all desktops */
1187 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
&&
1188 self
->desktop
!= DESKTOP_ALL
)
1190 self
->desktop
= DESKTOP_ALL
;
1195 static void client_change_allowed_actions(ObClient
*self
)
1200 /* desktop windows are kept on all desktops */
1201 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1202 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1204 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1205 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1206 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1207 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1208 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1209 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1210 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1211 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1212 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1213 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1214 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1215 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1216 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1217 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1218 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1221 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1223 /* make sure the window isn't breaking any rules now */
1225 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1226 if (self
->frame
) client_shade(self
, FALSE
);
1227 else self
->shaded
= FALSE
;
1229 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1230 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1231 else self
->iconic
= FALSE
;
1233 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1234 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1235 else self
->fullscreen
= FALSE
;
1237 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1239 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1240 else self
->max_vert
= self
->max_horz
= FALSE
;
1244 void client_reconfigure(ObClient
*self
)
1246 /* by making this pass FALSE for user, we avoid the emacs event storm where
1247 every configurenotify causes an update in its normal hints, i think this
1248 is generally what we want anyways... */
1249 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1250 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1253 void client_update_wmhints(ObClient
*self
)
1256 gboolean ur
= FALSE
;
1259 /* assume a window takes input if it doesnt specify */
1260 self
->can_focus
= TRUE
;
1262 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1263 if (hints
->flags
& InputHint
)
1264 self
->can_focus
= hints
->input
;
1266 /* only do this when first managing the window *AND* when we aren't
1268 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1269 if (hints
->flags
& StateHint
)
1270 self
->iconic
= hints
->initial_state
== IconicState
;
1272 if (hints
->flags
& XUrgencyHint
)
1275 if (!(hints
->flags
& WindowGroupHint
))
1276 hints
->window_group
= None
;
1278 /* did the group state change? */
1279 if (hints
->window_group
!=
1280 (self
->group
? self
->group
->leader
: None
)) {
1281 /* remove from the old group if there was one */
1282 if (self
->group
!= NULL
) {
1283 /* remove transients of the group */
1284 for (it
= self
->group
->members
; it
; it
= it
->next
)
1285 self
->transients
= g_slist_remove(self
->transients
,
1288 /* remove myself from parents in the group */
1289 if (self
->transient_for
== OB_TRAN_GROUP
) {
1290 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1291 ObClient
*c
= it
->data
;
1293 if (c
!= self
&& !c
->transient_for
)
1294 c
->transients
= g_slist_remove(c
->transients
,
1299 group_remove(self
->group
, self
);
1302 if (hints
->window_group
!= None
) {
1303 self
->group
= group_add(hints
->window_group
, self
);
1305 /* i can only have transients from the group if i am not
1307 if (!self
->transient_for
) {
1308 /* add other transients of the group that are already
1310 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1311 ObClient
*c
= it
->data
;
1312 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1314 g_slist_append(self
->transients
, c
);
1319 /* because the self->transient flag wont change from this call,
1320 we don't need to update the window's type and such, only its
1321 transient_for, and the transients lists of other windows in
1322 the group may be affected */
1323 client_update_transient_for(self
);
1326 /* the WM_HINTS can contain an icon */
1327 client_update_icons(self
);
1332 if (ur
!= self
->urgent
) {
1334 /* fire the urgent callback if we're mapped, otherwise, wait until
1335 after we're mapped */
1337 client_urgent_notify(self
);
1341 void client_update_title(ObClient
*self
)
1347 gboolean read_title
;
1350 old_title
= self
->title
;
1353 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
))
1354 /* try old x stuff */
1355 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
))
1356 data
= g_strdup("Unnamed Window");
1358 /* did the title change? then reset the title_count */
1359 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1360 self
->title_count
= 1;
1362 /* look for duplicates and append a number */
1364 for (it
= client_list
; it
; it
= it
->next
)
1365 if (it
->data
!= self
) {
1366 ObClient
*c
= it
->data
;
1367 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1368 nums
|= 1 << c
->title_count
;
1370 /* find first free number */
1371 for (i
= 1; i
<= 32; ++i
)
1372 if (!(nums
& (1 << i
))) {
1373 if (self
->title_count
== 1 || i
== 1)
1374 self
->title_count
= i
;
1377 /* dont display the number for the first window */
1378 if (self
->title_count
> 1) {
1380 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1385 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1390 frame_adjust_title(self
->frame
);
1394 /* update the icon title */
1396 g_free(self
->icon_title
);
1400 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1401 /* try old x stuff */
1402 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1403 data
= g_strdup(self
->title
);
1407 /* append the title count, dont display the number for the first window */
1408 if (read_title
&& self
->title_count
> 1) {
1409 char *vdata
, *ndata
;
1410 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1411 vdata
= g_strconcat(data
, ndata
, NULL
);
1417 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1419 self
->icon_title
= data
;
1422 void client_update_class(ObClient
*self
)
1427 if (self
->name
) g_free(self
->name
);
1428 if (self
->class) g_free(self
->class);
1429 if (self
->role
) g_free(self
->role
);
1431 self
->name
= self
->class = self
->role
= NULL
;
1433 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1435 self
->name
= g_strdup(data
[0]);
1437 self
->class = g_strdup(data
[1]);
1442 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1445 if (self
->name
== NULL
) self
->name
= g_strdup("");
1446 if (self
->class == NULL
) self
->class = g_strdup("");
1447 if (self
->role
== NULL
) self
->role
= g_strdup("");
1450 void client_update_strut(ObClient
*self
)
1454 gboolean got
= FALSE
;
1457 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1461 STRUT_PARTIAL_SET(strut
,
1462 data
[0], data
[2], data
[1], data
[3],
1463 data
[4], data
[5], data
[8], data
[9],
1464 data
[6], data
[7], data
[10], data
[11]);
1470 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1473 STRUT_PARTIAL_SET(strut
,
1474 data
[0], data
[2], data
[1], data
[3],
1475 0, 0, 0, 0, 0, 0, 0, 0);
1481 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1482 0, 0, 0, 0, 0, 0, 0, 0);
1484 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1485 self
->strut
= strut
;
1487 /* updating here is pointless while we're being mapped cuz we're not in
1488 the client list yet */
1490 screen_update_areas();
1494 void client_update_icons(ObClient
*self
)
1500 for (i
= 0; i
< self
->nicons
; ++i
)
1501 g_free(self
->icons
[i
].data
);
1502 if (self
->nicons
> 0)
1503 g_free(self
->icons
);
1506 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1507 /* figure out how many valid icons are in here */
1509 while (num
- i
> 2) {
1513 if (i
> num
|| w
*h
== 0) break;
1517 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1519 /* store the icons */
1521 for (j
= 0; j
< self
->nicons
; ++j
) {
1524 w
= self
->icons
[j
].width
= data
[i
++];
1525 h
= self
->icons
[j
].height
= data
[i
++];
1527 if (w
*h
== 0) continue;
1529 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1530 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1535 self
->icons
[j
].data
[t
] =
1536 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1537 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1538 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1539 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1545 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1546 kwm_win_icon
, &data
, &num
)) {
1549 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1550 xerror_set_ignore(TRUE
);
1551 if (!RrPixmapToRGBA(ob_rr_inst
,
1553 &self
->icons
[self
->nicons
-1].width
,
1554 &self
->icons
[self
->nicons
-1].height
,
1555 &self
->icons
[self
->nicons
-1].data
)) {
1556 g_free(&self
->icons
[self
->nicons
-1]);
1559 xerror_set_ignore(FALSE
);
1565 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1566 if (hints
->flags
& IconPixmapHint
) {
1568 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1569 xerror_set_ignore(TRUE
);
1570 if (!RrPixmapToRGBA(ob_rr_inst
,
1572 (hints
->flags
& IconMaskHint
?
1573 hints
->icon_mask
: None
),
1574 &self
->icons
[self
->nicons
-1].width
,
1575 &self
->icons
[self
->nicons
-1].height
,
1576 &self
->icons
[self
->nicons
-1].data
)){
1577 g_free(&self
->icons
[self
->nicons
-1]);
1580 xerror_set_ignore(FALSE
);
1586 if (!self
->nicons
) {
1588 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1589 self
->icons
[self
->nicons
-1].width
= 48;
1590 self
->icons
[self
->nicons
-1].height
= 48;
1591 self
->icons
[self
->nicons
-1].data
= g_memdup(ob_rr_theme
->def_win_icon
,
1597 frame_adjust_icon(self
->frame
);
1600 static void client_change_state(ObClient
*self
)
1603 guint32 netstate
[11];
1606 state
[0] = self
->wmstate
;
1608 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1612 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1614 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1616 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1617 if (self
->skip_taskbar
)
1618 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1619 if (self
->skip_pager
)
1620 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1621 if (self
->fullscreen
)
1622 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1624 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1626 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1628 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1630 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1631 if (self
->undecorated
)
1632 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1633 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1635 client_calc_layer(self
);
1638 frame_adjust_state(self
->frame
);
1641 ObClient
*client_search_focus_tree(ObClient
*self
)
1646 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
1647 if (client_focused(it
->data
)) return it
->data
;
1648 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1653 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1655 if (self
->transient_for
) {
1656 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1657 return client_search_focus_tree_full(self
->transient_for
);
1660 gboolean recursed
= FALSE
;
1662 for (it
= self
->group
->members
; it
; it
= it
->next
)
1663 if (!((ObClient
*)it
->data
)->transient_for
) {
1665 if ((c
= client_search_focus_tree_full(it
->data
)))
1674 /* this function checks the whole tree, the client_search_focus_tree~
1675 does not, so we need to check this window */
1676 if (client_focused(self
))
1678 return client_search_focus_tree(self
);
1681 static ObStackingLayer
calc_layer(ObClient
*self
)
1685 if (self
->fullscreen
&&
1686 (client_focused(self
) || client_search_focus_tree(self
)))
1687 l
= OB_STACKING_LAYER_FULLSCREEN
;
1688 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1689 l
= OB_STACKING_LAYER_DESKTOP
;
1690 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1691 if (!self
->below
) l
= OB_STACKING_LAYER_TOP
;
1692 else l
= OB_STACKING_LAYER_NORMAL
;
1694 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1695 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1696 else l
= OB_STACKING_LAYER_NORMAL
;
1701 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1702 ObStackingLayer l
, gboolean raised
)
1704 ObStackingLayer old
, own
;
1708 own
= calc_layer(self
);
1709 self
->layer
= l
> own
? l
: own
;
1711 for (it
= self
->transients
; it
; it
= it
->next
)
1712 client_calc_layer_recursive(it
->data
, orig
,
1713 l
, raised
? raised
: l
!= old
);
1715 if (!raised
&& l
!= old
)
1716 if (orig
->frame
) { /* only restack if the original window is managed */
1717 /* XXX add_non_intrusive ever? */
1718 stacking_remove(CLIENT_AS_WINDOW(self
));
1719 stacking_add(CLIENT_AS_WINDOW(self
));
1723 void client_calc_layer(ObClient
*self
)
1730 /* transients take on the layer of their parents */
1731 self
= client_search_top_transient(self
);
1733 l
= calc_layer(self
);
1735 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1738 gboolean
client_should_show(ObClient
*self
)
1740 if (self
->iconic
) return FALSE
;
1741 else if (!(self
->desktop
== screen_desktop
||
1742 self
->desktop
== DESKTOP_ALL
)) return FALSE
;
1743 else if (client_normal(self
) && screen_showing_desktop
) return FALSE
;
1748 static void client_showhide(ObClient
*self
)
1751 if (client_should_show(self
))
1752 frame_show(self
->frame
);
1754 frame_hide(self
->frame
);
1757 gboolean
client_normal(ObClient
*self
) {
1758 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1759 self
->type
== OB_CLIENT_TYPE_DOCK
||
1760 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1763 static void client_apply_startup_state(ObClient
*self
)
1765 /* these are in a carefully crafted order.. */
1768 self
->iconic
= FALSE
;
1769 client_iconify(self
, TRUE
, FALSE
);
1771 if (self
->fullscreen
) {
1772 self
->fullscreen
= FALSE
;
1773 client_fullscreen(self
, TRUE
, FALSE
);
1775 if (self
->undecorated
) {
1776 self
->undecorated
= FALSE
;
1777 client_set_undecorated(self
, TRUE
);
1780 self
->shaded
= FALSE
;
1781 client_shade(self
, TRUE
);
1784 client_urgent_notify(self
);
1786 if (self
->max_vert
&& self
->max_horz
) {
1787 self
->max_vert
= self
->max_horz
= FALSE
;
1788 client_maximize(self
, TRUE
, 0, FALSE
);
1789 } else if (self
->max_vert
) {
1790 self
->max_vert
= FALSE
;
1791 client_maximize(self
, TRUE
, 2, FALSE
);
1792 } else if (self
->max_horz
) {
1793 self
->max_horz
= FALSE
;
1794 client_maximize(self
, TRUE
, 1, FALSE
);
1797 /* nothing to do for the other states:
1806 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1807 int x
, int y
, int w
, int h
,
1808 gboolean user
, gboolean final
,
1809 gboolean force_reply
)
1812 gboolean send_resize_client
;
1813 gboolean moved
= FALSE
, resized
= FALSE
;
1814 guint fdecor
= self
->frame
->decorations
;
1815 gboolean fhorz
= self
->frame
->max_horz
;
1817 /* make the frame recalculate its dimentions n shit without changing
1818 anything visible for real, this way the constraints below can work with
1819 the updated frame dimensions. */
1820 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
1822 /* gets the frame's position */
1823 frame_client_gravity(self
->frame
, &x
, &y
);
1825 /* these positions are frame positions, not client positions */
1827 /* set the size and position if fullscreen */
1828 if (self
->fullscreen
) {
1831 XF86VidModeModeLine mode
;
1836 i
= client_monitor(self
);
1837 a
= screen_physical_area_monitor(i
);
1840 if (i
== 0 && /* primary head */
1841 extensions_vidmode
&&
1842 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
1843 /* get the mode last so the mode.privsize isnt freed incorrectly */
1844 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
1849 if (mode
.privsize
) XFree(mode
.private);
1859 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
1863 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
1865 /* set the size and position if maximized */
1866 if (self
->max_horz
) {
1868 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
1870 if (self
->max_vert
) {
1872 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
1876 /* gets the client's position */
1877 frame_frame_gravity(self
->frame
, &x
, &y
);
1879 /* these override the above states! if you cant move you can't move! */
1881 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
1885 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
1886 w
= self
->area
.width
;
1887 h
= self
->area
.height
;
1891 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
1892 int basew
, baseh
, minw
, minh
;
1894 /* base size is substituted with min size if not specified */
1895 if (self
->base_size
.width
|| self
->base_size
.height
) {
1896 basew
= self
->base_size
.width
;
1897 baseh
= self
->base_size
.height
;
1899 basew
= self
->min_size
.width
;
1900 baseh
= self
->min_size
.height
;
1902 /* min size is substituted with base size if not specified */
1903 if (self
->min_size
.width
|| self
->min_size
.height
) {
1904 minw
= self
->min_size
.width
;
1905 minh
= self
->min_size
.height
;
1907 minw
= self
->base_size
.width
;
1908 minh
= self
->base_size
.height
;
1911 /* if this is a user-requested resize, then check against min/max
1914 /* smaller than min size or bigger than max size? */
1915 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
1916 if (w
< minw
) w
= minw
;
1917 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
1918 if (h
< minh
) h
= minh
;
1923 /* keep to the increments */
1924 w
/= self
->size_inc
.width
;
1925 h
/= self
->size_inc
.height
;
1927 /* you cannot resize to nothing */
1928 if (basew
+ w
< 1) w
= 1 - basew
;
1929 if (baseh
+ h
< 1) h
= 1 - baseh
;
1931 /* store the logical size */
1932 SIZE_SET(self
->logical_size
,
1933 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
1934 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
1936 w
*= self
->size_inc
.width
;
1937 h
*= self
->size_inc
.height
;
1942 /* adjust the height to match the width for the aspect ratios.
1943 for this, min size is not substituted for base size ever. */
1944 w
-= self
->base_size
.width
;
1945 h
-= self
->base_size
.height
;
1947 if (self
->min_ratio
)
1948 if (h
* self
->min_ratio
> w
) h
= (int)(w
/ self
->min_ratio
);
1949 if (self
->max_ratio
)
1950 if (h
* self
->max_ratio
< w
) h
= (int)(w
/ self
->max_ratio
);
1952 w
+= self
->base_size
.width
;
1953 h
+= self
->base_size
.height
;
1957 case OB_CORNER_TOPLEFT
:
1959 case OB_CORNER_TOPRIGHT
:
1960 x
-= w
- self
->area
.width
;
1962 case OB_CORNER_BOTTOMLEFT
:
1963 y
-= h
- self
->area
.height
;
1965 case OB_CORNER_BOTTOMRIGHT
:
1966 x
-= w
- self
->area
.width
;
1967 y
-= h
- self
->area
.height
;
1971 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
1972 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
1974 oldw
= self
->area
.width
;
1975 oldh
= self
->area
.height
;
1976 RECT_SET(self
->area
, x
, y
, w
, h
);
1978 /* for app-requested resizes, always resize if 'resized' is true.
1979 for user-requested ones, only resize if final is true, or when
1980 resizing in redraw mode */
1981 send_resize_client
= ((!user
&& resized
) ||
1983 (resized
&& config_redraw_resize
))));
1985 /* if the client is enlarging, the resize the client before the frame */
1986 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
1987 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
1989 /* move/resize the frame to match the request */
1991 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
1992 moved
= resized
= TRUE
;
1994 if (moved
|| resized
)
1995 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
1997 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2000 event
.type
= ConfigureNotify
;
2001 event
.xconfigure
.display
= ob_display
;
2002 event
.xconfigure
.event
= self
->window
;
2003 event
.xconfigure
.window
= self
->window
;
2005 /* root window real coords */
2006 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2008 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2010 event
.xconfigure
.width
= w
;
2011 event
.xconfigure
.height
= h
;
2012 event
.xconfigure
.border_width
= 0;
2013 event
.xconfigure
.above
= self
->frame
->plate
;
2014 event
.xconfigure
.override_redirect
= FALSE
;
2015 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2016 FALSE
, StructureNotifyMask
, &event
);
2020 /* if the client is shrinking, then resize the frame before the client */
2021 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2022 XResizeWindow(ob_display
, self
->window
, w
, h
);
2027 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2031 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2032 self
->fullscreen
== fs
) return; /* already done */
2034 self
->fullscreen
= fs
;
2035 client_change_state(self
); /* change the state hints on the client,
2036 and adjust out layer/stacking */
2040 self
->pre_fullscreen_area
= self
->area
;
2042 /* these are not actually used cuz client_configure will set them
2043 as appropriate when the window is fullscreened */
2048 if (self
->pre_fullscreen_area
.width
> 0 &&
2049 self
->pre_fullscreen_area
.height
> 0)
2051 x
= self
->pre_fullscreen_area
.x
;
2052 y
= self
->pre_fullscreen_area
.y
;
2053 w
= self
->pre_fullscreen_area
.width
;
2054 h
= self
->pre_fullscreen_area
.height
;
2055 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2057 /* pick some fallbacks... */
2058 a
= screen_area_monitor(self
->desktop
, 0);
2059 x
= a
->x
+ a
->width
/ 4;
2060 y
= a
->y
+ a
->height
/ 4;
2066 client_setup_decor_and_functions(self
);
2068 client_move_resize(self
, x
, y
, w
, h
);
2070 /* try focus us when we go into fullscreen mode */
2074 static void client_iconify_recursive(ObClient
*self
,
2075 gboolean iconic
, gboolean curdesk
)
2078 gboolean changed
= FALSE
;
2081 if (self
->iconic
!= iconic
) {
2082 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2085 self
->iconic
= iconic
;
2088 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2091 old
= self
->wmstate
;
2092 self
->wmstate
= IconicState
;
2093 if (old
!= self
->wmstate
)
2094 PROP_MSG(self
->window
, kde_wm_change_state
,
2095 self
->wmstate
, 1, 0, 0);
2097 self
->ignore_unmaps
++;
2098 /* we unmap the client itself so that we can get MapRequest
2099 events, and because the ICCCM tells us to! */
2100 XUnmapWindow(ob_display
, self
->window
);
2102 /* update the focus lists.. iconic windows go to the bottom of
2103 the list, put the new iconic window at the 'top of the
2105 focus_order_to_top(self
);
2113 client_set_desktop(self
, screen_desktop
, FALSE
);
2115 old
= self
->wmstate
;
2116 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2117 if (old
!= self
->wmstate
)
2118 PROP_MSG(self
->window
, kde_wm_change_state
,
2119 self
->wmstate
, 1, 0, 0);
2121 XMapWindow(ob_display
, self
->window
);
2123 /* this puts it after the current focused window */
2124 focus_order_remove(self
);
2125 focus_order_add_new(self
);
2127 /* this is here cuz with the VIDMODE extension, the viewport can
2128 change while a fullscreen window is iconic, and when it
2129 uniconifies, it would be nice if it did so to the new position
2131 client_reconfigure(self
);
2138 client_change_state(self
);
2139 client_showhide(self
);
2140 screen_update_areas();
2143 /* iconify all transients */
2144 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2145 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2149 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2151 /* move up the transient chain as far as possible first */
2152 self
= client_search_top_transient(self
);
2154 client_iconify_recursive(client_search_top_transient(self
),
2158 void client_maximize(ObClient
*self
, gboolean max
, int dir
, gboolean savearea
)
2162 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2163 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2165 /* check if already done */
2167 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2168 if (dir
== 1 && self
->max_horz
) return;
2169 if (dir
== 2 && self
->max_vert
) return;
2171 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2172 if (dir
== 1 && !self
->max_horz
) return;
2173 if (dir
== 2 && !self
->max_vert
) return;
2176 /* we just tell it to configure in the same place and client_configure
2177 worries about filling the screen with the window */
2180 w
= self
->area
.width
;
2181 h
= self
->area
.height
;
2185 self
->pre_max_area
= self
->area
;
2189 a
= screen_area_monitor(self
->desktop
, 0);
2190 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2191 if (self
->pre_max_area
.width
> 0) {
2192 x
= self
->pre_max_area
.x
;
2193 w
= self
->pre_max_area
.width
;
2195 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2196 0, self
->pre_max_area
.height
);
2198 /* pick some fallbacks... */
2199 x
= a
->x
+ a
->width
/ 4;
2203 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2204 if (self
->pre_max_area
.height
> 0) {
2205 y
= self
->pre_max_area
.y
;
2206 h
= self
->pre_max_area
.height
;
2208 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2209 self
->pre_max_area
.width
, 0);
2211 /* pick some fallbacks... */
2212 y
= a
->y
+ a
->height
/ 4;
2218 if (dir
== 0 || dir
== 1) /* horz */
2219 self
->max_horz
= max
;
2220 if (dir
== 0 || dir
== 2) /* vert */
2221 self
->max_vert
= max
;
2223 client_change_state(self
); /* change the state hints on the client */
2225 client_setup_decor_and_functions(self
);
2227 client_move_resize(self
, x
, y
, w
, h
);
2230 void client_shade(ObClient
*self
, gboolean shade
)
2232 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2233 shade
) || /* can't shade */
2234 self
->shaded
== shade
) return; /* already done */
2236 /* when we're iconic, don't change the wmstate */
2237 if (!self
->iconic
) {
2240 old
= self
->wmstate
;
2241 self
->wmstate
= shade
? IconicState
: NormalState
;
2242 if (old
!= self
->wmstate
)
2243 PROP_MSG(self
->window
, kde_wm_change_state
,
2244 self
->wmstate
, 1, 0, 0);
2247 self
->shaded
= shade
;
2248 client_change_state(self
);
2249 /* resize the frame to just the titlebar */
2250 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2253 void client_close(ObClient
*self
)
2257 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2260 XXX: itd be cool to do timeouts and shit here for killing the client's
2262 like... if the window is around after 5 seconds, then the close button
2263 turns a nice red, and if this function is called again, the client is
2267 ce
.xclient
.type
= ClientMessage
;
2268 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2269 ce
.xclient
.display
= ob_display
;
2270 ce
.xclient
.window
= self
->window
;
2271 ce
.xclient
.format
= 32;
2272 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2273 ce
.xclient
.data
.l
[1] = event_lasttime
;
2274 ce
.xclient
.data
.l
[2] = 0l;
2275 ce
.xclient
.data
.l
[3] = 0l;
2276 ce
.xclient
.data
.l
[4] = 0l;
2277 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2280 void client_kill(ObClient
*self
)
2282 XKillClient(ob_display
, self
->window
);
2285 void client_set_desktop_recursive(ObClient
*self
,
2286 guint target
, gboolean donthide
)
2291 if (target
!= self
->desktop
) {
2293 ob_debug("Setting desktop %u\n", target
+1);
2295 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2297 /* remove from the old desktop(s) */
2298 focus_order_remove(self
);
2300 old
= self
->desktop
;
2301 self
->desktop
= target
;
2302 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2303 /* the frame can display the current desktop state */
2304 frame_adjust_state(self
->frame
);
2305 /* 'move' the window to the new desktop */
2307 client_showhide(self
);
2308 /* raise if it was not already on the desktop */
2309 if (old
!= DESKTOP_ALL
)
2311 screen_update_areas();
2313 /* add to the new desktop(s) */
2314 if (config_focus_new
)
2315 focus_order_to_top(self
);
2317 focus_order_to_bottom(self
);
2320 /* move all transients */
2321 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2322 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2326 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2328 client_set_desktop_recursive(client_search_top_transient(self
),
2332 ObClient
*client_search_modal_child(ObClient
*self
)
2337 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
2338 ObClient
*c
= it
->data
;
2339 if ((ret
= client_search_modal_child(c
))) return ret
;
2340 if (c
->modal
) return c
;
2345 gboolean
client_validate(ObClient
*self
)
2349 XSync(ob_display
, FALSE
); /* get all events on the server */
2351 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2352 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2353 XPutBackEvent(ob_display
, &e
);
2360 void client_set_wm_state(ObClient
*self
, long state
)
2362 if (state
== self
->wmstate
) return; /* no change */
2366 client_iconify(self
, TRUE
, TRUE
);
2369 client_iconify(self
, FALSE
, TRUE
);
2374 void client_set_state(ObClient
*self
, Atom action
, long data1
, long data2
)
2376 gboolean shaded
= self
->shaded
;
2377 gboolean fullscreen
= self
->fullscreen
;
2378 gboolean undecorated
= self
->undecorated
;
2379 gboolean max_horz
= self
->max_horz
;
2380 gboolean max_vert
= self
->max_vert
;
2383 if (!(action
== prop_atoms
.net_wm_state_add
||
2384 action
== prop_atoms
.net_wm_state_remove
||
2385 action
== prop_atoms
.net_wm_state_toggle
))
2386 /* an invalid action was passed to the client message, ignore it */
2389 for (i
= 0; i
< 2; ++i
) {
2390 Atom state
= i
== 0 ? data1
: data2
;
2392 if (!state
) continue;
2394 /* if toggling, then pick whether we're adding or removing */
2395 if (action
== prop_atoms
.net_wm_state_toggle
) {
2396 if (state
== prop_atoms
.net_wm_state_modal
)
2397 action
= self
->modal
? prop_atoms
.net_wm_state_remove
:
2398 prop_atoms
.net_wm_state_add
;
2399 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2400 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2401 prop_atoms
.net_wm_state_add
;
2402 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2403 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2404 prop_atoms
.net_wm_state_add
;
2405 else if (state
== prop_atoms
.net_wm_state_shaded
)
2406 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2407 prop_atoms
.net_wm_state_add
;
2408 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2409 action
= self
->skip_taskbar
?
2410 prop_atoms
.net_wm_state_remove
:
2411 prop_atoms
.net_wm_state_add
;
2412 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2413 action
= self
->skip_pager
?
2414 prop_atoms
.net_wm_state_remove
:
2415 prop_atoms
.net_wm_state_add
;
2416 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2417 action
= fullscreen
?
2418 prop_atoms
.net_wm_state_remove
:
2419 prop_atoms
.net_wm_state_add
;
2420 else if (state
== prop_atoms
.net_wm_state_above
)
2421 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2422 prop_atoms
.net_wm_state_add
;
2423 else if (state
== prop_atoms
.net_wm_state_below
)
2424 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2425 prop_atoms
.net_wm_state_add
;
2426 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2427 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2428 prop_atoms
.net_wm_state_add
;
2431 if (action
== prop_atoms
.net_wm_state_add
) {
2432 if (state
== prop_atoms
.net_wm_state_modal
) {
2433 /* XXX raise here or something? */
2435 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2437 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2439 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2441 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2442 self
->skip_taskbar
= TRUE
;
2443 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2444 self
->skip_pager
= TRUE
;
2445 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2447 } else if (state
== prop_atoms
.net_wm_state_above
) {
2449 } else if (state
== prop_atoms
.net_wm_state_below
) {
2451 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2455 } else { /* action == prop_atoms.net_wm_state_remove */
2456 if (state
== prop_atoms
.net_wm_state_modal
) {
2457 self
->modal
= FALSE
;
2458 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2460 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2462 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2464 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2465 self
->skip_taskbar
= FALSE
;
2466 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2467 self
->skip_pager
= FALSE
;
2468 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2470 } else if (state
== prop_atoms
.net_wm_state_above
) {
2471 self
->above
= FALSE
;
2472 } else if (state
== prop_atoms
.net_wm_state_below
) {
2473 self
->below
= FALSE
;
2474 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2475 undecorated
= FALSE
;
2479 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2480 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2482 if (max_horz
== max_vert
) { /* both going the same way */
2483 client_maximize(self
, max_horz
, 0, TRUE
);
2485 client_maximize(self
, max_horz
, 1, TRUE
);
2486 client_maximize(self
, max_vert
, 2, TRUE
);
2490 if (max_horz
!= self
->max_horz
)
2491 client_maximize(self
, max_horz
, 1, TRUE
);
2493 client_maximize(self
, max_vert
, 2, TRUE
);
2496 /* change fullscreen state before shading, as it will affect if the window
2498 if (fullscreen
!= self
->fullscreen
)
2499 client_fullscreen(self
, fullscreen
, TRUE
);
2500 if (shaded
!= self
->shaded
)
2501 client_shade(self
, shaded
);
2502 if (undecorated
!= self
->undecorated
)
2503 client_set_undecorated(self
, undecorated
);
2504 client_calc_layer(self
);
2505 client_change_state(self
); /* change the hint to reflect these changes */
2508 ObClient
*client_focus_target(ObClient
*self
)
2512 /* if we have a modal child, then focus it, not us */
2513 child
= client_search_modal_child(client_search_top_transient(self
));
2514 if (child
) return child
;
2518 gboolean
client_can_focus(ObClient
*self
)
2522 /* choose the correct target */
2523 self
= client_focus_target(self
);
2525 if (!self
->frame
->visible
)
2528 if (!((self
->can_focus
|| self
->focus_notify
) &&
2529 (self
->desktop
== screen_desktop
||
2530 self
->desktop
== DESKTOP_ALL
) &&
2534 /* do a check to see if the window has already been unmapped or destroyed
2535 do this intelligently while watching out for unmaps we've generated
2536 (ignore_unmaps > 0) */
2537 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2538 DestroyNotify
, &ev
)) {
2539 XPutBackEvent(ob_display
, &ev
);
2542 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2543 UnmapNotify
, &ev
)) {
2544 if (self
->ignore_unmaps
) {
2545 self
->ignore_unmaps
--;
2547 XPutBackEvent(ob_display
, &ev
);
2555 gboolean
client_focus(ObClient
*self
)
2557 /* choose the correct target */
2558 self
= client_focus_target(self
);
2560 if (!client_can_focus(self
)) {
2561 if (!self
->frame
->visible
) {
2562 /* update the focus lists */
2563 focus_order_to_top(self
);
2568 if (self
->can_focus
) {
2569 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2570 I choose to use it always, hopefully to find errors quicker, if any
2571 are left. (I hate X. I hate focus events.)
2573 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2574 #799. So now it is RevertToNone again.
2576 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2580 if (self
->focus_notify
) {
2582 ce
.xclient
.type
= ClientMessage
;
2583 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2584 ce
.xclient
.display
= ob_display
;
2585 ce
.xclient
.window
= self
->window
;
2586 ce
.xclient
.format
= 32;
2587 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2588 ce
.xclient
.data
.l
[1] = event_lasttime
;
2589 ce
.xclient
.data
.l
[2] = 0l;
2590 ce
.xclient
.data
.l
[3] = 0l;
2591 ce
.xclient
.data
.l
[4] = 0l;
2592 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2596 ob_debug("%sively focusing %lx at %d\n",
2597 (self
->can_focus
? "act" : "pass"),
2598 self
->window
, (int) event_lasttime
);
2601 /* Cause the FocusIn to come back to us. Important for desktop switches,
2602 since otherwise we'll have no FocusIn on the queue and send it off to
2603 the focus_backup. */
2604 XSync(ob_display
, FALSE
);
2608 void client_unfocus(ObClient
*self
)
2610 if (focus_client
== self
) {
2612 ob_debug("client_unfocus for %lx\n", self
->window
);
2614 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2618 void client_activate(ObClient
*self
, gboolean here
)
2620 if (client_normal(self
) && screen_showing_desktop
)
2621 screen_show_desktop(FALSE
);
2623 client_iconify(self
, FALSE
, here
);
2624 if (self
->desktop
!= DESKTOP_ALL
&&
2625 self
->desktop
!= screen_desktop
) {
2627 client_set_desktop(self
, screen_desktop
, FALSE
);
2629 screen_set_desktop(self
->desktop
);
2630 } else if (!self
->frame
->visible
)
2631 /* if its not visible for other reasons, then don't mess
2635 client_shade(self
, FALSE
);
2639 /* we do this an action here. this is rather important. this is because
2640 we want the results from the focus change to take place BEFORE we go
2641 about raising the window. when a fullscreen window loses focus, we need
2642 this or else the raise wont be able to raise above the to-lose-focus
2643 fullscreen window. */
2647 void client_raise(ObClient
*self
)
2649 action_run_string("Raise", self
);
2652 void client_lower(ObClient
*self
)
2654 action_run_string("Raise", self
);
2657 gboolean
client_focused(ObClient
*self
)
2659 return self
== focus_client
;
2662 ObClientIcon
*client_icon(ObClient
*self
, int w
, int h
)
2665 /* si is the smallest image >= req */
2666 /* li is the largest image < req */
2667 unsigned long size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2669 for (i
= 0; i
< self
->nicons
; ++i
) {
2670 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2671 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2675 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2680 if (largest
== 0) /* didnt find one smaller than the requested size */
2681 return &self
->icons
[si
];
2682 return &self
->icons
[li
];
2685 /* this be mostly ripped from fvwm */
2686 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2688 int my_cx
, my_cy
, his_cx
, his_cy
;
2691 int score
, best_score
;
2692 ObClient
*best_client
, *cur
;
2698 /* first, find the centre coords of the currently focused window */
2699 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2700 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2705 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2708 /* the currently selected window isn't interesting */
2711 if (!client_normal(cur
))
2713 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2717 if(client_focus_target(cur
) == cur
&&
2718 !(cur
->can_focus
|| cur
->focus_notify
))
2721 /* find the centre coords of this window, from the
2722 * currently focused window's point of view */
2723 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2724 + cur
->frame
->area
.width
/ 2;
2725 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2726 + cur
->frame
->area
.height
/ 2;
2728 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
2729 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
2731 /* Rotate the diagonals 45 degrees counterclockwise.
2732 * To do this, multiply the matrix /+h +h\ with the
2733 * vector (x y). \-h +h/
2734 * h = sqrt(0.5). We can set h := 1 since absolute
2735 * distance doesn't matter here. */
2736 tx
= his_cx
+ his_cy
;
2737 his_cy
= -his_cx
+ his_cy
;
2742 case OB_DIRECTION_NORTH
:
2743 case OB_DIRECTION_SOUTH
:
2744 case OB_DIRECTION_NORTHEAST
:
2745 case OB_DIRECTION_SOUTHWEST
:
2746 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
2747 distance
= ((dir
== OB_DIRECTION_NORTH
||
2748 dir
== OB_DIRECTION_NORTHEAST
) ?
2751 case OB_DIRECTION_EAST
:
2752 case OB_DIRECTION_WEST
:
2753 case OB_DIRECTION_SOUTHEAST
:
2754 case OB_DIRECTION_NORTHWEST
:
2755 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
2756 distance
= ((dir
== OB_DIRECTION_WEST
||
2757 dir
== OB_DIRECTION_NORTHWEST
) ?
2762 /* the target must be in the requested direction */
2766 /* Calculate score for this window. The smaller the better. */
2767 score
= distance
+ offset
;
2769 /* windows more than 45 degrees off the direction are
2770 * heavily penalized and will only be chosen if nothing
2771 * else within a million pixels */
2772 if(offset
> distance
)
2775 if(best_score
== -1 || score
< best_score
)
2783 void client_set_layer(ObClient
*self
, int layer
)
2787 self
->above
= FALSE
;
2788 } else if (layer
== 0) {
2789 self
->below
= self
->above
= FALSE
;
2791 self
->below
= FALSE
;
2794 client_calc_layer(self
);
2795 client_change_state(self
); /* reflect this in the state hints */
2798 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
2800 if (self
->undecorated
!= undecorated
) {
2801 self
->undecorated
= undecorated
;
2802 client_setup_decor_and_functions(self
);
2803 client_change_state(self
); /* reflect this in the state hints */
2807 guint
client_monitor(ObClient
*self
)
2811 for (i
= 0; i
< screen_num_monitors
; ++i
) {
2812 Rect
*area
= screen_physical_area_monitor(i
);
2813 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
))
2816 if (i
== screen_num_monitors
) i
= 0;
2817 g_assert(i
< screen_num_monitors
);
2821 ObClient
*client_search_top_transient(ObClient
*self
)
2823 /* move up the transient chain as far as possible */
2824 if (self
->transient_for
) {
2825 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2826 return client_search_top_transient(self
->transient_for
);
2830 g_assert(self
->group
);
2832 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2833 ObClient
*c
= it
->data
;
2835 /* checking transient_for prevents infinate loops! */
2836 if (c
!= self
&& !c
->transient_for
)
2847 ObClient
*client_search_focus_parent(ObClient
*self
)
2849 if (self
->transient_for
) {
2850 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2851 if (client_focused(self
->transient_for
))
2852 return self
->transient_for
;
2856 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2857 ObClient
*c
= it
->data
;
2859 /* checking transient_for prevents infinate loops! */
2860 if (c
!= self
&& !c
->transient_for
)
2861 if (client_focused(c
))
2870 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
2872 if (self
->transient_for
) {
2873 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2874 if (self
->transient_for
== search
)
2879 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2880 ObClient
*c
= it
->data
;
2882 /* checking transient_for prevents infinate loops! */
2883 if (c
!= self
&& !c
->transient_for
)
2893 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
2897 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
2898 if (sit
->data
== search
)
2900 if (client_search_transient(sit
->data
, search
))
2906 void client_update_sm_client_id(ObClient
*self
)
2908 g_free(self
->sm_client_id
);
2909 self
->sm_client_id
= NULL
;
2911 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
2913 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
2914 &self
->sm_client_id
);
2917 /* finds the nearest edge in the given direction from the current client
2918 * note to self: the edge is the -frame- edge (the actual one), not the
2921 int client_directional_edge_search(ObClient
*c
, ObDirection dir
)
2924 int my_edge_start
, my_edge_end
, my_offset
;
2931 a
= screen_area(c
->desktop
);
2934 case OB_DIRECTION_NORTH
:
2935 my_edge_start
= c
->frame
->area
.x
;
2936 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
2937 my_offset
= c
->frame
->area
.y
;
2939 dest
= a
->y
; /* default: top of screen */
2941 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2942 int his_edge_start
, his_edge_end
, his_offset
;
2943 ObClient
*cur
= it
->data
;
2947 if(!client_normal(cur
))
2949 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2954 his_edge_start
= cur
->frame
->area
.x
;
2955 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
2956 his_offset
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
2958 if(his_offset
+ 1 > my_offset
)
2961 if(his_offset
< dest
)
2964 if(his_edge_start
>= my_edge_start
&&
2965 his_edge_start
<= my_edge_end
)
2968 if(my_edge_start
>= his_edge_start
&&
2969 my_edge_start
<= his_edge_end
)
2974 case OB_DIRECTION_SOUTH
:
2975 my_edge_start
= c
->frame
->area
.x
;
2976 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
2977 my_offset
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
2979 dest
= a
->y
+ a
->height
; /* default: bottom of screen */
2981 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2982 int his_edge_start
, his_edge_end
, his_offset
;
2983 ObClient
*cur
= it
->data
;
2987 if(!client_normal(cur
))
2989 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2994 his_edge_start
= cur
->frame
->area
.x
;
2995 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
2996 his_offset
= cur
->frame
->area
.y
;
2999 if(his_offset
- 1 < my_offset
)
3002 if(his_offset
> dest
)
3005 if(his_edge_start
>= my_edge_start
&&
3006 his_edge_start
<= my_edge_end
)
3009 if(my_edge_start
>= his_edge_start
&&
3010 my_edge_start
<= his_edge_end
)
3015 case OB_DIRECTION_WEST
:
3016 my_edge_start
= c
->frame
->area
.y
;
3017 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3018 my_offset
= c
->frame
->area
.x
;
3020 dest
= a
->x
; /* default: leftmost egde of screen */
3022 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3023 int his_edge_start
, his_edge_end
, his_offset
;
3024 ObClient
*cur
= it
->data
;
3028 if(!client_normal(cur
))
3030 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3035 his_edge_start
= cur
->frame
->area
.y
;
3036 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3037 his_offset
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3039 if(his_offset
+ 1 > my_offset
)
3042 if(his_offset
< dest
)
3045 if(his_edge_start
>= my_edge_start
&&
3046 his_edge_start
<= my_edge_end
)
3049 if(my_edge_start
>= his_edge_start
&&
3050 my_edge_start
<= his_edge_end
)
3056 case OB_DIRECTION_EAST
:
3057 my_edge_start
= c
->frame
->area
.y
;
3058 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3059 my_offset
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3061 dest
= a
->x
+ a
->width
; /* default: rightmost edge of screen */
3063 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3064 int his_edge_start
, his_edge_end
, his_offset
;
3065 ObClient
*cur
= it
->data
;
3069 if(!client_normal(cur
))
3071 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3076 his_edge_start
= cur
->frame
->area
.y
;
3077 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3078 his_offset
= cur
->frame
->area
.x
;
3080 if(his_offset
- 1 < my_offset
)
3083 if(his_offset
> dest
)
3086 if(his_edge_start
>= my_edge_start
&&
3087 his_edge_start
<= my_edge_end
)
3090 if(my_edge_start
>= his_edge_start
&&
3091 my_edge_start
<= his_edge_end
)
3096 case OB_DIRECTION_NORTHEAST
:
3097 case OB_DIRECTION_SOUTHEAST
:
3098 case OB_DIRECTION_NORTHWEST
:
3099 case OB_DIRECTION_SOUTHWEST
:
3100 /* not implemented */
3102 g_assert_not_reached();
3107 ObClient
* client_under_pointer()
3111 ObClient
*ret
= NULL
;
3113 if (screen_pointer_pos(&x
, &y
)) {
3114 for (it
= stacking_list
; it
!= NULL
; it
= it
->next
) {
3115 if (WINDOW_IS_CLIENT(it
->data
)) {
3116 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3117 if (c
->desktop
== screen_desktop
&&
3118 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {