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
)) {
849 /* window is a transient for its group! */
850 target
= OB_TRAN_GROUP
;
855 self
->transient
= FALSE
;
857 /* if anything has changed... */
858 if (target
!= self
->transient_for
) {
859 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
862 /* remove from old parents */
863 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
864 ObClient
*c
= it
->data
;
865 if (c
!= self
&& !c
->transient_for
)
866 c
->transients
= g_slist_remove(c
->transients
, self
);
868 } else if (self
->transient_for
!= NULL
) { /* transient of window */
869 /* remove from old parent */
870 self
->transient_for
->transients
=
871 g_slist_remove(self
->transient_for
->transients
, self
);
873 self
->transient_for
= target
;
874 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
877 /* add to new parents */
878 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
879 ObClient
*c
= it
->data
;
880 if (c
!= self
&& !c
->transient_for
)
881 c
->transients
= g_slist_append(c
->transients
, self
);
884 /* remove all transients which are in the group, that causes
885 circlular pointer hell of doom */
886 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
888 for (sit
= self
->transients
; sit
; sit
= next
) {
889 next
= g_slist_next(sit
);
890 if (sit
->data
== it
->data
)
892 g_slist_delete_link(self
->transients
, sit
);
895 } else if (self
->transient_for
!= NULL
) { /* transient of window */
896 /* add to new parent */
897 self
->transient_for
->transients
=
898 g_slist_append(self
->transient_for
->transients
, self
);
903 static void client_get_mwm_hints(ObClient
*self
)
908 self
->mwmhints
.flags
= 0; /* default to none */
910 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
912 if (num
>= OB_MWM_ELEMENTS
) {
913 self
->mwmhints
.flags
= hints
[0];
914 self
->mwmhints
.functions
= hints
[1];
915 self
->mwmhints
.decorations
= hints
[2];
921 void client_get_type(ObClient
*self
)
928 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
929 /* use the first value that we know about in the array */
930 for (i
= 0; i
< num
; ++i
) {
931 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
932 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
933 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
934 self
->type
= OB_CLIENT_TYPE_DOCK
;
935 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
936 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
937 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
938 self
->type
= OB_CLIENT_TYPE_MENU
;
939 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
940 self
->type
= OB_CLIENT_TYPE_UTILITY
;
941 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
942 self
->type
= OB_CLIENT_TYPE_SPLASH
;
943 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
944 self
->type
= OB_CLIENT_TYPE_DIALOG
;
945 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
946 self
->type
= OB_CLIENT_TYPE_NORMAL
;
947 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
948 /* prevent this window from getting any decor or
950 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
951 OB_MWM_FLAG_DECORATIONS
);
952 self
->mwmhints
.decorations
= 0;
953 self
->mwmhints
.functions
= 0;
955 if (self
->type
!= (ObClientType
) -1)
956 break; /* grab the first legit type */
961 if (self
->type
== (ObClientType
) -1) {
962 /*the window type hint was not set, which means we either classify
963 ourself as a normal window or a dialog, depending on if we are a
966 self
->type
= OB_CLIENT_TYPE_DIALOG
;
968 self
->type
= OB_CLIENT_TYPE_NORMAL
;
972 void client_update_protocols(ObClient
*self
)
977 self
->focus_notify
= FALSE
;
978 self
->delete_window
= FALSE
;
980 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
981 for (i
= 0; i
< num_return
; ++i
) {
982 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
983 /* this means we can request the window to close */
984 self
->delete_window
= TRUE
;
985 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
986 /* if this protocol is requested, then the window will be
987 notified whenever we want it to receive focus */
988 self
->focus_notify
= TRUE
;
994 static void client_get_gravity(ObClient
*self
)
996 XWindowAttributes wattrib
;
999 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1000 g_assert(ret
!= BadWindow
);
1001 self
->gravity
= wattrib
.win_gravity
;
1004 void client_update_normal_hints(ObClient
*self
)
1008 int oldgravity
= self
->gravity
;
1011 self
->min_ratio
= 0.0f
;
1012 self
->max_ratio
= 0.0f
;
1013 SIZE_SET(self
->size_inc
, 1, 1);
1014 SIZE_SET(self
->base_size
, 0, 0);
1015 SIZE_SET(self
->min_size
, 0, 0);
1016 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1018 /* get the hints from the window */
1019 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1020 self
->positioned
= !!(size
.flags
& (PPosition
|USPosition
));
1022 if (size
.flags
& PWinGravity
) {
1023 self
->gravity
= size
.win_gravity
;
1025 /* if the client has a frame, i.e. has already been mapped and
1026 is changing its gravity */
1027 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1028 /* move our idea of the client's position based on its new
1030 self
->area
.x
= self
->frame
->area
.x
;
1031 self
->area
.y
= self
->frame
->area
.y
;
1032 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1036 if (size
.flags
& PAspect
) {
1037 if (size
.min_aspect
.y
)
1038 self
->min_ratio
= (float)size
.min_aspect
.x
/ size
.min_aspect
.y
;
1039 if (size
.max_aspect
.y
)
1040 self
->max_ratio
= (float)size
.max_aspect
.x
/ size
.max_aspect
.y
;
1043 if (size
.flags
& PMinSize
)
1044 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1046 if (size
.flags
& PMaxSize
)
1047 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1049 if (size
.flags
& PBaseSize
)
1050 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1052 if (size
.flags
& PResizeInc
)
1053 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1057 void client_setup_decor_and_functions(ObClient
*self
)
1059 /* start with everything (cept fullscreen) */
1061 (OB_FRAME_DECOR_TITLEBAR
|
1062 (ob_rr_theme
->show_handle
? OB_FRAME_DECOR_HANDLE
: 0) |
1063 OB_FRAME_DECOR_GRIPS
|
1064 OB_FRAME_DECOR_BORDER
|
1065 OB_FRAME_DECOR_ICON
|
1066 OB_FRAME_DECOR_ALLDESKTOPS
|
1067 OB_FRAME_DECOR_ICONIFY
|
1068 OB_FRAME_DECOR_MAXIMIZE
|
1069 OB_FRAME_DECOR_SHADE
);
1071 (OB_CLIENT_FUNC_RESIZE
|
1072 OB_CLIENT_FUNC_MOVE
|
1073 OB_CLIENT_FUNC_ICONIFY
|
1074 OB_CLIENT_FUNC_MAXIMIZE
|
1075 OB_CLIENT_FUNC_SHADE
);
1076 if (self
->delete_window
) {
1077 self
->functions
|= OB_CLIENT_FUNC_CLOSE
;
1078 self
->decorations
|= OB_FRAME_DECOR_CLOSE
;
1081 if (!(self
->min_size
.width
< self
->max_size
.width
||
1082 self
->min_size
.height
< self
->max_size
.height
))
1083 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1085 switch (self
->type
) {
1086 case OB_CLIENT_TYPE_NORMAL
:
1087 /* normal windows retain all of the possible decorations and
1088 functionality, and are the only windows that you can fullscreen */
1089 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1092 case OB_CLIENT_TYPE_DIALOG
:
1093 case OB_CLIENT_TYPE_UTILITY
:
1094 /* these windows cannot be maximized */
1095 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1098 case OB_CLIENT_TYPE_MENU
:
1099 case OB_CLIENT_TYPE_TOOLBAR
:
1100 /* these windows get less functionality */
1101 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1104 case OB_CLIENT_TYPE_DESKTOP
:
1105 case OB_CLIENT_TYPE_DOCK
:
1106 case OB_CLIENT_TYPE_SPLASH
:
1107 /* none of these windows are manipulated by the window manager */
1108 self
->decorations
= 0;
1109 self
->functions
= 0;
1113 /* Mwm Hints are applied subtractively to what has already been chosen for
1114 decor and functionality */
1115 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1116 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1117 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1118 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1119 /* if the mwm hints request no handle or title, then all
1120 decorations are disabled */
1121 self
->decorations
= 0;
1125 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1126 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1127 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1128 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1129 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1130 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1131 /* dont let mwm hints kill any buttons
1132 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1133 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1134 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1135 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1137 /* dont let mwm hints kill the close button
1138 if (! (self->mwmhints.functions & MwmFunc_Close))
1139 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1143 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1144 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1145 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1146 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1147 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1148 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1150 /* can't maximize without moving/resizing */
1151 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1152 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1153 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1154 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1155 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1158 /* kill the handle on fully maxed windows */
1159 if (self
->max_vert
&& self
->max_horz
)
1160 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1162 /* finally, the user can have requested no decorations, which overrides
1164 if (self
->undecorated
)
1165 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1167 /* if we don't have a titlebar, then we cannot shade! */
1168 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1169 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1171 /* now we need to check against rules for the client's current state */
1172 if (self
->fullscreen
) {
1173 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1174 OB_CLIENT_FUNC_FULLSCREEN
|
1175 OB_CLIENT_FUNC_ICONIFY
);
1176 self
->decorations
= 0;
1179 client_change_allowed_actions(self
);
1182 /* adjust the client's decorations, etc. */
1183 client_reconfigure(self
);
1185 /* this makes sure that these windows appear on all desktops */
1186 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
&&
1187 self
->desktop
!= DESKTOP_ALL
)
1189 self
->desktop
= DESKTOP_ALL
;
1194 static void client_change_allowed_actions(ObClient
*self
)
1199 /* desktop windows are kept on all desktops */
1200 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1201 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1203 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1204 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1205 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1206 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1207 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1208 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1209 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1210 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1211 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1212 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1213 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1214 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1215 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1216 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1217 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1220 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1222 /* make sure the window isn't breaking any rules now */
1224 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1225 if (self
->frame
) client_shade(self
, FALSE
);
1226 else self
->shaded
= FALSE
;
1228 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1229 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1230 else self
->iconic
= FALSE
;
1232 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1233 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1234 else self
->fullscreen
= FALSE
;
1236 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1238 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1239 else self
->max_vert
= self
->max_horz
= FALSE
;
1243 void client_reconfigure(ObClient
*self
)
1245 /* by making this pass FALSE for user, we avoid the emacs event storm where
1246 every configurenotify causes an update in its normal hints, i think this
1247 is generally what we want anyways... */
1248 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1249 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1252 void client_update_wmhints(ObClient
*self
)
1255 gboolean ur
= FALSE
;
1258 /* assume a window takes input if it doesnt specify */
1259 self
->can_focus
= TRUE
;
1261 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1262 if (hints
->flags
& InputHint
)
1263 self
->can_focus
= hints
->input
;
1265 /* only do this when first managing the window *AND* when we aren't
1267 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1268 if (hints
->flags
& StateHint
)
1269 self
->iconic
= hints
->initial_state
== IconicState
;
1271 if (hints
->flags
& XUrgencyHint
)
1274 if (!(hints
->flags
& WindowGroupHint
))
1275 hints
->window_group
= None
;
1277 /* did the group state change? */
1278 if (hints
->window_group
!=
1279 (self
->group
? self
->group
->leader
: None
)) {
1280 /* remove from the old group if there was one */
1281 if (self
->group
!= NULL
) {
1282 /* remove transients of the group */
1283 for (it
= self
->group
->members
; it
; it
= it
->next
)
1284 self
->transients
= g_slist_remove(self
->transients
,
1286 group_remove(self
->group
, self
);
1289 if (hints
->window_group
!= None
) {
1290 self
->group
= group_add(hints
->window_group
, self
);
1292 /* i can only have transients from the group if i am not
1294 if (!self
->transient_for
) {
1295 /* add other transients of the group that are already
1297 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1298 ObClient
*c
= it
->data
;
1299 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1301 g_slist_append(self
->transients
, c
);
1306 /* because the self->transient flag wont change from this call,
1307 we don't need to update the window's type and such, only its
1308 transient_for, and the transients lists of other windows in
1309 the group may be affected */
1310 client_update_transient_for(self
);
1313 /* the WM_HINTS can contain an icon */
1314 client_update_icons(self
);
1319 if (ur
!= self
->urgent
) {
1321 /* fire the urgent callback if we're mapped, otherwise, wait until
1322 after we're mapped */
1324 client_urgent_notify(self
);
1328 void client_update_title(ObClient
*self
)
1334 gboolean read_title
;
1337 old_title
= self
->title
;
1340 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
))
1341 /* try old x stuff */
1342 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
))
1343 data
= g_strdup("Unnamed Window");
1345 /* did the title change? then reset the title_count */
1346 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1347 self
->title_count
= 1;
1349 /* look for duplicates and append a number */
1351 for (it
= client_list
; it
; it
= it
->next
)
1352 if (it
->data
!= self
) {
1353 ObClient
*c
= it
->data
;
1354 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1355 nums
|= 1 << c
->title_count
;
1357 /* find first free number */
1358 for (i
= 1; i
<= 32; ++i
)
1359 if (!(nums
& (1 << i
))) {
1360 if (self
->title_count
== 1 || i
== 1)
1361 self
->title_count
= i
;
1364 /* dont display the number for the first window */
1365 if (self
->title_count
> 1) {
1367 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1372 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1377 frame_adjust_title(self
->frame
);
1381 /* update the icon title */
1383 g_free(self
->icon_title
);
1387 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1388 /* try old x stuff */
1389 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1390 data
= g_strdup(self
->title
);
1394 /* append the title count, dont display the number for the first window */
1395 if (read_title
&& self
->title_count
> 1) {
1396 char *vdata
, *ndata
;
1397 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1398 vdata
= g_strconcat(data
, ndata
, NULL
);
1404 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1406 self
->icon_title
= data
;
1409 void client_update_class(ObClient
*self
)
1414 if (self
->name
) g_free(self
->name
);
1415 if (self
->class) g_free(self
->class);
1416 if (self
->role
) g_free(self
->role
);
1418 self
->name
= self
->class = self
->role
= NULL
;
1420 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1422 self
->name
= g_strdup(data
[0]);
1424 self
->class = g_strdup(data
[1]);
1429 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1432 if (self
->name
== NULL
) self
->name
= g_strdup("");
1433 if (self
->class == NULL
) self
->class = g_strdup("");
1434 if (self
->role
== NULL
) self
->role
= g_strdup("");
1437 void client_update_strut(ObClient
*self
)
1441 gboolean got
= FALSE
;
1444 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1448 STRUT_PARTIAL_SET(strut
,
1449 data
[0], data
[2], data
[1], data
[3],
1450 data
[4], data
[5], data
[8], data
[9],
1451 data
[6], data
[7], data
[10], data
[11]);
1457 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1460 STRUT_PARTIAL_SET(strut
,
1461 data
[0], data
[2], data
[1], data
[3],
1462 0, 0, 0, 0, 0, 0, 0, 0);
1468 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1469 0, 0, 0, 0, 0, 0, 0, 0);
1471 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1472 self
->strut
= strut
;
1474 /* updating here is pointless while we're being mapped cuz we're not in
1475 the client list yet */
1477 screen_update_areas();
1481 void client_update_icons(ObClient
*self
)
1487 for (i
= 0; i
< self
->nicons
; ++i
)
1488 g_free(self
->icons
[i
].data
);
1489 if (self
->nicons
> 0)
1490 g_free(self
->icons
);
1493 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1494 /* figure out how many valid icons are in here */
1496 while (num
- i
> 2) {
1500 if (i
> num
|| w
*h
== 0) break;
1504 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1506 /* store the icons */
1508 for (j
= 0; j
< self
->nicons
; ++j
) {
1511 w
= self
->icons
[j
].width
= data
[i
++];
1512 h
= self
->icons
[j
].height
= data
[i
++];
1514 if (w
*h
== 0) continue;
1516 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1517 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1522 self
->icons
[j
].data
[t
] =
1523 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1524 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1525 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1526 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1532 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1533 kwm_win_icon
, &data
, &num
)) {
1536 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1537 xerror_set_ignore(TRUE
);
1538 if (!RrPixmapToRGBA(ob_rr_inst
,
1540 &self
->icons
[self
->nicons
-1].width
,
1541 &self
->icons
[self
->nicons
-1].height
,
1542 &self
->icons
[self
->nicons
-1].data
)) {
1543 g_free(&self
->icons
[self
->nicons
-1]);
1546 xerror_set_ignore(FALSE
);
1552 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1553 if (hints
->flags
& IconPixmapHint
) {
1555 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1556 xerror_set_ignore(TRUE
);
1557 if (!RrPixmapToRGBA(ob_rr_inst
,
1559 (hints
->flags
& IconMaskHint
?
1560 hints
->icon_mask
: None
),
1561 &self
->icons
[self
->nicons
-1].width
,
1562 &self
->icons
[self
->nicons
-1].height
,
1563 &self
->icons
[self
->nicons
-1].data
)){
1564 g_free(&self
->icons
[self
->nicons
-1]);
1567 xerror_set_ignore(FALSE
);
1573 if (!self
->nicons
) {
1575 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1576 self
->icons
[self
->nicons
-1].width
= 48;
1577 self
->icons
[self
->nicons
-1].height
= 48;
1578 self
->icons
[self
->nicons
-1].data
= g_memdup(ob_rr_theme
->def_win_icon
,
1584 frame_adjust_icon(self
->frame
);
1587 static void client_change_state(ObClient
*self
)
1590 guint32 netstate
[11];
1593 state
[0] = self
->wmstate
;
1595 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1599 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1601 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1603 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1604 if (self
->skip_taskbar
)
1605 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1606 if (self
->skip_pager
)
1607 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1608 if (self
->fullscreen
)
1609 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1611 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1613 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1615 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1617 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1618 if (self
->undecorated
)
1619 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1620 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1622 client_calc_layer(self
);
1625 frame_adjust_state(self
->frame
);
1628 ObClient
*client_search_focus_tree(ObClient
*self
)
1633 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
1634 if (client_focused(it
->data
)) return it
->data
;
1635 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1640 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1642 if (self
->transient_for
) {
1643 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1644 return client_search_focus_tree_full(self
->transient_for
);
1647 gboolean recursed
= FALSE
;
1649 for (it
= self
->group
->members
; it
; it
= it
->next
)
1650 if (!((ObClient
*)it
->data
)->transient_for
) {
1652 if ((c
= client_search_focus_tree_full(it
->data
)))
1661 /* this function checks the whole tree, the client_search_focus_tree~
1662 does not, so we need to check this window */
1663 if (client_focused(self
))
1665 return client_search_focus_tree(self
);
1668 static ObStackingLayer
calc_layer(ObClient
*self
)
1672 if (self
->fullscreen
&&
1673 (client_focused(self
) || client_search_focus_tree(self
)))
1674 l
= OB_STACKING_LAYER_FULLSCREEN
;
1675 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1676 l
= OB_STACKING_LAYER_DESKTOP
;
1677 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1678 if (!self
->below
) l
= OB_STACKING_LAYER_TOP
;
1679 else l
= OB_STACKING_LAYER_NORMAL
;
1681 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1682 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1683 else l
= OB_STACKING_LAYER_NORMAL
;
1688 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1689 ObStackingLayer l
, gboolean raised
)
1691 ObStackingLayer old
, own
;
1695 own
= calc_layer(self
);
1696 self
->layer
= l
> own
? l
: own
;
1698 for (it
= self
->transients
; it
; it
= it
->next
)
1699 client_calc_layer_recursive(it
->data
, orig
,
1700 l
, raised
? raised
: l
!= old
);
1702 if (!raised
&& l
!= old
)
1703 if (orig
->frame
) { /* only restack if the original window is managed */
1704 /* XXX add_non_intrusive ever? */
1705 stacking_remove(CLIENT_AS_WINDOW(self
));
1706 stacking_add(CLIENT_AS_WINDOW(self
));
1710 void client_calc_layer(ObClient
*self
)
1717 /* transients take on the layer of their parents */
1718 self
= client_search_top_transient(self
);
1720 l
= calc_layer(self
);
1722 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1725 gboolean
client_should_show(ObClient
*self
)
1727 if (self
->iconic
) return FALSE
;
1728 else if (!(self
->desktop
== screen_desktop
||
1729 self
->desktop
== DESKTOP_ALL
)) return FALSE
;
1730 else if (client_normal(self
) && screen_showing_desktop
) return FALSE
;
1735 static void client_showhide(ObClient
*self
)
1738 if (client_should_show(self
))
1739 frame_show(self
->frame
);
1741 frame_hide(self
->frame
);
1744 gboolean
client_normal(ObClient
*self
) {
1745 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1746 self
->type
== OB_CLIENT_TYPE_DOCK
||
1747 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1750 static void client_apply_startup_state(ObClient
*self
)
1752 /* these are in a carefully crafted order.. */
1755 self
->iconic
= FALSE
;
1756 client_iconify(self
, TRUE
, FALSE
);
1758 if (self
->fullscreen
) {
1759 self
->fullscreen
= FALSE
;
1760 client_fullscreen(self
, TRUE
, FALSE
);
1762 if (self
->undecorated
) {
1763 self
->undecorated
= FALSE
;
1764 client_set_undecorated(self
, TRUE
);
1767 self
->shaded
= FALSE
;
1768 client_shade(self
, TRUE
);
1771 client_urgent_notify(self
);
1773 if (self
->max_vert
&& self
->max_horz
) {
1774 self
->max_vert
= self
->max_horz
= FALSE
;
1775 client_maximize(self
, TRUE
, 0, FALSE
);
1776 } else if (self
->max_vert
) {
1777 self
->max_vert
= FALSE
;
1778 client_maximize(self
, TRUE
, 2, FALSE
);
1779 } else if (self
->max_horz
) {
1780 self
->max_horz
= FALSE
;
1781 client_maximize(self
, TRUE
, 1, FALSE
);
1784 /* nothing to do for the other states:
1793 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1794 int x
, int y
, int w
, int h
,
1795 gboolean user
, gboolean final
,
1796 gboolean force_reply
)
1799 gboolean send_resize_client
;
1800 gboolean moved
= FALSE
, resized
= FALSE
;
1801 guint fdecor
= self
->frame
->decorations
;
1802 gboolean fhorz
= self
->frame
->max_horz
;
1804 /* make the frame recalculate its dimentions n shit without changing
1805 anything visible for real, this way the constraints below can work with
1806 the updated frame dimensions. */
1807 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
1809 /* gets the frame's position */
1810 frame_client_gravity(self
->frame
, &x
, &y
);
1812 /* these positions are frame positions, not client positions */
1814 /* set the size and position if fullscreen */
1815 if (self
->fullscreen
) {
1818 XF86VidModeModeLine mode
;
1823 i
= client_monitor(self
);
1824 a
= screen_physical_area_monitor(i
);
1827 if (i
== 0 && /* primary head */
1828 extensions_vidmode
&&
1829 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
1830 /* get the mode last so the mode.privsize isnt freed incorrectly */
1831 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
1836 if (mode
.privsize
) XFree(mode
.private);
1846 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
1850 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
1852 /* set the size and position if maximized */
1853 if (self
->max_horz
) {
1855 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
1857 if (self
->max_vert
) {
1859 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
1863 /* gets the client's position */
1864 frame_frame_gravity(self
->frame
, &x
, &y
);
1866 /* these override the above states! if you cant move you can't move! */
1868 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
1872 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
1873 w
= self
->area
.width
;
1874 h
= self
->area
.height
;
1878 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
1879 int basew
, baseh
, minw
, minh
;
1881 /* base size is substituted with min size if not specified */
1882 if (self
->base_size
.width
|| self
->base_size
.height
) {
1883 basew
= self
->base_size
.width
;
1884 baseh
= self
->base_size
.height
;
1886 basew
= self
->min_size
.width
;
1887 baseh
= self
->min_size
.height
;
1889 /* min size is substituted with base size if not specified */
1890 if (self
->min_size
.width
|| self
->min_size
.height
) {
1891 minw
= self
->min_size
.width
;
1892 minh
= self
->min_size
.height
;
1894 minw
= self
->base_size
.width
;
1895 minh
= self
->base_size
.height
;
1898 /* if this is a user-requested resize, then check against min/max
1901 /* smaller than min size or bigger than max size? */
1902 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
1903 if (w
< minw
) w
= minw
;
1904 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
1905 if (h
< minh
) h
= minh
;
1910 /* keep to the increments */
1911 w
/= self
->size_inc
.width
;
1912 h
/= self
->size_inc
.height
;
1914 /* you cannot resize to nothing */
1915 if (basew
+ w
< 1) w
= 1 - basew
;
1916 if (baseh
+ h
< 1) h
= 1 - baseh
;
1918 /* store the logical size */
1919 SIZE_SET(self
->logical_size
,
1920 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
1921 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
1923 w
*= self
->size_inc
.width
;
1924 h
*= self
->size_inc
.height
;
1929 /* adjust the height to match the width for the aspect ratios.
1930 for this, min size is not substituted for base size ever. */
1931 w
-= self
->base_size
.width
;
1932 h
-= self
->base_size
.height
;
1934 if (self
->min_ratio
)
1935 if (h
* self
->min_ratio
> w
) h
= (int)(w
/ self
->min_ratio
);
1936 if (self
->max_ratio
)
1937 if (h
* self
->max_ratio
< w
) h
= (int)(w
/ self
->max_ratio
);
1939 w
+= self
->base_size
.width
;
1940 h
+= self
->base_size
.height
;
1944 case OB_CORNER_TOPLEFT
:
1946 case OB_CORNER_TOPRIGHT
:
1947 x
-= w
- self
->area
.width
;
1949 case OB_CORNER_BOTTOMLEFT
:
1950 y
-= h
- self
->area
.height
;
1952 case OB_CORNER_BOTTOMRIGHT
:
1953 x
-= w
- self
->area
.width
;
1954 y
-= h
- self
->area
.height
;
1958 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
1959 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
1961 oldw
= self
->area
.width
;
1962 oldh
= self
->area
.height
;
1963 RECT_SET(self
->area
, x
, y
, w
, h
);
1965 /* for app-requested resizes, always resize if 'resized' is true.
1966 for user-requested ones, only resize if final is true, or when
1967 resizing in redraw mode */
1968 send_resize_client
= ((!user
&& resized
) ||
1970 (resized
&& config_redraw_resize
))));
1972 /* if the client is enlarging, the resize the client before the frame */
1973 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
1974 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
1976 /* move/resize the frame to match the request */
1978 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
1979 moved
= resized
= TRUE
;
1981 if (moved
|| resized
)
1982 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
1984 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
1987 event
.type
= ConfigureNotify
;
1988 event
.xconfigure
.display
= ob_display
;
1989 event
.xconfigure
.event
= self
->window
;
1990 event
.xconfigure
.window
= self
->window
;
1992 /* root window real coords */
1993 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
1995 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
1997 event
.xconfigure
.width
= w
;
1998 event
.xconfigure
.height
= h
;
1999 event
.xconfigure
.border_width
= 0;
2000 event
.xconfigure
.above
= self
->frame
->plate
;
2001 event
.xconfigure
.override_redirect
= FALSE
;
2002 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2003 FALSE
, StructureNotifyMask
, &event
);
2007 /* if the client is shrinking, then resize the frame before the client */
2008 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2009 XResizeWindow(ob_display
, self
->window
, w
, h
);
2014 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2018 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2019 self
->fullscreen
== fs
) return; /* already done */
2021 self
->fullscreen
= fs
;
2022 client_change_state(self
); /* change the state hints on the client,
2023 and adjust out layer/stacking */
2027 self
->pre_fullscreen_area
= self
->area
;
2029 /* these are not actually used cuz client_configure will set them
2030 as appropriate when the window is fullscreened */
2035 if (self
->pre_fullscreen_area
.width
> 0 &&
2036 self
->pre_fullscreen_area
.height
> 0)
2038 x
= self
->pre_fullscreen_area
.x
;
2039 y
= self
->pre_fullscreen_area
.y
;
2040 w
= self
->pre_fullscreen_area
.width
;
2041 h
= self
->pre_fullscreen_area
.height
;
2042 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2044 /* pick some fallbacks... */
2045 a
= screen_area_monitor(self
->desktop
, 0);
2046 x
= a
->x
+ a
->width
/ 4;
2047 y
= a
->y
+ a
->height
/ 4;
2053 client_setup_decor_and_functions(self
);
2055 client_move_resize(self
, x
, y
, w
, h
);
2057 /* try focus us when we go into fullscreen mode */
2061 static void client_iconify_recursive(ObClient
*self
,
2062 gboolean iconic
, gboolean curdesk
)
2065 gboolean changed
= FALSE
;
2068 if (self
->iconic
!= iconic
) {
2069 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2072 self
->iconic
= iconic
;
2075 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2078 old
= self
->wmstate
;
2079 self
->wmstate
= IconicState
;
2080 if (old
!= self
->wmstate
)
2081 PROP_MSG(self
->window
, kde_wm_change_state
,
2082 self
->wmstate
, 1, 0, 0);
2084 self
->ignore_unmaps
++;
2085 /* we unmap the client itself so that we can get MapRequest
2086 events, and because the ICCCM tells us to! */
2087 XUnmapWindow(ob_display
, self
->window
);
2089 /* update the focus lists.. iconic windows go to the bottom of
2090 the list, put the new iconic window at the 'top of the
2092 focus_order_to_top(self
);
2100 client_set_desktop(self
, screen_desktop
, FALSE
);
2102 old
= self
->wmstate
;
2103 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2104 if (old
!= self
->wmstate
)
2105 PROP_MSG(self
->window
, kde_wm_change_state
,
2106 self
->wmstate
, 1, 0, 0);
2108 XMapWindow(ob_display
, self
->window
);
2110 /* this puts it after the current focused window */
2111 focus_order_remove(self
);
2112 focus_order_add_new(self
);
2114 /* this is here cuz with the VIDMODE extension, the viewport can
2115 change while a fullscreen window is iconic, and when it
2116 uniconifies, it would be nice if it did so to the new position
2118 client_reconfigure(self
);
2125 client_change_state(self
);
2126 client_showhide(self
);
2127 screen_update_areas();
2130 /* iconify all transients */
2131 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2132 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2136 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2138 /* move up the transient chain as far as possible first */
2139 self
= client_search_top_transient(self
);
2141 client_iconify_recursive(client_search_top_transient(self
),
2145 void client_maximize(ObClient
*self
, gboolean max
, int dir
, gboolean savearea
)
2149 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2150 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2152 /* check if already done */
2154 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2155 if (dir
== 1 && self
->max_horz
) return;
2156 if (dir
== 2 && self
->max_vert
) return;
2158 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2159 if (dir
== 1 && !self
->max_horz
) return;
2160 if (dir
== 2 && !self
->max_vert
) return;
2163 /* we just tell it to configure in the same place and client_configure
2164 worries about filling the screen with the window */
2167 w
= self
->area
.width
;
2168 h
= self
->area
.height
;
2172 self
->pre_max_area
= self
->area
;
2176 a
= screen_area_monitor(self
->desktop
, 0);
2177 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2178 if (self
->pre_max_area
.width
> 0) {
2179 x
= self
->pre_max_area
.x
;
2180 w
= self
->pre_max_area
.width
;
2182 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2183 0, self
->pre_max_area
.height
);
2185 /* pick some fallbacks... */
2186 x
= a
->x
+ a
->width
/ 4;
2190 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2191 if (self
->pre_max_area
.height
> 0) {
2192 y
= self
->pre_max_area
.y
;
2193 h
= self
->pre_max_area
.height
;
2195 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2196 self
->pre_max_area
.width
, 0);
2198 /* pick some fallbacks... */
2199 y
= a
->y
+ a
->height
/ 4;
2205 if (dir
== 0 || dir
== 1) /* horz */
2206 self
->max_horz
= max
;
2207 if (dir
== 0 || dir
== 2) /* vert */
2208 self
->max_vert
= max
;
2210 client_change_state(self
); /* change the state hints on the client */
2212 client_setup_decor_and_functions(self
);
2214 client_move_resize(self
, x
, y
, w
, h
);
2217 void client_shade(ObClient
*self
, gboolean shade
)
2219 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2220 shade
) || /* can't shade */
2221 self
->shaded
== shade
) return; /* already done */
2223 /* when we're iconic, don't change the wmstate */
2224 if (!self
->iconic
) {
2227 old
= self
->wmstate
;
2228 self
->wmstate
= shade
? IconicState
: NormalState
;
2229 if (old
!= self
->wmstate
)
2230 PROP_MSG(self
->window
, kde_wm_change_state
,
2231 self
->wmstate
, 1, 0, 0);
2234 self
->shaded
= shade
;
2235 client_change_state(self
);
2236 /* resize the frame to just the titlebar */
2237 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2240 void client_close(ObClient
*self
)
2244 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2247 XXX: itd be cool to do timeouts and shit here for killing the client's
2249 like... if the window is around after 5 seconds, then the close button
2250 turns a nice red, and if this function is called again, the client is
2254 ce
.xclient
.type
= ClientMessage
;
2255 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2256 ce
.xclient
.display
= ob_display
;
2257 ce
.xclient
.window
= self
->window
;
2258 ce
.xclient
.format
= 32;
2259 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2260 ce
.xclient
.data
.l
[1] = event_lasttime
;
2261 ce
.xclient
.data
.l
[2] = 0l;
2262 ce
.xclient
.data
.l
[3] = 0l;
2263 ce
.xclient
.data
.l
[4] = 0l;
2264 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2267 void client_kill(ObClient
*self
)
2269 XKillClient(ob_display
, self
->window
);
2272 void client_set_desktop_recursive(ObClient
*self
,
2273 guint target
, gboolean donthide
)
2278 if (target
!= self
->desktop
) {
2280 ob_debug("Setting desktop %u\n", target
+1);
2282 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2284 /* remove from the old desktop(s) */
2285 focus_order_remove(self
);
2287 old
= self
->desktop
;
2288 self
->desktop
= target
;
2289 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2290 /* the frame can display the current desktop state */
2291 frame_adjust_state(self
->frame
);
2292 /* 'move' the window to the new desktop */
2294 client_showhide(self
);
2295 /* raise if it was not already on the desktop */
2296 if (old
!= DESKTOP_ALL
)
2298 screen_update_areas();
2300 /* add to the new desktop(s) */
2301 if (config_focus_new
)
2302 focus_order_to_top(self
);
2304 focus_order_to_bottom(self
);
2307 /* move all transients */
2308 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2309 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2313 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2315 client_set_desktop_recursive(client_search_top_transient(self
),
2319 ObClient
*client_search_modal_child(ObClient
*self
)
2324 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
2325 ObClient
*c
= it
->data
;
2326 if ((ret
= client_search_modal_child(c
))) return ret
;
2327 if (c
->modal
) return c
;
2332 gboolean
client_validate(ObClient
*self
)
2336 XSync(ob_display
, FALSE
); /* get all events on the server */
2338 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2339 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2340 XPutBackEvent(ob_display
, &e
);
2347 void client_set_wm_state(ObClient
*self
, long state
)
2349 if (state
== self
->wmstate
) return; /* no change */
2353 client_iconify(self
, TRUE
, TRUE
);
2356 client_iconify(self
, FALSE
, TRUE
);
2361 void client_set_state(ObClient
*self
, Atom action
, long data1
, long data2
)
2363 gboolean shaded
= self
->shaded
;
2364 gboolean fullscreen
= self
->fullscreen
;
2365 gboolean undecorated
= self
->undecorated
;
2366 gboolean max_horz
= self
->max_horz
;
2367 gboolean max_vert
= self
->max_vert
;
2370 if (!(action
== prop_atoms
.net_wm_state_add
||
2371 action
== prop_atoms
.net_wm_state_remove
||
2372 action
== prop_atoms
.net_wm_state_toggle
))
2373 /* an invalid action was passed to the client message, ignore it */
2376 for (i
= 0; i
< 2; ++i
) {
2377 Atom state
= i
== 0 ? data1
: data2
;
2379 if (!state
) continue;
2381 /* if toggling, then pick whether we're adding or removing */
2382 if (action
== prop_atoms
.net_wm_state_toggle
) {
2383 if (state
== prop_atoms
.net_wm_state_modal
)
2384 action
= self
->modal
? prop_atoms
.net_wm_state_remove
:
2385 prop_atoms
.net_wm_state_add
;
2386 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2387 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2388 prop_atoms
.net_wm_state_add
;
2389 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2390 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2391 prop_atoms
.net_wm_state_add
;
2392 else if (state
== prop_atoms
.net_wm_state_shaded
)
2393 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2394 prop_atoms
.net_wm_state_add
;
2395 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2396 action
= self
->skip_taskbar
?
2397 prop_atoms
.net_wm_state_remove
:
2398 prop_atoms
.net_wm_state_add
;
2399 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2400 action
= self
->skip_pager
?
2401 prop_atoms
.net_wm_state_remove
:
2402 prop_atoms
.net_wm_state_add
;
2403 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2404 action
= fullscreen
?
2405 prop_atoms
.net_wm_state_remove
:
2406 prop_atoms
.net_wm_state_add
;
2407 else if (state
== prop_atoms
.net_wm_state_above
)
2408 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2409 prop_atoms
.net_wm_state_add
;
2410 else if (state
== prop_atoms
.net_wm_state_below
)
2411 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2412 prop_atoms
.net_wm_state_add
;
2413 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2414 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2415 prop_atoms
.net_wm_state_add
;
2418 if (action
== prop_atoms
.net_wm_state_add
) {
2419 if (state
== prop_atoms
.net_wm_state_modal
) {
2420 /* XXX raise here or something? */
2422 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2424 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2426 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2428 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2429 self
->skip_taskbar
= TRUE
;
2430 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2431 self
->skip_pager
= TRUE
;
2432 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2434 } else if (state
== prop_atoms
.net_wm_state_above
) {
2436 } else if (state
== prop_atoms
.net_wm_state_below
) {
2438 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2442 } else { /* action == prop_atoms.net_wm_state_remove */
2443 if (state
== prop_atoms
.net_wm_state_modal
) {
2444 self
->modal
= FALSE
;
2445 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2447 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2449 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2451 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2452 self
->skip_taskbar
= FALSE
;
2453 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2454 self
->skip_pager
= FALSE
;
2455 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2457 } else if (state
== prop_atoms
.net_wm_state_above
) {
2458 self
->above
= FALSE
;
2459 } else if (state
== prop_atoms
.net_wm_state_below
) {
2460 self
->below
= FALSE
;
2461 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2462 undecorated
= FALSE
;
2466 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2467 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2469 if (max_horz
== max_vert
) { /* both going the same way */
2470 client_maximize(self
, max_horz
, 0, TRUE
);
2472 client_maximize(self
, max_horz
, 1, TRUE
);
2473 client_maximize(self
, max_vert
, 2, TRUE
);
2477 if (max_horz
!= self
->max_horz
)
2478 client_maximize(self
, max_horz
, 1, TRUE
);
2480 client_maximize(self
, max_vert
, 2, TRUE
);
2483 /* change fullscreen state before shading, as it will affect if the window
2485 if (fullscreen
!= self
->fullscreen
)
2486 client_fullscreen(self
, fullscreen
, TRUE
);
2487 if (shaded
!= self
->shaded
)
2488 client_shade(self
, shaded
);
2489 if (undecorated
!= self
->undecorated
)
2490 client_set_undecorated(self
, undecorated
);
2491 client_calc_layer(self
);
2492 client_change_state(self
); /* change the hint to reflect these changes */
2495 ObClient
*client_focus_target(ObClient
*self
)
2499 /* if we have a modal child, then focus it, not us */
2500 child
= client_search_modal_child(client_search_top_transient(self
));
2501 if (child
) return child
;
2505 gboolean
client_can_focus(ObClient
*self
)
2509 /* choose the correct target */
2510 self
= client_focus_target(self
);
2512 if (!self
->frame
->visible
)
2515 if (!((self
->can_focus
|| self
->focus_notify
) &&
2516 (self
->desktop
== screen_desktop
||
2517 self
->desktop
== DESKTOP_ALL
) &&
2521 /* do a check to see if the window has already been unmapped or destroyed
2522 do this intelligently while watching out for unmaps we've generated
2523 (ignore_unmaps > 0) */
2524 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2525 DestroyNotify
, &ev
)) {
2526 XPutBackEvent(ob_display
, &ev
);
2529 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2530 UnmapNotify
, &ev
)) {
2531 if (self
->ignore_unmaps
) {
2532 self
->ignore_unmaps
--;
2534 XPutBackEvent(ob_display
, &ev
);
2542 gboolean
client_focus(ObClient
*self
)
2544 /* choose the correct target */
2545 self
= client_focus_target(self
);
2547 if (!client_can_focus(self
)) {
2548 if (!self
->frame
->visible
) {
2549 /* update the focus lists */
2550 focus_order_to_top(self
);
2555 if (self
->can_focus
) {
2556 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2557 I choose to use it always, hopefully to find errors quicker, if any
2558 are left. (I hate X. I hate focus events.)
2560 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2561 #799. So now it is RevertToNone again.
2563 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2567 if (self
->focus_notify
) {
2569 ce
.xclient
.type
= ClientMessage
;
2570 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2571 ce
.xclient
.display
= ob_display
;
2572 ce
.xclient
.window
= self
->window
;
2573 ce
.xclient
.format
= 32;
2574 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2575 ce
.xclient
.data
.l
[1] = event_lasttime
;
2576 ce
.xclient
.data
.l
[2] = 0l;
2577 ce
.xclient
.data
.l
[3] = 0l;
2578 ce
.xclient
.data
.l
[4] = 0l;
2579 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2583 ob_debug("%sively focusing %lx at %d\n",
2584 (self
->can_focus
? "act" : "pass"),
2585 self
->window
, (int) event_lasttime
);
2588 /* Cause the FocusIn to come back to us. Important for desktop switches,
2589 since otherwise we'll have no FocusIn on the queue and send it off to
2590 the focus_backup. */
2591 XSync(ob_display
, FALSE
);
2595 void client_unfocus(ObClient
*self
)
2597 if (focus_client
== self
) {
2599 ob_debug("client_unfocus for %lx\n", self
->window
);
2601 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2605 void client_activate(ObClient
*self
, gboolean here
)
2607 if (client_normal(self
) && screen_showing_desktop
)
2608 screen_show_desktop(FALSE
);
2610 client_iconify(self
, FALSE
, here
);
2611 if (self
->desktop
!= DESKTOP_ALL
&&
2612 self
->desktop
!= screen_desktop
) {
2614 client_set_desktop(self
, screen_desktop
, FALSE
);
2616 screen_set_desktop(self
->desktop
);
2617 } else if (!self
->frame
->visible
)
2618 /* if its not visible for other reasons, then don't mess
2622 client_shade(self
, FALSE
);
2626 /* we do this an action here. this is rather important. this is because
2627 we want the results from the focus change to take place BEFORE we go
2628 about raising the window. when a fullscreen window loses focus, we need
2629 this or else the raise wont be able to raise above the to-lose-focus
2630 fullscreen window. */
2634 void client_raise(ObClient
*self
)
2636 action_run_string("Raise", self
);
2639 void client_lower(ObClient
*self
)
2641 action_run_string("Raise", self
);
2644 gboolean
client_focused(ObClient
*self
)
2646 return self
== focus_client
;
2649 ObClientIcon
*client_icon(ObClient
*self
, int w
, int h
)
2652 /* si is the smallest image >= req */
2653 /* li is the largest image < req */
2654 unsigned long size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2656 for (i
= 0; i
< self
->nicons
; ++i
) {
2657 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2658 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2662 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2667 if (largest
== 0) /* didnt find one smaller than the requested size */
2668 return &self
->icons
[si
];
2669 return &self
->icons
[li
];
2672 /* this be mostly ripped from fvwm */
2673 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2675 int my_cx
, my_cy
, his_cx
, his_cy
;
2678 int score
, best_score
;
2679 ObClient
*best_client
, *cur
;
2685 /* first, find the centre coords of the currently focused window */
2686 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2687 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2692 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2695 /* the currently selected window isn't interesting */
2698 if (!client_normal(cur
))
2700 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2704 if(client_focus_target(cur
) == cur
&&
2705 !(cur
->can_focus
|| cur
->focus_notify
))
2708 /* find the centre coords of this window, from the
2709 * currently focused window's point of view */
2710 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2711 + cur
->frame
->area
.width
/ 2;
2712 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2713 + cur
->frame
->area
.height
/ 2;
2715 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
2716 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
2718 /* Rotate the diagonals 45 degrees counterclockwise.
2719 * To do this, multiply the matrix /+h +h\ with the
2720 * vector (x y). \-h +h/
2721 * h = sqrt(0.5). We can set h := 1 since absolute
2722 * distance doesn't matter here. */
2723 tx
= his_cx
+ his_cy
;
2724 his_cy
= -his_cx
+ his_cy
;
2729 case OB_DIRECTION_NORTH
:
2730 case OB_DIRECTION_SOUTH
:
2731 case OB_DIRECTION_NORTHEAST
:
2732 case OB_DIRECTION_SOUTHWEST
:
2733 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
2734 distance
= ((dir
== OB_DIRECTION_NORTH
||
2735 dir
== OB_DIRECTION_NORTHEAST
) ?
2738 case OB_DIRECTION_EAST
:
2739 case OB_DIRECTION_WEST
:
2740 case OB_DIRECTION_SOUTHEAST
:
2741 case OB_DIRECTION_NORTHWEST
:
2742 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
2743 distance
= ((dir
== OB_DIRECTION_WEST
||
2744 dir
== OB_DIRECTION_NORTHWEST
) ?
2749 /* the target must be in the requested direction */
2753 /* Calculate score for this window. The smaller the better. */
2754 score
= distance
+ offset
;
2756 /* windows more than 45 degrees off the direction are
2757 * heavily penalized and will only be chosen if nothing
2758 * else within a million pixels */
2759 if(offset
> distance
)
2762 if(best_score
== -1 || score
< best_score
)
2770 void client_set_layer(ObClient
*self
, int layer
)
2774 self
->above
= FALSE
;
2775 } else if (layer
== 0) {
2776 self
->below
= self
->above
= FALSE
;
2778 self
->below
= FALSE
;
2781 client_calc_layer(self
);
2782 client_change_state(self
); /* reflect this in the state hints */
2785 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
2787 if (self
->undecorated
!= undecorated
) {
2788 self
->undecorated
= undecorated
;
2789 client_setup_decor_and_functions(self
);
2790 client_change_state(self
); /* reflect this in the state hints */
2794 guint
client_monitor(ObClient
*self
)
2798 for (i
= 0; i
< screen_num_monitors
; ++i
) {
2799 Rect
*area
= screen_physical_area_monitor(i
);
2800 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
))
2803 if (i
== screen_num_monitors
) i
= 0;
2804 g_assert(i
< screen_num_monitors
);
2808 ObClient
*client_search_top_transient(ObClient
*self
)
2810 /* move up the transient chain as far as possible */
2811 if (self
->transient_for
) {
2812 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2813 return client_search_top_transient(self
->transient_for
);
2817 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2818 ObClient
*c
= it
->data
;
2820 /* checking transient_for prevents infinate loops! */
2821 if (c
!= self
&& !c
->transient_for
)
2832 ObClient
*client_search_focus_parent(ObClient
*self
)
2834 if (self
->transient_for
) {
2835 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2836 if (client_focused(self
->transient_for
))
2837 return self
->transient_for
;
2841 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2842 ObClient
*c
= it
->data
;
2844 /* checking transient_for prevents infinate loops! */
2845 if (c
!= self
&& !c
->transient_for
)
2846 if (client_focused(c
))
2855 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
2857 if (self
->transient_for
) {
2858 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2859 if (self
->transient_for
== search
)
2864 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2865 ObClient
*c
= it
->data
;
2867 /* checking transient_for prevents infinate loops! */
2868 if (c
!= self
&& !c
->transient_for
)
2878 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
2882 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
2883 if (sit
->data
== search
)
2885 if (client_search_transient(sit
->data
, search
))
2891 void client_update_sm_client_id(ObClient
*self
)
2893 g_free(self
->sm_client_id
);
2894 self
->sm_client_id
= NULL
;
2896 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
2898 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
2899 &self
->sm_client_id
);
2902 /* finds the nearest edge in the given direction from the current client
2903 * note to self: the edge is the -frame- edge (the actual one), not the
2906 int client_directional_edge_search(ObClient
*c
, ObDirection dir
)
2909 int my_edge_start
, my_edge_end
, my_offset
;
2916 a
= screen_area(c
->desktop
);
2919 case OB_DIRECTION_NORTH
:
2920 my_edge_start
= c
->frame
->area
.x
;
2921 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
2922 my_offset
= c
->frame
->area
.y
;
2924 dest
= a
->y
; /* default: top of screen */
2926 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2927 int his_edge_start
, his_edge_end
, his_offset
;
2928 ObClient
*cur
= it
->data
;
2932 if(!client_normal(cur
))
2934 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2939 his_edge_start
= cur
->frame
->area
.x
;
2940 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
2941 his_offset
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
2943 if(his_offset
+ 1 > my_offset
)
2946 if(his_offset
< dest
)
2949 if(his_edge_start
>= my_edge_start
&&
2950 his_edge_start
<= my_edge_end
)
2953 if(my_edge_start
>= his_edge_start
&&
2954 my_edge_start
<= his_edge_end
)
2959 case OB_DIRECTION_SOUTH
:
2960 my_edge_start
= c
->frame
->area
.x
;
2961 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
2962 my_offset
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
2964 dest
= a
->y
+ a
->height
; /* default: bottom of screen */
2966 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2967 int his_edge_start
, his_edge_end
, his_offset
;
2968 ObClient
*cur
= it
->data
;
2972 if(!client_normal(cur
))
2974 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2979 his_edge_start
= cur
->frame
->area
.x
;
2980 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
2981 his_offset
= cur
->frame
->area
.y
;
2984 if(his_offset
- 1 < my_offset
)
2987 if(his_offset
> dest
)
2990 if(his_edge_start
>= my_edge_start
&&
2991 his_edge_start
<= my_edge_end
)
2994 if(my_edge_start
>= his_edge_start
&&
2995 my_edge_start
<= his_edge_end
)
3000 case OB_DIRECTION_WEST
:
3001 my_edge_start
= c
->frame
->area
.y
;
3002 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3003 my_offset
= c
->frame
->area
.x
;
3005 dest
= a
->x
; /* default: leftmost egde of screen */
3007 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3008 int his_edge_start
, his_edge_end
, his_offset
;
3009 ObClient
*cur
= it
->data
;
3013 if(!client_normal(cur
))
3015 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3020 his_edge_start
= cur
->frame
->area
.y
;
3021 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3022 his_offset
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3024 if(his_offset
+ 1 > my_offset
)
3027 if(his_offset
< dest
)
3030 if(his_edge_start
>= my_edge_start
&&
3031 his_edge_start
<= my_edge_end
)
3034 if(my_edge_start
>= his_edge_start
&&
3035 my_edge_start
<= his_edge_end
)
3041 case OB_DIRECTION_EAST
:
3042 my_edge_start
= c
->frame
->area
.y
;
3043 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3044 my_offset
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3046 dest
= a
->x
+ a
->width
; /* default: rightmost edge of screen */
3048 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3049 int his_edge_start
, his_edge_end
, his_offset
;
3050 ObClient
*cur
= it
->data
;
3054 if(!client_normal(cur
))
3056 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3061 his_edge_start
= cur
->frame
->area
.y
;
3062 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3063 his_offset
= cur
->frame
->area
.x
;
3065 if(his_offset
- 1 < my_offset
)
3068 if(his_offset
> dest
)
3071 if(his_edge_start
>= my_edge_start
&&
3072 his_edge_start
<= my_edge_end
)
3075 if(my_edge_start
>= his_edge_start
&&
3076 my_edge_start
<= his_edge_end
)
3081 case OB_DIRECTION_NORTHEAST
:
3082 case OB_DIRECTION_SOUTHEAST
:
3083 case OB_DIRECTION_NORTHWEST
:
3084 case OB_DIRECTION_SOUTHWEST
:
3085 /* not implemented */
3087 g_assert_not_reached();
3092 ObClient
* client_under_pointer()
3096 ObClient
*ret
= NULL
;
3098 if (screen_pointer_pos(&x
, &y
)) {
3099 for (it
= stacking_list
; it
!= NULL
; it
= it
->next
) {
3100 if (WINDOW_IS_CLIENT(it
->data
)) {
3101 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3102 if (c
->desktop
== screen_desktop
&&
3103 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {