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 | \
55 ObClientDestructor func
;
59 GList
*client_list
= NULL
;
60 GSList
*client_destructors
= NULL
;
62 static void client_get_all(ObClient
*self
);
63 static void client_toggle_border(ObClient
*self
, gboolean show
);
64 static void client_get_startup_id(ObClient
*self
);
65 static void client_get_area(ObClient
*self
);
66 static void client_get_desktop(ObClient
*self
);
67 static void client_get_state(ObClient
*self
);
68 static void client_get_shaped(ObClient
*self
);
69 static void client_get_mwm_hints(ObClient
*self
);
70 static void client_get_gravity(ObClient
*self
);
71 static void client_showhide(ObClient
*self
);
72 static void client_change_allowed_actions(ObClient
*self
);
73 static void client_change_state(ObClient
*self
);
74 static void client_apply_startup_state(ObClient
*self
);
75 static void client_restore_session_state(ObClient
*self
);
76 static void client_restore_session_stacking(ObClient
*self
);
77 static void client_urgent_notify(ObClient
*self
);
79 void client_startup(gboolean reconfig
)
86 void client_shutdown(gboolean reconfig
)
90 void client_add_destructor(ObClientDestructor func
, gpointer data
)
92 Destructor
*d
= g_new(Destructor
, 1);
95 client_destructors
= g_slist_prepend(client_destructors
, d
);
98 void client_remove_destructor(ObClientDestructor func
)
102 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
103 Destructor
*d
= it
->data
;
104 if (d
->func
== func
) {
106 client_destructors
= g_slist_delete_link(client_destructors
, it
);
112 void client_set_list()
114 Window
*windows
, *win_it
;
116 guint size
= g_list_length(client_list
);
118 /* create an array of the window ids */
120 windows
= g_new(Window
, size
);
122 for (it
= client_list
; it
!= NULL
; it
= it
->next
, ++win_it
)
123 *win_it
= ((ObClient
*)it
->data
)->window
;
127 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
128 net_client_list
, window
, (guint32
*)windows
, size
);
137 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, void *data)
141 for (it = self->transients; it; it = it->next) {
142 if (!func(it->data, data)) return;
143 client_foreach_transient(it->data, func, data);
147 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, void *data)
149 if (self->transient_for) {
150 if (self->transient_for != OB_TRAN_GROUP) {
151 if (!func(self->transient_for, data)) return;
152 client_foreach_ancestor(self->transient_for, func, data);
156 for (it = self->group->members; it; it = it->next)
157 if (it->data != self &&
158 !((ObClient*)it->data)->transient_for) {
159 if (!func(it->data, data)) return;
160 client_foreach_ancestor(it->data, func, data);
167 void client_manage_all()
169 unsigned int i
, j
, nchild
;
172 XWindowAttributes attrib
;
174 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
175 &w
, &w
, &children
, &nchild
);
177 /* remove all icon windows from the list */
178 for (i
= 0; i
< nchild
; i
++) {
179 if (children
[i
] == None
) continue;
180 wmhints
= XGetWMHints(ob_display
, children
[i
]);
182 if ((wmhints
->flags
& IconWindowHint
) &&
183 (wmhints
->icon_window
!= children
[i
]))
184 for (j
= 0; j
< nchild
; j
++)
185 if (children
[j
] == wmhints
->icon_window
) {
193 for (i
= 0; i
< nchild
; ++i
) {
194 if (children
[i
] == None
)
196 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
197 if (attrib
.override_redirect
) continue;
199 if (attrib
.map_state
!= IsUnmapped
)
200 client_manage(children
[i
]);
206 void client_manage(Window window
)
210 XWindowAttributes attrib
;
211 XSetWindowAttributes attrib_set
;
213 gboolean activate
= FALSE
;
217 /* check if it has already been unmapped by the time we started mapping
218 the grab does a sync so we don't have to here */
219 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
220 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
)) {
221 XPutBackEvent(ob_display
, &e
);
224 return; /* don't manage it */
227 /* make sure it isn't an override-redirect window */
228 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
229 attrib
.override_redirect
) {
231 return; /* don't manage it */
234 /* is the window a docking app */
235 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
236 if ((wmhint
->flags
& StateHint
) &&
237 wmhint
->initial_state
== WithdrawnState
) {
238 dock_add(window
, wmhint
);
246 ob_debug("Managing window: %lx\n", window
);
248 /* choose the events we want to receive on the CLIENT window */
249 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
250 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
251 XChangeWindowAttributes(ob_display
, window
,
252 CWEventMask
|CWDontPropagate
, &attrib_set
);
255 /* create the ObClient struct, and populate it from the hints on the
257 self
= g_new0(ObClient
, 1);
258 self
->obwin
.type
= Window_Client
;
259 self
->window
= window
;
261 /* non-zero defaults */
262 self
->title_count
= 1;
263 self
->wmstate
= NormalState
;
265 self
->desktop
= screen_num_desktops
; /* always an invalid value */
267 client_get_all(self
);
268 client_restore_session_state(self
);
270 sn_app_started(self
->class);
272 client_change_state(self
);
274 /* remove the client's border (and adjust re gravity) */
275 client_toggle_border(self
, FALSE
);
277 /* specify that if we exit, the window should not be destroyed and should
278 be reparented back to root automatically */
279 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
281 /* create the decoration frame for the client window */
282 self
->frame
= frame_new();
284 frame_grab_client(self
->frame
, self
);
288 client_apply_startup_state(self
);
290 /* update the focus lists */
291 focus_order_add_new(self
);
293 stacking_add(CLIENT_AS_WINDOW(self
));
294 client_restore_session_stacking(self
);
296 /* focus the new window? */
297 if (ob_state() != OB_STATE_STARTING
&&
298 (config_focus_new
|| client_search_focus_parent(self
)) &&
299 /* note the check against Type_Normal/Dialog, not client_normal(self),
300 which would also include other types. in this case we want more
301 strict rules for focus */
302 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
303 self
->type
== OB_CLIENT_TYPE_DIALOG
))
307 if (self
->desktop
!= screen_desktop
) {
308 /* activate the window */
311 gboolean group_foc
= FALSE
;
316 for (it
= self
->group
->members
; it
; it
= it
->next
)
318 if (client_focused(it
->data
))
326 (!self
->transient_for
&& (!self
->group
||
327 !self
->group
->members
->next
))) ||
328 client_search_focus_tree_full(self
) ||
330 !client_normal(focus_client
))
332 /* activate the window */
339 if (ob_state() == OB_STATE_RUNNING
) {
340 int x
= self
->area
.x
, ox
= x
;
341 int y
= self
->area
.y
, oy
= y
;
343 place_client(self
, &x
, &y
);
345 /* make sure the window is visible */
346 client_find_onscreen(self
, &x
, &y
,
347 self
->frame
->area
.width
,
348 self
->frame
->area
.height
,
349 client_normal(self
));
351 if (x
!= ox
|| y
!= oy
)
352 client_move(self
, x
, y
);
355 client_showhide(self
);
357 /* use client_focus instead of client_activate cuz client_activate does
358 stuff like switch desktops etc and I'm not interested in all that when
359 a window maps since its not based on an action from the user like
360 clicking a window to activate is. so keep the new window out of the way
362 if (activate
) client_focus(self
);
364 /* client_activate does this but we aret using it so we have to do it
366 if (screen_showing_desktop
)
367 screen_show_desktop(FALSE
);
369 /* add to client list/map */
370 client_list
= g_list_append(client_list
, self
);
371 g_hash_table_insert(window_map
, &self
->window
, self
);
373 /* this has to happen after we're in the client_list */
374 screen_update_areas();
376 /* update the list hints */
379 keyboard_grab_for_client(self
, TRUE
);
380 mouse_grab_for_client(self
, TRUE
);
382 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
385 void client_unmanage_all()
387 while (client_list
!= NULL
)
388 client_unmanage(client_list
->data
);
391 void client_unmanage(ObClient
*self
)
396 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
398 g_assert(self
!= NULL
);
400 keyboard_grab_for_client(self
, FALSE
);
401 mouse_grab_for_client(self
, FALSE
);
403 /* remove the window from our save set */
404 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
406 /* we dont want events no more */
407 XSelectInput(ob_display
, self
->window
, NoEventMask
);
409 frame_hide(self
->frame
);
411 client_list
= g_list_remove(client_list
, self
);
412 stacking_remove(self
);
413 g_hash_table_remove(window_map
, &self
->window
);
415 /* update the focus lists */
416 focus_order_remove(self
);
418 /* once the client is out of the list, update the struts to remove it's
420 screen_update_areas();
422 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
423 Destructor
*d
= it
->data
;
424 d
->func(self
, d
->data
);
427 if (focus_client
== self
) {
430 /* focus the last focused window on the desktop, and ignore enter
431 events from the unmap so it doesnt mess with the focus */
432 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
433 /* remove these flags so we don't end up getting focused in the
435 self
->can_focus
= FALSE
;
436 self
->focus_notify
= FALSE
;
438 client_unfocus(self
);
441 /* tell our parent(s) that we're gone */
442 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
445 for (it
= self
->group
->members
; it
; it
= it
->next
)
446 if (it
->data
!= self
)
447 ((ObClient
*)it
->data
)->transients
=
448 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
449 } else if (self
->transient_for
) { /* transient of window */
450 self
->transient_for
->transients
=
451 g_slist_remove(self
->transient_for
->transients
, self
);
454 /* tell our transients that we're gone */
455 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
456 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
457 ((ObClient
*)it
->data
)->transient_for
= NULL
;
458 client_calc_layer(it
->data
);
462 /* remove from its group */
464 group_remove(self
->group
, self
);
468 /* give the client its border back */
469 client_toggle_border(self
, TRUE
);
471 /* reparent the window out of the frame, and free the frame */
472 frame_release_client(self
->frame
, self
);
475 if (ob_state() != OB_STATE_EXITING
) {
476 /* these values should not be persisted across a window
478 PROP_ERASE(self
->window
, net_wm_desktop
);
479 PROP_ERASE(self
->window
, net_wm_state
);
480 PROP_ERASE(self
->window
, wm_state
);
482 /* if we're left in an iconic state, the client wont be mapped. this is
483 bad, since we will no longer be managing the window on restart */
485 XMapWindow(ob_display
, self
->window
);
489 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
491 /* free all data allocated in the client struct */
492 g_slist_free(self
->transients
);
493 for (j
= 0; j
< self
->nicons
; ++j
)
494 g_free(self
->icons
[j
].data
);
495 if (self
->nicons
> 0)
498 g_free(self
->icon_title
);
502 g_free(self
->sm_client_id
);
505 /* update the list hints */
509 static void client_urgent_notify(ObClient
*self
)
512 frame_flash_start(self
->frame
);
514 frame_flash_stop(self
->frame
);
517 static void client_restore_session_state(ObClient
*self
)
521 if (!(it
= session_state_find(self
)))
524 self
->session
= it
->data
;
526 RECT_SET(self
->area
, self
->session
->x
, self
->session
->y
,
527 self
->session
->w
, self
->session
->h
);
528 self
->positioned
= TRUE
;
529 if (self
->session
->w
> 0 && self
->session
->h
> 0)
530 XResizeWindow(ob_display
, self
->window
,
531 self
->session
->w
, self
->session
->h
);
533 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
534 self
->session
->desktop
:
535 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
536 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
538 self
->shaded
= self
->session
->shaded
;
539 self
->iconic
= self
->session
->iconic
;
540 self
->skip_pager
= self
->session
->skip_pager
;
541 self
->skip_taskbar
= self
->session
->skip_taskbar
;
542 self
->fullscreen
= self
->session
->fullscreen
;
543 self
->above
= self
->session
->above
;
544 self
->below
= self
->session
->below
;
545 self
->max_horz
= self
->session
->max_horz
;
546 self
->max_vert
= self
->session
->max_vert
;
549 static void client_restore_session_stacking(ObClient
*self
)
553 if (!self
->session
) return;
555 it
= g_list_find(session_saved_state
, self
->session
);
556 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
559 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
560 if (session_state_cmp(it
->data
, cit
->data
))
563 client_calc_layer(self
);
564 stacking_below(CLIENT_AS_WINDOW(self
),
565 CLIENT_AS_WINDOW(cit
->data
));
571 void client_move_onscreen(ObClient
*self
, gboolean rude
)
573 int x
= self
->area
.x
;
574 int y
= self
->area
.y
;
575 if (client_find_onscreen(self
, &x
, &y
,
576 self
->frame
->area
.width
,
577 self
->frame
->area
.height
, rude
)) {
578 client_move(self
, x
, y
);
582 gboolean
client_find_onscreen(ObClient
*self
, int *x
, int *y
, int w
, int h
,
586 int ox
= *x
, oy
= *y
;
588 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
591 /* XXX watch for xinerama dead areas */
593 a
= screen_area(self
->desktop
);
594 if (client_normal(self
)) {
595 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
596 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
597 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
598 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
599 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
601 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
606 /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
607 Java can suck it too. */
609 /* dont let windows map/move into the strut unless they
610 are bigger than the available area */
612 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
613 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
614 *x
= a
->x
+ a
->width
- w
;
616 if (h
<= a
->height
) {
617 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
618 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
619 *y
= a
->y
+ a
->height
- h
;
623 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
626 return ox
!= *x
|| oy
!= *y
;
629 static void client_toggle_border(ObClient
*self
, gboolean show
)
631 /* adjust our idea of where the client is, based on its border. When the
632 border is removed, the client should now be considered to be in a
634 when re-adding the border to the client, the same operation needs to be
636 int oldx
= self
->area
.x
, oldy
= self
->area
.y
;
637 int x
= oldx
, y
= oldy
;
638 switch(self
->gravity
) {
640 case NorthWestGravity
:
642 case SouthWestGravity
:
644 case NorthEastGravity
:
646 case SouthEastGravity
:
647 if (show
) x
-= self
->border_width
* 2;
648 else x
+= self
->border_width
* 2;
655 if (show
) x
-= self
->border_width
;
656 else x
+= self
->border_width
;
659 switch(self
->gravity
) {
661 case NorthWestGravity
:
663 case NorthEastGravity
:
665 case SouthWestGravity
:
667 case SouthEastGravity
:
668 if (show
) y
-= self
->border_width
* 2;
669 else y
+= self
->border_width
* 2;
676 if (show
) y
-= self
->border_width
;
677 else y
+= self
->border_width
;
684 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
686 /* move the client so it is back it the right spot _with_ its
688 if (x
!= oldx
|| y
!= oldy
)
689 XMoveWindow(ob_display
, self
->window
, x
, y
);
691 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
695 static void client_get_all(ObClient
*self
)
697 client_get_area(self
);
698 client_update_transient_for(self
);
699 client_update_wmhints(self
);
700 client_get_startup_id(self
);
701 client_get_desktop(self
);
702 client_get_state(self
);
703 client_get_shaped(self
);
705 client_get_mwm_hints(self
);
706 client_get_type(self
);/* this can change the mwmhints for special cases */
708 client_update_protocols(self
);
710 client_get_gravity(self
); /* get the attribute gravity */
711 client_update_normal_hints(self
); /* this may override the attribute
714 /* got the type, the mwmhints, the protocols, and the normal hints
715 (min/max sizes), so we're ready to set up the decorations/functions */
716 client_setup_decor_and_functions(self
);
718 client_update_title(self
);
719 client_update_class(self
);
720 client_update_sm_client_id(self
);
721 client_update_strut(self
);
722 client_update_icons(self
);
725 static void client_get_startup_id(ObClient
*self
)
727 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
729 PROP_GETS(self
->group
->leader
,
730 net_startup_id
, utf8
, &self
->startup_id
);
733 static void client_get_area(ObClient
*self
)
735 XWindowAttributes wattrib
;
738 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
739 g_assert(ret
!= BadWindow
);
741 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
742 self
->border_width
= wattrib
.border_width
;
745 static void client_get_desktop(ObClient
*self
)
747 guint32 d
= screen_num_desktops
; /* an always-invalid value */
749 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
750 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
751 self
->desktop
= screen_num_desktops
- 1;
755 gboolean trdesk
= FALSE
;
757 if (self
->transient_for
) {
758 if (self
->transient_for
!= OB_TRAN_GROUP
) {
759 self
->desktop
= self
->transient_for
->desktop
;
764 for (it
= self
->group
->members
; it
; it
= it
->next
)
765 if (it
->data
!= self
&&
766 !((ObClient
*)it
->data
)->transient_for
) {
767 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
774 /* try get from the startup-notification protocol */
775 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
776 if (self
->desktop
>= screen_num_desktops
&&
777 self
->desktop
!= DESKTOP_ALL
)
778 self
->desktop
= screen_num_desktops
- 1;
780 /* defaults to the current desktop */
781 self
->desktop
= screen_desktop
;
784 if (self
->desktop
!= d
) {
785 /* set the desktop hint, to make sure that it always exists */
786 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
790 static void client_get_state(ObClient
*self
)
795 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
797 for (i
= 0; i
< num
; ++i
) {
798 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
800 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
802 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
804 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
805 self
->skip_taskbar
= TRUE
;
806 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
807 self
->skip_pager
= TRUE
;
808 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
809 self
->fullscreen
= TRUE
;
810 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
811 self
->max_vert
= TRUE
;
812 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
813 self
->max_horz
= TRUE
;
814 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
816 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
818 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
819 self
->undecorated
= TRUE
;
826 static void client_get_shaped(ObClient
*self
)
828 self
->shaped
= FALSE
;
830 if (extensions_shape
) {
835 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
837 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
838 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
840 self
->shaped
= (s
!= 0);
845 void client_update_transient_for(ObClient
*self
)
848 ObClient
*target
= NULL
;
850 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
851 self
->transient
= TRUE
;
852 if (t
!= self
->window
) { /* cant be transient to itself! */
853 target
= g_hash_table_lookup(window_map
, &t
);
854 /* if this happens then we need to check for it*/
855 g_assert(target
!= self
);
856 if (target
&& !WINDOW_IS_CLIENT(target
)) {
857 /* this can happen when a dialog is a child of
858 a dockapp, for example */
862 if (!target
&& self
->group
) {
863 /* not transient to a client, see if it is transient for a
865 if (t
== self
->group
->leader
||
867 t
== RootWindow(ob_display
, ob_screen
))
869 /* window is a transient for its group! */
870 target
= OB_TRAN_GROUP
;
875 self
->transient
= FALSE
;
877 /* if anything has changed... */
878 if (target
!= self
->transient_for
) {
879 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
882 /* remove from old parents */
883 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
884 ObClient
*c
= it
->data
;
885 if (c
!= self
&& !c
->transient_for
)
886 c
->transients
= g_slist_remove(c
->transients
, self
);
888 } else if (self
->transient_for
!= NULL
) { /* transient of window */
889 /* remove from old parent */
890 self
->transient_for
->transients
=
891 g_slist_remove(self
->transient_for
->transients
, self
);
893 self
->transient_for
= target
;
894 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
897 /* add to new parents */
898 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
899 ObClient
*c
= it
->data
;
900 if (c
!= self
&& !c
->transient_for
)
901 c
->transients
= g_slist_append(c
->transients
, self
);
904 /* remove all transients which are in the group, that causes
905 circlular pointer hell of doom */
906 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
908 for (sit
= self
->transients
; sit
; sit
= next
) {
909 next
= g_slist_next(sit
);
910 if (sit
->data
== it
->data
)
912 g_slist_delete_link(self
->transients
, sit
);
915 } else if (self
->transient_for
!= NULL
) { /* transient of window */
916 /* add to new parent */
917 self
->transient_for
->transients
=
918 g_slist_append(self
->transient_for
->transients
, self
);
923 static void client_get_mwm_hints(ObClient
*self
)
928 self
->mwmhints
.flags
= 0; /* default to none */
930 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
932 if (num
>= OB_MWM_ELEMENTS
) {
933 self
->mwmhints
.flags
= hints
[0];
934 self
->mwmhints
.functions
= hints
[1];
935 self
->mwmhints
.decorations
= hints
[2];
941 void client_get_type(ObClient
*self
)
948 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
949 /* use the first value that we know about in the array */
950 for (i
= 0; i
< num
; ++i
) {
951 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
952 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
953 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
954 self
->type
= OB_CLIENT_TYPE_DOCK
;
955 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
956 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
957 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
958 self
->type
= OB_CLIENT_TYPE_MENU
;
959 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
960 self
->type
= OB_CLIENT_TYPE_UTILITY
;
961 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
962 self
->type
= OB_CLIENT_TYPE_SPLASH
;
963 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
964 self
->type
= OB_CLIENT_TYPE_DIALOG
;
965 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
966 self
->type
= OB_CLIENT_TYPE_NORMAL
;
967 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
968 /* prevent this window from getting any decor or
970 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
971 OB_MWM_FLAG_DECORATIONS
);
972 self
->mwmhints
.decorations
= 0;
973 self
->mwmhints
.functions
= 0;
975 if (self
->type
!= (ObClientType
) -1)
976 break; /* grab the first legit type */
981 if (self
->type
== (ObClientType
) -1) {
982 /*the window type hint was not set, which means we either classify
983 ourself as a normal window or a dialog, depending on if we are a
986 self
->type
= OB_CLIENT_TYPE_DIALOG
;
988 self
->type
= OB_CLIENT_TYPE_NORMAL
;
992 void client_update_protocols(ObClient
*self
)
997 self
->focus_notify
= FALSE
;
998 self
->delete_window
= FALSE
;
1000 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1001 for (i
= 0; i
< num_return
; ++i
) {
1002 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1003 /* this means we can request the window to close */
1004 self
->delete_window
= TRUE
;
1005 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1006 /* if this protocol is requested, then the window will be
1007 notified whenever we want it to receive focus */
1008 self
->focus_notify
= TRUE
;
1014 static void client_get_gravity(ObClient
*self
)
1016 XWindowAttributes wattrib
;
1019 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1020 g_assert(ret
!= BadWindow
);
1021 self
->gravity
= wattrib
.win_gravity
;
1024 void client_update_normal_hints(ObClient
*self
)
1028 int oldgravity
= self
->gravity
;
1031 self
->min_ratio
= 0.0f
;
1032 self
->max_ratio
= 0.0f
;
1033 SIZE_SET(self
->size_inc
, 1, 1);
1034 SIZE_SET(self
->base_size
, 0, 0);
1035 SIZE_SET(self
->min_size
, 0, 0);
1036 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1038 /* get the hints from the window */
1039 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1040 self
->positioned
= !!(size
.flags
& (PPosition
|USPosition
));
1042 if (size
.flags
& PWinGravity
) {
1043 self
->gravity
= size
.win_gravity
;
1045 /* if the client has a frame, i.e. has already been mapped and
1046 is changing its gravity */
1047 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1048 /* move our idea of the client's position based on its new
1050 self
->area
.x
= self
->frame
->area
.x
;
1051 self
->area
.y
= self
->frame
->area
.y
;
1052 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1056 if (size
.flags
& PAspect
) {
1057 if (size
.min_aspect
.y
)
1058 self
->min_ratio
= (float)size
.min_aspect
.x
/ size
.min_aspect
.y
;
1059 if (size
.max_aspect
.y
)
1060 self
->max_ratio
= (float)size
.max_aspect
.x
/ size
.max_aspect
.y
;
1063 if (size
.flags
& PMinSize
)
1064 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1066 if (size
.flags
& PMaxSize
)
1067 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1069 if (size
.flags
& PBaseSize
)
1070 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1072 if (size
.flags
& PResizeInc
)
1073 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1077 void client_setup_decor_and_functions(ObClient
*self
)
1079 /* start with everything (cept fullscreen) */
1081 (OB_FRAME_DECOR_TITLEBAR
|
1082 (ob_rr_theme
->show_handle
? OB_FRAME_DECOR_HANDLE
: 0) |
1083 OB_FRAME_DECOR_GRIPS
|
1084 OB_FRAME_DECOR_BORDER
|
1085 OB_FRAME_DECOR_ICON
|
1086 OB_FRAME_DECOR_ALLDESKTOPS
|
1087 OB_FRAME_DECOR_ICONIFY
|
1088 OB_FRAME_DECOR_MAXIMIZE
|
1089 OB_FRAME_DECOR_SHADE
);
1091 (OB_CLIENT_FUNC_RESIZE
|
1092 OB_CLIENT_FUNC_MOVE
|
1093 OB_CLIENT_FUNC_ICONIFY
|
1094 OB_CLIENT_FUNC_MAXIMIZE
|
1095 OB_CLIENT_FUNC_SHADE
);
1096 if (self
->delete_window
) {
1097 self
->functions
|= OB_CLIENT_FUNC_CLOSE
;
1098 self
->decorations
|= OB_FRAME_DECOR_CLOSE
;
1101 if (!(self
->min_size
.width
< self
->max_size
.width
||
1102 self
->min_size
.height
< self
->max_size
.height
))
1103 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1105 switch (self
->type
) {
1106 case OB_CLIENT_TYPE_NORMAL
:
1107 /* normal windows retain all of the possible decorations and
1108 functionality, and are the only windows that you can fullscreen */
1109 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1112 case OB_CLIENT_TYPE_DIALOG
:
1113 case OB_CLIENT_TYPE_UTILITY
:
1114 /* these windows cannot be maximized */
1115 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1118 case OB_CLIENT_TYPE_MENU
:
1119 case OB_CLIENT_TYPE_TOOLBAR
:
1120 /* these windows get less functionality */
1121 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1124 case OB_CLIENT_TYPE_DESKTOP
:
1125 case OB_CLIENT_TYPE_DOCK
:
1126 case OB_CLIENT_TYPE_SPLASH
:
1127 /* none of these windows are manipulated by the window manager */
1128 self
->decorations
= 0;
1129 self
->functions
= 0;
1133 /* Mwm Hints are applied subtractively to what has already been chosen for
1134 decor and functionality */
1135 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1136 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1137 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1138 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1139 /* if the mwm hints request no handle or title, then all
1140 decorations are disabled */
1141 self
->decorations
= 0;
1145 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1146 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1147 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1148 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1149 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1150 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1151 /* dont let mwm hints kill any buttons
1152 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1153 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1154 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1155 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1157 /* dont let mwm hints kill the close button
1158 if (! (self->mwmhints.functions & MwmFunc_Close))
1159 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1163 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1164 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1165 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1166 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1167 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1168 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1170 /* can't maximize without moving/resizing */
1171 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1172 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1173 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1174 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1175 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1178 /* kill the handle on fully maxed windows */
1179 if (self
->max_vert
&& self
->max_horz
)
1180 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1182 /* finally, the user can have requested no decorations, which overrides
1184 if (self
->undecorated
)
1185 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1187 /* if we don't have a titlebar, then we cannot shade! */
1188 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1189 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1191 /* now we need to check against rules for the client's current state */
1192 if (self
->fullscreen
) {
1193 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1194 OB_CLIENT_FUNC_FULLSCREEN
|
1195 OB_CLIENT_FUNC_ICONIFY
);
1196 self
->decorations
= 0;
1199 client_change_allowed_actions(self
);
1202 /* adjust the client's decorations, etc. */
1203 client_reconfigure(self
);
1205 /* this makes sure that these windows appear on all desktops */
1206 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
&&
1207 self
->desktop
!= DESKTOP_ALL
)
1209 self
->desktop
= DESKTOP_ALL
;
1214 static void client_change_allowed_actions(ObClient
*self
)
1219 /* desktop windows are kept on all desktops */
1220 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1221 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1223 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1224 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1225 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1226 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1227 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1228 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1229 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1230 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1231 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1232 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1233 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1234 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1235 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1236 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1237 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1240 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1242 /* make sure the window isn't breaking any rules now */
1244 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1245 if (self
->frame
) client_shade(self
, FALSE
);
1246 else self
->shaded
= FALSE
;
1248 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1249 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1250 else self
->iconic
= FALSE
;
1252 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1253 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1254 else self
->fullscreen
= FALSE
;
1256 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1258 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1259 else self
->max_vert
= self
->max_horz
= FALSE
;
1263 void client_reconfigure(ObClient
*self
)
1265 /* by making this pass FALSE for user, we avoid the emacs event storm where
1266 every configurenotify causes an update in its normal hints, i think this
1267 is generally what we want anyways... */
1268 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1269 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1272 void client_update_wmhints(ObClient
*self
)
1275 gboolean ur
= FALSE
;
1278 /* assume a window takes input if it doesnt specify */
1279 self
->can_focus
= TRUE
;
1281 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1282 if (hints
->flags
& InputHint
)
1283 self
->can_focus
= hints
->input
;
1285 /* only do this when first managing the window *AND* when we aren't
1287 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1288 if (hints
->flags
& StateHint
)
1289 self
->iconic
= hints
->initial_state
== IconicState
;
1291 if (hints
->flags
& XUrgencyHint
)
1294 if (!(hints
->flags
& WindowGroupHint
))
1295 hints
->window_group
= None
;
1297 /* did the group state change? */
1298 if (hints
->window_group
!=
1299 (self
->group
? self
->group
->leader
: None
)) {
1300 /* remove from the old group if there was one */
1301 if (self
->group
!= NULL
) {
1302 /* remove transients of the group */
1303 for (it
= self
->group
->members
; it
; it
= it
->next
)
1304 self
->transients
= g_slist_remove(self
->transients
,
1307 /* remove myself from parents in the group */
1308 if (self
->transient_for
== OB_TRAN_GROUP
) {
1309 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1310 ObClient
*c
= it
->data
;
1312 if (c
!= self
&& !c
->transient_for
)
1313 c
->transients
= g_slist_remove(c
->transients
,
1318 group_remove(self
->group
, self
);
1321 if (hints
->window_group
!= None
) {
1322 self
->group
= group_add(hints
->window_group
, self
);
1324 /* i can only have transients from the group if i am not
1326 if (!self
->transient_for
) {
1327 /* add other transients of the group that are already
1329 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1330 ObClient
*c
= it
->data
;
1331 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1333 g_slist_append(self
->transients
, c
);
1338 /* because the self->transient flag wont change from this call,
1339 we don't need to update the window's type and such, only its
1340 transient_for, and the transients lists of other windows in
1341 the group may be affected */
1342 client_update_transient_for(self
);
1345 /* the WM_HINTS can contain an icon */
1346 client_update_icons(self
);
1351 if (ur
!= self
->urgent
) {
1353 /* fire the urgent callback if we're mapped, otherwise, wait until
1354 after we're mapped */
1356 client_urgent_notify(self
);
1360 void client_update_title(ObClient
*self
)
1366 gboolean read_title
;
1369 old_title
= self
->title
;
1372 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
))
1373 /* try old x stuff */
1374 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
))
1375 data
= g_strdup("Unnamed Window");
1377 /* did the title change? then reset the title_count */
1378 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1379 self
->title_count
= 1;
1381 /* look for duplicates and append a number */
1383 for (it
= client_list
; it
; it
= it
->next
)
1384 if (it
->data
!= self
) {
1385 ObClient
*c
= it
->data
;
1386 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1387 nums
|= 1 << c
->title_count
;
1389 /* find first free number */
1390 for (i
= 1; i
<= 32; ++i
)
1391 if (!(nums
& (1 << i
))) {
1392 if (self
->title_count
== 1 || i
== 1)
1393 self
->title_count
= i
;
1396 /* dont display the number for the first window */
1397 if (self
->title_count
> 1) {
1399 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1404 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1409 frame_adjust_title(self
->frame
);
1413 /* update the icon title */
1415 g_free(self
->icon_title
);
1419 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1420 /* try old x stuff */
1421 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1422 data
= g_strdup(self
->title
);
1426 /* append the title count, dont display the number for the first window */
1427 if (read_title
&& self
->title_count
> 1) {
1428 char *vdata
, *ndata
;
1429 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1430 vdata
= g_strconcat(data
, ndata
, NULL
);
1436 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1438 self
->icon_title
= data
;
1441 void client_update_class(ObClient
*self
)
1446 if (self
->name
) g_free(self
->name
);
1447 if (self
->class) g_free(self
->class);
1448 if (self
->role
) g_free(self
->role
);
1450 self
->name
= self
->class = self
->role
= NULL
;
1452 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1454 self
->name
= g_strdup(data
[0]);
1456 self
->class = g_strdup(data
[1]);
1461 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1464 if (self
->name
== NULL
) self
->name
= g_strdup("");
1465 if (self
->class == NULL
) self
->class = g_strdup("");
1466 if (self
->role
== NULL
) self
->role
= g_strdup("");
1469 void client_update_strut(ObClient
*self
)
1473 gboolean got
= FALSE
;
1476 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1480 STRUT_PARTIAL_SET(strut
,
1481 data
[0], data
[2], data
[1], data
[3],
1482 data
[4], data
[5], data
[8], data
[9],
1483 data
[6], data
[7], data
[10], data
[11]);
1489 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1492 STRUT_PARTIAL_SET(strut
,
1493 data
[0], data
[2], data
[1], data
[3],
1494 0, 0, 0, 0, 0, 0, 0, 0);
1500 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1501 0, 0, 0, 0, 0, 0, 0, 0);
1503 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1504 self
->strut
= strut
;
1506 /* updating here is pointless while we're being mapped cuz we're not in
1507 the client list yet */
1509 screen_update_areas();
1513 void client_update_icons(ObClient
*self
)
1519 for (i
= 0; i
< self
->nicons
; ++i
)
1520 g_free(self
->icons
[i
].data
);
1521 if (self
->nicons
> 0)
1522 g_free(self
->icons
);
1525 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1526 /* figure out how many valid icons are in here */
1528 while (num
- i
> 2) {
1532 if (i
> num
|| w
*h
== 0) break;
1536 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1538 /* store the icons */
1540 for (j
= 0; j
< self
->nicons
; ++j
) {
1543 w
= self
->icons
[j
].width
= data
[i
++];
1544 h
= self
->icons
[j
].height
= data
[i
++];
1546 if (w
*h
== 0) continue;
1548 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1549 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1554 self
->icons
[j
].data
[t
] =
1555 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1556 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1557 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1558 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1564 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1565 kwm_win_icon
, &data
, &num
)) {
1568 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1569 xerror_set_ignore(TRUE
);
1570 if (!RrPixmapToRGBA(ob_rr_inst
,
1572 &self
->icons
[self
->nicons
-1].width
,
1573 &self
->icons
[self
->nicons
-1].height
,
1574 &self
->icons
[self
->nicons
-1].data
)) {
1575 g_free(&self
->icons
[self
->nicons
-1]);
1578 xerror_set_ignore(FALSE
);
1584 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1585 if (hints
->flags
& IconPixmapHint
) {
1587 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1588 xerror_set_ignore(TRUE
);
1589 if (!RrPixmapToRGBA(ob_rr_inst
,
1591 (hints
->flags
& IconMaskHint
?
1592 hints
->icon_mask
: None
),
1593 &self
->icons
[self
->nicons
-1].width
,
1594 &self
->icons
[self
->nicons
-1].height
,
1595 &self
->icons
[self
->nicons
-1].data
)){
1596 g_free(&self
->icons
[self
->nicons
-1]);
1599 xerror_set_ignore(FALSE
);
1606 frame_adjust_icon(self
->frame
);
1609 static void client_change_state(ObClient
*self
)
1612 guint32 netstate
[11];
1615 state
[0] = self
->wmstate
;
1617 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1621 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1623 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1625 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1626 if (self
->skip_taskbar
)
1627 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1628 if (self
->skip_pager
)
1629 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1630 if (self
->fullscreen
)
1631 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1633 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1635 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1637 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1639 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1640 if (self
->undecorated
)
1641 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1642 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1644 client_calc_layer(self
);
1647 frame_adjust_state(self
->frame
);
1650 ObClient
*client_search_focus_tree(ObClient
*self
)
1655 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
1656 if (client_focused(it
->data
)) return it
->data
;
1657 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1662 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1664 if (self
->transient_for
) {
1665 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1666 return client_search_focus_tree_full(self
->transient_for
);
1669 gboolean recursed
= FALSE
;
1671 for (it
= self
->group
->members
; it
; it
= it
->next
)
1672 if (!((ObClient
*)it
->data
)->transient_for
) {
1674 if ((c
= client_search_focus_tree_full(it
->data
)))
1683 /* this function checks the whole tree, the client_search_focus_tree~
1684 does not, so we need to check this window */
1685 if (client_focused(self
))
1687 return client_search_focus_tree(self
);
1690 static ObStackingLayer
calc_layer(ObClient
*self
)
1694 if (self
->fullscreen
&&
1695 (client_focused(self
) || client_search_focus_tree(self
)))
1696 l
= OB_STACKING_LAYER_FULLSCREEN
;
1697 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1698 l
= OB_STACKING_LAYER_DESKTOP
;
1699 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1700 if (!self
->below
) l
= OB_STACKING_LAYER_TOP
;
1701 else l
= OB_STACKING_LAYER_NORMAL
;
1703 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1704 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1705 else l
= OB_STACKING_LAYER_NORMAL
;
1710 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1711 ObStackingLayer l
, gboolean raised
)
1713 ObStackingLayer old
, own
;
1717 own
= calc_layer(self
);
1718 self
->layer
= l
> own
? l
: own
;
1720 for (it
= self
->transients
; it
; it
= it
->next
)
1721 client_calc_layer_recursive(it
->data
, orig
,
1722 l
, raised
? raised
: l
!= old
);
1724 if (!raised
&& l
!= old
)
1725 if (orig
->frame
) { /* only restack if the original window is managed */
1726 /* XXX add_non_intrusive ever? */
1727 stacking_remove(CLIENT_AS_WINDOW(self
));
1728 stacking_add(CLIENT_AS_WINDOW(self
));
1732 void client_calc_layer(ObClient
*self
)
1739 /* transients take on the layer of their parents */
1740 self
= client_search_top_transient(self
);
1742 l
= calc_layer(self
);
1744 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1747 gboolean
client_should_show(ObClient
*self
)
1749 if (self
->iconic
) return FALSE
;
1750 else if (!(self
->desktop
== screen_desktop
||
1751 self
->desktop
== DESKTOP_ALL
)) return FALSE
;
1752 else if (client_normal(self
) && screen_showing_desktop
) return FALSE
;
1757 static void client_showhide(ObClient
*self
)
1760 if (client_should_show(self
))
1761 frame_show(self
->frame
);
1763 frame_hide(self
->frame
);
1766 gboolean
client_normal(ObClient
*self
) {
1767 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1768 self
->type
== OB_CLIENT_TYPE_DOCK
||
1769 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1772 static void client_apply_startup_state(ObClient
*self
)
1774 /* these are in a carefully crafted order.. */
1777 self
->iconic
= FALSE
;
1778 client_iconify(self
, TRUE
, FALSE
);
1780 if (self
->fullscreen
) {
1781 self
->fullscreen
= FALSE
;
1782 client_fullscreen(self
, TRUE
, FALSE
);
1784 if (self
->undecorated
) {
1785 self
->undecorated
= FALSE
;
1786 client_set_undecorated(self
, TRUE
);
1789 self
->shaded
= FALSE
;
1790 client_shade(self
, TRUE
);
1793 client_urgent_notify(self
);
1795 if (self
->max_vert
&& self
->max_horz
) {
1796 self
->max_vert
= self
->max_horz
= FALSE
;
1797 client_maximize(self
, TRUE
, 0, FALSE
);
1798 } else if (self
->max_vert
) {
1799 self
->max_vert
= FALSE
;
1800 client_maximize(self
, TRUE
, 2, FALSE
);
1801 } else if (self
->max_horz
) {
1802 self
->max_horz
= FALSE
;
1803 client_maximize(self
, TRUE
, 1, FALSE
);
1806 /* nothing to do for the other states:
1815 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1816 int x
, int y
, int w
, int h
,
1817 gboolean user
, gboolean final
,
1818 gboolean force_reply
)
1821 gboolean send_resize_client
;
1822 gboolean moved
= FALSE
, resized
= FALSE
;
1823 guint fdecor
= self
->frame
->decorations
;
1824 gboolean fhorz
= self
->frame
->max_horz
;
1826 /* make the frame recalculate its dimentions n shit without changing
1827 anything visible for real, this way the constraints below can work with
1828 the updated frame dimensions. */
1829 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
1831 /* gets the frame's position */
1832 frame_client_gravity(self
->frame
, &x
, &y
);
1834 /* these positions are frame positions, not client positions */
1836 /* set the size and position if fullscreen */
1837 if (self
->fullscreen
) {
1840 XF86VidModeModeLine mode
;
1845 i
= client_monitor(self
);
1846 a
= screen_physical_area_monitor(i
);
1849 if (i
== 0 && /* primary head */
1850 extensions_vidmode
&&
1851 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
1852 /* get the mode last so the mode.privsize isnt freed incorrectly */
1853 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
1858 if (mode
.privsize
) XFree(mode
.private);
1868 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
1872 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
1874 /* set the size and position if maximized */
1875 if (self
->max_horz
) {
1877 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
1879 if (self
->max_vert
) {
1881 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
1885 /* gets the client's position */
1886 frame_frame_gravity(self
->frame
, &x
, &y
);
1888 /* these override the above states! if you cant move you can't move! */
1890 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
1894 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
1895 w
= self
->area
.width
;
1896 h
= self
->area
.height
;
1900 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
1901 int basew
, baseh
, minw
, minh
;
1903 /* base size is substituted with min size if not specified */
1904 if (self
->base_size
.width
|| self
->base_size
.height
) {
1905 basew
= self
->base_size
.width
;
1906 baseh
= self
->base_size
.height
;
1908 basew
= self
->min_size
.width
;
1909 baseh
= self
->min_size
.height
;
1911 /* min size is substituted with base size if not specified */
1912 if (self
->min_size
.width
|| self
->min_size
.height
) {
1913 minw
= self
->min_size
.width
;
1914 minh
= self
->min_size
.height
;
1916 minw
= self
->base_size
.width
;
1917 minh
= self
->base_size
.height
;
1920 /* if this is a user-requested resize, then check against min/max
1923 /* smaller than min size or bigger than max size? */
1924 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
1925 if (w
< minw
) w
= minw
;
1926 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
1927 if (h
< minh
) h
= minh
;
1932 /* keep to the increments */
1933 w
/= self
->size_inc
.width
;
1934 h
/= self
->size_inc
.height
;
1936 /* you cannot resize to nothing */
1937 if (basew
+ w
< 1) w
= 1 - basew
;
1938 if (baseh
+ h
< 1) h
= 1 - baseh
;
1940 /* store the logical size */
1941 SIZE_SET(self
->logical_size
,
1942 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
1943 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
1945 w
*= self
->size_inc
.width
;
1946 h
*= self
->size_inc
.height
;
1951 /* adjust the height to match the width for the aspect ratios.
1952 for this, min size is not substituted for base size ever. */
1953 w
-= self
->base_size
.width
;
1954 h
-= self
->base_size
.height
;
1956 if (self
->min_ratio
)
1957 if (h
* self
->min_ratio
> w
) {
1958 h
= (int)(w
/ self
->min_ratio
);
1960 /* you cannot resize to nothing */
1963 w
= (int)(h
* self
->min_ratio
);
1966 if (self
->max_ratio
)
1967 if (h
* self
->max_ratio
< w
) {
1968 h
= (int)(w
/ self
->max_ratio
);
1970 /* you cannot resize to nothing */
1973 w
= (int)(h
* self
->min_ratio
);
1977 w
+= self
->base_size
.width
;
1978 h
+= self
->base_size
.height
;
1985 case OB_CORNER_TOPLEFT
:
1987 case OB_CORNER_TOPRIGHT
:
1988 x
-= w
- self
->area
.width
;
1990 case OB_CORNER_BOTTOMLEFT
:
1991 y
-= h
- self
->area
.height
;
1993 case OB_CORNER_BOTTOMRIGHT
:
1994 x
-= w
- self
->area
.width
;
1995 y
-= h
- self
->area
.height
;
1999 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2000 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2002 oldw
= self
->area
.width
;
2003 oldh
= self
->area
.height
;
2004 RECT_SET(self
->area
, x
, y
, w
, h
);
2006 /* for app-requested resizes, always resize if 'resized' is true.
2007 for user-requested ones, only resize if final is true, or when
2008 resizing in redraw mode */
2009 send_resize_client
= ((!user
&& resized
) ||
2011 (resized
&& config_redraw_resize
))));
2013 /* if the client is enlarging, the resize the client before the frame */
2014 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2015 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2017 /* move/resize the frame to match the request */
2019 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2020 moved
= resized
= TRUE
;
2022 if (moved
|| resized
)
2023 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2025 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2028 event
.type
= ConfigureNotify
;
2029 event
.xconfigure
.display
= ob_display
;
2030 event
.xconfigure
.event
= self
->window
;
2031 event
.xconfigure
.window
= self
->window
;
2033 /* root window real coords */
2034 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2036 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2038 event
.xconfigure
.width
= w
;
2039 event
.xconfigure
.height
= h
;
2040 event
.xconfigure
.border_width
= 0;
2041 event
.xconfigure
.above
= self
->frame
->plate
;
2042 event
.xconfigure
.override_redirect
= FALSE
;
2043 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2044 FALSE
, StructureNotifyMask
, &event
);
2048 /* if the client is shrinking, then resize the frame before the client */
2049 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2050 XResizeWindow(ob_display
, self
->window
, w
, h
);
2055 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2059 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2060 self
->fullscreen
== fs
) return; /* already done */
2062 self
->fullscreen
= fs
;
2063 client_change_state(self
); /* change the state hints on the client,
2064 and adjust out layer/stacking */
2068 self
->pre_fullscreen_area
= self
->area
;
2070 /* these are not actually used cuz client_configure will set them
2071 as appropriate when the window is fullscreened */
2076 if (self
->pre_fullscreen_area
.width
> 0 &&
2077 self
->pre_fullscreen_area
.height
> 0)
2079 x
= self
->pre_fullscreen_area
.x
;
2080 y
= self
->pre_fullscreen_area
.y
;
2081 w
= self
->pre_fullscreen_area
.width
;
2082 h
= self
->pre_fullscreen_area
.height
;
2083 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2085 /* pick some fallbacks... */
2086 a
= screen_area_monitor(self
->desktop
, 0);
2087 x
= a
->x
+ a
->width
/ 4;
2088 y
= a
->y
+ a
->height
/ 4;
2094 client_setup_decor_and_functions(self
);
2096 client_move_resize(self
, x
, y
, w
, h
);
2098 /* try focus us when we go into fullscreen mode */
2102 static void client_iconify_recursive(ObClient
*self
,
2103 gboolean iconic
, gboolean curdesk
)
2106 gboolean changed
= FALSE
;
2109 if (self
->iconic
!= iconic
) {
2110 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2113 self
->iconic
= iconic
;
2116 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2119 old
= self
->wmstate
;
2120 self
->wmstate
= IconicState
;
2121 if (old
!= self
->wmstate
)
2122 PROP_MSG(self
->window
, kde_wm_change_state
,
2123 self
->wmstate
, 1, 0, 0);
2125 self
->ignore_unmaps
++;
2126 /* we unmap the client itself so that we can get MapRequest
2127 events, and because the ICCCM tells us to! */
2128 XUnmapWindow(ob_display
, self
->window
);
2130 /* update the focus lists.. iconic windows go to the bottom of
2131 the list, put the new iconic window at the 'top of the
2133 focus_order_to_top(self
);
2141 client_set_desktop(self
, screen_desktop
, FALSE
);
2143 old
= self
->wmstate
;
2144 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2145 if (old
!= self
->wmstate
)
2146 PROP_MSG(self
->window
, kde_wm_change_state
,
2147 self
->wmstate
, 1, 0, 0);
2149 XMapWindow(ob_display
, self
->window
);
2151 /* this puts it after the current focused window */
2152 focus_order_remove(self
);
2153 focus_order_add_new(self
);
2155 /* this is here cuz with the VIDMODE extension, the viewport can
2156 change while a fullscreen window is iconic, and when it
2157 uniconifies, it would be nice if it did so to the new position
2159 client_reconfigure(self
);
2166 client_change_state(self
);
2167 client_showhide(self
);
2168 screen_update_areas();
2171 /* iconify all transients */
2172 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2173 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2177 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2179 /* move up the transient chain as far as possible first */
2180 self
= client_search_top_transient(self
);
2182 client_iconify_recursive(client_search_top_transient(self
),
2186 void client_maximize(ObClient
*self
, gboolean max
, int dir
, gboolean savearea
)
2190 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2191 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2193 /* check if already done */
2195 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2196 if (dir
== 1 && self
->max_horz
) return;
2197 if (dir
== 2 && self
->max_vert
) return;
2199 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2200 if (dir
== 1 && !self
->max_horz
) return;
2201 if (dir
== 2 && !self
->max_vert
) return;
2204 /* we just tell it to configure in the same place and client_configure
2205 worries about filling the screen with the window */
2208 w
= self
->area
.width
;
2209 h
= self
->area
.height
;
2213 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2214 RECT_SET(self
->pre_max_area
,
2215 self
->area
.x
, self
->pre_max_area
.y
,
2216 self
->area
.width
, self
->pre_max_area
.height
);
2218 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2219 RECT_SET(self
->pre_max_area
,
2220 self
->pre_max_area
.x
, self
->area
.y
,
2221 self
->pre_max_area
.width
, self
->area
.height
);
2227 a
= screen_area_monitor(self
->desktop
, 0);
2228 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2229 if (self
->pre_max_area
.width
> 0) {
2230 x
= self
->pre_max_area
.x
;
2231 w
= self
->pre_max_area
.width
;
2233 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2234 0, self
->pre_max_area
.height
);
2236 /* pick some fallbacks... */
2237 x
= a
->x
+ a
->width
/ 4;
2241 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2242 if (self
->pre_max_area
.height
> 0) {
2243 y
= self
->pre_max_area
.y
;
2244 h
= self
->pre_max_area
.height
;
2246 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2247 self
->pre_max_area
.width
, 0);
2249 /* pick some fallbacks... */
2250 y
= a
->y
+ a
->height
/ 4;
2256 if (dir
== 0 || dir
== 1) /* horz */
2257 self
->max_horz
= max
;
2258 if (dir
== 0 || dir
== 2) /* vert */
2259 self
->max_vert
= max
;
2261 client_change_state(self
); /* change the state hints on the client */
2263 client_setup_decor_and_functions(self
);
2265 client_move_resize(self
, x
, y
, w
, h
);
2268 void client_shade(ObClient
*self
, gboolean shade
)
2270 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2271 shade
) || /* can't shade */
2272 self
->shaded
== shade
) return; /* already done */
2274 /* when we're iconic, don't change the wmstate */
2275 if (!self
->iconic
) {
2278 old
= self
->wmstate
;
2279 self
->wmstate
= shade
? IconicState
: NormalState
;
2280 if (old
!= self
->wmstate
)
2281 PROP_MSG(self
->window
, kde_wm_change_state
,
2282 self
->wmstate
, 1, 0, 0);
2285 self
->shaded
= shade
;
2286 client_change_state(self
);
2287 /* resize the frame to just the titlebar */
2288 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2291 void client_close(ObClient
*self
)
2295 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2298 XXX: itd be cool to do timeouts and shit here for killing the client's
2300 like... if the window is around after 5 seconds, then the close button
2301 turns a nice red, and if this function is called again, the client is
2305 ce
.xclient
.type
= ClientMessage
;
2306 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2307 ce
.xclient
.display
= ob_display
;
2308 ce
.xclient
.window
= self
->window
;
2309 ce
.xclient
.format
= 32;
2310 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2311 ce
.xclient
.data
.l
[1] = event_lasttime
;
2312 ce
.xclient
.data
.l
[2] = 0l;
2313 ce
.xclient
.data
.l
[3] = 0l;
2314 ce
.xclient
.data
.l
[4] = 0l;
2315 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2318 void client_kill(ObClient
*self
)
2320 XKillClient(ob_display
, self
->window
);
2323 void client_set_desktop_recursive(ObClient
*self
,
2324 guint target
, gboolean donthide
)
2329 if (target
!= self
->desktop
) {
2331 ob_debug("Setting desktop %u\n", target
+1);
2333 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2335 /* remove from the old desktop(s) */
2336 focus_order_remove(self
);
2338 old
= self
->desktop
;
2339 self
->desktop
= target
;
2340 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2341 /* the frame can display the current desktop state */
2342 frame_adjust_state(self
->frame
);
2343 /* 'move' the window to the new desktop */
2345 client_showhide(self
);
2346 /* raise if it was not already on the desktop */
2347 if (old
!= DESKTOP_ALL
)
2349 screen_update_areas();
2351 /* add to the new desktop(s) */
2352 if (config_focus_new
)
2353 focus_order_to_top(self
);
2355 focus_order_to_bottom(self
);
2358 /* move all transients */
2359 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2360 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2364 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2366 client_set_desktop_recursive(client_search_top_transient(self
),
2370 ObClient
*client_search_modal_child(ObClient
*self
)
2375 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
2376 ObClient
*c
= it
->data
;
2377 if ((ret
= client_search_modal_child(c
))) return ret
;
2378 if (c
->modal
) return c
;
2383 gboolean
client_validate(ObClient
*self
)
2387 XSync(ob_display
, FALSE
); /* get all events on the server */
2389 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2390 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2391 XPutBackEvent(ob_display
, &e
);
2398 void client_set_wm_state(ObClient
*self
, long state
)
2400 if (state
== self
->wmstate
) return; /* no change */
2404 client_iconify(self
, TRUE
, TRUE
);
2407 client_iconify(self
, FALSE
, TRUE
);
2412 void client_set_state(ObClient
*self
, Atom action
, long data1
, long data2
)
2414 gboolean shaded
= self
->shaded
;
2415 gboolean fullscreen
= self
->fullscreen
;
2416 gboolean undecorated
= self
->undecorated
;
2417 gboolean max_horz
= self
->max_horz
;
2418 gboolean max_vert
= self
->max_vert
;
2421 if (!(action
== prop_atoms
.net_wm_state_add
||
2422 action
== prop_atoms
.net_wm_state_remove
||
2423 action
== prop_atoms
.net_wm_state_toggle
))
2424 /* an invalid action was passed to the client message, ignore it */
2427 for (i
= 0; i
< 2; ++i
) {
2428 Atom state
= i
== 0 ? data1
: data2
;
2430 if (!state
) continue;
2432 /* if toggling, then pick whether we're adding or removing */
2433 if (action
== prop_atoms
.net_wm_state_toggle
) {
2434 if (state
== prop_atoms
.net_wm_state_modal
)
2435 action
= self
->modal
? prop_atoms
.net_wm_state_remove
:
2436 prop_atoms
.net_wm_state_add
;
2437 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2438 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2439 prop_atoms
.net_wm_state_add
;
2440 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2441 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2442 prop_atoms
.net_wm_state_add
;
2443 else if (state
== prop_atoms
.net_wm_state_shaded
)
2444 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2445 prop_atoms
.net_wm_state_add
;
2446 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2447 action
= self
->skip_taskbar
?
2448 prop_atoms
.net_wm_state_remove
:
2449 prop_atoms
.net_wm_state_add
;
2450 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2451 action
= self
->skip_pager
?
2452 prop_atoms
.net_wm_state_remove
:
2453 prop_atoms
.net_wm_state_add
;
2454 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2455 action
= fullscreen
?
2456 prop_atoms
.net_wm_state_remove
:
2457 prop_atoms
.net_wm_state_add
;
2458 else if (state
== prop_atoms
.net_wm_state_above
)
2459 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2460 prop_atoms
.net_wm_state_add
;
2461 else if (state
== prop_atoms
.net_wm_state_below
)
2462 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2463 prop_atoms
.net_wm_state_add
;
2464 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2465 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2466 prop_atoms
.net_wm_state_add
;
2469 if (action
== prop_atoms
.net_wm_state_add
) {
2470 if (state
== prop_atoms
.net_wm_state_modal
) {
2471 /* XXX raise here or something? */
2473 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2475 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2477 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2479 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2480 self
->skip_taskbar
= TRUE
;
2481 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2482 self
->skip_pager
= TRUE
;
2483 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2485 } else if (state
== prop_atoms
.net_wm_state_above
) {
2487 } else if (state
== prop_atoms
.net_wm_state_below
) {
2489 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2493 } else { /* action == prop_atoms.net_wm_state_remove */
2494 if (state
== prop_atoms
.net_wm_state_modal
) {
2495 self
->modal
= FALSE
;
2496 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2498 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2500 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2502 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2503 self
->skip_taskbar
= FALSE
;
2504 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2505 self
->skip_pager
= FALSE
;
2506 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2508 } else if (state
== prop_atoms
.net_wm_state_above
) {
2509 self
->above
= FALSE
;
2510 } else if (state
== prop_atoms
.net_wm_state_below
) {
2511 self
->below
= FALSE
;
2512 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2513 undecorated
= FALSE
;
2517 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2518 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2520 if (max_horz
== max_vert
) { /* both going the same way */
2521 client_maximize(self
, max_horz
, 0, TRUE
);
2523 client_maximize(self
, max_horz
, 1, TRUE
);
2524 client_maximize(self
, max_vert
, 2, TRUE
);
2528 if (max_horz
!= self
->max_horz
)
2529 client_maximize(self
, max_horz
, 1, TRUE
);
2531 client_maximize(self
, max_vert
, 2, TRUE
);
2534 /* change fullscreen state before shading, as it will affect if the window
2536 if (fullscreen
!= self
->fullscreen
)
2537 client_fullscreen(self
, fullscreen
, TRUE
);
2538 if (shaded
!= self
->shaded
)
2539 client_shade(self
, shaded
);
2540 if (undecorated
!= self
->undecorated
)
2541 client_set_undecorated(self
, undecorated
);
2542 client_calc_layer(self
);
2543 client_change_state(self
); /* change the hint to reflect these changes */
2546 ObClient
*client_focus_target(ObClient
*self
)
2550 /* if we have a modal child, then focus it, not us */
2551 child
= client_search_modal_child(client_search_top_transient(self
));
2552 if (child
) return child
;
2556 gboolean
client_can_focus(ObClient
*self
)
2560 /* choose the correct target */
2561 self
= client_focus_target(self
);
2563 if (!self
->frame
->visible
)
2566 if (!(self
->can_focus
|| self
->focus_notify
))
2569 /* do a check to see if the window has already been unmapped or destroyed
2570 do this intelligently while watching out for unmaps we've generated
2571 (ignore_unmaps > 0) */
2572 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2573 DestroyNotify
, &ev
)) {
2574 XPutBackEvent(ob_display
, &ev
);
2577 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2578 UnmapNotify
, &ev
)) {
2579 if (self
->ignore_unmaps
) {
2580 self
->ignore_unmaps
--;
2582 XPutBackEvent(ob_display
, &ev
);
2590 gboolean
client_focus(ObClient
*self
)
2592 /* choose the correct target */
2593 self
= client_focus_target(self
);
2595 if (!client_can_focus(self
)) {
2596 if (!self
->frame
->visible
) {
2597 /* update the focus lists */
2598 focus_order_to_top(self
);
2603 if (self
->can_focus
) {
2604 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2605 I choose to use it always, hopefully to find errors quicker, if any
2606 are left. (I hate X. I hate focus events.)
2608 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2609 #799. So now it is RevertToNone again.
2611 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2615 if (self
->focus_notify
) {
2617 ce
.xclient
.type
= ClientMessage
;
2618 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2619 ce
.xclient
.display
= ob_display
;
2620 ce
.xclient
.window
= self
->window
;
2621 ce
.xclient
.format
= 32;
2622 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2623 ce
.xclient
.data
.l
[1] = event_lasttime
;
2624 ce
.xclient
.data
.l
[2] = 0l;
2625 ce
.xclient
.data
.l
[3] = 0l;
2626 ce
.xclient
.data
.l
[4] = 0l;
2627 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2631 ob_debug("%sively focusing %lx at %d\n",
2632 (self
->can_focus
? "act" : "pass"),
2633 self
->window
, (int) event_lasttime
);
2636 /* Cause the FocusIn to come back to us. Important for desktop switches,
2637 since otherwise we'll have no FocusIn on the queue and send it off to
2638 the focus_backup. */
2639 XSync(ob_display
, FALSE
);
2643 void client_unfocus(ObClient
*self
)
2645 if (focus_client
== self
) {
2647 ob_debug("client_unfocus for %lx\n", self
->window
);
2649 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2653 void client_activate(ObClient
*self
, gboolean here
)
2655 if (client_normal(self
) && screen_showing_desktop
)
2656 screen_show_desktop(FALSE
);
2658 client_iconify(self
, FALSE
, here
);
2659 if (self
->desktop
!= DESKTOP_ALL
&&
2660 self
->desktop
!= screen_desktop
) {
2662 client_set_desktop(self
, screen_desktop
, FALSE
);
2664 screen_set_desktop(self
->desktop
);
2665 } else if (!self
->frame
->visible
)
2666 /* if its not visible for other reasons, then don't mess
2670 client_shade(self
, FALSE
);
2674 /* we do this an action here. this is rather important. this is because
2675 we want the results from the focus change to take place BEFORE we go
2676 about raising the window. when a fullscreen window loses focus, we need
2677 this or else the raise wont be able to raise above the to-lose-focus
2678 fullscreen window. */
2682 void client_raise(ObClient
*self
)
2684 action_run_string("Raise", self
);
2687 void client_lower(ObClient
*self
)
2689 action_run_string("Raise", self
);
2692 gboolean
client_focused(ObClient
*self
)
2694 return self
== focus_client
;
2697 static ObClientIcon
* client_icon_recursive(ObClient
*self
, int w
, int h
)
2700 /* si is the smallest image >= req */
2701 /* li is the largest image < req */
2702 unsigned long size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2704 if (!self
->nicons
) {
2705 ObClientIcon
*parent
= NULL
;
2707 if (self
->transient_for
) {
2708 if (self
->transient_for
!= OB_TRAN_GROUP
)
2709 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2712 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2713 ObClient
*c
= it
->data
;
2714 if (c
!= self
&& !c
->transient_for
) {
2715 if ((parent
= client_icon_recursive(c
, w
, h
)))
2725 for (i
= 0; i
< self
->nicons
; ++i
) {
2726 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2727 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2731 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2736 if (largest
== 0) /* didnt find one smaller than the requested size */
2737 return &self
->icons
[si
];
2738 return &self
->icons
[li
];
2741 const ObClientIcon
* client_icon(ObClient
*self
, int w
, int h
)
2744 static ObClientIcon deficon
;
2746 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
2747 deficon
.width
= deficon
.height
= 48;
2748 deficon
.data
= ob_rr_theme
->def_win_icon
;
2754 /* this be mostly ripped from fvwm */
2755 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2757 int my_cx
, my_cy
, his_cx
, his_cy
;
2760 int score
, best_score
;
2761 ObClient
*best_client
, *cur
;
2767 /* first, find the centre coords of the currently focused window */
2768 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2769 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2774 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2777 /* the currently selected window isn't interesting */
2780 if (!client_normal(cur
))
2782 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2786 if(client_focus_target(cur
) == cur
&&
2787 !(cur
->can_focus
|| cur
->focus_notify
))
2790 /* find the centre coords of this window, from the
2791 * currently focused window's point of view */
2792 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2793 + cur
->frame
->area
.width
/ 2;
2794 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2795 + cur
->frame
->area
.height
/ 2;
2797 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
2798 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
2800 /* Rotate the diagonals 45 degrees counterclockwise.
2801 * To do this, multiply the matrix /+h +h\ with the
2802 * vector (x y). \-h +h/
2803 * h = sqrt(0.5). We can set h := 1 since absolute
2804 * distance doesn't matter here. */
2805 tx
= his_cx
+ his_cy
;
2806 his_cy
= -his_cx
+ his_cy
;
2811 case OB_DIRECTION_NORTH
:
2812 case OB_DIRECTION_SOUTH
:
2813 case OB_DIRECTION_NORTHEAST
:
2814 case OB_DIRECTION_SOUTHWEST
:
2815 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
2816 distance
= ((dir
== OB_DIRECTION_NORTH
||
2817 dir
== OB_DIRECTION_NORTHEAST
) ?
2820 case OB_DIRECTION_EAST
:
2821 case OB_DIRECTION_WEST
:
2822 case OB_DIRECTION_SOUTHEAST
:
2823 case OB_DIRECTION_NORTHWEST
:
2824 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
2825 distance
= ((dir
== OB_DIRECTION_WEST
||
2826 dir
== OB_DIRECTION_NORTHWEST
) ?
2831 /* the target must be in the requested direction */
2835 /* Calculate score for this window. The smaller the better. */
2836 score
= distance
+ offset
;
2838 /* windows more than 45 degrees off the direction are
2839 * heavily penalized and will only be chosen if nothing
2840 * else within a million pixels */
2841 if(offset
> distance
)
2844 if(best_score
== -1 || score
< best_score
)
2852 void client_set_layer(ObClient
*self
, int layer
)
2856 self
->above
= FALSE
;
2857 } else if (layer
== 0) {
2858 self
->below
= self
->above
= FALSE
;
2860 self
->below
= FALSE
;
2863 client_calc_layer(self
);
2864 client_change_state(self
); /* reflect this in the state hints */
2867 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
2869 if (self
->undecorated
!= undecorated
) {
2870 self
->undecorated
= undecorated
;
2871 client_setup_decor_and_functions(self
);
2872 client_change_state(self
); /* reflect this in the state hints */
2876 guint
client_monitor(ObClient
*self
)
2880 for (i
= 0; i
< screen_num_monitors
; ++i
) {
2881 Rect
*area
= screen_physical_area_monitor(i
);
2882 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
))
2885 if (i
== screen_num_monitors
) i
= 0;
2886 g_assert(i
< screen_num_monitors
);
2890 ObClient
*client_search_top_transient(ObClient
*self
)
2892 /* move up the transient chain as far as possible */
2893 if (self
->transient_for
) {
2894 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2895 return client_search_top_transient(self
->transient_for
);
2899 g_assert(self
->group
);
2901 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2902 ObClient
*c
= it
->data
;
2904 /* checking transient_for prevents infinate loops! */
2905 if (c
!= self
&& !c
->transient_for
)
2916 ObClient
*client_search_focus_parent(ObClient
*self
)
2918 if (self
->transient_for
) {
2919 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2920 if (client_focused(self
->transient_for
))
2921 return self
->transient_for
;
2925 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2926 ObClient
*c
= it
->data
;
2928 /* checking transient_for prevents infinate loops! */
2929 if (c
!= self
&& !c
->transient_for
)
2930 if (client_focused(c
))
2939 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
2941 if (self
->transient_for
) {
2942 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2943 if (self
->transient_for
== search
)
2948 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2949 ObClient
*c
= it
->data
;
2951 /* checking transient_for prevents infinate loops! */
2952 if (c
!= self
&& !c
->transient_for
)
2962 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
2966 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
2967 if (sit
->data
== search
)
2969 if (client_search_transient(sit
->data
, search
))
2975 void client_update_sm_client_id(ObClient
*self
)
2977 g_free(self
->sm_client_id
);
2978 self
->sm_client_id
= NULL
;
2980 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
2982 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
2983 &self
->sm_client_id
);
2986 /* finds the nearest edge in the given direction from the current client
2987 * note to self: the edge is the -frame- edge (the actual one), not the
2990 int client_directional_edge_search(ObClient
*c
, ObDirection dir
)
2993 int my_edge_start
, my_edge_end
, my_offset
;
3000 a
= screen_area(c
->desktop
);
3003 case OB_DIRECTION_NORTH
:
3004 my_edge_start
= c
->frame
->area
.x
;
3005 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3006 my_offset
= c
->frame
->area
.y
;
3008 dest
= a
->y
; /* default: top of screen */
3010 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3011 int his_edge_start
, his_edge_end
, his_offset
;
3012 ObClient
*cur
= it
->data
;
3016 if(!client_normal(cur
))
3018 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3023 his_edge_start
= cur
->frame
->area
.x
;
3024 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3025 his_offset
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3027 if(his_offset
+ 1 > my_offset
)
3030 if(his_offset
< dest
)
3033 if(his_edge_start
>= my_edge_start
&&
3034 his_edge_start
<= my_edge_end
)
3037 if(my_edge_start
>= his_edge_start
&&
3038 my_edge_start
<= his_edge_end
)
3043 case OB_DIRECTION_SOUTH
:
3044 my_edge_start
= c
->frame
->area
.x
;
3045 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3046 my_offset
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3048 dest
= a
->y
+ a
->height
; /* default: bottom of screen */
3050 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3051 int his_edge_start
, his_edge_end
, his_offset
;
3052 ObClient
*cur
= it
->data
;
3056 if(!client_normal(cur
))
3058 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3063 his_edge_start
= cur
->frame
->area
.x
;
3064 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3065 his_offset
= cur
->frame
->area
.y
;
3068 if(his_offset
- 1 < my_offset
)
3071 if(his_offset
> dest
)
3074 if(his_edge_start
>= my_edge_start
&&
3075 his_edge_start
<= my_edge_end
)
3078 if(my_edge_start
>= his_edge_start
&&
3079 my_edge_start
<= his_edge_end
)
3084 case OB_DIRECTION_WEST
:
3085 my_edge_start
= c
->frame
->area
.y
;
3086 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3087 my_offset
= c
->frame
->area
.x
;
3089 dest
= a
->x
; /* default: leftmost egde of screen */
3091 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3092 int his_edge_start
, his_edge_end
, his_offset
;
3093 ObClient
*cur
= it
->data
;
3097 if(!client_normal(cur
))
3099 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3104 his_edge_start
= cur
->frame
->area
.y
;
3105 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3106 his_offset
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3108 if(his_offset
+ 1 > my_offset
)
3111 if(his_offset
< dest
)
3114 if(his_edge_start
>= my_edge_start
&&
3115 his_edge_start
<= my_edge_end
)
3118 if(my_edge_start
>= his_edge_start
&&
3119 my_edge_start
<= his_edge_end
)
3125 case OB_DIRECTION_EAST
:
3126 my_edge_start
= c
->frame
->area
.y
;
3127 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3128 my_offset
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3130 dest
= a
->x
+ a
->width
; /* default: rightmost edge of screen */
3132 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3133 int his_edge_start
, his_edge_end
, his_offset
;
3134 ObClient
*cur
= it
->data
;
3138 if(!client_normal(cur
))
3140 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3145 his_edge_start
= cur
->frame
->area
.y
;
3146 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3147 his_offset
= cur
->frame
->area
.x
;
3149 if(his_offset
- 1 < my_offset
)
3152 if(his_offset
> dest
)
3155 if(his_edge_start
>= my_edge_start
&&
3156 his_edge_start
<= my_edge_end
)
3159 if(my_edge_start
>= his_edge_start
&&
3160 my_edge_start
<= his_edge_end
)
3165 case OB_DIRECTION_NORTHEAST
:
3166 case OB_DIRECTION_SOUTHEAST
:
3167 case OB_DIRECTION_NORTHWEST
:
3168 case OB_DIRECTION_SOUTHWEST
:
3169 /* not implemented */
3171 g_assert_not_reached();
3176 ObClient
* client_under_pointer()
3180 ObClient
*ret
= NULL
;
3182 if (screen_pointer_pos(&x
, &y
)) {
3183 for (it
= stacking_list
; it
!= NULL
; it
= it
->next
) {
3184 if (WINDOW_IS_CLIENT(it
->data
)) {
3185 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3186 if (c
->frame
->visible
&&
3187 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {