1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003 Ben Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
45 #include <X11/Xutil.h>
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
51 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
56 ObClientDestructor func
;
60 GList
*client_list
= NULL
;
62 static GSList
*client_destructors
= NULL
;
63 static Time client_last_user_time
= CurrentTime
;
65 static void client_get_all(ObClient
*self
);
66 static void client_toggle_border(ObClient
*self
, gboolean show
);
67 static void client_get_startup_id(ObClient
*self
);
68 static void client_get_area(ObClient
*self
);
69 static void client_get_desktop(ObClient
*self
);
70 static void client_get_state(ObClient
*self
);
71 static void client_get_layer(ObClient
*self
);
72 static void client_get_shaped(ObClient
*self
);
73 static void client_get_mwm_hints(ObClient
*self
);
74 static void client_get_gravity(ObClient
*self
);
75 static void client_showhide(ObClient
*self
);
76 static void client_change_allowed_actions(ObClient
*self
);
77 static void client_change_state(ObClient
*self
);
78 static void client_apply_startup_state(ObClient
*self
);
79 static void client_restore_session_state(ObClient
*self
);
80 static void client_restore_session_stacking(ObClient
*self
);
82 void client_startup(gboolean reconfig
)
89 void client_shutdown(gboolean reconfig
)
93 void client_add_destructor(ObClientDestructor func
, gpointer data
)
95 Destructor
*d
= g_new(Destructor
, 1);
98 client_destructors
= g_slist_prepend(client_destructors
, d
);
101 void client_remove_destructor(ObClientDestructor func
)
105 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
106 Destructor
*d
= it
->data
;
107 if (d
->func
== func
) {
109 client_destructors
= g_slist_delete_link(client_destructors
, it
);
115 void client_set_list()
117 Window
*windows
, *win_it
;
119 guint size
= g_list_length(client_list
);
121 /* create an array of the window ids */
123 windows
= g_new(Window
, size
);
125 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
126 *win_it
= ((ObClient
*)it
->data
)->window
;
130 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
131 net_client_list
, window
, (gulong
*)windows
, size
);
140 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
144 for (it = self->transients; it; it = g_slist_next(it)) {
145 if (!func(it->data, data)) return;
146 client_foreach_transient(it->data, func, data);
150 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
152 if (self->transient_for) {
153 if (self->transient_for != OB_TRAN_GROUP) {
154 if (!func(self->transient_for, data)) return;
155 client_foreach_ancestor(self->transient_for, func, data);
159 for (it = self->group->members; it; it = g_slist_next(it))
160 if (it->data != self &&
161 !((ObClient*)it->data)->transient_for) {
162 if (!func(it->data, data)) return;
163 client_foreach_ancestor(it->data, func, data);
170 void client_manage_all()
175 XWindowAttributes attrib
;
177 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
178 &w
, &w
, &children
, &nchild
);
180 /* remove all icon windows from the list */
181 for (i
= 0; i
< nchild
; i
++) {
182 if (children
[i
] == None
) continue;
183 wmhints
= XGetWMHints(ob_display
, children
[i
]);
185 if ((wmhints
->flags
& IconWindowHint
) &&
186 (wmhints
->icon_window
!= children
[i
]))
187 for (j
= 0; j
< nchild
; j
++)
188 if (children
[j
] == wmhints
->icon_window
) {
196 for (i
= 0; i
< nchild
; ++i
) {
197 if (children
[i
] == None
)
199 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
200 if (attrib
.override_redirect
) continue;
202 if (attrib
.map_state
!= IsUnmapped
)
203 client_manage(children
[i
]);
209 static ObAppSettings
*get_settings(ObClient
*client
)
211 GSList
*a
= config_per_app_settings
;
214 ObAppSettings
*app
= (ObAppSettings
*) a
->data
;
217 (app
->name
&& !app
->class && !strcmp(app
->name
, client
->name
))
218 || (app
->class && !app
->name
&& !strcmp(app
->class, client
->class))
219 || (app
->class && app
->name
&& !strcmp(app
->class, client
->class)
220 && !strcmp(app
->name
, client
->name
))
222 ob_debug("Window matching: %s\n", app
->name
);
223 /* Match if no role was specified in the per app setting, or if the
224 * string matches the beginning of the role, since apps like to set
225 * the role to things like browser-window-23c4b2f */
227 || !strncmp(app
->role
, client
->role
, strlen(app
->role
)))
236 void client_manage(Window window
)
240 XWindowAttributes attrib
;
241 XSetWindowAttributes attrib_set
;
243 gboolean activate
= FALSE
;
244 ObAppSettings
*settings
;
248 /* check if it has already been unmapped by the time we started mapping
249 the grab does a sync so we don't have to here */
250 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
251 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
253 XPutBackEvent(ob_display
, &e
);
256 return; /* don't manage it */
259 /* make sure it isn't an override-redirect window */
260 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
261 attrib
.override_redirect
)
264 return; /* don't manage it */
267 /* is the window a docking app */
268 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
269 if ((wmhint
->flags
& StateHint
) &&
270 wmhint
->initial_state
== WithdrawnState
)
272 dock_add(window
, wmhint
);
280 ob_debug("Managing window: %lx\n", window
);
282 /* choose the events we want to receive on the CLIENT window */
283 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
284 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
285 XChangeWindowAttributes(ob_display
, window
,
286 CWEventMask
|CWDontPropagate
, &attrib_set
);
289 /* create the ObClient struct, and populate it from the hints on the
291 self
= g_new0(ObClient
, 1);
292 self
->obwin
.type
= Window_Client
;
293 self
->window
= window
;
295 /* non-zero defaults */
296 self
->title_count
= 1;
297 self
->wmstate
= NormalState
;
299 self
->desktop
= screen_num_desktops
; /* always an invalid value */
300 self
->user_time
= ~0; /* maximum value, always newer than the real time */
302 client_get_all(self
);
303 client_restore_session_state(self
);
305 client_calc_layer(self
);
308 Time t
= sn_app_started(self
->startup_id
, self
->class);
309 if (t
) self
->user_time
= t
;
312 /* update the focus lists, do this before the call to change_state or
313 it can end up in the list twice! */
314 focus_order_add_new(self
);
316 client_change_state(self
);
318 /* remove the client's border (and adjust re gravity) */
319 client_toggle_border(self
, FALSE
);
321 /* specify that if we exit, the window should not be destroyed and should
322 be reparented back to root automatically */
323 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
325 /* create the decoration frame for the client window */
326 self
->frame
= frame_new(self
);
328 frame_grab_client(self
->frame
, self
);
332 client_apply_startup_state(self
);
334 /* get and set application level settings */
335 settings
= get_settings(self
);
337 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
338 client_restore_session_stacking(self
);
341 /* Don't worry, we won't actually both shade and undecorate the
342 * window when push comes to shove. */
343 if (settings
->shade
!= -1)
344 client_shade(self
, !!settings
->shade
);
345 if (settings
->decor
!= -1)
346 client_set_undecorated(self
, !settings
->decor
);
347 if (settings
->iconic
!= -1)
348 client_iconify(self
, !!settings
->iconic
, FALSE
);
349 if (settings
->skip_pager
!= -1) {
350 self
->skip_pager
= !!settings
->skip_pager
;
351 client_change_state(self
);
353 if (settings
->skip_taskbar
!= -1) {
354 self
->skip_taskbar
= !!settings
->skip_taskbar
;
355 client_change_state(self
);
358 /* 1 && -1 shouldn't be possible by the code in config.c */
359 if (settings
->max_vert
== 1 && settings
->max_horz
== 1)
360 client_maximize(self
, TRUE
, 0, TRUE
);
361 else if (settings
->max_vert
== 0 && settings
->max_horz
== 0)
362 client_maximize(self
, FALSE
, 0, TRUE
);
363 else if (settings
->max_vert
== 1 && settings
->max_horz
== 0) {
364 client_maximize(self
, TRUE
, 2, TRUE
);
365 client_maximize(self
, FALSE
, 1, TRUE
);
366 } else if (settings
->max_vert
== 0 && settings
->max_horz
== 1) {
367 client_maximize(self
, TRUE
, 1, TRUE
);
368 client_maximize(self
, FALSE
, 2, TRUE
);
371 if (settings
->fullscreen
!= -1)
372 client_fullscreen(self
, !!settings
->fullscreen
, TRUE
);
374 if (settings
->desktop
< screen_num_desktops
375 || settings
->desktop
== DESKTOP_ALL
)
376 client_set_desktop(self
, settings
->desktop
, TRUE
);
378 if (settings
->layer
> -2 && settings
->layer
< 2)
379 client_set_layer(self
, settings
->layer
);
383 /* focus the new window? */
384 if (ob_state() != OB_STATE_STARTING
&&
385 /* this means focus=true for window is same as config_focus_new=true */
386 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
387 client_search_focus_parent(self
)) &&
388 /* this checks for focus=false for the window */
389 (!settings
|| settings
->focus
!= 0) &&
390 /* note the check against Type_Normal/Dialog, not client_normal(self),
391 which would also include other types. in this case we want more
392 strict rules for focus */
393 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
394 self
->type
== OB_CLIENT_TYPE_DIALOG
))
398 if (self
->desktop
!= screen_desktop
) {
399 /* activate the window */
402 gboolean group_foc
= FALSE
;
407 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
409 if (client_focused(it
->data
))
417 (!self
->transient_for
&& (!self
->group
||
418 !self
->group
->members
->next
))) ||
419 client_search_focus_tree_full(self
) ||
421 !client_normal(focus_client
))
423 /* activate the window */
430 if (ob_state() == OB_STATE_RUNNING
) {
431 gint x
= self
->area
.x
, ox
= x
;
432 gint y
= self
->area
.y
, oy
= y
;
435 transient
= place_client(self
, &x
, &y
, settings
);
437 /* make sure the window is visible. */
438 client_find_onscreen(self
, &x
, &y
,
439 self
->frame
->area
.width
,
440 self
->frame
->area
.height
,
441 /* non-normal clients has less rules, and
442 windows that are being restored from a
443 session do also. we can assume you want
444 it back where you saved it. Clients saying
445 they placed themselves are subjected to
446 harder rules, ones that are placed by
447 place.c or by the user are allowed partially
448 off-screen and on xinerama divides (ie,
449 it is up to the placement routines to avoid
450 the xinerama divides) */
452 (((self
->positioned
& PPosition
) &&
453 !(self
->positioned
& USPosition
)) &&
454 client_normal(self
) &&
456 if (x
!= ox
|| y
!= oy
)
457 client_move(self
, x
, y
);
460 keyboard_grab_for_client(self
, TRUE
);
461 mouse_grab_for_client(self
, TRUE
);
464 /* This is focus stealing prevention, if a user_time has been set */
465 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
466 self
->window
, self
->user_time
, client_last_user_time
);
467 if (!self
->user_time
|| self
->user_time
>= client_last_user_time
||
468 client_search_focus_parent(self
) != NULL
)
470 /* since focus can change the stacking orders, if we focus the
471 window then the standard raise it gets is not enough, we need
472 to queue one for after the focus change takes place */
475 ob_debug("Focus stealing prevention activated for %s with time %u "
477 self
->title
, self
->user_time
, client_last_user_time
);
478 /* if the client isn't focused, then hilite it so the user
480 client_hilite(self
, TRUE
);
482 /* don't focus it ! (focus stealing prevention) */
487 /* This may look rather odd. Well it's because new windows are added
488 to the stacking order non-intrusively. If we're not going to focus
489 the new window or hilite it, then we raise it to the top. This will
490 take affect for things that don't get focused like splash screens.
491 Also if you don't have focus_new enabled, then it's going to get
492 raised to the top. Legacy begets legacy I guess?
497 /* this has to happen before we try focus the window, but we want it to
498 happen after the client's stacking has been determined or it looks bad
500 client_showhide(self
);
502 /* use client_focus instead of client_activate cuz client_activate does
503 stuff like switch desktops etc and I'm not interested in all that when
504 a window maps since its not based on an action from the user like
505 clicking a window to activate it. so keep the new window out of the way
508 /* if using focus_delay, stop the timer now so that focus doesn't
510 event_halt_focus_delay();
514 /* client_activate does this but we aret using it so we have to do it
516 if (screen_showing_desktop
)
517 screen_show_desktop(FALSE
);
519 /* add to client list/map */
520 client_list
= g_list_append(client_list
, self
);
521 g_hash_table_insert(window_map
, &self
->window
, self
);
523 /* this has to happen after we're in the client_list */
524 if (STRUT_EXISTS(self
->strut
))
525 screen_update_areas();
527 /* update the list hints */
530 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
533 void client_unmanage_all()
535 while (client_list
!= NULL
)
536 client_unmanage(client_list
->data
);
539 void client_unmanage(ObClient
*self
)
544 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
546 g_assert(self
!= NULL
);
548 keyboard_grab_for_client(self
, FALSE
);
549 mouse_grab_for_client(self
, FALSE
);
551 /* potentially fix focusLast */
552 if (config_focus_last
)
553 grab_pointer(TRUE
, OB_CURSOR_NONE
);
555 /* remove the window from our save set */
556 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
558 /* we dont want events no more */
559 XSelectInput(ob_display
, self
->window
, NoEventMask
);
561 frame_hide(self
->frame
);
563 client_list
= g_list_remove(client_list
, self
);
564 stacking_remove(self
);
565 g_hash_table_remove(window_map
, &self
->window
);
567 /* update the focus lists */
568 focus_order_remove(self
);
570 /* once the client is out of the list, update the struts to remove it's
572 if (STRUT_EXISTS(self
->strut
))
573 screen_update_areas();
575 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
576 Destructor
*d
= it
->data
;
577 d
->func(self
, d
->data
);
580 if (focus_client
== self
) {
583 /* focus the last focused window on the desktop, and ignore enter
584 events from the unmap so it doesnt mess with the focus */
585 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
586 /* remove these flags so we don't end up getting focused in the
588 self
->can_focus
= FALSE
;
589 self
->focus_notify
= FALSE
;
591 client_unfocus(self
);
594 /* tell our parent(s) that we're gone */
595 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
596 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
597 if (it
->data
!= self
)
598 ((ObClient
*)it
->data
)->transients
=
599 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
600 } else if (self
->transient_for
) { /* transient of window */
601 self
->transient_for
->transients
=
602 g_slist_remove(self
->transient_for
->transients
, self
);
605 /* tell our transients that we're gone */
606 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
607 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
608 ((ObClient
*)it
->data
)->transient_for
= NULL
;
609 client_calc_layer(it
->data
);
613 /* remove from its group */
615 group_remove(self
->group
, self
);
619 /* give the client its border back */
620 client_toggle_border(self
, TRUE
);
622 /* reparent the window out of the frame, and free the frame */
623 frame_release_client(self
->frame
, self
);
626 if (ob_state() != OB_STATE_EXITING
) {
627 /* these values should not be persisted across a window
629 PROP_ERASE(self
->window
, net_wm_desktop
);
630 PROP_ERASE(self
->window
, net_wm_state
);
631 PROP_ERASE(self
->window
, wm_state
);
633 /* if we're left in an unmapped state, the client wont be mapped. this
634 is bad, since we will no longer be managing the window on restart */
635 XMapWindow(ob_display
, self
->window
);
639 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
641 /* free all data allocated in the client struct */
642 g_slist_free(self
->transients
);
643 for (j
= 0; j
< self
->nicons
; ++j
)
644 g_free(self
->icons
[j
].data
);
645 if (self
->nicons
> 0)
648 g_free(self
->icon_title
);
652 g_free(self
->sm_client_id
);
655 /* update the list hints */
658 if (config_focus_last
)
659 grab_pointer(FALSE
, OB_CURSOR_NONE
);
662 static void client_restore_session_state(ObClient
*self
)
666 if (!(it
= session_state_find(self
)))
669 self
->session
= it
->data
;
671 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
672 self
->positioned
= PPosition
;
673 if (self
->session
->w
> 0)
674 self
->area
.width
= self
->session
->w
;
675 if (self
->session
->h
> 0)
676 self
->area
.height
= self
->session
->h
;
677 XResizeWindow(ob_display
, self
->window
,
678 self
->area
.width
, self
->area
.height
);
680 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
681 self
->session
->desktop
:
682 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
683 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
685 self
->shaded
= self
->session
->shaded
;
686 self
->iconic
= self
->session
->iconic
;
687 self
->skip_pager
= self
->session
->skip_pager
;
688 self
->skip_taskbar
= self
->session
->skip_taskbar
;
689 self
->fullscreen
= self
->session
->fullscreen
;
690 self
->above
= self
->session
->above
;
691 self
->below
= self
->session
->below
;
692 self
->max_horz
= self
->session
->max_horz
;
693 self
->max_vert
= self
->session
->max_vert
;
696 static void client_restore_session_stacking(ObClient
*self
)
700 if (!self
->session
) return;
702 it
= g_list_find(session_saved_state
, self
->session
);
703 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
706 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
707 if (session_state_cmp(it
->data
, cit
->data
))
710 client_calc_layer(self
);
711 stacking_below(CLIENT_AS_WINDOW(self
),
712 CLIENT_AS_WINDOW(cit
->data
));
718 void client_move_onscreen(ObClient
*self
, gboolean rude
)
720 gint x
= self
->area
.x
;
721 gint y
= self
->area
.y
;
722 if (client_find_onscreen(self
, &x
, &y
,
723 self
->frame
->area
.width
,
724 self
->frame
->area
.height
, rude
)) {
725 client_move(self
, x
, y
);
729 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
733 gint ox
= *x
, oy
= *y
;
735 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
738 /* XXX watch for xinerama dead areas */
739 /* This makes sure windows aren't entirely outside of the screen so you
740 can't see them at all.
741 It makes sure 10% of the window is on the screen at least. At don't let
742 it move itself off the top of the screen, which would hide the titlebar
743 on you. (The user can still do this if they want too, it's only limiting
746 if (client_normal(self
)) {
747 a
= screen_area(self
->desktop
);
748 if (!self
->strut
.right
&&
749 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
750 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
751 if (!self
->strut
.bottom
&&
752 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
753 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
754 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
755 *x
= a
->x
- self
->frame
->area
.width
*9/10;
756 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
757 *y
= a
->y
- self
->frame
->area
.width
*9/10;
760 /* This here doesn't let windows even a pixel outside the screen,
761 * when called from client_manage, programs placing themselves are
762 * forced completely onscreen, while things like
763 * xterm -geometry resolution-width/2 will work fine. Trying to
764 * place it completely offscreen will be handled in the above code.
765 * Sorry for this confused comment, i am tired. */
767 /* avoid the xinerama monitor divide while we're at it,
768 * remember to fix the placement stuff to avoid it also and
769 * then remove this XXX */
770 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
771 /* dont let windows map into the strut unless they
772 are bigger than the available area */
774 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
775 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
776 *x
= a
->x
+ a
->width
- w
;
778 if (h
<= a
->height
) {
779 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
780 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
781 *y
= a
->y
+ a
->height
- h
;
785 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
788 return ox
!= *x
|| oy
!= *y
;
791 static void client_toggle_border(ObClient
*self
, gboolean show
)
793 /* adjust our idea of where the client is, based on its border. When the
794 border is removed, the client should now be considered to be in a
796 when re-adding the border to the client, the same operation needs to be
798 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
799 gint x
= oldx
, y
= oldy
;
800 switch(self
->gravity
) {
802 case NorthWestGravity
:
804 case SouthWestGravity
:
806 case NorthEastGravity
:
808 case SouthEastGravity
:
809 if (show
) x
-= self
->border_width
* 2;
810 else x
+= self
->border_width
* 2;
817 if (show
) x
-= self
->border_width
;
818 else x
+= self
->border_width
;
821 switch(self
->gravity
) {
823 case NorthWestGravity
:
825 case NorthEastGravity
:
827 case SouthWestGravity
:
829 case SouthEastGravity
:
830 if (show
) y
-= self
->border_width
* 2;
831 else y
+= self
->border_width
* 2;
838 if (show
) y
-= self
->border_width
;
839 else y
+= self
->border_width
;
846 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
848 /* move the client so it is back it the right spot _with_ its
850 if (x
!= oldx
|| y
!= oldy
)
851 XMoveWindow(ob_display
, self
->window
, x
, y
);
853 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
857 static void client_get_all(ObClient
*self
)
859 client_get_area(self
);
860 client_get_mwm_hints(self
);
862 /* The transient hint is used to pick a type, but the type can also affect
863 transiency (dialogs are always made transients of their group if they
864 have one). This is Havoc's idea, but it is needed to make some apps
865 work right (eg tsclient). */
866 client_update_transient_for(self
);
867 client_get_type(self
);/* this can change the mwmhints for special cases */
868 client_get_state(self
);
869 client_update_transient_for(self
);
871 client_update_wmhints(self
);
872 client_get_startup_id(self
);
873 client_get_desktop(self
);/* uses transient data/group/startup id if a
874 desktop is not specified */
875 client_get_shaped(self
);
877 client_get_layer(self
); /* if layer hasn't been specified, get it from
878 other sources if possible */
881 /* a couple type-based defaults for new windows */
883 /* this makes sure that these windows appear on all desktops */
884 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
885 self
->desktop
= DESKTOP_ALL
;
888 client_update_protocols(self
);
890 client_get_gravity(self
); /* get the attribute gravity */
891 client_update_normal_hints(self
); /* this may override the attribute
894 /* got the type, the mwmhints, the protocols, and the normal hints
895 (min/max sizes), so we're ready to set up the decorations/functions */
896 client_setup_decor_and_functions(self
);
898 client_update_title(self
);
899 client_update_class(self
);
900 client_update_sm_client_id(self
);
901 client_update_strut(self
);
902 client_update_icons(self
);
903 client_update_user_time(self
, FALSE
);
906 static void client_get_startup_id(ObClient
*self
)
908 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
910 PROP_GETS(self
->group
->leader
,
911 net_startup_id
, utf8
, &self
->startup_id
);
914 static void client_get_area(ObClient
*self
)
916 XWindowAttributes wattrib
;
919 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
920 g_assert(ret
!= BadWindow
);
922 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
923 self
->border_width
= wattrib
.border_width
;
926 static void client_get_desktop(ObClient
*self
)
928 guint32 d
= screen_num_desktops
; /* an always-invalid value */
930 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
931 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
932 self
->desktop
= screen_num_desktops
- 1;
936 gboolean trdesk
= FALSE
;
938 if (self
->transient_for
) {
939 if (self
->transient_for
!= OB_TRAN_GROUP
) {
940 self
->desktop
= self
->transient_for
->desktop
;
945 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
946 if (it
->data
!= self
&&
947 !((ObClient
*)it
->data
)->transient_for
) {
948 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
955 /* try get from the startup-notification protocol */
956 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
957 if (self
->desktop
>= screen_num_desktops
&&
958 self
->desktop
!= DESKTOP_ALL
)
959 self
->desktop
= screen_num_desktops
- 1;
961 /* defaults to the current desktop */
962 self
->desktop
= screen_desktop
;
965 if (self
->desktop
!= d
) {
966 /* set the desktop hint, to make sure that it always exists */
967 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
971 static void client_get_layer(ObClient
*self
)
973 if (!(self
->above
|| self
->below
)) {
975 /* apply stuff from the group */
979 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
980 ObClient
*c
= it
->data
;
981 if (c
!= self
&& !client_search_transient(self
, c
) &&
982 client_normal(self
) && client_normal(c
))
985 (c
->above
? 1 : (c
->below
? -1 : 0)));
999 g_assert_not_reached();
1006 static void client_get_state(ObClient
*self
)
1011 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1013 for (i
= 0; i
< num
; ++i
) {
1014 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1016 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1017 self
->shaded
= TRUE
;
1018 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1019 self
->iconic
= TRUE
;
1020 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1021 self
->skip_taskbar
= TRUE
;
1022 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1023 self
->skip_pager
= TRUE
;
1024 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1025 self
->fullscreen
= TRUE
;
1026 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1027 self
->max_vert
= TRUE
;
1028 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1029 self
->max_horz
= TRUE
;
1030 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1032 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1034 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1035 self
->demands_attention
= TRUE
;
1036 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1037 self
->undecorated
= TRUE
;
1044 static void client_get_shaped(ObClient
*self
)
1046 self
->shaped
= FALSE
;
1048 if (extensions_shape
) {
1053 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1055 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1056 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1058 self
->shaped
= (s
!= 0);
1063 void client_update_transient_for(ObClient
*self
)
1066 ObClient
*target
= NULL
;
1068 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1069 self
->transient
= TRUE
;
1070 if (t
!= self
->window
) { /* cant be transient to itself! */
1071 target
= g_hash_table_lookup(window_map
, &t
);
1072 /* if this happens then we need to check for it*/
1073 g_assert(target
!= self
);
1074 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1075 /* this can happen when a dialog is a child of
1076 a dockapp, for example */
1080 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1082 Setting the transient_for to Root is actually illegal, however
1083 applications from time have done this to specify transient for
1086 Now you can do that by being a TYPE_DIALOG and not setting
1087 the transient_for hint at all on your window. But people still
1088 use Root, and Kwin is very strange in this regard.
1090 KWin 3.0 will not consider windows with transient_for set to
1091 Root as transient for their group *UNLESS* they are also modal.
1092 In that case, it will make them transient for the group. This
1093 leads to all sorts of weird behavior from KDE apps which are
1094 only tested in KWin. I'd like to follow their behavior just to
1095 make this work right with KDE stuff, but that seems wrong.
1097 if (!target
&& self
->group
) {
1098 /* not transient to a client, see if it is transient for a
1100 if (t
== RootWindow(ob_display
, ob_screen
)) {
1101 /* window is a transient for its group! */
1102 target
= OB_TRAN_GROUP
;
1106 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
1107 self
->transient
= TRUE
;
1108 target
= OB_TRAN_GROUP
;
1110 self
->transient
= FALSE
;
1112 /* if anything has changed... */
1113 if (target
!= self
->transient_for
) {
1114 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1117 /* remove from old parents */
1118 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1119 ObClient
*c
= it
->data
;
1120 if (c
!= self
&& !c
->transient_for
)
1121 c
->transients
= g_slist_remove(c
->transients
, self
);
1123 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1124 /* remove from old parent */
1125 self
->transient_for
->transients
=
1126 g_slist_remove(self
->transient_for
->transients
, self
);
1128 self
->transient_for
= target
;
1129 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1132 /* add to new parents */
1133 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1134 ObClient
*c
= it
->data
;
1135 if (c
!= self
&& !c
->transient_for
)
1136 c
->transients
= g_slist_append(c
->transients
, self
);
1139 /* remove all transients which are in the group, that causes
1140 circlular pointer hell of doom */
1141 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1143 for (sit
= self
->transients
; sit
; sit
= next
) {
1144 next
= g_slist_next(sit
);
1145 if (sit
->data
== it
->data
)
1147 g_slist_delete_link(self
->transients
, sit
);
1150 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1151 /* add to new parent */
1152 self
->transient_for
->transients
=
1153 g_slist_append(self
->transient_for
->transients
, self
);
1158 static void client_get_mwm_hints(ObClient
*self
)
1163 self
->mwmhints
.flags
= 0; /* default to none */
1165 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1167 if (num
>= OB_MWM_ELEMENTS
) {
1168 self
->mwmhints
.flags
= hints
[0];
1169 self
->mwmhints
.functions
= hints
[1];
1170 self
->mwmhints
.decorations
= hints
[2];
1176 void client_get_type(ObClient
*self
)
1183 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1184 /* use the first value that we know about in the array */
1185 for (i
= 0; i
< num
; ++i
) {
1186 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1187 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1188 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1189 self
->type
= OB_CLIENT_TYPE_DOCK
;
1190 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1191 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1192 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1193 self
->type
= OB_CLIENT_TYPE_MENU
;
1194 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1195 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1196 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1197 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1198 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1199 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1200 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1201 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1202 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1203 /* prevent this window from getting any decor or
1205 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1206 OB_MWM_FLAG_DECORATIONS
);
1207 self
->mwmhints
.decorations
= 0;
1208 self
->mwmhints
.functions
= 0;
1210 if (self
->type
!= (ObClientType
) -1)
1211 break; /* grab the first legit type */
1216 if (self
->type
== (ObClientType
) -1) {
1217 /*the window type hint was not set, which means we either classify
1218 ourself as a normal window or a dialog, depending on if we are a
1220 if (self
->transient
)
1221 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1223 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1227 void client_update_protocols(ObClient
*self
)
1230 guint num_return
, i
;
1232 self
->focus_notify
= FALSE
;
1233 self
->delete_window
= FALSE
;
1235 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1236 for (i
= 0; i
< num_return
; ++i
) {
1237 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1238 /* this means we can request the window to close */
1239 self
->delete_window
= TRUE
;
1240 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1241 /* if this protocol is requested, then the window will be
1242 notified whenever we want it to receive focus */
1243 self
->focus_notify
= TRUE
;
1249 static void client_get_gravity(ObClient
*self
)
1251 XWindowAttributes wattrib
;
1254 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1255 g_assert(ret
!= BadWindow
);
1256 self
->gravity
= wattrib
.win_gravity
;
1259 void client_update_normal_hints(ObClient
*self
)
1263 gint oldgravity
= self
->gravity
;
1266 self
->min_ratio
= 0.0f
;
1267 self
->max_ratio
= 0.0f
;
1268 SIZE_SET(self
->size_inc
, 1, 1);
1269 SIZE_SET(self
->base_size
, 0, 0);
1270 SIZE_SET(self
->min_size
, 0, 0);
1271 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1273 /* get the hints from the window */
1274 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1275 /* normal windows can't request placement! har har
1276 if (!client_normal(self))
1278 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1280 if (size
.flags
& PWinGravity
) {
1281 self
->gravity
= size
.win_gravity
;
1283 /* if the client has a frame, i.e. has already been mapped and
1284 is changing its gravity */
1285 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1286 /* move our idea of the client's position based on its new
1288 self
->area
.x
= self
->frame
->area
.x
;
1289 self
->area
.y
= self
->frame
->area
.y
;
1290 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1294 if (size
.flags
& PAspect
) {
1295 if (size
.min_aspect
.y
)
1297 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1298 if (size
.max_aspect
.y
)
1300 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1303 if (size
.flags
& PMinSize
)
1304 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1306 if (size
.flags
& PMaxSize
)
1307 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1309 if (size
.flags
& PBaseSize
)
1310 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1312 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1313 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1317 void client_setup_decor_and_functions(ObClient
*self
)
1319 /* start with everything (cept fullscreen) */
1321 (OB_FRAME_DECOR_TITLEBAR
|
1322 OB_FRAME_DECOR_HANDLE
|
1323 OB_FRAME_DECOR_GRIPS
|
1324 OB_FRAME_DECOR_BORDER
|
1325 OB_FRAME_DECOR_ICON
|
1326 OB_FRAME_DECOR_ALLDESKTOPS
|
1327 OB_FRAME_DECOR_ICONIFY
|
1328 OB_FRAME_DECOR_MAXIMIZE
|
1329 OB_FRAME_DECOR_SHADE
|
1330 OB_FRAME_DECOR_CLOSE
);
1332 (OB_CLIENT_FUNC_RESIZE
|
1333 OB_CLIENT_FUNC_MOVE
|
1334 OB_CLIENT_FUNC_ICONIFY
|
1335 OB_CLIENT_FUNC_MAXIMIZE
|
1336 OB_CLIENT_FUNC_SHADE
|
1337 OB_CLIENT_FUNC_CLOSE
);
1339 if (!(self
->min_size
.width
< self
->max_size
.width
||
1340 self
->min_size
.height
< self
->max_size
.height
))
1341 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1343 switch (self
->type
) {
1344 case OB_CLIENT_TYPE_NORMAL
:
1345 /* normal windows retain all of the possible decorations and
1346 functionality, and are the only windows that you can fullscreen */
1347 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1350 case OB_CLIENT_TYPE_DIALOG
:
1351 case OB_CLIENT_TYPE_UTILITY
:
1352 /* these windows cannot be maximized */
1353 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1356 case OB_CLIENT_TYPE_MENU
:
1357 case OB_CLIENT_TYPE_TOOLBAR
:
1358 /* these windows get less functionality */
1359 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1362 case OB_CLIENT_TYPE_DESKTOP
:
1363 case OB_CLIENT_TYPE_DOCK
:
1364 case OB_CLIENT_TYPE_SPLASH
:
1365 /* none of these windows are manipulated by the window manager */
1366 self
->decorations
= 0;
1367 self
->functions
= 0;
1371 /* Mwm Hints are applied subtractively to what has already been chosen for
1372 decor and functionality */
1373 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1374 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1375 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1376 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1378 /* if the mwm hints request no handle or title, then all
1379 decorations are disabled, but keep the border if that's
1381 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1382 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1384 self
->decorations
= 0;
1389 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1390 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1391 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1392 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1393 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1394 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1395 /* dont let mwm hints kill any buttons
1396 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1397 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1398 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1399 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1401 /* dont let mwm hints kill the close button
1402 if (! (self->mwmhints.functions & MwmFunc_Close))
1403 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1407 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1408 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1409 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1410 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1411 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1412 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1414 /* can't maximize without moving/resizing */
1415 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1416 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1417 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1418 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1419 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1422 /* kill the handle on fully maxed windows */
1423 if (self
->max_vert
&& self
->max_horz
)
1424 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1426 /* finally, the user can have requested no decorations, which overrides
1427 everything (but doesnt give it a border if it doesnt have one) */
1428 if (self
->undecorated
) {
1429 if (config_theme_keepborder
)
1430 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1432 self
->decorations
= 0;
1435 /* if we don't have a titlebar, then we cannot shade! */
1436 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1437 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1439 /* now we need to check against rules for the client's current state */
1440 if (self
->fullscreen
) {
1441 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1442 OB_CLIENT_FUNC_FULLSCREEN
|
1443 OB_CLIENT_FUNC_ICONIFY
);
1444 self
->decorations
= 0;
1447 client_change_allowed_actions(self
);
1450 /* adjust the client's decorations, etc. */
1451 client_reconfigure(self
);
1455 static void client_change_allowed_actions(ObClient
*self
)
1460 /* desktop windows are kept on all desktops */
1461 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1462 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1464 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1465 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1466 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1467 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1468 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1469 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1470 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1471 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1472 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1473 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1474 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1475 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1476 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1477 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1478 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1481 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1483 /* make sure the window isn't breaking any rules now */
1485 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1486 if (self
->frame
) client_shade(self
, FALSE
);
1487 else self
->shaded
= FALSE
;
1489 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1490 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1491 else self
->iconic
= FALSE
;
1493 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1494 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1495 else self
->fullscreen
= FALSE
;
1497 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1499 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1500 else self
->max_vert
= self
->max_horz
= FALSE
;
1504 void client_reconfigure(ObClient
*self
)
1506 /* by making this pass FALSE for user, we avoid the emacs event storm where
1507 every configurenotify causes an update in its normal hints, i think this
1508 is generally what we want anyways... */
1509 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1510 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1513 void client_update_wmhints(ObClient
*self
)
1518 /* assume a window takes input if it doesnt specify */
1519 self
->can_focus
= TRUE
;
1521 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1522 if (hints
->flags
& InputHint
)
1523 self
->can_focus
= hints
->input
;
1525 /* only do this when first managing the window *AND* when we aren't
1527 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1528 if (hints
->flags
& StateHint
)
1529 self
->iconic
= hints
->initial_state
== IconicState
;
1531 if (!(hints
->flags
& WindowGroupHint
))
1532 hints
->window_group
= None
;
1534 /* did the group state change? */
1535 if (hints
->window_group
!=
1536 (self
->group
? self
->group
->leader
: None
)) {
1537 /* remove from the old group if there was one */
1538 if (self
->group
!= NULL
) {
1539 /* remove transients of the group */
1540 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1541 self
->transients
= g_slist_remove(self
->transients
,
1544 /* remove myself from parents in the group */
1545 if (self
->transient_for
== OB_TRAN_GROUP
) {
1546 for (it
= self
->group
->members
; it
;
1547 it
= g_slist_next(it
))
1549 ObClient
*c
= it
->data
;
1551 if (c
!= self
&& !c
->transient_for
)
1552 c
->transients
= g_slist_remove(c
->transients
,
1557 group_remove(self
->group
, self
);
1560 if (hints
->window_group
!= None
) {
1561 self
->group
= group_add(hints
->window_group
, self
);
1563 /* i can only have transients from the group if i am not
1565 if (!self
->transient_for
) {
1566 /* add other transients of the group that are already
1568 for (it
= self
->group
->members
; it
;
1569 it
= g_slist_next(it
))
1571 ObClient
*c
= it
->data
;
1572 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1574 g_slist_append(self
->transients
, c
);
1579 /* because the self->transient flag wont change from this call,
1580 we don't need to update the window's type and such, only its
1581 transient_for, and the transients lists of other windows in
1582 the group may be affected */
1583 client_update_transient_for(self
);
1586 /* the WM_HINTS can contain an icon */
1587 client_update_icons(self
);
1593 void client_update_title(ObClient
*self
)
1599 gboolean read_title
;
1602 old_title
= self
->title
;
1605 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1606 /* try old x stuff */
1607 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1608 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1609 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1610 if (self
->transient
) {
1611 data
= g_strdup("");
1614 data
= g_strdup("Unnamed Window");
1618 if (config_title_number
) {
1620 /* did the title change? then reset the title_count */
1621 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1622 self
->title_count
= 1;
1624 /* look for duplicates and append a number */
1626 for (it
= client_list
; it
; it
= g_list_next(it
))
1627 if (it
->data
!= self
) {
1628 ObClient
*c
= it
->data
;
1630 if (c
->title_count
== 1) {
1631 if (!strcmp(c
->title
, data
))
1632 nums
|= 1 << c
->title_count
;
1637 /* find the beginning of our " - [%u]", this relies on
1638 that syntax being used */
1639 end
= strrchr(c
->title
, '-') - 1;
1640 len
= end
- c
->title
;
1641 if (!strncmp(c
->title
, data
, len
))
1642 nums
|= 1 << c
->title_count
;
1645 /* find first free number */
1646 for (i
= 1; i
<= 32; ++i
)
1647 if (!(nums
& (1 << i
))) {
1648 if (self
->title_count
== 1 || i
== 1)
1649 self
->title_count
= i
;
1652 /* dont display the number for the first window */
1653 if (self
->title_count
> 1) {
1655 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1660 self
->title_count
= 1;
1663 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1667 frame_adjust_title(self
->frame
);
1671 /* update the icon title */
1673 g_free(self
->icon_title
);
1677 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1678 /* try old x stuff */
1679 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)
1680 || PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
))) {
1681 data
= g_strdup(self
->title
);
1685 /* append the title count, dont display the number for the first window.
1686 * We don't need to check for config_title_number here since title_count
1687 * is not set above 1 then. */
1688 if (read_title
&& self
->title_count
> 1) {
1690 newdata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1695 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1697 self
->icon_title
= data
;
1700 void client_update_class(ObClient
*self
)
1705 if (self
->name
) g_free(self
->name
);
1706 if (self
->class) g_free(self
->class);
1707 if (self
->role
) g_free(self
->role
);
1709 self
->name
= self
->class = self
->role
= NULL
;
1711 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1713 self
->name
= g_strdup(data
[0]);
1715 self
->class = g_strdup(data
[1]);
1720 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1723 if (self
->name
== NULL
) self
->name
= g_strdup("");
1724 if (self
->class == NULL
) self
->class = g_strdup("");
1725 if (self
->role
== NULL
) self
->role
= g_strdup("");
1728 void client_update_strut(ObClient
*self
)
1732 gboolean got
= FALSE
;
1735 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1739 STRUT_PARTIAL_SET(strut
,
1740 data
[0], data
[2], data
[1], data
[3],
1741 data
[4], data
[5], data
[8], data
[9],
1742 data
[6], data
[7], data
[10], data
[11]);
1748 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1754 /* use the screen's width/height */
1755 a
= screen_physical_area();
1757 STRUT_PARTIAL_SET(strut
,
1758 data
[0], data
[2], data
[1], data
[3],
1759 a
->y
, a
->y
+ a
->height
- 1,
1760 a
->x
, a
->x
+ a
->width
- 1,
1761 a
->y
, a
->y
+ a
->height
- 1,
1762 a
->x
, a
->x
+ a
->width
- 1);
1768 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1769 0, 0, 0, 0, 0, 0, 0, 0);
1771 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1772 self
->strut
= strut
;
1774 /* updating here is pointless while we're being mapped cuz we're not in
1775 the client list yet */
1777 screen_update_areas();
1781 void client_update_icons(ObClient
*self
)
1787 for (i
= 0; i
< self
->nicons
; ++i
)
1788 g_free(self
->icons
[i
].data
);
1789 if (self
->nicons
> 0)
1790 g_free(self
->icons
);
1793 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1794 /* figure out how many valid icons are in here */
1796 while (num
- i
> 2) {
1800 if (i
> num
|| w
*h
== 0) break;
1804 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1806 /* store the icons */
1808 for (j
= 0; j
< self
->nicons
; ++j
) {
1811 w
= self
->icons
[j
].width
= data
[i
++];
1812 h
= self
->icons
[j
].height
= data
[i
++];
1814 if (w
*h
== 0) continue;
1816 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1817 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1822 self
->icons
[j
].data
[t
] =
1823 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1824 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1825 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1826 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1835 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1836 if (hints
->flags
& IconPixmapHint
) {
1838 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1839 xerror_set_ignore(TRUE
);
1840 if (!RrPixmapToRGBA(ob_rr_inst
,
1842 (hints
->flags
& IconMaskHint
?
1843 hints
->icon_mask
: None
),
1844 &self
->icons
[self
->nicons
-1].width
,
1845 &self
->icons
[self
->nicons
-1].height
,
1846 &self
->icons
[self
->nicons
-1].data
)){
1847 g_free(&self
->icons
[self
->nicons
-1]);
1850 xerror_set_ignore(FALSE
);
1857 frame_adjust_icon(self
->frame
);
1860 void client_update_user_time(ObClient
*self
, gboolean new_event
)
1864 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1865 self
->user_time
= time
;
1866 /* we set this every time, not just when it grows, because in practice
1867 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1868 backward we don't want all windows to stop focusing. we'll just
1869 assume noone is setting times older than the last one, cuz that
1870 would be pretty stupid anyways
1871 However! This is called when a window is mapped to get its user time
1872 but it's an old number, it's not changing it from new user
1873 interaction, so in that case, don't change the last user time.
1876 client_last_user_time
= time
;
1878 /*ob_debug("window 0x%x user time %u\n", self->window, time);*/
1882 static void client_change_state(ObClient
*self
)
1885 gulong netstate
[11];
1888 state
[0] = self
->wmstate
;
1890 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1894 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1896 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1898 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1899 if (self
->skip_taskbar
)
1900 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1901 if (self
->skip_pager
)
1902 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1903 if (self
->fullscreen
)
1904 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1906 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1908 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1910 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1912 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1913 if (self
->demands_attention
)
1914 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1915 if (self
->undecorated
)
1916 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1917 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1920 frame_adjust_state(self
->frame
);
1923 ObClient
*client_search_focus_tree(ObClient
*self
)
1928 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1929 if (client_focused(it
->data
)) return it
->data
;
1930 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1935 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1937 if (self
->transient_for
) {
1938 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1939 return client_search_focus_tree_full(self
->transient_for
);
1942 gboolean recursed
= FALSE
;
1944 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1945 if (!((ObClient
*)it
->data
)->transient_for
) {
1947 if ((c
= client_search_focus_tree_full(it
->data
)))
1956 /* this function checks the whole tree, the client_search_focus_tree~
1957 does not, so we need to check this window */
1958 if (client_focused(self
))
1960 return client_search_focus_tree(self
);
1963 static ObStackingLayer
calc_layer(ObClient
*self
)
1967 if (self
->fullscreen
&&
1968 (client_focused(self
) || client_search_focus_tree(self
)))
1969 l
= OB_STACKING_LAYER_FULLSCREEN
;
1970 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1971 l
= OB_STACKING_LAYER_DESKTOP
;
1972 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1973 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1974 else l
= OB_STACKING_LAYER_ABOVE
;
1976 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1977 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1978 else l
= OB_STACKING_LAYER_NORMAL
;
1983 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1984 ObStackingLayer min
, gboolean raised
)
1986 ObStackingLayer old
, own
;
1990 own
= calc_layer(self
);
1991 self
->layer
= MAX(own
, min
);
1993 ob_debug("layer for %s: %d\n", self
->title
, self
->layer
);
1995 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1996 client_calc_layer_recursive(it
->data
, orig
,
1998 raised
? raised
: self
->layer
!= old
);
2000 if (!raised
&& self
->layer
!= old
)
2001 if (orig
->frame
) { /* only restack if the original window is managed */
2002 stacking_remove(CLIENT_AS_WINDOW(self
));
2003 stacking_add(CLIENT_AS_WINDOW(self
));
2007 void client_calc_layer(ObClient
*self
)
2014 /* transients take on the layer of their parents */
2015 it
= client_search_top_transients(self
);
2017 for (; it
; it
= g_slist_next(it
))
2018 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2021 gboolean
client_should_show(ObClient
*self
)
2025 if (client_normal(self
) && screen_showing_desktop
)
2028 if (self->transient_for) {
2029 if (self->transient_for != OB_TRAN_GROUP)
2030 return client_should_show(self->transient_for);
2034 for (it = self->group->members; it; it = g_slist_next(it)) {
2035 ObClient *c = it->data;
2036 if (c != self && !c->transient_for) {
2037 if (client_should_show(c))
2044 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2050 static void client_showhide(ObClient
*self
)
2053 if (client_should_show(self
))
2054 frame_show(self
->frame
);
2056 frame_hide(self
->frame
);
2059 gboolean
client_normal(ObClient
*self
) {
2060 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2061 self
->type
== OB_CLIENT_TYPE_DOCK
||
2062 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2065 static void client_apply_startup_state(ObClient
*self
)
2067 /* these are in a carefully crafted order.. */
2070 self
->iconic
= FALSE
;
2071 client_iconify(self
, TRUE
, FALSE
);
2073 if (self
->fullscreen
) {
2074 self
->fullscreen
= FALSE
;
2075 client_fullscreen(self
, TRUE
, FALSE
);
2077 if (self
->undecorated
) {
2078 self
->undecorated
= FALSE
;
2079 client_set_undecorated(self
, TRUE
);
2082 self
->shaded
= FALSE
;
2083 client_shade(self
, TRUE
);
2085 if (self
->demands_attention
) {
2086 self
->demands_attention
= FALSE
;
2087 client_hilite(self
, TRUE
);
2090 if (self
->max_vert
&& self
->max_horz
) {
2091 self
->max_vert
= self
->max_horz
= FALSE
;
2092 client_maximize(self
, TRUE
, 0, FALSE
);
2093 } else if (self
->max_vert
) {
2094 self
->max_vert
= FALSE
;
2095 client_maximize(self
, TRUE
, 2, FALSE
);
2096 } else if (self
->max_horz
) {
2097 self
->max_horz
= FALSE
;
2098 client_maximize(self
, TRUE
, 1, FALSE
);
2101 /* nothing to do for the other states:
2110 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2111 gint x
, gint y
, gint w
, gint h
,
2112 gboolean user
, gboolean final
,
2113 gboolean force_reply
)
2116 gboolean send_resize_client
;
2117 gboolean moved
= FALSE
, resized
= FALSE
;
2118 guint fdecor
= self
->frame
->decorations
;
2119 gboolean fhorz
= self
->frame
->max_horz
;
2120 Rect desired_area
= {x
, y
, w
, h
};
2122 /* make the frame recalculate its dimentions n shit without changing
2123 anything visible for real, this way the constraints below can work with
2124 the updated frame dimensions. */
2125 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2127 /* gets the frame's position */
2128 frame_client_gravity(self
->frame
, &x
, &y
);
2130 /* these positions are frame positions, not client positions */
2132 /* set the size and position if fullscreen */
2133 if (self
->fullscreen
) {
2137 i
= screen_find_monitor(&desired_area
);
2138 a
= screen_physical_area_monitor(i
);
2145 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2150 i
= screen_find_monitor(&desired_area
);
2151 a
= screen_area_monitor(self
->desktop
, i
);
2153 /* set the size and position if maximized */
2154 if (self
->max_horz
) {
2156 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2158 if (self
->max_vert
) {
2160 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2164 /* gets the client's position */
2165 frame_frame_gravity(self
->frame
, &x
, &y
);
2167 /* these override the above states! if you cant move you can't move! */
2169 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2173 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2174 w
= self
->area
.width
;
2175 h
= self
->area
.height
;
2179 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2180 gint basew
, baseh
, minw
, minh
;
2182 /* base size is substituted with min size if not specified */
2183 if (self
->base_size
.width
|| self
->base_size
.height
) {
2184 basew
= self
->base_size
.width
;
2185 baseh
= self
->base_size
.height
;
2187 basew
= self
->min_size
.width
;
2188 baseh
= self
->min_size
.height
;
2190 /* min size is substituted with base size if not specified */
2191 if (self
->min_size
.width
|| self
->min_size
.height
) {
2192 minw
= self
->min_size
.width
;
2193 minh
= self
->min_size
.height
;
2195 minw
= self
->base_size
.width
;
2196 minh
= self
->base_size
.height
;
2199 /* if this is a user-requested resize, then check against min/max
2202 /* smaller than min size or bigger than max size? */
2203 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2204 if (w
< minw
) w
= minw
;
2205 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2206 if (h
< minh
) h
= minh
;
2211 /* keep to the increments */
2212 w
/= self
->size_inc
.width
;
2213 h
/= self
->size_inc
.height
;
2215 /* you cannot resize to nothing */
2216 if (basew
+ w
< 1) w
= 1 - basew
;
2217 if (baseh
+ h
< 1) h
= 1 - baseh
;
2219 /* store the logical size */
2220 SIZE_SET(self
->logical_size
,
2221 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2222 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2224 w
*= self
->size_inc
.width
;
2225 h
*= self
->size_inc
.height
;
2230 /* adjust the height to match the width for the aspect ratios.
2231 for this, min size is not substituted for base size ever. */
2232 w
-= self
->base_size
.width
;
2233 h
-= self
->base_size
.height
;
2235 if (!self
->fullscreen
) {
2236 if (self
->min_ratio
)
2237 if (h
* self
->min_ratio
> w
) {
2238 h
= (gint
)(w
/ self
->min_ratio
);
2240 /* you cannot resize to nothing */
2243 w
= (gint
)(h
* self
->min_ratio
);
2246 if (self
->max_ratio
)
2247 if (h
* self
->max_ratio
< w
) {
2248 h
= (gint
)(w
/ self
->max_ratio
);
2250 /* you cannot resize to nothing */
2253 w
= (gint
)(h
* self
->min_ratio
);
2258 w
+= self
->base_size
.width
;
2259 h
+= self
->base_size
.height
;
2266 case OB_CORNER_TOPLEFT
:
2268 case OB_CORNER_TOPRIGHT
:
2269 x
-= w
- self
->area
.width
;
2271 case OB_CORNER_BOTTOMLEFT
:
2272 y
-= h
- self
->area
.height
;
2274 case OB_CORNER_BOTTOMRIGHT
:
2275 x
-= w
- self
->area
.width
;
2276 y
-= h
- self
->area
.height
;
2280 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2281 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2283 oldw
= self
->area
.width
;
2284 oldh
= self
->area
.height
;
2285 RECT_SET(self
->area
, x
, y
, w
, h
);
2287 /* for app-requested resizes, always resize if 'resized' is true.
2288 for user-requested ones, only resize if final is true, or when
2289 resizing in redraw mode */
2290 send_resize_client
= ((!user
&& resized
) ||
2292 (resized
&& config_resize_redraw
))));
2294 /* if the client is enlarging, then resize the client before the frame */
2295 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2296 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2298 /* move/resize the frame to match the request */
2300 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2301 moved
= resized
= TRUE
;
2303 if (moved
|| resized
)
2304 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2306 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2309 event
.type
= ConfigureNotify
;
2310 event
.xconfigure
.display
= ob_display
;
2311 event
.xconfigure
.event
= self
->window
;
2312 event
.xconfigure
.window
= self
->window
;
2314 /* root window real coords */
2315 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2317 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2319 event
.xconfigure
.width
= w
;
2320 event
.xconfigure
.height
= h
;
2321 event
.xconfigure
.border_width
= 0;
2322 event
.xconfigure
.above
= self
->frame
->plate
;
2323 event
.xconfigure
.override_redirect
= FALSE
;
2324 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2325 FALSE
, StructureNotifyMask
, &event
);
2329 /* if the client is shrinking, then resize the frame before the client */
2330 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2331 XResizeWindow(ob_display
, self
->window
, w
, h
);
2336 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2340 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2341 self
->fullscreen
== fs
) return; /* already done */
2343 self
->fullscreen
= fs
;
2344 client_change_state(self
); /* change the state hints on the client */
2345 client_calc_layer(self
); /* and adjust out layer/stacking */
2349 self
->pre_fullscreen_area
= self
->area
;
2351 /* these are not actually used cuz client_configure will set them
2352 as appropriate when the window is fullscreened */
2357 if (self
->pre_fullscreen_area
.width
> 0 &&
2358 self
->pre_fullscreen_area
.height
> 0)
2360 x
= self
->pre_fullscreen_area
.x
;
2361 y
= self
->pre_fullscreen_area
.y
;
2362 w
= self
->pre_fullscreen_area
.width
;
2363 h
= self
->pre_fullscreen_area
.height
;
2364 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2366 /* pick some fallbacks... */
2367 a
= screen_area_monitor(self
->desktop
, 0);
2368 x
= a
->x
+ a
->width
/ 4;
2369 y
= a
->y
+ a
->height
/ 4;
2375 client_setup_decor_and_functions(self
);
2377 client_move_resize(self
, x
, y
, w
, h
);
2379 /* try focus us when we go into fullscreen mode */
2383 static void client_iconify_recursive(ObClient
*self
,
2384 gboolean iconic
, gboolean curdesk
)
2387 gboolean changed
= FALSE
;
2390 if (self
->iconic
!= iconic
) {
2391 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2394 self
->iconic
= iconic
;
2397 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2400 old
= self
->wmstate
;
2401 self
->wmstate
= IconicState
;
2402 if (old
!= self
->wmstate
)
2403 PROP_MSG(self
->window
, kde_wm_change_state
,
2404 self
->wmstate
, 1, 0, 0);
2406 /* update the focus lists.. iconic windows go to the bottom of
2407 the list, put the new iconic window at the 'top of the
2409 focus_order_to_top(self
);
2417 client_set_desktop(self
, screen_desktop
, FALSE
);
2419 old
= self
->wmstate
;
2420 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2421 if (old
!= self
->wmstate
)
2422 PROP_MSG(self
->window
, kde_wm_change_state
,
2423 self
->wmstate
, 1, 0, 0);
2425 /* this puts it after the current focused window */
2426 focus_order_remove(self
);
2427 focus_order_add_new(self
);
2434 client_change_state(self
);
2435 client_showhide(self
);
2436 if (STRUT_EXISTS(self
->strut
))
2437 screen_update_areas();
2440 /* iconify all transients */
2441 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2442 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2446 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2450 /* move up the transient chain as far as possible first */
2451 it
= client_search_top_transients(self
);
2453 for (; it
; it
= g_slist_next(it
))
2454 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2457 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2461 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2462 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2464 /* check if already done */
2466 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2467 if (dir
== 1 && self
->max_horz
) return;
2468 if (dir
== 2 && self
->max_vert
) return;
2470 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2471 if (dir
== 1 && !self
->max_horz
) return;
2472 if (dir
== 2 && !self
->max_vert
) return;
2475 /* we just tell it to configure in the same place and client_configure
2476 worries about filling the screen with the window */
2479 w
= self
->area
.width
;
2480 h
= self
->area
.height
;
2484 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2485 RECT_SET(self
->pre_max_area
,
2486 self
->area
.x
, self
->pre_max_area
.y
,
2487 self
->area
.width
, self
->pre_max_area
.height
);
2489 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2490 RECT_SET(self
->pre_max_area
,
2491 self
->pre_max_area
.x
, self
->area
.y
,
2492 self
->pre_max_area
.width
, self
->area
.height
);
2498 a
= screen_area_monitor(self
->desktop
, 0);
2499 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2500 if (self
->pre_max_area
.width
> 0) {
2501 x
= self
->pre_max_area
.x
;
2502 w
= self
->pre_max_area
.width
;
2504 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2505 0, self
->pre_max_area
.height
);
2507 /* pick some fallbacks... */
2508 x
= a
->x
+ a
->width
/ 4;
2512 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2513 if (self
->pre_max_area
.height
> 0) {
2514 y
= self
->pre_max_area
.y
;
2515 h
= self
->pre_max_area
.height
;
2517 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2518 self
->pre_max_area
.width
, 0);
2520 /* pick some fallbacks... */
2521 y
= a
->y
+ a
->height
/ 4;
2527 if (dir
== 0 || dir
== 1) /* horz */
2528 self
->max_horz
= max
;
2529 if (dir
== 0 || dir
== 2) /* vert */
2530 self
->max_vert
= max
;
2532 client_change_state(self
); /* change the state hints on the client */
2534 client_setup_decor_and_functions(self
);
2536 client_move_resize(self
, x
, y
, w
, h
);
2539 void client_shade(ObClient
*self
, gboolean shade
)
2541 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2542 shade
) || /* can't shade */
2543 self
->shaded
== shade
) return; /* already done */
2545 /* when we're iconic, don't change the wmstate */
2546 if (!self
->iconic
) {
2549 old
= self
->wmstate
;
2550 self
->wmstate
= shade
? IconicState
: NormalState
;
2551 if (old
!= self
->wmstate
)
2552 PROP_MSG(self
->window
, kde_wm_change_state
,
2553 self
->wmstate
, 1, 0, 0);
2556 self
->shaded
= shade
;
2557 client_change_state(self
);
2558 /* resize the frame to just the titlebar */
2559 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2562 void client_close(ObClient
*self
)
2566 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2568 /* in the case that the client provides no means to requesting that it
2569 close, we just kill it */
2570 if (!self
->delete_window
)
2574 XXX: itd be cool to do timeouts and shit here for killing the client's
2576 like... if the window is around after 5 seconds, then the close button
2577 turns a nice red, and if this function is called again, the client is
2581 ce
.xclient
.type
= ClientMessage
;
2582 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2583 ce
.xclient
.display
= ob_display
;
2584 ce
.xclient
.window
= self
->window
;
2585 ce
.xclient
.format
= 32;
2586 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2587 ce
.xclient
.data
.l
[1] = event_curtime
;
2588 ce
.xclient
.data
.l
[2] = 0l;
2589 ce
.xclient
.data
.l
[3] = 0l;
2590 ce
.xclient
.data
.l
[4] = 0l;
2591 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2594 void client_kill(ObClient
*self
)
2596 XKillClient(ob_display
, self
->window
);
2599 void client_hilite(ObClient
*self
, gboolean hilite
)
2601 /* don't allow focused windows to hilite */
2602 self
->demands_attention
= hilite
&& !client_focused(self
);
2603 if (self
->demands_attention
)
2604 frame_flash_start(self
->frame
);
2606 frame_flash_stop(self
->frame
);
2607 client_change_state(self
);
2610 void client_set_desktop_recursive(ObClient
*self
,
2611 guint target
, gboolean donthide
)
2616 if (target
!= self
->desktop
) {
2618 ob_debug("Setting desktop %u\n", target
+1);
2620 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2622 /* remove from the old desktop(s) */
2623 focus_order_remove(self
);
2625 old
= self
->desktop
;
2626 self
->desktop
= target
;
2627 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2628 /* the frame can display the current desktop state */
2629 frame_adjust_state(self
->frame
);
2630 /* 'move' the window to the new desktop */
2632 client_showhide(self
);
2633 /* raise if it was not already on the desktop */
2634 if (old
!= DESKTOP_ALL
)
2636 if (STRUT_EXISTS(self
->strut
))
2637 screen_update_areas();
2639 /* add to the new desktop(s) */
2640 if (config_focus_new
)
2641 focus_order_to_top(self
);
2643 focus_order_to_bottom(self
);
2646 /* move all transients */
2647 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2648 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2652 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2656 it
= client_search_top_transients(self
);
2658 for(; it
; it
= g_slist_next(it
))
2659 client_set_desktop_recursive(it
->data
, target
, donthide
);
2662 ObClient
*client_search_modal_child(ObClient
*self
)
2667 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2668 ObClient
*c
= it
->data
;
2669 if ((ret
= client_search_modal_child(c
))) return ret
;
2670 if (c
->modal
) return c
;
2675 gboolean
client_validate(ObClient
*self
)
2679 XSync(ob_display
, FALSE
); /* get all events on the server */
2681 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2682 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2683 XPutBackEvent(ob_display
, &e
);
2690 void client_set_wm_state(ObClient
*self
, glong state
)
2692 if (state
== self
->wmstate
) return; /* no change */
2696 client_iconify(self
, TRUE
, TRUE
);
2699 client_iconify(self
, FALSE
, TRUE
);
2704 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2706 gboolean shaded
= self
->shaded
;
2707 gboolean fullscreen
= self
->fullscreen
;
2708 gboolean undecorated
= self
->undecorated
;
2709 gboolean max_horz
= self
->max_horz
;
2710 gboolean max_vert
= self
->max_vert
;
2711 gboolean modal
= self
->modal
;
2712 gboolean iconic
= self
->iconic
;
2713 gboolean demands_attention
= self
->demands_attention
;
2716 if (!(action
== prop_atoms
.net_wm_state_add
||
2717 action
== prop_atoms
.net_wm_state_remove
||
2718 action
== prop_atoms
.net_wm_state_toggle
))
2719 /* an invalid action was passed to the client message, ignore it */
2722 for (i
= 0; i
< 2; ++i
) {
2723 Atom state
= i
== 0 ? data1
: data2
;
2725 if (!state
) continue;
2727 /* if toggling, then pick whether we're adding or removing */
2728 if (action
== prop_atoms
.net_wm_state_toggle
) {
2729 if (state
== prop_atoms
.net_wm_state_modal
)
2730 action
= modal
? prop_atoms
.net_wm_state_remove
:
2731 prop_atoms
.net_wm_state_add
;
2732 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2733 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2734 prop_atoms
.net_wm_state_add
;
2735 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2736 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2737 prop_atoms
.net_wm_state_add
;
2738 else if (state
== prop_atoms
.net_wm_state_shaded
)
2739 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2740 prop_atoms
.net_wm_state_add
;
2741 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2742 action
= self
->skip_taskbar
?
2743 prop_atoms
.net_wm_state_remove
:
2744 prop_atoms
.net_wm_state_add
;
2745 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2746 action
= self
->skip_pager
?
2747 prop_atoms
.net_wm_state_remove
:
2748 prop_atoms
.net_wm_state_add
;
2749 else if (state
== prop_atoms
.net_wm_state_hidden
)
2750 action
= self
->iconic
?
2751 prop_atoms
.net_wm_state_remove
:
2752 prop_atoms
.net_wm_state_add
;
2753 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2754 action
= fullscreen
?
2755 prop_atoms
.net_wm_state_remove
:
2756 prop_atoms
.net_wm_state_add
;
2757 else if (state
== prop_atoms
.net_wm_state_above
)
2758 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2759 prop_atoms
.net_wm_state_add
;
2760 else if (state
== prop_atoms
.net_wm_state_below
)
2761 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2762 prop_atoms
.net_wm_state_add
;
2763 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2764 action
= self
->demands_attention
?
2765 prop_atoms
.net_wm_state_remove
:
2766 prop_atoms
.net_wm_state_add
;
2767 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2768 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2769 prop_atoms
.net_wm_state_add
;
2772 if (action
== prop_atoms
.net_wm_state_add
) {
2773 if (state
== prop_atoms
.net_wm_state_modal
) {
2775 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2777 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2779 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2781 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2782 self
->skip_taskbar
= TRUE
;
2783 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2784 self
->skip_pager
= TRUE
;
2785 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2787 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2789 } else if (state
== prop_atoms
.net_wm_state_above
) {
2791 self
->below
= FALSE
;
2792 } else if (state
== prop_atoms
.net_wm_state_below
) {
2793 self
->above
= FALSE
;
2795 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2796 demands_attention
= TRUE
;
2797 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2801 } else { /* action == prop_atoms.net_wm_state_remove */
2802 if (state
== prop_atoms
.net_wm_state_modal
) {
2804 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2806 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2808 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2810 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2811 self
->skip_taskbar
= FALSE
;
2812 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2813 self
->skip_pager
= FALSE
;
2814 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2816 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2818 } else if (state
== prop_atoms
.net_wm_state_above
) {
2819 self
->above
= FALSE
;
2820 } else if (state
== prop_atoms
.net_wm_state_below
) {
2821 self
->below
= FALSE
;
2822 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2823 demands_attention
= FALSE
;
2824 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2825 undecorated
= FALSE
;
2829 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2830 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2832 if (max_horz
== max_vert
) { /* both going the same way */
2833 client_maximize(self
, max_horz
, 0, TRUE
);
2835 client_maximize(self
, max_horz
, 1, TRUE
);
2836 client_maximize(self
, max_vert
, 2, TRUE
);
2840 if (max_horz
!= self
->max_horz
)
2841 client_maximize(self
, max_horz
, 1, TRUE
);
2843 client_maximize(self
, max_vert
, 2, TRUE
);
2846 /* change fullscreen state before shading, as it will affect if the window
2848 if (fullscreen
!= self
->fullscreen
)
2849 client_fullscreen(self
, fullscreen
, TRUE
);
2850 if (shaded
!= self
->shaded
)
2851 client_shade(self
, shaded
);
2852 if (undecorated
!= self
->undecorated
)
2853 client_set_undecorated(self
, undecorated
);
2854 if (modal
!= self
->modal
) {
2855 self
->modal
= modal
;
2856 /* when a window changes modality, then its stacking order with its
2857 transients needs to change */
2860 if (iconic
!= self
->iconic
)
2861 client_iconify(self
, iconic
, FALSE
);
2863 if (demands_attention
!= self
->demands_attention
)
2864 client_hilite(self
, demands_attention
);
2866 client_change_state(self
); /* change the hint to reflect these changes */
2869 ObClient
*client_focus_target(ObClient
*self
)
2871 ObClient
*child
= NULL
;
2873 child
= client_search_modal_child(self
);
2874 if (child
) return child
;
2878 gboolean
client_can_focus(ObClient
*self
)
2882 /* choose the correct target */
2883 self
= client_focus_target(self
);
2885 if (!self
->frame
->visible
)
2888 if (!(self
->can_focus
|| self
->focus_notify
))
2891 /* do a check to see if the window has already been unmapped or destroyed
2892 do this intelligently while watching out for unmaps we've generated
2893 (ignore_unmaps > 0) */
2894 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2895 DestroyNotify
, &ev
)) {
2896 XPutBackEvent(ob_display
, &ev
);
2899 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2900 UnmapNotify
, &ev
)) {
2901 if (self
->ignore_unmaps
) {
2902 self
->ignore_unmaps
--;
2904 XPutBackEvent(ob_display
, &ev
);
2912 gboolean
client_focus(ObClient
*self
)
2914 /* choose the correct target */
2915 self
= client_focus_target(self
);
2917 if (!client_can_focus(self
)) {
2918 if (!self
->frame
->visible
) {
2919 /* update the focus lists */
2920 focus_order_to_top(self
);
2925 if (self
->can_focus
) {
2926 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2927 I choose to use it always, hopefully to find errors quicker, if any
2928 are left. (I hate X. I hate focus events.)
2930 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2931 #799. So now it is RevertToNone again.
2933 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2937 if (self
->focus_notify
) {
2939 ce
.xclient
.type
= ClientMessage
;
2940 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2941 ce
.xclient
.display
= ob_display
;
2942 ce
.xclient
.window
= self
->window
;
2943 ce
.xclient
.format
= 32;
2944 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2945 ce
.xclient
.data
.l
[1] = event_curtime
;
2946 ce
.xclient
.data
.l
[2] = 0l;
2947 ce
.xclient
.data
.l
[3] = 0l;
2948 ce
.xclient
.data
.l
[4] = 0l;
2949 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2953 ob_debug("%sively focusing %lx at %d\n",
2954 (self
->can_focus
? "act" : "pass"),
2955 self
->window
, (gint
) event_curtime
);
2958 /* Cause the FocusIn to come back to us. Important for desktop switches,
2959 since otherwise we'll have no FocusIn on the queue and send it off to
2960 the focus_backup. */
2961 XSync(ob_display
, FALSE
);
2965 /* Used when the current client is closed, focus_last will then prevent
2966 * focus from going to the mouse pointer */
2967 void client_unfocus(ObClient
*self
)
2969 if (focus_client
== self
) {
2971 ob_debug("client_unfocus for %lx\n", self
->window
);
2973 focus_fallback(OB_FOCUS_FALLBACK_CLOSED
);
2977 void client_activate(ObClient
*self
, gboolean here
, gboolean user
, Time time
)
2979 /* XXX do some stuff here if user is false to determine if we really want
2980 to activate it or not (a parent or group member is currently
2983 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
2985 self
->window
, time
, client_last_user_time
,
2986 (user
? "user" : "application"));
2987 if (!user
&& time
&& time
< client_last_user_time
)
2988 client_hilite(self
, TRUE
);
2990 if (client_normal(self
) && screen_showing_desktop
)
2991 screen_show_desktop(FALSE
);
2993 client_iconify(self
, FALSE
, here
);
2994 if (self
->desktop
!= DESKTOP_ALL
&&
2995 self
->desktop
!= screen_desktop
) {
2997 client_set_desktop(self
, screen_desktop
, FALSE
);
2999 screen_set_desktop(self
->desktop
);
3000 } else if (!self
->frame
->visible
)
3001 /* if its not visible for other reasons, then don't mess
3005 client_shade(self
, FALSE
);
3009 /* we do this an action here. this is rather important. this is because
3010 we want the results from the focus change to take place BEFORE we go
3011 about raising the window. when a fullscreen window loses focus, we
3012 need this or else the raise wont be able to raise above the
3013 to-lose-focus fullscreen window. */
3018 void client_raise(ObClient
*self
)
3020 action_run_string("Raise", self
, CurrentTime
);
3023 void client_lower(ObClient
*self
)
3025 action_run_string("Lower", self
, CurrentTime
);
3028 gboolean
client_focused(ObClient
*self
)
3030 return self
== focus_client
;
3033 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3036 /* si is the smallest image >= req */
3037 /* li is the largest image < req */
3038 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3040 if (!self
->nicons
) {
3041 ObClientIcon
*parent
= NULL
;
3043 if (self
->transient_for
) {
3044 if (self
->transient_for
!= OB_TRAN_GROUP
)
3045 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3048 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3049 ObClient
*c
= it
->data
;
3050 if (c
!= self
&& !c
->transient_for
) {
3051 if ((parent
= client_icon_recursive(c
, w
, h
)))
3061 for (i
= 0; i
< self
->nicons
; ++i
) {
3062 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3063 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3067 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3072 if (largest
== 0) /* didnt find one smaller than the requested size */
3073 return &self
->icons
[si
];
3074 return &self
->icons
[li
];
3077 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3080 static ObClientIcon deficon
;
3082 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3083 deficon
.width
= deficon
.height
= 48;
3084 deficon
.data
= ob_rr_theme
->def_win_icon
;
3090 /* this be mostly ripped from fvwm */
3091 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3093 gint my_cx
, my_cy
, his_cx
, his_cy
;
3096 gint score
, best_score
;
3097 ObClient
*best_client
, *cur
;
3103 /* first, find the centre coords of the currently focused window */
3104 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3105 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3110 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3113 /* the currently selected window isn't interesting */
3116 if (!client_normal(cur
))
3118 /* using c->desktop instead of screen_desktop doesn't work if the
3119 * current window was omnipresent, hope this doesn't have any other
3121 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3125 if(!(client_focus_target(cur
) == cur
&&
3126 client_can_focus(cur
)))
3129 /* find the centre coords of this window, from the
3130 * currently focused window's point of view */
3131 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3132 + cur
->frame
->area
.width
/ 2;
3133 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3134 + cur
->frame
->area
.height
/ 2;
3136 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3137 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3139 /* Rotate the diagonals 45 degrees counterclockwise.
3140 * To do this, multiply the matrix /+h +h\ with the
3141 * vector (x y). \-h +h/
3142 * h = sqrt(0.5). We can set h := 1 since absolute
3143 * distance doesn't matter here. */
3144 tx
= his_cx
+ his_cy
;
3145 his_cy
= -his_cx
+ his_cy
;
3150 case OB_DIRECTION_NORTH
:
3151 case OB_DIRECTION_SOUTH
:
3152 case OB_DIRECTION_NORTHEAST
:
3153 case OB_DIRECTION_SOUTHWEST
:
3154 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3155 distance
= ((dir
== OB_DIRECTION_NORTH
||
3156 dir
== OB_DIRECTION_NORTHEAST
) ?
3159 case OB_DIRECTION_EAST
:
3160 case OB_DIRECTION_WEST
:
3161 case OB_DIRECTION_SOUTHEAST
:
3162 case OB_DIRECTION_NORTHWEST
:
3163 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3164 distance
= ((dir
== OB_DIRECTION_WEST
||
3165 dir
== OB_DIRECTION_NORTHWEST
) ?
3170 /* the target must be in the requested direction */
3174 /* Calculate score for this window. The smaller the better. */
3175 score
= distance
+ offset
;
3177 /* windows more than 45 degrees off the direction are
3178 * heavily penalized and will only be chosen if nothing
3179 * else within a million pixels */
3180 if(offset
> distance
)
3183 if(best_score
== -1 || score
< best_score
)
3191 void client_set_layer(ObClient
*self
, gint layer
)
3195 self
->above
= FALSE
;
3196 } else if (layer
== 0) {
3197 self
->below
= self
->above
= FALSE
;
3199 self
->below
= FALSE
;
3202 client_calc_layer(self
);
3203 client_change_state(self
); /* reflect this in the state hints */
3206 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3208 if (self
->undecorated
!= undecorated
) {
3209 self
->undecorated
= undecorated
;
3210 client_setup_decor_and_functions(self
);
3211 /* Make sure the client knows it might have moved. Maybe there is a
3212 * better way of doing this so only one client_configure is sent, but
3213 * since 125 of these are sent per second when moving the window (with
3214 * user = FALSE) i doubt it matters much.
3216 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3217 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3218 client_change_state(self
); /* reflect this in the state hints */
3222 guint
client_monitor(ObClient
*self
)
3224 return screen_find_monitor(&self
->frame
->area
);
3227 GSList
*client_search_top_transients(ObClient
*self
)
3231 /* move up the direct transient chain as far as possible */
3232 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3233 self
= self
->transient_for
;
3235 if (!self
->transient_for
)
3236 ret
= g_slist_prepend(ret
, self
);
3240 g_assert(self
->group
);
3242 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3243 ObClient
*c
= it
->data
;
3245 if (!c
->transient_for
)
3246 ret
= g_slist_prepend(ret
, c
);
3249 if (ret
== NULL
) /* no group parents */
3250 ret
= g_slist_prepend(ret
, self
);
3256 ObClient
*client_search_focus_parent(ObClient
*self
)
3258 if (self
->transient_for
) {
3259 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3260 if (client_focused(self
->transient_for
))
3261 return self
->transient_for
;
3265 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3266 ObClient
*c
= it
->data
;
3268 /* checking transient_for prevents infinate loops! */
3269 if (c
!= self
&& !c
->transient_for
)
3270 if (client_focused(c
))
3279 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3281 if (self
->transient_for
) {
3282 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3283 if (self
->transient_for
== search
)
3288 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3289 ObClient
*c
= it
->data
;
3291 /* checking transient_for prevents infinate loops! */
3292 if (c
!= self
&& !c
->transient_for
)
3302 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3306 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3307 if (sit
->data
== search
)
3309 if (client_search_transient(sit
->data
, search
))
3315 void client_update_sm_client_id(ObClient
*self
)
3317 g_free(self
->sm_client_id
);
3318 self
->sm_client_id
= NULL
;
3320 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3322 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3323 &self
->sm_client_id
);
3326 #define WANT_EDGE(cur, c) \
3329 if(!client_normal(cur)) \
3331 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3335 if(cur->layer < c->layer && !config_resist_layers_below) \
3338 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3339 if ((his_edge_start >= my_edge_start && \
3340 his_edge_start <= my_edge_end) || \
3341 (my_edge_start >= his_edge_start && \
3342 my_edge_start <= his_edge_end)) \
3345 /* finds the nearest edge in the given direction from the current client
3346 * note to self: the edge is the -frame- edge (the actual one), not the
3349 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3351 gint dest
, monitor_dest
;
3352 gint my_edge_start
, my_edge_end
, my_offset
;
3359 a
= screen_area(c
->desktop
);
3360 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3363 case OB_DIRECTION_NORTH
:
3364 my_edge_start
= c
->frame
->area
.x
;
3365 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3366 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3368 /* default: top of screen */
3369 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3370 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3371 /* if the monitor edge comes before the screen edge, */
3372 /* use that as the destination instead. (For xinerama) */
3373 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3374 dest
= monitor_dest
;
3376 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3377 gint his_edge_start
, his_edge_end
, his_offset
;
3378 ObClient
*cur
= it
->data
;
3382 his_edge_start
= cur
->frame
->area
.x
;
3383 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3384 his_offset
= cur
->frame
->area
.y
+
3385 (hang
? 0 : cur
->frame
->area
.height
);
3387 if(his_offset
+ 1 > my_offset
)
3390 if(his_offset
< dest
)
3393 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3396 case OB_DIRECTION_SOUTH
:
3397 my_edge_start
= c
->frame
->area
.x
;
3398 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3399 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3401 /* default: bottom of screen */
3402 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3403 monitor_dest
= monitor
->y
+ monitor
->height
-
3404 (hang
? c
->frame
->area
.height
: 0);
3405 /* if the monitor edge comes before the screen edge, */
3406 /* use that as the destination instead. (For xinerama) */
3407 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3408 dest
= monitor_dest
;
3410 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3411 gint his_edge_start
, his_edge_end
, his_offset
;
3412 ObClient
*cur
= it
->data
;
3416 his_edge_start
= cur
->frame
->area
.x
;
3417 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3418 his_offset
= cur
->frame
->area
.y
+
3419 (hang
? cur
->frame
->area
.height
: 0);
3422 if(his_offset
- 1 < my_offset
)
3425 if(his_offset
> dest
)
3428 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3431 case OB_DIRECTION_WEST
:
3432 my_edge_start
= c
->frame
->area
.y
;
3433 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3434 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3436 /* default: leftmost egde of screen */
3437 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3438 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3439 /* if the monitor edge comes before the screen edge, */
3440 /* use that as the destination instead. (For xinerama) */
3441 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3442 dest
= monitor_dest
;
3444 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3445 gint his_edge_start
, his_edge_end
, his_offset
;
3446 ObClient
*cur
= it
->data
;
3450 his_edge_start
= cur
->frame
->area
.y
;
3451 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3452 his_offset
= cur
->frame
->area
.x
+
3453 (hang
? 0 : cur
->frame
->area
.width
);
3455 if(his_offset
+ 1 > my_offset
)
3458 if(his_offset
< dest
)
3461 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3464 case OB_DIRECTION_EAST
:
3465 my_edge_start
= c
->frame
->area
.y
;
3466 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3467 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3469 /* default: rightmost edge of screen */
3470 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3471 monitor_dest
= monitor
->x
+ monitor
->width
-
3472 (hang
? c
->frame
->area
.width
: 0);
3473 /* if the monitor edge comes before the screen edge, */
3474 /* use that as the destination instead. (For xinerama) */
3475 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3476 dest
= monitor_dest
;
3478 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3479 gint his_edge_start
, his_edge_end
, his_offset
;
3480 ObClient
*cur
= it
->data
;
3484 his_edge_start
= cur
->frame
->area
.y
;
3485 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3486 his_offset
= cur
->frame
->area
.x
+
3487 (hang
? cur
->frame
->area
.width
: 0);
3489 if(his_offset
- 1 < my_offset
)
3492 if(his_offset
> dest
)
3495 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3498 case OB_DIRECTION_NORTHEAST
:
3499 case OB_DIRECTION_SOUTHEAST
:
3500 case OB_DIRECTION_NORTHWEST
:
3501 case OB_DIRECTION_SOUTHWEST
:
3502 /* not implemented */
3504 g_assert_not_reached();
3505 dest
= 0; /* suppress warning */
3510 ObClient
* client_under_pointer()
3514 ObClient
*ret
= NULL
;
3516 if (screen_pointer_pos(&x
, &y
)) {
3517 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3518 if (WINDOW_IS_CLIENT(it
->data
)) {
3519 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3520 if (c
->frame
->visible
&&
3521 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3531 gboolean
client_has_group_siblings(ObClient
*self
)
3533 return self
->group
&& self
->group
->members
->next
;