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
;
62 static GSList
*client_destructors
= NULL
;
63 static Time client_last_user_time
= CurrentTime
;
65 static void client_get_all(ObClient
*self
);
66 static void client_toggle_border(ObClient
*self
, gboolean show
);
67 static void client_get_startup_id(ObClient
*self
);
68 static void client_get_area(ObClient
*self
);
69 static void client_get_desktop(ObClient
*self
);
70 static void client_get_state(ObClient
*self
);
71 static void client_get_shaped(ObClient
*self
);
72 static void client_get_mwm_hints(ObClient
*self
);
73 static void client_get_gravity(ObClient
*self
);
74 static void client_showhide(ObClient
*self
);
75 static void client_change_allowed_actions(ObClient
*self
);
76 static void client_change_state(ObClient
*self
);
77 static void client_apply_startup_state(ObClient
*self
);
78 static void client_restore_session_state(ObClient
*self
);
79 static void client_restore_session_stacking(ObClient
*self
);
81 void client_startup(gboolean reconfig
)
88 void client_shutdown(gboolean reconfig
)
92 void client_add_destructor(ObClientDestructor func
, gpointer data
)
94 Destructor
*d
= g_new(Destructor
, 1);
97 client_destructors
= g_slist_prepend(client_destructors
, d
);
100 void client_remove_destructor(ObClientDestructor func
)
104 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
105 Destructor
*d
= it
->data
;
106 if (d
->func
== func
) {
108 client_destructors
= g_slist_delete_link(client_destructors
, it
);
114 void client_set_list()
116 Window
*windows
, *win_it
;
118 guint size
= g_list_length(client_list
);
120 /* create an array of the window ids */
122 windows
= g_new(Window
, size
);
124 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
125 *win_it
= ((ObClient
*)it
->data
)->window
;
129 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
130 net_client_list
, window
, (gulong
*)windows
, size
);
139 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
143 for (it = self->transients; it; it = g_slist_next(it)) {
144 if (!func(it->data, data)) return;
145 client_foreach_transient(it->data, func, data);
149 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
151 if (self->transient_for) {
152 if (self->transient_for != OB_TRAN_GROUP) {
153 if (!func(self->transient_for, data)) return;
154 client_foreach_ancestor(self->transient_for, func, data);
158 for (it = self->group->members; it; it = g_slist_next(it))
159 if (it->data != self &&
160 !((ObClient*)it->data)->transient_for) {
161 if (!func(it->data, data)) return;
162 client_foreach_ancestor(it->data, func, data);
169 void client_manage_all()
174 XWindowAttributes attrib
;
176 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
177 &w
, &w
, &children
, &nchild
);
179 /* remove all icon windows from the list */
180 for (i
= 0; i
< nchild
; i
++) {
181 if (children
[i
] == None
) continue;
182 wmhints
= XGetWMHints(ob_display
, children
[i
]);
184 if ((wmhints
->flags
& IconWindowHint
) &&
185 (wmhints
->icon_window
!= children
[i
]))
186 for (j
= 0; j
< nchild
; j
++)
187 if (children
[j
] == wmhints
->icon_window
) {
195 for (i
= 0; i
< nchild
; ++i
) {
196 if (children
[i
] == None
)
198 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
199 if (attrib
.override_redirect
) continue;
201 if (attrib
.map_state
!= IsUnmapped
)
202 client_manage(children
[i
]);
208 static ObAppSettings
*get_settings(ObClient
*client
)
210 GSList
*a
= config_per_app_settings
;
213 ObAppSettings
*app
= (ObAppSettings
*) a
->data
;
216 (app
->name
&& !app
->class && !strcmp(app
->name
, client
->name
))
217 || (app
->class && !app
->name
&& !strcmp(app
->class, client
->class))
218 || (app
->class && app
->name
&& !strcmp(app
->class, client
->class)
219 && !strcmp(app
->name
, client
->name
))
221 ob_debug("Window matching: %s\n", app
->name
);
222 /* Match if no role was specified in the per app setting, or if the
223 * string matches the beginning of the role, since apps like to set
224 * the role to things like browser-window-23c4b2f */
226 || !strncmp(app
->role
, client
->role
, strlen(app
->role
)))
235 void client_manage(Window window
)
239 XWindowAttributes attrib
;
240 XSetWindowAttributes attrib_set
;
242 gboolean activate
= FALSE
;
243 ObAppSettings
*settings
;
247 /* check if it has already been unmapped by the time we started mapping
248 the grab does a sync so we don't have to here */
249 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
250 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
252 XPutBackEvent(ob_display
, &e
);
255 return; /* don't manage it */
258 /* make sure it isn't an override-redirect window */
259 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
260 attrib
.override_redirect
)
263 return; /* don't manage it */
266 /* is the window a docking app */
267 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
268 if ((wmhint
->flags
& StateHint
) &&
269 wmhint
->initial_state
== WithdrawnState
)
271 dock_add(window
, wmhint
);
279 ob_debug("Managing window: %lx\n", window
);
281 /* choose the events we want to receive on the CLIENT window */
282 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
283 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
284 XChangeWindowAttributes(ob_display
, window
,
285 CWEventMask
|CWDontPropagate
, &attrib_set
);
288 /* create the ObClient struct, and populate it from the hints on the
290 self
= g_new0(ObClient
, 1);
291 self
->obwin
.type
= Window_Client
;
292 self
->window
= window
;
294 /* non-zero defaults */
295 self
->title_count
= 1;
296 self
->wmstate
= NormalState
;
298 self
->desktop
= screen_num_desktops
; /* always an invalid value */
300 client_get_all(self
);
301 client_restore_session_state(self
);
303 self
->user_time
= sn_app_started(self
->startup_id
, self
->class);
305 /* update the focus lists, do this before the call to change_state or
306 it can end up in the list twice! */
307 focus_order_add_new(self
);
309 client_change_state(self
);
311 /* remove the client's border (and adjust re gravity) */
312 client_toggle_border(self
, FALSE
);
314 /* specify that if we exit, the window should not be destroyed and should
315 be reparented back to root automatically */
316 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
318 /* create the decoration frame for the client window */
319 self
->frame
= frame_new(self
);
321 frame_grab_client(self
->frame
, self
);
325 client_apply_startup_state(self
);
327 /* get and set application level settings */
328 settings
= get_settings(self
);
330 stacking_add(CLIENT_AS_WINDOW(self
));
331 client_restore_session_stacking(self
);
334 /* Don't worry, we won't actually both shade and undecorate the
335 * window when push comes to shove. */
336 if (settings
->shade
!= -1)
337 client_shade(self
, !!settings
->shade
);
338 if (settings
->decor
!= -1)
339 client_set_undecorated(self
, !settings
->decor
);
340 if (settings
->iconic
!= -1)
341 client_iconify(self
, !!settings
->iconic
, FALSE
);
342 if (settings
->skip_pager
!= -1) {
343 self
->skip_pager
= !!settings
->skip_pager
;
344 client_change_state(self
);
346 if (settings
->skip_taskbar
!= -1) {
347 self
->skip_taskbar
= !!settings
->skip_taskbar
;
348 client_change_state(self
);
351 /* 1 && -1 shouldn't be possible by the code in config.c */
352 if (settings
->max_vert
== 1 && settings
->max_horz
== 1)
353 client_maximize(self
, TRUE
, 0, TRUE
);
354 else if (settings
->max_vert
== 0 && settings
->max_horz
== 0)
355 client_maximize(self
, FALSE
, 0, TRUE
);
356 else if (settings
->max_vert
== 1 && settings
->max_horz
== 0) {
357 client_maximize(self
, TRUE
, 2, TRUE
);
358 client_maximize(self
, FALSE
, 1, TRUE
);
359 } else if (settings
->max_vert
== 0 && settings
->max_horz
== 1) {
360 client_maximize(self
, TRUE
, 1, TRUE
);
361 client_maximize(self
, FALSE
, 2, TRUE
);
364 if (settings
->fullscreen
!= -1)
365 client_fullscreen(self
, !!settings
->fullscreen
, TRUE
);
367 if (settings
->desktop
< screen_num_desktops
368 || settings
->desktop
== DESKTOP_ALL
)
369 client_set_desktop(self
, settings
->desktop
, TRUE
);
371 if (settings
->layer
> -2 && settings
->layer
< 2)
372 client_set_layer(self
, settings
->layer
);
376 /* focus the new window? */
377 if (ob_state() != OB_STATE_STARTING
&&
378 /* this means focus=true for window is same as config_focus_new=true */
379 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
380 client_search_focus_parent(self
)) &&
381 /* this checks for focus=false for the window */
382 (!settings
|| settings
->focus
!= 0) &&
383 /* note the check against Type_Normal/Dialog, not client_normal(self),
384 which would also include other types. in this case we want more
385 strict rules for focus */
386 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
387 self
->type
== OB_CLIENT_TYPE_DIALOG
))
391 if (self
->desktop
!= screen_desktop
) {
392 /* activate the window */
395 gboolean group_foc
= FALSE
;
400 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
402 if (client_focused(it
->data
))
410 (!self
->transient_for
&& (!self
->group
||
411 !self
->group
->members
->next
))) ||
412 client_search_focus_tree_full(self
) ||
414 !client_normal(focus_client
))
416 /* activate the window */
423 if (ob_state() == OB_STATE_RUNNING
) {
424 gint x
= self
->area
.x
, ox
= x
;
425 gint y
= self
->area
.y
, oy
= y
;
428 transient
= place_client(self
, &x
, &y
, settings
);
430 /* make sure the window is visible. */
431 client_find_onscreen(self
, &x
, &y
,
432 self
->frame
->area
.width
,
433 self
->frame
->area
.height
,
434 /* non-normal clients has less rules, and
435 windows that are being restored from a
436 session do also. we can assume you want
437 it back where you saved it. Clients saying
438 they placed themselves are subjected to
439 harder rules, ones that are placed by
440 place.c or by the user are allowed partially
441 off-screen and on xinerama divides (ie,
442 it is up to the placement routines to avoid
443 the xinerama divides) */
445 (((self
->positioned
& PPosition
) &&
446 !(self
->positioned
& USPosition
)) &&
447 client_normal(self
) &&
449 if (x
!= ox
|| y
!= oy
)
450 client_move(self
, x
, y
);
453 keyboard_grab_for_client(self
, TRUE
);
454 mouse_grab_for_client(self
, TRUE
);
456 client_showhide(self
);
458 /* use client_focus instead of client_activate cuz client_activate does
459 stuff like switch desktops etc and I'm not interested in all that when
460 a window maps since its not based on an action from the user like
461 clicking a window to activate is. so keep the new window out of the way
464 /* This is focus stealing prevention, if a user_time has been set */
465 if (self
->user_time
== CurrentTime
||
466 self
->user_time
> client_last_user_time
)
468 /* if using focus_delay, stop the timer now so that focus doesn't
470 event_halt_focus_delay();
473 /* since focus can change the stacking orders, if we focus the
474 window then the standard raise it gets is not enough, we need
475 to queue one for after the focus change takes place */
478 ob_debug("Focus stealing prevention activated for %s\n",
480 /* if the client isn't focused, then hilite it so the user
482 client_hilite(self
, TRUE
);
486 /* client_activate does this but we aret using it so we have to do it
488 if (screen_showing_desktop
)
489 screen_show_desktop(FALSE
);
491 /* add to client list/map */
492 client_list
= g_list_append(client_list
, self
);
493 g_hash_table_insert(window_map
, &self
->window
, self
);
495 /* this has to happen after we're in the client_list */
496 if (STRUT_EXISTS(self
->strut
))
497 screen_update_areas();
499 /* update the list hints */
502 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
505 void client_unmanage_all()
507 while (client_list
!= NULL
)
508 client_unmanage(client_list
->data
);
511 void client_unmanage(ObClient
*self
)
516 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
518 g_assert(self
!= NULL
);
520 keyboard_grab_for_client(self
, FALSE
);
521 mouse_grab_for_client(self
, FALSE
);
523 /* potentially fix focusLast */
524 if (config_focus_last
)
525 grab_pointer(TRUE
, OB_CURSOR_NONE
);
527 /* remove the window from our save set */
528 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
530 /* we dont want events no more */
531 XSelectInput(ob_display
, self
->window
, NoEventMask
);
533 frame_hide(self
->frame
);
535 client_list
= g_list_remove(client_list
, self
);
536 stacking_remove(self
);
537 g_hash_table_remove(window_map
, &self
->window
);
539 /* update the focus lists */
540 focus_order_remove(self
);
542 /* once the client is out of the list, update the struts to remove it's
544 if (STRUT_EXISTS(self
->strut
))
545 screen_update_areas();
547 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
548 Destructor
*d
= it
->data
;
549 d
->func(self
, d
->data
);
552 if (focus_client
== self
) {
555 /* focus the last focused window on the desktop, and ignore enter
556 events from the unmap so it doesnt mess with the focus */
557 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
558 /* remove these flags so we don't end up getting focused in the
560 self
->can_focus
= FALSE
;
561 self
->focus_notify
= FALSE
;
563 client_unfocus(self
);
566 /* tell our parent(s) that we're gone */
567 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
568 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
569 if (it
->data
!= self
)
570 ((ObClient
*)it
->data
)->transients
=
571 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
572 } else if (self
->transient_for
) { /* transient of window */
573 self
->transient_for
->transients
=
574 g_slist_remove(self
->transient_for
->transients
, self
);
577 /* tell our transients that we're gone */
578 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
579 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
580 ((ObClient
*)it
->data
)->transient_for
= NULL
;
581 client_calc_layer(it
->data
);
585 /* remove from its group */
587 group_remove(self
->group
, self
);
591 /* give the client its border back */
592 client_toggle_border(self
, TRUE
);
594 /* reparent the window out of the frame, and free the frame */
595 frame_release_client(self
->frame
, self
);
598 if (ob_state() != OB_STATE_EXITING
) {
599 /* these values should not be persisted across a window
601 PROP_ERASE(self
->window
, net_wm_desktop
);
602 PROP_ERASE(self
->window
, net_wm_state
);
603 PROP_ERASE(self
->window
, wm_state
);
605 /* if we're left in an unmapped state, the client wont be mapped. this
606 is bad, since we will no longer be managing the window on restart */
607 XMapWindow(ob_display
, self
->window
);
611 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
613 /* free all data allocated in the client struct */
614 g_slist_free(self
->transients
);
615 for (j
= 0; j
< self
->nicons
; ++j
)
616 g_free(self
->icons
[j
].data
);
617 if (self
->nicons
> 0)
620 g_free(self
->icon_title
);
624 g_free(self
->sm_client_id
);
627 /* update the list hints */
630 if (config_focus_last
)
631 grab_pointer(FALSE
, OB_CURSOR_NONE
);
634 static void client_restore_session_state(ObClient
*self
)
638 if (!(it
= session_state_find(self
)))
641 self
->session
= it
->data
;
643 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
644 self
->positioned
= PPosition
;
645 if (self
->session
->w
> 0)
646 self
->area
.width
= self
->session
->w
;
647 if (self
->session
->h
> 0)
648 self
->area
.height
= self
->session
->h
;
649 XResizeWindow(ob_display
, self
->window
,
650 self
->area
.width
, self
->area
.height
);
652 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
653 self
->session
->desktop
:
654 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
655 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
657 self
->shaded
= self
->session
->shaded
;
658 self
->iconic
= self
->session
->iconic
;
659 self
->skip_pager
= self
->session
->skip_pager
;
660 self
->skip_taskbar
= self
->session
->skip_taskbar
;
661 self
->fullscreen
= self
->session
->fullscreen
;
662 self
->above
= self
->session
->above
;
663 self
->below
= self
->session
->below
;
664 self
->max_horz
= self
->session
->max_horz
;
665 self
->max_vert
= self
->session
->max_vert
;
668 static void client_restore_session_stacking(ObClient
*self
)
672 if (!self
->session
) return;
674 it
= g_list_find(session_saved_state
, self
->session
);
675 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
678 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
679 if (session_state_cmp(it
->data
, cit
->data
))
682 client_calc_layer(self
);
683 stacking_below(CLIENT_AS_WINDOW(self
),
684 CLIENT_AS_WINDOW(cit
->data
));
690 void client_move_onscreen(ObClient
*self
, gboolean rude
)
692 gint x
= self
->area
.x
;
693 gint y
= self
->area
.y
;
694 if (client_find_onscreen(self
, &x
, &y
,
695 self
->frame
->area
.width
,
696 self
->frame
->area
.height
, rude
)) {
697 client_move(self
, x
, y
);
701 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
705 gint ox
= *x
, oy
= *y
;
707 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
710 /* XXX watch for xinerama dead areas */
711 /* This makes sure windows aren't entirely outside of the screen so you
712 can't see them at all.
713 It makes sure 10% of the window is on the screen at least. At don't let
714 it move itself off the top of the screen, which would hide the titlebar
715 on you. (The user can still do this if they want too, it's only limiting
718 if (client_normal(self
)) {
719 a
= screen_area(self
->desktop
);
720 if (!self
->strut
.right
&&
721 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
722 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
723 if (!self
->strut
.bottom
&&
724 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
725 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
726 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
727 *x
= a
->x
- self
->frame
->area
.width
*9/10;
728 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
729 *y
= a
->y
- self
->frame
->area
.width
*9/10;
732 /* This here doesn't let windows even a pixel outside the screen,
733 * when called from client_manage, programs placing themselves are
734 * forced completely onscreen, while things like
735 * xterm -geometry resolution-width/2 will work fine. Trying to
736 * place it completely offscreen will be handled in the above code.
737 * Sorry for this confused comment, i am tired. */
739 /* avoid the xinerama monitor divide while we're at it,
740 * remember to fix the placement stuff to avoid it also and
741 * then remove this XXX */
742 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
743 /* dont let windows map into the strut unless they
744 are bigger than the available area */
746 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
747 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
748 *x
= a
->x
+ a
->width
- w
;
750 if (h
<= a
->height
) {
751 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
752 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
753 *y
= a
->y
+ a
->height
- h
;
757 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
760 return ox
!= *x
|| oy
!= *y
;
763 static void client_toggle_border(ObClient
*self
, gboolean show
)
765 /* adjust our idea of where the client is, based on its border. When the
766 border is removed, the client should now be considered to be in a
768 when re-adding the border to the client, the same operation needs to be
770 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
771 gint x
= oldx
, y
= oldy
;
772 switch(self
->gravity
) {
774 case NorthWestGravity
:
776 case SouthWestGravity
:
778 case NorthEastGravity
:
780 case SouthEastGravity
:
781 if (show
) x
-= self
->border_width
* 2;
782 else x
+= self
->border_width
* 2;
789 if (show
) x
-= self
->border_width
;
790 else x
+= self
->border_width
;
793 switch(self
->gravity
) {
795 case NorthWestGravity
:
797 case NorthEastGravity
:
799 case SouthWestGravity
:
801 case SouthEastGravity
:
802 if (show
) y
-= self
->border_width
* 2;
803 else y
+= self
->border_width
* 2;
810 if (show
) y
-= self
->border_width
;
811 else y
+= self
->border_width
;
818 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
820 /* move the client so it is back it the right spot _with_ its
822 if (x
!= oldx
|| y
!= oldy
)
823 XMoveWindow(ob_display
, self
->window
, x
, y
);
825 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
829 static void client_get_all(ObClient
*self
)
831 client_get_area(self
);
832 client_get_mwm_hints(self
);
834 /* The transient hint is used to pick a type, but the type can also affect
835 transiency (dialogs are always made transients of their group if they
836 have one). This is Havoc's idea, but it is needed to make some apps
837 work right (eg tsclient). */
838 client_update_transient_for(self
);
839 client_get_type(self
);/* this can change the mwmhints for special cases */
840 client_update_transient_for(self
);
842 client_update_wmhints(self
);
843 client_get_startup_id(self
);
844 client_get_desktop(self
);/* uses transient data/group/startup id if a
845 desktop is not specified */
846 client_get_shaped(self
);
848 client_get_state(self
);
851 /* a couple type-based defaults for new windows */
853 /* this makes sure that these windows appear on all desktops */
854 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
855 self
->desktop
= DESKTOP_ALL
;
858 client_update_protocols(self
);
860 client_get_gravity(self
); /* get the attribute gravity */
861 client_update_normal_hints(self
); /* this may override the attribute
864 /* got the type, the mwmhints, the protocols, and the normal hints
865 (min/max sizes), so we're ready to set up the decorations/functions */
866 client_setup_decor_and_functions(self
);
868 client_update_title(self
);
869 client_update_class(self
);
870 client_update_sm_client_id(self
);
871 client_update_strut(self
);
872 client_update_icons(self
);
873 client_update_user_time(self
, FALSE
);
876 static void client_get_startup_id(ObClient
*self
)
878 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
880 PROP_GETS(self
->group
->leader
,
881 net_startup_id
, utf8
, &self
->startup_id
);
884 static void client_get_area(ObClient
*self
)
886 XWindowAttributes wattrib
;
889 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
890 g_assert(ret
!= BadWindow
);
892 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
893 self
->border_width
= wattrib
.border_width
;
896 static void client_get_desktop(ObClient
*self
)
898 guint32 d
= screen_num_desktops
; /* an always-invalid value */
900 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
901 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
902 self
->desktop
= screen_num_desktops
- 1;
906 gboolean trdesk
= FALSE
;
908 if (self
->transient_for
) {
909 if (self
->transient_for
!= OB_TRAN_GROUP
) {
910 self
->desktop
= self
->transient_for
->desktop
;
915 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
916 if (it
->data
!= self
&&
917 !((ObClient
*)it
->data
)->transient_for
) {
918 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
925 /* try get from the startup-notification protocol */
926 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
927 if (self
->desktop
>= screen_num_desktops
&&
928 self
->desktop
!= DESKTOP_ALL
)
929 self
->desktop
= screen_num_desktops
- 1;
931 /* defaults to the current desktop */
932 self
->desktop
= screen_desktop
;
935 if (self
->desktop
!= d
) {
936 /* set the desktop hint, to make sure that it always exists */
937 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
941 static void client_get_state(ObClient
*self
)
946 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
948 for (i
= 0; i
< num
; ++i
) {
949 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
951 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
953 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
955 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
956 self
->skip_taskbar
= TRUE
;
957 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
958 self
->skip_pager
= TRUE
;
959 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
960 self
->fullscreen
= TRUE
;
961 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
962 self
->max_vert
= TRUE
;
963 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
964 self
->max_horz
= TRUE
;
965 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
967 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
969 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
970 self
->demands_attention
= TRUE
;
971 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
972 self
->undecorated
= TRUE
;
978 if (!(self
->above
|| self
->below
)) {
980 /* apply stuff from the group */
984 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
985 ObClient
*c
= it
->data
;
986 if (c
!= self
&& !client_search_transient(self
, c
) &&
987 client_normal(self
) && client_normal(c
))
990 (c
->above
? 1 : (c
->below
? -1 : 0)));
1004 g_assert_not_reached();
1011 static void client_get_shaped(ObClient
*self
)
1013 self
->shaped
= FALSE
;
1015 if (extensions_shape
) {
1020 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1022 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1023 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1025 self
->shaped
= (s
!= 0);
1030 void client_update_transient_for(ObClient
*self
)
1033 ObClient
*target
= NULL
;
1035 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1036 self
->transient
= TRUE
;
1037 if (t
!= self
->window
) { /* cant be transient to itself! */
1038 target
= g_hash_table_lookup(window_map
, &t
);
1039 /* if this happens then we need to check for it*/
1040 g_assert(target
!= self
);
1041 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1042 /* this can happen when a dialog is a child of
1043 a dockapp, for example */
1048 /* we used to do this, but it violates the ICCCM and causes problems because
1049 toolkits seem to set transient_for = root rather arbitrarily (eg kicker's
1050 config dialogs), so it is being removed. the ewmh provides other ways to
1051 make things transient for their group. -dana
1053 if (!target
&& self
->group
) {
1054 /* not transient to a client, see if it is transient for a
1056 if (t
== self
->group
->leader
||
1058 t
== RootWindow(ob_display
, ob_screen
))
1060 /* window is a transient for its group! */
1061 target
= OB_TRAN_GROUP
;
1067 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
1068 self
->transient
= TRUE
;
1069 target
= OB_TRAN_GROUP
;
1071 self
->transient
= FALSE
;
1073 /* if anything has changed... */
1074 if (target
!= self
->transient_for
) {
1075 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1078 /* remove from old parents */
1079 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1080 ObClient
*c
= it
->data
;
1081 if (c
!= self
&& !c
->transient_for
)
1082 c
->transients
= g_slist_remove(c
->transients
, self
);
1084 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1085 /* remove from old parent */
1086 self
->transient_for
->transients
=
1087 g_slist_remove(self
->transient_for
->transients
, self
);
1089 self
->transient_for
= target
;
1090 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1093 /* add to new parents */
1094 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1095 ObClient
*c
= it
->data
;
1096 if (c
!= self
&& !c
->transient_for
)
1097 c
->transients
= g_slist_append(c
->transients
, self
);
1100 /* remove all transients which are in the group, that causes
1101 circlular pointer hell of doom */
1102 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1104 for (sit
= self
->transients
; sit
; sit
= next
) {
1105 next
= g_slist_next(sit
);
1106 if (sit
->data
== it
->data
)
1108 g_slist_delete_link(self
->transients
, sit
);
1111 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1112 /* add to new parent */
1113 self
->transient_for
->transients
=
1114 g_slist_append(self
->transient_for
->transients
, self
);
1119 static void client_get_mwm_hints(ObClient
*self
)
1124 self
->mwmhints
.flags
= 0; /* default to none */
1126 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1128 if (num
>= OB_MWM_ELEMENTS
) {
1129 self
->mwmhints
.flags
= hints
[0];
1130 self
->mwmhints
.functions
= hints
[1];
1131 self
->mwmhints
.decorations
= hints
[2];
1137 void client_get_type(ObClient
*self
)
1144 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1145 /* use the first value that we know about in the array */
1146 for (i
= 0; i
< num
; ++i
) {
1147 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1148 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1149 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1150 self
->type
= OB_CLIENT_TYPE_DOCK
;
1151 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1152 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1153 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1154 self
->type
= OB_CLIENT_TYPE_MENU
;
1155 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1156 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1157 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1158 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1159 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1160 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1161 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1162 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1163 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1164 /* prevent this window from getting any decor or
1166 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1167 OB_MWM_FLAG_DECORATIONS
);
1168 self
->mwmhints
.decorations
= 0;
1169 self
->mwmhints
.functions
= 0;
1171 if (self
->type
!= (ObClientType
) -1)
1172 break; /* grab the first legit type */
1177 if (self
->type
== (ObClientType
) -1) {
1178 /*the window type hint was not set, which means we either classify
1179 ourself as a normal window or a dialog, depending on if we are a
1181 if (self
->transient
)
1182 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1184 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1188 void client_update_protocols(ObClient
*self
)
1191 guint num_return
, i
;
1193 self
->focus_notify
= FALSE
;
1194 self
->delete_window
= FALSE
;
1196 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1197 for (i
= 0; i
< num_return
; ++i
) {
1198 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1199 /* this means we can request the window to close */
1200 self
->delete_window
= TRUE
;
1201 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1202 /* if this protocol is requested, then the window will be
1203 notified whenever we want it to receive focus */
1204 self
->focus_notify
= TRUE
;
1210 static void client_get_gravity(ObClient
*self
)
1212 XWindowAttributes wattrib
;
1215 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1216 g_assert(ret
!= BadWindow
);
1217 self
->gravity
= wattrib
.win_gravity
;
1220 void client_update_normal_hints(ObClient
*self
)
1224 gint oldgravity
= self
->gravity
;
1227 self
->min_ratio
= 0.0f
;
1228 self
->max_ratio
= 0.0f
;
1229 SIZE_SET(self
->size_inc
, 1, 1);
1230 SIZE_SET(self
->base_size
, 0, 0);
1231 SIZE_SET(self
->min_size
, 0, 0);
1232 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1234 /* get the hints from the window */
1235 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1236 /* normal windows can't request placement! har har
1237 if (!client_normal(self))
1239 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1241 if (size
.flags
& PWinGravity
) {
1242 self
->gravity
= size
.win_gravity
;
1244 /* if the client has a frame, i.e. has already been mapped and
1245 is changing its gravity */
1246 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1247 /* move our idea of the client's position based on its new
1249 self
->area
.x
= self
->frame
->area
.x
;
1250 self
->area
.y
= self
->frame
->area
.y
;
1251 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1255 if (size
.flags
& PAspect
) {
1256 if (size
.min_aspect
.y
)
1258 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1259 if (size
.max_aspect
.y
)
1261 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1264 if (size
.flags
& PMinSize
)
1265 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1267 if (size
.flags
& PMaxSize
)
1268 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1270 if (size
.flags
& PBaseSize
)
1271 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1273 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1274 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1278 void client_setup_decor_and_functions(ObClient
*self
)
1280 /* start with everything (cept fullscreen) */
1282 (OB_FRAME_DECOR_TITLEBAR
|
1283 OB_FRAME_DECOR_HANDLE
|
1284 OB_FRAME_DECOR_GRIPS
|
1285 OB_FRAME_DECOR_BORDER
|
1286 OB_FRAME_DECOR_ICON
|
1287 OB_FRAME_DECOR_ALLDESKTOPS
|
1288 OB_FRAME_DECOR_ICONIFY
|
1289 OB_FRAME_DECOR_MAXIMIZE
|
1290 OB_FRAME_DECOR_SHADE
|
1291 OB_FRAME_DECOR_CLOSE
);
1293 (OB_CLIENT_FUNC_RESIZE
|
1294 OB_CLIENT_FUNC_MOVE
|
1295 OB_CLIENT_FUNC_ICONIFY
|
1296 OB_CLIENT_FUNC_MAXIMIZE
|
1297 OB_CLIENT_FUNC_SHADE
|
1298 OB_CLIENT_FUNC_CLOSE
);
1300 if (!(self
->min_size
.width
< self
->max_size
.width
||
1301 self
->min_size
.height
< self
->max_size
.height
))
1302 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1304 switch (self
->type
) {
1305 case OB_CLIENT_TYPE_NORMAL
:
1306 /* normal windows retain all of the possible decorations and
1307 functionality, and are the only windows that you can fullscreen */
1308 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1311 case OB_CLIENT_TYPE_DIALOG
:
1312 case OB_CLIENT_TYPE_UTILITY
:
1313 /* these windows cannot be maximized */
1314 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1317 case OB_CLIENT_TYPE_MENU
:
1318 case OB_CLIENT_TYPE_TOOLBAR
:
1319 /* these windows get less functionality */
1320 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1323 case OB_CLIENT_TYPE_DESKTOP
:
1324 case OB_CLIENT_TYPE_DOCK
:
1325 case OB_CLIENT_TYPE_SPLASH
:
1326 /* none of these windows are manipulated by the window manager */
1327 self
->decorations
= 0;
1328 self
->functions
= 0;
1332 /* Mwm Hints are applied subtractively to what has already been chosen for
1333 decor and functionality */
1334 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1335 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1336 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1337 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1339 /* if the mwm hints request no handle or title, then all
1340 decorations are disabled, but keep the border if that's
1342 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1343 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1345 self
->decorations
= 0;
1350 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1351 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1352 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1353 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1354 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1355 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1356 /* dont let mwm hints kill any buttons
1357 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1358 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1359 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1360 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1362 /* dont let mwm hints kill the close button
1363 if (! (self->mwmhints.functions & MwmFunc_Close))
1364 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1368 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1369 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1370 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1371 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1372 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1373 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1375 /* can't maximize without moving/resizing */
1376 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1377 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1378 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1379 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1380 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1383 /* kill the handle on fully maxed windows */
1384 if (self
->max_vert
&& self
->max_horz
)
1385 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1387 /* finally, the user can have requested no decorations, which overrides
1388 everything (but doesnt give it a border if it doesnt have one) */
1389 if (self
->undecorated
) {
1390 if (config_theme_keepborder
)
1391 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1393 self
->decorations
= 0;
1396 /* if we don't have a titlebar, then we cannot shade! */
1397 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1398 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1400 /* now we need to check against rules for the client's current state */
1401 if (self
->fullscreen
) {
1402 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1403 OB_CLIENT_FUNC_FULLSCREEN
|
1404 OB_CLIENT_FUNC_ICONIFY
);
1405 self
->decorations
= 0;
1408 client_change_allowed_actions(self
);
1411 /* adjust the client's decorations, etc. */
1412 client_reconfigure(self
);
1416 static void client_change_allowed_actions(ObClient
*self
)
1421 /* desktop windows are kept on all desktops */
1422 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1423 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1425 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1426 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1427 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1428 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1429 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1430 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1431 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1432 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1433 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1434 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1435 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1436 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1437 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1438 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1439 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1442 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1444 /* make sure the window isn't breaking any rules now */
1446 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1447 if (self
->frame
) client_shade(self
, FALSE
);
1448 else self
->shaded
= FALSE
;
1450 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1451 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1452 else self
->iconic
= FALSE
;
1454 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1455 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1456 else self
->fullscreen
= FALSE
;
1458 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1460 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1461 else self
->max_vert
= self
->max_horz
= FALSE
;
1465 void client_reconfigure(ObClient
*self
)
1467 /* by making this pass FALSE for user, we avoid the emacs event storm where
1468 every configurenotify causes an update in its normal hints, i think this
1469 is generally what we want anyways... */
1470 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1471 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1474 void client_update_wmhints(ObClient
*self
)
1479 /* assume a window takes input if it doesnt specify */
1480 self
->can_focus
= TRUE
;
1482 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1483 if (hints
->flags
& InputHint
)
1484 self
->can_focus
= hints
->input
;
1486 /* only do this when first managing the window *AND* when we aren't
1488 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1489 if (hints
->flags
& StateHint
)
1490 self
->iconic
= hints
->initial_state
== IconicState
;
1492 if (!(hints
->flags
& WindowGroupHint
))
1493 hints
->window_group
= None
;
1495 /* did the group state change? */
1496 if (hints
->window_group
!=
1497 (self
->group
? self
->group
->leader
: None
)) {
1498 /* remove from the old group if there was one */
1499 if (self
->group
!= NULL
) {
1500 /* remove transients of the group */
1501 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1502 self
->transients
= g_slist_remove(self
->transients
,
1505 /* remove myself from parents in the group */
1506 if (self
->transient_for
== OB_TRAN_GROUP
) {
1507 for (it
= self
->group
->members
; it
;
1508 it
= g_slist_next(it
))
1510 ObClient
*c
= it
->data
;
1512 if (c
!= self
&& !c
->transient_for
)
1513 c
->transients
= g_slist_remove(c
->transients
,
1518 group_remove(self
->group
, self
);
1521 if (hints
->window_group
!= None
) {
1522 self
->group
= group_add(hints
->window_group
, self
);
1524 /* i can only have transients from the group if i am not
1526 if (!self
->transient_for
) {
1527 /* add other transients of the group that are already
1529 for (it
= self
->group
->members
; it
;
1530 it
= g_slist_next(it
))
1532 ObClient
*c
= it
->data
;
1533 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1535 g_slist_append(self
->transients
, c
);
1540 /* because the self->transient flag wont change from this call,
1541 we don't need to update the window's type and such, only its
1542 transient_for, and the transients lists of other windows in
1543 the group may be affected */
1544 client_update_transient_for(self
);
1547 /* the WM_HINTS can contain an icon */
1548 client_update_icons(self
);
1554 void client_update_title(ObClient
*self
)
1560 gboolean read_title
;
1563 old_title
= self
->title
;
1566 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1567 /* try old x stuff */
1568 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1569 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1570 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1571 if (self
->transient
) {
1572 data
= g_strdup("");
1575 data
= g_strdup("Unnamed Window");
1579 if (config_title_number
) {
1581 /* did the title change? then reset the title_count */
1582 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1583 self
->title_count
= 1;
1585 /* look for duplicates and append a number */
1587 for (it
= client_list
; it
; it
= g_list_next(it
))
1588 if (it
->data
!= self
) {
1589 ObClient
*c
= it
->data
;
1591 if (c
->title_count
== 1) {
1592 if (!strcmp(c
->title
, data
))
1593 nums
|= 1 << c
->title_count
;
1598 /* find the beginning of our " - [%u]", this relies on
1599 that syntax being used */
1600 end
= strrchr(c
->title
, '-') - 1;
1601 len
= end
- c
->title
;
1602 if (!strncmp(c
->title
, data
, len
))
1603 nums
|= 1 << c
->title_count
;
1606 /* find first free number */
1607 for (i
= 1; i
<= 32; ++i
)
1608 if (!(nums
& (1 << i
))) {
1609 if (self
->title_count
== 1 || i
== 1)
1610 self
->title_count
= i
;
1613 /* dont display the number for the first window */
1614 if (self
->title_count
> 1) {
1616 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1621 self
->title_count
= 1;
1624 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1628 frame_adjust_title(self
->frame
);
1632 /* update the icon title */
1634 g_free(self
->icon_title
);
1638 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1639 /* try old x stuff */
1640 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)
1641 || PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
))) {
1642 data
= g_strdup(self
->title
);
1646 /* append the title count, dont display the number for the first window.
1647 * We don't need to check for config_title_number here since title_count
1648 * is not set above 1 then. */
1649 if (read_title
&& self
->title_count
> 1) {
1651 newdata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1656 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1658 self
->icon_title
= data
;
1661 void client_update_class(ObClient
*self
)
1666 if (self
->name
) g_free(self
->name
);
1667 if (self
->class) g_free(self
->class);
1668 if (self
->role
) g_free(self
->role
);
1670 self
->name
= self
->class = self
->role
= NULL
;
1672 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1674 self
->name
= g_strdup(data
[0]);
1676 self
->class = g_strdup(data
[1]);
1681 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1684 if (self
->name
== NULL
) self
->name
= g_strdup("");
1685 if (self
->class == NULL
) self
->class = g_strdup("");
1686 if (self
->role
== NULL
) self
->role
= g_strdup("");
1689 void client_update_strut(ObClient
*self
)
1693 gboolean got
= FALSE
;
1696 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1700 STRUT_PARTIAL_SET(strut
,
1701 data
[0], data
[2], data
[1], data
[3],
1702 data
[4], data
[5], data
[8], data
[9],
1703 data
[6], data
[7], data
[10], data
[11]);
1709 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1715 /* use the screen's width/height */
1716 a
= screen_physical_area();
1718 STRUT_PARTIAL_SET(strut
,
1719 data
[0], data
[2], data
[1], data
[3],
1720 a
->y
, a
->y
+ a
->height
- 1,
1721 a
->x
, a
->x
+ a
->width
- 1,
1722 a
->y
, a
->y
+ a
->height
- 1,
1723 a
->x
, a
->x
+ a
->width
- 1);
1729 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1730 0, 0, 0, 0, 0, 0, 0, 0);
1732 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1733 self
->strut
= strut
;
1735 /* updating here is pointless while we're being mapped cuz we're not in
1736 the client list yet */
1738 screen_update_areas();
1742 void client_update_icons(ObClient
*self
)
1748 for (i
= 0; i
< self
->nicons
; ++i
)
1749 g_free(self
->icons
[i
].data
);
1750 if (self
->nicons
> 0)
1751 g_free(self
->icons
);
1754 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1755 /* figure out how many valid icons are in here */
1757 while (num
- i
> 2) {
1761 if (i
> num
|| w
*h
== 0) break;
1765 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1767 /* store the icons */
1769 for (j
= 0; j
< self
->nicons
; ++j
) {
1772 w
= self
->icons
[j
].width
= data
[i
++];
1773 h
= self
->icons
[j
].height
= data
[i
++];
1775 if (w
*h
== 0) continue;
1777 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1778 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1783 self
->icons
[j
].data
[t
] =
1784 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1785 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1786 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1787 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1796 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1797 if (hints
->flags
& IconPixmapHint
) {
1799 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1800 xerror_set_ignore(TRUE
);
1801 if (!RrPixmapToRGBA(ob_rr_inst
,
1803 (hints
->flags
& IconMaskHint
?
1804 hints
->icon_mask
: None
),
1805 &self
->icons
[self
->nicons
-1].width
,
1806 &self
->icons
[self
->nicons
-1].height
,
1807 &self
->icons
[self
->nicons
-1].data
)){
1808 g_free(&self
->icons
[self
->nicons
-1]);
1811 xerror_set_ignore(FALSE
);
1818 frame_adjust_icon(self
->frame
);
1821 void client_update_user_time(ObClient
*self
, gboolean new_event
)
1825 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1826 self
->user_time
= time
;
1827 /* we set this every time, not just when it grows, because in practice
1828 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1829 backward we don't want all windows to stop focusing. we'll just
1830 assume noone is setting times older than the last one, cuz that
1831 would be pretty stupid anyways
1832 However! This is called when a window is mapped to get its user time
1833 but it's an old number, it's not changing it from new user
1834 interaction, so in that case, don't change the last user time.
1837 client_last_user_time
= time
;
1841 static void client_change_state(ObClient
*self
)
1844 gulong netstate
[11];
1847 state
[0] = self
->wmstate
;
1849 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1853 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1855 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1857 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1858 if (self
->skip_taskbar
)
1859 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1860 if (self
->skip_pager
)
1861 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1862 if (self
->fullscreen
)
1863 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1865 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1867 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1869 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1871 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1872 if (self
->demands_attention
)
1873 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1874 if (self
->undecorated
)
1875 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1876 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1878 client_calc_layer(self
);
1881 frame_adjust_state(self
->frame
);
1884 ObClient
*client_search_focus_tree(ObClient
*self
)
1889 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1890 if (client_focused(it
->data
)) return it
->data
;
1891 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1896 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1898 if (self
->transient_for
) {
1899 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1900 return client_search_focus_tree_full(self
->transient_for
);
1903 gboolean recursed
= FALSE
;
1905 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1906 if (!((ObClient
*)it
->data
)->transient_for
) {
1908 if ((c
= client_search_focus_tree_full(it
->data
)))
1917 /* this function checks the whole tree, the client_search_focus_tree~
1918 does not, so we need to check this window */
1919 if (client_focused(self
))
1921 return client_search_focus_tree(self
);
1924 static ObStackingLayer
calc_layer(ObClient
*self
)
1928 if (self
->fullscreen
&&
1929 (client_focused(self
) || client_search_focus_tree(self
)))
1930 l
= OB_STACKING_LAYER_FULLSCREEN
;
1931 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1932 l
= OB_STACKING_LAYER_DESKTOP
;
1933 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1934 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1935 else l
= OB_STACKING_LAYER_ABOVE
;
1937 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1938 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1939 else l
= OB_STACKING_LAYER_NORMAL
;
1944 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1945 ObStackingLayer l
, gboolean raised
)
1947 ObStackingLayer old
, own
;
1951 own
= calc_layer(self
);
1952 self
->layer
= l
> own
? l
: own
;
1954 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1955 client_calc_layer_recursive(it
->data
, orig
,
1956 l
, raised
? raised
: l
!= old
);
1958 if (!raised
&& l
!= old
)
1959 if (orig
->frame
) { /* only restack if the original window is managed */
1960 stacking_remove(CLIENT_AS_WINDOW(self
));
1961 stacking_add(CLIENT_AS_WINDOW(self
));
1965 void client_calc_layer(ObClient
*self
)
1972 /* transients take on the layer of their parents */
1973 self
= client_search_top_transient(self
);
1975 l
= calc_layer(self
);
1977 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1980 gboolean
client_should_show(ObClient
*self
)
1984 if (client_normal(self
) && screen_showing_desktop
)
1987 if (self->transient_for) {
1988 if (self->transient_for != OB_TRAN_GROUP)
1989 return client_should_show(self->transient_for);
1993 for (it = self->group->members; it; it = g_slist_next(it)) {
1994 ObClient *c = it->data;
1995 if (c != self && !c->transient_for) {
1996 if (client_should_show(c))
2003 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2009 static void client_showhide(ObClient
*self
)
2012 if (client_should_show(self
))
2013 frame_show(self
->frame
);
2015 frame_hide(self
->frame
);
2018 gboolean
client_normal(ObClient
*self
) {
2019 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2020 self
->type
== OB_CLIENT_TYPE_DOCK
||
2021 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2024 static void client_apply_startup_state(ObClient
*self
)
2026 /* these are in a carefully crafted order.. */
2029 self
->iconic
= FALSE
;
2030 client_iconify(self
, TRUE
, FALSE
);
2032 if (self
->fullscreen
) {
2033 self
->fullscreen
= FALSE
;
2034 client_fullscreen(self
, TRUE
, FALSE
);
2036 if (self
->undecorated
) {
2037 self
->undecorated
= FALSE
;
2038 client_set_undecorated(self
, TRUE
);
2041 self
->shaded
= FALSE
;
2042 client_shade(self
, TRUE
);
2044 if (self
->demands_attention
) {
2045 self
->demands_attention
= FALSE
;
2046 client_hilite(self
, TRUE
);
2049 if (self
->max_vert
&& self
->max_horz
) {
2050 self
->max_vert
= self
->max_horz
= FALSE
;
2051 client_maximize(self
, TRUE
, 0, FALSE
);
2052 } else if (self
->max_vert
) {
2053 self
->max_vert
= FALSE
;
2054 client_maximize(self
, TRUE
, 2, FALSE
);
2055 } else if (self
->max_horz
) {
2056 self
->max_horz
= FALSE
;
2057 client_maximize(self
, TRUE
, 1, FALSE
);
2060 /* nothing to do for the other states:
2069 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2070 gint x
, gint y
, gint w
, gint h
,
2071 gboolean user
, gboolean final
,
2072 gboolean force_reply
)
2075 gboolean send_resize_client
;
2076 gboolean moved
= FALSE
, resized
= FALSE
;
2077 guint fdecor
= self
->frame
->decorations
;
2078 gboolean fhorz
= self
->frame
->max_horz
;
2080 /* make the frame recalculate its dimentions n shit without changing
2081 anything visible for real, this way the constraints below can work with
2082 the updated frame dimensions. */
2083 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2085 /* gets the frame's position */
2086 frame_client_gravity(self
->frame
, &x
, &y
);
2088 /* these positions are frame positions, not client positions */
2090 /* set the size and position if fullscreen */
2091 if (self
->fullscreen
) {
2095 i
= client_monitor(self
);
2096 a
= screen_physical_area_monitor(i
);
2103 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2107 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
2109 /* set the size and position if maximized */
2110 if (self
->max_horz
) {
2112 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2114 if (self
->max_vert
) {
2116 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2120 /* gets the client's position */
2121 frame_frame_gravity(self
->frame
, &x
, &y
);
2123 /* these override the above states! if you cant move you can't move! */
2125 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2129 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2130 w
= self
->area
.width
;
2131 h
= self
->area
.height
;
2135 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2136 gint basew
, baseh
, minw
, minh
;
2138 /* base size is substituted with min size if not specified */
2139 if (self
->base_size
.width
|| self
->base_size
.height
) {
2140 basew
= self
->base_size
.width
;
2141 baseh
= self
->base_size
.height
;
2143 basew
= self
->min_size
.width
;
2144 baseh
= self
->min_size
.height
;
2146 /* min size is substituted with base size if not specified */
2147 if (self
->min_size
.width
|| self
->min_size
.height
) {
2148 minw
= self
->min_size
.width
;
2149 minh
= self
->min_size
.height
;
2151 minw
= self
->base_size
.width
;
2152 minh
= self
->base_size
.height
;
2155 /* if this is a user-requested resize, then check against min/max
2158 /* smaller than min size or bigger than max size? */
2159 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2160 if (w
< minw
) w
= minw
;
2161 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2162 if (h
< minh
) h
= minh
;
2167 /* keep to the increments */
2168 w
/= self
->size_inc
.width
;
2169 h
/= self
->size_inc
.height
;
2171 /* you cannot resize to nothing */
2172 if (basew
+ w
< 1) w
= 1 - basew
;
2173 if (baseh
+ h
< 1) h
= 1 - baseh
;
2175 /* store the logical size */
2176 SIZE_SET(self
->logical_size
,
2177 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2178 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2180 w
*= self
->size_inc
.width
;
2181 h
*= self
->size_inc
.height
;
2186 /* adjust the height to match the width for the aspect ratios.
2187 for this, min size is not substituted for base size ever. */
2188 w
-= self
->base_size
.width
;
2189 h
-= self
->base_size
.height
;
2191 if (!self
->fullscreen
) {
2192 if (self
->min_ratio
)
2193 if (h
* self
->min_ratio
> w
) {
2194 h
= (gint
)(w
/ self
->min_ratio
);
2196 /* you cannot resize to nothing */
2199 w
= (gint
)(h
* self
->min_ratio
);
2202 if (self
->max_ratio
)
2203 if (h
* self
->max_ratio
< w
) {
2204 h
= (gint
)(w
/ self
->max_ratio
);
2206 /* you cannot resize to nothing */
2209 w
= (gint
)(h
* self
->min_ratio
);
2214 w
+= self
->base_size
.width
;
2215 h
+= self
->base_size
.height
;
2222 case OB_CORNER_TOPLEFT
:
2224 case OB_CORNER_TOPRIGHT
:
2225 x
-= w
- self
->area
.width
;
2227 case OB_CORNER_BOTTOMLEFT
:
2228 y
-= h
- self
->area
.height
;
2230 case OB_CORNER_BOTTOMRIGHT
:
2231 x
-= w
- self
->area
.width
;
2232 y
-= h
- self
->area
.height
;
2236 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2237 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2239 oldw
= self
->area
.width
;
2240 oldh
= self
->area
.height
;
2241 RECT_SET(self
->area
, x
, y
, w
, h
);
2243 /* for app-requested resizes, always resize if 'resized' is true.
2244 for user-requested ones, only resize if final is true, or when
2245 resizing in redraw mode */
2246 send_resize_client
= ((!user
&& resized
) ||
2248 (resized
&& config_resize_redraw
))));
2250 /* if the client is enlarging, then resize the client before the frame */
2251 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2252 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2254 /* move/resize the frame to match the request */
2256 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2257 moved
= resized
= TRUE
;
2259 if (moved
|| resized
)
2260 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2262 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2265 event
.type
= ConfigureNotify
;
2266 event
.xconfigure
.display
= ob_display
;
2267 event
.xconfigure
.event
= self
->window
;
2268 event
.xconfigure
.window
= self
->window
;
2270 /* root window real coords */
2271 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2273 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2275 event
.xconfigure
.width
= w
;
2276 event
.xconfigure
.height
= h
;
2277 event
.xconfigure
.border_width
= 0;
2278 event
.xconfigure
.above
= self
->frame
->plate
;
2279 event
.xconfigure
.override_redirect
= FALSE
;
2280 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2281 FALSE
, StructureNotifyMask
, &event
);
2285 /* if the client is shrinking, then resize the frame before the client */
2286 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2287 XResizeWindow(ob_display
, self
->window
, w
, h
);
2292 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2296 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2297 self
->fullscreen
== fs
) return; /* already done */
2299 self
->fullscreen
= fs
;
2300 client_change_state(self
); /* change the state hints on the client,
2301 and adjust out layer/stacking */
2305 self
->pre_fullscreen_area
= self
->area
;
2307 /* these are not actually used cuz client_configure will set them
2308 as appropriate when the window is fullscreened */
2313 if (self
->pre_fullscreen_area
.width
> 0 &&
2314 self
->pre_fullscreen_area
.height
> 0)
2316 x
= self
->pre_fullscreen_area
.x
;
2317 y
= self
->pre_fullscreen_area
.y
;
2318 w
= self
->pre_fullscreen_area
.width
;
2319 h
= self
->pre_fullscreen_area
.height
;
2320 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2322 /* pick some fallbacks... */
2323 a
= screen_area_monitor(self
->desktop
, 0);
2324 x
= a
->x
+ a
->width
/ 4;
2325 y
= a
->y
+ a
->height
/ 4;
2331 client_setup_decor_and_functions(self
);
2333 client_move_resize(self
, x
, y
, w
, h
);
2335 /* try focus us when we go into fullscreen mode */
2339 static void client_iconify_recursive(ObClient
*self
,
2340 gboolean iconic
, gboolean curdesk
)
2343 gboolean changed
= FALSE
;
2346 if (self
->iconic
!= iconic
) {
2347 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2350 self
->iconic
= iconic
;
2353 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2356 old
= self
->wmstate
;
2357 self
->wmstate
= IconicState
;
2358 if (old
!= self
->wmstate
)
2359 PROP_MSG(self
->window
, kde_wm_change_state
,
2360 self
->wmstate
, 1, 0, 0);
2362 /* update the focus lists.. iconic windows go to the bottom of
2363 the list, put the new iconic window at the 'top of the
2365 focus_order_to_top(self
);
2373 client_set_desktop(self
, screen_desktop
, FALSE
);
2375 old
= self
->wmstate
;
2376 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2377 if (old
!= self
->wmstate
)
2378 PROP_MSG(self
->window
, kde_wm_change_state
,
2379 self
->wmstate
, 1, 0, 0);
2381 /* this puts it after the current focused window */
2382 focus_order_remove(self
);
2383 focus_order_add_new(self
);
2390 client_change_state(self
);
2391 client_showhide(self
);
2392 if (STRUT_EXISTS(self
->strut
))
2393 screen_update_areas();
2396 /* iconify all transients */
2397 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2398 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2402 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2404 /* move up the transient chain as far as possible first */
2405 self
= client_search_top_transient(self
);
2407 client_iconify_recursive(client_search_top_transient(self
),
2411 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2415 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2416 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2418 /* check if already done */
2420 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2421 if (dir
== 1 && self
->max_horz
) return;
2422 if (dir
== 2 && self
->max_vert
) return;
2424 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2425 if (dir
== 1 && !self
->max_horz
) return;
2426 if (dir
== 2 && !self
->max_vert
) return;
2429 /* we just tell it to configure in the same place and client_configure
2430 worries about filling the screen with the window */
2433 w
= self
->area
.width
;
2434 h
= self
->area
.height
;
2438 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2439 RECT_SET(self
->pre_max_area
,
2440 self
->area
.x
, self
->pre_max_area
.y
,
2441 self
->area
.width
, self
->pre_max_area
.height
);
2443 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2444 RECT_SET(self
->pre_max_area
,
2445 self
->pre_max_area
.x
, self
->area
.y
,
2446 self
->pre_max_area
.width
, self
->area
.height
);
2452 a
= screen_area_monitor(self
->desktop
, 0);
2453 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2454 if (self
->pre_max_area
.width
> 0) {
2455 x
= self
->pre_max_area
.x
;
2456 w
= self
->pre_max_area
.width
;
2458 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2459 0, self
->pre_max_area
.height
);
2461 /* pick some fallbacks... */
2462 x
= a
->x
+ a
->width
/ 4;
2466 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2467 if (self
->pre_max_area
.height
> 0) {
2468 y
= self
->pre_max_area
.y
;
2469 h
= self
->pre_max_area
.height
;
2471 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2472 self
->pre_max_area
.width
, 0);
2474 /* pick some fallbacks... */
2475 y
= a
->y
+ a
->height
/ 4;
2481 if (dir
== 0 || dir
== 1) /* horz */
2482 self
->max_horz
= max
;
2483 if (dir
== 0 || dir
== 2) /* vert */
2484 self
->max_vert
= max
;
2486 client_change_state(self
); /* change the state hints on the client */
2488 client_setup_decor_and_functions(self
);
2490 client_move_resize(self
, x
, y
, w
, h
);
2493 void client_shade(ObClient
*self
, gboolean shade
)
2495 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2496 shade
) || /* can't shade */
2497 self
->shaded
== shade
) return; /* already done */
2499 /* when we're iconic, don't change the wmstate */
2500 if (!self
->iconic
) {
2503 old
= self
->wmstate
;
2504 self
->wmstate
= shade
? IconicState
: NormalState
;
2505 if (old
!= self
->wmstate
)
2506 PROP_MSG(self
->window
, kde_wm_change_state
,
2507 self
->wmstate
, 1, 0, 0);
2510 self
->shaded
= shade
;
2511 client_change_state(self
);
2512 /* resize the frame to just the titlebar */
2513 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2516 void client_close(ObClient
*self
)
2520 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2522 /* in the case that the client provides no means to requesting that it
2523 close, we just kill it */
2524 if (!self
->delete_window
)
2528 XXX: itd be cool to do timeouts and shit here for killing the client's
2530 like... if the window is around after 5 seconds, then the close button
2531 turns a nice red, and if this function is called again, the client is
2535 ce
.xclient
.type
= ClientMessage
;
2536 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2537 ce
.xclient
.display
= ob_display
;
2538 ce
.xclient
.window
= self
->window
;
2539 ce
.xclient
.format
= 32;
2540 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2541 ce
.xclient
.data
.l
[1] = event_curtime
;
2542 ce
.xclient
.data
.l
[2] = 0l;
2543 ce
.xclient
.data
.l
[3] = 0l;
2544 ce
.xclient
.data
.l
[4] = 0l;
2545 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2548 void client_kill(ObClient
*self
)
2550 XKillClient(ob_display
, self
->window
);
2553 void client_hilite(ObClient
*self
, gboolean hilite
)
2555 /* don't allow focused windows to hilite */
2556 self
->demands_attention
= hilite
&& !client_focused(self
);
2557 if (self
->demands_attention
)
2558 frame_flash_start(self
->frame
);
2560 frame_flash_stop(self
->frame
);
2561 client_change_state(self
);
2564 void client_set_desktop_recursive(ObClient
*self
,
2565 guint target
, gboolean donthide
)
2570 if (target
!= self
->desktop
) {
2572 ob_debug("Setting desktop %u\n", target
+1);
2574 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2576 /* remove from the old desktop(s) */
2577 focus_order_remove(self
);
2579 old
= self
->desktop
;
2580 self
->desktop
= target
;
2581 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2582 /* the frame can display the current desktop state */
2583 frame_adjust_state(self
->frame
);
2584 /* 'move' the window to the new desktop */
2586 client_showhide(self
);
2587 /* raise if it was not already on the desktop */
2588 if (old
!= DESKTOP_ALL
)
2590 if (STRUT_EXISTS(self
->strut
))
2591 screen_update_areas();
2593 /* add to the new desktop(s) */
2594 if (config_focus_new
)
2595 focus_order_to_top(self
);
2597 focus_order_to_bottom(self
);
2600 /* move all transients */
2601 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2602 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2606 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2608 client_set_desktop_recursive(client_search_top_transient(self
),
2612 ObClient
*client_search_modal_child(ObClient
*self
)
2617 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2618 ObClient
*c
= it
->data
;
2619 if ((ret
= client_search_modal_child(c
))) return ret
;
2620 if (c
->modal
) return c
;
2625 gboolean
client_validate(ObClient
*self
)
2629 XSync(ob_display
, FALSE
); /* get all events on the server */
2631 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2632 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2633 XPutBackEvent(ob_display
, &e
);
2640 void client_set_wm_state(ObClient
*self
, glong state
)
2642 if (state
== self
->wmstate
) return; /* no change */
2646 client_iconify(self
, TRUE
, TRUE
);
2649 client_iconify(self
, FALSE
, TRUE
);
2654 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2656 gboolean shaded
= self
->shaded
;
2657 gboolean fullscreen
= self
->fullscreen
;
2658 gboolean undecorated
= self
->undecorated
;
2659 gboolean max_horz
= self
->max_horz
;
2660 gboolean max_vert
= self
->max_vert
;
2661 gboolean modal
= self
->modal
;
2662 gboolean iconic
= self
->iconic
;
2663 gboolean demands_attention
= self
->demands_attention
;
2666 if (!(action
== prop_atoms
.net_wm_state_add
||
2667 action
== prop_atoms
.net_wm_state_remove
||
2668 action
== prop_atoms
.net_wm_state_toggle
))
2669 /* an invalid action was passed to the client message, ignore it */
2672 for (i
= 0; i
< 2; ++i
) {
2673 Atom state
= i
== 0 ? data1
: data2
;
2675 if (!state
) continue;
2677 /* if toggling, then pick whether we're adding or removing */
2678 if (action
== prop_atoms
.net_wm_state_toggle
) {
2679 if (state
== prop_atoms
.net_wm_state_modal
)
2680 action
= modal
? prop_atoms
.net_wm_state_remove
:
2681 prop_atoms
.net_wm_state_add
;
2682 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2683 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2684 prop_atoms
.net_wm_state_add
;
2685 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2686 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2687 prop_atoms
.net_wm_state_add
;
2688 else if (state
== prop_atoms
.net_wm_state_shaded
)
2689 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2690 prop_atoms
.net_wm_state_add
;
2691 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2692 action
= self
->skip_taskbar
?
2693 prop_atoms
.net_wm_state_remove
:
2694 prop_atoms
.net_wm_state_add
;
2695 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2696 action
= self
->skip_pager
?
2697 prop_atoms
.net_wm_state_remove
:
2698 prop_atoms
.net_wm_state_add
;
2699 else if (state
== prop_atoms
.net_wm_state_hidden
)
2700 action
= self
->iconic
?
2701 prop_atoms
.net_wm_state_remove
:
2702 prop_atoms
.net_wm_state_add
;
2703 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2704 action
= fullscreen
?
2705 prop_atoms
.net_wm_state_remove
:
2706 prop_atoms
.net_wm_state_add
;
2707 else if (state
== prop_atoms
.net_wm_state_above
)
2708 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2709 prop_atoms
.net_wm_state_add
;
2710 else if (state
== prop_atoms
.net_wm_state_below
)
2711 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2712 prop_atoms
.net_wm_state_add
;
2713 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2714 action
= self
->demands_attention
?
2715 prop_atoms
.net_wm_state_remove
:
2716 prop_atoms
.net_wm_state_add
;
2717 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2718 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2719 prop_atoms
.net_wm_state_add
;
2722 if (action
== prop_atoms
.net_wm_state_add
) {
2723 if (state
== prop_atoms
.net_wm_state_modal
) {
2725 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2727 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2729 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2731 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2732 self
->skip_taskbar
= TRUE
;
2733 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2734 self
->skip_pager
= TRUE
;
2735 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2737 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2739 } else if (state
== prop_atoms
.net_wm_state_above
) {
2741 self
->below
= FALSE
;
2742 } else if (state
== prop_atoms
.net_wm_state_below
) {
2743 self
->above
= FALSE
;
2745 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2746 demands_attention
= TRUE
;
2747 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2751 } else { /* action == prop_atoms.net_wm_state_remove */
2752 if (state
== prop_atoms
.net_wm_state_modal
) {
2754 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2756 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2758 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2760 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2761 self
->skip_taskbar
= FALSE
;
2762 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2763 self
->skip_pager
= FALSE
;
2764 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2766 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2768 } else if (state
== prop_atoms
.net_wm_state_above
) {
2769 self
->above
= FALSE
;
2770 } else if (state
== prop_atoms
.net_wm_state_below
) {
2771 self
->below
= FALSE
;
2772 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2773 demands_attention
= FALSE
;
2774 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2775 undecorated
= FALSE
;
2779 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2780 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2782 if (max_horz
== max_vert
) { /* both going the same way */
2783 client_maximize(self
, max_horz
, 0, TRUE
);
2785 client_maximize(self
, max_horz
, 1, TRUE
);
2786 client_maximize(self
, max_vert
, 2, TRUE
);
2790 if (max_horz
!= self
->max_horz
)
2791 client_maximize(self
, max_horz
, 1, TRUE
);
2793 client_maximize(self
, max_vert
, 2, TRUE
);
2796 /* change fullscreen state before shading, as it will affect if the window
2798 if (fullscreen
!= self
->fullscreen
)
2799 client_fullscreen(self
, fullscreen
, TRUE
);
2800 if (shaded
!= self
->shaded
)
2801 client_shade(self
, shaded
);
2802 if (undecorated
!= self
->undecorated
)
2803 client_set_undecorated(self
, undecorated
);
2804 if (modal
!= self
->modal
) {
2805 self
->modal
= modal
;
2806 /* when a window changes modality, then its stacking order with its
2807 transients needs to change */
2810 if (iconic
!= self
->iconic
)
2811 client_iconify(self
, iconic
, FALSE
);
2813 if (demands_attention
!= self
->demands_attention
)
2814 client_hilite(self
, demands_attention
);
2816 client_calc_layer(self
);
2817 client_change_state(self
); /* change the hint to reflect these changes */
2820 ObClient
*client_focus_target(ObClient
*self
)
2824 /* if we have a modal child, then focus it, not us */
2825 child
= client_search_modal_child(client_search_top_transient(self
));
2826 if (child
) return child
;
2830 gboolean
client_can_focus(ObClient
*self
)
2834 /* choose the correct target */
2835 self
= client_focus_target(self
);
2837 if (!self
->frame
->visible
)
2840 if (!(self
->can_focus
|| self
->focus_notify
))
2843 /* do a check to see if the window has already been unmapped or destroyed
2844 do this intelligently while watching out for unmaps we've generated
2845 (ignore_unmaps > 0) */
2846 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2847 DestroyNotify
, &ev
)) {
2848 XPutBackEvent(ob_display
, &ev
);
2851 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2852 UnmapNotify
, &ev
)) {
2853 if (self
->ignore_unmaps
) {
2854 self
->ignore_unmaps
--;
2856 XPutBackEvent(ob_display
, &ev
);
2864 gboolean
client_focus(ObClient
*self
)
2866 /* choose the correct target */
2867 self
= client_focus_target(self
);
2869 if (!client_can_focus(self
)) {
2870 if (!self
->frame
->visible
) {
2871 /* update the focus lists */
2872 focus_order_to_top(self
);
2877 if (self
->can_focus
) {
2878 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2879 I choose to use it always, hopefully to find errors quicker, if any
2880 are left. (I hate X. I hate focus events.)
2882 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2883 #799. So now it is RevertToNone again.
2885 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2889 if (self
->focus_notify
) {
2891 ce
.xclient
.type
= ClientMessage
;
2892 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2893 ce
.xclient
.display
= ob_display
;
2894 ce
.xclient
.window
= self
->window
;
2895 ce
.xclient
.format
= 32;
2896 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2897 ce
.xclient
.data
.l
[1] = event_curtime
;
2898 ce
.xclient
.data
.l
[2] = 0l;
2899 ce
.xclient
.data
.l
[3] = 0l;
2900 ce
.xclient
.data
.l
[4] = 0l;
2901 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2905 ob_debug("%sively focusing %lx at %d\n",
2906 (self
->can_focus
? "act" : "pass"),
2907 self
->window
, (gint
) event_curtime
);
2910 /* Cause the FocusIn to come back to us. Important for desktop switches,
2911 since otherwise we'll have no FocusIn on the queue and send it off to
2912 the focus_backup. */
2913 XSync(ob_display
, FALSE
);
2917 /* Used when the current client is closed, focus_last will then prevent
2918 * focus from going to the mouse pointer */
2919 void client_unfocus(ObClient
*self
)
2921 if (focus_client
== self
) {
2923 ob_debug("client_unfocus for %lx\n", self
->window
);
2925 focus_fallback(OB_FOCUS_FALLBACK_CLOSED
);
2929 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
2931 /* XXX do some stuff here if user is false to determine if we really want
2932 to activate it or not (a parent or group member is currently active) */
2934 if (client_normal(self
) && screen_showing_desktop
)
2935 screen_show_desktop(FALSE
);
2937 client_iconify(self
, FALSE
, here
);
2938 if (self
->desktop
!= DESKTOP_ALL
&&
2939 self
->desktop
!= screen_desktop
) {
2941 client_set_desktop(self
, screen_desktop
, FALSE
);
2943 screen_set_desktop(self
->desktop
);
2944 } else if (!self
->frame
->visible
)
2945 /* if its not visible for other reasons, then don't mess
2949 client_shade(self
, FALSE
);
2953 /* we do this an action here. this is rather important. this is because
2954 we want the results from the focus change to take place BEFORE we go
2955 about raising the window. when a fullscreen window loses focus, we need
2956 this or else the raise wont be able to raise above the to-lose-focus
2957 fullscreen window. */
2961 void client_raise(ObClient
*self
)
2963 action_run_string("Raise", self
);
2966 void client_lower(ObClient
*self
)
2968 action_run_string("Lower", self
);
2971 gboolean
client_focused(ObClient
*self
)
2973 return self
== focus_client
;
2976 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
2979 /* si is the smallest image >= req */
2980 /* li is the largest image < req */
2981 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2983 if (!self
->nicons
) {
2984 ObClientIcon
*parent
= NULL
;
2986 if (self
->transient_for
) {
2987 if (self
->transient_for
!= OB_TRAN_GROUP
)
2988 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2991 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2992 ObClient
*c
= it
->data
;
2993 if (c
!= self
&& !c
->transient_for
) {
2994 if ((parent
= client_icon_recursive(c
, w
, h
)))
3004 for (i
= 0; i
< self
->nicons
; ++i
) {
3005 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3006 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3010 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3015 if (largest
== 0) /* didnt find one smaller than the requested size */
3016 return &self
->icons
[si
];
3017 return &self
->icons
[li
];
3020 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3023 static ObClientIcon deficon
;
3025 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3026 deficon
.width
= deficon
.height
= 48;
3027 deficon
.data
= ob_rr_theme
->def_win_icon
;
3033 /* this be mostly ripped from fvwm */
3034 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3036 gint my_cx
, my_cy
, his_cx
, his_cy
;
3039 gint score
, best_score
;
3040 ObClient
*best_client
, *cur
;
3046 /* first, find the centre coords of the currently focused window */
3047 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3048 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3053 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3056 /* the currently selected window isn't interesting */
3059 if (!client_normal(cur
))
3061 /* using c->desktop instead of screen_desktop doesn't work if the
3062 * current window was omnipresent, hope this doesn't have any other
3064 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3068 if(!(client_focus_target(cur
) == cur
&&
3069 client_can_focus(cur
)))
3072 /* find the centre coords of this window, from the
3073 * currently focused window's point of view */
3074 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3075 + cur
->frame
->area
.width
/ 2;
3076 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3077 + cur
->frame
->area
.height
/ 2;
3079 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3080 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3082 /* Rotate the diagonals 45 degrees counterclockwise.
3083 * To do this, multiply the matrix /+h +h\ with the
3084 * vector (x y). \-h +h/
3085 * h = sqrt(0.5). We can set h := 1 since absolute
3086 * distance doesn't matter here. */
3087 tx
= his_cx
+ his_cy
;
3088 his_cy
= -his_cx
+ his_cy
;
3093 case OB_DIRECTION_NORTH
:
3094 case OB_DIRECTION_SOUTH
:
3095 case OB_DIRECTION_NORTHEAST
:
3096 case OB_DIRECTION_SOUTHWEST
:
3097 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3098 distance
= ((dir
== OB_DIRECTION_NORTH
||
3099 dir
== OB_DIRECTION_NORTHEAST
) ?
3102 case OB_DIRECTION_EAST
:
3103 case OB_DIRECTION_WEST
:
3104 case OB_DIRECTION_SOUTHEAST
:
3105 case OB_DIRECTION_NORTHWEST
:
3106 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3107 distance
= ((dir
== OB_DIRECTION_WEST
||
3108 dir
== OB_DIRECTION_NORTHWEST
) ?
3113 /* the target must be in the requested direction */
3117 /* Calculate score for this window. The smaller the better. */
3118 score
= distance
+ offset
;
3120 /* windows more than 45 degrees off the direction are
3121 * heavily penalized and will only be chosen if nothing
3122 * else within a million pixels */
3123 if(offset
> distance
)
3126 if(best_score
== -1 || score
< best_score
)
3134 void client_set_layer(ObClient
*self
, gint layer
)
3138 self
->above
= FALSE
;
3139 } else if (layer
== 0) {
3140 self
->below
= self
->above
= FALSE
;
3142 self
->below
= FALSE
;
3145 client_calc_layer(self
);
3146 client_change_state(self
); /* reflect this in the state hints */
3149 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3151 if (self
->undecorated
!= undecorated
) {
3152 self
->undecorated
= undecorated
;
3153 client_setup_decor_and_functions(self
);
3154 /* Make sure the client knows it might have moved. Maybe there is a
3155 * better way of doing this so only one client_configure is sent, but
3156 * since 125 of these are sent per second when moving the window (with
3157 * user = FALSE) i doubt it matters much.
3159 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3160 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3161 client_change_state(self
); /* reflect this in the state hints */
3165 /* Determines which physical monitor a client is on by calculating the
3166 area of the part of the client on each monitor. The number of the
3167 monitor containing the greatest area of the client is returned.*/
3168 guint
client_monitor(ObClient
*self
)
3174 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3175 Rect
*area
= screen_physical_area_monitor(i
);
3176 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3180 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3181 v
= r
.width
* r
.height
;
3192 ObClient
*client_search_top_transient(ObClient
*self
)
3194 /* move up the transient chain as far as possible */
3195 if (self
->transient_for
) {
3196 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3197 return client_search_top_transient(self
->transient_for
);
3201 g_assert(self
->group
);
3203 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3204 ObClient
*c
= it
->data
;
3206 /* checking transient_for prevents infinate loops! */
3207 if (c
!= self
&& !c
->transient_for
)
3218 ObClient
*client_search_focus_parent(ObClient
*self
)
3220 if (self
->transient_for
) {
3221 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3222 if (client_focused(self
->transient_for
))
3223 return self
->transient_for
;
3227 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3228 ObClient
*c
= it
->data
;
3230 /* checking transient_for prevents infinate loops! */
3231 if (c
!= self
&& !c
->transient_for
)
3232 if (client_focused(c
))
3241 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3243 if (self
->transient_for
) {
3244 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3245 if (self
->transient_for
== search
)
3250 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3251 ObClient
*c
= it
->data
;
3253 /* checking transient_for prevents infinate loops! */
3254 if (c
!= self
&& !c
->transient_for
)
3264 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3268 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3269 if (sit
->data
== search
)
3271 if (client_search_transient(sit
->data
, search
))
3277 void client_update_sm_client_id(ObClient
*self
)
3279 g_free(self
->sm_client_id
);
3280 self
->sm_client_id
= NULL
;
3282 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3284 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3285 &self
->sm_client_id
);
3288 #define WANT_EDGE(cur, c) \
3291 if(!client_normal(cur)) \
3293 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3297 if(cur->layer < c->layer && !config_resist_layers_below) \
3300 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3301 if ((his_edge_start >= my_edge_start && \
3302 his_edge_start <= my_edge_end) || \
3303 (my_edge_start >= his_edge_start && \
3304 my_edge_start <= his_edge_end)) \
3307 /* finds the nearest edge in the given direction from the current client
3308 * note to self: the edge is the -frame- edge (the actual one), not the
3311 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3313 gint dest
, monitor_dest
;
3314 gint my_edge_start
, my_edge_end
, my_offset
;
3321 a
= screen_area(c
->desktop
);
3322 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3325 case OB_DIRECTION_NORTH
:
3326 my_edge_start
= c
->frame
->area
.x
;
3327 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3328 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3330 /* default: top of screen */
3331 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3332 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 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
.x
;
3345 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3346 his_offset
= cur
->frame
->area
.y
+
3347 (hang
? 0 : cur
->frame
->area
.height
);
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_SOUTH
:
3359 my_edge_start
= c
->frame
->area
.x
;
3360 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3361 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3363 /* default: bottom of screen */
3364 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3365 monitor_dest
= monitor
->y
+ monitor
->height
-
3366 (hang
? c
->frame
->area
.height
: 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
.x
;
3379 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3380 his_offset
= cur
->frame
->area
.y
+
3381 (hang
? cur
->frame
->area
.height
: 0);
3384 if(his_offset
- 1 < my_offset
)
3387 if(his_offset
> dest
)
3390 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3393 case OB_DIRECTION_WEST
:
3394 my_edge_start
= c
->frame
->area
.y
;
3395 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3396 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3398 /* default: leftmost egde of screen */
3399 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3400 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3401 /* if the monitor edge comes before the screen edge, */
3402 /* use that as the destination instead. (For xinerama) */
3403 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3404 dest
= monitor_dest
;
3406 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3407 gint his_edge_start
, his_edge_end
, his_offset
;
3408 ObClient
*cur
= it
->data
;
3412 his_edge_start
= cur
->frame
->area
.y
;
3413 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3414 his_offset
= cur
->frame
->area
.x
+
3415 (hang
? 0 : cur
->frame
->area
.width
);
3417 if(his_offset
+ 1 > my_offset
)
3420 if(his_offset
< dest
)
3423 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3426 case OB_DIRECTION_EAST
:
3427 my_edge_start
= c
->frame
->area
.y
;
3428 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3429 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3431 /* default: rightmost edge of screen */
3432 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3433 monitor_dest
= monitor
->x
+ monitor
->width
-
3434 (hang
? c
->frame
->area
.width
: 0);
3435 /* if the monitor edge comes before the screen edge, */
3436 /* use that as the destination instead. (For xinerama) */
3437 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3438 dest
= monitor_dest
;
3440 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3441 gint his_edge_start
, his_edge_end
, his_offset
;
3442 ObClient
*cur
= it
->data
;
3446 his_edge_start
= cur
->frame
->area
.y
;
3447 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3448 his_offset
= cur
->frame
->area
.x
+
3449 (hang
? cur
->frame
->area
.width
: 0);
3451 if(his_offset
- 1 < my_offset
)
3454 if(his_offset
> dest
)
3457 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3460 case OB_DIRECTION_NORTHEAST
:
3461 case OB_DIRECTION_SOUTHEAST
:
3462 case OB_DIRECTION_NORTHWEST
:
3463 case OB_DIRECTION_SOUTHWEST
:
3464 /* not implemented */
3466 g_assert_not_reached();
3467 dest
= 0; /* suppress warning */
3472 ObClient
* client_under_pointer()
3476 ObClient
*ret
= NULL
;
3478 if (screen_pointer_pos(&x
, &y
)) {
3479 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3480 if (WINDOW_IS_CLIENT(it
->data
)) {
3481 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3482 if (c
->frame
->visible
&&
3483 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3493 gboolean
client_has_group_siblings(ObClient
*self
)
3495 return self
->group
&& self
->group
->members
->next
;