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 /* XXX move that function here */
299 settings
= (ObAppSetting
*) get_client_settings(self
);
302 if (settings
->shade
&& !settings
->decor
)
303 settings
->decor
= TRUE
;
305 client_shade(self
, settings
->shade
);
306 client_set_undecorated(self
, !settings
->decor
);
308 if (settings
->desktop
!= -1)
309 client_set_desktop(self
, settings
->desktop
, FALSE
);
311 client_set_layer(self
, settings
->layer
);
314 stacking_add(CLIENT_AS_WINDOW(self
));
315 client_restore_session_stacking(self
);
317 /* focus the new window? */
318 if (ob_state() != OB_STATE_STARTING
&&
319 (config_focus_new
|| client_search_focus_parent(self
)) ||
320 (settings
&& settings
->focus
) &&
321 /* note the check against Type_Normal/Dialog, not client_normal(self),
322 which would also include other types. in this case we want more
323 strict rules for focus */
324 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
325 self
->type
== OB_CLIENT_TYPE_DIALOG
))
329 if (self
->desktop
!= screen_desktop
) {
330 /* activate the window */
333 gboolean group_foc
= FALSE
;
338 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
340 if (client_focused(it
->data
))
348 (!self
->transient_for
&& (!self
->group
||
349 !self
->group
->members
->next
))) ||
350 client_search_focus_tree_full(self
) ||
352 !client_normal(focus_client
))
354 /* activate the window */
361 if (ob_state() == OB_STATE_RUNNING
) {
362 gint x
= self
->area
.x
, ox
= x
;
363 gint y
= self
->area
.y
, oy
= y
;
366 /* XXX put this in place.c */
367 place_window_from_settings(settings
, self
, &x
, &y
);
369 place_client(self
, &x
, &y
);
371 /* make sure the window is visible. */
372 client_find_onscreen(self
, &x
, &y
,
373 self
->frame
->area
.width
,
374 self
->frame
->area
.height
,
375 /* non-normal clients has less rules, and
376 windows that are being restored from a
377 session do also. we can assume you want
378 it back where you saved it. Clients saying
379 they placed themselves are subjected to
380 harder rules, ones that are placed by
381 place.c or by the user are allowed partially
382 off-screen and on xinerama divides (ie,
383 it is up to the placement routines to avoid
384 the xinerama divides) */
385 ((self
->positioned
& PPosition
) &&
386 !(self
->positioned
& USPosition
)) &&
387 client_normal(self
) &&
389 if (x
!= ox
|| y
!= oy
)
390 client_move(self
, x
, y
);
393 keyboard_grab_for_client(self
, TRUE
);
394 mouse_grab_for_client(self
, TRUE
);
396 client_showhide(self
);
398 /* use client_focus instead of client_activate cuz client_activate does
399 stuff like switch desktops etc and I'm not interested in all that when
400 a window maps since its not based on an action from the user like
401 clicking a window to activate is. so keep the new window out of the way
404 /* if using focus_delay, stop the timer now so that focus doesn't go
406 event_halt_focus_delay();
409 /* since focus can change the stacking orders, if we focus the window
410 then the standard raise it gets is not enough, we need to queue one
411 for after the focus change takes place */
415 /* client_activate does this but we aret using it so we have to do it
417 if (screen_showing_desktop
)
418 screen_show_desktop(FALSE
);
420 /* add to client list/map */
421 client_list
= g_list_append(client_list
, self
);
422 g_hash_table_insert(window_map
, &self
->window
, self
);
424 /* this has to happen after we're in the client_list */
425 screen_update_areas();
427 /* update the list hints */
430 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
433 void client_unmanage_all()
435 while (client_list
!= NULL
)
436 client_unmanage(client_list
->data
);
439 void client_unmanage(ObClient
*self
)
444 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
446 g_assert(self
!= NULL
);
448 keyboard_grab_for_client(self
, FALSE
);
449 mouse_grab_for_client(self
, FALSE
);
451 /* potentially fix focusLast */
452 if (config_focus_last
)
453 grab_pointer(TRUE
, OB_CURSOR_NONE
);
455 /* remove the window from our save set */
456 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
458 /* we dont want events no more */
459 XSelectInput(ob_display
, self
->window
, NoEventMask
);
461 frame_hide(self
->frame
);
463 client_list
= g_list_remove(client_list
, self
);
464 stacking_remove(self
);
465 g_hash_table_remove(window_map
, &self
->window
);
467 /* update the focus lists */
468 focus_order_remove(self
);
470 /* once the client is out of the list, update the struts to remove it's
472 screen_update_areas();
474 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
475 Destructor
*d
= it
->data
;
476 d
->func(self
, d
->data
);
479 if (focus_client
== self
) {
482 /* focus the last focused window on the desktop, and ignore enter
483 events from the unmap so it doesnt mess with the focus */
484 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
485 /* remove these flags so we don't end up getting focused in the
487 self
->can_focus
= FALSE
;
488 self
->focus_notify
= FALSE
;
490 client_unfocus(self
);
493 /* tell our parent(s) that we're gone */
494 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
495 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
496 if (it
->data
!= self
)
497 ((ObClient
*)it
->data
)->transients
=
498 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
499 } else if (self
->transient_for
) { /* transient of window */
500 self
->transient_for
->transients
=
501 g_slist_remove(self
->transient_for
->transients
, self
);
504 /* tell our transients that we're gone */
505 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
506 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
507 ((ObClient
*)it
->data
)->transient_for
= NULL
;
508 client_calc_layer(it
->data
);
512 /* remove from its group */
514 group_remove(self
->group
, self
);
518 /* give the client its border back */
519 client_toggle_border(self
, TRUE
);
521 /* reparent the window out of the frame, and free the frame */
522 frame_release_client(self
->frame
, self
);
525 if (ob_state() != OB_STATE_EXITING
) {
526 /* these values should not be persisted across a window
528 PROP_ERASE(self
->window
, net_wm_desktop
);
529 PROP_ERASE(self
->window
, net_wm_state
);
530 PROP_ERASE(self
->window
, wm_state
);
532 /* if we're left in an unmapped state, the client wont be mapped. this
533 is bad, since we will no longer be managing the window on restart */
534 XMapWindow(ob_display
, self
->window
);
538 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
540 /* free all data allocated in the client struct */
541 g_slist_free(self
->transients
);
542 for (j
= 0; j
< self
->nicons
; ++j
)
543 g_free(self
->icons
[j
].data
);
544 if (self
->nicons
> 0)
547 g_free(self
->icon_title
);
551 g_free(self
->sm_client_id
);
554 /* update the list hints */
557 if (config_focus_last
)
558 grab_pointer(FALSE
, OB_CURSOR_NONE
);
561 static void client_urgent_notify(ObClient
*self
)
564 frame_flash_start(self
->frame
);
566 frame_flash_stop(self
->frame
);
569 static void client_restore_session_state(ObClient
*self
)
573 if (!(it
= session_state_find(self
)))
576 self
->session
= it
->data
;
578 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
579 self
->positioned
= PPosition
;
580 if (self
->session
->w
> 0)
581 self
->area
.width
= self
->session
->w
;
582 if (self
->session
->h
> 0)
583 self
->area
.height
= self
->session
->h
;
584 XResizeWindow(ob_display
, self
->window
,
585 self
->area
.width
, self
->area
.height
);
587 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
588 self
->session
->desktop
:
589 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
590 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
592 self
->shaded
= self
->session
->shaded
;
593 self
->iconic
= self
->session
->iconic
;
594 self
->skip_pager
= self
->session
->skip_pager
;
595 self
->skip_taskbar
= self
->session
->skip_taskbar
;
596 self
->fullscreen
= self
->session
->fullscreen
;
597 self
->above
= self
->session
->above
;
598 self
->below
= self
->session
->below
;
599 self
->max_horz
= self
->session
->max_horz
;
600 self
->max_vert
= self
->session
->max_vert
;
603 static void client_restore_session_stacking(ObClient
*self
)
607 if (!self
->session
) return;
609 it
= g_list_find(session_saved_state
, self
->session
);
610 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
613 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
614 if (session_state_cmp(it
->data
, cit
->data
))
617 client_calc_layer(self
);
618 stacking_below(CLIENT_AS_WINDOW(self
),
619 CLIENT_AS_WINDOW(cit
->data
));
625 void client_move_onscreen(ObClient
*self
, gboolean rude
)
627 gint x
= self
->area
.x
;
628 gint y
= self
->area
.y
;
629 if (client_find_onscreen(self
, &x
, &y
,
630 self
->frame
->area
.width
,
631 self
->frame
->area
.height
, rude
)) {
632 client_move(self
, x
, y
);
636 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
640 gint ox
= *x
, oy
= *y
;
642 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
645 /* XXX watch for xinerama dead areas */
646 /* This makes sure windows aren't entirely outside of the screen so you
647 * can't see them at all */
648 if (client_normal(self
)) {
649 a
= screen_area(self
->desktop
);
650 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
651 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
652 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
653 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
654 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
656 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
660 /* This here doesn't let windows even a pixel outside the screen,
661 * when called from client_manage, programs placing themselves are
662 * forced completely onscreen, while things like
663 * xterm -geometry resolution-width/2 will work fine. Trying to
664 * place it completely offscreen will be handled in the above code.
665 * Sorry for this confused comment, i am tired. */
667 /* avoid the xinerama monitor divide while we're at it,
668 * remember to fix the placement stuff to avoid it also and
669 * then remove this XXX */
670 a
= screen_physical_area_monitor(client_monitor(self
));
671 /* dont let windows map/move into the strut unless they
672 are bigger than the available area */
674 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
675 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
676 *x
= a
->x
+ a
->width
- w
;
678 if (h
<= a
->height
) {
679 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
680 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
681 *y
= a
->y
+ a
->height
- h
;
685 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
688 return ox
!= *x
|| oy
!= *y
;
691 static void client_toggle_border(ObClient
*self
, gboolean show
)
693 /* adjust our idea of where the client is, based on its border. When the
694 border is removed, the client should now be considered to be in a
696 when re-adding the border to the client, the same operation needs to be
698 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
699 gint x
= oldx
, y
= oldy
;
700 switch(self
->gravity
) {
702 case NorthWestGravity
:
704 case SouthWestGravity
:
706 case NorthEastGravity
:
708 case SouthEastGravity
:
709 if (show
) x
-= self
->border_width
* 2;
710 else x
+= self
->border_width
* 2;
717 if (show
) x
-= self
->border_width
;
718 else x
+= self
->border_width
;
721 switch(self
->gravity
) {
723 case NorthWestGravity
:
725 case NorthEastGravity
:
727 case SouthWestGravity
:
729 case SouthEastGravity
:
730 if (show
) y
-= self
->border_width
* 2;
731 else y
+= self
->border_width
* 2;
738 if (show
) y
-= self
->border_width
;
739 else y
+= self
->border_width
;
746 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
748 /* move the client so it is back it the right spot _with_ its
750 if (x
!= oldx
|| y
!= oldy
)
751 XMoveWindow(ob_display
, self
->window
, x
, y
);
753 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
757 static void client_get_all(ObClient
*self
)
759 client_get_area(self
);
760 client_update_transient_for(self
);
761 client_update_wmhints(self
);
762 client_get_startup_id(self
);
763 client_get_desktop(self
);
764 client_get_shaped(self
);
766 client_get_mwm_hints(self
);
767 client_get_type(self
);/* this can change the mwmhints for special cases */
769 /* The transient hint is used to pick a type, but the type can also affect
770 transiency (dialogs are always made transients). This is Havoc's idea,
771 but it is needed to make some apps work right (eg tsclient). */
772 client_update_transient_for(self
);
774 client_get_state(self
);
777 /* a couple type-based defaults for new windows */
779 /* this makes sure that these windows appear on all desktops */
780 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
781 self
->desktop
= DESKTOP_ALL
;
784 client_update_protocols(self
);
786 client_get_gravity(self
); /* get the attribute gravity */
787 client_update_normal_hints(self
); /* this may override the attribute
790 /* got the type, the mwmhints, the protocols, and the normal hints
791 (min/max sizes), so we're ready to set up the decorations/functions */
792 client_setup_decor_and_functions(self
);
794 client_update_title(self
);
795 client_update_class(self
);
796 client_update_sm_client_id(self
);
797 client_update_strut(self
);
798 client_update_icons(self
);
801 static void client_get_startup_id(ObClient
*self
)
803 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
805 PROP_GETS(self
->group
->leader
,
806 net_startup_id
, utf8
, &self
->startup_id
);
809 static void client_get_area(ObClient
*self
)
811 XWindowAttributes wattrib
;
814 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
815 g_assert(ret
!= BadWindow
);
817 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
818 self
->border_width
= wattrib
.border_width
;
821 static void client_get_desktop(ObClient
*self
)
823 guint32 d
= screen_num_desktops
; /* an always-invalid value */
825 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
826 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
827 self
->desktop
= screen_num_desktops
- 1;
831 gboolean trdesk
= FALSE
;
833 if (self
->transient_for
) {
834 if (self
->transient_for
!= OB_TRAN_GROUP
) {
835 self
->desktop
= self
->transient_for
->desktop
;
840 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
841 if (it
->data
!= self
&&
842 !((ObClient
*)it
->data
)->transient_for
) {
843 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
850 /* try get from the startup-notification protocol */
851 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
852 if (self
->desktop
>= screen_num_desktops
&&
853 self
->desktop
!= DESKTOP_ALL
)
854 self
->desktop
= screen_num_desktops
- 1;
856 /* defaults to the current desktop */
857 self
->desktop
= screen_desktop
;
860 if (self
->desktop
!= d
) {
861 /* set the desktop hint, to make sure that it always exists */
862 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
866 static void client_get_state(ObClient
*self
)
871 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
873 for (i
= 0; i
< num
; ++i
) {
874 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
876 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
878 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
880 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
881 self
->skip_taskbar
= TRUE
;
882 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
883 self
->skip_pager
= TRUE
;
884 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
885 self
->fullscreen
= TRUE
;
886 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
887 self
->max_vert
= TRUE
;
888 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
889 self
->max_horz
= TRUE
;
890 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
892 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
894 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
895 self
->undecorated
= TRUE
;
901 if (!(self
->above
|| self
->below
)) {
903 /* apply stuff from the group */
907 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
908 ObClient
*c
= it
->data
;
909 if (c
!= self
&& !client_search_transient(self
, c
) &&
910 client_normal(self
) && client_normal(c
))
913 (c
->above
? 1 : (c
->below
? -1 : 0)));
927 g_assert_not_reached();
934 static void client_get_shaped(ObClient
*self
)
936 self
->shaped
= FALSE
;
938 if (extensions_shape
) {
943 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
945 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
946 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
948 self
->shaped
= (s
!= 0);
953 void client_update_transient_for(ObClient
*self
)
956 ObClient
*target
= NULL
;
958 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
959 self
->transient
= TRUE
;
960 if (t
!= self
->window
) { /* cant be transient to itself! */
961 target
= g_hash_table_lookup(window_map
, &t
);
962 /* if this happens then we need to check for it*/
963 g_assert(target
!= self
);
964 if (target
&& !WINDOW_IS_CLIENT(target
)) {
965 /* this can happen when a dialog is a child of
966 a dockapp, for example */
970 if (!target
&& self
->group
) {
971 /* not transient to a client, see if it is transient for a
973 if (t
== self
->group
->leader
||
975 t
== RootWindow(ob_display
, ob_screen
))
977 /* window is a transient for its group! */
978 target
= OB_TRAN_GROUP
;
982 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
983 self
->transient
= TRUE
;
984 target
= OB_TRAN_GROUP
;
986 self
->transient
= FALSE
;
988 /* if anything has changed... */
989 if (target
!= self
->transient_for
) {
990 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
993 /* remove from old parents */
994 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
995 ObClient
*c
= it
->data
;
996 if (c
!= self
&& !c
->transient_for
)
997 c
->transients
= g_slist_remove(c
->transients
, self
);
999 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1000 /* remove from old parent */
1001 self
->transient_for
->transients
=
1002 g_slist_remove(self
->transient_for
->transients
, self
);
1004 self
->transient_for
= target
;
1005 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1008 /* add to new parents */
1009 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1010 ObClient
*c
= it
->data
;
1011 if (c
!= self
&& !c
->transient_for
)
1012 c
->transients
= g_slist_append(c
->transients
, self
);
1015 /* remove all transients which are in the group, that causes
1016 circlular pointer hell of doom */
1017 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1019 for (sit
= self
->transients
; sit
; sit
= next
) {
1020 next
= g_slist_next(sit
);
1021 if (sit
->data
== it
->data
)
1023 g_slist_delete_link(self
->transients
, sit
);
1026 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1027 /* add to new parent */
1028 self
->transient_for
->transients
=
1029 g_slist_append(self
->transient_for
->transients
, self
);
1034 static void client_get_mwm_hints(ObClient
*self
)
1039 self
->mwmhints
.flags
= 0; /* default to none */
1041 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1043 if (num
>= OB_MWM_ELEMENTS
) {
1044 self
->mwmhints
.flags
= hints
[0];
1045 self
->mwmhints
.functions
= hints
[1];
1046 self
->mwmhints
.decorations
= hints
[2];
1052 void client_get_type(ObClient
*self
)
1059 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1060 /* use the first value that we know about in the array */
1061 for (i
= 0; i
< num
; ++i
) {
1062 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1063 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1064 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1065 self
->type
= OB_CLIENT_TYPE_DOCK
;
1066 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1067 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1068 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1069 self
->type
= OB_CLIENT_TYPE_MENU
;
1070 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1071 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1072 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1073 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1074 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1075 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1076 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1077 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1078 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1079 /* prevent this window from getting any decor or
1081 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1082 OB_MWM_FLAG_DECORATIONS
);
1083 self
->mwmhints
.decorations
= 0;
1084 self
->mwmhints
.functions
= 0;
1086 if (self
->type
!= (ObClientType
) -1)
1087 break; /* grab the first legit type */
1092 if (self
->type
== (ObClientType
) -1) {
1093 /*the window type hint was not set, which means we either classify
1094 ourself as a normal window or a dialog, depending on if we are a
1096 if (self
->transient
)
1097 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1099 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1103 void client_update_protocols(ObClient
*self
)
1106 guint num_return
, i
;
1108 self
->focus_notify
= FALSE
;
1109 self
->delete_window
= FALSE
;
1111 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1112 for (i
= 0; i
< num_return
; ++i
) {
1113 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1114 /* this means we can request the window to close */
1115 self
->delete_window
= TRUE
;
1116 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1117 /* if this protocol is requested, then the window will be
1118 notified whenever we want it to receive focus */
1119 self
->focus_notify
= TRUE
;
1125 static void client_get_gravity(ObClient
*self
)
1127 XWindowAttributes wattrib
;
1130 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1131 g_assert(ret
!= BadWindow
);
1132 self
->gravity
= wattrib
.win_gravity
;
1135 void client_update_normal_hints(ObClient
*self
)
1139 gint oldgravity
= self
->gravity
;
1142 self
->min_ratio
= 0.0f
;
1143 self
->max_ratio
= 0.0f
;
1144 SIZE_SET(self
->size_inc
, 1, 1);
1145 SIZE_SET(self
->base_size
, 0, 0);
1146 SIZE_SET(self
->min_size
, 0, 0);
1147 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1149 /* get the hints from the window */
1150 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1151 /* normal windows can't request placement! har har
1152 if (!client_normal(self))
1154 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1156 if (size
.flags
& PWinGravity
) {
1157 self
->gravity
= size
.win_gravity
;
1159 /* if the client has a frame, i.e. has already been mapped and
1160 is changing its gravity */
1161 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1162 /* move our idea of the client's position based on its new
1164 self
->area
.x
= self
->frame
->area
.x
;
1165 self
->area
.y
= self
->frame
->area
.y
;
1166 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1170 if (size
.flags
& PAspect
) {
1171 if (size
.min_aspect
.y
)
1173 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1174 if (size
.max_aspect
.y
)
1176 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1179 if (size
.flags
& PMinSize
)
1180 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1182 if (size
.flags
& PMaxSize
)
1183 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1185 if (size
.flags
& PBaseSize
)
1186 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1188 if (size
.flags
& PResizeInc
)
1189 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1193 void client_setup_decor_and_functions(ObClient
*self
)
1195 /* start with everything (cept fullscreen) */
1197 (OB_FRAME_DECOR_TITLEBAR
|
1198 (ob_rr_theme
->show_handle
? OB_FRAME_DECOR_HANDLE
: 0) |
1199 OB_FRAME_DECOR_GRIPS
|
1200 OB_FRAME_DECOR_BORDER
|
1201 OB_FRAME_DECOR_ICON
|
1202 OB_FRAME_DECOR_ALLDESKTOPS
|
1203 OB_FRAME_DECOR_ICONIFY
|
1204 OB_FRAME_DECOR_MAXIMIZE
|
1205 OB_FRAME_DECOR_SHADE
|
1206 OB_FRAME_DECOR_CLOSE
);
1208 (OB_CLIENT_FUNC_RESIZE
|
1209 OB_CLIENT_FUNC_MOVE
|
1210 OB_CLIENT_FUNC_ICONIFY
|
1211 OB_CLIENT_FUNC_MAXIMIZE
|
1212 OB_CLIENT_FUNC_SHADE
|
1213 OB_CLIENT_FUNC_CLOSE
);
1215 if (!(self
->min_size
.width
< self
->max_size
.width
||
1216 self
->min_size
.height
< self
->max_size
.height
))
1217 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1219 switch (self
->type
) {
1220 case OB_CLIENT_TYPE_NORMAL
:
1221 /* normal windows retain all of the possible decorations and
1222 functionality, and are the only windows that you can fullscreen */
1223 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1226 case OB_CLIENT_TYPE_DIALOG
:
1227 case OB_CLIENT_TYPE_UTILITY
:
1228 /* these windows cannot be maximized */
1229 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1232 case OB_CLIENT_TYPE_MENU
:
1233 case OB_CLIENT_TYPE_TOOLBAR
:
1234 /* these windows get less functionality */
1235 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1238 case OB_CLIENT_TYPE_DESKTOP
:
1239 case OB_CLIENT_TYPE_DOCK
:
1240 case OB_CLIENT_TYPE_SPLASH
:
1241 /* none of these windows are manipulated by the window manager */
1242 self
->decorations
= 0;
1243 self
->functions
= 0;
1247 /* Mwm Hints are applied subtractively to what has already been chosen for
1248 decor and functionality */
1249 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1250 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1251 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1252 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1253 /* if the mwm hints request no handle or title, then all
1254 decorations are disabled */
1255 self
->decorations
= config_theme_keepborder
? OB_FRAME_DECOR_BORDER
: 0;
1259 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1260 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1261 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1262 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1263 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1264 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1265 /* dont let mwm hints kill any buttons
1266 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1267 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1268 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1269 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1271 /* dont let mwm hints kill the close button
1272 if (! (self->mwmhints.functions & MwmFunc_Close))
1273 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1277 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1278 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1279 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1280 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1281 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1282 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1284 /* can't maximize without moving/resizing */
1285 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1286 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1287 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1288 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1289 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1292 /* kill the handle on fully maxed windows */
1293 if (self
->max_vert
&& self
->max_horz
)
1294 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1296 /* finally, the user can have requested no decorations, which overrides
1297 everything (but doesnt give it a border if it doesnt have one) */
1298 if (self
->undecorated
) {
1299 if (config_theme_keepborder
)
1300 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1302 self
->decorations
= 0;
1305 /* if we don't have a titlebar, then we cannot shade! */
1306 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1307 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1309 /* now we need to check against rules for the client's current state */
1310 if (self
->fullscreen
) {
1311 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1312 OB_CLIENT_FUNC_FULLSCREEN
|
1313 OB_CLIENT_FUNC_ICONIFY
);
1314 self
->decorations
= 0;
1317 client_change_allowed_actions(self
);
1320 /* adjust the client's decorations, etc. */
1321 client_reconfigure(self
);
1325 static void client_change_allowed_actions(ObClient
*self
)
1330 /* desktop windows are kept on all desktops */
1331 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1332 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1334 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1335 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1336 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1337 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1338 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1339 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1340 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1341 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1342 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1343 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1344 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1345 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1346 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1347 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1348 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1351 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1353 /* make sure the window isn't breaking any rules now */
1355 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1356 if (self
->frame
) client_shade(self
, FALSE
);
1357 else self
->shaded
= FALSE
;
1359 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1360 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1361 else self
->iconic
= FALSE
;
1363 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1364 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1365 else self
->fullscreen
= FALSE
;
1367 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1369 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1370 else self
->max_vert
= self
->max_horz
= FALSE
;
1374 void client_reconfigure(ObClient
*self
)
1376 /* by making this pass FALSE for user, we avoid the emacs event storm where
1377 every configurenotify causes an update in its normal hints, i think this
1378 is generally what we want anyways... */
1379 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1380 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1383 void client_update_wmhints(ObClient
*self
)
1386 gboolean ur
= FALSE
;
1389 /* assume a window takes input if it doesnt specify */
1390 self
->can_focus
= TRUE
;
1392 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1393 if (hints
->flags
& InputHint
)
1394 self
->can_focus
= hints
->input
;
1396 /* only do this when first managing the window *AND* when we aren't
1398 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1399 if (hints
->flags
& StateHint
)
1400 self
->iconic
= hints
->initial_state
== IconicState
;
1402 if (hints
->flags
& XUrgencyHint
)
1405 if (!(hints
->flags
& WindowGroupHint
))
1406 hints
->window_group
= None
;
1408 /* did the group state change? */
1409 if (hints
->window_group
!=
1410 (self
->group
? self
->group
->leader
: None
)) {
1411 /* remove from the old group if there was one */
1412 if (self
->group
!= NULL
) {
1413 /* remove transients of the group */
1414 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1415 self
->transients
= g_slist_remove(self
->transients
,
1418 /* remove myself from parents in the group */
1419 if (self
->transient_for
== OB_TRAN_GROUP
) {
1420 for (it
= self
->group
->members
; it
;
1421 it
= g_slist_next(it
))
1423 ObClient
*c
= it
->data
;
1425 if (c
!= self
&& !c
->transient_for
)
1426 c
->transients
= g_slist_remove(c
->transients
,
1431 group_remove(self
->group
, self
);
1434 if (hints
->window_group
!= None
) {
1435 self
->group
= group_add(hints
->window_group
, self
);
1437 /* i can only have transients from the group if i am not
1439 if (!self
->transient_for
) {
1440 /* add other transients of the group that are already
1442 for (it
= self
->group
->members
; it
;
1443 it
= g_slist_next(it
))
1445 ObClient
*c
= it
->data
;
1446 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1448 g_slist_append(self
->transients
, c
);
1453 /* because the self->transient flag wont change from this call,
1454 we don't need to update the window's type and such, only its
1455 transient_for, and the transients lists of other windows in
1456 the group may be affected */
1457 client_update_transient_for(self
);
1460 /* the WM_HINTS can contain an icon */
1461 client_update_icons(self
);
1466 if (ur
!= self
->urgent
) {
1468 /* fire the urgent callback if we're mapped, otherwise, wait until
1469 after we're mapped */
1471 client_urgent_notify(self
);
1475 void client_update_title(ObClient
*self
)
1481 gboolean read_title
;
1484 old_title
= self
->title
;
1487 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1488 /* try old x stuff */
1489 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
)) {
1490 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1491 if (self
->transient
) {
1492 data
= g_strdup("");
1495 data
= g_strdup("Unnamed Window");
1499 /* did the title change? then reset the title_count */
1500 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1501 self
->title_count
= 1;
1503 /* look for duplicates and append a number */
1505 for (it
= client_list
; it
; it
= g_list_next(it
))
1506 if (it
->data
!= self
) {
1507 ObClient
*c
= it
->data
;
1508 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1509 nums
|= 1 << c
->title_count
;
1511 /* find first free number */
1512 for (i
= 1; i
<= 32; ++i
)
1513 if (!(nums
& (1 << i
))) {
1514 if (self
->title_count
== 1 || i
== 1)
1515 self
->title_count
= i
;
1518 /* dont display the number for the first window */
1519 if (self
->title_count
> 1) {
1521 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1526 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1531 frame_adjust_title(self
->frame
);
1535 /* update the icon title */
1537 g_free(self
->icon_title
);
1541 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1542 /* try old x stuff */
1543 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1544 data
= g_strdup(self
->title
);
1548 /* append the title count, dont display the number for the first window */
1549 if (read_title
&& self
->title_count
> 1) {
1550 gchar
*vdata
, *ndata
;
1551 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1552 vdata
= g_strconcat(data
, ndata
, NULL
);
1558 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1560 self
->icon_title
= data
;
1563 void client_update_class(ObClient
*self
)
1568 if (self
->name
) g_free(self
->name
);
1569 if (self
->class) g_free(self
->class);
1570 if (self
->role
) g_free(self
->role
);
1572 self
->name
= self
->class = self
->role
= NULL
;
1574 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1576 self
->name
= g_strdup(data
[0]);
1578 self
->class = g_strdup(data
[1]);
1583 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1586 if (self
->name
== NULL
) self
->name
= g_strdup("");
1587 if (self
->class == NULL
) self
->class = g_strdup("");
1588 if (self
->role
== NULL
) self
->role
= g_strdup("");
1591 void client_update_strut(ObClient
*self
)
1595 gboolean got
= FALSE
;
1598 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1602 STRUT_PARTIAL_SET(strut
,
1603 data
[0], data
[2], data
[1], data
[3],
1604 data
[4], data
[5], data
[8], data
[9],
1605 data
[6], data
[7], data
[10], data
[11]);
1611 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1617 /* use the screen's width/height */
1618 a
= screen_physical_area();
1620 STRUT_PARTIAL_SET(strut
,
1621 data
[0], data
[2], data
[1], data
[3],
1622 a
->y
, a
->y
+ a
->height
- 1,
1623 a
->x
, a
->x
+ a
->width
- 1,
1624 a
->y
, a
->y
+ a
->height
- 1,
1625 a
->x
, a
->x
+ a
->width
- 1);
1631 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1632 0, 0, 0, 0, 0, 0, 0, 0);
1634 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1635 self
->strut
= strut
;
1637 /* updating here is pointless while we're being mapped cuz we're not in
1638 the client list yet */
1640 screen_update_areas();
1644 void client_update_icons(ObClient
*self
)
1650 for (i
= 0; i
< self
->nicons
; ++i
)
1651 g_free(self
->icons
[i
].data
);
1652 if (self
->nicons
> 0)
1653 g_free(self
->icons
);
1656 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1657 /* figure out how many valid icons are in here */
1659 while (num
- i
> 2) {
1663 if (i
> num
|| w
*h
== 0) break;
1667 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1669 /* store the icons */
1671 for (j
= 0; j
< self
->nicons
; ++j
) {
1674 w
= self
->icons
[j
].width
= data
[i
++];
1675 h
= self
->icons
[j
].height
= data
[i
++];
1677 if (w
*h
== 0) continue;
1679 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1680 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1685 self
->icons
[j
].data
[t
] =
1686 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1687 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1688 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1689 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1695 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1696 kwm_win_icon
, &data
, &num
)) {
1699 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1700 xerror_set_ignore(TRUE
);
1701 if (!RrPixmapToRGBA(ob_rr_inst
,
1703 &self
->icons
[self
->nicons
-1].width
,
1704 &self
->icons
[self
->nicons
-1].height
,
1705 &self
->icons
[self
->nicons
-1].data
)) {
1706 g_free(&self
->icons
[self
->nicons
-1]);
1709 xerror_set_ignore(FALSE
);
1715 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1716 if (hints
->flags
& IconPixmapHint
) {
1718 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1719 xerror_set_ignore(TRUE
);
1720 if (!RrPixmapToRGBA(ob_rr_inst
,
1722 (hints
->flags
& IconMaskHint
?
1723 hints
->icon_mask
: None
),
1724 &self
->icons
[self
->nicons
-1].width
,
1725 &self
->icons
[self
->nicons
-1].height
,
1726 &self
->icons
[self
->nicons
-1].data
)){
1727 g_free(&self
->icons
[self
->nicons
-1]);
1730 xerror_set_ignore(FALSE
);
1737 frame_adjust_icon(self
->frame
);
1740 static void client_change_state(ObClient
*self
)
1743 gulong netstate
[11];
1746 state
[0] = self
->wmstate
;
1748 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1752 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1754 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1756 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1757 if (self
->skip_taskbar
)
1758 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1759 if (self
->skip_pager
)
1760 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1761 if (self
->fullscreen
)
1762 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1764 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1766 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1768 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1770 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1771 if (self
->undecorated
)
1772 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1773 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1775 client_calc_layer(self
);
1778 frame_adjust_state(self
->frame
);
1781 ObClient
*client_search_focus_tree(ObClient
*self
)
1786 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1787 if (client_focused(it
->data
)) return it
->data
;
1788 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1793 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1795 if (self
->transient_for
) {
1796 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1797 return client_search_focus_tree_full(self
->transient_for
);
1800 gboolean recursed
= FALSE
;
1802 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1803 if (!((ObClient
*)it
->data
)->transient_for
) {
1805 if ((c
= client_search_focus_tree_full(it
->data
)))
1814 /* this function checks the whole tree, the client_search_focus_tree~
1815 does not, so we need to check this window */
1816 if (client_focused(self
))
1818 return client_search_focus_tree(self
);
1821 static ObStackingLayer
calc_layer(ObClient
*self
)
1825 if (self
->fullscreen
&&
1826 (client_focused(self
) || client_search_focus_tree(self
)))
1827 l
= OB_STACKING_LAYER_FULLSCREEN
;
1828 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1829 l
= OB_STACKING_LAYER_DESKTOP
;
1830 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1831 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1832 else l
= OB_STACKING_LAYER_ABOVE
;
1834 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1835 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1836 else l
= OB_STACKING_LAYER_NORMAL
;
1841 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1842 ObStackingLayer l
, gboolean raised
)
1844 ObStackingLayer old
, own
;
1848 own
= calc_layer(self
);
1849 self
->layer
= l
> own
? l
: own
;
1851 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1852 client_calc_layer_recursive(it
->data
, orig
,
1853 l
, raised
? raised
: l
!= old
);
1855 if (!raised
&& l
!= old
)
1856 if (orig
->frame
) { /* only restack if the original window is managed */
1857 stacking_remove(CLIENT_AS_WINDOW(self
));
1858 stacking_add(CLIENT_AS_WINDOW(self
));
1862 void client_calc_layer(ObClient
*self
)
1869 /* transients take on the layer of their parents */
1870 self
= client_search_top_transient(self
);
1872 l
= calc_layer(self
);
1874 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1877 gboolean
client_should_show(ObClient
*self
)
1881 if (client_normal(self
) && screen_showing_desktop
)
1884 if (self->transient_for) {
1885 if (self->transient_for != OB_TRAN_GROUP)
1886 return client_should_show(self->transient_for);
1890 for (it = self->group->members; it; it = g_slist_next(it)) {
1891 ObClient *c = it->data;
1892 if (c != self && !c->transient_for) {
1893 if (client_should_show(c))
1900 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
1906 static void client_showhide(ObClient
*self
)
1909 if (client_should_show(self
))
1910 frame_show(self
->frame
);
1912 frame_hide(self
->frame
);
1915 gboolean
client_normal(ObClient
*self
) {
1916 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1917 self
->type
== OB_CLIENT_TYPE_DOCK
||
1918 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1921 static void client_apply_startup_state(ObClient
*self
)
1923 /* these are in a carefully crafted order.. */
1926 self
->iconic
= FALSE
;
1927 client_iconify(self
, TRUE
, FALSE
);
1929 if (self
->fullscreen
) {
1930 self
->fullscreen
= FALSE
;
1931 client_fullscreen(self
, TRUE
, FALSE
);
1933 if (self
->undecorated
) {
1934 self
->undecorated
= FALSE
;
1935 client_set_undecorated(self
, TRUE
);
1938 self
->shaded
= FALSE
;
1939 client_shade(self
, TRUE
);
1942 client_urgent_notify(self
);
1944 if (self
->max_vert
&& self
->max_horz
) {
1945 self
->max_vert
= self
->max_horz
= FALSE
;
1946 client_maximize(self
, TRUE
, 0, FALSE
);
1947 } else if (self
->max_vert
) {
1948 self
->max_vert
= FALSE
;
1949 client_maximize(self
, TRUE
, 2, FALSE
);
1950 } else if (self
->max_horz
) {
1951 self
->max_horz
= FALSE
;
1952 client_maximize(self
, TRUE
, 1, FALSE
);
1955 /* nothing to do for the other states:
1964 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1965 gint x
, gint y
, gint w
, gint h
,
1966 gboolean user
, gboolean final
,
1967 gboolean force_reply
)
1970 gboolean send_resize_client
;
1971 gboolean moved
= FALSE
, resized
= FALSE
;
1972 guint fdecor
= self
->frame
->decorations
;
1973 gboolean fhorz
= self
->frame
->max_horz
;
1975 /* make the frame recalculate its dimentions n shit without changing
1976 anything visible for real, this way the constraints below can work with
1977 the updated frame dimensions. */
1978 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
1980 /* gets the frame's position */
1981 frame_client_gravity(self
->frame
, &x
, &y
);
1983 /* these positions are frame positions, not client positions */
1985 /* set the size and position if fullscreen */
1986 if (self
->fullscreen
) {
1989 XF86VidModeModeLine mode
;
1994 i
= client_monitor(self
);
1995 a
= screen_physical_area_monitor(i
);
1998 if (i
== 0 && /* primary head */
1999 extensions_vidmode
&&
2000 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
2001 /* get the mode last so the mode.privsize isnt freed incorrectly */
2002 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
2007 if (mode
.privsize
) XFree(mode
.private);
2017 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2021 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
2023 /* set the size and position if maximized */
2024 if (self
->max_horz
) {
2026 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2028 if (self
->max_vert
) {
2030 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2034 /* gets the client's position */
2035 frame_frame_gravity(self
->frame
, &x
, &y
);
2037 /* these override the above states! if you cant move you can't move! */
2039 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2043 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2044 w
= self
->area
.width
;
2045 h
= self
->area
.height
;
2049 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2050 gint basew
, baseh
, minw
, minh
;
2052 /* base size is substituted with min size if not specified */
2053 if (self
->base_size
.width
|| self
->base_size
.height
) {
2054 basew
= self
->base_size
.width
;
2055 baseh
= self
->base_size
.height
;
2057 basew
= self
->min_size
.width
;
2058 baseh
= self
->min_size
.height
;
2060 /* min size is substituted with base size if not specified */
2061 if (self
->min_size
.width
|| self
->min_size
.height
) {
2062 minw
= self
->min_size
.width
;
2063 minh
= self
->min_size
.height
;
2065 minw
= self
->base_size
.width
;
2066 minh
= self
->base_size
.height
;
2069 /* if this is a user-requested resize, then check against min/max
2072 /* smaller than min size or bigger than max size? */
2073 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2074 if (w
< minw
) w
= minw
;
2075 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2076 if (h
< minh
) h
= minh
;
2081 /* keep to the increments */
2082 w
/= self
->size_inc
.width
;
2083 h
/= self
->size_inc
.height
;
2085 /* you cannot resize to nothing */
2086 if (basew
+ w
< 1) w
= 1 - basew
;
2087 if (baseh
+ h
< 1) h
= 1 - baseh
;
2089 /* store the logical size */
2090 SIZE_SET(self
->logical_size
,
2091 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2092 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2094 w
*= self
->size_inc
.width
;
2095 h
*= self
->size_inc
.height
;
2100 /* adjust the height to match the width for the aspect ratios.
2101 for this, min size is not substituted for base size ever. */
2102 w
-= self
->base_size
.width
;
2103 h
-= self
->base_size
.height
;
2105 if (!self
->fullscreen
) {
2106 if (self
->min_ratio
)
2107 if (h
* self
->min_ratio
> w
) {
2108 h
= (gint
)(w
/ self
->min_ratio
);
2110 /* you cannot resize to nothing */
2113 w
= (gint
)(h
* self
->min_ratio
);
2116 if (self
->max_ratio
)
2117 if (h
* self
->max_ratio
< w
) {
2118 h
= (gint
)(w
/ self
->max_ratio
);
2120 /* you cannot resize to nothing */
2123 w
= (gint
)(h
* self
->min_ratio
);
2128 w
+= self
->base_size
.width
;
2129 h
+= self
->base_size
.height
;
2136 case OB_CORNER_TOPLEFT
:
2138 case OB_CORNER_TOPRIGHT
:
2139 x
-= w
- self
->area
.width
;
2141 case OB_CORNER_BOTTOMLEFT
:
2142 y
-= h
- self
->area
.height
;
2144 case OB_CORNER_BOTTOMRIGHT
:
2145 x
-= w
- self
->area
.width
;
2146 y
-= h
- self
->area
.height
;
2150 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2151 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2153 oldw
= self
->area
.width
;
2154 oldh
= self
->area
.height
;
2155 RECT_SET(self
->area
, x
, y
, w
, h
);
2157 /* for app-requested resizes, always resize if 'resized' is true.
2158 for user-requested ones, only resize if final is true, or when
2159 resizing in redraw mode */
2160 send_resize_client
= ((!user
&& resized
) ||
2162 (resized
&& config_resize_redraw
))));
2164 /* if the client is enlarging, then resize the client before the frame */
2165 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2166 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2168 /* move/resize the frame to match the request */
2170 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2171 moved
= resized
= TRUE
;
2173 if (moved
|| resized
)
2174 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2176 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2179 event
.type
= ConfigureNotify
;
2180 event
.xconfigure
.display
= ob_display
;
2181 event
.xconfigure
.event
= self
->window
;
2182 event
.xconfigure
.window
= self
->window
;
2184 /* root window real coords */
2185 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2187 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2189 event
.xconfigure
.width
= w
;
2190 event
.xconfigure
.height
= h
;
2191 event
.xconfigure
.border_width
= 0;
2192 event
.xconfigure
.above
= self
->frame
->plate
;
2193 event
.xconfigure
.override_redirect
= FALSE
;
2194 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2195 FALSE
, StructureNotifyMask
, &event
);
2199 /* if the client is shrinking, then resize the frame before the client */
2200 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2201 XResizeWindow(ob_display
, self
->window
, w
, h
);
2206 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2210 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2211 self
->fullscreen
== fs
) return; /* already done */
2213 self
->fullscreen
= fs
;
2214 client_change_state(self
); /* change the state hints on the client,
2215 and adjust out layer/stacking */
2219 self
->pre_fullscreen_area
= self
->area
;
2221 /* these are not actually used cuz client_configure will set them
2222 as appropriate when the window is fullscreened */
2227 if (self
->pre_fullscreen_area
.width
> 0 &&
2228 self
->pre_fullscreen_area
.height
> 0)
2230 x
= self
->pre_fullscreen_area
.x
;
2231 y
= self
->pre_fullscreen_area
.y
;
2232 w
= self
->pre_fullscreen_area
.width
;
2233 h
= self
->pre_fullscreen_area
.height
;
2234 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2236 /* pick some fallbacks... */
2237 a
= screen_area_monitor(self
->desktop
, 0);
2238 x
= a
->x
+ a
->width
/ 4;
2239 y
= a
->y
+ a
->height
/ 4;
2245 client_setup_decor_and_functions(self
);
2247 client_move_resize(self
, x
, y
, w
, h
);
2249 /* try focus us when we go into fullscreen mode */
2253 static void client_iconify_recursive(ObClient
*self
,
2254 gboolean iconic
, gboolean curdesk
)
2257 gboolean changed
= FALSE
;
2260 if (self
->iconic
!= iconic
) {
2261 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2264 self
->iconic
= iconic
;
2267 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2270 old
= self
->wmstate
;
2271 self
->wmstate
= IconicState
;
2272 if (old
!= self
->wmstate
)
2273 PROP_MSG(self
->window
, kde_wm_change_state
,
2274 self
->wmstate
, 1, 0, 0);
2276 /* update the focus lists.. iconic windows go to the bottom of
2277 the list, put the new iconic window at the 'top of the
2279 focus_order_to_top(self
);
2287 client_set_desktop(self
, screen_desktop
, FALSE
);
2289 old
= self
->wmstate
;
2290 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2291 if (old
!= self
->wmstate
)
2292 PROP_MSG(self
->window
, kde_wm_change_state
,
2293 self
->wmstate
, 1, 0, 0);
2295 /* this puts it after the current focused window */
2296 focus_order_remove(self
);
2297 focus_order_add_new(self
);
2299 /* this is here cuz with the VIDMODE extension, the viewport can
2300 change while a fullscreen window is iconic, and when it
2301 uniconifies, it would be nice if it did so to the new position
2303 client_reconfigure(self
);
2310 client_change_state(self
);
2311 client_showhide(self
);
2312 screen_update_areas();
2315 /* iconify all transients */
2316 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2317 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2321 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2323 /* move up the transient chain as far as possible first */
2324 self
= client_search_top_transient(self
);
2326 client_iconify_recursive(client_search_top_transient(self
),
2330 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2334 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2335 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2337 /* check if already done */
2339 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2340 if (dir
== 1 && self
->max_horz
) return;
2341 if (dir
== 2 && self
->max_vert
) return;
2343 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2344 if (dir
== 1 && !self
->max_horz
) return;
2345 if (dir
== 2 && !self
->max_vert
) return;
2348 /* we just tell it to configure in the same place and client_configure
2349 worries about filling the screen with the window */
2352 w
= self
->area
.width
;
2353 h
= self
->area
.height
;
2357 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2358 RECT_SET(self
->pre_max_area
,
2359 self
->area
.x
, self
->pre_max_area
.y
,
2360 self
->area
.width
, self
->pre_max_area
.height
);
2362 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2363 RECT_SET(self
->pre_max_area
,
2364 self
->pre_max_area
.x
, self
->area
.y
,
2365 self
->pre_max_area
.width
, self
->area
.height
);
2371 a
= screen_area_monitor(self
->desktop
, 0);
2372 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2373 if (self
->pre_max_area
.width
> 0) {
2374 x
= self
->pre_max_area
.x
;
2375 w
= self
->pre_max_area
.width
;
2377 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2378 0, self
->pre_max_area
.height
);
2380 /* pick some fallbacks... */
2381 x
= a
->x
+ a
->width
/ 4;
2385 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2386 if (self
->pre_max_area
.height
> 0) {
2387 y
= self
->pre_max_area
.y
;
2388 h
= self
->pre_max_area
.height
;
2390 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2391 self
->pre_max_area
.width
, 0);
2393 /* pick some fallbacks... */
2394 y
= a
->y
+ a
->height
/ 4;
2400 if (dir
== 0 || dir
== 1) /* horz */
2401 self
->max_horz
= max
;
2402 if (dir
== 0 || dir
== 2) /* vert */
2403 self
->max_vert
= max
;
2405 client_change_state(self
); /* change the state hints on the client */
2407 client_setup_decor_and_functions(self
);
2409 client_move_resize(self
, x
, y
, w
, h
);
2412 void client_shade(ObClient
*self
, gboolean shade
)
2414 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2415 shade
) || /* can't shade */
2416 self
->shaded
== shade
) return; /* already done */
2418 /* when we're iconic, don't change the wmstate */
2419 if (!self
->iconic
) {
2422 old
= self
->wmstate
;
2423 self
->wmstate
= shade
? IconicState
: NormalState
;
2424 if (old
!= self
->wmstate
)
2425 PROP_MSG(self
->window
, kde_wm_change_state
,
2426 self
->wmstate
, 1, 0, 0);
2429 self
->shaded
= shade
;
2430 client_change_state(self
);
2431 /* resize the frame to just the titlebar */
2432 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2435 void client_close(ObClient
*self
)
2439 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2441 /* in the case that the client provides no means to requesting that it
2442 close, we just kill it */
2443 if (!self
->delete_window
)
2447 XXX: itd be cool to do timeouts and shit here for killing the client's
2449 like... if the window is around after 5 seconds, then the close button
2450 turns a nice red, and if this function is called again, the client is
2454 ce
.xclient
.type
= ClientMessage
;
2455 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2456 ce
.xclient
.display
= ob_display
;
2457 ce
.xclient
.window
= self
->window
;
2458 ce
.xclient
.format
= 32;
2459 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2460 ce
.xclient
.data
.l
[1] = event_lasttime
;
2461 ce
.xclient
.data
.l
[2] = 0l;
2462 ce
.xclient
.data
.l
[3] = 0l;
2463 ce
.xclient
.data
.l
[4] = 0l;
2464 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2467 void client_kill(ObClient
*self
)
2469 XKillClient(ob_display
, self
->window
);
2472 void client_set_desktop_recursive(ObClient
*self
,
2473 guint target
, gboolean donthide
)
2478 if (target
!= self
->desktop
) {
2480 ob_debug("Setting desktop %u\n", target
+1);
2482 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2484 /* remove from the old desktop(s) */
2485 focus_order_remove(self
);
2487 old
= self
->desktop
;
2488 self
->desktop
= target
;
2489 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2490 /* the frame can display the current desktop state */
2491 frame_adjust_state(self
->frame
);
2492 /* 'move' the window to the new desktop */
2494 client_showhide(self
);
2495 /* raise if it was not already on the desktop */
2496 if (old
!= DESKTOP_ALL
)
2498 screen_update_areas();
2500 /* add to the new desktop(s) */
2501 if (config_focus_new
)
2502 focus_order_to_top(self
);
2504 focus_order_to_bottom(self
);
2507 /* move all transients */
2508 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2509 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2513 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2515 client_set_desktop_recursive(client_search_top_transient(self
),
2519 ObClient
*client_search_modal_child(ObClient
*self
)
2524 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2525 ObClient
*c
= it
->data
;
2526 if ((ret
= client_search_modal_child(c
))) return ret
;
2527 if (c
->modal
) return c
;
2532 gboolean
client_validate(ObClient
*self
)
2536 XSync(ob_display
, FALSE
); /* get all events on the server */
2538 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2539 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2540 XPutBackEvent(ob_display
, &e
);
2547 void client_set_wm_state(ObClient
*self
, glong state
)
2549 if (state
== self
->wmstate
) return; /* no change */
2553 client_iconify(self
, TRUE
, TRUE
);
2556 client_iconify(self
, FALSE
, TRUE
);
2561 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2563 gboolean shaded
= self
->shaded
;
2564 gboolean fullscreen
= self
->fullscreen
;
2565 gboolean undecorated
= self
->undecorated
;
2566 gboolean max_horz
= self
->max_horz
;
2567 gboolean max_vert
= self
->max_vert
;
2568 gboolean modal
= self
->modal
;
2569 gboolean iconic
= self
->iconic
;
2572 if (!(action
== prop_atoms
.net_wm_state_add
||
2573 action
== prop_atoms
.net_wm_state_remove
||
2574 action
== prop_atoms
.net_wm_state_toggle
))
2575 /* an invalid action was passed to the client message, ignore it */
2578 for (i
= 0; i
< 2; ++i
) {
2579 Atom state
= i
== 0 ? data1
: data2
;
2581 if (!state
) continue;
2583 /* if toggling, then pick whether we're adding or removing */
2584 if (action
== prop_atoms
.net_wm_state_toggle
) {
2585 if (state
== prop_atoms
.net_wm_state_modal
)
2586 action
= modal
? prop_atoms
.net_wm_state_remove
:
2587 prop_atoms
.net_wm_state_add
;
2588 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2589 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2590 prop_atoms
.net_wm_state_add
;
2591 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2592 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2593 prop_atoms
.net_wm_state_add
;
2594 else if (state
== prop_atoms
.net_wm_state_shaded
)
2595 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2596 prop_atoms
.net_wm_state_add
;
2597 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2598 action
= self
->skip_taskbar
?
2599 prop_atoms
.net_wm_state_remove
:
2600 prop_atoms
.net_wm_state_add
;
2601 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2602 action
= self
->skip_pager
?
2603 prop_atoms
.net_wm_state_remove
:
2604 prop_atoms
.net_wm_state_add
;
2605 else if (state
== prop_atoms
.net_wm_state_hidden
)
2606 action
= self
->iconic
?
2607 prop_atoms
.net_wm_state_remove
:
2608 prop_atoms
.net_wm_state_add
;
2609 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2610 action
= fullscreen
?
2611 prop_atoms
.net_wm_state_remove
:
2612 prop_atoms
.net_wm_state_add
;
2613 else if (state
== prop_atoms
.net_wm_state_above
)
2614 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2615 prop_atoms
.net_wm_state_add
;
2616 else if (state
== prop_atoms
.net_wm_state_below
)
2617 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2618 prop_atoms
.net_wm_state_add
;
2619 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2620 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2621 prop_atoms
.net_wm_state_add
;
2624 if (action
== prop_atoms
.net_wm_state_add
) {
2625 if (state
== prop_atoms
.net_wm_state_modal
) {
2627 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2629 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2631 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2633 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2634 self
->skip_taskbar
= TRUE
;
2635 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2636 self
->skip_pager
= TRUE
;
2637 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2639 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2641 } else if (state
== prop_atoms
.net_wm_state_above
) {
2643 self
->below
= FALSE
;
2644 } else if (state
== prop_atoms
.net_wm_state_below
) {
2645 self
->above
= FALSE
;
2647 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2651 } else { /* action == prop_atoms.net_wm_state_remove */
2652 if (state
== prop_atoms
.net_wm_state_modal
) {
2654 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2656 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2658 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2660 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2661 self
->skip_taskbar
= FALSE
;
2662 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2663 self
->skip_pager
= FALSE
;
2664 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2666 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2668 } else if (state
== prop_atoms
.net_wm_state_above
) {
2669 self
->above
= FALSE
;
2670 } else if (state
== prop_atoms
.net_wm_state_below
) {
2671 self
->below
= FALSE
;
2672 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2673 undecorated
= FALSE
;
2677 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2678 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2680 if (max_horz
== max_vert
) { /* both going the same way */
2681 client_maximize(self
, max_horz
, 0, TRUE
);
2683 client_maximize(self
, max_horz
, 1, TRUE
);
2684 client_maximize(self
, max_vert
, 2, TRUE
);
2688 if (max_horz
!= self
->max_horz
)
2689 client_maximize(self
, max_horz
, 1, TRUE
);
2691 client_maximize(self
, max_vert
, 2, TRUE
);
2694 /* change fullscreen state before shading, as it will affect if the window
2696 if (fullscreen
!= self
->fullscreen
)
2697 client_fullscreen(self
, fullscreen
, TRUE
);
2698 if (shaded
!= self
->shaded
)
2699 client_shade(self
, shaded
);
2700 if (undecorated
!= self
->undecorated
)
2701 client_set_undecorated(self
, undecorated
);
2702 if (modal
!= self
->modal
) {
2703 self
->modal
= modal
;
2704 /* when a window changes modality, then its stacking order with its
2705 transients needs to change */
2708 if (iconic
!= self
->iconic
)
2709 client_iconify(self
, iconic
, FALSE
);
2711 client_calc_layer(self
);
2712 client_change_state(self
); /* change the hint to reflect these changes */
2715 ObClient
*client_focus_target(ObClient
*self
)
2719 /* if we have a modal child, then focus it, not us */
2720 child
= client_search_modal_child(client_search_top_transient(self
));
2721 if (child
) return child
;
2725 gboolean
client_can_focus(ObClient
*self
)
2729 /* choose the correct target */
2730 self
= client_focus_target(self
);
2732 if (!self
->frame
->visible
)
2735 if (!(self
->can_focus
|| self
->focus_notify
))
2738 /* do a check to see if the window has already been unmapped or destroyed
2739 do this intelligently while watching out for unmaps we've generated
2740 (ignore_unmaps > 0) */
2741 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2742 DestroyNotify
, &ev
)) {
2743 XPutBackEvent(ob_display
, &ev
);
2746 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2747 UnmapNotify
, &ev
)) {
2748 if (self
->ignore_unmaps
) {
2749 self
->ignore_unmaps
--;
2751 XPutBackEvent(ob_display
, &ev
);
2759 gboolean
client_focus(ObClient
*self
)
2761 /* choose the correct target */
2762 self
= client_focus_target(self
);
2764 if (!client_can_focus(self
)) {
2765 if (!self
->frame
->visible
) {
2766 /* update the focus lists */
2767 focus_order_to_top(self
);
2772 if (self
->can_focus
) {
2773 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2774 I choose to use it always, hopefully to find errors quicker, if any
2775 are left. (I hate X. I hate focus events.)
2777 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2778 #799. So now it is RevertToNone again.
2780 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2784 if (self
->focus_notify
) {
2786 ce
.xclient
.type
= ClientMessage
;
2787 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2788 ce
.xclient
.display
= ob_display
;
2789 ce
.xclient
.window
= self
->window
;
2790 ce
.xclient
.format
= 32;
2791 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2792 ce
.xclient
.data
.l
[1] = event_lasttime
;
2793 ce
.xclient
.data
.l
[2] = 0l;
2794 ce
.xclient
.data
.l
[3] = 0l;
2795 ce
.xclient
.data
.l
[4] = 0l;
2796 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2800 ob_debug("%sively focusing %lx at %d\n",
2801 (self
->can_focus
? "act" : "pass"),
2802 self
->window
, (gint
) event_lasttime
);
2805 /* Cause the FocusIn to come back to us. Important for desktop switches,
2806 since otherwise we'll have no FocusIn on the queue and send it off to
2807 the focus_backup. */
2808 XSync(ob_display
, FALSE
);
2812 void client_unfocus(ObClient
*self
)
2814 if (focus_client
== self
) {
2816 ob_debug("client_unfocus for %lx\n", self
->window
);
2818 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2822 void client_activate(ObClient
*self
, gboolean here
)
2824 /* This check is for the client_list_menu trying to activate
2825 * a closed client. */
2826 if (!g_list_find(client_list
, self
)) return;
2827 if (client_normal(self
) && screen_showing_desktop
)
2828 screen_show_desktop(FALSE
);
2830 client_iconify(self
, FALSE
, here
);
2831 if (self
->desktop
!= DESKTOP_ALL
&&
2832 self
->desktop
!= screen_desktop
) {
2834 client_set_desktop(self
, screen_desktop
, FALSE
);
2836 screen_set_desktop(self
->desktop
);
2837 } else if (!self
->frame
->visible
)
2838 /* if its not visible for other reasons, then don't mess
2842 client_shade(self
, FALSE
);
2846 /* we do this an action here. this is rather important. this is because
2847 we want the results from the focus change to take place BEFORE we go
2848 about raising the window. when a fullscreen window loses focus, we need
2849 this or else the raise wont be able to raise above the to-lose-focus
2850 fullscreen window. */
2854 void client_raise(ObClient
*self
)
2856 action_run_string("Raise", self
);
2859 void client_lower(ObClient
*self
)
2861 action_run_string("Lower", self
);
2864 gboolean
client_focused(ObClient
*self
)
2866 return self
== focus_client
;
2869 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
2872 /* si is the smallest image >= req */
2873 /* li is the largest image < req */
2874 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2876 if (!self
->nicons
) {
2877 ObClientIcon
*parent
= NULL
;
2879 if (self
->transient_for
) {
2880 if (self
->transient_for
!= OB_TRAN_GROUP
)
2881 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2884 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2885 ObClient
*c
= it
->data
;
2886 if (c
!= self
&& !c
->transient_for
) {
2887 if ((parent
= client_icon_recursive(c
, w
, h
)))
2897 for (i
= 0; i
< self
->nicons
; ++i
) {
2898 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2899 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2903 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2908 if (largest
== 0) /* didnt find one smaller than the requested size */
2909 return &self
->icons
[si
];
2910 return &self
->icons
[li
];
2913 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
2916 static ObClientIcon deficon
;
2918 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
2919 deficon
.width
= deficon
.height
= 48;
2920 deficon
.data
= ob_rr_theme
->def_win_icon
;
2926 /* this be mostly ripped from fvwm */
2927 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2929 gint my_cx
, my_cy
, his_cx
, his_cy
;
2932 gint score
, best_score
;
2933 ObClient
*best_client
, *cur
;
2939 /* first, find the centre coords of the currently focused window */
2940 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2941 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2946 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
2949 /* the currently selected window isn't interesting */
2952 if (!client_normal(cur
))
2954 /* using c->desktop instead of screen_desktop doesn't work if the
2955 * current window was omnipresent, hope this doesn't have any other
2957 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2961 if(client_focus_target(cur
) == cur
&&
2962 !(cur
->can_focus
|| cur
->focus_notify
))
2965 /* find the centre coords of this window, from the
2966 * currently focused window's point of view */
2967 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2968 + cur
->frame
->area
.width
/ 2;
2969 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2970 + cur
->frame
->area
.height
/ 2;
2972 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
2973 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
2975 /* Rotate the diagonals 45 degrees counterclockwise.
2976 * To do this, multiply the matrix /+h +h\ with the
2977 * vector (x y). \-h +h/
2978 * h = sqrt(0.5). We can set h := 1 since absolute
2979 * distance doesn't matter here. */
2980 tx
= his_cx
+ his_cy
;
2981 his_cy
= -his_cx
+ his_cy
;
2986 case OB_DIRECTION_NORTH
:
2987 case OB_DIRECTION_SOUTH
:
2988 case OB_DIRECTION_NORTHEAST
:
2989 case OB_DIRECTION_SOUTHWEST
:
2990 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
2991 distance
= ((dir
== OB_DIRECTION_NORTH
||
2992 dir
== OB_DIRECTION_NORTHEAST
) ?
2995 case OB_DIRECTION_EAST
:
2996 case OB_DIRECTION_WEST
:
2997 case OB_DIRECTION_SOUTHEAST
:
2998 case OB_DIRECTION_NORTHWEST
:
2999 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3000 distance
= ((dir
== OB_DIRECTION_WEST
||
3001 dir
== OB_DIRECTION_NORTHWEST
) ?
3006 /* the target must be in the requested direction */
3010 /* Calculate score for this window. The smaller the better. */
3011 score
= distance
+ offset
;
3013 /* windows more than 45 degrees off the direction are
3014 * heavily penalized and will only be chosen if nothing
3015 * else within a million pixels */
3016 if(offset
> distance
)
3019 if(best_score
== -1 || score
< best_score
)
3027 void client_set_layer(ObClient
*self
, gint layer
)
3031 self
->above
= FALSE
;
3032 } else if (layer
== 0) {
3033 self
->below
= self
->above
= FALSE
;
3035 self
->below
= FALSE
;
3038 client_calc_layer(self
);
3039 client_change_state(self
); /* reflect this in the state hints */
3042 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3044 if (self
->undecorated
!= undecorated
) {
3045 self
->undecorated
= undecorated
;
3046 client_setup_decor_and_functions(self
);
3047 /* Make sure the client knows it might have moved. Maybe there is a
3048 * better way of doing this so only one client_configure is sent, but
3049 * since 125 of these are sent per second when moving the window (with
3050 * user = FALSE) i doubt it matters much.
3052 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3053 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3054 client_change_state(self
); /* reflect this in the state hints */
3058 /* Determines which physical monitor a client is on by calculating the
3059 area of the part of the client on each monitor. The number of the
3060 monitor containing the greatest area of the client is returned.*/
3061 guint
client_monitor(ObClient
*self
)
3067 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3068 Rect
*area
= screen_physical_area_monitor(i
);
3069 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3073 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3074 v
= r
.width
* r
.height
;
3085 ObClient
*client_search_top_transient(ObClient
*self
)
3087 /* move up the transient chain as far as possible */
3088 if (self
->transient_for
) {
3089 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3090 return client_search_top_transient(self
->transient_for
);
3094 g_assert(self
->group
);
3096 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3097 ObClient
*c
= it
->data
;
3099 /* checking transient_for prevents infinate loops! */
3100 if (c
!= self
&& !c
->transient_for
)
3111 ObClient
*client_search_focus_parent(ObClient
*self
)
3113 if (self
->transient_for
) {
3114 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3115 if (client_focused(self
->transient_for
))
3116 return self
->transient_for
;
3120 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3121 ObClient
*c
= it
->data
;
3123 /* checking transient_for prevents infinate loops! */
3124 if (c
!= self
&& !c
->transient_for
)
3125 if (client_focused(c
))
3134 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3136 if (self
->transient_for
) {
3137 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3138 if (self
->transient_for
== search
)
3143 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3144 ObClient
*c
= it
->data
;
3146 /* checking transient_for prevents infinate loops! */
3147 if (c
!= self
&& !c
->transient_for
)
3157 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3161 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3162 if (sit
->data
== search
)
3164 if (client_search_transient(sit
->data
, search
))
3170 void client_update_sm_client_id(ObClient
*self
)
3172 g_free(self
->sm_client_id
);
3173 self
->sm_client_id
= NULL
;
3175 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3177 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3178 &self
->sm_client_id
);
3181 /* finds the nearest edge in the given direction from the current client
3182 * note to self: the edge is the -frame- edge (the actual one), not the
3185 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
)
3187 gint dest
, monitor_dest
;
3188 gint my_edge_start
, my_edge_end
, my_offset
;
3195 a
= screen_area(c
->desktop
);
3196 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3199 case OB_DIRECTION_NORTH
:
3200 my_edge_start
= c
->frame
->area
.x
;
3201 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3202 my_offset
= c
->frame
->area
.y
;
3204 /* default: top of screen */
3206 monitor_dest
= monitor
->y
;
3207 /* if the monitor edge comes before the screen edge, */
3208 /* use that as the destination instead. (For xinerama) */
3209 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3210 dest
= monitor_dest
;
3212 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3213 gint his_edge_start
, his_edge_end
, his_offset
;
3214 ObClient
*cur
= it
->data
;
3218 if(!client_normal(cur
))
3220 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3224 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3227 his_edge_start
= cur
->frame
->area
.x
;
3228 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3229 his_offset
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3231 if(his_offset
+ 1 > my_offset
)
3234 if(his_offset
< dest
)
3237 if(his_edge_start
>= my_edge_start
&&
3238 his_edge_start
<= my_edge_end
)
3241 if(my_edge_start
>= his_edge_start
&&
3242 my_edge_start
<= his_edge_end
)
3247 case OB_DIRECTION_SOUTH
:
3248 my_edge_start
= c
->frame
->area
.x
;
3249 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3250 my_offset
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3252 /* default: bottom of screen */
3253 dest
= a
->y
+ a
->height
;
3254 monitor_dest
= monitor
->y
+ monitor
->height
;
3255 /* if the monitor edge comes before the screen edge, */
3256 /* use that as the destination instead. (For xinerama) */
3257 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3258 dest
= monitor_dest
;
3260 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3261 gint his_edge_start
, his_edge_end
, his_offset
;
3262 ObClient
*cur
= it
->data
;
3266 if(!client_normal(cur
))
3268 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3272 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3275 his_edge_start
= cur
->frame
->area
.x
;
3276 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3277 his_offset
= cur
->frame
->area
.y
;
3280 if(his_offset
- 1 < my_offset
)
3283 if(his_offset
> dest
)
3286 if(his_edge_start
>= my_edge_start
&&
3287 his_edge_start
<= my_edge_end
)
3290 if(my_edge_start
>= his_edge_start
&&
3291 my_edge_start
<= his_edge_end
)
3296 case OB_DIRECTION_WEST
:
3297 my_edge_start
= c
->frame
->area
.y
;
3298 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3299 my_offset
= c
->frame
->area
.x
;
3301 /* default: leftmost egde of screen */
3303 monitor_dest
= monitor
->x
;
3304 /* if the monitor edge comes before the screen edge, */
3305 /* use that as the destination instead. (For xinerama) */
3306 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3307 dest
= monitor_dest
;
3309 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3310 gint his_edge_start
, his_edge_end
, his_offset
;
3311 ObClient
*cur
= it
->data
;
3315 if(!client_normal(cur
))
3317 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3321 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3324 his_edge_start
= cur
->frame
->area
.y
;
3325 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3326 his_offset
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3328 if(his_offset
+ 1 > my_offset
)
3331 if(his_offset
< dest
)
3334 if(his_edge_start
>= my_edge_start
&&
3335 his_edge_start
<= my_edge_end
)
3338 if(my_edge_start
>= his_edge_start
&&
3339 my_edge_start
<= his_edge_end
)
3345 case OB_DIRECTION_EAST
:
3346 my_edge_start
= c
->frame
->area
.y
;
3347 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3348 my_offset
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3350 /* default: rightmost edge of screen */
3351 dest
= a
->x
+ a
->width
;
3352 monitor_dest
= monitor
->x
+ monitor
->width
;
3353 /* if the monitor edge comes before the screen edge, */
3354 /* use that as the destination instead. (For xinerama) */
3355 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3356 dest
= monitor_dest
;
3358 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3359 gint his_edge_start
, his_edge_end
, his_offset
;
3360 ObClient
*cur
= it
->data
;
3364 if(!client_normal(cur
))
3366 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3370 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3373 his_edge_start
= cur
->frame
->area
.y
;
3374 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3375 his_offset
= cur
->frame
->area
.x
;
3377 if(his_offset
- 1 < my_offset
)
3380 if(his_offset
> dest
)
3383 if(his_edge_start
>= my_edge_start
&&
3384 his_edge_start
<= my_edge_end
)
3387 if(my_edge_start
>= his_edge_start
&&
3388 my_edge_start
<= his_edge_end
)
3393 case OB_DIRECTION_NORTHEAST
:
3394 case OB_DIRECTION_SOUTHEAST
:
3395 case OB_DIRECTION_NORTHWEST
:
3396 case OB_DIRECTION_SOUTHWEST
:
3397 /* not implemented */
3399 g_assert_not_reached();
3400 dest
= 0; /* suppress warning */
3405 ObClient
* client_under_pointer()
3409 ObClient
*ret
= NULL
;
3411 if (screen_pointer_pos(&x
, &y
)) {
3412 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3413 if (WINDOW_IS_CLIENT(it
->data
)) {
3414 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3415 if (c
->frame
->visible
&&
3416 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3426 gboolean
client_has_group_siblings(ObClient
*self
)
3428 return self
->group
&& self
->group
->members
->next
;