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-2007 Dana 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 | StructureNotifyMask)
50 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
55 ObClientDestructor func
;
59 GList
*client_list
= NULL
;
61 static GSList
*client_destructors
= NULL
;
63 static void client_get_all(ObClient
*self
);
64 static void client_toggle_border(ObClient
*self
, gboolean show
);
65 static void client_get_startup_id(ObClient
*self
);
66 static void client_get_area(ObClient
*self
);
67 static void client_get_desktop(ObClient
*self
);
68 static void client_get_state(ObClient
*self
);
69 static void client_get_layer(ObClient
*self
);
70 static void client_get_shaped(ObClient
*self
);
71 static void client_get_mwm_hints(ObClient
*self
);
72 static void client_get_gravity(ObClient
*self
);
73 static void client_change_allowed_actions(ObClient
*self
);
74 static void client_change_state(ObClient
*self
);
75 static void client_change_wm_state(ObClient
*self
);
76 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
77 static void client_restore_session_state(ObClient
*self
);
78 static void client_restore_session_stacking(ObClient
*self
);
79 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
80 static void client_unfocus(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 void client_manage(Window window
)
213 XWindowAttributes attrib
;
214 XSetWindowAttributes attrib_set
;
216 gboolean activate
= FALSE
;
217 ObAppSettings
*settings
;
222 /* check if it has already been unmapped by the time we started mapping.
223 the grab does a sync so we don't have to here */
224 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
225 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
227 XPutBackEvent(ob_display
, &e
);
229 ob_debug("Trying to manage unmapped window. Aborting that.\n");
231 return; /* don't manage it */
234 /* make sure it isn't an override-redirect window */
235 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
236 attrib
.override_redirect
)
239 return; /* don't manage it */
242 /* is the window a docking app */
243 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
244 if ((wmhint
->flags
& StateHint
) &&
245 wmhint
->initial_state
== WithdrawnState
)
247 dock_add(window
, wmhint
);
255 ob_debug("Managing window: %lx\n", window
);
257 /* choose the events we want to receive on the CLIENT window */
258 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
259 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
260 XChangeWindowAttributes(ob_display
, window
,
261 CWEventMask
|CWDontPropagate
, &attrib_set
);
264 /* create the ObClient struct, and populate it from the hints on the
266 self
= g_new0(ObClient
, 1);
267 self
->obwin
.type
= Window_Client
;
268 self
->window
= window
;
270 /* non-zero defaults */
271 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
273 self
->desktop
= screen_num_desktops
; /* always an invalid value */
274 self
->user_time
= CurrentTime
;
276 client_get_all(self
);
277 /* per-app settings override stuff, and return the settings for other
279 settings
= client_get_settings_state(self
);
280 /* the session should get the last say */
281 client_restore_session_state(self
);
283 client_calc_layer(self
);
286 Time t
= sn_app_started(self
->startup_id
, self
->class);
287 if (t
) self
->user_time
= t
;
290 /* update the focus lists, do this before the call to change_state or
291 it can end up in the list twice! */
292 focus_order_add_new(self
);
294 /* remove the client's border (and adjust re gravity) */
295 client_toggle_border(self
, FALSE
);
297 /* specify that if we exit, the window should not be destroyed and should
298 be reparented back to root automatically */
299 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
301 /* create the decoration frame for the client window */
302 self
->frame
= frame_new(self
);
304 frame_grab_client(self
->frame
, self
);
306 /* do this after we have a frame.. it uses the frame to help determine the
307 WM_STATE to apply. */
308 client_change_state(self
);
312 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
313 client_restore_session_stacking(self
);
315 /* focus the new window? */
316 if (ob_state() != OB_STATE_STARTING
&&
317 /* this means focus=true for window is same as config_focus_new=true */
318 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
319 client_search_focus_parent(self
)) &&
320 /* this checks for focus=false for the window */
321 (!settings
|| settings
->focus
!= 0) &&
322 /* note the check against Type_Normal/Dialog, not client_normal(self),
323 which would also include other types. in this case we want more
324 strict rules for focus */
325 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
326 self
->type
== OB_CLIENT_TYPE_DIALOG
))
330 if (self
->desktop
!= screen_desktop
) {
331 /* activate the window */
334 gboolean group_foc
= FALSE
;
339 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
341 if (client_focused(it
->data
))
349 (!self
->transient_for
&& (!self
->group
||
350 !self
->group
->members
->next
))) ||
351 client_search_focus_tree_full(self
) ||
353 !client_normal(focus_client
))
355 /* activate the window */
362 /* get the current position */
366 /* figure out placement for the window */
367 if (ob_state() == OB_STATE_RUNNING
) {
370 transient
= place_client(self
, &newx
, &newy
, settings
);
372 /* make sure the window is visible. */
373 client_find_onscreen(self
, &newx
, &newy
,
374 self
->frame
->area
.width
,
375 self
->frame
->area
.height
,
376 /* non-normal clients has less rules, and
377 windows that are being restored from a
378 session do also. we can assume you want
379 it back where you saved it. Clients saying
380 they placed themselves are subjected to
381 harder rules, ones that are placed by
382 place.c or by the user are allowed partially
383 off-screen and on xinerama divides (ie,
384 it is up to the placement routines to avoid
385 the xinerama divides) */
387 (((self
->positioned
& PPosition
) &&
388 !(self
->positioned
& USPosition
)) &&
389 client_normal(self
) &&
393 /* do this after the window is placed, so the premax/prefullscreen numbers
395 also, this moves the window to the position where it has been placed
397 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
398 self
->window
, newx
, newy
, self
->area
.width
, self
->area
.height
);
399 client_apply_startup_state(self
, newx
, newy
);
401 keyboard_grab_for_client(self
, TRUE
);
402 mouse_grab_for_client(self
, TRUE
);
405 guint32 last_time
= focus_client
?
406 focus_client
->user_time
: CurrentTime
;
408 /* This is focus stealing prevention */
409 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
410 self
->window
, self
->user_time
, last_time
);
412 /* If a nothing at all, or a parent was focused, then focus this
415 if (!focus_client
|| client_search_focus_parent(self
) != NULL
)
419 /* If time stamp is old, don't steal focus */
420 if (self
->user_time
&& last_time
&&
421 !event_time_after(self
->user_time
, last_time
))
425 /* Don't steal focus from globally active clients.
426 I stole this idea from KWin. It seems nice.
428 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
))
434 /* since focus can change the stacking orders, if we focus the
435 window then the standard raise it gets is not enough, we need
436 to queue one for after the focus change takes place */
439 ob_debug("Focus stealing prevention activated for %s with time %u "
441 self
->title
, self
->user_time
, last_time
);
442 /* if the client isn't focused, then hilite it so the user
444 client_hilite(self
, TRUE
);
448 /* This may look rather odd. Well it's because new windows are added
449 to the stacking order non-intrusively. If we're not going to focus
450 the new window or hilite it, then we raise it to the top. This will
451 take affect for things that don't get focused like splash screens.
452 Also if you don't have focus_new enabled, then it's going to get
453 raised to the top. Legacy begets legacy I guess?
458 /* this has to happen before we try focus the window, but we want it to
459 happen after the client's stacking has been determined or it looks bad
463 /* use client_focus instead of client_activate cuz client_activate does
464 stuff like switch desktops etc and I'm not interested in all that when
465 a window maps since its not based on an action from the user like
466 clicking a window to activate it. so keep the new window out of the way
469 /* if using focus_delay, stop the timer now so that focus doesn't
471 event_halt_focus_delay();
475 /* client_activate does this but we aret using it so we have to do it
477 if (screen_showing_desktop
)
478 screen_show_desktop(FALSE
);
480 /* add to client list/map */
481 client_list
= g_list_append(client_list
, self
);
482 g_hash_table_insert(window_map
, &self
->window
, self
);
484 /* this has to happen after we're in the client_list */
485 if (STRUT_EXISTS(self
->strut
))
486 screen_update_areas();
488 /* update the list hints */
491 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
494 void client_unmanage_all()
496 while (client_list
!= NULL
)
497 client_unmanage(client_list
->data
);
500 void client_unmanage(ObClient
*self
)
505 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
506 self
->title
? self
->title
: "");
508 g_assert(self
!= NULL
);
510 /* update the focus lists */
511 focus_order_remove(self
);
513 if (focus_client
== self
) {
516 /* focus the last focused window on the desktop, and ignore enter
517 events from the unmap so it doesnt mess with the focus */
518 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
519 /* remove these flags so we don't end up getting focused in the
521 self
->can_focus
= FALSE
;
522 self
->focus_notify
= FALSE
;
524 client_unfocus(self
);
527 /* potentially fix focusLast */
528 if (config_focus_last
)
529 grab_pointer(TRUE
, OB_CURSOR_NONE
);
531 frame_hide(self
->frame
);
534 keyboard_grab_for_client(self
, FALSE
);
535 mouse_grab_for_client(self
, FALSE
);
537 /* remove the window from our save set */
538 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
540 /* we dont want events no more */
541 XSelectInput(ob_display
, self
->window
, NoEventMask
);
543 client_list
= g_list_remove(client_list
, self
);
544 stacking_remove(self
);
545 g_hash_table_remove(window_map
, &self
->window
);
547 /* once the client is out of the list, update the struts to remove its
549 if (STRUT_EXISTS(self
->strut
))
550 screen_update_areas();
552 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
553 Destructor
*d
= it
->data
;
554 d
->func(self
, d
->data
);
557 /* tell our parent(s) that we're gone */
558 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
559 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
560 if (it
->data
!= self
)
561 ((ObClient
*)it
->data
)->transients
=
562 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
563 } else if (self
->transient_for
) { /* transient of window */
564 self
->transient_for
->transients
=
565 g_slist_remove(self
->transient_for
->transients
, self
);
568 /* tell our transients that we're gone */
569 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
570 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
571 ((ObClient
*)it
->data
)->transient_for
= NULL
;
572 client_calc_layer(it
->data
);
576 /* remove from its group */
578 group_remove(self
->group
, self
);
582 /* restore the window's original geometry so it is not lost */
586 if (self
->fullscreen
)
587 a
= self
->pre_fullscreen_area
;
588 else if (self
->max_horz
|| self
->max_vert
) {
589 if (self
->max_horz
) {
590 a
.x
= self
->pre_max_area
.x
;
591 a
.width
= self
->pre_max_area
.width
;
593 if (self
->max_vert
) {
594 a
.y
= self
->pre_max_area
.y
;
595 a
.height
= self
->pre_max_area
.height
;
599 /* give the client its border back */
600 client_toggle_border(self
, TRUE
);
602 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
603 self
->decorations
= 0; /* unmanaged windows have no decor */
605 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
608 /* reparent the window out of the frame, and free the frame */
609 frame_release_client(self
->frame
, self
);
612 if (ob_state() != OB_STATE_EXITING
) {
613 /* these values should not be persisted across a window
615 PROP_ERASE(self
->window
, net_wm_desktop
);
616 PROP_ERASE(self
->window
, net_wm_state
);
617 PROP_ERASE(self
->window
, wm_state
);
619 /* if we're left in an unmapped state, the client wont be mapped. this
620 is bad, since we will no longer be managing the window on restart */
621 XMapWindow(ob_display
, self
->window
);
624 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
626 /* free all data allocated in the client struct */
627 g_slist_free(self
->transients
);
628 for (j
= 0; j
< self
->nicons
; ++j
)
629 g_free(self
->icons
[j
].data
);
630 if (self
->nicons
> 0)
633 g_free(self
->icon_title
);
637 g_free(self
->sm_client_id
);
640 /* update the list hints */
643 if (config_focus_last
)
644 grab_pointer(FALSE
, OB_CURSOR_NONE
);
647 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
649 ObAppSettings
*settings
= NULL
;
652 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
653 ObAppSettings
*app
= it
->data
;
655 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
656 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
657 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
658 && !strcmp(app
->name
, self
->name
)))
660 ob_debug("Window matching: %s\n", app
->name
);
661 /* Match if no role was specified in the per app setting, or if the
662 * string matches the beginning of the role, since apps like to set
663 * the role to things like browser-window-23c4b2f */
665 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
675 if (settings
->shade
!= -1)
676 self
->shaded
= !!settings
->shade
;
677 if (settings
->decor
!= -1)
678 self
->undecorated
= !settings
->decor
;
679 if (settings
->iconic
!= -1)
680 self
->iconic
= !!settings
->iconic
;
681 if (settings
->skip_pager
!= -1)
682 self
->skip_pager
= !!settings
->skip_pager
;
683 if (settings
->skip_taskbar
!= -1)
684 self
->skip_taskbar
= !!settings
->skip_taskbar
;
686 if (settings
->max_vert
!= -1)
687 self
->max_vert
= !!settings
->max_vert
;
688 if (settings
->max_horz
!= -1)
689 self
->max_vert
= !!settings
->max_horz
;
691 if (settings
->fullscreen
!= -1)
692 self
->fullscreen
= !!settings
->fullscreen
;
694 if (settings
->desktop
< screen_num_desktops
695 || settings
->desktop
== DESKTOP_ALL
)
696 self
->desktop
= settings
->desktop
;
698 if (settings
->layer
== -1) {
702 else if (settings
->layer
== 0) {
706 else if (settings
->layer
== 1) {
714 static void client_restore_session_state(ObClient
*self
)
718 if (!(it
= session_state_find(self
)))
721 self
->session
= it
->data
;
723 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
724 self
->positioned
= PPosition
;
725 if (self
->session
->w
> 0)
726 self
->area
.width
= self
->session
->w
;
727 if (self
->session
->h
> 0)
728 self
->area
.height
= self
->session
->h
;
729 XResizeWindow(ob_display
, self
->window
,
730 self
->area
.width
, self
->area
.height
);
732 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
733 self
->session
->desktop
:
734 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
735 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
737 self
->shaded
= self
->session
->shaded
;
738 self
->iconic
= self
->session
->iconic
;
739 self
->skip_pager
= self
->session
->skip_pager
;
740 self
->skip_taskbar
= self
->session
->skip_taskbar
;
741 self
->fullscreen
= self
->session
->fullscreen
;
742 self
->above
= self
->session
->above
;
743 self
->below
= self
->session
->below
;
744 self
->max_horz
= self
->session
->max_horz
;
745 self
->max_vert
= self
->session
->max_vert
;
748 static void client_restore_session_stacking(ObClient
*self
)
752 if (!self
->session
) return;
754 it
= g_list_find(session_saved_state
, self
->session
);
755 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
758 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
759 if (session_state_cmp(it
->data
, cit
->data
))
762 client_calc_layer(self
);
763 stacking_below(CLIENT_AS_WINDOW(self
),
764 CLIENT_AS_WINDOW(cit
->data
));
770 void client_move_onscreen(ObClient
*self
, gboolean rude
)
772 gint x
= self
->area
.x
;
773 gint y
= self
->area
.y
;
774 if (client_find_onscreen(self
, &x
, &y
,
775 self
->frame
->area
.width
,
776 self
->frame
->area
.height
, rude
)) {
777 client_move(self
, x
, y
);
781 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
785 gint ox
= *x
, oy
= *y
;
787 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
790 /* XXX watch for xinerama dead areas */
791 /* This makes sure windows aren't entirely outside of the screen so you
792 can't see them at all.
793 It makes sure 10% of the window is on the screen at least. At don't let
794 it move itself off the top of the screen, which would hide the titlebar
795 on you. (The user can still do this if they want too, it's only limiting
798 if (client_normal(self
)) {
799 a
= screen_area(self
->desktop
);
800 if (!self
->strut
.right
&&
801 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
802 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
803 if (!self
->strut
.bottom
&&
804 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
805 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
806 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
807 *x
= a
->x
- self
->frame
->area
.width
*9/10;
808 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
809 *y
= a
->y
- self
->frame
->area
.width
*9/10;
812 /* This here doesn't let windows even a pixel outside the screen,
813 * when called from client_manage, programs placing themselves are
814 * forced completely onscreen, while things like
815 * xterm -geometry resolution-width/2 will work fine. Trying to
816 * place it completely offscreen will be handled in the above code.
817 * Sorry for this confused comment, i am tired. */
819 /* avoid the xinerama monitor divide while we're at it,
820 * remember to fix the placement stuff to avoid it also and
821 * then remove this XXX */
822 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
823 /* dont let windows map into the strut unless they
824 are bigger than the available area */
826 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
827 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
828 *x
= a
->x
+ a
->width
- w
;
830 if (h
<= a
->height
) {
831 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
832 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
833 *y
= a
->y
+ a
->height
- h
;
837 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
840 return ox
!= *x
|| oy
!= *y
;
843 static void client_toggle_border(ObClient
*self
, gboolean show
)
845 /* adjust our idea of where the client is, based on its border. When the
846 border is removed, the client should now be considered to be in a
848 when re-adding the border to the client, the same operation needs to be
850 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
851 gint x
= oldx
, y
= oldy
;
852 switch(self
->gravity
) {
854 case NorthWestGravity
:
856 case SouthWestGravity
:
858 case NorthEastGravity
:
860 case SouthEastGravity
:
861 if (show
) x
-= self
->border_width
* 2;
862 else x
+= self
->border_width
* 2;
869 if (show
) x
-= self
->border_width
;
870 else x
+= self
->border_width
;
873 switch(self
->gravity
) {
875 case NorthWestGravity
:
877 case NorthEastGravity
:
879 case SouthWestGravity
:
881 case SouthEastGravity
:
882 if (show
) y
-= self
->border_width
* 2;
883 else y
+= self
->border_width
* 2;
890 if (show
) y
-= self
->border_width
;
891 else y
+= self
->border_width
;
898 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
900 /* set border_width to 0 because there is no border to add into
901 calculations anymore */
902 self
->border_width
= 0;
904 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
908 static void client_get_all(ObClient
*self
)
910 client_get_area(self
);
911 client_get_mwm_hints(self
);
913 /* The transient hint is used to pick a type, but the type can also affect
914 transiency (dialogs are always made transients of their group if they
915 have one). This is Havoc's idea, but it is needed to make some apps
916 work right (eg tsclient). */
917 client_update_transient_for(self
);
918 client_get_type(self
);/* this can change the mwmhints for special cases */
919 client_get_state(self
);
920 client_update_transient_for(self
);
922 client_update_wmhints(self
);
923 client_get_startup_id(self
);
924 client_get_desktop(self
);/* uses transient data/group/startup id if a
925 desktop is not specified */
926 client_get_shaped(self
);
928 client_get_layer(self
); /* if layer hasn't been specified, get it from
929 other sources if possible */
932 /* a couple type-based defaults for new windows */
934 /* this makes sure that these windows appear on all desktops */
935 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
936 self
->desktop
= DESKTOP_ALL
;
939 client_update_protocols(self
);
941 client_get_gravity(self
); /* get the attribute gravity */
942 client_update_normal_hints(self
); /* this may override the attribute
945 /* got the type, the mwmhints, the protocols, and the normal hints
946 (min/max sizes), so we're ready to set up the decorations/functions */
947 client_setup_decor_and_functions(self
);
949 client_update_title(self
);
950 client_update_class(self
);
951 client_update_sm_client_id(self
);
952 client_update_strut(self
);
953 client_update_icons(self
);
954 client_update_user_time(self
);
957 static void client_get_startup_id(ObClient
*self
)
959 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
961 PROP_GETS(self
->group
->leader
,
962 net_startup_id
, utf8
, &self
->startup_id
);
965 static void client_get_area(ObClient
*self
)
967 XWindowAttributes wattrib
;
970 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
971 g_assert(ret
!= BadWindow
);
973 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
974 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
975 self
->border_width
= wattrib
.border_width
;
977 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
978 wattrib
.width
, wattrib
.height
);
981 static void client_get_desktop(ObClient
*self
)
983 guint32 d
= screen_num_desktops
; /* an always-invalid value */
985 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
986 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
987 self
->desktop
= screen_num_desktops
- 1;
991 gboolean trdesk
= FALSE
;
993 if (self
->transient_for
) {
994 if (self
->transient_for
!= OB_TRAN_GROUP
) {
995 self
->desktop
= self
->transient_for
->desktop
;
1000 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1001 if (it
->data
!= self
&&
1002 !((ObClient
*)it
->data
)->transient_for
) {
1003 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
1010 /* try get from the startup-notification protocol */
1011 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1012 if (self
->desktop
>= screen_num_desktops
&&
1013 self
->desktop
!= DESKTOP_ALL
)
1014 self
->desktop
= screen_num_desktops
- 1;
1016 /* defaults to the current desktop */
1017 self
->desktop
= screen_desktop
;
1020 if (self
->desktop
!= d
) {
1021 /* set the desktop hint, to make sure that it always exists */
1022 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
1026 static void client_get_layer(ObClient
*self
)
1028 if (!(self
->above
|| self
->below
)) {
1030 /* apply stuff from the group */
1034 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1035 ObClient
*c
= it
->data
;
1036 if (c
!= self
&& !client_search_transient(self
, c
) &&
1037 client_normal(self
) && client_normal(c
))
1040 (c
->above
? 1 : (c
->below
? -1 : 0)));
1054 g_assert_not_reached();
1061 static void client_get_state(ObClient
*self
)
1066 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1068 for (i
= 0; i
< num
; ++i
) {
1069 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1071 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1072 self
->shaded
= TRUE
;
1073 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1074 self
->iconic
= TRUE
;
1075 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1076 self
->skip_taskbar
= TRUE
;
1077 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1078 self
->skip_pager
= TRUE
;
1079 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1080 self
->fullscreen
= TRUE
;
1081 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1082 self
->max_vert
= TRUE
;
1083 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1084 self
->max_horz
= TRUE
;
1085 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1087 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1089 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1090 self
->demands_attention
= TRUE
;
1091 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1092 self
->undecorated
= TRUE
;
1099 static void client_get_shaped(ObClient
*self
)
1101 self
->shaped
= FALSE
;
1103 if (extensions_shape
) {
1108 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1110 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1111 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1113 self
->shaped
= (s
!= 0);
1118 void client_update_transient_for(ObClient
*self
)
1121 ObClient
*target
= NULL
;
1123 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1124 self
->transient
= TRUE
;
1125 if (t
!= self
->window
) { /* cant be transient to itself! */
1126 target
= g_hash_table_lookup(window_map
, &t
);
1127 /* if this happens then we need to check for it*/
1128 g_assert(target
!= self
);
1129 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1130 /* this can happen when a dialog is a child of
1131 a dockapp, for example */
1135 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1137 Setting the transient_for to Root is actually illegal, however
1138 applications from time have done this to specify transient for
1141 Now you can do that by being a TYPE_DIALOG and not setting
1142 the transient_for hint at all on your window. But people still
1143 use Root, and Kwin is very strange in this regard.
1145 KWin 3.0 will not consider windows with transient_for set to
1146 Root as transient for their group *UNLESS* they are also modal.
1147 In that case, it will make them transient for the group. This
1148 leads to all sorts of weird behavior from KDE apps which are
1149 only tested in KWin. I'd like to follow their behavior just to
1150 make this work right with KDE stuff, but that seems wrong.
1152 if (!target
&& self
->group
) {
1153 /* not transient to a client, see if it is transient for a
1155 if (t
== RootWindow(ob_display
, ob_screen
)) {
1156 /* window is a transient for its group! */
1157 target
= OB_TRAN_GROUP
;
1161 } else if (self
->group
) {
1162 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1163 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1164 self
->type
== OB_CLIENT_TYPE_MENU
||
1165 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1167 self
->transient
= TRUE
;
1168 target
= OB_TRAN_GROUP
;
1171 self
->transient
= FALSE
;
1173 /* if anything has changed... */
1174 if (target
!= self
->transient_for
) {
1175 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1178 /* remove from old parents */
1179 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1180 ObClient
*c
= it
->data
;
1181 if (c
!= self
&& !c
->transient_for
)
1182 c
->transients
= g_slist_remove(c
->transients
, self
);
1184 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1185 /* remove from old parent */
1186 self
->transient_for
->transients
=
1187 g_slist_remove(self
->transient_for
->transients
, self
);
1189 self
->transient_for
= target
;
1190 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1193 /* add to new parents */
1194 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1195 ObClient
*c
= it
->data
;
1196 if (c
!= self
&& !c
->transient_for
)
1197 c
->transients
= g_slist_append(c
->transients
, self
);
1200 /* remove all transients which are in the group, that causes
1201 circlular pointer hell of doom */
1202 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1204 for (sit
= self
->transients
; sit
; sit
= next
) {
1205 next
= g_slist_next(sit
);
1206 if (sit
->data
== it
->data
)
1208 g_slist_delete_link(self
->transients
, sit
);
1211 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1212 /* add to new parent */
1213 self
->transient_for
->transients
=
1214 g_slist_append(self
->transient_for
->transients
, self
);
1219 static void client_get_mwm_hints(ObClient
*self
)
1224 self
->mwmhints
.flags
= 0; /* default to none */
1226 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1228 if (num
>= OB_MWM_ELEMENTS
) {
1229 self
->mwmhints
.flags
= hints
[0];
1230 self
->mwmhints
.functions
= hints
[1];
1231 self
->mwmhints
.decorations
= hints
[2];
1237 void client_get_type(ObClient
*self
)
1244 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1245 /* use the first value that we know about in the array */
1246 for (i
= 0; i
< num
; ++i
) {
1247 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1248 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1249 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1250 self
->type
= OB_CLIENT_TYPE_DOCK
;
1251 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1252 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1253 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1254 self
->type
= OB_CLIENT_TYPE_MENU
;
1255 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1256 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1257 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1258 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1259 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1260 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1261 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1262 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1263 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1264 /* prevent this window from getting any decor or
1266 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1267 OB_MWM_FLAG_DECORATIONS
);
1268 self
->mwmhints
.decorations
= 0;
1269 self
->mwmhints
.functions
= 0;
1271 if (self
->type
!= (ObClientType
) -1)
1272 break; /* grab the first legit type */
1277 if (self
->type
== (ObClientType
) -1) {
1278 /*the window type hint was not set, which means we either classify
1279 ourself as a normal window or a dialog, depending on if we are a
1281 if (self
->transient
)
1282 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1284 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1288 void client_update_protocols(ObClient
*self
)
1291 guint num_return
, i
;
1293 self
->focus_notify
= FALSE
;
1294 self
->delete_window
= FALSE
;
1296 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1297 for (i
= 0; i
< num_return
; ++i
) {
1298 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1299 /* this means we can request the window to close */
1300 self
->delete_window
= TRUE
;
1301 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1302 /* if this protocol is requested, then the window will be
1303 notified whenever we want it to receive focus */
1304 self
->focus_notify
= TRUE
;
1310 static void client_get_gravity(ObClient
*self
)
1312 XWindowAttributes wattrib
;
1315 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1316 g_assert(ret
!= BadWindow
);
1317 self
->gravity
= wattrib
.win_gravity
;
1320 void client_update_normal_hints(ObClient
*self
)
1324 gint oldgravity
= self
->gravity
;
1327 self
->min_ratio
= 0.0f
;
1328 self
->max_ratio
= 0.0f
;
1329 SIZE_SET(self
->size_inc
, 1, 1);
1330 SIZE_SET(self
->base_size
, 0, 0);
1331 SIZE_SET(self
->min_size
, 0, 0);
1332 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1334 /* get the hints from the window */
1335 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1336 /* normal windows can't request placement! har har
1337 if (!client_normal(self))
1339 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1341 if (size
.flags
& PWinGravity
) {
1342 self
->gravity
= size
.win_gravity
;
1344 /* if the client has a frame, i.e. has already been mapped and
1345 is changing its gravity */
1346 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1347 /* move our idea of the client's position based on its new
1349 self
->area
.x
= self
->frame
->area
.x
;
1350 self
->area
.y
= self
->frame
->area
.y
;
1351 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1355 if (size
.flags
& PAspect
) {
1356 if (size
.min_aspect
.y
)
1358 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1359 if (size
.max_aspect
.y
)
1361 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1364 if (size
.flags
& PMinSize
)
1365 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1367 if (size
.flags
& PMaxSize
)
1368 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1370 if (size
.flags
& PBaseSize
)
1371 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1373 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1374 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1378 void client_setup_decor_and_functions(ObClient
*self
)
1380 /* start with everything (cept fullscreen) */
1382 (OB_FRAME_DECOR_TITLEBAR
|
1383 OB_FRAME_DECOR_HANDLE
|
1384 OB_FRAME_DECOR_GRIPS
|
1385 OB_FRAME_DECOR_BORDER
|
1386 OB_FRAME_DECOR_ICON
|
1387 OB_FRAME_DECOR_ALLDESKTOPS
|
1388 OB_FRAME_DECOR_ICONIFY
|
1389 OB_FRAME_DECOR_MAXIMIZE
|
1390 OB_FRAME_DECOR_SHADE
|
1391 OB_FRAME_DECOR_CLOSE
);
1393 (OB_CLIENT_FUNC_RESIZE
|
1394 OB_CLIENT_FUNC_MOVE
|
1395 OB_CLIENT_FUNC_ICONIFY
|
1396 OB_CLIENT_FUNC_MAXIMIZE
|
1397 OB_CLIENT_FUNC_SHADE
|
1398 OB_CLIENT_FUNC_CLOSE
);
1400 if (!(self
->min_size
.width
< self
->max_size
.width
||
1401 self
->min_size
.height
< self
->max_size
.height
))
1402 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1404 switch (self
->type
) {
1405 case OB_CLIENT_TYPE_NORMAL
:
1406 /* normal windows retain all of the possible decorations and
1407 functionality, and are the only windows that you can fullscreen */
1408 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1411 case OB_CLIENT_TYPE_DIALOG
:
1412 case OB_CLIENT_TYPE_UTILITY
:
1413 /* these windows cannot be maximized */
1414 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1417 case OB_CLIENT_TYPE_MENU
:
1418 case OB_CLIENT_TYPE_TOOLBAR
:
1419 /* these windows get less functionality */
1420 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1423 case OB_CLIENT_TYPE_DESKTOP
:
1424 case OB_CLIENT_TYPE_DOCK
:
1425 case OB_CLIENT_TYPE_SPLASH
:
1426 /* none of these windows are manipulated by the window manager */
1427 self
->decorations
= 0;
1428 self
->functions
= 0;
1432 /* Mwm Hints are applied subtractively to what has already been chosen for
1433 decor and functionality */
1434 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1435 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1436 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1437 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1439 /* if the mwm hints request no handle or title, then all
1440 decorations are disabled, but keep the border if that's
1442 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1443 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1445 self
->decorations
= 0;
1450 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1451 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1452 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1453 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1454 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1455 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1456 /* dont let mwm hints kill any buttons
1457 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1458 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1459 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1460 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1462 /* dont let mwm hints kill the close button
1463 if (! (self->mwmhints.functions & MwmFunc_Close))
1464 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1468 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1469 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1470 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1471 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1472 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1473 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1475 /* can't maximize without moving/resizing */
1476 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1477 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1478 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1479 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1480 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1483 /* kill the handle on fully maxed windows */
1484 if (self
->max_vert
&& self
->max_horz
)
1485 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1487 /* finally, the user can have requested no decorations, which overrides
1488 everything (but doesnt give it a border if it doesnt have one) */
1489 if (self
->undecorated
) {
1490 if (config_theme_keepborder
)
1491 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1493 self
->decorations
= 0;
1496 /* if we don't have a titlebar, then we cannot shade! */
1497 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1498 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1500 /* now we need to check against rules for the client's current state */
1501 if (self
->fullscreen
) {
1502 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1503 OB_CLIENT_FUNC_FULLSCREEN
|
1504 OB_CLIENT_FUNC_ICONIFY
);
1505 self
->decorations
= 0;
1508 client_change_allowed_actions(self
);
1511 /* adjust the client's decorations, etc. */
1512 client_reconfigure(self
);
1516 static void client_change_allowed_actions(ObClient
*self
)
1521 /* desktop windows are kept on all desktops */
1522 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1523 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1525 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1526 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1527 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1528 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1529 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1530 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1531 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1532 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1533 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1534 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1535 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1536 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1537 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1538 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1539 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1542 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1544 /* make sure the window isn't breaking any rules now */
1546 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1547 if (self
->frame
) client_shade(self
, FALSE
);
1548 else self
->shaded
= FALSE
;
1550 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1551 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1552 else self
->iconic
= FALSE
;
1554 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1555 if (self
->frame
) client_fullscreen(self
, FALSE
);
1556 else self
->fullscreen
= FALSE
;
1558 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1560 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1561 else self
->max_vert
= self
->max_horz
= FALSE
;
1565 void client_reconfigure(ObClient
*self
)
1567 /* by making this pass FALSE for user, we avoid the emacs event storm where
1568 every configurenotify causes an update in its normal hints, i think this
1569 is generally what we want anyways... */
1570 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1571 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1574 void client_update_wmhints(ObClient
*self
)
1579 /* assume a window takes input if it doesnt specify */
1580 self
->can_focus
= TRUE
;
1582 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1583 if (hints
->flags
& InputHint
)
1584 self
->can_focus
= hints
->input
;
1586 /* only do this when first managing the window *AND* when we aren't
1588 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1589 if (hints
->flags
& StateHint
)
1590 self
->iconic
= hints
->initial_state
== IconicState
;
1592 if (!(hints
->flags
& WindowGroupHint
))
1593 hints
->window_group
= None
;
1595 /* did the group state change? */
1596 if (hints
->window_group
!=
1597 (self
->group
? self
->group
->leader
: None
)) {
1598 /* remove from the old group if there was one */
1599 if (self
->group
!= NULL
) {
1600 /* remove transients of the group */
1601 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1602 self
->transients
= g_slist_remove(self
->transients
,
1605 /* remove myself from parents in the group */
1606 if (self
->transient_for
== OB_TRAN_GROUP
) {
1607 for (it
= self
->group
->members
; it
;
1608 it
= g_slist_next(it
))
1610 ObClient
*c
= it
->data
;
1612 if (c
!= self
&& !c
->transient_for
)
1613 c
->transients
= g_slist_remove(c
->transients
,
1618 group_remove(self
->group
, self
);
1621 if (hints
->window_group
!= None
) {
1622 self
->group
= group_add(hints
->window_group
, self
);
1624 /* i can only have transients from the group if i am not
1626 if (!self
->transient_for
) {
1627 /* add other transients of the group that are already
1629 for (it
= self
->group
->members
; it
;
1630 it
= g_slist_next(it
))
1632 ObClient
*c
= it
->data
;
1633 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1635 g_slist_append(self
->transients
, c
);
1640 /* because the self->transient flag wont change from this call,
1641 we don't need to update the window's type and such, only its
1642 transient_for, and the transients lists of other windows in
1643 the group may be affected */
1644 client_update_transient_for(self
);
1647 /* the WM_HINTS can contain an icon */
1648 client_update_icons(self
);
1654 void client_update_title(ObClient
*self
)
1658 g_free(self
->title
);
1661 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1662 /* try old x stuff */
1663 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1664 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1665 if (self
->transient
) {
1667 GNOME alert windows are not given titles:
1668 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1670 data
= g_strdup("");
1672 data
= g_strdup("Unnamed Window");
1676 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1680 frame_adjust_title(self
->frame
);
1682 /* update the icon title */
1684 g_free(self
->icon_title
);
1687 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1688 /* try old x stuff */
1689 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1690 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1691 data
= g_strdup(self
->title
);
1693 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1694 self
->icon_title
= data
;
1697 void client_update_class(ObClient
*self
)
1702 if (self
->name
) g_free(self
->name
);
1703 if (self
->class) g_free(self
->class);
1704 if (self
->role
) g_free(self
->role
);
1706 self
->name
= self
->class = self
->role
= NULL
;
1708 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1710 self
->name
= g_strdup(data
[0]);
1712 self
->class = g_strdup(data
[1]);
1717 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1720 if (self
->name
== NULL
) self
->name
= g_strdup("");
1721 if (self
->class == NULL
) self
->class = g_strdup("");
1722 if (self
->role
== NULL
) self
->role
= g_strdup("");
1725 void client_update_strut(ObClient
*self
)
1729 gboolean got
= FALSE
;
1732 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1736 STRUT_PARTIAL_SET(strut
,
1737 data
[0], data
[2], data
[1], data
[3],
1738 data
[4], data
[5], data
[8], data
[9],
1739 data
[6], data
[7], data
[10], data
[11]);
1745 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1751 /* use the screen's width/height */
1752 a
= screen_physical_area();
1754 STRUT_PARTIAL_SET(strut
,
1755 data
[0], data
[2], data
[1], data
[3],
1756 a
->y
, a
->y
+ a
->height
- 1,
1757 a
->x
, a
->x
+ a
->width
- 1,
1758 a
->y
, a
->y
+ a
->height
- 1,
1759 a
->x
, a
->x
+ a
->width
- 1);
1765 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1766 0, 0, 0, 0, 0, 0, 0, 0);
1768 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1769 self
->strut
= strut
;
1771 /* updating here is pointless while we're being mapped cuz we're not in
1772 the client list yet */
1774 screen_update_areas();
1778 void client_update_icons(ObClient
*self
)
1784 for (i
= 0; i
< self
->nicons
; ++i
)
1785 g_free(self
->icons
[i
].data
);
1786 if (self
->nicons
> 0)
1787 g_free(self
->icons
);
1790 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1791 /* figure out how many valid icons are in here */
1793 while (num
- i
> 2) {
1797 if (i
> num
|| w
*h
== 0) break;
1801 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1803 /* store the icons */
1805 for (j
= 0; j
< self
->nicons
; ++j
) {
1808 w
= self
->icons
[j
].width
= data
[i
++];
1809 h
= self
->icons
[j
].height
= data
[i
++];
1811 if (w
*h
== 0) continue;
1813 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1814 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1819 self
->icons
[j
].data
[t
] =
1820 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1821 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1822 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1823 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1832 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1833 if (hints
->flags
& IconPixmapHint
) {
1835 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1836 xerror_set_ignore(TRUE
);
1837 if (!RrPixmapToRGBA(ob_rr_inst
,
1839 (hints
->flags
& IconMaskHint
?
1840 hints
->icon_mask
: None
),
1841 &self
->icons
[self
->nicons
-1].width
,
1842 &self
->icons
[self
->nicons
-1].height
,
1843 &self
->icons
[self
->nicons
-1].data
)){
1844 g_free(&self
->icons
[self
->nicons
-1]);
1847 xerror_set_ignore(FALSE
);
1854 frame_adjust_icon(self
->frame
);
1857 void client_update_user_time(ObClient
*self
)
1861 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1862 /* we set this every time, not just when it grows, because in practice
1863 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1864 backward we don't want all windows to stop focusing. we'll just
1865 assume noone is setting times older than the last one, cuz that
1866 would be pretty stupid anyways
1868 self
->user_time
= time
;
1871 ob_debug("window %s user time %u\n", self->title, time);
1876 static void client_change_wm_state(ObClient
*self
)
1881 old
= self
->wmstate
;
1883 if (self
->shaded
|| self
->iconic
|| !self
->frame
->visible
)
1884 self
->wmstate
= IconicState
;
1886 self
->wmstate
= NormalState
;
1888 if (old
!= self
->wmstate
) {
1889 PROP_MSG(self
->window
, kde_wm_change_state
,
1890 self
->wmstate
, 1, 0, 0);
1892 state
[0] = self
->wmstate
;
1894 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1898 static void client_change_state(ObClient
*self
)
1900 gulong netstate
[11];
1905 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1907 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1909 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1910 if (self
->skip_taskbar
)
1911 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1912 if (self
->skip_pager
)
1913 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1914 if (self
->fullscreen
)
1915 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1917 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1919 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1921 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1923 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1924 if (self
->demands_attention
)
1925 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1926 if (self
->undecorated
)
1927 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1928 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1931 frame_adjust_state(self
->frame
);
1934 ObClient
*client_search_focus_tree(ObClient
*self
)
1939 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1940 if (client_focused(it
->data
)) return it
->data
;
1941 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1946 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1948 if (self
->transient_for
) {
1949 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1950 return client_search_focus_tree_full(self
->transient_for
);
1953 gboolean recursed
= FALSE
;
1955 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1956 if (!((ObClient
*)it
->data
)->transient_for
) {
1958 if ((c
= client_search_focus_tree_full(it
->data
)))
1967 /* this function checks the whole tree, the client_search_focus_tree~
1968 does not, so we need to check this window */
1969 if (client_focused(self
))
1971 return client_search_focus_tree(self
);
1974 static ObStackingLayer
calc_layer(ObClient
*self
)
1978 if (self
->fullscreen
&&
1979 (client_focused(self
) || client_search_focus_tree(self
)))
1980 l
= OB_STACKING_LAYER_FULLSCREEN
;
1981 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1982 l
= OB_STACKING_LAYER_DESKTOP
;
1983 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1984 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1985 else l
= OB_STACKING_LAYER_ABOVE
;
1987 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1988 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1989 else l
= OB_STACKING_LAYER_NORMAL
;
1994 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1995 ObStackingLayer min
, gboolean raised
)
1997 ObStackingLayer old
, own
;
2001 own
= calc_layer(self
);
2002 self
->layer
= MAX(own
, min
);
2004 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2005 client_calc_layer_recursive(it
->data
, orig
,
2007 raised
? raised
: self
->layer
!= old
);
2009 if (!raised
&& self
->layer
!= old
)
2010 if (orig
->frame
) { /* only restack if the original window is managed */
2011 stacking_remove(CLIENT_AS_WINDOW(self
));
2012 stacking_add(CLIENT_AS_WINDOW(self
));
2016 void client_calc_layer(ObClient
*self
)
2023 /* transients take on the layer of their parents */
2024 it
= client_search_all_top_parents(self
);
2026 for (; it
; it
= g_slist_next(it
))
2027 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2030 gboolean
client_should_show(ObClient
*self
)
2034 if (client_normal(self
) && screen_showing_desktop
)
2037 if (self->transient_for) {
2038 if (self->transient_for != OB_TRAN_GROUP)
2039 return client_should_show(self->transient_for);
2043 for (it = self->group->members; it; it = g_slist_next(it)) {
2044 ObClient *c = it->data;
2045 if (c != self && !c->transient_for) {
2046 if (client_should_show(c))
2053 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2059 void client_show(ObClient
*self
)
2062 if (client_should_show(self
)) {
2063 frame_show(self
->frame
);
2066 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2067 needs to be in IconicState. This includes when it is on another
2070 client_change_wm_state(self
);
2073 void client_hide(ObClient
*self
)
2075 if (!client_should_show(self
)) {
2076 frame_hide(self
->frame
);
2079 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2080 needs to be in IconicState. This includes when it is on another
2083 client_change_wm_state(self
);
2086 void client_showhide(ObClient
*self
)
2089 if (client_should_show(self
)) {
2090 frame_show(self
->frame
);
2093 frame_hide(self
->frame
);
2096 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2097 needs to be in IconicState. This includes when it is on another
2100 client_change_wm_state(self
);
2103 gboolean
client_normal(ObClient
*self
) {
2104 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2105 self
->type
== OB_CLIENT_TYPE_DOCK
||
2106 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2109 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2111 gboolean pos
= FALSE
; /* has the window's position been configured? */
2114 /* save the position, and set self->area for these to use */
2120 /* these are in a carefully crafted order.. */
2123 self
->iconic
= FALSE
;
2124 client_iconify(self
, TRUE
, FALSE
);
2126 if (self
->fullscreen
) {
2127 self
->fullscreen
= FALSE
;
2128 client_fullscreen(self
, TRUE
);
2131 if (self
->undecorated
) {
2132 self
->undecorated
= FALSE
;
2133 client_set_undecorated(self
, TRUE
);
2136 self
->shaded
= FALSE
;
2137 client_shade(self
, TRUE
);
2139 if (self
->demands_attention
) {
2140 self
->demands_attention
= FALSE
;
2141 client_hilite(self
, TRUE
);
2144 if (self
->max_vert
&& self
->max_horz
) {
2145 self
->max_vert
= self
->max_horz
= FALSE
;
2146 client_maximize(self
, TRUE
, 0);
2148 } else if (self
->max_vert
) {
2149 self
->max_vert
= FALSE
;
2150 client_maximize(self
, TRUE
, 2);
2152 } else if (self
->max_horz
) {
2153 self
->max_horz
= FALSE
;
2154 client_maximize(self
, TRUE
, 1);
2158 /* if the client didn't get positioned yet, then do so now
2159 call client_move even if the window is not being moved anywhere, because
2160 when we reparent it and decorate it, it is getting moved and we need to
2161 be telling it so with a ConfigureNotify event.
2164 /* use the saved position */
2167 client_move(self
, x
, y
);
2170 /* nothing to do for the other states:
2179 void client_try_configure(ObClient
*self
, ObCorner anchor
,
2180 gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2181 gint
*logicalw
, gint
*logicalh
,
2184 Rect desired_area
= {*x
, *y
, *w
, *h
};
2186 /* make the frame recalculate its dimentions n shit without changing
2187 anything visible for real, this way the constraints below can work with
2188 the updated frame dimensions. */
2189 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2191 /* work within the prefered sizes given by the window */
2192 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2193 gint basew
, baseh
, minw
, minh
;
2195 /* base size is substituted with min size if not specified */
2196 if (self
->base_size
.width
|| self
->base_size
.height
) {
2197 basew
= self
->base_size
.width
;
2198 baseh
= self
->base_size
.height
;
2200 basew
= self
->min_size
.width
;
2201 baseh
= self
->min_size
.height
;
2203 /* min size is substituted with base size if not specified */
2204 if (self
->min_size
.width
|| self
->min_size
.height
) {
2205 minw
= self
->min_size
.width
;
2206 minh
= self
->min_size
.height
;
2208 minw
= self
->base_size
.width
;
2209 minh
= self
->base_size
.height
;
2212 /* if this is a user-requested resize, then check against min/max
2215 /* smaller than min size or bigger than max size? */
2216 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2217 if (*w
< minw
) *w
= minw
;
2218 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2219 if (*h
< minh
) *h
= minh
;
2224 /* keep to the increments */
2225 *w
/= self
->size_inc
.width
;
2226 *h
/= self
->size_inc
.height
;
2228 /* you cannot resize to nothing */
2229 if (basew
+ *w
< 1) *w
= 1 - basew
;
2230 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2232 /* save the logical size */
2233 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2234 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2236 *w
*= self
->size_inc
.width
;
2237 *h
*= self
->size_inc
.height
;
2242 /* adjust the height to match the width for the aspect ratios.
2243 for this, min size is not substituted for base size ever. */
2244 *w
-= self
->base_size
.width
;
2245 *h
-= self
->base_size
.height
;
2247 if (!self
->fullscreen
) {
2248 if (self
->min_ratio
)
2249 if (*h
* self
->min_ratio
> *w
) {
2250 *h
= (gint
)(*w
/ self
->min_ratio
);
2252 /* you cannot resize to nothing */
2255 *w
= (gint
)(*h
* self
->min_ratio
);
2258 if (self
->max_ratio
)
2259 if (*h
* self
->max_ratio
< *w
) {
2260 *h
= (gint
)(*w
/ self
->max_ratio
);
2262 /* you cannot resize to nothing */
2265 *w
= (gint
)(*h
* self
->min_ratio
);
2270 *w
+= self
->base_size
.width
;
2271 *h
+= self
->base_size
.height
;
2274 /* gets the frame's position */
2275 frame_client_gravity(self
->frame
, x
, y
);
2277 /* these positions are frame positions, not client positions */
2279 /* set the size and position if fullscreen */
2280 if (self
->fullscreen
) {
2284 i
= screen_find_monitor(&desired_area
);
2285 a
= screen_physical_area_monitor(i
);
2292 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2293 is entering fullscreen */
2294 } else if (self
->max_horz
|| self
->max_vert
) {
2298 i
= screen_find_monitor(&desired_area
);
2299 a
= screen_area_monitor(self
->desktop
, i
);
2301 /* set the size and position if maximized */
2302 if (self
->max_horz
) {
2304 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2306 if (self
->max_vert
) {
2308 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2311 /* maximizing is not allowed if the user can't move+resize the window
2315 /* gets the client's position */
2316 frame_frame_gravity(self
->frame
, x
, y
);
2318 /* these override the above states! if you cant move you can't move! */
2320 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2324 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2325 *w
= self
->area
.width
;
2326 *h
= self
->area
.height
;
2334 case OB_CORNER_TOPLEFT
:
2336 case OB_CORNER_TOPRIGHT
:
2337 *x
-= *w
- self
->area
.width
;
2339 case OB_CORNER_BOTTOMLEFT
:
2340 *y
-= *h
- self
->area
.height
;
2342 case OB_CORNER_BOTTOMRIGHT
:
2343 *x
-= *w
- self
->area
.width
;
2344 *y
-= *h
- self
->area
.height
;
2350 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2351 gint x
, gint y
, gint w
, gint h
,
2352 gboolean user
, gboolean final
,
2353 gboolean force_reply
)
2355 gint oldw
, oldh
, oldrx
, oldry
;
2356 gboolean send_resize_client
;
2357 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2358 guint fdecor
= self
->frame
->decorations
;
2359 gboolean fhorz
= self
->frame
->max_horz
;
2360 gint logicalw
, logicalh
;
2362 /* find the new x, y, width, and height (and logical size) */
2363 client_try_configure(self
, anchor
, &x
, &y
, &w
, &h
,
2364 &logicalw
, &logicalh
, user
);
2366 /* set the logical size if things changed */
2367 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2368 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2370 /* figure out if we moved or resized or what */
2371 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2372 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2374 oldw
= self
->area
.width
;
2375 oldh
= self
->area
.height
;
2376 RECT_SET(self
->area
, x
, y
, w
, h
);
2378 /* for app-requested resizes, always resize if 'resized' is true.
2379 for user-requested ones, only resize if final is true, or when
2380 resizing in redraw mode */
2381 send_resize_client
= ((!user
&& resized
) ||
2383 (resized
&& config_resize_redraw
))));
2385 /* if the client is enlarging, then resize the client before the frame */
2386 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2387 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2389 /* find the frame's dimensions and move/resize it */
2390 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2391 moved
= resized
= TRUE
;
2392 if (moved
|| resized
)
2393 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2395 /* find the client's position relative to the root window */
2396 oldrx
= self
->root_pos
.x
;
2397 oldry
= self
->root_pos
.y
;
2398 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2399 self
->frame
->size
.left
-
2400 self
->border_width
) ||
2401 oldry
!= (signed)(self
->frame
->area
.y
+
2402 self
->frame
->size
.top
-
2403 self
->border_width
));
2405 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2409 POINT_SET(self
->root_pos
,
2410 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2412 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2413 self
->border_width
);
2415 event
.type
= ConfigureNotify
;
2416 event
.xconfigure
.display
= ob_display
;
2417 event
.xconfigure
.event
= self
->window
;
2418 event
.xconfigure
.window
= self
->window
;
2420 /* root window real coords */
2421 event
.xconfigure
.x
= self
->root_pos
.x
;
2422 event
.xconfigure
.y
= self
->root_pos
.y
;
2423 event
.xconfigure
.width
= w
;
2424 event
.xconfigure
.height
= h
;
2425 event
.xconfigure
.border_width
= 0;
2426 event
.xconfigure
.above
= self
->frame
->plate
;
2427 event
.xconfigure
.override_redirect
= FALSE
;
2428 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2429 FALSE
, StructureNotifyMask
, &event
);
2432 /* if the client is shrinking, then resize the frame before the client */
2433 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2434 XResizeWindow(ob_display
, self
->window
, w
, h
);
2439 void client_fullscreen(ObClient
*self
, gboolean fs
)
2443 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2444 self
->fullscreen
== fs
) return; /* already done */
2446 self
->fullscreen
= fs
;
2447 client_change_state(self
); /* change the state hints on the client */
2448 client_calc_layer(self
); /* and adjust out layer/stacking */
2451 self
->pre_fullscreen_area
= self
->area
;
2452 /* if the window is maximized, its area isn't all that meaningful.
2453 save it's premax area instead. */
2454 if (self
->max_horz
) {
2455 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2456 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2458 if (self
->max_vert
) {
2459 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2460 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2463 /* these are not actually used cuz client_configure will set them
2464 as appropriate when the window is fullscreened */
2469 if (self
->pre_fullscreen_area
.width
> 0 &&
2470 self
->pre_fullscreen_area
.height
> 0)
2472 x
= self
->pre_fullscreen_area
.x
;
2473 y
= self
->pre_fullscreen_area
.y
;
2474 w
= self
->pre_fullscreen_area
.width
;
2475 h
= self
->pre_fullscreen_area
.height
;
2476 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2478 /* pick some fallbacks... */
2479 a
= screen_area_monitor(self
->desktop
, 0);
2480 x
= a
->x
+ a
->width
/ 4;
2481 y
= a
->y
+ a
->height
/ 4;
2487 client_setup_decor_and_functions(self
);
2489 client_move_resize(self
, x
, y
, w
, h
);
2491 /* try focus us when we go into fullscreen mode */
2495 static void client_iconify_recursive(ObClient
*self
,
2496 gboolean iconic
, gboolean curdesk
)
2499 gboolean changed
= FALSE
;
2502 if (self
->iconic
!= iconic
) {
2503 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2507 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2508 self
->iconic
= iconic
;
2510 /* update the focus lists.. iconic windows go to the bottom of
2511 the list, put the new iconic window at the 'top of the
2513 focus_order_to_top(self
);
2518 self
->iconic
= iconic
;
2521 client_set_desktop(self
, screen_desktop
, FALSE
);
2523 /* this puts it after the current focused window */
2524 focus_order_remove(self
);
2525 focus_order_add_new(self
);
2532 client_change_state(self
);
2533 client_showhide(self
);
2534 if (STRUT_EXISTS(self
->strut
))
2535 screen_update_areas();
2538 /* iconify all direct transients */
2539 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2540 if (it
->data
!= self
)
2541 if (client_is_direct_child(self
, it
->data
))
2542 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2545 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2547 /* move up the transient chain as far as possible first */
2548 self
= client_search_top_parent(self
);
2549 client_iconify_recursive(self
, iconic
, curdesk
);
2552 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2556 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2557 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2559 /* check if already done */
2561 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2562 if (dir
== 1 && self
->max_horz
) return;
2563 if (dir
== 2 && self
->max_vert
) return;
2565 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2566 if (dir
== 1 && !self
->max_horz
) return;
2567 if (dir
== 2 && !self
->max_vert
) return;
2570 /* we just tell it to configure in the same place and client_configure
2571 worries about filling the screen with the window */
2574 w
= self
->area
.width
;
2575 h
= self
->area
.height
;
2578 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2579 RECT_SET(self
->pre_max_area
,
2580 self
->area
.x
, self
->pre_max_area
.y
,
2581 self
->area
.width
, self
->pre_max_area
.height
);
2583 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2584 RECT_SET(self
->pre_max_area
,
2585 self
->pre_max_area
.x
, self
->area
.y
,
2586 self
->pre_max_area
.width
, self
->area
.height
);
2591 a
= screen_area_monitor(self
->desktop
, 0);
2592 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2593 if (self
->pre_max_area
.width
> 0) {
2594 x
= self
->pre_max_area
.x
;
2595 w
= self
->pre_max_area
.width
;
2597 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2598 0, self
->pre_max_area
.height
);
2600 /* pick some fallbacks... */
2601 x
= a
->x
+ a
->width
/ 4;
2605 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2606 if (self
->pre_max_area
.height
> 0) {
2607 y
= self
->pre_max_area
.y
;
2608 h
= self
->pre_max_area
.height
;
2610 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2611 self
->pre_max_area
.width
, 0);
2613 /* pick some fallbacks... */
2614 y
= a
->y
+ a
->height
/ 4;
2620 if (dir
== 0 || dir
== 1) /* horz */
2621 self
->max_horz
= max
;
2622 if (dir
== 0 || dir
== 2) /* vert */
2623 self
->max_vert
= max
;
2625 client_change_state(self
); /* change the state hints on the client */
2627 client_setup_decor_and_functions(self
);
2629 client_move_resize(self
, x
, y
, w
, h
);
2632 void client_shade(ObClient
*self
, gboolean shade
)
2634 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2635 shade
) || /* can't shade */
2636 self
->shaded
== shade
) return; /* already done */
2638 self
->shaded
= shade
;
2639 client_change_state(self
);
2640 client_change_wm_state(self
); /* the window is being hidden/shown */
2641 /* resize the frame to just the titlebar */
2642 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2645 void client_close(ObClient
*self
)
2649 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2651 /* in the case that the client provides no means to requesting that it
2652 close, we just kill it */
2653 if (!self
->delete_window
)
2657 XXX: itd be cool to do timeouts and shit here for killing the client's
2659 like... if the window is around after 5 seconds, then the close button
2660 turns a nice red, and if this function is called again, the client is
2664 ce
.xclient
.type
= ClientMessage
;
2665 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2666 ce
.xclient
.display
= ob_display
;
2667 ce
.xclient
.window
= self
->window
;
2668 ce
.xclient
.format
= 32;
2669 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2670 ce
.xclient
.data
.l
[1] = event_curtime
;
2671 ce
.xclient
.data
.l
[2] = 0l;
2672 ce
.xclient
.data
.l
[3] = 0l;
2673 ce
.xclient
.data
.l
[4] = 0l;
2674 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2677 void client_kill(ObClient
*self
)
2679 XKillClient(ob_display
, self
->window
);
2682 void client_hilite(ObClient
*self
, gboolean hilite
)
2684 if (self
->demands_attention
== hilite
)
2685 return; /* no change */
2687 /* don't allow focused windows to hilite */
2688 self
->demands_attention
= hilite
&& !client_focused(self
);
2689 if (self
->demands_attention
)
2690 frame_flash_start(self
->frame
);
2692 frame_flash_stop(self
->frame
);
2693 client_change_state(self
);
2696 void client_set_desktop_recursive(ObClient
*self
,
2697 guint target
, gboolean donthide
)
2702 if (target
!= self
->desktop
) {
2704 ob_debug("Setting desktop %u\n", target
+1);
2706 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2708 /* remove from the old desktop(s) */
2709 focus_order_remove(self
);
2711 old
= self
->desktop
;
2712 self
->desktop
= target
;
2713 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2714 /* the frame can display the current desktop state */
2715 frame_adjust_state(self
->frame
);
2716 /* 'move' the window to the new desktop */
2718 client_showhide(self
);
2719 /* raise if it was not already on the desktop */
2720 if (old
!= DESKTOP_ALL
)
2722 if (STRUT_EXISTS(self
->strut
))
2723 screen_update_areas();
2725 /* add to the new desktop(s) */
2726 if (config_focus_new
)
2727 focus_order_to_top(self
);
2729 focus_order_to_bottom(self
);
2732 /* move all transients */
2733 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2734 if (it
->data
!= self
)
2735 if (client_is_direct_child(self
, it
->data
))
2736 client_set_desktop_recursive(it
->data
, target
, donthide
);
2739 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2741 self
= client_search_top_parent(self
);
2742 client_set_desktop_recursive(self
, target
, donthide
);
2745 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2747 while (child
!= parent
&&
2748 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2749 child
= child
->transient_for
;
2750 return child
== parent
;
2753 ObClient
*client_search_modal_child(ObClient
*self
)
2758 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2759 ObClient
*c
= it
->data
;
2760 if ((ret
= client_search_modal_child(c
))) return ret
;
2761 if (c
->modal
) return c
;
2766 gboolean
client_validate(ObClient
*self
)
2770 XSync(ob_display
, FALSE
); /* get all events on the server */
2772 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2773 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2774 XPutBackEvent(ob_display
, &e
);
2781 void client_set_wm_state(ObClient
*self
, glong state
)
2783 if (state
== self
->wmstate
) return; /* no change */
2787 client_iconify(self
, TRUE
, TRUE
);
2790 client_iconify(self
, FALSE
, TRUE
);
2795 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2797 gboolean shaded
= self
->shaded
;
2798 gboolean fullscreen
= self
->fullscreen
;
2799 gboolean undecorated
= self
->undecorated
;
2800 gboolean max_horz
= self
->max_horz
;
2801 gboolean max_vert
= self
->max_vert
;
2802 gboolean modal
= self
->modal
;
2803 gboolean iconic
= self
->iconic
;
2804 gboolean demands_attention
= self
->demands_attention
;
2807 if (!(action
== prop_atoms
.net_wm_state_add
||
2808 action
== prop_atoms
.net_wm_state_remove
||
2809 action
== prop_atoms
.net_wm_state_toggle
))
2810 /* an invalid action was passed to the client message, ignore it */
2813 for (i
= 0; i
< 2; ++i
) {
2814 Atom state
= i
== 0 ? data1
: data2
;
2816 if (!state
) continue;
2818 /* if toggling, then pick whether we're adding or removing */
2819 if (action
== prop_atoms
.net_wm_state_toggle
) {
2820 if (state
== prop_atoms
.net_wm_state_modal
)
2821 action
= modal
? prop_atoms
.net_wm_state_remove
:
2822 prop_atoms
.net_wm_state_add
;
2823 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2824 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2825 prop_atoms
.net_wm_state_add
;
2826 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2827 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2828 prop_atoms
.net_wm_state_add
;
2829 else if (state
== prop_atoms
.net_wm_state_shaded
)
2830 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2831 prop_atoms
.net_wm_state_add
;
2832 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2833 action
= self
->skip_taskbar
?
2834 prop_atoms
.net_wm_state_remove
:
2835 prop_atoms
.net_wm_state_add
;
2836 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2837 action
= self
->skip_pager
?
2838 prop_atoms
.net_wm_state_remove
:
2839 prop_atoms
.net_wm_state_add
;
2840 else if (state
== prop_atoms
.net_wm_state_hidden
)
2841 action
= self
->iconic
?
2842 prop_atoms
.net_wm_state_remove
:
2843 prop_atoms
.net_wm_state_add
;
2844 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2845 action
= fullscreen
?
2846 prop_atoms
.net_wm_state_remove
:
2847 prop_atoms
.net_wm_state_add
;
2848 else if (state
== prop_atoms
.net_wm_state_above
)
2849 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2850 prop_atoms
.net_wm_state_add
;
2851 else if (state
== prop_atoms
.net_wm_state_below
)
2852 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2853 prop_atoms
.net_wm_state_add
;
2854 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2855 action
= self
->demands_attention
?
2856 prop_atoms
.net_wm_state_remove
:
2857 prop_atoms
.net_wm_state_add
;
2858 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2859 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2860 prop_atoms
.net_wm_state_add
;
2863 if (action
== prop_atoms
.net_wm_state_add
) {
2864 if (state
== prop_atoms
.net_wm_state_modal
) {
2866 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2868 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2870 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2872 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2873 self
->skip_taskbar
= TRUE
;
2874 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2875 self
->skip_pager
= TRUE
;
2876 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2878 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2880 } else if (state
== prop_atoms
.net_wm_state_above
) {
2882 self
->below
= FALSE
;
2883 } else if (state
== prop_atoms
.net_wm_state_below
) {
2884 self
->above
= FALSE
;
2886 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2887 demands_attention
= TRUE
;
2888 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2892 } else { /* action == prop_atoms.net_wm_state_remove */
2893 if (state
== prop_atoms
.net_wm_state_modal
) {
2895 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2897 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2899 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2901 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2902 self
->skip_taskbar
= FALSE
;
2903 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2904 self
->skip_pager
= FALSE
;
2905 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2907 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2909 } else if (state
== prop_atoms
.net_wm_state_above
) {
2910 self
->above
= FALSE
;
2911 } else if (state
== prop_atoms
.net_wm_state_below
) {
2912 self
->below
= FALSE
;
2913 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2914 demands_attention
= FALSE
;
2915 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2916 undecorated
= FALSE
;
2920 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2921 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2923 if (max_horz
== max_vert
) { /* both going the same way */
2924 client_maximize(self
, max_horz
, 0);
2926 client_maximize(self
, max_horz
, 1);
2927 client_maximize(self
, max_vert
, 2);
2931 if (max_horz
!= self
->max_horz
)
2932 client_maximize(self
, max_horz
, 1);
2934 client_maximize(self
, max_vert
, 2);
2937 /* change fullscreen state before shading, as it will affect if the window
2939 if (fullscreen
!= self
->fullscreen
)
2940 client_fullscreen(self
, fullscreen
);
2941 if (shaded
!= self
->shaded
)
2942 client_shade(self
, shaded
);
2943 if (undecorated
!= self
->undecorated
)
2944 client_set_undecorated(self
, undecorated
);
2945 if (modal
!= self
->modal
) {
2946 self
->modal
= modal
;
2947 /* when a window changes modality, then its stacking order with its
2948 transients needs to change */
2951 if (iconic
!= self
->iconic
)
2952 client_iconify(self
, iconic
, FALSE
);
2954 if (demands_attention
!= self
->demands_attention
)
2955 client_hilite(self
, demands_attention
);
2957 client_change_state(self
); /* change the hint to reflect these changes */
2960 ObClient
*client_focus_target(ObClient
*self
)
2962 ObClient
*child
= NULL
;
2964 child
= client_search_modal_child(self
);
2965 if (child
) return child
;
2969 gboolean
client_can_focus(ObClient
*self
)
2973 /* choose the correct target */
2974 self
= client_focus_target(self
);
2976 if (!self
->frame
->visible
)
2979 if (!(self
->can_focus
|| self
->focus_notify
))
2982 /* do a check to see if the window has already been unmapped or destroyed
2983 do this intelligently while watching out for unmaps we've generated
2984 (ignore_unmaps > 0) */
2985 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2986 DestroyNotify
, &ev
)) {
2987 XPutBackEvent(ob_display
, &ev
);
2990 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2991 UnmapNotify
, &ev
)) {
2992 if (self
->ignore_unmaps
) {
2993 self
->ignore_unmaps
--;
2995 XPutBackEvent(ob_display
, &ev
);
3003 gboolean
client_focus(ObClient
*self
)
3005 /* choose the correct target */
3006 self
= client_focus_target(self
);
3008 if (!client_can_focus(self
)) {
3009 if (!self
->frame
->visible
) {
3010 /* update the focus lists */
3011 focus_order_to_top(self
);
3016 ob_debug("Focusing client \"%s\" at time %u\n", self
->title
, event_curtime
);
3018 if (self
->can_focus
) {
3019 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3023 if (self
->focus_notify
) {
3025 ce
.xclient
.type
= ClientMessage
;
3026 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3027 ce
.xclient
.display
= ob_display
;
3028 ce
.xclient
.window
= self
->window
;
3029 ce
.xclient
.format
= 32;
3030 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3031 ce
.xclient
.data
.l
[1] = event_curtime
;
3032 ce
.xclient
.data
.l
[2] = 0l;
3033 ce
.xclient
.data
.l
[3] = 0l;
3034 ce
.xclient
.data
.l
[4] = 0l;
3035 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3039 ob_debug("%sively focusing %lx at %d\n",
3040 (self
->can_focus
? "act" : "pass"),
3041 self
->window
, (gint
) event_curtime
);
3044 /* Cause the FocusIn to come back to us. Important for desktop switches,
3045 since otherwise we'll have no FocusIn on the queue and send it off to
3046 the focus_backup. */
3047 XSync(ob_display
, FALSE
);
3051 /* Used when the current client is closed or otherwise hidden, focus_last will
3052 then prevent focus from going to the mouse pointer
3054 static void client_unfocus(ObClient
*self
)
3056 if (focus_client
== self
) {
3058 ob_debug("client_unfocus for %lx\n", self
->window
);
3060 focus_fallback(FALSE
);
3064 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3066 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3068 /* XXX do some stuff here if user is false to determine if we really want
3069 to activate it or not (a parent or group member is currently
3072 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3074 self
->window
, event_curtime
, last_time
,
3075 (user
? "user" : "application"));
3077 if (!user
&& event_curtime
&& last_time
&&
3078 !event_time_after(event_curtime
, last_time
))
3080 client_hilite(self
, TRUE
);
3082 if (client_normal(self
) && screen_showing_desktop
)
3083 screen_show_desktop(FALSE
);
3085 client_iconify(self
, FALSE
, here
);
3086 if (self
->desktop
!= DESKTOP_ALL
&&
3087 self
->desktop
!= screen_desktop
) {
3089 client_set_desktop(self
, screen_desktop
, FALSE
);
3091 screen_set_desktop(self
->desktop
);
3092 } else if (!self
->frame
->visible
)
3093 /* if its not visible for other reasons, then don't mess
3097 client_shade(self
, FALSE
);
3101 /* we do this an action here. this is rather important. this is because
3102 we want the results from the focus change to take place BEFORE we go
3103 about raising the window. when a fullscreen window loses focus, we
3104 need this or else the raise wont be able to raise above the
3105 to-lose-focus fullscreen window. */
3110 void client_raise(ObClient
*self
)
3112 action_run_string("Raise", self
, CurrentTime
);
3115 void client_lower(ObClient
*self
)
3117 action_run_string("Lower", self
, CurrentTime
);
3120 gboolean
client_focused(ObClient
*self
)
3122 return self
== focus_client
;
3125 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3128 /* si is the smallest image >= req */
3129 /* li is the largest image < req */
3130 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3132 if (!self
->nicons
) {
3133 ObClientIcon
*parent
= NULL
;
3135 if (self
->transient_for
) {
3136 if (self
->transient_for
!= OB_TRAN_GROUP
)
3137 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3140 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3141 ObClient
*c
= it
->data
;
3142 if (c
!= self
&& !c
->transient_for
) {
3143 if ((parent
= client_icon_recursive(c
, w
, h
)))
3153 for (i
= 0; i
< self
->nicons
; ++i
) {
3154 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3155 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3159 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3164 if (largest
== 0) /* didnt find one smaller than the requested size */
3165 return &self
->icons
[si
];
3166 return &self
->icons
[li
];
3169 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3172 static ObClientIcon deficon
;
3174 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3175 deficon
.width
= deficon
.height
= 48;
3176 deficon
.data
= ob_rr_theme
->def_win_icon
;
3182 /* this be mostly ripped from fvwm */
3183 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3185 gint my_cx
, my_cy
, his_cx
, his_cy
;
3188 gint score
, best_score
;
3189 ObClient
*best_client
, *cur
;
3195 /* first, find the centre coords of the currently focused window */
3196 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3197 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3202 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3205 /* the currently selected window isn't interesting */
3208 if (!client_normal(cur
))
3210 /* using c->desktop instead of screen_desktop doesn't work if the
3211 * current window was omnipresent, hope this doesn't have any other
3213 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3217 if(!(client_focus_target(cur
) == cur
&&
3218 client_can_focus(cur
)))
3221 /* find the centre coords of this window, from the
3222 * currently focused window's point of view */
3223 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3224 + cur
->frame
->area
.width
/ 2;
3225 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3226 + cur
->frame
->area
.height
/ 2;
3228 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3229 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3231 /* Rotate the diagonals 45 degrees counterclockwise.
3232 * To do this, multiply the matrix /+h +h\ with the
3233 * vector (x y). \-h +h/
3234 * h = sqrt(0.5). We can set h := 1 since absolute
3235 * distance doesn't matter here. */
3236 tx
= his_cx
+ his_cy
;
3237 his_cy
= -his_cx
+ his_cy
;
3242 case OB_DIRECTION_NORTH
:
3243 case OB_DIRECTION_SOUTH
:
3244 case OB_DIRECTION_NORTHEAST
:
3245 case OB_DIRECTION_SOUTHWEST
:
3246 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3247 distance
= ((dir
== OB_DIRECTION_NORTH
||
3248 dir
== OB_DIRECTION_NORTHEAST
) ?
3251 case OB_DIRECTION_EAST
:
3252 case OB_DIRECTION_WEST
:
3253 case OB_DIRECTION_SOUTHEAST
:
3254 case OB_DIRECTION_NORTHWEST
:
3255 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3256 distance
= ((dir
== OB_DIRECTION_WEST
||
3257 dir
== OB_DIRECTION_NORTHWEST
) ?
3262 /* the target must be in the requested direction */
3266 /* Calculate score for this window. The smaller the better. */
3267 score
= distance
+ offset
;
3269 /* windows more than 45 degrees off the direction are
3270 * heavily penalized and will only be chosen if nothing
3271 * else within a million pixels */
3272 if(offset
> distance
)
3275 if(best_score
== -1 || score
< best_score
)
3283 void client_set_layer(ObClient
*self
, gint layer
)
3287 self
->above
= FALSE
;
3288 } else if (layer
== 0) {
3289 self
->below
= self
->above
= FALSE
;
3291 self
->below
= FALSE
;
3294 client_calc_layer(self
);
3295 client_change_state(self
); /* reflect this in the state hints */
3298 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3300 if (self
->undecorated
!= undecorated
) {
3301 self
->undecorated
= undecorated
;
3302 client_setup_decor_and_functions(self
);
3303 /* Make sure the client knows it might have moved. Maybe there is a
3304 * better way of doing this so only one client_configure is sent, but
3305 * since 125 of these are sent per second when moving the window (with
3306 * user = FALSE) i doubt it matters much.
3308 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3309 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3310 client_change_state(self
); /* reflect this in the state hints */
3314 guint
client_monitor(ObClient
*self
)
3316 return screen_find_monitor(&self
->frame
->area
);
3319 ObClient
*client_search_top_parent(ObClient
*self
)
3321 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3322 self
= self
->transient_for
;
3326 GSList
*client_search_all_top_parents(ObClient
*self
)
3330 /* move up the direct transient chain as far as possible */
3331 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3332 self
= self
->transient_for
;
3334 if (!self
->transient_for
)
3335 ret
= g_slist_prepend(ret
, self
);
3339 g_assert(self
->group
);
3341 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3342 ObClient
*c
= it
->data
;
3344 if (!c
->transient_for
&& client_normal(c
))
3345 ret
= g_slist_prepend(ret
, c
);
3348 if (ret
== NULL
) /* no group parents */
3349 ret
= g_slist_prepend(ret
, self
);
3355 ObClient
*client_search_focus_parent(ObClient
*self
)
3357 if (self
->transient_for
) {
3358 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3359 if (client_focused(self
->transient_for
))
3360 return self
->transient_for
;
3364 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3365 ObClient
*c
= it
->data
;
3367 /* checking transient_for prevents infinate loops! */
3368 if (c
!= self
&& !c
->transient_for
)
3369 if (client_focused(c
))
3378 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3380 if (self
->transient_for
) {
3381 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3382 if (self
->transient_for
== search
)
3387 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3388 ObClient
*c
= it
->data
;
3390 /* checking transient_for prevents infinate loops! */
3391 if (c
!= self
&& !c
->transient_for
)
3401 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3405 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3406 if (sit
->data
== search
)
3408 if (client_search_transient(sit
->data
, search
))
3414 void client_update_sm_client_id(ObClient
*self
)
3416 g_free(self
->sm_client_id
);
3417 self
->sm_client_id
= NULL
;
3419 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3421 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3422 &self
->sm_client_id
);
3425 #define WANT_EDGE(cur, c) \
3428 if(!client_normal(cur)) \
3430 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3434 if(cur->layer < c->layer && !config_resist_layers_below) \
3437 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3438 if ((his_edge_start >= my_edge_start && \
3439 his_edge_start <= my_edge_end) || \
3440 (my_edge_start >= his_edge_start && \
3441 my_edge_start <= his_edge_end)) \
3444 /* finds the nearest edge in the given direction from the current client
3445 * note to self: the edge is the -frame- edge (the actual one), not the
3448 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3450 gint dest
, monitor_dest
;
3451 gint my_edge_start
, my_edge_end
, my_offset
;
3458 a
= screen_area(c
->desktop
);
3459 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3462 case OB_DIRECTION_NORTH
:
3463 my_edge_start
= c
->frame
->area
.x
;
3464 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3465 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3467 /* default: top of screen */
3468 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3469 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3470 /* if the monitor edge comes before the screen edge, */
3471 /* use that as the destination instead. (For xinerama) */
3472 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3473 dest
= monitor_dest
;
3475 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3476 gint his_edge_start
, his_edge_end
, his_offset
;
3477 ObClient
*cur
= it
->data
;
3481 his_edge_start
= cur
->frame
->area
.x
;
3482 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3483 his_offset
= cur
->frame
->area
.y
+
3484 (hang
? 0 : cur
->frame
->area
.height
);
3486 if(his_offset
+ 1 > my_offset
)
3489 if(his_offset
< dest
)
3492 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3495 case OB_DIRECTION_SOUTH
:
3496 my_edge_start
= c
->frame
->area
.x
;
3497 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3498 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3500 /* default: bottom of screen */
3501 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3502 monitor_dest
= monitor
->y
+ monitor
->height
-
3503 (hang
? c
->frame
->area
.height
: 0);
3504 /* if the monitor edge comes before the screen edge, */
3505 /* use that as the destination instead. (For xinerama) */
3506 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3507 dest
= monitor_dest
;
3509 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3510 gint his_edge_start
, his_edge_end
, his_offset
;
3511 ObClient
*cur
= it
->data
;
3515 his_edge_start
= cur
->frame
->area
.x
;
3516 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3517 his_offset
= cur
->frame
->area
.y
+
3518 (hang
? cur
->frame
->area
.height
: 0);
3521 if(his_offset
- 1 < my_offset
)
3524 if(his_offset
> dest
)
3527 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3530 case OB_DIRECTION_WEST
:
3531 my_edge_start
= c
->frame
->area
.y
;
3532 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3533 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3535 /* default: leftmost egde of screen */
3536 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3537 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3538 /* if the monitor edge comes before the screen edge, */
3539 /* use that as the destination instead. (For xinerama) */
3540 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3541 dest
= monitor_dest
;
3543 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3544 gint his_edge_start
, his_edge_end
, his_offset
;
3545 ObClient
*cur
= it
->data
;
3549 his_edge_start
= cur
->frame
->area
.y
;
3550 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3551 his_offset
= cur
->frame
->area
.x
+
3552 (hang
? 0 : cur
->frame
->area
.width
);
3554 if(his_offset
+ 1 > my_offset
)
3557 if(his_offset
< dest
)
3560 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3563 case OB_DIRECTION_EAST
:
3564 my_edge_start
= c
->frame
->area
.y
;
3565 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3566 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3568 /* default: rightmost edge of screen */
3569 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3570 monitor_dest
= monitor
->x
+ monitor
->width
-
3571 (hang
? c
->frame
->area
.width
: 0);
3572 /* if the monitor edge comes before the screen edge, */
3573 /* use that as the destination instead. (For xinerama) */
3574 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3575 dest
= monitor_dest
;
3577 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3578 gint his_edge_start
, his_edge_end
, his_offset
;
3579 ObClient
*cur
= it
->data
;
3583 his_edge_start
= cur
->frame
->area
.y
;
3584 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3585 his_offset
= cur
->frame
->area
.x
+
3586 (hang
? cur
->frame
->area
.width
: 0);
3588 if(his_offset
- 1 < my_offset
)
3591 if(his_offset
> dest
)
3594 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3597 case OB_DIRECTION_NORTHEAST
:
3598 case OB_DIRECTION_SOUTHEAST
:
3599 case OB_DIRECTION_NORTHWEST
:
3600 case OB_DIRECTION_SOUTHWEST
:
3601 /* not implemented */
3603 g_assert_not_reached();
3604 dest
= 0; /* suppress warning */
3609 ObClient
* client_under_pointer()
3613 ObClient
*ret
= NULL
;
3615 if (screen_pointer_pos(&x
, &y
)) {
3616 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3617 if (WINDOW_IS_CLIENT(it
->data
)) {
3618 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3619 if (c
->frame
->visible
&&
3620 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3630 gboolean
client_has_group_siblings(ObClient
*self
)
3632 return self
->group
&& self
->group
->members
->next
;