1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 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 static ObAppSettings
*get_settings(ObClient
*client
)
209 GSList
*a
= config_per_app_settings
;
212 ObAppSettings
*app
= (ObAppSettings
*) a
->data
;
215 (app
->name
&& !app
->class && !strcmp(app
->name
, client
->name
))
216 || (app
->class && !app
->name
&& !strcmp(app
->class, client
->class))
217 || (app
->class && app
->name
&& !strcmp(app
->class, client
->class)
218 && !strcmp(app
->name
, client
->name
))
220 ob_debug("Window matching: %s\n", app
->name
);
221 /* Match if no role was specified in the per app setting, or if the
222 * string matches the beginning of the role, since apps like to set
223 * the role to things like browser-window-23c4b2f */
225 || !strncmp(app
->role
, client
->role
, strlen(app
->role
)))
234 void client_manage(Window window
)
238 XWindowAttributes attrib
;
239 XSetWindowAttributes attrib_set
;
241 gboolean activate
= FALSE
;
242 ObAppSettings
*settings
;
246 /* check if it has already been unmapped by the time we started mapping
247 the grab does a sync so we don't have to here */
248 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
249 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
)) {
250 XPutBackEvent(ob_display
, &e
);
253 return; /* don't manage it */
256 /* make sure it isn't an override-redirect window */
257 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
258 attrib
.override_redirect
) {
260 return; /* don't manage it */
263 /* is the window a docking app */
264 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
265 if ((wmhint
->flags
& StateHint
) &&
266 wmhint
->initial_state
== WithdrawnState
) {
267 dock_add(window
, wmhint
);
275 ob_debug("Managing window: %lx\n", window
);
277 /* choose the events we want to receive on the CLIENT window */
278 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
279 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
280 XChangeWindowAttributes(ob_display
, window
,
281 CWEventMask
|CWDontPropagate
, &attrib_set
);
284 /* create the ObClient struct, and populate it from the hints on the
286 self
= g_new0(ObClient
, 1);
287 self
->obwin
.type
= Window_Client
;
288 self
->window
= window
;
290 /* non-zero defaults */
291 self
->title_count
= 1;
292 self
->wmstate
= NormalState
;
294 self
->desktop
= screen_num_desktops
; /* always an invalid value */
296 client_get_all(self
);
297 client_restore_session_state(self
);
299 sn_app_started(self
->class);
301 /* update the focus lists, do this before the call to change_state or
302 it can end up in the list twice! */
303 focus_order_add_new(self
);
305 client_change_state(self
);
307 /* remove the client's border (and adjust re gravity) */
308 client_toggle_border(self
, FALSE
);
310 /* specify that if we exit, the window should not be destroyed and should
311 be reparented back to root automatically */
312 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
314 /* create the decoration frame for the client window */
315 self
->frame
= frame_new();
317 frame_grab_client(self
->frame
, self
);
321 client_apply_startup_state(self
);
323 /* get and set application level settings */
324 settings
= get_settings(self
);
326 stacking_add(CLIENT_AS_WINDOW(self
));
327 client_restore_session_stacking(self
);
330 /* Don't worry, we won't actually both shade and undecorate the
331 * window when push comes to shove. */
332 if (settings
->shade
!= -1)
333 client_shade(self
, settings
->shade
);
334 if (settings
->decor
!= -1)
335 client_set_undecorated(self
, !settings
->decor
);
336 if (settings
->iconic
!= -1)
337 client_iconify(self
, settings
->iconic
, FALSE
);
338 if (settings
->skip_pager
!= -1) {
339 self
->skip_pager
= !!settings
->skip_pager
;
340 client_change_state(self
);
342 if (settings
->skip_taskbar
!= -1) {
343 self
->skip_taskbar
= !!settings
->skip_taskbar
;
344 client_change_state(self
);
347 /* 1 && -1 shouldn't be possible by the code in config.c */
348 if (settings
->max_vert
== 1 && settings
->max_horz
== 1)
349 client_maximize(self
, TRUE
, 0, TRUE
);
350 else if (settings
->max_vert
== 0 && settings
->max_horz
== 0)
351 client_maximize(self
, FALSE
, 0, TRUE
);
352 else if (settings
->max_vert
== 1 && settings
->max_horz
== 0) {
353 client_maximize(self
, TRUE
, 2, TRUE
);
354 client_maximize(self
, FALSE
, 1, TRUE
);
355 } else if (settings
->max_vert
== 0 && settings
->max_horz
== 1) {
356 client_maximize(self
, TRUE
, 1, TRUE
);
357 client_maximize(self
, FALSE
, 2, TRUE
);
360 if (settings
->fullscreen
!= -1)
361 client_fullscreen(self
, !!settings
->fullscreen
, TRUE
);
363 if (settings
->desktop
< screen_num_desktops
364 || settings
->desktop
== DESKTOP_ALL
)
365 client_set_desktop(self
, settings
->desktop
, TRUE
);
367 if (settings
->layer
> -2 && settings
->layer
< 2)
368 client_set_layer(self
, settings
->layer
);
372 /* focus the new window? */
373 if (ob_state() != OB_STATE_STARTING
&&
374 (config_focus_new
|| client_search_focus_parent(self
)) ||
375 (settings
&& settings
->focus
== TRUE
) &&
376 /* note the check against Type_Normal/Dialog, not client_normal(self),
377 which would also include other types. in this case we want more
378 strict rules for focus */
379 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
380 self
->type
== OB_CLIENT_TYPE_DIALOG
))
384 if (self
->desktop
!= screen_desktop
) {
385 /* activate the window */
388 gboolean group_foc
= FALSE
;
393 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
395 if (client_focused(it
->data
))
403 (!self
->transient_for
&& (!self
->group
||
404 !self
->group
->members
->next
))) ||
405 client_search_focus_tree_full(self
) ||
407 !client_normal(focus_client
))
409 /* activate the window */
416 if (ob_state() == OB_STATE_RUNNING
) {
417 gint x
= self
->area
.x
, ox
= x
;
418 gint y
= self
->area
.y
, oy
= y
;
421 transient
= place_client(self
, &x
, &y
, settings
);
423 /* make sure the window is visible. */
424 client_find_onscreen(self
, &x
, &y
,
425 self
->frame
->area
.width
,
426 self
->frame
->area
.height
,
427 /* non-normal clients has less rules, and
428 windows that are being restored from a
429 session do also. we can assume you want
430 it back where you saved it. Clients saying
431 they placed themselves are subjected to
432 harder rules, ones that are placed by
433 place.c or by the user are allowed partially
434 off-screen and on xinerama divides (ie,
435 it is up to the placement routines to avoid
436 the xinerama divides) */
438 (((self
->positioned
& PPosition
) &&
439 !(self
->positioned
& USPosition
)) &&
440 client_normal(self
) &&
442 if (x
!= ox
|| y
!= oy
)
443 client_move(self
, x
, y
);
446 keyboard_grab_for_client(self
, TRUE
);
447 mouse_grab_for_client(self
, TRUE
);
449 client_showhide(self
);
451 /* use client_focus instead of client_activate cuz client_activate does
452 stuff like switch desktops etc and I'm not interested in all that when
453 a window maps since its not based on an action from the user like
454 clicking a window to activate is. so keep the new window out of the way
457 /* if using focus_delay, stop the timer now so that focus doesn't go
459 event_halt_focus_delay();
462 /* since focus can change the stacking orders, if we focus the window
463 then the standard raise it gets is not enough, we need to queue one
464 for after the focus change takes place */
468 /* client_activate does this but we aret using it so we have to do it
470 if (screen_showing_desktop
)
471 screen_show_desktop(FALSE
);
473 /* add to client list/map */
474 client_list
= g_list_append(client_list
, self
);
475 g_hash_table_insert(window_map
, &self
->window
, self
);
477 /* this has to happen after we're in the client_list */
478 screen_update_areas();
480 /* update the list hints */
483 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
486 void client_unmanage_all()
488 while (client_list
!= NULL
)
489 client_unmanage(client_list
->data
);
492 void client_unmanage(ObClient
*self
)
497 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
499 g_assert(self
!= NULL
);
501 keyboard_grab_for_client(self
, FALSE
);
502 mouse_grab_for_client(self
, FALSE
);
504 /* potentially fix focusLast */
505 if (config_focus_last
)
506 grab_pointer(TRUE
, OB_CURSOR_NONE
);
508 /* remove the window from our save set */
509 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
511 /* we dont want events no more */
512 XSelectInput(ob_display
, self
->window
, NoEventMask
);
514 frame_hide(self
->frame
);
516 client_list
= g_list_remove(client_list
, self
);
517 stacking_remove(self
);
518 g_hash_table_remove(window_map
, &self
->window
);
520 /* update the focus lists */
521 focus_order_remove(self
);
523 /* once the client is out of the list, update the struts to remove it's
525 screen_update_areas();
527 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
528 Destructor
*d
= it
->data
;
529 d
->func(self
, d
->data
);
532 if (focus_client
== self
) {
535 /* focus the last focused window on the desktop, and ignore enter
536 events from the unmap so it doesnt mess with the focus */
537 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
538 /* remove these flags so we don't end up getting focused in the
540 self
->can_focus
= FALSE
;
541 self
->focus_notify
= FALSE
;
543 client_unfocus(self
);
546 /* tell our parent(s) that we're gone */
547 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
548 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
549 if (it
->data
!= self
)
550 ((ObClient
*)it
->data
)->transients
=
551 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
552 } else if (self
->transient_for
) { /* transient of window */
553 self
->transient_for
->transients
=
554 g_slist_remove(self
->transient_for
->transients
, self
);
557 /* tell our transients that we're gone */
558 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
559 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
560 ((ObClient
*)it
->data
)->transient_for
= NULL
;
561 client_calc_layer(it
->data
);
565 /* remove from its group */
567 group_remove(self
->group
, self
);
571 /* give the client its border back */
572 client_toggle_border(self
, TRUE
);
574 /* reparent the window out of the frame, and free the frame */
575 frame_release_client(self
->frame
, self
);
578 if (ob_state() != OB_STATE_EXITING
) {
579 /* these values should not be persisted across a window
581 PROP_ERASE(self
->window
, net_wm_desktop
);
582 PROP_ERASE(self
->window
, net_wm_state
);
583 PROP_ERASE(self
->window
, wm_state
);
585 /* if we're left in an unmapped state, the client wont be mapped. this
586 is bad, since we will no longer be managing the window on restart */
587 XMapWindow(ob_display
, self
->window
);
591 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
593 /* free all data allocated in the client struct */
594 g_slist_free(self
->transients
);
595 for (j
= 0; j
< self
->nicons
; ++j
)
596 g_free(self
->icons
[j
].data
);
597 if (self
->nicons
> 0)
600 g_free(self
->icon_title
);
604 g_free(self
->sm_client_id
);
607 /* update the list hints */
610 if (config_focus_last
)
611 grab_pointer(FALSE
, OB_CURSOR_NONE
);
614 static void client_urgent_notify(ObClient
*self
)
617 frame_flash_start(self
->frame
);
619 frame_flash_stop(self
->frame
);
622 static void client_restore_session_state(ObClient
*self
)
626 if (!(it
= session_state_find(self
)))
629 self
->session
= it
->data
;
631 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
632 self
->positioned
= PPosition
;
633 if (self
->session
->w
> 0)
634 self
->area
.width
= self
->session
->w
;
635 if (self
->session
->h
> 0)
636 self
->area
.height
= self
->session
->h
;
637 XResizeWindow(ob_display
, self
->window
,
638 self
->area
.width
, self
->area
.height
);
640 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
641 self
->session
->desktop
:
642 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
643 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
645 self
->shaded
= self
->session
->shaded
;
646 self
->iconic
= self
->session
->iconic
;
647 self
->skip_pager
= self
->session
->skip_pager
;
648 self
->skip_taskbar
= self
->session
->skip_taskbar
;
649 self
->fullscreen
= self
->session
->fullscreen
;
650 self
->above
= self
->session
->above
;
651 self
->below
= self
->session
->below
;
652 self
->max_horz
= self
->session
->max_horz
;
653 self
->max_vert
= self
->session
->max_vert
;
656 static void client_restore_session_stacking(ObClient
*self
)
660 if (!self
->session
) return;
662 it
= g_list_find(session_saved_state
, self
->session
);
663 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
666 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
667 if (session_state_cmp(it
->data
, cit
->data
))
670 client_calc_layer(self
);
671 stacking_below(CLIENT_AS_WINDOW(self
),
672 CLIENT_AS_WINDOW(cit
->data
));
678 void client_move_onscreen(ObClient
*self
, gboolean rude
)
680 gint x
= self
->area
.x
;
681 gint y
= self
->area
.y
;
682 if (client_find_onscreen(self
, &x
, &y
,
683 self
->frame
->area
.width
,
684 self
->frame
->area
.height
, rude
)) {
685 client_move(self
, x
, y
);
689 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
693 gint ox
= *x
, oy
= *y
;
695 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
698 /* XXX watch for xinerama dead areas */
699 /* This makes sure windows aren't entirely outside of the screen so you
700 * can't see them at all */
701 if (client_normal(self
)) {
702 a
= screen_area(self
->desktop
);
703 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
704 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
705 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
706 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
707 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
709 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
713 /* This here doesn't let windows even a pixel outside the screen,
714 * when called from client_manage, programs placing themselves are
715 * forced completely onscreen, while things like
716 * xterm -geometry resolution-width/2 will work fine. Trying to
717 * place it completely offscreen will be handled in the above code.
718 * Sorry for this confused comment, i am tired. */
720 /* avoid the xinerama monitor divide while we're at it,
721 * remember to fix the placement stuff to avoid it also and
722 * then remove this XXX */
723 a
= screen_physical_area_monitor(client_monitor(self
));
724 /* dont let windows map/move into the strut unless they
725 are bigger than the available area */
727 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
728 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
729 *x
= a
->x
+ a
->width
- w
;
731 if (h
<= a
->height
) {
732 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
733 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
734 *y
= a
->y
+ a
->height
- h
;
738 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
741 return ox
!= *x
|| oy
!= *y
;
744 static void client_toggle_border(ObClient
*self
, gboolean show
)
746 /* adjust our idea of where the client is, based on its border. When the
747 border is removed, the client should now be considered to be in a
749 when re-adding the border to the client, the same operation needs to be
751 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
752 gint x
= oldx
, y
= oldy
;
753 switch(self
->gravity
) {
755 case NorthWestGravity
:
757 case SouthWestGravity
:
759 case NorthEastGravity
:
761 case SouthEastGravity
:
762 if (show
) x
-= self
->border_width
* 2;
763 else x
+= self
->border_width
* 2;
770 if (show
) x
-= self
->border_width
;
771 else x
+= self
->border_width
;
774 switch(self
->gravity
) {
776 case NorthWestGravity
:
778 case NorthEastGravity
:
780 case SouthWestGravity
:
782 case SouthEastGravity
:
783 if (show
) y
-= self
->border_width
* 2;
784 else y
+= self
->border_width
* 2;
791 if (show
) y
-= self
->border_width
;
792 else y
+= self
->border_width
;
799 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
801 /* move the client so it is back it the right spot _with_ its
803 if (x
!= oldx
|| y
!= oldy
)
804 XMoveWindow(ob_display
, self
->window
, x
, y
);
806 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
810 static void client_get_all(ObClient
*self
)
812 client_get_area(self
);
813 client_update_transient_for(self
);
814 client_update_wmhints(self
);
815 client_get_startup_id(self
);
816 client_get_desktop(self
);
817 client_get_shaped(self
);
819 client_get_mwm_hints(self
);
820 client_get_type(self
);/* this can change the mwmhints for special cases */
822 /* The transient hint is used to pick a type, but the type can also affect
823 transiency (dialogs are always made transients). This is Havoc's idea,
824 but it is needed to make some apps work right (eg tsclient). */
825 client_update_transient_for(self
);
827 client_get_state(self
);
830 /* a couple type-based defaults for new windows */
832 /* this makes sure that these windows appear on all desktops */
833 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
834 self
->desktop
= DESKTOP_ALL
;
837 client_update_protocols(self
);
839 client_get_gravity(self
); /* get the attribute gravity */
840 client_update_normal_hints(self
); /* this may override the attribute
843 /* got the type, the mwmhints, the protocols, and the normal hints
844 (min/max sizes), so we're ready to set up the decorations/functions */
845 client_setup_decor_and_functions(self
);
847 client_update_title(self
);
848 client_update_class(self
);
849 client_update_sm_client_id(self
);
850 client_update_strut(self
);
851 client_update_icons(self
);
854 static void client_get_startup_id(ObClient
*self
)
856 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
858 PROP_GETS(self
->group
->leader
,
859 net_startup_id
, utf8
, &self
->startup_id
);
862 static void client_get_area(ObClient
*self
)
864 XWindowAttributes wattrib
;
867 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
868 g_assert(ret
!= BadWindow
);
870 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
871 self
->border_width
= wattrib
.border_width
;
874 static void client_get_desktop(ObClient
*self
)
876 guint32 d
= screen_num_desktops
; /* an always-invalid value */
878 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
879 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
880 self
->desktop
= screen_num_desktops
- 1;
884 gboolean trdesk
= FALSE
;
886 if (self
->transient_for
) {
887 if (self
->transient_for
!= OB_TRAN_GROUP
) {
888 self
->desktop
= self
->transient_for
->desktop
;
893 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
894 if (it
->data
!= self
&&
895 !((ObClient
*)it
->data
)->transient_for
) {
896 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
903 /* try get from the startup-notification protocol */
904 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
905 if (self
->desktop
>= screen_num_desktops
&&
906 self
->desktop
!= DESKTOP_ALL
)
907 self
->desktop
= screen_num_desktops
- 1;
909 /* defaults to the current desktop */
910 self
->desktop
= screen_desktop
;
913 if (self
->desktop
!= d
) {
914 /* set the desktop hint, to make sure that it always exists */
915 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
919 static void client_get_state(ObClient
*self
)
924 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
926 for (i
= 0; i
< num
; ++i
) {
927 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
929 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
931 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
933 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
934 self
->skip_taskbar
= TRUE
;
935 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
936 self
->skip_pager
= TRUE
;
937 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
938 self
->fullscreen
= TRUE
;
939 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
940 self
->max_vert
= TRUE
;
941 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
942 self
->max_horz
= TRUE
;
943 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
945 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
947 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
948 self
->undecorated
= TRUE
;
954 if (!(self
->above
|| self
->below
)) {
956 /* apply stuff from the group */
960 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
961 ObClient
*c
= it
->data
;
962 if (c
!= self
&& !client_search_transient(self
, c
) &&
963 client_normal(self
) && client_normal(c
))
966 (c
->above
? 1 : (c
->below
? -1 : 0)));
980 g_assert_not_reached();
987 static void client_get_shaped(ObClient
*self
)
989 self
->shaped
= FALSE
;
991 if (extensions_shape
) {
996 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
998 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
999 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1001 self
->shaped
= (s
!= 0);
1006 void client_update_transient_for(ObClient
*self
)
1009 ObClient
*target
= NULL
;
1011 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1012 self
->transient
= TRUE
;
1013 if (t
!= self
->window
) { /* cant be transient to itself! */
1014 target
= g_hash_table_lookup(window_map
, &t
);
1015 /* if this happens then we need to check for it*/
1016 g_assert(target
!= self
);
1017 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1018 /* this can happen when a dialog is a child of
1019 a dockapp, for example */
1023 if (!target
&& self
->group
) {
1024 /* not transient to a client, see if it is transient for a
1026 if (t
== self
->group
->leader
||
1028 t
== RootWindow(ob_display
, ob_screen
))
1030 /* window is a transient for its group! */
1031 target
= OB_TRAN_GROUP
;
1035 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
1036 self
->transient
= TRUE
;
1037 target
= OB_TRAN_GROUP
;
1039 self
->transient
= FALSE
;
1041 /* if anything has changed... */
1042 if (target
!= self
->transient_for
) {
1043 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1046 /* remove from old parents */
1047 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1048 ObClient
*c
= it
->data
;
1049 if (c
!= self
&& !c
->transient_for
)
1050 c
->transients
= g_slist_remove(c
->transients
, self
);
1052 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1053 /* remove from old parent */
1054 self
->transient_for
->transients
=
1055 g_slist_remove(self
->transient_for
->transients
, self
);
1057 self
->transient_for
= target
;
1058 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1061 /* add to new parents */
1062 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1063 ObClient
*c
= it
->data
;
1064 if (c
!= self
&& !c
->transient_for
)
1065 c
->transients
= g_slist_append(c
->transients
, self
);
1068 /* remove all transients which are in the group, that causes
1069 circlular pointer hell of doom */
1070 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1072 for (sit
= self
->transients
; sit
; sit
= next
) {
1073 next
= g_slist_next(sit
);
1074 if (sit
->data
== it
->data
)
1076 g_slist_delete_link(self
->transients
, sit
);
1079 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1080 /* add to new parent */
1081 self
->transient_for
->transients
=
1082 g_slist_append(self
->transient_for
->transients
, self
);
1087 static void client_get_mwm_hints(ObClient
*self
)
1092 self
->mwmhints
.flags
= 0; /* default to none */
1094 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1096 if (num
>= OB_MWM_ELEMENTS
) {
1097 self
->mwmhints
.flags
= hints
[0];
1098 self
->mwmhints
.functions
= hints
[1];
1099 self
->mwmhints
.decorations
= hints
[2];
1105 void client_get_type(ObClient
*self
)
1112 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1113 /* use the first value that we know about in the array */
1114 for (i
= 0; i
< num
; ++i
) {
1115 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1116 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1117 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1118 self
->type
= OB_CLIENT_TYPE_DOCK
;
1119 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1120 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1121 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1122 self
->type
= OB_CLIENT_TYPE_MENU
;
1123 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1124 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1125 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1126 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1127 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1128 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1129 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1130 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1131 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1132 /* prevent this window from getting any decor or
1134 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1135 OB_MWM_FLAG_DECORATIONS
);
1136 self
->mwmhints
.decorations
= 0;
1137 self
->mwmhints
.functions
= 0;
1139 if (self
->type
!= (ObClientType
) -1)
1140 break; /* grab the first legit type */
1145 if (self
->type
== (ObClientType
) -1) {
1146 /*the window type hint was not set, which means we either classify
1147 ourself as a normal window or a dialog, depending on if we are a
1149 if (self
->transient
)
1150 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1152 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1156 void client_update_protocols(ObClient
*self
)
1159 guint num_return
, i
;
1161 self
->focus_notify
= FALSE
;
1162 self
->delete_window
= FALSE
;
1164 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1165 for (i
= 0; i
< num_return
; ++i
) {
1166 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1167 /* this means we can request the window to close */
1168 self
->delete_window
= TRUE
;
1169 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1170 /* if this protocol is requested, then the window will be
1171 notified whenever we want it to receive focus */
1172 self
->focus_notify
= TRUE
;
1178 static void client_get_gravity(ObClient
*self
)
1180 XWindowAttributes wattrib
;
1183 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1184 g_assert(ret
!= BadWindow
);
1185 self
->gravity
= wattrib
.win_gravity
;
1188 void client_update_normal_hints(ObClient
*self
)
1192 gint oldgravity
= self
->gravity
;
1195 self
->min_ratio
= 0.0f
;
1196 self
->max_ratio
= 0.0f
;
1197 SIZE_SET(self
->size_inc
, 1, 1);
1198 SIZE_SET(self
->base_size
, 0, 0);
1199 SIZE_SET(self
->min_size
, 0, 0);
1200 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1202 /* get the hints from the window */
1203 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1204 /* normal windows can't request placement! har har
1205 if (!client_normal(self))
1207 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1209 if (size
.flags
& PWinGravity
) {
1210 self
->gravity
= size
.win_gravity
;
1212 /* if the client has a frame, i.e. has already been mapped and
1213 is changing its gravity */
1214 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1215 /* move our idea of the client's position based on its new
1217 self
->area
.x
= self
->frame
->area
.x
;
1218 self
->area
.y
= self
->frame
->area
.y
;
1219 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1223 if (size
.flags
& PAspect
) {
1224 if (size
.min_aspect
.y
)
1226 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1227 if (size
.max_aspect
.y
)
1229 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1232 if (size
.flags
& PMinSize
)
1233 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1235 if (size
.flags
& PMaxSize
)
1236 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1238 if (size
.flags
& PBaseSize
)
1239 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1241 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1242 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1246 void client_setup_decor_and_functions(ObClient
*self
)
1248 /* start with everything (cept fullscreen) */
1250 (OB_FRAME_DECOR_TITLEBAR
|
1251 OB_FRAME_DECOR_HANDLE
|
1252 OB_FRAME_DECOR_GRIPS
|
1253 OB_FRAME_DECOR_BORDER
|
1254 OB_FRAME_DECOR_ICON
|
1255 OB_FRAME_DECOR_ALLDESKTOPS
|
1256 OB_FRAME_DECOR_ICONIFY
|
1257 OB_FRAME_DECOR_MAXIMIZE
|
1258 OB_FRAME_DECOR_SHADE
|
1259 OB_FRAME_DECOR_CLOSE
);
1261 (OB_CLIENT_FUNC_RESIZE
|
1262 OB_CLIENT_FUNC_MOVE
|
1263 OB_CLIENT_FUNC_ICONIFY
|
1264 OB_CLIENT_FUNC_MAXIMIZE
|
1265 OB_CLIENT_FUNC_SHADE
|
1266 OB_CLIENT_FUNC_CLOSE
);
1268 if (!(self
->min_size
.width
< self
->max_size
.width
||
1269 self
->min_size
.height
< self
->max_size
.height
))
1270 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1272 switch (self
->type
) {
1273 case OB_CLIENT_TYPE_NORMAL
:
1274 /* normal windows retain all of the possible decorations and
1275 functionality, and are the only windows that you can fullscreen */
1276 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1279 case OB_CLIENT_TYPE_DIALOG
:
1280 case OB_CLIENT_TYPE_UTILITY
:
1281 /* these windows cannot be maximized */
1282 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1285 case OB_CLIENT_TYPE_MENU
:
1286 case OB_CLIENT_TYPE_TOOLBAR
:
1287 /* these windows get less functionality */
1288 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1291 case OB_CLIENT_TYPE_DESKTOP
:
1292 case OB_CLIENT_TYPE_DOCK
:
1293 case OB_CLIENT_TYPE_SPLASH
:
1294 /* none of these windows are manipulated by the window manager */
1295 self
->decorations
= 0;
1296 self
->functions
= 0;
1300 /* Mwm Hints are applied subtractively to what has already been chosen for
1301 decor and functionality */
1302 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1303 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1304 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1305 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1306 /* if the mwm hints request no handle or title, then all
1307 decorations are disabled, but keep the border if that's
1309 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1310 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1312 self
->decorations
= 0;
1316 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1317 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1318 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1319 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1320 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1321 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1322 /* dont let mwm hints kill any buttons
1323 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1324 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1325 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1326 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1328 /* dont let mwm hints kill the close button
1329 if (! (self->mwmhints.functions & MwmFunc_Close))
1330 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1334 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1335 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1336 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1337 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1338 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1339 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1341 /* can't maximize without moving/resizing */
1342 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1343 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1344 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1345 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1346 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1349 /* kill the handle on fully maxed windows */
1350 if (self
->max_vert
&& self
->max_horz
)
1351 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1353 /* finally, the user can have requested no decorations, which overrides
1354 everything (but doesnt give it a border if it doesnt have one) */
1355 if (self
->undecorated
) {
1356 if (config_theme_keepborder
)
1357 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1359 self
->decorations
= 0;
1362 /* if we don't have a titlebar, then we cannot shade! */
1363 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1364 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1366 /* now we need to check against rules for the client's current state */
1367 if (self
->fullscreen
) {
1368 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1369 OB_CLIENT_FUNC_FULLSCREEN
|
1370 OB_CLIENT_FUNC_ICONIFY
);
1371 self
->decorations
= 0;
1374 client_change_allowed_actions(self
);
1377 /* adjust the client's decorations, etc. */
1378 client_reconfigure(self
);
1382 static void client_change_allowed_actions(ObClient
*self
)
1387 /* desktop windows are kept on all desktops */
1388 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1389 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1391 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1392 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1393 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1394 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1395 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1396 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1397 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1398 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1399 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1400 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1401 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1402 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1403 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1404 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1405 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1408 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1410 /* make sure the window isn't breaking any rules now */
1412 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1413 if (self
->frame
) client_shade(self
, FALSE
);
1414 else self
->shaded
= FALSE
;
1416 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1417 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1418 else self
->iconic
= FALSE
;
1420 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1421 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1422 else self
->fullscreen
= FALSE
;
1424 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1426 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1427 else self
->max_vert
= self
->max_horz
= FALSE
;
1431 void client_reconfigure(ObClient
*self
)
1433 /* by making this pass FALSE for user, we avoid the emacs event storm where
1434 every configurenotify causes an update in its normal hints, i think this
1435 is generally what we want anyways... */
1436 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1437 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1440 void client_update_wmhints(ObClient
*self
)
1443 gboolean ur
= FALSE
;
1446 /* assume a window takes input if it doesnt specify */
1447 self
->can_focus
= TRUE
;
1449 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1450 if (hints
->flags
& InputHint
)
1451 self
->can_focus
= hints
->input
;
1453 /* only do this when first managing the window *AND* when we aren't
1455 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1456 if (hints
->flags
& StateHint
)
1457 self
->iconic
= hints
->initial_state
== IconicState
;
1459 if (hints
->flags
& XUrgencyHint
)
1462 if (!(hints
->flags
& WindowGroupHint
))
1463 hints
->window_group
= None
;
1465 /* did the group state change? */
1466 if (hints
->window_group
!=
1467 (self
->group
? self
->group
->leader
: None
)) {
1468 /* remove from the old group if there was one */
1469 if (self
->group
!= NULL
) {
1470 /* remove transients of the group */
1471 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1472 self
->transients
= g_slist_remove(self
->transients
,
1475 /* remove myself from parents in the group */
1476 if (self
->transient_for
== OB_TRAN_GROUP
) {
1477 for (it
= self
->group
->members
; it
;
1478 it
= g_slist_next(it
))
1480 ObClient
*c
= it
->data
;
1482 if (c
!= self
&& !c
->transient_for
)
1483 c
->transients
= g_slist_remove(c
->transients
,
1488 group_remove(self
->group
, self
);
1491 if (hints
->window_group
!= None
) {
1492 self
->group
= group_add(hints
->window_group
, self
);
1494 /* i can only have transients from the group if i am not
1496 if (!self
->transient_for
) {
1497 /* add other transients of the group that are already
1499 for (it
= self
->group
->members
; it
;
1500 it
= g_slist_next(it
))
1502 ObClient
*c
= it
->data
;
1503 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1505 g_slist_append(self
->transients
, c
);
1510 /* because the self->transient flag wont change from this call,
1511 we don't need to update the window's type and such, only its
1512 transient_for, and the transients lists of other windows in
1513 the group may be affected */
1514 client_update_transient_for(self
);
1517 /* the WM_HINTS can contain an icon */
1518 client_update_icons(self
);
1523 if (ur
!= self
->urgent
) {
1525 /* fire the urgent callback if we're mapped, otherwise, wait until
1526 after we're mapped */
1528 client_urgent_notify(self
);
1532 void client_update_title(ObClient
*self
)
1538 gboolean read_title
;
1541 old_title
= self
->title
;
1544 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1545 /* try old x stuff */
1546 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1547 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1548 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1549 if (self
->transient
) {
1550 data
= g_strdup("");
1553 data
= g_strdup("Unnamed Window");
1557 if (config_title_number
) {
1559 /* did the title change? then reset the title_count */
1560 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1561 self
->title_count
= 1;
1563 /* look for duplicates and append a number */
1565 for (it
= client_list
; it
; it
= g_list_next(it
))
1566 if (it
->data
!= self
) {
1567 ObClient
*c
= it
->data
;
1568 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1569 nums
|= 1 << c
->title_count
;
1571 /* find first free number */
1572 for (i
= 1; i
<= 32; ++i
)
1573 if (!(nums
& (1 << i
))) {
1574 if (self
->title_count
== 1 || i
== 1)
1575 self
->title_count
= i
;
1578 /* dont display the number for the first window */
1579 if (self
->title_count
> 1) {
1581 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1586 self
->title_count
= 1;
1589 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1593 frame_adjust_title(self
->frame
);
1597 /* update the icon title */
1599 g_free(self
->icon_title
);
1603 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1604 /* try old x stuff */
1605 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)
1606 || PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
))) {
1607 data
= g_strdup(self
->title
);
1611 /* append the title count, dont display the number for the first window.
1612 * We don't need to check for config_title_number here since title_count
1613 * is not set above 1 then. */
1614 if (read_title
&& self
->title_count
> 1) {
1615 gchar
*vdata
, *ndata
;
1616 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1617 vdata
= g_strconcat(data
, ndata
, NULL
);
1623 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1625 self
->icon_title
= data
;
1628 void client_update_class(ObClient
*self
)
1633 if (self
->name
) g_free(self
->name
);
1634 if (self
->class) g_free(self
->class);
1635 if (self
->role
) g_free(self
->role
);
1637 self
->name
= self
->class = self
->role
= NULL
;
1639 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1641 self
->name
= g_strdup(data
[0]);
1643 self
->class = g_strdup(data
[1]);
1648 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1651 if (self
->name
== NULL
) self
->name
= g_strdup("");
1652 if (self
->class == NULL
) self
->class = g_strdup("");
1653 if (self
->role
== NULL
) self
->role
= g_strdup("");
1656 void client_update_strut(ObClient
*self
)
1660 gboolean got
= FALSE
;
1663 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1667 STRUT_PARTIAL_SET(strut
,
1668 data
[0], data
[2], data
[1], data
[3],
1669 data
[4], data
[5], data
[8], data
[9],
1670 data
[6], data
[7], data
[10], data
[11]);
1676 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1682 /* use the screen's width/height */
1683 a
= screen_physical_area();
1685 STRUT_PARTIAL_SET(strut
,
1686 data
[0], data
[2], data
[1], data
[3],
1687 a
->y
, a
->y
+ a
->height
- 1,
1688 a
->x
, a
->x
+ a
->width
- 1,
1689 a
->y
, a
->y
+ a
->height
- 1,
1690 a
->x
, a
->x
+ a
->width
- 1);
1696 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1697 0, 0, 0, 0, 0, 0, 0, 0);
1699 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1700 self
->strut
= strut
;
1702 /* updating here is pointless while we're being mapped cuz we're not in
1703 the client list yet */
1705 screen_update_areas();
1709 void client_update_icons(ObClient
*self
)
1715 for (i
= 0; i
< self
->nicons
; ++i
)
1716 g_free(self
->icons
[i
].data
);
1717 if (self
->nicons
> 0)
1718 g_free(self
->icons
);
1721 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1722 /* figure out how many valid icons are in here */
1724 while (num
- i
> 2) {
1728 if (i
> num
|| w
*h
== 0) break;
1732 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1734 /* store the icons */
1736 for (j
= 0; j
< self
->nicons
; ++j
) {
1739 w
= self
->icons
[j
].width
= data
[i
++];
1740 h
= self
->icons
[j
].height
= data
[i
++];
1742 if (w
*h
== 0) continue;
1744 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1745 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1750 self
->icons
[j
].data
[t
] =
1751 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1752 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1753 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1754 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1760 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1761 kwm_win_icon
, &data
, &num
)) {
1764 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1765 xerror_set_ignore(TRUE
);
1766 if (!RrPixmapToRGBA(ob_rr_inst
,
1768 &self
->icons
[self
->nicons
-1].width
,
1769 &self
->icons
[self
->nicons
-1].height
,
1770 &self
->icons
[self
->nicons
-1].data
)) {
1771 g_free(&self
->icons
[self
->nicons
-1]);
1774 xerror_set_ignore(FALSE
);
1780 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1781 if (hints
->flags
& IconPixmapHint
) {
1783 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1784 xerror_set_ignore(TRUE
);
1785 if (!RrPixmapToRGBA(ob_rr_inst
,
1787 (hints
->flags
& IconMaskHint
?
1788 hints
->icon_mask
: None
),
1789 &self
->icons
[self
->nicons
-1].width
,
1790 &self
->icons
[self
->nicons
-1].height
,
1791 &self
->icons
[self
->nicons
-1].data
)){
1792 g_free(&self
->icons
[self
->nicons
-1]);
1795 xerror_set_ignore(FALSE
);
1802 frame_adjust_icon(self
->frame
);
1805 static void client_change_state(ObClient
*self
)
1808 gulong netstate
[11];
1811 state
[0] = self
->wmstate
;
1813 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1817 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1819 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1821 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1822 if (self
->skip_taskbar
)
1823 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1824 if (self
->skip_pager
)
1825 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1826 if (self
->fullscreen
)
1827 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1829 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1831 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1833 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1835 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1836 if (self
->undecorated
)
1837 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1838 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1840 client_calc_layer(self
);
1843 frame_adjust_state(self
->frame
);
1846 ObClient
*client_search_focus_tree(ObClient
*self
)
1851 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1852 if (client_focused(it
->data
)) return it
->data
;
1853 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1858 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1860 if (self
->transient_for
) {
1861 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1862 return client_search_focus_tree_full(self
->transient_for
);
1865 gboolean recursed
= FALSE
;
1867 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1868 if (!((ObClient
*)it
->data
)->transient_for
) {
1870 if ((c
= client_search_focus_tree_full(it
->data
)))
1879 /* this function checks the whole tree, the client_search_focus_tree~
1880 does not, so we need to check this window */
1881 if (client_focused(self
))
1883 return client_search_focus_tree(self
);
1886 static ObStackingLayer
calc_layer(ObClient
*self
)
1890 if (self
->fullscreen
&&
1891 (client_focused(self
) || client_search_focus_tree(self
)))
1892 l
= OB_STACKING_LAYER_FULLSCREEN
;
1893 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1894 l
= OB_STACKING_LAYER_DESKTOP
;
1895 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1896 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1897 else l
= OB_STACKING_LAYER_ABOVE
;
1899 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1900 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1901 else l
= OB_STACKING_LAYER_NORMAL
;
1906 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1907 ObStackingLayer l
, gboolean raised
)
1909 ObStackingLayer old
, own
;
1913 own
= calc_layer(self
);
1914 self
->layer
= l
> own
? l
: own
;
1916 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1917 client_calc_layer_recursive(it
->data
, orig
,
1918 l
, raised
? raised
: l
!= old
);
1920 if (!raised
&& l
!= old
)
1921 if (orig
->frame
) { /* only restack if the original window is managed */
1922 stacking_remove(CLIENT_AS_WINDOW(self
));
1923 stacking_add(CLIENT_AS_WINDOW(self
));
1927 void client_calc_layer(ObClient
*self
)
1934 /* transients take on the layer of their parents */
1935 self
= client_search_top_transient(self
);
1937 l
= calc_layer(self
);
1939 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1942 gboolean
client_should_show(ObClient
*self
)
1946 if (client_normal(self
) && screen_showing_desktop
)
1949 if (self->transient_for) {
1950 if (self->transient_for != OB_TRAN_GROUP)
1951 return client_should_show(self->transient_for);
1955 for (it = self->group->members; it; it = g_slist_next(it)) {
1956 ObClient *c = it->data;
1957 if (c != self && !c->transient_for) {
1958 if (client_should_show(c))
1965 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
1971 static void client_showhide(ObClient
*self
)
1974 if (client_should_show(self
))
1975 frame_show(self
->frame
);
1977 frame_hide(self
->frame
);
1980 gboolean
client_normal(ObClient
*self
) {
1981 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1982 self
->type
== OB_CLIENT_TYPE_DOCK
||
1983 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1986 static void client_apply_startup_state(ObClient
*self
)
1988 /* these are in a carefully crafted order.. */
1991 self
->iconic
= FALSE
;
1992 client_iconify(self
, TRUE
, FALSE
);
1994 if (self
->fullscreen
) {
1995 self
->fullscreen
= FALSE
;
1996 client_fullscreen(self
, TRUE
, FALSE
);
1998 if (self
->undecorated
) {
1999 self
->undecorated
= FALSE
;
2000 client_set_undecorated(self
, TRUE
);
2003 self
->shaded
= FALSE
;
2004 client_shade(self
, TRUE
);
2007 client_urgent_notify(self
);
2009 if (self
->max_vert
&& self
->max_horz
) {
2010 self
->max_vert
= self
->max_horz
= FALSE
;
2011 client_maximize(self
, TRUE
, 0, FALSE
);
2012 } else if (self
->max_vert
) {
2013 self
->max_vert
= FALSE
;
2014 client_maximize(self
, TRUE
, 2, FALSE
);
2015 } else if (self
->max_horz
) {
2016 self
->max_horz
= FALSE
;
2017 client_maximize(self
, TRUE
, 1, FALSE
);
2020 /* nothing to do for the other states:
2029 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2030 gint x
, gint y
, gint w
, gint h
,
2031 gboolean user
, gboolean final
,
2032 gboolean force_reply
)
2035 gboolean send_resize_client
;
2036 gboolean moved
= FALSE
, resized
= FALSE
;
2037 guint fdecor
= self
->frame
->decorations
;
2038 gboolean fhorz
= self
->frame
->max_horz
;
2040 /* make the frame recalculate its dimentions n shit without changing
2041 anything visible for real, this way the constraints below can work with
2042 the updated frame dimensions. */
2043 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2045 /* gets the frame's position */
2046 frame_client_gravity(self
->frame
, &x
, &y
);
2048 /* these positions are frame positions, not client positions */
2050 /* set the size and position if fullscreen */
2051 if (self
->fullscreen
) {
2055 i
= client_monitor(self
);
2056 a
= screen_physical_area_monitor(i
);
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
);
2350 client_change_state(self
);
2351 client_showhide(self
);
2352 screen_update_areas();
2355 /* iconify all transients */
2356 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2357 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2361 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2363 /* move up the transient chain as far as possible first */
2364 self
= client_search_top_transient(self
);
2366 client_iconify_recursive(client_search_top_transient(self
),
2370 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2374 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2375 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2377 /* check if already done */
2379 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2380 if (dir
== 1 && self
->max_horz
) return;
2381 if (dir
== 2 && self
->max_vert
) return;
2383 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2384 if (dir
== 1 && !self
->max_horz
) return;
2385 if (dir
== 2 && !self
->max_vert
) return;
2388 /* we just tell it to configure in the same place and client_configure
2389 worries about filling the screen with the window */
2392 w
= self
->area
.width
;
2393 h
= self
->area
.height
;
2397 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2398 RECT_SET(self
->pre_max_area
,
2399 self
->area
.x
, self
->pre_max_area
.y
,
2400 self
->area
.width
, self
->pre_max_area
.height
);
2402 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2403 RECT_SET(self
->pre_max_area
,
2404 self
->pre_max_area
.x
, self
->area
.y
,
2405 self
->pre_max_area
.width
, self
->area
.height
);
2411 a
= screen_area_monitor(self
->desktop
, 0);
2412 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2413 if (self
->pre_max_area
.width
> 0) {
2414 x
= self
->pre_max_area
.x
;
2415 w
= self
->pre_max_area
.width
;
2417 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2418 0, self
->pre_max_area
.height
);
2420 /* pick some fallbacks... */
2421 x
= a
->x
+ a
->width
/ 4;
2425 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2426 if (self
->pre_max_area
.height
> 0) {
2427 y
= self
->pre_max_area
.y
;
2428 h
= self
->pre_max_area
.height
;
2430 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2431 self
->pre_max_area
.width
, 0);
2433 /* pick some fallbacks... */
2434 y
= a
->y
+ a
->height
/ 4;
2440 if (dir
== 0 || dir
== 1) /* horz */
2441 self
->max_horz
= max
;
2442 if (dir
== 0 || dir
== 2) /* vert */
2443 self
->max_vert
= max
;
2445 client_change_state(self
); /* change the state hints on the client */
2447 client_setup_decor_and_functions(self
);
2449 client_move_resize(self
, x
, y
, w
, h
);
2452 void client_shade(ObClient
*self
, gboolean shade
)
2454 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2455 shade
) || /* can't shade */
2456 self
->shaded
== shade
) return; /* already done */
2458 /* when we're iconic, don't change the wmstate */
2459 if (!self
->iconic
) {
2462 old
= self
->wmstate
;
2463 self
->wmstate
= shade
? IconicState
: NormalState
;
2464 if (old
!= self
->wmstate
)
2465 PROP_MSG(self
->window
, kde_wm_change_state
,
2466 self
->wmstate
, 1, 0, 0);
2469 self
->shaded
= shade
;
2470 client_change_state(self
);
2471 /* resize the frame to just the titlebar */
2472 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2475 void client_close(ObClient
*self
)
2479 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2481 /* in the case that the client provides no means to requesting that it
2482 close, we just kill it */
2483 if (!self
->delete_window
)
2487 XXX: itd be cool to do timeouts and shit here for killing the client's
2489 like... if the window is around after 5 seconds, then the close button
2490 turns a nice red, and if this function is called again, the client is
2494 ce
.xclient
.type
= ClientMessage
;
2495 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2496 ce
.xclient
.display
= ob_display
;
2497 ce
.xclient
.window
= self
->window
;
2498 ce
.xclient
.format
= 32;
2499 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2500 ce
.xclient
.data
.l
[1] = event_lasttime
;
2501 ce
.xclient
.data
.l
[2] = 0l;
2502 ce
.xclient
.data
.l
[3] = 0l;
2503 ce
.xclient
.data
.l
[4] = 0l;
2504 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2507 void client_kill(ObClient
*self
)
2509 XKillClient(ob_display
, self
->window
);
2512 void client_set_desktop_recursive(ObClient
*self
,
2513 guint target
, gboolean donthide
)
2518 if (target
!= self
->desktop
) {
2520 ob_debug("Setting desktop %u\n", target
+1);
2522 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2524 /* remove from the old desktop(s) */
2525 focus_order_remove(self
);
2527 old
= self
->desktop
;
2528 self
->desktop
= target
;
2529 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2530 /* the frame can display the current desktop state */
2531 frame_adjust_state(self
->frame
);
2532 /* 'move' the window to the new desktop */
2534 client_showhide(self
);
2535 /* raise if it was not already on the desktop */
2536 if (old
!= DESKTOP_ALL
)
2538 screen_update_areas();
2540 /* add to the new desktop(s) */
2541 if (config_focus_new
)
2542 focus_order_to_top(self
);
2544 focus_order_to_bottom(self
);
2547 /* move all transients */
2548 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2549 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2553 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2555 client_set_desktop_recursive(client_search_top_transient(self
),
2559 ObClient
*client_search_modal_child(ObClient
*self
)
2564 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2565 ObClient
*c
= it
->data
;
2566 if ((ret
= client_search_modal_child(c
))) return ret
;
2567 if (c
->modal
) return c
;
2572 gboolean
client_validate(ObClient
*self
)
2576 XSync(ob_display
, FALSE
); /* get all events on the server */
2578 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2579 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2580 XPutBackEvent(ob_display
, &e
);
2587 void client_set_wm_state(ObClient
*self
, glong state
)
2589 if (state
== self
->wmstate
) return; /* no change */
2593 client_iconify(self
, TRUE
, TRUE
);
2596 client_iconify(self
, FALSE
, TRUE
);
2601 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2603 gboolean shaded
= self
->shaded
;
2604 gboolean fullscreen
= self
->fullscreen
;
2605 gboolean undecorated
= self
->undecorated
;
2606 gboolean max_horz
= self
->max_horz
;
2607 gboolean max_vert
= self
->max_vert
;
2608 gboolean modal
= self
->modal
;
2609 gboolean iconic
= self
->iconic
;
2612 if (!(action
== prop_atoms
.net_wm_state_add
||
2613 action
== prop_atoms
.net_wm_state_remove
||
2614 action
== prop_atoms
.net_wm_state_toggle
))
2615 /* an invalid action was passed to the client message, ignore it */
2618 for (i
= 0; i
< 2; ++i
) {
2619 Atom state
= i
== 0 ? data1
: data2
;
2621 if (!state
) continue;
2623 /* if toggling, then pick whether we're adding or removing */
2624 if (action
== prop_atoms
.net_wm_state_toggle
) {
2625 if (state
== prop_atoms
.net_wm_state_modal
)
2626 action
= modal
? prop_atoms
.net_wm_state_remove
:
2627 prop_atoms
.net_wm_state_add
;
2628 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2629 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2630 prop_atoms
.net_wm_state_add
;
2631 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2632 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2633 prop_atoms
.net_wm_state_add
;
2634 else if (state
== prop_atoms
.net_wm_state_shaded
)
2635 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2636 prop_atoms
.net_wm_state_add
;
2637 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2638 action
= self
->skip_taskbar
?
2639 prop_atoms
.net_wm_state_remove
:
2640 prop_atoms
.net_wm_state_add
;
2641 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2642 action
= self
->skip_pager
?
2643 prop_atoms
.net_wm_state_remove
:
2644 prop_atoms
.net_wm_state_add
;
2645 else if (state
== prop_atoms
.net_wm_state_hidden
)
2646 action
= self
->iconic
?
2647 prop_atoms
.net_wm_state_remove
:
2648 prop_atoms
.net_wm_state_add
;
2649 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2650 action
= fullscreen
?
2651 prop_atoms
.net_wm_state_remove
:
2652 prop_atoms
.net_wm_state_add
;
2653 else if (state
== prop_atoms
.net_wm_state_above
)
2654 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2655 prop_atoms
.net_wm_state_add
;
2656 else if (state
== prop_atoms
.net_wm_state_below
)
2657 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2658 prop_atoms
.net_wm_state_add
;
2659 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2660 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2661 prop_atoms
.net_wm_state_add
;
2664 if (action
== prop_atoms
.net_wm_state_add
) {
2665 if (state
== prop_atoms
.net_wm_state_modal
) {
2667 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2669 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2671 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2673 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2674 self
->skip_taskbar
= TRUE
;
2675 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2676 self
->skip_pager
= TRUE
;
2677 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2679 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2681 } else if (state
== prop_atoms
.net_wm_state_above
) {
2683 self
->below
= FALSE
;
2684 } else if (state
== prop_atoms
.net_wm_state_below
) {
2685 self
->above
= FALSE
;
2687 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2691 } else { /* action == prop_atoms.net_wm_state_remove */
2692 if (state
== prop_atoms
.net_wm_state_modal
) {
2694 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2696 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2698 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2700 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2701 self
->skip_taskbar
= FALSE
;
2702 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2703 self
->skip_pager
= FALSE
;
2704 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2706 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2708 } else if (state
== prop_atoms
.net_wm_state_above
) {
2709 self
->above
= FALSE
;
2710 } else if (state
== prop_atoms
.net_wm_state_below
) {
2711 self
->below
= FALSE
;
2712 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2713 undecorated
= FALSE
;
2717 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2718 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2720 if (max_horz
== max_vert
) { /* both going the same way */
2721 client_maximize(self
, max_horz
, 0, TRUE
);
2723 client_maximize(self
, max_horz
, 1, TRUE
);
2724 client_maximize(self
, max_vert
, 2, TRUE
);
2728 if (max_horz
!= self
->max_horz
)
2729 client_maximize(self
, max_horz
, 1, TRUE
);
2731 client_maximize(self
, max_vert
, 2, TRUE
);
2734 /* change fullscreen state before shading, as it will affect if the window
2736 if (fullscreen
!= self
->fullscreen
)
2737 client_fullscreen(self
, fullscreen
, TRUE
);
2738 if (shaded
!= self
->shaded
)
2739 client_shade(self
, shaded
);
2740 if (undecorated
!= self
->undecorated
)
2741 client_set_undecorated(self
, undecorated
);
2742 if (modal
!= self
->modal
) {
2743 self
->modal
= modal
;
2744 /* when a window changes modality, then its stacking order with its
2745 transients needs to change */
2748 if (iconic
!= self
->iconic
)
2749 client_iconify(self
, iconic
, FALSE
);
2751 client_calc_layer(self
);
2752 client_change_state(self
); /* change the hint to reflect these changes */
2755 ObClient
*client_focus_target(ObClient
*self
)
2759 /* if we have a modal child, then focus it, not us */
2760 child
= client_search_modal_child(client_search_top_transient(self
));
2761 if (child
) return child
;
2765 gboolean
client_can_focus(ObClient
*self
)
2769 /* choose the correct target */
2770 self
= client_focus_target(self
);
2772 if (!self
->frame
->visible
)
2775 if (!(self
->can_focus
|| self
->focus_notify
))
2778 /* do a check to see if the window has already been unmapped or destroyed
2779 do this intelligently while watching out for unmaps we've generated
2780 (ignore_unmaps > 0) */
2781 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2782 DestroyNotify
, &ev
)) {
2783 XPutBackEvent(ob_display
, &ev
);
2786 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2787 UnmapNotify
, &ev
)) {
2788 if (self
->ignore_unmaps
) {
2789 self
->ignore_unmaps
--;
2791 XPutBackEvent(ob_display
, &ev
);
2799 gboolean
client_focus(ObClient
*self
)
2801 /* choose the correct target */
2802 self
= client_focus_target(self
);
2804 if (!client_can_focus(self
)) {
2805 if (!self
->frame
->visible
) {
2806 /* update the focus lists */
2807 focus_order_to_top(self
);
2812 if (self
->can_focus
) {
2813 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2814 I choose to use it always, hopefully to find errors quicker, if any
2815 are left. (I hate X. I hate focus events.)
2817 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2818 #799. So now it is RevertToNone again.
2820 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2824 if (self
->focus_notify
) {
2826 ce
.xclient
.type
= ClientMessage
;
2827 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2828 ce
.xclient
.display
= ob_display
;
2829 ce
.xclient
.window
= self
->window
;
2830 ce
.xclient
.format
= 32;
2831 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2832 ce
.xclient
.data
.l
[1] = event_lasttime
;
2833 ce
.xclient
.data
.l
[2] = 0l;
2834 ce
.xclient
.data
.l
[3] = 0l;
2835 ce
.xclient
.data
.l
[4] = 0l;
2836 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2840 ob_debug("%sively focusing %lx at %d\n",
2841 (self
->can_focus
? "act" : "pass"),
2842 self
->window
, (gint
) event_lasttime
);
2845 /* Cause the FocusIn to come back to us. Important for desktop switches,
2846 since otherwise we'll have no FocusIn on the queue and send it off to
2847 the focus_backup. */
2848 XSync(ob_display
, FALSE
);
2852 /* Used when the current client is closed, focus_last will then prevent
2853 * focus from going to the mouse pointer */
2854 void client_unfocus(ObClient
*self
)
2856 if (focus_client
== self
) {
2858 ob_debug("client_unfocus for %lx\n", self
->window
);
2860 focus_fallback(OB_FOCUS_FALLBACK_CLOSED
);
2864 void client_activate(ObClient
*self
, gboolean here
)
2866 if (client_normal(self
) && screen_showing_desktop
)
2867 screen_show_desktop(FALSE
);
2869 client_iconify(self
, FALSE
, here
);
2870 if (self
->desktop
!= DESKTOP_ALL
&&
2871 self
->desktop
!= screen_desktop
) {
2873 client_set_desktop(self
, screen_desktop
, FALSE
);
2875 screen_set_desktop(self
->desktop
);
2876 } else if (!self
->frame
->visible
)
2877 /* if its not visible for other reasons, then don't mess
2881 client_shade(self
, FALSE
);
2885 /* we do this an action here. this is rather important. this is because
2886 we want the results from the focus change to take place BEFORE we go
2887 about raising the window. when a fullscreen window loses focus, we need
2888 this or else the raise wont be able to raise above the to-lose-focus
2889 fullscreen window. */
2893 void client_raise(ObClient
*self
)
2895 action_run_string("Raise", self
);
2898 void client_lower(ObClient
*self
)
2900 action_run_string("Lower", self
);
2903 gboolean
client_focused(ObClient
*self
)
2905 return self
== focus_client
;
2908 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
2911 /* si is the smallest image >= req */
2912 /* li is the largest image < req */
2913 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2915 if (!self
->nicons
) {
2916 ObClientIcon
*parent
= NULL
;
2918 if (self
->transient_for
) {
2919 if (self
->transient_for
!= OB_TRAN_GROUP
)
2920 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2923 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2924 ObClient
*c
= it
->data
;
2925 if (c
!= self
&& !c
->transient_for
) {
2926 if ((parent
= client_icon_recursive(c
, w
, h
)))
2936 for (i
= 0; i
< self
->nicons
; ++i
) {
2937 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2938 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2942 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2947 if (largest
== 0) /* didnt find one smaller than the requested size */
2948 return &self
->icons
[si
];
2949 return &self
->icons
[li
];
2952 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
2955 static ObClientIcon deficon
;
2957 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
2958 deficon
.width
= deficon
.height
= 48;
2959 deficon
.data
= ob_rr_theme
->def_win_icon
;
2965 /* this be mostly ripped from fvwm */
2966 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2968 gint my_cx
, my_cy
, his_cx
, his_cy
;
2971 gint score
, best_score
;
2972 ObClient
*best_client
, *cur
;
2978 /* first, find the centre coords of the currently focused window */
2979 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2980 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2985 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
2988 /* the currently selected window isn't interesting */
2991 if (!client_normal(cur
))
2993 /* using c->desktop instead of screen_desktop doesn't work if the
2994 * current window was omnipresent, hope this doesn't have any other
2996 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3000 if(client_focus_target(cur
) == cur
&&
3001 !(cur
->can_focus
|| cur
->focus_notify
))
3004 /* find the centre coords of this window, from the
3005 * currently focused window's point of view */
3006 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3007 + cur
->frame
->area
.width
/ 2;
3008 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3009 + cur
->frame
->area
.height
/ 2;
3011 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3012 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3014 /* Rotate the diagonals 45 degrees counterclockwise.
3015 * To do this, multiply the matrix /+h +h\ with the
3016 * vector (x y). \-h +h/
3017 * h = sqrt(0.5). We can set h := 1 since absolute
3018 * distance doesn't matter here. */
3019 tx
= his_cx
+ his_cy
;
3020 his_cy
= -his_cx
+ his_cy
;
3025 case OB_DIRECTION_NORTH
:
3026 case OB_DIRECTION_SOUTH
:
3027 case OB_DIRECTION_NORTHEAST
:
3028 case OB_DIRECTION_SOUTHWEST
:
3029 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3030 distance
= ((dir
== OB_DIRECTION_NORTH
||
3031 dir
== OB_DIRECTION_NORTHEAST
) ?
3034 case OB_DIRECTION_EAST
:
3035 case OB_DIRECTION_WEST
:
3036 case OB_DIRECTION_SOUTHEAST
:
3037 case OB_DIRECTION_NORTHWEST
:
3038 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3039 distance
= ((dir
== OB_DIRECTION_WEST
||
3040 dir
== OB_DIRECTION_NORTHWEST
) ?
3045 /* the target must be in the requested direction */
3049 /* Calculate score for this window. The smaller the better. */
3050 score
= distance
+ offset
;
3052 /* windows more than 45 degrees off the direction are
3053 * heavily penalized and will only be chosen if nothing
3054 * else within a million pixels */
3055 if(offset
> distance
)
3058 if(best_score
== -1 || score
< best_score
)
3066 void client_set_layer(ObClient
*self
, gint layer
)
3070 self
->above
= FALSE
;
3071 } else if (layer
== 0) {
3072 self
->below
= self
->above
= FALSE
;
3074 self
->below
= FALSE
;
3077 client_calc_layer(self
);
3078 client_change_state(self
); /* reflect this in the state hints */
3081 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3083 if (self
->undecorated
!= undecorated
) {
3084 self
->undecorated
= undecorated
;
3085 client_setup_decor_and_functions(self
);
3086 /* Make sure the client knows it might have moved. Maybe there is a
3087 * better way of doing this so only one client_configure is sent, but
3088 * since 125 of these are sent per second when moving the window (with
3089 * user = FALSE) i doubt it matters much.
3091 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3092 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3093 client_change_state(self
); /* reflect this in the state hints */
3097 /* Determines which physical monitor a client is on by calculating the
3098 area of the part of the client on each monitor. The number of the
3099 monitor containing the greatest area of the client is returned.*/
3100 guint
client_monitor(ObClient
*self
)
3106 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3107 Rect
*area
= screen_physical_area_monitor(i
);
3108 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3112 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3113 v
= r
.width
* r
.height
;
3124 ObClient
*client_search_top_transient(ObClient
*self
)
3126 /* move up the transient chain as far as possible */
3127 if (self
->transient_for
) {
3128 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3129 return client_search_top_transient(self
->transient_for
);
3133 g_assert(self
->group
);
3135 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3136 ObClient
*c
= it
->data
;
3138 /* checking transient_for prevents infinate loops! */
3139 if (c
!= self
&& !c
->transient_for
)
3150 ObClient
*client_search_focus_parent(ObClient
*self
)
3152 if (self
->transient_for
) {
3153 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3154 if (client_focused(self
->transient_for
))
3155 return self
->transient_for
;
3159 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3160 ObClient
*c
= it
->data
;
3162 /* checking transient_for prevents infinate loops! */
3163 if (c
!= self
&& !c
->transient_for
)
3164 if (client_focused(c
))
3173 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3175 if (self
->transient_for
) {
3176 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3177 if (self
->transient_for
== search
)
3182 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3183 ObClient
*c
= it
->data
;
3185 /* checking transient_for prevents infinate loops! */
3186 if (c
!= self
&& !c
->transient_for
)
3196 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3200 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3201 if (sit
->data
== search
)
3203 if (client_search_transient(sit
->data
, search
))
3209 void client_update_sm_client_id(ObClient
*self
)
3211 g_free(self
->sm_client_id
);
3212 self
->sm_client_id
= NULL
;
3214 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3216 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3217 &self
->sm_client_id
);
3220 #define WANT_EDGE(cur, c) \
3223 if(!client_normal(cur)) \
3225 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3229 if(cur->layer < c->layer && !config_resist_layers_below) \
3232 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3233 if ((his_edge_start >= my_edge_start && \
3234 his_edge_start <= my_edge_end) || \
3235 (my_edge_start >= his_edge_start && \
3236 my_edge_start <= his_edge_end)) \
3239 /* finds the nearest edge in the given direction from the current client
3240 * note to self: the edge is the -frame- edge (the actual one), not the
3243 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3245 gint dest
, monitor_dest
;
3246 gint my_edge_start
, my_edge_end
, my_offset
;
3253 a
= screen_area(c
->desktop
);
3254 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3257 case OB_DIRECTION_NORTH
:
3258 my_edge_start
= c
->frame
->area
.x
;
3259 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3260 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3262 /* default: top of screen */
3263 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3264 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3265 /* if the monitor edge comes before the screen edge, */
3266 /* use that as the destination instead. (For xinerama) */
3267 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3268 dest
= monitor_dest
;
3270 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3271 gint his_edge_start
, his_edge_end
, his_offset
;
3272 ObClient
*cur
= it
->data
;
3276 his_edge_start
= cur
->frame
->area
.x
;
3277 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3278 his_offset
= cur
->frame
->area
.y
+
3279 (hang
? 0 : cur
->frame
->area
.height
);
3281 if(his_offset
+ 1 > my_offset
)
3284 if(his_offset
< dest
)
3287 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3290 case OB_DIRECTION_SOUTH
:
3291 my_edge_start
= c
->frame
->area
.x
;
3292 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3293 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3295 /* default: bottom of screen */
3296 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3297 monitor_dest
= monitor
->y
+ monitor
->height
-
3298 (hang
? c
->frame
->area
.height
: 0);
3299 /* if the monitor edge comes before the screen edge, */
3300 /* use that as the destination instead. (For xinerama) */
3301 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3302 dest
= monitor_dest
;
3304 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3305 gint his_edge_start
, his_edge_end
, his_offset
;
3306 ObClient
*cur
= it
->data
;
3310 his_edge_start
= cur
->frame
->area
.x
;
3311 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3312 his_offset
= cur
->frame
->area
.y
+
3313 (hang
? cur
->frame
->area
.height
: 0);
3316 if(his_offset
- 1 < my_offset
)
3319 if(his_offset
> dest
)
3322 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3325 case OB_DIRECTION_WEST
:
3326 my_edge_start
= c
->frame
->area
.y
;
3327 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3328 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3330 /* default: leftmost egde of screen */
3331 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3332 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3333 /* if the monitor edge comes before the screen edge, */
3334 /* use that as the destination instead. (For xinerama) */
3335 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3336 dest
= monitor_dest
;
3338 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3339 gint his_edge_start
, his_edge_end
, his_offset
;
3340 ObClient
*cur
= it
->data
;
3344 his_edge_start
= cur
->frame
->area
.y
;
3345 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3346 his_offset
= cur
->frame
->area
.x
+
3347 (hang
? 0 : cur
->frame
->area
.width
);
3349 if(his_offset
+ 1 > my_offset
)
3352 if(his_offset
< dest
)
3355 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3358 case OB_DIRECTION_EAST
:
3359 my_edge_start
= c
->frame
->area
.y
;
3360 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3361 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3363 /* default: rightmost edge of screen */
3364 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3365 monitor_dest
= monitor
->x
+ monitor
->width
-
3366 (hang
? c
->frame
->area
.width
: 0);
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 his_edge_start
= cur
->frame
->area
.y
;
3379 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3380 his_offset
= cur
->frame
->area
.x
+
3381 (hang
? cur
->frame
->area
.width
: 0);
3383 if(his_offset
- 1 < my_offset
)
3386 if(his_offset
> dest
)
3389 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3392 case OB_DIRECTION_NORTHEAST
:
3393 case OB_DIRECTION_SOUTHEAST
:
3394 case OB_DIRECTION_NORTHWEST
:
3395 case OB_DIRECTION_SOUTHWEST
:
3396 /* not implemented */
3398 g_assert_not_reached();
3399 dest
= 0; /* suppress warning */
3404 ObClient
* client_under_pointer()
3408 ObClient
*ret
= NULL
;
3410 if (screen_pointer_pos(&x
, &y
)) {
3411 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3412 if (WINDOW_IS_CLIENT(it
->data
)) {
3413 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3414 if (c
->frame
->visible
&&
3415 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3425 gboolean
client_has_group_siblings(ObClient
*self
)
3427 return self
->group
&& self
->group
->members
->next
;