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 /* non-normal clients has less rules, and
350 windows that are being restored from a session
351 do also. we can assume you want it back where
353 client_normal(self
) && !self
->session
);
355 if (x
!= ox
|| y
!= oy
)
356 client_move(self
, x
, y
);
359 client_showhide(self
);
361 /* use client_focus instead of client_activate cuz client_activate does
362 stuff like switch desktops etc and I'm not interested in all that when
363 a window maps since its not based on an action from the user like
364 clicking a window to activate is. so keep the new window out of the way
367 /* if using focus_delay, stop the timer now so that focus doesn't go
369 event_halt_focus_delay();
372 /* since focus can change the stacking orders, if we focus the window
373 then the standard raise it gets is not enough, we need to queue one
374 for after the focus change takes place */
378 /* client_activate does this but we aret using it so we have to do it
380 if (screen_showing_desktop
)
381 screen_show_desktop(FALSE
);
383 /* add to client list/map */
384 client_list
= g_list_append(client_list
, self
);
385 g_hash_table_insert(window_map
, &self
->window
, self
);
387 /* this has to happen after we're in the client_list */
388 screen_update_areas();
390 /* update the list hints */
393 keyboard_grab_for_client(self
, TRUE
);
394 mouse_grab_for_client(self
, TRUE
);
396 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
399 void client_unmanage_all()
401 while (client_list
!= NULL
)
402 client_unmanage(client_list
->data
);
405 void client_unmanage(ObClient
*self
)
410 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
412 g_assert(self
!= NULL
);
414 keyboard_grab_for_client(self
, FALSE
);
415 mouse_grab_for_client(self
, FALSE
);
417 /* remove the window from our save set */
418 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
420 /* we dont want events no more */
421 XSelectInput(ob_display
, self
->window
, NoEventMask
);
423 frame_hide(self
->frame
);
425 client_list
= g_list_remove(client_list
, self
);
426 stacking_remove(self
);
427 g_hash_table_remove(window_map
, &self
->window
);
429 /* update the focus lists */
430 focus_order_remove(self
);
432 /* once the client is out of the list, update the struts to remove it's
434 screen_update_areas();
436 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
437 Destructor
*d
= it
->data
;
438 d
->func(self
, d
->data
);
441 if (focus_client
== self
) {
444 /* focus the last focused window on the desktop, and ignore enter
445 events from the unmap so it doesnt mess with the focus */
446 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
447 /* remove these flags so we don't end up getting focused in the
449 self
->can_focus
= FALSE
;
450 self
->focus_notify
= FALSE
;
452 client_unfocus(self
);
455 /* tell our parent(s) that we're gone */
456 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
459 for (it
= self
->group
->members
; it
; it
= it
->next
)
460 if (it
->data
!= self
)
461 ((ObClient
*)it
->data
)->transients
=
462 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
463 } else if (self
->transient_for
) { /* transient of window */
464 self
->transient_for
->transients
=
465 g_slist_remove(self
->transient_for
->transients
, self
);
468 /* tell our transients that we're gone */
469 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
470 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
471 ((ObClient
*)it
->data
)->transient_for
= NULL
;
472 client_calc_layer(it
->data
);
476 /* remove from its group */
478 group_remove(self
->group
, self
);
482 /* give the client its border back */
483 client_toggle_border(self
, TRUE
);
485 /* reparent the window out of the frame, and free the frame */
486 frame_release_client(self
->frame
, self
);
489 if (ob_state() != OB_STATE_EXITING
) {
490 /* these values should not be persisted across a window
492 PROP_ERASE(self
->window
, net_wm_desktop
);
493 PROP_ERASE(self
->window
, net_wm_state
);
494 PROP_ERASE(self
->window
, wm_state
);
496 /* if we're left in an iconic state, the client wont be mapped. this is
497 bad, since we will no longer be managing the window on restart */
499 XMapWindow(ob_display
, self
->window
);
503 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
505 /* free all data allocated in the client struct */
506 g_slist_free(self
->transients
);
507 for (j
= 0; j
< self
->nicons
; ++j
)
508 g_free(self
->icons
[j
].data
);
509 if (self
->nicons
> 0)
512 g_free(self
->icon_title
);
516 g_free(self
->sm_client_id
);
519 /* update the list hints */
523 static void client_urgent_notify(ObClient
*self
)
526 frame_flash_start(self
->frame
);
528 frame_flash_stop(self
->frame
);
531 static void client_restore_session_state(ObClient
*self
)
535 if (!(it
= session_state_find(self
)))
538 self
->session
= it
->data
;
540 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
541 self
->positioned
= TRUE
;
542 if (self
->session
->w
> 0)
543 self
->area
.width
= self
->session
->w
;
544 if (self
->session
->h
> 0)
545 self
->area
.height
= self
->session
->h
;
546 XResizeWindow(ob_display
, self
->window
,
547 self
->area
.width
, self
->area
.height
);
549 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
550 self
->session
->desktop
:
551 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
552 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
554 self
->shaded
= self
->session
->shaded
;
555 self
->iconic
= self
->session
->iconic
;
556 self
->skip_pager
= self
->session
->skip_pager
;
557 self
->skip_taskbar
= self
->session
->skip_taskbar
;
558 self
->fullscreen
= self
->session
->fullscreen
;
559 self
->above
= self
->session
->above
;
560 self
->below
= self
->session
->below
;
561 self
->max_horz
= self
->session
->max_horz
;
562 self
->max_vert
= self
->session
->max_vert
;
565 static void client_restore_session_stacking(ObClient
*self
)
569 if (!self
->session
) return;
571 it
= g_list_find(session_saved_state
, self
->session
);
572 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
575 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
576 if (session_state_cmp(it
->data
, cit
->data
))
579 client_calc_layer(self
);
580 stacking_below(CLIENT_AS_WINDOW(self
),
581 CLIENT_AS_WINDOW(cit
->data
));
587 void client_move_onscreen(ObClient
*self
, gboolean rude
)
589 int x
= self
->area
.x
;
590 int y
= self
->area
.y
;
591 if (client_find_onscreen(self
, &x
, &y
,
592 self
->frame
->area
.width
,
593 self
->frame
->area
.height
, rude
)) {
594 client_move(self
, x
, y
);
598 gboolean
client_find_onscreen(ObClient
*self
, int *x
, int *y
, int w
, int h
,
602 int ox
= *x
, oy
= *y
;
604 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
607 /* XXX watch for xinerama dead areas */
609 a
= screen_area(self
->desktop
);
610 if (client_normal(self
)) {
611 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
612 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
613 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
614 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
615 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
617 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
622 /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
623 Java can suck it too. */
625 /* dont let windows map/move into the strut unless they
626 are bigger than the available area */
628 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
629 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
630 *x
= a
->x
+ a
->width
- w
;
632 if (h
<= a
->height
) {
633 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
634 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
635 *y
= a
->y
+ a
->height
- h
;
639 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
642 return ox
!= *x
|| oy
!= *y
;
645 static void client_toggle_border(ObClient
*self
, gboolean show
)
647 /* adjust our idea of where the client is, based on its border. When the
648 border is removed, the client should now be considered to be in a
650 when re-adding the border to the client, the same operation needs to be
652 int oldx
= self
->area
.x
, oldy
= self
->area
.y
;
653 int x
= oldx
, y
= oldy
;
654 switch(self
->gravity
) {
656 case NorthWestGravity
:
658 case SouthWestGravity
:
660 case NorthEastGravity
:
662 case SouthEastGravity
:
663 if (show
) x
-= self
->border_width
* 2;
664 else x
+= self
->border_width
* 2;
671 if (show
) x
-= self
->border_width
;
672 else x
+= self
->border_width
;
675 switch(self
->gravity
) {
677 case NorthWestGravity
:
679 case NorthEastGravity
:
681 case SouthWestGravity
:
683 case SouthEastGravity
:
684 if (show
) y
-= self
->border_width
* 2;
685 else y
+= self
->border_width
* 2;
692 if (show
) y
-= self
->border_width
;
693 else y
+= self
->border_width
;
700 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
702 /* move the client so it is back it the right spot _with_ its
704 if (x
!= oldx
|| y
!= oldy
)
705 XMoveWindow(ob_display
, self
->window
, x
, y
);
707 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
711 static void client_get_all(ObClient
*self
)
713 client_get_area(self
);
714 client_update_transient_for(self
);
715 client_update_wmhints(self
);
716 client_get_startup_id(self
);
717 client_get_desktop(self
);
718 client_get_state(self
);
719 client_get_shaped(self
);
721 client_get_mwm_hints(self
);
722 client_get_type(self
);/* this can change the mwmhints for special cases */
725 /* a couple type-based defaults for new windows */
727 /* this makes sure that these windows appear on all desktops */
728 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
729 self
->desktop
= DESKTOP_ALL
;
731 /* dock windows default to ABOVE */
732 if (self
->type
== OB_CLIENT_TYPE_DOCK
&& !self
->below
)
736 client_update_protocols(self
);
738 client_get_gravity(self
); /* get the attribute gravity */
739 client_update_normal_hints(self
); /* this may override the attribute
742 /* got the type, the mwmhints, the protocols, and the normal hints
743 (min/max sizes), so we're ready to set up the decorations/functions */
744 client_setup_decor_and_functions(self
);
746 client_update_title(self
);
747 client_update_class(self
);
748 client_update_sm_client_id(self
);
749 client_update_strut(self
);
750 client_update_icons(self
);
753 static void client_get_startup_id(ObClient
*self
)
755 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
757 PROP_GETS(self
->group
->leader
,
758 net_startup_id
, utf8
, &self
->startup_id
);
761 static void client_get_area(ObClient
*self
)
763 XWindowAttributes wattrib
;
766 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
767 g_assert(ret
!= BadWindow
);
769 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
770 self
->border_width
= wattrib
.border_width
;
773 static void client_get_desktop(ObClient
*self
)
775 guint32 d
= screen_num_desktops
; /* an always-invalid value */
777 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
778 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
779 self
->desktop
= screen_num_desktops
- 1;
783 gboolean trdesk
= FALSE
;
785 if (self
->transient_for
) {
786 if (self
->transient_for
!= OB_TRAN_GROUP
) {
787 self
->desktop
= self
->transient_for
->desktop
;
792 for (it
= self
->group
->members
; it
; it
= it
->next
)
793 if (it
->data
!= self
&&
794 !((ObClient
*)it
->data
)->transient_for
) {
795 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
802 /* try get from the startup-notification protocol */
803 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
804 if (self
->desktop
>= screen_num_desktops
&&
805 self
->desktop
!= DESKTOP_ALL
)
806 self
->desktop
= screen_num_desktops
- 1;
808 /* defaults to the current desktop */
809 self
->desktop
= screen_desktop
;
812 if (self
->desktop
!= d
) {
813 /* set the desktop hint, to make sure that it always exists */
814 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
818 static void client_get_state(ObClient
*self
)
823 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
825 for (i
= 0; i
< num
; ++i
) {
826 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
828 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
830 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
832 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
833 self
->skip_taskbar
= TRUE
;
834 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
835 self
->skip_pager
= TRUE
;
836 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
837 self
->fullscreen
= TRUE
;
838 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
839 self
->max_vert
= TRUE
;
840 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
841 self
->max_horz
= TRUE
;
842 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
844 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
846 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
847 self
->undecorated
= TRUE
;
853 if (!(self
->above
|| self
->below
)) {
854 if (client_has_group_siblings(self
)) {
855 /* apply stuff from the group */
859 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
860 ObClient
*c
= it
->data
;
861 if (c
!= self
&& !client_search_transient(self
, c
) &&
862 client_normal(c
) == client_normal(self
))
865 (c
->above
? 1 : (c
->below
? -1 : 0)));
878 g_assert_not_reached();
885 static void client_get_shaped(ObClient
*self
)
887 self
->shaped
= FALSE
;
889 if (extensions_shape
) {
894 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
896 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
897 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
899 self
->shaped
= (s
!= 0);
904 void client_update_transient_for(ObClient
*self
)
907 ObClient
*target
= NULL
;
909 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
910 self
->transient
= TRUE
;
911 if (t
!= self
->window
) { /* cant be transient to itself! */
912 target
= g_hash_table_lookup(window_map
, &t
);
913 /* if this happens then we need to check for it*/
914 g_assert(target
!= self
);
915 if (target
&& !WINDOW_IS_CLIENT(target
)) {
916 /* this can happen when a dialog is a child of
917 a dockapp, for example */
921 if (!target
&& self
->group
) {
922 /* not transient to a client, see if it is transient for a
924 if (t
== self
->group
->leader
||
926 t
== RootWindow(ob_display
, ob_screen
))
928 /* window is a transient for its group! */
929 target
= OB_TRAN_GROUP
;
934 self
->transient
= FALSE
;
936 /* if anything has changed... */
937 if (target
!= self
->transient_for
) {
938 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
941 /* remove from old parents */
942 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
943 ObClient
*c
= it
->data
;
944 if (c
!= self
&& !c
->transient_for
)
945 c
->transients
= g_slist_remove(c
->transients
, self
);
947 } else if (self
->transient_for
!= NULL
) { /* transient of window */
948 /* remove from old parent */
949 self
->transient_for
->transients
=
950 g_slist_remove(self
->transient_for
->transients
, self
);
952 self
->transient_for
= target
;
953 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
956 /* add to new parents */
957 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
958 ObClient
*c
= it
->data
;
959 if (c
!= self
&& !c
->transient_for
)
960 c
->transients
= g_slist_append(c
->transients
, self
);
963 /* remove all transients which are in the group, that causes
964 circlular pointer hell of doom */
965 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
967 for (sit
= self
->transients
; sit
; sit
= next
) {
968 next
= g_slist_next(sit
);
969 if (sit
->data
== it
->data
)
971 g_slist_delete_link(self
->transients
, sit
);
974 } else if (self
->transient_for
!= NULL
) { /* transient of window */
975 /* add to new parent */
976 self
->transient_for
->transients
=
977 g_slist_append(self
->transient_for
->transients
, self
);
982 static void client_get_mwm_hints(ObClient
*self
)
987 self
->mwmhints
.flags
= 0; /* default to none */
989 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
991 if (num
>= OB_MWM_ELEMENTS
) {
992 self
->mwmhints
.flags
= hints
[0];
993 self
->mwmhints
.functions
= hints
[1];
994 self
->mwmhints
.decorations
= hints
[2];
1000 void client_get_type(ObClient
*self
)
1007 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1008 /* use the first value that we know about in the array */
1009 for (i
= 0; i
< num
; ++i
) {
1010 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1011 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1012 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1013 self
->type
= OB_CLIENT_TYPE_DOCK
;
1014 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1015 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1016 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1017 self
->type
= OB_CLIENT_TYPE_MENU
;
1018 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1019 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1020 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1021 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1022 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1023 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1024 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1025 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1026 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1027 /* prevent this window from getting any decor or
1029 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1030 OB_MWM_FLAG_DECORATIONS
);
1031 self
->mwmhints
.decorations
= 0;
1032 self
->mwmhints
.functions
= 0;
1034 if (self
->type
!= (ObClientType
) -1)
1035 break; /* grab the first legit type */
1040 if (self
->type
== (ObClientType
) -1) {
1041 /*the window type hint was not set, which means we either classify
1042 ourself as a normal window or a dialog, depending on if we are a
1044 if (self
->transient
)
1045 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1047 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1051 void client_update_protocols(ObClient
*self
)
1054 guint num_return
, i
;
1056 self
->focus_notify
= FALSE
;
1057 self
->delete_window
= FALSE
;
1059 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1060 for (i
= 0; i
< num_return
; ++i
) {
1061 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1062 /* this means we can request the window to close */
1063 self
->delete_window
= TRUE
;
1064 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1065 /* if this protocol is requested, then the window will be
1066 notified whenever we want it to receive focus */
1067 self
->focus_notify
= TRUE
;
1073 static void client_get_gravity(ObClient
*self
)
1075 XWindowAttributes wattrib
;
1078 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1079 g_assert(ret
!= BadWindow
);
1080 self
->gravity
= wattrib
.win_gravity
;
1083 void client_update_normal_hints(ObClient
*self
)
1087 int oldgravity
= self
->gravity
;
1090 self
->min_ratio
= 0.0f
;
1091 self
->max_ratio
= 0.0f
;
1092 SIZE_SET(self
->size_inc
, 1, 1);
1093 SIZE_SET(self
->base_size
, 0, 0);
1094 SIZE_SET(self
->min_size
, 0, 0);
1095 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1097 /* get the hints from the window */
1098 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1099 self
->positioned
= !!(size
.flags
& (PPosition
|USPosition
));
1101 if (size
.flags
& PWinGravity
) {
1102 self
->gravity
= size
.win_gravity
;
1104 /* if the client has a frame, i.e. has already been mapped and
1105 is changing its gravity */
1106 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1107 /* move our idea of the client's position based on its new
1109 self
->area
.x
= self
->frame
->area
.x
;
1110 self
->area
.y
= self
->frame
->area
.y
;
1111 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1115 if (size
.flags
& PAspect
) {
1116 if (size
.min_aspect
.y
)
1117 self
->min_ratio
= (float)size
.min_aspect
.x
/ size
.min_aspect
.y
;
1118 if (size
.max_aspect
.y
)
1119 self
->max_ratio
= (float)size
.max_aspect
.x
/ size
.max_aspect
.y
;
1122 if (size
.flags
& PMinSize
)
1123 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1125 if (size
.flags
& PMaxSize
)
1126 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1128 if (size
.flags
& PBaseSize
)
1129 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1131 if (size
.flags
& PResizeInc
)
1132 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1136 void client_setup_decor_and_functions(ObClient
*self
)
1138 /* start with everything (cept fullscreen) */
1140 (OB_FRAME_DECOR_TITLEBAR
|
1141 (ob_rr_theme
->show_handle
? OB_FRAME_DECOR_HANDLE
: 0) |
1142 OB_FRAME_DECOR_GRIPS
|
1143 OB_FRAME_DECOR_BORDER
|
1144 OB_FRAME_DECOR_ICON
|
1145 OB_FRAME_DECOR_ALLDESKTOPS
|
1146 OB_FRAME_DECOR_ICONIFY
|
1147 OB_FRAME_DECOR_MAXIMIZE
|
1148 OB_FRAME_DECOR_SHADE
|
1149 OB_FRAME_DECOR_CLOSE
);
1151 (OB_CLIENT_FUNC_RESIZE
|
1152 OB_CLIENT_FUNC_MOVE
|
1153 OB_CLIENT_FUNC_ICONIFY
|
1154 OB_CLIENT_FUNC_MAXIMIZE
|
1155 OB_CLIENT_FUNC_SHADE
|
1156 OB_CLIENT_FUNC_CLOSE
);
1158 if (!(self
->min_size
.width
< self
->max_size
.width
||
1159 self
->min_size
.height
< self
->max_size
.height
))
1160 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1162 switch (self
->type
) {
1163 case OB_CLIENT_TYPE_NORMAL
:
1164 /* normal windows retain all of the possible decorations and
1165 functionality, and are the only windows that you can fullscreen */
1166 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1169 case OB_CLIENT_TYPE_DIALOG
:
1170 case OB_CLIENT_TYPE_UTILITY
:
1171 /* these windows cannot be maximized */
1172 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1175 case OB_CLIENT_TYPE_MENU
:
1176 case OB_CLIENT_TYPE_TOOLBAR
:
1177 /* these windows get less functionality */
1178 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1181 case OB_CLIENT_TYPE_DESKTOP
:
1182 case OB_CLIENT_TYPE_DOCK
:
1183 case OB_CLIENT_TYPE_SPLASH
:
1184 /* none of these windows are manipulated by the window manager */
1185 self
->decorations
= 0;
1186 self
->functions
= 0;
1190 /* Mwm Hints are applied subtractively to what has already been chosen for
1191 decor and functionality */
1192 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1193 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1194 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1195 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1196 /* if the mwm hints request no handle or title, then all
1197 decorations are disabled */
1198 self
->decorations
= 0;
1202 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1203 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1204 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1205 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1206 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1207 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1208 /* dont let mwm hints kill any buttons
1209 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1210 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1211 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1212 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1214 /* dont let mwm hints kill the close button
1215 if (! (self->mwmhints.functions & MwmFunc_Close))
1216 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1220 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1221 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1222 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1223 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1224 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1225 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1227 /* can't maximize without moving/resizing */
1228 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1229 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1230 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1231 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1232 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1235 /* kill the handle on fully maxed windows */
1236 if (self
->max_vert
&& self
->max_horz
)
1237 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1239 /* finally, the user can have requested no decorations, which overrides
1240 everything (but doesnt give it a border if it doesnt have one) */
1241 if (self
->undecorated
)
1242 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1244 /* if we don't have a titlebar, then we cannot shade! */
1245 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1246 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1248 /* now we need to check against rules for the client's current state */
1249 if (self
->fullscreen
) {
1250 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1251 OB_CLIENT_FUNC_FULLSCREEN
|
1252 OB_CLIENT_FUNC_ICONIFY
);
1253 self
->decorations
= 0;
1256 client_change_allowed_actions(self
);
1259 /* adjust the client's decorations, etc. */
1260 client_reconfigure(self
);
1264 static void client_change_allowed_actions(ObClient
*self
)
1269 /* desktop windows are kept on all desktops */
1270 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1271 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1273 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1274 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1275 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1276 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1277 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1278 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1279 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1280 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1281 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1282 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1283 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1284 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1285 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1286 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1287 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1290 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1292 /* make sure the window isn't breaking any rules now */
1294 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1295 if (self
->frame
) client_shade(self
, FALSE
);
1296 else self
->shaded
= FALSE
;
1298 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1299 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1300 else self
->iconic
= FALSE
;
1302 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1303 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1304 else self
->fullscreen
= FALSE
;
1306 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1308 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1309 else self
->max_vert
= self
->max_horz
= FALSE
;
1313 void client_reconfigure(ObClient
*self
)
1315 /* by making this pass FALSE for user, we avoid the emacs event storm where
1316 every configurenotify causes an update in its normal hints, i think this
1317 is generally what we want anyways... */
1318 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1319 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1322 void client_update_wmhints(ObClient
*self
)
1325 gboolean ur
= FALSE
;
1328 /* assume a window takes input if it doesnt specify */
1329 self
->can_focus
= TRUE
;
1331 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1332 if (hints
->flags
& InputHint
)
1333 self
->can_focus
= hints
->input
;
1335 /* only do this when first managing the window *AND* when we aren't
1337 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1338 if (hints
->flags
& StateHint
)
1339 self
->iconic
= hints
->initial_state
== IconicState
;
1341 if (hints
->flags
& XUrgencyHint
)
1344 if (!(hints
->flags
& WindowGroupHint
))
1345 hints
->window_group
= None
;
1347 /* did the group state change? */
1348 if (hints
->window_group
!=
1349 (self
->group
? self
->group
->leader
: None
)) {
1350 /* remove from the old group if there was one */
1351 if (self
->group
!= NULL
) {
1352 /* remove transients of the group */
1353 for (it
= self
->group
->members
; it
; it
= it
->next
)
1354 self
->transients
= g_slist_remove(self
->transients
,
1357 /* remove myself from parents in the group */
1358 if (self
->transient_for
== OB_TRAN_GROUP
) {
1359 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1360 ObClient
*c
= it
->data
;
1362 if (c
!= self
&& !c
->transient_for
)
1363 c
->transients
= g_slist_remove(c
->transients
,
1368 group_remove(self
->group
, self
);
1371 if (hints
->window_group
!= None
) {
1372 self
->group
= group_add(hints
->window_group
, self
);
1374 /* i can only have transients from the group if i am not
1376 if (!self
->transient_for
) {
1377 /* add other transients of the group that are already
1379 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1380 ObClient
*c
= it
->data
;
1381 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1383 g_slist_append(self
->transients
, c
);
1388 /* because the self->transient flag wont change from this call,
1389 we don't need to update the window's type and such, only its
1390 transient_for, and the transients lists of other windows in
1391 the group may be affected */
1392 client_update_transient_for(self
);
1395 /* the WM_HINTS can contain an icon */
1396 client_update_icons(self
);
1401 if (ur
!= self
->urgent
) {
1403 /* fire the urgent callback if we're mapped, otherwise, wait until
1404 after we're mapped */
1406 client_urgent_notify(self
);
1410 void client_update_title(ObClient
*self
)
1416 gboolean read_title
;
1419 old_title
= self
->title
;
1422 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
))
1423 /* try old x stuff */
1424 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
))
1425 data
= g_strdup("Unnamed Window");
1427 /* did the title change? then reset the title_count */
1428 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1429 self
->title_count
= 1;
1431 /* look for duplicates and append a number */
1433 for (it
= client_list
; it
; it
= it
->next
)
1434 if (it
->data
!= self
) {
1435 ObClient
*c
= it
->data
;
1436 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1437 nums
|= 1 << c
->title_count
;
1439 /* find first free number */
1440 for (i
= 1; i
<= 32; ++i
)
1441 if (!(nums
& (1 << i
))) {
1442 if (self
->title_count
== 1 || i
== 1)
1443 self
->title_count
= i
;
1446 /* dont display the number for the first window */
1447 if (self
->title_count
> 1) {
1449 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1454 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1459 frame_adjust_title(self
->frame
);
1463 /* update the icon title */
1465 g_free(self
->icon_title
);
1469 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1470 /* try old x stuff */
1471 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1472 data
= g_strdup(self
->title
);
1476 /* append the title count, dont display the number for the first window */
1477 if (read_title
&& self
->title_count
> 1) {
1478 char *vdata
, *ndata
;
1479 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1480 vdata
= g_strconcat(data
, ndata
, NULL
);
1486 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1488 self
->icon_title
= data
;
1491 void client_update_class(ObClient
*self
)
1496 if (self
->name
) g_free(self
->name
);
1497 if (self
->class) g_free(self
->class);
1498 if (self
->role
) g_free(self
->role
);
1500 self
->name
= self
->class = self
->role
= NULL
;
1502 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1504 self
->name
= g_strdup(data
[0]);
1506 self
->class = g_strdup(data
[1]);
1511 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1514 if (self
->name
== NULL
) self
->name
= g_strdup("");
1515 if (self
->class == NULL
) self
->class = g_strdup("");
1516 if (self
->role
== NULL
) self
->role
= g_strdup("");
1519 void client_update_strut(ObClient
*self
)
1523 gboolean got
= FALSE
;
1526 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1530 STRUT_PARTIAL_SET(strut
,
1531 data
[0], data
[2], data
[1], data
[3],
1532 data
[4], data
[5], data
[8], data
[9],
1533 data
[6], data
[7], data
[10], data
[11]);
1539 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1542 STRUT_PARTIAL_SET(strut
,
1543 data
[0], data
[2], data
[1], data
[3],
1544 0, 0, 0, 0, 0, 0, 0, 0);
1550 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1551 0, 0, 0, 0, 0, 0, 0, 0);
1553 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1554 self
->strut
= strut
;
1556 /* updating here is pointless while we're being mapped cuz we're not in
1557 the client list yet */
1559 screen_update_areas();
1563 void client_update_icons(ObClient
*self
)
1569 for (i
= 0; i
< self
->nicons
; ++i
)
1570 g_free(self
->icons
[i
].data
);
1571 if (self
->nicons
> 0)
1572 g_free(self
->icons
);
1575 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1576 /* figure out how many valid icons are in here */
1578 while (num
- i
> 2) {
1582 if (i
> num
|| w
*h
== 0) break;
1586 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1588 /* store the icons */
1590 for (j
= 0; j
< self
->nicons
; ++j
) {
1593 w
= self
->icons
[j
].width
= data
[i
++];
1594 h
= self
->icons
[j
].height
= data
[i
++];
1596 if (w
*h
== 0) continue;
1598 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1599 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1604 self
->icons
[j
].data
[t
] =
1605 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1606 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1607 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1608 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1614 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1615 kwm_win_icon
, &data
, &num
)) {
1618 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1619 xerror_set_ignore(TRUE
);
1620 if (!RrPixmapToRGBA(ob_rr_inst
,
1622 &self
->icons
[self
->nicons
-1].width
,
1623 &self
->icons
[self
->nicons
-1].height
,
1624 &self
->icons
[self
->nicons
-1].data
)) {
1625 g_free(&self
->icons
[self
->nicons
-1]);
1628 xerror_set_ignore(FALSE
);
1634 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1635 if (hints
->flags
& IconPixmapHint
) {
1637 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1638 xerror_set_ignore(TRUE
);
1639 if (!RrPixmapToRGBA(ob_rr_inst
,
1641 (hints
->flags
& IconMaskHint
?
1642 hints
->icon_mask
: None
),
1643 &self
->icons
[self
->nicons
-1].width
,
1644 &self
->icons
[self
->nicons
-1].height
,
1645 &self
->icons
[self
->nicons
-1].data
)){
1646 g_free(&self
->icons
[self
->nicons
-1]);
1649 xerror_set_ignore(FALSE
);
1656 frame_adjust_icon(self
->frame
);
1659 static void client_change_state(ObClient
*self
)
1662 guint32 netstate
[11];
1665 state
[0] = self
->wmstate
;
1667 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1671 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1673 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1675 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1676 if (self
->skip_taskbar
)
1677 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1678 if (self
->skip_pager
)
1679 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1680 if (self
->fullscreen
)
1681 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1683 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1685 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1687 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1689 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1690 if (self
->undecorated
)
1691 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1692 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1694 client_calc_layer(self
);
1697 frame_adjust_state(self
->frame
);
1700 ObClient
*client_search_focus_tree(ObClient
*self
)
1705 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
1706 if (client_focused(it
->data
)) return it
->data
;
1707 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1712 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1714 if (self
->transient_for
) {
1715 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1716 return client_search_focus_tree_full(self
->transient_for
);
1719 gboolean recursed
= FALSE
;
1721 for (it
= self
->group
->members
; it
; it
= it
->next
)
1722 if (!((ObClient
*)it
->data
)->transient_for
) {
1724 if ((c
= client_search_focus_tree_full(it
->data
)))
1733 /* this function checks the whole tree, the client_search_focus_tree~
1734 does not, so we need to check this window */
1735 if (client_focused(self
))
1737 return client_search_focus_tree(self
);
1740 static ObStackingLayer
calc_layer(ObClient
*self
)
1744 if (self
->fullscreen
&&
1745 (client_focused(self
) || client_search_focus_tree(self
)))
1746 l
= OB_STACKING_LAYER_FULLSCREEN
;
1747 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1748 l
= OB_STACKING_LAYER_DESKTOP
;
1749 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1750 if (self
->above
) l
= OB_STACKING_LAYER_DOCK_ABOVE
;
1751 else if (self
->below
) l
= OB_STACKING_LAYER_DOCK_BELOW
;
1752 else l
= OB_STACKING_LAYER_NORMAL
;
1754 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1755 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1756 else l
= OB_STACKING_LAYER_NORMAL
;
1761 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1762 ObStackingLayer l
, gboolean raised
)
1764 ObStackingLayer old
, own
;
1768 own
= calc_layer(self
);
1769 self
->layer
= l
> own
? l
: own
;
1771 for (it
= self
->transients
; it
; it
= it
->next
)
1772 client_calc_layer_recursive(it
->data
, orig
,
1773 l
, raised
? raised
: l
!= old
);
1775 if (!raised
&& l
!= old
)
1776 if (orig
->frame
) { /* only restack if the original window is managed */
1777 stacking_remove(CLIENT_AS_WINDOW(self
));
1778 stacking_add(CLIENT_AS_WINDOW(self
));
1782 void client_calc_layer(ObClient
*self
)
1789 /* transients take on the layer of their parents */
1790 self
= client_search_top_transient(self
);
1792 l
= calc_layer(self
);
1794 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1797 gboolean
client_should_show(ObClient
*self
)
1799 if (self
->iconic
) return FALSE
;
1800 else if (!(self
->desktop
== screen_desktop
||
1801 self
->desktop
== DESKTOP_ALL
)) return FALSE
;
1802 else if (client_normal(self
) && screen_showing_desktop
) return FALSE
;
1807 static void client_showhide(ObClient
*self
)
1810 if (client_should_show(self
))
1811 frame_show(self
->frame
);
1813 frame_hide(self
->frame
);
1816 gboolean
client_normal(ObClient
*self
) {
1817 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1818 self
->type
== OB_CLIENT_TYPE_DOCK
||
1819 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1822 static void client_apply_startup_state(ObClient
*self
)
1824 /* these are in a carefully crafted order.. */
1827 self
->iconic
= FALSE
;
1828 client_iconify(self
, TRUE
, FALSE
);
1830 if (self
->fullscreen
) {
1831 self
->fullscreen
= FALSE
;
1832 client_fullscreen(self
, TRUE
, FALSE
);
1834 if (self
->undecorated
) {
1835 self
->undecorated
= FALSE
;
1836 client_set_undecorated(self
, TRUE
);
1839 self
->shaded
= FALSE
;
1840 client_shade(self
, TRUE
);
1843 client_urgent_notify(self
);
1845 if (self
->max_vert
&& self
->max_horz
) {
1846 self
->max_vert
= self
->max_horz
= FALSE
;
1847 client_maximize(self
, TRUE
, 0, FALSE
);
1848 } else if (self
->max_vert
) {
1849 self
->max_vert
= FALSE
;
1850 client_maximize(self
, TRUE
, 2, FALSE
);
1851 } else if (self
->max_horz
) {
1852 self
->max_horz
= FALSE
;
1853 client_maximize(self
, TRUE
, 1, FALSE
);
1856 /* nothing to do for the other states:
1865 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1866 int x
, int y
, int w
, int h
,
1867 gboolean user
, gboolean final
,
1868 gboolean force_reply
)
1871 gboolean send_resize_client
;
1872 gboolean moved
= FALSE
, resized
= FALSE
;
1873 guint fdecor
= self
->frame
->decorations
;
1874 gboolean fhorz
= self
->frame
->max_horz
;
1876 /* make the frame recalculate its dimentions n shit without changing
1877 anything visible for real, this way the constraints below can work with
1878 the updated frame dimensions. */
1879 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
1881 /* gets the frame's position */
1882 frame_client_gravity(self
->frame
, &x
, &y
);
1884 /* these positions are frame positions, not client positions */
1886 /* set the size and position if fullscreen */
1887 if (self
->fullscreen
) {
1890 XF86VidModeModeLine mode
;
1895 i
= client_monitor(self
);
1896 a
= screen_physical_area_monitor(i
);
1899 if (i
== 0 && /* primary head */
1900 extensions_vidmode
&&
1901 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
1902 /* get the mode last so the mode.privsize isnt freed incorrectly */
1903 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
1908 if (mode
.privsize
) XFree(mode
.private);
1918 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
1922 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
1924 /* set the size and position if maximized */
1925 if (self
->max_horz
) {
1927 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
1929 if (self
->max_vert
) {
1931 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
1935 /* gets the client's position */
1936 frame_frame_gravity(self
->frame
, &x
, &y
);
1938 /* these override the above states! if you cant move you can't move! */
1940 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
1944 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
1945 w
= self
->area
.width
;
1946 h
= self
->area
.height
;
1950 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
1951 int basew
, baseh
, minw
, minh
;
1953 /* base size is substituted with min size if not specified */
1954 if (self
->base_size
.width
|| self
->base_size
.height
) {
1955 basew
= self
->base_size
.width
;
1956 baseh
= self
->base_size
.height
;
1958 basew
= self
->min_size
.width
;
1959 baseh
= self
->min_size
.height
;
1961 /* min size is substituted with base size if not specified */
1962 if (self
->min_size
.width
|| self
->min_size
.height
) {
1963 minw
= self
->min_size
.width
;
1964 minh
= self
->min_size
.height
;
1966 minw
= self
->base_size
.width
;
1967 minh
= self
->base_size
.height
;
1970 /* if this is a user-requested resize, then check against min/max
1973 /* smaller than min size or bigger than max size? */
1974 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
1975 if (w
< minw
) w
= minw
;
1976 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
1977 if (h
< minh
) h
= minh
;
1982 /* keep to the increments */
1983 w
/= self
->size_inc
.width
;
1984 h
/= self
->size_inc
.height
;
1986 /* you cannot resize to nothing */
1987 if (basew
+ w
< 1) w
= 1 - basew
;
1988 if (baseh
+ h
< 1) h
= 1 - baseh
;
1990 /* store the logical size */
1991 SIZE_SET(self
->logical_size
,
1992 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
1993 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
1995 w
*= self
->size_inc
.width
;
1996 h
*= self
->size_inc
.height
;
2001 /* adjust the height to match the width for the aspect ratios.
2002 for this, min size is not substituted for base size ever. */
2003 w
-= self
->base_size
.width
;
2004 h
-= self
->base_size
.height
;
2006 if (self
->min_ratio
)
2007 if (h
* self
->min_ratio
> w
) {
2008 h
= (int)(w
/ self
->min_ratio
);
2010 /* you cannot resize to nothing */
2013 w
= (int)(h
* self
->min_ratio
);
2016 if (self
->max_ratio
)
2017 if (h
* self
->max_ratio
< w
) {
2018 h
= (int)(w
/ self
->max_ratio
);
2020 /* you cannot resize to nothing */
2023 w
= (int)(h
* self
->min_ratio
);
2027 w
+= self
->base_size
.width
;
2028 h
+= self
->base_size
.height
;
2035 case OB_CORNER_TOPLEFT
:
2037 case OB_CORNER_TOPRIGHT
:
2038 x
-= w
- self
->area
.width
;
2040 case OB_CORNER_BOTTOMLEFT
:
2041 y
-= h
- self
->area
.height
;
2043 case OB_CORNER_BOTTOMRIGHT
:
2044 x
-= w
- self
->area
.width
;
2045 y
-= h
- self
->area
.height
;
2049 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2050 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2052 oldw
= self
->area
.width
;
2053 oldh
= self
->area
.height
;
2054 RECT_SET(self
->area
, x
, y
, w
, h
);
2056 /* for app-requested resizes, always resize if 'resized' is true.
2057 for user-requested ones, only resize if final is true, or when
2058 resizing in redraw mode */
2059 send_resize_client
= ((!user
&& resized
) ||
2061 (resized
&& config_redraw_resize
))));
2063 /* if the client is enlarging, the resize the client before the frame */
2064 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2065 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2067 /* move/resize the frame to match the request */
2069 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2070 moved
= resized
= TRUE
;
2072 if (moved
|| resized
)
2073 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2075 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2078 event
.type
= ConfigureNotify
;
2079 event
.xconfigure
.display
= ob_display
;
2080 event
.xconfigure
.event
= self
->window
;
2081 event
.xconfigure
.window
= self
->window
;
2083 /* root window real coords */
2084 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2086 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2088 event
.xconfigure
.width
= w
;
2089 event
.xconfigure
.height
= h
;
2090 event
.xconfigure
.border_width
= 0;
2091 event
.xconfigure
.above
= self
->frame
->plate
;
2092 event
.xconfigure
.override_redirect
= FALSE
;
2093 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2094 FALSE
, StructureNotifyMask
, &event
);
2098 /* if the client is shrinking, then resize the frame before the client */
2099 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2100 XResizeWindow(ob_display
, self
->window
, w
, h
);
2105 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2109 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2110 self
->fullscreen
== fs
) return; /* already done */
2112 self
->fullscreen
= fs
;
2113 client_change_state(self
); /* change the state hints on the client,
2114 and adjust out layer/stacking */
2118 self
->pre_fullscreen_area
= self
->area
;
2120 /* these are not actually used cuz client_configure will set them
2121 as appropriate when the window is fullscreened */
2126 if (self
->pre_fullscreen_area
.width
> 0 &&
2127 self
->pre_fullscreen_area
.height
> 0)
2129 x
= self
->pre_fullscreen_area
.x
;
2130 y
= self
->pre_fullscreen_area
.y
;
2131 w
= self
->pre_fullscreen_area
.width
;
2132 h
= self
->pre_fullscreen_area
.height
;
2133 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2135 /* pick some fallbacks... */
2136 a
= screen_area_monitor(self
->desktop
, 0);
2137 x
= a
->x
+ a
->width
/ 4;
2138 y
= a
->y
+ a
->height
/ 4;
2144 client_setup_decor_and_functions(self
);
2146 client_move_resize(self
, x
, y
, w
, h
);
2148 /* try focus us when we go into fullscreen mode */
2152 static void client_iconify_recursive(ObClient
*self
,
2153 gboolean iconic
, gboolean curdesk
)
2156 gboolean changed
= FALSE
;
2159 if (self
->iconic
!= iconic
) {
2160 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2163 self
->iconic
= iconic
;
2166 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2169 old
= self
->wmstate
;
2170 self
->wmstate
= IconicState
;
2171 if (old
!= self
->wmstate
)
2172 PROP_MSG(self
->window
, kde_wm_change_state
,
2173 self
->wmstate
, 1, 0, 0);
2175 self
->ignore_unmaps
++;
2176 /* we unmap the client itself so that we can get MapRequest
2177 events, and because the ICCCM tells us to! */
2178 XUnmapWindow(ob_display
, self
->window
);
2180 /* update the focus lists.. iconic windows go to the bottom of
2181 the list, put the new iconic window at the 'top of the
2183 focus_order_to_top(self
);
2191 client_set_desktop(self
, screen_desktop
, FALSE
);
2193 old
= self
->wmstate
;
2194 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2195 if (old
!= self
->wmstate
)
2196 PROP_MSG(self
->window
, kde_wm_change_state
,
2197 self
->wmstate
, 1, 0, 0);
2199 XMapWindow(ob_display
, self
->window
);
2201 /* this puts it after the current focused window */
2202 focus_order_remove(self
);
2203 focus_order_add_new(self
);
2205 /* this is here cuz with the VIDMODE extension, the viewport can
2206 change while a fullscreen window is iconic, and when it
2207 uniconifies, it would be nice if it did so to the new position
2209 client_reconfigure(self
);
2216 client_change_state(self
);
2217 client_showhide(self
);
2218 screen_update_areas();
2221 /* iconify all transients */
2222 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2223 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2227 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2229 /* move up the transient chain as far as possible first */
2230 self
= client_search_top_transient(self
);
2232 client_iconify_recursive(client_search_top_transient(self
),
2236 void client_maximize(ObClient
*self
, gboolean max
, int dir
, gboolean savearea
)
2240 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2241 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2243 /* check if already done */
2245 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2246 if (dir
== 1 && self
->max_horz
) return;
2247 if (dir
== 2 && self
->max_vert
) return;
2249 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2250 if (dir
== 1 && !self
->max_horz
) return;
2251 if (dir
== 2 && !self
->max_vert
) return;
2254 /* we just tell it to configure in the same place and client_configure
2255 worries about filling the screen with the window */
2258 w
= self
->area
.width
;
2259 h
= self
->area
.height
;
2263 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2264 RECT_SET(self
->pre_max_area
,
2265 self
->area
.x
, self
->pre_max_area
.y
,
2266 self
->area
.width
, self
->pre_max_area
.height
);
2268 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2269 RECT_SET(self
->pre_max_area
,
2270 self
->pre_max_area
.x
, self
->area
.y
,
2271 self
->pre_max_area
.width
, self
->area
.height
);
2277 a
= screen_area_monitor(self
->desktop
, 0);
2278 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2279 if (self
->pre_max_area
.width
> 0) {
2280 x
= self
->pre_max_area
.x
;
2281 w
= self
->pre_max_area
.width
;
2283 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2284 0, self
->pre_max_area
.height
);
2286 /* pick some fallbacks... */
2287 x
= a
->x
+ a
->width
/ 4;
2291 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2292 if (self
->pre_max_area
.height
> 0) {
2293 y
= self
->pre_max_area
.y
;
2294 h
= self
->pre_max_area
.height
;
2296 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2297 self
->pre_max_area
.width
, 0);
2299 /* pick some fallbacks... */
2300 y
= a
->y
+ a
->height
/ 4;
2306 if (dir
== 0 || dir
== 1) /* horz */
2307 self
->max_horz
= max
;
2308 if (dir
== 0 || dir
== 2) /* vert */
2309 self
->max_vert
= max
;
2311 client_change_state(self
); /* change the state hints on the client */
2313 client_setup_decor_and_functions(self
);
2315 client_move_resize(self
, x
, y
, w
, h
);
2318 void client_shade(ObClient
*self
, gboolean shade
)
2320 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2321 shade
) || /* can't shade */
2322 self
->shaded
== shade
) return; /* already done */
2324 /* when we're iconic, don't change the wmstate */
2325 if (!self
->iconic
) {
2328 old
= self
->wmstate
;
2329 self
->wmstate
= shade
? IconicState
: NormalState
;
2330 if (old
!= self
->wmstate
)
2331 PROP_MSG(self
->window
, kde_wm_change_state
,
2332 self
->wmstate
, 1, 0, 0);
2335 self
->shaded
= shade
;
2336 client_change_state(self
);
2337 /* resize the frame to just the titlebar */
2338 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2341 void client_close(ObClient
*self
)
2345 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2347 /* in the case that the client provides no means to requesting that it
2348 close, we just kill it */
2349 if (!self
->delete_window
)
2353 XXX: itd be cool to do timeouts and shit here for killing the client's
2355 like... if the window is around after 5 seconds, then the close button
2356 turns a nice red, and if this function is called again, the client is
2360 ce
.xclient
.type
= ClientMessage
;
2361 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2362 ce
.xclient
.display
= ob_display
;
2363 ce
.xclient
.window
= self
->window
;
2364 ce
.xclient
.format
= 32;
2365 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2366 ce
.xclient
.data
.l
[1] = event_lasttime
;
2367 ce
.xclient
.data
.l
[2] = 0l;
2368 ce
.xclient
.data
.l
[3] = 0l;
2369 ce
.xclient
.data
.l
[4] = 0l;
2370 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2373 void client_kill(ObClient
*self
)
2375 XKillClient(ob_display
, self
->window
);
2378 void client_set_desktop_recursive(ObClient
*self
,
2379 guint target
, gboolean donthide
)
2384 if (target
!= self
->desktop
) {
2386 ob_debug("Setting desktop %u\n", target
+1);
2388 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2390 /* remove from the old desktop(s) */
2391 focus_order_remove(self
);
2393 old
= self
->desktop
;
2394 self
->desktop
= target
;
2395 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2396 /* the frame can display the current desktop state */
2397 frame_adjust_state(self
->frame
);
2398 /* 'move' the window to the new desktop */
2400 client_showhide(self
);
2401 /* raise if it was not already on the desktop */
2402 if (old
!= DESKTOP_ALL
)
2404 screen_update_areas();
2406 /* add to the new desktop(s) */
2407 if (config_focus_new
)
2408 focus_order_to_top(self
);
2410 focus_order_to_bottom(self
);
2413 /* move all transients */
2414 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2415 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2419 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2421 client_set_desktop_recursive(client_search_top_transient(self
),
2425 ObClient
*client_search_modal_child(ObClient
*self
)
2430 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
2431 ObClient
*c
= it
->data
;
2432 if ((ret
= client_search_modal_child(c
))) return ret
;
2433 if (c
->modal
) return c
;
2438 gboolean
client_validate(ObClient
*self
)
2442 XSync(ob_display
, FALSE
); /* get all events on the server */
2444 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2445 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2446 XPutBackEvent(ob_display
, &e
);
2453 void client_set_wm_state(ObClient
*self
, long state
)
2455 if (state
== self
->wmstate
) return; /* no change */
2459 client_iconify(self
, TRUE
, TRUE
);
2462 client_iconify(self
, FALSE
, TRUE
);
2467 void client_set_state(ObClient
*self
, Atom action
, long data1
, long data2
)
2469 gboolean shaded
= self
->shaded
;
2470 gboolean fullscreen
= self
->fullscreen
;
2471 gboolean undecorated
= self
->undecorated
;
2472 gboolean max_horz
= self
->max_horz
;
2473 gboolean max_vert
= self
->max_vert
;
2474 gboolean modal
= self
->modal
;
2477 if (!(action
== prop_atoms
.net_wm_state_add
||
2478 action
== prop_atoms
.net_wm_state_remove
||
2479 action
== prop_atoms
.net_wm_state_toggle
))
2480 /* an invalid action was passed to the client message, ignore it */
2483 for (i
= 0; i
< 2; ++i
) {
2484 Atom state
= i
== 0 ? data1
: data2
;
2486 if (!state
) continue;
2488 /* if toggling, then pick whether we're adding or removing */
2489 if (action
== prop_atoms
.net_wm_state_toggle
) {
2490 if (state
== prop_atoms
.net_wm_state_modal
)
2491 action
= modal
? prop_atoms
.net_wm_state_remove
:
2492 prop_atoms
.net_wm_state_add
;
2493 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2494 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2495 prop_atoms
.net_wm_state_add
;
2496 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2497 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2498 prop_atoms
.net_wm_state_add
;
2499 else if (state
== prop_atoms
.net_wm_state_shaded
)
2500 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2501 prop_atoms
.net_wm_state_add
;
2502 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2503 action
= self
->skip_taskbar
?
2504 prop_atoms
.net_wm_state_remove
:
2505 prop_atoms
.net_wm_state_add
;
2506 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2507 action
= self
->skip_pager
?
2508 prop_atoms
.net_wm_state_remove
:
2509 prop_atoms
.net_wm_state_add
;
2510 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2511 action
= fullscreen
?
2512 prop_atoms
.net_wm_state_remove
:
2513 prop_atoms
.net_wm_state_add
;
2514 else if (state
== prop_atoms
.net_wm_state_above
)
2515 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2516 prop_atoms
.net_wm_state_add
;
2517 else if (state
== prop_atoms
.net_wm_state_below
)
2518 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2519 prop_atoms
.net_wm_state_add
;
2520 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2521 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2522 prop_atoms
.net_wm_state_add
;
2525 if (action
== prop_atoms
.net_wm_state_add
) {
2526 if (state
== prop_atoms
.net_wm_state_modal
) {
2528 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2530 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2532 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2534 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2535 self
->skip_taskbar
= TRUE
;
2536 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2537 self
->skip_pager
= TRUE
;
2538 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2540 } else if (state
== prop_atoms
.net_wm_state_above
) {
2542 } else if (state
== prop_atoms
.net_wm_state_below
) {
2544 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2548 } else { /* action == prop_atoms.net_wm_state_remove */
2549 if (state
== prop_atoms
.net_wm_state_modal
) {
2551 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2553 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2555 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2557 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2558 self
->skip_taskbar
= FALSE
;
2559 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2560 self
->skip_pager
= FALSE
;
2561 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2563 } else if (state
== prop_atoms
.net_wm_state_above
) {
2564 self
->above
= FALSE
;
2565 } else if (state
== prop_atoms
.net_wm_state_below
) {
2566 self
->below
= FALSE
;
2567 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2568 undecorated
= FALSE
;
2572 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2573 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2575 if (max_horz
== max_vert
) { /* both going the same way */
2576 client_maximize(self
, max_horz
, 0, TRUE
);
2578 client_maximize(self
, max_horz
, 1, TRUE
);
2579 client_maximize(self
, max_vert
, 2, TRUE
);
2583 if (max_horz
!= self
->max_horz
)
2584 client_maximize(self
, max_horz
, 1, TRUE
);
2586 client_maximize(self
, max_vert
, 2, TRUE
);
2589 /* change fullscreen state before shading, as it will affect if the window
2591 if (fullscreen
!= self
->fullscreen
)
2592 client_fullscreen(self
, fullscreen
, TRUE
);
2593 if (shaded
!= self
->shaded
)
2594 client_shade(self
, shaded
);
2595 if (undecorated
!= self
->undecorated
)
2596 client_set_undecorated(self
, undecorated
);
2597 if (modal
!= self
->modal
) {
2598 self
->modal
= modal
;
2599 /* when a window changes modality, then its stacking order with its
2600 transients needs to change */
2603 client_calc_layer(self
);
2604 client_change_state(self
); /* change the hint to reflect these changes */
2607 ObClient
*client_focus_target(ObClient
*self
)
2611 /* if we have a modal child, then focus it, not us */
2612 child
= client_search_modal_child(client_search_top_transient(self
));
2613 if (child
) return child
;
2617 gboolean
client_can_focus(ObClient
*self
)
2621 /* choose the correct target */
2622 self
= client_focus_target(self
);
2624 if (!self
->frame
->visible
)
2627 if (!(self
->can_focus
|| self
->focus_notify
))
2630 /* do a check to see if the window has already been unmapped or destroyed
2631 do this intelligently while watching out for unmaps we've generated
2632 (ignore_unmaps > 0) */
2633 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2634 DestroyNotify
, &ev
)) {
2635 XPutBackEvent(ob_display
, &ev
);
2638 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2639 UnmapNotify
, &ev
)) {
2640 if (self
->ignore_unmaps
) {
2641 self
->ignore_unmaps
--;
2643 XPutBackEvent(ob_display
, &ev
);
2651 gboolean
client_focus(ObClient
*self
)
2653 /* choose the correct target */
2654 self
= client_focus_target(self
);
2656 if (!client_can_focus(self
)) {
2657 if (!self
->frame
->visible
) {
2658 /* update the focus lists */
2659 focus_order_to_top(self
);
2664 if (self
->can_focus
) {
2665 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2666 I choose to use it always, hopefully to find errors quicker, if any
2667 are left. (I hate X. I hate focus events.)
2669 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2670 #799. So now it is RevertToNone again.
2672 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2676 if (self
->focus_notify
) {
2678 ce
.xclient
.type
= ClientMessage
;
2679 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2680 ce
.xclient
.display
= ob_display
;
2681 ce
.xclient
.window
= self
->window
;
2682 ce
.xclient
.format
= 32;
2683 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2684 ce
.xclient
.data
.l
[1] = event_lasttime
;
2685 ce
.xclient
.data
.l
[2] = 0l;
2686 ce
.xclient
.data
.l
[3] = 0l;
2687 ce
.xclient
.data
.l
[4] = 0l;
2688 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2692 ob_debug("%sively focusing %lx at %d\n",
2693 (self
->can_focus
? "act" : "pass"),
2694 self
->window
, (int) event_lasttime
);
2697 /* Cause the FocusIn to come back to us. Important for desktop switches,
2698 since otherwise we'll have no FocusIn on the queue and send it off to
2699 the focus_backup. */
2700 XSync(ob_display
, FALSE
);
2704 void client_unfocus(ObClient
*self
)
2706 if (focus_client
== self
) {
2708 ob_debug("client_unfocus for %lx\n", self
->window
);
2710 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2714 void client_activate(ObClient
*self
, gboolean here
)
2716 if (client_normal(self
) && screen_showing_desktop
)
2717 screen_show_desktop(FALSE
);
2719 client_iconify(self
, FALSE
, here
);
2720 if (self
->desktop
!= DESKTOP_ALL
&&
2721 self
->desktop
!= screen_desktop
) {
2723 client_set_desktop(self
, screen_desktop
, FALSE
);
2725 screen_set_desktop(self
->desktop
);
2726 } else if (!self
->frame
->visible
)
2727 /* if its not visible for other reasons, then don't mess
2731 client_shade(self
, FALSE
);
2735 /* we do this an action here. this is rather important. this is because
2736 we want the results from the focus change to take place BEFORE we go
2737 about raising the window. when a fullscreen window loses focus, we need
2738 this or else the raise wont be able to raise above the to-lose-focus
2739 fullscreen window. */
2743 void client_raise(ObClient
*self
)
2745 action_run_string("Raise", self
);
2748 void client_lower(ObClient
*self
)
2750 action_run_string("Raise", self
);
2753 gboolean
client_focused(ObClient
*self
)
2755 return self
== focus_client
;
2758 static ObClientIcon
* client_icon_recursive(ObClient
*self
, int w
, int h
)
2761 /* si is the smallest image >= req */
2762 /* li is the largest image < req */
2763 unsigned long size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2765 if (!self
->nicons
) {
2766 ObClientIcon
*parent
= NULL
;
2768 if (self
->transient_for
) {
2769 if (self
->transient_for
!= OB_TRAN_GROUP
)
2770 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2773 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2774 ObClient
*c
= it
->data
;
2775 if (c
!= self
&& !c
->transient_for
) {
2776 if ((parent
= client_icon_recursive(c
, w
, h
)))
2786 for (i
= 0; i
< self
->nicons
; ++i
) {
2787 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2788 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2792 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2797 if (largest
== 0) /* didnt find one smaller than the requested size */
2798 return &self
->icons
[si
];
2799 return &self
->icons
[li
];
2802 const ObClientIcon
* client_icon(ObClient
*self
, int w
, int h
)
2805 static ObClientIcon deficon
;
2807 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
2808 deficon
.width
= deficon
.height
= 48;
2809 deficon
.data
= ob_rr_theme
->def_win_icon
;
2815 /* this be mostly ripped from fvwm */
2816 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2818 int my_cx
, my_cy
, his_cx
, his_cy
;
2821 int score
, best_score
;
2822 ObClient
*best_client
, *cur
;
2828 /* first, find the centre coords of the currently focused window */
2829 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2830 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2835 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2838 /* the currently selected window isn't interesting */
2841 if (!client_normal(cur
))
2843 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2847 if(client_focus_target(cur
) == cur
&&
2848 !(cur
->can_focus
|| cur
->focus_notify
))
2851 /* find the centre coords of this window, from the
2852 * currently focused window's point of view */
2853 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2854 + cur
->frame
->area
.width
/ 2;
2855 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2856 + cur
->frame
->area
.height
/ 2;
2858 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
2859 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
2861 /* Rotate the diagonals 45 degrees counterclockwise.
2862 * To do this, multiply the matrix /+h +h\ with the
2863 * vector (x y). \-h +h/
2864 * h = sqrt(0.5). We can set h := 1 since absolute
2865 * distance doesn't matter here. */
2866 tx
= his_cx
+ his_cy
;
2867 his_cy
= -his_cx
+ his_cy
;
2872 case OB_DIRECTION_NORTH
:
2873 case OB_DIRECTION_SOUTH
:
2874 case OB_DIRECTION_NORTHEAST
:
2875 case OB_DIRECTION_SOUTHWEST
:
2876 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
2877 distance
= ((dir
== OB_DIRECTION_NORTH
||
2878 dir
== OB_DIRECTION_NORTHEAST
) ?
2881 case OB_DIRECTION_EAST
:
2882 case OB_DIRECTION_WEST
:
2883 case OB_DIRECTION_SOUTHEAST
:
2884 case OB_DIRECTION_NORTHWEST
:
2885 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
2886 distance
= ((dir
== OB_DIRECTION_WEST
||
2887 dir
== OB_DIRECTION_NORTHWEST
) ?
2892 /* the target must be in the requested direction */
2896 /* Calculate score for this window. The smaller the better. */
2897 score
= distance
+ offset
;
2899 /* windows more than 45 degrees off the direction are
2900 * heavily penalized and will only be chosen if nothing
2901 * else within a million pixels */
2902 if(offset
> distance
)
2905 if(best_score
== -1 || score
< best_score
)
2913 void client_set_layer(ObClient
*self
, int layer
)
2917 self
->above
= FALSE
;
2918 } else if (layer
== 0) {
2919 self
->below
= self
->above
= FALSE
;
2921 self
->below
= FALSE
;
2924 client_calc_layer(self
);
2925 client_change_state(self
); /* reflect this in the state hints */
2928 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
2930 if (self
->undecorated
!= undecorated
) {
2931 self
->undecorated
= undecorated
;
2932 client_setup_decor_and_functions(self
);
2933 client_change_state(self
); /* reflect this in the state hints */
2937 guint
client_monitor(ObClient
*self
)
2943 for (i
= 0; i
< screen_num_monitors
; ++i
) {
2944 Rect
*area
= screen_physical_area_monitor(i
);
2945 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
2949 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
2950 v
= r
.width
* r
.height
;
2961 ObClient
*client_search_top_transient(ObClient
*self
)
2963 /* move up the transient chain as far as possible */
2964 if (self
->transient_for
) {
2965 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2966 return client_search_top_transient(self
->transient_for
);
2970 g_assert(self
->group
);
2972 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2973 ObClient
*c
= it
->data
;
2975 /* checking transient_for prevents infinate loops! */
2976 if (c
!= self
&& !c
->transient_for
)
2987 ObClient
*client_search_focus_parent(ObClient
*self
)
2989 if (self
->transient_for
) {
2990 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2991 if (client_focused(self
->transient_for
))
2992 return self
->transient_for
;
2996 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2997 ObClient
*c
= it
->data
;
2999 /* checking transient_for prevents infinate loops! */
3000 if (c
!= self
&& !c
->transient_for
)
3001 if (client_focused(c
))
3010 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3012 if (self
->transient_for
) {
3013 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3014 if (self
->transient_for
== search
)
3019 for (it
= self
->group
->members
; it
; it
= it
->next
) {
3020 ObClient
*c
= it
->data
;
3022 /* checking transient_for prevents infinate loops! */
3023 if (c
!= self
&& !c
->transient_for
)
3033 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3037 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3038 if (sit
->data
== search
)
3040 if (client_search_transient(sit
->data
, search
))
3046 void client_update_sm_client_id(ObClient
*self
)
3048 g_free(self
->sm_client_id
);
3049 self
->sm_client_id
= NULL
;
3051 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3053 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3054 &self
->sm_client_id
);
3057 /* finds the nearest edge in the given direction from the current client
3058 * note to self: the edge is the -frame- edge (the actual one), not the
3061 int client_directional_edge_search(ObClient
*c
, ObDirection dir
)
3064 int my_edge_start
, my_edge_end
, my_offset
;
3071 a
= screen_area(c
->desktop
);
3074 case OB_DIRECTION_NORTH
:
3075 my_edge_start
= c
->frame
->area
.x
;
3076 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3077 my_offset
= c
->frame
->area
.y
;
3079 /* default: top of screen */
3082 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3083 int his_edge_start
, his_edge_end
, his_offset
;
3084 ObClient
*cur
= it
->data
;
3088 if(!client_normal(cur
))
3090 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3095 his_edge_start
= cur
->frame
->area
.x
;
3096 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3097 his_offset
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3099 if(his_offset
+ 1 > my_offset
)
3102 if(his_offset
< dest
)
3105 if(his_edge_start
>= my_edge_start
&&
3106 his_edge_start
<= my_edge_end
)
3109 if(my_edge_start
>= his_edge_start
&&
3110 my_edge_start
<= his_edge_end
)
3115 case OB_DIRECTION_SOUTH
:
3116 my_edge_start
= c
->frame
->area
.x
;
3117 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3118 my_offset
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3120 /* default: bottom of screen */
3121 dest
= a
->y
+ a
->height
;
3123 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3124 int his_edge_start
, his_edge_end
, his_offset
;
3125 ObClient
*cur
= it
->data
;
3129 if(!client_normal(cur
))
3131 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3136 his_edge_start
= cur
->frame
->area
.x
;
3137 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3138 his_offset
= cur
->frame
->area
.y
;
3141 if(his_offset
- 1 < my_offset
)
3144 if(his_offset
> dest
)
3147 if(his_edge_start
>= my_edge_start
&&
3148 his_edge_start
<= my_edge_end
)
3151 if(my_edge_start
>= his_edge_start
&&
3152 my_edge_start
<= his_edge_end
)
3157 case OB_DIRECTION_WEST
:
3158 my_edge_start
= c
->frame
->area
.y
;
3159 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3160 my_offset
= c
->frame
->area
.x
;
3162 /* default: leftmost egde of screen */
3165 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3166 int his_edge_start
, his_edge_end
, his_offset
;
3167 ObClient
*cur
= it
->data
;
3171 if(!client_normal(cur
))
3173 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3178 his_edge_start
= cur
->frame
->area
.y
;
3179 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3180 his_offset
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3182 if(his_offset
+ 1 > my_offset
)
3185 if(his_offset
< dest
)
3188 if(his_edge_start
>= my_edge_start
&&
3189 his_edge_start
<= my_edge_end
)
3192 if(my_edge_start
>= his_edge_start
&&
3193 my_edge_start
<= his_edge_end
)
3199 case OB_DIRECTION_EAST
:
3200 my_edge_start
= c
->frame
->area
.y
;
3201 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3202 my_offset
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3204 /* default: rightmost edge of screen */
3205 dest
= a
->x
+ a
->width
;
3207 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
3208 int his_edge_start
, his_edge_end
, his_offset
;
3209 ObClient
*cur
= it
->data
;
3213 if(!client_normal(cur
))
3215 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3220 his_edge_start
= cur
->frame
->area
.y
;
3221 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3222 his_offset
= cur
->frame
->area
.x
;
3224 if(his_offset
- 1 < my_offset
)
3227 if(his_offset
> dest
)
3230 if(his_edge_start
>= my_edge_start
&&
3231 his_edge_start
<= my_edge_end
)
3234 if(my_edge_start
>= his_edge_start
&&
3235 my_edge_start
<= his_edge_end
)
3240 case OB_DIRECTION_NORTHEAST
:
3241 case OB_DIRECTION_SOUTHEAST
:
3242 case OB_DIRECTION_NORTHWEST
:
3243 case OB_DIRECTION_SOUTHWEST
:
3244 /* not implemented */
3246 g_assert_not_reached();
3251 ObClient
* client_under_pointer()
3255 ObClient
*ret
= NULL
;
3257 if (screen_pointer_pos(&x
, &y
)) {
3258 for (it
= stacking_list
; it
!= NULL
; it
= it
->next
) {
3259 if (WINDOW_IS_CLIENT(it
->data
)) {
3260 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3261 if (c
->frame
->visible
&&
3262 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3272 gboolean
client_has_group_siblings(ObClient
*self
)
3274 return self
->group
&& self
->group
->members
->next
;