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
;
62 static Time client_last_user_time
= CurrentTime
;
64 static void client_get_all(ObClient
*self
);
65 static void client_toggle_border(ObClient
*self
, gboolean show
);
66 static void client_get_startup_id(ObClient
*self
);
67 static void client_get_area(ObClient
*self
);
68 static void client_get_desktop(ObClient
*self
);
69 static void client_get_state(ObClient
*self
);
70 static void client_get_layer(ObClient
*self
);
71 static void client_get_shaped(ObClient
*self
);
72 static void client_get_mwm_hints(ObClient
*self
);
73 static void client_get_gravity(ObClient
*self
);
74 static void client_change_allowed_actions(ObClient
*self
);
75 static void client_change_state(ObClient
*self
);
76 static void client_change_wm_state(ObClient
*self
);
77 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
78 static void client_restore_session_state(ObClient
*self
);
79 static void client_restore_session_stacking(ObClient
*self
);
80 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
81 static void client_unfocus(ObClient
*self
);
83 void client_startup(gboolean reconfig
)
90 void client_shutdown(gboolean reconfig
)
94 void client_add_destructor(ObClientDestructor func
, gpointer data
)
96 Destructor
*d
= g_new(Destructor
, 1);
99 client_destructors
= g_slist_prepend(client_destructors
, d
);
102 void client_remove_destructor(ObClientDestructor func
)
106 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
107 Destructor
*d
= it
->data
;
108 if (d
->func
== func
) {
110 client_destructors
= g_slist_delete_link(client_destructors
, it
);
116 void client_set_list()
118 Window
*windows
, *win_it
;
120 guint size
= g_list_length(client_list
);
122 /* create an array of the window ids */
124 windows
= g_new(Window
, size
);
126 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
127 *win_it
= ((ObClient
*)it
->data
)->window
;
131 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
132 net_client_list
, window
, (gulong
*)windows
, size
);
141 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
145 for (it = self->transients; it; it = g_slist_next(it)) {
146 if (!func(it->data, data)) return;
147 client_foreach_transient(it->data, func, data);
151 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
153 if (self->transient_for) {
154 if (self->transient_for != OB_TRAN_GROUP) {
155 if (!func(self->transient_for, data)) return;
156 client_foreach_ancestor(self->transient_for, func, data);
160 for (it = self->group->members; it; it = g_slist_next(it))
161 if (it->data != self &&
162 !((ObClient*)it->data)->transient_for) {
163 if (!func(it->data, data)) return;
164 client_foreach_ancestor(it->data, func, data);
171 void client_manage_all()
176 XWindowAttributes attrib
;
178 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
179 &w
, &w
, &children
, &nchild
);
181 /* remove all icon windows from the list */
182 for (i
= 0; i
< nchild
; i
++) {
183 if (children
[i
] == None
) continue;
184 wmhints
= XGetWMHints(ob_display
, children
[i
]);
186 if ((wmhints
->flags
& IconWindowHint
) &&
187 (wmhints
->icon_window
!= children
[i
]))
188 for (j
= 0; j
< nchild
; j
++)
189 if (children
[j
] == wmhints
->icon_window
) {
197 for (i
= 0; i
< nchild
; ++i
) {
198 if (children
[i
] == None
)
200 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
201 if (attrib
.override_redirect
) continue;
203 if (attrib
.map_state
!= IsUnmapped
)
204 client_manage(children
[i
]);
210 void client_manage(Window window
)
214 XWindowAttributes attrib
;
215 XSetWindowAttributes attrib_set
;
217 gboolean activate
= FALSE
;
218 ObAppSettings
*settings
;
223 /* check if it has already been unmapped by the time we started mapping.
224 the grab does a sync so we don't have to here */
225 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
226 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
228 XPutBackEvent(ob_display
, &e
);
230 ob_debug("Trying to manage unmapped window. Aborting that.\n");
232 return; /* don't manage it */
235 /* make sure it isn't an override-redirect window */
236 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
237 attrib
.override_redirect
)
240 return; /* don't manage it */
243 /* is the window a docking app */
244 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
245 if ((wmhint
->flags
& StateHint
) &&
246 wmhint
->initial_state
== WithdrawnState
)
248 dock_add(window
, wmhint
);
256 ob_debug("Managing window: %lx\n", window
);
258 /* choose the events we want to receive on the CLIENT window */
259 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
260 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
261 XChangeWindowAttributes(ob_display
, window
,
262 CWEventMask
|CWDontPropagate
, &attrib_set
);
265 /* create the ObClient struct, and populate it from the hints on the
267 self
= g_new0(ObClient
, 1);
268 self
->obwin
.type
= Window_Client
;
269 self
->window
= window
;
271 /* non-zero defaults */
272 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
274 self
->desktop
= screen_num_desktops
; /* always an invalid value */
275 self
->user_time
= client_last_user_time
;
277 client_get_all(self
);
278 /* per-app settings override stuff, and return the settings for other
280 settings
= client_get_settings_state(self
);
281 /* the session should get the last say */
282 client_restore_session_state(self
);
284 client_calc_layer(self
);
287 Time t
= sn_app_started(self
->startup_id
, self
->class);
288 if (t
) self
->user_time
= t
;
291 /* update the focus lists, do this before the call to change_state or
292 it can end up in the list twice! */
293 focus_order_add_new(self
);
295 /* remove the client's border (and adjust re gravity) */
296 client_toggle_border(self
, FALSE
);
298 /* specify that if we exit, the window should not be destroyed and should
299 be reparented back to root automatically */
300 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
302 /* create the decoration frame for the client window */
303 self
->frame
= frame_new(self
);
305 frame_grab_client(self
->frame
, self
);
307 /* do this after we have a frame.. it uses the frame to help determine the
308 WM_STATE to apply. */
309 client_change_state(self
);
313 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
314 client_restore_session_stacking(self
);
316 /* focus the new window? */
317 if (ob_state() != OB_STATE_STARTING
&&
318 /* this means focus=true for window is same as config_focus_new=true */
319 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
320 client_search_focus_parent(self
)) &&
321 /* this checks for focus=false for the window */
322 (!settings
|| settings
->focus
!= 0) &&
323 /* note the check against Type_Normal/Dialog, not client_normal(self),
324 which would also include other types. in this case we want more
325 strict rules for focus */
326 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
327 self
->type
== OB_CLIENT_TYPE_DIALOG
))
331 if (self
->desktop
!= screen_desktop
) {
332 /* activate the window */
335 gboolean group_foc
= FALSE
;
340 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
342 if (client_focused(it
->data
))
350 (!self
->transient_for
&& (!self
->group
||
351 !self
->group
->members
->next
))) ||
352 client_search_focus_tree_full(self
) ||
354 !client_normal(focus_client
))
356 /* activate the window */
363 /* get the current position */
367 /* figure out placement for the window */
368 if (ob_state() == OB_STATE_RUNNING
) {
371 transient
= place_client(self
, &newx
, &newy
, settings
);
373 /* make sure the window is visible. */
374 client_find_onscreen(self
, &newx
, &newy
,
375 self
->frame
->area
.width
,
376 self
->frame
->area
.height
,
377 /* non-normal clients has less rules, and
378 windows that are being restored from a
379 session do also. we can assume you want
380 it back where you saved it. Clients saying
381 they placed themselves are subjected to
382 harder rules, ones that are placed by
383 place.c or by the user are allowed partially
384 off-screen and on xinerama divides (ie,
385 it is up to the placement routines to avoid
386 the xinerama divides) */
388 (((self
->positioned
& PPosition
) &&
389 !(self
->positioned
& USPosition
)) &&
390 client_normal(self
) &&
394 /* do this after the window is placed, so the premax/prefullscreen numbers
396 also, this moves the window to the position where it has been placed
398 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
399 self
->window
, newx
, newy
, self
->area
.width
, self
->area
.height
);
400 client_apply_startup_state(self
, newx
, newy
);
402 keyboard_grab_for_client(self
, TRUE
);
403 mouse_grab_for_client(self
, TRUE
);
406 /* This is focus stealing prevention */
407 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
408 self
->window
, self
->user_time
, client_last_user_time
);
410 /* If a nothing at all, or a parent was focused, then focus this
413 if (!focus_client
|| client_search_focus_parent(self
) != NULL
)
417 /* If time stamp is old, don't steal focus */
418 if (self
->user_time
&&
419 !event_time_after(self
->user_time
, client_last_user_time
))
423 /* Don't steal focus from globally active clients.
424 I stole this idea from KWin. It seems nice.
426 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
))
432 /* since focus can change the stacking orders, if we focus the
433 window then the standard raise it gets is not enough, we need
434 to queue one for after the focus change takes place */
437 ob_debug("Focus stealing prevention activated for %s with time %u "
439 self
->title
, self
->user_time
, client_last_user_time
);
440 /* if the client isn't focused, then hilite it so the user
442 client_hilite(self
, TRUE
);
446 /* This may look rather odd. Well it's because new windows are added
447 to the stacking order non-intrusively. If we're not going to focus
448 the new window or hilite it, then we raise it to the top. This will
449 take affect for things that don't get focused like splash screens.
450 Also if you don't have focus_new enabled, then it's going to get
451 raised to the top. Legacy begets legacy I guess?
456 /* this has to happen before we try focus the window, but we want it to
457 happen after the client's stacking has been determined or it looks bad
461 /* use client_focus instead of client_activate cuz client_activate does
462 stuff like switch desktops etc and I'm not interested in all that when
463 a window maps since its not based on an action from the user like
464 clicking a window to activate it. so keep the new window out of the way
467 /* if using focus_delay, stop the timer now so that focus doesn't
469 event_halt_focus_delay();
473 /* client_activate does this but we aret using it so we have to do it
475 if (screen_showing_desktop
)
476 screen_show_desktop(FALSE
);
478 /* add to client list/map */
479 client_list
= g_list_append(client_list
, self
);
480 g_hash_table_insert(window_map
, &self
->window
, self
);
482 /* this has to happen after we're in the client_list */
483 if (STRUT_EXISTS(self
->strut
))
484 screen_update_areas();
486 /* update the list hints */
489 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
492 void client_unmanage_all()
494 while (client_list
!= NULL
)
495 client_unmanage(client_list
->data
);
498 void client_unmanage(ObClient
*self
)
503 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
504 self
->title
? self
->title
: "");
506 g_assert(self
!= NULL
);
508 /* update the focus lists */
509 focus_order_remove(self
);
511 if (focus_client
== self
) {
514 /* focus the last focused window on the desktop, and ignore enter
515 events from the unmap so it doesnt mess with the focus */
516 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
517 /* remove these flags so we don't end up getting focused in the
519 self
->can_focus
= FALSE
;
520 self
->focus_notify
= FALSE
;
522 client_unfocus(self
);
525 /* potentially fix focusLast */
526 if (config_focus_last
)
527 grab_pointer(TRUE
, OB_CURSOR_NONE
);
529 frame_hide(self
->frame
);
532 keyboard_grab_for_client(self
, FALSE
);
533 mouse_grab_for_client(self
, FALSE
);
535 /* remove the window from our save set */
536 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
538 /* we dont want events no more */
539 XSelectInput(ob_display
, self
->window
, NoEventMask
);
541 client_list
= g_list_remove(client_list
, self
);
542 stacking_remove(self
);
543 g_hash_table_remove(window_map
, &self
->window
);
545 /* once the client is out of the list, update the struts to remove its
547 if (STRUT_EXISTS(self
->strut
))
548 screen_update_areas();
550 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
551 Destructor
*d
= it
->data
;
552 d
->func(self
, d
->data
);
555 /* tell our parent(s) that we're gone */
556 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
557 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
558 if (it
->data
!= self
)
559 ((ObClient
*)it
->data
)->transients
=
560 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
561 } else if (self
->transient_for
) { /* transient of window */
562 self
->transient_for
->transients
=
563 g_slist_remove(self
->transient_for
->transients
, self
);
566 /* tell our transients that we're gone */
567 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
568 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
569 ((ObClient
*)it
->data
)->transient_for
= NULL
;
570 client_calc_layer(it
->data
);
574 /* remove from its group */
576 group_remove(self
->group
, self
);
580 /* restore the window's original geometry so it is not lost */
584 if (self
->fullscreen
)
585 a
= self
->pre_fullscreen_area
;
586 else if (self
->max_horz
|| self
->max_vert
) {
587 if (self
->max_horz
) {
588 a
.x
= self
->pre_max_area
.x
;
589 a
.width
= self
->pre_max_area
.width
;
591 if (self
->max_vert
) {
592 a
.y
= self
->pre_max_area
.y
;
593 a
.height
= self
->pre_max_area
.height
;
597 /* give the client its border back */
598 client_toggle_border(self
, TRUE
);
600 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
601 self
->decorations
= 0; /* unmanaged windows have no decor */
603 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
606 /* reparent the window out of the frame, and free the frame */
607 frame_release_client(self
->frame
, self
);
610 if (ob_state() != OB_STATE_EXITING
) {
611 /* these values should not be persisted across a window
613 PROP_ERASE(self
->window
, net_wm_desktop
);
614 PROP_ERASE(self
->window
, net_wm_state
);
615 PROP_ERASE(self
->window
, wm_state
);
617 /* if we're left in an unmapped state, the client wont be mapped. this
618 is bad, since we will no longer be managing the window on restart */
619 XMapWindow(ob_display
, self
->window
);
622 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
624 /* free all data allocated in the client struct */
625 g_slist_free(self
->transients
);
626 for (j
= 0; j
< self
->nicons
; ++j
)
627 g_free(self
->icons
[j
].data
);
628 if (self
->nicons
> 0)
631 g_free(self
->icon_title
);
635 g_free(self
->sm_client_id
);
638 /* update the list hints */
641 if (config_focus_last
)
642 grab_pointer(FALSE
, OB_CURSOR_NONE
);
645 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
647 ObAppSettings
*settings
= NULL
;
650 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
651 ObAppSettings
*app
= it
->data
;
653 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
654 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
655 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
656 && !strcmp(app
->name
, self
->name
)))
658 ob_debug("Window matching: %s\n", app
->name
);
659 /* Match if no role was specified in the per app setting, or if the
660 * string matches the beginning of the role, since apps like to set
661 * the role to things like browser-window-23c4b2f */
663 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
673 if (settings
->shade
!= -1)
674 self
->shaded
= !!settings
->shade
;
675 if (settings
->decor
!= -1)
676 self
->undecorated
= !settings
->decor
;
677 if (settings
->iconic
!= -1)
678 self
->iconic
= !!settings
->iconic
;
679 if (settings
->skip_pager
!= -1)
680 self
->skip_pager
= !!settings
->skip_pager
;
681 if (settings
->skip_taskbar
!= -1)
682 self
->skip_taskbar
= !!settings
->skip_taskbar
;
684 if (settings
->max_vert
!= -1)
685 self
->max_vert
= !!settings
->max_vert
;
686 if (settings
->max_horz
!= -1)
687 self
->max_vert
= !!settings
->max_horz
;
689 if (settings
->fullscreen
!= -1)
690 self
->fullscreen
= !!settings
->fullscreen
;
692 if (settings
->desktop
< screen_num_desktops
693 || settings
->desktop
== DESKTOP_ALL
)
694 self
->desktop
= settings
->desktop
;
696 if (settings
->layer
== -1) {
700 else if (settings
->layer
== 0) {
704 else if (settings
->layer
== 1) {
712 static void client_restore_session_state(ObClient
*self
)
716 if (!(it
= session_state_find(self
)))
719 self
->session
= it
->data
;
721 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
722 self
->positioned
= PPosition
;
723 if (self
->session
->w
> 0)
724 self
->area
.width
= self
->session
->w
;
725 if (self
->session
->h
> 0)
726 self
->area
.height
= self
->session
->h
;
727 XResizeWindow(ob_display
, self
->window
,
728 self
->area
.width
, self
->area
.height
);
730 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
731 self
->session
->desktop
:
732 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
733 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
735 self
->shaded
= self
->session
->shaded
;
736 self
->iconic
= self
->session
->iconic
;
737 self
->skip_pager
= self
->session
->skip_pager
;
738 self
->skip_taskbar
= self
->session
->skip_taskbar
;
739 self
->fullscreen
= self
->session
->fullscreen
;
740 self
->above
= self
->session
->above
;
741 self
->below
= self
->session
->below
;
742 self
->max_horz
= self
->session
->max_horz
;
743 self
->max_vert
= self
->session
->max_vert
;
746 static void client_restore_session_stacking(ObClient
*self
)
750 if (!self
->session
) return;
752 it
= g_list_find(session_saved_state
, self
->session
);
753 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
756 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
757 if (session_state_cmp(it
->data
, cit
->data
))
760 client_calc_layer(self
);
761 stacking_below(CLIENT_AS_WINDOW(self
),
762 CLIENT_AS_WINDOW(cit
->data
));
768 void client_move_onscreen(ObClient
*self
, gboolean rude
)
770 gint x
= self
->area
.x
;
771 gint y
= self
->area
.y
;
772 if (client_find_onscreen(self
, &x
, &y
,
773 self
->frame
->area
.width
,
774 self
->frame
->area
.height
, rude
)) {
775 client_move(self
, x
, y
);
779 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
783 gint ox
= *x
, oy
= *y
;
785 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
788 /* XXX watch for xinerama dead areas */
789 /* This makes sure windows aren't entirely outside of the screen so you
790 can't see them at all.
791 It makes sure 10% of the window is on the screen at least. At don't let
792 it move itself off the top of the screen, which would hide the titlebar
793 on you. (The user can still do this if they want too, it's only limiting
796 if (client_normal(self
)) {
797 a
= screen_area(self
->desktop
);
798 if (!self
->strut
.right
&&
799 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
800 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
801 if (!self
->strut
.bottom
&&
802 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
803 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
804 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
805 *x
= a
->x
- self
->frame
->area
.width
*9/10;
806 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
807 *y
= a
->y
- self
->frame
->area
.width
*9/10;
810 /* This here doesn't let windows even a pixel outside the screen,
811 * when called from client_manage, programs placing themselves are
812 * forced completely onscreen, while things like
813 * xterm -geometry resolution-width/2 will work fine. Trying to
814 * place it completely offscreen will be handled in the above code.
815 * Sorry for this confused comment, i am tired. */
817 /* avoid the xinerama monitor divide while we're at it,
818 * remember to fix the placement stuff to avoid it also and
819 * then remove this XXX */
820 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
821 /* dont let windows map into the strut unless they
822 are bigger than the available area */
824 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
825 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
826 *x
= a
->x
+ a
->width
- w
;
828 if (h
<= a
->height
) {
829 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
830 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
831 *y
= a
->y
+ a
->height
- h
;
835 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
838 return ox
!= *x
|| oy
!= *y
;
841 static void client_toggle_border(ObClient
*self
, gboolean show
)
843 /* adjust our idea of where the client is, based on its border. When the
844 border is removed, the client should now be considered to be in a
846 when re-adding the border to the client, the same operation needs to be
848 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
849 gint x
= oldx
, y
= oldy
;
850 switch(self
->gravity
) {
852 case NorthWestGravity
:
854 case SouthWestGravity
:
856 case NorthEastGravity
:
858 case SouthEastGravity
:
859 if (show
) x
-= self
->border_width
* 2;
860 else x
+= self
->border_width
* 2;
867 if (show
) x
-= self
->border_width
;
868 else x
+= self
->border_width
;
871 switch(self
->gravity
) {
873 case NorthWestGravity
:
875 case NorthEastGravity
:
877 case SouthWestGravity
:
879 case SouthEastGravity
:
880 if (show
) y
-= self
->border_width
* 2;
881 else y
+= self
->border_width
* 2;
888 if (show
) y
-= self
->border_width
;
889 else y
+= self
->border_width
;
896 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
898 /* set border_width to 0 because there is no border to add into
899 calculations anymore */
900 self
->border_width
= 0;
902 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
906 static void client_get_all(ObClient
*self
)
908 client_get_area(self
);
909 client_get_mwm_hints(self
);
911 /* The transient hint is used to pick a type, but the type can also affect
912 transiency (dialogs are always made transients of their group if they
913 have one). This is Havoc's idea, but it is needed to make some apps
914 work right (eg tsclient). */
915 client_update_transient_for(self
);
916 client_get_type(self
);/* this can change the mwmhints for special cases */
917 client_get_state(self
);
918 client_update_transient_for(self
);
920 client_update_wmhints(self
);
921 client_get_startup_id(self
);
922 client_get_desktop(self
);/* uses transient data/group/startup id if a
923 desktop is not specified */
924 client_get_shaped(self
);
926 client_get_layer(self
); /* if layer hasn't been specified, get it from
927 other sources if possible */
930 /* a couple type-based defaults for new windows */
932 /* this makes sure that these windows appear on all desktops */
933 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
934 self
->desktop
= DESKTOP_ALL
;
937 client_update_protocols(self
);
939 client_get_gravity(self
); /* get the attribute gravity */
940 client_update_normal_hints(self
); /* this may override the attribute
943 /* got the type, the mwmhints, the protocols, and the normal hints
944 (min/max sizes), so we're ready to set up the decorations/functions */
945 client_setup_decor_and_functions(self
);
947 client_update_title(self
);
948 client_update_class(self
);
949 client_update_sm_client_id(self
);
950 client_update_strut(self
);
951 client_update_icons(self
);
952 client_update_user_time(self
, FALSE
);
955 static void client_get_startup_id(ObClient
*self
)
957 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
959 PROP_GETS(self
->group
->leader
,
960 net_startup_id
, utf8
, &self
->startup_id
);
963 static void client_get_area(ObClient
*self
)
965 XWindowAttributes wattrib
;
968 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
969 g_assert(ret
!= BadWindow
);
971 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
972 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
973 self
->border_width
= wattrib
.border_width
;
975 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
976 wattrib
.width
, wattrib
.height
);
979 static void client_get_desktop(ObClient
*self
)
981 guint32 d
= screen_num_desktops
; /* an always-invalid value */
983 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
984 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
985 self
->desktop
= screen_num_desktops
- 1;
989 gboolean trdesk
= FALSE
;
991 if (self
->transient_for
) {
992 if (self
->transient_for
!= OB_TRAN_GROUP
) {
993 self
->desktop
= self
->transient_for
->desktop
;
998 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
999 if (it
->data
!= self
&&
1000 !((ObClient
*)it
->data
)->transient_for
) {
1001 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
1008 /* try get from the startup-notification protocol */
1009 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1010 if (self
->desktop
>= screen_num_desktops
&&
1011 self
->desktop
!= DESKTOP_ALL
)
1012 self
->desktop
= screen_num_desktops
- 1;
1014 /* defaults to the current desktop */
1015 self
->desktop
= screen_desktop
;
1018 if (self
->desktop
!= d
) {
1019 /* set the desktop hint, to make sure that it always exists */
1020 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
1024 static void client_get_layer(ObClient
*self
)
1026 if (!(self
->above
|| self
->below
)) {
1028 /* apply stuff from the group */
1032 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1033 ObClient
*c
= it
->data
;
1034 if (c
!= self
&& !client_search_transient(self
, c
) &&
1035 client_normal(self
) && client_normal(c
))
1038 (c
->above
? 1 : (c
->below
? -1 : 0)));
1052 g_assert_not_reached();
1059 static void client_get_state(ObClient
*self
)
1064 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1066 for (i
= 0; i
< num
; ++i
) {
1067 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1069 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1070 self
->shaded
= TRUE
;
1071 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1072 self
->iconic
= TRUE
;
1073 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1074 self
->skip_taskbar
= TRUE
;
1075 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1076 self
->skip_pager
= TRUE
;
1077 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1078 self
->fullscreen
= TRUE
;
1079 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1080 self
->max_vert
= TRUE
;
1081 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1082 self
->max_horz
= TRUE
;
1083 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1085 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1087 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1088 self
->demands_attention
= TRUE
;
1089 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1090 self
->undecorated
= TRUE
;
1097 static void client_get_shaped(ObClient
*self
)
1099 self
->shaped
= FALSE
;
1101 if (extensions_shape
) {
1106 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1108 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1109 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1111 self
->shaped
= (s
!= 0);
1116 void client_update_transient_for(ObClient
*self
)
1119 ObClient
*target
= NULL
;
1121 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1122 self
->transient
= TRUE
;
1123 if (t
!= self
->window
) { /* cant be transient to itself! */
1124 target
= g_hash_table_lookup(window_map
, &t
);
1125 /* if this happens then we need to check for it*/
1126 g_assert(target
!= self
);
1127 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1128 /* this can happen when a dialog is a child of
1129 a dockapp, for example */
1133 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1135 Setting the transient_for to Root is actually illegal, however
1136 applications from time have done this to specify transient for
1139 Now you can do that by being a TYPE_DIALOG and not setting
1140 the transient_for hint at all on your window. But people still
1141 use Root, and Kwin is very strange in this regard.
1143 KWin 3.0 will not consider windows with transient_for set to
1144 Root as transient for their group *UNLESS* they are also modal.
1145 In that case, it will make them transient for the group. This
1146 leads to all sorts of weird behavior from KDE apps which are
1147 only tested in KWin. I'd like to follow their behavior just to
1148 make this work right with KDE stuff, but that seems wrong.
1150 if (!target
&& self
->group
) {
1151 /* not transient to a client, see if it is transient for a
1153 if (t
== RootWindow(ob_display
, ob_screen
)) {
1154 /* window is a transient for its group! */
1155 target
= OB_TRAN_GROUP
;
1159 } else if (self
->group
) {
1160 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1161 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1162 self
->type
== OB_CLIENT_TYPE_MENU
||
1163 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1165 self
->transient
= TRUE
;
1166 target
= OB_TRAN_GROUP
;
1169 self
->transient
= FALSE
;
1171 /* if anything has changed... */
1172 if (target
!= self
->transient_for
) {
1173 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1176 /* remove from old parents */
1177 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1178 ObClient
*c
= it
->data
;
1179 if (c
!= self
&& !c
->transient_for
)
1180 c
->transients
= g_slist_remove(c
->transients
, self
);
1182 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1183 /* remove from old parent */
1184 self
->transient_for
->transients
=
1185 g_slist_remove(self
->transient_for
->transients
, self
);
1187 self
->transient_for
= target
;
1188 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1191 /* add to new parents */
1192 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1193 ObClient
*c
= it
->data
;
1194 if (c
!= self
&& !c
->transient_for
)
1195 c
->transients
= g_slist_append(c
->transients
, self
);
1198 /* remove all transients which are in the group, that causes
1199 circlular pointer hell of doom */
1200 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1202 for (sit
= self
->transients
; sit
; sit
= next
) {
1203 next
= g_slist_next(sit
);
1204 if (sit
->data
== it
->data
)
1206 g_slist_delete_link(self
->transients
, sit
);
1209 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1210 /* add to new parent */
1211 self
->transient_for
->transients
=
1212 g_slist_append(self
->transient_for
->transients
, self
);
1217 static void client_get_mwm_hints(ObClient
*self
)
1222 self
->mwmhints
.flags
= 0; /* default to none */
1224 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1226 if (num
>= OB_MWM_ELEMENTS
) {
1227 self
->mwmhints
.flags
= hints
[0];
1228 self
->mwmhints
.functions
= hints
[1];
1229 self
->mwmhints
.decorations
= hints
[2];
1235 void client_get_type(ObClient
*self
)
1242 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1243 /* use the first value that we know about in the array */
1244 for (i
= 0; i
< num
; ++i
) {
1245 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1246 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1247 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1248 self
->type
= OB_CLIENT_TYPE_DOCK
;
1249 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1250 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1251 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1252 self
->type
= OB_CLIENT_TYPE_MENU
;
1253 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1254 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1255 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1256 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1257 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1258 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1259 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1260 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1261 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1262 /* prevent this window from getting any decor or
1264 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1265 OB_MWM_FLAG_DECORATIONS
);
1266 self
->mwmhints
.decorations
= 0;
1267 self
->mwmhints
.functions
= 0;
1269 if (self
->type
!= (ObClientType
) -1)
1270 break; /* grab the first legit type */
1275 if (self
->type
== (ObClientType
) -1) {
1276 /*the window type hint was not set, which means we either classify
1277 ourself as a normal window or a dialog, depending on if we are a
1279 if (self
->transient
)
1280 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1282 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1286 void client_update_protocols(ObClient
*self
)
1289 guint num_return
, i
;
1291 self
->focus_notify
= FALSE
;
1292 self
->delete_window
= FALSE
;
1294 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1295 for (i
= 0; i
< num_return
; ++i
) {
1296 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1297 /* this means we can request the window to close */
1298 self
->delete_window
= TRUE
;
1299 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1300 /* if this protocol is requested, then the window will be
1301 notified whenever we want it to receive focus */
1302 self
->focus_notify
= TRUE
;
1308 static void client_get_gravity(ObClient
*self
)
1310 XWindowAttributes wattrib
;
1313 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1314 g_assert(ret
!= BadWindow
);
1315 self
->gravity
= wattrib
.win_gravity
;
1318 void client_update_normal_hints(ObClient
*self
)
1322 gint oldgravity
= self
->gravity
;
1325 self
->min_ratio
= 0.0f
;
1326 self
->max_ratio
= 0.0f
;
1327 SIZE_SET(self
->size_inc
, 1, 1);
1328 SIZE_SET(self
->base_size
, 0, 0);
1329 SIZE_SET(self
->min_size
, 0, 0);
1330 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1332 /* get the hints from the window */
1333 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1334 /* normal windows can't request placement! har har
1335 if (!client_normal(self))
1337 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1339 if (size
.flags
& PWinGravity
) {
1340 self
->gravity
= size
.win_gravity
;
1342 /* if the client has a frame, i.e. has already been mapped and
1343 is changing its gravity */
1344 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1345 /* move our idea of the client's position based on its new
1347 self
->area
.x
= self
->frame
->area
.x
;
1348 self
->area
.y
= self
->frame
->area
.y
;
1349 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1353 if (size
.flags
& PAspect
) {
1354 if (size
.min_aspect
.y
)
1356 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1357 if (size
.max_aspect
.y
)
1359 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1362 if (size
.flags
& PMinSize
)
1363 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1365 if (size
.flags
& PMaxSize
)
1366 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1368 if (size
.flags
& PBaseSize
)
1369 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1371 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1372 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1376 void client_setup_decor_and_functions(ObClient
*self
)
1378 /* start with everything (cept fullscreen) */
1380 (OB_FRAME_DECOR_TITLEBAR
|
1381 OB_FRAME_DECOR_HANDLE
|
1382 OB_FRAME_DECOR_GRIPS
|
1383 OB_FRAME_DECOR_BORDER
|
1384 OB_FRAME_DECOR_ICON
|
1385 OB_FRAME_DECOR_ALLDESKTOPS
|
1386 OB_FRAME_DECOR_ICONIFY
|
1387 OB_FRAME_DECOR_MAXIMIZE
|
1388 OB_FRAME_DECOR_SHADE
|
1389 OB_FRAME_DECOR_CLOSE
);
1391 (OB_CLIENT_FUNC_RESIZE
|
1392 OB_CLIENT_FUNC_MOVE
|
1393 OB_CLIENT_FUNC_ICONIFY
|
1394 OB_CLIENT_FUNC_MAXIMIZE
|
1395 OB_CLIENT_FUNC_SHADE
|
1396 OB_CLIENT_FUNC_CLOSE
);
1398 if (!(self
->min_size
.width
< self
->max_size
.width
||
1399 self
->min_size
.height
< self
->max_size
.height
))
1400 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1402 switch (self
->type
) {
1403 case OB_CLIENT_TYPE_NORMAL
:
1404 /* normal windows retain all of the possible decorations and
1405 functionality, and are the only windows that you can fullscreen */
1406 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1409 case OB_CLIENT_TYPE_DIALOG
:
1410 case OB_CLIENT_TYPE_UTILITY
:
1411 /* these windows cannot be maximized */
1412 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1415 case OB_CLIENT_TYPE_MENU
:
1416 case OB_CLIENT_TYPE_TOOLBAR
:
1417 /* these windows get less functionality */
1418 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1421 case OB_CLIENT_TYPE_DESKTOP
:
1422 case OB_CLIENT_TYPE_DOCK
:
1423 case OB_CLIENT_TYPE_SPLASH
:
1424 /* none of these windows are manipulated by the window manager */
1425 self
->decorations
= 0;
1426 self
->functions
= 0;
1430 /* Mwm Hints are applied subtractively to what has already been chosen for
1431 decor and functionality */
1432 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1433 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1434 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1435 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1437 /* if the mwm hints request no handle or title, then all
1438 decorations are disabled, but keep the border if that's
1440 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1441 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1443 self
->decorations
= 0;
1448 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1449 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1450 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1451 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1452 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1453 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1454 /* dont let mwm hints kill any buttons
1455 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1456 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1457 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1458 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1460 /* dont let mwm hints kill the close button
1461 if (! (self->mwmhints.functions & MwmFunc_Close))
1462 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1466 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1467 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1468 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1469 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1470 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1471 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1473 /* can't maximize without moving/resizing */
1474 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1475 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1476 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1477 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1478 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1481 /* kill the handle on fully maxed windows */
1482 if (self
->max_vert
&& self
->max_horz
)
1483 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1485 /* finally, the user can have requested no decorations, which overrides
1486 everything (but doesnt give it a border if it doesnt have one) */
1487 if (self
->undecorated
) {
1488 if (config_theme_keepborder
)
1489 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1491 self
->decorations
= 0;
1494 /* if we don't have a titlebar, then we cannot shade! */
1495 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1496 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1498 /* now we need to check against rules for the client's current state */
1499 if (self
->fullscreen
) {
1500 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1501 OB_CLIENT_FUNC_FULLSCREEN
|
1502 OB_CLIENT_FUNC_ICONIFY
);
1503 self
->decorations
= 0;
1506 client_change_allowed_actions(self
);
1509 /* adjust the client's decorations, etc. */
1510 client_reconfigure(self
);
1514 static void client_change_allowed_actions(ObClient
*self
)
1519 /* desktop windows are kept on all desktops */
1520 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1521 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1523 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1524 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1525 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1526 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1527 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1528 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1529 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1530 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1531 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1532 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1533 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1534 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1535 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1536 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1537 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1540 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1542 /* make sure the window isn't breaking any rules now */
1544 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1545 if (self
->frame
) client_shade(self
, FALSE
);
1546 else self
->shaded
= FALSE
;
1548 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1549 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1550 else self
->iconic
= FALSE
;
1552 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1553 if (self
->frame
) client_fullscreen(self
, FALSE
);
1554 else self
->fullscreen
= FALSE
;
1556 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1558 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1559 else self
->max_vert
= self
->max_horz
= FALSE
;
1563 void client_reconfigure(ObClient
*self
)
1565 /* by making this pass FALSE for user, we avoid the emacs event storm where
1566 every configurenotify causes an update in its normal hints, i think this
1567 is generally what we want anyways... */
1568 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1569 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1572 void client_update_wmhints(ObClient
*self
)
1577 /* assume a window takes input if it doesnt specify */
1578 self
->can_focus
= TRUE
;
1580 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1581 if (hints
->flags
& InputHint
)
1582 self
->can_focus
= hints
->input
;
1584 /* only do this when first managing the window *AND* when we aren't
1586 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1587 if (hints
->flags
& StateHint
)
1588 self
->iconic
= hints
->initial_state
== IconicState
;
1590 if (!(hints
->flags
& WindowGroupHint
))
1591 hints
->window_group
= None
;
1593 /* did the group state change? */
1594 if (hints
->window_group
!=
1595 (self
->group
? self
->group
->leader
: None
)) {
1596 /* remove from the old group if there was one */
1597 if (self
->group
!= NULL
) {
1598 /* remove transients of the group */
1599 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1600 self
->transients
= g_slist_remove(self
->transients
,
1603 /* remove myself from parents in the group */
1604 if (self
->transient_for
== OB_TRAN_GROUP
) {
1605 for (it
= self
->group
->members
; it
;
1606 it
= g_slist_next(it
))
1608 ObClient
*c
= it
->data
;
1610 if (c
!= self
&& !c
->transient_for
)
1611 c
->transients
= g_slist_remove(c
->transients
,
1616 group_remove(self
->group
, self
);
1619 if (hints
->window_group
!= None
) {
1620 self
->group
= group_add(hints
->window_group
, self
);
1622 /* i can only have transients from the group if i am not
1624 if (!self
->transient_for
) {
1625 /* add other transients of the group that are already
1627 for (it
= self
->group
->members
; it
;
1628 it
= g_slist_next(it
))
1630 ObClient
*c
= it
->data
;
1631 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1633 g_slist_append(self
->transients
, c
);
1638 /* because the self->transient flag wont change from this call,
1639 we don't need to update the window's type and such, only its
1640 transient_for, and the transients lists of other windows in
1641 the group may be affected */
1642 client_update_transient_for(self
);
1645 /* the WM_HINTS can contain an icon */
1646 client_update_icons(self
);
1652 void client_update_title(ObClient
*self
)
1656 g_free(self
->title
);
1659 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1660 /* try old x stuff */
1661 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1662 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1663 if (self
->transient
) {
1665 GNOME alert windows are not given titles:
1666 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1668 data
= g_strdup("");
1670 data
= g_strdup("Unnamed Window");
1674 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1678 frame_adjust_title(self
->frame
);
1680 /* update the icon title */
1682 g_free(self
->icon_title
);
1685 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1686 /* try old x stuff */
1687 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1688 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1689 data
= g_strdup(self
->title
);
1691 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1692 self
->icon_title
= data
;
1695 void client_update_class(ObClient
*self
)
1700 if (self
->name
) g_free(self
->name
);
1701 if (self
->class) g_free(self
->class);
1702 if (self
->role
) g_free(self
->role
);
1704 self
->name
= self
->class = self
->role
= NULL
;
1706 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1708 self
->name
= g_strdup(data
[0]);
1710 self
->class = g_strdup(data
[1]);
1715 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1718 if (self
->name
== NULL
) self
->name
= g_strdup("");
1719 if (self
->class == NULL
) self
->class = g_strdup("");
1720 if (self
->role
== NULL
) self
->role
= g_strdup("");
1723 void client_update_strut(ObClient
*self
)
1727 gboolean got
= FALSE
;
1730 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1734 STRUT_PARTIAL_SET(strut
,
1735 data
[0], data
[2], data
[1], data
[3],
1736 data
[4], data
[5], data
[8], data
[9],
1737 data
[6], data
[7], data
[10], data
[11]);
1743 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1749 /* use the screen's width/height */
1750 a
= screen_physical_area();
1752 STRUT_PARTIAL_SET(strut
,
1753 data
[0], data
[2], data
[1], data
[3],
1754 a
->y
, a
->y
+ a
->height
- 1,
1755 a
->x
, a
->x
+ a
->width
- 1,
1756 a
->y
, a
->y
+ a
->height
- 1,
1757 a
->x
, a
->x
+ a
->width
- 1);
1763 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1764 0, 0, 0, 0, 0, 0, 0, 0);
1766 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1767 self
->strut
= strut
;
1769 /* updating here is pointless while we're being mapped cuz we're not in
1770 the client list yet */
1772 screen_update_areas();
1776 void client_update_icons(ObClient
*self
)
1782 for (i
= 0; i
< self
->nicons
; ++i
)
1783 g_free(self
->icons
[i
].data
);
1784 if (self
->nicons
> 0)
1785 g_free(self
->icons
);
1788 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1789 /* figure out how many valid icons are in here */
1791 while (num
- i
> 2) {
1795 if (i
> num
|| w
*h
== 0) break;
1799 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1801 /* store the icons */
1803 for (j
= 0; j
< self
->nicons
; ++j
) {
1806 w
= self
->icons
[j
].width
= data
[i
++];
1807 h
= self
->icons
[j
].height
= data
[i
++];
1809 if (w
*h
== 0) continue;
1811 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1812 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1817 self
->icons
[j
].data
[t
] =
1818 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1819 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1820 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1821 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1830 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1831 if (hints
->flags
& IconPixmapHint
) {
1833 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1834 xerror_set_ignore(TRUE
);
1835 if (!RrPixmapToRGBA(ob_rr_inst
,
1837 (hints
->flags
& IconMaskHint
?
1838 hints
->icon_mask
: None
),
1839 &self
->icons
[self
->nicons
-1].width
,
1840 &self
->icons
[self
->nicons
-1].height
,
1841 &self
->icons
[self
->nicons
-1].data
)){
1842 g_free(&self
->icons
[self
->nicons
-1]);
1845 xerror_set_ignore(FALSE
);
1852 frame_adjust_icon(self
->frame
);
1855 void client_update_user_time(ObClient
*self
, gboolean new_event
)
1859 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1860 self
->user_time
= time
;
1861 /* we set this every time, not just when it grows, because in practice
1862 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1863 backward we don't want all windows to stop focusing. we'll just
1864 assume noone is setting times older than the last one, cuz that
1865 would be pretty stupid anyways
1866 However! This is called when a window is mapped to get its user time
1867 but it's an old number, it's not changing it from new user
1868 interaction, so in that case, don't change the last user time.
1871 client_last_user_time
= time
;
1874 ob_debug("window %s user time %u\n", self->title, time);
1875 ob_debug("last user time %u\n", client_last_user_time);
1880 static void client_change_wm_state(ObClient
*self
)
1885 old
= self
->wmstate
;
1887 if (self
->shaded
|| self
->iconic
|| !self
->frame
->visible
)
1888 self
->wmstate
= IconicState
;
1890 self
->wmstate
= NormalState
;
1892 if (old
!= self
->wmstate
) {
1893 PROP_MSG(self
->window
, kde_wm_change_state
,
1894 self
->wmstate
, 1, 0, 0);
1896 state
[0] = self
->wmstate
;
1898 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1902 static void client_change_state(ObClient
*self
)
1904 gulong netstate
[11];
1909 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1911 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1913 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1914 if (self
->skip_taskbar
)
1915 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1916 if (self
->skip_pager
)
1917 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1918 if (self
->fullscreen
)
1919 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1921 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1923 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1925 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1927 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1928 if (self
->demands_attention
)
1929 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1930 if (self
->undecorated
)
1931 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1932 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1935 frame_adjust_state(self
->frame
);
1938 ObClient
*client_search_focus_tree(ObClient
*self
)
1943 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1944 if (client_focused(it
->data
)) return it
->data
;
1945 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1950 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1952 if (self
->transient_for
) {
1953 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1954 return client_search_focus_tree_full(self
->transient_for
);
1957 gboolean recursed
= FALSE
;
1959 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1960 if (!((ObClient
*)it
->data
)->transient_for
) {
1962 if ((c
= client_search_focus_tree_full(it
->data
)))
1971 /* this function checks the whole tree, the client_search_focus_tree~
1972 does not, so we need to check this window */
1973 if (client_focused(self
))
1975 return client_search_focus_tree(self
);
1978 static ObStackingLayer
calc_layer(ObClient
*self
)
1982 if (self
->fullscreen
&&
1983 (client_focused(self
) || client_search_focus_tree(self
)))
1984 l
= OB_STACKING_LAYER_FULLSCREEN
;
1985 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1986 l
= OB_STACKING_LAYER_DESKTOP
;
1987 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1988 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1989 else l
= OB_STACKING_LAYER_ABOVE
;
1991 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1992 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1993 else l
= OB_STACKING_LAYER_NORMAL
;
1998 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1999 ObStackingLayer min
, gboolean raised
)
2001 ObStackingLayer old
, own
;
2005 own
= calc_layer(self
);
2006 self
->layer
= MAX(own
, min
);
2008 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2009 client_calc_layer_recursive(it
->data
, orig
,
2011 raised
? raised
: self
->layer
!= old
);
2013 if (!raised
&& self
->layer
!= old
)
2014 if (orig
->frame
) { /* only restack if the original window is managed */
2015 stacking_remove(CLIENT_AS_WINDOW(self
));
2016 stacking_add(CLIENT_AS_WINDOW(self
));
2020 void client_calc_layer(ObClient
*self
)
2027 /* transients take on the layer of their parents */
2028 it
= client_search_all_top_parents(self
);
2030 for (; it
; it
= g_slist_next(it
))
2031 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2034 gboolean
client_should_show(ObClient
*self
)
2038 if (client_normal(self
) && screen_showing_desktop
)
2041 if (self->transient_for) {
2042 if (self->transient_for != OB_TRAN_GROUP)
2043 return client_should_show(self->transient_for);
2047 for (it = self->group->members; it; it = g_slist_next(it)) {
2048 ObClient *c = it->data;
2049 if (c != self && !c->transient_for) {
2050 if (client_should_show(c))
2057 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2063 void client_show(ObClient
*self
)
2066 if (client_should_show(self
)) {
2067 frame_show(self
->frame
);
2070 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2071 needs to be in IconicState. This includes when it is on another
2074 client_change_wm_state(self
);
2077 void client_hide(ObClient
*self
)
2079 if (!client_should_show(self
)) {
2080 frame_hide(self
->frame
);
2083 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2084 needs to be in IconicState. This includes when it is on another
2087 client_change_wm_state(self
);
2090 void client_showhide(ObClient
*self
)
2093 if (client_should_show(self
)) {
2094 frame_show(self
->frame
);
2097 frame_hide(self
->frame
);
2100 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2101 needs to be in IconicState. This includes when it is on another
2104 client_change_wm_state(self
);
2107 gboolean
client_normal(ObClient
*self
) {
2108 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2109 self
->type
== OB_CLIENT_TYPE_DOCK
||
2110 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2113 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2115 gboolean pos
= FALSE
; /* has the window's position been configured? */
2118 /* save the position, and set self->area for these to use */
2124 /* these are in a carefully crafted order.. */
2127 self
->iconic
= FALSE
;
2128 client_iconify(self
, TRUE
, FALSE
);
2130 if (self
->fullscreen
) {
2131 self
->fullscreen
= FALSE
;
2132 client_fullscreen(self
, TRUE
);
2135 if (self
->undecorated
) {
2136 self
->undecorated
= FALSE
;
2137 client_set_undecorated(self
, TRUE
);
2140 self
->shaded
= FALSE
;
2141 client_shade(self
, TRUE
);
2143 if (self
->demands_attention
) {
2144 self
->demands_attention
= FALSE
;
2145 client_hilite(self
, TRUE
);
2148 if (self
->max_vert
&& self
->max_horz
) {
2149 self
->max_vert
= self
->max_horz
= FALSE
;
2150 client_maximize(self
, TRUE
, 0);
2152 } else if (self
->max_vert
) {
2153 self
->max_vert
= FALSE
;
2154 client_maximize(self
, TRUE
, 2);
2156 } else if (self
->max_horz
) {
2157 self
->max_horz
= FALSE
;
2158 client_maximize(self
, TRUE
, 1);
2162 /* if the client didn't get positioned yet, then do so now
2163 call client_move even if the window is not being moved anywhere, because
2164 when we reparent it and decorate it, it is getting moved and we need to
2165 be telling it so with a ConfigureNotify event.
2168 /* use the saved position */
2171 client_move(self
, x
, y
);
2174 /* nothing to do for the other states:
2183 void client_try_configure(ObClient
*self
, ObCorner anchor
,
2184 gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2185 gint
*logicalw
, gint
*logicalh
,
2188 Rect desired_area
= {*x
, *y
, *w
, *h
};
2190 /* make the frame recalculate its dimentions n shit without changing
2191 anything visible for real, this way the constraints below can work with
2192 the updated frame dimensions. */
2193 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2195 /* work within the prefered sizes given by the window */
2196 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2197 gint basew
, baseh
, minw
, minh
;
2199 /* base size is substituted with min size if not specified */
2200 if (self
->base_size
.width
|| self
->base_size
.height
) {
2201 basew
= self
->base_size
.width
;
2202 baseh
= self
->base_size
.height
;
2204 basew
= self
->min_size
.width
;
2205 baseh
= self
->min_size
.height
;
2207 /* min size is substituted with base size if not specified */
2208 if (self
->min_size
.width
|| self
->min_size
.height
) {
2209 minw
= self
->min_size
.width
;
2210 minh
= self
->min_size
.height
;
2212 minw
= self
->base_size
.width
;
2213 minh
= self
->base_size
.height
;
2216 /* if this is a user-requested resize, then check against min/max
2219 /* smaller than min size or bigger than max size? */
2220 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2221 if (*w
< minw
) *w
= minw
;
2222 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2223 if (*h
< minh
) *h
= minh
;
2228 /* keep to the increments */
2229 *w
/= self
->size_inc
.width
;
2230 *h
/= self
->size_inc
.height
;
2232 /* you cannot resize to nothing */
2233 if (basew
+ *w
< 1) *w
= 1 - basew
;
2234 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2236 /* save the logical size */
2237 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2238 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2240 *w
*= self
->size_inc
.width
;
2241 *h
*= self
->size_inc
.height
;
2246 /* adjust the height to match the width for the aspect ratios.
2247 for this, min size is not substituted for base size ever. */
2248 *w
-= self
->base_size
.width
;
2249 *h
-= self
->base_size
.height
;
2251 if (!self
->fullscreen
) {
2252 if (self
->min_ratio
)
2253 if (*h
* self
->min_ratio
> *w
) {
2254 *h
= (gint
)(*w
/ self
->min_ratio
);
2256 /* you cannot resize to nothing */
2259 *w
= (gint
)(*h
* self
->min_ratio
);
2262 if (self
->max_ratio
)
2263 if (*h
* self
->max_ratio
< *w
) {
2264 *h
= (gint
)(*w
/ self
->max_ratio
);
2266 /* you cannot resize to nothing */
2269 *w
= (gint
)(*h
* self
->min_ratio
);
2274 *w
+= self
->base_size
.width
;
2275 *h
+= self
->base_size
.height
;
2278 /* gets the frame's position */
2279 frame_client_gravity(self
->frame
, x
, y
);
2281 /* these positions are frame positions, not client positions */
2283 /* set the size and position if fullscreen */
2284 if (self
->fullscreen
) {
2288 i
= screen_find_monitor(&desired_area
);
2289 a
= screen_physical_area_monitor(i
);
2296 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2297 is entering fullscreen */
2298 } else if (self
->max_horz
|| self
->max_vert
) {
2302 i
= screen_find_monitor(&desired_area
);
2303 a
= screen_area_monitor(self
->desktop
, i
);
2305 /* set the size and position if maximized */
2306 if (self
->max_horz
) {
2308 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2310 if (self
->max_vert
) {
2312 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2315 /* maximizing is not allowed if the user can't move+resize the window
2319 /* gets the client's position */
2320 frame_frame_gravity(self
->frame
, x
, y
);
2322 /* these override the above states! if you cant move you can't move! */
2324 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2328 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2329 *w
= self
->area
.width
;
2330 *h
= self
->area
.height
;
2338 case OB_CORNER_TOPLEFT
:
2340 case OB_CORNER_TOPRIGHT
:
2341 *x
-= *w
- self
->area
.width
;
2343 case OB_CORNER_BOTTOMLEFT
:
2344 *y
-= *h
- self
->area
.height
;
2346 case OB_CORNER_BOTTOMRIGHT
:
2347 *x
-= *w
- self
->area
.width
;
2348 *y
-= *h
- self
->area
.height
;
2354 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2355 gint x
, gint y
, gint w
, gint h
,
2356 gboolean user
, gboolean final
,
2357 gboolean force_reply
)
2359 gint oldw
, oldh
, oldrx
, oldry
;
2360 gboolean send_resize_client
;
2361 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2362 guint fdecor
= self
->frame
->decorations
;
2363 gboolean fhorz
= self
->frame
->max_horz
;
2364 gint logicalw
, logicalh
;
2366 /* find the new x, y, width, and height (and logical size) */
2367 client_try_configure(self
, anchor
, &x
, &y
, &w
, &h
,
2368 &logicalw
, &logicalh
, user
);
2370 /* set the logical size if things changed */
2371 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2372 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2374 /* figure out if we moved or resized or what */
2375 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2376 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2378 oldw
= self
->area
.width
;
2379 oldh
= self
->area
.height
;
2380 RECT_SET(self
->area
, x
, y
, w
, h
);
2382 /* for app-requested resizes, always resize if 'resized' is true.
2383 for user-requested ones, only resize if final is true, or when
2384 resizing in redraw mode */
2385 send_resize_client
= ((!user
&& resized
) ||
2387 (resized
&& config_resize_redraw
))));
2389 /* if the client is enlarging, then resize the client before the frame */
2390 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2391 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2393 /* find the frame's dimensions and move/resize it */
2394 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2395 moved
= resized
= TRUE
;
2396 if (moved
|| resized
)
2397 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2399 /* find the client's position relative to the root window */
2400 oldrx
= self
->root_pos
.x
;
2401 oldry
= self
->root_pos
.y
;
2402 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2403 self
->frame
->size
.left
-
2404 self
->border_width
) ||
2405 oldry
!= (signed)(self
->frame
->area
.y
+
2406 self
->frame
->size
.top
-
2407 self
->border_width
));
2409 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2413 POINT_SET(self
->root_pos
,
2414 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2416 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2417 self
->border_width
);
2419 event
.type
= ConfigureNotify
;
2420 event
.xconfigure
.display
= ob_display
;
2421 event
.xconfigure
.event
= self
->window
;
2422 event
.xconfigure
.window
= self
->window
;
2424 /* root window real coords */
2425 event
.xconfigure
.x
= self
->root_pos
.x
;
2426 event
.xconfigure
.y
= self
->root_pos
.y
;
2427 event
.xconfigure
.width
= w
;
2428 event
.xconfigure
.height
= h
;
2429 event
.xconfigure
.border_width
= 0;
2430 event
.xconfigure
.above
= self
->frame
->plate
;
2431 event
.xconfigure
.override_redirect
= FALSE
;
2432 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2433 FALSE
, StructureNotifyMask
, &event
);
2436 /* if the client is shrinking, then resize the frame before the client */
2437 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2438 XResizeWindow(ob_display
, self
->window
, w
, h
);
2443 void client_fullscreen(ObClient
*self
, gboolean fs
)
2447 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2448 self
->fullscreen
== fs
) return; /* already done */
2450 self
->fullscreen
= fs
;
2451 client_change_state(self
); /* change the state hints on the client */
2452 client_calc_layer(self
); /* and adjust out layer/stacking */
2455 self
->pre_fullscreen_area
= self
->area
;
2456 /* if the window is maximized, its area isn't all that meaningful.
2457 save it's premax area instead. */
2458 if (self
->max_horz
) {
2459 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2460 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2462 if (self
->max_vert
) {
2463 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2464 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2467 /* these are not actually used cuz client_configure will set them
2468 as appropriate when the window is fullscreened */
2473 if (self
->pre_fullscreen_area
.width
> 0 &&
2474 self
->pre_fullscreen_area
.height
> 0)
2476 x
= self
->pre_fullscreen_area
.x
;
2477 y
= self
->pre_fullscreen_area
.y
;
2478 w
= self
->pre_fullscreen_area
.width
;
2479 h
= self
->pre_fullscreen_area
.height
;
2480 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2482 /* pick some fallbacks... */
2483 a
= screen_area_monitor(self
->desktop
, 0);
2484 x
= a
->x
+ a
->width
/ 4;
2485 y
= a
->y
+ a
->height
/ 4;
2491 client_setup_decor_and_functions(self
);
2493 client_move_resize(self
, x
, y
, w
, h
);
2495 /* try focus us when we go into fullscreen mode */
2499 static void client_iconify_recursive(ObClient
*self
,
2500 gboolean iconic
, gboolean curdesk
)
2503 gboolean changed
= FALSE
;
2506 if (self
->iconic
!= iconic
) {
2507 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2511 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2512 self
->iconic
= iconic
;
2514 /* update the focus lists.. iconic windows go to the bottom of
2515 the list, put the new iconic window at the 'top of the
2517 focus_order_to_top(self
);
2522 self
->iconic
= iconic
;
2525 client_set_desktop(self
, screen_desktop
, FALSE
);
2527 /* this puts it after the current focused window */
2528 focus_order_remove(self
);
2529 focus_order_add_new(self
);
2536 client_change_state(self
);
2537 client_showhide(self
);
2538 if (STRUT_EXISTS(self
->strut
))
2539 screen_update_areas();
2542 /* iconify all direct transients */
2543 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2544 if (it
->data
!= self
)
2545 if (client_is_direct_child(self
, it
->data
))
2546 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2549 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2551 /* move up the transient chain as far as possible first */
2552 self
= client_search_top_parent(self
);
2553 client_iconify_recursive(self
, iconic
, curdesk
);
2556 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2560 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2561 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2563 /* check if already done */
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;
2569 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2570 if (dir
== 1 && !self
->max_horz
) return;
2571 if (dir
== 2 && !self
->max_vert
) return;
2574 /* we just tell it to configure in the same place and client_configure
2575 worries about filling the screen with the window */
2578 w
= self
->area
.width
;
2579 h
= self
->area
.height
;
2582 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2583 RECT_SET(self
->pre_max_area
,
2584 self
->area
.x
, self
->pre_max_area
.y
,
2585 self
->area
.width
, self
->pre_max_area
.height
);
2587 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2588 RECT_SET(self
->pre_max_area
,
2589 self
->pre_max_area
.x
, self
->area
.y
,
2590 self
->pre_max_area
.width
, self
->area
.height
);
2595 a
= screen_area_monitor(self
->desktop
, 0);
2596 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2597 if (self
->pre_max_area
.width
> 0) {
2598 x
= self
->pre_max_area
.x
;
2599 w
= self
->pre_max_area
.width
;
2601 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2602 0, self
->pre_max_area
.height
);
2604 /* pick some fallbacks... */
2605 x
= a
->x
+ a
->width
/ 4;
2609 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2610 if (self
->pre_max_area
.height
> 0) {
2611 y
= self
->pre_max_area
.y
;
2612 h
= self
->pre_max_area
.height
;
2614 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2615 self
->pre_max_area
.width
, 0);
2617 /* pick some fallbacks... */
2618 y
= a
->y
+ a
->height
/ 4;
2624 if (dir
== 0 || dir
== 1) /* horz */
2625 self
->max_horz
= max
;
2626 if (dir
== 0 || dir
== 2) /* vert */
2627 self
->max_vert
= max
;
2629 client_change_state(self
); /* change the state hints on the client */
2631 client_setup_decor_and_functions(self
);
2633 client_move_resize(self
, x
, y
, w
, h
);
2636 void client_shade(ObClient
*self
, gboolean shade
)
2638 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2639 shade
) || /* can't shade */
2640 self
->shaded
== shade
) return; /* already done */
2642 self
->shaded
= shade
;
2643 client_change_state(self
);
2644 client_change_wm_state(self
); /* the window is being hidden/shown */
2645 /* resize the frame to just the titlebar */
2646 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2649 void client_close(ObClient
*self
)
2653 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2655 /* in the case that the client provides no means to requesting that it
2656 close, we just kill it */
2657 if (!self
->delete_window
)
2661 XXX: itd be cool to do timeouts and shit here for killing the client's
2663 like... if the window is around after 5 seconds, then the close button
2664 turns a nice red, and if this function is called again, the client is
2668 ce
.xclient
.type
= ClientMessage
;
2669 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2670 ce
.xclient
.display
= ob_display
;
2671 ce
.xclient
.window
= self
->window
;
2672 ce
.xclient
.format
= 32;
2673 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2674 ce
.xclient
.data
.l
[1] = event_curtime
;
2675 ce
.xclient
.data
.l
[2] = 0l;
2676 ce
.xclient
.data
.l
[3] = 0l;
2677 ce
.xclient
.data
.l
[4] = 0l;
2678 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2681 void client_kill(ObClient
*self
)
2683 XKillClient(ob_display
, self
->window
);
2686 void client_hilite(ObClient
*self
, gboolean hilite
)
2688 if (self
->demands_attention
== hilite
)
2689 return; /* no change */
2691 /* don't allow focused windows to hilite */
2692 self
->demands_attention
= hilite
&& !client_focused(self
);
2693 if (self
->demands_attention
)
2694 frame_flash_start(self
->frame
);
2696 frame_flash_stop(self
->frame
);
2697 client_change_state(self
);
2700 void client_set_desktop_recursive(ObClient
*self
,
2701 guint target
, gboolean donthide
)
2706 if (target
!= self
->desktop
) {
2708 ob_debug("Setting desktop %u\n", target
+1);
2710 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2712 /* remove from the old desktop(s) */
2713 focus_order_remove(self
);
2715 old
= self
->desktop
;
2716 self
->desktop
= target
;
2717 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2718 /* the frame can display the current desktop state */
2719 frame_adjust_state(self
->frame
);
2720 /* 'move' the window to the new desktop */
2722 client_showhide(self
);
2723 /* raise if it was not already on the desktop */
2724 if (old
!= DESKTOP_ALL
)
2726 if (STRUT_EXISTS(self
->strut
))
2727 screen_update_areas();
2729 /* add to the new desktop(s) */
2730 if (config_focus_new
)
2731 focus_order_to_top(self
);
2733 focus_order_to_bottom(self
);
2736 /* move all transients */
2737 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2738 if (it
->data
!= self
)
2739 if (client_is_direct_child(self
, it
->data
))
2740 client_set_desktop_recursive(it
->data
, target
, donthide
);
2743 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2745 self
= client_search_top_parent(self
);
2746 client_set_desktop_recursive(self
, target
, donthide
);
2749 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2751 while (child
!= parent
&&
2752 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2753 child
= child
->transient_for
;
2754 return child
== parent
;
2757 ObClient
*client_search_modal_child(ObClient
*self
)
2762 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2763 ObClient
*c
= it
->data
;
2764 if ((ret
= client_search_modal_child(c
))) return ret
;
2765 if (c
->modal
) return c
;
2770 gboolean
client_validate(ObClient
*self
)
2774 XSync(ob_display
, FALSE
); /* get all events on the server */
2776 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2777 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2778 XPutBackEvent(ob_display
, &e
);
2785 void client_set_wm_state(ObClient
*self
, glong state
)
2787 if (state
== self
->wmstate
) return; /* no change */
2791 client_iconify(self
, TRUE
, TRUE
);
2794 client_iconify(self
, FALSE
, TRUE
);
2799 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2801 gboolean shaded
= self
->shaded
;
2802 gboolean fullscreen
= self
->fullscreen
;
2803 gboolean undecorated
= self
->undecorated
;
2804 gboolean max_horz
= self
->max_horz
;
2805 gboolean max_vert
= self
->max_vert
;
2806 gboolean modal
= self
->modal
;
2807 gboolean iconic
= self
->iconic
;
2808 gboolean demands_attention
= self
->demands_attention
;
2811 if (!(action
== prop_atoms
.net_wm_state_add
||
2812 action
== prop_atoms
.net_wm_state_remove
||
2813 action
== prop_atoms
.net_wm_state_toggle
))
2814 /* an invalid action was passed to the client message, ignore it */
2817 for (i
= 0; i
< 2; ++i
) {
2818 Atom state
= i
== 0 ? data1
: data2
;
2820 if (!state
) continue;
2822 /* if toggling, then pick whether we're adding or removing */
2823 if (action
== prop_atoms
.net_wm_state_toggle
) {
2824 if (state
== prop_atoms
.net_wm_state_modal
)
2825 action
= modal
? prop_atoms
.net_wm_state_remove
:
2826 prop_atoms
.net_wm_state_add
;
2827 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2828 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2829 prop_atoms
.net_wm_state_add
;
2830 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2831 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2832 prop_atoms
.net_wm_state_add
;
2833 else if (state
== prop_atoms
.net_wm_state_shaded
)
2834 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2835 prop_atoms
.net_wm_state_add
;
2836 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2837 action
= self
->skip_taskbar
?
2838 prop_atoms
.net_wm_state_remove
:
2839 prop_atoms
.net_wm_state_add
;
2840 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2841 action
= self
->skip_pager
?
2842 prop_atoms
.net_wm_state_remove
:
2843 prop_atoms
.net_wm_state_add
;
2844 else if (state
== prop_atoms
.net_wm_state_hidden
)
2845 action
= self
->iconic
?
2846 prop_atoms
.net_wm_state_remove
:
2847 prop_atoms
.net_wm_state_add
;
2848 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2849 action
= fullscreen
?
2850 prop_atoms
.net_wm_state_remove
:
2851 prop_atoms
.net_wm_state_add
;
2852 else if (state
== prop_atoms
.net_wm_state_above
)
2853 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2854 prop_atoms
.net_wm_state_add
;
2855 else if (state
== prop_atoms
.net_wm_state_below
)
2856 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2857 prop_atoms
.net_wm_state_add
;
2858 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2859 action
= self
->demands_attention
?
2860 prop_atoms
.net_wm_state_remove
:
2861 prop_atoms
.net_wm_state_add
;
2862 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2863 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2864 prop_atoms
.net_wm_state_add
;
2867 if (action
== prop_atoms
.net_wm_state_add
) {
2868 if (state
== prop_atoms
.net_wm_state_modal
) {
2870 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2872 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2874 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2876 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2877 self
->skip_taskbar
= TRUE
;
2878 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2879 self
->skip_pager
= TRUE
;
2880 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2882 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2884 } else if (state
== prop_atoms
.net_wm_state_above
) {
2886 self
->below
= FALSE
;
2887 } else if (state
== prop_atoms
.net_wm_state_below
) {
2888 self
->above
= FALSE
;
2890 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2891 demands_attention
= TRUE
;
2892 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2896 } else { /* action == prop_atoms.net_wm_state_remove */
2897 if (state
== prop_atoms
.net_wm_state_modal
) {
2899 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2901 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2903 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2905 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2906 self
->skip_taskbar
= FALSE
;
2907 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2908 self
->skip_pager
= FALSE
;
2909 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2911 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2913 } else if (state
== prop_atoms
.net_wm_state_above
) {
2914 self
->above
= FALSE
;
2915 } else if (state
== prop_atoms
.net_wm_state_below
) {
2916 self
->below
= FALSE
;
2917 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2918 demands_attention
= FALSE
;
2919 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2920 undecorated
= FALSE
;
2924 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2925 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2927 if (max_horz
== max_vert
) { /* both going the same way */
2928 client_maximize(self
, max_horz
, 0);
2930 client_maximize(self
, max_horz
, 1);
2931 client_maximize(self
, max_vert
, 2);
2935 if (max_horz
!= self
->max_horz
)
2936 client_maximize(self
, max_horz
, 1);
2938 client_maximize(self
, max_vert
, 2);
2941 /* change fullscreen state before shading, as it will affect if the window
2943 if (fullscreen
!= self
->fullscreen
)
2944 client_fullscreen(self
, fullscreen
);
2945 if (shaded
!= self
->shaded
)
2946 client_shade(self
, shaded
);
2947 if (undecorated
!= self
->undecorated
)
2948 client_set_undecorated(self
, undecorated
);
2949 if (modal
!= self
->modal
) {
2950 self
->modal
= modal
;
2951 /* when a window changes modality, then its stacking order with its
2952 transients needs to change */
2955 if (iconic
!= self
->iconic
)
2956 client_iconify(self
, iconic
, FALSE
);
2958 if (demands_attention
!= self
->demands_attention
)
2959 client_hilite(self
, demands_attention
);
2961 client_change_state(self
); /* change the hint to reflect these changes */
2964 ObClient
*client_focus_target(ObClient
*self
)
2966 ObClient
*child
= NULL
;
2968 child
= client_search_modal_child(self
);
2969 if (child
) return child
;
2973 gboolean
client_can_focus(ObClient
*self
)
2977 /* choose the correct target */
2978 self
= client_focus_target(self
);
2980 if (!self
->frame
->visible
)
2983 if (!(self
->can_focus
|| self
->focus_notify
))
2986 /* do a check to see if the window has already been unmapped or destroyed
2987 do this intelligently while watching out for unmaps we've generated
2988 (ignore_unmaps > 0) */
2989 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2990 DestroyNotify
, &ev
)) {
2991 XPutBackEvent(ob_display
, &ev
);
2994 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2995 UnmapNotify
, &ev
)) {
2996 if (self
->ignore_unmaps
) {
2997 self
->ignore_unmaps
--;
2999 XPutBackEvent(ob_display
, &ev
);
3007 gboolean
client_focus(ObClient
*self
)
3009 /* choose the correct target */
3010 self
= client_focus_target(self
);
3012 if (!client_can_focus(self
)) {
3013 if (!self
->frame
->visible
) {
3014 /* update the focus lists */
3015 focus_order_to_top(self
);
3020 ob_debug("Focusing client \"%s\" at time %u\n", self
->title
, event_curtime
);
3022 if (self
->can_focus
) {
3023 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3027 if (self
->focus_notify
) {
3029 ce
.xclient
.type
= ClientMessage
;
3030 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3031 ce
.xclient
.display
= ob_display
;
3032 ce
.xclient
.window
= self
->window
;
3033 ce
.xclient
.format
= 32;
3034 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3035 ce
.xclient
.data
.l
[1] = event_curtime
;
3036 ce
.xclient
.data
.l
[2] = 0l;
3037 ce
.xclient
.data
.l
[3] = 0l;
3038 ce
.xclient
.data
.l
[4] = 0l;
3039 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3043 ob_debug("%sively focusing %lx at %d\n",
3044 (self
->can_focus
? "act" : "pass"),
3045 self
->window
, (gint
) event_curtime
);
3048 /* Cause the FocusIn to come back to us. Important for desktop switches,
3049 since otherwise we'll have no FocusIn on the queue and send it off to
3050 the focus_backup. */
3051 XSync(ob_display
, FALSE
);
3055 /* Used when the current client is closed or otherwise hidden, focus_last will
3056 then prevent focus from going to the mouse pointer
3058 static void client_unfocus(ObClient
*self
)
3060 if (focus_client
== self
) {
3062 ob_debug("client_unfocus for %lx\n", self
->window
);
3064 focus_fallback(FALSE
);
3068 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3070 /* XXX do some stuff here if user is false to determine if we really want
3071 to activate it or not (a parent or group member is currently
3074 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3076 self
->window
, event_curtime
, client_last_user_time
,
3077 (user
? "user" : "application"));
3078 if (!user
&& event_curtime
&&
3079 !event_time_after(event_curtime
, client_last_user_time
))
3081 client_hilite(self
, TRUE
);
3083 if (client_normal(self
) && screen_showing_desktop
)
3084 screen_show_desktop(FALSE
);
3086 client_iconify(self
, FALSE
, here
);
3087 if (self
->desktop
!= DESKTOP_ALL
&&
3088 self
->desktop
!= screen_desktop
) {
3090 client_set_desktop(self
, screen_desktop
, FALSE
);
3092 screen_set_desktop(self
->desktop
);
3093 } else if (!self
->frame
->visible
)
3094 /* if its not visible for other reasons, then don't mess
3098 client_shade(self
, FALSE
);
3102 /* we do this an action here. this is rather important. this is because
3103 we want the results from the focus change to take place BEFORE we go
3104 about raising the window. when a fullscreen window loses focus, we
3105 need this or else the raise wont be able to raise above the
3106 to-lose-focus fullscreen window. */
3111 void client_raise(ObClient
*self
)
3113 action_run_string("Raise", self
, CurrentTime
);
3116 void client_lower(ObClient
*self
)
3118 action_run_string("Lower", self
, CurrentTime
);
3121 gboolean
client_focused(ObClient
*self
)
3123 return self
== focus_client
;
3126 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3129 /* si is the smallest image >= req */
3130 /* li is the largest image < req */
3131 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3133 if (!self
->nicons
) {
3134 ObClientIcon
*parent
= NULL
;
3136 if (self
->transient_for
) {
3137 if (self
->transient_for
!= OB_TRAN_GROUP
)
3138 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3141 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3142 ObClient
*c
= it
->data
;
3143 if (c
!= self
&& !c
->transient_for
) {
3144 if ((parent
= client_icon_recursive(c
, w
, h
)))
3154 for (i
= 0; i
< self
->nicons
; ++i
) {
3155 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3156 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3160 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3165 if (largest
== 0) /* didnt find one smaller than the requested size */
3166 return &self
->icons
[si
];
3167 return &self
->icons
[li
];
3170 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3173 static ObClientIcon deficon
;
3175 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3176 deficon
.width
= deficon
.height
= 48;
3177 deficon
.data
= ob_rr_theme
->def_win_icon
;
3183 /* this be mostly ripped from fvwm */
3184 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3186 gint my_cx
, my_cy
, his_cx
, his_cy
;
3189 gint score
, best_score
;
3190 ObClient
*best_client
, *cur
;
3196 /* first, find the centre coords of the currently focused window */
3197 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3198 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3203 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3206 /* the currently selected window isn't interesting */
3209 if (!client_normal(cur
))
3211 /* using c->desktop instead of screen_desktop doesn't work if the
3212 * current window was omnipresent, hope this doesn't have any other
3214 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3218 if(!(client_focus_target(cur
) == cur
&&
3219 client_can_focus(cur
)))
3222 /* find the centre coords of this window, from the
3223 * currently focused window's point of view */
3224 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3225 + cur
->frame
->area
.width
/ 2;
3226 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3227 + cur
->frame
->area
.height
/ 2;
3229 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3230 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3232 /* Rotate the diagonals 45 degrees counterclockwise.
3233 * To do this, multiply the matrix /+h +h\ with the
3234 * vector (x y). \-h +h/
3235 * h = sqrt(0.5). We can set h := 1 since absolute
3236 * distance doesn't matter here. */
3237 tx
= his_cx
+ his_cy
;
3238 his_cy
= -his_cx
+ his_cy
;
3243 case OB_DIRECTION_NORTH
:
3244 case OB_DIRECTION_SOUTH
:
3245 case OB_DIRECTION_NORTHEAST
:
3246 case OB_DIRECTION_SOUTHWEST
:
3247 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3248 distance
= ((dir
== OB_DIRECTION_NORTH
||
3249 dir
== OB_DIRECTION_NORTHEAST
) ?
3252 case OB_DIRECTION_EAST
:
3253 case OB_DIRECTION_WEST
:
3254 case OB_DIRECTION_SOUTHEAST
:
3255 case OB_DIRECTION_NORTHWEST
:
3256 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3257 distance
= ((dir
== OB_DIRECTION_WEST
||
3258 dir
== OB_DIRECTION_NORTHWEST
) ?
3263 /* the target must be in the requested direction */
3267 /* Calculate score for this window. The smaller the better. */
3268 score
= distance
+ offset
;
3270 /* windows more than 45 degrees off the direction are
3271 * heavily penalized and will only be chosen if nothing
3272 * else within a million pixels */
3273 if(offset
> distance
)
3276 if(best_score
== -1 || score
< best_score
)
3284 void client_set_layer(ObClient
*self
, gint layer
)
3288 self
->above
= FALSE
;
3289 } else if (layer
== 0) {
3290 self
->below
= self
->above
= FALSE
;
3292 self
->below
= FALSE
;
3295 client_calc_layer(self
);
3296 client_change_state(self
); /* reflect this in the state hints */
3299 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3301 if (self
->undecorated
!= undecorated
) {
3302 self
->undecorated
= undecorated
;
3303 client_setup_decor_and_functions(self
);
3304 /* Make sure the client knows it might have moved. Maybe there is a
3305 * better way of doing this so only one client_configure is sent, but
3306 * since 125 of these are sent per second when moving the window (with
3307 * user = FALSE) i doubt it matters much.
3309 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3310 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3311 client_change_state(self
); /* reflect this in the state hints */
3315 guint
client_monitor(ObClient
*self
)
3317 return screen_find_monitor(&self
->frame
->area
);
3320 ObClient
*client_search_top_parent(ObClient
*self
)
3322 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3323 self
= self
->transient_for
;
3327 GSList
*client_search_all_top_parents(ObClient
*self
)
3331 /* move up the direct transient chain as far as possible */
3332 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3333 self
= self
->transient_for
;
3335 if (!self
->transient_for
)
3336 ret
= g_slist_prepend(ret
, self
);
3340 g_assert(self
->group
);
3342 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3343 ObClient
*c
= it
->data
;
3345 if (!c
->transient_for
)
3346 ret
= g_slist_prepend(ret
, c
);
3349 if (ret
== NULL
) /* no group parents */
3350 ret
= g_slist_prepend(ret
, self
);
3356 ObClient
*client_search_focus_parent(ObClient
*self
)
3358 if (self
->transient_for
) {
3359 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3360 if (client_focused(self
->transient_for
))
3361 return self
->transient_for
;
3365 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3366 ObClient
*c
= it
->data
;
3368 /* checking transient_for prevents infinate loops! */
3369 if (c
!= self
&& !c
->transient_for
)
3370 if (client_focused(c
))
3379 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3381 if (self
->transient_for
) {
3382 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3383 if (self
->transient_for
== search
)
3388 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3389 ObClient
*c
= it
->data
;
3391 /* checking transient_for prevents infinate loops! */
3392 if (c
!= self
&& !c
->transient_for
)
3402 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3406 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3407 if (sit
->data
== search
)
3409 if (client_search_transient(sit
->data
, search
))
3415 void client_update_sm_client_id(ObClient
*self
)
3417 g_free(self
->sm_client_id
);
3418 self
->sm_client_id
= NULL
;
3420 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3422 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3423 &self
->sm_client_id
);
3426 #define WANT_EDGE(cur, c) \
3429 if(!client_normal(cur)) \
3431 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3435 if(cur->layer < c->layer && !config_resist_layers_below) \
3438 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3439 if ((his_edge_start >= my_edge_start && \
3440 his_edge_start <= my_edge_end) || \
3441 (my_edge_start >= his_edge_start && \
3442 my_edge_start <= his_edge_end)) \
3445 /* finds the nearest edge in the given direction from the current client
3446 * note to self: the edge is the -frame- edge (the actual one), not the
3449 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3451 gint dest
, monitor_dest
;
3452 gint my_edge_start
, my_edge_end
, my_offset
;
3459 a
= screen_area(c
->desktop
);
3460 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3463 case OB_DIRECTION_NORTH
:
3464 my_edge_start
= c
->frame
->area
.x
;
3465 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3466 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3468 /* default: top of screen */
3469 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3470 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3471 /* if the monitor edge comes before the screen edge, */
3472 /* use that as the destination instead. (For xinerama) */
3473 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3474 dest
= monitor_dest
;
3476 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3477 gint his_edge_start
, his_edge_end
, his_offset
;
3478 ObClient
*cur
= it
->data
;
3482 his_edge_start
= cur
->frame
->area
.x
;
3483 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3484 his_offset
= cur
->frame
->area
.y
+
3485 (hang
? 0 : cur
->frame
->area
.height
);
3487 if(his_offset
+ 1 > my_offset
)
3490 if(his_offset
< dest
)
3493 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3496 case OB_DIRECTION_SOUTH
:
3497 my_edge_start
= c
->frame
->area
.x
;
3498 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3499 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3501 /* default: bottom of screen */
3502 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3503 monitor_dest
= monitor
->y
+ monitor
->height
-
3504 (hang
? c
->frame
->area
.height
: 0);
3505 /* if the monitor edge comes before the screen edge, */
3506 /* use that as the destination instead. (For xinerama) */
3507 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3508 dest
= monitor_dest
;
3510 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3511 gint his_edge_start
, his_edge_end
, his_offset
;
3512 ObClient
*cur
= it
->data
;
3516 his_edge_start
= cur
->frame
->area
.x
;
3517 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3518 his_offset
= cur
->frame
->area
.y
+
3519 (hang
? cur
->frame
->area
.height
: 0);
3522 if(his_offset
- 1 < my_offset
)
3525 if(his_offset
> dest
)
3528 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3531 case OB_DIRECTION_WEST
:
3532 my_edge_start
= c
->frame
->area
.y
;
3533 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3534 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3536 /* default: leftmost egde of screen */
3537 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3538 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3539 /* if the monitor edge comes before the screen edge, */
3540 /* use that as the destination instead. (For xinerama) */
3541 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3542 dest
= monitor_dest
;
3544 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3545 gint his_edge_start
, his_edge_end
, his_offset
;
3546 ObClient
*cur
= it
->data
;
3550 his_edge_start
= cur
->frame
->area
.y
;
3551 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3552 his_offset
= cur
->frame
->area
.x
+
3553 (hang
? 0 : cur
->frame
->area
.width
);
3555 if(his_offset
+ 1 > my_offset
)
3558 if(his_offset
< dest
)
3561 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3564 case OB_DIRECTION_EAST
:
3565 my_edge_start
= c
->frame
->area
.y
;
3566 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3567 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3569 /* default: rightmost edge of screen */
3570 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3571 monitor_dest
= monitor
->x
+ monitor
->width
-
3572 (hang
? c
->frame
->area
.width
: 0);
3573 /* if the monitor edge comes before the screen edge, */
3574 /* use that as the destination instead. (For xinerama) */
3575 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3576 dest
= monitor_dest
;
3578 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3579 gint his_edge_start
, his_edge_end
, his_offset
;
3580 ObClient
*cur
= it
->data
;
3584 his_edge_start
= cur
->frame
->area
.y
;
3585 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3586 his_offset
= cur
->frame
->area
.x
+
3587 (hang
? cur
->frame
->area
.width
: 0);
3589 if(his_offset
- 1 < my_offset
)
3592 if(his_offset
> dest
)
3595 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3598 case OB_DIRECTION_NORTHEAST
:
3599 case OB_DIRECTION_SOUTHEAST
:
3600 case OB_DIRECTION_NORTHWEST
:
3601 case OB_DIRECTION_SOUTHWEST
:
3602 /* not implemented */
3604 g_assert_not_reached();
3605 dest
= 0; /* suppress warning */
3610 ObClient
* client_under_pointer()
3614 ObClient
*ret
= NULL
;
3616 if (screen_pointer_pos(&x
, &y
)) {
3617 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3618 if (WINDOW_IS_CLIENT(it
->data
)) {
3619 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3620 if (c
->frame
->visible
&&
3621 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3631 gboolean
client_has_group_siblings(ObClient
*self
)
3633 return self
->group
&& self
->group
->members
->next
;