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 if (!app
->role
|| !strcmp(app
->role
, client
->role
))
227 void client_manage(Window window
)
231 XWindowAttributes attrib
;
232 XSetWindowAttributes attrib_set
;
234 gboolean activate
= FALSE
;
235 ObAppSettings
*settings
;
239 /* check if it has already been unmapped by the time we started mapping
240 the grab does a sync so we don't have to here */
241 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
242 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
)) {
243 XPutBackEvent(ob_display
, &e
);
246 return; /* don't manage it */
249 /* make sure it isn't an override-redirect window */
250 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
251 attrib
.override_redirect
) {
253 return; /* don't manage it */
256 /* is the window a docking app */
257 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
258 if ((wmhint
->flags
& StateHint
) &&
259 wmhint
->initial_state
== WithdrawnState
) {
260 dock_add(window
, wmhint
);
268 ob_debug("Managing window: %lx\n", window
);
270 /* choose the events we want to receive on the CLIENT window */
271 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
272 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
273 XChangeWindowAttributes(ob_display
, window
,
274 CWEventMask
|CWDontPropagate
, &attrib_set
);
277 /* create the ObClient struct, and populate it from the hints on the
279 self
= g_new0(ObClient
, 1);
280 self
->obwin
.type
= Window_Client
;
281 self
->window
= window
;
283 /* non-zero defaults */
284 self
->title_count
= 1;
285 self
->wmstate
= NormalState
;
287 self
->desktop
= screen_num_desktops
; /* always an invalid value */
289 client_get_all(self
);
290 client_restore_session_state(self
);
292 sn_app_started(self
->class);
294 /* update the focus lists, do this before the call to change_state or
295 it can end up in the list twice! */
296 focus_order_add_new(self
);
298 client_change_state(self
);
300 /* remove the client's border (and adjust re gravity) */
301 client_toggle_border(self
, FALSE
);
303 /* specify that if we exit, the window should not be destroyed and should
304 be reparented back to root automatically */
305 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
307 /* create the decoration frame for the client window */
308 self
->frame
= frame_new();
310 frame_grab_client(self
->frame
, self
);
314 client_apply_startup_state(self
);
316 /* get and set application level settings */
317 settings
= get_settings(self
);
320 if (settings
->shade
&& !settings
->decor
)
321 settings
->decor
= TRUE
;
323 client_shade(self
, settings
->shade
);
324 client_set_undecorated(self
, !settings
->decor
);
326 if (settings
->desktop
!= -1)
327 client_set_desktop(self
, settings
->desktop
, FALSE
);
329 client_set_layer(self
, settings
->layer
);
332 stacking_add(CLIENT_AS_WINDOW(self
));
333 client_restore_session_stacking(self
);
335 /* focus the new window? */
336 if (ob_state() != OB_STATE_STARTING
&&
337 (config_focus_new
|| client_search_focus_parent(self
)) ||
338 (settings
&& settings
->focus
) &&
339 /* note the check against Type_Normal/Dialog, not client_normal(self),
340 which would also include other types. in this case we want more
341 strict rules for focus */
342 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
343 self
->type
== OB_CLIENT_TYPE_DIALOG
))
347 if (self
->desktop
!= screen_desktop
) {
348 /* activate the window */
351 gboolean group_foc
= FALSE
;
356 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
358 if (client_focused(it
->data
))
366 (!self
->transient_for
&& (!self
->group
||
367 !self
->group
->members
->next
))) ||
368 client_search_focus_tree_full(self
) ||
370 !client_normal(focus_client
))
372 /* activate the window */
379 if (ob_state() == OB_STATE_RUNNING
) {
380 gint x
= self
->area
.x
, ox
= x
;
381 gint y
= self
->area
.y
, oy
= y
;
383 place_client(self
, &x
, &y
, settings
);
385 /* make sure the window is visible. */
386 client_find_onscreen(self
, &x
, &y
,
387 self
->frame
->area
.width
,
388 self
->frame
->area
.height
,
389 /* non-normal clients has less rules, and
390 windows that are being restored from a
391 session do also. we can assume you want
392 it back where you saved it. Clients saying
393 they placed themselves are subjected to
394 harder rules, ones that are placed by
395 place.c or by the user are allowed partially
396 off-screen and on xinerama divides (ie,
397 it is up to the placement routines to avoid
398 the xinerama divides) */
399 ((self
->positioned
& PPosition
) &&
400 !(self
->positioned
& USPosition
)) &&
401 client_normal(self
) &&
403 if (x
!= ox
|| y
!= oy
)
404 client_move(self
, x
, y
);
407 keyboard_grab_for_client(self
, TRUE
);
408 mouse_grab_for_client(self
, TRUE
);
410 client_showhide(self
);
412 /* use client_focus instead of client_activate cuz client_activate does
413 stuff like switch desktops etc and I'm not interested in all that when
414 a window maps since its not based on an action from the user like
415 clicking a window to activate is. so keep the new window out of the way
418 /* if using focus_delay, stop the timer now so that focus doesn't go
420 event_halt_focus_delay();
423 /* since focus can change the stacking orders, if we focus the window
424 then the standard raise it gets is not enough, we need to queue one
425 for after the focus change takes place */
429 /* client_activate does this but we aret using it so we have to do it
431 if (screen_showing_desktop
)
432 screen_show_desktop(FALSE
);
434 /* add to client list/map */
435 client_list
= g_list_append(client_list
, self
);
436 g_hash_table_insert(window_map
, &self
->window
, self
);
438 /* this has to happen after we're in the client_list */
439 screen_update_areas();
441 /* update the list hints */
444 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
447 void client_unmanage_all()
449 while (client_list
!= NULL
)
450 client_unmanage(client_list
->data
);
453 void client_unmanage(ObClient
*self
)
458 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
460 g_assert(self
!= NULL
);
462 keyboard_grab_for_client(self
, FALSE
);
463 mouse_grab_for_client(self
, FALSE
);
465 /* potentially fix focusLast */
466 if (config_focus_last
)
467 grab_pointer(TRUE
, OB_CURSOR_NONE
);
469 /* remove the window from our save set */
470 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
472 /* we dont want events no more */
473 XSelectInput(ob_display
, self
->window
, NoEventMask
);
475 frame_hide(self
->frame
);
477 client_list
= g_list_remove(client_list
, self
);
478 stacking_remove(self
);
479 g_hash_table_remove(window_map
, &self
->window
);
481 /* update the focus lists */
482 focus_order_remove(self
);
484 /* once the client is out of the list, update the struts to remove it's
486 screen_update_areas();
488 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
489 Destructor
*d
= it
->data
;
490 d
->func(self
, d
->data
);
493 if (focus_client
== self
) {
496 /* focus the last focused window on the desktop, and ignore enter
497 events from the unmap so it doesnt mess with the focus */
498 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
499 /* remove these flags so we don't end up getting focused in the
501 self
->can_focus
= FALSE
;
502 self
->focus_notify
= FALSE
;
504 client_unfocus(self
);
507 /* tell our parent(s) that we're gone */
508 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
509 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
510 if (it
->data
!= self
)
511 ((ObClient
*)it
->data
)->transients
=
512 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
513 } else if (self
->transient_for
) { /* transient of window */
514 self
->transient_for
->transients
=
515 g_slist_remove(self
->transient_for
->transients
, self
);
518 /* tell our transients that we're gone */
519 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
520 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
521 ((ObClient
*)it
->data
)->transient_for
= NULL
;
522 client_calc_layer(it
->data
);
526 /* remove from its group */
528 group_remove(self
->group
, self
);
532 /* give the client its border back */
533 client_toggle_border(self
, TRUE
);
535 /* reparent the window out of the frame, and free the frame */
536 frame_release_client(self
->frame
, self
);
539 if (ob_state() != OB_STATE_EXITING
) {
540 /* these values should not be persisted across a window
542 PROP_ERASE(self
->window
, net_wm_desktop
);
543 PROP_ERASE(self
->window
, net_wm_state
);
544 PROP_ERASE(self
->window
, wm_state
);
546 /* if we're left in an unmapped state, the client wont be mapped. this
547 is bad, since we will no longer be managing the window on restart */
548 XMapWindow(ob_display
, self
->window
);
552 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
554 /* free all data allocated in the client struct */
555 g_slist_free(self
->transients
);
556 for (j
= 0; j
< self
->nicons
; ++j
)
557 g_free(self
->icons
[j
].data
);
558 if (self
->nicons
> 0)
561 g_free(self
->icon_title
);
565 g_free(self
->sm_client_id
);
568 /* update the list hints */
571 if (config_focus_last
)
572 grab_pointer(FALSE
, OB_CURSOR_NONE
);
575 static void client_urgent_notify(ObClient
*self
)
578 frame_flash_start(self
->frame
);
580 frame_flash_stop(self
->frame
);
583 static void client_restore_session_state(ObClient
*self
)
587 if (!(it
= session_state_find(self
)))
590 self
->session
= it
->data
;
592 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
593 self
->positioned
= PPosition
;
594 if (self
->session
->w
> 0)
595 self
->area
.width
= self
->session
->w
;
596 if (self
->session
->h
> 0)
597 self
->area
.height
= self
->session
->h
;
598 XResizeWindow(ob_display
, self
->window
,
599 self
->area
.width
, self
->area
.height
);
601 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
602 self
->session
->desktop
:
603 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
604 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
606 self
->shaded
= self
->session
->shaded
;
607 self
->iconic
= self
->session
->iconic
;
608 self
->skip_pager
= self
->session
->skip_pager
;
609 self
->skip_taskbar
= self
->session
->skip_taskbar
;
610 self
->fullscreen
= self
->session
->fullscreen
;
611 self
->above
= self
->session
->above
;
612 self
->below
= self
->session
->below
;
613 self
->max_horz
= self
->session
->max_horz
;
614 self
->max_vert
= self
->session
->max_vert
;
617 static void client_restore_session_stacking(ObClient
*self
)
621 if (!self
->session
) return;
623 it
= g_list_find(session_saved_state
, self
->session
);
624 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
627 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
628 if (session_state_cmp(it
->data
, cit
->data
))
631 client_calc_layer(self
);
632 stacking_below(CLIENT_AS_WINDOW(self
),
633 CLIENT_AS_WINDOW(cit
->data
));
639 void client_move_onscreen(ObClient
*self
, gboolean rude
)
641 gint x
= self
->area
.x
;
642 gint y
= self
->area
.y
;
643 if (client_find_onscreen(self
, &x
, &y
,
644 self
->frame
->area
.width
,
645 self
->frame
->area
.height
, rude
)) {
646 client_move(self
, x
, y
);
650 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
654 gint ox
= *x
, oy
= *y
;
656 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
659 /* XXX watch for xinerama dead areas */
660 /* This makes sure windows aren't entirely outside of the screen so you
661 * can't see them at all */
662 if (client_normal(self
)) {
663 a
= screen_area(self
->desktop
);
664 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
665 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
666 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
667 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
668 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
670 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
674 /* This here doesn't let windows even a pixel outside the screen,
675 * when called from client_manage, programs placing themselves are
676 * forced completely onscreen, while things like
677 * xterm -geometry resolution-width/2 will work fine. Trying to
678 * place it completely offscreen will be handled in the above code.
679 * Sorry for this confused comment, i am tired. */
681 /* avoid the xinerama monitor divide while we're at it,
682 * remember to fix the placement stuff to avoid it also and
683 * then remove this XXX */
684 a
= screen_physical_area_monitor(client_monitor(self
));
685 /* dont let windows map/move into the strut unless they
686 are bigger than the available area */
688 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
689 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
690 *x
= a
->x
+ a
->width
- w
;
692 if (h
<= a
->height
) {
693 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
694 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
695 *y
= a
->y
+ a
->height
- h
;
699 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
702 return ox
!= *x
|| oy
!= *y
;
705 static void client_toggle_border(ObClient
*self
, gboolean show
)
707 /* adjust our idea of where the client is, based on its border. When the
708 border is removed, the client should now be considered to be in a
710 when re-adding the border to the client, the same operation needs to be
712 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
713 gint x
= oldx
, y
= oldy
;
714 switch(self
->gravity
) {
716 case NorthWestGravity
:
718 case SouthWestGravity
:
720 case NorthEastGravity
:
722 case SouthEastGravity
:
723 if (show
) x
-= self
->border_width
* 2;
724 else x
+= self
->border_width
* 2;
731 if (show
) x
-= self
->border_width
;
732 else x
+= self
->border_width
;
735 switch(self
->gravity
) {
737 case NorthWestGravity
:
739 case NorthEastGravity
:
741 case SouthWestGravity
:
743 case SouthEastGravity
:
744 if (show
) y
-= self
->border_width
* 2;
745 else y
+= self
->border_width
* 2;
752 if (show
) y
-= self
->border_width
;
753 else y
+= self
->border_width
;
760 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
762 /* move the client so it is back it the right spot _with_ its
764 if (x
!= oldx
|| y
!= oldy
)
765 XMoveWindow(ob_display
, self
->window
, x
, y
);
767 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
771 static void client_get_all(ObClient
*self
)
773 client_get_area(self
);
774 client_update_transient_for(self
);
775 client_update_wmhints(self
);
776 client_get_startup_id(self
);
777 client_get_desktop(self
);
778 client_get_shaped(self
);
780 client_get_mwm_hints(self
);
781 client_get_type(self
);/* this can change the mwmhints for special cases */
783 /* The transient hint is used to pick a type, but the type can also affect
784 transiency (dialogs are always made transients). This is Havoc's idea,
785 but it is needed to make some apps work right (eg tsclient). */
786 client_update_transient_for(self
);
788 client_get_state(self
);
791 /* a couple type-based defaults for new windows */
793 /* this makes sure that these windows appear on all desktops */
794 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
795 self
->desktop
= DESKTOP_ALL
;
798 client_update_protocols(self
);
800 client_get_gravity(self
); /* get the attribute gravity */
801 client_update_normal_hints(self
); /* this may override the attribute
804 /* got the type, the mwmhints, the protocols, and the normal hints
805 (min/max sizes), so we're ready to set up the decorations/functions */
806 client_setup_decor_and_functions(self
);
808 client_update_title(self
);
809 client_update_class(self
);
810 client_update_sm_client_id(self
);
811 client_update_strut(self
);
812 client_update_icons(self
);
815 static void client_get_startup_id(ObClient
*self
)
817 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
819 PROP_GETS(self
->group
->leader
,
820 net_startup_id
, utf8
, &self
->startup_id
);
823 static void client_get_area(ObClient
*self
)
825 XWindowAttributes wattrib
;
828 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
829 g_assert(ret
!= BadWindow
);
831 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
832 self
->border_width
= wattrib
.border_width
;
835 static void client_get_desktop(ObClient
*self
)
837 guint32 d
= screen_num_desktops
; /* an always-invalid value */
839 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
840 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
841 self
->desktop
= screen_num_desktops
- 1;
845 gboolean trdesk
= FALSE
;
847 if (self
->transient_for
) {
848 if (self
->transient_for
!= OB_TRAN_GROUP
) {
849 self
->desktop
= self
->transient_for
->desktop
;
854 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
855 if (it
->data
!= self
&&
856 !((ObClient
*)it
->data
)->transient_for
) {
857 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
864 /* try get from the startup-notification protocol */
865 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
866 if (self
->desktop
>= screen_num_desktops
&&
867 self
->desktop
!= DESKTOP_ALL
)
868 self
->desktop
= screen_num_desktops
- 1;
870 /* defaults to the current desktop */
871 self
->desktop
= screen_desktop
;
874 if (self
->desktop
!= d
) {
875 /* set the desktop hint, to make sure that it always exists */
876 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
880 static void client_get_state(ObClient
*self
)
885 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
887 for (i
= 0; i
< num
; ++i
) {
888 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
890 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
892 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
894 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
895 self
->skip_taskbar
= TRUE
;
896 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
897 self
->skip_pager
= TRUE
;
898 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
899 self
->fullscreen
= TRUE
;
900 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
901 self
->max_vert
= TRUE
;
902 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
903 self
->max_horz
= TRUE
;
904 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
906 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
908 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
909 self
->undecorated
= TRUE
;
915 if (!(self
->above
|| self
->below
)) {
917 /* apply stuff from the group */
921 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
922 ObClient
*c
= it
->data
;
923 if (c
!= self
&& !client_search_transient(self
, c
) &&
924 client_normal(self
) && client_normal(c
))
927 (c
->above
? 1 : (c
->below
? -1 : 0)));
941 g_assert_not_reached();
948 static void client_get_shaped(ObClient
*self
)
950 self
->shaped
= FALSE
;
952 if (extensions_shape
) {
957 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
959 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
960 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
962 self
->shaped
= (s
!= 0);
967 void client_update_transient_for(ObClient
*self
)
970 ObClient
*target
= NULL
;
972 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
973 self
->transient
= TRUE
;
974 if (t
!= self
->window
) { /* cant be transient to itself! */
975 target
= g_hash_table_lookup(window_map
, &t
);
976 /* if this happens then we need to check for it*/
977 g_assert(target
!= self
);
978 if (target
&& !WINDOW_IS_CLIENT(target
)) {
979 /* this can happen when a dialog is a child of
980 a dockapp, for example */
984 if (!target
&& self
->group
) {
985 /* not transient to a client, see if it is transient for a
987 if (t
== self
->group
->leader
||
989 t
== RootWindow(ob_display
, ob_screen
))
991 /* window is a transient for its group! */
992 target
= OB_TRAN_GROUP
;
996 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
997 self
->transient
= TRUE
;
998 target
= OB_TRAN_GROUP
;
1000 self
->transient
= FALSE
;
1002 /* if anything has changed... */
1003 if (target
!= self
->transient_for
) {
1004 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1007 /* remove from old parents */
1008 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1009 ObClient
*c
= it
->data
;
1010 if (c
!= self
&& !c
->transient_for
)
1011 c
->transients
= g_slist_remove(c
->transients
, self
);
1013 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1014 /* remove from old parent */
1015 self
->transient_for
->transients
=
1016 g_slist_remove(self
->transient_for
->transients
, self
);
1018 self
->transient_for
= target
;
1019 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1022 /* add to new parents */
1023 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1024 ObClient
*c
= it
->data
;
1025 if (c
!= self
&& !c
->transient_for
)
1026 c
->transients
= g_slist_append(c
->transients
, self
);
1029 /* remove all transients which are in the group, that causes
1030 circlular pointer hell of doom */
1031 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1033 for (sit
= self
->transients
; sit
; sit
= next
) {
1034 next
= g_slist_next(sit
);
1035 if (sit
->data
== it
->data
)
1037 g_slist_delete_link(self
->transients
, sit
);
1040 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1041 /* add to new parent */
1042 self
->transient_for
->transients
=
1043 g_slist_append(self
->transient_for
->transients
, self
);
1048 static void client_get_mwm_hints(ObClient
*self
)
1053 self
->mwmhints
.flags
= 0; /* default to none */
1055 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1057 if (num
>= OB_MWM_ELEMENTS
) {
1058 self
->mwmhints
.flags
= hints
[0];
1059 self
->mwmhints
.functions
= hints
[1];
1060 self
->mwmhints
.decorations
= hints
[2];
1066 void client_get_type(ObClient
*self
)
1073 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1074 /* use the first value that we know about in the array */
1075 for (i
= 0; i
< num
; ++i
) {
1076 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1077 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1078 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1079 self
->type
= OB_CLIENT_TYPE_DOCK
;
1080 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1081 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1082 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1083 self
->type
= OB_CLIENT_TYPE_MENU
;
1084 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1085 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1086 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1087 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1088 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1089 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1090 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1091 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1092 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1093 /* prevent this window from getting any decor or
1095 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1096 OB_MWM_FLAG_DECORATIONS
);
1097 self
->mwmhints
.decorations
= 0;
1098 self
->mwmhints
.functions
= 0;
1100 if (self
->type
!= (ObClientType
) -1)
1101 break; /* grab the first legit type */
1106 if (self
->type
== (ObClientType
) -1) {
1107 /*the window type hint was not set, which means we either classify
1108 ourself as a normal window or a dialog, depending on if we are a
1110 if (self
->transient
)
1111 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1113 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1117 void client_update_protocols(ObClient
*self
)
1120 guint num_return
, i
;
1122 self
->focus_notify
= FALSE
;
1123 self
->delete_window
= FALSE
;
1125 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1126 for (i
= 0; i
< num_return
; ++i
) {
1127 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1128 /* this means we can request the window to close */
1129 self
->delete_window
= TRUE
;
1130 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1131 /* if this protocol is requested, then the window will be
1132 notified whenever we want it to receive focus */
1133 self
->focus_notify
= TRUE
;
1139 static void client_get_gravity(ObClient
*self
)
1141 XWindowAttributes wattrib
;
1144 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1145 g_assert(ret
!= BadWindow
);
1146 self
->gravity
= wattrib
.win_gravity
;
1149 void client_update_normal_hints(ObClient
*self
)
1153 gint oldgravity
= self
->gravity
;
1156 self
->min_ratio
= 0.0f
;
1157 self
->max_ratio
= 0.0f
;
1158 SIZE_SET(self
->size_inc
, 1, 1);
1159 SIZE_SET(self
->base_size
, 0, 0);
1160 SIZE_SET(self
->min_size
, 0, 0);
1161 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1163 /* get the hints from the window */
1164 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1165 /* normal windows can't request placement! har har
1166 if (!client_normal(self))
1168 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1170 if (size
.flags
& PWinGravity
) {
1171 self
->gravity
= size
.win_gravity
;
1173 /* if the client has a frame, i.e. has already been mapped and
1174 is changing its gravity */
1175 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1176 /* move our idea of the client's position based on its new
1178 self
->area
.x
= self
->frame
->area
.x
;
1179 self
->area
.y
= self
->frame
->area
.y
;
1180 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1184 if (size
.flags
& PAspect
) {
1185 if (size
.min_aspect
.y
)
1187 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1188 if (size
.max_aspect
.y
)
1190 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1193 if (size
.flags
& PMinSize
)
1194 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1196 if (size
.flags
& PMaxSize
)
1197 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1199 if (size
.flags
& PBaseSize
)
1200 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1202 if (size
.flags
& PResizeInc
)
1203 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1207 void client_setup_decor_and_functions(ObClient
*self
)
1209 /* start with everything (cept fullscreen) */
1211 (OB_FRAME_DECOR_TITLEBAR
|
1212 (ob_rr_theme
->show_handle
? OB_FRAME_DECOR_HANDLE
: 0) |
1213 OB_FRAME_DECOR_GRIPS
|
1214 OB_FRAME_DECOR_BORDER
|
1215 OB_FRAME_DECOR_ICON
|
1216 OB_FRAME_DECOR_ALLDESKTOPS
|
1217 OB_FRAME_DECOR_ICONIFY
|
1218 OB_FRAME_DECOR_MAXIMIZE
|
1219 OB_FRAME_DECOR_SHADE
|
1220 OB_FRAME_DECOR_CLOSE
);
1222 (OB_CLIENT_FUNC_RESIZE
|
1223 OB_CLIENT_FUNC_MOVE
|
1224 OB_CLIENT_FUNC_ICONIFY
|
1225 OB_CLIENT_FUNC_MAXIMIZE
|
1226 OB_CLIENT_FUNC_SHADE
|
1227 OB_CLIENT_FUNC_CLOSE
);
1229 if (!(self
->min_size
.width
< self
->max_size
.width
||
1230 self
->min_size
.height
< self
->max_size
.height
))
1231 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1233 switch (self
->type
) {
1234 case OB_CLIENT_TYPE_NORMAL
:
1235 /* normal windows retain all of the possible decorations and
1236 functionality, and are the only windows that you can fullscreen */
1237 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1240 case OB_CLIENT_TYPE_DIALOG
:
1241 case OB_CLIENT_TYPE_UTILITY
:
1242 /* these windows cannot be maximized */
1243 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1246 case OB_CLIENT_TYPE_MENU
:
1247 case OB_CLIENT_TYPE_TOOLBAR
:
1248 /* these windows get less functionality */
1249 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1252 case OB_CLIENT_TYPE_DESKTOP
:
1253 case OB_CLIENT_TYPE_DOCK
:
1254 case OB_CLIENT_TYPE_SPLASH
:
1255 /* none of these windows are manipulated by the window manager */
1256 self
->decorations
= 0;
1257 self
->functions
= 0;
1261 /* Mwm Hints are applied subtractively to what has already been chosen for
1262 decor and functionality */
1263 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1264 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1265 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1266 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1267 /* if the mwm hints request no handle or title, then all
1268 decorations are disabled */
1269 self
->decorations
= config_theme_keepborder
? OB_FRAME_DECOR_BORDER
: 0;
1273 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1274 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1275 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1276 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1277 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1278 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1279 /* dont let mwm hints kill any buttons
1280 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1281 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1282 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1283 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1285 /* dont let mwm hints kill the close button
1286 if (! (self->mwmhints.functions & MwmFunc_Close))
1287 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1291 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1292 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1293 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1294 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1295 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1296 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1298 /* can't maximize without moving/resizing */
1299 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1300 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1301 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1302 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1303 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1306 /* kill the handle on fully maxed windows */
1307 if (self
->max_vert
&& self
->max_horz
)
1308 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1310 /* finally, the user can have requested no decorations, which overrides
1311 everything (but doesnt give it a border if it doesnt have one) */
1312 if (self
->undecorated
) {
1313 if (config_theme_keepborder
)
1314 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1316 self
->decorations
= 0;
1319 /* if we don't have a titlebar, then we cannot shade! */
1320 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1321 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1323 /* now we need to check against rules for the client's current state */
1324 if (self
->fullscreen
) {
1325 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1326 OB_CLIENT_FUNC_FULLSCREEN
|
1327 OB_CLIENT_FUNC_ICONIFY
);
1328 self
->decorations
= 0;
1331 client_change_allowed_actions(self
);
1334 /* adjust the client's decorations, etc. */
1335 client_reconfigure(self
);
1339 static void client_change_allowed_actions(ObClient
*self
)
1344 /* desktop windows are kept on all desktops */
1345 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1346 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1348 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1349 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1350 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1351 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1352 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1353 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1354 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1355 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1356 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1357 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1358 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1359 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1360 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1361 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1362 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1365 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1367 /* make sure the window isn't breaking any rules now */
1369 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1370 if (self
->frame
) client_shade(self
, FALSE
);
1371 else self
->shaded
= FALSE
;
1373 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1374 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1375 else self
->iconic
= FALSE
;
1377 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1378 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1379 else self
->fullscreen
= FALSE
;
1381 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1383 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1384 else self
->max_vert
= self
->max_horz
= FALSE
;
1388 void client_reconfigure(ObClient
*self
)
1390 /* by making this pass FALSE for user, we avoid the emacs event storm where
1391 every configurenotify causes an update in its normal hints, i think this
1392 is generally what we want anyways... */
1393 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1394 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1397 void client_update_wmhints(ObClient
*self
)
1400 gboolean ur
= FALSE
;
1403 /* assume a window takes input if it doesnt specify */
1404 self
->can_focus
= TRUE
;
1406 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1407 if (hints
->flags
& InputHint
)
1408 self
->can_focus
= hints
->input
;
1410 /* only do this when first managing the window *AND* when we aren't
1412 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1413 if (hints
->flags
& StateHint
)
1414 self
->iconic
= hints
->initial_state
== IconicState
;
1416 if (hints
->flags
& XUrgencyHint
)
1419 if (!(hints
->flags
& WindowGroupHint
))
1420 hints
->window_group
= None
;
1422 /* did the group state change? */
1423 if (hints
->window_group
!=
1424 (self
->group
? self
->group
->leader
: None
)) {
1425 /* remove from the old group if there was one */
1426 if (self
->group
!= NULL
) {
1427 /* remove transients of the group */
1428 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1429 self
->transients
= g_slist_remove(self
->transients
,
1432 /* remove myself from parents in the group */
1433 if (self
->transient_for
== OB_TRAN_GROUP
) {
1434 for (it
= self
->group
->members
; it
;
1435 it
= g_slist_next(it
))
1437 ObClient
*c
= it
->data
;
1439 if (c
!= self
&& !c
->transient_for
)
1440 c
->transients
= g_slist_remove(c
->transients
,
1445 group_remove(self
->group
, self
);
1448 if (hints
->window_group
!= None
) {
1449 self
->group
= group_add(hints
->window_group
, self
);
1451 /* i can only have transients from the group if i am not
1453 if (!self
->transient_for
) {
1454 /* add other transients of the group that are already
1456 for (it
= self
->group
->members
; it
;
1457 it
= g_slist_next(it
))
1459 ObClient
*c
= it
->data
;
1460 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1462 g_slist_append(self
->transients
, c
);
1467 /* because the self->transient flag wont change from this call,
1468 we don't need to update the window's type and such, only its
1469 transient_for, and the transients lists of other windows in
1470 the group may be affected */
1471 client_update_transient_for(self
);
1474 /* the WM_HINTS can contain an icon */
1475 client_update_icons(self
);
1480 if (ur
!= self
->urgent
) {
1482 /* fire the urgent callback if we're mapped, otherwise, wait until
1483 after we're mapped */
1485 client_urgent_notify(self
);
1489 void client_update_title(ObClient
*self
)
1495 gboolean read_title
;
1498 old_title
= self
->title
;
1501 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1502 /* try old x stuff */
1503 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
)) {
1504 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1505 if (self
->transient
) {
1506 data
= g_strdup("");
1509 data
= g_strdup("Unnamed Window");
1513 /* did the title change? then reset the title_count */
1514 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1515 self
->title_count
= 1;
1517 /* look for duplicates and append a number */
1519 for (it
= client_list
; it
; it
= g_list_next(it
))
1520 if (it
->data
!= self
) {
1521 ObClient
*c
= it
->data
;
1522 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1523 nums
|= 1 << c
->title_count
;
1525 /* find first free number */
1526 for (i
= 1; i
<= 32; ++i
)
1527 if (!(nums
& (1 << i
))) {
1528 if (self
->title_count
== 1 || i
== 1)
1529 self
->title_count
= i
;
1532 /* dont display the number for the first window */
1533 if (self
->title_count
> 1) {
1535 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1540 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1545 frame_adjust_title(self
->frame
);
1549 /* update the icon title */
1551 g_free(self
->icon_title
);
1555 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1556 /* try old x stuff */
1557 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1558 data
= g_strdup(self
->title
);
1562 /* append the title count, dont display the number for the first window */
1563 if (read_title
&& self
->title_count
> 1) {
1564 gchar
*vdata
, *ndata
;
1565 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1566 vdata
= g_strconcat(data
, ndata
, NULL
);
1572 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1574 self
->icon_title
= data
;
1577 void client_update_class(ObClient
*self
)
1582 if (self
->name
) g_free(self
->name
);
1583 if (self
->class) g_free(self
->class);
1584 if (self
->role
) g_free(self
->role
);
1586 self
->name
= self
->class = self
->role
= NULL
;
1588 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1590 self
->name
= g_strdup(data
[0]);
1592 self
->class = g_strdup(data
[1]);
1597 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1600 if (self
->name
== NULL
) self
->name
= g_strdup("");
1601 if (self
->class == NULL
) self
->class = g_strdup("");
1602 if (self
->role
== NULL
) self
->role
= g_strdup("");
1605 void client_update_strut(ObClient
*self
)
1609 gboolean got
= FALSE
;
1612 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1616 STRUT_PARTIAL_SET(strut
,
1617 data
[0], data
[2], data
[1], data
[3],
1618 data
[4], data
[5], data
[8], data
[9],
1619 data
[6], data
[7], data
[10], data
[11]);
1625 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1631 /* use the screen's width/height */
1632 a
= screen_physical_area();
1634 STRUT_PARTIAL_SET(strut
,
1635 data
[0], data
[2], data
[1], data
[3],
1636 a
->y
, a
->y
+ a
->height
- 1,
1637 a
->x
, a
->x
+ a
->width
- 1,
1638 a
->y
, a
->y
+ a
->height
- 1,
1639 a
->x
, a
->x
+ a
->width
- 1);
1645 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1646 0, 0, 0, 0, 0, 0, 0, 0);
1648 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1649 self
->strut
= strut
;
1651 /* updating here is pointless while we're being mapped cuz we're not in
1652 the client list yet */
1654 screen_update_areas();
1658 void client_update_icons(ObClient
*self
)
1664 for (i
= 0; i
< self
->nicons
; ++i
)
1665 g_free(self
->icons
[i
].data
);
1666 if (self
->nicons
> 0)
1667 g_free(self
->icons
);
1670 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1671 /* figure out how many valid icons are in here */
1673 while (num
- i
> 2) {
1677 if (i
> num
|| w
*h
== 0) break;
1681 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1683 /* store the icons */
1685 for (j
= 0; j
< self
->nicons
; ++j
) {
1688 w
= self
->icons
[j
].width
= data
[i
++];
1689 h
= self
->icons
[j
].height
= data
[i
++];
1691 if (w
*h
== 0) continue;
1693 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1694 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1699 self
->icons
[j
].data
[t
] =
1700 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1701 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1702 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1703 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1709 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1710 kwm_win_icon
, &data
, &num
)) {
1713 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1714 xerror_set_ignore(TRUE
);
1715 if (!RrPixmapToRGBA(ob_rr_inst
,
1717 &self
->icons
[self
->nicons
-1].width
,
1718 &self
->icons
[self
->nicons
-1].height
,
1719 &self
->icons
[self
->nicons
-1].data
)) {
1720 g_free(&self
->icons
[self
->nicons
-1]);
1723 xerror_set_ignore(FALSE
);
1729 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1730 if (hints
->flags
& IconPixmapHint
) {
1732 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1733 xerror_set_ignore(TRUE
);
1734 if (!RrPixmapToRGBA(ob_rr_inst
,
1736 (hints
->flags
& IconMaskHint
?
1737 hints
->icon_mask
: None
),
1738 &self
->icons
[self
->nicons
-1].width
,
1739 &self
->icons
[self
->nicons
-1].height
,
1740 &self
->icons
[self
->nicons
-1].data
)){
1741 g_free(&self
->icons
[self
->nicons
-1]);
1744 xerror_set_ignore(FALSE
);
1751 frame_adjust_icon(self
->frame
);
1754 static void client_change_state(ObClient
*self
)
1757 gulong netstate
[11];
1760 state
[0] = self
->wmstate
;
1762 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1766 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1768 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1770 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1771 if (self
->skip_taskbar
)
1772 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1773 if (self
->skip_pager
)
1774 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1775 if (self
->fullscreen
)
1776 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1778 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1780 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1782 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1784 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1785 if (self
->undecorated
)
1786 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1787 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1789 client_calc_layer(self
);
1792 frame_adjust_state(self
->frame
);
1795 ObClient
*client_search_focus_tree(ObClient
*self
)
1800 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1801 if (client_focused(it
->data
)) return it
->data
;
1802 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1807 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1809 if (self
->transient_for
) {
1810 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1811 return client_search_focus_tree_full(self
->transient_for
);
1814 gboolean recursed
= FALSE
;
1816 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1817 if (!((ObClient
*)it
->data
)->transient_for
) {
1819 if ((c
= client_search_focus_tree_full(it
->data
)))
1828 /* this function checks the whole tree, the client_search_focus_tree~
1829 does not, so we need to check this window */
1830 if (client_focused(self
))
1832 return client_search_focus_tree(self
);
1835 static ObStackingLayer
calc_layer(ObClient
*self
)
1839 if (self
->fullscreen
&&
1840 (client_focused(self
) || client_search_focus_tree(self
)))
1841 l
= OB_STACKING_LAYER_FULLSCREEN
;
1842 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1843 l
= OB_STACKING_LAYER_DESKTOP
;
1844 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1845 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1846 else l
= OB_STACKING_LAYER_ABOVE
;
1848 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1849 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1850 else l
= OB_STACKING_LAYER_NORMAL
;
1855 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1856 ObStackingLayer l
, gboolean raised
)
1858 ObStackingLayer old
, own
;
1862 own
= calc_layer(self
);
1863 self
->layer
= l
> own
? l
: own
;
1865 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1866 client_calc_layer_recursive(it
->data
, orig
,
1867 l
, raised
? raised
: l
!= old
);
1869 if (!raised
&& l
!= old
)
1870 if (orig
->frame
) { /* only restack if the original window is managed */
1871 stacking_remove(CLIENT_AS_WINDOW(self
));
1872 stacking_add(CLIENT_AS_WINDOW(self
));
1876 void client_calc_layer(ObClient
*self
)
1883 /* transients take on the layer of their parents */
1884 self
= client_search_top_transient(self
);
1886 l
= calc_layer(self
);
1888 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1891 gboolean
client_should_show(ObClient
*self
)
1895 if (client_normal(self
) && screen_showing_desktop
)
1898 if (self->transient_for) {
1899 if (self->transient_for != OB_TRAN_GROUP)
1900 return client_should_show(self->transient_for);
1904 for (it = self->group->members; it; it = g_slist_next(it)) {
1905 ObClient *c = it->data;
1906 if (c != self && !c->transient_for) {
1907 if (client_should_show(c))
1914 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
1920 static void client_showhide(ObClient
*self
)
1923 if (client_should_show(self
))
1924 frame_show(self
->frame
);
1926 frame_hide(self
->frame
);
1929 gboolean
client_normal(ObClient
*self
) {
1930 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1931 self
->type
== OB_CLIENT_TYPE_DOCK
||
1932 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1935 static void client_apply_startup_state(ObClient
*self
)
1937 /* these are in a carefully crafted order.. */
1940 self
->iconic
= FALSE
;
1941 client_iconify(self
, TRUE
, FALSE
);
1943 if (self
->fullscreen
) {
1944 self
->fullscreen
= FALSE
;
1945 client_fullscreen(self
, TRUE
, FALSE
);
1947 if (self
->undecorated
) {
1948 self
->undecorated
= FALSE
;
1949 client_set_undecorated(self
, TRUE
);
1952 self
->shaded
= FALSE
;
1953 client_shade(self
, TRUE
);
1956 client_urgent_notify(self
);
1958 if (self
->max_vert
&& self
->max_horz
) {
1959 self
->max_vert
= self
->max_horz
= FALSE
;
1960 client_maximize(self
, TRUE
, 0, FALSE
);
1961 } else if (self
->max_vert
) {
1962 self
->max_vert
= FALSE
;
1963 client_maximize(self
, TRUE
, 2, FALSE
);
1964 } else if (self
->max_horz
) {
1965 self
->max_horz
= FALSE
;
1966 client_maximize(self
, TRUE
, 1, FALSE
);
1969 /* nothing to do for the other states:
1978 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1979 gint x
, gint y
, gint w
, gint h
,
1980 gboolean user
, gboolean final
,
1981 gboolean force_reply
)
1984 gboolean send_resize_client
;
1985 gboolean moved
= FALSE
, resized
= FALSE
;
1986 guint fdecor
= self
->frame
->decorations
;
1987 gboolean fhorz
= self
->frame
->max_horz
;
1989 /* make the frame recalculate its dimentions n shit without changing
1990 anything visible for real, this way the constraints below can work with
1991 the updated frame dimensions. */
1992 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
1994 /* gets the frame's position */
1995 frame_client_gravity(self
->frame
, &x
, &y
);
1997 /* these positions are frame positions, not client positions */
1999 /* set the size and position if fullscreen */
2000 if (self
->fullscreen
) {
2003 XF86VidModeModeLine mode
;
2008 i
= client_monitor(self
);
2009 a
= screen_physical_area_monitor(i
);
2012 if (i
== 0 && /* primary head */
2013 extensions_vidmode
&&
2014 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
2015 /* get the mode last so the mode.privsize isnt freed incorrectly */
2016 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
2021 if (mode
.privsize
) XFree(mode
.private);
2031 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2035 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
2037 /* set the size and position if maximized */
2038 if (self
->max_horz
) {
2040 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2042 if (self
->max_vert
) {
2044 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2048 /* gets the client's position */
2049 frame_frame_gravity(self
->frame
, &x
, &y
);
2051 /* these override the above states! if you cant move you can't move! */
2053 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2057 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2058 w
= self
->area
.width
;
2059 h
= self
->area
.height
;
2063 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2064 gint basew
, baseh
, minw
, minh
;
2066 /* base size is substituted with min size if not specified */
2067 if (self
->base_size
.width
|| self
->base_size
.height
) {
2068 basew
= self
->base_size
.width
;
2069 baseh
= self
->base_size
.height
;
2071 basew
= self
->min_size
.width
;
2072 baseh
= self
->min_size
.height
;
2074 /* min size is substituted with base size if not specified */
2075 if (self
->min_size
.width
|| self
->min_size
.height
) {
2076 minw
= self
->min_size
.width
;
2077 minh
= self
->min_size
.height
;
2079 minw
= self
->base_size
.width
;
2080 minh
= self
->base_size
.height
;
2083 /* if this is a user-requested resize, then check against min/max
2086 /* smaller than min size or bigger than max size? */
2087 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2088 if (w
< minw
) w
= minw
;
2089 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2090 if (h
< minh
) h
= minh
;
2095 /* keep to the increments */
2096 w
/= self
->size_inc
.width
;
2097 h
/= self
->size_inc
.height
;
2099 /* you cannot resize to nothing */
2100 if (basew
+ w
< 1) w
= 1 - basew
;
2101 if (baseh
+ h
< 1) h
= 1 - baseh
;
2103 /* store the logical size */
2104 SIZE_SET(self
->logical_size
,
2105 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2106 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2108 w
*= self
->size_inc
.width
;
2109 h
*= self
->size_inc
.height
;
2114 /* adjust the height to match the width for the aspect ratios.
2115 for this, min size is not substituted for base size ever. */
2116 w
-= self
->base_size
.width
;
2117 h
-= self
->base_size
.height
;
2119 if (!self
->fullscreen
) {
2120 if (self
->min_ratio
)
2121 if (h
* self
->min_ratio
> w
) {
2122 h
= (gint
)(w
/ self
->min_ratio
);
2124 /* you cannot resize to nothing */
2127 w
= (gint
)(h
* self
->min_ratio
);
2130 if (self
->max_ratio
)
2131 if (h
* self
->max_ratio
< w
) {
2132 h
= (gint
)(w
/ self
->max_ratio
);
2134 /* you cannot resize to nothing */
2137 w
= (gint
)(h
* self
->min_ratio
);
2142 w
+= self
->base_size
.width
;
2143 h
+= self
->base_size
.height
;
2150 case OB_CORNER_TOPLEFT
:
2152 case OB_CORNER_TOPRIGHT
:
2153 x
-= w
- self
->area
.width
;
2155 case OB_CORNER_BOTTOMLEFT
:
2156 y
-= h
- self
->area
.height
;
2158 case OB_CORNER_BOTTOMRIGHT
:
2159 x
-= w
- self
->area
.width
;
2160 y
-= h
- self
->area
.height
;
2164 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2165 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2167 oldw
= self
->area
.width
;
2168 oldh
= self
->area
.height
;
2169 RECT_SET(self
->area
, x
, y
, w
, h
);
2171 /* for app-requested resizes, always resize if 'resized' is true.
2172 for user-requested ones, only resize if final is true, or when
2173 resizing in redraw mode */
2174 send_resize_client
= ((!user
&& resized
) ||
2176 (resized
&& config_resize_redraw
))));
2178 /* if the client is enlarging, then resize the client before the frame */
2179 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2180 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2182 /* move/resize the frame to match the request */
2184 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2185 moved
= resized
= TRUE
;
2187 if (moved
|| resized
)
2188 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2190 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2193 event
.type
= ConfigureNotify
;
2194 event
.xconfigure
.display
= ob_display
;
2195 event
.xconfigure
.event
= self
->window
;
2196 event
.xconfigure
.window
= self
->window
;
2198 /* root window real coords */
2199 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2201 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2203 event
.xconfigure
.width
= w
;
2204 event
.xconfigure
.height
= h
;
2205 event
.xconfigure
.border_width
= 0;
2206 event
.xconfigure
.above
= self
->frame
->plate
;
2207 event
.xconfigure
.override_redirect
= FALSE
;
2208 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2209 FALSE
, StructureNotifyMask
, &event
);
2213 /* if the client is shrinking, then resize the frame before the client */
2214 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2215 XResizeWindow(ob_display
, self
->window
, w
, h
);
2220 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2224 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2225 self
->fullscreen
== fs
) return; /* already done */
2227 self
->fullscreen
= fs
;
2228 client_change_state(self
); /* change the state hints on the client,
2229 and adjust out layer/stacking */
2233 self
->pre_fullscreen_area
= self
->area
;
2235 /* these are not actually used cuz client_configure will set them
2236 as appropriate when the window is fullscreened */
2241 if (self
->pre_fullscreen_area
.width
> 0 &&
2242 self
->pre_fullscreen_area
.height
> 0)
2244 x
= self
->pre_fullscreen_area
.x
;
2245 y
= self
->pre_fullscreen_area
.y
;
2246 w
= self
->pre_fullscreen_area
.width
;
2247 h
= self
->pre_fullscreen_area
.height
;
2248 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2250 /* pick some fallbacks... */
2251 a
= screen_area_monitor(self
->desktop
, 0);
2252 x
= a
->x
+ a
->width
/ 4;
2253 y
= a
->y
+ a
->height
/ 4;
2259 client_setup_decor_and_functions(self
);
2261 client_move_resize(self
, x
, y
, w
, h
);
2263 /* try focus us when we go into fullscreen mode */
2267 static void client_iconify_recursive(ObClient
*self
,
2268 gboolean iconic
, gboolean curdesk
)
2271 gboolean changed
= FALSE
;
2274 if (self
->iconic
!= iconic
) {
2275 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2278 self
->iconic
= iconic
;
2281 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2284 old
= self
->wmstate
;
2285 self
->wmstate
= IconicState
;
2286 if (old
!= self
->wmstate
)
2287 PROP_MSG(self
->window
, kde_wm_change_state
,
2288 self
->wmstate
, 1, 0, 0);
2290 /* update the focus lists.. iconic windows go to the bottom of
2291 the list, put the new iconic window at the 'top of the
2293 focus_order_to_top(self
);
2301 client_set_desktop(self
, screen_desktop
, FALSE
);
2303 old
= self
->wmstate
;
2304 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2305 if (old
!= self
->wmstate
)
2306 PROP_MSG(self
->window
, kde_wm_change_state
,
2307 self
->wmstate
, 1, 0, 0);
2309 /* this puts it after the current focused window */
2310 focus_order_remove(self
);
2311 focus_order_add_new(self
);
2313 /* this is here cuz with the VIDMODE extension, the viewport can
2314 change while a fullscreen window is iconic, and when it
2315 uniconifies, it would be nice if it did so to the new position
2317 client_reconfigure(self
);
2324 client_change_state(self
);
2325 client_showhide(self
);
2326 screen_update_areas();
2329 /* iconify all transients */
2330 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2331 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2335 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2337 /* move up the transient chain as far as possible first */
2338 self
= client_search_top_transient(self
);
2340 client_iconify_recursive(client_search_top_transient(self
),
2344 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2348 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2349 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2351 /* check if already done */
2353 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2354 if (dir
== 1 && self
->max_horz
) return;
2355 if (dir
== 2 && self
->max_vert
) return;
2357 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2358 if (dir
== 1 && !self
->max_horz
) return;
2359 if (dir
== 2 && !self
->max_vert
) return;
2362 /* we just tell it to configure in the same place and client_configure
2363 worries about filling the screen with the window */
2366 w
= self
->area
.width
;
2367 h
= self
->area
.height
;
2371 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2372 RECT_SET(self
->pre_max_area
,
2373 self
->area
.x
, self
->pre_max_area
.y
,
2374 self
->area
.width
, self
->pre_max_area
.height
);
2376 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2377 RECT_SET(self
->pre_max_area
,
2378 self
->pre_max_area
.x
, self
->area
.y
,
2379 self
->pre_max_area
.width
, self
->area
.height
);
2385 a
= screen_area_monitor(self
->desktop
, 0);
2386 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2387 if (self
->pre_max_area
.width
> 0) {
2388 x
= self
->pre_max_area
.x
;
2389 w
= self
->pre_max_area
.width
;
2391 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2392 0, self
->pre_max_area
.height
);
2394 /* pick some fallbacks... */
2395 x
= a
->x
+ a
->width
/ 4;
2399 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2400 if (self
->pre_max_area
.height
> 0) {
2401 y
= self
->pre_max_area
.y
;
2402 h
= self
->pre_max_area
.height
;
2404 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2405 self
->pre_max_area
.width
, 0);
2407 /* pick some fallbacks... */
2408 y
= a
->y
+ a
->height
/ 4;
2414 if (dir
== 0 || dir
== 1) /* horz */
2415 self
->max_horz
= max
;
2416 if (dir
== 0 || dir
== 2) /* vert */
2417 self
->max_vert
= max
;
2419 client_change_state(self
); /* change the state hints on the client */
2421 client_setup_decor_and_functions(self
);
2423 client_move_resize(self
, x
, y
, w
, h
);
2426 void client_shade(ObClient
*self
, gboolean shade
)
2428 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2429 shade
) || /* can't shade */
2430 self
->shaded
== shade
) return; /* already done */
2432 /* when we're iconic, don't change the wmstate */
2433 if (!self
->iconic
) {
2436 old
= self
->wmstate
;
2437 self
->wmstate
= shade
? IconicState
: NormalState
;
2438 if (old
!= self
->wmstate
)
2439 PROP_MSG(self
->window
, kde_wm_change_state
,
2440 self
->wmstate
, 1, 0, 0);
2443 self
->shaded
= shade
;
2444 client_change_state(self
);
2445 /* resize the frame to just the titlebar */
2446 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2449 void client_close(ObClient
*self
)
2453 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2455 /* in the case that the client provides no means to requesting that it
2456 close, we just kill it */
2457 if (!self
->delete_window
)
2461 XXX: itd be cool to do timeouts and shit here for killing the client's
2463 like... if the window is around after 5 seconds, then the close button
2464 turns a nice red, and if this function is called again, the client is
2468 ce
.xclient
.type
= ClientMessage
;
2469 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2470 ce
.xclient
.display
= ob_display
;
2471 ce
.xclient
.window
= self
->window
;
2472 ce
.xclient
.format
= 32;
2473 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2474 ce
.xclient
.data
.l
[1] = event_lasttime
;
2475 ce
.xclient
.data
.l
[2] = 0l;
2476 ce
.xclient
.data
.l
[3] = 0l;
2477 ce
.xclient
.data
.l
[4] = 0l;
2478 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2481 void client_kill(ObClient
*self
)
2483 XKillClient(ob_display
, self
->window
);
2486 void client_set_desktop_recursive(ObClient
*self
,
2487 guint target
, gboolean donthide
)
2492 if (target
!= self
->desktop
) {
2494 ob_debug("Setting desktop %u\n", target
+1);
2496 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2498 /* remove from the old desktop(s) */
2499 focus_order_remove(self
);
2501 old
= self
->desktop
;
2502 self
->desktop
= target
;
2503 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2504 /* the frame can display the current desktop state */
2505 frame_adjust_state(self
->frame
);
2506 /* 'move' the window to the new desktop */
2508 client_showhide(self
);
2509 /* raise if it was not already on the desktop */
2510 if (old
!= DESKTOP_ALL
)
2512 screen_update_areas();
2514 /* add to the new desktop(s) */
2515 if (config_focus_new
)
2516 focus_order_to_top(self
);
2518 focus_order_to_bottom(self
);
2521 /* move all transients */
2522 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2523 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2527 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2529 client_set_desktop_recursive(client_search_top_transient(self
),
2533 ObClient
*client_search_modal_child(ObClient
*self
)
2538 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2539 ObClient
*c
= it
->data
;
2540 if ((ret
= client_search_modal_child(c
))) return ret
;
2541 if (c
->modal
) return c
;
2546 gboolean
client_validate(ObClient
*self
)
2550 XSync(ob_display
, FALSE
); /* get all events on the server */
2552 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2553 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2554 XPutBackEvent(ob_display
, &e
);
2561 void client_set_wm_state(ObClient
*self
, glong state
)
2563 if (state
== self
->wmstate
) return; /* no change */
2567 client_iconify(self
, TRUE
, TRUE
);
2570 client_iconify(self
, FALSE
, TRUE
);
2575 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2577 gboolean shaded
= self
->shaded
;
2578 gboolean fullscreen
= self
->fullscreen
;
2579 gboolean undecorated
= self
->undecorated
;
2580 gboolean max_horz
= self
->max_horz
;
2581 gboolean max_vert
= self
->max_vert
;
2582 gboolean modal
= self
->modal
;
2583 gboolean iconic
= self
->iconic
;
2586 if (!(action
== prop_atoms
.net_wm_state_add
||
2587 action
== prop_atoms
.net_wm_state_remove
||
2588 action
== prop_atoms
.net_wm_state_toggle
))
2589 /* an invalid action was passed to the client message, ignore it */
2592 for (i
= 0; i
< 2; ++i
) {
2593 Atom state
= i
== 0 ? data1
: data2
;
2595 if (!state
) continue;
2597 /* if toggling, then pick whether we're adding or removing */
2598 if (action
== prop_atoms
.net_wm_state_toggle
) {
2599 if (state
== prop_atoms
.net_wm_state_modal
)
2600 action
= modal
? prop_atoms
.net_wm_state_remove
:
2601 prop_atoms
.net_wm_state_add
;
2602 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2603 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2604 prop_atoms
.net_wm_state_add
;
2605 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2606 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2607 prop_atoms
.net_wm_state_add
;
2608 else if (state
== prop_atoms
.net_wm_state_shaded
)
2609 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2610 prop_atoms
.net_wm_state_add
;
2611 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2612 action
= self
->skip_taskbar
?
2613 prop_atoms
.net_wm_state_remove
:
2614 prop_atoms
.net_wm_state_add
;
2615 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2616 action
= self
->skip_pager
?
2617 prop_atoms
.net_wm_state_remove
:
2618 prop_atoms
.net_wm_state_add
;
2619 else if (state
== prop_atoms
.net_wm_state_hidden
)
2620 action
= self
->iconic
?
2621 prop_atoms
.net_wm_state_remove
:
2622 prop_atoms
.net_wm_state_add
;
2623 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2624 action
= fullscreen
?
2625 prop_atoms
.net_wm_state_remove
:
2626 prop_atoms
.net_wm_state_add
;
2627 else if (state
== prop_atoms
.net_wm_state_above
)
2628 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2629 prop_atoms
.net_wm_state_add
;
2630 else if (state
== prop_atoms
.net_wm_state_below
)
2631 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2632 prop_atoms
.net_wm_state_add
;
2633 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2634 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2635 prop_atoms
.net_wm_state_add
;
2638 if (action
== prop_atoms
.net_wm_state_add
) {
2639 if (state
== prop_atoms
.net_wm_state_modal
) {
2641 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2643 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2645 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2647 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2648 self
->skip_taskbar
= TRUE
;
2649 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2650 self
->skip_pager
= TRUE
;
2651 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2653 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2655 } else if (state
== prop_atoms
.net_wm_state_above
) {
2657 self
->below
= FALSE
;
2658 } else if (state
== prop_atoms
.net_wm_state_below
) {
2659 self
->above
= FALSE
;
2661 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2665 } else { /* action == prop_atoms.net_wm_state_remove */
2666 if (state
== prop_atoms
.net_wm_state_modal
) {
2668 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2670 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2672 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2674 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2675 self
->skip_taskbar
= FALSE
;
2676 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2677 self
->skip_pager
= FALSE
;
2678 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2680 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2682 } else if (state
== prop_atoms
.net_wm_state_above
) {
2683 self
->above
= FALSE
;
2684 } else if (state
== prop_atoms
.net_wm_state_below
) {
2685 self
->below
= FALSE
;
2686 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2687 undecorated
= FALSE
;
2691 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2692 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2694 if (max_horz
== max_vert
) { /* both going the same way */
2695 client_maximize(self
, max_horz
, 0, TRUE
);
2697 client_maximize(self
, max_horz
, 1, TRUE
);
2698 client_maximize(self
, max_vert
, 2, TRUE
);
2702 if (max_horz
!= self
->max_horz
)
2703 client_maximize(self
, max_horz
, 1, TRUE
);
2705 client_maximize(self
, max_vert
, 2, TRUE
);
2708 /* change fullscreen state before shading, as it will affect if the window
2710 if (fullscreen
!= self
->fullscreen
)
2711 client_fullscreen(self
, fullscreen
, TRUE
);
2712 if (shaded
!= self
->shaded
)
2713 client_shade(self
, shaded
);
2714 if (undecorated
!= self
->undecorated
)
2715 client_set_undecorated(self
, undecorated
);
2716 if (modal
!= self
->modal
) {
2717 self
->modal
= modal
;
2718 /* when a window changes modality, then its stacking order with its
2719 transients needs to change */
2722 if (iconic
!= self
->iconic
)
2723 client_iconify(self
, iconic
, FALSE
);
2725 client_calc_layer(self
);
2726 client_change_state(self
); /* change the hint to reflect these changes */
2729 ObClient
*client_focus_target(ObClient
*self
)
2733 /* if we have a modal child, then focus it, not us */
2734 child
= client_search_modal_child(client_search_top_transient(self
));
2735 if (child
) return child
;
2739 gboolean
client_can_focus(ObClient
*self
)
2743 /* choose the correct target */
2744 self
= client_focus_target(self
);
2746 if (!self
->frame
->visible
)
2749 if (!(self
->can_focus
|| self
->focus_notify
))
2752 /* do a check to see if the window has already been unmapped or destroyed
2753 do this intelligently while watching out for unmaps we've generated
2754 (ignore_unmaps > 0) */
2755 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2756 DestroyNotify
, &ev
)) {
2757 XPutBackEvent(ob_display
, &ev
);
2760 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2761 UnmapNotify
, &ev
)) {
2762 if (self
->ignore_unmaps
) {
2763 self
->ignore_unmaps
--;
2765 XPutBackEvent(ob_display
, &ev
);
2773 gboolean
client_focus(ObClient
*self
)
2775 /* choose the correct target */
2776 self
= client_focus_target(self
);
2778 if (!client_can_focus(self
)) {
2779 if (!self
->frame
->visible
) {
2780 /* update the focus lists */
2781 focus_order_to_top(self
);
2786 if (self
->can_focus
) {
2787 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2788 I choose to use it always, hopefully to find errors quicker, if any
2789 are left. (I hate X. I hate focus events.)
2791 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2792 #799. So now it is RevertToNone again.
2794 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2798 if (self
->focus_notify
) {
2800 ce
.xclient
.type
= ClientMessage
;
2801 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2802 ce
.xclient
.display
= ob_display
;
2803 ce
.xclient
.window
= self
->window
;
2804 ce
.xclient
.format
= 32;
2805 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2806 ce
.xclient
.data
.l
[1] = event_lasttime
;
2807 ce
.xclient
.data
.l
[2] = 0l;
2808 ce
.xclient
.data
.l
[3] = 0l;
2809 ce
.xclient
.data
.l
[4] = 0l;
2810 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2814 ob_debug("%sively focusing %lx at %d\n",
2815 (self
->can_focus
? "act" : "pass"),
2816 self
->window
, (gint
) event_lasttime
);
2819 /* Cause the FocusIn to come back to us. Important for desktop switches,
2820 since otherwise we'll have no FocusIn on the queue and send it off to
2821 the focus_backup. */
2822 XSync(ob_display
, FALSE
);
2826 void client_unfocus(ObClient
*self
)
2828 if (focus_client
== self
) {
2830 ob_debug("client_unfocus for %lx\n", self
->window
);
2832 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2836 void client_activate(ObClient
*self
, gboolean here
)
2838 /* This check is for the client_list_menu trying to activate
2839 * a closed client. */
2840 if (!g_list_find(client_list
, self
)) return;
2841 if (client_normal(self
) && screen_showing_desktop
)
2842 screen_show_desktop(FALSE
);
2844 client_iconify(self
, FALSE
, here
);
2845 if (self
->desktop
!= DESKTOP_ALL
&&
2846 self
->desktop
!= screen_desktop
) {
2848 client_set_desktop(self
, screen_desktop
, FALSE
);
2850 screen_set_desktop(self
->desktop
);
2851 } else if (!self
->frame
->visible
)
2852 /* if its not visible for other reasons, then don't mess
2856 client_shade(self
, FALSE
);
2860 /* we do this an action here. this is rather important. this is because
2861 we want the results from the focus change to take place BEFORE we go
2862 about raising the window. when a fullscreen window loses focus, we need
2863 this or else the raise wont be able to raise above the to-lose-focus
2864 fullscreen window. */
2868 void client_raise(ObClient
*self
)
2870 action_run_string("Raise", self
);
2873 void client_lower(ObClient
*self
)
2875 action_run_string("Lower", self
);
2878 gboolean
client_focused(ObClient
*self
)
2880 return self
== focus_client
;
2883 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
2886 /* si is the smallest image >= req */
2887 /* li is the largest image < req */
2888 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2890 if (!self
->nicons
) {
2891 ObClientIcon
*parent
= NULL
;
2893 if (self
->transient_for
) {
2894 if (self
->transient_for
!= OB_TRAN_GROUP
)
2895 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2898 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2899 ObClient
*c
= it
->data
;
2900 if (c
!= self
&& !c
->transient_for
) {
2901 if ((parent
= client_icon_recursive(c
, w
, h
)))
2911 for (i
= 0; i
< self
->nicons
; ++i
) {
2912 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2913 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2917 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2922 if (largest
== 0) /* didnt find one smaller than the requested size */
2923 return &self
->icons
[si
];
2924 return &self
->icons
[li
];
2927 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
2930 static ObClientIcon deficon
;
2932 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
2933 deficon
.width
= deficon
.height
= 48;
2934 deficon
.data
= ob_rr_theme
->def_win_icon
;
2940 /* this be mostly ripped from fvwm */
2941 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2943 gint my_cx
, my_cy
, his_cx
, his_cy
;
2946 gint score
, best_score
;
2947 ObClient
*best_client
, *cur
;
2953 /* first, find the centre coords of the currently focused window */
2954 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2955 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2960 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
2963 /* the currently selected window isn't interesting */
2966 if (!client_normal(cur
))
2968 /* using c->desktop instead of screen_desktop doesn't work if the
2969 * current window was omnipresent, hope this doesn't have any other
2971 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2975 if(client_focus_target(cur
) == cur
&&
2976 !(cur
->can_focus
|| cur
->focus_notify
))
2979 /* find the centre coords of this window, from the
2980 * currently focused window's point of view */
2981 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2982 + cur
->frame
->area
.width
/ 2;
2983 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2984 + cur
->frame
->area
.height
/ 2;
2986 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
2987 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
2989 /* Rotate the diagonals 45 degrees counterclockwise.
2990 * To do this, multiply the matrix /+h +h\ with the
2991 * vector (x y). \-h +h/
2992 * h = sqrt(0.5). We can set h := 1 since absolute
2993 * distance doesn't matter here. */
2994 tx
= his_cx
+ his_cy
;
2995 his_cy
= -his_cx
+ his_cy
;
3000 case OB_DIRECTION_NORTH
:
3001 case OB_DIRECTION_SOUTH
:
3002 case OB_DIRECTION_NORTHEAST
:
3003 case OB_DIRECTION_SOUTHWEST
:
3004 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3005 distance
= ((dir
== OB_DIRECTION_NORTH
||
3006 dir
== OB_DIRECTION_NORTHEAST
) ?
3009 case OB_DIRECTION_EAST
:
3010 case OB_DIRECTION_WEST
:
3011 case OB_DIRECTION_SOUTHEAST
:
3012 case OB_DIRECTION_NORTHWEST
:
3013 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3014 distance
= ((dir
== OB_DIRECTION_WEST
||
3015 dir
== OB_DIRECTION_NORTHWEST
) ?
3020 /* the target must be in the requested direction */
3024 /* Calculate score for this window. The smaller the better. */
3025 score
= distance
+ offset
;
3027 /* windows more than 45 degrees off the direction are
3028 * heavily penalized and will only be chosen if nothing
3029 * else within a million pixels */
3030 if(offset
> distance
)
3033 if(best_score
== -1 || score
< best_score
)
3041 void client_set_layer(ObClient
*self
, gint layer
)
3045 self
->above
= FALSE
;
3046 } else if (layer
== 0) {
3047 self
->below
= self
->above
= FALSE
;
3049 self
->below
= FALSE
;
3052 client_calc_layer(self
);
3053 client_change_state(self
); /* reflect this in the state hints */
3056 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3058 if (self
->undecorated
!= undecorated
) {
3059 self
->undecorated
= undecorated
;
3060 client_setup_decor_and_functions(self
);
3061 /* Make sure the client knows it might have moved. Maybe there is a
3062 * better way of doing this so only one client_configure is sent, but
3063 * since 125 of these are sent per second when moving the window (with
3064 * user = FALSE) i doubt it matters much.
3066 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3067 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3068 client_change_state(self
); /* reflect this in the state hints */
3072 /* Determines which physical monitor a client is on by calculating the
3073 area of the part of the client on each monitor. The number of the
3074 monitor containing the greatest area of the client is returned.*/
3075 guint
client_monitor(ObClient
*self
)
3081 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3082 Rect
*area
= screen_physical_area_monitor(i
);
3083 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3087 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3088 v
= r
.width
* r
.height
;
3099 ObClient
*client_search_top_transient(ObClient
*self
)
3101 /* move up the transient chain as far as possible */
3102 if (self
->transient_for
) {
3103 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3104 return client_search_top_transient(self
->transient_for
);
3108 g_assert(self
->group
);
3110 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3111 ObClient
*c
= it
->data
;
3113 /* checking transient_for prevents infinate loops! */
3114 if (c
!= self
&& !c
->transient_for
)
3125 ObClient
*client_search_focus_parent(ObClient
*self
)
3127 if (self
->transient_for
) {
3128 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3129 if (client_focused(self
->transient_for
))
3130 return self
->transient_for
;
3134 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3135 ObClient
*c
= it
->data
;
3137 /* checking transient_for prevents infinate loops! */
3138 if (c
!= self
&& !c
->transient_for
)
3139 if (client_focused(c
))
3148 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3150 if (self
->transient_for
) {
3151 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3152 if (self
->transient_for
== search
)
3157 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3158 ObClient
*c
= it
->data
;
3160 /* checking transient_for prevents infinate loops! */
3161 if (c
!= self
&& !c
->transient_for
)
3171 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3175 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3176 if (sit
->data
== search
)
3178 if (client_search_transient(sit
->data
, search
))
3184 void client_update_sm_client_id(ObClient
*self
)
3186 g_free(self
->sm_client_id
);
3187 self
->sm_client_id
= NULL
;
3189 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3191 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3192 &self
->sm_client_id
);
3195 /* finds the nearest edge in the given direction from the current client
3196 * note to self: the edge is the -frame- edge (the actual one), not the
3199 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
)
3201 gint dest
, monitor_dest
;
3202 gint my_edge_start
, my_edge_end
, my_offset
;
3209 a
= screen_area(c
->desktop
);
3210 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3213 case OB_DIRECTION_NORTH
:
3214 my_edge_start
= c
->frame
->area
.x
;
3215 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3216 my_offset
= c
->frame
->area
.y
;
3218 /* default: top of screen */
3220 monitor_dest
= monitor
->y
;
3221 /* if the monitor edge comes before the screen edge, */
3222 /* use that as the destination instead. (For xinerama) */
3223 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3224 dest
= monitor_dest
;
3226 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3227 gint his_edge_start
, his_edge_end
, his_offset
;
3228 ObClient
*cur
= it
->data
;
3232 if(!client_normal(cur
))
3234 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3238 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3241 his_edge_start
= cur
->frame
->area
.x
;
3242 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3243 his_offset
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3245 if(his_offset
+ 1 > my_offset
)
3248 if(his_offset
< dest
)
3251 if(his_edge_start
>= my_edge_start
&&
3252 his_edge_start
<= my_edge_end
)
3255 if(my_edge_start
>= his_edge_start
&&
3256 my_edge_start
<= his_edge_end
)
3261 case OB_DIRECTION_SOUTH
:
3262 my_edge_start
= c
->frame
->area
.x
;
3263 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3264 my_offset
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3266 /* default: bottom of screen */
3267 dest
= a
->y
+ a
->height
;
3268 monitor_dest
= monitor
->y
+ monitor
->height
;
3269 /* if the monitor edge comes before the screen edge, */
3270 /* use that as the destination instead. (For xinerama) */
3271 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3272 dest
= monitor_dest
;
3274 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3275 gint his_edge_start
, his_edge_end
, his_offset
;
3276 ObClient
*cur
= it
->data
;
3280 if(!client_normal(cur
))
3282 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3286 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3289 his_edge_start
= cur
->frame
->area
.x
;
3290 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3291 his_offset
= cur
->frame
->area
.y
;
3294 if(his_offset
- 1 < my_offset
)
3297 if(his_offset
> dest
)
3300 if(his_edge_start
>= my_edge_start
&&
3301 his_edge_start
<= my_edge_end
)
3304 if(my_edge_start
>= his_edge_start
&&
3305 my_edge_start
<= his_edge_end
)
3310 case OB_DIRECTION_WEST
:
3311 my_edge_start
= c
->frame
->area
.y
;
3312 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3313 my_offset
= c
->frame
->area
.x
;
3315 /* default: leftmost egde of screen */
3317 monitor_dest
= monitor
->x
;
3318 /* if the monitor edge comes before the screen edge, */
3319 /* use that as the destination instead. (For xinerama) */
3320 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3321 dest
= monitor_dest
;
3323 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3324 gint his_edge_start
, his_edge_end
, his_offset
;
3325 ObClient
*cur
= it
->data
;
3329 if(!client_normal(cur
))
3331 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3335 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3338 his_edge_start
= cur
->frame
->area
.y
;
3339 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3340 his_offset
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3342 if(his_offset
+ 1 > my_offset
)
3345 if(his_offset
< dest
)
3348 if(his_edge_start
>= my_edge_start
&&
3349 his_edge_start
<= my_edge_end
)
3352 if(my_edge_start
>= his_edge_start
&&
3353 my_edge_start
<= his_edge_end
)
3359 case OB_DIRECTION_EAST
:
3360 my_edge_start
= c
->frame
->area
.y
;
3361 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3362 my_offset
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3364 /* default: rightmost edge of screen */
3365 dest
= a
->x
+ a
->width
;
3366 monitor_dest
= monitor
->x
+ monitor
->width
;
3367 /* if the monitor edge comes before the screen edge, */
3368 /* use that as the destination instead. (For xinerama) */
3369 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3370 dest
= monitor_dest
;
3372 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3373 gint his_edge_start
, his_edge_end
, his_offset
;
3374 ObClient
*cur
= it
->data
;
3378 if(!client_normal(cur
))
3380 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3384 if(cur
->layer
< c
->layer
&& !config_resist_layers_below
)
3387 his_edge_start
= cur
->frame
->area
.y
;
3388 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3389 his_offset
= cur
->frame
->area
.x
;
3391 if(his_offset
- 1 < my_offset
)
3394 if(his_offset
> dest
)
3397 if(his_edge_start
>= my_edge_start
&&
3398 his_edge_start
<= my_edge_end
)
3401 if(my_edge_start
>= his_edge_start
&&
3402 my_edge_start
<= his_edge_end
)
3407 case OB_DIRECTION_NORTHEAST
:
3408 case OB_DIRECTION_SOUTHEAST
:
3409 case OB_DIRECTION_NORTHWEST
:
3410 case OB_DIRECTION_SOUTHWEST
:
3411 /* not implemented */
3413 g_assert_not_reached();
3414 dest
= 0; /* suppress warning */
3419 ObClient
* client_under_pointer()
3423 ObClient
*ret
= NULL
;
3425 if (screen_pointer_pos(&x
, &y
)) {
3426 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3427 if (WINDOW_IS_CLIENT(it
->data
)) {
3428 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3429 if (c
->frame
->visible
&&
3430 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3440 gboolean
client_has_group_siblings(ObClient
*self
)
3442 return self
->group
&& self
->group
->members
->next
;