1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
45 #include <X11/Xutil.h>
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask)
50 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
55 ObClientDestructor func
;
59 GList
*client_list
= NULL
;
61 static GSList
*client_destructors
= NULL
;
62 static Time client_last_user_time
= CurrentTime
;
64 static void client_get_all(ObClient
*self
);
65 static void client_toggle_border(ObClient
*self
, gboolean show
);
66 static void client_get_startup_id(ObClient
*self
);
67 static void client_get_area(ObClient
*self
);
68 static void client_get_desktop(ObClient
*self
);
69 static void client_get_state(ObClient
*self
);
70 static void client_get_layer(ObClient
*self
);
71 static void client_get_shaped(ObClient
*self
);
72 static void client_get_mwm_hints(ObClient
*self
);
73 static void client_get_gravity(ObClient
*self
);
74 static void client_showhide(ObClient
*self
);
75 static void client_change_allowed_actions(ObClient
*self
);
76 static void client_change_state(ObClient
*self
);
77 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
78 static void client_restore_session_state(ObClient
*self
);
79 static void client_restore_session_stacking(ObClient
*self
);
80 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
82 void client_startup(gboolean reconfig
)
89 void client_shutdown(gboolean reconfig
)
93 void client_add_destructor(ObClientDestructor func
, gpointer data
)
95 Destructor
*d
= g_new(Destructor
, 1);
98 client_destructors
= g_slist_prepend(client_destructors
, d
);
101 void client_remove_destructor(ObClientDestructor func
)
105 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
106 Destructor
*d
= it
->data
;
107 if (d
->func
== func
) {
109 client_destructors
= g_slist_delete_link(client_destructors
, it
);
115 void client_set_list()
117 Window
*windows
, *win_it
;
119 guint size
= g_list_length(client_list
);
121 /* create an array of the window ids */
123 windows
= g_new(Window
, size
);
125 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
126 *win_it
= ((ObClient
*)it
->data
)->window
;
130 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
131 net_client_list
, window
, (gulong
*)windows
, size
);
140 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
144 for (it = self->transients; it; it = g_slist_next(it)) {
145 if (!func(it->data, data)) return;
146 client_foreach_transient(it->data, func, data);
150 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
152 if (self->transient_for) {
153 if (self->transient_for != OB_TRAN_GROUP) {
154 if (!func(self->transient_for, data)) return;
155 client_foreach_ancestor(self->transient_for, func, data);
159 for (it = self->group->members; it; it = g_slist_next(it))
160 if (it->data != self &&
161 !((ObClient*)it->data)->transient_for) {
162 if (!func(it->data, data)) return;
163 client_foreach_ancestor(it->data, func, data);
170 void client_manage_all()
175 XWindowAttributes attrib
;
177 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
178 &w
, &w
, &children
, &nchild
);
180 /* remove all icon windows from the list */
181 for (i
= 0; i
< nchild
; i
++) {
182 if (children
[i
] == None
) continue;
183 wmhints
= XGetWMHints(ob_display
, children
[i
]);
185 if ((wmhints
->flags
& IconWindowHint
) &&
186 (wmhints
->icon_window
!= children
[i
]))
187 for (j
= 0; j
< nchild
; j
++)
188 if (children
[j
] == wmhints
->icon_window
) {
196 for (i
= 0; i
< nchild
; ++i
) {
197 if (children
[i
] == None
)
199 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
200 if (attrib
.override_redirect
) continue;
202 if (attrib
.map_state
!= IsUnmapped
)
203 client_manage(children
[i
]);
209 void client_manage(Window window
)
213 XWindowAttributes attrib
;
214 XSetWindowAttributes attrib_set
;
216 gboolean activate
= FALSE
;
217 ObAppSettings
*settings
;
222 /* check if it has already been unmapped by the time we started mapping
223 the grab does a sync so we don't have to here */
224 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
225 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
227 XPutBackEvent(ob_display
, &e
);
230 return; /* don't manage it */
233 /* make sure it isn't an override-redirect window */
234 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
235 attrib
.override_redirect
)
238 return; /* don't manage it */
241 /* is the window a docking app */
242 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
243 if ((wmhint
->flags
& StateHint
) &&
244 wmhint
->initial_state
== WithdrawnState
)
246 dock_add(window
, wmhint
);
254 ob_debug("Managing window: %lx\n", window
);
256 /* choose the events we want to receive on the CLIENT window */
257 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
258 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
259 XChangeWindowAttributes(ob_display
, window
,
260 CWEventMask
|CWDontPropagate
, &attrib_set
);
263 /* create the ObClient struct, and populate it from the hints on the
265 self
= g_new0(ObClient
, 1);
266 self
->obwin
.type
= Window_Client
;
267 self
->window
= window
;
269 /* non-zero defaults */
270 self
->title_count
= 1;
271 self
->wmstate
= NormalState
;
273 self
->desktop
= screen_num_desktops
; /* always an invalid value */
274 self
->user_time
= ~0; /* maximum value, always newer than the real time */
276 client_get_all(self
);
277 client_restore_session_state(self
);
278 /* per-app settings override stuff, and return the settings for other
280 settings
= client_get_settings_state(self
);
282 client_calc_layer(self
);
285 Time t
= sn_app_started(self
->startup_id
, self
->class);
286 if (t
) self
->user_time
= t
;
289 /* update the focus lists, do this before the call to change_state or
290 it can end up in the list twice! */
291 focus_order_add_new(self
);
293 client_change_state(self
);
295 /* remove the client's border (and adjust re gravity) */
296 client_toggle_border(self
, FALSE
);
298 /* specify that if we exit, the window should not be destroyed and should
299 be reparented back to root automatically */
300 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
302 /* create the decoration frame for the client window */
303 self
->frame
= frame_new(self
);
305 frame_grab_client(self
->frame
, self
);
309 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
310 client_restore_session_stacking(self
);
312 /* focus the new window? */
313 if (ob_state() != OB_STATE_STARTING
&&
314 /* this means focus=true for window is same as config_focus_new=true */
315 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
316 client_search_focus_parent(self
)) &&
317 /* this checks for focus=false for the window */
318 (!settings
|| settings
->focus
!= 0) &&
319 /* note the check against Type_Normal/Dialog, not client_normal(self),
320 which would also include other types. in this case we want more
321 strict rules for focus */
322 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
323 self
->type
== OB_CLIENT_TYPE_DIALOG
))
327 if (self
->desktop
!= screen_desktop
) {
328 /* activate the window */
331 gboolean group_foc
= FALSE
;
336 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
338 if (client_focused(it
->data
))
346 (!self
->transient_for
&& (!self
->group
||
347 !self
->group
->members
->next
))) ||
348 client_search_focus_tree_full(self
) ||
350 !client_normal(focus_client
))
352 /* activate the window */
359 /* get the current position */
363 /* figure out placement for the window */
364 if (ob_state() == OB_STATE_RUNNING
) {
367 transient
= place_client(self
, &newx
, &newy
, settings
);
369 /* make sure the window is visible. */
370 client_find_onscreen(self
, &newx
, &newy
,
371 self
->frame
->area
.width
,
372 self
->frame
->area
.height
,
373 /* non-normal clients has less rules, and
374 windows that are being restored from a
375 session do also. we can assume you want
376 it back where you saved it. Clients saying
377 they placed themselves are subjected to
378 harder rules, ones that are placed by
379 place.c or by the user are allowed partially
380 off-screen and on xinerama divides (ie,
381 it is up to the placement routines to avoid
382 the xinerama divides) */
384 (((self
->positioned
& PPosition
) &&
385 !(self
->positioned
& USPosition
)) &&
386 client_normal(self
) &&
390 /* do this after the window is placed, so the premax/prefullscreen numbers
392 also, this moves the window to the position where it has been placed
394 client_apply_startup_state(self
, newx
, newy
);
396 keyboard_grab_for_client(self
, TRUE
);
397 mouse_grab_for_client(self
, TRUE
);
400 /* This is focus stealing prevention */
401 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
402 self
->window
, self
->user_time
, client_last_user_time
);
404 /* If a nothing at all, or a parent was focused, then focus this
407 if (!focus_client
|| client_search_focus_parent(self
) != NULL
)
411 /* If time stamp is old, don't steal focus */
412 if (self
->user_time
&& self
->user_time
< client_last_user_time
)
414 /* Don't steal focus from globally active clients.
415 I stole this idea from KWin. It seems nice.
417 if (!focus_client
->can_focus
&& focus_client
->focus_notify
)
423 /* since focus can change the stacking orders, if we focus the
424 window then the standard raise it gets is not enough, we need
425 to queue one for after the focus change takes place */
428 ob_debug("Focus stealing prevention activated for %s with time %u "
430 self
->title
, self
->user_time
, client_last_user_time
);
431 /* if the client isn't focused, then hilite it so the user
433 client_hilite(self
, TRUE
);
437 /* This may look rather odd. Well it's because new windows are added
438 to the stacking order non-intrusively. If we're not going to focus
439 the new window or hilite it, then we raise it to the top. This will
440 take affect for things that don't get focused like splash screens.
441 Also if you don't have focus_new enabled, then it's going to get
442 raised to the top. Legacy begets legacy I guess?
447 /* this has to happen before we try focus the window, but we want it to
448 happen after the client's stacking has been determined or it looks bad
450 client_showhide(self
);
452 /* use client_focus instead of client_activate cuz client_activate does
453 stuff like switch desktops etc and I'm not interested in all that when
454 a window maps since its not based on an action from the user like
455 clicking a window to activate it. so keep the new window out of the way
458 /* if using focus_delay, stop the timer now so that focus doesn't
460 event_halt_focus_delay();
464 /* client_activate does this but we aret using it so we have to do it
466 if (screen_showing_desktop
)
467 screen_show_desktop(FALSE
);
469 /* add to client list/map */
470 client_list
= g_list_append(client_list
, self
);
471 g_hash_table_insert(window_map
, &self
->window
, self
);
473 /* this has to happen after we're in the client_list */
474 if (STRUT_EXISTS(self
->strut
))
475 screen_update_areas();
477 /* update the list hints */
480 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
483 void client_unmanage_all()
485 while (client_list
!= NULL
)
486 client_unmanage(client_list
->data
);
489 void client_unmanage(ObClient
*self
)
494 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
495 self
->title
? self
->title
: "");
497 g_assert(self
!= NULL
);
499 keyboard_grab_for_client(self
, FALSE
);
500 mouse_grab_for_client(self
, FALSE
);
502 /* potentially fix focusLast */
503 if (config_focus_last
)
504 grab_pointer(TRUE
, OB_CURSOR_NONE
);
506 /* remove the window from our save set */
507 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
509 /* we dont want events no more */
510 XSelectInput(ob_display
, self
->window
, NoEventMask
);
512 frame_hide(self
->frame
);
514 client_list
= g_list_remove(client_list
, self
);
515 stacking_remove(self
);
516 g_hash_table_remove(window_map
, &self
->window
);
518 /* update the focus lists */
519 focus_order_remove(self
);
521 /* once the client is out of the list, update the struts to remove it's
523 if (STRUT_EXISTS(self
->strut
))
524 screen_update_areas();
526 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
527 Destructor
*d
= it
->data
;
528 d
->func(self
, d
->data
);
531 if (focus_client
== self
) {
534 /* focus the last focused window on the desktop, and ignore enter
535 events from the unmap so it doesnt mess with the focus */
536 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
537 /* remove these flags so we don't end up getting focused in the
539 self
->can_focus
= FALSE
;
540 self
->focus_notify
= FALSE
;
542 client_unfocus(self
);
545 /* tell our parent(s) that we're gone */
546 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
547 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
548 if (it
->data
!= self
)
549 ((ObClient
*)it
->data
)->transients
=
550 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
551 } else if (self
->transient_for
) { /* transient of window */
552 self
->transient_for
->transients
=
553 g_slist_remove(self
->transient_for
->transients
, self
);
556 /* tell our transients that we're gone */
557 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
558 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
559 ((ObClient
*)it
->data
)->transient_for
= NULL
;
560 client_calc_layer(it
->data
);
564 /* remove from its group */
566 group_remove(self
->group
, self
);
570 /* give the client its border back */
571 client_toggle_border(self
, TRUE
);
573 /* reparent the window out of the frame, and free the frame */
574 frame_release_client(self
->frame
, self
);
577 /* restore the window's original geometry so it is not lost */
578 if (self
->fullscreen
)
579 XMoveResizeWindow(ob_display
, self
->window
,
580 self
->pre_fullscreen_area
.x
,
581 self
->pre_fullscreen_area
.y
,
582 self
->pre_fullscreen_area
.width
,
583 self
->pre_fullscreen_area
.height
);
584 else if (self
->max_horz
|| self
->max_vert
) {
586 if (self
->max_horz
) {
587 a
.x
= self
->pre_max_area
.x
;
588 a
.width
= self
->pre_max_area
.width
;
590 if (self
->max_vert
) {
591 a
.y
= self
->pre_max_area
.y
;
592 a
.height
= self
->pre_max_area
.height
;
594 XMoveResizeWindow(ob_display
, self
->window
,
595 a
.x
, a
.y
, a
.width
, a
.height
);
598 if (ob_state() != OB_STATE_EXITING
) {
599 /* these values should not be persisted across a window
601 PROP_ERASE(self
->window
, net_wm_desktop
);
602 PROP_ERASE(self
->window
, net_wm_state
);
603 PROP_ERASE(self
->window
, wm_state
);
605 /* if we're left in an unmapped state, the client wont be mapped. this
606 is bad, since we will no longer be managing the window on restart */
607 XMapWindow(ob_display
, self
->window
);
611 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
613 /* free all data allocated in the client struct */
614 g_slist_free(self
->transients
);
615 for (j
= 0; j
< self
->nicons
; ++j
)
616 g_free(self
->icons
[j
].data
);
617 if (self
->nicons
> 0)
620 g_free(self
->icon_title
);
624 g_free(self
->sm_client_id
);
627 /* update the list hints */
630 if (config_focus_last
)
631 grab_pointer(FALSE
, OB_CURSOR_NONE
);
634 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
636 ObAppSettings
*settings
= NULL
;
639 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
642 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
643 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
644 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
645 && !strcmp(app
->name
, self
->name
)))
647 ob_debug("Window matching: %s\n", app
->name
);
648 /* Match if no role was specified in the per app setting, or if the
649 * string matches the beginning of the role, since apps like to set
650 * the role to things like browser-window-23c4b2f */
652 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
662 if (settings
->shade
!= -1)
663 self
->shaded
= !!settings
->shade
;
664 if (settings
->decor
!= -1)
665 self
->undecorated
= !settings
->decor
;
666 if (settings
->iconic
!= -1)
667 self
->iconic
= !!settings
->iconic
;
668 if (settings
->skip_pager
!= -1)
669 self
->skip_pager
= !!settings
->skip_pager
;
670 if (settings
->skip_taskbar
!= -1)
671 self
->skip_taskbar
= !!settings
->skip_taskbar
;
673 if (settings
->max_vert
!= -1)
674 self
->max_vert
= !!settings
->max_vert
;
675 if (settings
->max_horz
!= -1)
676 self
->max_vert
= !!settings
->max_horz
;
678 if (settings
->fullscreen
!= -1)
679 self
->fullscreen
= !!settings
->fullscreen
;
681 if (settings
->desktop
< screen_num_desktops
682 || settings
->desktop
== DESKTOP_ALL
)
683 client_set_desktop(self
, settings
->desktop
, TRUE
);
685 if (settings
->layer
== -1) {
689 else if (settings
->layer
== 0) {
693 else if (settings
->layer
== 1) {
701 static void client_restore_session_state(ObClient
*self
)
705 if (!(it
= session_state_find(self
)))
708 self
->session
= it
->data
;
710 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
711 self
->positioned
= PPosition
;
712 if (self
->session
->w
> 0)
713 self
->area
.width
= self
->session
->w
;
714 if (self
->session
->h
> 0)
715 self
->area
.height
= self
->session
->h
;
716 XResizeWindow(ob_display
, self
->window
,
717 self
->area
.width
, self
->area
.height
);
719 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
720 self
->session
->desktop
:
721 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
722 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
724 self
->shaded
= self
->session
->shaded
;
725 self
->iconic
= self
->session
->iconic
;
726 self
->skip_pager
= self
->session
->skip_pager
;
727 self
->skip_taskbar
= self
->session
->skip_taskbar
;
728 self
->fullscreen
= self
->session
->fullscreen
;
729 self
->above
= self
->session
->above
;
730 self
->below
= self
->session
->below
;
731 self
->max_horz
= self
->session
->max_horz
;
732 self
->max_vert
= self
->session
->max_vert
;
735 static void client_restore_session_stacking(ObClient
*self
)
739 if (!self
->session
) return;
741 it
= g_list_find(session_saved_state
, self
->session
);
742 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
745 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
746 if (session_state_cmp(it
->data
, cit
->data
))
749 client_calc_layer(self
);
750 stacking_below(CLIENT_AS_WINDOW(self
),
751 CLIENT_AS_WINDOW(cit
->data
));
757 void client_move_onscreen(ObClient
*self
, gboolean rude
)
759 gint x
= self
->area
.x
;
760 gint y
= self
->area
.y
;
761 if (client_find_onscreen(self
, &x
, &y
,
762 self
->frame
->area
.width
,
763 self
->frame
->area
.height
, rude
)) {
764 client_move(self
, x
, y
);
768 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
772 gint ox
= *x
, oy
= *y
;
774 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
777 /* XXX watch for xinerama dead areas */
778 /* This makes sure windows aren't entirely outside of the screen so you
779 can't see them at all.
780 It makes sure 10% of the window is on the screen at least. At don't let
781 it move itself off the top of the screen, which would hide the titlebar
782 on you. (The user can still do this if they want too, it's only limiting
785 if (client_normal(self
)) {
786 a
= screen_area(self
->desktop
);
787 if (!self
->strut
.right
&&
788 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
789 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
790 if (!self
->strut
.bottom
&&
791 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
792 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
793 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
794 *x
= a
->x
- self
->frame
->area
.width
*9/10;
795 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
796 *y
= a
->y
- self
->frame
->area
.width
*9/10;
799 /* This here doesn't let windows even a pixel outside the screen,
800 * when called from client_manage, programs placing themselves are
801 * forced completely onscreen, while things like
802 * xterm -geometry resolution-width/2 will work fine. Trying to
803 * place it completely offscreen will be handled in the above code.
804 * Sorry for this confused comment, i am tired. */
806 /* avoid the xinerama monitor divide while we're at it,
807 * remember to fix the placement stuff to avoid it also and
808 * then remove this XXX */
809 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
810 /* dont let windows map into the strut unless they
811 are bigger than the available area */
813 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
814 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
815 *x
= a
->x
+ a
->width
- w
;
817 if (h
<= a
->height
) {
818 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
819 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
820 *y
= a
->y
+ a
->height
- h
;
824 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
827 return ox
!= *x
|| oy
!= *y
;
830 static void client_toggle_border(ObClient
*self
, gboolean show
)
832 /* adjust our idea of where the client is, based on its border. When the
833 border is removed, the client should now be considered to be in a
835 when re-adding the border to the client, the same operation needs to be
837 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
838 gint x
= oldx
, y
= oldy
;
839 switch(self
->gravity
) {
841 case NorthWestGravity
:
843 case SouthWestGravity
:
845 case NorthEastGravity
:
847 case SouthEastGravity
:
848 if (show
) x
-= self
->border_width
* 2;
849 else x
+= self
->border_width
* 2;
856 if (show
) x
-= self
->border_width
;
857 else x
+= self
->border_width
;
860 switch(self
->gravity
) {
862 case NorthWestGravity
:
864 case NorthEastGravity
:
866 case SouthWestGravity
:
868 case SouthEastGravity
:
869 if (show
) y
-= self
->border_width
* 2;
870 else y
+= self
->border_width
* 2;
877 if (show
) y
-= self
->border_width
;
878 else y
+= self
->border_width
;
885 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
887 /* move the client so it is back it the right spot _with_ its
889 if (x
!= oldx
|| y
!= oldy
)
890 XMoveWindow(ob_display
, self
->window
, x
, y
);
892 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
896 static void client_get_all(ObClient
*self
)
898 client_get_area(self
);
899 client_get_mwm_hints(self
);
901 /* The transient hint is used to pick a type, but the type can also affect
902 transiency (dialogs are always made transients of their group if they
903 have one). This is Havoc's idea, but it is needed to make some apps
904 work right (eg tsclient). */
905 client_update_transient_for(self
);
906 client_get_type(self
);/* this can change the mwmhints for special cases */
907 client_get_state(self
);
908 client_update_transient_for(self
);
910 client_update_wmhints(self
);
911 client_get_startup_id(self
);
912 client_get_desktop(self
);/* uses transient data/group/startup id if a
913 desktop is not specified */
914 client_get_shaped(self
);
916 client_get_layer(self
); /* if layer hasn't been specified, get it from
917 other sources if possible */
920 /* a couple type-based defaults for new windows */
922 /* this makes sure that these windows appear on all desktops */
923 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
924 self
->desktop
= DESKTOP_ALL
;
927 client_update_protocols(self
);
929 client_get_gravity(self
); /* get the attribute gravity */
930 client_update_normal_hints(self
); /* this may override the attribute
933 /* got the type, the mwmhints, the protocols, and the normal hints
934 (min/max sizes), so we're ready to set up the decorations/functions */
935 client_setup_decor_and_functions(self
);
937 client_update_title(self
);
938 client_update_class(self
);
939 client_update_sm_client_id(self
);
940 client_update_strut(self
);
941 client_update_icons(self
);
942 client_update_user_time(self
, FALSE
);
945 static void client_get_startup_id(ObClient
*self
)
947 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
949 PROP_GETS(self
->group
->leader
,
950 net_startup_id
, utf8
, &self
->startup_id
);
953 static void client_get_area(ObClient
*self
)
955 XWindowAttributes wattrib
;
958 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
959 g_assert(ret
!= BadWindow
);
961 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
962 self
->border_width
= wattrib
.border_width
;
964 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
965 wattrib
.width
, wattrib
.height
);
968 static void client_get_desktop(ObClient
*self
)
970 guint32 d
= screen_num_desktops
; /* an always-invalid value */
972 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
973 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
974 self
->desktop
= screen_num_desktops
- 1;
978 gboolean trdesk
= FALSE
;
980 if (self
->transient_for
) {
981 if (self
->transient_for
!= OB_TRAN_GROUP
) {
982 self
->desktop
= self
->transient_for
->desktop
;
987 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
988 if (it
->data
!= self
&&
989 !((ObClient
*)it
->data
)->transient_for
) {
990 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
997 /* try get from the startup-notification protocol */
998 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
999 if (self
->desktop
>= screen_num_desktops
&&
1000 self
->desktop
!= DESKTOP_ALL
)
1001 self
->desktop
= screen_num_desktops
- 1;
1003 /* defaults to the current desktop */
1004 self
->desktop
= screen_desktop
;
1007 if (self
->desktop
!= d
) {
1008 /* set the desktop hint, to make sure that it always exists */
1009 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
1013 static void client_get_layer(ObClient
*self
)
1015 if (!(self
->above
|| self
->below
)) {
1017 /* apply stuff from the group */
1021 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1022 ObClient
*c
= it
->data
;
1023 if (c
!= self
&& !client_search_transient(self
, c
) &&
1024 client_normal(self
) && client_normal(c
))
1027 (c
->above
? 1 : (c
->below
? -1 : 0)));
1041 g_assert_not_reached();
1048 static void client_get_state(ObClient
*self
)
1053 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1055 for (i
= 0; i
< num
; ++i
) {
1056 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1058 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1059 self
->shaded
= TRUE
;
1060 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1061 self
->iconic
= TRUE
;
1062 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1063 self
->skip_taskbar
= TRUE
;
1064 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1065 self
->skip_pager
= TRUE
;
1066 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1067 self
->fullscreen
= TRUE
;
1068 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1069 self
->max_vert
= TRUE
;
1070 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1071 self
->max_horz
= TRUE
;
1072 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1074 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1076 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1077 self
->demands_attention
= TRUE
;
1078 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1079 self
->undecorated
= TRUE
;
1086 static void client_get_shaped(ObClient
*self
)
1088 self
->shaped
= FALSE
;
1090 if (extensions_shape
) {
1095 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1097 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1098 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1100 self
->shaped
= (s
!= 0);
1105 void client_update_transient_for(ObClient
*self
)
1108 ObClient
*target
= NULL
;
1110 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1111 self
->transient
= TRUE
;
1112 if (t
!= self
->window
) { /* cant be transient to itself! */
1113 target
= g_hash_table_lookup(window_map
, &t
);
1114 /* if this happens then we need to check for it*/
1115 g_assert(target
!= self
);
1116 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1117 /* this can happen when a dialog is a child of
1118 a dockapp, for example */
1122 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1124 Setting the transient_for to Root is actually illegal, however
1125 applications from time have done this to specify transient for
1128 Now you can do that by being a TYPE_DIALOG and not setting
1129 the transient_for hint at all on your window. But people still
1130 use Root, and Kwin is very strange in this regard.
1132 KWin 3.0 will not consider windows with transient_for set to
1133 Root as transient for their group *UNLESS* they are also modal.
1134 In that case, it will make them transient for the group. This
1135 leads to all sorts of weird behavior from KDE apps which are
1136 only tested in KWin. I'd like to follow their behavior just to
1137 make this work right with KDE stuff, but that seems wrong.
1139 if (!target
&& self
->group
) {
1140 /* not transient to a client, see if it is transient for a
1142 if (t
== RootWindow(ob_display
, ob_screen
)) {
1143 /* window is a transient for its group! */
1144 target
= OB_TRAN_GROUP
;
1148 } else if (self
->group
) {
1149 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1150 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1151 self
->type
== OB_CLIENT_TYPE_MENU
||
1152 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1154 self
->transient
= TRUE
;
1155 target
= OB_TRAN_GROUP
;
1158 self
->transient
= FALSE
;
1160 /* if anything has changed... */
1161 if (target
!= self
->transient_for
) {
1162 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1165 /* remove from old parents */
1166 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1167 ObClient
*c
= it
->data
;
1168 if (c
!= self
&& !c
->transient_for
)
1169 c
->transients
= g_slist_remove(c
->transients
, self
);
1171 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1172 /* remove from old parent */
1173 self
->transient_for
->transients
=
1174 g_slist_remove(self
->transient_for
->transients
, self
);
1176 self
->transient_for
= target
;
1177 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1180 /* add to new parents */
1181 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1182 ObClient
*c
= it
->data
;
1183 if (c
!= self
&& !c
->transient_for
)
1184 c
->transients
= g_slist_append(c
->transients
, self
);
1187 /* remove all transients which are in the group, that causes
1188 circlular pointer hell of doom */
1189 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1191 for (sit
= self
->transients
; sit
; sit
= next
) {
1192 next
= g_slist_next(sit
);
1193 if (sit
->data
== it
->data
)
1195 g_slist_delete_link(self
->transients
, sit
);
1198 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1199 /* add to new parent */
1200 self
->transient_for
->transients
=
1201 g_slist_append(self
->transient_for
->transients
, self
);
1206 static void client_get_mwm_hints(ObClient
*self
)
1211 self
->mwmhints
.flags
= 0; /* default to none */
1213 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1215 if (num
>= OB_MWM_ELEMENTS
) {
1216 self
->mwmhints
.flags
= hints
[0];
1217 self
->mwmhints
.functions
= hints
[1];
1218 self
->mwmhints
.decorations
= hints
[2];
1224 void client_get_type(ObClient
*self
)
1231 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1232 /* use the first value that we know about in the array */
1233 for (i
= 0; i
< num
; ++i
) {
1234 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1235 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1236 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1237 self
->type
= OB_CLIENT_TYPE_DOCK
;
1238 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1239 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1240 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1241 self
->type
= OB_CLIENT_TYPE_MENU
;
1242 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1243 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1244 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1245 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1246 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1247 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1248 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1249 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1250 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1251 /* prevent this window from getting any decor or
1253 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1254 OB_MWM_FLAG_DECORATIONS
);
1255 self
->mwmhints
.decorations
= 0;
1256 self
->mwmhints
.functions
= 0;
1258 if (self
->type
!= (ObClientType
) -1)
1259 break; /* grab the first legit type */
1264 if (self
->type
== (ObClientType
) -1) {
1265 /*the window type hint was not set, which means we either classify
1266 ourself as a normal window or a dialog, depending on if we are a
1268 if (self
->transient
)
1269 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1271 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1275 void client_update_protocols(ObClient
*self
)
1278 guint num_return
, i
;
1280 self
->focus_notify
= FALSE
;
1281 self
->delete_window
= FALSE
;
1283 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1284 for (i
= 0; i
< num_return
; ++i
) {
1285 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1286 /* this means we can request the window to close */
1287 self
->delete_window
= TRUE
;
1288 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1289 /* if this protocol is requested, then the window will be
1290 notified whenever we want it to receive focus */
1291 self
->focus_notify
= TRUE
;
1297 static void client_get_gravity(ObClient
*self
)
1299 XWindowAttributes wattrib
;
1302 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1303 g_assert(ret
!= BadWindow
);
1304 self
->gravity
= wattrib
.win_gravity
;
1307 void client_update_normal_hints(ObClient
*self
)
1311 gint oldgravity
= self
->gravity
;
1314 self
->min_ratio
= 0.0f
;
1315 self
->max_ratio
= 0.0f
;
1316 SIZE_SET(self
->size_inc
, 1, 1);
1317 SIZE_SET(self
->base_size
, 0, 0);
1318 SIZE_SET(self
->min_size
, 0, 0);
1319 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1321 /* get the hints from the window */
1322 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1323 /* normal windows can't request placement! har har
1324 if (!client_normal(self))
1326 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1328 if (size
.flags
& PWinGravity
) {
1329 self
->gravity
= size
.win_gravity
;
1331 /* if the client has a frame, i.e. has already been mapped and
1332 is changing its gravity */
1333 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1334 /* move our idea of the client's position based on its new
1336 self
->area
.x
= self
->frame
->area
.x
;
1337 self
->area
.y
= self
->frame
->area
.y
;
1338 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1342 if (size
.flags
& PAspect
) {
1343 if (size
.min_aspect
.y
)
1345 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1346 if (size
.max_aspect
.y
)
1348 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1351 if (size
.flags
& PMinSize
)
1352 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1354 if (size
.flags
& PMaxSize
)
1355 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1357 if (size
.flags
& PBaseSize
)
1358 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1360 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1361 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1365 void client_setup_decor_and_functions(ObClient
*self
)
1367 /* start with everything (cept fullscreen) */
1369 (OB_FRAME_DECOR_TITLEBAR
|
1370 OB_FRAME_DECOR_HANDLE
|
1371 OB_FRAME_DECOR_GRIPS
|
1372 OB_FRAME_DECOR_BORDER
|
1373 OB_FRAME_DECOR_ICON
|
1374 OB_FRAME_DECOR_ALLDESKTOPS
|
1375 OB_FRAME_DECOR_ICONIFY
|
1376 OB_FRAME_DECOR_MAXIMIZE
|
1377 OB_FRAME_DECOR_SHADE
|
1378 OB_FRAME_DECOR_CLOSE
);
1380 (OB_CLIENT_FUNC_RESIZE
|
1381 OB_CLIENT_FUNC_MOVE
|
1382 OB_CLIENT_FUNC_ICONIFY
|
1383 OB_CLIENT_FUNC_MAXIMIZE
|
1384 OB_CLIENT_FUNC_SHADE
|
1385 OB_CLIENT_FUNC_CLOSE
);
1387 if (!(self
->min_size
.width
< self
->max_size
.width
||
1388 self
->min_size
.height
< self
->max_size
.height
))
1389 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1391 switch (self
->type
) {
1392 case OB_CLIENT_TYPE_NORMAL
:
1393 /* normal windows retain all of the possible decorations and
1394 functionality, and are the only windows that you can fullscreen */
1395 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1398 case OB_CLIENT_TYPE_DIALOG
:
1399 case OB_CLIENT_TYPE_UTILITY
:
1400 /* these windows cannot be maximized */
1401 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1404 case OB_CLIENT_TYPE_MENU
:
1405 case OB_CLIENT_TYPE_TOOLBAR
:
1406 /* these windows get less functionality */
1407 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1410 case OB_CLIENT_TYPE_DESKTOP
:
1411 case OB_CLIENT_TYPE_DOCK
:
1412 case OB_CLIENT_TYPE_SPLASH
:
1413 /* none of these windows are manipulated by the window manager */
1414 self
->decorations
= 0;
1415 self
->functions
= 0;
1419 /* Mwm Hints are applied subtractively to what has already been chosen for
1420 decor and functionality */
1421 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1422 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1423 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1424 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1426 /* if the mwm hints request no handle or title, then all
1427 decorations are disabled, but keep the border if that's
1429 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1430 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1432 self
->decorations
= 0;
1437 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1438 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1439 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1440 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1441 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1442 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1443 /* dont let mwm hints kill any buttons
1444 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1445 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1446 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1447 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1449 /* dont let mwm hints kill the close button
1450 if (! (self->mwmhints.functions & MwmFunc_Close))
1451 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1455 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1456 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1457 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1458 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1459 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1460 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1462 /* can't maximize without moving/resizing */
1463 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1464 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1465 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1466 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1467 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1470 /* kill the handle on fully maxed windows */
1471 if (self
->max_vert
&& self
->max_horz
)
1472 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1474 /* finally, the user can have requested no decorations, which overrides
1475 everything (but doesnt give it a border if it doesnt have one) */
1476 if (self
->undecorated
) {
1477 if (config_theme_keepborder
)
1478 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1480 self
->decorations
= 0;
1483 /* if we don't have a titlebar, then we cannot shade! */
1484 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1485 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1487 /* now we need to check against rules for the client's current state */
1488 if (self
->fullscreen
) {
1489 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1490 OB_CLIENT_FUNC_FULLSCREEN
|
1491 OB_CLIENT_FUNC_ICONIFY
);
1492 self
->decorations
= 0;
1495 client_change_allowed_actions(self
);
1498 /* adjust the client's decorations, etc. */
1499 client_reconfigure(self
);
1503 static void client_change_allowed_actions(ObClient
*self
)
1508 /* desktop windows are kept on all desktops */
1509 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1510 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1512 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1513 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1514 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1515 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1516 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1517 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1518 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1519 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1520 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1521 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1522 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1523 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1524 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1525 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1526 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1529 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1531 /* make sure the window isn't breaking any rules now */
1533 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1534 if (self
->frame
) client_shade(self
, FALSE
);
1535 else self
->shaded
= FALSE
;
1537 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1538 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1539 else self
->iconic
= FALSE
;
1541 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1542 if (self
->frame
) client_fullscreen(self
, FALSE
);
1543 else self
->fullscreen
= FALSE
;
1545 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1547 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1548 else self
->max_vert
= self
->max_horz
= FALSE
;
1552 void client_reconfigure(ObClient
*self
)
1554 /* by making this pass FALSE for user, we avoid the emacs event storm where
1555 every configurenotify causes an update in its normal hints, i think this
1556 is generally what we want anyways... */
1557 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1558 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1561 void client_update_wmhints(ObClient
*self
)
1566 /* assume a window takes input if it doesnt specify */
1567 self
->can_focus
= TRUE
;
1569 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1570 if (hints
->flags
& InputHint
)
1571 self
->can_focus
= hints
->input
;
1573 /* only do this when first managing the window *AND* when we aren't
1575 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1576 if (hints
->flags
& StateHint
)
1577 self
->iconic
= hints
->initial_state
== IconicState
;
1579 if (!(hints
->flags
& WindowGroupHint
))
1580 hints
->window_group
= None
;
1582 /* did the group state change? */
1583 if (hints
->window_group
!=
1584 (self
->group
? self
->group
->leader
: None
)) {
1585 /* remove from the old group if there was one */
1586 if (self
->group
!= NULL
) {
1587 /* remove transients of the group */
1588 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1589 self
->transients
= g_slist_remove(self
->transients
,
1592 /* remove myself from parents in the group */
1593 if (self
->transient_for
== OB_TRAN_GROUP
) {
1594 for (it
= self
->group
->members
; it
;
1595 it
= g_slist_next(it
))
1597 ObClient
*c
= it
->data
;
1599 if (c
!= self
&& !c
->transient_for
)
1600 c
->transients
= g_slist_remove(c
->transients
,
1605 group_remove(self
->group
, self
);
1608 if (hints
->window_group
!= None
) {
1609 self
->group
= group_add(hints
->window_group
, self
);
1611 /* i can only have transients from the group if i am not
1613 if (!self
->transient_for
) {
1614 /* add other transients of the group that are already
1616 for (it
= self
->group
->members
; it
;
1617 it
= g_slist_next(it
))
1619 ObClient
*c
= it
->data
;
1620 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1622 g_slist_append(self
->transients
, c
);
1627 /* because the self->transient flag wont change from this call,
1628 we don't need to update the window's type and such, only its
1629 transient_for, and the transients lists of other windows in
1630 the group may be affected */
1631 client_update_transient_for(self
);
1634 /* the WM_HINTS can contain an icon */
1635 client_update_icons(self
);
1641 void client_update_title(ObClient
*self
)
1647 gboolean read_title
;
1650 old_title
= self
->title
;
1653 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1654 /* try old x stuff */
1655 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1656 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1657 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1658 if (self
->transient
) {
1659 data
= g_strdup("");
1662 data
= g_strdup("Unnamed Window");
1666 if (config_title_number
) {
1668 /* did the title change? then reset the title_count */
1669 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1670 self
->title_count
= 1;
1672 /* look for duplicates and append a number */
1674 for (it
= client_list
; it
; it
= g_list_next(it
))
1675 if (it
->data
!= self
) {
1676 ObClient
*c
= it
->data
;
1678 if (c
->title_count
== 1) {
1679 if (!strcmp(c
->title
, data
))
1680 nums
|= 1 << c
->title_count
;
1685 /* find the beginning of our " - [%u]", this relies on
1686 that syntax being used */
1687 end
= strrchr(c
->title
, '-') - 1;
1688 len
= end
- c
->title
;
1689 if (!strncmp(c
->title
, data
, len
))
1690 nums
|= 1 << c
->title_count
;
1693 /* find first free number */
1694 for (i
= 1; i
<= 32; ++i
)
1695 if (!(nums
& (1 << i
))) {
1696 if (self
->title_count
== 1 || i
== 1)
1697 self
->title_count
= i
;
1700 /* dont display the number for the first window */
1701 if (self
->title_count
> 1) {
1703 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1708 self
->title_count
= 1;
1711 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1715 frame_adjust_title(self
->frame
);
1719 /* update the icon title */
1721 g_free(self
->icon_title
);
1725 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1726 /* try old x stuff */
1727 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)
1728 || PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
))) {
1729 data
= g_strdup(self
->title
);
1733 /* append the title count, dont display the number for the first window.
1734 * We don't need to check for config_title_number here since title_count
1735 * is not set above 1 then. */
1736 if (read_title
&& self
->title_count
> 1) {
1738 newdata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1743 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1745 self
->icon_title
= data
;
1748 void client_update_class(ObClient
*self
)
1753 if (self
->name
) g_free(self
->name
);
1754 if (self
->class) g_free(self
->class);
1755 if (self
->role
) g_free(self
->role
);
1757 self
->name
= self
->class = self
->role
= NULL
;
1759 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1761 self
->name
= g_strdup(data
[0]);
1763 self
->class = g_strdup(data
[1]);
1768 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1771 if (self
->name
== NULL
) self
->name
= g_strdup("");
1772 if (self
->class == NULL
) self
->class = g_strdup("");
1773 if (self
->role
== NULL
) self
->role
= g_strdup("");
1776 void client_update_strut(ObClient
*self
)
1780 gboolean got
= FALSE
;
1783 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1787 STRUT_PARTIAL_SET(strut
,
1788 data
[0], data
[2], data
[1], data
[3],
1789 data
[4], data
[5], data
[8], data
[9],
1790 data
[6], data
[7], data
[10], data
[11]);
1796 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1802 /* use the screen's width/height */
1803 a
= screen_physical_area();
1805 STRUT_PARTIAL_SET(strut
,
1806 data
[0], data
[2], data
[1], data
[3],
1807 a
->y
, a
->y
+ a
->height
- 1,
1808 a
->x
, a
->x
+ a
->width
- 1,
1809 a
->y
, a
->y
+ a
->height
- 1,
1810 a
->x
, a
->x
+ a
->width
- 1);
1816 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1817 0, 0, 0, 0, 0, 0, 0, 0);
1819 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1820 self
->strut
= strut
;
1822 /* updating here is pointless while we're being mapped cuz we're not in
1823 the client list yet */
1825 screen_update_areas();
1829 void client_update_icons(ObClient
*self
)
1835 for (i
= 0; i
< self
->nicons
; ++i
)
1836 g_free(self
->icons
[i
].data
);
1837 if (self
->nicons
> 0)
1838 g_free(self
->icons
);
1841 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1842 /* figure out how many valid icons are in here */
1844 while (num
- i
> 2) {
1848 if (i
> num
|| w
*h
== 0) break;
1852 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1854 /* store the icons */
1856 for (j
= 0; j
< self
->nicons
; ++j
) {
1859 w
= self
->icons
[j
].width
= data
[i
++];
1860 h
= self
->icons
[j
].height
= data
[i
++];
1862 if (w
*h
== 0) continue;
1864 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1865 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1870 self
->icons
[j
].data
[t
] =
1871 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1872 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1873 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1874 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1883 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1884 if (hints
->flags
& IconPixmapHint
) {
1886 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1887 xerror_set_ignore(TRUE
);
1888 if (!RrPixmapToRGBA(ob_rr_inst
,
1890 (hints
->flags
& IconMaskHint
?
1891 hints
->icon_mask
: None
),
1892 &self
->icons
[self
->nicons
-1].width
,
1893 &self
->icons
[self
->nicons
-1].height
,
1894 &self
->icons
[self
->nicons
-1].data
)){
1895 g_free(&self
->icons
[self
->nicons
-1]);
1898 xerror_set_ignore(FALSE
);
1905 frame_adjust_icon(self
->frame
);
1908 void client_update_user_time(ObClient
*self
, gboolean new_event
)
1912 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1913 self
->user_time
= time
;
1914 /* we set this every time, not just when it grows, because in practice
1915 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1916 backward we don't want all windows to stop focusing. we'll just
1917 assume noone is setting times older than the last one, cuz that
1918 would be pretty stupid anyways
1919 However! This is called when a window is mapped to get its user time
1920 but it's an old number, it's not changing it from new user
1921 interaction, so in that case, don't change the last user time.
1924 client_last_user_time
= time
;
1926 /*ob_debug("window 0x%x user time %u\n", self->window, time);*/
1930 static void client_change_state(ObClient
*self
)
1933 gulong netstate
[11];
1936 state
[0] = self
->wmstate
;
1938 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1942 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1944 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1946 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1947 if (self
->skip_taskbar
)
1948 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1949 if (self
->skip_pager
)
1950 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1951 if (self
->fullscreen
)
1952 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1954 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1956 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1958 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1960 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1961 if (self
->demands_attention
)
1962 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1963 if (self
->undecorated
)
1964 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1965 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1968 frame_adjust_state(self
->frame
);
1971 ObClient
*client_search_focus_tree(ObClient
*self
)
1976 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1977 if (client_focused(it
->data
)) return it
->data
;
1978 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1983 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1985 if (self
->transient_for
) {
1986 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1987 return client_search_focus_tree_full(self
->transient_for
);
1990 gboolean recursed
= FALSE
;
1992 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1993 if (!((ObClient
*)it
->data
)->transient_for
) {
1995 if ((c
= client_search_focus_tree_full(it
->data
)))
2004 /* this function checks the whole tree, the client_search_focus_tree~
2005 does not, so we need to check this window */
2006 if (client_focused(self
))
2008 return client_search_focus_tree(self
);
2011 static ObStackingLayer
calc_layer(ObClient
*self
)
2015 if (self
->fullscreen
&&
2016 (client_focused(self
) || client_search_focus_tree(self
)))
2017 l
= OB_STACKING_LAYER_FULLSCREEN
;
2018 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2019 l
= OB_STACKING_LAYER_DESKTOP
;
2020 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2021 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2022 else l
= OB_STACKING_LAYER_ABOVE
;
2024 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2025 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2026 else l
= OB_STACKING_LAYER_NORMAL
;
2031 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2032 ObStackingLayer min
, gboolean raised
)
2034 ObStackingLayer old
, own
;
2038 own
= calc_layer(self
);
2039 self
->layer
= MAX(own
, min
);
2041 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2042 client_calc_layer_recursive(it
->data
, orig
,
2044 raised
? raised
: self
->layer
!= old
);
2046 if (!raised
&& self
->layer
!= old
)
2047 if (orig
->frame
) { /* only restack if the original window is managed */
2048 stacking_remove(CLIENT_AS_WINDOW(self
));
2049 stacking_add(CLIENT_AS_WINDOW(self
));
2053 void client_calc_layer(ObClient
*self
)
2060 /* transients take on the layer of their parents */
2061 it
= client_search_all_top_parents(self
);
2063 for (; it
; it
= g_slist_next(it
))
2064 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2067 gboolean
client_should_show(ObClient
*self
)
2071 if (client_normal(self
) && screen_showing_desktop
)
2074 if (self->transient_for) {
2075 if (self->transient_for != OB_TRAN_GROUP)
2076 return client_should_show(self->transient_for);
2080 for (it = self->group->members; it; it = g_slist_next(it)) {
2081 ObClient *c = it->data;
2082 if (c != self && !c->transient_for) {
2083 if (client_should_show(c))
2090 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2096 static void client_showhide(ObClient
*self
)
2099 if (client_should_show(self
))
2100 frame_show(self
->frame
);
2102 frame_hide(self
->frame
);
2105 gboolean
client_normal(ObClient
*self
) {
2106 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2107 self
->type
== OB_CLIENT_TYPE_DOCK
||
2108 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2111 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2113 gboolean pos
= FALSE
; /* has the window's position been configured? */
2116 /* save the position, and set self->area for these to use */
2122 /* these are in a carefully crafted order.. */
2125 self
->iconic
= FALSE
;
2126 client_iconify(self
, TRUE
, FALSE
);
2128 if (self
->fullscreen
) {
2129 self
->fullscreen
= FALSE
;
2130 client_fullscreen(self
, TRUE
);
2133 if (self
->undecorated
) {
2134 self
->undecorated
= FALSE
;
2135 client_set_undecorated(self
, TRUE
);
2138 self
->shaded
= FALSE
;
2139 client_shade(self
, TRUE
);
2141 if (self
->demands_attention
) {
2142 self
->demands_attention
= FALSE
;
2143 client_hilite(self
, TRUE
);
2146 if (self
->max_vert
&& self
->max_horz
) {
2147 self
->max_vert
= self
->max_horz
= FALSE
;
2148 client_maximize(self
, TRUE
, 0);
2150 } else if (self
->max_vert
) {
2151 self
->max_vert
= FALSE
;
2152 client_maximize(self
, TRUE
, 2);
2154 } else if (self
->max_horz
) {
2155 self
->max_horz
= FALSE
;
2156 client_maximize(self
, TRUE
, 1);
2160 /* if the client didn't get positioned yet, then do so now */
2161 if (!pos
&& (ox
!= x
|| oy
!= y
)) {
2162 /* use the saved position */
2165 client_move(self
, x
, y
);
2168 /* nothing to do for the other states:
2177 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2178 gint x
, gint y
, gint w
, gint h
,
2179 gboolean user
, gboolean final
,
2180 gboolean force_reply
)
2183 gboolean send_resize_client
;
2184 gboolean moved
= FALSE
, resized
= FALSE
;
2185 guint fdecor
= self
->frame
->decorations
;
2186 gboolean fhorz
= self
->frame
->max_horz
;
2187 Rect desired_area
= {x
, y
, w
, h
};
2189 /* make the frame recalculate its dimentions n shit without changing
2190 anything visible for real, this way the constraints below can work with
2191 the updated frame dimensions. */
2192 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2194 /* gets the frame's position */
2195 frame_client_gravity(self
->frame
, &x
, &y
);
2197 /* these positions are frame positions, not client positions */
2199 /* set the size and position if fullscreen */
2200 if (self
->fullscreen
) {
2204 i
= screen_find_monitor(&desired_area
);
2205 a
= screen_physical_area_monitor(i
);
2212 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2217 i
= screen_find_monitor(&desired_area
);
2218 a
= screen_area_monitor(self
->desktop
, i
);
2220 /* set the size and position if maximized */
2221 if (self
->max_horz
) {
2223 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2225 if (self
->max_vert
) {
2227 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2231 /* gets the client's position */
2232 frame_frame_gravity(self
->frame
, &x
, &y
);
2234 /* these override the above states! if you cant move you can't move! */
2236 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2240 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2241 w
= self
->area
.width
;
2242 h
= self
->area
.height
;
2246 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2247 gint basew
, baseh
, minw
, minh
;
2249 /* base size is substituted with min size if not specified */
2250 if (self
->base_size
.width
|| self
->base_size
.height
) {
2251 basew
= self
->base_size
.width
;
2252 baseh
= self
->base_size
.height
;
2254 basew
= self
->min_size
.width
;
2255 baseh
= self
->min_size
.height
;
2257 /* min size is substituted with base size if not specified */
2258 if (self
->min_size
.width
|| self
->min_size
.height
) {
2259 minw
= self
->min_size
.width
;
2260 minh
= self
->min_size
.height
;
2262 minw
= self
->base_size
.width
;
2263 minh
= self
->base_size
.height
;
2266 /* if this is a user-requested resize, then check against min/max
2269 /* smaller than min size or bigger than max size? */
2270 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2271 if (w
< minw
) w
= minw
;
2272 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2273 if (h
< minh
) h
= minh
;
2278 /* keep to the increments */
2279 w
/= self
->size_inc
.width
;
2280 h
/= self
->size_inc
.height
;
2282 /* you cannot resize to nothing */
2283 if (basew
+ w
< 1) w
= 1 - basew
;
2284 if (baseh
+ h
< 1) h
= 1 - baseh
;
2286 /* store the logical size */
2287 SIZE_SET(self
->logical_size
,
2288 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2289 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2291 w
*= self
->size_inc
.width
;
2292 h
*= self
->size_inc
.height
;
2297 /* adjust the height to match the width for the aspect ratios.
2298 for this, min size is not substituted for base size ever. */
2299 w
-= self
->base_size
.width
;
2300 h
-= self
->base_size
.height
;
2302 if (!self
->fullscreen
) {
2303 if (self
->min_ratio
)
2304 if (h
* self
->min_ratio
> w
) {
2305 h
= (gint
)(w
/ self
->min_ratio
);
2307 /* you cannot resize to nothing */
2310 w
= (gint
)(h
* self
->min_ratio
);
2313 if (self
->max_ratio
)
2314 if (h
* self
->max_ratio
< w
) {
2315 h
= (gint
)(w
/ self
->max_ratio
);
2317 /* you cannot resize to nothing */
2320 w
= (gint
)(h
* self
->min_ratio
);
2325 w
+= self
->base_size
.width
;
2326 h
+= self
->base_size
.height
;
2333 case OB_CORNER_TOPLEFT
:
2335 case OB_CORNER_TOPRIGHT
:
2336 x
-= w
- self
->area
.width
;
2338 case OB_CORNER_BOTTOMLEFT
:
2339 y
-= h
- self
->area
.height
;
2341 case OB_CORNER_BOTTOMRIGHT
:
2342 x
-= w
- self
->area
.width
;
2343 y
-= h
- self
->area
.height
;
2347 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2348 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2350 oldw
= self
->area
.width
;
2351 oldh
= self
->area
.height
;
2352 RECT_SET(self
->area
, x
, y
, w
, h
);
2354 /* for app-requested resizes, always resize if 'resized' is true.
2355 for user-requested ones, only resize if final is true, or when
2356 resizing in redraw mode */
2357 send_resize_client
= ((!user
&& resized
) ||
2359 (resized
&& config_resize_redraw
))));
2361 /* if the client is enlarging, then resize the client before the frame */
2362 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2363 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2365 /* move/resize the frame to match the request */
2367 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2368 moved
= resized
= TRUE
;
2370 if (moved
|| resized
)
2371 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2373 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2376 event
.type
= ConfigureNotify
;
2377 event
.xconfigure
.display
= ob_display
;
2378 event
.xconfigure
.event
= self
->window
;
2379 event
.xconfigure
.window
= self
->window
;
2381 /* root window real coords */
2382 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2384 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2386 event
.xconfigure
.width
= w
;
2387 event
.xconfigure
.height
= h
;
2388 event
.xconfigure
.border_width
= 0;
2389 event
.xconfigure
.above
= self
->frame
->plate
;
2390 event
.xconfigure
.override_redirect
= FALSE
;
2391 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2392 FALSE
, StructureNotifyMask
, &event
);
2396 /* if the client is shrinking, then resize the frame before the client */
2397 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2398 XResizeWindow(ob_display
, self
->window
, w
, h
);
2403 void client_fullscreen(ObClient
*self
, gboolean fs
)
2407 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2408 self
->fullscreen
== fs
) return; /* already done */
2410 self
->fullscreen
= fs
;
2411 client_change_state(self
); /* change the state hints on the client */
2412 client_calc_layer(self
); /* and adjust out layer/stacking */
2415 self
->pre_fullscreen_area
= self
->area
;
2416 /* if the window is maximized, its area isn't all that meaningful.
2417 save it's premax area instead. */
2418 if (self
->max_horz
) {
2419 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2420 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2422 if (self
->max_vert
) {
2423 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2424 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2427 /* these are not actually used cuz client_configure will set them
2428 as appropriate when the window is fullscreened */
2433 if (self
->pre_fullscreen_area
.width
> 0 &&
2434 self
->pre_fullscreen_area
.height
> 0)
2436 x
= self
->pre_fullscreen_area
.x
;
2437 y
= self
->pre_fullscreen_area
.y
;
2438 w
= self
->pre_fullscreen_area
.width
;
2439 h
= self
->pre_fullscreen_area
.height
;
2440 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2442 /* pick some fallbacks... */
2443 a
= screen_area_monitor(self
->desktop
, 0);
2444 x
= a
->x
+ a
->width
/ 4;
2445 y
= a
->y
+ a
->height
/ 4;
2451 client_setup_decor_and_functions(self
);
2453 client_move_resize(self
, x
, y
, w
, h
);
2455 /* try focus us when we go into fullscreen mode */
2459 static void client_iconify_recursive(ObClient
*self
,
2460 gboolean iconic
, gboolean curdesk
)
2463 gboolean changed
= FALSE
;
2466 if (self
->iconic
!= iconic
) {
2467 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2470 self
->iconic
= iconic
;
2473 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2476 old
= self
->wmstate
;
2477 self
->wmstate
= IconicState
;
2478 if (old
!= self
->wmstate
)
2479 PROP_MSG(self
->window
, kde_wm_change_state
,
2480 self
->wmstate
, 1, 0, 0);
2482 /* update the focus lists.. iconic windows go to the bottom of
2483 the list, put the new iconic window at the 'top of the
2485 focus_order_to_top(self
);
2487 /* Fall back focus since we're disappearing */
2488 if (focus_client
== self
)
2489 client_unfocus(self
);
2497 client_set_desktop(self
, screen_desktop
, FALSE
);
2499 old
= self
->wmstate
;
2500 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2501 if (old
!= self
->wmstate
)
2502 PROP_MSG(self
->window
, kde_wm_change_state
,
2503 self
->wmstate
, 1, 0, 0);
2505 /* this puts it after the current focused window */
2506 focus_order_remove(self
);
2507 focus_order_add_new(self
);
2514 client_change_state(self
);
2515 client_showhide(self
);
2516 if (STRUT_EXISTS(self
->strut
))
2517 screen_update_areas();
2520 /* iconify all direct transients */
2521 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2522 if (it
->data
!= self
)
2523 if (client_is_direct_child(self
, it
->data
))
2524 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2527 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2529 /* move up the transient chain as far as possible first */
2530 self
= client_search_top_parent(self
);
2531 client_iconify_recursive(self
, iconic
, curdesk
);
2534 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2538 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2539 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2541 /* check if already done */
2543 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2544 if (dir
== 1 && self
->max_horz
) return;
2545 if (dir
== 2 && self
->max_vert
) return;
2547 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2548 if (dir
== 1 && !self
->max_horz
) return;
2549 if (dir
== 2 && !self
->max_vert
) return;
2552 /* we just tell it to configure in the same place and client_configure
2553 worries about filling the screen with the window */
2556 w
= self
->area
.width
;
2557 h
= self
->area
.height
;
2560 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2561 RECT_SET(self
->pre_max_area
,
2562 self
->area
.x
, self
->pre_max_area
.y
,
2563 self
->area
.width
, self
->pre_max_area
.height
);
2565 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2566 RECT_SET(self
->pre_max_area
,
2567 self
->pre_max_area
.x
, self
->area
.y
,
2568 self
->pre_max_area
.width
, self
->area
.height
);
2573 a
= screen_area_monitor(self
->desktop
, 0);
2574 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2575 if (self
->pre_max_area
.width
> 0) {
2576 x
= self
->pre_max_area
.x
;
2577 w
= self
->pre_max_area
.width
;
2579 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2580 0, self
->pre_max_area
.height
);
2582 /* pick some fallbacks... */
2583 x
= a
->x
+ a
->width
/ 4;
2587 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2588 if (self
->pre_max_area
.height
> 0) {
2589 y
= self
->pre_max_area
.y
;
2590 h
= self
->pre_max_area
.height
;
2592 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2593 self
->pre_max_area
.width
, 0);
2595 /* pick some fallbacks... */
2596 y
= a
->y
+ a
->height
/ 4;
2602 if (dir
== 0 || dir
== 1) /* horz */
2603 self
->max_horz
= max
;
2604 if (dir
== 0 || dir
== 2) /* vert */
2605 self
->max_vert
= max
;
2607 client_change_state(self
); /* change the state hints on the client */
2609 client_setup_decor_and_functions(self
);
2611 client_move_resize(self
, x
, y
, w
, h
);
2614 void client_shade(ObClient
*self
, gboolean shade
)
2616 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2617 shade
) || /* can't shade */
2618 self
->shaded
== shade
) return; /* already done */
2620 /* when we're iconic, don't change the wmstate */
2621 if (!self
->iconic
) {
2624 old
= self
->wmstate
;
2625 self
->wmstate
= shade
? IconicState
: NormalState
;
2626 if (old
!= self
->wmstate
)
2627 PROP_MSG(self
->window
, kde_wm_change_state
,
2628 self
->wmstate
, 1, 0, 0);
2631 self
->shaded
= shade
;
2632 client_change_state(self
);
2633 /* resize the frame to just the titlebar */
2634 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2637 void client_close(ObClient
*self
)
2641 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2643 /* in the case that the client provides no means to requesting that it
2644 close, we just kill it */
2645 if (!self
->delete_window
)
2649 XXX: itd be cool to do timeouts and shit here for killing the client's
2651 like... if the window is around after 5 seconds, then the close button
2652 turns a nice red, and if this function is called again, the client is
2656 ce
.xclient
.type
= ClientMessage
;
2657 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2658 ce
.xclient
.display
= ob_display
;
2659 ce
.xclient
.window
= self
->window
;
2660 ce
.xclient
.format
= 32;
2661 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2662 ce
.xclient
.data
.l
[1] = event_curtime
;
2663 ce
.xclient
.data
.l
[2] = 0l;
2664 ce
.xclient
.data
.l
[3] = 0l;
2665 ce
.xclient
.data
.l
[4] = 0l;
2666 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2669 void client_kill(ObClient
*self
)
2671 XKillClient(ob_display
, self
->window
);
2674 void client_hilite(ObClient
*self
, gboolean hilite
)
2676 if (self
->demands_attention
== hilite
)
2677 return; /* no change */
2679 /* don't allow focused windows to hilite */
2680 self
->demands_attention
= hilite
&& !client_focused(self
);
2681 if (self
->demands_attention
)
2682 frame_flash_start(self
->frame
);
2684 frame_flash_stop(self
->frame
);
2685 client_change_state(self
);
2688 void client_set_desktop_recursive(ObClient
*self
,
2689 guint target
, gboolean donthide
)
2694 if (target
!= self
->desktop
) {
2696 ob_debug("Setting desktop %u\n", target
+1);
2698 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2700 /* remove from the old desktop(s) */
2701 focus_order_remove(self
);
2703 old
= self
->desktop
;
2704 self
->desktop
= target
;
2705 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2706 /* the frame can display the current desktop state */
2707 frame_adjust_state(self
->frame
);
2708 /* 'move' the window to the new desktop */
2710 client_showhide(self
);
2711 /* raise if it was not already on the desktop */
2712 if (old
!= DESKTOP_ALL
)
2714 if (STRUT_EXISTS(self
->strut
))
2715 screen_update_areas();
2717 /* add to the new desktop(s) */
2718 if (config_focus_new
)
2719 focus_order_to_top(self
);
2721 focus_order_to_bottom(self
);
2724 /* move all transients */
2725 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2726 if (it
->data
!= self
)
2727 if (client_is_direct_child(self
, it
->data
))
2728 client_set_desktop_recursive(it
->data
, target
, donthide
);
2731 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2733 self
= client_search_top_parent(self
);
2734 client_set_desktop_recursive(self
, target
, donthide
);
2737 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2739 while (child
!= parent
&&
2740 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2741 child
= child
->transient_for
;
2742 return child
== parent
;
2745 ObClient
*client_search_modal_child(ObClient
*self
)
2750 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2751 ObClient
*c
= it
->data
;
2752 if ((ret
= client_search_modal_child(c
))) return ret
;
2753 if (c
->modal
) return c
;
2758 gboolean
client_validate(ObClient
*self
)
2762 XSync(ob_display
, FALSE
); /* get all events on the server */
2764 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2765 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2766 XPutBackEvent(ob_display
, &e
);
2773 void client_set_wm_state(ObClient
*self
, glong state
)
2775 if (state
== self
->wmstate
) return; /* no change */
2779 client_iconify(self
, TRUE
, TRUE
);
2782 client_iconify(self
, FALSE
, TRUE
);
2787 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2789 gboolean shaded
= self
->shaded
;
2790 gboolean fullscreen
= self
->fullscreen
;
2791 gboolean undecorated
= self
->undecorated
;
2792 gboolean max_horz
= self
->max_horz
;
2793 gboolean max_vert
= self
->max_vert
;
2794 gboolean modal
= self
->modal
;
2795 gboolean iconic
= self
->iconic
;
2796 gboolean demands_attention
= self
->demands_attention
;
2799 if (!(action
== prop_atoms
.net_wm_state_add
||
2800 action
== prop_atoms
.net_wm_state_remove
||
2801 action
== prop_atoms
.net_wm_state_toggle
))
2802 /* an invalid action was passed to the client message, ignore it */
2805 for (i
= 0; i
< 2; ++i
) {
2806 Atom state
= i
== 0 ? data1
: data2
;
2808 if (!state
) continue;
2810 /* if toggling, then pick whether we're adding or removing */
2811 if (action
== prop_atoms
.net_wm_state_toggle
) {
2812 if (state
== prop_atoms
.net_wm_state_modal
)
2813 action
= modal
? prop_atoms
.net_wm_state_remove
:
2814 prop_atoms
.net_wm_state_add
;
2815 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2816 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2817 prop_atoms
.net_wm_state_add
;
2818 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2819 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2820 prop_atoms
.net_wm_state_add
;
2821 else if (state
== prop_atoms
.net_wm_state_shaded
)
2822 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2823 prop_atoms
.net_wm_state_add
;
2824 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2825 action
= self
->skip_taskbar
?
2826 prop_atoms
.net_wm_state_remove
:
2827 prop_atoms
.net_wm_state_add
;
2828 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2829 action
= self
->skip_pager
?
2830 prop_atoms
.net_wm_state_remove
:
2831 prop_atoms
.net_wm_state_add
;
2832 else if (state
== prop_atoms
.net_wm_state_hidden
)
2833 action
= self
->iconic
?
2834 prop_atoms
.net_wm_state_remove
:
2835 prop_atoms
.net_wm_state_add
;
2836 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2837 action
= fullscreen
?
2838 prop_atoms
.net_wm_state_remove
:
2839 prop_atoms
.net_wm_state_add
;
2840 else if (state
== prop_atoms
.net_wm_state_above
)
2841 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2842 prop_atoms
.net_wm_state_add
;
2843 else if (state
== prop_atoms
.net_wm_state_below
)
2844 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2845 prop_atoms
.net_wm_state_add
;
2846 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2847 action
= self
->demands_attention
?
2848 prop_atoms
.net_wm_state_remove
:
2849 prop_atoms
.net_wm_state_add
;
2850 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2851 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2852 prop_atoms
.net_wm_state_add
;
2855 if (action
== prop_atoms
.net_wm_state_add
) {
2856 if (state
== prop_atoms
.net_wm_state_modal
) {
2858 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2860 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2862 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2864 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2865 self
->skip_taskbar
= TRUE
;
2866 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2867 self
->skip_pager
= TRUE
;
2868 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2870 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2872 } else if (state
== prop_atoms
.net_wm_state_above
) {
2874 self
->below
= FALSE
;
2875 } else if (state
== prop_atoms
.net_wm_state_below
) {
2876 self
->above
= FALSE
;
2878 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2879 demands_attention
= TRUE
;
2880 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2884 } else { /* action == prop_atoms.net_wm_state_remove */
2885 if (state
== prop_atoms
.net_wm_state_modal
) {
2887 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2889 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2891 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2893 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2894 self
->skip_taskbar
= FALSE
;
2895 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2896 self
->skip_pager
= FALSE
;
2897 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2899 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2901 } else if (state
== prop_atoms
.net_wm_state_above
) {
2902 self
->above
= FALSE
;
2903 } else if (state
== prop_atoms
.net_wm_state_below
) {
2904 self
->below
= FALSE
;
2905 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2906 demands_attention
= FALSE
;
2907 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2908 undecorated
= FALSE
;
2912 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2913 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2915 if (max_horz
== max_vert
) { /* both going the same way */
2916 client_maximize(self
, max_horz
, 0);
2918 client_maximize(self
, max_horz
, 1);
2919 client_maximize(self
, max_vert
, 2);
2923 if (max_horz
!= self
->max_horz
)
2924 client_maximize(self
, max_horz
, 1);
2926 client_maximize(self
, max_vert
, 2);
2929 /* change fullscreen state before shading, as it will affect if the window
2931 if (fullscreen
!= self
->fullscreen
)
2932 client_fullscreen(self
, fullscreen
);
2933 if (shaded
!= self
->shaded
)
2934 client_shade(self
, shaded
);
2935 if (undecorated
!= self
->undecorated
)
2936 client_set_undecorated(self
, undecorated
);
2937 if (modal
!= self
->modal
) {
2938 self
->modal
= modal
;
2939 /* when a window changes modality, then its stacking order with its
2940 transients needs to change */
2943 if (iconic
!= self
->iconic
)
2944 client_iconify(self
, iconic
, FALSE
);
2946 if (demands_attention
!= self
->demands_attention
)
2947 client_hilite(self
, demands_attention
);
2949 client_change_state(self
); /* change the hint to reflect these changes */
2952 ObClient
*client_focus_target(ObClient
*self
)
2954 ObClient
*child
= NULL
;
2956 child
= client_search_modal_child(self
);
2957 if (child
) return child
;
2961 gboolean
client_can_focus(ObClient
*self
)
2965 /* choose the correct target */
2966 self
= client_focus_target(self
);
2968 if (!self
->frame
->visible
)
2971 if (!(self
->can_focus
|| self
->focus_notify
))
2974 /* do a check to see if the window has already been unmapped or destroyed
2975 do this intelligently while watching out for unmaps we've generated
2976 (ignore_unmaps > 0) */
2977 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2978 DestroyNotify
, &ev
)) {
2979 XPutBackEvent(ob_display
, &ev
);
2982 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2983 UnmapNotify
, &ev
)) {
2984 if (self
->ignore_unmaps
) {
2985 self
->ignore_unmaps
--;
2987 XPutBackEvent(ob_display
, &ev
);
2995 gboolean
client_focus(ObClient
*self
)
2997 /* choose the correct target */
2998 self
= client_focus_target(self
);
3001 if (!client_validate(self
))
3005 if (!client_can_focus(self
)) {
3006 if (!self
->frame
->visible
) {
3007 /* update the focus lists */
3008 focus_order_to_top(self
);
3013 ob_debug("Focusing client \"%s\" at time %u\n", self
->title
, event_curtime
);
3015 if (self
->can_focus
) {
3016 /* RevertToPointerRoot causes much more headache than RevertToNone, so
3017 I choose to use it always, hopefully to find errors quicker, if any
3018 are left. (I hate X. I hate focus events.)
3020 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
3021 #799. So now it is RevertToNone again.
3023 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
3027 if (self
->focus_notify
) {
3029 ce
.xclient
.type
= ClientMessage
;
3030 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3031 ce
.xclient
.display
= ob_display
;
3032 ce
.xclient
.window
= self
->window
;
3033 ce
.xclient
.format
= 32;
3034 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3035 ce
.xclient
.data
.l
[1] = event_curtime
;
3036 ce
.xclient
.data
.l
[2] = 0l;
3037 ce
.xclient
.data
.l
[3] = 0l;
3038 ce
.xclient
.data
.l
[4] = 0l;
3039 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3043 ob_debug("%sively focusing %lx at %d\n",
3044 (self
->can_focus
? "act" : "pass"),
3045 self
->window
, (gint
) event_curtime
);
3048 /* Cause the FocusIn to come back to us. Important for desktop switches,
3049 since otherwise we'll have no FocusIn on the queue and send it off to
3050 the focus_backup. */
3051 XSync(ob_display
, FALSE
);
3055 /* Used when the current client is closed or otherwise hidden, focus_last will
3056 then prevent focus from going to the mouse pointer
3058 void client_unfocus(ObClient
*self
)
3060 if (focus_client
== self
) {
3062 ob_debug("client_unfocus for %lx\n", self
->window
);
3064 focus_fallback(OB_FOCUS_FALLBACK_CLOSED
);
3068 void client_activate(ObClient
*self
, gboolean here
, gboolean user
, Time time
)
3070 /* XXX do some stuff here if user is false to determine if we really want
3071 to activate it or not (a parent or group member is currently
3074 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3076 self
->window
, time
, client_last_user_time
,
3077 (user
? "user" : "application"));
3078 if (!user
&& time
&& time
< client_last_user_time
)
3079 client_hilite(self
, TRUE
);
3081 if (client_normal(self
) && screen_showing_desktop
)
3082 screen_show_desktop(FALSE
);
3084 client_iconify(self
, FALSE
, here
);
3085 if (self
->desktop
!= DESKTOP_ALL
&&
3086 self
->desktop
!= screen_desktop
) {
3088 client_set_desktop(self
, screen_desktop
, FALSE
);
3090 screen_set_desktop(self
->desktop
);
3091 } else if (!self
->frame
->visible
)
3092 /* if its not visible for other reasons, then don't mess
3096 client_shade(self
, FALSE
);
3100 /* we do this an action here. this is rather important. this is because
3101 we want the results from the focus change to take place BEFORE we go
3102 about raising the window. when a fullscreen window loses focus, we
3103 need this or else the raise wont be able to raise above the
3104 to-lose-focus fullscreen window. */
3109 void client_raise(ObClient
*self
)
3111 action_run_string("Raise", self
, CurrentTime
);
3114 void client_lower(ObClient
*self
)
3116 action_run_string("Lower", self
, CurrentTime
);
3119 gboolean
client_focused(ObClient
*self
)
3121 return self
== focus_client
;
3124 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3127 /* si is the smallest image >= req */
3128 /* li is the largest image < req */
3129 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3131 if (!self
->nicons
) {
3132 ObClientIcon
*parent
= NULL
;
3134 if (self
->transient_for
) {
3135 if (self
->transient_for
!= OB_TRAN_GROUP
)
3136 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3139 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3140 ObClient
*c
= it
->data
;
3141 if (c
!= self
&& !c
->transient_for
) {
3142 if ((parent
= client_icon_recursive(c
, w
, h
)))
3152 for (i
= 0; i
< self
->nicons
; ++i
) {
3153 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3154 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3158 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3163 if (largest
== 0) /* didnt find one smaller than the requested size */
3164 return &self
->icons
[si
];
3165 return &self
->icons
[li
];
3168 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3171 static ObClientIcon deficon
;
3173 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3174 deficon
.width
= deficon
.height
= 48;
3175 deficon
.data
= ob_rr_theme
->def_win_icon
;
3181 /* this be mostly ripped from fvwm */
3182 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3184 gint my_cx
, my_cy
, his_cx
, his_cy
;
3187 gint score
, best_score
;
3188 ObClient
*best_client
, *cur
;
3194 /* first, find the centre coords of the currently focused window */
3195 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3196 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3201 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3204 /* the currently selected window isn't interesting */
3207 if (!client_normal(cur
))
3209 /* using c->desktop instead of screen_desktop doesn't work if the
3210 * current window was omnipresent, hope this doesn't have any other
3212 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3216 if(!(client_focus_target(cur
) == cur
&&
3217 client_can_focus(cur
)))
3220 /* find the centre coords of this window, from the
3221 * currently focused window's point of view */
3222 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3223 + cur
->frame
->area
.width
/ 2;
3224 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3225 + cur
->frame
->area
.height
/ 2;
3227 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3228 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3230 /* Rotate the diagonals 45 degrees counterclockwise.
3231 * To do this, multiply the matrix /+h +h\ with the
3232 * vector (x y). \-h +h/
3233 * h = sqrt(0.5). We can set h := 1 since absolute
3234 * distance doesn't matter here. */
3235 tx
= his_cx
+ his_cy
;
3236 his_cy
= -his_cx
+ his_cy
;
3241 case OB_DIRECTION_NORTH
:
3242 case OB_DIRECTION_SOUTH
:
3243 case OB_DIRECTION_NORTHEAST
:
3244 case OB_DIRECTION_SOUTHWEST
:
3245 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3246 distance
= ((dir
== OB_DIRECTION_NORTH
||
3247 dir
== OB_DIRECTION_NORTHEAST
) ?
3250 case OB_DIRECTION_EAST
:
3251 case OB_DIRECTION_WEST
:
3252 case OB_DIRECTION_SOUTHEAST
:
3253 case OB_DIRECTION_NORTHWEST
:
3254 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3255 distance
= ((dir
== OB_DIRECTION_WEST
||
3256 dir
== OB_DIRECTION_NORTHWEST
) ?
3261 /* the target must be in the requested direction */
3265 /* Calculate score for this window. The smaller the better. */
3266 score
= distance
+ offset
;
3268 /* windows more than 45 degrees off the direction are
3269 * heavily penalized and will only be chosen if nothing
3270 * else within a million pixels */
3271 if(offset
> distance
)
3274 if(best_score
== -1 || score
< best_score
)
3282 void client_set_layer(ObClient
*self
, gint layer
)
3286 self
->above
= FALSE
;
3287 } else if (layer
== 0) {
3288 self
->below
= self
->above
= FALSE
;
3290 self
->below
= FALSE
;
3293 client_calc_layer(self
);
3294 client_change_state(self
); /* reflect this in the state hints */
3297 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3299 if (self
->undecorated
!= undecorated
) {
3300 self
->undecorated
= undecorated
;
3301 client_setup_decor_and_functions(self
);
3302 /* Make sure the client knows it might have moved. Maybe there is a
3303 * better way of doing this so only one client_configure is sent, but
3304 * since 125 of these are sent per second when moving the window (with
3305 * user = FALSE) i doubt it matters much.
3307 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3308 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3309 client_change_state(self
); /* reflect this in the state hints */
3313 guint
client_monitor(ObClient
*self
)
3315 return screen_find_monitor(&self
->frame
->area
);
3318 ObClient
*client_search_top_parent(ObClient
*self
)
3320 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3321 self
= self
->transient_for
;
3325 GSList
*client_search_all_top_parents(ObClient
*self
)
3329 /* move up the direct transient chain as far as possible */
3330 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3331 self
= self
->transient_for
;
3333 if (!self
->transient_for
)
3334 ret
= g_slist_prepend(ret
, self
);
3338 g_assert(self
->group
);
3340 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3341 ObClient
*c
= it
->data
;
3343 if (!c
->transient_for
)
3344 ret
= g_slist_prepend(ret
, c
);
3347 if (ret
== NULL
) /* no group parents */
3348 ret
= g_slist_prepend(ret
, self
);
3354 ObClient
*client_search_focus_parent(ObClient
*self
)
3356 if (self
->transient_for
) {
3357 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3358 if (client_focused(self
->transient_for
))
3359 return self
->transient_for
;
3363 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3364 ObClient
*c
= it
->data
;
3366 /* checking transient_for prevents infinate loops! */
3367 if (c
!= self
&& !c
->transient_for
)
3368 if (client_focused(c
))
3377 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3379 if (self
->transient_for
) {
3380 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3381 if (self
->transient_for
== search
)
3386 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3387 ObClient
*c
= it
->data
;
3389 /* checking transient_for prevents infinate loops! */
3390 if (c
!= self
&& !c
->transient_for
)
3400 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3404 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3405 if (sit
->data
== search
)
3407 if (client_search_transient(sit
->data
, search
))
3413 void client_update_sm_client_id(ObClient
*self
)
3415 g_free(self
->sm_client_id
);
3416 self
->sm_client_id
= NULL
;
3418 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3420 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3421 &self
->sm_client_id
);
3424 #define WANT_EDGE(cur, c) \
3427 if(!client_normal(cur)) \
3429 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3433 if(cur->layer < c->layer && !config_resist_layers_below) \
3436 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3437 if ((his_edge_start >= my_edge_start && \
3438 his_edge_start <= my_edge_end) || \
3439 (my_edge_start >= his_edge_start && \
3440 my_edge_start <= his_edge_end)) \
3443 /* finds the nearest edge in the given direction from the current client
3444 * note to self: the edge is the -frame- edge (the actual one), not the
3447 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3449 gint dest
, monitor_dest
;
3450 gint my_edge_start
, my_edge_end
, my_offset
;
3457 a
= screen_area(c
->desktop
);
3458 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3461 case OB_DIRECTION_NORTH
:
3462 my_edge_start
= c
->frame
->area
.x
;
3463 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3464 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3466 /* default: top of screen */
3467 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3468 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3469 /* if the monitor edge comes before the screen edge, */
3470 /* use that as the destination instead. (For xinerama) */
3471 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3472 dest
= monitor_dest
;
3474 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3475 gint his_edge_start
, his_edge_end
, his_offset
;
3476 ObClient
*cur
= it
->data
;
3480 his_edge_start
= cur
->frame
->area
.x
;
3481 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3482 his_offset
= cur
->frame
->area
.y
+
3483 (hang
? 0 : cur
->frame
->area
.height
);
3485 if(his_offset
+ 1 > my_offset
)
3488 if(his_offset
< dest
)
3491 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3494 case OB_DIRECTION_SOUTH
:
3495 my_edge_start
= c
->frame
->area
.x
;
3496 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3497 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3499 /* default: bottom of screen */
3500 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3501 monitor_dest
= monitor
->y
+ monitor
->height
-
3502 (hang
? c
->frame
->area
.height
: 0);
3503 /* if the monitor edge comes before the screen edge, */
3504 /* use that as the destination instead. (For xinerama) */
3505 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3506 dest
= monitor_dest
;
3508 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3509 gint his_edge_start
, his_edge_end
, his_offset
;
3510 ObClient
*cur
= it
->data
;
3514 his_edge_start
= cur
->frame
->area
.x
;
3515 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3516 his_offset
= cur
->frame
->area
.y
+
3517 (hang
? cur
->frame
->area
.height
: 0);
3520 if(his_offset
- 1 < my_offset
)
3523 if(his_offset
> dest
)
3526 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3529 case OB_DIRECTION_WEST
:
3530 my_edge_start
= c
->frame
->area
.y
;
3531 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3532 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3534 /* default: leftmost egde of screen */
3535 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3536 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3537 /* if the monitor edge comes before the screen edge, */
3538 /* use that as the destination instead. (For xinerama) */
3539 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3540 dest
= monitor_dest
;
3542 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3543 gint his_edge_start
, his_edge_end
, his_offset
;
3544 ObClient
*cur
= it
->data
;
3548 his_edge_start
= cur
->frame
->area
.y
;
3549 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3550 his_offset
= cur
->frame
->area
.x
+
3551 (hang
? 0 : cur
->frame
->area
.width
);
3553 if(his_offset
+ 1 > my_offset
)
3556 if(his_offset
< dest
)
3559 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3562 case OB_DIRECTION_EAST
:
3563 my_edge_start
= c
->frame
->area
.y
;
3564 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3565 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3567 /* default: rightmost edge of screen */
3568 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3569 monitor_dest
= monitor
->x
+ monitor
->width
-
3570 (hang
? c
->frame
->area
.width
: 0);
3571 /* if the monitor edge comes before the screen edge, */
3572 /* use that as the destination instead. (For xinerama) */
3573 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3574 dest
= monitor_dest
;
3576 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3577 gint his_edge_start
, his_edge_end
, his_offset
;
3578 ObClient
*cur
= it
->data
;
3582 his_edge_start
= cur
->frame
->area
.y
;
3583 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3584 his_offset
= cur
->frame
->area
.x
+
3585 (hang
? cur
->frame
->area
.width
: 0);
3587 if(his_offset
- 1 < my_offset
)
3590 if(his_offset
> dest
)
3593 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3596 case OB_DIRECTION_NORTHEAST
:
3597 case OB_DIRECTION_SOUTHEAST
:
3598 case OB_DIRECTION_NORTHWEST
:
3599 case OB_DIRECTION_SOUTHWEST
:
3600 /* not implemented */
3602 g_assert_not_reached();
3603 dest
= 0; /* suppress warning */
3608 ObClient
* client_under_pointer()
3612 ObClient
*ret
= NULL
;
3614 if (screen_pointer_pos(&x
, &y
)) {
3615 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3616 if (WINDOW_IS_CLIENT(it
->data
)) {
3617 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3618 if (c
->frame
->visible
&&
3619 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3629 gboolean
client_has_group_siblings(ObClient
*self
)
3631 return self
->group
&& self
->group
->members
->next
;