1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2004 Mikael Magnusson
5 Copyright (c) 2003 Ben Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
43 #include "per_app_settings.h"
46 #include <X11/Xutil.h>
48 /*! The event mask to grab on client windows */
49 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
52 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
57 ObClientDestructor func
;
61 GList
*client_list
= NULL
;
62 GSList
*client_destructors
= NULL
;
64 static void client_get_all(ObClient
*self
);
65 static void client_toggle_border(ObClient
*self
, gboolean show
);
66 static void client_get_startup_id(ObClient
*self
);
67 static void client_get_area(ObClient
*self
);
68 static void client_get_desktop(ObClient
*self
);
69 static void client_get_state(ObClient
*self
);
70 static void client_get_shaped(ObClient
*self
);
71 static void client_get_mwm_hints(ObClient
*self
);
72 static void client_get_gravity(ObClient
*self
);
73 static void client_showhide(ObClient
*self
);
74 static void client_change_allowed_actions(ObClient
*self
);
75 static void client_change_state(ObClient
*self
);
76 static void client_apply_startup_state(ObClient
*self
);
77 static void client_restore_session_state(ObClient
*self
);
78 static void client_restore_session_stacking(ObClient
*self
);
79 static void client_urgent_notify(ObClient
*self
);
81 void client_startup(gboolean reconfig
)
88 void client_shutdown(gboolean reconfig
)
92 void client_add_destructor(ObClientDestructor func
, gpointer data
)
94 Destructor
*d
= g_new(Destructor
, 1);
97 client_destructors
= g_slist_prepend(client_destructors
, d
);
100 void client_remove_destructor(ObClientDestructor func
)
104 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
105 Destructor
*d
= it
->data
;
106 if (d
->func
== func
) {
108 client_destructors
= g_slist_delete_link(client_destructors
, it
);
114 void client_set_list()
116 Window
*windows
, *win_it
;
118 guint size
= g_list_length(client_list
);
120 /* create an array of the window ids */
122 windows
= g_new(Window
, size
);
124 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
125 *win_it
= ((ObClient
*)it
->data
)->window
;
129 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
130 net_client_list
, window
, (gulong
*)windows
, size
);
139 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
143 for (it = self->transients; it; it = g_slist_next(it)) {
144 if (!func(it->data, data)) return;
145 client_foreach_transient(it->data, func, data);
149 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
151 if (self->transient_for) {
152 if (self->transient_for != OB_TRAN_GROUP) {
153 if (!func(self->transient_for, data)) return;
154 client_foreach_ancestor(self->transient_for, func, data);
158 for (it = self->group->members; it; it = g_slist_next(it))
159 if (it->data != self &&
160 !((ObClient*)it->data)->transient_for) {
161 if (!func(it->data, data)) return;
162 client_foreach_ancestor(it->data, func, data);
169 void client_manage_all()
174 XWindowAttributes attrib
;
176 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
177 &w
, &w
, &children
, &nchild
);
179 /* remove all icon windows from the list */
180 for (i
= 0; i
< nchild
; i
++) {
181 if (children
[i
] == None
) continue;
182 wmhints
= XGetWMHints(ob_display
, children
[i
]);
184 if ((wmhints
->flags
& IconWindowHint
) &&
185 (wmhints
->icon_window
!= children
[i
]))
186 for (j
= 0; j
< nchild
; j
++)
187 if (children
[j
] == wmhints
->icon_window
) {
195 for (i
= 0; i
< nchild
; ++i
) {
196 if (children
[i
] == None
)
198 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
199 if (attrib
.override_redirect
) continue;
201 if (attrib
.map_state
!= IsUnmapped
)
202 client_manage(children
[i
]);
208 void client_manage(Window window
)
212 XWindowAttributes attrib
;
213 XSetWindowAttributes attrib_set
;
215 gboolean activate
= FALSE
;
216 ObAppSetting
*settings
;
220 /* check if it has already been unmapped by the time we started mapping
221 the grab does a sync so we don't have to here */
222 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
223 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
)) {
224 XPutBackEvent(ob_display
, &e
);
227 return; /* don't manage it */
230 /* make sure it isn't an override-redirect window */
231 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
232 attrib
.override_redirect
) {
234 return; /* don't manage it */
237 /* is the window a docking app */
238 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
239 if ((wmhint
->flags
& StateHint
) &&
240 wmhint
->initial_state
== WithdrawnState
) {
241 dock_add(window
, wmhint
);
249 ob_debug("Managing window: %lx\n", window
);
251 /* choose the events we want to receive on the CLIENT window */
252 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
253 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
254 XChangeWindowAttributes(ob_display
, window
,
255 CWEventMask
|CWDontPropagate
, &attrib_set
);
258 /* create the ObClient struct, and populate it from the hints on the
260 self
= g_new0(ObClient
, 1);
261 self
->obwin
.type
= Window_Client
;
262 self
->window
= window
;
264 /* non-zero defaults */
265 self
->title_count
= 1;
266 self
->wmstate
= NormalState
;
268 self
->desktop
= screen_num_desktops
; /* always an invalid value */
270 client_get_all(self
);
271 client_restore_session_state(self
);
273 sn_app_started(self
->class);
275 /* update the focus lists, do this before the call to change_state or
276 it can end up in the list twice! */
277 focus_order_add_new(self
);
279 client_change_state(self
);
281 /* remove the client's border (and adjust re gravity) */
282 client_toggle_border(self
, FALSE
);
284 /* specify that if we exit, the window should not be destroyed and should
285 be reparented back to root automatically */
286 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
288 /* create the decoration frame for the client window */
289 self
->frame
= frame_new();
291 frame_grab_client(self
->frame
, self
);
295 client_apply_startup_state(self
);
297 /* get and set application level settings */
298 settings
= (ObAppSetting
*) get_client_settings(self
);
301 if (settings
->shade
&& !settings
->decor
)
302 settings
->decor
= TRUE
;
304 client_shade(self
, settings
->shade
);
305 client_set_undecorated(self
, !settings
->decor
);
307 if (settings
->desktop
!= -1)
308 client_set_desktop(self
, settings
->desktop
, FALSE
);
310 client_set_layer(self
, settings
->layer
);
313 stacking_add(CLIENT_AS_WINDOW(self
));
314 client_restore_session_stacking(self
);
316 /* focus the new window? */
317 if (ob_state() != OB_STATE_STARTING
&&
318 (config_focus_new
|| client_search_focus_parent(self
)) ||
319 (settings
&& settings
->focus
) &&
320 /* note the check against Type_Normal/Dialog, not client_normal(self),
321 which would also include other types. in this case we want more
322 strict rules for focus */
323 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
324 self
->type
== OB_CLIENT_TYPE_DIALOG
))
328 if (self
->desktop
!= screen_desktop
) {
329 /* activate the window */
332 gboolean group_foc
= FALSE
;
337 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
339 if (client_focused(it
->data
))
347 (!self
->transient_for
&& (!self
->group
||
348 !self
->group
->members
->next
))) ||
349 client_search_focus_tree_full(self
) ||
351 !client_normal(focus_client
))
353 /* activate the window */
360 if (ob_state() == OB_STATE_RUNNING
) {
361 gint x
= self
->area
.x
, ox
= x
;
362 gint y
= self
->area
.y
, oy
= y
;
364 place_client(self
, &x
, &y
);
367 place_window_from_settings(settings
, self
, &x
, &y
);
369 /* make sure the window is visible. */
370 client_find_onscreen(self
, &x
, &y
,
371 self
->frame
->area
.width
,
372 self
->frame
->area
.height
,
373 /* non-normal clients has less rules, and
374 windows that are being restored from a
375 session do also. we can assume you want
376 it back where you saved it. Clients saying
377 they placed themselves are subjected to
378 harder rules, ones that are placed by
379 place.c or by the user are allowed partially
380 off-screen and on xinerama divides (ie,
381 it is up to the placement routines to avoid
382 the xinerama divides) */
383 ((self
->positioned
& PPosition
) &&
384 !(self
->positioned
& USPosition
)) &&
385 client_normal(self
) &&
387 if (x
!= ox
|| y
!= oy
)
388 client_move(self
, x
, y
);
391 keyboard_grab_for_client(self
, TRUE
);
392 mouse_grab_for_client(self
, TRUE
);
394 client_showhide(self
);
396 /* use client_focus instead of client_activate cuz client_activate does
397 stuff like switch desktops etc and I'm not interested in all that when
398 a window maps since its not based on an action from the user like
399 clicking a window to activate is. so keep the new window out of the way
402 /* if using focus_delay, stop the timer now so that focus doesn't go
404 event_halt_focus_delay();
407 /* since focus can change the stacking orders, if we focus the window
408 then the standard raise it gets is not enough, we need to queue one
409 for after the focus change takes place */
413 /* client_activate does this but we aret using it so we have to do it
415 if (screen_showing_desktop
)
416 screen_show_desktop(FALSE
);
418 /* add to client list/map */
419 client_list
= g_list_append(client_list
, self
);
420 g_hash_table_insert(window_map
, &self
->window
, self
);
422 /* this has to happen after we're in the client_list */
423 screen_update_areas();
425 /* update the list hints */
428 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
431 void client_unmanage_all()
433 while (client_list
!= NULL
)
434 client_unmanage(client_list
->data
);
437 void client_unmanage(ObClient
*self
)
442 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
444 g_assert(self
!= NULL
);
446 keyboard_grab_for_client(self
, FALSE
);
447 mouse_grab_for_client(self
, FALSE
);
449 /* potentially fix focusLast */
450 if (config_focus_last
)
451 grab_pointer(TRUE
, OB_CURSOR_NONE
);
453 /* remove the window from our save set */
454 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
456 /* we dont want events no more */
457 XSelectInput(ob_display
, self
->window
, NoEventMask
);
459 frame_hide(self
->frame
);
461 client_list
= g_list_remove(client_list
, self
);
462 stacking_remove(self
);
463 g_hash_table_remove(window_map
, &self
->window
);
465 /* update the focus lists */
466 focus_order_remove(self
);
468 /* once the client is out of the list, update the struts to remove it's
470 screen_update_areas();
472 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
473 Destructor
*d
= it
->data
;
474 d
->func(self
, d
->data
);
477 if (focus_client
== self
) {
480 /* focus the last focused window on the desktop, and ignore enter
481 events from the unmap so it doesnt mess with the focus */
482 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
483 /* remove these flags so we don't end up getting focused in the
485 self
->can_focus
= FALSE
;
486 self
->focus_notify
= FALSE
;
488 client_unfocus(self
);
491 /* tell our parent(s) that we're gone */
492 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
493 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
494 if (it
->data
!= self
)
495 ((ObClient
*)it
->data
)->transients
=
496 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
497 } else if (self
->transient_for
) { /* transient of window */
498 self
->transient_for
->transients
=
499 g_slist_remove(self
->transient_for
->transients
, self
);
502 /* tell our transients that we're gone */
503 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
504 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
505 ((ObClient
*)it
->data
)->transient_for
= NULL
;
506 client_calc_layer(it
->data
);
510 /* remove from its group */
512 group_remove(self
->group
, self
);
516 /* give the client its border back */
517 client_toggle_border(self
, TRUE
);
519 /* reparent the window out of the frame, and free the frame */
520 frame_release_client(self
->frame
, self
);
523 if (ob_state() != OB_STATE_EXITING
) {
524 /* these values should not be persisted across a window
526 PROP_ERASE(self
->window
, net_wm_desktop
);
527 PROP_ERASE(self
->window
, net_wm_state
);
528 PROP_ERASE(self
->window
, wm_state
);
530 /* if we're left in an unmapped state, the client wont be mapped. this
531 is bad, since we will no longer be managing the window on restart */
532 XMapWindow(ob_display
, self
->window
);
536 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
538 /* free all data allocated in the client struct */
539 g_slist_free(self
->transients
);
540 for (j
= 0; j
< self
->nicons
; ++j
)
541 g_free(self
->icons
[j
].data
);
542 if (self
->nicons
> 0)
545 g_free(self
->icon_title
);
549 g_free(self
->sm_client_id
);
552 /* update the list hints */
555 if (config_focus_last
)
556 grab_pointer(FALSE
, OB_CURSOR_NONE
);
559 static void client_urgent_notify(ObClient
*self
)
562 frame_flash_start(self
->frame
);
564 frame_flash_stop(self
->frame
);
567 static void client_restore_session_state(ObClient
*self
)
571 if (!(it
= session_state_find(self
)))
574 self
->session
= it
->data
;
576 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
577 self
->positioned
= PPosition
;
578 if (self
->session
->w
> 0)
579 self
->area
.width
= self
->session
->w
;
580 if (self
->session
->h
> 0)
581 self
->area
.height
= self
->session
->h
;
582 XResizeWindow(ob_display
, self
->window
,
583 self
->area
.width
, self
->area
.height
);
585 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
586 self
->session
->desktop
:
587 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
588 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
590 self
->shaded
= self
->session
->shaded
;
591 self
->iconic
= self
->session
->iconic
;
592 self
->skip_pager
= self
->session
->skip_pager
;
593 self
->skip_taskbar
= self
->session
->skip_taskbar
;
594 self
->fullscreen
= self
->session
->fullscreen
;
595 self
->above
= self
->session
->above
;
596 self
->below
= self
->session
->below
;
597 self
->max_horz
= self
->session
->max_horz
;
598 self
->max_vert
= self
->session
->max_vert
;
601 static void client_restore_session_stacking(ObClient
*self
)
605 if (!self
->session
) return;
607 it
= g_list_find(session_saved_state
, self
->session
);
608 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
611 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
612 if (session_state_cmp(it
->data
, cit
->data
))
615 client_calc_layer(self
);
616 stacking_below(CLIENT_AS_WINDOW(self
),
617 CLIENT_AS_WINDOW(cit
->data
));
623 void client_move_onscreen(ObClient
*self
, gboolean rude
)
625 gint x
= self
->area
.x
;
626 gint y
= self
->area
.y
;
627 if (client_find_onscreen(self
, &x
, &y
,
628 self
->frame
->area
.width
,
629 self
->frame
->area
.height
, rude
)) {
630 client_move(self
, x
, y
);
634 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
638 gint ox
= *x
, oy
= *y
;
640 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
643 /* XXX watch for xinerama dead areas */
644 /* This makes sure windows aren't entirely outside of the screen so you
645 * can't see them at all */
646 if (client_normal(self
)) {
647 a
= screen_area(self
->desktop
);
648 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
649 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
650 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
651 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
652 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
654 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
658 /* This here doesn't let windows even a pixel outside the screen,
659 * when called from client_manage, programs placing themselves are
660 * forced completely onscreen, while things like
661 * xterm -geometry resolution-width/2 will work fine. Trying to
662 * place it completely offscreen will be handled in the above code.
663 * Sorry for this confused comment, i am tired. */
665 /* avoid the xinerama monitor divide while we're at it,
666 * remember to fix the placement stuff to avoid it also and
667 * then remove this XXX */
668 a
= screen_physical_area_monitor(client_monitor(self
));
669 /* dont let windows map/move into the strut unless they
670 are bigger than the available area */
672 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
673 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
674 *x
= a
->x
+ a
->width
- w
;
676 if (h
<= a
->height
) {
677 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
678 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
679 *y
= a
->y
+ a
->height
- h
;
683 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
686 return ox
!= *x
|| oy
!= *y
;
689 static void client_toggle_border(ObClient
*self
, gboolean show
)
691 /* adjust our idea of where the client is, based on its border. When the
692 border is removed, the client should now be considered to be in a
694 when re-adding the border to the client, the same operation needs to be
696 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
697 gint x
= oldx
, y
= oldy
;
698 switch(self
->gravity
) {
700 case NorthWestGravity
:
702 case SouthWestGravity
:
704 case NorthEastGravity
:
706 case SouthEastGravity
:
707 if (show
) x
-= self
->border_width
* 2;
708 else x
+= self
->border_width
* 2;
715 if (show
) x
-= self
->border_width
;
716 else x
+= self
->border_width
;
719 switch(self
->gravity
) {
721 case NorthWestGravity
:
723 case NorthEastGravity
:
725 case SouthWestGravity
:
727 case SouthEastGravity
:
728 if (show
) y
-= self
->border_width
* 2;
729 else y
+= self
->border_width
* 2;
736 if (show
) y
-= self
->border_width
;
737 else y
+= self
->border_width
;
744 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
746 /* move the client so it is back it the right spot _with_ its
748 if (x
!= oldx
|| y
!= oldy
)
749 XMoveWindow(ob_display
, self
->window
, x
, y
);
751 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
755 static void client_get_all(ObClient
*self
)
757 client_get_area(self
);
758 client_update_transient_for(self
);
759 client_update_wmhints(self
);
760 client_get_startup_id(self
);
761 client_get_desktop(self
);
762 client_get_shaped(self
);
764 client_get_mwm_hints(self
);
765 client_get_type(self
);/* this can change the mwmhints for special cases */
767 /* The transient hint is used to pick a type, but the type can also affect
768 transiency (dialogs are always made transients). This is Havoc's idea,
769 but it is needed to make some apps work right (eg tsclient). */
770 client_update_transient_for(self
);
772 client_get_state(self
);
775 /* a couple type-based defaults for new windows */
777 /* this makes sure that these windows appear on all desktops */
778 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
779 self
->desktop
= DESKTOP_ALL
;
782 client_update_protocols(self
);
784 client_get_gravity(self
); /* get the attribute gravity */
785 client_update_normal_hints(self
); /* this may override the attribute
788 /* got the type, the mwmhints, the protocols, and the normal hints
789 (min/max sizes), so we're ready to set up the decorations/functions */
790 client_setup_decor_and_functions(self
);
792 client_update_title(self
);
793 client_update_class(self
);
794 client_update_sm_client_id(self
);
795 client_update_strut(self
);
796 client_update_icons(self
);
799 static void client_get_startup_id(ObClient
*self
)
801 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
803 PROP_GETS(self
->group
->leader
,
804 net_startup_id
, utf8
, &self
->startup_id
);
807 static void client_get_area(ObClient
*self
)
809 XWindowAttributes wattrib
;
812 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
813 g_assert(ret
!= BadWindow
);
815 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
816 self
->border_width
= wattrib
.border_width
;
819 static void client_get_desktop(ObClient
*self
)
821 guint32 d
= screen_num_desktops
; /* an always-invalid value */
823 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
824 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
825 self
->desktop
= screen_num_desktops
- 1;
829 gboolean trdesk
= FALSE
;
831 if (self
->transient_for
) {
832 if (self
->transient_for
!= OB_TRAN_GROUP
) {
833 self
->desktop
= self
->transient_for
->desktop
;
838 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
839 if (it
->data
!= self
&&
840 !((ObClient
*)it
->data
)->transient_for
) {
841 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
848 /* try get from the startup-notification protocol */
849 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
850 if (self
->desktop
>= screen_num_desktops
&&
851 self
->desktop
!= DESKTOP_ALL
)
852 self
->desktop
= screen_num_desktops
- 1;
854 /* defaults to the current desktop */
855 self
->desktop
= screen_desktop
;
858 if (self
->desktop
!= d
) {
859 /* set the desktop hint, to make sure that it always exists */
860 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
864 static void client_get_state(ObClient
*self
)
869 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
871 for (i
= 0; i
< num
; ++i
) {
872 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
874 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
876 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
878 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
879 self
->skip_taskbar
= TRUE
;
880 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
881 self
->skip_pager
= TRUE
;
882 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
883 self
->fullscreen
= TRUE
;
884 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
885 self
->max_vert
= TRUE
;
886 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
887 self
->max_horz
= TRUE
;
888 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
890 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
892 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
893 self
->undecorated
= TRUE
;
899 if (!(self
->above
|| self
->below
)) {
901 /* apply stuff from the group */
905 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
906 ObClient
*c
= it
->data
;
907 if (c
!= self
&& !client_search_transient(self
, c
) &&
908 client_normal(self
) && client_normal(c
))
911 (c
->above
? 1 : (c
->below
? -1 : 0)));
925 g_assert_not_reached();
932 static void client_get_shaped(ObClient
*self
)
934 self
->shaped
= FALSE
;
936 if (extensions_shape
) {
941 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
943 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
944 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
946 self
->shaped
= (s
!= 0);
951 void client_update_transient_for(ObClient
*self
)
954 ObClient
*target
= NULL
;
956 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
957 self
->transient
= TRUE
;
958 if (t
!= self
->window
) { /* cant be transient to itself! */
959 target
= g_hash_table_lookup(window_map
, &t
);
960 /* if this happens then we need to check for it*/
961 g_assert(target
!= self
);
962 if (target
&& !WINDOW_IS_CLIENT(target
)) {
963 /* this can happen when a dialog is a child of
964 a dockapp, for example */
968 if (!target
&& self
->group
) {
969 /* not transient to a client, see if it is transient for a
971 if (t
== self
->group
->leader
||
973 t
== RootWindow(ob_display
, ob_screen
))
975 /* window is a transient for its group! */
976 target
= OB_TRAN_GROUP
;
980 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
981 self
->transient
= TRUE
;
982 target
= OB_TRAN_GROUP
;
984 self
->transient
= FALSE
;
986 /* if anything has changed... */
987 if (target
!= self
->transient_for
) {
988 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
991 /* remove from old parents */
992 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
993 ObClient
*c
= it
->data
;
994 if (c
!= self
&& !c
->transient_for
)
995 c
->transients
= g_slist_remove(c
->transients
, self
);
997 } else if (self
->transient_for
!= NULL
) { /* transient of window */
998 /* remove from old parent */
999 self
->transient_for
->transients
=
1000 g_slist_remove(self
->transient_for
->transients
, self
);
1002 self
->transient_for
= target
;
1003 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1006 /* add to new parents */
1007 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1008 ObClient
*c
= it
->data
;
1009 if (c
!= self
&& !c
->transient_for
)
1010 c
->transients
= g_slist_append(c
->transients
, self
);
1013 /* remove all transients which are in the group, that causes
1014 circlular pointer hell of doom */
1015 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1017 for (sit
= self
->transients
; sit
; sit
= next
) {
1018 next
= g_slist_next(sit
);
1019 if (sit
->data
== it
->data
)
1021 g_slist_delete_link(self
->transients
, sit
);
1024 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1025 /* add to new parent */
1026 self
->transient_for
->transients
=
1027 g_slist_append(self
->transient_for
->transients
, self
);
1032 static void client_get_mwm_hints(ObClient
*self
)
1037 self
->mwmhints
.flags
= 0; /* default to none */
1039 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1041 if (num
>= OB_MWM_ELEMENTS
) {
1042 self
->mwmhints
.flags
= hints
[0];
1043 self
->mwmhints
.functions
= hints
[1];
1044 self
->mwmhints
.decorations
= hints
[2];
1050 void client_get_type(ObClient
*self
)
1057 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1058 /* use the first value that we know about in the array */
1059 for (i
= 0; i
< num
; ++i
) {
1060 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1061 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1062 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1063 self
->type
= OB_CLIENT_TYPE_DOCK
;
1064 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1065 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1066 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1067 self
->type
= OB_CLIENT_TYPE_MENU
;
1068 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1069 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1070 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1071 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1072 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1073 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1074 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1075 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1076 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1077 /* prevent this window from getting any decor or
1079 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1080 OB_MWM_FLAG_DECORATIONS
);
1081 self
->mwmhints
.decorations
= 0;
1082 self
->mwmhints
.functions
= 0;
1084 if (self
->type
!= (ObClientType
) -1)
1085 break; /* grab the first legit type */
1090 if (self
->type
== (ObClientType
) -1) {
1091 /*the window type hint was not set, which means we either classify
1092 ourself as a normal window or a dialog, depending on if we are a
1094 if (self
->transient
)
1095 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1097 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1101 void client_update_protocols(ObClient
*self
)
1104 guint num_return
, i
;
1106 self
->focus_notify
= FALSE
;
1107 self
->delete_window
= FALSE
;
1109 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1110 for (i
= 0; i
< num_return
; ++i
) {
1111 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1112 /* this means we can request the window to close */
1113 self
->delete_window
= TRUE
;
1114 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1115 /* if this protocol is requested, then the window will be
1116 notified whenever we want it to receive focus */
1117 self
->focus_notify
= TRUE
;
1123 static void client_get_gravity(ObClient
*self
)
1125 XWindowAttributes wattrib
;
1128 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1129 g_assert(ret
!= BadWindow
);
1130 self
->gravity
= wattrib
.win_gravity
;
1133 void client_update_normal_hints(ObClient
*self
)
1137 gint oldgravity
= self
->gravity
;
1140 self
->min_ratio
= 0.0f
;
1141 self
->max_ratio
= 0.0f
;
1142 SIZE_SET(self
->size_inc
, 1, 1);
1143 SIZE_SET(self
->base_size
, 0, 0);
1144 SIZE_SET(self
->min_size
, 0, 0);
1145 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1147 /* get the hints from the window */
1148 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1149 /* normal windows can't request placement! har har
1150 if (!client_normal(self))
1152 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1154 if (size
.flags
& PWinGravity
) {
1155 self
->gravity
= size
.win_gravity
;
1157 /* if the client has a frame, i.e. has already been mapped and
1158 is changing its gravity */
1159 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1160 /* move our idea of the client's position based on its new
1162 self
->area
.x
= self
->frame
->area
.x
;
1163 self
->area
.y
= self
->frame
->area
.y
;
1164 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1168 if (size
.flags
& PAspect
) {
1169 if (size
.min_aspect
.y
)
1171 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1172 if (size
.max_aspect
.y
)
1174 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1177 if (size
.flags
& PMinSize
)
1178 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1180 if (size
.flags
& PMaxSize
)
1181 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1183 if (size
.flags
& PBaseSize
)
1184 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1186 if (size
.flags
& PResizeInc
)
1187 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1191 void client_setup_decor_and_functions(ObClient
*self
)
1193 /* start with everything (cept fullscreen) */
1195 (OB_FRAME_DECOR_TITLEBAR
|
1196 (ob_rr_theme
->show_handle
? OB_FRAME_DECOR_HANDLE
: 0) |
1197 OB_FRAME_DECOR_GRIPS
|
1198 OB_FRAME_DECOR_BORDER
|
1199 OB_FRAME_DECOR_ICON
|
1200 OB_FRAME_DECOR_ALLDESKTOPS
|
1201 OB_FRAME_DECOR_ICONIFY
|
1202 OB_FRAME_DECOR_MAXIMIZE
|
1203 OB_FRAME_DECOR_SHADE
|
1204 OB_FRAME_DECOR_CLOSE
);
1206 (OB_CLIENT_FUNC_RESIZE
|
1207 OB_CLIENT_FUNC_MOVE
|
1208 OB_CLIENT_FUNC_ICONIFY
|
1209 OB_CLIENT_FUNC_MAXIMIZE
|
1210 OB_CLIENT_FUNC_SHADE
|
1211 OB_CLIENT_FUNC_CLOSE
);
1213 if (!(self
->min_size
.width
< self
->max_size
.width
||
1214 self
->min_size
.height
< self
->max_size
.height
))
1215 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1217 switch (self
->type
) {
1218 case OB_CLIENT_TYPE_NORMAL
:
1219 /* normal windows retain all of the possible decorations and
1220 functionality, and are the only windows that you can fullscreen */
1221 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1224 case OB_CLIENT_TYPE_DIALOG
:
1225 case OB_CLIENT_TYPE_UTILITY
:
1226 /* these windows cannot be maximized */
1227 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1230 case OB_CLIENT_TYPE_MENU
:
1231 case OB_CLIENT_TYPE_TOOLBAR
:
1232 /* these windows get less functionality */
1233 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1236 case OB_CLIENT_TYPE_DESKTOP
:
1237 case OB_CLIENT_TYPE_DOCK
:
1238 case OB_CLIENT_TYPE_SPLASH
:
1239 /* none of these windows are manipulated by the window manager */
1240 self
->decorations
= 0;
1241 self
->functions
= 0;
1245 /* Mwm Hints are applied subtractively to what has already been chosen for
1246 decor and functionality */
1247 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1248 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1249 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1250 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1251 /* if the mwm hints request no handle or title, then all
1252 decorations are disabled */
1253 self
->decorations
= config_theme_keepborder
? OB_FRAME_DECOR_BORDER
: 0;
1257 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1258 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1259 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1260 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1261 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1262 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1263 /* dont let mwm hints kill any buttons
1264 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1265 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1266 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1267 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1269 /* dont let mwm hints kill the close button
1270 if (! (self->mwmhints.functions & MwmFunc_Close))
1271 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1275 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1276 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1277 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1278 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1279 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1280 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1282 /* can't maximize without moving/resizing */
1283 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1284 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1285 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1286 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1287 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1290 /* kill the handle on fully maxed windows */
1291 if (self
->max_vert
&& self
->max_horz
)
1292 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1294 /* finally, the user can have requested no decorations, which overrides
1295 everything (but doesnt give it a border if it doesnt have one) */
1296 if (self
->undecorated
) {
1297 if (config_theme_keepborder
)
1298 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1300 self
->decorations
= 0;
1303 /* if we don't have a titlebar, then we cannot shade! */
1304 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1305 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1307 /* now we need to check against rules for the client's current state */
1308 if (self
->fullscreen
) {
1309 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1310 OB_CLIENT_FUNC_FULLSCREEN
|
1311 OB_CLIENT_FUNC_ICONIFY
);
1312 self
->decorations
= 0;
1315 client_change_allowed_actions(self
);
1318 /* adjust the client's decorations, etc. */
1319 client_reconfigure(self
);
1323 static void client_change_allowed_actions(ObClient
*self
)
1328 /* desktop windows are kept on all desktops */
1329 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1330 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1332 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1333 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1334 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1335 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1336 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1337 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1338 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1339 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1340 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1341 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1342 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1343 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1344 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1345 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1346 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1349 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1351 /* make sure the window isn't breaking any rules now */
1353 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1354 if (self
->frame
) client_shade(self
, FALSE
);
1355 else self
->shaded
= FALSE
;
1357 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1358 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1359 else self
->iconic
= FALSE
;
1361 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1362 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1363 else self
->fullscreen
= FALSE
;
1365 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1367 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1368 else self
->max_vert
= self
->max_horz
= FALSE
;
1372 void client_reconfigure(ObClient
*self
)
1374 /* by making this pass FALSE for user, we avoid the emacs event storm where
1375 every configurenotify causes an update in its normal hints, i think this
1376 is generally what we want anyways... */
1377 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1378 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1381 void client_update_wmhints(ObClient
*self
)
1384 gboolean ur
= FALSE
;
1387 /* assume a window takes input if it doesnt specify */
1388 self
->can_focus
= TRUE
;
1390 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1391 if (hints
->flags
& InputHint
)
1392 self
->can_focus
= hints
->input
;
1394 /* only do this when first managing the window *AND* when we aren't
1396 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1397 if (hints
->flags
& StateHint
)
1398 self
->iconic
= hints
->initial_state
== IconicState
;
1400 if (hints
->flags
& XUrgencyHint
)
1403 if (!(hints
->flags
& WindowGroupHint
))
1404 hints
->window_group
= None
;
1406 /* did the group state change? */
1407 if (hints
->window_group
!=
1408 (self
->group
? self
->group
->leader
: None
)) {
1409 /* remove from the old group if there was one */
1410 if (self
->group
!= NULL
) {
1411 /* remove transients of the group */
1412 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1413 self
->transients
= g_slist_remove(self
->transients
,
1416 /* remove myself from parents in the group */
1417 if (self
->transient_for
== OB_TRAN_GROUP
) {
1418 for (it
= self
->group
->members
; it
;
1419 it
= g_slist_next(it
))
1421 ObClient
*c
= it
->data
;
1423 if (c
!= self
&& !c
->transient_for
)
1424 c
->transients
= g_slist_remove(c
->transients
,
1429 group_remove(self
->group
, self
);
1432 if (hints
->window_group
!= None
) {
1433 self
->group
= group_add(hints
->window_group
, self
);
1435 /* i can only have transients from the group if i am not
1437 if (!self
->transient_for
) {
1438 /* add other transients of the group that are already
1440 for (it
= self
->group
->members
; it
;
1441 it
= g_slist_next(it
))
1443 ObClient
*c
= it
->data
;
1444 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1446 g_slist_append(self
->transients
, c
);
1451 /* because the self->transient flag wont change from this call,
1452 we don't need to update the window's type and such, only its
1453 transient_for, and the transients lists of other windows in
1454 the group may be affected */
1455 client_update_transient_for(self
);
1458 /* the WM_HINTS can contain an icon */
1459 client_update_icons(self
);
1464 if (ur
!= self
->urgent
) {
1466 /* fire the urgent callback if we're mapped, otherwise, wait until
1467 after we're mapped */
1469 client_urgent_notify(self
);
1473 void client_update_title(ObClient
*self
)
1479 gboolean read_title
;
1482 old_title
= self
->title
;
1485 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1486 /* try old x stuff */
1487 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
)) {
1488 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1489 if (self
->transient
) {
1490 data
= g_strdup("");
1493 data
= g_strdup("Unnamed Window");
1497 /* did the title change? then reset the title_count */
1498 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1499 self
->title_count
= 1;
1501 /* look for duplicates and append a number */
1503 for (it
= client_list
; it
; it
= g_list_next(it
))
1504 if (it
->data
!= self
) {
1505 ObClient
*c
= it
->data
;
1506 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1507 nums
|= 1 << c
->title_count
;
1509 /* find first free number */
1510 for (i
= 1; i
<= 32; ++i
)
1511 if (!(nums
& (1 << i
))) {
1512 if (self
->title_count
== 1 || i
== 1)
1513 self
->title_count
= i
;
1516 /* dont display the number for the first window */
1517 if (self
->title_count
> 1) {
1519 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1524 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1529 frame_adjust_title(self
->frame
);
1533 /* update the icon title */
1535 g_free(self
->icon_title
);
1539 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1540 /* try old x stuff */
1541 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1542 data
= g_strdup(self
->title
);
1546 /* append the title count, dont display the number for the first window */
1547 if (read_title
&& self
->title_count
> 1) {
1548 gchar
*vdata
, *ndata
;
1549 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1550 vdata
= g_strconcat(data
, ndata
, NULL
);
1556 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1558 self
->icon_title
= data
;
1561 void client_update_class(ObClient
*self
)
1566 if (self
->name
) g_free(self
->name
);
1567 if (self
->class) g_free(self
->class);
1568 if (self
->role
) g_free(self
->role
);
1570 self
->name
= self
->class = self
->role
= NULL
;
1572 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1574 self
->name
= g_strdup(data
[0]);
1576 self
->class = g_strdup(data
[1]);
1581 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1584 if (self
->name
== NULL
) self
->name
= g_strdup("");
1585 if (self
->class == NULL
) self
->class = g_strdup("");
1586 if (self
->role
== NULL
) self
->role
= g_strdup("");
1589 void client_update_strut(ObClient
*self
)
1593 gboolean got
= FALSE
;
1596 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1600 STRUT_PARTIAL_SET(strut
,
1601 data
[0], data
[2], data
[1], data
[3],
1602 data
[4], data
[5], data
[8], data
[9],
1603 data
[6], data
[7], data
[10], data
[11]);
1609 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1615 /* use the screen's width/height */
1616 a
= screen_physical_area();
1618 STRUT_PARTIAL_SET(strut
,
1619 data
[0], data
[2], data
[1], data
[3],
1620 a
->y
, a
->y
+ a
->height
- 1,
1621 a
->x
, a
->x
+ a
->width
- 1,
1622 a
->y
, a
->y
+ a
->height
- 1,
1623 a
->x
, a
->x
+ a
->width
- 1);
1629 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1630 0, 0, 0, 0, 0, 0, 0, 0);
1632 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1633 self
->strut
= strut
;
1635 /* updating here is pointless while we're being mapped cuz we're not in
1636 the client list yet */
1638 screen_update_areas();
1642 void client_update_icons(ObClient
*self
)
1648 for (i
= 0; i
< self
->nicons
; ++i
)
1649 g_free(self
->icons
[i
].data
);
1650 if (self
->nicons
> 0)
1651 g_free(self
->icons
);
1654 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1655 /* figure out how many valid icons are in here */
1657 while (num
- i
> 2) {
1661 if (i
> num
|| w
*h
== 0) break;
1665 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1667 /* store the icons */
1669 for (j
= 0; j
< self
->nicons
; ++j
) {
1672 w
= self
->icons
[j
].width
= data
[i
++];
1673 h
= self
->icons
[j
].height
= data
[i
++];
1675 if (w
*h
== 0) continue;
1677 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1678 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1683 self
->icons
[j
].data
[t
] =
1684 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1685 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1686 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1687 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1693 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1694 kwm_win_icon
, &data
, &num
)) {
1697 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1698 xerror_set_ignore(TRUE
);
1699 if (!RrPixmapToRGBA(ob_rr_inst
,
1701 &self
->icons
[self
->nicons
-1].width
,
1702 &self
->icons
[self
->nicons
-1].height
,
1703 &self
->icons
[self
->nicons
-1].data
)) {
1704 g_free(&self
->icons
[self
->nicons
-1]);
1707 xerror_set_ignore(FALSE
);
1713 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1714 if (hints
->flags
& IconPixmapHint
) {
1716 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1717 xerror_set_ignore(TRUE
);
1718 if (!RrPixmapToRGBA(ob_rr_inst
,
1720 (hints
->flags
& IconMaskHint
?
1721 hints
->icon_mask
: None
),
1722 &self
->icons
[self
->nicons
-1].width
,
1723 &self
->icons
[self
->nicons
-1].height
,
1724 &self
->icons
[self
->nicons
-1].data
)){
1725 g_free(&self
->icons
[self
->nicons
-1]);
1728 xerror_set_ignore(FALSE
);
1735 frame_adjust_icon(self
->frame
);
1738 static void client_change_state(ObClient
*self
)
1741 gulong netstate
[11];
1744 state
[0] = self
->wmstate
;
1746 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1750 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1752 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1754 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1755 if (self
->skip_taskbar
)
1756 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1757 if (self
->skip_pager
)
1758 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1759 if (self
->fullscreen
)
1760 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1762 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1764 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1766 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1768 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1769 if (self
->undecorated
)
1770 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1771 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1773 client_calc_layer(self
);
1776 frame_adjust_state(self
->frame
);
1779 ObClient
*client_search_focus_tree(ObClient
*self
)
1784 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1785 if (client_focused(it
->data
)) return it
->data
;
1786 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1791 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1793 if (self
->transient_for
) {
1794 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1795 return client_search_focus_tree_full(self
->transient_for
);
1798 gboolean recursed
= FALSE
;
1800 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1801 if (!((ObClient
*)it
->data
)->transient_for
) {
1803 if ((c
= client_search_focus_tree_full(it
->data
)))
1812 /* this function checks the whole tree, the client_search_focus_tree~
1813 does not, so we need to check this window */
1814 if (client_focused(self
))
1816 return client_search_focus_tree(self
);
1819 static ObStackingLayer
calc_layer(ObClient
*self
)
1823 if (self
->fullscreen
&&
1824 (client_focused(self
) || client_search_focus_tree(self
)))
1825 l
= OB_STACKING_LAYER_FULLSCREEN
;
1826 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1827 l
= OB_STACKING_LAYER_DESKTOP
;
1828 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1829 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1830 else l
= OB_STACKING_LAYER_ABOVE
;
1832 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1833 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1834 else l
= OB_STACKING_LAYER_NORMAL
;
1839 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1840 ObStackingLayer l
, gboolean raised
)
1842 ObStackingLayer old
, own
;
1846 own
= calc_layer(self
);
1847 self
->layer
= l
> own
? l
: own
;
1849 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1850 client_calc_layer_recursive(it
->data
, orig
,
1851 l
, raised
? raised
: l
!= old
);
1853 if (!raised
&& l
!= old
)
1854 if (orig
->frame
) { /* only restack if the original window is managed */
1855 stacking_remove(CLIENT_AS_WINDOW(self
));
1856 stacking_add(CLIENT_AS_WINDOW(self
));
1860 void client_calc_layer(ObClient
*self
)
1867 /* transients take on the layer of their parents */
1868 self
= client_search_top_transient(self
);
1870 l
= calc_layer(self
);
1872 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1875 gboolean
client_should_show(ObClient
*self
)
1879 if (client_normal(self
) && screen_showing_desktop
)
1882 if (self->transient_for) {
1883 if (self->transient_for != OB_TRAN_GROUP)
1884 return client_should_show(self->transient_for);
1888 for (it = self->group->members; it; it = g_slist_next(it)) {
1889 ObClient *c = it->data;
1890 if (c != self && !c->transient_for) {
1891 if (client_should_show(c))
1898 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
1904 static void client_showhide(ObClient
*self
)
1907 if (client_should_show(self
))
1908 frame_show(self
->frame
);
1910 frame_hide(self
->frame
);
1913 gboolean
client_normal(ObClient
*self
) {
1914 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1915 self
->type
== OB_CLIENT_TYPE_DOCK
||
1916 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1919 static void client_apply_startup_state(ObClient
*self
)
1921 /* these are in a carefully crafted order.. */
1924 self
->iconic
= FALSE
;
1925 client_iconify(self
, TRUE
, FALSE
);
1927 if (self
->fullscreen
) {
1928 self
->fullscreen
= FALSE
;
1929 client_fullscreen(self
, TRUE
, FALSE
);
1931 if (self
->undecorated
) {
1932 self
->undecorated
= FALSE
;
1933 client_set_undecorated(self
, TRUE
);
1936 self
->shaded
= FALSE
;
1937 client_shade(self
, TRUE
);
1940 client_urgent_notify(self
);
1942 if (self
->max_vert
&& self
->max_horz
) {
1943 self
->max_vert
= self
->max_horz
= FALSE
;
1944 client_maximize(self
, TRUE
, 0, FALSE
);
1945 } else if (self
->max_vert
) {
1946 self
->max_vert
= FALSE
;
1947 client_maximize(self
, TRUE
, 2, FALSE
);
1948 } else if (self
->max_horz
) {
1949 self
->max_horz
= FALSE
;
1950 client_maximize(self
, TRUE
, 1, FALSE
);
1953 /* nothing to do for the other states:
1962 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1963 gint x
, gint y
, gint w
, gint h
,
1964 gboolean user
, gboolean final
,
1965 gboolean force_reply
)
1968 gboolean send_resize_client
;
1969 gboolean moved
= FALSE
, resized
= FALSE
;
1970 guint fdecor
= self
->frame
->decorations
;
1971 gboolean fhorz
= self
->frame
->max_horz
;
1973 /* make the frame recalculate its dimentions n shit without changing
1974 anything visible for real, this way the constraints below can work with
1975 the updated frame dimensions. */
1976 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
1978 /* gets the frame's position */
1979 frame_client_gravity(self
->frame
, &x
, &y
);
1981 /* these positions are frame positions, not client positions */
1983 /* set the size and position if fullscreen */
1984 if (self
->fullscreen
) {
1987 XF86VidModeModeLine mode
;
1992 i
= client_monitor(self
);
1993 a
= screen_physical_area_monitor(i
);
1996 if (i
== 0 && /* primary head */
1997 extensions_vidmode
&&
1998 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
1999 /* get the mode last so the mode.privsize isnt freed incorrectly */
2000 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
2005 if (mode
.privsize
) XFree(mode
.private);
2015 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2019 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
2021 /* set the size and position if maximized */
2022 if (self
->max_horz
) {
2024 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2026 if (self
->max_vert
) {
2028 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2032 /* gets the client's position */
2033 frame_frame_gravity(self
->frame
, &x
, &y
);
2035 /* these override the above states! if you cant move you can't move! */
2037 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2041 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2042 w
= self
->area
.width
;
2043 h
= self
->area
.height
;
2047 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2048 gint basew
, baseh
, minw
, minh
;
2050 /* base size is substituted with min size if not specified */
2051 if (self
->base_size
.width
|| self
->base_size
.height
) {
2052 basew
= self
->base_size
.width
;
2053 baseh
= self
->base_size
.height
;
2055 basew
= self
->min_size
.width
;
2056 baseh
= self
->min_size
.height
;
2058 /* min size is substituted with base size if not specified */
2059 if (self
->min_size
.width
|| self
->min_size
.height
) {
2060 minw
= self
->min_size
.width
;
2061 minh
= self
->min_size
.height
;
2063 minw
= self
->base_size
.width
;
2064 minh
= self
->base_size
.height
;
2067 /* if this is a user-requested resize, then check against min/max
2070 /* smaller than min size or bigger than max size? */
2071 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2072 if (w
< minw
) w
= minw
;
2073 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2074 if (h
< minh
) h
= minh
;
2079 /* keep to the increments */
2080 w
/= self
->size_inc
.width
;
2081 h
/= self
->size_inc
.height
;
2083 /* you cannot resize to nothing */
2084 if (basew
+ w
< 1) w
= 1 - basew
;
2085 if (baseh
+ h
< 1) h
= 1 - baseh
;
2087 /* store the logical size */
2088 SIZE_SET(self
->logical_size
,
2089 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2090 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2092 w
*= self
->size_inc
.width
;
2093 h
*= self
->size_inc
.height
;
2098 /* adjust the height to match the width for the aspect ratios.
2099 for this, min size is not substituted for base size ever. */
2100 w
-= self
->base_size
.width
;
2101 h
-= self
->base_size
.height
;
2103 if (!self
->fullscreen
) {
2104 if (self
->min_ratio
)
2105 if (h
* self
->min_ratio
> w
) {
2106 h
= (gint
)(w
/ self
->min_ratio
);
2108 /* you cannot resize to nothing */
2111 w
= (gint
)(h
* self
->min_ratio
);
2114 if (self
->max_ratio
)
2115 if (h
* self
->max_ratio
< w
) {
2116 h
= (gint
)(w
/ self
->max_ratio
);
2118 /* you cannot resize to nothing */
2121 w
= (gint
)(h
* self
->min_ratio
);
2126 w
+= self
->base_size
.width
;
2127 h
+= self
->base_size
.height
;
2134 case OB_CORNER_TOPLEFT
:
2136 case OB_CORNER_TOPRIGHT
:
2137 x
-= w
- self
->area
.width
;
2139 case OB_CORNER_BOTTOMLEFT
:
2140 y
-= h
- self
->area
.height
;
2142 case OB_CORNER_BOTTOMRIGHT
:
2143 x
-= w
- self
->area
.width
;
2144 y
-= h
- self
->area
.height
;
2148 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2149 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2151 oldw
= self
->area
.width
;
2152 oldh
= self
->area
.height
;
2153 RECT_SET(self
->area
, x
, y
, w
, h
);
2155 /* for app-requested resizes, always resize if 'resized' is true.
2156 for user-requested ones, only resize if final is true, or when
2157 resizing in redraw mode */
2158 send_resize_client
= ((!user
&& resized
) ||
2160 (resized
&& config_resize_redraw
))));
2162 /* if the client is enlarging, then resize the client before the frame */
2163 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2164 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2166 /* move/resize the frame to match the request */
2168 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2169 moved
= resized
= TRUE
;
2171 if (moved
|| resized
)
2172 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2174 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2177 event
.type
= ConfigureNotify
;
2178 event
.xconfigure
.display
= ob_display
;
2179 event
.xconfigure
.event
= self
->window
;
2180 event
.xconfigure
.window
= self
->window
;
2182 /* root window real coords */
2183 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2185 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2187 event
.xconfigure
.width
= w
;
2188 event
.xconfigure
.height
= h
;
2189 event
.xconfigure
.border_width
= 0;
2190 event
.xconfigure
.above
= self
->frame
->plate
;
2191 event
.xconfigure
.override_redirect
= FALSE
;
2192 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2193 FALSE
, StructureNotifyMask
, &event
);
2197 /* if the client is shrinking, then resize the frame before the client */
2198 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2199 XResizeWindow(ob_display
, self
->window
, w
, h
);
2204 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2208 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2209 self
->fullscreen
== fs
) return; /* already done */
2211 self
->fullscreen
= fs
;
2212 client_change_state(self
); /* change the state hints on the client,
2213 and adjust out layer/stacking */
2217 self
->pre_fullscreen_area
= self
->area
;
2219 /* these are not actually used cuz client_configure will set them
2220 as appropriate when the window is fullscreened */
2225 if (self
->pre_fullscreen_area
.width
> 0 &&
2226 self
->pre_fullscreen_area
.height
> 0)
2228 x
= self
->pre_fullscreen_area
.x
;
2229 y
= self
->pre_fullscreen_area
.y
;
2230 w
= self
->pre_fullscreen_area
.width
;
2231 h
= self
->pre_fullscreen_area
.height
;
2232 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2234 /* pick some fallbacks... */
2235 a
= screen_area_monitor(self
->desktop
, 0);
2236 x
= a
->x
+ a
->width
/ 4;
2237 y
= a
->y
+ a
->height
/ 4;
2243 client_setup_decor_and_functions(self
);
2245 client_move_resize(self
, x
, y
, w
, h
);
2247 /* try focus us when we go into fullscreen mode */
2251 static void client_iconify_recursive(ObClient
*self
,
2252 gboolean iconic
, gboolean curdesk
)
2255 gboolean changed
= FALSE
;
2258 if (self
->iconic
!= iconic
) {
2259 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2262 self
->iconic
= iconic
;
2265 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2268 old
= self
->wmstate
;
2269 self
->wmstate
= IconicState
;
2270 if (old
!= self
->wmstate
)
2271 PROP_MSG(self
->window
, kde_wm_change_state
,
2272 self
->wmstate
, 1, 0, 0);
2274 /* update the focus lists.. iconic windows go to the bottom of
2275 the list, put the new iconic window at the 'top of the
2277 focus_order_to_top(self
);
2285 client_set_desktop(self
, screen_desktop
, FALSE
);
2287 old
= self
->wmstate
;
2288 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2289 if (old
!= self
->wmstate
)
2290 PROP_MSG(self
->window
, kde_wm_change_state
,
2291 self
->wmstate
, 1, 0, 0);
2293 /* this puts it after the current focused window */
2294 focus_order_remove(self
);
2295 focus_order_add_new(self
);
2297 /* this is here cuz with the VIDMODE extension, the viewport can
2298 change while a fullscreen window is iconic, and when it
2299 uniconifies, it would be nice if it did so to the new position
2301 client_reconfigure(self
);
2308 client_change_state(self
);
2309 client_showhide(self
);
2310 screen_update_areas();
2313 /* iconify all transients */
2314 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2315 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2319 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2321 /* move up the transient chain as far as possible first */
2322 self
= client_search_top_transient(self
);
2324 client_iconify_recursive(client_search_top_transient(self
),
2328 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2332 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2333 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2335 /* check if already done */
2337 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2338 if (dir
== 1 && self
->max_horz
) return;
2339 if (dir
== 2 && self
->max_vert
) return;
2341 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2342 if (dir
== 1 && !self
->max_horz
) return;
2343 if (dir
== 2 && !self
->max_vert
) return;
2346 /* we just tell it to configure in the same place and client_configure
2347 worries about filling the screen with the window */
2350 w
= self
->area
.width
;
2351 h
= self
->area
.height
;
2355 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2356 RECT_SET(self
->pre_max_area
,
2357 self
->area
.x
, self
->pre_max_area
.y
,
2358 self
->area
.width
, self
->pre_max_area
.height
);
2360 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2361 RECT_SET(self
->pre_max_area
,
2362 self
->pre_max_area
.x
, self
->area
.y
,
2363 self
->pre_max_area
.width
, self
->area
.height
);
2369 a
= screen_area_monitor(self
->desktop
, 0);
2370 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2371 if (self
->pre_max_area
.width
> 0) {
2372 x
= self
->pre_max_area
.x
;
2373 w
= self
->pre_max_area
.width
;
2375 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2376 0, self
->pre_max_area
.height
);
2378 /* pick some fallbacks... */
2379 x
= a
->x
+ a
->width
/ 4;
2383 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2384 if (self
->pre_max_area
.height
> 0) {
2385 y
= self
->pre_max_area
.y
;
2386 h
= self
->pre_max_area
.height
;
2388 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2389 self
->pre_max_area
.width
, 0);
2391 /* pick some fallbacks... */
2392 y
= a
->y
+ a
->height
/ 4;
2398 if (dir
== 0 || dir
== 1) /* horz */
2399 self
->max_horz
= max
;
2400 if (dir
== 0 || dir
== 2) /* vert */
2401 self
->max_vert
= max
;
2403 client_change_state(self
); /* change the state hints on the client */
2405 client_setup_decor_and_functions(self
);
2407 client_move_resize(self
, x
, y
, w
, h
);
2410 void client_shade(ObClient
*self
, gboolean shade
)
2412 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2413 shade
) || /* can't shade */
2414 self
->shaded
== shade
) return; /* already done */
2416 /* when we're iconic, don't change the wmstate */
2417 if (!self
->iconic
) {
2420 old
= self
->wmstate
;
2421 self
->wmstate
= shade
? IconicState
: NormalState
;
2422 if (old
!= self
->wmstate
)
2423 PROP_MSG(self
->window
, kde_wm_change_state
,
2424 self
->wmstate
, 1, 0, 0);
2427 self
->shaded
= shade
;
2428 client_change_state(self
);
2429 /* resize the frame to just the titlebar */
2430 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2433 void client_close(ObClient
*self
)
2437 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2439 /* in the case that the client provides no means to requesting that it
2440 close, we just kill it */
2441 if (!self
->delete_window
)
2445 XXX: itd be cool to do timeouts and shit here for killing the client's
2447 like... if the window is around after 5 seconds, then the close button
2448 turns a nice red, and if this function is called again, the client is
2452 ce
.xclient
.type
= ClientMessage
;
2453 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2454 ce
.xclient
.display
= ob_display
;
2455 ce
.xclient
.window
= self
->window
;
2456 ce
.xclient
.format
= 32;
2457 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2458 ce
.xclient
.data
.l
[1] = event_lasttime
;
2459 ce
.xclient
.data
.l
[2] = 0l;
2460 ce
.xclient
.data
.l
[3] = 0l;
2461 ce
.xclient
.data
.l
[4] = 0l;
2462 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2465 void client_kill(ObClient
*self
)
2467 XKillClient(ob_display
, self
->window
);
2470 void client_set_desktop_recursive(ObClient
*self
,
2471 guint target
, gboolean donthide
)
2476 if (target
!= self
->desktop
) {
2478 ob_debug("Setting desktop %u\n", target
+1);
2480 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2482 /* remove from the old desktop(s) */
2483 focus_order_remove(self
);
2485 old
= self
->desktop
;
2486 self
->desktop
= target
;
2487 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2488 /* the frame can display the current desktop state */
2489 frame_adjust_state(self
->frame
);
2490 /* 'move' the window to the new desktop */
2492 client_showhide(self
);
2493 /* raise if it was not already on the desktop */
2494 if (old
!= DESKTOP_ALL
)
2496 screen_update_areas();
2498 /* add to the new desktop(s) */
2499 if (config_focus_new
)
2500 focus_order_to_top(self
);
2502 focus_order_to_bottom(self
);
2505 /* move all transients */
2506 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2507 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2511 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2513 client_set_desktop_recursive(client_search_top_transient(self
),
2517 ObClient
*client_search_modal_child(ObClient
*self
)
2522 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2523 ObClient
*c
= it
->data
;
2524 if ((ret
= client_search_modal_child(c
))) return ret
;
2525 if (c
->modal
) return c
;
2530 gboolean
client_validate(ObClient
*self
)
2534 XSync(ob_display
, FALSE
); /* get all events on the server */
2536 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2537 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2538 XPutBackEvent(ob_display
, &e
);
2545 void client_set_wm_state(ObClient
*self
, glong state
)
2547 if (state
== self
->wmstate
) return; /* no change */
2551 client_iconify(self
, TRUE
, TRUE
);
2554 client_iconify(self
, FALSE
, TRUE
);
2559 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2561 gboolean shaded
= self
->shaded
;
2562 gboolean fullscreen
= self
->fullscreen
;
2563 gboolean undecorated
= self
->undecorated
;
2564 gboolean max_horz
= self
->max_horz
;
2565 gboolean max_vert
= self
->max_vert
;
2566 gboolean modal
= self
->modal
;
2567 gboolean iconic
= self
->iconic
;
2570 if (!(action
== prop_atoms
.net_wm_state_add
||
2571 action
== prop_atoms
.net_wm_state_remove
||
2572 action
== prop_atoms
.net_wm_state_toggle
))
2573 /* an invalid action was passed to the client message, ignore it */
2576 for (i
= 0; i
< 2; ++i
) {
2577 Atom state
= i
== 0 ? data1
: data2
;
2579 if (!state
) continue;
2581 /* if toggling, then pick whether we're adding or removing */
2582 if (action
== prop_atoms
.net_wm_state_toggle
) {
2583 if (state
== prop_atoms
.net_wm_state_modal
)
2584 action
= modal
? prop_atoms
.net_wm_state_remove
:
2585 prop_atoms
.net_wm_state_add
;
2586 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2587 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2588 prop_atoms
.net_wm_state_add
;
2589 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2590 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2591 prop_atoms
.net_wm_state_add
;
2592 else if (state
== prop_atoms
.net_wm_state_shaded
)
2593 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2594 prop_atoms
.net_wm_state_add
;
2595 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2596 action
= self
->skip_taskbar
?
2597 prop_atoms
.net_wm_state_remove
:
2598 prop_atoms
.net_wm_state_add
;
2599 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2600 action
= self
->skip_pager
?
2601 prop_atoms
.net_wm_state_remove
:
2602 prop_atoms
.net_wm_state_add
;
2603 else if (state
== prop_atoms
.net_wm_state_hidden
)
2604 action
= self
->iconic
?
2605 prop_atoms
.net_wm_state_remove
:
2606 prop_atoms
.net_wm_state_add
;
2607 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2608 action
= fullscreen
?
2609 prop_atoms
.net_wm_state_remove
:
2610 prop_atoms
.net_wm_state_add
;
2611 else if (state
== prop_atoms
.net_wm_state_above
)
2612 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2613 prop_atoms
.net_wm_state_add
;
2614 else if (state
== prop_atoms
.net_wm_state_below
)
2615 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2616 prop_atoms
.net_wm_state_add
;
2617 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2618 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2619 prop_atoms
.net_wm_state_add
;
2622 if (action
== prop_atoms
.net_wm_state_add
) {
2623 if (state
== prop_atoms
.net_wm_state_modal
) {
2625 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2627 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2629 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2631 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2632 self
->skip_taskbar
= TRUE
;
2633 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2634 self
->skip_pager
= TRUE
;
2635 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2637 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2639 } else if (state
== prop_atoms
.net_wm_state_above
) {
2641 self
->below
= FALSE
;
2642 } else if (state
== prop_atoms
.net_wm_state_below
) {
2643 self
->above
= FALSE
;
2645 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2649 } else { /* action == prop_atoms.net_wm_state_remove */
2650 if (state
== prop_atoms
.net_wm_state_modal
) {
2652 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2654 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2656 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2658 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2659 self
->skip_taskbar
= FALSE
;
2660 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2661 self
->skip_pager
= FALSE
;
2662 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2664 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2666 } else if (state
== prop_atoms
.net_wm_state_above
) {
2667 self
->above
= FALSE
;
2668 } else if (state
== prop_atoms
.net_wm_state_below
) {
2669 self
->below
= FALSE
;
2670 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2671 undecorated
= FALSE
;
2675 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2676 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2678 if (max_horz
== max_vert
) { /* both going the same way */
2679 client_maximize(self
, max_horz
, 0, TRUE
);
2681 client_maximize(self
, max_horz
, 1, TRUE
);
2682 client_maximize(self
, max_vert
, 2, TRUE
);
2686 if (max_horz
!= self
->max_horz
)
2687 client_maximize(self
, max_horz
, 1, TRUE
);
2689 client_maximize(self
, max_vert
, 2, TRUE
);
2692 /* change fullscreen state before shading, as it will affect if the window
2694 if (fullscreen
!= self
->fullscreen
)
2695 client_fullscreen(self
, fullscreen
, TRUE
);
2696 if (shaded
!= self
->shaded
)
2697 client_shade(self
, shaded
);
2698 if (undecorated
!= self
->undecorated
)
2699 client_set_undecorated(self
, undecorated
);
2700 if (modal
!= self
->modal
) {
2701 self
->modal
= modal
;
2702 /* when a window changes modality, then its stacking order with its
2703 transients needs to change */
2706 if (iconic
!= self
->iconic
)
2707 client_iconify(self
, iconic
, FALSE
);
2709 client_calc_layer(self
);
2710 client_change_state(self
); /* change the hint to reflect these changes */
2713 ObClient
*client_focus_target(ObClient
*self
)
2717 /* if we have a modal child, then focus it, not us */
2718 child
= client_search_modal_child(client_search_top_transient(self
));
2719 if (child
) return child
;
2723 gboolean
client_can_focus(ObClient
*self
)
2727 /* choose the correct target */
2728 self
= client_focus_target(self
);
2730 if (!self
->frame
->visible
)
2733 if (!(self
->can_focus
|| self
->focus_notify
))
2736 /* do a check to see if the window has already been unmapped or destroyed
2737 do this intelligently while watching out for unmaps we've generated
2738 (ignore_unmaps > 0) */
2739 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2740 DestroyNotify
, &ev
)) {
2741 XPutBackEvent(ob_display
, &ev
);
2744 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2745 UnmapNotify
, &ev
)) {
2746 if (self
->ignore_unmaps
) {
2747 self
->ignore_unmaps
--;
2749 XPutBackEvent(ob_display
, &ev
);
2757 gboolean
client_focus(ObClient
*self
)
2759 /* choose the correct target */
2760 self
= client_focus_target(self
);
2762 if (!client_can_focus(self
)) {
2763 if (!self
->frame
->visible
) {
2764 /* update the focus lists */
2765 focus_order_to_top(self
);
2770 if (self
->can_focus
) {
2771 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2772 I choose to use it always, hopefully to find errors quicker, if any
2773 are left. (I hate X. I hate focus events.)
2775 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2776 #799. So now it is RevertToNone again.
2778 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2782 if (self
->focus_notify
) {
2784 ce
.xclient
.type
= ClientMessage
;
2785 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2786 ce
.xclient
.display
= ob_display
;
2787 ce
.xclient
.window
= self
->window
;
2788 ce
.xclient
.format
= 32;
2789 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2790 ce
.xclient
.data
.l
[1] = event_lasttime
;
2791 ce
.xclient
.data
.l
[2] = 0l;
2792 ce
.xclient
.data
.l
[3] = 0l;
2793 ce
.xclient
.data
.l
[4] = 0l;
2794 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2798 ob_debug("%sively focusing %lx at %d\n",
2799 (self
->can_focus
? "act" : "pass"),
2800 self
->window
, (gint
) event_lasttime
);
2803 /* Cause the FocusIn to come back to us. Important for desktop switches,
2804 since otherwise we'll have no FocusIn on the queue and send it off to
2805 the focus_backup. */
2806 XSync(ob_display
, FALSE
);
2810 void client_unfocus(ObClient
*self
)
2812 if (focus_client
== self
) {
2814 ob_debug("client_unfocus for %lx\n", self
->window
);
2816 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2820 void client_activate(ObClient
*self
, gboolean here
)
2822 /* This check is for the client_list_menu trying to activate
2823 * a closed client. */
2824 if (!g_list_find(client_list
, self
)) return;
2825 if (client_normal(self
) && screen_showing_desktop
)
2826 screen_show_desktop(FALSE
);
2828 client_iconify(self
, FALSE
, here
);
2829 if (self
->desktop
!= DESKTOP_ALL
&&
2830 self
->desktop
!= screen_desktop
) {
2832 client_set_desktop(self
, screen_desktop
, FALSE
);
2834 screen_set_desktop(self
->desktop
);
2835 } else if (!self
->frame
->visible
)
2836 /* if its not visible for other reasons, then don't mess
2840 client_shade(self
, FALSE
);
2844 /* we do this an action here. this is rather important. this is because
2845 we want the results from the focus change to take place BEFORE we go
2846 about raising the window. when a fullscreen window loses focus, we need
2847 this or else the raise wont be able to raise above the to-lose-focus
2848 fullscreen window. */
2852 void client_raise(ObClient
*self
)
2854 action_run_string("Raise", self
);
2857 void client_lower(ObClient
*self
)
2859 action_run_string("Lower", self
);
2862 gboolean
client_focused(ObClient
*self
)
2864 return self
== focus_client
;
2867 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
2870 /* si is the smallest image >= req */
2871 /* li is the largest image < req */
2872 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2874 if (!self
->nicons
) {
2875 ObClientIcon
*parent
= NULL
;
2877 if (self
->transient_for
) {
2878 if (self
->transient_for
!= OB_TRAN_GROUP
)
2879 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2882 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2883 ObClient
*c
= it
->data
;
2884 if (c
!= self
&& !c
->transient_for
) {
2885 if ((parent
= client_icon_recursive(c
, w
, h
)))
2895 for (i
= 0; i
< self
->nicons
; ++i
) {
2896 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2897 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2901 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2906 if (largest
== 0) /* didnt find one smaller than the requested size */
2907 return &self
->icons
[si
];
2908 return &self
->icons
[li
];
2911 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
2914 static ObClientIcon deficon
;
2916 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
2917 deficon
.width
= deficon
.height
= 48;
2918 deficon
.data
= ob_rr_theme
->def_win_icon
;
2924 /* this be mostly ripped from fvwm */
2925 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2927 gint my_cx
, my_cy
, his_cx
, his_cy
;
2930 gint score
, best_score
;
2931 ObClient
*best_client
, *cur
;
2937 /* first, find the centre coords of the currently focused window */
2938 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2939 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2944 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
2947 /* the currently selected window isn't interesting */
2950 if (!client_normal(cur
))
2952 /* using c->desktop instead of screen_desktop doesn't work if the
2953 * current window was omnipresent, hope this doesn't have any other
2955 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2959 if(client_focus_target(cur
) == cur
&&
2960 !(cur
->can_focus
|| cur
->focus_notify
))
2963 /* find the centre coords of this window, from the
2964 * currently focused window's point of view */
2965 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2966 + cur
->frame
->area
.width
/ 2;
2967 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2968 + cur
->frame
->area
.height
/ 2;
2970 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
2971 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
2973 /* Rotate the diagonals 45 degrees counterclockwise.
2974 * To do this, multiply the matrix /+h +h\ with the
2975 * vector (x y). \-h +h/
2976 * h = sqrt(0.5). We can set h := 1 since absolute
2977 * distance doesn't matter here. */
2978 tx
= his_cx
+ his_cy
;
2979 his_cy
= -his_cx
+ his_cy
;
2984 case OB_DIRECTION_NORTH
:
2985 case OB_DIRECTION_SOUTH
:
2986 case OB_DIRECTION_NORTHEAST
:
2987 case OB_DIRECTION_SOUTHWEST
:
2988 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
2989 distance
= ((dir
== OB_DIRECTION_NORTH
||
2990 dir
== OB_DIRECTION_NORTHEAST
) ?
2993 case OB_DIRECTION_EAST
:
2994 case OB_DIRECTION_WEST
:
2995 case OB_DIRECTION_SOUTHEAST
:
2996 case OB_DIRECTION_NORTHWEST
:
2997 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
2998 distance
= ((dir
== OB_DIRECTION_WEST
||
2999 dir
== OB_DIRECTION_NORTHWEST
) ?
3004 /* the target must be in the requested direction */
3008 /* Calculate score for this window. The smaller the better. */
3009 score
= distance
+ offset
;
3011 /* windows more than 45 degrees off the direction are
3012 * heavily penalized and will only be chosen if nothing
3013 * else within a million pixels */
3014 if(offset
> distance
)
3017 if(best_score
== -1 || score
< best_score
)
3025 void client_set_layer(ObClient
*self
, gint layer
)
3029 self
->above
= FALSE
;
3030 } else if (layer
== 0) {
3031 self
->below
= self
->above
= FALSE
;
3033 self
->below
= FALSE
;
3036 client_calc_layer(self
);
3037 client_change_state(self
); /* reflect this in the state hints */
3040 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3042 if (self
->undecorated
!= undecorated
) {
3043 self
->undecorated
= undecorated
;
3044 client_setup_decor_and_functions(self
);
3045 /* Make sure the client knows it might have moved. Maybe there is a
3046 * better way of doing this so only one client_configure is sent, but
3047 * since 125 of these are sent per second when moving the window (with
3048 * user = FALSE) i doubt it matters much.
3050 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3051 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3052 client_change_state(self
); /* reflect this in the state hints */
3056 /* Determines which physical monitor a client is on by calculating the
3057 area of the part of the client on each monitor. The number of the
3058 monitor containing the greatest area of the client is returned.*/
3059 guint
client_monitor(ObClient
*self
)
3065 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3066 Rect
*area
= screen_physical_area_monitor(i
);
3067 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3071 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3072 v
= r
.width
* r
.height
;
3083 ObClient
*client_search_top_transient(ObClient
*self
)
3085 /* move up the transient chain as far as possible */
3086 if (self
->transient_for
) {
3087 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3088 return client_search_top_transient(self
->transient_for
);
3092 g_assert(self
->group
);
3094 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3095 ObClient
*c
= it
->data
;
3097 /* checking transient_for prevents infinate loops! */
3098 if (c
!= self
&& !c
->transient_for
)
3109 ObClient
*client_search_focus_parent(ObClient
*self
)
3111 if (self
->transient_for
) {
3112 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3113 if (client_focused(self
->transient_for
))
3114 return self
->transient_for
;
3118 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3119 ObClient
*c
= it
->data
;
3121 /* checking transient_for prevents infinate loops! */
3122 if (c
!= self
&& !c
->transient_for
)
3123 if (client_focused(c
))
3132 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3134 if (self
->transient_for
) {
3135 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3136 if (self
->transient_for
== search
)
3141 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3142 ObClient
*c
= it
->data
;
3144 /* checking transient_for prevents infinate loops! */
3145 if (c
!= self
&& !c
->transient_for
)
3155 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3159 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3160 if (sit
->data
== search
)
3162 if (client_search_transient(sit
->data
, search
))
3168 void client_update_sm_client_id(ObClient
*self
)
3170 g_free(self
->sm_client_id
);
3171 self
->sm_client_id
= NULL
;
3173 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3175 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3176 &self
->sm_client_id
);
3179 /* finds the nearest edge in the given direction from the current client
3180 * note to self: the edge is the -frame- edge (the actual one), not the
3183 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
)
3185 gint dest
, monitor_dest
;
3186 gint my_edge_start
, my_edge_end
, my_offset
;
3193 a
= screen_area(c
->desktop
);
3194 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3197 case OB_DIRECTION_NORTH
:
3198 my_edge_start
= c
->frame
->area
.x
;
3199 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3200 my_offset
= c
->frame
->area
.y
;
3202 /* default: top of screen */
3204 monitor_dest
= monitor
->y
;
3205 /* if the monitor edge comes before the screen edge, */
3206 /* use that as the destination instead. (For xinerama) */
3207 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3208 dest
= monitor_dest
;
3210 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3211 gint his_edge_start
, his_edge_end
, his_offset
;
3212 ObClient
*cur
= it
->data
;
3216 if(!client_normal(cur
))
3218 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3222 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3225 his_edge_start
= cur
->frame
->area
.x
;
3226 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3227 his_offset
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3229 if(his_offset
+ 1 > my_offset
)
3232 if(his_offset
< dest
)
3235 if(his_edge_start
>= my_edge_start
&&
3236 his_edge_start
<= my_edge_end
)
3239 if(my_edge_start
>= his_edge_start
&&
3240 my_edge_start
<= his_edge_end
)
3245 case OB_DIRECTION_SOUTH
:
3246 my_edge_start
= c
->frame
->area
.x
;
3247 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3248 my_offset
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3250 /* default: bottom of screen */
3251 dest
= a
->y
+ a
->height
;
3252 monitor_dest
= monitor
->y
+ monitor
->height
;
3253 /* if the monitor edge comes before the screen edge, */
3254 /* use that as the destination instead. (For xinerama) */
3255 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3256 dest
= monitor_dest
;
3258 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3259 gint his_edge_start
, his_edge_end
, his_offset
;
3260 ObClient
*cur
= it
->data
;
3264 if(!client_normal(cur
))
3266 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3270 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3273 his_edge_start
= cur
->frame
->area
.x
;
3274 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3275 his_offset
= cur
->frame
->area
.y
;
3278 if(his_offset
- 1 < my_offset
)
3281 if(his_offset
> dest
)
3284 if(his_edge_start
>= my_edge_start
&&
3285 his_edge_start
<= my_edge_end
)
3288 if(my_edge_start
>= his_edge_start
&&
3289 my_edge_start
<= his_edge_end
)
3294 case OB_DIRECTION_WEST
:
3295 my_edge_start
= c
->frame
->area
.y
;
3296 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3297 my_offset
= c
->frame
->area
.x
;
3299 /* default: leftmost egde of screen */
3301 monitor_dest
= monitor
->x
;
3302 /* if the monitor edge comes before the screen edge, */
3303 /* use that as the destination instead. (For xinerama) */
3304 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3305 dest
= monitor_dest
;
3307 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3308 gint his_edge_start
, his_edge_end
, his_offset
;
3309 ObClient
*cur
= it
->data
;
3313 if(!client_normal(cur
))
3315 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3319 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3322 his_edge_start
= cur
->frame
->area
.y
;
3323 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3324 his_offset
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3326 if(his_offset
+ 1 > my_offset
)
3329 if(his_offset
< dest
)
3332 if(his_edge_start
>= my_edge_start
&&
3333 his_edge_start
<= my_edge_end
)
3336 if(my_edge_start
>= his_edge_start
&&
3337 my_edge_start
<= his_edge_end
)
3343 case OB_DIRECTION_EAST
:
3344 my_edge_start
= c
->frame
->area
.y
;
3345 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3346 my_offset
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3348 /* default: rightmost edge of screen */
3349 dest
= a
->x
+ a
->width
;
3350 monitor_dest
= monitor
->x
+ monitor
->width
;
3351 /* if the monitor edge comes before the screen edge, */
3352 /* use that as the destination instead. (For xinerama) */
3353 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3354 dest
= monitor_dest
;
3356 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3357 gint his_edge_start
, his_edge_end
, his_offset
;
3358 ObClient
*cur
= it
->data
;
3362 if(!client_normal(cur
))
3364 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3368 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3371 his_edge_start
= cur
->frame
->area
.y
;
3372 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3373 his_offset
= cur
->frame
->area
.x
;
3375 if(his_offset
- 1 < my_offset
)
3378 if(his_offset
> dest
)
3381 if(his_edge_start
>= my_edge_start
&&
3382 his_edge_start
<= my_edge_end
)
3385 if(my_edge_start
>= his_edge_start
&&
3386 my_edge_start
<= his_edge_end
)
3391 case OB_DIRECTION_NORTHEAST
:
3392 case OB_DIRECTION_SOUTHEAST
:
3393 case OB_DIRECTION_NORTHWEST
:
3394 case OB_DIRECTION_SOUTHWEST
:
3395 /* not implemented */
3397 g_assert_not_reached();
3398 dest
= 0; /* suppress warning */
3403 ObClient
* client_under_pointer()
3407 ObClient
*ret
= NULL
;
3409 if (screen_pointer_pos(&x
, &y
)) {
3410 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3411 if (WINDOW_IS_CLIENT(it
->data
)) {
3412 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3413 if (c
->frame
->visible
&&
3414 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3424 gboolean
client_has_group_siblings(ObClient
*self
)
3426 return self
->group
&& self
->group
->members
->next
;