1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
45 #include <X11/Xutil.h>
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask)
50 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
55 ObClientDestructor func
;
59 GList
*client_list
= NULL
;
61 static GSList
*client_destructors
= NULL
;
63 static void client_get_all(ObClient
*self
);
64 static void client_toggle_border(ObClient
*self
, gboolean show
);
65 static void client_get_startup_id(ObClient
*self
);
66 static void client_get_area(ObClient
*self
);
67 static void client_get_desktop(ObClient
*self
);
68 static void client_get_state(ObClient
*self
);
69 static void client_get_layer(ObClient
*self
);
70 static void client_get_shaped(ObClient
*self
);
71 static void client_get_mwm_hints(ObClient
*self
);
72 static void client_get_gravity(ObClient
*self
);
73 static void client_get_client_machine(ObClient
*self
);
74 static void client_change_allowed_actions(ObClient
*self
);
75 static void client_change_state(ObClient
*self
);
76 static void client_change_wm_state(ObClient
*self
);
77 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
78 static void client_restore_session_state(ObClient
*self
);
79 static void client_restore_session_stacking(ObClient
*self
);
80 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
82 void client_startup(gboolean reconfig
)
89 void client_shutdown(gboolean reconfig
)
93 void client_add_destructor(ObClientDestructor func
, gpointer data
)
95 Destructor
*d
= g_new(Destructor
, 1);
98 client_destructors
= g_slist_prepend(client_destructors
, d
);
101 void client_remove_destructor(ObClientDestructor func
)
105 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
106 Destructor
*d
= it
->data
;
107 if (d
->func
== func
) {
109 client_destructors
= g_slist_delete_link(client_destructors
, it
);
115 void client_set_list()
117 Window
*windows
, *win_it
;
119 guint size
= g_list_length(client_list
);
121 /* create an array of the window ids */
123 windows
= g_new(Window
, size
);
125 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
126 *win_it
= ((ObClient
*)it
->data
)->window
;
130 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
131 net_client_list
, window
, (gulong
*)windows
, size
);
140 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
144 for (it = self->transients; it; it = g_slist_next(it)) {
145 if (!func(it->data, data)) return;
146 client_foreach_transient(it->data, func, data);
150 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
152 if (self->transient_for) {
153 if (self->transient_for != OB_TRAN_GROUP) {
154 if (!func(self->transient_for, data)) return;
155 client_foreach_ancestor(self->transient_for, func, data);
159 for (it = self->group->members; it; it = g_slist_next(it))
160 if (it->data != self &&
161 !((ObClient*)it->data)->transient_for) {
162 if (!func(it->data, data)) return;
163 client_foreach_ancestor(it->data, func, data);
170 void client_manage_all()
175 XWindowAttributes attrib
;
177 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
178 &w
, &w
, &children
, &nchild
);
180 /* remove all icon windows from the list */
181 for (i
= 0; i
< nchild
; i
++) {
182 if (children
[i
] == None
) continue;
183 wmhints
= XGetWMHints(ob_display
, children
[i
]);
185 if ((wmhints
->flags
& IconWindowHint
) &&
186 (wmhints
->icon_window
!= children
[i
]))
187 for (j
= 0; j
< nchild
; j
++)
188 if (children
[j
] == wmhints
->icon_window
) {
196 for (i
= 0; i
< nchild
; ++i
) {
197 if (children
[i
] == None
)
199 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
200 if (attrib
.override_redirect
) continue;
202 if (attrib
.map_state
!= IsUnmapped
)
203 client_manage(children
[i
]);
209 void client_manage(Window window
)
213 XWindowAttributes attrib
;
214 XSetWindowAttributes attrib_set
;
216 gboolean activate
= FALSE
;
217 ObAppSettings
*settings
;
222 /* check if it has already been unmapped by the time we started mapping.
223 the grab does a sync so we don't have to here */
224 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
225 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
227 XPutBackEvent(ob_display
, &e
);
229 ob_debug("Trying to manage unmapped window. Aborting that.\n");
231 return; /* don't manage it */
234 /* make sure it isn't an override-redirect window */
235 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
236 attrib
.override_redirect
)
239 return; /* don't manage it */
242 /* is the window a docking app */
243 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
244 if ((wmhint
->flags
& StateHint
) &&
245 wmhint
->initial_state
== WithdrawnState
)
247 dock_add(window
, wmhint
);
255 ob_debug("Managing window: %lx\n", window
);
257 /* choose the events we want to receive on the CLIENT window */
258 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
259 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
260 XChangeWindowAttributes(ob_display
, window
,
261 CWEventMask
|CWDontPropagate
, &attrib_set
);
264 /* create the ObClient struct, and populate it from the hints on the
266 self
= g_new0(ObClient
, 1);
267 self
->obwin
.type
= Window_Client
;
268 self
->window
= window
;
270 /* non-zero defaults */
271 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
273 self
->desktop
= screen_num_desktops
; /* always an invalid value */
274 self
->user_time
= CurrentTime
;
276 client_get_all(self
);
277 /* per-app settings override stuff, and return the settings for other
279 settings
= client_get_settings_state(self
);
280 /* the session should get the last say */
281 client_restore_session_state(self
);
283 client_calc_layer(self
);
286 Time t
= sn_app_started(self
->startup_id
, self
->class);
287 if (t
) self
->user_time
= t
;
290 /* update the focus lists, do this before the call to change_state or
291 it can end up in the list twice! */
292 focus_order_add_new(self
);
294 /* remove the client's border (and adjust re gravity) */
295 client_toggle_border(self
, FALSE
);
297 /* specify that if we exit, the window should not be destroyed and should
298 be reparented back to root automatically */
299 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
301 /* create the decoration frame for the client window */
302 self
->frame
= frame_new(self
);
304 frame_grab_client(self
->frame
, self
);
306 /* do this after we have a frame.. it uses the frame to help determine the
307 WM_STATE to apply. */
308 client_change_state(self
);
312 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
313 client_restore_session_stacking(self
);
315 /* focus the new window? */
316 if (ob_state() != OB_STATE_STARTING
&&
317 /* this means focus=true for window is same as config_focus_new=true */
318 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
319 client_search_focus_parent(self
)) &&
320 /* this checks for focus=false for the window */
321 (!settings
|| settings
->focus
!= 0) &&
322 /* note the check against Type_Normal/Dialog, not client_normal(self),
323 which would also include other types. in this case we want more
324 strict rules for focus */
325 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
326 self
->type
== OB_CLIENT_TYPE_DIALOG
))
330 if (self
->desktop
!= screen_desktop
) {
331 /* activate the window */
334 gboolean group_foc
= FALSE
;
339 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
341 if (client_focused(it
->data
))
349 (!self
->transient_for
&& (!self
->group
||
350 !self
->group
->members
->next
))) ||
351 client_search_focus_tree_full(self
) ||
353 !client_normal(focus_client
))
355 /* activate the window */
362 /* get the current position */
366 /* figure out placement for the window */
367 if (ob_state() == OB_STATE_RUNNING
) {
370 transient
= place_client(self
, &newx
, &newy
, settings
);
372 /* make sure the window is visible. */
373 client_find_onscreen(self
, &newx
, &newy
,
374 self
->frame
->area
.width
,
375 self
->frame
->area
.height
,
376 /* non-normal clients has less rules, and
377 windows that are being restored from a
378 session do also. we can assume you want
379 it back where you saved it. Clients saying
380 they placed themselves are subjected to
381 harder rules, ones that are placed by
382 place.c or by the user are allowed partially
383 off-screen and on xinerama divides (ie,
384 it is up to the placement routines to avoid
385 the xinerama divides) */
387 (((self
->positioned
& PPosition
) &&
388 !(self
->positioned
& USPosition
)) &&
389 client_normal(self
) &&
393 /* do this after the window is placed, so the premax/prefullscreen numbers
395 also, this moves the window to the position where it has been placed
397 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
398 self
->window
, newx
, newy
, self
->area
.width
, self
->area
.height
);
399 client_apply_startup_state(self
, newx
, newy
);
401 keyboard_grab_for_client(self
, TRUE
);
402 mouse_grab_for_client(self
, TRUE
);
405 guint32 last_time
= focus_client
?
406 focus_client
->user_time
: CurrentTime
;
408 /* This is focus stealing prevention */
409 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
410 self
->window
, self
->user_time
, last_time
);
412 /* If a nothing at all, or a parent was focused, then focus this
415 if (!focus_client
|| client_search_focus_parent(self
) != NULL
)
419 /* If time stamp is old, don't steal focus */
420 if (self
->user_time
&& last_time
&&
421 !event_time_after(self
->user_time
, last_time
))
425 /* Don't steal focus from globally active clients.
426 I stole this idea from KWin. It seems nice.
428 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
))
434 /* since focus can change the stacking orders, if we focus the
435 window then the standard raise it gets is not enough, we need
436 to queue one for after the focus change takes place */
439 ob_debug("Focus stealing prevention activated for %s with time %u "
441 self
->title
, self
->user_time
, last_time
);
442 /* if the client isn't focused, then hilite it so the user
444 client_hilite(self
, TRUE
);
448 /* This may look rather odd. Well it's because new windows are added
449 to the stacking order non-intrusively. If we're not going to focus
450 the new window or hilite it, then we raise it to the top. This will
451 take affect for things that don't get focused like splash screens.
452 Also if you don't have focus_new enabled, then it's going to get
453 raised to the top. Legacy begets legacy I guess?
458 /* this has to happen before we try focus the window, but we want it to
459 happen after the client's stacking has been determined or it looks bad
463 /* use client_focus instead of client_activate cuz client_activate does
464 stuff like switch desktops etc and I'm not interested in all that when
465 a window maps since its not based on an action from the user like
466 clicking a window to activate it. so keep the new window out of the way
469 /* if using focus_delay, stop the timer now so that focus doesn't
471 event_halt_focus_delay();
475 /* client_activate does this but we aren't using it so we have to do it
477 if (screen_showing_desktop
)
478 screen_show_desktop(FALSE
);
480 /* add to client list/map */
481 client_list
= g_list_append(client_list
, self
);
482 g_hash_table_insert(window_map
, &self
->window
, self
);
484 /* this has to happen after we're in the client_list */
485 if (STRUT_EXISTS(self
->strut
))
486 screen_update_areas();
488 /* update the list hints */
491 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
494 void client_unmanage_all()
496 while (client_list
!= NULL
)
497 client_unmanage(client_list
->data
);
500 void client_unmanage(ObClient
*self
)
505 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
506 self
->title
? self
->title
: "");
508 g_assert(self
!= NULL
);
510 /* we dont want events no more. do this before hiding the frame so we
511 don't generate more events */
512 XSelectInput(ob_display
, self
->window
, NoEventMask
);
514 frame_hide(self
->frame
);
515 /* flush to send the hide to the server quickly */
518 if (focus_client
== self
) {
519 /* ignore enter events from the unmap so it doesnt mess with the focus
521 event_ignore_queued_enters();
525 keyboard_grab_for_client(self
, FALSE
);
526 mouse_grab_for_client(self
, FALSE
);
528 /* remove the window from our save set */
529 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
531 /* update the focus lists */
532 focus_order_remove(self
);
534 client_list
= g_list_remove(client_list
, self
);
535 stacking_remove(self
);
536 g_hash_table_remove(window_map
, &self
->window
);
538 /* once the client is out of the list, update the struts to remove its
540 if (STRUT_EXISTS(self
->strut
))
541 screen_update_areas();
543 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
544 Destructor
*d
= it
->data
;
545 d
->func(self
, d
->data
);
548 /* tell our parent(s) that we're gone */
549 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
550 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
551 if (it
->data
!= self
)
552 ((ObClient
*)it
->data
)->transients
=
553 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
554 } else if (self
->transient_for
) { /* transient of window */
555 self
->transient_for
->transients
=
556 g_slist_remove(self
->transient_for
->transients
, self
);
559 /* tell our transients that we're gone */
560 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
561 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
562 ((ObClient
*)it
->data
)->transient_for
= NULL
;
563 client_calc_layer(it
->data
);
567 /* remove from its group */
569 group_remove(self
->group
, self
);
573 /* restore the window's original geometry so it is not lost */
577 if (self
->fullscreen
)
578 a
= self
->pre_fullscreen_area
;
579 else if (self
->max_horz
|| self
->max_vert
) {
580 if (self
->max_horz
) {
581 a
.x
= self
->pre_max_area
.x
;
582 a
.width
= self
->pre_max_area
.width
;
584 if (self
->max_vert
) {
585 a
.y
= self
->pre_max_area
.y
;
586 a
.height
= self
->pre_max_area
.height
;
590 /* give the client its border back */
591 client_toggle_border(self
, TRUE
);
593 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
594 self
->decorations
= 0; /* unmanaged windows have no decor */
596 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
599 /* reparent the window out of the frame, and free the frame */
600 frame_release_client(self
->frame
, self
);
603 if (ob_state() != OB_STATE_EXITING
) {
604 /* these values should not be persisted across a window
606 PROP_ERASE(self
->window
, net_wm_desktop
);
607 PROP_ERASE(self
->window
, net_wm_state
);
608 PROP_ERASE(self
->window
, wm_state
);
610 /* if we're left in an unmapped state, the client wont be mapped. this
611 is bad, since we will no longer be managing the window on restart */
612 XMapWindow(ob_display
, self
->window
);
615 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
617 /* free all data allocated in the client struct */
618 g_slist_free(self
->transients
);
619 for (j
= 0; j
< self
->nicons
; ++j
)
620 g_free(self
->icons
[j
].data
);
621 if (self
->nicons
> 0)
624 g_free(self
->icon_title
);
628 g_free(self
->client_machine
);
629 g_free(self
->sm_client_id
);
632 /* update the list hints */
636 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
638 ObAppSettings
*settings
= NULL
;
641 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
642 ObAppSettings
*app
= it
->data
;
644 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
645 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
646 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
647 && !strcmp(app
->name
, self
->name
)))
649 ob_debug("Window matching: %s\n", app
->name
);
650 /* Match if no role was specified in the per app setting, or if the
651 * string matches the beginning of the role, since apps like to set
652 * the role to things like browser-window-23c4b2f */
654 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
664 if (settings
->shade
!= -1)
665 self
->shaded
= !!settings
->shade
;
666 if (settings
->decor
!= -1)
667 self
->undecorated
= !settings
->decor
;
668 if (settings
->iconic
!= -1)
669 self
->iconic
= !!settings
->iconic
;
670 if (settings
->skip_pager
!= -1)
671 self
->skip_pager
= !!settings
->skip_pager
;
672 if (settings
->skip_taskbar
!= -1)
673 self
->skip_taskbar
= !!settings
->skip_taskbar
;
675 if (settings
->max_vert
!= -1)
676 self
->max_vert
= !!settings
->max_vert
;
677 if (settings
->max_horz
!= -1)
678 self
->max_vert
= !!settings
->max_horz
;
680 if (settings
->fullscreen
!= -1)
681 self
->fullscreen
= !!settings
->fullscreen
;
683 if (settings
->desktop
< screen_num_desktops
684 || settings
->desktop
== DESKTOP_ALL
)
685 self
->desktop
= settings
->desktop
;
687 if (settings
->layer
== -1) {
691 else if (settings
->layer
== 0) {
695 else if (settings
->layer
== 1) {
703 static void client_restore_session_state(ObClient
*self
)
707 if (!(it
= session_state_find(self
)))
710 self
->session
= it
->data
;
712 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
713 self
->positioned
= PPosition
;
714 if (self
->session
->w
> 0)
715 self
->area
.width
= self
->session
->w
;
716 if (self
->session
->h
> 0)
717 self
->area
.height
= self
->session
->h
;
718 XResizeWindow(ob_display
, self
->window
,
719 self
->area
.width
, self
->area
.height
);
721 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
722 self
->session
->desktop
:
723 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
724 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
726 self
->shaded
= self
->session
->shaded
;
727 self
->iconic
= self
->session
->iconic
;
728 self
->skip_pager
= self
->session
->skip_pager
;
729 self
->skip_taskbar
= self
->session
->skip_taskbar
;
730 self
->fullscreen
= self
->session
->fullscreen
;
731 self
->above
= self
->session
->above
;
732 self
->below
= self
->session
->below
;
733 self
->max_horz
= self
->session
->max_horz
;
734 self
->max_vert
= self
->session
->max_vert
;
737 static void client_restore_session_stacking(ObClient
*self
)
741 if (!self
->session
) return;
743 it
= g_list_find(session_saved_state
, self
->session
);
744 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
747 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
748 if (session_state_cmp(it
->data
, cit
->data
))
751 client_calc_layer(self
);
752 stacking_below(CLIENT_AS_WINDOW(self
),
753 CLIENT_AS_WINDOW(cit
->data
));
759 void client_move_onscreen(ObClient
*self
, gboolean rude
)
761 gint x
= self
->area
.x
;
762 gint y
= self
->area
.y
;
763 if (client_find_onscreen(self
, &x
, &y
,
764 self
->frame
->area
.width
,
765 self
->frame
->area
.height
, rude
)) {
766 client_move(self
, x
, y
);
770 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
774 gint ox
= *x
, oy
= *y
;
776 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
779 /* XXX watch for xinerama dead areas */
780 /* This makes sure windows aren't entirely outside of the screen so you
781 can't see them at all.
782 It makes sure 10% of the window is on the screen at least. At don't let
783 it move itself off the top of the screen, which would hide the titlebar
784 on you. (The user can still do this if they want too, it's only limiting
787 if (client_normal(self
)) {
788 a
= screen_area(self
->desktop
);
789 if (!self
->strut
.right
&&
790 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
791 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
792 if (!self
->strut
.bottom
&&
793 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
794 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
795 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
796 *x
= a
->x
- self
->frame
->area
.width
*9/10;
797 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
798 *y
= a
->y
- self
->frame
->area
.width
*9/10;
801 /* This here doesn't let windows even a pixel outside the screen,
802 * when called from client_manage, programs placing themselves are
803 * forced completely onscreen, while things like
804 * xterm -geometry resolution-width/2 will work fine. Trying to
805 * place it completely offscreen will be handled in the above code.
806 * Sorry for this confused comment, i am tired. */
808 /* avoid the xinerama monitor divide while we're at it,
809 * remember to fix the placement stuff to avoid it also and
810 * then remove this XXX */
811 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
812 /* dont let windows map into the strut unless they
813 are bigger than the available area */
815 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
816 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
817 *x
= a
->x
+ a
->width
- w
;
819 if (h
<= a
->height
) {
820 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
821 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
822 *y
= a
->y
+ a
->height
- h
;
826 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
829 return ox
!= *x
|| oy
!= *y
;
832 static void client_toggle_border(ObClient
*self
, gboolean show
)
834 /* adjust our idea of where the client is, based on its border. When the
835 border is removed, the client should now be considered to be in a
837 when re-adding the border to the client, the same operation needs to be
839 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
840 gint x
= oldx
, y
= oldy
;
841 switch(self
->gravity
) {
843 case NorthWestGravity
:
845 case SouthWestGravity
:
847 case NorthEastGravity
:
849 case SouthEastGravity
:
850 if (show
) x
-= self
->border_width
* 2;
851 else x
+= self
->border_width
* 2;
858 if (show
) x
-= self
->border_width
;
859 else x
+= self
->border_width
;
862 switch(self
->gravity
) {
864 case NorthWestGravity
:
866 case NorthEastGravity
:
868 case SouthWestGravity
:
870 case SouthEastGravity
:
871 if (show
) y
-= self
->border_width
* 2;
872 else y
+= self
->border_width
* 2;
879 if (show
) y
-= self
->border_width
;
880 else y
+= self
->border_width
;
887 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
889 /* set border_width to 0 because there is no border to add into
890 calculations anymore */
891 self
->border_width
= 0;
893 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
897 static void client_get_all(ObClient
*self
)
899 client_get_area(self
);
900 client_get_mwm_hints(self
);
902 /* The transient hint is used to pick a type, but the type can also affect
903 transiency (dialogs are always made transients of their group if they
904 have one). This is Havoc's idea, but it is needed to make some apps
905 work right (eg tsclient). */
906 client_update_transient_for(self
);
907 client_get_type(self
);/* this can change the mwmhints for special cases */
908 client_get_state(self
);
909 client_update_transient_for(self
);
911 client_update_wmhints(self
);
912 client_get_startup_id(self
);
913 client_get_desktop(self
);/* uses transient data/group/startup id if a
914 desktop is not specified */
915 client_get_shaped(self
);
917 client_get_layer(self
); /* if layer hasn't been specified, get it from
918 other sources if possible */
921 /* a couple type-based defaults for new windows */
923 /* this makes sure that these windows appear on all desktops */
924 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
925 self
->desktop
= DESKTOP_ALL
;
928 client_update_protocols(self
);
930 client_get_gravity(self
); /* get the attribute gravity */
931 client_update_normal_hints(self
); /* this may override the attribute
934 /* got the type, the mwmhints, the protocols, and the normal hints
935 (min/max sizes), so we're ready to set up the decorations/functions */
936 client_setup_decor_and_functions(self
);
938 client_get_client_machine(self
);
939 client_update_title(self
);
940 client_update_class(self
);
941 client_update_sm_client_id(self
);
942 client_update_strut(self
);
943 client_update_icons(self
);
944 client_update_user_time(self
);
947 static void client_get_startup_id(ObClient
*self
)
949 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
951 PROP_GETS(self
->group
->leader
,
952 net_startup_id
, utf8
, &self
->startup_id
);
955 static void client_get_area(ObClient
*self
)
957 XWindowAttributes wattrib
;
960 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
961 g_assert(ret
!= BadWindow
);
963 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
964 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
965 self
->border_width
= wattrib
.border_width
;
967 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
968 wattrib
.width
, wattrib
.height
);
971 static void client_get_desktop(ObClient
*self
)
973 guint32 d
= screen_num_desktops
; /* an always-invalid value */
975 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
976 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
977 self
->desktop
= screen_num_desktops
- 1;
981 gboolean trdesk
= FALSE
;
983 if (self
->transient_for
) {
984 if (self
->transient_for
!= OB_TRAN_GROUP
) {
985 self
->desktop
= self
->transient_for
->desktop
;
990 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
991 if (it
->data
!= self
&&
992 !((ObClient
*)it
->data
)->transient_for
) {
993 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
1000 /* try get from the startup-notification protocol */
1001 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1002 if (self
->desktop
>= screen_num_desktops
&&
1003 self
->desktop
!= DESKTOP_ALL
)
1004 self
->desktop
= screen_num_desktops
- 1;
1006 /* defaults to the current desktop */
1007 self
->desktop
= screen_desktop
;
1010 if (self
->desktop
!= d
) {
1011 /* set the desktop hint, to make sure that it always exists */
1012 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
1016 static void client_get_layer(ObClient
*self
)
1018 if (!(self
->above
|| self
->below
)) {
1020 /* apply stuff from the group */
1024 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1025 ObClient
*c
= it
->data
;
1026 if (c
!= self
&& !client_search_transient(self
, c
) &&
1027 client_normal(self
) && client_normal(c
))
1030 (c
->above
? 1 : (c
->below
? -1 : 0)));
1044 g_assert_not_reached();
1051 static void client_get_state(ObClient
*self
)
1056 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1058 for (i
= 0; i
< num
; ++i
) {
1059 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1061 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1062 self
->shaded
= TRUE
;
1063 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1064 self
->iconic
= TRUE
;
1065 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1066 self
->skip_taskbar
= TRUE
;
1067 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1068 self
->skip_pager
= TRUE
;
1069 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1070 self
->fullscreen
= TRUE
;
1071 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1072 self
->max_vert
= TRUE
;
1073 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1074 self
->max_horz
= TRUE
;
1075 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1077 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1079 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1080 self
->demands_attention
= TRUE
;
1081 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1082 self
->undecorated
= TRUE
;
1089 static void client_get_shaped(ObClient
*self
)
1091 self
->shaped
= FALSE
;
1093 if (extensions_shape
) {
1098 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1100 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1101 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1103 self
->shaped
= (s
!= 0);
1108 void client_update_transient_for(ObClient
*self
)
1111 ObClient
*target
= NULL
;
1113 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1114 self
->transient
= TRUE
;
1115 if (t
!= self
->window
) { /* cant be transient to itself! */
1116 target
= g_hash_table_lookup(window_map
, &t
);
1117 /* if this happens then we need to check for it*/
1118 g_assert(target
!= self
);
1119 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1120 /* this can happen when a dialog is a child of
1121 a dockapp, for example */
1125 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1127 Setting the transient_for to Root is actually illegal, however
1128 applications from time have done this to specify transient for
1131 Now you can do that by being a TYPE_DIALOG and not setting
1132 the transient_for hint at all on your window. But people still
1133 use Root, and Kwin is very strange in this regard.
1135 KWin 3.0 will not consider windows with transient_for set to
1136 Root as transient for their group *UNLESS* they are also modal.
1137 In that case, it will make them transient for the group. This
1138 leads to all sorts of weird behavior from KDE apps which are
1139 only tested in KWin. I'd like to follow their behavior just to
1140 make this work right with KDE stuff, but that seems wrong.
1142 if (!target
&& self
->group
) {
1143 /* not transient to a client, see if it is transient for a
1145 if (t
== RootWindow(ob_display
, ob_screen
)) {
1146 /* window is a transient for its group! */
1147 target
= OB_TRAN_GROUP
;
1151 } else if (self
->group
) {
1152 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1153 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1154 self
->type
== OB_CLIENT_TYPE_MENU
||
1155 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1157 self
->transient
= TRUE
;
1158 target
= OB_TRAN_GROUP
;
1161 self
->transient
= FALSE
;
1163 /* if anything has changed... */
1164 if (target
!= self
->transient_for
) {
1165 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1168 /* remove from old parents */
1169 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1170 ObClient
*c
= it
->data
;
1171 if (c
!= self
&& !c
->transient_for
)
1172 c
->transients
= g_slist_remove(c
->transients
, self
);
1174 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1175 /* remove from old parent */
1176 self
->transient_for
->transients
=
1177 g_slist_remove(self
->transient_for
->transients
, self
);
1179 self
->transient_for
= target
;
1180 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1183 /* add to new parents */
1184 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1185 ObClient
*c
= it
->data
;
1186 if (c
!= self
&& !c
->transient_for
)
1187 c
->transients
= g_slist_append(c
->transients
, self
);
1190 /* remove all transients which are in the group, that causes
1191 circlular pointer hell of doom */
1192 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1194 for (sit
= self
->transients
; sit
; sit
= next
) {
1195 next
= g_slist_next(sit
);
1196 if (sit
->data
== it
->data
)
1198 g_slist_delete_link(self
->transients
, sit
);
1201 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1202 /* add to new parent */
1203 self
->transient_for
->transients
=
1204 g_slist_append(self
->transient_for
->transients
, self
);
1209 static void client_get_mwm_hints(ObClient
*self
)
1214 self
->mwmhints
.flags
= 0; /* default to none */
1216 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1218 if (num
>= OB_MWM_ELEMENTS
) {
1219 self
->mwmhints
.flags
= hints
[0];
1220 self
->mwmhints
.functions
= hints
[1];
1221 self
->mwmhints
.decorations
= hints
[2];
1227 void client_get_type(ObClient
*self
)
1234 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1235 /* use the first value that we know about in the array */
1236 for (i
= 0; i
< num
; ++i
) {
1237 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1238 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1239 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1240 self
->type
= OB_CLIENT_TYPE_DOCK
;
1241 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1242 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1243 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1244 self
->type
= OB_CLIENT_TYPE_MENU
;
1245 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1246 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1247 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1248 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1249 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1250 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1251 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1252 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1253 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1254 /* prevent this window from getting any decor or
1256 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1257 OB_MWM_FLAG_DECORATIONS
);
1258 self
->mwmhints
.decorations
= 0;
1259 self
->mwmhints
.functions
= 0;
1261 if (self
->type
!= (ObClientType
) -1)
1262 break; /* grab the first legit type */
1267 if (self
->type
== (ObClientType
) -1) {
1268 /*the window type hint was not set, which means we either classify
1269 ourself as a normal window or a dialog, depending on if we are a
1271 if (self
->transient
)
1272 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1274 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1278 void client_update_protocols(ObClient
*self
)
1281 guint num_return
, i
;
1283 self
->focus_notify
= FALSE
;
1284 self
->delete_window
= FALSE
;
1286 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1287 for (i
= 0; i
< num_return
; ++i
) {
1288 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1289 /* this means we can request the window to close */
1290 self
->delete_window
= TRUE
;
1291 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1292 /* if this protocol is requested, then the window will be
1293 notified whenever we want it to receive focus */
1294 self
->focus_notify
= TRUE
;
1300 static void client_get_gravity(ObClient
*self
)
1302 XWindowAttributes wattrib
;
1305 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1306 g_assert(ret
!= BadWindow
);
1307 self
->gravity
= wattrib
.win_gravity
;
1310 void client_update_normal_hints(ObClient
*self
)
1314 gint oldgravity
= self
->gravity
;
1317 self
->min_ratio
= 0.0f
;
1318 self
->max_ratio
= 0.0f
;
1319 SIZE_SET(self
->size_inc
, 1, 1);
1320 SIZE_SET(self
->base_size
, 0, 0);
1321 SIZE_SET(self
->min_size
, 0, 0);
1322 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1324 /* get the hints from the window */
1325 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1326 /* normal windows can't request placement! har har
1327 if (!client_normal(self))
1329 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1331 if (size
.flags
& PWinGravity
) {
1332 self
->gravity
= size
.win_gravity
;
1334 /* if the client has a frame, i.e. has already been mapped and
1335 is changing its gravity */
1336 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1337 /* move our idea of the client's position based on its new
1339 self
->area
.x
= self
->frame
->area
.x
;
1340 self
->area
.y
= self
->frame
->area
.y
;
1341 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1345 if (size
.flags
& PAspect
) {
1346 if (size
.min_aspect
.y
)
1348 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1349 if (size
.max_aspect
.y
)
1351 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1354 if (size
.flags
& PMinSize
)
1355 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1357 if (size
.flags
& PMaxSize
)
1358 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1360 if (size
.flags
& PBaseSize
)
1361 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1363 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1364 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1368 void client_setup_decor_and_functions(ObClient
*self
)
1370 /* start with everything (cept fullscreen) */
1372 (OB_FRAME_DECOR_TITLEBAR
|
1373 OB_FRAME_DECOR_HANDLE
|
1374 OB_FRAME_DECOR_GRIPS
|
1375 OB_FRAME_DECOR_BORDER
|
1376 OB_FRAME_DECOR_ICON
|
1377 OB_FRAME_DECOR_ALLDESKTOPS
|
1378 OB_FRAME_DECOR_ICONIFY
|
1379 OB_FRAME_DECOR_MAXIMIZE
|
1380 OB_FRAME_DECOR_SHADE
|
1381 OB_FRAME_DECOR_CLOSE
);
1383 (OB_CLIENT_FUNC_RESIZE
|
1384 OB_CLIENT_FUNC_MOVE
|
1385 OB_CLIENT_FUNC_ICONIFY
|
1386 OB_CLIENT_FUNC_MAXIMIZE
|
1387 OB_CLIENT_FUNC_SHADE
|
1388 OB_CLIENT_FUNC_CLOSE
);
1390 if (!(self
->min_size
.width
< self
->max_size
.width
||
1391 self
->min_size
.height
< self
->max_size
.height
))
1392 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1394 switch (self
->type
) {
1395 case OB_CLIENT_TYPE_NORMAL
:
1396 /* normal windows retain all of the possible decorations and
1397 functionality, and are the only windows that you can fullscreen */
1398 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1401 case OB_CLIENT_TYPE_DIALOG
:
1402 case OB_CLIENT_TYPE_UTILITY
:
1403 /* these windows cannot be maximized */
1404 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1407 case OB_CLIENT_TYPE_MENU
:
1408 case OB_CLIENT_TYPE_TOOLBAR
:
1409 /* these windows get less functionality */
1410 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1413 case OB_CLIENT_TYPE_DESKTOP
:
1414 case OB_CLIENT_TYPE_DOCK
:
1415 case OB_CLIENT_TYPE_SPLASH
:
1416 /* none of these windows are manipulated by the window manager */
1417 self
->decorations
= 0;
1418 self
->functions
= 0;
1422 /* Mwm Hints are applied subtractively to what has already been chosen for
1423 decor and functionality */
1424 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1425 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1426 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1427 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1429 /* if the mwm hints request no handle or title, then all
1430 decorations are disabled, but keep the border if that's
1432 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1433 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1435 self
->decorations
= 0;
1440 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1441 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1442 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1443 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1444 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1445 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1446 /* dont let mwm hints kill any buttons
1447 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1448 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1449 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1450 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1452 /* dont let mwm hints kill the close button
1453 if (! (self->mwmhints.functions & MwmFunc_Close))
1454 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1458 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1459 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1460 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1461 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1462 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1463 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1465 /* can't maximize without moving/resizing */
1466 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1467 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1468 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1469 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1470 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1473 /* kill the handle on fully maxed windows */
1474 if (self
->max_vert
&& self
->max_horz
)
1475 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1477 /* finally, the user can have requested no decorations, which overrides
1478 everything (but doesnt give it a border if it doesnt have one) */
1479 if (self
->undecorated
) {
1480 if (config_theme_keepborder
)
1481 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1483 self
->decorations
= 0;
1486 /* if we don't have a titlebar, then we cannot shade! */
1487 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1488 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1490 /* now we need to check against rules for the client's current state */
1491 if (self
->fullscreen
) {
1492 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1493 OB_CLIENT_FUNC_FULLSCREEN
|
1494 OB_CLIENT_FUNC_ICONIFY
);
1495 self
->decorations
= 0;
1498 client_change_allowed_actions(self
);
1501 /* adjust the client's decorations, etc. */
1502 client_reconfigure(self
);
1506 static void client_change_allowed_actions(ObClient
*self
)
1511 /* desktop windows are kept on all desktops */
1512 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1513 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1515 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1516 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1517 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1518 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1519 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1520 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1521 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1522 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1523 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1524 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1525 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1526 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1527 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1528 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1529 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1532 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1534 /* make sure the window isn't breaking any rules now */
1536 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1537 if (self
->frame
) client_shade(self
, FALSE
);
1538 else self
->shaded
= FALSE
;
1540 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1541 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1542 else self
->iconic
= FALSE
;
1544 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1545 if (self
->frame
) client_fullscreen(self
, FALSE
);
1546 else self
->fullscreen
= FALSE
;
1548 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1550 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1551 else self
->max_vert
= self
->max_horz
= FALSE
;
1555 void client_reconfigure(ObClient
*self
)
1557 /* by making this pass FALSE for user, we avoid the emacs event storm where
1558 every configurenotify causes an update in its normal hints, i think this
1559 is generally what we want anyways... */
1560 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1561 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1564 void client_update_wmhints(ObClient
*self
)
1569 /* assume a window takes input if it doesnt specify */
1570 self
->can_focus
= TRUE
;
1572 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1573 if (hints
->flags
& InputHint
)
1574 self
->can_focus
= hints
->input
;
1576 /* only do this when first managing the window *AND* when we aren't
1578 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1579 if (hints
->flags
& StateHint
)
1580 self
->iconic
= hints
->initial_state
== IconicState
;
1582 if (!(hints
->flags
& WindowGroupHint
))
1583 hints
->window_group
= None
;
1585 /* did the group state change? */
1586 if (hints
->window_group
!=
1587 (self
->group
? self
->group
->leader
: None
)) {
1588 /* remove from the old group if there was one */
1589 if (self
->group
!= NULL
) {
1590 /* remove transients of the group */
1591 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1592 self
->transients
= g_slist_remove(self
->transients
,
1595 /* remove myself from parents in the group */
1596 if (self
->transient_for
== OB_TRAN_GROUP
) {
1597 for (it
= self
->group
->members
; it
;
1598 it
= g_slist_next(it
))
1600 ObClient
*c
= it
->data
;
1602 if (c
!= self
&& !c
->transient_for
)
1603 c
->transients
= g_slist_remove(c
->transients
,
1608 group_remove(self
->group
, self
);
1611 if (hints
->window_group
!= None
) {
1612 self
->group
= group_add(hints
->window_group
, self
);
1614 /* i can only have transients from the group if i am not
1616 if (!self
->transient_for
) {
1617 /* add other transients of the group that are already
1619 for (it
= self
->group
->members
; it
;
1620 it
= g_slist_next(it
))
1622 ObClient
*c
= it
->data
;
1623 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1625 g_slist_append(self
->transients
, c
);
1630 /* because the self->transient flag wont change from this call,
1631 we don't need to update the window's type and such, only its
1632 transient_for, and the transients lists of other windows in
1633 the group may be affected */
1634 client_update_transient_for(self
);
1637 /* the WM_HINTS can contain an icon */
1638 client_update_icons(self
);
1644 void client_update_title(ObClient
*self
)
1647 gchar
*visible
= NULL
;
1649 g_free(self
->title
);
1652 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1653 /* try old x stuff */
1654 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1655 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1656 if (self
->transient
) {
1658 GNOME alert windows are not given titles:
1659 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1661 data
= g_strdup("");
1663 data
= g_strdup("Unnamed Window");
1667 if (self
->client_machine
) {
1668 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1673 PROP_SETS(self
->window
, net_wm_visible_name
, visible
);
1674 self
->title
= visible
;
1677 frame_adjust_title(self
->frame
);
1679 /* update the icon title */
1681 g_free(self
->icon_title
);
1684 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1685 /* try old x stuff */
1686 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1687 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1688 data
= g_strdup(self
->title
);
1690 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1691 self
->icon_title
= data
;
1694 void client_update_class(ObClient
*self
)
1699 if (self
->name
) g_free(self
->name
);
1700 if (self
->class) g_free(self
->class);
1701 if (self
->role
) g_free(self
->role
);
1703 self
->name
= self
->class = self
->role
= NULL
;
1705 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1707 self
->name
= g_strdup(data
[0]);
1709 self
->class = g_strdup(data
[1]);
1714 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1717 if (self
->name
== NULL
) self
->name
= g_strdup("");
1718 if (self
->class == NULL
) self
->class = g_strdup("");
1719 if (self
->role
== NULL
) self
->role
= g_strdup("");
1722 void client_update_strut(ObClient
*self
)
1726 gboolean got
= FALSE
;
1729 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1733 STRUT_PARTIAL_SET(strut
,
1734 data
[0], data
[2], data
[1], data
[3],
1735 data
[4], data
[5], data
[8], data
[9],
1736 data
[6], data
[7], data
[10], data
[11]);
1742 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1748 /* use the screen's width/height */
1749 a
= screen_physical_area();
1751 STRUT_PARTIAL_SET(strut
,
1752 data
[0], data
[2], data
[1], data
[3],
1753 a
->y
, a
->y
+ a
->height
- 1,
1754 a
->x
, a
->x
+ a
->width
- 1,
1755 a
->y
, a
->y
+ a
->height
- 1,
1756 a
->x
, a
->x
+ a
->width
- 1);
1762 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1763 0, 0, 0, 0, 0, 0, 0, 0);
1765 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1766 self
->strut
= strut
;
1768 /* updating here is pointless while we're being mapped cuz we're not in
1769 the client list yet */
1771 screen_update_areas();
1775 void client_update_icons(ObClient
*self
)
1781 for (i
= 0; i
< self
->nicons
; ++i
)
1782 g_free(self
->icons
[i
].data
);
1783 if (self
->nicons
> 0)
1784 g_free(self
->icons
);
1787 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1788 /* figure out how many valid icons are in here */
1790 while (num
- i
> 2) {
1794 if (i
> num
|| w
*h
== 0) break;
1798 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1800 /* store the icons */
1802 for (j
= 0; j
< self
->nicons
; ++j
) {
1805 w
= self
->icons
[j
].width
= data
[i
++];
1806 h
= self
->icons
[j
].height
= data
[i
++];
1808 if (w
*h
== 0) continue;
1810 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1811 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1816 self
->icons
[j
].data
[t
] =
1817 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1818 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1819 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1820 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1829 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1830 if (hints
->flags
& IconPixmapHint
) {
1832 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1833 xerror_set_ignore(TRUE
);
1834 if (!RrPixmapToRGBA(ob_rr_inst
,
1836 (hints
->flags
& IconMaskHint
?
1837 hints
->icon_mask
: None
),
1838 &self
->icons
[self
->nicons
-1].width
,
1839 &self
->icons
[self
->nicons
-1].height
,
1840 &self
->icons
[self
->nicons
-1].data
)){
1841 g_free(&self
->icons
[self
->nicons
-1]);
1844 xerror_set_ignore(FALSE
);
1851 frame_adjust_icon(self
->frame
);
1854 void client_update_user_time(ObClient
*self
)
1858 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1859 /* we set this every time, not just when it grows, because in practice
1860 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1861 backward we don't want all windows to stop focusing. we'll just
1862 assume noone is setting times older than the last one, cuz that
1863 would be pretty stupid anyways
1865 self
->user_time
= time
;
1868 ob_debug("window %s user time %u\n", self->title, time);
1873 static void client_get_client_machine(ObClient
*self
)
1876 gchar localhost
[128];
1878 g_free(self
->client_machine
);
1880 if (PROP_GETS(self
->window
, wm_client_machine
, locale
, &data
)) {
1881 gethostname(localhost
, 127);
1882 localhost
[127] = '\0';
1883 if (strcmp(localhost
, data
))
1884 self
->client_machine
= data
;
1888 static void client_change_wm_state(ObClient
*self
)
1893 old
= self
->wmstate
;
1895 if (self
->shaded
|| self
->iconic
|| !self
->frame
->visible
)
1896 self
->wmstate
= IconicState
;
1898 self
->wmstate
= NormalState
;
1900 if (old
!= self
->wmstate
) {
1901 PROP_MSG(self
->window
, kde_wm_change_state
,
1902 self
->wmstate
, 1, 0, 0);
1904 state
[0] = self
->wmstate
;
1906 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1910 static void client_change_state(ObClient
*self
)
1912 gulong netstate
[11];
1917 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1919 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1921 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1922 if (self
->skip_taskbar
)
1923 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1924 if (self
->skip_pager
)
1925 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1926 if (self
->fullscreen
)
1927 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1929 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1931 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1933 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1935 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1936 if (self
->demands_attention
)
1937 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1938 if (self
->undecorated
)
1939 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1940 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1943 frame_adjust_state(self
->frame
);
1946 ObClient
*client_search_focus_tree(ObClient
*self
)
1951 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1952 if (client_focused(it
->data
)) return it
->data
;
1953 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1958 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1960 if (self
->transient_for
) {
1961 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1962 return client_search_focus_tree_full(self
->transient_for
);
1965 gboolean recursed
= FALSE
;
1967 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1968 if (!((ObClient
*)it
->data
)->transient_for
) {
1970 if ((c
= client_search_focus_tree_full(it
->data
)))
1979 /* this function checks the whole tree, the client_search_focus_tree~
1980 does not, so we need to check this window */
1981 if (client_focused(self
))
1983 return client_search_focus_tree(self
);
1986 static ObStackingLayer
calc_layer(ObClient
*self
)
1990 if (self
->fullscreen
&&
1991 (client_focused(self
) || client_search_focus_tree(self
)))
1992 l
= OB_STACKING_LAYER_FULLSCREEN
;
1993 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1994 l
= OB_STACKING_LAYER_DESKTOP
;
1995 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1996 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1997 else l
= OB_STACKING_LAYER_ABOVE
;
1999 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2000 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2001 else l
= OB_STACKING_LAYER_NORMAL
;
2006 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2007 ObStackingLayer min
, gboolean raised
)
2009 ObStackingLayer old
, own
;
2013 own
= calc_layer(self
);
2014 self
->layer
= MAX(own
, min
);
2016 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2017 client_calc_layer_recursive(it
->data
, orig
,
2019 raised
? raised
: self
->layer
!= old
);
2021 if (!raised
&& self
->layer
!= old
)
2022 if (orig
->frame
) { /* only restack if the original window is managed */
2023 stacking_remove(CLIENT_AS_WINDOW(self
));
2024 stacking_add(CLIENT_AS_WINDOW(self
));
2028 void client_calc_layer(ObClient
*self
)
2035 /* transients take on the layer of their parents */
2036 it
= client_search_all_top_parents(self
);
2038 for (; it
; it
= g_slist_next(it
))
2039 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2042 gboolean
client_should_show(ObClient
*self
)
2046 if (client_normal(self
) && screen_showing_desktop
)
2049 if (self->transient_for) {
2050 if (self->transient_for != OB_TRAN_GROUP)
2051 return client_should_show(self->transient_for);
2055 for (it = self->group->members; it; it = g_slist_next(it)) {
2056 ObClient *c = it->data;
2057 if (c != self && !c->transient_for) {
2058 if (client_should_show(c))
2065 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2071 void client_show(ObClient
*self
)
2074 if (client_should_show(self
)) {
2075 frame_show(self
->frame
);
2078 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2079 needs to be in IconicState. This includes when it is on another
2082 client_change_wm_state(self
);
2085 void client_hide(ObClient
*self
)
2087 if (!client_should_show(self
)) {
2088 frame_hide(self
->frame
);
2091 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2092 needs to be in IconicState. This includes when it is on another
2095 client_change_wm_state(self
);
2098 void client_showhide(ObClient
*self
)
2101 if (client_should_show(self
)) {
2102 frame_show(self
->frame
);
2105 frame_hide(self
->frame
);
2108 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2109 needs to be in IconicState. This includes when it is on another
2112 client_change_wm_state(self
);
2115 gboolean
client_normal(ObClient
*self
) {
2116 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2117 self
->type
== OB_CLIENT_TYPE_DOCK
||
2118 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2121 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2123 gboolean pos
= FALSE
; /* has the window's position been configured? */
2126 /* save the position, and set self->area for these to use */
2132 /* these are in a carefully crafted order.. */
2135 self
->iconic
= FALSE
;
2136 client_iconify(self
, TRUE
, FALSE
);
2138 if (self
->fullscreen
) {
2139 self
->fullscreen
= FALSE
;
2140 client_fullscreen(self
, TRUE
);
2143 if (self
->undecorated
) {
2144 self
->undecorated
= FALSE
;
2145 client_set_undecorated(self
, TRUE
);
2148 self
->shaded
= FALSE
;
2149 client_shade(self
, TRUE
);
2151 if (self
->demands_attention
) {
2152 self
->demands_attention
= FALSE
;
2153 client_hilite(self
, TRUE
);
2156 if (self
->max_vert
&& self
->max_horz
) {
2157 self
->max_vert
= self
->max_horz
= FALSE
;
2158 client_maximize(self
, TRUE
, 0);
2160 } else if (self
->max_vert
) {
2161 self
->max_vert
= FALSE
;
2162 client_maximize(self
, TRUE
, 2);
2164 } else if (self
->max_horz
) {
2165 self
->max_horz
= FALSE
;
2166 client_maximize(self
, TRUE
, 1);
2170 /* if the client didn't get positioned yet, then do so now
2171 call client_move even if the window is not being moved anywhere, because
2172 when we reparent it and decorate it, it is getting moved and we need to
2173 be telling it so with a ConfigureNotify event.
2176 /* use the saved position */
2179 client_move(self
, x
, y
);
2182 /* nothing to do for the other states:
2191 void client_try_configure(ObClient
*self
, ObCorner anchor
,
2192 gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2193 gint
*logicalw
, gint
*logicalh
,
2196 Rect desired_area
= {*x
, *y
, *w
, *h
};
2198 /* make the frame recalculate its dimentions n shit without changing
2199 anything visible for real, this way the constraints below can work with
2200 the updated frame dimensions. */
2201 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2203 /* work within the prefered sizes given by the window */
2204 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2205 gint basew
, baseh
, minw
, minh
;
2207 /* base size is substituted with min size if not specified */
2208 if (self
->base_size
.width
|| self
->base_size
.height
) {
2209 basew
= self
->base_size
.width
;
2210 baseh
= self
->base_size
.height
;
2212 basew
= self
->min_size
.width
;
2213 baseh
= self
->min_size
.height
;
2215 /* min size is substituted with base size if not specified */
2216 if (self
->min_size
.width
|| self
->min_size
.height
) {
2217 minw
= self
->min_size
.width
;
2218 minh
= self
->min_size
.height
;
2220 minw
= self
->base_size
.width
;
2221 minh
= self
->base_size
.height
;
2224 /* if this is a user-requested resize, then check against min/max
2227 /* smaller than min size or bigger than max size? */
2228 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2229 if (*w
< minw
) *w
= minw
;
2230 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2231 if (*h
< minh
) *h
= minh
;
2236 /* keep to the increments */
2237 *w
/= self
->size_inc
.width
;
2238 *h
/= self
->size_inc
.height
;
2240 /* you cannot resize to nothing */
2241 if (basew
+ *w
< 1) *w
= 1 - basew
;
2242 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2244 /* save the logical size */
2245 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2246 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2248 *w
*= self
->size_inc
.width
;
2249 *h
*= self
->size_inc
.height
;
2254 /* adjust the height to match the width for the aspect ratios.
2255 for this, min size is not substituted for base size ever. */
2256 *w
-= self
->base_size
.width
;
2257 *h
-= self
->base_size
.height
;
2259 if (!self
->fullscreen
) {
2260 if (self
->min_ratio
)
2261 if (*h
* self
->min_ratio
> *w
) {
2262 *h
= (gint
)(*w
/ self
->min_ratio
);
2264 /* you cannot resize to nothing */
2267 *w
= (gint
)(*h
* self
->min_ratio
);
2270 if (self
->max_ratio
)
2271 if (*h
* self
->max_ratio
< *w
) {
2272 *h
= (gint
)(*w
/ self
->max_ratio
);
2274 /* you cannot resize to nothing */
2277 *w
= (gint
)(*h
* self
->min_ratio
);
2282 *w
+= self
->base_size
.width
;
2283 *h
+= self
->base_size
.height
;
2286 /* gets the frame's position */
2287 frame_client_gravity(self
->frame
, x
, y
);
2289 /* these positions are frame positions, not client positions */
2291 /* set the size and position if fullscreen */
2292 if (self
->fullscreen
) {
2296 i
= screen_find_monitor(&desired_area
);
2297 a
= screen_physical_area_monitor(i
);
2304 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2305 is entering fullscreen */
2306 } else if (self
->max_horz
|| self
->max_vert
) {
2310 i
= screen_find_monitor(&desired_area
);
2311 a
= screen_area_monitor(self
->desktop
, i
);
2313 /* set the size and position if maximized */
2314 if (self
->max_horz
) {
2316 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2318 if (self
->max_vert
) {
2320 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2323 /* maximizing is not allowed if the user can't move+resize the window
2327 /* gets the client's position */
2328 frame_frame_gravity(self
->frame
, x
, y
);
2330 /* these override the above states! if you cant move you can't move! */
2332 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2336 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2337 *w
= self
->area
.width
;
2338 *h
= self
->area
.height
;
2346 case OB_CORNER_TOPLEFT
:
2348 case OB_CORNER_TOPRIGHT
:
2349 *x
-= *w
- self
->area
.width
;
2351 case OB_CORNER_BOTTOMLEFT
:
2352 *y
-= *h
- self
->area
.height
;
2354 case OB_CORNER_BOTTOMRIGHT
:
2355 *x
-= *w
- self
->area
.width
;
2356 *y
-= *h
- self
->area
.height
;
2362 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2363 gint x
, gint y
, gint w
, gint h
,
2364 gboolean user
, gboolean final
,
2365 gboolean force_reply
)
2367 gint oldw
, oldh
, oldrx
, oldry
;
2368 gboolean send_resize_client
;
2369 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2370 guint fdecor
= self
->frame
->decorations
;
2371 gboolean fhorz
= self
->frame
->max_horz
;
2372 gint logicalw
, logicalh
;
2374 /* find the new x, y, width, and height (and logical size) */
2375 client_try_configure(self
, anchor
, &x
, &y
, &w
, &h
,
2376 &logicalw
, &logicalh
, user
);
2378 /* set the logical size if things changed */
2379 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2380 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2382 /* figure out if we moved or resized or what */
2383 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2384 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2386 oldw
= self
->area
.width
;
2387 oldh
= self
->area
.height
;
2388 RECT_SET(self
->area
, x
, y
, w
, h
);
2390 /* for app-requested resizes, always resize if 'resized' is true.
2391 for user-requested ones, only resize if final is true, or when
2392 resizing in redraw mode */
2393 send_resize_client
= ((!user
&& resized
) ||
2395 (resized
&& config_resize_redraw
))));
2397 /* if the client is enlarging, then resize the client before the frame */
2398 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2399 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2401 /* find the frame's dimensions and move/resize it */
2402 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2403 moved
= resized
= TRUE
;
2404 if (moved
|| resized
)
2405 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2407 /* find the client's position relative to the root window */
2408 oldrx
= self
->root_pos
.x
;
2409 oldry
= self
->root_pos
.y
;
2410 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2411 self
->frame
->size
.left
-
2412 self
->border_width
) ||
2413 oldry
!= (signed)(self
->frame
->area
.y
+
2414 self
->frame
->size
.top
-
2415 self
->border_width
));
2417 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2421 POINT_SET(self
->root_pos
,
2422 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2424 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2425 self
->border_width
);
2427 event
.type
= ConfigureNotify
;
2428 event
.xconfigure
.display
= ob_display
;
2429 event
.xconfigure
.event
= self
->window
;
2430 event
.xconfigure
.window
= self
->window
;
2432 /* root window real coords */
2433 event
.xconfigure
.x
= self
->root_pos
.x
;
2434 event
.xconfigure
.y
= self
->root_pos
.y
;
2435 event
.xconfigure
.width
= w
;
2436 event
.xconfigure
.height
= h
;
2437 event
.xconfigure
.border_width
= 0;
2438 event
.xconfigure
.above
= self
->frame
->plate
;
2439 event
.xconfigure
.override_redirect
= FALSE
;
2440 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2441 FALSE
, StructureNotifyMask
, &event
);
2444 /* if the client is shrinking, then resize the frame before the client */
2445 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2446 XResizeWindow(ob_display
, self
->window
, w
, h
);
2451 void client_fullscreen(ObClient
*self
, gboolean fs
)
2455 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2456 self
->fullscreen
== fs
) return; /* already done */
2458 self
->fullscreen
= fs
;
2459 client_change_state(self
); /* change the state hints on the client */
2460 client_calc_layer(self
); /* and adjust out layer/stacking */
2463 self
->pre_fullscreen_area
= self
->area
;
2464 /* if the window is maximized, its area isn't all that meaningful.
2465 save it's premax area instead. */
2466 if (self
->max_horz
) {
2467 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2468 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2470 if (self
->max_vert
) {
2471 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2472 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2475 /* these are not actually used cuz client_configure will set them
2476 as appropriate when the window is fullscreened */
2481 if (self
->pre_fullscreen_area
.width
> 0 &&
2482 self
->pre_fullscreen_area
.height
> 0)
2484 x
= self
->pre_fullscreen_area
.x
;
2485 y
= self
->pre_fullscreen_area
.y
;
2486 w
= self
->pre_fullscreen_area
.width
;
2487 h
= self
->pre_fullscreen_area
.height
;
2488 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2490 /* pick some fallbacks... */
2491 a
= screen_area_monitor(self
->desktop
, 0);
2492 x
= a
->x
+ a
->width
/ 4;
2493 y
= a
->y
+ a
->height
/ 4;
2499 client_setup_decor_and_functions(self
);
2501 client_move_resize(self
, x
, y
, w
, h
);
2503 /* try focus us when we go into fullscreen mode */
2507 static void client_iconify_recursive(ObClient
*self
,
2508 gboolean iconic
, gboolean curdesk
)
2511 gboolean changed
= FALSE
;
2514 if (self
->iconic
!= iconic
) {
2515 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2519 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2520 self
->iconic
= iconic
;
2522 /* update the focus lists.. iconic windows go to the bottom of
2523 the list, put the new iconic window at the 'top of the
2525 focus_order_to_top(self
);
2530 self
->iconic
= iconic
;
2533 client_set_desktop(self
, screen_desktop
, FALSE
);
2535 /* this puts it after the current focused window */
2536 focus_order_remove(self
);
2537 focus_order_add_new(self
);
2544 client_change_state(self
);
2545 client_showhide(self
);
2546 if (STRUT_EXISTS(self
->strut
))
2547 screen_update_areas();
2550 /* iconify all direct transients */
2551 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2552 if (it
->data
!= self
)
2553 if (client_is_direct_child(self
, it
->data
))
2554 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2557 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2559 /* move up the transient chain as far as possible first */
2560 self
= client_search_top_parent(self
);
2561 client_iconify_recursive(self
, iconic
, curdesk
);
2564 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2568 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2569 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2571 /* check if already done */
2573 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2574 if (dir
== 1 && self
->max_horz
) return;
2575 if (dir
== 2 && self
->max_vert
) return;
2577 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2578 if (dir
== 1 && !self
->max_horz
) return;
2579 if (dir
== 2 && !self
->max_vert
) return;
2582 /* we just tell it to configure in the same place and client_configure
2583 worries about filling the screen with the window */
2586 w
= self
->area
.width
;
2587 h
= self
->area
.height
;
2590 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2591 RECT_SET(self
->pre_max_area
,
2592 self
->area
.x
, self
->pre_max_area
.y
,
2593 self
->area
.width
, self
->pre_max_area
.height
);
2595 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2596 RECT_SET(self
->pre_max_area
,
2597 self
->pre_max_area
.x
, self
->area
.y
,
2598 self
->pre_max_area
.width
, self
->area
.height
);
2603 a
= screen_area_monitor(self
->desktop
, 0);
2604 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2605 if (self
->pre_max_area
.width
> 0) {
2606 x
= self
->pre_max_area
.x
;
2607 w
= self
->pre_max_area
.width
;
2609 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2610 0, self
->pre_max_area
.height
);
2612 /* pick some fallbacks... */
2613 x
= a
->x
+ a
->width
/ 4;
2617 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2618 if (self
->pre_max_area
.height
> 0) {
2619 y
= self
->pre_max_area
.y
;
2620 h
= self
->pre_max_area
.height
;
2622 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2623 self
->pre_max_area
.width
, 0);
2625 /* pick some fallbacks... */
2626 y
= a
->y
+ a
->height
/ 4;
2632 if (dir
== 0 || dir
== 1) /* horz */
2633 self
->max_horz
= max
;
2634 if (dir
== 0 || dir
== 2) /* vert */
2635 self
->max_vert
= max
;
2637 client_change_state(self
); /* change the state hints on the client */
2639 client_setup_decor_and_functions(self
);
2641 client_move_resize(self
, x
, y
, w
, h
);
2644 void client_shade(ObClient
*self
, gboolean shade
)
2646 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2647 shade
) || /* can't shade */
2648 self
->shaded
== shade
) return; /* already done */
2650 self
->shaded
= shade
;
2651 client_change_state(self
);
2652 client_change_wm_state(self
); /* the window is being hidden/shown */
2653 /* resize the frame to just the titlebar */
2654 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2657 void client_close(ObClient
*self
)
2661 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2663 /* in the case that the client provides no means to requesting that it
2664 close, we just kill it */
2665 if (!self
->delete_window
)
2669 XXX: itd be cool to do timeouts and shit here for killing the client's
2671 like... if the window is around after 5 seconds, then the close button
2672 turns a nice red, and if this function is called again, the client is
2676 ce
.xclient
.type
= ClientMessage
;
2677 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2678 ce
.xclient
.display
= ob_display
;
2679 ce
.xclient
.window
= self
->window
;
2680 ce
.xclient
.format
= 32;
2681 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2682 ce
.xclient
.data
.l
[1] = event_curtime
;
2683 ce
.xclient
.data
.l
[2] = 0l;
2684 ce
.xclient
.data
.l
[3] = 0l;
2685 ce
.xclient
.data
.l
[4] = 0l;
2686 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2689 void client_kill(ObClient
*self
)
2691 XKillClient(ob_display
, self
->window
);
2694 void client_hilite(ObClient
*self
, gboolean hilite
)
2696 if (self
->demands_attention
== hilite
)
2697 return; /* no change */
2699 /* don't allow focused windows to hilite */
2700 self
->demands_attention
= hilite
&& !client_focused(self
);
2701 if (self
->demands_attention
)
2702 frame_flash_start(self
->frame
);
2704 frame_flash_stop(self
->frame
);
2705 client_change_state(self
);
2708 void client_set_desktop_recursive(ObClient
*self
,
2709 guint target
, gboolean donthide
)
2714 if (target
!= self
->desktop
) {
2716 ob_debug("Setting desktop %u\n", target
+1);
2718 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2720 /* remove from the old desktop(s) */
2721 focus_order_remove(self
);
2723 old
= self
->desktop
;
2724 self
->desktop
= target
;
2725 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2726 /* the frame can display the current desktop state */
2727 frame_adjust_state(self
->frame
);
2728 /* 'move' the window to the new desktop */
2730 client_showhide(self
);
2731 /* raise if it was not already on the desktop */
2732 if (old
!= DESKTOP_ALL
)
2734 if (STRUT_EXISTS(self
->strut
))
2735 screen_update_areas();
2737 /* add to the new desktop(s) */
2738 if (config_focus_new
)
2739 focus_order_to_top(self
);
2741 focus_order_to_bottom(self
);
2744 /* move all transients */
2745 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2746 if (it
->data
!= self
)
2747 if (client_is_direct_child(self
, it
->data
))
2748 client_set_desktop_recursive(it
->data
, target
, donthide
);
2751 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2753 self
= client_search_top_parent(self
);
2754 client_set_desktop_recursive(self
, target
, donthide
);
2757 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2759 while (child
!= parent
&&
2760 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2761 child
= child
->transient_for
;
2762 return child
== parent
;
2765 ObClient
*client_search_modal_child(ObClient
*self
)
2770 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2771 ObClient
*c
= it
->data
;
2772 if ((ret
= client_search_modal_child(c
))) return ret
;
2773 if (c
->modal
) return c
;
2778 gboolean
client_validate(ObClient
*self
)
2782 XSync(ob_display
, FALSE
); /* get all events on the server */
2784 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2785 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2786 XPutBackEvent(ob_display
, &e
);
2793 void client_set_wm_state(ObClient
*self
, glong state
)
2795 if (state
== self
->wmstate
) return; /* no change */
2799 client_iconify(self
, TRUE
, TRUE
);
2802 client_iconify(self
, FALSE
, TRUE
);
2807 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2809 gboolean shaded
= self
->shaded
;
2810 gboolean fullscreen
= self
->fullscreen
;
2811 gboolean undecorated
= self
->undecorated
;
2812 gboolean max_horz
= self
->max_horz
;
2813 gboolean max_vert
= self
->max_vert
;
2814 gboolean modal
= self
->modal
;
2815 gboolean iconic
= self
->iconic
;
2816 gboolean demands_attention
= self
->demands_attention
;
2819 if (!(action
== prop_atoms
.net_wm_state_add
||
2820 action
== prop_atoms
.net_wm_state_remove
||
2821 action
== prop_atoms
.net_wm_state_toggle
))
2822 /* an invalid action was passed to the client message, ignore it */
2825 for (i
= 0; i
< 2; ++i
) {
2826 Atom state
= i
== 0 ? data1
: data2
;
2828 if (!state
) continue;
2830 /* if toggling, then pick whether we're adding or removing */
2831 if (action
== prop_atoms
.net_wm_state_toggle
) {
2832 if (state
== prop_atoms
.net_wm_state_modal
)
2833 action
= modal
? prop_atoms
.net_wm_state_remove
:
2834 prop_atoms
.net_wm_state_add
;
2835 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2836 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2837 prop_atoms
.net_wm_state_add
;
2838 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2839 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2840 prop_atoms
.net_wm_state_add
;
2841 else if (state
== prop_atoms
.net_wm_state_shaded
)
2842 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2843 prop_atoms
.net_wm_state_add
;
2844 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2845 action
= self
->skip_taskbar
?
2846 prop_atoms
.net_wm_state_remove
:
2847 prop_atoms
.net_wm_state_add
;
2848 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2849 action
= self
->skip_pager
?
2850 prop_atoms
.net_wm_state_remove
:
2851 prop_atoms
.net_wm_state_add
;
2852 else if (state
== prop_atoms
.net_wm_state_hidden
)
2853 action
= self
->iconic
?
2854 prop_atoms
.net_wm_state_remove
:
2855 prop_atoms
.net_wm_state_add
;
2856 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2857 action
= fullscreen
?
2858 prop_atoms
.net_wm_state_remove
:
2859 prop_atoms
.net_wm_state_add
;
2860 else if (state
== prop_atoms
.net_wm_state_above
)
2861 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2862 prop_atoms
.net_wm_state_add
;
2863 else if (state
== prop_atoms
.net_wm_state_below
)
2864 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2865 prop_atoms
.net_wm_state_add
;
2866 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2867 action
= self
->demands_attention
?
2868 prop_atoms
.net_wm_state_remove
:
2869 prop_atoms
.net_wm_state_add
;
2870 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2871 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2872 prop_atoms
.net_wm_state_add
;
2875 if (action
== prop_atoms
.net_wm_state_add
) {
2876 if (state
== prop_atoms
.net_wm_state_modal
) {
2878 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2880 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2882 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2884 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2885 self
->skip_taskbar
= TRUE
;
2886 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2887 self
->skip_pager
= TRUE
;
2888 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2890 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2892 } else if (state
== prop_atoms
.net_wm_state_above
) {
2894 self
->below
= FALSE
;
2895 } else if (state
== prop_atoms
.net_wm_state_below
) {
2896 self
->above
= FALSE
;
2898 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2899 demands_attention
= TRUE
;
2900 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2904 } else { /* action == prop_atoms.net_wm_state_remove */
2905 if (state
== prop_atoms
.net_wm_state_modal
) {
2907 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2909 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2911 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2913 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2914 self
->skip_taskbar
= FALSE
;
2915 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2916 self
->skip_pager
= FALSE
;
2917 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2919 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2921 } else if (state
== prop_atoms
.net_wm_state_above
) {
2922 self
->above
= FALSE
;
2923 } else if (state
== prop_atoms
.net_wm_state_below
) {
2924 self
->below
= FALSE
;
2925 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2926 demands_attention
= FALSE
;
2927 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2928 undecorated
= FALSE
;
2932 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2933 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2935 if (max_horz
== max_vert
) { /* both going the same way */
2936 client_maximize(self
, max_horz
, 0);
2938 client_maximize(self
, max_horz
, 1);
2939 client_maximize(self
, max_vert
, 2);
2943 if (max_horz
!= self
->max_horz
)
2944 client_maximize(self
, max_horz
, 1);
2946 client_maximize(self
, max_vert
, 2);
2949 /* change fullscreen state before shading, as it will affect if the window
2951 if (fullscreen
!= self
->fullscreen
)
2952 client_fullscreen(self
, fullscreen
);
2953 if (shaded
!= self
->shaded
)
2954 client_shade(self
, shaded
);
2955 if (undecorated
!= self
->undecorated
)
2956 client_set_undecorated(self
, undecorated
);
2957 if (modal
!= self
->modal
) {
2958 self
->modal
= modal
;
2959 /* when a window changes modality, then its stacking order with its
2960 transients needs to change */
2963 if (iconic
!= self
->iconic
)
2964 client_iconify(self
, iconic
, FALSE
);
2966 if (demands_attention
!= self
->demands_attention
)
2967 client_hilite(self
, demands_attention
);
2969 client_change_state(self
); /* change the hint to reflect these changes */
2972 ObClient
*client_focus_target(ObClient
*self
)
2974 ObClient
*child
= NULL
;
2976 child
= client_search_modal_child(self
);
2977 if (child
) return child
;
2981 gboolean
client_can_focus(ObClient
*self
)
2985 /* choose the correct target */
2986 self
= client_focus_target(self
);
2988 if (!self
->frame
->visible
)
2991 if (!(self
->can_focus
|| self
->focus_notify
))
2994 /* do a check to see if the window has already been unmapped or destroyed
2995 do this intelligently while watching out for unmaps we've generated
2996 (ignore_unmaps > 0) */
2997 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2998 DestroyNotify
, &ev
)) {
2999 XPutBackEvent(ob_display
, &ev
);
3002 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
3003 UnmapNotify
, &ev
)) {
3004 if (self
->ignore_unmaps
) {
3005 self
->ignore_unmaps
--;
3007 XPutBackEvent(ob_display
, &ev
);
3015 gboolean
client_focus(ObClient
*self
)
3017 /* choose the correct target */
3018 self
= client_focus_target(self
);
3020 if (!client_can_focus(self
)) {
3021 if (!self
->frame
->visible
) {
3022 /* update the focus lists */
3023 focus_order_to_top(self
);
3028 ob_debug_type(OB_DEBUG_FOCUS
,
3029 "Focusing client \"%s\" at time %u\n",
3030 self
->title
, event_curtime
);
3032 if (self
->can_focus
) {
3033 /* This can cause a BadMatch error with CurrentTime, or if an app
3034 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3035 xerror_set_ignore(TRUE
);
3036 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3038 xerror_set_ignore(FALSE
);
3041 if (self
->focus_notify
) {
3043 ce
.xclient
.type
= ClientMessage
;
3044 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3045 ce
.xclient
.display
= ob_display
;
3046 ce
.xclient
.window
= self
->window
;
3047 ce
.xclient
.format
= 32;
3048 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3049 ce
.xclient
.data
.l
[1] = event_curtime
;
3050 ce
.xclient
.data
.l
[2] = 0l;
3051 ce
.xclient
.data
.l
[3] = 0l;
3052 ce
.xclient
.data
.l
[4] = 0l;
3053 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3057 ob_debug("%sively focusing %lx at %d\n",
3058 (self
->can_focus
? "act" : "pass"),
3059 self
->window
, (gint
) event_curtime
);
3062 /* Cause the FocusIn to come back to us. Important for desktop switches,
3063 since otherwise we'll have no FocusIn on the queue and send it off to
3064 the focus_backup. */
3065 XSync(ob_display
, FALSE
);
3069 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3071 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3073 /* XXX do some stuff here if user is false to determine if we really want
3074 to activate it or not (a parent or group member is currently
3077 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3079 self
->window
, event_curtime
, last_time
,
3080 (user
? "user" : "application"));
3082 if (!user
&& event_curtime
&& last_time
&&
3083 !event_time_after(event_curtime
, last_time
))
3085 client_hilite(self
, TRUE
);
3087 if (client_normal(self
) && screen_showing_desktop
)
3088 screen_show_desktop(FALSE
);
3090 client_iconify(self
, FALSE
, here
);
3091 if (self
->desktop
!= DESKTOP_ALL
&&
3092 self
->desktop
!= screen_desktop
) {
3094 client_set_desktop(self
, screen_desktop
, FALSE
);
3096 screen_set_desktop(self
->desktop
);
3097 } else if (!self
->frame
->visible
)
3098 /* if its not visible for other reasons, then don't mess
3102 client_shade(self
, FALSE
);
3106 /* we do this an action here. this is rather important. this is because
3107 we want the results from the focus change to take place BEFORE we go
3108 about raising the window. when a fullscreen window loses focus, we
3109 need this or else the raise wont be able to raise above the
3110 to-lose-focus fullscreen window. */
3115 void client_raise(ObClient
*self
)
3117 action_run_string("Raise", self
, CurrentTime
);
3120 void client_lower(ObClient
*self
)
3122 action_run_string("Lower", self
, CurrentTime
);
3125 gboolean
client_focused(ObClient
*self
)
3127 return self
== focus_client
;
3130 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3133 /* si is the smallest image >= req */
3134 /* li is the largest image < req */
3135 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3137 if (!self
->nicons
) {
3138 ObClientIcon
*parent
= NULL
;
3140 if (self
->transient_for
) {
3141 if (self
->transient_for
!= OB_TRAN_GROUP
)
3142 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3145 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3146 ObClient
*c
= it
->data
;
3147 if (c
!= self
&& !c
->transient_for
) {
3148 if ((parent
= client_icon_recursive(c
, w
, h
)))
3158 for (i
= 0; i
< self
->nicons
; ++i
) {
3159 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3160 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3164 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3169 if (largest
== 0) /* didnt find one smaller than the requested size */
3170 return &self
->icons
[si
];
3171 return &self
->icons
[li
];
3174 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3177 static ObClientIcon deficon
;
3179 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3180 deficon
.width
= deficon
.height
= 48;
3181 deficon
.data
= ob_rr_theme
->def_win_icon
;
3187 /* this be mostly ripped from fvwm */
3188 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3190 gint my_cx
, my_cy
, his_cx
, his_cy
;
3193 gint score
, best_score
;
3194 ObClient
*best_client
, *cur
;
3200 /* first, find the centre coords of the currently focused window */
3201 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3202 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3207 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3210 /* the currently selected window isn't interesting */
3213 if (!client_normal(cur
))
3215 /* using c->desktop instead of screen_desktop doesn't work if the
3216 * current window was omnipresent, hope this doesn't have any other
3218 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3222 if(!(client_focus_target(cur
) == cur
&&
3223 client_can_focus(cur
)))
3226 /* find the centre coords of this window, from the
3227 * currently focused window's point of view */
3228 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3229 + cur
->frame
->area
.width
/ 2;
3230 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3231 + cur
->frame
->area
.height
/ 2;
3233 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3234 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3236 /* Rotate the diagonals 45 degrees counterclockwise.
3237 * To do this, multiply the matrix /+h +h\ with the
3238 * vector (x y). \-h +h/
3239 * h = sqrt(0.5). We can set h := 1 since absolute
3240 * distance doesn't matter here. */
3241 tx
= his_cx
+ his_cy
;
3242 his_cy
= -his_cx
+ his_cy
;
3247 case OB_DIRECTION_NORTH
:
3248 case OB_DIRECTION_SOUTH
:
3249 case OB_DIRECTION_NORTHEAST
:
3250 case OB_DIRECTION_SOUTHWEST
:
3251 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3252 distance
= ((dir
== OB_DIRECTION_NORTH
||
3253 dir
== OB_DIRECTION_NORTHEAST
) ?
3256 case OB_DIRECTION_EAST
:
3257 case OB_DIRECTION_WEST
:
3258 case OB_DIRECTION_SOUTHEAST
:
3259 case OB_DIRECTION_NORTHWEST
:
3260 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3261 distance
= ((dir
== OB_DIRECTION_WEST
||
3262 dir
== OB_DIRECTION_NORTHWEST
) ?
3267 /* the target must be in the requested direction */
3271 /* Calculate score for this window. The smaller the better. */
3272 score
= distance
+ offset
;
3274 /* windows more than 45 degrees off the direction are
3275 * heavily penalized and will only be chosen if nothing
3276 * else within a million pixels */
3277 if(offset
> distance
)
3280 if(best_score
== -1 || score
< best_score
)
3288 void client_set_layer(ObClient
*self
, gint layer
)
3292 self
->above
= FALSE
;
3293 } else if (layer
== 0) {
3294 self
->below
= self
->above
= FALSE
;
3296 self
->below
= FALSE
;
3299 client_calc_layer(self
);
3300 client_change_state(self
); /* reflect this in the state hints */
3303 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3305 if (self
->undecorated
!= undecorated
) {
3306 self
->undecorated
= undecorated
;
3307 client_setup_decor_and_functions(self
);
3308 /* Make sure the client knows it might have moved. Maybe there is a
3309 * better way of doing this so only one client_configure is sent, but
3310 * since 125 of these are sent per second when moving the window (with
3311 * user = FALSE) i doubt it matters much.
3313 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3314 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3315 client_change_state(self
); /* reflect this in the state hints */
3319 guint
client_monitor(ObClient
*self
)
3321 return screen_find_monitor(&self
->frame
->area
);
3324 ObClient
*client_search_top_parent(ObClient
*self
)
3326 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3327 self
= self
->transient_for
;
3331 GSList
*client_search_all_top_parents(ObClient
*self
)
3335 /* move up the direct transient chain as far as possible */
3336 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3337 self
= self
->transient_for
;
3339 if (!self
->transient_for
)
3340 ret
= g_slist_prepend(ret
, self
);
3344 g_assert(self
->group
);
3346 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3347 ObClient
*c
= it
->data
;
3349 if (!c
->transient_for
&& client_normal(c
))
3350 ret
= g_slist_prepend(ret
, c
);
3353 if (ret
== NULL
) /* no group parents */
3354 ret
= g_slist_prepend(ret
, self
);
3360 ObClient
*client_search_focus_parent(ObClient
*self
)
3362 if (self
->transient_for
) {
3363 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3364 if (client_focused(self
->transient_for
))
3365 return self
->transient_for
;
3369 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3370 ObClient
*c
= it
->data
;
3372 /* checking transient_for prevents infinate loops! */
3373 if (c
!= self
&& !c
->transient_for
)
3374 if (client_focused(c
))
3383 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3385 if (self
->transient_for
) {
3386 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3387 if (self
->transient_for
== search
)
3392 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3393 ObClient
*c
= it
->data
;
3395 /* checking transient_for prevents infinate loops! */
3396 if (c
!= self
&& !c
->transient_for
)
3406 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3410 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3411 if (sit
->data
== search
)
3413 if (client_search_transient(sit
->data
, search
))
3419 void client_update_sm_client_id(ObClient
*self
)
3421 g_free(self
->sm_client_id
);
3422 self
->sm_client_id
= NULL
;
3424 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3426 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3427 &self
->sm_client_id
);
3430 #define WANT_EDGE(cur, c) \
3433 if(!client_normal(cur)) \
3435 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3439 if(cur->layer < c->layer && !config_resist_layers_below) \
3442 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3443 if ((his_edge_start >= my_edge_start && \
3444 his_edge_start <= my_edge_end) || \
3445 (my_edge_start >= his_edge_start && \
3446 my_edge_start <= his_edge_end)) \
3449 /* finds the nearest edge in the given direction from the current client
3450 * note to self: the edge is the -frame- edge (the actual one), not the
3453 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3455 gint dest
, monitor_dest
;
3456 gint my_edge_start
, my_edge_end
, my_offset
;
3463 a
= screen_area(c
->desktop
);
3464 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3467 case OB_DIRECTION_NORTH
:
3468 my_edge_start
= c
->frame
->area
.x
;
3469 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3470 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3472 /* default: top of screen */
3473 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3474 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3475 /* if the monitor edge comes before the screen edge, */
3476 /* use that as the destination instead. (For xinerama) */
3477 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3478 dest
= monitor_dest
;
3480 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3481 gint his_edge_start
, his_edge_end
, his_offset
;
3482 ObClient
*cur
= it
->data
;
3486 his_edge_start
= cur
->frame
->area
.x
;
3487 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3488 his_offset
= cur
->frame
->area
.y
+
3489 (hang
? 0 : cur
->frame
->area
.height
);
3491 if(his_offset
+ 1 > my_offset
)
3494 if(his_offset
< dest
)
3497 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3500 case OB_DIRECTION_SOUTH
:
3501 my_edge_start
= c
->frame
->area
.x
;
3502 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3503 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3505 /* default: bottom of screen */
3506 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3507 monitor_dest
= monitor
->y
+ monitor
->height
-
3508 (hang
? c
->frame
->area
.height
: 0);
3509 /* if the monitor edge comes before the screen edge, */
3510 /* use that as the destination instead. (For xinerama) */
3511 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3512 dest
= monitor_dest
;
3514 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3515 gint his_edge_start
, his_edge_end
, his_offset
;
3516 ObClient
*cur
= it
->data
;
3520 his_edge_start
= cur
->frame
->area
.x
;
3521 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3522 his_offset
= cur
->frame
->area
.y
+
3523 (hang
? cur
->frame
->area
.height
: 0);
3526 if(his_offset
- 1 < my_offset
)
3529 if(his_offset
> dest
)
3532 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3535 case OB_DIRECTION_WEST
:
3536 my_edge_start
= c
->frame
->area
.y
;
3537 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3538 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3540 /* default: leftmost egde of screen */
3541 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3542 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3543 /* if the monitor edge comes before the screen edge, */
3544 /* use that as the destination instead. (For xinerama) */
3545 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3546 dest
= monitor_dest
;
3548 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3549 gint his_edge_start
, his_edge_end
, his_offset
;
3550 ObClient
*cur
= it
->data
;
3554 his_edge_start
= cur
->frame
->area
.y
;
3555 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3556 his_offset
= cur
->frame
->area
.x
+
3557 (hang
? 0 : cur
->frame
->area
.width
);
3559 if(his_offset
+ 1 > my_offset
)
3562 if(his_offset
< dest
)
3565 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3568 case OB_DIRECTION_EAST
:
3569 my_edge_start
= c
->frame
->area
.y
;
3570 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3571 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3573 /* default: rightmost edge of screen */
3574 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3575 monitor_dest
= monitor
->x
+ monitor
->width
-
3576 (hang
? c
->frame
->area
.width
: 0);
3577 /* if the monitor edge comes before the screen edge, */
3578 /* use that as the destination instead. (For xinerama) */
3579 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3580 dest
= monitor_dest
;
3582 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3583 gint his_edge_start
, his_edge_end
, his_offset
;
3584 ObClient
*cur
= it
->data
;
3588 his_edge_start
= cur
->frame
->area
.y
;
3589 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3590 his_offset
= cur
->frame
->area
.x
+
3591 (hang
? cur
->frame
->area
.width
: 0);
3593 if(his_offset
- 1 < my_offset
)
3596 if(his_offset
> dest
)
3599 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3602 case OB_DIRECTION_NORTHEAST
:
3603 case OB_DIRECTION_SOUTHEAST
:
3604 case OB_DIRECTION_NORTHWEST
:
3605 case OB_DIRECTION_SOUTHWEST
:
3606 /* not implemented */
3608 g_assert_not_reached();
3609 dest
= 0; /* suppress warning */
3614 ObClient
* client_under_pointer()
3618 ObClient
*ret
= NULL
;
3620 if (screen_pointer_pos(&x
, &y
)) {
3621 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3622 if (WINDOW_IS_CLIENT(it
->data
)) {
3623 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3624 if (c
->frame
->visible
&&
3625 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3635 gboolean
client_has_group_siblings(ObClient
*self
)
3637 return self
->group
&& self
->group
->members
->next
;