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"
45 #include <X11/Xutil.h>
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
51 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
56 ObClientDestructor func
;
60 GList
*client_list
= NULL
;
61 GSList
*client_destructors
= NULL
;
63 static void client_get_all(ObClient
*self
);
64 static void client_toggle_border(ObClient
*self
, gboolean show
);
65 static void client_get_startup_id(ObClient
*self
);
66 static void client_get_area(ObClient
*self
);
67 static void client_get_desktop(ObClient
*self
);
68 static void client_get_state(ObClient
*self
);
69 static void client_get_shaped(ObClient
*self
);
70 static void client_get_mwm_hints(ObClient
*self
);
71 static void client_get_gravity(ObClient
*self
);
72 static void client_showhide(ObClient
*self
);
73 static void client_change_allowed_actions(ObClient
*self
);
74 static void client_change_state(ObClient
*self
);
75 static void client_apply_startup_state(ObClient
*self
);
76 static void client_restore_session_state(ObClient
*self
);
77 static void client_restore_session_stacking(ObClient
*self
);
78 static void client_urgent_notify(ObClient
*self
);
80 void client_startup(gboolean reconfig
)
87 void client_shutdown(gboolean reconfig
)
91 void client_add_destructor(ObClientDestructor func
, gpointer data
)
93 Destructor
*d
= g_new(Destructor
, 1);
96 client_destructors
= g_slist_prepend(client_destructors
, d
);
99 void client_remove_destructor(ObClientDestructor func
)
103 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
104 Destructor
*d
= it
->data
;
105 if (d
->func
== func
) {
107 client_destructors
= g_slist_delete_link(client_destructors
, it
);
113 void client_set_list()
115 Window
*windows
, *win_it
;
117 guint size
= g_list_length(client_list
);
119 /* create an array of the window ids */
121 windows
= g_new(Window
, size
);
123 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
124 *win_it
= ((ObClient
*)it
->data
)->window
;
128 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
129 net_client_list
, window
, (gulong
*)windows
, size
);
138 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
142 for (it = self->transients; it; it = g_slist_next(it)) {
143 if (!func(it->data, data)) return;
144 client_foreach_transient(it->data, func, data);
148 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
150 if (self->transient_for) {
151 if (self->transient_for != OB_TRAN_GROUP) {
152 if (!func(self->transient_for, data)) return;
153 client_foreach_ancestor(self->transient_for, func, data);
157 for (it = self->group->members; it; it = g_slist_next(it))
158 if (it->data != self &&
159 !((ObClient*)it->data)->transient_for) {
160 if (!func(it->data, data)) return;
161 client_foreach_ancestor(it->data, func, data);
168 void client_manage_all()
173 XWindowAttributes attrib
;
175 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
176 &w
, &w
, &children
, &nchild
);
178 /* remove all icon windows from the list */
179 for (i
= 0; i
< nchild
; i
++) {
180 if (children
[i
] == None
) continue;
181 wmhints
= XGetWMHints(ob_display
, children
[i
]);
183 if ((wmhints
->flags
& IconWindowHint
) &&
184 (wmhints
->icon_window
!= children
[i
]))
185 for (j
= 0; j
< nchild
; j
++)
186 if (children
[j
] == wmhints
->icon_window
) {
194 for (i
= 0; i
< nchild
; ++i
) {
195 if (children
[i
] == None
)
197 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
198 if (attrib
.override_redirect
) continue;
200 if (attrib
.map_state
!= IsUnmapped
)
201 client_manage(children
[i
]);
207 /* This should possibly do something more interesting than just match
208 * against WM_CLASS literally. */
209 static ObAppSettings
*get_settings(ObClient
*client
)
211 GSList
*a
= config_per_app_settings
;
214 ObAppSettings
*app
= (ObAppSettings
*) a
->data
;
216 if (!strcmp(app
->name
, client
->name
)) {
217 ob_debug("Window matching: %s\n", app
->name
);
218 /* Match if no role was specified in the per app setting, or if the string
219 * matches the beginning of the role, since apps like to set the role to
220 * things like browser-window-23c4b2f */
221 if (!app
->role
|| !strncmp(app
->role
, client
->role
, strlen(app
->role
)))
230 void client_manage(Window window
)
234 XWindowAttributes attrib
;
235 XSetWindowAttributes attrib_set
;
237 gboolean activate
= FALSE
;
238 ObAppSettings
*settings
;
242 /* check if it has already been unmapped by the time we started mapping
243 the grab does a sync so we don't have to here */
244 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
245 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
)) {
246 XPutBackEvent(ob_display
, &e
);
249 return; /* don't manage it */
252 /* make sure it isn't an override-redirect window */
253 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
254 attrib
.override_redirect
) {
256 return; /* don't manage it */
259 /* is the window a docking app */
260 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
261 if ((wmhint
->flags
& StateHint
) &&
262 wmhint
->initial_state
== WithdrawnState
) {
263 dock_add(window
, wmhint
);
271 ob_debug("Managing window: %lx\n", window
);
273 /* choose the events we want to receive on the CLIENT window */
274 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
275 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
276 XChangeWindowAttributes(ob_display
, window
,
277 CWEventMask
|CWDontPropagate
, &attrib_set
);
280 /* create the ObClient struct, and populate it from the hints on the
282 self
= g_new0(ObClient
, 1);
283 self
->obwin
.type
= Window_Client
;
284 self
->window
= window
;
286 /* non-zero defaults */
287 self
->title_count
= 1;
288 self
->wmstate
= NormalState
;
290 self
->desktop
= screen_num_desktops
; /* always an invalid value */
292 client_get_all(self
);
293 client_restore_session_state(self
);
295 sn_app_started(self
->class);
297 /* update the focus lists, do this before the call to change_state or
298 it can end up in the list twice! */
299 focus_order_add_new(self
);
301 client_change_state(self
);
303 /* remove the client's border (and adjust re gravity) */
304 client_toggle_border(self
, FALSE
);
306 /* specify that if we exit, the window should not be destroyed and should
307 be reparented back to root automatically */
308 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
310 /* create the decoration frame for the client window */
311 self
->frame
= frame_new();
313 frame_grab_client(self
->frame
, self
);
317 client_apply_startup_state(self
);
319 /* get and set application level settings */
320 settings
= get_settings(self
);
323 /* Don't worry, we won't actually both shade and undecorate the
324 * window when push comes to shove. */
325 if (settings
->shade
!= -1)
326 client_shade(self
, settings
->shade
);
327 if (settings
->decor
!= -1)
328 client_set_undecorated(self
, !settings
->decor
);
329 if (settings
->iconic
!= -1)
330 client_iconify(self
, settings
->iconic
, FALSE
);
331 if (settings
->skip_pager
!= -1) {
332 self
->skip_pager
= !!settings
->skip_pager
;
333 client_change_state(self
);
335 if (settings
->skip_taskbar
!= -1) {
336 self
->skip_taskbar
= !!settings
->skip_taskbar
;
337 client_change_state(self
);
340 /* 1 && -1 shouldn't be possible by the code in config.c */
341 if (settings
->max_vert
== 1 && self
->max_horz
== 1)
342 client_maximize(self
, TRUE
, 0, TRUE
);
343 else if (settings
->max_vert
== 0 && self
->max_horz
== 0)
344 client_maximize(self
, FALSE
, 0, TRUE
);
345 else if (settings
->max_vert
== 1 && self
->max_horz
== 0) {
346 client_maximize(self
, TRUE
, 2, TRUE
);
347 client_maximize(self
, FALSE
, 1, TRUE
);
348 } else if (settings
->max_vert
== 0 && self
->max_horz
== 1) {
349 client_maximize(self
, TRUE
, 1, TRUE
);
350 client_maximize(self
, FALSE
, 2, TRUE
);
353 if (settings
->fullscreen
!= -1)
354 client_fullscreen(self
, !!settings
->fullscreen
, TRUE
);
356 if (settings
->desktop
< screen_num_desktops
)
357 client_set_desktop(self
, settings
->desktop
, FALSE
);
359 if (settings
->layer
> -2 && settings
->layer
< 2)
360 client_set_layer(self
, settings
->layer
);
364 stacking_add(CLIENT_AS_WINDOW(self
));
365 client_restore_session_stacking(self
);
367 /* focus the new window? */
368 if (ob_state() != OB_STATE_STARTING
&&
369 (config_focus_new
|| client_search_focus_parent(self
)) ||
370 (settings
&& settings
->focus
== TRUE
) &&
371 /* note the check against Type_Normal/Dialog, not client_normal(self),
372 which would also include other types. in this case we want more
373 strict rules for focus */
374 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
375 self
->type
== OB_CLIENT_TYPE_DIALOG
))
379 if (self
->desktop
!= screen_desktop
) {
380 /* activate the window */
383 gboolean group_foc
= FALSE
;
388 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
390 if (client_focused(it
->data
))
398 (!self
->transient_for
&& (!self
->group
||
399 !self
->group
->members
->next
))) ||
400 client_search_focus_tree_full(self
) ||
402 !client_normal(focus_client
))
404 /* activate the window */
411 if (ob_state() == OB_STATE_RUNNING
) {
412 gint x
= self
->area
.x
, ox
= x
;
413 gint y
= self
->area
.y
, oy
= y
;
415 place_client(self
, &x
, &y
, settings
);
417 /* make sure the window is visible. */
418 client_find_onscreen(self
, &x
, &y
,
419 self
->frame
->area
.width
,
420 self
->frame
->area
.height
,
421 /* non-normal clients has less rules, and
422 windows that are being restored from a
423 session do also. we can assume you want
424 it back where you saved it. Clients saying
425 they placed themselves are subjected to
426 harder rules, ones that are placed by
427 place.c or by the user are allowed partially
428 off-screen and on xinerama divides (ie,
429 it is up to the placement routines to avoid
430 the xinerama divides) */
431 ((self
->positioned
& PPosition
) &&
432 !(self
->positioned
& USPosition
)) &&
433 client_normal(self
) &&
435 if (x
!= ox
|| y
!= oy
)
436 client_move(self
, x
, y
);
439 keyboard_grab_for_client(self
, TRUE
);
440 mouse_grab_for_client(self
, TRUE
);
442 client_showhide(self
);
444 /* use client_focus instead of client_activate cuz client_activate does
445 stuff like switch desktops etc and I'm not interested in all that when
446 a window maps since its not based on an action from the user like
447 clicking a window to activate is. so keep the new window out of the way
450 /* if using focus_delay, stop the timer now so that focus doesn't go
452 event_halt_focus_delay();
455 /* since focus can change the stacking orders, if we focus the window
456 then the standard raise it gets is not enough, we need to queue one
457 for after the focus change takes place */
461 /* client_activate does this but we aret using it so we have to do it
463 if (screen_showing_desktop
)
464 screen_show_desktop(FALSE
);
466 /* add to client list/map */
467 client_list
= g_list_append(client_list
, self
);
468 g_hash_table_insert(window_map
, &self
->window
, self
);
470 /* this has to happen after we're in the client_list */
471 screen_update_areas();
473 /* update the list hints */
476 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
479 void client_unmanage_all()
481 while (client_list
!= NULL
)
482 client_unmanage(client_list
->data
);
485 void client_unmanage(ObClient
*self
)
490 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
492 g_assert(self
!= NULL
);
494 keyboard_grab_for_client(self
, FALSE
);
495 mouse_grab_for_client(self
, FALSE
);
497 /* potentially fix focusLast */
498 if (config_focus_last
)
499 grab_pointer(TRUE
, OB_CURSOR_NONE
);
501 /* remove the window from our save set */
502 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
504 /* we dont want events no more */
505 XSelectInput(ob_display
, self
->window
, NoEventMask
);
507 frame_hide(self
->frame
);
509 client_list
= g_list_remove(client_list
, self
);
510 stacking_remove(self
);
511 g_hash_table_remove(window_map
, &self
->window
);
513 /* update the focus lists */
514 focus_order_remove(self
);
516 /* once the client is out of the list, update the struts to remove it's
518 screen_update_areas();
520 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
521 Destructor
*d
= it
->data
;
522 d
->func(self
, d
->data
);
525 if (focus_client
== self
) {
528 /* focus the last focused window on the desktop, and ignore enter
529 events from the unmap so it doesnt mess with the focus */
530 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
531 /* remove these flags so we don't end up getting focused in the
533 self
->can_focus
= FALSE
;
534 self
->focus_notify
= FALSE
;
536 client_unfocus(self
);
539 /* tell our parent(s) that we're gone */
540 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
541 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
542 if (it
->data
!= self
)
543 ((ObClient
*)it
->data
)->transients
=
544 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
545 } else if (self
->transient_for
) { /* transient of window */
546 self
->transient_for
->transients
=
547 g_slist_remove(self
->transient_for
->transients
, self
);
550 /* tell our transients that we're gone */
551 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
552 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
553 ((ObClient
*)it
->data
)->transient_for
= NULL
;
554 client_calc_layer(it
->data
);
558 /* remove from its group */
560 group_remove(self
->group
, self
);
564 /* give the client its border back */
565 client_toggle_border(self
, TRUE
);
567 /* reparent the window out of the frame, and free the frame */
568 frame_release_client(self
->frame
, self
);
571 if (ob_state() != OB_STATE_EXITING
) {
572 /* these values should not be persisted across a window
574 PROP_ERASE(self
->window
, net_wm_desktop
);
575 PROP_ERASE(self
->window
, net_wm_state
);
576 PROP_ERASE(self
->window
, wm_state
);
578 /* if we're left in an unmapped state, the client wont be mapped. this
579 is bad, since we will no longer be managing the window on restart */
580 XMapWindow(ob_display
, self
->window
);
584 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
586 /* free all data allocated in the client struct */
587 g_slist_free(self
->transients
);
588 for (j
= 0; j
< self
->nicons
; ++j
)
589 g_free(self
->icons
[j
].data
);
590 if (self
->nicons
> 0)
593 g_free(self
->icon_title
);
597 g_free(self
->sm_client_id
);
600 /* update the list hints */
603 if (config_focus_last
)
604 grab_pointer(FALSE
, OB_CURSOR_NONE
);
607 static void client_urgent_notify(ObClient
*self
)
610 frame_flash_start(self
->frame
);
612 frame_flash_stop(self
->frame
);
615 static void client_restore_session_state(ObClient
*self
)
619 if (!(it
= session_state_find(self
)))
622 self
->session
= it
->data
;
624 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
625 self
->positioned
= PPosition
;
626 if (self
->session
->w
> 0)
627 self
->area
.width
= self
->session
->w
;
628 if (self
->session
->h
> 0)
629 self
->area
.height
= self
->session
->h
;
630 XResizeWindow(ob_display
, self
->window
,
631 self
->area
.width
, self
->area
.height
);
633 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
634 self
->session
->desktop
:
635 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
636 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
638 self
->shaded
= self
->session
->shaded
;
639 self
->iconic
= self
->session
->iconic
;
640 self
->skip_pager
= self
->session
->skip_pager
;
641 self
->skip_taskbar
= self
->session
->skip_taskbar
;
642 self
->fullscreen
= self
->session
->fullscreen
;
643 self
->above
= self
->session
->above
;
644 self
->below
= self
->session
->below
;
645 self
->max_horz
= self
->session
->max_horz
;
646 self
->max_vert
= self
->session
->max_vert
;
649 static void client_restore_session_stacking(ObClient
*self
)
653 if (!self
->session
) return;
655 it
= g_list_find(session_saved_state
, self
->session
);
656 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
659 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
660 if (session_state_cmp(it
->data
, cit
->data
))
663 client_calc_layer(self
);
664 stacking_below(CLIENT_AS_WINDOW(self
),
665 CLIENT_AS_WINDOW(cit
->data
));
671 void client_move_onscreen(ObClient
*self
, gboolean rude
)
673 gint x
= self
->area
.x
;
674 gint y
= self
->area
.y
;
675 if (client_find_onscreen(self
, &x
, &y
,
676 self
->frame
->area
.width
,
677 self
->frame
->area
.height
, rude
)) {
678 client_move(self
, x
, y
);
682 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
686 gint ox
= *x
, oy
= *y
;
688 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
691 /* XXX watch for xinerama dead areas */
692 /* This makes sure windows aren't entirely outside of the screen so you
693 * can't see them at all */
694 if (client_normal(self
)) {
695 a
= screen_area(self
->desktop
);
696 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
697 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
698 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
699 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
700 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
702 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
706 /* This here doesn't let windows even a pixel outside the screen,
707 * when called from client_manage, programs placing themselves are
708 * forced completely onscreen, while things like
709 * xterm -geometry resolution-width/2 will work fine. Trying to
710 * place it completely offscreen will be handled in the above code.
711 * Sorry for this confused comment, i am tired. */
713 /* avoid the xinerama monitor divide while we're at it,
714 * remember to fix the placement stuff to avoid it also and
715 * then remove this XXX */
716 a
= screen_physical_area_monitor(client_monitor(self
));
717 /* dont let windows map/move into the strut unless they
718 are bigger than the available area */
720 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
721 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
722 *x
= a
->x
+ a
->width
- w
;
724 if (h
<= a
->height
) {
725 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
726 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
727 *y
= a
->y
+ a
->height
- h
;
731 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
734 return ox
!= *x
|| oy
!= *y
;
737 static void client_toggle_border(ObClient
*self
, gboolean show
)
739 /* adjust our idea of where the client is, based on its border. When the
740 border is removed, the client should now be considered to be in a
742 when re-adding the border to the client, the same operation needs to be
744 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
745 gint x
= oldx
, y
= oldy
;
746 switch(self
->gravity
) {
748 case NorthWestGravity
:
750 case SouthWestGravity
:
752 case NorthEastGravity
:
754 case SouthEastGravity
:
755 if (show
) x
-= self
->border_width
* 2;
756 else x
+= self
->border_width
* 2;
763 if (show
) x
-= self
->border_width
;
764 else x
+= self
->border_width
;
767 switch(self
->gravity
) {
769 case NorthWestGravity
:
771 case NorthEastGravity
:
773 case SouthWestGravity
:
775 case SouthEastGravity
:
776 if (show
) y
-= self
->border_width
* 2;
777 else y
+= self
->border_width
* 2;
784 if (show
) y
-= self
->border_width
;
785 else y
+= self
->border_width
;
792 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
794 /* move the client so it is back it the right spot _with_ its
796 if (x
!= oldx
|| y
!= oldy
)
797 XMoveWindow(ob_display
, self
->window
, x
, y
);
799 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
803 static void client_get_all(ObClient
*self
)
805 client_get_area(self
);
806 client_update_transient_for(self
);
807 client_update_wmhints(self
);
808 client_get_startup_id(self
);
809 client_get_desktop(self
);
810 client_get_shaped(self
);
812 client_get_mwm_hints(self
);
813 client_get_type(self
);/* this can change the mwmhints for special cases */
815 /* The transient hint is used to pick a type, but the type can also affect
816 transiency (dialogs are always made transients). This is Havoc's idea,
817 but it is needed to make some apps work right (eg tsclient). */
818 client_update_transient_for(self
);
820 client_get_state(self
);
823 /* a couple type-based defaults for new windows */
825 /* this makes sure that these windows appear on all desktops */
826 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
827 self
->desktop
= DESKTOP_ALL
;
830 client_update_protocols(self
);
832 client_get_gravity(self
); /* get the attribute gravity */
833 client_update_normal_hints(self
); /* this may override the attribute
836 /* got the type, the mwmhints, the protocols, and the normal hints
837 (min/max sizes), so we're ready to set up the decorations/functions */
838 client_setup_decor_and_functions(self
);
840 client_update_title(self
);
841 client_update_class(self
);
842 client_update_sm_client_id(self
);
843 client_update_strut(self
);
844 client_update_icons(self
);
847 static void client_get_startup_id(ObClient
*self
)
849 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
851 PROP_GETS(self
->group
->leader
,
852 net_startup_id
, utf8
, &self
->startup_id
);
855 static void client_get_area(ObClient
*self
)
857 XWindowAttributes wattrib
;
860 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
861 g_assert(ret
!= BadWindow
);
863 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
864 self
->border_width
= wattrib
.border_width
;
867 static void client_get_desktop(ObClient
*self
)
869 guint32 d
= screen_num_desktops
; /* an always-invalid value */
871 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
872 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
873 self
->desktop
= screen_num_desktops
- 1;
877 gboolean trdesk
= FALSE
;
879 if (self
->transient_for
) {
880 if (self
->transient_for
!= OB_TRAN_GROUP
) {
881 self
->desktop
= self
->transient_for
->desktop
;
886 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
887 if (it
->data
!= self
&&
888 !((ObClient
*)it
->data
)->transient_for
) {
889 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
896 /* try get from the startup-notification protocol */
897 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
898 if (self
->desktop
>= screen_num_desktops
&&
899 self
->desktop
!= DESKTOP_ALL
)
900 self
->desktop
= screen_num_desktops
- 1;
902 /* defaults to the current desktop */
903 self
->desktop
= screen_desktop
;
906 if (self
->desktop
!= d
) {
907 /* set the desktop hint, to make sure that it always exists */
908 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
912 static void client_get_state(ObClient
*self
)
917 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
919 for (i
= 0; i
< num
; ++i
) {
920 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
922 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
924 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
926 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
927 self
->skip_taskbar
= TRUE
;
928 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
929 self
->skip_pager
= TRUE
;
930 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
931 self
->fullscreen
= TRUE
;
932 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
933 self
->max_vert
= TRUE
;
934 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
935 self
->max_horz
= TRUE
;
936 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
938 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
940 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
941 self
->undecorated
= TRUE
;
947 if (!(self
->above
|| self
->below
)) {
949 /* apply stuff from the group */
953 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
954 ObClient
*c
= it
->data
;
955 if (c
!= self
&& !client_search_transient(self
, c
) &&
956 client_normal(self
) && client_normal(c
))
959 (c
->above
? 1 : (c
->below
? -1 : 0)));
973 g_assert_not_reached();
980 static void client_get_shaped(ObClient
*self
)
982 self
->shaped
= FALSE
;
984 if (extensions_shape
) {
989 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
991 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
992 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
994 self
->shaped
= (s
!= 0);
999 void client_update_transient_for(ObClient
*self
)
1002 ObClient
*target
= NULL
;
1004 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1005 self
->transient
= TRUE
;
1006 if (t
!= self
->window
) { /* cant be transient to itself! */
1007 target
= g_hash_table_lookup(window_map
, &t
);
1008 /* if this happens then we need to check for it*/
1009 g_assert(target
!= self
);
1010 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1011 /* this can happen when a dialog is a child of
1012 a dockapp, for example */
1016 if (!target
&& self
->group
) {
1017 /* not transient to a client, see if it is transient for a
1019 if (t
== self
->group
->leader
||
1021 t
== RootWindow(ob_display
, ob_screen
))
1023 /* window is a transient for its group! */
1024 target
= OB_TRAN_GROUP
;
1028 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
1029 self
->transient
= TRUE
;
1030 target
= OB_TRAN_GROUP
;
1032 self
->transient
= FALSE
;
1034 /* if anything has changed... */
1035 if (target
!= self
->transient_for
) {
1036 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1039 /* remove from old parents */
1040 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1041 ObClient
*c
= it
->data
;
1042 if (c
!= self
&& !c
->transient_for
)
1043 c
->transients
= g_slist_remove(c
->transients
, self
);
1045 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1046 /* remove from old parent */
1047 self
->transient_for
->transients
=
1048 g_slist_remove(self
->transient_for
->transients
, self
);
1050 self
->transient_for
= target
;
1051 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1054 /* add to new parents */
1055 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1056 ObClient
*c
= it
->data
;
1057 if (c
!= self
&& !c
->transient_for
)
1058 c
->transients
= g_slist_append(c
->transients
, self
);
1061 /* remove all transients which are in the group, that causes
1062 circlular pointer hell of doom */
1063 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1065 for (sit
= self
->transients
; sit
; sit
= next
) {
1066 next
= g_slist_next(sit
);
1067 if (sit
->data
== it
->data
)
1069 g_slist_delete_link(self
->transients
, sit
);
1072 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1073 /* add to new parent */
1074 self
->transient_for
->transients
=
1075 g_slist_append(self
->transient_for
->transients
, self
);
1080 static void client_get_mwm_hints(ObClient
*self
)
1085 self
->mwmhints
.flags
= 0; /* default to none */
1087 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1089 if (num
>= OB_MWM_ELEMENTS
) {
1090 self
->mwmhints
.flags
= hints
[0];
1091 self
->mwmhints
.functions
= hints
[1];
1092 self
->mwmhints
.decorations
= hints
[2];
1098 void client_get_type(ObClient
*self
)
1105 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1106 /* use the first value that we know about in the array */
1107 for (i
= 0; i
< num
; ++i
) {
1108 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1109 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1110 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1111 self
->type
= OB_CLIENT_TYPE_DOCK
;
1112 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1113 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1114 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1115 self
->type
= OB_CLIENT_TYPE_MENU
;
1116 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1117 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1118 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1119 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1120 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1121 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1122 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1123 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1124 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1125 /* prevent this window from getting any decor or
1127 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1128 OB_MWM_FLAG_DECORATIONS
);
1129 self
->mwmhints
.decorations
= 0;
1130 self
->mwmhints
.functions
= 0;
1132 if (self
->type
!= (ObClientType
) -1)
1133 break; /* grab the first legit type */
1138 if (self
->type
== (ObClientType
) -1) {
1139 /*the window type hint was not set, which means we either classify
1140 ourself as a normal window or a dialog, depending on if we are a
1142 if (self
->transient
)
1143 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1145 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1149 void client_update_protocols(ObClient
*self
)
1152 guint num_return
, i
;
1154 self
->focus_notify
= FALSE
;
1155 self
->delete_window
= FALSE
;
1157 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1158 for (i
= 0; i
< num_return
; ++i
) {
1159 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1160 /* this means we can request the window to close */
1161 self
->delete_window
= TRUE
;
1162 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1163 /* if this protocol is requested, then the window will be
1164 notified whenever we want it to receive focus */
1165 self
->focus_notify
= TRUE
;
1171 static void client_get_gravity(ObClient
*self
)
1173 XWindowAttributes wattrib
;
1176 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1177 g_assert(ret
!= BadWindow
);
1178 self
->gravity
= wattrib
.win_gravity
;
1181 void client_update_normal_hints(ObClient
*self
)
1185 gint oldgravity
= self
->gravity
;
1188 self
->min_ratio
= 0.0f
;
1189 self
->max_ratio
= 0.0f
;
1190 SIZE_SET(self
->size_inc
, 1, 1);
1191 SIZE_SET(self
->base_size
, 0, 0);
1192 SIZE_SET(self
->min_size
, 0, 0);
1193 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1195 /* get the hints from the window */
1196 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1197 /* normal windows can't request placement! har har
1198 if (!client_normal(self))
1200 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1202 if (size
.flags
& PWinGravity
) {
1203 self
->gravity
= size
.win_gravity
;
1205 /* if the client has a frame, i.e. has already been mapped and
1206 is changing its gravity */
1207 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1208 /* move our idea of the client's position based on its new
1210 self
->area
.x
= self
->frame
->area
.x
;
1211 self
->area
.y
= self
->frame
->area
.y
;
1212 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1216 if (size
.flags
& PAspect
) {
1217 if (size
.min_aspect
.y
)
1219 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1220 if (size
.max_aspect
.y
)
1222 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1225 if (size
.flags
& PMinSize
)
1226 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1228 if (size
.flags
& PMaxSize
)
1229 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1231 if (size
.flags
& PBaseSize
)
1232 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1234 if (size
.flags
& PResizeInc
)
1235 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1239 void client_setup_decor_and_functions(ObClient
*self
)
1241 /* start with everything (cept fullscreen) */
1243 (OB_FRAME_DECOR_TITLEBAR
|
1244 (ob_rr_theme
->show_handle
? OB_FRAME_DECOR_HANDLE
: 0) |
1245 OB_FRAME_DECOR_GRIPS
|
1246 OB_FRAME_DECOR_BORDER
|
1247 OB_FRAME_DECOR_ICON
|
1248 OB_FRAME_DECOR_ALLDESKTOPS
|
1249 OB_FRAME_DECOR_ICONIFY
|
1250 OB_FRAME_DECOR_MAXIMIZE
|
1251 OB_FRAME_DECOR_SHADE
|
1252 OB_FRAME_DECOR_CLOSE
);
1254 (OB_CLIENT_FUNC_RESIZE
|
1255 OB_CLIENT_FUNC_MOVE
|
1256 OB_CLIENT_FUNC_ICONIFY
|
1257 OB_CLIENT_FUNC_MAXIMIZE
|
1258 OB_CLIENT_FUNC_SHADE
|
1259 OB_CLIENT_FUNC_CLOSE
);
1261 if (!(self
->min_size
.width
< self
->max_size
.width
||
1262 self
->min_size
.height
< self
->max_size
.height
))
1263 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1265 switch (self
->type
) {
1266 case OB_CLIENT_TYPE_NORMAL
:
1267 /* normal windows retain all of the possible decorations and
1268 functionality, and are the only windows that you can fullscreen */
1269 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1272 case OB_CLIENT_TYPE_DIALOG
:
1273 case OB_CLIENT_TYPE_UTILITY
:
1274 /* these windows cannot be maximized */
1275 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1278 case OB_CLIENT_TYPE_MENU
:
1279 case OB_CLIENT_TYPE_TOOLBAR
:
1280 /* these windows get less functionality */
1281 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1284 case OB_CLIENT_TYPE_DESKTOP
:
1285 case OB_CLIENT_TYPE_DOCK
:
1286 case OB_CLIENT_TYPE_SPLASH
:
1287 /* none of these windows are manipulated by the window manager */
1288 self
->decorations
= 0;
1289 self
->functions
= 0;
1293 /* Mwm Hints are applied subtractively to what has already been chosen for
1294 decor and functionality */
1295 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1296 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1297 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1298 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1299 /* if the mwm hints request no handle or title, then all
1300 decorations are disabled */
1301 self
->decorations
= config_theme_keepborder
? OB_FRAME_DECOR_BORDER
: 0;
1305 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1306 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1307 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1308 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1309 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1310 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1311 /* dont let mwm hints kill any buttons
1312 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1313 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1314 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1315 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1317 /* dont let mwm hints kill the close button
1318 if (! (self->mwmhints.functions & MwmFunc_Close))
1319 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1323 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1324 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1325 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1326 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1327 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1328 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1330 /* can't maximize without moving/resizing */
1331 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1332 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1333 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1334 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1335 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1338 /* kill the handle on fully maxed windows */
1339 if (self
->max_vert
&& self
->max_horz
)
1340 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1342 /* finally, the user can have requested no decorations, which overrides
1343 everything (but doesnt give it a border if it doesnt have one) */
1344 if (self
->undecorated
) {
1345 if (config_theme_keepborder
)
1346 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1348 self
->decorations
= 0;
1351 /* if we don't have a titlebar, then we cannot shade! */
1352 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1353 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1355 /* now we need to check against rules for the client's current state */
1356 if (self
->fullscreen
) {
1357 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1358 OB_CLIENT_FUNC_FULLSCREEN
|
1359 OB_CLIENT_FUNC_ICONIFY
);
1360 self
->decorations
= 0;
1363 client_change_allowed_actions(self
);
1366 /* adjust the client's decorations, etc. */
1367 client_reconfigure(self
);
1371 static void client_change_allowed_actions(ObClient
*self
)
1376 /* desktop windows are kept on all desktops */
1377 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1378 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1380 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1381 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1382 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1383 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1384 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1385 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1386 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1387 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1388 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1389 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1390 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1391 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1392 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1393 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1394 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1397 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1399 /* make sure the window isn't breaking any rules now */
1401 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1402 if (self
->frame
) client_shade(self
, FALSE
);
1403 else self
->shaded
= FALSE
;
1405 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1406 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1407 else self
->iconic
= FALSE
;
1409 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1410 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1411 else self
->fullscreen
= FALSE
;
1413 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1415 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1416 else self
->max_vert
= self
->max_horz
= FALSE
;
1420 void client_reconfigure(ObClient
*self
)
1422 /* by making this pass FALSE for user, we avoid the emacs event storm where
1423 every configurenotify causes an update in its normal hints, i think this
1424 is generally what we want anyways... */
1425 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1426 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1429 void client_update_wmhints(ObClient
*self
)
1432 gboolean ur
= FALSE
;
1435 /* assume a window takes input if it doesnt specify */
1436 self
->can_focus
= TRUE
;
1438 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1439 if (hints
->flags
& InputHint
)
1440 self
->can_focus
= hints
->input
;
1442 /* only do this when first managing the window *AND* when we aren't
1444 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1445 if (hints
->flags
& StateHint
)
1446 self
->iconic
= hints
->initial_state
== IconicState
;
1448 if (hints
->flags
& XUrgencyHint
)
1451 if (!(hints
->flags
& WindowGroupHint
))
1452 hints
->window_group
= None
;
1454 /* did the group state change? */
1455 if (hints
->window_group
!=
1456 (self
->group
? self
->group
->leader
: None
)) {
1457 /* remove from the old group if there was one */
1458 if (self
->group
!= NULL
) {
1459 /* remove transients of the group */
1460 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1461 self
->transients
= g_slist_remove(self
->transients
,
1464 /* remove myself from parents in the group */
1465 if (self
->transient_for
== OB_TRAN_GROUP
) {
1466 for (it
= self
->group
->members
; it
;
1467 it
= g_slist_next(it
))
1469 ObClient
*c
= it
->data
;
1471 if (c
!= self
&& !c
->transient_for
)
1472 c
->transients
= g_slist_remove(c
->transients
,
1477 group_remove(self
->group
, self
);
1480 if (hints
->window_group
!= None
) {
1481 self
->group
= group_add(hints
->window_group
, self
);
1483 /* i can only have transients from the group if i am not
1485 if (!self
->transient_for
) {
1486 /* add other transients of the group that are already
1488 for (it
= self
->group
->members
; it
;
1489 it
= g_slist_next(it
))
1491 ObClient
*c
= it
->data
;
1492 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1494 g_slist_append(self
->transients
, c
);
1499 /* because the self->transient flag wont change from this call,
1500 we don't need to update the window's type and such, only its
1501 transient_for, and the transients lists of other windows in
1502 the group may be affected */
1503 client_update_transient_for(self
);
1506 /* the WM_HINTS can contain an icon */
1507 client_update_icons(self
);
1512 if (ur
!= self
->urgent
) {
1514 /* fire the urgent callback if we're mapped, otherwise, wait until
1515 after we're mapped */
1517 client_urgent_notify(self
);
1521 void client_update_title(ObClient
*self
)
1527 gboolean read_title
;
1530 old_title
= self
->title
;
1533 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1534 /* try old x stuff */
1535 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
)) {
1536 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1537 if (self
->transient
) {
1538 data
= g_strdup("");
1541 data
= g_strdup("Unnamed Window");
1545 /* did the title change? then reset the title_count */
1546 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1547 self
->title_count
= 1;
1549 /* look for duplicates and append a number */
1551 for (it
= client_list
; it
; it
= g_list_next(it
))
1552 if (it
->data
!= self
) {
1553 ObClient
*c
= it
->data
;
1554 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1555 nums
|= 1 << c
->title_count
;
1557 /* find first free number */
1558 for (i
= 1; i
<= 32; ++i
)
1559 if (!(nums
& (1 << i
))) {
1560 if (self
->title_count
== 1 || i
== 1)
1561 self
->title_count
= i
;
1564 /* dont display the number for the first window */
1565 if (self
->title_count
> 1) {
1567 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1572 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1577 frame_adjust_title(self
->frame
);
1581 /* update the icon title */
1583 g_free(self
->icon_title
);
1587 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1588 /* try old x stuff */
1589 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1590 data
= g_strdup(self
->title
);
1594 /* append the title count, dont display the number for the first window */
1595 if (read_title
&& self
->title_count
> 1) {
1596 gchar
*vdata
, *ndata
;
1597 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1598 vdata
= g_strconcat(data
, ndata
, NULL
);
1604 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1606 self
->icon_title
= data
;
1609 void client_update_class(ObClient
*self
)
1614 if (self
->name
) g_free(self
->name
);
1615 if (self
->class) g_free(self
->class);
1616 if (self
->role
) g_free(self
->role
);
1618 self
->name
= self
->class = self
->role
= NULL
;
1620 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1622 self
->name
= g_strdup(data
[0]);
1624 self
->class = g_strdup(data
[1]);
1629 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1632 if (self
->name
== NULL
) self
->name
= g_strdup("");
1633 if (self
->class == NULL
) self
->class = g_strdup("");
1634 if (self
->role
== NULL
) self
->role
= g_strdup("");
1637 void client_update_strut(ObClient
*self
)
1641 gboolean got
= FALSE
;
1644 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1648 STRUT_PARTIAL_SET(strut
,
1649 data
[0], data
[2], data
[1], data
[3],
1650 data
[4], data
[5], data
[8], data
[9],
1651 data
[6], data
[7], data
[10], data
[11]);
1657 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1663 /* use the screen's width/height */
1664 a
= screen_physical_area();
1666 STRUT_PARTIAL_SET(strut
,
1667 data
[0], data
[2], data
[1], data
[3],
1668 a
->y
, a
->y
+ a
->height
- 1,
1669 a
->x
, a
->x
+ a
->width
- 1,
1670 a
->y
, a
->y
+ a
->height
- 1,
1671 a
->x
, a
->x
+ a
->width
- 1);
1677 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1678 0, 0, 0, 0, 0, 0, 0, 0);
1680 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1681 self
->strut
= strut
;
1683 /* updating here is pointless while we're being mapped cuz we're not in
1684 the client list yet */
1686 screen_update_areas();
1690 void client_update_icons(ObClient
*self
)
1696 for (i
= 0; i
< self
->nicons
; ++i
)
1697 g_free(self
->icons
[i
].data
);
1698 if (self
->nicons
> 0)
1699 g_free(self
->icons
);
1702 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1703 /* figure out how many valid icons are in here */
1705 while (num
- i
> 2) {
1709 if (i
> num
|| w
*h
== 0) break;
1713 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1715 /* store the icons */
1717 for (j
= 0; j
< self
->nicons
; ++j
) {
1720 w
= self
->icons
[j
].width
= data
[i
++];
1721 h
= self
->icons
[j
].height
= data
[i
++];
1723 if (w
*h
== 0) continue;
1725 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1726 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1731 self
->icons
[j
].data
[t
] =
1732 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1733 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1734 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1735 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1741 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1742 kwm_win_icon
, &data
, &num
)) {
1745 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1746 xerror_set_ignore(TRUE
);
1747 if (!RrPixmapToRGBA(ob_rr_inst
,
1749 &self
->icons
[self
->nicons
-1].width
,
1750 &self
->icons
[self
->nicons
-1].height
,
1751 &self
->icons
[self
->nicons
-1].data
)) {
1752 g_free(&self
->icons
[self
->nicons
-1]);
1755 xerror_set_ignore(FALSE
);
1761 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1762 if (hints
->flags
& IconPixmapHint
) {
1764 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1765 xerror_set_ignore(TRUE
);
1766 if (!RrPixmapToRGBA(ob_rr_inst
,
1768 (hints
->flags
& IconMaskHint
?
1769 hints
->icon_mask
: None
),
1770 &self
->icons
[self
->nicons
-1].width
,
1771 &self
->icons
[self
->nicons
-1].height
,
1772 &self
->icons
[self
->nicons
-1].data
)){
1773 g_free(&self
->icons
[self
->nicons
-1]);
1776 xerror_set_ignore(FALSE
);
1783 frame_adjust_icon(self
->frame
);
1786 static void client_change_state(ObClient
*self
)
1789 gulong netstate
[11];
1792 state
[0] = self
->wmstate
;
1794 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1798 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1800 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1802 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1803 if (self
->skip_taskbar
)
1804 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1805 if (self
->skip_pager
)
1806 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1807 if (self
->fullscreen
)
1808 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1810 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1812 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1814 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1816 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1817 if (self
->undecorated
)
1818 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1819 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1821 client_calc_layer(self
);
1824 frame_adjust_state(self
->frame
);
1827 ObClient
*client_search_focus_tree(ObClient
*self
)
1832 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1833 if (client_focused(it
->data
)) return it
->data
;
1834 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1839 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1841 if (self
->transient_for
) {
1842 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1843 return client_search_focus_tree_full(self
->transient_for
);
1846 gboolean recursed
= FALSE
;
1848 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1849 if (!((ObClient
*)it
->data
)->transient_for
) {
1851 if ((c
= client_search_focus_tree_full(it
->data
)))
1860 /* this function checks the whole tree, the client_search_focus_tree~
1861 does not, so we need to check this window */
1862 if (client_focused(self
))
1864 return client_search_focus_tree(self
);
1867 static ObStackingLayer
calc_layer(ObClient
*self
)
1871 if (self
->fullscreen
&&
1872 (client_focused(self
) || client_search_focus_tree(self
)))
1873 l
= OB_STACKING_LAYER_FULLSCREEN
;
1874 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1875 l
= OB_STACKING_LAYER_DESKTOP
;
1876 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1877 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1878 else l
= OB_STACKING_LAYER_ABOVE
;
1880 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1881 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1882 else l
= OB_STACKING_LAYER_NORMAL
;
1887 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1888 ObStackingLayer l
, gboolean raised
)
1890 ObStackingLayer old
, own
;
1894 own
= calc_layer(self
);
1895 self
->layer
= l
> own
? l
: own
;
1897 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1898 client_calc_layer_recursive(it
->data
, orig
,
1899 l
, raised
? raised
: l
!= old
);
1901 if (!raised
&& l
!= old
)
1902 if (orig
->frame
) { /* only restack if the original window is managed */
1903 stacking_remove(CLIENT_AS_WINDOW(self
));
1904 stacking_add(CLIENT_AS_WINDOW(self
));
1908 void client_calc_layer(ObClient
*self
)
1915 /* transients take on the layer of their parents */
1916 self
= client_search_top_transient(self
);
1918 l
= calc_layer(self
);
1920 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1923 gboolean
client_should_show(ObClient
*self
)
1927 if (client_normal(self
) && screen_showing_desktop
)
1930 if (self->transient_for) {
1931 if (self->transient_for != OB_TRAN_GROUP)
1932 return client_should_show(self->transient_for);
1936 for (it = self->group->members; it; it = g_slist_next(it)) {
1937 ObClient *c = it->data;
1938 if (c != self && !c->transient_for) {
1939 if (client_should_show(c))
1946 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
1952 static void client_showhide(ObClient
*self
)
1955 if (client_should_show(self
))
1956 frame_show(self
->frame
);
1958 frame_hide(self
->frame
);
1961 gboolean
client_normal(ObClient
*self
) {
1962 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1963 self
->type
== OB_CLIENT_TYPE_DOCK
||
1964 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1967 static void client_apply_startup_state(ObClient
*self
)
1969 /* these are in a carefully crafted order.. */
1972 self
->iconic
= FALSE
;
1973 client_iconify(self
, TRUE
, FALSE
);
1975 if (self
->fullscreen
) {
1976 self
->fullscreen
= FALSE
;
1977 client_fullscreen(self
, TRUE
, FALSE
);
1979 if (self
->undecorated
) {
1980 self
->undecorated
= FALSE
;
1981 client_set_undecorated(self
, TRUE
);
1984 self
->shaded
= FALSE
;
1985 client_shade(self
, TRUE
);
1988 client_urgent_notify(self
);
1990 if (self
->max_vert
&& self
->max_horz
) {
1991 self
->max_vert
= self
->max_horz
= FALSE
;
1992 client_maximize(self
, TRUE
, 0, FALSE
);
1993 } else if (self
->max_vert
) {
1994 self
->max_vert
= FALSE
;
1995 client_maximize(self
, TRUE
, 2, FALSE
);
1996 } else if (self
->max_horz
) {
1997 self
->max_horz
= FALSE
;
1998 client_maximize(self
, TRUE
, 1, FALSE
);
2001 /* nothing to do for the other states:
2010 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2011 gint x
, gint y
, gint w
, gint h
,
2012 gboolean user
, gboolean final
,
2013 gboolean force_reply
)
2016 gboolean send_resize_client
;
2017 gboolean moved
= FALSE
, resized
= FALSE
;
2018 guint fdecor
= self
->frame
->decorations
;
2019 gboolean fhorz
= self
->frame
->max_horz
;
2021 /* make the frame recalculate its dimentions n shit without changing
2022 anything visible for real, this way the constraints below can work with
2023 the updated frame dimensions. */
2024 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2026 /* gets the frame's position */
2027 frame_client_gravity(self
->frame
, &x
, &y
);
2029 /* these positions are frame positions, not client positions */
2031 /* set the size and position if fullscreen */
2032 if (self
->fullscreen
) {
2035 XF86VidModeModeLine mode
;
2040 i
= client_monitor(self
);
2041 a
= screen_physical_area_monitor(i
);
2044 if (i
== 0 && /* primary head */
2045 extensions_vidmode
&&
2046 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
2047 /* get the mode last so the mode.privsize isnt freed incorrectly */
2048 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
2053 if (mode
.privsize
) XFree(mode
.private);
2063 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2067 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
2069 /* set the size and position if maximized */
2070 if (self
->max_horz
) {
2072 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2074 if (self
->max_vert
) {
2076 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2080 /* gets the client's position */
2081 frame_frame_gravity(self
->frame
, &x
, &y
);
2083 /* these override the above states! if you cant move you can't move! */
2085 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2089 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2090 w
= self
->area
.width
;
2091 h
= self
->area
.height
;
2095 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2096 gint basew
, baseh
, minw
, minh
;
2098 /* base size is substituted with min size if not specified */
2099 if (self
->base_size
.width
|| self
->base_size
.height
) {
2100 basew
= self
->base_size
.width
;
2101 baseh
= self
->base_size
.height
;
2103 basew
= self
->min_size
.width
;
2104 baseh
= self
->min_size
.height
;
2106 /* min size is substituted with base size if not specified */
2107 if (self
->min_size
.width
|| self
->min_size
.height
) {
2108 minw
= self
->min_size
.width
;
2109 minh
= self
->min_size
.height
;
2111 minw
= self
->base_size
.width
;
2112 minh
= self
->base_size
.height
;
2115 /* if this is a user-requested resize, then check against min/max
2118 /* smaller than min size or bigger than max size? */
2119 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2120 if (w
< minw
) w
= minw
;
2121 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2122 if (h
< minh
) h
= minh
;
2127 /* keep to the increments */
2128 w
/= self
->size_inc
.width
;
2129 h
/= self
->size_inc
.height
;
2131 /* you cannot resize to nothing */
2132 if (basew
+ w
< 1) w
= 1 - basew
;
2133 if (baseh
+ h
< 1) h
= 1 - baseh
;
2135 /* store the logical size */
2136 SIZE_SET(self
->logical_size
,
2137 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2138 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2140 w
*= self
->size_inc
.width
;
2141 h
*= self
->size_inc
.height
;
2146 /* adjust the height to match the width for the aspect ratios.
2147 for this, min size is not substituted for base size ever. */
2148 w
-= self
->base_size
.width
;
2149 h
-= self
->base_size
.height
;
2151 if (!self
->fullscreen
) {
2152 if (self
->min_ratio
)
2153 if (h
* self
->min_ratio
> w
) {
2154 h
= (gint
)(w
/ self
->min_ratio
);
2156 /* you cannot resize to nothing */
2159 w
= (gint
)(h
* self
->min_ratio
);
2162 if (self
->max_ratio
)
2163 if (h
* self
->max_ratio
< w
) {
2164 h
= (gint
)(w
/ self
->max_ratio
);
2166 /* you cannot resize to nothing */
2169 w
= (gint
)(h
* self
->min_ratio
);
2174 w
+= self
->base_size
.width
;
2175 h
+= self
->base_size
.height
;
2182 case OB_CORNER_TOPLEFT
:
2184 case OB_CORNER_TOPRIGHT
:
2185 x
-= w
- self
->area
.width
;
2187 case OB_CORNER_BOTTOMLEFT
:
2188 y
-= h
- self
->area
.height
;
2190 case OB_CORNER_BOTTOMRIGHT
:
2191 x
-= w
- self
->area
.width
;
2192 y
-= h
- self
->area
.height
;
2196 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2197 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2199 oldw
= self
->area
.width
;
2200 oldh
= self
->area
.height
;
2201 RECT_SET(self
->area
, x
, y
, w
, h
);
2203 /* for app-requested resizes, always resize if 'resized' is true.
2204 for user-requested ones, only resize if final is true, or when
2205 resizing in redraw mode */
2206 send_resize_client
= ((!user
&& resized
) ||
2208 (resized
&& config_resize_redraw
))));
2210 /* if the client is enlarging, then resize the client before the frame */
2211 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2212 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2214 /* move/resize the frame to match the request */
2216 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2217 moved
= resized
= TRUE
;
2219 if (moved
|| resized
)
2220 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2222 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2225 event
.type
= ConfigureNotify
;
2226 event
.xconfigure
.display
= ob_display
;
2227 event
.xconfigure
.event
= self
->window
;
2228 event
.xconfigure
.window
= self
->window
;
2230 /* root window real coords */
2231 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2233 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2235 event
.xconfigure
.width
= w
;
2236 event
.xconfigure
.height
= h
;
2237 event
.xconfigure
.border_width
= 0;
2238 event
.xconfigure
.above
= self
->frame
->plate
;
2239 event
.xconfigure
.override_redirect
= FALSE
;
2240 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2241 FALSE
, StructureNotifyMask
, &event
);
2245 /* if the client is shrinking, then resize the frame before the client */
2246 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2247 XResizeWindow(ob_display
, self
->window
, w
, h
);
2252 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2256 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2257 self
->fullscreen
== fs
) return; /* already done */
2259 self
->fullscreen
= fs
;
2260 client_change_state(self
); /* change the state hints on the client,
2261 and adjust out layer/stacking */
2265 self
->pre_fullscreen_area
= self
->area
;
2267 /* these are not actually used cuz client_configure will set them
2268 as appropriate when the window is fullscreened */
2273 if (self
->pre_fullscreen_area
.width
> 0 &&
2274 self
->pre_fullscreen_area
.height
> 0)
2276 x
= self
->pre_fullscreen_area
.x
;
2277 y
= self
->pre_fullscreen_area
.y
;
2278 w
= self
->pre_fullscreen_area
.width
;
2279 h
= self
->pre_fullscreen_area
.height
;
2280 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2282 /* pick some fallbacks... */
2283 a
= screen_area_monitor(self
->desktop
, 0);
2284 x
= a
->x
+ a
->width
/ 4;
2285 y
= a
->y
+ a
->height
/ 4;
2291 client_setup_decor_and_functions(self
);
2293 client_move_resize(self
, x
, y
, w
, h
);
2295 /* try focus us when we go into fullscreen mode */
2299 static void client_iconify_recursive(ObClient
*self
,
2300 gboolean iconic
, gboolean curdesk
)
2303 gboolean changed
= FALSE
;
2306 if (self
->iconic
!= iconic
) {
2307 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2310 self
->iconic
= iconic
;
2313 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2316 old
= self
->wmstate
;
2317 self
->wmstate
= IconicState
;
2318 if (old
!= self
->wmstate
)
2319 PROP_MSG(self
->window
, kde_wm_change_state
,
2320 self
->wmstate
, 1, 0, 0);
2322 /* update the focus lists.. iconic windows go to the bottom of
2323 the list, put the new iconic window at the 'top of the
2325 focus_order_to_top(self
);
2333 client_set_desktop(self
, screen_desktop
, FALSE
);
2335 old
= self
->wmstate
;
2336 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2337 if (old
!= self
->wmstate
)
2338 PROP_MSG(self
->window
, kde_wm_change_state
,
2339 self
->wmstate
, 1, 0, 0);
2341 /* this puts it after the current focused window */
2342 focus_order_remove(self
);
2343 focus_order_add_new(self
);
2345 /* this is here cuz with the VIDMODE extension, the viewport can
2346 change while a fullscreen window is iconic, and when it
2347 uniconifies, it would be nice if it did so to the new position
2349 client_reconfigure(self
);
2356 client_change_state(self
);
2357 client_showhide(self
);
2358 screen_update_areas();
2361 /* iconify all transients */
2362 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2363 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2367 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2369 /* move up the transient chain as far as possible first */
2370 self
= client_search_top_transient(self
);
2372 client_iconify_recursive(client_search_top_transient(self
),
2376 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2380 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2381 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2383 /* check if already done */
2385 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2386 if (dir
== 1 && self
->max_horz
) return;
2387 if (dir
== 2 && self
->max_vert
) return;
2389 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2390 if (dir
== 1 && !self
->max_horz
) return;
2391 if (dir
== 2 && !self
->max_vert
) return;
2394 /* we just tell it to configure in the same place and client_configure
2395 worries about filling the screen with the window */
2398 w
= self
->area
.width
;
2399 h
= self
->area
.height
;
2403 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2404 RECT_SET(self
->pre_max_area
,
2405 self
->area
.x
, self
->pre_max_area
.y
,
2406 self
->area
.width
, self
->pre_max_area
.height
);
2408 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2409 RECT_SET(self
->pre_max_area
,
2410 self
->pre_max_area
.x
, self
->area
.y
,
2411 self
->pre_max_area
.width
, self
->area
.height
);
2417 a
= screen_area_monitor(self
->desktop
, 0);
2418 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2419 if (self
->pre_max_area
.width
> 0) {
2420 x
= self
->pre_max_area
.x
;
2421 w
= self
->pre_max_area
.width
;
2423 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2424 0, self
->pre_max_area
.height
);
2426 /* pick some fallbacks... */
2427 x
= a
->x
+ a
->width
/ 4;
2431 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2432 if (self
->pre_max_area
.height
> 0) {
2433 y
= self
->pre_max_area
.y
;
2434 h
= self
->pre_max_area
.height
;
2436 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2437 self
->pre_max_area
.width
, 0);
2439 /* pick some fallbacks... */
2440 y
= a
->y
+ a
->height
/ 4;
2446 if (dir
== 0 || dir
== 1) /* horz */
2447 self
->max_horz
= max
;
2448 if (dir
== 0 || dir
== 2) /* vert */
2449 self
->max_vert
= max
;
2451 client_change_state(self
); /* change the state hints on the client */
2453 client_setup_decor_and_functions(self
);
2455 client_move_resize(self
, x
, y
, w
, h
);
2458 void client_shade(ObClient
*self
, gboolean shade
)
2460 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2461 shade
) || /* can't shade */
2462 self
->shaded
== shade
) return; /* already done */
2464 /* when we're iconic, don't change the wmstate */
2465 if (!self
->iconic
) {
2468 old
= self
->wmstate
;
2469 self
->wmstate
= shade
? IconicState
: NormalState
;
2470 if (old
!= self
->wmstate
)
2471 PROP_MSG(self
->window
, kde_wm_change_state
,
2472 self
->wmstate
, 1, 0, 0);
2475 self
->shaded
= shade
;
2476 client_change_state(self
);
2477 /* resize the frame to just the titlebar */
2478 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2481 void client_close(ObClient
*self
)
2485 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2487 /* in the case that the client provides no means to requesting that it
2488 close, we just kill it */
2489 if (!self
->delete_window
)
2493 XXX: itd be cool to do timeouts and shit here for killing the client's
2495 like... if the window is around after 5 seconds, then the close button
2496 turns a nice red, and if this function is called again, the client is
2500 ce
.xclient
.type
= ClientMessage
;
2501 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2502 ce
.xclient
.display
= ob_display
;
2503 ce
.xclient
.window
= self
->window
;
2504 ce
.xclient
.format
= 32;
2505 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2506 ce
.xclient
.data
.l
[1] = event_lasttime
;
2507 ce
.xclient
.data
.l
[2] = 0l;
2508 ce
.xclient
.data
.l
[3] = 0l;
2509 ce
.xclient
.data
.l
[4] = 0l;
2510 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2513 void client_kill(ObClient
*self
)
2515 XKillClient(ob_display
, self
->window
);
2518 void client_set_desktop_recursive(ObClient
*self
,
2519 guint target
, gboolean donthide
)
2524 if (target
!= self
->desktop
) {
2526 ob_debug("Setting desktop %u\n", target
+1);
2528 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2530 /* remove from the old desktop(s) */
2531 focus_order_remove(self
);
2533 old
= self
->desktop
;
2534 self
->desktop
= target
;
2535 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2536 /* the frame can display the current desktop state */
2537 frame_adjust_state(self
->frame
);
2538 /* 'move' the window to the new desktop */
2540 client_showhide(self
);
2541 /* raise if it was not already on the desktop */
2542 if (old
!= DESKTOP_ALL
)
2544 screen_update_areas();
2546 /* add to the new desktop(s) */
2547 if (config_focus_new
)
2548 focus_order_to_top(self
);
2550 focus_order_to_bottom(self
);
2553 /* move all transients */
2554 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2555 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2559 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2561 client_set_desktop_recursive(client_search_top_transient(self
),
2565 ObClient
*client_search_modal_child(ObClient
*self
)
2570 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2571 ObClient
*c
= it
->data
;
2572 if ((ret
= client_search_modal_child(c
))) return ret
;
2573 if (c
->modal
) return c
;
2578 gboolean
client_validate(ObClient
*self
)
2582 XSync(ob_display
, FALSE
); /* get all events on the server */
2584 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2585 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2586 XPutBackEvent(ob_display
, &e
);
2593 void client_set_wm_state(ObClient
*self
, glong state
)
2595 if (state
== self
->wmstate
) return; /* no change */
2599 client_iconify(self
, TRUE
, TRUE
);
2602 client_iconify(self
, FALSE
, TRUE
);
2607 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2609 gboolean shaded
= self
->shaded
;
2610 gboolean fullscreen
= self
->fullscreen
;
2611 gboolean undecorated
= self
->undecorated
;
2612 gboolean max_horz
= self
->max_horz
;
2613 gboolean max_vert
= self
->max_vert
;
2614 gboolean modal
= self
->modal
;
2615 gboolean iconic
= self
->iconic
;
2618 if (!(action
== prop_atoms
.net_wm_state_add
||
2619 action
== prop_atoms
.net_wm_state_remove
||
2620 action
== prop_atoms
.net_wm_state_toggle
))
2621 /* an invalid action was passed to the client message, ignore it */
2624 for (i
= 0; i
< 2; ++i
) {
2625 Atom state
= i
== 0 ? data1
: data2
;
2627 if (!state
) continue;
2629 /* if toggling, then pick whether we're adding or removing */
2630 if (action
== prop_atoms
.net_wm_state_toggle
) {
2631 if (state
== prop_atoms
.net_wm_state_modal
)
2632 action
= modal
? prop_atoms
.net_wm_state_remove
:
2633 prop_atoms
.net_wm_state_add
;
2634 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2635 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2636 prop_atoms
.net_wm_state_add
;
2637 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2638 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2639 prop_atoms
.net_wm_state_add
;
2640 else if (state
== prop_atoms
.net_wm_state_shaded
)
2641 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2642 prop_atoms
.net_wm_state_add
;
2643 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2644 action
= self
->skip_taskbar
?
2645 prop_atoms
.net_wm_state_remove
:
2646 prop_atoms
.net_wm_state_add
;
2647 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2648 action
= self
->skip_pager
?
2649 prop_atoms
.net_wm_state_remove
:
2650 prop_atoms
.net_wm_state_add
;
2651 else if (state
== prop_atoms
.net_wm_state_hidden
)
2652 action
= self
->iconic
?
2653 prop_atoms
.net_wm_state_remove
:
2654 prop_atoms
.net_wm_state_add
;
2655 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2656 action
= fullscreen
?
2657 prop_atoms
.net_wm_state_remove
:
2658 prop_atoms
.net_wm_state_add
;
2659 else if (state
== prop_atoms
.net_wm_state_above
)
2660 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2661 prop_atoms
.net_wm_state_add
;
2662 else if (state
== prop_atoms
.net_wm_state_below
)
2663 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2664 prop_atoms
.net_wm_state_add
;
2665 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2666 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2667 prop_atoms
.net_wm_state_add
;
2670 if (action
== prop_atoms
.net_wm_state_add
) {
2671 if (state
== prop_atoms
.net_wm_state_modal
) {
2673 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2675 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2677 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2679 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2680 self
->skip_taskbar
= TRUE
;
2681 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2682 self
->skip_pager
= TRUE
;
2683 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2685 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2687 } else if (state
== prop_atoms
.net_wm_state_above
) {
2689 self
->below
= FALSE
;
2690 } else if (state
== prop_atoms
.net_wm_state_below
) {
2691 self
->above
= FALSE
;
2693 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2697 } else { /* action == prop_atoms.net_wm_state_remove */
2698 if (state
== prop_atoms
.net_wm_state_modal
) {
2700 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2702 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2704 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2706 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2707 self
->skip_taskbar
= FALSE
;
2708 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2709 self
->skip_pager
= FALSE
;
2710 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2712 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2714 } else if (state
== prop_atoms
.net_wm_state_above
) {
2715 self
->above
= FALSE
;
2716 } else if (state
== prop_atoms
.net_wm_state_below
) {
2717 self
->below
= FALSE
;
2718 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2719 undecorated
= FALSE
;
2723 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2724 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2726 if (max_horz
== max_vert
) { /* both going the same way */
2727 client_maximize(self
, max_horz
, 0, TRUE
);
2729 client_maximize(self
, max_horz
, 1, TRUE
);
2730 client_maximize(self
, max_vert
, 2, TRUE
);
2734 if (max_horz
!= self
->max_horz
)
2735 client_maximize(self
, max_horz
, 1, TRUE
);
2737 client_maximize(self
, max_vert
, 2, TRUE
);
2740 /* change fullscreen state before shading, as it will affect if the window
2742 if (fullscreen
!= self
->fullscreen
)
2743 client_fullscreen(self
, fullscreen
, TRUE
);
2744 if (shaded
!= self
->shaded
)
2745 client_shade(self
, shaded
);
2746 if (undecorated
!= self
->undecorated
)
2747 client_set_undecorated(self
, undecorated
);
2748 if (modal
!= self
->modal
) {
2749 self
->modal
= modal
;
2750 /* when a window changes modality, then its stacking order with its
2751 transients needs to change */
2754 if (iconic
!= self
->iconic
)
2755 client_iconify(self
, iconic
, FALSE
);
2757 client_calc_layer(self
);
2758 client_change_state(self
); /* change the hint to reflect these changes */
2761 ObClient
*client_focus_target(ObClient
*self
)
2765 /* if we have a modal child, then focus it, not us */
2766 child
= client_search_modal_child(client_search_top_transient(self
));
2767 if (child
) return child
;
2771 gboolean
client_can_focus(ObClient
*self
)
2775 /* choose the correct target */
2776 self
= client_focus_target(self
);
2778 if (!self
->frame
->visible
)
2781 if (!(self
->can_focus
|| self
->focus_notify
))
2784 /* do a check to see if the window has already been unmapped or destroyed
2785 do this intelligently while watching out for unmaps we've generated
2786 (ignore_unmaps > 0) */
2787 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2788 DestroyNotify
, &ev
)) {
2789 XPutBackEvent(ob_display
, &ev
);
2792 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2793 UnmapNotify
, &ev
)) {
2794 if (self
->ignore_unmaps
) {
2795 self
->ignore_unmaps
--;
2797 XPutBackEvent(ob_display
, &ev
);
2805 gboolean
client_focus(ObClient
*self
)
2807 /* choose the correct target */
2808 self
= client_focus_target(self
);
2810 if (!client_can_focus(self
)) {
2811 if (!self
->frame
->visible
) {
2812 /* update the focus lists */
2813 focus_order_to_top(self
);
2818 if (self
->can_focus
) {
2819 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2820 I choose to use it always, hopefully to find errors quicker, if any
2821 are left. (I hate X. I hate focus events.)
2823 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2824 #799. So now it is RevertToNone again.
2826 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2830 if (self
->focus_notify
) {
2832 ce
.xclient
.type
= ClientMessage
;
2833 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2834 ce
.xclient
.display
= ob_display
;
2835 ce
.xclient
.window
= self
->window
;
2836 ce
.xclient
.format
= 32;
2837 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2838 ce
.xclient
.data
.l
[1] = event_lasttime
;
2839 ce
.xclient
.data
.l
[2] = 0l;
2840 ce
.xclient
.data
.l
[3] = 0l;
2841 ce
.xclient
.data
.l
[4] = 0l;
2842 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2846 ob_debug("%sively focusing %lx at %d\n",
2847 (self
->can_focus
? "act" : "pass"),
2848 self
->window
, (gint
) event_lasttime
);
2851 /* Cause the FocusIn to come back to us. Important for desktop switches,
2852 since otherwise we'll have no FocusIn on the queue and send it off to
2853 the focus_backup. */
2854 XSync(ob_display
, FALSE
);
2858 void client_unfocus(ObClient
*self
)
2860 if (focus_client
== self
) {
2862 ob_debug("client_unfocus for %lx\n", self
->window
);
2864 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2868 void client_activate(ObClient
*self
, gboolean here
)
2870 /* This check is for the client_list_menu trying to activate
2871 * a closed client. */
2872 if (!g_list_find(client_list
, self
)) return;
2873 if (client_normal(self
) && screen_showing_desktop
)
2874 screen_show_desktop(FALSE
);
2876 client_iconify(self
, FALSE
, here
);
2877 if (self
->desktop
!= DESKTOP_ALL
&&
2878 self
->desktop
!= screen_desktop
) {
2880 client_set_desktop(self
, screen_desktop
, FALSE
);
2882 screen_set_desktop(self
->desktop
);
2883 } else if (!self
->frame
->visible
)
2884 /* if its not visible for other reasons, then don't mess
2888 client_shade(self
, FALSE
);
2892 /* we do this an action here. this is rather important. this is because
2893 we want the results from the focus change to take place BEFORE we go
2894 about raising the window. when a fullscreen window loses focus, we need
2895 this or else the raise wont be able to raise above the to-lose-focus
2896 fullscreen window. */
2900 void client_raise(ObClient
*self
)
2902 action_run_string("Raise", self
);
2905 void client_lower(ObClient
*self
)
2907 action_run_string("Lower", self
);
2910 gboolean
client_focused(ObClient
*self
)
2912 return self
== focus_client
;
2915 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
2918 /* si is the smallest image >= req */
2919 /* li is the largest image < req */
2920 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2922 if (!self
->nicons
) {
2923 ObClientIcon
*parent
= NULL
;
2925 if (self
->transient_for
) {
2926 if (self
->transient_for
!= OB_TRAN_GROUP
)
2927 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2930 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2931 ObClient
*c
= it
->data
;
2932 if (c
!= self
&& !c
->transient_for
) {
2933 if ((parent
= client_icon_recursive(c
, w
, h
)))
2943 for (i
= 0; i
< self
->nicons
; ++i
) {
2944 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2945 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2949 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2954 if (largest
== 0) /* didnt find one smaller than the requested size */
2955 return &self
->icons
[si
];
2956 return &self
->icons
[li
];
2959 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
2962 static ObClientIcon deficon
;
2964 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
2965 deficon
.width
= deficon
.height
= 48;
2966 deficon
.data
= ob_rr_theme
->def_win_icon
;
2972 /* this be mostly ripped from fvwm */
2973 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2975 gint my_cx
, my_cy
, his_cx
, his_cy
;
2978 gint score
, best_score
;
2979 ObClient
*best_client
, *cur
;
2985 /* first, find the centre coords of the currently focused window */
2986 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2987 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2992 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
2995 /* the currently selected window isn't interesting */
2998 if (!client_normal(cur
))
3000 /* using c->desktop instead of screen_desktop doesn't work if the
3001 * current window was omnipresent, hope this doesn't have any other
3003 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3007 if(client_focus_target(cur
) == cur
&&
3008 !(cur
->can_focus
|| cur
->focus_notify
))
3011 /* find the centre coords of this window, from the
3012 * currently focused window's point of view */
3013 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3014 + cur
->frame
->area
.width
/ 2;
3015 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3016 + cur
->frame
->area
.height
/ 2;
3018 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3019 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3021 /* Rotate the diagonals 45 degrees counterclockwise.
3022 * To do this, multiply the matrix /+h +h\ with the
3023 * vector (x y). \-h +h/
3024 * h = sqrt(0.5). We can set h := 1 since absolute
3025 * distance doesn't matter here. */
3026 tx
= his_cx
+ his_cy
;
3027 his_cy
= -his_cx
+ his_cy
;
3032 case OB_DIRECTION_NORTH
:
3033 case OB_DIRECTION_SOUTH
:
3034 case OB_DIRECTION_NORTHEAST
:
3035 case OB_DIRECTION_SOUTHWEST
:
3036 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3037 distance
= ((dir
== OB_DIRECTION_NORTH
||
3038 dir
== OB_DIRECTION_NORTHEAST
) ?
3041 case OB_DIRECTION_EAST
:
3042 case OB_DIRECTION_WEST
:
3043 case OB_DIRECTION_SOUTHEAST
:
3044 case OB_DIRECTION_NORTHWEST
:
3045 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3046 distance
= ((dir
== OB_DIRECTION_WEST
||
3047 dir
== OB_DIRECTION_NORTHWEST
) ?
3052 /* the target must be in the requested direction */
3056 /* Calculate score for this window. The smaller the better. */
3057 score
= distance
+ offset
;
3059 /* windows more than 45 degrees off the direction are
3060 * heavily penalized and will only be chosen if nothing
3061 * else within a million pixels */
3062 if(offset
> distance
)
3065 if(best_score
== -1 || score
< best_score
)
3073 void client_set_layer(ObClient
*self
, gint layer
)
3077 self
->above
= FALSE
;
3078 } else if (layer
== 0) {
3079 self
->below
= self
->above
= FALSE
;
3081 self
->below
= FALSE
;
3084 client_calc_layer(self
);
3085 client_change_state(self
); /* reflect this in the state hints */
3088 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3090 if (self
->undecorated
!= undecorated
) {
3091 self
->undecorated
= undecorated
;
3092 client_setup_decor_and_functions(self
);
3093 /* Make sure the client knows it might have moved. Maybe there is a
3094 * better way of doing this so only one client_configure is sent, but
3095 * since 125 of these are sent per second when moving the window (with
3096 * user = FALSE) i doubt it matters much.
3098 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3099 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3100 client_change_state(self
); /* reflect this in the state hints */
3104 /* Determines which physical monitor a client is on by calculating the
3105 area of the part of the client on each monitor. The number of the
3106 monitor containing the greatest area of the client is returned.*/
3107 guint
client_monitor(ObClient
*self
)
3113 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3114 Rect
*area
= screen_physical_area_monitor(i
);
3115 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3119 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3120 v
= r
.width
* r
.height
;
3131 ObClient
*client_search_top_transient(ObClient
*self
)
3133 /* move up the transient chain as far as possible */
3134 if (self
->transient_for
) {
3135 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3136 return client_search_top_transient(self
->transient_for
);
3140 g_assert(self
->group
);
3142 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3143 ObClient
*c
= it
->data
;
3145 /* checking transient_for prevents infinate loops! */
3146 if (c
!= self
&& !c
->transient_for
)
3157 ObClient
*client_search_focus_parent(ObClient
*self
)
3159 if (self
->transient_for
) {
3160 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3161 if (client_focused(self
->transient_for
))
3162 return self
->transient_for
;
3166 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3167 ObClient
*c
= it
->data
;
3169 /* checking transient_for prevents infinate loops! */
3170 if (c
!= self
&& !c
->transient_for
)
3171 if (client_focused(c
))
3180 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3182 if (self
->transient_for
) {
3183 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3184 if (self
->transient_for
== search
)
3189 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3190 ObClient
*c
= it
->data
;
3192 /* checking transient_for prevents infinate loops! */
3193 if (c
!= self
&& !c
->transient_for
)
3203 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3207 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3208 if (sit
->data
== search
)
3210 if (client_search_transient(sit
->data
, search
))
3216 void client_update_sm_client_id(ObClient
*self
)
3218 g_free(self
->sm_client_id
);
3219 self
->sm_client_id
= NULL
;
3221 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3223 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3224 &self
->sm_client_id
);
3227 /* finds the nearest edge in the given direction from the current client
3228 * note to self: the edge is the -frame- edge (the actual one), not the
3231 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
)
3233 gint dest
, monitor_dest
;
3234 gint my_edge_start
, my_edge_end
, my_offset
;
3241 a
= screen_area(c
->desktop
);
3242 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3245 case OB_DIRECTION_NORTH
:
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
;
3250 /* default: top of screen */
3252 monitor_dest
= monitor
->y
;
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
+ cur
->frame
->area
.height
;
3277 if(his_offset
+ 1 > my_offset
)
3280 if(his_offset
< dest
)
3283 if(his_edge_start
>= my_edge_start
&&
3284 his_edge_start
<= my_edge_end
)
3287 if(my_edge_start
>= his_edge_start
&&
3288 my_edge_start
<= his_edge_end
)
3293 case OB_DIRECTION_SOUTH
:
3294 my_edge_start
= c
->frame
->area
.x
;
3295 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3296 my_offset
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3298 /* default: bottom of screen */
3299 dest
= a
->y
+ a
->height
;
3300 monitor_dest
= monitor
->y
+ monitor
->height
;
3301 /* if the monitor edge comes before the screen edge, */
3302 /* use that as the destination instead. (For xinerama) */
3303 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3304 dest
= monitor_dest
;
3306 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3307 gint his_edge_start
, his_edge_end
, his_offset
;
3308 ObClient
*cur
= it
->data
;
3312 if(!client_normal(cur
))
3314 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3318 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3321 his_edge_start
= cur
->frame
->area
.x
;
3322 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3323 his_offset
= cur
->frame
->area
.y
;
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
)
3342 case OB_DIRECTION_WEST
:
3343 my_edge_start
= c
->frame
->area
.y
;
3344 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3345 my_offset
= c
->frame
->area
.x
;
3347 /* default: leftmost egde of screen */
3349 monitor_dest
= monitor
->x
;
3350 /* if the monitor edge comes before the screen edge, */
3351 /* use that as the destination instead. (For xinerama) */
3352 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3353 dest
= monitor_dest
;
3355 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3356 gint his_edge_start
, his_edge_end
, his_offset
;
3357 ObClient
*cur
= it
->data
;
3361 if(!client_normal(cur
))
3363 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3367 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3370 his_edge_start
= cur
->frame
->area
.y
;
3371 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3372 his_offset
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3374 if(his_offset
+ 1 > my_offset
)
3377 if(his_offset
< dest
)
3380 if(his_edge_start
>= my_edge_start
&&
3381 his_edge_start
<= my_edge_end
)
3384 if(my_edge_start
>= his_edge_start
&&
3385 my_edge_start
<= his_edge_end
)
3391 case OB_DIRECTION_EAST
:
3392 my_edge_start
= c
->frame
->area
.y
;
3393 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3394 my_offset
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3396 /* default: rightmost edge of screen */
3397 dest
= a
->x
+ a
->width
;
3398 monitor_dest
= monitor
->x
+ monitor
->width
;
3399 /* if the monitor edge comes before the screen edge, */
3400 /* use that as the destination instead. (For xinerama) */
3401 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3402 dest
= monitor_dest
;
3404 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3405 gint his_edge_start
, his_edge_end
, his_offset
;
3406 ObClient
*cur
= it
->data
;
3410 if(!client_normal(cur
))
3412 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3416 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3419 his_edge_start
= cur
->frame
->area
.y
;
3420 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3421 his_offset
= cur
->frame
->area
.x
;
3423 if(his_offset
- 1 < my_offset
)
3426 if(his_offset
> dest
)
3429 if(his_edge_start
>= my_edge_start
&&
3430 his_edge_start
<= my_edge_end
)
3433 if(my_edge_start
>= his_edge_start
&&
3434 my_edge_start
<= his_edge_end
)
3439 case OB_DIRECTION_NORTHEAST
:
3440 case OB_DIRECTION_SOUTHEAST
:
3441 case OB_DIRECTION_NORTHWEST
:
3442 case OB_DIRECTION_SOUTHWEST
:
3443 /* not implemented */
3445 g_assert_not_reached();
3446 dest
= 0; /* suppress warning */
3451 ObClient
* client_under_pointer()
3455 ObClient
*ret
= NULL
;
3457 if (screen_pointer_pos(&x
, &y
)) {
3458 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3459 if (WINDOW_IS_CLIENT(it
->data
)) {
3460 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3461 if (c
->frame
->visible
&&
3462 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3472 gboolean
client_has_group_siblings(ObClient
*self
)
3474 return self
->group
&& self
->group
->members
->next
;