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.
21 #include "client_time_heap.h"
23 #include "startupnotify.h"
27 #include "moveresize.h"
30 #include "extensions.h"
40 #include "menuframe.h"
43 #include "render/render.h"
46 #include <X11/Xutil.h>
48 /*! The event mask to grab on client windows */
49 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask)
51 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
56 ObClientDestructor func
;
60 GList
*client_list
= NULL
;
61 ObClientTimeHeap
*client_user_times
= NULL
;
63 static GSList
*client_destructors
= NULL
;
65 static void client_get_all(ObClient
*self
);
66 static void client_toggle_border(ObClient
*self
, gboolean show
);
67 static void client_get_startup_id(ObClient
*self
);
68 static void client_get_area(ObClient
*self
);
69 static void client_get_desktop(ObClient
*self
);
70 static void client_get_state(ObClient
*self
);
71 static void client_get_layer(ObClient
*self
);
72 static void client_get_shaped(ObClient
*self
);
73 static void client_get_mwm_hints(ObClient
*self
);
74 static void client_get_gravity(ObClient
*self
);
75 static void client_change_allowed_actions(ObClient
*self
);
76 static void client_change_state(ObClient
*self
);
77 static void client_change_wm_state(ObClient
*self
);
78 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
79 static void client_restore_session_state(ObClient
*self
);
80 static void client_restore_session_stacking(ObClient
*self
);
81 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
82 static void client_unfocus(ObClient
*self
);
84 void client_startup(gboolean reconfig
)
88 client_user_times
= client_time_heap_new();
92 void client_shutdown(gboolean reconfig
)
94 client_time_heap_free(client_user_times
);
97 void client_add_destructor(ObClientDestructor func
, gpointer data
)
99 Destructor
*d
= g_new(Destructor
, 1);
102 client_destructors
= g_slist_prepend(client_destructors
, d
);
105 void client_remove_destructor(ObClientDestructor func
)
109 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
110 Destructor
*d
= it
->data
;
111 if (d
->func
== func
) {
113 client_destructors
= g_slist_delete_link(client_destructors
, it
);
119 void client_set_list()
121 Window
*windows
, *win_it
;
123 guint size
= g_list_length(client_list
);
125 /* create an array of the window ids */
127 windows
= g_new(Window
, size
);
129 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
130 *win_it
= ((ObClient
*)it
->data
)->window
;
134 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
135 net_client_list
, window
, (gulong
*)windows
, size
);
144 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
148 for (it = self->transients; it; it = g_slist_next(it)) {
149 if (!func(it->data, data)) return;
150 client_foreach_transient(it->data, func, data);
154 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
156 if (self->transient_for) {
157 if (self->transient_for != OB_TRAN_GROUP) {
158 if (!func(self->transient_for, data)) return;
159 client_foreach_ancestor(self->transient_for, func, data);
163 for (it = self->group->members; it; it = g_slist_next(it))
164 if (it->data != self &&
165 !((ObClient*)it->data)->transient_for) {
166 if (!func(it->data, data)) return;
167 client_foreach_ancestor(it->data, func, data);
174 void client_manage_all()
179 XWindowAttributes attrib
;
181 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
182 &w
, &w
, &children
, &nchild
);
184 /* remove all icon windows from the list */
185 for (i
= 0; i
< nchild
; i
++) {
186 if (children
[i
] == None
) continue;
187 wmhints
= XGetWMHints(ob_display
, children
[i
]);
189 if ((wmhints
->flags
& IconWindowHint
) &&
190 (wmhints
->icon_window
!= children
[i
]))
191 for (j
= 0; j
< nchild
; j
++)
192 if (children
[j
] == wmhints
->icon_window
) {
200 for (i
= 0; i
< nchild
; ++i
) {
201 if (children
[i
] == None
)
203 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
204 if (attrib
.override_redirect
) continue;
206 if (attrib
.map_state
!= IsUnmapped
)
207 client_manage(children
[i
]);
213 void client_manage(Window window
)
217 XWindowAttributes attrib
;
218 XSetWindowAttributes attrib_set
;
220 gboolean activate
= FALSE
;
221 ObAppSettings
*settings
;
226 /* check if it has already been unmapped by the time we started mapping.
227 the grab does a sync so we don't have to here */
228 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
229 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
231 XPutBackEvent(ob_display
, &e
);
233 ob_debug("Trying to manage unmapped window. Aborting that.\n");
235 return; /* don't manage it */
238 /* make sure it isn't an override-redirect window */
239 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
240 attrib
.override_redirect
)
243 return; /* don't manage it */
246 /* is the window a docking app */
247 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
248 if ((wmhint
->flags
& StateHint
) &&
249 wmhint
->initial_state
== WithdrawnState
)
251 dock_add(window
, wmhint
);
259 ob_debug("Managing window: %lx\n", window
);
261 /* choose the events we want to receive on the CLIENT window */
262 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
263 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
264 XChangeWindowAttributes(ob_display
, window
,
265 CWEventMask
|CWDontPropagate
, &attrib_set
);
268 /* create the ObClient struct, and populate it from the hints on the
270 self
= g_new0(ObClient
, 1);
271 self
->obwin
.type
= Window_Client
;
272 self
->window
= window
;
274 /* non-zero defaults */
275 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
277 self
->desktop
= screen_num_desktops
; /* always an invalid value */
278 self
->user_time
= CurrentTime
;
280 client_get_all(self
);
281 /* per-app settings override stuff, and return the settings for other
283 settings
= client_get_settings_state(self
);
284 /* the session should get the last say */
285 client_restore_session_state(self
);
287 client_calc_layer(self
);
290 Time t
= sn_app_started(self
->startup_id
, self
->class);
291 if (t
) self
->user_time
= t
;
294 /* update the focus lists, do this before the call to change_state or
295 it can end up in the list twice! */
296 focus_order_add_new(self
);
298 /* remove the client's border (and adjust re gravity) */
299 client_toggle_border(self
, FALSE
);
301 /* specify that if we exit, the window should not be destroyed and should
302 be reparented back to root automatically */
303 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
305 /* create the decoration frame for the client window */
306 self
->frame
= frame_new(self
);
308 frame_grab_client(self
->frame
, self
);
310 /* do this after we have a frame.. it uses the frame to help determine the
311 WM_STATE to apply. */
312 client_change_state(self
);
316 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
317 client_restore_session_stacking(self
);
319 /* focus the new window? */
320 if (ob_state() != OB_STATE_STARTING
&&
321 /* this means focus=true for window is same as config_focus_new=true */
322 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
323 client_search_focus_parent(self
)) &&
324 /* this checks for focus=false for the window */
325 (!settings
|| settings
->focus
!= 0) &&
326 /* note the check against Type_Normal/Dialog, not client_normal(self),
327 which would also include other types. in this case we want more
328 strict rules for focus */
329 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
330 self
->type
== OB_CLIENT_TYPE_DIALOG
))
334 if (self
->desktop
!= screen_desktop
) {
335 /* activate the window */
338 gboolean group_foc
= FALSE
;
343 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
345 if (client_focused(it
->data
))
353 (!self
->transient_for
&& (!self
->group
||
354 !self
->group
->members
->next
))) ||
355 client_search_focus_tree_full(self
) ||
357 !client_normal(focus_client
))
359 /* activate the window */
366 /* get the current position */
370 /* figure out placement for the window */
371 if (ob_state() == OB_STATE_RUNNING
) {
374 transient
= place_client(self
, &newx
, &newy
, settings
);
376 /* make sure the window is visible. */
377 client_find_onscreen(self
, &newx
, &newy
,
378 self
->frame
->area
.width
,
379 self
->frame
->area
.height
,
380 /* non-normal clients has less rules, and
381 windows that are being restored from a
382 session do also. we can assume you want
383 it back where you saved it. Clients saying
384 they placed themselves are subjected to
385 harder rules, ones that are placed by
386 place.c or by the user are allowed partially
387 off-screen and on xinerama divides (ie,
388 it is up to the placement routines to avoid
389 the xinerama divides) */
391 (((self
->positioned
& PPosition
) &&
392 !(self
->positioned
& USPosition
)) &&
393 client_normal(self
) &&
397 /* do this after the window is placed, so the premax/prefullscreen numbers
399 also, this moves the window to the position where it has been placed
401 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
402 self
->window
, newx
, newy
, self
->area
.width
, self
->area
.height
);
403 client_apply_startup_state(self
, newx
, newy
);
405 keyboard_grab_for_client(self
, TRUE
);
406 mouse_grab_for_client(self
, TRUE
);
409 /* This is focus stealing prevention */
410 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
411 self
->window
, self
->user_time
,
412 client_time_heap_maximum(client_user_times
));
414 /* If a nothing at all, or a parent was focused, then focus this
417 if (!focus_client
|| client_search_focus_parent(self
) != NULL
)
421 guint32 last_time
= client_time_heap_maximum(client_user_times
);
422 /* If time stamp is old, don't steal focus */
423 if (self
->user_time
&& last_time
&&
424 !event_time_after(self
->user_time
, last_time
))
428 /* Don't steal focus from globally active clients.
429 I stole this idea from KWin. It seems nice.
431 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
))
437 /* since focus can change the stacking orders, if we focus the
438 window then the standard raise it gets is not enough, we need
439 to queue one for after the focus change takes place */
442 ob_debug("Focus stealing prevention activated for %s with time %u "
444 self
->title
, self
->user_time
,
445 client_time_heap_maximum(client_user_times
));
446 /* if the client isn't focused, then hilite it so the user
448 client_hilite(self
, TRUE
);
452 /* This may look rather odd. Well it's because new windows are added
453 to the stacking order non-intrusively. If we're not going to focus
454 the new window or hilite it, then we raise it to the top. This will
455 take affect for things that don't get focused like splash screens.
456 Also if you don't have focus_new enabled, then it's going to get
457 raised to the top. Legacy begets legacy I guess?
462 /* this has to happen before we try focus the window, but we want it to
463 happen after the client's stacking has been determined or it looks bad
467 /* use client_focus instead of client_activate cuz client_activate does
468 stuff like switch desktops etc and I'm not interested in all that when
469 a window maps since its not based on an action from the user like
470 clicking a window to activate it. so keep the new window out of the way
473 /* if using focus_delay, stop the timer now so that focus doesn't
475 event_halt_focus_delay();
479 /* client_activate does this but we aret using it so we have to do it
481 if (screen_showing_desktop
)
482 screen_show_desktop(FALSE
);
484 /* add to client list/map */
485 client_list
= g_list_append(client_list
, self
);
486 g_hash_table_insert(window_map
, &self
->window
, self
);
488 /* this has to happen after we're in the client_list */
489 if (STRUT_EXISTS(self
->strut
))
490 screen_update_areas();
492 /* update the list hints */
495 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
498 void client_unmanage_all()
500 while (client_list
!= NULL
)
501 client_unmanage(client_list
->data
);
504 void client_unmanage(ObClient
*self
)
509 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
510 self
->title
? self
->title
: "");
512 g_assert(self
!= NULL
);
514 /* update the focus lists */
515 focus_order_remove(self
);
517 if (focus_client
== self
) {
520 /* focus the last focused window on the desktop, and ignore enter
521 events from the unmap so it doesnt mess with the focus */
522 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
523 /* remove these flags so we don't end up getting focused in the
525 self
->can_focus
= FALSE
;
526 self
->focus_notify
= FALSE
;
528 client_unfocus(self
);
531 /* potentially fix focusLast */
532 if (config_focus_last
)
533 grab_pointer(TRUE
, OB_CURSOR_NONE
);
535 frame_hide(self
->frame
);
538 keyboard_grab_for_client(self
, FALSE
);
539 mouse_grab_for_client(self
, FALSE
);
541 /* remove the window from our save set */
542 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
544 /* we dont want events no more */
545 XSelectInput(ob_display
, self
->window
, NoEventMask
);
547 /* remove from the time heap */
548 client_time_heap_remove(client_user_times
, self
);
550 client_list
= g_list_remove(client_list
, self
);
551 stacking_remove(self
);
552 g_hash_table_remove(window_map
, &self
->window
);
554 /* once the client is out of the list, update the struts to remove its
556 if (STRUT_EXISTS(self
->strut
))
557 screen_update_areas();
559 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
560 Destructor
*d
= it
->data
;
561 d
->func(self
, d
->data
);
564 /* tell our parent(s) that we're gone */
565 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
566 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
567 if (it
->data
!= self
)
568 ((ObClient
*)it
->data
)->transients
=
569 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
570 } else if (self
->transient_for
) { /* transient of window */
571 self
->transient_for
->transients
=
572 g_slist_remove(self
->transient_for
->transients
, self
);
575 /* tell our transients that we're gone */
576 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
577 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
578 ((ObClient
*)it
->data
)->transient_for
= NULL
;
579 client_calc_layer(it
->data
);
583 /* remove from its group */
585 group_remove(self
->group
, self
);
589 /* restore the window's original geometry so it is not lost */
593 if (self
->fullscreen
)
594 a
= self
->pre_fullscreen_area
;
595 else if (self
->max_horz
|| self
->max_vert
) {
596 if (self
->max_horz
) {
597 a
.x
= self
->pre_max_area
.x
;
598 a
.width
= self
->pre_max_area
.width
;
600 if (self
->max_vert
) {
601 a
.y
= self
->pre_max_area
.y
;
602 a
.height
= self
->pre_max_area
.height
;
606 /* give the client its border back */
607 client_toggle_border(self
, TRUE
);
609 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
610 self
->decorations
= 0; /* unmanaged windows have no decor */
612 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
615 /* reparent the window out of the frame, and free the frame */
616 frame_release_client(self
->frame
, self
);
619 if (ob_state() != OB_STATE_EXITING
) {
620 /* these values should not be persisted across a window
622 PROP_ERASE(self
->window
, net_wm_desktop
);
623 PROP_ERASE(self
->window
, net_wm_state
);
624 PROP_ERASE(self
->window
, wm_state
);
626 /* if we're left in an unmapped state, the client wont be mapped. this
627 is bad, since we will no longer be managing the window on restart */
628 XMapWindow(ob_display
, self
->window
);
631 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
633 /* free all data allocated in the client struct */
634 g_slist_free(self
->transients
);
635 for (j
= 0; j
< self
->nicons
; ++j
)
636 g_free(self
->icons
[j
].data
);
637 if (self
->nicons
> 0)
640 g_free(self
->icon_title
);
644 g_free(self
->sm_client_id
);
647 /* update the list hints */
650 if (config_focus_last
)
651 grab_pointer(FALSE
, OB_CURSOR_NONE
);
654 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
656 ObAppSettings
*settings
= NULL
;
659 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
660 ObAppSettings
*app
= it
->data
;
662 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
663 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
664 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
665 && !strcmp(app
->name
, self
->name
)))
667 ob_debug("Window matching: %s\n", app
->name
);
668 /* Match if no role was specified in the per app setting, or if the
669 * string matches the beginning of the role, since apps like to set
670 * the role to things like browser-window-23c4b2f */
672 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
682 if (settings
->shade
!= -1)
683 self
->shaded
= !!settings
->shade
;
684 if (settings
->decor
!= -1)
685 self
->undecorated
= !settings
->decor
;
686 if (settings
->iconic
!= -1)
687 self
->iconic
= !!settings
->iconic
;
688 if (settings
->skip_pager
!= -1)
689 self
->skip_pager
= !!settings
->skip_pager
;
690 if (settings
->skip_taskbar
!= -1)
691 self
->skip_taskbar
= !!settings
->skip_taskbar
;
693 if (settings
->max_vert
!= -1)
694 self
->max_vert
= !!settings
->max_vert
;
695 if (settings
->max_horz
!= -1)
696 self
->max_vert
= !!settings
->max_horz
;
698 if (settings
->fullscreen
!= -1)
699 self
->fullscreen
= !!settings
->fullscreen
;
701 if (settings
->desktop
< screen_num_desktops
702 || settings
->desktop
== DESKTOP_ALL
)
703 self
->desktop
= settings
->desktop
;
705 if (settings
->layer
== -1) {
709 else if (settings
->layer
== 0) {
713 else if (settings
->layer
== 1) {
721 static void client_restore_session_state(ObClient
*self
)
725 if (!(it
= session_state_find(self
)))
728 self
->session
= it
->data
;
730 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
731 self
->positioned
= PPosition
;
732 if (self
->session
->w
> 0)
733 self
->area
.width
= self
->session
->w
;
734 if (self
->session
->h
> 0)
735 self
->area
.height
= self
->session
->h
;
736 XResizeWindow(ob_display
, self
->window
,
737 self
->area
.width
, self
->area
.height
);
739 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
740 self
->session
->desktop
:
741 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
742 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
744 self
->shaded
= self
->session
->shaded
;
745 self
->iconic
= self
->session
->iconic
;
746 self
->skip_pager
= self
->session
->skip_pager
;
747 self
->skip_taskbar
= self
->session
->skip_taskbar
;
748 self
->fullscreen
= self
->session
->fullscreen
;
749 self
->above
= self
->session
->above
;
750 self
->below
= self
->session
->below
;
751 self
->max_horz
= self
->session
->max_horz
;
752 self
->max_vert
= self
->session
->max_vert
;
755 static void client_restore_session_stacking(ObClient
*self
)
759 if (!self
->session
) return;
761 it
= g_list_find(session_saved_state
, self
->session
);
762 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
765 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
766 if (session_state_cmp(it
->data
, cit
->data
))
769 client_calc_layer(self
);
770 stacking_below(CLIENT_AS_WINDOW(self
),
771 CLIENT_AS_WINDOW(cit
->data
));
777 void client_move_onscreen(ObClient
*self
, gboolean rude
)
779 gint x
= self
->area
.x
;
780 gint y
= self
->area
.y
;
781 if (client_find_onscreen(self
, &x
, &y
,
782 self
->frame
->area
.width
,
783 self
->frame
->area
.height
, rude
)) {
784 client_move(self
, x
, y
);
788 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
792 gint ox
= *x
, oy
= *y
;
794 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
797 /* XXX watch for xinerama dead areas */
798 /* This makes sure windows aren't entirely outside of the screen so you
799 can't see them at all.
800 It makes sure 10% of the window is on the screen at least. At don't let
801 it move itself off the top of the screen, which would hide the titlebar
802 on you. (The user can still do this if they want too, it's only limiting
805 if (client_normal(self
)) {
806 a
= screen_area(self
->desktop
);
807 if (!self
->strut
.right
&&
808 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
809 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
810 if (!self
->strut
.bottom
&&
811 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
812 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
813 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
814 *x
= a
->x
- self
->frame
->area
.width
*9/10;
815 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
816 *y
= a
->y
- self
->frame
->area
.width
*9/10;
819 /* This here doesn't let windows even a pixel outside the screen,
820 * when called from client_manage, programs placing themselves are
821 * forced completely onscreen, while things like
822 * xterm -geometry resolution-width/2 will work fine. Trying to
823 * place it completely offscreen will be handled in the above code.
824 * Sorry for this confused comment, i am tired. */
826 /* avoid the xinerama monitor divide while we're at it,
827 * remember to fix the placement stuff to avoid it also and
828 * then remove this XXX */
829 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
830 /* dont let windows map into the strut unless they
831 are bigger than the available area */
833 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
834 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
835 *x
= a
->x
+ a
->width
- w
;
837 if (h
<= a
->height
) {
838 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
839 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
840 *y
= a
->y
+ a
->height
- h
;
844 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
847 return ox
!= *x
|| oy
!= *y
;
850 static void client_toggle_border(ObClient
*self
, gboolean show
)
852 /* adjust our idea of where the client is, based on its border. When the
853 border is removed, the client should now be considered to be in a
855 when re-adding the border to the client, the same operation needs to be
857 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
858 gint x
= oldx
, y
= oldy
;
859 switch(self
->gravity
) {
861 case NorthWestGravity
:
863 case SouthWestGravity
:
865 case NorthEastGravity
:
867 case SouthEastGravity
:
868 if (show
) x
-= self
->border_width
* 2;
869 else x
+= self
->border_width
* 2;
876 if (show
) x
-= self
->border_width
;
877 else x
+= self
->border_width
;
880 switch(self
->gravity
) {
882 case NorthWestGravity
:
884 case NorthEastGravity
:
886 case SouthWestGravity
:
888 case SouthEastGravity
:
889 if (show
) y
-= self
->border_width
* 2;
890 else y
+= self
->border_width
* 2;
897 if (show
) y
-= self
->border_width
;
898 else y
+= self
->border_width
;
905 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
907 /* set border_width to 0 because there is no border to add into
908 calculations anymore */
909 self
->border_width
= 0;
911 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
915 static void client_get_all(ObClient
*self
)
917 client_get_area(self
);
918 client_get_mwm_hints(self
);
920 /* The transient hint is used to pick a type, but the type can also affect
921 transiency (dialogs are always made transients of their group if they
922 have one). This is Havoc's idea, but it is needed to make some apps
923 work right (eg tsclient). */
924 client_update_transient_for(self
);
925 client_get_type(self
);/* this can change the mwmhints for special cases */
926 client_get_state(self
);
927 client_update_transient_for(self
);
929 client_update_wmhints(self
);
930 client_get_startup_id(self
);
931 client_get_desktop(self
);/* uses transient data/group/startup id if a
932 desktop is not specified */
933 client_get_shaped(self
);
935 client_get_layer(self
); /* if layer hasn't been specified, get it from
936 other sources if possible */
939 /* a couple type-based defaults for new windows */
941 /* this makes sure that these windows appear on all desktops */
942 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
943 self
->desktop
= DESKTOP_ALL
;
946 client_update_protocols(self
);
948 client_get_gravity(self
); /* get the attribute gravity */
949 client_update_normal_hints(self
); /* this may override the attribute
952 /* got the type, the mwmhints, the protocols, and the normal hints
953 (min/max sizes), so we're ready to set up the decorations/functions */
954 client_setup_decor_and_functions(self
);
956 client_update_title(self
);
957 client_update_class(self
);
958 client_update_sm_client_id(self
);
959 client_update_strut(self
);
960 client_update_icons(self
);
961 client_update_user_time(self
);
964 static void client_get_startup_id(ObClient
*self
)
966 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
968 PROP_GETS(self
->group
->leader
,
969 net_startup_id
, utf8
, &self
->startup_id
);
972 static void client_get_area(ObClient
*self
)
974 XWindowAttributes wattrib
;
977 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
978 g_assert(ret
!= BadWindow
);
980 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
981 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
982 self
->border_width
= wattrib
.border_width
;
984 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
985 wattrib
.width
, wattrib
.height
);
988 static void client_get_desktop(ObClient
*self
)
990 guint32 d
= screen_num_desktops
; /* an always-invalid value */
992 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
993 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
994 self
->desktop
= screen_num_desktops
- 1;
998 gboolean trdesk
= FALSE
;
1000 if (self
->transient_for
) {
1001 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1002 self
->desktop
= self
->transient_for
->desktop
;
1007 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1008 if (it
->data
!= self
&&
1009 !((ObClient
*)it
->data
)->transient_for
) {
1010 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
1017 /* try get from the startup-notification protocol */
1018 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1019 if (self
->desktop
>= screen_num_desktops
&&
1020 self
->desktop
!= DESKTOP_ALL
)
1021 self
->desktop
= screen_num_desktops
- 1;
1023 /* defaults to the current desktop */
1024 self
->desktop
= screen_desktop
;
1027 if (self
->desktop
!= d
) {
1028 /* set the desktop hint, to make sure that it always exists */
1029 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
1033 static void client_get_layer(ObClient
*self
)
1035 if (!(self
->above
|| self
->below
)) {
1037 /* apply stuff from the group */
1041 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1042 ObClient
*c
= it
->data
;
1043 if (c
!= self
&& !client_search_transient(self
, c
) &&
1044 client_normal(self
) && client_normal(c
))
1047 (c
->above
? 1 : (c
->below
? -1 : 0)));
1061 g_assert_not_reached();
1068 static void client_get_state(ObClient
*self
)
1073 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1075 for (i
= 0; i
< num
; ++i
) {
1076 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1078 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1079 self
->shaded
= TRUE
;
1080 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1081 self
->iconic
= TRUE
;
1082 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1083 self
->skip_taskbar
= TRUE
;
1084 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1085 self
->skip_pager
= TRUE
;
1086 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1087 self
->fullscreen
= TRUE
;
1088 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1089 self
->max_vert
= TRUE
;
1090 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1091 self
->max_horz
= TRUE
;
1092 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1094 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1096 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1097 self
->demands_attention
= TRUE
;
1098 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1099 self
->undecorated
= TRUE
;
1106 static void client_get_shaped(ObClient
*self
)
1108 self
->shaped
= FALSE
;
1110 if (extensions_shape
) {
1115 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1117 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1118 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1120 self
->shaped
= (s
!= 0);
1125 void client_update_transient_for(ObClient
*self
)
1128 ObClient
*target
= NULL
;
1130 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1131 self
->transient
= TRUE
;
1132 if (t
!= self
->window
) { /* cant be transient to itself! */
1133 target
= g_hash_table_lookup(window_map
, &t
);
1134 /* if this happens then we need to check for it*/
1135 g_assert(target
!= self
);
1136 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1137 /* this can happen when a dialog is a child of
1138 a dockapp, for example */
1142 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1144 Setting the transient_for to Root is actually illegal, however
1145 applications from time have done this to specify transient for
1148 Now you can do that by being a TYPE_DIALOG and not setting
1149 the transient_for hint at all on your window. But people still
1150 use Root, and Kwin is very strange in this regard.
1152 KWin 3.0 will not consider windows with transient_for set to
1153 Root as transient for their group *UNLESS* they are also modal.
1154 In that case, it will make them transient for the group. This
1155 leads to all sorts of weird behavior from KDE apps which are
1156 only tested in KWin. I'd like to follow their behavior just to
1157 make this work right with KDE stuff, but that seems wrong.
1159 if (!target
&& self
->group
) {
1160 /* not transient to a client, see if it is transient for a
1162 if (t
== RootWindow(ob_display
, ob_screen
)) {
1163 /* window is a transient for its group! */
1164 target
= OB_TRAN_GROUP
;
1168 } else if (self
->group
) {
1169 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1170 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1171 self
->type
== OB_CLIENT_TYPE_MENU
||
1172 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1174 self
->transient
= TRUE
;
1175 target
= OB_TRAN_GROUP
;
1178 self
->transient
= FALSE
;
1180 /* if anything has changed... */
1181 if (target
!= self
->transient_for
) {
1182 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1185 /* remove from old parents */
1186 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1187 ObClient
*c
= it
->data
;
1188 if (c
!= self
&& !c
->transient_for
)
1189 c
->transients
= g_slist_remove(c
->transients
, self
);
1191 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1192 /* remove from old parent */
1193 self
->transient_for
->transients
=
1194 g_slist_remove(self
->transient_for
->transients
, self
);
1196 self
->transient_for
= target
;
1197 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1200 /* add to new parents */
1201 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1202 ObClient
*c
= it
->data
;
1203 if (c
!= self
&& !c
->transient_for
)
1204 c
->transients
= g_slist_append(c
->transients
, self
);
1207 /* remove all transients which are in the group, that causes
1208 circlular pointer hell of doom */
1209 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1211 for (sit
= self
->transients
; sit
; sit
= next
) {
1212 next
= g_slist_next(sit
);
1213 if (sit
->data
== it
->data
)
1215 g_slist_delete_link(self
->transients
, sit
);
1218 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1219 /* add to new parent */
1220 self
->transient_for
->transients
=
1221 g_slist_append(self
->transient_for
->transients
, self
);
1226 static void client_get_mwm_hints(ObClient
*self
)
1231 self
->mwmhints
.flags
= 0; /* default to none */
1233 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1235 if (num
>= OB_MWM_ELEMENTS
) {
1236 self
->mwmhints
.flags
= hints
[0];
1237 self
->mwmhints
.functions
= hints
[1];
1238 self
->mwmhints
.decorations
= hints
[2];
1244 void client_get_type(ObClient
*self
)
1251 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1252 /* use the first value that we know about in the array */
1253 for (i
= 0; i
< num
; ++i
) {
1254 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1255 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1256 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1257 self
->type
= OB_CLIENT_TYPE_DOCK
;
1258 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1259 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1260 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1261 self
->type
= OB_CLIENT_TYPE_MENU
;
1262 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1263 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1264 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1265 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1266 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1267 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1268 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1269 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1270 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1271 /* prevent this window from getting any decor or
1273 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1274 OB_MWM_FLAG_DECORATIONS
);
1275 self
->mwmhints
.decorations
= 0;
1276 self
->mwmhints
.functions
= 0;
1278 if (self
->type
!= (ObClientType
) -1)
1279 break; /* grab the first legit type */
1284 if (self
->type
== (ObClientType
) -1) {
1285 /*the window type hint was not set, which means we either classify
1286 ourself as a normal window or a dialog, depending on if we are a
1288 if (self
->transient
)
1289 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1291 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1295 void client_update_protocols(ObClient
*self
)
1298 guint num_return
, i
;
1300 self
->focus_notify
= FALSE
;
1301 self
->delete_window
= FALSE
;
1303 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1304 for (i
= 0; i
< num_return
; ++i
) {
1305 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1306 /* this means we can request the window to close */
1307 self
->delete_window
= TRUE
;
1308 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1309 /* if this protocol is requested, then the window will be
1310 notified whenever we want it to receive focus */
1311 self
->focus_notify
= TRUE
;
1317 static void client_get_gravity(ObClient
*self
)
1319 XWindowAttributes wattrib
;
1322 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1323 g_assert(ret
!= BadWindow
);
1324 self
->gravity
= wattrib
.win_gravity
;
1327 void client_update_normal_hints(ObClient
*self
)
1331 gint oldgravity
= self
->gravity
;
1334 self
->min_ratio
= 0.0f
;
1335 self
->max_ratio
= 0.0f
;
1336 SIZE_SET(self
->size_inc
, 1, 1);
1337 SIZE_SET(self
->base_size
, 0, 0);
1338 SIZE_SET(self
->min_size
, 0, 0);
1339 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1341 /* get the hints from the window */
1342 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1343 /* normal windows can't request placement! har har
1344 if (!client_normal(self))
1346 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1348 if (size
.flags
& PWinGravity
) {
1349 self
->gravity
= size
.win_gravity
;
1351 /* if the client has a frame, i.e. has already been mapped and
1352 is changing its gravity */
1353 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1354 /* move our idea of the client's position based on its new
1356 self
->area
.x
= self
->frame
->area
.x
;
1357 self
->area
.y
= self
->frame
->area
.y
;
1358 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1362 if (size
.flags
& PAspect
) {
1363 if (size
.min_aspect
.y
)
1365 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1366 if (size
.max_aspect
.y
)
1368 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1371 if (size
.flags
& PMinSize
)
1372 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1374 if (size
.flags
& PMaxSize
)
1375 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1377 if (size
.flags
& PBaseSize
)
1378 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1380 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1381 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1385 void client_setup_decor_and_functions(ObClient
*self
)
1387 /* start with everything (cept fullscreen) */
1389 (OB_FRAME_DECOR_TITLEBAR
|
1390 OB_FRAME_DECOR_HANDLE
|
1391 OB_FRAME_DECOR_GRIPS
|
1392 OB_FRAME_DECOR_BORDER
|
1393 OB_FRAME_DECOR_ICON
|
1394 OB_FRAME_DECOR_ALLDESKTOPS
|
1395 OB_FRAME_DECOR_ICONIFY
|
1396 OB_FRAME_DECOR_MAXIMIZE
|
1397 OB_FRAME_DECOR_SHADE
|
1398 OB_FRAME_DECOR_CLOSE
);
1400 (OB_CLIENT_FUNC_RESIZE
|
1401 OB_CLIENT_FUNC_MOVE
|
1402 OB_CLIENT_FUNC_ICONIFY
|
1403 OB_CLIENT_FUNC_MAXIMIZE
|
1404 OB_CLIENT_FUNC_SHADE
|
1405 OB_CLIENT_FUNC_CLOSE
);
1407 if (!(self
->min_size
.width
< self
->max_size
.width
||
1408 self
->min_size
.height
< self
->max_size
.height
))
1409 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1411 switch (self
->type
) {
1412 case OB_CLIENT_TYPE_NORMAL
:
1413 /* normal windows retain all of the possible decorations and
1414 functionality, and are the only windows that you can fullscreen */
1415 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1418 case OB_CLIENT_TYPE_DIALOG
:
1419 case OB_CLIENT_TYPE_UTILITY
:
1420 /* these windows cannot be maximized */
1421 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1424 case OB_CLIENT_TYPE_MENU
:
1425 case OB_CLIENT_TYPE_TOOLBAR
:
1426 /* these windows get less functionality */
1427 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1430 case OB_CLIENT_TYPE_DESKTOP
:
1431 case OB_CLIENT_TYPE_DOCK
:
1432 case OB_CLIENT_TYPE_SPLASH
:
1433 /* none of these windows are manipulated by the window manager */
1434 self
->decorations
= 0;
1435 self
->functions
= 0;
1439 /* Mwm Hints are applied subtractively to what has already been chosen for
1440 decor and functionality */
1441 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1442 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1443 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1444 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1446 /* if the mwm hints request no handle or title, then all
1447 decorations are disabled, but keep the border if that's
1449 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1450 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1452 self
->decorations
= 0;
1457 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1458 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1459 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1460 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1461 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1462 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1463 /* dont let mwm hints kill any buttons
1464 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1465 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1466 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1467 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1469 /* dont let mwm hints kill the close button
1470 if (! (self->mwmhints.functions & MwmFunc_Close))
1471 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1475 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1476 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1477 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1478 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1479 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1480 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1482 /* can't maximize without moving/resizing */
1483 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1484 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1485 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1486 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1487 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1490 /* kill the handle on fully maxed windows */
1491 if (self
->max_vert
&& self
->max_horz
)
1492 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1494 /* finally, the user can have requested no decorations, which overrides
1495 everything (but doesnt give it a border if it doesnt have one) */
1496 if (self
->undecorated
) {
1497 if (config_theme_keepborder
)
1498 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1500 self
->decorations
= 0;
1503 /* if we don't have a titlebar, then we cannot shade! */
1504 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1505 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1507 /* now we need to check against rules for the client's current state */
1508 if (self
->fullscreen
) {
1509 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1510 OB_CLIENT_FUNC_FULLSCREEN
|
1511 OB_CLIENT_FUNC_ICONIFY
);
1512 self
->decorations
= 0;
1515 client_change_allowed_actions(self
);
1518 /* adjust the client's decorations, etc. */
1519 client_reconfigure(self
);
1523 static void client_change_allowed_actions(ObClient
*self
)
1528 /* desktop windows are kept on all desktops */
1529 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1530 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1532 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1533 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1534 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1535 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1536 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1537 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1538 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1539 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1540 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1541 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1542 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1543 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1544 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1545 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1546 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1549 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1551 /* make sure the window isn't breaking any rules now */
1553 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1554 if (self
->frame
) client_shade(self
, FALSE
);
1555 else self
->shaded
= FALSE
;
1557 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1558 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1559 else self
->iconic
= FALSE
;
1561 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1562 if (self
->frame
) client_fullscreen(self
, FALSE
);
1563 else self
->fullscreen
= FALSE
;
1565 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1567 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1568 else self
->max_vert
= self
->max_horz
= FALSE
;
1572 void client_reconfigure(ObClient
*self
)
1574 /* by making this pass FALSE for user, we avoid the emacs event storm where
1575 every configurenotify causes an update in its normal hints, i think this
1576 is generally what we want anyways... */
1577 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1578 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1581 void client_update_wmhints(ObClient
*self
)
1586 /* assume a window takes input if it doesnt specify */
1587 self
->can_focus
= TRUE
;
1589 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1590 if (hints
->flags
& InputHint
)
1591 self
->can_focus
= hints
->input
;
1593 /* only do this when first managing the window *AND* when we aren't
1595 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1596 if (hints
->flags
& StateHint
)
1597 self
->iconic
= hints
->initial_state
== IconicState
;
1599 if (!(hints
->flags
& WindowGroupHint
))
1600 hints
->window_group
= None
;
1602 /* did the group state change? */
1603 if (hints
->window_group
!=
1604 (self
->group
? self
->group
->leader
: None
)) {
1605 /* remove from the old group if there was one */
1606 if (self
->group
!= NULL
) {
1607 /* remove transients of the group */
1608 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1609 self
->transients
= g_slist_remove(self
->transients
,
1612 /* remove myself from parents in the group */
1613 if (self
->transient_for
== OB_TRAN_GROUP
) {
1614 for (it
= self
->group
->members
; it
;
1615 it
= g_slist_next(it
))
1617 ObClient
*c
= it
->data
;
1619 if (c
!= self
&& !c
->transient_for
)
1620 c
->transients
= g_slist_remove(c
->transients
,
1625 group_remove(self
->group
, self
);
1628 if (hints
->window_group
!= None
) {
1629 self
->group
= group_add(hints
->window_group
, self
);
1631 /* i can only have transients from the group if i am not
1633 if (!self
->transient_for
) {
1634 /* add other transients of the group that are already
1636 for (it
= self
->group
->members
; it
;
1637 it
= g_slist_next(it
))
1639 ObClient
*c
= it
->data
;
1640 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1642 g_slist_append(self
->transients
, c
);
1647 /* because the self->transient flag wont change from this call,
1648 we don't need to update the window's type and such, only its
1649 transient_for, and the transients lists of other windows in
1650 the group may be affected */
1651 client_update_transient_for(self
);
1654 /* the WM_HINTS can contain an icon */
1655 client_update_icons(self
);
1661 void client_update_title(ObClient
*self
)
1665 g_free(self
->title
);
1668 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1669 /* try old x stuff */
1670 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1671 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1672 if (self
->transient
) {
1674 GNOME alert windows are not given titles:
1675 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1677 data
= g_strdup("");
1679 data
= g_strdup("Unnamed Window");
1683 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1687 frame_adjust_title(self
->frame
);
1689 /* update the icon title */
1691 g_free(self
->icon_title
);
1694 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1695 /* try old x stuff */
1696 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1697 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1698 data
= g_strdup(self
->title
);
1700 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1701 self
->icon_title
= data
;
1704 void client_update_class(ObClient
*self
)
1709 if (self
->name
) g_free(self
->name
);
1710 if (self
->class) g_free(self
->class);
1711 if (self
->role
) g_free(self
->role
);
1713 self
->name
= self
->class = self
->role
= NULL
;
1715 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1717 self
->name
= g_strdup(data
[0]);
1719 self
->class = g_strdup(data
[1]);
1724 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1727 if (self
->name
== NULL
) self
->name
= g_strdup("");
1728 if (self
->class == NULL
) self
->class = g_strdup("");
1729 if (self
->role
== NULL
) self
->role
= g_strdup("");
1732 void client_update_strut(ObClient
*self
)
1736 gboolean got
= FALSE
;
1739 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1743 STRUT_PARTIAL_SET(strut
,
1744 data
[0], data
[2], data
[1], data
[3],
1745 data
[4], data
[5], data
[8], data
[9],
1746 data
[6], data
[7], data
[10], data
[11]);
1752 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1758 /* use the screen's width/height */
1759 a
= screen_physical_area();
1761 STRUT_PARTIAL_SET(strut
,
1762 data
[0], data
[2], data
[1], data
[3],
1763 a
->y
, a
->y
+ a
->height
- 1,
1764 a
->x
, a
->x
+ a
->width
- 1,
1765 a
->y
, a
->y
+ a
->height
- 1,
1766 a
->x
, a
->x
+ a
->width
- 1);
1772 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1773 0, 0, 0, 0, 0, 0, 0, 0);
1775 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1776 self
->strut
= strut
;
1778 /* updating here is pointless while we're being mapped cuz we're not in
1779 the client list yet */
1781 screen_update_areas();
1785 void client_update_icons(ObClient
*self
)
1791 for (i
= 0; i
< self
->nicons
; ++i
)
1792 g_free(self
->icons
[i
].data
);
1793 if (self
->nicons
> 0)
1794 g_free(self
->icons
);
1797 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1798 /* figure out how many valid icons are in here */
1800 while (num
- i
> 2) {
1804 if (i
> num
|| w
*h
== 0) break;
1808 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1810 /* store the icons */
1812 for (j
= 0; j
< self
->nicons
; ++j
) {
1815 w
= self
->icons
[j
].width
= data
[i
++];
1816 h
= self
->icons
[j
].height
= data
[i
++];
1818 if (w
*h
== 0) continue;
1820 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1821 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1826 self
->icons
[j
].data
[t
] =
1827 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1828 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1829 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1830 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1839 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1840 if (hints
->flags
& IconPixmapHint
) {
1842 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1843 xerror_set_ignore(TRUE
);
1844 if (!RrPixmapToRGBA(ob_rr_inst
,
1846 (hints
->flags
& IconMaskHint
?
1847 hints
->icon_mask
: None
),
1848 &self
->icons
[self
->nicons
-1].width
,
1849 &self
->icons
[self
->nicons
-1].height
,
1850 &self
->icons
[self
->nicons
-1].data
)){
1851 g_free(&self
->icons
[self
->nicons
-1]);
1854 xerror_set_ignore(FALSE
);
1861 frame_adjust_icon(self
->frame
);
1864 void client_update_user_time(ObClient
*self
)
1868 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1869 guint32 otime
= self
->user_time
;
1870 /* we set this every time, not just when it grows, because in practice
1871 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1872 backward we don't want all windows to stop focusing. we'll just
1873 assume noone is setting times older than the last one, cuz that
1874 would be pretty stupid anyways
1876 self
->user_time
= time
;
1877 /* adjust the time heap - windows with CurrentTime for their user_time
1878 are not in the heap */
1879 if (time
== CurrentTime
&& otime
!= CurrentTime
)
1880 client_time_heap_remove(client_user_times
, self
);
1881 else if (time
!= CurrentTime
&& otime
== CurrentTime
)
1882 client_time_heap_add(client_user_times
, self
);
1883 else if (time
!= CurrentTime
&& otime
!= CurrentTime
) {
1884 if (event_time_after(time
, otime
))
1885 client_time_heap_increase_key(client_user_times
, self
);
1887 client_time_heap_decrease_key(client_user_times
, self
);
1891 ob_debug("window %s user time %u\n", self->title, time);
1892 ob_debug("last user time %u\n", client_time_heap_maximum(client_user_times));
1897 static void client_change_wm_state(ObClient
*self
)
1902 old
= self
->wmstate
;
1904 if (self
->shaded
|| self
->iconic
|| !self
->frame
->visible
)
1905 self
->wmstate
= IconicState
;
1907 self
->wmstate
= NormalState
;
1909 if (old
!= self
->wmstate
) {
1910 PROP_MSG(self
->window
, kde_wm_change_state
,
1911 self
->wmstate
, 1, 0, 0);
1913 state
[0] = self
->wmstate
;
1915 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1919 static void client_change_state(ObClient
*self
)
1921 gulong netstate
[11];
1926 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1928 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1930 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1931 if (self
->skip_taskbar
)
1932 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1933 if (self
->skip_pager
)
1934 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1935 if (self
->fullscreen
)
1936 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1938 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1940 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1942 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1944 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1945 if (self
->demands_attention
)
1946 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1947 if (self
->undecorated
)
1948 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1949 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1952 frame_adjust_state(self
->frame
);
1955 ObClient
*client_search_focus_tree(ObClient
*self
)
1960 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1961 if (client_focused(it
->data
)) return it
->data
;
1962 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1967 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1969 if (self
->transient_for
) {
1970 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1971 return client_search_focus_tree_full(self
->transient_for
);
1974 gboolean recursed
= FALSE
;
1976 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1977 if (!((ObClient
*)it
->data
)->transient_for
) {
1979 if ((c
= client_search_focus_tree_full(it
->data
)))
1988 /* this function checks the whole tree, the client_search_focus_tree~
1989 does not, so we need to check this window */
1990 if (client_focused(self
))
1992 return client_search_focus_tree(self
);
1995 static ObStackingLayer
calc_layer(ObClient
*self
)
1999 if (self
->fullscreen
&&
2000 (client_focused(self
) || client_search_focus_tree(self
)))
2001 l
= OB_STACKING_LAYER_FULLSCREEN
;
2002 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2003 l
= OB_STACKING_LAYER_DESKTOP
;
2004 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2005 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2006 else l
= OB_STACKING_LAYER_ABOVE
;
2008 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2009 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2010 else l
= OB_STACKING_LAYER_NORMAL
;
2015 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2016 ObStackingLayer min
, gboolean raised
)
2018 ObStackingLayer old
, own
;
2022 own
= calc_layer(self
);
2023 self
->layer
= MAX(own
, min
);
2025 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2026 client_calc_layer_recursive(it
->data
, orig
,
2028 raised
? raised
: self
->layer
!= old
);
2030 if (!raised
&& self
->layer
!= old
)
2031 if (orig
->frame
) { /* only restack if the original window is managed */
2032 stacking_remove(CLIENT_AS_WINDOW(self
));
2033 stacking_add(CLIENT_AS_WINDOW(self
));
2037 void client_calc_layer(ObClient
*self
)
2044 /* transients take on the layer of their parents */
2045 it
= client_search_all_top_parents(self
);
2047 for (; it
; it
= g_slist_next(it
))
2048 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2051 gboolean
client_should_show(ObClient
*self
)
2055 if (client_normal(self
) && screen_showing_desktop
)
2058 if (self->transient_for) {
2059 if (self->transient_for != OB_TRAN_GROUP)
2060 return client_should_show(self->transient_for);
2064 for (it = self->group->members; it; it = g_slist_next(it)) {
2065 ObClient *c = it->data;
2066 if (c != self && !c->transient_for) {
2067 if (client_should_show(c))
2074 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2080 void client_show(ObClient
*self
)
2083 if (client_should_show(self
)) {
2084 frame_show(self
->frame
);
2087 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2088 needs to be in IconicState. This includes when it is on another
2091 client_change_wm_state(self
);
2094 void client_hide(ObClient
*self
)
2096 if (!client_should_show(self
)) {
2097 frame_hide(self
->frame
);
2100 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2101 needs to be in IconicState. This includes when it is on another
2104 client_change_wm_state(self
);
2107 void client_showhide(ObClient
*self
)
2110 if (client_should_show(self
)) {
2111 frame_show(self
->frame
);
2114 frame_hide(self
->frame
);
2117 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2118 needs to be in IconicState. This includes when it is on another
2121 client_change_wm_state(self
);
2124 gboolean
client_normal(ObClient
*self
) {
2125 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2126 self
->type
== OB_CLIENT_TYPE_DOCK
||
2127 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2130 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2132 gboolean pos
= FALSE
; /* has the window's position been configured? */
2135 /* save the position, and set self->area for these to use */
2141 /* these are in a carefully crafted order.. */
2144 self
->iconic
= FALSE
;
2145 client_iconify(self
, TRUE
, FALSE
);
2147 if (self
->fullscreen
) {
2148 self
->fullscreen
= FALSE
;
2149 client_fullscreen(self
, TRUE
);
2152 if (self
->undecorated
) {
2153 self
->undecorated
= FALSE
;
2154 client_set_undecorated(self
, TRUE
);
2157 self
->shaded
= FALSE
;
2158 client_shade(self
, TRUE
);
2160 if (self
->demands_attention
) {
2161 self
->demands_attention
= FALSE
;
2162 client_hilite(self
, TRUE
);
2165 if (self
->max_vert
&& self
->max_horz
) {
2166 self
->max_vert
= self
->max_horz
= FALSE
;
2167 client_maximize(self
, TRUE
, 0);
2169 } else if (self
->max_vert
) {
2170 self
->max_vert
= FALSE
;
2171 client_maximize(self
, TRUE
, 2);
2173 } else if (self
->max_horz
) {
2174 self
->max_horz
= FALSE
;
2175 client_maximize(self
, TRUE
, 1);
2179 /* if the client didn't get positioned yet, then do so now
2180 call client_move even if the window is not being moved anywhere, because
2181 when we reparent it and decorate it, it is getting moved and we need to
2182 be telling it so with a ConfigureNotify event.
2185 /* use the saved position */
2188 client_move(self
, x
, y
);
2191 /* nothing to do for the other states:
2200 void client_try_configure(ObClient
*self
, ObCorner anchor
,
2201 gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2202 gint
*logicalw
, gint
*logicalh
,
2205 Rect desired_area
= {*x
, *y
, *w
, *h
};
2207 /* make the frame recalculate its dimentions n shit without changing
2208 anything visible for real, this way the constraints below can work with
2209 the updated frame dimensions. */
2210 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2212 /* work within the prefered sizes given by the window */
2213 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2214 gint basew
, baseh
, minw
, minh
;
2216 /* base size is substituted with min size if not specified */
2217 if (self
->base_size
.width
|| self
->base_size
.height
) {
2218 basew
= self
->base_size
.width
;
2219 baseh
= self
->base_size
.height
;
2221 basew
= self
->min_size
.width
;
2222 baseh
= self
->min_size
.height
;
2224 /* min size is substituted with base size if not specified */
2225 if (self
->min_size
.width
|| self
->min_size
.height
) {
2226 minw
= self
->min_size
.width
;
2227 minh
= self
->min_size
.height
;
2229 minw
= self
->base_size
.width
;
2230 minh
= self
->base_size
.height
;
2233 /* if this is a user-requested resize, then check against min/max
2236 /* smaller than min size or bigger than max size? */
2237 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2238 if (*w
< minw
) *w
= minw
;
2239 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2240 if (*h
< minh
) *h
= minh
;
2245 /* keep to the increments */
2246 *w
/= self
->size_inc
.width
;
2247 *h
/= self
->size_inc
.height
;
2249 /* you cannot resize to nothing */
2250 if (basew
+ *w
< 1) *w
= 1 - basew
;
2251 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2253 /* save the logical size */
2254 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2255 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2257 *w
*= self
->size_inc
.width
;
2258 *h
*= self
->size_inc
.height
;
2263 /* adjust the height to match the width for the aspect ratios.
2264 for this, min size is not substituted for base size ever. */
2265 *w
-= self
->base_size
.width
;
2266 *h
-= self
->base_size
.height
;
2268 if (!self
->fullscreen
) {
2269 if (self
->min_ratio
)
2270 if (*h
* self
->min_ratio
> *w
) {
2271 *h
= (gint
)(*w
/ self
->min_ratio
);
2273 /* you cannot resize to nothing */
2276 *w
= (gint
)(*h
* self
->min_ratio
);
2279 if (self
->max_ratio
)
2280 if (*h
* self
->max_ratio
< *w
) {
2281 *h
= (gint
)(*w
/ self
->max_ratio
);
2283 /* you cannot resize to nothing */
2286 *w
= (gint
)(*h
* self
->min_ratio
);
2291 *w
+= self
->base_size
.width
;
2292 *h
+= self
->base_size
.height
;
2295 /* gets the frame's position */
2296 frame_client_gravity(self
->frame
, x
, y
);
2298 /* these positions are frame positions, not client positions */
2300 /* set the size and position if fullscreen */
2301 if (self
->fullscreen
) {
2305 i
= screen_find_monitor(&desired_area
);
2306 a
= screen_physical_area_monitor(i
);
2313 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2314 is entering fullscreen */
2315 } else if (self
->max_horz
|| self
->max_vert
) {
2319 i
= screen_find_monitor(&desired_area
);
2320 a
= screen_area_monitor(self
->desktop
, i
);
2322 /* set the size and position if maximized */
2323 if (self
->max_horz
) {
2325 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2327 if (self
->max_vert
) {
2329 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2332 /* maximizing is not allowed if the user can't move+resize the window
2336 /* gets the client's position */
2337 frame_frame_gravity(self
->frame
, x
, y
);
2339 /* these override the above states! if you cant move you can't move! */
2341 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2345 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2346 *w
= self
->area
.width
;
2347 *h
= self
->area
.height
;
2355 case OB_CORNER_TOPLEFT
:
2357 case OB_CORNER_TOPRIGHT
:
2358 *x
-= *w
- self
->area
.width
;
2360 case OB_CORNER_BOTTOMLEFT
:
2361 *y
-= *h
- self
->area
.height
;
2363 case OB_CORNER_BOTTOMRIGHT
:
2364 *x
-= *w
- self
->area
.width
;
2365 *y
-= *h
- self
->area
.height
;
2371 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2372 gint x
, gint y
, gint w
, gint h
,
2373 gboolean user
, gboolean final
,
2374 gboolean force_reply
)
2376 gint oldw
, oldh
, oldrx
, oldry
;
2377 gboolean send_resize_client
;
2378 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2379 guint fdecor
= self
->frame
->decorations
;
2380 gboolean fhorz
= self
->frame
->max_horz
;
2381 gint logicalw
, logicalh
;
2383 /* find the new x, y, width, and height (and logical size) */
2384 client_try_configure(self
, anchor
, &x
, &y
, &w
, &h
,
2385 &logicalw
, &logicalh
, user
);
2387 /* set the logical size if things changed */
2388 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2389 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2391 /* figure out if we moved or resized or what */
2392 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2393 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2395 oldw
= self
->area
.width
;
2396 oldh
= self
->area
.height
;
2397 RECT_SET(self
->area
, x
, y
, w
, h
);
2399 /* for app-requested resizes, always resize if 'resized' is true.
2400 for user-requested ones, only resize if final is true, or when
2401 resizing in redraw mode */
2402 send_resize_client
= ((!user
&& resized
) ||
2404 (resized
&& config_resize_redraw
))));
2406 /* if the client is enlarging, then resize the client before the frame */
2407 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2408 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2410 /* find the frame's dimensions and move/resize it */
2411 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2412 moved
= resized
= TRUE
;
2413 if (moved
|| resized
)
2414 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2416 /* find the client's position relative to the root window */
2417 oldrx
= self
->root_pos
.x
;
2418 oldry
= self
->root_pos
.y
;
2419 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2420 self
->frame
->size
.left
-
2421 self
->border_width
) ||
2422 oldry
!= (signed)(self
->frame
->area
.y
+
2423 self
->frame
->size
.top
-
2424 self
->border_width
));
2426 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2430 POINT_SET(self
->root_pos
,
2431 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2433 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2434 self
->border_width
);
2436 event
.type
= ConfigureNotify
;
2437 event
.xconfigure
.display
= ob_display
;
2438 event
.xconfigure
.event
= self
->window
;
2439 event
.xconfigure
.window
= self
->window
;
2441 /* root window real coords */
2442 event
.xconfigure
.x
= self
->root_pos
.x
;
2443 event
.xconfigure
.y
= self
->root_pos
.y
;
2444 event
.xconfigure
.width
= w
;
2445 event
.xconfigure
.height
= h
;
2446 event
.xconfigure
.border_width
= 0;
2447 event
.xconfigure
.above
= self
->frame
->plate
;
2448 event
.xconfigure
.override_redirect
= FALSE
;
2449 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2450 FALSE
, StructureNotifyMask
, &event
);
2453 /* if the client is shrinking, then resize the frame before the client */
2454 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2455 XResizeWindow(ob_display
, self
->window
, w
, h
);
2460 void client_fullscreen(ObClient
*self
, gboolean fs
)
2464 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2465 self
->fullscreen
== fs
) return; /* already done */
2467 self
->fullscreen
= fs
;
2468 client_change_state(self
); /* change the state hints on the client */
2469 client_calc_layer(self
); /* and adjust out layer/stacking */
2472 self
->pre_fullscreen_area
= self
->area
;
2473 /* if the window is maximized, its area isn't all that meaningful.
2474 save it's premax area instead. */
2475 if (self
->max_horz
) {
2476 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2477 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2479 if (self
->max_vert
) {
2480 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2481 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2484 /* these are not actually used cuz client_configure will set them
2485 as appropriate when the window is fullscreened */
2490 if (self
->pre_fullscreen_area
.width
> 0 &&
2491 self
->pre_fullscreen_area
.height
> 0)
2493 x
= self
->pre_fullscreen_area
.x
;
2494 y
= self
->pre_fullscreen_area
.y
;
2495 w
= self
->pre_fullscreen_area
.width
;
2496 h
= self
->pre_fullscreen_area
.height
;
2497 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2499 /* pick some fallbacks... */
2500 a
= screen_area_monitor(self
->desktop
, 0);
2501 x
= a
->x
+ a
->width
/ 4;
2502 y
= a
->y
+ a
->height
/ 4;
2508 client_setup_decor_and_functions(self
);
2510 client_move_resize(self
, x
, y
, w
, h
);
2512 /* try focus us when we go into fullscreen mode */
2516 static void client_iconify_recursive(ObClient
*self
,
2517 gboolean iconic
, gboolean curdesk
)
2520 gboolean changed
= FALSE
;
2523 if (self
->iconic
!= iconic
) {
2524 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2528 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2529 self
->iconic
= iconic
;
2531 /* update the focus lists.. iconic windows go to the bottom of
2532 the list, put the new iconic window at the 'top of the
2534 focus_order_to_top(self
);
2539 self
->iconic
= iconic
;
2542 client_set_desktop(self
, screen_desktop
, FALSE
);
2544 /* this puts it after the current focused window */
2545 focus_order_remove(self
);
2546 focus_order_add_new(self
);
2553 client_change_state(self
);
2554 client_showhide(self
);
2555 if (STRUT_EXISTS(self
->strut
))
2556 screen_update_areas();
2559 /* iconify all direct transients */
2560 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2561 if (it
->data
!= self
)
2562 if (client_is_direct_child(self
, it
->data
))
2563 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2566 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2568 /* move up the transient chain as far as possible first */
2569 self
= client_search_top_parent(self
);
2570 client_iconify_recursive(self
, iconic
, curdesk
);
2573 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2577 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2578 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2580 /* check if already done */
2582 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2583 if (dir
== 1 && self
->max_horz
) return;
2584 if (dir
== 2 && self
->max_vert
) return;
2586 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2587 if (dir
== 1 && !self
->max_horz
) return;
2588 if (dir
== 2 && !self
->max_vert
) return;
2591 /* we just tell it to configure in the same place and client_configure
2592 worries about filling the screen with the window */
2595 w
= self
->area
.width
;
2596 h
= self
->area
.height
;
2599 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2600 RECT_SET(self
->pre_max_area
,
2601 self
->area
.x
, self
->pre_max_area
.y
,
2602 self
->area
.width
, self
->pre_max_area
.height
);
2604 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2605 RECT_SET(self
->pre_max_area
,
2606 self
->pre_max_area
.x
, self
->area
.y
,
2607 self
->pre_max_area
.width
, self
->area
.height
);
2612 a
= screen_area_monitor(self
->desktop
, 0);
2613 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2614 if (self
->pre_max_area
.width
> 0) {
2615 x
= self
->pre_max_area
.x
;
2616 w
= self
->pre_max_area
.width
;
2618 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2619 0, self
->pre_max_area
.height
);
2621 /* pick some fallbacks... */
2622 x
= a
->x
+ a
->width
/ 4;
2626 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2627 if (self
->pre_max_area
.height
> 0) {
2628 y
= self
->pre_max_area
.y
;
2629 h
= self
->pre_max_area
.height
;
2631 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2632 self
->pre_max_area
.width
, 0);
2634 /* pick some fallbacks... */
2635 y
= a
->y
+ a
->height
/ 4;
2641 if (dir
== 0 || dir
== 1) /* horz */
2642 self
->max_horz
= max
;
2643 if (dir
== 0 || dir
== 2) /* vert */
2644 self
->max_vert
= max
;
2646 client_change_state(self
); /* change the state hints on the client */
2648 client_setup_decor_and_functions(self
);
2650 client_move_resize(self
, x
, y
, w
, h
);
2653 void client_shade(ObClient
*self
, gboolean shade
)
2655 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2656 shade
) || /* can't shade */
2657 self
->shaded
== shade
) return; /* already done */
2659 self
->shaded
= shade
;
2660 client_change_state(self
);
2661 client_change_wm_state(self
); /* the window is being hidden/shown */
2662 /* resize the frame to just the titlebar */
2663 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2666 void client_close(ObClient
*self
)
2670 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2672 /* in the case that the client provides no means to requesting that it
2673 close, we just kill it */
2674 if (!self
->delete_window
)
2678 XXX: itd be cool to do timeouts and shit here for killing the client's
2680 like... if the window is around after 5 seconds, then the close button
2681 turns a nice red, and if this function is called again, the client is
2685 ce
.xclient
.type
= ClientMessage
;
2686 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2687 ce
.xclient
.display
= ob_display
;
2688 ce
.xclient
.window
= self
->window
;
2689 ce
.xclient
.format
= 32;
2690 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2691 ce
.xclient
.data
.l
[1] = event_curtime
;
2692 ce
.xclient
.data
.l
[2] = 0l;
2693 ce
.xclient
.data
.l
[3] = 0l;
2694 ce
.xclient
.data
.l
[4] = 0l;
2695 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2698 void client_kill(ObClient
*self
)
2700 XKillClient(ob_display
, self
->window
);
2703 void client_hilite(ObClient
*self
, gboolean hilite
)
2705 if (self
->demands_attention
== hilite
)
2706 return; /* no change */
2708 /* don't allow focused windows to hilite */
2709 self
->demands_attention
= hilite
&& !client_focused(self
);
2710 if (self
->demands_attention
)
2711 frame_flash_start(self
->frame
);
2713 frame_flash_stop(self
->frame
);
2714 client_change_state(self
);
2717 void client_set_desktop_recursive(ObClient
*self
,
2718 guint target
, gboolean donthide
)
2723 if (target
!= self
->desktop
) {
2725 ob_debug("Setting desktop %u\n", target
+1);
2727 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2729 /* remove from the old desktop(s) */
2730 focus_order_remove(self
);
2732 old
= self
->desktop
;
2733 self
->desktop
= target
;
2734 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2735 /* the frame can display the current desktop state */
2736 frame_adjust_state(self
->frame
);
2737 /* 'move' the window to the new desktop */
2739 client_showhide(self
);
2740 /* raise if it was not already on the desktop */
2741 if (old
!= DESKTOP_ALL
)
2743 if (STRUT_EXISTS(self
->strut
))
2744 screen_update_areas();
2746 /* add to the new desktop(s) */
2747 if (config_focus_new
)
2748 focus_order_to_top(self
);
2750 focus_order_to_bottom(self
);
2753 /* move all transients */
2754 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2755 if (it
->data
!= self
)
2756 if (client_is_direct_child(self
, it
->data
))
2757 client_set_desktop_recursive(it
->data
, target
, donthide
);
2760 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2762 self
= client_search_top_parent(self
);
2763 client_set_desktop_recursive(self
, target
, donthide
);
2766 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2768 while (child
!= parent
&&
2769 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2770 child
= child
->transient_for
;
2771 return child
== parent
;
2774 ObClient
*client_search_modal_child(ObClient
*self
)
2779 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2780 ObClient
*c
= it
->data
;
2781 if ((ret
= client_search_modal_child(c
))) return ret
;
2782 if (c
->modal
) return c
;
2787 gboolean
client_validate(ObClient
*self
)
2791 XSync(ob_display
, FALSE
); /* get all events on the server */
2793 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2794 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2795 XPutBackEvent(ob_display
, &e
);
2802 void client_set_wm_state(ObClient
*self
, glong state
)
2804 if (state
== self
->wmstate
) return; /* no change */
2808 client_iconify(self
, TRUE
, TRUE
);
2811 client_iconify(self
, FALSE
, TRUE
);
2816 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2818 gboolean shaded
= self
->shaded
;
2819 gboolean fullscreen
= self
->fullscreen
;
2820 gboolean undecorated
= self
->undecorated
;
2821 gboolean max_horz
= self
->max_horz
;
2822 gboolean max_vert
= self
->max_vert
;
2823 gboolean modal
= self
->modal
;
2824 gboolean iconic
= self
->iconic
;
2825 gboolean demands_attention
= self
->demands_attention
;
2828 if (!(action
== prop_atoms
.net_wm_state_add
||
2829 action
== prop_atoms
.net_wm_state_remove
||
2830 action
== prop_atoms
.net_wm_state_toggle
))
2831 /* an invalid action was passed to the client message, ignore it */
2834 for (i
= 0; i
< 2; ++i
) {
2835 Atom state
= i
== 0 ? data1
: data2
;
2837 if (!state
) continue;
2839 /* if toggling, then pick whether we're adding or removing */
2840 if (action
== prop_atoms
.net_wm_state_toggle
) {
2841 if (state
== prop_atoms
.net_wm_state_modal
)
2842 action
= modal
? prop_atoms
.net_wm_state_remove
:
2843 prop_atoms
.net_wm_state_add
;
2844 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2845 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2846 prop_atoms
.net_wm_state_add
;
2847 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2848 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2849 prop_atoms
.net_wm_state_add
;
2850 else if (state
== prop_atoms
.net_wm_state_shaded
)
2851 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2852 prop_atoms
.net_wm_state_add
;
2853 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2854 action
= self
->skip_taskbar
?
2855 prop_atoms
.net_wm_state_remove
:
2856 prop_atoms
.net_wm_state_add
;
2857 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2858 action
= self
->skip_pager
?
2859 prop_atoms
.net_wm_state_remove
:
2860 prop_atoms
.net_wm_state_add
;
2861 else if (state
== prop_atoms
.net_wm_state_hidden
)
2862 action
= self
->iconic
?
2863 prop_atoms
.net_wm_state_remove
:
2864 prop_atoms
.net_wm_state_add
;
2865 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2866 action
= fullscreen
?
2867 prop_atoms
.net_wm_state_remove
:
2868 prop_atoms
.net_wm_state_add
;
2869 else if (state
== prop_atoms
.net_wm_state_above
)
2870 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2871 prop_atoms
.net_wm_state_add
;
2872 else if (state
== prop_atoms
.net_wm_state_below
)
2873 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2874 prop_atoms
.net_wm_state_add
;
2875 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2876 action
= self
->demands_attention
?
2877 prop_atoms
.net_wm_state_remove
:
2878 prop_atoms
.net_wm_state_add
;
2879 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2880 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2881 prop_atoms
.net_wm_state_add
;
2884 if (action
== prop_atoms
.net_wm_state_add
) {
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
= TRUE
;
2895 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2896 self
->skip_pager
= TRUE
;
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
) {
2903 self
->below
= FALSE
;
2904 } else if (state
== prop_atoms
.net_wm_state_below
) {
2905 self
->above
= FALSE
;
2907 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2908 demands_attention
= TRUE
;
2909 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2913 } else { /* action == prop_atoms.net_wm_state_remove */
2914 if (state
== prop_atoms
.net_wm_state_modal
) {
2916 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2918 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2920 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2922 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2923 self
->skip_taskbar
= FALSE
;
2924 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2925 self
->skip_pager
= FALSE
;
2926 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2928 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2930 } else if (state
== prop_atoms
.net_wm_state_above
) {
2931 self
->above
= FALSE
;
2932 } else if (state
== prop_atoms
.net_wm_state_below
) {
2933 self
->below
= FALSE
;
2934 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2935 demands_attention
= FALSE
;
2936 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2937 undecorated
= FALSE
;
2941 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2942 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2944 if (max_horz
== max_vert
) { /* both going the same way */
2945 client_maximize(self
, max_horz
, 0);
2947 client_maximize(self
, max_horz
, 1);
2948 client_maximize(self
, max_vert
, 2);
2952 if (max_horz
!= self
->max_horz
)
2953 client_maximize(self
, max_horz
, 1);
2955 client_maximize(self
, max_vert
, 2);
2958 /* change fullscreen state before shading, as it will affect if the window
2960 if (fullscreen
!= self
->fullscreen
)
2961 client_fullscreen(self
, fullscreen
);
2962 if (shaded
!= self
->shaded
)
2963 client_shade(self
, shaded
);
2964 if (undecorated
!= self
->undecorated
)
2965 client_set_undecorated(self
, undecorated
);
2966 if (modal
!= self
->modal
) {
2967 self
->modal
= modal
;
2968 /* when a window changes modality, then its stacking order with its
2969 transients needs to change */
2972 if (iconic
!= self
->iconic
)
2973 client_iconify(self
, iconic
, FALSE
);
2975 if (demands_attention
!= self
->demands_attention
)
2976 client_hilite(self
, demands_attention
);
2978 client_change_state(self
); /* change the hint to reflect these changes */
2981 ObClient
*client_focus_target(ObClient
*self
)
2983 ObClient
*child
= NULL
;
2985 child
= client_search_modal_child(self
);
2986 if (child
) return child
;
2990 gboolean
client_can_focus(ObClient
*self
)
2994 /* choose the correct target */
2995 self
= client_focus_target(self
);
2997 if (!self
->frame
->visible
)
3000 if (!(self
->can_focus
|| self
->focus_notify
))
3003 /* do a check to see if the window has already been unmapped or destroyed
3004 do this intelligently while watching out for unmaps we've generated
3005 (ignore_unmaps > 0) */
3006 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
3007 DestroyNotify
, &ev
)) {
3008 XPutBackEvent(ob_display
, &ev
);
3011 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
3012 UnmapNotify
, &ev
)) {
3013 if (self
->ignore_unmaps
) {
3014 self
->ignore_unmaps
--;
3016 XPutBackEvent(ob_display
, &ev
);
3024 gboolean
client_focus(ObClient
*self
)
3026 /* choose the correct target */
3027 self
= client_focus_target(self
);
3029 if (!client_can_focus(self
)) {
3030 if (!self
->frame
->visible
) {
3031 /* update the focus lists */
3032 focus_order_to_top(self
);
3037 ob_debug("Focusing client \"%s\" at time %u\n", self
->title
, event_curtime
);
3039 if (self
->can_focus
) {
3040 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3044 if (self
->focus_notify
) {
3046 ce
.xclient
.type
= ClientMessage
;
3047 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3048 ce
.xclient
.display
= ob_display
;
3049 ce
.xclient
.window
= self
->window
;
3050 ce
.xclient
.format
= 32;
3051 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3052 ce
.xclient
.data
.l
[1] = event_curtime
;
3053 ce
.xclient
.data
.l
[2] = 0l;
3054 ce
.xclient
.data
.l
[3] = 0l;
3055 ce
.xclient
.data
.l
[4] = 0l;
3056 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3060 ob_debug("%sively focusing %lx at %d\n",
3061 (self
->can_focus
? "act" : "pass"),
3062 self
->window
, (gint
) event_curtime
);
3065 /* Cause the FocusIn to come back to us. Important for desktop switches,
3066 since otherwise we'll have no FocusIn on the queue and send it off to
3067 the focus_backup. */
3068 XSync(ob_display
, FALSE
);
3072 /* Used when the current client is closed or otherwise hidden, focus_last will
3073 then prevent focus from going to the mouse pointer
3075 static void client_unfocus(ObClient
*self
)
3077 if (focus_client
== self
) {
3079 ob_debug("client_unfocus for %lx\n", self
->window
);
3081 focus_fallback(FALSE
);
3085 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3089 /* XXX do some stuff here if user is false to determine if we really want
3090 to activate it or not (a parent or group member is currently
3093 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3095 self
->window
, event_curtime
,
3096 client_time_heap_maximum(client_user_times
),
3097 (user
? "user" : "application"));
3099 last_time
= client_time_heap_maximum(client_user_times
);
3100 if (!user
&& event_curtime
&& last_time
&&
3101 !event_time_after(event_curtime
, last_time
))
3103 client_hilite(self
, TRUE
);
3105 if (client_normal(self
) && screen_showing_desktop
)
3106 screen_show_desktop(FALSE
);
3108 client_iconify(self
, FALSE
, here
);
3109 if (self
->desktop
!= DESKTOP_ALL
&&
3110 self
->desktop
!= screen_desktop
) {
3112 client_set_desktop(self
, screen_desktop
, FALSE
);
3114 screen_set_desktop(self
->desktop
);
3115 } else if (!self
->frame
->visible
)
3116 /* if its not visible for other reasons, then don't mess
3120 client_shade(self
, FALSE
);
3124 /* we do this an action here. this is rather important. this is because
3125 we want the results from the focus change to take place BEFORE we go
3126 about raising the window. when a fullscreen window loses focus, we
3127 need this or else the raise wont be able to raise above the
3128 to-lose-focus fullscreen window. */
3133 void client_raise(ObClient
*self
)
3135 action_run_string("Raise", self
, CurrentTime
);
3138 void client_lower(ObClient
*self
)
3140 action_run_string("Lower", self
, CurrentTime
);
3143 gboolean
client_focused(ObClient
*self
)
3145 return self
== focus_client
;
3148 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3151 /* si is the smallest image >= req */
3152 /* li is the largest image < req */
3153 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3155 if (!self
->nicons
) {
3156 ObClientIcon
*parent
= NULL
;
3158 if (self
->transient_for
) {
3159 if (self
->transient_for
!= OB_TRAN_GROUP
)
3160 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3163 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3164 ObClient
*c
= it
->data
;
3165 if (c
!= self
&& !c
->transient_for
) {
3166 if ((parent
= client_icon_recursive(c
, w
, h
)))
3176 for (i
= 0; i
< self
->nicons
; ++i
) {
3177 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3178 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3182 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3187 if (largest
== 0) /* didnt find one smaller than the requested size */
3188 return &self
->icons
[si
];
3189 return &self
->icons
[li
];
3192 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3195 static ObClientIcon deficon
;
3197 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3198 deficon
.width
= deficon
.height
= 48;
3199 deficon
.data
= ob_rr_theme
->def_win_icon
;
3205 /* this be mostly ripped from fvwm */
3206 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3208 gint my_cx
, my_cy
, his_cx
, his_cy
;
3211 gint score
, best_score
;
3212 ObClient
*best_client
, *cur
;
3218 /* first, find the centre coords of the currently focused window */
3219 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3220 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3225 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3228 /* the currently selected window isn't interesting */
3231 if (!client_normal(cur
))
3233 /* using c->desktop instead of screen_desktop doesn't work if the
3234 * current window was omnipresent, hope this doesn't have any other
3236 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3240 if(!(client_focus_target(cur
) == cur
&&
3241 client_can_focus(cur
)))
3244 /* find the centre coords of this window, from the
3245 * currently focused window's point of view */
3246 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3247 + cur
->frame
->area
.width
/ 2;
3248 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3249 + cur
->frame
->area
.height
/ 2;
3251 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3252 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3254 /* Rotate the diagonals 45 degrees counterclockwise.
3255 * To do this, multiply the matrix /+h +h\ with the
3256 * vector (x y). \-h +h/
3257 * h = sqrt(0.5). We can set h := 1 since absolute
3258 * distance doesn't matter here. */
3259 tx
= his_cx
+ his_cy
;
3260 his_cy
= -his_cx
+ his_cy
;
3265 case OB_DIRECTION_NORTH
:
3266 case OB_DIRECTION_SOUTH
:
3267 case OB_DIRECTION_NORTHEAST
:
3268 case OB_DIRECTION_SOUTHWEST
:
3269 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3270 distance
= ((dir
== OB_DIRECTION_NORTH
||
3271 dir
== OB_DIRECTION_NORTHEAST
) ?
3274 case OB_DIRECTION_EAST
:
3275 case OB_DIRECTION_WEST
:
3276 case OB_DIRECTION_SOUTHEAST
:
3277 case OB_DIRECTION_NORTHWEST
:
3278 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3279 distance
= ((dir
== OB_DIRECTION_WEST
||
3280 dir
== OB_DIRECTION_NORTHWEST
) ?
3285 /* the target must be in the requested direction */
3289 /* Calculate score for this window. The smaller the better. */
3290 score
= distance
+ offset
;
3292 /* windows more than 45 degrees off the direction are
3293 * heavily penalized and will only be chosen if nothing
3294 * else within a million pixels */
3295 if(offset
> distance
)
3298 if(best_score
== -1 || score
< best_score
)
3306 void client_set_layer(ObClient
*self
, gint layer
)
3310 self
->above
= FALSE
;
3311 } else if (layer
== 0) {
3312 self
->below
= self
->above
= FALSE
;
3314 self
->below
= FALSE
;
3317 client_calc_layer(self
);
3318 client_change_state(self
); /* reflect this in the state hints */
3321 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3323 if (self
->undecorated
!= undecorated
) {
3324 self
->undecorated
= undecorated
;
3325 client_setup_decor_and_functions(self
);
3326 /* Make sure the client knows it might have moved. Maybe there is a
3327 * better way of doing this so only one client_configure is sent, but
3328 * since 125 of these are sent per second when moving the window (with
3329 * user = FALSE) i doubt it matters much.
3331 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3332 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3333 client_change_state(self
); /* reflect this in the state hints */
3337 guint
client_monitor(ObClient
*self
)
3339 return screen_find_monitor(&self
->frame
->area
);
3342 ObClient
*client_search_top_parent(ObClient
*self
)
3344 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3345 self
= self
->transient_for
;
3349 GSList
*client_search_all_top_parents(ObClient
*self
)
3353 /* move up the direct transient chain as far as possible */
3354 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3355 self
= self
->transient_for
;
3357 if (!self
->transient_for
)
3358 ret
= g_slist_prepend(ret
, self
);
3362 g_assert(self
->group
);
3364 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3365 ObClient
*c
= it
->data
;
3367 if (!c
->transient_for
)
3368 ret
= g_slist_prepend(ret
, c
);
3371 if (ret
== NULL
) /* no group parents */
3372 ret
= g_slist_prepend(ret
, self
);
3378 ObClient
*client_search_focus_parent(ObClient
*self
)
3380 if (self
->transient_for
) {
3381 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3382 if (client_focused(self
->transient_for
))
3383 return self
->transient_for
;
3387 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3388 ObClient
*c
= it
->data
;
3390 /* checking transient_for prevents infinate loops! */
3391 if (c
!= self
&& !c
->transient_for
)
3392 if (client_focused(c
))
3401 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3403 if (self
->transient_for
) {
3404 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3405 if (self
->transient_for
== search
)
3410 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3411 ObClient
*c
= it
->data
;
3413 /* checking transient_for prevents infinate loops! */
3414 if (c
!= self
&& !c
->transient_for
)
3424 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3428 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3429 if (sit
->data
== search
)
3431 if (client_search_transient(sit
->data
, search
))
3437 void client_update_sm_client_id(ObClient
*self
)
3439 g_free(self
->sm_client_id
);
3440 self
->sm_client_id
= NULL
;
3442 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3444 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3445 &self
->sm_client_id
);
3448 #define WANT_EDGE(cur, c) \
3451 if(!client_normal(cur)) \
3453 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3457 if(cur->layer < c->layer && !config_resist_layers_below) \
3460 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3461 if ((his_edge_start >= my_edge_start && \
3462 his_edge_start <= my_edge_end) || \
3463 (my_edge_start >= his_edge_start && \
3464 my_edge_start <= his_edge_end)) \
3467 /* finds the nearest edge in the given direction from the current client
3468 * note to self: the edge is the -frame- edge (the actual one), not the
3471 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3473 gint dest
, monitor_dest
;
3474 gint my_edge_start
, my_edge_end
, my_offset
;
3481 a
= screen_area(c
->desktop
);
3482 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3485 case OB_DIRECTION_NORTH
:
3486 my_edge_start
= c
->frame
->area
.x
;
3487 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3488 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3490 /* default: top of screen */
3491 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3492 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3493 /* if the monitor edge comes before the screen edge, */
3494 /* use that as the destination instead. (For xinerama) */
3495 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3496 dest
= monitor_dest
;
3498 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3499 gint his_edge_start
, his_edge_end
, his_offset
;
3500 ObClient
*cur
= it
->data
;
3504 his_edge_start
= cur
->frame
->area
.x
;
3505 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3506 his_offset
= cur
->frame
->area
.y
+
3507 (hang
? 0 : cur
->frame
->area
.height
);
3509 if(his_offset
+ 1 > my_offset
)
3512 if(his_offset
< dest
)
3515 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3518 case OB_DIRECTION_SOUTH
:
3519 my_edge_start
= c
->frame
->area
.x
;
3520 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3521 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3523 /* default: bottom of screen */
3524 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3525 monitor_dest
= monitor
->y
+ monitor
->height
-
3526 (hang
? c
->frame
->area
.height
: 0);
3527 /* if the monitor edge comes before the screen edge, */
3528 /* use that as the destination instead. (For xinerama) */
3529 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3530 dest
= monitor_dest
;
3532 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3533 gint his_edge_start
, his_edge_end
, his_offset
;
3534 ObClient
*cur
= it
->data
;
3538 his_edge_start
= cur
->frame
->area
.x
;
3539 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3540 his_offset
= cur
->frame
->area
.y
+
3541 (hang
? cur
->frame
->area
.height
: 0);
3544 if(his_offset
- 1 < my_offset
)
3547 if(his_offset
> dest
)
3550 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3553 case OB_DIRECTION_WEST
:
3554 my_edge_start
= c
->frame
->area
.y
;
3555 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3556 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3558 /* default: leftmost egde of screen */
3559 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3560 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3561 /* if the monitor edge comes before the screen edge, */
3562 /* use that as the destination instead. (For xinerama) */
3563 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3564 dest
= monitor_dest
;
3566 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3567 gint his_edge_start
, his_edge_end
, his_offset
;
3568 ObClient
*cur
= it
->data
;
3572 his_edge_start
= cur
->frame
->area
.y
;
3573 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3574 his_offset
= cur
->frame
->area
.x
+
3575 (hang
? 0 : cur
->frame
->area
.width
);
3577 if(his_offset
+ 1 > my_offset
)
3580 if(his_offset
< dest
)
3583 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3586 case OB_DIRECTION_EAST
:
3587 my_edge_start
= c
->frame
->area
.y
;
3588 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3589 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3591 /* default: rightmost edge of screen */
3592 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3593 monitor_dest
= monitor
->x
+ monitor
->width
-
3594 (hang
? c
->frame
->area
.width
: 0);
3595 /* if the monitor edge comes before the screen edge, */
3596 /* use that as the destination instead. (For xinerama) */
3597 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3598 dest
= monitor_dest
;
3600 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3601 gint his_edge_start
, his_edge_end
, his_offset
;
3602 ObClient
*cur
= it
->data
;
3606 his_edge_start
= cur
->frame
->area
.y
;
3607 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3608 his_offset
= cur
->frame
->area
.x
+
3609 (hang
? cur
->frame
->area
.width
: 0);
3611 if(his_offset
- 1 < my_offset
)
3614 if(his_offset
> dest
)
3617 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3620 case OB_DIRECTION_NORTHEAST
:
3621 case OB_DIRECTION_SOUTHEAST
:
3622 case OB_DIRECTION_NORTHWEST
:
3623 case OB_DIRECTION_SOUTHWEST
:
3624 /* not implemented */
3626 g_assert_not_reached();
3627 dest
= 0; /* suppress warning */
3632 ObClient
* client_under_pointer()
3636 ObClient
*ret
= NULL
;
3638 if (screen_pointer_pos(&x
, &y
)) {
3639 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3640 if (WINDOW_IS_CLIENT(it
->data
)) {
3641 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3642 if (c
->frame
->visible
&&
3643 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3653 gboolean
client_has_group_siblings(ObClient
*self
)
3655 return self
->group
&& self
->group
->members
->next
;