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_layer(ObClient
*self
);
72 static void client_get_shaped(ObClient
*self
);
73 static void client_get_mwm_hints(ObClient
*self
);
74 static void client_get_gravity(ObClient
*self
);
75 static void client_showhide(ObClient
*self
);
76 static void client_change_allowed_actions(ObClient
*self
);
77 static void client_change_state(ObClient
*self
);
78 static void client_apply_startup_state(ObClient
*self
);
79 static void client_restore_session_state(ObClient
*self
);
80 static void client_restore_session_stacking(ObClient
*self
);
82 void client_startup(gboolean reconfig
)
89 void client_shutdown(gboolean reconfig
)
93 void client_add_destructor(ObClientDestructor func
, gpointer data
)
95 Destructor
*d
= g_new(Destructor
, 1);
98 client_destructors
= g_slist_prepend(client_destructors
, d
);
101 void client_remove_destructor(ObClientDestructor func
)
105 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
106 Destructor
*d
= it
->data
;
107 if (d
->func
== func
) {
109 client_destructors
= g_slist_delete_link(client_destructors
, it
);
115 void client_set_list()
117 Window
*windows
, *win_it
;
119 guint size
= g_list_length(client_list
);
121 /* create an array of the window ids */
123 windows
= g_new(Window
, size
);
125 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
126 *win_it
= ((ObClient
*)it
->data
)->window
;
130 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
131 net_client_list
, window
, (gulong
*)windows
, size
);
140 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
144 for (it = self->transients; it; it = g_slist_next(it)) {
145 if (!func(it->data, data)) return;
146 client_foreach_transient(it->data, func, data);
150 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
152 if (self->transient_for) {
153 if (self->transient_for != OB_TRAN_GROUP) {
154 if (!func(self->transient_for, data)) return;
155 client_foreach_ancestor(self->transient_for, func, data);
159 for (it = self->group->members; it; it = g_slist_next(it))
160 if (it->data != self &&
161 !((ObClient*)it->data)->transient_for) {
162 if (!func(it->data, data)) return;
163 client_foreach_ancestor(it->data, func, data);
170 void client_manage_all()
175 XWindowAttributes attrib
;
177 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
178 &w
, &w
, &children
, &nchild
);
180 /* remove all icon windows from the list */
181 for (i
= 0; i
< nchild
; i
++) {
182 if (children
[i
] == None
) continue;
183 wmhints
= XGetWMHints(ob_display
, children
[i
]);
185 if ((wmhints
->flags
& IconWindowHint
) &&
186 (wmhints
->icon_window
!= children
[i
]))
187 for (j
= 0; j
< nchild
; j
++)
188 if (children
[j
] == wmhints
->icon_window
) {
196 for (i
= 0; i
< nchild
; ++i
) {
197 if (children
[i
] == None
)
199 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
200 if (attrib
.override_redirect
) continue;
202 if (attrib
.map_state
!= IsUnmapped
)
203 client_manage(children
[i
]);
209 static ObAppSettings
*get_settings(ObClient
*client
)
211 GSList
*a
= config_per_app_settings
;
214 ObAppSettings
*app
= (ObAppSettings
*) a
->data
;
217 (app
->name
&& !app
->class && !strcmp(app
->name
, client
->name
))
218 || (app
->class && !app
->name
&& !strcmp(app
->class, client
->class))
219 || (app
->class && app
->name
&& !strcmp(app
->class, client
->class)
220 && !strcmp(app
->name
, client
->name
))
222 ob_debug("Window matching: %s\n", app
->name
);
223 /* Match if no role was specified in the per app setting, or if the
224 * string matches the beginning of the role, since apps like to set
225 * the role to things like browser-window-23c4b2f */
227 || !strncmp(app
->role
, client
->role
, strlen(app
->role
)))
236 void client_manage(Window window
)
240 XWindowAttributes attrib
;
241 XSetWindowAttributes attrib_set
;
243 gboolean activate
= FALSE
;
244 ObAppSettings
*settings
;
248 /* check if it has already been unmapped by the time we started mapping
249 the grab does a sync so we don't have to here */
250 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
251 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
253 XPutBackEvent(ob_display
, &e
);
256 return; /* don't manage it */
259 /* make sure it isn't an override-redirect window */
260 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
261 attrib
.override_redirect
)
264 return; /* don't manage it */
267 /* is the window a docking app */
268 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
269 if ((wmhint
->flags
& StateHint
) &&
270 wmhint
->initial_state
== WithdrawnState
)
272 dock_add(window
, wmhint
);
280 ob_debug("Managing window: %lx\n", window
);
282 /* choose the events we want to receive on the CLIENT window */
283 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
284 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
285 XChangeWindowAttributes(ob_display
, window
,
286 CWEventMask
|CWDontPropagate
, &attrib_set
);
289 /* create the ObClient struct, and populate it from the hints on the
291 self
= g_new0(ObClient
, 1);
292 self
->obwin
.type
= Window_Client
;
293 self
->window
= window
;
295 /* non-zero defaults */
296 self
->title_count
= 1;
297 self
->wmstate
= NormalState
;
299 self
->desktop
= screen_num_desktops
; /* always an invalid value */
300 self
->user_time
= ~0; /* maximum value, always newer than the real time */
302 client_get_all(self
);
303 client_restore_session_state(self
);
305 client_calc_layer(self
);
308 Time t
= sn_app_started(self
->startup_id
, self
->class);
309 if (t
) self
->user_time
= t
;
312 /* update the focus lists, do this before the call to change_state or
313 it can end up in the list twice! */
314 focus_order_add_new(self
);
316 client_change_state(self
);
318 /* remove the client's border (and adjust re gravity) */
319 client_toggle_border(self
, FALSE
);
321 /* specify that if we exit, the window should not be destroyed and should
322 be reparented back to root automatically */
323 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
325 /* create the decoration frame for the client window */
326 self
->frame
= frame_new(self
);
328 frame_grab_client(self
->frame
, self
);
332 client_apply_startup_state(self
);
334 /* get and set application level settings */
335 settings
= get_settings(self
);
337 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
338 client_restore_session_stacking(self
);
341 /* Don't worry, we won't actually both shade and undecorate the
342 * window when push comes to shove. */
343 if (settings
->shade
!= -1)
344 client_shade(self
, !!settings
->shade
);
345 if (settings
->decor
!= -1)
346 client_set_undecorated(self
, !settings
->decor
);
347 if (settings
->iconic
!= -1)
348 client_iconify(self
, !!settings
->iconic
, FALSE
);
349 if (settings
->skip_pager
!= -1) {
350 self
->skip_pager
= !!settings
->skip_pager
;
351 client_change_state(self
);
353 if (settings
->skip_taskbar
!= -1) {
354 self
->skip_taskbar
= !!settings
->skip_taskbar
;
355 client_change_state(self
);
358 /* 1 && -1 shouldn't be possible by the code in config.c */
359 if (settings
->max_vert
== 1 && settings
->max_horz
== 1)
360 client_maximize(self
, TRUE
, 0, TRUE
);
361 else if (settings
->max_vert
== 0 && settings
->max_horz
== 0)
362 client_maximize(self
, FALSE
, 0, TRUE
);
363 else if (settings
->max_vert
== 1 && settings
->max_horz
== 0) {
364 client_maximize(self
, TRUE
, 2, TRUE
);
365 client_maximize(self
, FALSE
, 1, TRUE
);
366 } else if (settings
->max_vert
== 0 && settings
->max_horz
== 1) {
367 client_maximize(self
, TRUE
, 1, TRUE
);
368 client_maximize(self
, FALSE
, 2, TRUE
);
371 if (settings
->fullscreen
!= -1)
372 client_fullscreen(self
, !!settings
->fullscreen
, TRUE
);
374 if (settings
->desktop
< screen_num_desktops
375 || settings
->desktop
== DESKTOP_ALL
)
376 client_set_desktop(self
, settings
->desktop
, TRUE
);
378 if (settings
->layer
> -2 && settings
->layer
< 2)
379 client_set_layer(self
, settings
->layer
);
383 /* focus the new window? */
384 if (ob_state() != OB_STATE_STARTING
&&
385 /* this means focus=true for window is same as config_focus_new=true */
386 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
387 client_search_focus_parent(self
)) &&
388 /* this checks for focus=false for the window */
389 (!settings
|| settings
->focus
!= 0) &&
390 /* note the check against Type_Normal/Dialog, not client_normal(self),
391 which would also include other types. in this case we want more
392 strict rules for focus */
393 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
394 self
->type
== OB_CLIENT_TYPE_DIALOG
))
398 if (self
->desktop
!= screen_desktop
) {
399 /* activate the window */
402 gboolean group_foc
= FALSE
;
407 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
409 if (client_focused(it
->data
))
417 (!self
->transient_for
&& (!self
->group
||
418 !self
->group
->members
->next
))) ||
419 client_search_focus_tree_full(self
) ||
421 !client_normal(focus_client
))
423 /* activate the window */
430 if (ob_state() == OB_STATE_RUNNING
) {
431 gint x
= self
->area
.x
, ox
= x
;
432 gint y
= self
->area
.y
, oy
= y
;
435 transient
= place_client(self
, &x
, &y
, settings
);
437 /* make sure the window is visible. */
438 client_find_onscreen(self
, &x
, &y
,
439 self
->frame
->area
.width
,
440 self
->frame
->area
.height
,
441 /* non-normal clients has less rules, and
442 windows that are being restored from a
443 session do also. we can assume you want
444 it back where you saved it. Clients saying
445 they placed themselves are subjected to
446 harder rules, ones that are placed by
447 place.c or by the user are allowed partially
448 off-screen and on xinerama divides (ie,
449 it is up to the placement routines to avoid
450 the xinerama divides) */
452 (((self
->positioned
& PPosition
) &&
453 !(self
->positioned
& USPosition
)) &&
454 client_normal(self
) &&
456 if (x
!= ox
|| y
!= oy
)
457 client_move(self
, x
, y
);
460 keyboard_grab_for_client(self
, TRUE
);
461 mouse_grab_for_client(self
, TRUE
);
464 /* This is focus stealing prevention */
465 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
466 self
->window
, self
->user_time
, client_last_user_time
);
468 /* If a nothing at all, or a parent was focused, then focus this
471 if (client_search_focus_parent(self
) != NULL
||
478 /* If time stamp is old, don't steal focus */
479 if (self
->user_time
&& self
->user_time
< client_last_user_time
)
481 /* Don't steal focus from globally active clients.
482 I stole this idea from KWin. It seems nice.
484 if (focus_client
&& focus_client
->can_focus
!= TRUE
&&
485 focus_client
->focus_notify
== TRUE
)
493 /* since focus can change the stacking orders, if we focus the
494 window then the standard raise it gets is not enough, we need
495 to queue one for after the focus change takes place */
498 ob_debug("Focus stealing prevention activated for %s with time %u "
500 self
->title
, self
->user_time
, client_last_user_time
);
501 /* if the client isn't focused, then hilite it so the user
503 client_hilite(self
, TRUE
);
507 /* This may look rather odd. Well it's because new windows are added
508 to the stacking order non-intrusively. If we're not going to focus
509 the new window or hilite it, then we raise it to the top. This will
510 take affect for things that don't get focused like splash screens.
511 Also if you don't have focus_new enabled, then it's going to get
512 raised to the top. Legacy begets legacy I guess?
517 /* this has to happen before we try focus the window, but we want it to
518 happen after the client's stacking has been determined or it looks bad
520 client_showhide(self
);
522 /* use client_focus instead of client_activate cuz client_activate does
523 stuff like switch desktops etc and I'm not interested in all that when
524 a window maps since its not based on an action from the user like
525 clicking a window to activate it. so keep the new window out of the way
528 /* if using focus_delay, stop the timer now so that focus doesn't
530 event_halt_focus_delay();
534 /* client_activate does this but we aret using it so we have to do it
536 if (screen_showing_desktop
)
537 screen_show_desktop(FALSE
);
539 /* add to client list/map */
540 client_list
= g_list_append(client_list
, self
);
541 g_hash_table_insert(window_map
, &self
->window
, self
);
543 /* this has to happen after we're in the client_list */
544 if (STRUT_EXISTS(self
->strut
))
545 screen_update_areas();
547 /* update the list hints */
550 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
553 void client_unmanage_all()
555 while (client_list
!= NULL
)
556 client_unmanage(client_list
->data
);
559 void client_unmanage(ObClient
*self
)
564 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
566 g_assert(self
!= NULL
);
568 keyboard_grab_for_client(self
, FALSE
);
569 mouse_grab_for_client(self
, FALSE
);
571 /* potentially fix focusLast */
572 if (config_focus_last
)
573 grab_pointer(TRUE
, OB_CURSOR_NONE
);
575 /* remove the window from our save set */
576 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
578 /* we dont want events no more */
579 XSelectInput(ob_display
, self
->window
, NoEventMask
);
581 frame_hide(self
->frame
);
583 client_list
= g_list_remove(client_list
, self
);
584 stacking_remove(self
);
585 g_hash_table_remove(window_map
, &self
->window
);
587 /* update the focus lists */
588 focus_order_remove(self
);
590 /* once the client is out of the list, update the struts to remove it's
592 if (STRUT_EXISTS(self
->strut
))
593 screen_update_areas();
595 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
596 Destructor
*d
= it
->data
;
597 d
->func(self
, d
->data
);
600 if (focus_client
== self
) {
603 /* focus the last focused window on the desktop, and ignore enter
604 events from the unmap so it doesnt mess with the focus */
605 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
606 /* remove these flags so we don't end up getting focused in the
608 self
->can_focus
= FALSE
;
609 self
->focus_notify
= FALSE
;
611 client_unfocus(self
);
614 /* tell our parent(s) that we're gone */
615 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
616 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
617 if (it
->data
!= self
)
618 ((ObClient
*)it
->data
)->transients
=
619 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
620 } else if (self
->transient_for
) { /* transient of window */
621 self
->transient_for
->transients
=
622 g_slist_remove(self
->transient_for
->transients
, self
);
625 /* tell our transients that we're gone */
626 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
627 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
628 ((ObClient
*)it
->data
)->transient_for
= NULL
;
629 client_calc_layer(it
->data
);
633 /* remove from its group */
635 group_remove(self
->group
, self
);
639 /* give the client its border back */
640 client_toggle_border(self
, TRUE
);
642 /* reparent the window out of the frame, and free the frame */
643 frame_release_client(self
->frame
, self
);
646 if (ob_state() != OB_STATE_EXITING
) {
647 /* these values should not be persisted across a window
649 PROP_ERASE(self
->window
, net_wm_desktop
);
650 PROP_ERASE(self
->window
, net_wm_state
);
651 PROP_ERASE(self
->window
, wm_state
);
653 /* if we're left in an unmapped state, the client wont be mapped. this
654 is bad, since we will no longer be managing the window on restart */
655 XMapWindow(ob_display
, self
->window
);
659 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
661 /* free all data allocated in the client struct */
662 g_slist_free(self
->transients
);
663 for (j
= 0; j
< self
->nicons
; ++j
)
664 g_free(self
->icons
[j
].data
);
665 if (self
->nicons
> 0)
668 g_free(self
->icon_title
);
672 g_free(self
->sm_client_id
);
675 /* update the list hints */
678 if (config_focus_last
)
679 grab_pointer(FALSE
, OB_CURSOR_NONE
);
682 static void client_restore_session_state(ObClient
*self
)
686 if (!(it
= session_state_find(self
)))
689 self
->session
= it
->data
;
691 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
692 self
->positioned
= PPosition
;
693 if (self
->session
->w
> 0)
694 self
->area
.width
= self
->session
->w
;
695 if (self
->session
->h
> 0)
696 self
->area
.height
= self
->session
->h
;
697 XResizeWindow(ob_display
, self
->window
,
698 self
->area
.width
, self
->area
.height
);
700 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
701 self
->session
->desktop
:
702 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
703 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
705 self
->shaded
= self
->session
->shaded
;
706 self
->iconic
= self
->session
->iconic
;
707 self
->skip_pager
= self
->session
->skip_pager
;
708 self
->skip_taskbar
= self
->session
->skip_taskbar
;
709 self
->fullscreen
= self
->session
->fullscreen
;
710 self
->above
= self
->session
->above
;
711 self
->below
= self
->session
->below
;
712 self
->max_horz
= self
->session
->max_horz
;
713 self
->max_vert
= self
->session
->max_vert
;
716 static void client_restore_session_stacking(ObClient
*self
)
720 if (!self
->session
) return;
722 it
= g_list_find(session_saved_state
, self
->session
);
723 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
726 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
727 if (session_state_cmp(it
->data
, cit
->data
))
730 client_calc_layer(self
);
731 stacking_below(CLIENT_AS_WINDOW(self
),
732 CLIENT_AS_WINDOW(cit
->data
));
738 void client_move_onscreen(ObClient
*self
, gboolean rude
)
740 gint x
= self
->area
.x
;
741 gint y
= self
->area
.y
;
742 if (client_find_onscreen(self
, &x
, &y
,
743 self
->frame
->area
.width
,
744 self
->frame
->area
.height
, rude
)) {
745 client_move(self
, x
, y
);
749 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
753 gint ox
= *x
, oy
= *y
;
755 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
758 /* XXX watch for xinerama dead areas */
759 /* This makes sure windows aren't entirely outside of the screen so you
760 can't see them at all.
761 It makes sure 10% of the window is on the screen at least. At don't let
762 it move itself off the top of the screen, which would hide the titlebar
763 on you. (The user can still do this if they want too, it's only limiting
766 if (client_normal(self
)) {
767 a
= screen_area(self
->desktop
);
768 if (!self
->strut
.right
&&
769 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
770 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
771 if (!self
->strut
.bottom
&&
772 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
773 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
774 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
775 *x
= a
->x
- self
->frame
->area
.width
*9/10;
776 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
777 *y
= a
->y
- self
->frame
->area
.width
*9/10;
780 /* This here doesn't let windows even a pixel outside the screen,
781 * when called from client_manage, programs placing themselves are
782 * forced completely onscreen, while things like
783 * xterm -geometry resolution-width/2 will work fine. Trying to
784 * place it completely offscreen will be handled in the above code.
785 * Sorry for this confused comment, i am tired. */
787 /* avoid the xinerama monitor divide while we're at it,
788 * remember to fix the placement stuff to avoid it also and
789 * then remove this XXX */
790 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
791 /* dont let windows map into the strut unless they
792 are bigger than the available area */
794 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
795 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
796 *x
= a
->x
+ a
->width
- w
;
798 if (h
<= a
->height
) {
799 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
800 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
801 *y
= a
->y
+ a
->height
- h
;
805 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
808 return ox
!= *x
|| oy
!= *y
;
811 static void client_toggle_border(ObClient
*self
, gboolean show
)
813 /* adjust our idea of where the client is, based on its border. When the
814 border is removed, the client should now be considered to be in a
816 when re-adding the border to the client, the same operation needs to be
818 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
819 gint x
= oldx
, y
= oldy
;
820 switch(self
->gravity
) {
822 case NorthWestGravity
:
824 case SouthWestGravity
:
826 case NorthEastGravity
:
828 case SouthEastGravity
:
829 if (show
) x
-= self
->border_width
* 2;
830 else x
+= self
->border_width
* 2;
837 if (show
) x
-= self
->border_width
;
838 else x
+= self
->border_width
;
841 switch(self
->gravity
) {
843 case NorthWestGravity
:
845 case NorthEastGravity
:
847 case SouthWestGravity
:
849 case SouthEastGravity
:
850 if (show
) y
-= self
->border_width
* 2;
851 else y
+= self
->border_width
* 2;
858 if (show
) y
-= self
->border_width
;
859 else y
+= self
->border_width
;
866 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
868 /* move the client so it is back it the right spot _with_ its
870 if (x
!= oldx
|| y
!= oldy
)
871 XMoveWindow(ob_display
, self
->window
, x
, y
);
873 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
877 static void client_get_all(ObClient
*self
)
879 client_get_area(self
);
880 client_get_mwm_hints(self
);
882 /* The transient hint is used to pick a type, but the type can also affect
883 transiency (dialogs are always made transients of their group if they
884 have one). This is Havoc's idea, but it is needed to make some apps
885 work right (eg tsclient). */
886 client_update_transient_for(self
);
887 client_get_type(self
);/* this can change the mwmhints for special cases */
888 client_get_state(self
);
889 client_update_transient_for(self
);
891 client_update_wmhints(self
);
892 client_get_startup_id(self
);
893 client_get_desktop(self
);/* uses transient data/group/startup id if a
894 desktop is not specified */
895 client_get_shaped(self
);
897 client_get_layer(self
); /* if layer hasn't been specified, get it from
898 other sources if possible */
901 /* a couple type-based defaults for new windows */
903 /* this makes sure that these windows appear on all desktops */
904 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
905 self
->desktop
= DESKTOP_ALL
;
908 client_update_protocols(self
);
910 client_get_gravity(self
); /* get the attribute gravity */
911 client_update_normal_hints(self
); /* this may override the attribute
914 /* got the type, the mwmhints, the protocols, and the normal hints
915 (min/max sizes), so we're ready to set up the decorations/functions */
916 client_setup_decor_and_functions(self
);
918 client_update_title(self
);
919 client_update_class(self
);
920 client_update_sm_client_id(self
);
921 client_update_strut(self
);
922 client_update_icons(self
);
923 client_update_user_time(self
, FALSE
);
926 static void client_get_startup_id(ObClient
*self
)
928 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
930 PROP_GETS(self
->group
->leader
,
931 net_startup_id
, utf8
, &self
->startup_id
);
934 static void client_get_area(ObClient
*self
)
936 XWindowAttributes wattrib
;
939 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
940 g_assert(ret
!= BadWindow
);
942 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
943 self
->border_width
= wattrib
.border_width
;
946 static void client_get_desktop(ObClient
*self
)
948 guint32 d
= screen_num_desktops
; /* an always-invalid value */
950 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
951 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
952 self
->desktop
= screen_num_desktops
- 1;
956 gboolean trdesk
= FALSE
;
958 if (self
->transient_for
) {
959 if (self
->transient_for
!= OB_TRAN_GROUP
) {
960 self
->desktop
= self
->transient_for
->desktop
;
965 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
966 if (it
->data
!= self
&&
967 !((ObClient
*)it
->data
)->transient_for
) {
968 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
975 /* try get from the startup-notification protocol */
976 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
977 if (self
->desktop
>= screen_num_desktops
&&
978 self
->desktop
!= DESKTOP_ALL
)
979 self
->desktop
= screen_num_desktops
- 1;
981 /* defaults to the current desktop */
982 self
->desktop
= screen_desktop
;
985 if (self
->desktop
!= d
) {
986 /* set the desktop hint, to make sure that it always exists */
987 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
991 static void client_get_layer(ObClient
*self
)
993 if (!(self
->above
|| self
->below
)) {
995 /* apply stuff from the group */
999 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1000 ObClient
*c
= it
->data
;
1001 if (c
!= self
&& !client_search_transient(self
, c
) &&
1002 client_normal(self
) && client_normal(c
))
1005 (c
->above
? 1 : (c
->below
? -1 : 0)));
1019 g_assert_not_reached();
1026 static void client_get_state(ObClient
*self
)
1031 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1033 for (i
= 0; i
< num
; ++i
) {
1034 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1036 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1037 self
->shaded
= TRUE
;
1038 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1039 self
->iconic
= TRUE
;
1040 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1041 self
->skip_taskbar
= TRUE
;
1042 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1043 self
->skip_pager
= TRUE
;
1044 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1045 self
->fullscreen
= TRUE
;
1046 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1047 self
->max_vert
= TRUE
;
1048 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1049 self
->max_horz
= TRUE
;
1050 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1052 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1054 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1055 self
->demands_attention
= TRUE
;
1056 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1057 self
->undecorated
= TRUE
;
1064 static void client_get_shaped(ObClient
*self
)
1066 self
->shaped
= FALSE
;
1068 if (extensions_shape
) {
1073 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1075 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1076 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1078 self
->shaped
= (s
!= 0);
1083 void client_update_transient_for(ObClient
*self
)
1086 ObClient
*target
= NULL
;
1088 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1089 self
->transient
= TRUE
;
1090 if (t
!= self
->window
) { /* cant be transient to itself! */
1091 target
= g_hash_table_lookup(window_map
, &t
);
1092 /* if this happens then we need to check for it*/
1093 g_assert(target
!= self
);
1094 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1095 /* this can happen when a dialog is a child of
1096 a dockapp, for example */
1100 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1102 Setting the transient_for to Root is actually illegal, however
1103 applications from time have done this to specify transient for
1106 Now you can do that by being a TYPE_DIALOG and not setting
1107 the transient_for hint at all on your window. But people still
1108 use Root, and Kwin is very strange in this regard.
1110 KWin 3.0 will not consider windows with transient_for set to
1111 Root as transient for their group *UNLESS* they are also modal.
1112 In that case, it will make them transient for the group. This
1113 leads to all sorts of weird behavior from KDE apps which are
1114 only tested in KWin. I'd like to follow their behavior just to
1115 make this work right with KDE stuff, but that seems wrong.
1117 if (!target
&& self
->group
) {
1118 /* not transient to a client, see if it is transient for a
1120 if (t
== RootWindow(ob_display
, ob_screen
)) {
1121 /* window is a transient for its group! */
1122 target
= OB_TRAN_GROUP
;
1126 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
1127 self
->transient
= TRUE
;
1128 target
= OB_TRAN_GROUP
;
1130 self
->transient
= FALSE
;
1132 /* if anything has changed... */
1133 if (target
!= self
->transient_for
) {
1134 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1137 /* remove from old parents */
1138 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1139 ObClient
*c
= it
->data
;
1140 if (c
!= self
&& !c
->transient_for
)
1141 c
->transients
= g_slist_remove(c
->transients
, self
);
1143 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1144 /* remove from old parent */
1145 self
->transient_for
->transients
=
1146 g_slist_remove(self
->transient_for
->transients
, self
);
1148 self
->transient_for
= target
;
1149 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1152 /* add to new parents */
1153 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1154 ObClient
*c
= it
->data
;
1155 if (c
!= self
&& !c
->transient_for
)
1156 c
->transients
= g_slist_append(c
->transients
, self
);
1159 /* remove all transients which are in the group, that causes
1160 circlular pointer hell of doom */
1161 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1163 for (sit
= self
->transients
; sit
; sit
= next
) {
1164 next
= g_slist_next(sit
);
1165 if (sit
->data
== it
->data
)
1167 g_slist_delete_link(self
->transients
, sit
);
1170 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1171 /* add to new parent */
1172 self
->transient_for
->transients
=
1173 g_slist_append(self
->transient_for
->transients
, self
);
1178 static void client_get_mwm_hints(ObClient
*self
)
1183 self
->mwmhints
.flags
= 0; /* default to none */
1185 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1187 if (num
>= OB_MWM_ELEMENTS
) {
1188 self
->mwmhints
.flags
= hints
[0];
1189 self
->mwmhints
.functions
= hints
[1];
1190 self
->mwmhints
.decorations
= hints
[2];
1196 void client_get_type(ObClient
*self
)
1203 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1204 /* use the first value that we know about in the array */
1205 for (i
= 0; i
< num
; ++i
) {
1206 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1207 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1208 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1209 self
->type
= OB_CLIENT_TYPE_DOCK
;
1210 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1211 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1212 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1213 self
->type
= OB_CLIENT_TYPE_MENU
;
1214 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1215 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1216 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1217 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1218 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1219 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1220 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1221 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1222 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1223 /* prevent this window from getting any decor or
1225 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1226 OB_MWM_FLAG_DECORATIONS
);
1227 self
->mwmhints
.decorations
= 0;
1228 self
->mwmhints
.functions
= 0;
1230 if (self
->type
!= (ObClientType
) -1)
1231 break; /* grab the first legit type */
1236 if (self
->type
== (ObClientType
) -1) {
1237 /*the window type hint was not set, which means we either classify
1238 ourself as a normal window or a dialog, depending on if we are a
1240 if (self
->transient
)
1241 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1243 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1247 void client_update_protocols(ObClient
*self
)
1250 guint num_return
, i
;
1252 self
->focus_notify
= FALSE
;
1253 self
->delete_window
= FALSE
;
1255 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1256 for (i
= 0; i
< num_return
; ++i
) {
1257 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1258 /* this means we can request the window to close */
1259 self
->delete_window
= TRUE
;
1260 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1261 /* if this protocol is requested, then the window will be
1262 notified whenever we want it to receive focus */
1263 self
->focus_notify
= TRUE
;
1269 static void client_get_gravity(ObClient
*self
)
1271 XWindowAttributes wattrib
;
1274 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1275 g_assert(ret
!= BadWindow
);
1276 self
->gravity
= wattrib
.win_gravity
;
1279 void client_update_normal_hints(ObClient
*self
)
1283 gint oldgravity
= self
->gravity
;
1286 self
->min_ratio
= 0.0f
;
1287 self
->max_ratio
= 0.0f
;
1288 SIZE_SET(self
->size_inc
, 1, 1);
1289 SIZE_SET(self
->base_size
, 0, 0);
1290 SIZE_SET(self
->min_size
, 0, 0);
1291 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1293 /* get the hints from the window */
1294 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1295 /* normal windows can't request placement! har har
1296 if (!client_normal(self))
1298 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1300 if (size
.flags
& PWinGravity
) {
1301 self
->gravity
= size
.win_gravity
;
1303 /* if the client has a frame, i.e. has already been mapped and
1304 is changing its gravity */
1305 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1306 /* move our idea of the client's position based on its new
1308 self
->area
.x
= self
->frame
->area
.x
;
1309 self
->area
.y
= self
->frame
->area
.y
;
1310 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1314 if (size
.flags
& PAspect
) {
1315 if (size
.min_aspect
.y
)
1317 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1318 if (size
.max_aspect
.y
)
1320 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1323 if (size
.flags
& PMinSize
)
1324 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1326 if (size
.flags
& PMaxSize
)
1327 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1329 if (size
.flags
& PBaseSize
)
1330 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1332 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1333 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1337 void client_setup_decor_and_functions(ObClient
*self
)
1339 /* start with everything (cept fullscreen) */
1341 (OB_FRAME_DECOR_TITLEBAR
|
1342 OB_FRAME_DECOR_HANDLE
|
1343 OB_FRAME_DECOR_GRIPS
|
1344 OB_FRAME_DECOR_BORDER
|
1345 OB_FRAME_DECOR_ICON
|
1346 OB_FRAME_DECOR_ALLDESKTOPS
|
1347 OB_FRAME_DECOR_ICONIFY
|
1348 OB_FRAME_DECOR_MAXIMIZE
|
1349 OB_FRAME_DECOR_SHADE
|
1350 OB_FRAME_DECOR_CLOSE
);
1352 (OB_CLIENT_FUNC_RESIZE
|
1353 OB_CLIENT_FUNC_MOVE
|
1354 OB_CLIENT_FUNC_ICONIFY
|
1355 OB_CLIENT_FUNC_MAXIMIZE
|
1356 OB_CLIENT_FUNC_SHADE
|
1357 OB_CLIENT_FUNC_CLOSE
);
1359 if (!(self
->min_size
.width
< self
->max_size
.width
||
1360 self
->min_size
.height
< self
->max_size
.height
))
1361 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1363 switch (self
->type
) {
1364 case OB_CLIENT_TYPE_NORMAL
:
1365 /* normal windows retain all of the possible decorations and
1366 functionality, and are the only windows that you can fullscreen */
1367 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1370 case OB_CLIENT_TYPE_DIALOG
:
1371 case OB_CLIENT_TYPE_UTILITY
:
1372 /* these windows cannot be maximized */
1373 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1376 case OB_CLIENT_TYPE_MENU
:
1377 case OB_CLIENT_TYPE_TOOLBAR
:
1378 /* these windows get less functionality */
1379 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1382 case OB_CLIENT_TYPE_DESKTOP
:
1383 case OB_CLIENT_TYPE_DOCK
:
1384 case OB_CLIENT_TYPE_SPLASH
:
1385 /* none of these windows are manipulated by the window manager */
1386 self
->decorations
= 0;
1387 self
->functions
= 0;
1391 /* Mwm Hints are applied subtractively to what has already been chosen for
1392 decor and functionality */
1393 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1394 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1395 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1396 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1398 /* if the mwm hints request no handle or title, then all
1399 decorations are disabled, but keep the border if that's
1401 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1402 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1404 self
->decorations
= 0;
1409 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1410 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1411 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1412 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1413 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1414 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1415 /* dont let mwm hints kill any buttons
1416 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1417 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1418 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1419 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1421 /* dont let mwm hints kill the close button
1422 if (! (self->mwmhints.functions & MwmFunc_Close))
1423 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1427 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1428 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1429 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1430 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1431 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1432 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1434 /* can't maximize without moving/resizing */
1435 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1436 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1437 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1438 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1439 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1442 /* kill the handle on fully maxed windows */
1443 if (self
->max_vert
&& self
->max_horz
)
1444 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1446 /* finally, the user can have requested no decorations, which overrides
1447 everything (but doesnt give it a border if it doesnt have one) */
1448 if (self
->undecorated
) {
1449 if (config_theme_keepborder
)
1450 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1452 self
->decorations
= 0;
1455 /* if we don't have a titlebar, then we cannot shade! */
1456 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1457 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1459 /* now we need to check against rules for the client's current state */
1460 if (self
->fullscreen
) {
1461 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1462 OB_CLIENT_FUNC_FULLSCREEN
|
1463 OB_CLIENT_FUNC_ICONIFY
);
1464 self
->decorations
= 0;
1467 client_change_allowed_actions(self
);
1470 /* adjust the client's decorations, etc. */
1471 client_reconfigure(self
);
1475 static void client_change_allowed_actions(ObClient
*self
)
1480 /* desktop windows are kept on all desktops */
1481 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1482 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1484 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1485 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1486 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1487 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1488 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1489 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1490 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1491 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1492 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1493 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1494 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1495 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1496 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1497 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1498 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1501 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1503 /* make sure the window isn't breaking any rules now */
1505 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1506 if (self
->frame
) client_shade(self
, FALSE
);
1507 else self
->shaded
= FALSE
;
1509 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1510 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1511 else self
->iconic
= FALSE
;
1513 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1514 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1515 else self
->fullscreen
= FALSE
;
1517 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1519 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1520 else self
->max_vert
= self
->max_horz
= FALSE
;
1524 void client_reconfigure(ObClient
*self
)
1526 /* by making this pass FALSE for user, we avoid the emacs event storm where
1527 every configurenotify causes an update in its normal hints, i think this
1528 is generally what we want anyways... */
1529 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1530 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1533 void client_update_wmhints(ObClient
*self
)
1538 /* assume a window takes input if it doesnt specify */
1539 self
->can_focus
= TRUE
;
1541 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1542 if (hints
->flags
& InputHint
)
1543 self
->can_focus
= hints
->input
;
1545 /* only do this when first managing the window *AND* when we aren't
1547 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1548 if (hints
->flags
& StateHint
)
1549 self
->iconic
= hints
->initial_state
== IconicState
;
1551 if (!(hints
->flags
& WindowGroupHint
))
1552 hints
->window_group
= None
;
1554 /* did the group state change? */
1555 if (hints
->window_group
!=
1556 (self
->group
? self
->group
->leader
: None
)) {
1557 /* remove from the old group if there was one */
1558 if (self
->group
!= NULL
) {
1559 /* remove transients of the group */
1560 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1561 self
->transients
= g_slist_remove(self
->transients
,
1564 /* remove myself from parents in the group */
1565 if (self
->transient_for
== OB_TRAN_GROUP
) {
1566 for (it
= self
->group
->members
; it
;
1567 it
= g_slist_next(it
))
1569 ObClient
*c
= it
->data
;
1571 if (c
!= self
&& !c
->transient_for
)
1572 c
->transients
= g_slist_remove(c
->transients
,
1577 group_remove(self
->group
, self
);
1580 if (hints
->window_group
!= None
) {
1581 self
->group
= group_add(hints
->window_group
, self
);
1583 /* i can only have transients from the group if i am not
1585 if (!self
->transient_for
) {
1586 /* add other transients of the group that are already
1588 for (it
= self
->group
->members
; it
;
1589 it
= g_slist_next(it
))
1591 ObClient
*c
= it
->data
;
1592 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1594 g_slist_append(self
->transients
, c
);
1599 /* because the self->transient flag wont change from this call,
1600 we don't need to update the window's type and such, only its
1601 transient_for, and the transients lists of other windows in
1602 the group may be affected */
1603 client_update_transient_for(self
);
1606 /* the WM_HINTS can contain an icon */
1607 client_update_icons(self
);
1613 void client_update_title(ObClient
*self
)
1619 gboolean read_title
;
1622 old_title
= self
->title
;
1625 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1626 /* try old x stuff */
1627 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1628 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1629 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1630 if (self
->transient
) {
1631 data
= g_strdup("");
1634 data
= g_strdup("Unnamed Window");
1638 if (config_title_number
) {
1640 /* did the title change? then reset the title_count */
1641 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1642 self
->title_count
= 1;
1644 /* look for duplicates and append a number */
1646 for (it
= client_list
; it
; it
= g_list_next(it
))
1647 if (it
->data
!= self
) {
1648 ObClient
*c
= it
->data
;
1650 if (c
->title_count
== 1) {
1651 if (!strcmp(c
->title
, data
))
1652 nums
|= 1 << c
->title_count
;
1657 /* find the beginning of our " - [%u]", this relies on
1658 that syntax being used */
1659 end
= strrchr(c
->title
, '-') - 1;
1660 len
= end
- c
->title
;
1661 if (!strncmp(c
->title
, data
, len
))
1662 nums
|= 1 << c
->title_count
;
1665 /* find first free number */
1666 for (i
= 1; i
<= 32; ++i
)
1667 if (!(nums
& (1 << i
))) {
1668 if (self
->title_count
== 1 || i
== 1)
1669 self
->title_count
= i
;
1672 /* dont display the number for the first window */
1673 if (self
->title_count
> 1) {
1675 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1680 self
->title_count
= 1;
1683 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1687 frame_adjust_title(self
->frame
);
1691 /* update the icon title */
1693 g_free(self
->icon_title
);
1697 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1698 /* try old x stuff */
1699 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)
1700 || PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
))) {
1701 data
= g_strdup(self
->title
);
1705 /* append the title count, dont display the number for the first window.
1706 * We don't need to check for config_title_number here since title_count
1707 * is not set above 1 then. */
1708 if (read_title
&& self
->title_count
> 1) {
1710 newdata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1715 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1717 self
->icon_title
= data
;
1720 void client_update_class(ObClient
*self
)
1725 if (self
->name
) g_free(self
->name
);
1726 if (self
->class) g_free(self
->class);
1727 if (self
->role
) g_free(self
->role
);
1729 self
->name
= self
->class = self
->role
= NULL
;
1731 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1733 self
->name
= g_strdup(data
[0]);
1735 self
->class = g_strdup(data
[1]);
1740 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1743 if (self
->name
== NULL
) self
->name
= g_strdup("");
1744 if (self
->class == NULL
) self
->class = g_strdup("");
1745 if (self
->role
== NULL
) self
->role
= g_strdup("");
1748 void client_update_strut(ObClient
*self
)
1752 gboolean got
= FALSE
;
1755 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1759 STRUT_PARTIAL_SET(strut
,
1760 data
[0], data
[2], data
[1], data
[3],
1761 data
[4], data
[5], data
[8], data
[9],
1762 data
[6], data
[7], data
[10], data
[11]);
1768 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1774 /* use the screen's width/height */
1775 a
= screen_physical_area();
1777 STRUT_PARTIAL_SET(strut
,
1778 data
[0], data
[2], data
[1], data
[3],
1779 a
->y
, a
->y
+ a
->height
- 1,
1780 a
->x
, a
->x
+ a
->width
- 1,
1781 a
->y
, a
->y
+ a
->height
- 1,
1782 a
->x
, a
->x
+ a
->width
- 1);
1788 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1789 0, 0, 0, 0, 0, 0, 0, 0);
1791 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1792 self
->strut
= strut
;
1794 /* updating here is pointless while we're being mapped cuz we're not in
1795 the client list yet */
1797 screen_update_areas();
1801 void client_update_icons(ObClient
*self
)
1807 for (i
= 0; i
< self
->nicons
; ++i
)
1808 g_free(self
->icons
[i
].data
);
1809 if (self
->nicons
> 0)
1810 g_free(self
->icons
);
1813 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1814 /* figure out how many valid icons are in here */
1816 while (num
- i
> 2) {
1820 if (i
> num
|| w
*h
== 0) break;
1824 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1826 /* store the icons */
1828 for (j
= 0; j
< self
->nicons
; ++j
) {
1831 w
= self
->icons
[j
].width
= data
[i
++];
1832 h
= self
->icons
[j
].height
= data
[i
++];
1834 if (w
*h
== 0) continue;
1836 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1837 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1842 self
->icons
[j
].data
[t
] =
1843 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1844 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1845 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1846 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1855 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1856 if (hints
->flags
& IconPixmapHint
) {
1858 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1859 xerror_set_ignore(TRUE
);
1860 if (!RrPixmapToRGBA(ob_rr_inst
,
1862 (hints
->flags
& IconMaskHint
?
1863 hints
->icon_mask
: None
),
1864 &self
->icons
[self
->nicons
-1].width
,
1865 &self
->icons
[self
->nicons
-1].height
,
1866 &self
->icons
[self
->nicons
-1].data
)){
1867 g_free(&self
->icons
[self
->nicons
-1]);
1870 xerror_set_ignore(FALSE
);
1877 frame_adjust_icon(self
->frame
);
1880 void client_update_user_time(ObClient
*self
, gboolean new_event
)
1884 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1885 self
->user_time
= time
;
1886 /* we set this every time, not just when it grows, because in practice
1887 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1888 backward we don't want all windows to stop focusing. we'll just
1889 assume noone is setting times older than the last one, cuz that
1890 would be pretty stupid anyways
1891 However! This is called when a window is mapped to get its user time
1892 but it's an old number, it's not changing it from new user
1893 interaction, so in that case, don't change the last user time.
1896 client_last_user_time
= time
;
1898 /*ob_debug("window 0x%x user time %u\n", self->window, time);*/
1902 static void client_change_state(ObClient
*self
)
1905 gulong netstate
[11];
1908 state
[0] = self
->wmstate
;
1910 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1914 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1916 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1918 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1919 if (self
->skip_taskbar
)
1920 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1921 if (self
->skip_pager
)
1922 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1923 if (self
->fullscreen
)
1924 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1926 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1928 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1930 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1932 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1933 if (self
->demands_attention
)
1934 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1935 if (self
->undecorated
)
1936 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1937 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1940 frame_adjust_state(self
->frame
);
1943 ObClient
*client_search_focus_tree(ObClient
*self
)
1948 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1949 if (client_focused(it
->data
)) return it
->data
;
1950 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1955 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1957 if (self
->transient_for
) {
1958 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1959 return client_search_focus_tree_full(self
->transient_for
);
1962 gboolean recursed
= FALSE
;
1964 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1965 if (!((ObClient
*)it
->data
)->transient_for
) {
1967 if ((c
= client_search_focus_tree_full(it
->data
)))
1976 /* this function checks the whole tree, the client_search_focus_tree~
1977 does not, so we need to check this window */
1978 if (client_focused(self
))
1980 return client_search_focus_tree(self
);
1983 static ObStackingLayer
calc_layer(ObClient
*self
)
1987 if (self
->fullscreen
&&
1988 (client_focused(self
) || client_search_focus_tree(self
)))
1989 l
= OB_STACKING_LAYER_FULLSCREEN
;
1990 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1991 l
= OB_STACKING_LAYER_DESKTOP
;
1992 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1993 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1994 else l
= OB_STACKING_LAYER_ABOVE
;
1996 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1997 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1998 else l
= OB_STACKING_LAYER_NORMAL
;
2003 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2004 ObStackingLayer min
, gboolean raised
)
2006 ObStackingLayer old
, own
;
2010 own
= calc_layer(self
);
2011 self
->layer
= MAX(own
, min
);
2013 ob_debug("layer for %s: %d\n", self
->title
, self
->layer
);
2015 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2016 client_calc_layer_recursive(it
->data
, orig
,
2018 raised
? raised
: self
->layer
!= old
);
2020 if (!raised
&& self
->layer
!= old
)
2021 if (orig
->frame
) { /* only restack if the original window is managed */
2022 stacking_remove(CLIENT_AS_WINDOW(self
));
2023 stacking_add(CLIENT_AS_WINDOW(self
));
2027 void client_calc_layer(ObClient
*self
)
2034 /* transients take on the layer of their parents */
2035 it
= client_search_top_transients(self
);
2037 for (; it
; it
= g_slist_next(it
))
2038 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2041 gboolean
client_should_show(ObClient
*self
)
2045 if (client_normal(self
) && screen_showing_desktop
)
2048 if (self->transient_for) {
2049 if (self->transient_for != OB_TRAN_GROUP)
2050 return client_should_show(self->transient_for);
2054 for (it = self->group->members; it; it = g_slist_next(it)) {
2055 ObClient *c = it->data;
2056 if (c != self && !c->transient_for) {
2057 if (client_should_show(c))
2064 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2070 static void client_showhide(ObClient
*self
)
2073 if (client_should_show(self
))
2074 frame_show(self
->frame
);
2076 frame_hide(self
->frame
);
2079 gboolean
client_normal(ObClient
*self
) {
2080 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2081 self
->type
== OB_CLIENT_TYPE_DOCK
||
2082 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2085 static void client_apply_startup_state(ObClient
*self
)
2087 /* these are in a carefully crafted order.. */
2090 self
->iconic
= FALSE
;
2091 client_iconify(self
, TRUE
, FALSE
);
2093 if (self
->fullscreen
) {
2094 self
->fullscreen
= FALSE
;
2095 client_fullscreen(self
, TRUE
, FALSE
);
2097 if (self
->undecorated
) {
2098 self
->undecorated
= FALSE
;
2099 client_set_undecorated(self
, TRUE
);
2102 self
->shaded
= FALSE
;
2103 client_shade(self
, TRUE
);
2105 if (self
->demands_attention
) {
2106 self
->demands_attention
= FALSE
;
2107 client_hilite(self
, TRUE
);
2110 if (self
->max_vert
&& self
->max_horz
) {
2111 self
->max_vert
= self
->max_horz
= FALSE
;
2112 client_maximize(self
, TRUE
, 0, FALSE
);
2113 } else if (self
->max_vert
) {
2114 self
->max_vert
= FALSE
;
2115 client_maximize(self
, TRUE
, 2, FALSE
);
2116 } else if (self
->max_horz
) {
2117 self
->max_horz
= FALSE
;
2118 client_maximize(self
, TRUE
, 1, FALSE
);
2121 /* nothing to do for the other states:
2130 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2131 gint x
, gint y
, gint w
, gint h
,
2132 gboolean user
, gboolean final
,
2133 gboolean force_reply
)
2136 gboolean send_resize_client
;
2137 gboolean moved
= FALSE
, resized
= FALSE
;
2138 guint fdecor
= self
->frame
->decorations
;
2139 gboolean fhorz
= self
->frame
->max_horz
;
2140 Rect desired_area
= {x
, y
, w
, h
};
2142 /* make the frame recalculate its dimentions n shit without changing
2143 anything visible for real, this way the constraints below can work with
2144 the updated frame dimensions. */
2145 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2147 /* gets the frame's position */
2148 frame_client_gravity(self
->frame
, &x
, &y
);
2150 /* these positions are frame positions, not client positions */
2152 /* set the size and position if fullscreen */
2153 if (self
->fullscreen
) {
2157 i
= screen_find_monitor(&desired_area
);
2158 a
= screen_physical_area_monitor(i
);
2165 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2170 i
= screen_find_monitor(&desired_area
);
2171 a
= screen_area_monitor(self
->desktop
, i
);
2173 /* set the size and position if maximized */
2174 if (self
->max_horz
) {
2176 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2178 if (self
->max_vert
) {
2180 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2184 /* gets the client's position */
2185 frame_frame_gravity(self
->frame
, &x
, &y
);
2187 /* these override the above states! if you cant move you can't move! */
2189 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2193 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2194 w
= self
->area
.width
;
2195 h
= self
->area
.height
;
2199 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2200 gint basew
, baseh
, minw
, minh
;
2202 /* base size is substituted with min size if not specified */
2203 if (self
->base_size
.width
|| self
->base_size
.height
) {
2204 basew
= self
->base_size
.width
;
2205 baseh
= self
->base_size
.height
;
2207 basew
= self
->min_size
.width
;
2208 baseh
= self
->min_size
.height
;
2210 /* min size is substituted with base size if not specified */
2211 if (self
->min_size
.width
|| self
->min_size
.height
) {
2212 minw
= self
->min_size
.width
;
2213 minh
= self
->min_size
.height
;
2215 minw
= self
->base_size
.width
;
2216 minh
= self
->base_size
.height
;
2219 /* if this is a user-requested resize, then check against min/max
2222 /* smaller than min size or bigger than max size? */
2223 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2224 if (w
< minw
) w
= minw
;
2225 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2226 if (h
< minh
) h
= minh
;
2231 /* keep to the increments */
2232 w
/= self
->size_inc
.width
;
2233 h
/= self
->size_inc
.height
;
2235 /* you cannot resize to nothing */
2236 if (basew
+ w
< 1) w
= 1 - basew
;
2237 if (baseh
+ h
< 1) h
= 1 - baseh
;
2239 /* store the logical size */
2240 SIZE_SET(self
->logical_size
,
2241 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2242 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2244 w
*= self
->size_inc
.width
;
2245 h
*= self
->size_inc
.height
;
2250 /* adjust the height to match the width for the aspect ratios.
2251 for this, min size is not substituted for base size ever. */
2252 w
-= self
->base_size
.width
;
2253 h
-= self
->base_size
.height
;
2255 if (!self
->fullscreen
) {
2256 if (self
->min_ratio
)
2257 if (h
* self
->min_ratio
> w
) {
2258 h
= (gint
)(w
/ self
->min_ratio
);
2260 /* you cannot resize to nothing */
2263 w
= (gint
)(h
* self
->min_ratio
);
2266 if (self
->max_ratio
)
2267 if (h
* self
->max_ratio
< w
) {
2268 h
= (gint
)(w
/ self
->max_ratio
);
2270 /* you cannot resize to nothing */
2273 w
= (gint
)(h
* self
->min_ratio
);
2278 w
+= self
->base_size
.width
;
2279 h
+= self
->base_size
.height
;
2286 case OB_CORNER_TOPLEFT
:
2288 case OB_CORNER_TOPRIGHT
:
2289 x
-= w
- self
->area
.width
;
2291 case OB_CORNER_BOTTOMLEFT
:
2292 y
-= h
- self
->area
.height
;
2294 case OB_CORNER_BOTTOMRIGHT
:
2295 x
-= w
- self
->area
.width
;
2296 y
-= h
- self
->area
.height
;
2300 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2301 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2303 oldw
= self
->area
.width
;
2304 oldh
= self
->area
.height
;
2305 RECT_SET(self
->area
, x
, y
, w
, h
);
2307 /* for app-requested resizes, always resize if 'resized' is true.
2308 for user-requested ones, only resize if final is true, or when
2309 resizing in redraw mode */
2310 send_resize_client
= ((!user
&& resized
) ||
2312 (resized
&& config_resize_redraw
))));
2314 /* if the client is enlarging, then resize the client before the frame */
2315 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2316 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2318 /* move/resize the frame to match the request */
2320 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2321 moved
= resized
= TRUE
;
2323 if (moved
|| resized
)
2324 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2326 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2329 event
.type
= ConfigureNotify
;
2330 event
.xconfigure
.display
= ob_display
;
2331 event
.xconfigure
.event
= self
->window
;
2332 event
.xconfigure
.window
= self
->window
;
2334 /* root window real coords */
2335 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2337 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2339 event
.xconfigure
.width
= w
;
2340 event
.xconfigure
.height
= h
;
2341 event
.xconfigure
.border_width
= 0;
2342 event
.xconfigure
.above
= self
->frame
->plate
;
2343 event
.xconfigure
.override_redirect
= FALSE
;
2344 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2345 FALSE
, StructureNotifyMask
, &event
);
2349 /* if the client is shrinking, then resize the frame before the client */
2350 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2351 XResizeWindow(ob_display
, self
->window
, w
, h
);
2356 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2360 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2361 self
->fullscreen
== fs
) return; /* already done */
2363 self
->fullscreen
= fs
;
2364 client_change_state(self
); /* change the state hints on the client */
2365 client_calc_layer(self
); /* and adjust out layer/stacking */
2369 self
->pre_fullscreen_area
= self
->area
;
2371 /* these are not actually used cuz client_configure will set them
2372 as appropriate when the window is fullscreened */
2377 if (self
->pre_fullscreen_area
.width
> 0 &&
2378 self
->pre_fullscreen_area
.height
> 0)
2380 x
= self
->pre_fullscreen_area
.x
;
2381 y
= self
->pre_fullscreen_area
.y
;
2382 w
= self
->pre_fullscreen_area
.width
;
2383 h
= self
->pre_fullscreen_area
.height
;
2384 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2386 /* pick some fallbacks... */
2387 a
= screen_area_monitor(self
->desktop
, 0);
2388 x
= a
->x
+ a
->width
/ 4;
2389 y
= a
->y
+ a
->height
/ 4;
2395 client_setup_decor_and_functions(self
);
2397 client_move_resize(self
, x
, y
, w
, h
);
2399 /* try focus us when we go into fullscreen mode */
2403 static void client_iconify_recursive(ObClient
*self
,
2404 gboolean iconic
, gboolean curdesk
)
2407 gboolean changed
= FALSE
;
2410 if (self
->iconic
!= iconic
) {
2411 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2414 self
->iconic
= iconic
;
2417 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2420 old
= self
->wmstate
;
2421 self
->wmstate
= IconicState
;
2422 if (old
!= self
->wmstate
)
2423 PROP_MSG(self
->window
, kde_wm_change_state
,
2424 self
->wmstate
, 1, 0, 0);
2426 /* update the focus lists.. iconic windows go to the bottom of
2427 the list, put the new iconic window at the 'top of the
2429 focus_order_to_top(self
);
2437 client_set_desktop(self
, screen_desktop
, FALSE
);
2439 old
= self
->wmstate
;
2440 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2441 if (old
!= self
->wmstate
)
2442 PROP_MSG(self
->window
, kde_wm_change_state
,
2443 self
->wmstate
, 1, 0, 0);
2445 /* this puts it after the current focused window */
2446 focus_order_remove(self
);
2447 focus_order_add_new(self
);
2454 client_change_state(self
);
2455 client_showhide(self
);
2456 if (STRUT_EXISTS(self
->strut
))
2457 screen_update_areas();
2460 /* iconify all transients */
2461 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2462 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2466 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2470 /* move up the transient chain as far as possible first */
2471 it
= client_search_top_transients(self
);
2473 for (; it
; it
= g_slist_next(it
))
2474 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2477 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2481 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2482 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2484 /* check if already done */
2486 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2487 if (dir
== 1 && self
->max_horz
) return;
2488 if (dir
== 2 && self
->max_vert
) return;
2490 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2491 if (dir
== 1 && !self
->max_horz
) return;
2492 if (dir
== 2 && !self
->max_vert
) return;
2495 /* we just tell it to configure in the same place and client_configure
2496 worries about filling the screen with the window */
2499 w
= self
->area
.width
;
2500 h
= self
->area
.height
;
2504 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2505 RECT_SET(self
->pre_max_area
,
2506 self
->area
.x
, self
->pre_max_area
.y
,
2507 self
->area
.width
, self
->pre_max_area
.height
);
2509 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2510 RECT_SET(self
->pre_max_area
,
2511 self
->pre_max_area
.x
, self
->area
.y
,
2512 self
->pre_max_area
.width
, self
->area
.height
);
2518 a
= screen_area_monitor(self
->desktop
, 0);
2519 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2520 if (self
->pre_max_area
.width
> 0) {
2521 x
= self
->pre_max_area
.x
;
2522 w
= self
->pre_max_area
.width
;
2524 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2525 0, self
->pre_max_area
.height
);
2527 /* pick some fallbacks... */
2528 x
= a
->x
+ a
->width
/ 4;
2532 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2533 if (self
->pre_max_area
.height
> 0) {
2534 y
= self
->pre_max_area
.y
;
2535 h
= self
->pre_max_area
.height
;
2537 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2538 self
->pre_max_area
.width
, 0);
2540 /* pick some fallbacks... */
2541 y
= a
->y
+ a
->height
/ 4;
2547 if (dir
== 0 || dir
== 1) /* horz */
2548 self
->max_horz
= max
;
2549 if (dir
== 0 || dir
== 2) /* vert */
2550 self
->max_vert
= max
;
2552 client_change_state(self
); /* change the state hints on the client */
2554 client_setup_decor_and_functions(self
);
2556 client_move_resize(self
, x
, y
, w
, h
);
2559 void client_shade(ObClient
*self
, gboolean shade
)
2561 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2562 shade
) || /* can't shade */
2563 self
->shaded
== shade
) return; /* already done */
2565 /* when we're iconic, don't change the wmstate */
2566 if (!self
->iconic
) {
2569 old
= self
->wmstate
;
2570 self
->wmstate
= shade
? IconicState
: NormalState
;
2571 if (old
!= self
->wmstate
)
2572 PROP_MSG(self
->window
, kde_wm_change_state
,
2573 self
->wmstate
, 1, 0, 0);
2576 self
->shaded
= shade
;
2577 client_change_state(self
);
2578 /* resize the frame to just the titlebar */
2579 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2582 void client_close(ObClient
*self
)
2586 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2588 /* in the case that the client provides no means to requesting that it
2589 close, we just kill it */
2590 if (!self
->delete_window
)
2594 XXX: itd be cool to do timeouts and shit here for killing the client's
2596 like... if the window is around after 5 seconds, then the close button
2597 turns a nice red, and if this function is called again, the client is
2601 ce
.xclient
.type
= ClientMessage
;
2602 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2603 ce
.xclient
.display
= ob_display
;
2604 ce
.xclient
.window
= self
->window
;
2605 ce
.xclient
.format
= 32;
2606 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2607 ce
.xclient
.data
.l
[1] = event_curtime
;
2608 ce
.xclient
.data
.l
[2] = 0l;
2609 ce
.xclient
.data
.l
[3] = 0l;
2610 ce
.xclient
.data
.l
[4] = 0l;
2611 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2614 void client_kill(ObClient
*self
)
2616 XKillClient(ob_display
, self
->window
);
2619 void client_hilite(ObClient
*self
, gboolean hilite
)
2621 /* don't allow focused windows to hilite */
2622 self
->demands_attention
= hilite
&& !client_focused(self
);
2623 if (self
->demands_attention
)
2624 frame_flash_start(self
->frame
);
2626 frame_flash_stop(self
->frame
);
2627 client_change_state(self
);
2630 void client_set_desktop_recursive(ObClient
*self
,
2631 guint target
, gboolean donthide
)
2636 if (target
!= self
->desktop
) {
2638 ob_debug("Setting desktop %u\n", target
+1);
2640 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2642 /* remove from the old desktop(s) */
2643 focus_order_remove(self
);
2645 old
= self
->desktop
;
2646 self
->desktop
= target
;
2647 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2648 /* the frame can display the current desktop state */
2649 frame_adjust_state(self
->frame
);
2650 /* 'move' the window to the new desktop */
2652 client_showhide(self
);
2653 /* raise if it was not already on the desktop */
2654 if (old
!= DESKTOP_ALL
)
2656 if (STRUT_EXISTS(self
->strut
))
2657 screen_update_areas();
2659 /* add to the new desktop(s) */
2660 if (config_focus_new
)
2661 focus_order_to_top(self
);
2663 focus_order_to_bottom(self
);
2666 /* move all transients */
2667 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2668 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2672 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2676 it
= client_search_top_transients(self
);
2678 for(; it
; it
= g_slist_next(it
))
2679 client_set_desktop_recursive(it
->data
, target
, donthide
);
2682 ObClient
*client_search_modal_child(ObClient
*self
)
2687 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2688 ObClient
*c
= it
->data
;
2689 if ((ret
= client_search_modal_child(c
))) return ret
;
2690 if (c
->modal
) return c
;
2695 gboolean
client_validate(ObClient
*self
)
2699 XSync(ob_display
, FALSE
); /* get all events on the server */
2701 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2702 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2703 XPutBackEvent(ob_display
, &e
);
2710 void client_set_wm_state(ObClient
*self
, glong state
)
2712 if (state
== self
->wmstate
) return; /* no change */
2716 client_iconify(self
, TRUE
, TRUE
);
2719 client_iconify(self
, FALSE
, TRUE
);
2724 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2726 gboolean shaded
= self
->shaded
;
2727 gboolean fullscreen
= self
->fullscreen
;
2728 gboolean undecorated
= self
->undecorated
;
2729 gboolean max_horz
= self
->max_horz
;
2730 gboolean max_vert
= self
->max_vert
;
2731 gboolean modal
= self
->modal
;
2732 gboolean iconic
= self
->iconic
;
2733 gboolean demands_attention
= self
->demands_attention
;
2736 if (!(action
== prop_atoms
.net_wm_state_add
||
2737 action
== prop_atoms
.net_wm_state_remove
||
2738 action
== prop_atoms
.net_wm_state_toggle
))
2739 /* an invalid action was passed to the client message, ignore it */
2742 for (i
= 0; i
< 2; ++i
) {
2743 Atom state
= i
== 0 ? data1
: data2
;
2745 if (!state
) continue;
2747 /* if toggling, then pick whether we're adding or removing */
2748 if (action
== prop_atoms
.net_wm_state_toggle
) {
2749 if (state
== prop_atoms
.net_wm_state_modal
)
2750 action
= modal
? prop_atoms
.net_wm_state_remove
:
2751 prop_atoms
.net_wm_state_add
;
2752 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2753 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2754 prop_atoms
.net_wm_state_add
;
2755 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2756 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2757 prop_atoms
.net_wm_state_add
;
2758 else if (state
== prop_atoms
.net_wm_state_shaded
)
2759 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2760 prop_atoms
.net_wm_state_add
;
2761 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2762 action
= self
->skip_taskbar
?
2763 prop_atoms
.net_wm_state_remove
:
2764 prop_atoms
.net_wm_state_add
;
2765 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2766 action
= self
->skip_pager
?
2767 prop_atoms
.net_wm_state_remove
:
2768 prop_atoms
.net_wm_state_add
;
2769 else if (state
== prop_atoms
.net_wm_state_hidden
)
2770 action
= self
->iconic
?
2771 prop_atoms
.net_wm_state_remove
:
2772 prop_atoms
.net_wm_state_add
;
2773 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2774 action
= fullscreen
?
2775 prop_atoms
.net_wm_state_remove
:
2776 prop_atoms
.net_wm_state_add
;
2777 else if (state
== prop_atoms
.net_wm_state_above
)
2778 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2779 prop_atoms
.net_wm_state_add
;
2780 else if (state
== prop_atoms
.net_wm_state_below
)
2781 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2782 prop_atoms
.net_wm_state_add
;
2783 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2784 action
= self
->demands_attention
?
2785 prop_atoms
.net_wm_state_remove
:
2786 prop_atoms
.net_wm_state_add
;
2787 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2788 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2789 prop_atoms
.net_wm_state_add
;
2792 if (action
== prop_atoms
.net_wm_state_add
) {
2793 if (state
== prop_atoms
.net_wm_state_modal
) {
2795 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2797 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2799 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2801 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2802 self
->skip_taskbar
= TRUE
;
2803 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2804 self
->skip_pager
= TRUE
;
2805 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2807 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2809 } else if (state
== prop_atoms
.net_wm_state_above
) {
2811 self
->below
= FALSE
;
2812 } else if (state
== prop_atoms
.net_wm_state_below
) {
2813 self
->above
= FALSE
;
2815 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2816 demands_attention
= TRUE
;
2817 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2821 } else { /* action == prop_atoms.net_wm_state_remove */
2822 if (state
== prop_atoms
.net_wm_state_modal
) {
2824 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2826 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2828 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2830 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2831 self
->skip_taskbar
= FALSE
;
2832 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2833 self
->skip_pager
= FALSE
;
2834 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2836 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2838 } else if (state
== prop_atoms
.net_wm_state_above
) {
2839 self
->above
= FALSE
;
2840 } else if (state
== prop_atoms
.net_wm_state_below
) {
2841 self
->below
= FALSE
;
2842 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2843 demands_attention
= FALSE
;
2844 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2845 undecorated
= FALSE
;
2849 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2850 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2852 if (max_horz
== max_vert
) { /* both going the same way */
2853 client_maximize(self
, max_horz
, 0, TRUE
);
2855 client_maximize(self
, max_horz
, 1, TRUE
);
2856 client_maximize(self
, max_vert
, 2, TRUE
);
2860 if (max_horz
!= self
->max_horz
)
2861 client_maximize(self
, max_horz
, 1, TRUE
);
2863 client_maximize(self
, max_vert
, 2, TRUE
);
2866 /* change fullscreen state before shading, as it will affect if the window
2868 if (fullscreen
!= self
->fullscreen
)
2869 client_fullscreen(self
, fullscreen
, TRUE
);
2870 if (shaded
!= self
->shaded
)
2871 client_shade(self
, shaded
);
2872 if (undecorated
!= self
->undecorated
)
2873 client_set_undecorated(self
, undecorated
);
2874 if (modal
!= self
->modal
) {
2875 self
->modal
= modal
;
2876 /* when a window changes modality, then its stacking order with its
2877 transients needs to change */
2880 if (iconic
!= self
->iconic
)
2881 client_iconify(self
, iconic
, FALSE
);
2883 if (demands_attention
!= self
->demands_attention
)
2884 client_hilite(self
, demands_attention
);
2886 client_change_state(self
); /* change the hint to reflect these changes */
2889 ObClient
*client_focus_target(ObClient
*self
)
2891 ObClient
*child
= NULL
;
2893 child
= client_search_modal_child(self
);
2894 if (child
) return child
;
2898 gboolean
client_can_focus(ObClient
*self
)
2902 /* choose the correct target */
2903 self
= client_focus_target(self
);
2905 if (!self
->frame
->visible
)
2908 if (!(self
->can_focus
|| self
->focus_notify
))
2911 /* do a check to see if the window has already been unmapped or destroyed
2912 do this intelligently while watching out for unmaps we've generated
2913 (ignore_unmaps > 0) */
2914 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2915 DestroyNotify
, &ev
)) {
2916 XPutBackEvent(ob_display
, &ev
);
2919 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2920 UnmapNotify
, &ev
)) {
2921 if (self
->ignore_unmaps
) {
2922 self
->ignore_unmaps
--;
2924 XPutBackEvent(ob_display
, &ev
);
2932 gboolean
client_focus(ObClient
*self
)
2934 /* choose the correct target */
2935 self
= client_focus_target(self
);
2937 if (!client_can_focus(self
)) {
2938 if (!self
->frame
->visible
) {
2939 /* update the focus lists */
2940 focus_order_to_top(self
);
2945 if (self
->can_focus
) {
2946 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2947 I choose to use it always, hopefully to find errors quicker, if any
2948 are left. (I hate X. I hate focus events.)
2950 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2951 #799. So now it is RevertToNone again.
2953 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2957 if (self
->focus_notify
) {
2959 ce
.xclient
.type
= ClientMessage
;
2960 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2961 ce
.xclient
.display
= ob_display
;
2962 ce
.xclient
.window
= self
->window
;
2963 ce
.xclient
.format
= 32;
2964 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2965 ce
.xclient
.data
.l
[1] = event_curtime
;
2966 ce
.xclient
.data
.l
[2] = 0l;
2967 ce
.xclient
.data
.l
[3] = 0l;
2968 ce
.xclient
.data
.l
[4] = 0l;
2969 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2973 ob_debug("%sively focusing %lx at %d\n",
2974 (self
->can_focus
? "act" : "pass"),
2975 self
->window
, (gint
) event_curtime
);
2978 /* Cause the FocusIn to come back to us. Important for desktop switches,
2979 since otherwise we'll have no FocusIn on the queue and send it off to
2980 the focus_backup. */
2981 XSync(ob_display
, FALSE
);
2985 /* Used when the current client is closed, focus_last will then prevent
2986 * focus from going to the mouse pointer */
2987 void client_unfocus(ObClient
*self
)
2989 if (focus_client
== self
) {
2991 ob_debug("client_unfocus for %lx\n", self
->window
);
2993 focus_fallback(OB_FOCUS_FALLBACK_CLOSED
);
2997 void client_activate(ObClient
*self
, gboolean here
, gboolean user
, Time time
)
2999 /* XXX do some stuff here if user is false to determine if we really want
3000 to activate it or not (a parent or group member is currently
3003 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3005 self
->window
, time
, client_last_user_time
,
3006 (user
? "user" : "application"));
3007 if (!user
&& time
&& time
< client_last_user_time
)
3008 client_hilite(self
, TRUE
);
3010 if (client_normal(self
) && screen_showing_desktop
)
3011 screen_show_desktop(FALSE
);
3013 client_iconify(self
, FALSE
, here
);
3014 if (self
->desktop
!= DESKTOP_ALL
&&
3015 self
->desktop
!= screen_desktop
) {
3017 client_set_desktop(self
, screen_desktop
, FALSE
);
3019 screen_set_desktop(self
->desktop
);
3020 } else if (!self
->frame
->visible
)
3021 /* if its not visible for other reasons, then don't mess
3025 client_shade(self
, FALSE
);
3029 /* we do this an action here. this is rather important. this is because
3030 we want the results from the focus change to take place BEFORE we go
3031 about raising the window. when a fullscreen window loses focus, we
3032 need this or else the raise wont be able to raise above the
3033 to-lose-focus fullscreen window. */
3038 void client_raise(ObClient
*self
)
3040 action_run_string("Raise", self
, CurrentTime
);
3043 void client_lower(ObClient
*self
)
3045 action_run_string("Lower", self
, CurrentTime
);
3048 gboolean
client_focused(ObClient
*self
)
3050 return self
== focus_client
;
3053 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3056 /* si is the smallest image >= req */
3057 /* li is the largest image < req */
3058 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3060 if (!self
->nicons
) {
3061 ObClientIcon
*parent
= NULL
;
3063 if (self
->transient_for
) {
3064 if (self
->transient_for
!= OB_TRAN_GROUP
)
3065 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3068 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3069 ObClient
*c
= it
->data
;
3070 if (c
!= self
&& !c
->transient_for
) {
3071 if ((parent
= client_icon_recursive(c
, w
, h
)))
3081 for (i
= 0; i
< self
->nicons
; ++i
) {
3082 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3083 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3087 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3092 if (largest
== 0) /* didnt find one smaller than the requested size */
3093 return &self
->icons
[si
];
3094 return &self
->icons
[li
];
3097 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3100 static ObClientIcon deficon
;
3102 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3103 deficon
.width
= deficon
.height
= 48;
3104 deficon
.data
= ob_rr_theme
->def_win_icon
;
3110 /* this be mostly ripped from fvwm */
3111 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3113 gint my_cx
, my_cy
, his_cx
, his_cy
;
3116 gint score
, best_score
;
3117 ObClient
*best_client
, *cur
;
3123 /* first, find the centre coords of the currently focused window */
3124 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3125 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3130 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3133 /* the currently selected window isn't interesting */
3136 if (!client_normal(cur
))
3138 /* using c->desktop instead of screen_desktop doesn't work if the
3139 * current window was omnipresent, hope this doesn't have any other
3141 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3145 if(!(client_focus_target(cur
) == cur
&&
3146 client_can_focus(cur
)))
3149 /* find the centre coords of this window, from the
3150 * currently focused window's point of view */
3151 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3152 + cur
->frame
->area
.width
/ 2;
3153 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3154 + cur
->frame
->area
.height
/ 2;
3156 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3157 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3159 /* Rotate the diagonals 45 degrees counterclockwise.
3160 * To do this, multiply the matrix /+h +h\ with the
3161 * vector (x y). \-h +h/
3162 * h = sqrt(0.5). We can set h := 1 since absolute
3163 * distance doesn't matter here. */
3164 tx
= his_cx
+ his_cy
;
3165 his_cy
= -his_cx
+ his_cy
;
3170 case OB_DIRECTION_NORTH
:
3171 case OB_DIRECTION_SOUTH
:
3172 case OB_DIRECTION_NORTHEAST
:
3173 case OB_DIRECTION_SOUTHWEST
:
3174 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3175 distance
= ((dir
== OB_DIRECTION_NORTH
||
3176 dir
== OB_DIRECTION_NORTHEAST
) ?
3179 case OB_DIRECTION_EAST
:
3180 case OB_DIRECTION_WEST
:
3181 case OB_DIRECTION_SOUTHEAST
:
3182 case OB_DIRECTION_NORTHWEST
:
3183 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3184 distance
= ((dir
== OB_DIRECTION_WEST
||
3185 dir
== OB_DIRECTION_NORTHWEST
) ?
3190 /* the target must be in the requested direction */
3194 /* Calculate score for this window. The smaller the better. */
3195 score
= distance
+ offset
;
3197 /* windows more than 45 degrees off the direction are
3198 * heavily penalized and will only be chosen if nothing
3199 * else within a million pixels */
3200 if(offset
> distance
)
3203 if(best_score
== -1 || score
< best_score
)
3211 void client_set_layer(ObClient
*self
, gint layer
)
3215 self
->above
= FALSE
;
3216 } else if (layer
== 0) {
3217 self
->below
= self
->above
= FALSE
;
3219 self
->below
= FALSE
;
3222 client_calc_layer(self
);
3223 client_change_state(self
); /* reflect this in the state hints */
3226 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3228 if (self
->undecorated
!= undecorated
) {
3229 self
->undecorated
= undecorated
;
3230 client_setup_decor_and_functions(self
);
3231 /* Make sure the client knows it might have moved. Maybe there is a
3232 * better way of doing this so only one client_configure is sent, but
3233 * since 125 of these are sent per second when moving the window (with
3234 * user = FALSE) i doubt it matters much.
3236 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3237 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3238 client_change_state(self
); /* reflect this in the state hints */
3242 guint
client_monitor(ObClient
*self
)
3244 return screen_find_monitor(&self
->frame
->area
);
3247 GSList
*client_search_top_transients(ObClient
*self
)
3251 /* move up the direct transient chain as far as possible */
3252 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3253 self
= self
->transient_for
;
3255 if (!self
->transient_for
)
3256 ret
= g_slist_prepend(ret
, self
);
3260 g_assert(self
->group
);
3262 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3263 ObClient
*c
= it
->data
;
3265 if (!c
->transient_for
)
3266 ret
= g_slist_prepend(ret
, c
);
3269 if (ret
== NULL
) /* no group parents */
3270 ret
= g_slist_prepend(ret
, self
);
3276 ObClient
*client_search_focus_parent(ObClient
*self
)
3278 if (self
->transient_for
) {
3279 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3280 if (client_focused(self
->transient_for
))
3281 return self
->transient_for
;
3285 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3286 ObClient
*c
= it
->data
;
3288 /* checking transient_for prevents infinate loops! */
3289 if (c
!= self
&& !c
->transient_for
)
3290 if (client_focused(c
))
3299 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3301 if (self
->transient_for
) {
3302 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3303 if (self
->transient_for
== search
)
3308 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3309 ObClient
*c
= it
->data
;
3311 /* checking transient_for prevents infinate loops! */
3312 if (c
!= self
&& !c
->transient_for
)
3322 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3326 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3327 if (sit
->data
== search
)
3329 if (client_search_transient(sit
->data
, search
))
3335 void client_update_sm_client_id(ObClient
*self
)
3337 g_free(self
->sm_client_id
);
3338 self
->sm_client_id
= NULL
;
3340 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3342 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3343 &self
->sm_client_id
);
3346 #define WANT_EDGE(cur, c) \
3349 if(!client_normal(cur)) \
3351 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3355 if(cur->layer < c->layer && !config_resist_layers_below) \
3358 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3359 if ((his_edge_start >= my_edge_start && \
3360 his_edge_start <= my_edge_end) || \
3361 (my_edge_start >= his_edge_start && \
3362 my_edge_start <= his_edge_end)) \
3365 /* finds the nearest edge in the given direction from the current client
3366 * note to self: the edge is the -frame- edge (the actual one), not the
3369 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3371 gint dest
, monitor_dest
;
3372 gint my_edge_start
, my_edge_end
, my_offset
;
3379 a
= screen_area(c
->desktop
);
3380 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3383 case OB_DIRECTION_NORTH
:
3384 my_edge_start
= c
->frame
->area
.x
;
3385 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3386 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3388 /* default: top of screen */
3389 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3390 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3391 /* if the monitor edge comes before the screen edge, */
3392 /* use that as the destination instead. (For xinerama) */
3393 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3394 dest
= monitor_dest
;
3396 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3397 gint his_edge_start
, his_edge_end
, his_offset
;
3398 ObClient
*cur
= it
->data
;
3402 his_edge_start
= cur
->frame
->area
.x
;
3403 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3404 his_offset
= cur
->frame
->area
.y
+
3405 (hang
? 0 : cur
->frame
->area
.height
);
3407 if(his_offset
+ 1 > my_offset
)
3410 if(his_offset
< dest
)
3413 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3416 case OB_DIRECTION_SOUTH
:
3417 my_edge_start
= c
->frame
->area
.x
;
3418 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3419 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3421 /* default: bottom of screen */
3422 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3423 monitor_dest
= monitor
->y
+ monitor
->height
-
3424 (hang
? c
->frame
->area
.height
: 0);
3425 /* if the monitor edge comes before the screen edge, */
3426 /* use that as the destination instead. (For xinerama) */
3427 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3428 dest
= monitor_dest
;
3430 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3431 gint his_edge_start
, his_edge_end
, his_offset
;
3432 ObClient
*cur
= it
->data
;
3436 his_edge_start
= cur
->frame
->area
.x
;
3437 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3438 his_offset
= cur
->frame
->area
.y
+
3439 (hang
? cur
->frame
->area
.height
: 0);
3442 if(his_offset
- 1 < my_offset
)
3445 if(his_offset
> dest
)
3448 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3451 case OB_DIRECTION_WEST
:
3452 my_edge_start
= c
->frame
->area
.y
;
3453 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3454 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3456 /* default: leftmost egde of screen */
3457 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3458 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3459 /* if the monitor edge comes before the screen edge, */
3460 /* use that as the destination instead. (For xinerama) */
3461 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3462 dest
= monitor_dest
;
3464 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3465 gint his_edge_start
, his_edge_end
, his_offset
;
3466 ObClient
*cur
= it
->data
;
3470 his_edge_start
= cur
->frame
->area
.y
;
3471 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3472 his_offset
= cur
->frame
->area
.x
+
3473 (hang
? 0 : cur
->frame
->area
.width
);
3475 if(his_offset
+ 1 > my_offset
)
3478 if(his_offset
< dest
)
3481 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3484 case OB_DIRECTION_EAST
:
3485 my_edge_start
= c
->frame
->area
.y
;
3486 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3487 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3489 /* default: rightmost edge of screen */
3490 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3491 monitor_dest
= monitor
->x
+ monitor
->width
-
3492 (hang
? c
->frame
->area
.width
: 0);
3493 /* if the monitor edge comes before the screen edge, */
3494 /* use that as the destination instead. (For xinerama) */
3495 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3496 dest
= monitor_dest
;
3498 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3499 gint his_edge_start
, his_edge_end
, his_offset
;
3500 ObClient
*cur
= it
->data
;
3504 his_edge_start
= cur
->frame
->area
.y
;
3505 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3506 his_offset
= cur
->frame
->area
.x
+
3507 (hang
? cur
->frame
->area
.width
: 0);
3509 if(his_offset
- 1 < my_offset
)
3512 if(his_offset
> dest
)
3515 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3518 case OB_DIRECTION_NORTHEAST
:
3519 case OB_DIRECTION_SOUTHEAST
:
3520 case OB_DIRECTION_NORTHWEST
:
3521 case OB_DIRECTION_SOUTHWEST
:
3522 /* not implemented */
3524 g_assert_not_reached();
3525 dest
= 0; /* suppress warning */
3530 ObClient
* client_under_pointer()
3534 ObClient
*ret
= NULL
;
3536 if (screen_pointer_pos(&x
, &y
)) {
3537 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3538 if (WINDOW_IS_CLIENT(it
->data
)) {
3539 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3540 if (c
->frame
->visible
&&
3541 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3551 gboolean
client_has_group_siblings(ObClient
*self
)
3553 return self
->group
&& self
->group
->members
->next
;