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 ((settings
&& settings
->focus
== TRUE
) ||
375 (!settings
&& (config_focus_new
||
376 client_search_focus_parent(self
)))) &&
377 /* note the check against Type_Normal/Dialog, not client_normal(self),
378 which would also include other types. in this case we want more
379 strict rules for focus */
380 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
381 self
->type
== OB_CLIENT_TYPE_DIALOG
))
385 if (self
->desktop
!= screen_desktop
) {
386 /* activate the window */
389 gboolean group_foc
= FALSE
;
394 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
396 if (client_focused(it
->data
))
404 (!self
->transient_for
&& (!self
->group
||
405 !self
->group
->members
->next
))) ||
406 client_search_focus_tree_full(self
) ||
408 !client_normal(focus_client
))
410 /* activate the window */
417 if (ob_state() == OB_STATE_RUNNING
) {
418 gint x
= self
->area
.x
, ox
= x
;
419 gint y
= self
->area
.y
, oy
= y
;
422 transient
= place_client(self
, &x
, &y
, settings
);
424 /* make sure the window is visible. */
425 client_find_onscreen(self
, &x
, &y
,
426 self
->frame
->area
.width
,
427 self
->frame
->area
.height
,
428 /* non-normal clients has less rules, and
429 windows that are being restored from a
430 session do also. we can assume you want
431 it back where you saved it. Clients saying
432 they placed themselves are subjected to
433 harder rules, ones that are placed by
434 place.c or by the user are allowed partially
435 off-screen and on xinerama divides (ie,
436 it is up to the placement routines to avoid
437 the xinerama divides) */
439 (((self
->positioned
& PPosition
) &&
440 !(self
->positioned
& USPosition
)) &&
441 client_normal(self
) &&
443 if (x
!= ox
|| y
!= oy
)
444 client_move(self
, x
, y
);
447 keyboard_grab_for_client(self
, TRUE
);
448 mouse_grab_for_client(self
, TRUE
);
450 client_showhide(self
);
452 /* use client_focus instead of client_activate cuz client_activate does
453 stuff like switch desktops etc and I'm not interested in all that when
454 a window maps since its not based on an action from the user like
455 clicking a window to activate is. so keep the new window out of the way
458 /* if using focus_delay, stop the timer now so that focus doesn't go
460 event_halt_focus_delay();
463 /* since focus can change the stacking orders, if we focus the window
464 then the standard raise it gets is not enough, we need to queue one
465 for after the focus change takes place */
469 /* client_activate does this but we aret using it so we have to do it
471 if (screen_showing_desktop
)
472 screen_show_desktop(FALSE
);
474 /* add to client list/map */
475 client_list
= g_list_append(client_list
, self
);
476 g_hash_table_insert(window_map
, &self
->window
, self
);
478 /* this has to happen after we're in the client_list */
479 screen_update_areas();
481 /* update the list hints */
484 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
487 void client_unmanage_all()
489 while (client_list
!= NULL
)
490 client_unmanage(client_list
->data
);
493 void client_unmanage(ObClient
*self
)
498 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
500 g_assert(self
!= NULL
);
502 keyboard_grab_for_client(self
, FALSE
);
503 mouse_grab_for_client(self
, FALSE
);
505 /* potentially fix focusLast */
506 if (config_focus_last
)
507 grab_pointer(TRUE
, OB_CURSOR_NONE
);
509 /* remove the window from our save set */
510 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
512 /* we dont want events no more */
513 XSelectInput(ob_display
, self
->window
, NoEventMask
);
515 frame_hide(self
->frame
);
517 client_list
= g_list_remove(client_list
, self
);
518 stacking_remove(self
);
519 g_hash_table_remove(window_map
, &self
->window
);
521 /* update the focus lists */
522 focus_order_remove(self
);
524 /* once the client is out of the list, update the struts to remove it's
526 screen_update_areas();
528 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
529 Destructor
*d
= it
->data
;
530 d
->func(self
, d
->data
);
533 if (focus_client
== self
) {
536 /* focus the last focused window on the desktop, and ignore enter
537 events from the unmap so it doesnt mess with the focus */
538 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
539 /* remove these flags so we don't end up getting focused in the
541 self
->can_focus
= FALSE
;
542 self
->focus_notify
= FALSE
;
544 client_unfocus(self
);
547 /* tell our parent(s) that we're gone */
548 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
549 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
550 if (it
->data
!= self
)
551 ((ObClient
*)it
->data
)->transients
=
552 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
553 } else if (self
->transient_for
) { /* transient of window */
554 self
->transient_for
->transients
=
555 g_slist_remove(self
->transient_for
->transients
, self
);
558 /* tell our transients that we're gone */
559 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
560 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
561 ((ObClient
*)it
->data
)->transient_for
= NULL
;
562 client_calc_layer(it
->data
);
566 /* remove from its group */
568 group_remove(self
->group
, self
);
572 /* give the client its border back */
573 client_toggle_border(self
, TRUE
);
575 /* reparent the window out of the frame, and free the frame */
576 frame_release_client(self
->frame
, self
);
579 if (ob_state() != OB_STATE_EXITING
) {
580 /* these values should not be persisted across a window
582 PROP_ERASE(self
->window
, net_wm_desktop
);
583 PROP_ERASE(self
->window
, net_wm_state
);
584 PROP_ERASE(self
->window
, wm_state
);
586 /* if we're left in an unmapped state, the client wont be mapped. this
587 is bad, since we will no longer be managing the window on restart */
588 XMapWindow(ob_display
, self
->window
);
592 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
594 /* free all data allocated in the client struct */
595 g_slist_free(self
->transients
);
596 for (j
= 0; j
< self
->nicons
; ++j
)
597 g_free(self
->icons
[j
].data
);
598 if (self
->nicons
> 0)
601 g_free(self
->icon_title
);
605 g_free(self
->sm_client_id
);
608 /* update the list hints */
611 if (config_focus_last
)
612 grab_pointer(FALSE
, OB_CURSOR_NONE
);
615 static void client_urgent_notify(ObClient
*self
)
618 frame_flash_start(self
->frame
);
620 frame_flash_stop(self
->frame
);
623 static void client_restore_session_state(ObClient
*self
)
627 if (!(it
= session_state_find(self
)))
630 self
->session
= it
->data
;
632 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
633 self
->positioned
= PPosition
;
634 if (self
->session
->w
> 0)
635 self
->area
.width
= self
->session
->w
;
636 if (self
->session
->h
> 0)
637 self
->area
.height
= self
->session
->h
;
638 XResizeWindow(ob_display
, self
->window
,
639 self
->area
.width
, self
->area
.height
);
641 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
642 self
->session
->desktop
:
643 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
644 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
646 self
->shaded
= self
->session
->shaded
;
647 self
->iconic
= self
->session
->iconic
;
648 self
->skip_pager
= self
->session
->skip_pager
;
649 self
->skip_taskbar
= self
->session
->skip_taskbar
;
650 self
->fullscreen
= self
->session
->fullscreen
;
651 self
->above
= self
->session
->above
;
652 self
->below
= self
->session
->below
;
653 self
->max_horz
= self
->session
->max_horz
;
654 self
->max_vert
= self
->session
->max_vert
;
657 static void client_restore_session_stacking(ObClient
*self
)
661 if (!self
->session
) return;
663 it
= g_list_find(session_saved_state
, self
->session
);
664 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
667 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
668 if (session_state_cmp(it
->data
, cit
->data
))
671 client_calc_layer(self
);
672 stacking_below(CLIENT_AS_WINDOW(self
),
673 CLIENT_AS_WINDOW(cit
->data
));
679 void client_move_onscreen(ObClient
*self
, gboolean rude
)
681 gint x
= self
->area
.x
;
682 gint y
= self
->area
.y
;
683 if (client_find_onscreen(self
, &x
, &y
,
684 self
->frame
->area
.width
,
685 self
->frame
->area
.height
, rude
)) {
686 client_move(self
, x
, y
);
690 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
694 gint ox
= *x
, oy
= *y
;
696 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
699 /* XXX watch for xinerama dead areas */
700 /* This makes sure windows aren't entirely outside of the screen so you
701 * can't see them at all */
702 if (client_normal(self
)) {
703 a
= screen_area(self
->desktop
);
704 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
705 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
706 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
707 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
708 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
710 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
714 /* This here doesn't let windows even a pixel outside the screen,
715 * when called from client_manage, programs placing themselves are
716 * forced completely onscreen, while things like
717 * xterm -geometry resolution-width/2 will work fine. Trying to
718 * place it completely offscreen will be handled in the above code.
719 * Sorry for this confused comment, i am tired. */
721 /* avoid the xinerama monitor divide while we're at it,
722 * remember to fix the placement stuff to avoid it also and
723 * then remove this XXX */
724 a
= screen_physical_area_monitor(client_monitor(self
));
725 /* dont let windows map/move into the strut unless they
726 are bigger than the available area */
728 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
729 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
730 *x
= a
->x
+ a
->width
- w
;
732 if (h
<= a
->height
) {
733 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
734 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
735 *y
= a
->y
+ a
->height
- h
;
739 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
742 return ox
!= *x
|| oy
!= *y
;
745 static void client_toggle_border(ObClient
*self
, gboolean show
)
747 /* adjust our idea of where the client is, based on its border. When the
748 border is removed, the client should now be considered to be in a
750 when re-adding the border to the client, the same operation needs to be
752 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
753 gint x
= oldx
, y
= oldy
;
754 switch(self
->gravity
) {
756 case NorthWestGravity
:
758 case SouthWestGravity
:
760 case NorthEastGravity
:
762 case SouthEastGravity
:
763 if (show
) x
-= self
->border_width
* 2;
764 else x
+= self
->border_width
* 2;
771 if (show
) x
-= self
->border_width
;
772 else x
+= self
->border_width
;
775 switch(self
->gravity
) {
777 case NorthWestGravity
:
779 case NorthEastGravity
:
781 case SouthWestGravity
:
783 case SouthEastGravity
:
784 if (show
) y
-= self
->border_width
* 2;
785 else y
+= self
->border_width
* 2;
792 if (show
) y
-= self
->border_width
;
793 else y
+= self
->border_width
;
800 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
802 /* move the client so it is back it the right spot _with_ its
804 if (x
!= oldx
|| y
!= oldy
)
805 XMoveWindow(ob_display
, self
->window
, x
, y
);
807 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
811 static void client_get_all(ObClient
*self
)
813 client_get_area(self
);
814 client_get_mwm_hints(self
);
816 /* The transient hint is used to pick a type, but the type can also affect
817 transiency (dialogs are always made transients of their group if they
818 have one). This is Havoc's idea, but it is needed to make some apps
819 work right (eg tsclient). */
820 client_update_transient_for(self
);
821 client_get_type(self
);/* this can change the mwmhints for special cases */
822 client_update_transient_for(self
);
824 client_update_wmhints(self
);
825 client_get_startup_id(self
);
826 client_get_desktop(self
);/* uses transient data/group/startup id if a
827 desktop is not specified */
828 client_get_shaped(self
);
830 client_get_state(self
);
833 /* a couple type-based defaults for new windows */
835 /* this makes sure that these windows appear on all desktops */
836 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
837 self
->desktop
= DESKTOP_ALL
;
840 client_update_protocols(self
);
842 client_get_gravity(self
); /* get the attribute gravity */
843 client_update_normal_hints(self
); /* this may override the attribute
846 /* got the type, the mwmhints, the protocols, and the normal hints
847 (min/max sizes), so we're ready to set up the decorations/functions */
848 client_setup_decor_and_functions(self
);
850 client_update_title(self
);
851 client_update_class(self
);
852 client_update_sm_client_id(self
);
853 client_update_strut(self
);
854 client_update_icons(self
);
857 static void client_get_startup_id(ObClient
*self
)
859 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
861 PROP_GETS(self
->group
->leader
,
862 net_startup_id
, utf8
, &self
->startup_id
);
865 static void client_get_area(ObClient
*self
)
867 XWindowAttributes wattrib
;
870 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
871 g_assert(ret
!= BadWindow
);
873 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
874 self
->border_width
= wattrib
.border_width
;
877 static void client_get_desktop(ObClient
*self
)
879 guint32 d
= screen_num_desktops
; /* an always-invalid value */
881 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
882 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
883 self
->desktop
= screen_num_desktops
- 1;
887 gboolean trdesk
= FALSE
;
889 if (self
->transient_for
) {
890 if (self
->transient_for
!= OB_TRAN_GROUP
) {
891 self
->desktop
= self
->transient_for
->desktop
;
896 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
897 if (it
->data
!= self
&&
898 !((ObClient
*)it
->data
)->transient_for
) {
899 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
906 /* try get from the startup-notification protocol */
907 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
908 if (self
->desktop
>= screen_num_desktops
&&
909 self
->desktop
!= DESKTOP_ALL
)
910 self
->desktop
= screen_num_desktops
- 1;
912 /* defaults to the current desktop */
913 self
->desktop
= screen_desktop
;
916 if (self
->desktop
!= d
) {
917 /* set the desktop hint, to make sure that it always exists */
918 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
922 static void client_get_state(ObClient
*self
)
927 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
929 for (i
= 0; i
< num
; ++i
) {
930 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
932 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
934 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
936 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
937 self
->skip_taskbar
= TRUE
;
938 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
939 self
->skip_pager
= TRUE
;
940 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
941 self
->fullscreen
= TRUE
;
942 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
943 self
->max_vert
= TRUE
;
944 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
945 self
->max_horz
= TRUE
;
946 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
948 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
950 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
951 self
->undecorated
= TRUE
;
957 if (!(self
->above
|| self
->below
)) {
959 /* apply stuff from the group */
963 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
964 ObClient
*c
= it
->data
;
965 if (c
!= self
&& !client_search_transient(self
, c
) &&
966 client_normal(self
) && client_normal(c
))
969 (c
->above
? 1 : (c
->below
? -1 : 0)));
983 g_assert_not_reached();
990 static void client_get_shaped(ObClient
*self
)
992 self
->shaped
= FALSE
;
994 if (extensions_shape
) {
999 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1001 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1002 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1004 self
->shaped
= (s
!= 0);
1009 void client_update_transient_for(ObClient
*self
)
1012 ObClient
*target
= NULL
;
1014 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1015 self
->transient
= TRUE
;
1016 if (t
!= self
->window
) { /* cant be transient to itself! */
1017 target
= g_hash_table_lookup(window_map
, &t
);
1018 /* if this happens then we need to check for it*/
1019 g_assert(target
!= self
);
1020 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1021 /* this can happen when a dialog is a child of
1022 a dockapp, for example */
1026 if (!target
&& self
->group
) {
1027 /* not transient to a client, see if it is transient for a
1029 if (t
== self
->group
->leader
||
1031 t
== RootWindow(ob_display
, ob_screen
))
1033 /* window is a transient for its group! */
1034 target
= OB_TRAN_GROUP
;
1038 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
1039 self
->transient
= TRUE
;
1040 target
= OB_TRAN_GROUP
;
1042 self
->transient
= FALSE
;
1044 /* if anything has changed... */
1045 if (target
!= self
->transient_for
) {
1046 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1049 /* remove from old parents */
1050 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1051 ObClient
*c
= it
->data
;
1052 if (c
!= self
&& !c
->transient_for
)
1053 c
->transients
= g_slist_remove(c
->transients
, self
);
1055 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1056 /* remove from old parent */
1057 self
->transient_for
->transients
=
1058 g_slist_remove(self
->transient_for
->transients
, self
);
1060 self
->transient_for
= target
;
1061 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1064 /* add to new parents */
1065 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1066 ObClient
*c
= it
->data
;
1067 if (c
!= self
&& !c
->transient_for
)
1068 c
->transients
= g_slist_append(c
->transients
, self
);
1071 /* remove all transients which are in the group, that causes
1072 circlular pointer hell of doom */
1073 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1075 for (sit
= self
->transients
; sit
; sit
= next
) {
1076 next
= g_slist_next(sit
);
1077 if (sit
->data
== it
->data
)
1079 g_slist_delete_link(self
->transients
, sit
);
1082 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1083 /* add to new parent */
1084 self
->transient_for
->transients
=
1085 g_slist_append(self
->transient_for
->transients
, self
);
1090 static void client_get_mwm_hints(ObClient
*self
)
1095 self
->mwmhints
.flags
= 0; /* default to none */
1097 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1099 if (num
>= OB_MWM_ELEMENTS
) {
1100 self
->mwmhints
.flags
= hints
[0];
1101 self
->mwmhints
.functions
= hints
[1];
1102 self
->mwmhints
.decorations
= hints
[2];
1108 void client_get_type(ObClient
*self
)
1115 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1116 /* use the first value that we know about in the array */
1117 for (i
= 0; i
< num
; ++i
) {
1118 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1119 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1120 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1121 self
->type
= OB_CLIENT_TYPE_DOCK
;
1122 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1123 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1124 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1125 self
->type
= OB_CLIENT_TYPE_MENU
;
1126 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1127 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1128 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1129 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1130 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1131 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1132 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1133 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1134 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1135 /* prevent this window from getting any decor or
1137 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1138 OB_MWM_FLAG_DECORATIONS
);
1139 self
->mwmhints
.decorations
= 0;
1140 self
->mwmhints
.functions
= 0;
1142 if (self
->type
!= (ObClientType
) -1)
1143 break; /* grab the first legit type */
1148 if (self
->type
== (ObClientType
) -1) {
1149 /*the window type hint was not set, which means we either classify
1150 ourself as a normal window or a dialog, depending on if we are a
1152 if (self
->transient
)
1153 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1155 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1159 void client_update_protocols(ObClient
*self
)
1162 guint num_return
, i
;
1164 self
->focus_notify
= FALSE
;
1165 self
->delete_window
= FALSE
;
1167 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1168 for (i
= 0; i
< num_return
; ++i
) {
1169 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1170 /* this means we can request the window to close */
1171 self
->delete_window
= TRUE
;
1172 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1173 /* if this protocol is requested, then the window will be
1174 notified whenever we want it to receive focus */
1175 self
->focus_notify
= TRUE
;
1181 static void client_get_gravity(ObClient
*self
)
1183 XWindowAttributes wattrib
;
1186 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1187 g_assert(ret
!= BadWindow
);
1188 self
->gravity
= wattrib
.win_gravity
;
1191 void client_update_normal_hints(ObClient
*self
)
1195 gint oldgravity
= self
->gravity
;
1198 self
->min_ratio
= 0.0f
;
1199 self
->max_ratio
= 0.0f
;
1200 SIZE_SET(self
->size_inc
, 1, 1);
1201 SIZE_SET(self
->base_size
, 0, 0);
1202 SIZE_SET(self
->min_size
, 0, 0);
1203 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1205 /* get the hints from the window */
1206 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1207 /* normal windows can't request placement! har har
1208 if (!client_normal(self))
1210 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1212 if (size
.flags
& PWinGravity
) {
1213 self
->gravity
= size
.win_gravity
;
1215 /* if the client has a frame, i.e. has already been mapped and
1216 is changing its gravity */
1217 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1218 /* move our idea of the client's position based on its new
1220 self
->area
.x
= self
->frame
->area
.x
;
1221 self
->area
.y
= self
->frame
->area
.y
;
1222 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1226 if (size
.flags
& PAspect
) {
1227 if (size
.min_aspect
.y
)
1229 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1230 if (size
.max_aspect
.y
)
1232 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1235 if (size
.flags
& PMinSize
)
1236 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1238 if (size
.flags
& PMaxSize
)
1239 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1241 if (size
.flags
& PBaseSize
)
1242 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1244 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1245 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1249 void client_setup_decor_and_functions(ObClient
*self
)
1251 /* start with everything (cept fullscreen) */
1253 (OB_FRAME_DECOR_TITLEBAR
|
1254 OB_FRAME_DECOR_HANDLE
|
1255 OB_FRAME_DECOR_GRIPS
|
1256 OB_FRAME_DECOR_BORDER
|
1257 OB_FRAME_DECOR_ICON
|
1258 OB_FRAME_DECOR_ALLDESKTOPS
|
1259 OB_FRAME_DECOR_ICONIFY
|
1260 OB_FRAME_DECOR_MAXIMIZE
|
1261 OB_FRAME_DECOR_SHADE
|
1262 OB_FRAME_DECOR_CLOSE
);
1264 (OB_CLIENT_FUNC_RESIZE
|
1265 OB_CLIENT_FUNC_MOVE
|
1266 OB_CLIENT_FUNC_ICONIFY
|
1267 OB_CLIENT_FUNC_MAXIMIZE
|
1268 OB_CLIENT_FUNC_SHADE
|
1269 OB_CLIENT_FUNC_CLOSE
);
1271 if (!(self
->min_size
.width
< self
->max_size
.width
||
1272 self
->min_size
.height
< self
->max_size
.height
))
1273 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1275 switch (self
->type
) {
1276 case OB_CLIENT_TYPE_NORMAL
:
1277 /* normal windows retain all of the possible decorations and
1278 functionality, and are the only windows that you can fullscreen */
1279 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1282 case OB_CLIENT_TYPE_DIALOG
:
1283 case OB_CLIENT_TYPE_UTILITY
:
1284 /* these windows cannot be maximized */
1285 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1288 case OB_CLIENT_TYPE_MENU
:
1289 case OB_CLIENT_TYPE_TOOLBAR
:
1290 /* these windows get less functionality */
1291 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1294 case OB_CLIENT_TYPE_DESKTOP
:
1295 case OB_CLIENT_TYPE_DOCK
:
1296 case OB_CLIENT_TYPE_SPLASH
:
1297 /* none of these windows are manipulated by the window manager */
1298 self
->decorations
= 0;
1299 self
->functions
= 0;
1303 /* Mwm Hints are applied subtractively to what has already been chosen for
1304 decor and functionality */
1305 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1306 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1307 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1308 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1310 /* if the mwm hints request no handle or title, then all
1311 decorations are disabled, but keep the border if that's
1313 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1314 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1316 self
->decorations
= 0;
1321 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1322 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1323 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1324 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1325 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1326 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1327 /* dont let mwm hints kill any buttons
1328 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1329 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1330 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1331 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1333 /* dont let mwm hints kill the close button
1334 if (! (self->mwmhints.functions & MwmFunc_Close))
1335 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1339 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1340 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1341 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1342 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1343 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1344 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1346 /* can't maximize without moving/resizing */
1347 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1348 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1349 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1350 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1351 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1354 /* kill the handle on fully maxed windows */
1355 if (self
->max_vert
&& self
->max_horz
)
1356 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1358 /* finally, the user can have requested no decorations, which overrides
1359 everything (but doesnt give it a border if it doesnt have one) */
1360 if (self
->undecorated
) {
1361 if (config_theme_keepborder
)
1362 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1364 self
->decorations
= 0;
1367 /* if we don't have a titlebar, then we cannot shade! */
1368 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1369 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1371 /* now we need to check against rules for the client's current state */
1372 if (self
->fullscreen
) {
1373 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1374 OB_CLIENT_FUNC_FULLSCREEN
|
1375 OB_CLIENT_FUNC_ICONIFY
);
1376 self
->decorations
= 0;
1379 client_change_allowed_actions(self
);
1382 /* adjust the client's decorations, etc. */
1383 client_reconfigure(self
);
1387 static void client_change_allowed_actions(ObClient
*self
)
1392 /* desktop windows are kept on all desktops */
1393 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1394 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1396 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1397 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1398 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1399 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1400 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1401 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1402 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1403 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1404 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1405 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1406 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1407 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1408 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1409 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1410 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1413 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1415 /* make sure the window isn't breaking any rules now */
1417 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1418 if (self
->frame
) client_shade(self
, FALSE
);
1419 else self
->shaded
= FALSE
;
1421 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1422 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1423 else self
->iconic
= FALSE
;
1425 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1426 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1427 else self
->fullscreen
= FALSE
;
1429 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1431 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1432 else self
->max_vert
= self
->max_horz
= FALSE
;
1436 void client_reconfigure(ObClient
*self
)
1438 /* by making this pass FALSE for user, we avoid the emacs event storm where
1439 every configurenotify causes an update in its normal hints, i think this
1440 is generally what we want anyways... */
1441 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1442 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1445 void client_update_wmhints(ObClient
*self
)
1448 gboolean ur
= FALSE
;
1451 /* assume a window takes input if it doesnt specify */
1452 self
->can_focus
= TRUE
;
1454 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1455 if (hints
->flags
& InputHint
)
1456 self
->can_focus
= hints
->input
;
1458 /* only do this when first managing the window *AND* when we aren't
1460 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1461 if (hints
->flags
& StateHint
)
1462 self
->iconic
= hints
->initial_state
== IconicState
;
1464 if (hints
->flags
& XUrgencyHint
)
1467 if (!(hints
->flags
& WindowGroupHint
))
1468 hints
->window_group
= None
;
1470 /* did the group state change? */
1471 if (hints
->window_group
!=
1472 (self
->group
? self
->group
->leader
: None
)) {
1473 /* remove from the old group if there was one */
1474 if (self
->group
!= NULL
) {
1475 /* remove transients of the group */
1476 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1477 self
->transients
= g_slist_remove(self
->transients
,
1480 /* remove myself from parents in the group */
1481 if (self
->transient_for
== OB_TRAN_GROUP
) {
1482 for (it
= self
->group
->members
; it
;
1483 it
= g_slist_next(it
))
1485 ObClient
*c
= it
->data
;
1487 if (c
!= self
&& !c
->transient_for
)
1488 c
->transients
= g_slist_remove(c
->transients
,
1493 group_remove(self
->group
, self
);
1496 if (hints
->window_group
!= None
) {
1497 self
->group
= group_add(hints
->window_group
, self
);
1499 /* i can only have transients from the group if i am not
1501 if (!self
->transient_for
) {
1502 /* add other transients of the group that are already
1504 for (it
= self
->group
->members
; it
;
1505 it
= g_slist_next(it
))
1507 ObClient
*c
= it
->data
;
1508 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1510 g_slist_append(self
->transients
, c
);
1515 /* because the self->transient flag wont change from this call,
1516 we don't need to update the window's type and such, only its
1517 transient_for, and the transients lists of other windows in
1518 the group may be affected */
1519 client_update_transient_for(self
);
1522 /* the WM_HINTS can contain an icon */
1523 client_update_icons(self
);
1528 if (ur
!= self
->urgent
) {
1530 /* fire the urgent callback if we're mapped, otherwise, wait until
1531 after we're mapped */
1533 client_urgent_notify(self
);
1537 void client_update_title(ObClient
*self
)
1543 gboolean read_title
;
1546 old_title
= self
->title
;
1549 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1550 /* try old x stuff */
1551 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1552 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1553 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1554 if (self
->transient
) {
1555 data
= g_strdup("");
1558 data
= g_strdup("Unnamed Window");
1562 if (config_title_number
) {
1564 /* did the title change? then reset the title_count */
1565 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1566 self
->title_count
= 1;
1568 /* look for duplicates and append a number */
1570 for (it
= client_list
; it
; it
= g_list_next(it
))
1571 if (it
->data
!= self
) {
1572 ObClient
*c
= it
->data
;
1573 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1574 nums
|= 1 << c
->title_count
;
1576 /* find first free number */
1577 for (i
= 1; i
<= 32; ++i
)
1578 if (!(nums
& (1 << i
))) {
1579 if (self
->title_count
== 1 || i
== 1)
1580 self
->title_count
= i
;
1583 /* dont display the number for the first window */
1584 if (self
->title_count
> 1) {
1586 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1591 self
->title_count
= 1;
1594 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1598 frame_adjust_title(self
->frame
);
1602 /* update the icon title */
1604 g_free(self
->icon_title
);
1608 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1609 /* try old x stuff */
1610 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)
1611 || PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
))) {
1612 data
= g_strdup(self
->title
);
1616 /* append the title count, dont display the number for the first window.
1617 * We don't need to check for config_title_number here since title_count
1618 * is not set above 1 then. */
1619 if (read_title
&& self
->title_count
> 1) {
1620 gchar
*vdata
, *ndata
;
1621 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1622 vdata
= g_strconcat(data
, ndata
, NULL
);
1628 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1630 self
->icon_title
= data
;
1633 void client_update_class(ObClient
*self
)
1638 if (self
->name
) g_free(self
->name
);
1639 if (self
->class) g_free(self
->class);
1640 if (self
->role
) g_free(self
->role
);
1642 self
->name
= self
->class = self
->role
= NULL
;
1644 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1646 self
->name
= g_strdup(data
[0]);
1648 self
->class = g_strdup(data
[1]);
1653 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1656 if (self
->name
== NULL
) self
->name
= g_strdup("");
1657 if (self
->class == NULL
) self
->class = g_strdup("");
1658 if (self
->role
== NULL
) self
->role
= g_strdup("");
1661 void client_update_strut(ObClient
*self
)
1665 gboolean got
= FALSE
;
1668 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1672 STRUT_PARTIAL_SET(strut
,
1673 data
[0], data
[2], data
[1], data
[3],
1674 data
[4], data
[5], data
[8], data
[9],
1675 data
[6], data
[7], data
[10], data
[11]);
1681 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1687 /* use the screen's width/height */
1688 a
= screen_physical_area();
1690 STRUT_PARTIAL_SET(strut
,
1691 data
[0], data
[2], data
[1], data
[3],
1692 a
->y
, a
->y
+ a
->height
- 1,
1693 a
->x
, a
->x
+ a
->width
- 1,
1694 a
->y
, a
->y
+ a
->height
- 1,
1695 a
->x
, a
->x
+ a
->width
- 1);
1701 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1702 0, 0, 0, 0, 0, 0, 0, 0);
1704 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1705 self
->strut
= strut
;
1707 /* updating here is pointless while we're being mapped cuz we're not in
1708 the client list yet */
1710 screen_update_areas();
1714 void client_update_icons(ObClient
*self
)
1720 for (i
= 0; i
< self
->nicons
; ++i
)
1721 g_free(self
->icons
[i
].data
);
1722 if (self
->nicons
> 0)
1723 g_free(self
->icons
);
1726 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1727 /* figure out how many valid icons are in here */
1729 while (num
- i
> 2) {
1733 if (i
> num
|| w
*h
== 0) break;
1737 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1739 /* store the icons */
1741 for (j
= 0; j
< self
->nicons
; ++j
) {
1744 w
= self
->icons
[j
].width
= data
[i
++];
1745 h
= self
->icons
[j
].height
= data
[i
++];
1747 if (w
*h
== 0) continue;
1749 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1750 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1755 self
->icons
[j
].data
[t
] =
1756 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1757 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1758 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1759 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1765 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1766 kwm_win_icon
, &data
, &num
)) {
1769 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1770 xerror_set_ignore(TRUE
);
1771 if (!RrPixmapToRGBA(ob_rr_inst
,
1773 &self
->icons
[self
->nicons
-1].width
,
1774 &self
->icons
[self
->nicons
-1].height
,
1775 &self
->icons
[self
->nicons
-1].data
)) {
1776 g_free(&self
->icons
[self
->nicons
-1]);
1779 xerror_set_ignore(FALSE
);
1785 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1786 if (hints
->flags
& IconPixmapHint
) {
1788 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1789 xerror_set_ignore(TRUE
);
1790 if (!RrPixmapToRGBA(ob_rr_inst
,
1792 (hints
->flags
& IconMaskHint
?
1793 hints
->icon_mask
: None
),
1794 &self
->icons
[self
->nicons
-1].width
,
1795 &self
->icons
[self
->nicons
-1].height
,
1796 &self
->icons
[self
->nicons
-1].data
)){
1797 g_free(&self
->icons
[self
->nicons
-1]);
1800 xerror_set_ignore(FALSE
);
1807 frame_adjust_icon(self
->frame
);
1810 static void client_change_state(ObClient
*self
)
1813 gulong netstate
[11];
1816 state
[0] = self
->wmstate
;
1818 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1822 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1824 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1826 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1827 if (self
->skip_taskbar
)
1828 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1829 if (self
->skip_pager
)
1830 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1831 if (self
->fullscreen
)
1832 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1834 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1836 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1838 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1840 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1841 if (self
->undecorated
)
1842 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1843 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1845 client_calc_layer(self
);
1848 frame_adjust_state(self
->frame
);
1851 ObClient
*client_search_focus_tree(ObClient
*self
)
1856 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1857 if (client_focused(it
->data
)) return it
->data
;
1858 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1863 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1865 if (self
->transient_for
) {
1866 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1867 return client_search_focus_tree_full(self
->transient_for
);
1870 gboolean recursed
= FALSE
;
1872 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1873 if (!((ObClient
*)it
->data
)->transient_for
) {
1875 if ((c
= client_search_focus_tree_full(it
->data
)))
1884 /* this function checks the whole tree, the client_search_focus_tree~
1885 does not, so we need to check this window */
1886 if (client_focused(self
))
1888 return client_search_focus_tree(self
);
1891 static ObStackingLayer
calc_layer(ObClient
*self
)
1895 if (self
->fullscreen
&&
1896 (client_focused(self
) || client_search_focus_tree(self
)))
1897 l
= OB_STACKING_LAYER_FULLSCREEN
;
1898 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1899 l
= OB_STACKING_LAYER_DESKTOP
;
1900 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1901 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1902 else l
= OB_STACKING_LAYER_ABOVE
;
1904 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1905 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1906 else l
= OB_STACKING_LAYER_NORMAL
;
1911 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1912 ObStackingLayer l
, gboolean raised
)
1914 ObStackingLayer old
, own
;
1918 own
= calc_layer(self
);
1919 self
->layer
= l
> own
? l
: own
;
1921 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1922 client_calc_layer_recursive(it
->data
, orig
,
1923 l
, raised
? raised
: l
!= old
);
1925 if (!raised
&& l
!= old
)
1926 if (orig
->frame
) { /* only restack if the original window is managed */
1927 stacking_remove(CLIENT_AS_WINDOW(self
));
1928 stacking_add(CLIENT_AS_WINDOW(self
));
1932 void client_calc_layer(ObClient
*self
)
1939 /* transients take on the layer of their parents */
1940 self
= client_search_top_transient(self
);
1942 l
= calc_layer(self
);
1944 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1947 gboolean
client_should_show(ObClient
*self
)
1951 if (client_normal(self
) && screen_showing_desktop
)
1954 if (self->transient_for) {
1955 if (self->transient_for != OB_TRAN_GROUP)
1956 return client_should_show(self->transient_for);
1960 for (it = self->group->members; it; it = g_slist_next(it)) {
1961 ObClient *c = it->data;
1962 if (c != self && !c->transient_for) {
1963 if (client_should_show(c))
1970 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
1976 static void client_showhide(ObClient
*self
)
1979 if (client_should_show(self
))
1980 frame_show(self
->frame
);
1982 frame_hide(self
->frame
);
1985 gboolean
client_normal(ObClient
*self
) {
1986 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1987 self
->type
== OB_CLIENT_TYPE_DOCK
||
1988 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1991 static void client_apply_startup_state(ObClient
*self
)
1993 /* these are in a carefully crafted order.. */
1996 self
->iconic
= FALSE
;
1997 client_iconify(self
, TRUE
, FALSE
);
1999 if (self
->fullscreen
) {
2000 self
->fullscreen
= FALSE
;
2001 client_fullscreen(self
, TRUE
, FALSE
);
2003 if (self
->undecorated
) {
2004 self
->undecorated
= FALSE
;
2005 client_set_undecorated(self
, TRUE
);
2008 self
->shaded
= FALSE
;
2009 client_shade(self
, TRUE
);
2012 client_urgent_notify(self
);
2014 if (self
->max_vert
&& self
->max_horz
) {
2015 self
->max_vert
= self
->max_horz
= FALSE
;
2016 client_maximize(self
, TRUE
, 0, FALSE
);
2017 } else if (self
->max_vert
) {
2018 self
->max_vert
= FALSE
;
2019 client_maximize(self
, TRUE
, 2, FALSE
);
2020 } else if (self
->max_horz
) {
2021 self
->max_horz
= FALSE
;
2022 client_maximize(self
, TRUE
, 1, FALSE
);
2025 /* nothing to do for the other states:
2034 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2035 gint x
, gint y
, gint w
, gint h
,
2036 gboolean user
, gboolean final
,
2037 gboolean force_reply
)
2040 gboolean send_resize_client
;
2041 gboolean moved
= FALSE
, resized
= FALSE
;
2042 guint fdecor
= self
->frame
->decorations
;
2043 gboolean fhorz
= self
->frame
->max_horz
;
2045 /* make the frame recalculate its dimentions n shit without changing
2046 anything visible for real, this way the constraints below can work with
2047 the updated frame dimensions. */
2048 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2050 /* gets the frame's position */
2051 frame_client_gravity(self
->frame
, &x
, &y
);
2053 /* these positions are frame positions, not client positions */
2055 /* set the size and position if fullscreen */
2056 if (self
->fullscreen
) {
2060 i
= client_monitor(self
);
2061 a
= screen_physical_area_monitor(i
);
2068 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2072 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
2074 /* set the size and position if maximized */
2075 if (self
->max_horz
) {
2077 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2079 if (self
->max_vert
) {
2081 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2085 /* gets the client's position */
2086 frame_frame_gravity(self
->frame
, &x
, &y
);
2088 /* these override the above states! if you cant move you can't move! */
2090 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2094 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2095 w
= self
->area
.width
;
2096 h
= self
->area
.height
;
2100 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2101 gint basew
, baseh
, minw
, minh
;
2103 /* base size is substituted with min size if not specified */
2104 if (self
->base_size
.width
|| self
->base_size
.height
) {
2105 basew
= self
->base_size
.width
;
2106 baseh
= self
->base_size
.height
;
2108 basew
= self
->min_size
.width
;
2109 baseh
= self
->min_size
.height
;
2111 /* min size is substituted with base size if not specified */
2112 if (self
->min_size
.width
|| self
->min_size
.height
) {
2113 minw
= self
->min_size
.width
;
2114 minh
= self
->min_size
.height
;
2116 minw
= self
->base_size
.width
;
2117 minh
= self
->base_size
.height
;
2120 /* if this is a user-requested resize, then check against min/max
2123 /* smaller than min size or bigger than max size? */
2124 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2125 if (w
< minw
) w
= minw
;
2126 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2127 if (h
< minh
) h
= minh
;
2132 /* keep to the increments */
2133 w
/= self
->size_inc
.width
;
2134 h
/= self
->size_inc
.height
;
2136 /* you cannot resize to nothing */
2137 if (basew
+ w
< 1) w
= 1 - basew
;
2138 if (baseh
+ h
< 1) h
= 1 - baseh
;
2140 /* store the logical size */
2141 SIZE_SET(self
->logical_size
,
2142 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2143 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2145 w
*= self
->size_inc
.width
;
2146 h
*= self
->size_inc
.height
;
2151 /* adjust the height to match the width for the aspect ratios.
2152 for this, min size is not substituted for base size ever. */
2153 w
-= self
->base_size
.width
;
2154 h
-= self
->base_size
.height
;
2156 if (!self
->fullscreen
) {
2157 if (self
->min_ratio
)
2158 if (h
* self
->min_ratio
> w
) {
2159 h
= (gint
)(w
/ self
->min_ratio
);
2161 /* you cannot resize to nothing */
2164 w
= (gint
)(h
* self
->min_ratio
);
2167 if (self
->max_ratio
)
2168 if (h
* self
->max_ratio
< w
) {
2169 h
= (gint
)(w
/ self
->max_ratio
);
2171 /* you cannot resize to nothing */
2174 w
= (gint
)(h
* self
->min_ratio
);
2179 w
+= self
->base_size
.width
;
2180 h
+= self
->base_size
.height
;
2187 case OB_CORNER_TOPLEFT
:
2189 case OB_CORNER_TOPRIGHT
:
2190 x
-= w
- self
->area
.width
;
2192 case OB_CORNER_BOTTOMLEFT
:
2193 y
-= h
- self
->area
.height
;
2195 case OB_CORNER_BOTTOMRIGHT
:
2196 x
-= w
- self
->area
.width
;
2197 y
-= h
- self
->area
.height
;
2201 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2202 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2204 oldw
= self
->area
.width
;
2205 oldh
= self
->area
.height
;
2206 RECT_SET(self
->area
, x
, y
, w
, h
);
2208 /* for app-requested resizes, always resize if 'resized' is true.
2209 for user-requested ones, only resize if final is true, or when
2210 resizing in redraw mode */
2211 send_resize_client
= ((!user
&& resized
) ||
2213 (resized
&& config_resize_redraw
))));
2215 /* if the client is enlarging, then resize the client before the frame */
2216 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2217 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2219 /* move/resize the frame to match the request */
2221 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2222 moved
= resized
= TRUE
;
2224 if (moved
|| resized
)
2225 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2227 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2230 event
.type
= ConfigureNotify
;
2231 event
.xconfigure
.display
= ob_display
;
2232 event
.xconfigure
.event
= self
->window
;
2233 event
.xconfigure
.window
= self
->window
;
2235 /* root window real coords */
2236 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2238 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2240 event
.xconfigure
.width
= w
;
2241 event
.xconfigure
.height
= h
;
2242 event
.xconfigure
.border_width
= 0;
2243 event
.xconfigure
.above
= self
->frame
->plate
;
2244 event
.xconfigure
.override_redirect
= FALSE
;
2245 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2246 FALSE
, StructureNotifyMask
, &event
);
2250 /* if the client is shrinking, then resize the frame before the client */
2251 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2252 XResizeWindow(ob_display
, self
->window
, w
, h
);
2257 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2261 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2262 self
->fullscreen
== fs
) return; /* already done */
2264 self
->fullscreen
= fs
;
2265 client_change_state(self
); /* change the state hints on the client,
2266 and adjust out layer/stacking */
2270 self
->pre_fullscreen_area
= self
->area
;
2272 /* these are not actually used cuz client_configure will set them
2273 as appropriate when the window is fullscreened */
2278 if (self
->pre_fullscreen_area
.width
> 0 &&
2279 self
->pre_fullscreen_area
.height
> 0)
2281 x
= self
->pre_fullscreen_area
.x
;
2282 y
= self
->pre_fullscreen_area
.y
;
2283 w
= self
->pre_fullscreen_area
.width
;
2284 h
= self
->pre_fullscreen_area
.height
;
2285 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2287 /* pick some fallbacks... */
2288 a
= screen_area_monitor(self
->desktop
, 0);
2289 x
= a
->x
+ a
->width
/ 4;
2290 y
= a
->y
+ a
->height
/ 4;
2296 client_setup_decor_and_functions(self
);
2298 client_move_resize(self
, x
, y
, w
, h
);
2300 /* try focus us when we go into fullscreen mode */
2304 static void client_iconify_recursive(ObClient
*self
,
2305 gboolean iconic
, gboolean curdesk
)
2308 gboolean changed
= FALSE
;
2311 if (self
->iconic
!= iconic
) {
2312 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2315 self
->iconic
= iconic
;
2318 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2321 old
= self
->wmstate
;
2322 self
->wmstate
= IconicState
;
2323 if (old
!= self
->wmstate
)
2324 PROP_MSG(self
->window
, kde_wm_change_state
,
2325 self
->wmstate
, 1, 0, 0);
2327 /* update the focus lists.. iconic windows go to the bottom of
2328 the list, put the new iconic window at the 'top of the
2330 focus_order_to_top(self
);
2338 client_set_desktop(self
, screen_desktop
, FALSE
);
2340 old
= self
->wmstate
;
2341 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2342 if (old
!= self
->wmstate
)
2343 PROP_MSG(self
->window
, kde_wm_change_state
,
2344 self
->wmstate
, 1, 0, 0);
2346 /* this puts it after the current focused window */
2347 focus_order_remove(self
);
2348 focus_order_add_new(self
);
2355 client_change_state(self
);
2356 client_showhide(self
);
2357 screen_update_areas();
2360 /* iconify all transients */
2361 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2362 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2366 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2368 /* move up the transient chain as far as possible first */
2369 self
= client_search_top_transient(self
);
2371 client_iconify_recursive(client_search_top_transient(self
),
2375 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2379 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2380 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2382 /* check if already done */
2384 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2385 if (dir
== 1 && self
->max_horz
) return;
2386 if (dir
== 2 && self
->max_vert
) return;
2388 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2389 if (dir
== 1 && !self
->max_horz
) return;
2390 if (dir
== 2 && !self
->max_vert
) return;
2393 /* we just tell it to configure in the same place and client_configure
2394 worries about filling the screen with the window */
2397 w
= self
->area
.width
;
2398 h
= self
->area
.height
;
2402 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2403 RECT_SET(self
->pre_max_area
,
2404 self
->area
.x
, self
->pre_max_area
.y
,
2405 self
->area
.width
, self
->pre_max_area
.height
);
2407 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2408 RECT_SET(self
->pre_max_area
,
2409 self
->pre_max_area
.x
, self
->area
.y
,
2410 self
->pre_max_area
.width
, self
->area
.height
);
2416 a
= screen_area_monitor(self
->desktop
, 0);
2417 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2418 if (self
->pre_max_area
.width
> 0) {
2419 x
= self
->pre_max_area
.x
;
2420 w
= self
->pre_max_area
.width
;
2422 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2423 0, self
->pre_max_area
.height
);
2425 /* pick some fallbacks... */
2426 x
= a
->x
+ a
->width
/ 4;
2430 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2431 if (self
->pre_max_area
.height
> 0) {
2432 y
= self
->pre_max_area
.y
;
2433 h
= self
->pre_max_area
.height
;
2435 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2436 self
->pre_max_area
.width
, 0);
2438 /* pick some fallbacks... */
2439 y
= a
->y
+ a
->height
/ 4;
2445 if (dir
== 0 || dir
== 1) /* horz */
2446 self
->max_horz
= max
;
2447 if (dir
== 0 || dir
== 2) /* vert */
2448 self
->max_vert
= max
;
2450 client_change_state(self
); /* change the state hints on the client */
2452 client_setup_decor_and_functions(self
);
2454 client_move_resize(self
, x
, y
, w
, h
);
2457 void client_shade(ObClient
*self
, gboolean shade
)
2459 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2460 shade
) || /* can't shade */
2461 self
->shaded
== shade
) return; /* already done */
2463 /* when we're iconic, don't change the wmstate */
2464 if (!self
->iconic
) {
2467 old
= self
->wmstate
;
2468 self
->wmstate
= shade
? IconicState
: NormalState
;
2469 if (old
!= self
->wmstate
)
2470 PROP_MSG(self
->window
, kde_wm_change_state
,
2471 self
->wmstate
, 1, 0, 0);
2474 self
->shaded
= shade
;
2475 client_change_state(self
);
2476 /* resize the frame to just the titlebar */
2477 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2480 void client_close(ObClient
*self
)
2484 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2486 /* in the case that the client provides no means to requesting that it
2487 close, we just kill it */
2488 if (!self
->delete_window
)
2492 XXX: itd be cool to do timeouts and shit here for killing the client's
2494 like... if the window is around after 5 seconds, then the close button
2495 turns a nice red, and if this function is called again, the client is
2499 ce
.xclient
.type
= ClientMessage
;
2500 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2501 ce
.xclient
.display
= ob_display
;
2502 ce
.xclient
.window
= self
->window
;
2503 ce
.xclient
.format
= 32;
2504 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2505 ce
.xclient
.data
.l
[1] = event_curtime
;
2506 ce
.xclient
.data
.l
[2] = 0l;
2507 ce
.xclient
.data
.l
[3] = 0l;
2508 ce
.xclient
.data
.l
[4] = 0l;
2509 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2512 void client_kill(ObClient
*self
)
2514 XKillClient(ob_display
, self
->window
);
2517 void client_set_desktop_recursive(ObClient
*self
,
2518 guint target
, gboolean donthide
)
2523 if (target
!= self
->desktop
) {
2525 ob_debug("Setting desktop %u\n", target
+1);
2527 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2529 /* remove from the old desktop(s) */
2530 focus_order_remove(self
);
2532 old
= self
->desktop
;
2533 self
->desktop
= target
;
2534 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2535 /* the frame can display the current desktop state */
2536 frame_adjust_state(self
->frame
);
2537 /* 'move' the window to the new desktop */
2539 client_showhide(self
);
2540 /* raise if it was not already on the desktop */
2541 if (old
!= DESKTOP_ALL
)
2543 screen_update_areas();
2545 /* add to the new desktop(s) */
2546 if (config_focus_new
)
2547 focus_order_to_top(self
);
2549 focus_order_to_bottom(self
);
2552 /* move all transients */
2553 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2554 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2558 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2560 client_set_desktop_recursive(client_search_top_transient(self
),
2564 ObClient
*client_search_modal_child(ObClient
*self
)
2569 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2570 ObClient
*c
= it
->data
;
2571 if ((ret
= client_search_modal_child(c
))) return ret
;
2572 if (c
->modal
) return c
;
2577 gboolean
client_validate(ObClient
*self
)
2581 XSync(ob_display
, FALSE
); /* get all events on the server */
2583 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2584 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2585 XPutBackEvent(ob_display
, &e
);
2592 void client_set_wm_state(ObClient
*self
, glong state
)
2594 if (state
== self
->wmstate
) return; /* no change */
2598 client_iconify(self
, TRUE
, TRUE
);
2601 client_iconify(self
, FALSE
, TRUE
);
2606 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2608 gboolean shaded
= self
->shaded
;
2609 gboolean fullscreen
= self
->fullscreen
;
2610 gboolean undecorated
= self
->undecorated
;
2611 gboolean max_horz
= self
->max_horz
;
2612 gboolean max_vert
= self
->max_vert
;
2613 gboolean modal
= self
->modal
;
2614 gboolean iconic
= self
->iconic
;
2617 if (!(action
== prop_atoms
.net_wm_state_add
||
2618 action
== prop_atoms
.net_wm_state_remove
||
2619 action
== prop_atoms
.net_wm_state_toggle
))
2620 /* an invalid action was passed to the client message, ignore it */
2623 for (i
= 0; i
< 2; ++i
) {
2624 Atom state
= i
== 0 ? data1
: data2
;
2626 if (!state
) continue;
2628 /* if toggling, then pick whether we're adding or removing */
2629 if (action
== prop_atoms
.net_wm_state_toggle
) {
2630 if (state
== prop_atoms
.net_wm_state_modal
)
2631 action
= modal
? prop_atoms
.net_wm_state_remove
:
2632 prop_atoms
.net_wm_state_add
;
2633 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2634 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2635 prop_atoms
.net_wm_state_add
;
2636 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2637 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2638 prop_atoms
.net_wm_state_add
;
2639 else if (state
== prop_atoms
.net_wm_state_shaded
)
2640 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2641 prop_atoms
.net_wm_state_add
;
2642 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2643 action
= self
->skip_taskbar
?
2644 prop_atoms
.net_wm_state_remove
:
2645 prop_atoms
.net_wm_state_add
;
2646 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2647 action
= self
->skip_pager
?
2648 prop_atoms
.net_wm_state_remove
:
2649 prop_atoms
.net_wm_state_add
;
2650 else if (state
== prop_atoms
.net_wm_state_hidden
)
2651 action
= self
->iconic
?
2652 prop_atoms
.net_wm_state_remove
:
2653 prop_atoms
.net_wm_state_add
;
2654 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2655 action
= fullscreen
?
2656 prop_atoms
.net_wm_state_remove
:
2657 prop_atoms
.net_wm_state_add
;
2658 else if (state
== prop_atoms
.net_wm_state_above
)
2659 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2660 prop_atoms
.net_wm_state_add
;
2661 else if (state
== prop_atoms
.net_wm_state_below
)
2662 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2663 prop_atoms
.net_wm_state_add
;
2664 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2665 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2666 prop_atoms
.net_wm_state_add
;
2669 if (action
== prop_atoms
.net_wm_state_add
) {
2670 if (state
== prop_atoms
.net_wm_state_modal
) {
2672 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2674 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2676 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2678 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2679 self
->skip_taskbar
= TRUE
;
2680 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2681 self
->skip_pager
= TRUE
;
2682 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2684 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2686 } else if (state
== prop_atoms
.net_wm_state_above
) {
2688 self
->below
= FALSE
;
2689 } else if (state
== prop_atoms
.net_wm_state_below
) {
2690 self
->above
= FALSE
;
2692 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2696 } else { /* action == prop_atoms.net_wm_state_remove */
2697 if (state
== prop_atoms
.net_wm_state_modal
) {
2699 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2701 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2703 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2705 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2706 self
->skip_taskbar
= FALSE
;
2707 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2708 self
->skip_pager
= FALSE
;
2709 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2711 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2713 } else if (state
== prop_atoms
.net_wm_state_above
) {
2714 self
->above
= FALSE
;
2715 } else if (state
== prop_atoms
.net_wm_state_below
) {
2716 self
->below
= FALSE
;
2717 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2718 undecorated
= FALSE
;
2722 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2723 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2725 if (max_horz
== max_vert
) { /* both going the same way */
2726 client_maximize(self
, max_horz
, 0, TRUE
);
2728 client_maximize(self
, max_horz
, 1, TRUE
);
2729 client_maximize(self
, max_vert
, 2, TRUE
);
2733 if (max_horz
!= self
->max_horz
)
2734 client_maximize(self
, max_horz
, 1, TRUE
);
2736 client_maximize(self
, max_vert
, 2, TRUE
);
2739 /* change fullscreen state before shading, as it will affect if the window
2741 if (fullscreen
!= self
->fullscreen
)
2742 client_fullscreen(self
, fullscreen
, TRUE
);
2743 if (shaded
!= self
->shaded
)
2744 client_shade(self
, shaded
);
2745 if (undecorated
!= self
->undecorated
)
2746 client_set_undecorated(self
, undecorated
);
2747 if (modal
!= self
->modal
) {
2748 self
->modal
= modal
;
2749 /* when a window changes modality, then its stacking order with its
2750 transients needs to change */
2753 if (iconic
!= self
->iconic
)
2754 client_iconify(self
, iconic
, FALSE
);
2756 client_calc_layer(self
);
2757 client_change_state(self
); /* change the hint to reflect these changes */
2760 ObClient
*client_focus_target(ObClient
*self
)
2764 /* if we have a modal child, then focus it, not us */
2765 child
= client_search_modal_child(client_search_top_transient(self
));
2766 if (child
) return child
;
2770 gboolean
client_can_focus(ObClient
*self
)
2774 /* choose the correct target */
2775 self
= client_focus_target(self
);
2777 if (!self
->frame
->visible
)
2780 if (!(self
->can_focus
|| self
->focus_notify
))
2783 /* do a check to see if the window has already been unmapped or destroyed
2784 do this intelligently while watching out for unmaps we've generated
2785 (ignore_unmaps > 0) */
2786 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2787 DestroyNotify
, &ev
)) {
2788 XPutBackEvent(ob_display
, &ev
);
2791 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2792 UnmapNotify
, &ev
)) {
2793 if (self
->ignore_unmaps
) {
2794 self
->ignore_unmaps
--;
2796 XPutBackEvent(ob_display
, &ev
);
2804 gboolean
client_focus(ObClient
*self
)
2806 /* choose the correct target */
2807 self
= client_focus_target(self
);
2809 if (!client_can_focus(self
)) {
2810 if (!self
->frame
->visible
) {
2811 /* update the focus lists */
2812 focus_order_to_top(self
);
2817 if (self
->can_focus
) {
2818 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2819 I choose to use it always, hopefully to find errors quicker, if any
2820 are left. (I hate X. I hate focus events.)
2822 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2823 #799. So now it is RevertToNone again.
2825 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2829 if (self
->focus_notify
) {
2831 ce
.xclient
.type
= ClientMessage
;
2832 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2833 ce
.xclient
.display
= ob_display
;
2834 ce
.xclient
.window
= self
->window
;
2835 ce
.xclient
.format
= 32;
2836 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2837 ce
.xclient
.data
.l
[1] = event_curtime
;
2838 ce
.xclient
.data
.l
[2] = 0l;
2839 ce
.xclient
.data
.l
[3] = 0l;
2840 ce
.xclient
.data
.l
[4] = 0l;
2841 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2845 ob_debug("%sively focusing %lx at %d\n",
2846 (self
->can_focus
? "act" : "pass"),
2847 self
->window
, (gint
) event_curtime
);
2850 /* Cause the FocusIn to come back to us. Important for desktop switches,
2851 since otherwise we'll have no FocusIn on the queue and send it off to
2852 the focus_backup. */
2853 XSync(ob_display
, FALSE
);
2857 /* Used when the current client is closed, focus_last will then prevent
2858 * focus from going to the mouse pointer */
2859 void client_unfocus(ObClient
*self
)
2861 if (focus_client
== self
) {
2863 ob_debug("client_unfocus for %lx\n", self
->window
);
2865 focus_fallback(OB_FOCUS_FALLBACK_CLOSED
);
2869 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
2871 /* XXX do some stuff here if user is false to determine if we really want
2872 to activate it or not (a parent or group member is currently active) */
2874 if (client_normal(self
) && screen_showing_desktop
)
2875 screen_show_desktop(FALSE
);
2877 client_iconify(self
, FALSE
, here
);
2878 if (self
->desktop
!= DESKTOP_ALL
&&
2879 self
->desktop
!= screen_desktop
) {
2881 client_set_desktop(self
, screen_desktop
, FALSE
);
2883 screen_set_desktop(self
->desktop
);
2884 } else if (!self
->frame
->visible
)
2885 /* if its not visible for other reasons, then don't mess
2889 client_shade(self
, FALSE
);
2893 /* we do this an action here. this is rather important. this is because
2894 we want the results from the focus change to take place BEFORE we go
2895 about raising the window. when a fullscreen window loses focus, we need
2896 this or else the raise wont be able to raise above the to-lose-focus
2897 fullscreen window. */
2901 void client_raise(ObClient
*self
)
2903 action_run_string("Raise", self
);
2906 void client_lower(ObClient
*self
)
2908 action_run_string("Lower", self
);
2911 gboolean
client_focused(ObClient
*self
)
2913 return self
== focus_client
;
2916 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
2919 /* si is the smallest image >= req */
2920 /* li is the largest image < req */
2921 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2923 if (!self
->nicons
) {
2924 ObClientIcon
*parent
= NULL
;
2926 if (self
->transient_for
) {
2927 if (self
->transient_for
!= OB_TRAN_GROUP
)
2928 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2931 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2932 ObClient
*c
= it
->data
;
2933 if (c
!= self
&& !c
->transient_for
) {
2934 if ((parent
= client_icon_recursive(c
, w
, h
)))
2944 for (i
= 0; i
< self
->nicons
; ++i
) {
2945 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2946 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2950 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2955 if (largest
== 0) /* didnt find one smaller than the requested size */
2956 return &self
->icons
[si
];
2957 return &self
->icons
[li
];
2960 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
2963 static ObClientIcon deficon
;
2965 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
2966 deficon
.width
= deficon
.height
= 48;
2967 deficon
.data
= ob_rr_theme
->def_win_icon
;
2973 /* this be mostly ripped from fvwm */
2974 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2976 gint my_cx
, my_cy
, his_cx
, his_cy
;
2979 gint score
, best_score
;
2980 ObClient
*best_client
, *cur
;
2986 /* first, find the centre coords of the currently focused window */
2987 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2988 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2993 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
2996 /* the currently selected window isn't interesting */
2999 if (!client_normal(cur
))
3001 /* using c->desktop instead of screen_desktop doesn't work if the
3002 * current window was omnipresent, hope this doesn't have any other
3004 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3008 if(!(client_focus_target(cur
) == cur
&&
3009 client_can_focus(cur
)))
3012 /* find the centre coords of this window, from the
3013 * currently focused window's point of view */
3014 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3015 + cur
->frame
->area
.width
/ 2;
3016 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3017 + cur
->frame
->area
.height
/ 2;
3019 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3020 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3022 /* Rotate the diagonals 45 degrees counterclockwise.
3023 * To do this, multiply the matrix /+h +h\ with the
3024 * vector (x y). \-h +h/
3025 * h = sqrt(0.5). We can set h := 1 since absolute
3026 * distance doesn't matter here. */
3027 tx
= his_cx
+ his_cy
;
3028 his_cy
= -his_cx
+ his_cy
;
3033 case OB_DIRECTION_NORTH
:
3034 case OB_DIRECTION_SOUTH
:
3035 case OB_DIRECTION_NORTHEAST
:
3036 case OB_DIRECTION_SOUTHWEST
:
3037 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3038 distance
= ((dir
== OB_DIRECTION_NORTH
||
3039 dir
== OB_DIRECTION_NORTHEAST
) ?
3042 case OB_DIRECTION_EAST
:
3043 case OB_DIRECTION_WEST
:
3044 case OB_DIRECTION_SOUTHEAST
:
3045 case OB_DIRECTION_NORTHWEST
:
3046 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3047 distance
= ((dir
== OB_DIRECTION_WEST
||
3048 dir
== OB_DIRECTION_NORTHWEST
) ?
3053 /* the target must be in the requested direction */
3057 /* Calculate score for this window. The smaller the better. */
3058 score
= distance
+ offset
;
3060 /* windows more than 45 degrees off the direction are
3061 * heavily penalized and will only be chosen if nothing
3062 * else within a million pixels */
3063 if(offset
> distance
)
3066 if(best_score
== -1 || score
< best_score
)
3074 void client_set_layer(ObClient
*self
, gint layer
)
3078 self
->above
= FALSE
;
3079 } else if (layer
== 0) {
3080 self
->below
= self
->above
= FALSE
;
3082 self
->below
= FALSE
;
3085 client_calc_layer(self
);
3086 client_change_state(self
); /* reflect this in the state hints */
3089 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3091 if (self
->undecorated
!= undecorated
) {
3092 self
->undecorated
= undecorated
;
3093 client_setup_decor_and_functions(self
);
3094 /* Make sure the client knows it might have moved. Maybe there is a
3095 * better way of doing this so only one client_configure is sent, but
3096 * since 125 of these are sent per second when moving the window (with
3097 * user = FALSE) i doubt it matters much.
3099 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3100 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3101 client_change_state(self
); /* reflect this in the state hints */
3105 /* Determines which physical monitor a client is on by calculating the
3106 area of the part of the client on each monitor. The number of the
3107 monitor containing the greatest area of the client is returned.*/
3108 guint
client_monitor(ObClient
*self
)
3114 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3115 Rect
*area
= screen_physical_area_monitor(i
);
3116 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3120 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3121 v
= r
.width
* r
.height
;
3132 ObClient
*client_search_top_transient(ObClient
*self
)
3134 /* move up the transient chain as far as possible */
3135 if (self
->transient_for
) {
3136 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3137 return client_search_top_transient(self
->transient_for
);
3141 g_assert(self
->group
);
3143 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3144 ObClient
*c
= it
->data
;
3146 /* checking transient_for prevents infinate loops! */
3147 if (c
!= self
&& !c
->transient_for
)
3158 ObClient
*client_search_focus_parent(ObClient
*self
)
3160 if (self
->transient_for
) {
3161 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3162 if (client_focused(self
->transient_for
))
3163 return self
->transient_for
;
3167 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3168 ObClient
*c
= it
->data
;
3170 /* checking transient_for prevents infinate loops! */
3171 if (c
!= self
&& !c
->transient_for
)
3172 if (client_focused(c
))
3181 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3183 if (self
->transient_for
) {
3184 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3185 if (self
->transient_for
== search
)
3190 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3191 ObClient
*c
= it
->data
;
3193 /* checking transient_for prevents infinate loops! */
3194 if (c
!= self
&& !c
->transient_for
)
3204 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3208 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3209 if (sit
->data
== search
)
3211 if (client_search_transient(sit
->data
, search
))
3217 void client_update_sm_client_id(ObClient
*self
)
3219 g_free(self
->sm_client_id
);
3220 self
->sm_client_id
= NULL
;
3222 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3224 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3225 &self
->sm_client_id
);
3228 #define WANT_EDGE(cur, c) \
3231 if(!client_normal(cur)) \
3233 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3237 if(cur->layer < c->layer && !config_resist_layers_below) \
3240 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3241 if ((his_edge_start >= my_edge_start && \
3242 his_edge_start <= my_edge_end) || \
3243 (my_edge_start >= his_edge_start && \
3244 my_edge_start <= his_edge_end)) \
3247 /* finds the nearest edge in the given direction from the current client
3248 * note to self: the edge is the -frame- edge (the actual one), not the
3251 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3253 gint dest
, monitor_dest
;
3254 gint my_edge_start
, my_edge_end
, my_offset
;
3261 a
= screen_area(c
->desktop
);
3262 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3265 case OB_DIRECTION_NORTH
:
3266 my_edge_start
= c
->frame
->area
.x
;
3267 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3268 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3270 /* default: top of screen */
3271 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3272 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3273 /* if the monitor edge comes before the screen edge, */
3274 /* use that as the destination instead. (For xinerama) */
3275 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3276 dest
= monitor_dest
;
3278 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3279 gint his_edge_start
, his_edge_end
, his_offset
;
3280 ObClient
*cur
= it
->data
;
3284 his_edge_start
= cur
->frame
->area
.x
;
3285 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3286 his_offset
= cur
->frame
->area
.y
+
3287 (hang
? 0 : cur
->frame
->area
.height
);
3289 if(his_offset
+ 1 > my_offset
)
3292 if(his_offset
< dest
)
3295 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3298 case OB_DIRECTION_SOUTH
:
3299 my_edge_start
= c
->frame
->area
.x
;
3300 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3301 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3303 /* default: bottom of screen */
3304 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3305 monitor_dest
= monitor
->y
+ monitor
->height
-
3306 (hang
? c
->frame
->area
.height
: 0);
3307 /* if the monitor edge comes before the screen edge, */
3308 /* use that as the destination instead. (For xinerama) */
3309 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3310 dest
= monitor_dest
;
3312 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3313 gint his_edge_start
, his_edge_end
, his_offset
;
3314 ObClient
*cur
= it
->data
;
3318 his_edge_start
= cur
->frame
->area
.x
;
3319 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3320 his_offset
= cur
->frame
->area
.y
+
3321 (hang
? cur
->frame
->area
.height
: 0);
3324 if(his_offset
- 1 < my_offset
)
3327 if(his_offset
> dest
)
3330 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3333 case OB_DIRECTION_WEST
:
3334 my_edge_start
= c
->frame
->area
.y
;
3335 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3336 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3338 /* default: leftmost egde of screen */
3339 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3340 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3341 /* if the monitor edge comes before the screen edge, */
3342 /* use that as the destination instead. (For xinerama) */
3343 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3344 dest
= monitor_dest
;
3346 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3347 gint his_edge_start
, his_edge_end
, his_offset
;
3348 ObClient
*cur
= it
->data
;
3352 his_edge_start
= cur
->frame
->area
.y
;
3353 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3354 his_offset
= cur
->frame
->area
.x
+
3355 (hang
? 0 : cur
->frame
->area
.width
);
3357 if(his_offset
+ 1 > my_offset
)
3360 if(his_offset
< dest
)
3363 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3366 case OB_DIRECTION_EAST
:
3367 my_edge_start
= c
->frame
->area
.y
;
3368 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3369 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3371 /* default: rightmost edge of screen */
3372 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3373 monitor_dest
= monitor
->x
+ monitor
->width
-
3374 (hang
? c
->frame
->area
.width
: 0);
3375 /* if the monitor edge comes before the screen edge, */
3376 /* use that as the destination instead. (For xinerama) */
3377 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3378 dest
= monitor_dest
;
3380 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3381 gint his_edge_start
, his_edge_end
, his_offset
;
3382 ObClient
*cur
= it
->data
;
3386 his_edge_start
= cur
->frame
->area
.y
;
3387 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3388 his_offset
= cur
->frame
->area
.x
+
3389 (hang
? cur
->frame
->area
.width
: 0);
3391 if(his_offset
- 1 < my_offset
)
3394 if(his_offset
> dest
)
3397 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3400 case OB_DIRECTION_NORTHEAST
:
3401 case OB_DIRECTION_SOUTHEAST
:
3402 case OB_DIRECTION_NORTHWEST
:
3403 case OB_DIRECTION_SOUTHWEST
:
3404 /* not implemented */
3406 g_assert_not_reached();
3407 dest
= 0; /* suppress warning */
3412 ObClient
* client_under_pointer()
3416 ObClient
*ret
= NULL
;
3418 if (screen_pointer_pos(&x
, &y
)) {
3419 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3420 if (WINDOW_IS_CLIENT(it
->data
)) {
3421 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3422 if (c
->frame
->visible
&&
3423 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3433 gboolean
client_has_group_siblings(ObClient
*self
)
3435 return self
->group
&& self
->group
->members
->next
;