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"
49 #include <X11/Xutil.h>
51 /*! The event mask to grab on client windows */
52 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
55 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
60 ObClientDestructor func
;
64 GList
*client_list
= NULL
;
66 static GSList
*client_destructors
= NULL
;
68 static void client_get_all(ObClient
*self
);
69 static void client_toggle_border(ObClient
*self
, gboolean show
);
70 static void client_get_startup_id(ObClient
*self
);
71 static void client_get_area(ObClient
*self
);
72 static void client_get_desktop(ObClient
*self
);
73 static void client_get_state(ObClient
*self
);
74 static void client_get_layer(ObClient
*self
);
75 static void client_get_shaped(ObClient
*self
);
76 static void client_get_mwm_hints(ObClient
*self
);
77 static void client_get_gravity(ObClient
*self
);
78 static void client_get_client_machine(ObClient
*self
);
79 static void client_get_colormap(ObClient
*self
);
80 static void client_change_allowed_actions(ObClient
*self
);
81 static void client_change_state(ObClient
*self
);
82 static void client_change_wm_state(ObClient
*self
);
83 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
84 static void client_restore_session_state(ObClient
*self
);
85 static void client_restore_session_stacking(ObClient
*self
);
86 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
88 void client_startup(gboolean reconfig
)
95 void client_shutdown(gboolean reconfig
)
99 void client_add_destructor(ObClientDestructor func
, gpointer data
)
101 Destructor
*d
= g_new(Destructor
, 1);
104 client_destructors
= g_slist_prepend(client_destructors
, d
);
107 void client_remove_destructor(ObClientDestructor func
)
111 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
112 Destructor
*d
= it
->data
;
113 if (d
->func
== func
) {
115 client_destructors
= g_slist_delete_link(client_destructors
, it
);
121 void client_set_list()
123 Window
*windows
, *win_it
;
125 guint size
= g_list_length(client_list
);
127 /* create an array of the window ids */
129 windows
= g_new(Window
, size
);
131 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
132 *win_it
= ((ObClient
*)it
->data
)->window
;
136 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
137 net_client_list
, window
, (gulong
*)windows
, size
);
146 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
150 for (it = self->transients; it; it = g_slist_next(it)) {
151 if (!func(it->data, data)) return;
152 client_foreach_transient(it->data, func, data);
156 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
158 if (self->transient_for) {
159 if (self->transient_for != OB_TRAN_GROUP) {
160 if (!func(self->transient_for, data)) return;
161 client_foreach_ancestor(self->transient_for, func, data);
165 for (it = self->group->members; it; it = g_slist_next(it))
166 if (it->data != self &&
167 !((ObClient*)it->data)->transient_for) {
168 if (!func(it->data, data)) return;
169 client_foreach_ancestor(it->data, func, data);
176 void client_manage_all()
181 XWindowAttributes attrib
;
183 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
184 &w
, &w
, &children
, &nchild
);
186 /* remove all icon windows from the list */
187 for (i
= 0; i
< nchild
; i
++) {
188 if (children
[i
] == None
) continue;
189 wmhints
= XGetWMHints(ob_display
, children
[i
]);
191 if ((wmhints
->flags
& IconWindowHint
) &&
192 (wmhints
->icon_window
!= children
[i
]))
193 for (j
= 0; j
< nchild
; j
++)
194 if (children
[j
] == wmhints
->icon_window
) {
202 for (i
= 0; i
< nchild
; ++i
) {
203 if (children
[i
] == None
)
205 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
206 if (attrib
.override_redirect
) continue;
208 if (attrib
.map_state
!= IsUnmapped
)
209 client_manage(children
[i
]);
215 void client_manage(Window window
)
219 XWindowAttributes attrib
;
220 XSetWindowAttributes attrib_set
;
222 gboolean activate
= FALSE
;
223 ObAppSettings
*settings
;
228 /* check if it has already been unmapped by the time we started mapping.
229 the grab does a sync so we don't have to here */
230 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
231 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
233 XPutBackEvent(ob_display
, &e
);
235 ob_debug("Trying to manage unmapped window. Aborting that.\n");
237 return; /* don't manage it */
240 /* make sure it isn't an override-redirect window */
241 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
242 attrib
.override_redirect
)
245 return; /* don't manage it */
248 /* is the window a docking app */
249 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
250 if ((wmhint
->flags
& StateHint
) &&
251 wmhint
->initial_state
== WithdrawnState
)
253 dock_add(window
, wmhint
);
261 ob_debug("Managing window: %lx\n", window
);
263 /* choose the events we want to receive on the CLIENT window */
264 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
265 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
266 XChangeWindowAttributes(ob_display
, window
,
267 CWEventMask
|CWDontPropagate
, &attrib_set
);
270 /* create the ObClient struct, and populate it from the hints on the
272 self
= g_new0(ObClient
, 1);
273 self
->obwin
.type
= Window_Client
;
274 self
->window
= window
;
276 /* non-zero defaults */
277 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
279 self
->desktop
= screen_num_desktops
; /* always an invalid value */
280 self
->user_time
= CurrentTime
;
282 client_get_all(self
);
283 /* per-app settings override stuff, and return the settings for other
285 settings
= client_get_settings_state(self
);
286 /* the session should get the last say */
287 client_restore_session_state(self
);
289 client_calc_layer(self
);
292 Time t
= sn_app_started(self
->startup_id
, self
->class);
293 if (t
) self
->user_time
= t
;
296 /* update the focus lists, do this before the call to change_state or
297 it can end up in the list twice! */
298 focus_order_add_new(self
);
300 /* remove the client's border (and adjust re gravity) */
301 client_toggle_border(self
, FALSE
);
303 /* specify that if we exit, the window should not be destroyed and should
304 be reparented back to root automatically */
305 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
307 /* create the decoration frame for the client window */
308 self
->frame
= frame_new(self
);
310 frame_grab_client(self
->frame
, self
);
312 /* do this after we have a frame.. it uses the frame to help determine the
313 WM_STATE to apply. */
314 client_change_state(self
);
318 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
319 client_restore_session_stacking(self
);
321 /* focus the new window? */
322 if (ob_state() != OB_STATE_STARTING
&&
324 /* this means focus=true for window is same as config_focus_new=true */
325 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
326 client_search_focus_parent(self
)) &&
327 /* this checks for focus=false for the window */
328 (!settings
|| settings
->focus
!= 0) &&
329 /* note the check against Type_Normal/Dialog, not client_normal(self),
330 which would also include other types. in this case we want more
331 strict rules for focus */
332 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
333 self
->type
== OB_CLIENT_TYPE_DIALOG
))
338 /* get the current position */
342 /* figure out placement for the window */
343 if (ob_state() == OB_STATE_RUNNING
) {
346 transient
= place_client(self
, &newx
, &newy
, settings
);
348 /* make sure the window is visible. */
349 client_find_onscreen(self
, &newx
, &newy
,
350 self
->frame
->area
.width
,
351 self
->frame
->area
.height
,
352 /* non-normal clients has less rules, and
353 windows that are being restored from a
354 session do also. we can assume you want
355 it back where you saved it. Clients saying
356 they placed themselves are subjected to
357 harder rules, ones that are placed by
358 place.c or by the user are allowed partially
359 off-screen and on xinerama divides (ie,
360 it is up to the placement routines to avoid
361 the xinerama divides) */
363 (((self
->positioned
& PPosition
) &&
364 !(self
->positioned
& USPosition
)) &&
365 client_normal(self
) &&
369 /* do this after the window is placed, so the premax/prefullscreen numbers
371 also, this moves the window to the position where it has been placed
373 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
374 self
->window
, newx
, newy
, self
->area
.width
, self
->area
.height
);
375 client_apply_startup_state(self
, newx
, newy
);
377 mouse_grab_for_client(self
, TRUE
);
380 guint32 last_time
= focus_client
?
381 focus_client
->user_time
: CurrentTime
;
383 /* This is focus stealing prevention */
384 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
385 self
->window
, self
->user_time
, last_time
);
387 /* if it's on another desktop */
388 if (!(self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
389 && /* the timestamp is from before you changed desktops */
390 self
->user_time
&& screen_desktop_user_time
&&
391 !event_time_after(self
->user_time
, screen_desktop_user_time
))
395 /* If nothing is focused, or a parent was focused, then focus this
398 else if (!focus_client
|| client_search_focus_parent(self
) != NULL
)
402 /* If time stamp is old, don't steal focus */
403 if (self
->user_time
&& last_time
&&
404 !event_time_after(self
->user_time
, last_time
))
408 /* Don't steal focus from globally active clients.
409 I stole this idea from KWin. It seems nice.
411 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
))
416 ob_debug("Focus stealing prevention activated for %s with time %u "
418 self
->title
, self
->user_time
, last_time
);
419 /* if the client isn't focused, then hilite it so the user
421 client_hilite(self
, TRUE
);
425 /* This may look rather odd. Well it's because new windows are added
426 to the stacking order non-intrusively. If we're not going to focus
427 the new window or hilite it, then we raise it to the top. This will
428 take affect for things that don't get focused like splash screens.
429 Also if you don't have focus_new enabled, then it's going to get
430 raised to the top. Legacy begets legacy I guess?
435 /* this has to happen before we try focus the window, but we want it to
436 happen after the client's stacking has been determined or it looks bad
440 /* use client_focus instead of client_activate cuz client_activate does
441 stuff like switch desktops etc and I'm not interested in all that when
442 a window maps since its not based on an action from the user like
443 clicking a window to activate it. so keep the new window out of the way
446 client_activate(self
, FALSE
, TRUE
);
448 /* add to client list/map */
449 client_list
= g_list_append(client_list
, self
);
450 g_hash_table_insert(window_map
, &self
->window
, self
);
452 /* this has to happen after we're in the client_list */
453 if (STRUT_EXISTS(self
->strut
))
454 screen_update_areas();
456 /* update the list hints */
459 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
462 void client_unmanage_all()
464 while (client_list
!= NULL
)
465 client_unmanage(client_list
->data
);
468 void client_unmanage(ObClient
*self
)
473 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
474 self
->title
? self
->title
: "");
476 g_assert(self
!= NULL
);
478 /* we dont want events no more. do this before hiding the frame so we
479 don't generate more events */
480 XSelectInput(ob_display
, self
->window
, NoEventMask
);
482 frame_hide(self
->frame
);
483 /* flush to send the hide to the server quickly */
486 /* ignore enter events from the unmap so it doesnt mess with the focus */
487 event_ignore_queued_enters();
489 mouse_grab_for_client(self
, FALSE
);
491 /* remove the window from our save set */
492 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
494 /* update the focus lists */
495 focus_order_remove(self
);
496 if (client_focused(self
)) {
497 /* we have to fall back here because we might not get a focus out.
498 1. we need to xselectinput off the window before we unmap it because
499 otherwise we end up getting unmapnotifies we don't want and they
500 can mess up mapping it again quickly
501 2. this means that if we unmanage from a synthetic unmapnotify, we
502 are the ones unmapped it, and causing the focusout. so we won't
503 get the focusout event.
504 3. we can't handle focusin events on the root window because they
505 come from all screens, so the focus change gets lost
507 if this ever gets removed in the future MAKE SURE to replace it
509 /- don't leave an invalid focus_client -/
512 focus_fallback(FALSE
);
515 client_list
= g_list_remove(client_list
, self
);
516 stacking_remove(self
);
517 g_hash_table_remove(window_map
, &self
->window
);
519 /* once the client is out of the list, update the struts to remove its
521 if (STRUT_EXISTS(self
->strut
))
522 screen_update_areas();
524 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
525 Destructor
*d
= it
->data
;
526 d
->func(self
, d
->data
);
529 /* tell our parent(s) that we're gone */
530 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
531 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
532 if (it
->data
!= self
)
533 ((ObClient
*)it
->data
)->transients
=
534 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
535 } else if (self
->transient_for
) { /* transient of window */
536 self
->transient_for
->transients
=
537 g_slist_remove(self
->transient_for
->transients
, self
);
540 /* tell our transients that we're gone */
541 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
542 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
543 ((ObClient
*)it
->data
)->transient_for
= NULL
;
544 client_calc_layer(it
->data
);
548 /* remove from its group */
550 group_remove(self
->group
, self
);
554 /* restore the window's original geometry so it is not lost */
558 if (self
->fullscreen
)
559 a
= self
->pre_fullscreen_area
;
560 else if (self
->max_horz
|| self
->max_vert
) {
561 if (self
->max_horz
) {
562 a
.x
= self
->pre_max_area
.x
;
563 a
.width
= self
->pre_max_area
.width
;
565 if (self
->max_vert
) {
566 a
.y
= self
->pre_max_area
.y
;
567 a
.height
= self
->pre_max_area
.height
;
571 /* give the client its border back */
572 client_toggle_border(self
, TRUE
);
574 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
575 self
->decorations
= 0; /* unmanaged windows have no decor */
577 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
580 /* reparent the window out of the frame, and free the frame */
581 frame_release_client(self
->frame
, self
);
584 if (ob_state() != OB_STATE_EXITING
) {
585 /* these values should not be persisted across a window
587 PROP_ERASE(self
->window
, net_wm_desktop
);
588 PROP_ERASE(self
->window
, net_wm_state
);
589 PROP_ERASE(self
->window
, wm_state
);
591 /* if we're left in an unmapped state, the client wont be mapped. this
592 is bad, since we will no longer be managing the window on restart */
593 XMapWindow(ob_display
, self
->window
);
596 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
598 /* free all data allocated in the client struct */
599 g_slist_free(self
->transients
);
600 for (j
= 0; j
< self
->nicons
; ++j
)
601 g_free(self
->icons
[j
].data
);
602 if (self
->nicons
> 0)
605 g_free(self
->icon_title
);
609 g_free(self
->client_machine
);
610 g_free(self
->sm_client_id
);
613 /* update the list hints */
617 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
619 ObAppSettings
*settings
= NULL
;
622 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
623 ObAppSettings
*app
= it
->data
;
625 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
626 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
627 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
628 && !strcmp(app
->name
, self
->name
)))
630 ob_debug("Window matching: %s\n", app
->name
);
631 /* Match if no role was specified in the per app setting, or if the
632 * string matches the beginning of the role, since apps like to set
633 * the role to things like browser-window-23c4b2f */
635 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
645 if (settings
->shade
!= -1)
646 self
->shaded
= !!settings
->shade
;
647 if (settings
->decor
!= -1)
648 self
->undecorated
= !settings
->decor
;
649 if (settings
->iconic
!= -1)
650 self
->iconic
= !!settings
->iconic
;
651 if (settings
->skip_pager
!= -1)
652 self
->skip_pager
= !!settings
->skip_pager
;
653 if (settings
->skip_taskbar
!= -1)
654 self
->skip_taskbar
= !!settings
->skip_taskbar
;
656 if (settings
->max_vert
!= -1)
657 self
->max_vert
= !!settings
->max_vert
;
658 if (settings
->max_horz
!= -1)
659 self
->max_horz
= !!settings
->max_horz
;
661 if (settings
->fullscreen
!= -1)
662 self
->fullscreen
= !!settings
->fullscreen
;
664 if (settings
->desktop
< screen_num_desktops
665 || settings
->desktop
== DESKTOP_ALL
)
666 self
->desktop
= settings
->desktop
;
668 if (settings
->layer
== -1) {
672 else if (settings
->layer
== 0) {
676 else if (settings
->layer
== 1) {
684 static void client_restore_session_state(ObClient
*self
)
688 if (!(it
= session_state_find(self
)))
691 self
->session
= it
->data
;
693 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
694 self
->positioned
= PPosition
;
695 if (self
->session
->w
> 0)
696 self
->area
.width
= self
->session
->w
;
697 if (self
->session
->h
> 0)
698 self
->area
.height
= self
->session
->h
;
699 XResizeWindow(ob_display
, self
->window
,
700 self
->area
.width
, self
->area
.height
);
702 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
703 self
->session
->desktop
:
704 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
705 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
707 self
->shaded
= self
->session
->shaded
;
708 self
->iconic
= self
->session
->iconic
;
709 self
->skip_pager
= self
->session
->skip_pager
;
710 self
->skip_taskbar
= self
->session
->skip_taskbar
;
711 self
->fullscreen
= self
->session
->fullscreen
;
712 self
->above
= self
->session
->above
;
713 self
->below
= self
->session
->below
;
714 self
->max_horz
= self
->session
->max_horz
;
715 self
->max_vert
= self
->session
->max_vert
;
718 static void client_restore_session_stacking(ObClient
*self
)
722 if (!self
->session
) return;
724 it
= g_list_find(session_saved_state
, self
->session
);
725 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
728 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
729 if (session_state_cmp(it
->data
, cit
->data
))
732 client_calc_layer(self
);
733 stacking_below(CLIENT_AS_WINDOW(self
),
734 CLIENT_AS_WINDOW(cit
->data
));
740 void client_move_onscreen(ObClient
*self
, gboolean rude
)
742 gint x
= self
->area
.x
;
743 gint y
= self
->area
.y
;
744 if (client_find_onscreen(self
, &x
, &y
,
745 self
->frame
->area
.width
,
746 self
->frame
->area
.height
, rude
)) {
747 client_move(self
, x
, y
);
751 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
755 gint ox
= *x
, oy
= *y
;
757 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
760 /* XXX watch for xinerama dead areas */
761 /* This makes sure windows aren't entirely outside of the screen so you
762 can't see them at all.
763 It makes sure 10% of the window is on the screen at least. At don't let
764 it move itself off the top of the screen, which would hide the titlebar
765 on you. (The user can still do this if they want too, it's only limiting
768 if (client_normal(self
)) {
769 a
= screen_area(self
->desktop
);
770 if (!self
->strut
.right
&&
771 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
772 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
773 if (!self
->strut
.bottom
&&
774 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
775 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
776 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
777 *x
= a
->x
- self
->frame
->area
.width
*9/10;
778 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
779 *y
= a
->y
- self
->frame
->area
.width
*9/10;
782 /* This here doesn't let windows even a pixel outside the screen,
783 * when called from client_manage, programs placing themselves are
784 * forced completely onscreen, while things like
785 * xterm -geometry resolution-width/2 will work fine. Trying to
786 * place it completely offscreen will be handled in the above code.
787 * Sorry for this confused comment, i am tired. */
789 /* avoid the xinerama monitor divide while we're at it,
790 * remember to fix the placement stuff to avoid it also and
791 * then remove this XXX */
792 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
793 /* dont let windows map into the strut unless they
794 are bigger than the available area */
796 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
797 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
798 *x
= a
->x
+ a
->width
- w
;
800 if (h
<= a
->height
) {
801 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
802 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
803 *y
= a
->y
+ a
->height
- h
;
807 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
810 return ox
!= *x
|| oy
!= *y
;
813 static void client_toggle_border(ObClient
*self
, gboolean show
)
815 /* adjust our idea of where the client is, based on its border. When the
816 border is removed, the client should now be considered to be in a
818 when re-adding the border to the client, the same operation needs to be
820 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
821 gint x
= oldx
, y
= oldy
;
822 switch(self
->gravity
) {
824 case NorthWestGravity
:
826 case SouthWestGravity
:
828 case NorthEastGravity
:
830 case SouthEastGravity
:
831 if (show
) x
-= self
->border_width
* 2;
832 else x
+= self
->border_width
* 2;
839 if (show
) x
-= self
->border_width
;
840 else x
+= self
->border_width
;
843 switch(self
->gravity
) {
845 case NorthWestGravity
:
847 case NorthEastGravity
:
849 case SouthWestGravity
:
851 case SouthEastGravity
:
852 if (show
) y
-= self
->border_width
* 2;
853 else y
+= self
->border_width
* 2;
860 if (show
) y
-= self
->border_width
;
861 else y
+= self
->border_width
;
868 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
870 /* set border_width to 0 because there is no border to add into
871 calculations anymore */
872 self
->border_width
= 0;
874 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
878 static void client_get_all(ObClient
*self
)
880 client_get_area(self
);
881 client_get_mwm_hints(self
);
883 /* The transient hint is used to pick a type, but the type can also affect
884 transiency (dialogs are always made transients of their group if they
885 have one). This is Havoc's idea, but it is needed to make some apps
886 work right (eg tsclient). */
887 client_update_transient_for(self
);
888 client_get_type(self
);/* this can change the mwmhints for special cases */
889 client_get_state(self
);
890 client_update_transient_for(self
);
892 client_update_wmhints(self
);
893 client_get_startup_id(self
);
894 client_get_desktop(self
);/* uses transient data/group/startup id if a
895 desktop is not specified */
896 client_get_shaped(self
);
898 client_get_layer(self
); /* if layer hasn't been specified, get it from
899 other sources if possible */
902 /* a couple type-based defaults for new windows */
904 /* this makes sure that these windows appear on all desktops */
905 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
906 self
->desktop
= DESKTOP_ALL
;
909 client_update_protocols(self
);
911 client_get_gravity(self
); /* get the attribute gravity */
912 client_update_normal_hints(self
); /* this may override the attribute
915 /* got the type, the mwmhints, the protocols, and the normal hints
916 (min/max sizes), so we're ready to set up the decorations/functions */
917 client_setup_decor_and_functions(self
);
920 client_update_sync_request_counter(self
);
922 client_get_client_machine(self
);
923 client_get_colormap(self
);
924 client_update_title(self
);
925 client_update_class(self
);
926 client_update_sm_client_id(self
);
927 client_update_strut(self
);
928 client_update_icons(self
);
929 client_update_user_time(self
);
932 static void client_get_startup_id(ObClient
*self
)
934 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
936 PROP_GETS(self
->group
->leader
,
937 net_startup_id
, utf8
, &self
->startup_id
);
940 static void client_get_area(ObClient
*self
)
942 XWindowAttributes wattrib
;
945 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
946 g_assert(ret
!= BadWindow
);
948 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
949 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
950 self
->border_width
= wattrib
.border_width
;
952 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
953 wattrib
.width
, wattrib
.height
);
956 static void client_get_desktop(ObClient
*self
)
958 guint32 d
= screen_num_desktops
; /* an always-invalid value */
960 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
961 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
962 self
->desktop
= screen_num_desktops
- 1;
966 gboolean trdesk
= FALSE
;
968 if (self
->transient_for
) {
969 if (self
->transient_for
!= OB_TRAN_GROUP
) {
970 self
->desktop
= self
->transient_for
->desktop
;
975 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
976 if (it
->data
!= self
&&
977 !((ObClient
*)it
->data
)->transient_for
) {
978 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
985 /* try get from the startup-notification protocol */
986 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
987 if (self
->desktop
>= screen_num_desktops
&&
988 self
->desktop
!= DESKTOP_ALL
)
989 self
->desktop
= screen_num_desktops
- 1;
991 /* defaults to the current desktop */
992 self
->desktop
= screen_desktop
;
997 static void client_get_layer(ObClient
*self
)
999 if (!(self
->above
|| self
->below
)) {
1001 /* apply stuff from the group */
1005 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1006 ObClient
*c
= it
->data
;
1007 if (c
!= self
&& !client_search_transient(self
, c
) &&
1008 client_normal(self
) && client_normal(c
))
1011 (c
->above
? 1 : (c
->below
? -1 : 0)));
1025 g_assert_not_reached();
1032 static void client_get_state(ObClient
*self
)
1037 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1039 for (i
= 0; i
< num
; ++i
) {
1040 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1042 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1043 self
->shaded
= TRUE
;
1044 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1045 self
->iconic
= TRUE
;
1046 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1047 self
->skip_taskbar
= TRUE
;
1048 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1049 self
->skip_pager
= TRUE
;
1050 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1051 self
->fullscreen
= TRUE
;
1052 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1053 self
->max_vert
= TRUE
;
1054 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1055 self
->max_horz
= TRUE
;
1056 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1058 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1060 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1061 self
->demands_attention
= TRUE
;
1062 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1063 self
->undecorated
= TRUE
;
1070 static void client_get_shaped(ObClient
*self
)
1072 self
->shaped
= FALSE
;
1074 if (extensions_shape
) {
1079 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1081 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1082 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1084 self
->shaped
= (s
!= 0);
1089 void client_update_transient_for(ObClient
*self
)
1092 ObClient
*target
= NULL
;
1094 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1095 self
->transient
= TRUE
;
1096 if (t
!= self
->window
) { /* cant be transient to itself! */
1097 target
= g_hash_table_lookup(window_map
, &t
);
1098 /* if this happens then we need to check for it*/
1099 g_assert(target
!= self
);
1100 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1101 /* this can happen when a dialog is a child of
1102 a dockapp, for example */
1106 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1108 Setting the transient_for to Root is actually illegal, however
1109 applications from time have done this to specify transient for
1112 Now you can do that by being a TYPE_DIALOG and not setting
1113 the transient_for hint at all on your window. But people still
1114 use Root, and Kwin is very strange in this regard.
1116 KWin 3.0 will not consider windows with transient_for set to
1117 Root as transient for their group *UNLESS* they are also modal.
1118 In that case, it will make them transient for the group. This
1119 leads to all sorts of weird behavior from KDE apps which are
1120 only tested in KWin. I'd like to follow their behavior just to
1121 make this work right with KDE stuff, but that seems wrong.
1123 if (!target
&& self
->group
) {
1124 /* not transient to a client, see if it is transient for a
1126 if (t
== RootWindow(ob_display
, ob_screen
)) {
1127 /* window is a transient for its group! */
1128 target
= OB_TRAN_GROUP
;
1132 } else if (self
->group
) {
1133 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1134 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1135 self
->type
== OB_CLIENT_TYPE_MENU
||
1136 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1138 self
->transient
= TRUE
;
1139 target
= OB_TRAN_GROUP
;
1142 self
->transient
= FALSE
;
1144 /* if anything has changed... */
1145 if (target
!= self
->transient_for
) {
1146 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1149 /* remove from old parents */
1150 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1151 ObClient
*c
= it
->data
;
1152 if (c
!= self
&& (!c
->transient_for
||
1153 c
->transient_for
!= OB_TRAN_GROUP
))
1154 c
->transients
= g_slist_remove(c
->transients
, self
);
1156 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1157 /* remove from old parent */
1158 self
->transient_for
->transients
=
1159 g_slist_remove(self
->transient_for
->transients
, self
);
1161 self
->transient_for
= target
;
1162 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1165 /* add to new parents */
1166 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1167 ObClient
*c
= it
->data
;
1168 if (c
!= self
&& (!c
->transient_for
||
1169 c
->transient_for
!= OB_TRAN_GROUP
))
1170 c
->transients
= g_slist_append(c
->transients
, self
);
1173 /* remove all transients which are in the group, that causes
1174 circlular pointer hell of doom */
1175 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1177 for (sit
= self
->transients
; sit
; sit
= next
) {
1178 next
= g_slist_next(sit
);
1179 if (sit
->data
== it
->data
)
1181 g_slist_delete_link(self
->transients
, sit
);
1184 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1185 /* add to new parent */
1186 self
->transient_for
->transients
=
1187 g_slist_append(self
->transient_for
->transients
, self
);
1192 static void client_get_mwm_hints(ObClient
*self
)
1197 self
->mwmhints
.flags
= 0; /* default to none */
1199 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1201 if (num
>= OB_MWM_ELEMENTS
) {
1202 self
->mwmhints
.flags
= hints
[0];
1203 self
->mwmhints
.functions
= hints
[1];
1204 self
->mwmhints
.decorations
= hints
[2];
1210 void client_get_type(ObClient
*self
)
1217 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1218 /* use the first value that we know about in the array */
1219 for (i
= 0; i
< num
; ++i
) {
1220 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1221 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1222 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1223 self
->type
= OB_CLIENT_TYPE_DOCK
;
1224 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1225 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1226 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1227 self
->type
= OB_CLIENT_TYPE_MENU
;
1228 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1229 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1230 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1231 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1232 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1233 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1234 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1235 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1236 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1237 /* prevent this window from getting any decor or
1239 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1240 OB_MWM_FLAG_DECORATIONS
);
1241 self
->mwmhints
.decorations
= 0;
1242 self
->mwmhints
.functions
= 0;
1244 if (self
->type
!= (ObClientType
) -1)
1245 break; /* grab the first legit type */
1250 if (self
->type
== (ObClientType
) -1) {
1251 /*the window type hint was not set, which means we either classify
1252 ourself as a normal window or a dialog, depending on if we are a
1254 if (self
->transient
)
1255 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1257 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1261 void client_update_protocols(ObClient
*self
)
1264 guint num_return
, i
;
1266 self
->focus_notify
= FALSE
;
1267 self
->delete_window
= FALSE
;
1269 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1270 for (i
= 0; i
< num_return
; ++i
) {
1271 if (proto
[i
] == prop_atoms
.wm_delete_window
)
1272 /* this means we can request the window to close */
1273 self
->delete_window
= TRUE
;
1274 else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1275 /* if this protocol is requested, then the window will be
1276 notified whenever we want it to receive focus */
1277 self
->focus_notify
= TRUE
;
1279 else if (proto
[i
] == prop_atoms
.net_wm_sync_request
)
1280 /* if this protocol is requested, then resizing the
1281 window will be synchronized between the frame and the
1283 self
->sync_request
= TRUE
;
1291 void client_update_sync_request_counter(ObClient
*self
)
1295 if (PROP_GET32(self
->window
, net_wm_sync_request_counter
, cardinal
, &i
)) {
1296 self
->sync_counter
= i
;
1298 self
->sync_counter
= None
;
1302 static void client_get_gravity(ObClient
*self
)
1304 XWindowAttributes wattrib
;
1307 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1308 g_assert(ret
!= BadWindow
);
1309 self
->gravity
= wattrib
.win_gravity
;
1312 void client_get_colormap(ObClient
*self
)
1314 XWindowAttributes wa
;
1316 if (XGetWindowAttributes(ob_display
, self
->window
, &wa
))
1317 client_update_colormap(self
, wa
.colormap
);
1320 void client_update_colormap(ObClient
*self
, Colormap colormap
)
1322 self
->colormap
= colormap
;
1325 void client_update_normal_hints(ObClient
*self
)
1329 gint oldgravity
= self
->gravity
;
1332 self
->min_ratio
= 0.0f
;
1333 self
->max_ratio
= 0.0f
;
1334 SIZE_SET(self
->size_inc
, 1, 1);
1335 SIZE_SET(self
->base_size
, 0, 0);
1336 SIZE_SET(self
->min_size
, 0, 0);
1337 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1339 /* get the hints from the window */
1340 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1341 /* normal windows can't request placement! har har
1342 if (!client_normal(self))
1344 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1346 if (size
.flags
& PWinGravity
) {
1347 self
->gravity
= size
.win_gravity
;
1349 /* if the client has a frame, i.e. has already been mapped and
1350 is changing its gravity */
1351 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1352 /* move our idea of the client's position based on its new
1354 self
->area
.x
= self
->frame
->area
.x
;
1355 self
->area
.y
= self
->frame
->area
.y
;
1356 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1360 if (size
.flags
& PAspect
) {
1361 if (size
.min_aspect
.y
)
1363 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1364 if (size
.max_aspect
.y
)
1366 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1369 if (size
.flags
& PMinSize
)
1370 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1372 if (size
.flags
& PMaxSize
)
1373 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1375 if (size
.flags
& PBaseSize
)
1376 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1378 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1379 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1383 void client_setup_decor_and_functions(ObClient
*self
)
1385 /* start with everything (cept fullscreen) */
1387 (OB_FRAME_DECOR_TITLEBAR
|
1388 OB_FRAME_DECOR_HANDLE
|
1389 OB_FRAME_DECOR_GRIPS
|
1390 OB_FRAME_DECOR_BORDER
|
1391 OB_FRAME_DECOR_ICON
|
1392 OB_FRAME_DECOR_ALLDESKTOPS
|
1393 OB_FRAME_DECOR_ICONIFY
|
1394 OB_FRAME_DECOR_MAXIMIZE
|
1395 OB_FRAME_DECOR_SHADE
|
1396 OB_FRAME_DECOR_CLOSE
);
1398 (OB_CLIENT_FUNC_RESIZE
|
1399 OB_CLIENT_FUNC_MOVE
|
1400 OB_CLIENT_FUNC_ICONIFY
|
1401 OB_CLIENT_FUNC_MAXIMIZE
|
1402 OB_CLIENT_FUNC_SHADE
|
1403 OB_CLIENT_FUNC_CLOSE
);
1405 if (!(self
->min_size
.width
< self
->max_size
.width
||
1406 self
->min_size
.height
< self
->max_size
.height
))
1407 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1409 switch (self
->type
) {
1410 case OB_CLIENT_TYPE_NORMAL
:
1411 /* normal windows retain all of the possible decorations and
1412 functionality, and are the only windows that you can fullscreen */
1413 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1416 case OB_CLIENT_TYPE_DIALOG
:
1417 case OB_CLIENT_TYPE_UTILITY
:
1418 /* these windows cannot be maximized */
1419 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1422 case OB_CLIENT_TYPE_MENU
:
1423 case OB_CLIENT_TYPE_TOOLBAR
:
1424 /* these windows get less functionality */
1425 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1428 case OB_CLIENT_TYPE_DESKTOP
:
1429 case OB_CLIENT_TYPE_DOCK
:
1430 case OB_CLIENT_TYPE_SPLASH
:
1431 /* none of these windows are manipulated by the window manager */
1432 self
->decorations
= 0;
1433 self
->functions
= 0;
1437 /* Mwm Hints are applied subtractively to what has already been chosen for
1438 decor and functionality */
1439 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1440 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1441 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1442 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1444 /* if the mwm hints request no handle or title, then all
1445 decorations are disabled, but keep the border if that's
1447 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1448 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1450 self
->decorations
= 0;
1455 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1456 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1457 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1458 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1459 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1460 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1461 /* dont let mwm hints kill any buttons
1462 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1463 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1464 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1465 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1467 /* dont let mwm hints kill the close button
1468 if (! (self->mwmhints.functions & MwmFunc_Close))
1469 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1473 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1474 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1475 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1476 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1477 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1478 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1480 /* can't maximize without moving/resizing */
1481 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1482 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1483 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1484 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1485 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1488 /* kill the handle on fully maxed windows */
1489 if (self
->max_vert
&& self
->max_horz
)
1490 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1492 /* finally, the user can have requested no decorations, which overrides
1493 everything (but doesnt give it a border if it doesnt have one) */
1494 if (self
->undecorated
) {
1495 if (config_theme_keepborder
)
1496 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1498 self
->decorations
= 0;
1501 /* if we don't have a titlebar, then we cannot shade! */
1502 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1503 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1505 /* now we need to check against rules for the client's current state */
1506 if (self
->fullscreen
) {
1507 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1508 OB_CLIENT_FUNC_FULLSCREEN
|
1509 OB_CLIENT_FUNC_ICONIFY
);
1510 self
->decorations
= 0;
1513 client_change_allowed_actions(self
);
1516 /* adjust the client's decorations, etc. */
1517 client_reconfigure(self
);
1521 static void client_change_allowed_actions(ObClient
*self
)
1526 /* desktop windows are kept on all desktops */
1527 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1528 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1530 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1531 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1532 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1533 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1534 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1535 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1536 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1537 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1538 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1539 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1540 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1541 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1542 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1543 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1544 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1547 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1549 /* make sure the window isn't breaking any rules now */
1551 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1552 if (self
->frame
) client_shade(self
, FALSE
);
1553 else self
->shaded
= FALSE
;
1555 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1556 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1557 else self
->iconic
= FALSE
;
1559 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1560 if (self
->frame
) client_fullscreen(self
, FALSE
);
1561 else self
->fullscreen
= FALSE
;
1563 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1565 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1566 else self
->max_vert
= self
->max_horz
= FALSE
;
1570 void client_reconfigure(ObClient
*self
)
1572 /* by making this pass FALSE for user, we avoid the emacs event storm where
1573 every configurenotify causes an update in its normal hints, i think this
1574 is generally what we want anyways... */
1575 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1576 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1579 void client_update_wmhints(ObClient
*self
)
1584 /* assume a window takes input if it doesnt specify */
1585 self
->can_focus
= TRUE
;
1587 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1588 if (hints
->flags
& InputHint
)
1589 self
->can_focus
= hints
->input
;
1591 /* only do this when first managing the window *AND* when we aren't
1593 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1594 if (hints
->flags
& StateHint
)
1595 self
->iconic
= hints
->initial_state
== IconicState
;
1597 if (!(hints
->flags
& WindowGroupHint
))
1598 hints
->window_group
= None
;
1600 /* did the group state change? */
1601 if (hints
->window_group
!=
1602 (self
->group
? self
->group
->leader
: None
)) {
1603 /* remove from the old group if there was one */
1604 if (self
->group
!= NULL
) {
1605 /* remove transients of the group */
1606 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1607 self
->transients
= g_slist_remove(self
->transients
,
1610 /* remove myself from parents in the group */
1611 if (self
->transient_for
== OB_TRAN_GROUP
) {
1612 for (it
= self
->group
->members
; it
;
1613 it
= g_slist_next(it
))
1615 ObClient
*c
= it
->data
;
1617 if (c
!= self
&& !c
->transient_for
)
1618 c
->transients
= g_slist_remove(c
->transients
,
1623 group_remove(self
->group
, self
);
1626 if (hints
->window_group
!= None
) {
1627 self
->group
= group_add(hints
->window_group
, self
);
1629 /* i can only have transients from the group if i am not
1631 if (!self
->transient_for
) {
1632 /* add other transients of the group that are already
1634 for (it
= self
->group
->members
; it
;
1635 it
= g_slist_next(it
))
1637 ObClient
*c
= it
->data
;
1638 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1640 g_slist_append(self
->transients
, c
);
1645 /* because the self->transient flag wont change from this call,
1646 we don't need to update the window's type and such, only its
1647 transient_for, and the transients lists of other windows in
1648 the group may be affected */
1649 client_update_transient_for(self
);
1652 /* the WM_HINTS can contain an icon */
1653 client_update_icons(self
);
1659 void client_update_title(ObClient
*self
)
1662 gchar
*visible
= NULL
;
1664 g_free(self
->title
);
1667 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1668 /* try old x stuff */
1669 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1670 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1671 if (self
->transient
) {
1673 GNOME alert windows are not given titles:
1674 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1676 data
= g_strdup("");
1678 data
= g_strdup("Unnamed Window");
1682 if (self
->client_machine
) {
1683 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1688 PROP_SETS(self
->window
, net_wm_visible_name
, visible
);
1689 self
->title
= visible
;
1692 frame_adjust_title(self
->frame
);
1694 /* update the icon title */
1696 g_free(self
->icon_title
);
1699 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1700 /* try old x stuff */
1701 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1702 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1703 data
= g_strdup(self
->title
);
1705 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1706 self
->icon_title
= data
;
1709 void client_update_class(ObClient
*self
)
1714 if (self
->name
) g_free(self
->name
);
1715 if (self
->class) g_free(self
->class);
1716 if (self
->role
) g_free(self
->role
);
1718 self
->name
= self
->class = self
->role
= NULL
;
1720 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1722 self
->name
= g_strdup(data
[0]);
1724 self
->class = g_strdup(data
[1]);
1729 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1732 if (self
->name
== NULL
) self
->name
= g_strdup("");
1733 if (self
->class == NULL
) self
->class = g_strdup("");
1734 if (self
->role
== NULL
) self
->role
= g_strdup("");
1737 void client_update_strut(ObClient
*self
)
1741 gboolean got
= FALSE
;
1744 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1748 STRUT_PARTIAL_SET(strut
,
1749 data
[0], data
[2], data
[1], data
[3],
1750 data
[4], data
[5], data
[8], data
[9],
1751 data
[6], data
[7], data
[10], data
[11]);
1757 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1763 /* use the screen's width/height */
1764 a
= screen_physical_area();
1766 STRUT_PARTIAL_SET(strut
,
1767 data
[0], data
[2], data
[1], data
[3],
1768 a
->y
, a
->y
+ a
->height
- 1,
1769 a
->x
, a
->x
+ a
->width
- 1,
1770 a
->y
, a
->y
+ a
->height
- 1,
1771 a
->x
, a
->x
+ a
->width
- 1);
1777 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1778 0, 0, 0, 0, 0, 0, 0, 0);
1780 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1781 self
->strut
= strut
;
1783 /* updating here is pointless while we're being mapped cuz we're not in
1784 the client list yet */
1786 screen_update_areas();
1790 void client_update_icons(ObClient
*self
)
1796 for (i
= 0; i
< self
->nicons
; ++i
)
1797 g_free(self
->icons
[i
].data
);
1798 if (self
->nicons
> 0)
1799 g_free(self
->icons
);
1802 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1803 /* figure out how many valid icons are in here */
1805 while (num
- i
> 2) {
1809 if (i
> num
|| w
*h
== 0) break;
1813 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1815 /* store the icons */
1817 for (j
= 0; j
< self
->nicons
; ++j
) {
1820 w
= self
->icons
[j
].width
= data
[i
++];
1821 h
= self
->icons
[j
].height
= data
[i
++];
1823 if (w
*h
== 0) continue;
1825 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1826 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1831 self
->icons
[j
].data
[t
] =
1832 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1833 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1834 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1835 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1844 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1845 if (hints
->flags
& IconPixmapHint
) {
1847 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1848 xerror_set_ignore(TRUE
);
1849 if (!RrPixmapToRGBA(ob_rr_inst
,
1851 (hints
->flags
& IconMaskHint
?
1852 hints
->icon_mask
: None
),
1853 &self
->icons
[self
->nicons
-1].width
,
1854 &self
->icons
[self
->nicons
-1].height
,
1855 &self
->icons
[self
->nicons
-1].data
)){
1856 g_free(&self
->icons
[self
->nicons
-1]);
1859 xerror_set_ignore(FALSE
);
1865 /* set the default icon onto the window
1866 in theory, this could be a race, but if a window doesn't set an icon
1867 or removes it entirely, it's not very likely it is going to set one
1868 right away afterwards */
1869 if (self
->nicons
== 0) {
1870 RrPixel32
*icon
= ob_rr_theme
->def_win_icon
;
1873 data
= g_new(gulong
, 48*48+2);
1874 data
[0] = data
[1] = 48;
1875 for (i
= 0; i
< 48*48; ++i
)
1876 data
[i
+2] = (((icon
[i
] >> RrDefaultAlphaOffset
) & 0xff) << 24) +
1877 (((icon
[i
] >> RrDefaultRedOffset
) & 0xff) << 16) +
1878 (((icon
[i
] >> RrDefaultGreenOffset
) & 0xff) << 8) +
1879 (((icon
[i
] >> RrDefaultBlueOffset
) & 0xff) << 0);
1880 PROP_SETA32(self
->window
, net_wm_icon
, cardinal
, data
, 48*48+2);
1882 } else if (self
->frame
)
1883 /* don't draw the icon empty if we're just setting one now anyways,
1884 we'll get the property change any second */
1885 frame_adjust_icon(self
->frame
);
1888 void client_update_user_time(ObClient
*self
)
1892 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1893 /* we set this every time, not just when it grows, because in practice
1894 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1895 backward we don't want all windows to stop focusing. we'll just
1896 assume noone is setting times older than the last one, cuz that
1897 would be pretty stupid anyways
1899 self
->user_time
= time
;
1902 ob_debug("window %s user time %u\n", self->title, time);
1907 static void client_get_client_machine(ObClient
*self
)
1910 gchar localhost
[128];
1912 g_free(self
->client_machine
);
1914 if (PROP_GETS(self
->window
, wm_client_machine
, locale
, &data
)) {
1915 gethostname(localhost
, 127);
1916 localhost
[127] = '\0';
1917 if (strcmp(localhost
, data
))
1918 self
->client_machine
= data
;
1922 static void client_change_wm_state(ObClient
*self
)
1927 old
= self
->wmstate
;
1929 if (self
->shaded
|| self
->iconic
|| !self
->frame
->visible
)
1930 self
->wmstate
= IconicState
;
1932 self
->wmstate
= NormalState
;
1934 if (old
!= self
->wmstate
) {
1935 PROP_MSG(self
->window
, kde_wm_change_state
,
1936 self
->wmstate
, 1, 0, 0);
1938 state
[0] = self
->wmstate
;
1940 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1944 static void client_change_state(ObClient
*self
)
1946 gulong netstate
[11];
1951 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1953 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1955 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1956 if (self
->skip_taskbar
)
1957 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1958 if (self
->skip_pager
)
1959 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1960 if (self
->fullscreen
)
1961 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1963 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1965 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1967 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1969 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1970 if (self
->demands_attention
)
1971 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1972 if (self
->undecorated
)
1973 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1974 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1977 frame_adjust_state(self
->frame
);
1980 ObClient
*client_search_focus_tree(ObClient
*self
)
1985 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1986 if (client_focused(it
->data
)) return it
->data
;
1987 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1992 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1994 if (self
->transient_for
) {
1995 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1996 return client_search_focus_tree_full(self
->transient_for
);
1999 gboolean recursed
= FALSE
;
2001 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
2002 if (!((ObClient
*)it
->data
)->transient_for
) {
2004 if ((c
= client_search_focus_tree_full(it
->data
)))
2013 /* this function checks the whole tree, the client_search_focus_tree~
2014 does not, so we need to check this window */
2015 if (client_focused(self
))
2017 return client_search_focus_tree(self
);
2020 static ObStackingLayer
calc_layer(ObClient
*self
)
2024 if (self
->fullscreen
&&
2025 (client_focused(self
) || client_search_focus_tree(self
)))
2026 l
= OB_STACKING_LAYER_FULLSCREEN
;
2027 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2028 l
= OB_STACKING_LAYER_DESKTOP
;
2029 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2030 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2031 else l
= OB_STACKING_LAYER_ABOVE
;
2033 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2034 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2035 else l
= OB_STACKING_LAYER_NORMAL
;
2040 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2041 ObStackingLayer min
, gboolean raised
)
2043 ObStackingLayer old
, own
;
2047 own
= calc_layer(self
);
2048 self
->layer
= MAX(own
, min
);
2050 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2051 client_calc_layer_recursive(it
->data
, orig
,
2053 raised
? raised
: self
->layer
!= old
);
2055 if (!raised
&& self
->layer
!= old
)
2056 if (orig
->frame
) { /* only restack if the original window is managed */
2057 stacking_remove(CLIENT_AS_WINDOW(self
));
2058 stacking_add(CLIENT_AS_WINDOW(self
));
2062 void client_calc_layer(ObClient
*self
)
2069 /* transients take on the layer of their parents */
2070 it
= client_search_all_top_parents(self
);
2072 for (; it
; it
= g_slist_next(it
))
2073 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2076 gboolean
client_should_show(ObClient
*self
)
2080 if (client_normal(self
) && screen_showing_desktop
)
2083 if (self->transient_for) {
2084 if (self->transient_for != OB_TRAN_GROUP)
2085 return client_should_show(self->transient_for);
2089 for (it = self->group->members; it; it = g_slist_next(it)) {
2090 ObClient *c = it->data;
2091 if (c != self && !c->transient_for) {
2092 if (client_should_show(c))
2099 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2105 void client_show(ObClient
*self
)
2108 if (client_should_show(self
)) {
2109 frame_show(self
->frame
);
2112 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2113 needs to be in IconicState. This includes when it is on another
2116 client_change_wm_state(self
);
2119 void client_hide(ObClient
*self
)
2121 if (!client_should_show(self
)) {
2122 frame_hide(self
->frame
);
2125 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2126 needs to be in IconicState. This includes when it is on another
2129 client_change_wm_state(self
);
2132 void client_showhide(ObClient
*self
)
2135 if (client_should_show(self
)) {
2136 frame_show(self
->frame
);
2139 frame_hide(self
->frame
);
2142 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2143 needs to be in IconicState. This includes when it is on another
2146 client_change_wm_state(self
);
2149 gboolean
client_normal(ObClient
*self
) {
2150 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2151 self
->type
== OB_CLIENT_TYPE_DOCK
||
2152 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2155 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2157 gboolean pos
= FALSE
; /* has the window's position been configured? */
2160 /* save the position, and set self->area for these to use */
2166 /* set the desktop hint, to make sure that it always exists */
2167 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
2169 /* these are in a carefully crafted order.. */
2172 self
->iconic
= FALSE
;
2173 client_iconify(self
, TRUE
, FALSE
);
2175 if (self
->fullscreen
) {
2176 self
->fullscreen
= FALSE
;
2177 client_fullscreen(self
, TRUE
);
2180 if (self
->undecorated
) {
2181 self
->undecorated
= FALSE
;
2182 client_set_undecorated(self
, TRUE
);
2185 self
->shaded
= FALSE
;
2186 client_shade(self
, TRUE
);
2188 if (self
->demands_attention
) {
2189 self
->demands_attention
= FALSE
;
2190 client_hilite(self
, TRUE
);
2193 if (self
->max_vert
&& self
->max_horz
) {
2194 self
->max_vert
= self
->max_horz
= FALSE
;
2195 client_maximize(self
, TRUE
, 0);
2197 } else if (self
->max_vert
) {
2198 self
->max_vert
= FALSE
;
2199 client_maximize(self
, TRUE
, 2);
2201 } else if (self
->max_horz
) {
2202 self
->max_horz
= FALSE
;
2203 client_maximize(self
, TRUE
, 1);
2207 /* if the client didn't get positioned yet, then do so now
2208 call client_move even if the window is not being moved anywhere, because
2209 when we reparent it and decorate it, it is getting moved and we need to
2210 be telling it so with a ConfigureNotify event.
2213 /* use the saved position */
2216 client_move(self
, x
, y
);
2219 /* nothing to do for the other states:
2228 void client_try_configure(ObClient
*self
, ObCorner anchor
,
2229 gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2230 gint
*logicalw
, gint
*logicalh
,
2233 Rect desired_area
= {*x
, *y
, *w
, *h
};
2235 /* make the frame recalculate its dimentions n shit without changing
2236 anything visible for real, this way the constraints below can work with
2237 the updated frame dimensions. */
2238 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2240 /* work within the prefered sizes given by the window */
2241 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2242 gint basew
, baseh
, minw
, minh
;
2244 /* base size is substituted with min size if not specified */
2245 if (self
->base_size
.width
|| self
->base_size
.height
) {
2246 basew
= self
->base_size
.width
;
2247 baseh
= self
->base_size
.height
;
2249 basew
= self
->min_size
.width
;
2250 baseh
= self
->min_size
.height
;
2252 /* min size is substituted with base size if not specified */
2253 if (self
->min_size
.width
|| self
->min_size
.height
) {
2254 minw
= self
->min_size
.width
;
2255 minh
= self
->min_size
.height
;
2257 minw
= self
->base_size
.width
;
2258 minh
= self
->base_size
.height
;
2261 /* if this is a user-requested resize, then check against min/max
2264 /* smaller than min size or bigger than max size? */
2265 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2266 if (*w
< minw
) *w
= minw
;
2267 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2268 if (*h
< minh
) *h
= minh
;
2273 /* keep to the increments */
2274 *w
/= self
->size_inc
.width
;
2275 *h
/= self
->size_inc
.height
;
2277 /* you cannot resize to nothing */
2278 if (basew
+ *w
< 1) *w
= 1 - basew
;
2279 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2281 /* save the logical size */
2282 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2283 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2285 *w
*= self
->size_inc
.width
;
2286 *h
*= self
->size_inc
.height
;
2291 /* adjust the height to match the width for the aspect ratios.
2292 for this, min size is not substituted for base size ever. */
2293 *w
-= self
->base_size
.width
;
2294 *h
-= self
->base_size
.height
;
2296 if (!self
->fullscreen
) {
2297 if (self
->min_ratio
)
2298 if (*h
* self
->min_ratio
> *w
) {
2299 *h
= (gint
)(*w
/ self
->min_ratio
);
2301 /* you cannot resize to nothing */
2304 *w
= (gint
)(*h
* self
->min_ratio
);
2307 if (self
->max_ratio
)
2308 if (*h
* self
->max_ratio
< *w
) {
2309 *h
= (gint
)(*w
/ self
->max_ratio
);
2311 /* you cannot resize to nothing */
2314 *w
= (gint
)(*h
* self
->min_ratio
);
2319 *w
+= self
->base_size
.width
;
2320 *h
+= self
->base_size
.height
;
2323 /* gets the frame's position */
2324 frame_client_gravity(self
->frame
, x
, y
);
2326 /* these positions are frame positions, not client positions */
2328 /* set the size and position if fullscreen */
2329 if (self
->fullscreen
) {
2333 i
= screen_find_monitor(&desired_area
);
2334 a
= screen_physical_area_monitor(i
);
2341 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2342 is entering fullscreen */
2343 } else if (self
->max_horz
|| self
->max_vert
) {
2347 i
= screen_find_monitor(&desired_area
);
2348 a
= screen_area_monitor(self
->desktop
, i
);
2350 /* set the size and position if maximized */
2351 if (self
->max_horz
) {
2353 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2355 if (self
->max_vert
) {
2357 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2360 /* maximizing is not allowed if the user can't move+resize the window
2364 /* gets the client's position */
2365 frame_frame_gravity(self
->frame
, x
, y
);
2367 /* these override the above states! if you cant move you can't move! */
2369 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2373 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2374 *w
= self
->area
.width
;
2375 *h
= self
->area
.height
;
2383 case OB_CORNER_TOPLEFT
:
2385 case OB_CORNER_TOPRIGHT
:
2386 *x
-= *w
- self
->area
.width
;
2388 case OB_CORNER_BOTTOMLEFT
:
2389 *y
-= *h
- self
->area
.height
;
2391 case OB_CORNER_BOTTOMRIGHT
:
2392 *x
-= *w
- self
->area
.width
;
2393 *y
-= *h
- self
->area
.height
;
2399 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2400 gint x
, gint y
, gint w
, gint h
,
2401 gboolean user
, gboolean final
,
2402 gboolean force_reply
)
2404 gint oldw
, oldh
, oldrx
, oldry
;
2405 gboolean send_resize_client
;
2406 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2407 guint fdecor
= self
->frame
->decorations
;
2408 gboolean fhorz
= self
->frame
->max_horz
;
2409 gint logicalw
, logicalh
;
2411 /* find the new x, y, width, and height (and logical size) */
2412 client_try_configure(self
, anchor
, &x
, &y
, &w
, &h
,
2413 &logicalw
, &logicalh
, user
);
2415 /* set the logical size if things changed */
2416 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2417 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2419 /* figure out if we moved or resized or what */
2420 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2421 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2423 oldw
= self
->area
.width
;
2424 oldh
= self
->area
.height
;
2425 RECT_SET(self
->area
, x
, y
, w
, h
);
2427 /* for app-requested resizes, always resize if 'resized' is true.
2428 for user-requested ones, only resize if final is true, or when
2429 resizing in redraw mode */
2430 send_resize_client
= ((!user
&& resized
) ||
2432 (resized
&& config_resize_redraw
))));
2434 /* if the client is enlarging, then resize the client before the frame */
2435 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
)) {
2436 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2437 frame_adjust_client_area(self
->frame
);
2440 /* find the frame's dimensions and move/resize it */
2441 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2442 moved
= resized
= TRUE
;
2443 if (moved
|| resized
)
2444 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2446 /* find the client's position relative to the root window */
2447 oldrx
= self
->root_pos
.x
;
2448 oldry
= self
->root_pos
.y
;
2449 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2450 self
->frame
->size
.left
-
2451 self
->border_width
) ||
2452 oldry
!= (signed)(self
->frame
->area
.y
+
2453 self
->frame
->size
.top
-
2454 self
->border_width
));
2456 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2460 POINT_SET(self
->root_pos
,
2461 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2463 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2464 self
->border_width
);
2466 event
.type
= ConfigureNotify
;
2467 event
.xconfigure
.display
= ob_display
;
2468 event
.xconfigure
.event
= self
->window
;
2469 event
.xconfigure
.window
= self
->window
;
2471 /* root window real coords */
2472 event
.xconfigure
.x
= self
->root_pos
.x
;
2473 event
.xconfigure
.y
= self
->root_pos
.y
;
2474 event
.xconfigure
.width
= w
;
2475 event
.xconfigure
.height
= h
;
2476 event
.xconfigure
.border_width
= 0;
2477 event
.xconfigure
.above
= self
->frame
->plate
;
2478 event
.xconfigure
.override_redirect
= FALSE
;
2479 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2480 FALSE
, StructureNotifyMask
, &event
);
2483 /* if the client is shrinking, then resize the frame before the client */
2484 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
))) {
2485 frame_adjust_client_area(self
->frame
);
2486 XResizeWindow(ob_display
, self
->window
, w
, h
);
2492 void client_fullscreen(ObClient
*self
, gboolean fs
)
2496 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2497 self
->fullscreen
== fs
) return; /* already done */
2499 self
->fullscreen
= fs
;
2500 client_change_state(self
); /* change the state hints on the client */
2501 client_calc_layer(self
); /* and adjust out layer/stacking */
2504 self
->pre_fullscreen_area
= self
->area
;
2505 /* if the window is maximized, its area isn't all that meaningful.
2506 save it's premax area instead. */
2507 if (self
->max_horz
) {
2508 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2509 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2511 if (self
->max_vert
) {
2512 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2513 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2516 /* these are not actually used cuz client_configure will set them
2517 as appropriate when the window is fullscreened */
2522 if (self
->pre_fullscreen_area
.width
> 0 &&
2523 self
->pre_fullscreen_area
.height
> 0)
2525 x
= self
->pre_fullscreen_area
.x
;
2526 y
= self
->pre_fullscreen_area
.y
;
2527 w
= self
->pre_fullscreen_area
.width
;
2528 h
= self
->pre_fullscreen_area
.height
;
2529 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2531 /* pick some fallbacks... */
2532 a
= screen_area_monitor(self
->desktop
, 0);
2533 x
= a
->x
+ a
->width
/ 4;
2534 y
= a
->y
+ a
->height
/ 4;
2540 client_setup_decor_and_functions(self
);
2542 client_move_resize(self
, x
, y
, w
, h
);
2544 /* try focus us when we go into fullscreen mode */
2548 static void client_iconify_recursive(ObClient
*self
,
2549 gboolean iconic
, gboolean curdesk
)
2552 gboolean changed
= FALSE
;
2555 if (self
->iconic
!= iconic
) {
2556 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2560 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2561 self
->iconic
= iconic
;
2563 /* update the focus lists.. iconic windows go to the bottom of
2564 the list, put the new iconic window at the 'top of the
2566 focus_order_to_top(self
);
2571 self
->iconic
= iconic
;
2574 client_set_desktop(self
, screen_desktop
, FALSE
);
2576 /* this puts it after the current focused window */
2577 focus_order_remove(self
);
2578 focus_order_add_new(self
);
2585 client_change_state(self
);
2586 client_showhide(self
);
2587 if (STRUT_EXISTS(self
->strut
))
2588 screen_update_areas();
2591 /* iconify all direct transients */
2592 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2593 if (it
->data
!= self
)
2594 if (client_is_direct_child(self
, it
->data
))
2595 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2598 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2600 /* move up the transient chain as far as possible first */
2601 self
= client_search_top_parent(self
);
2602 client_iconify_recursive(self
, iconic
, curdesk
);
2605 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2609 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2610 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2612 /* check if already done */
2614 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2615 if (dir
== 1 && self
->max_horz
) return;
2616 if (dir
== 2 && self
->max_vert
) return;
2618 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2619 if (dir
== 1 && !self
->max_horz
) return;
2620 if (dir
== 2 && !self
->max_vert
) return;
2623 /* we just tell it to configure in the same place and client_configure
2624 worries about filling the screen with the window */
2627 w
= self
->area
.width
;
2628 h
= self
->area
.height
;
2631 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2632 RECT_SET(self
->pre_max_area
,
2633 self
->area
.x
, self
->pre_max_area
.y
,
2634 self
->area
.width
, self
->pre_max_area
.height
);
2636 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2637 RECT_SET(self
->pre_max_area
,
2638 self
->pre_max_area
.x
, self
->area
.y
,
2639 self
->pre_max_area
.width
, self
->area
.height
);
2644 a
= screen_area_monitor(self
->desktop
, 0);
2645 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2646 if (self
->pre_max_area
.width
> 0) {
2647 x
= self
->pre_max_area
.x
;
2648 w
= self
->pre_max_area
.width
;
2650 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2651 0, self
->pre_max_area
.height
);
2653 /* pick some fallbacks... */
2654 x
= a
->x
+ a
->width
/ 4;
2658 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2659 if (self
->pre_max_area
.height
> 0) {
2660 y
= self
->pre_max_area
.y
;
2661 h
= self
->pre_max_area
.height
;
2663 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2664 self
->pre_max_area
.width
, 0);
2666 /* pick some fallbacks... */
2667 y
= a
->y
+ a
->height
/ 4;
2673 if (dir
== 0 || dir
== 1) /* horz */
2674 self
->max_horz
= max
;
2675 if (dir
== 0 || dir
== 2) /* vert */
2676 self
->max_vert
= max
;
2678 client_change_state(self
); /* change the state hints on the client */
2680 client_setup_decor_and_functions(self
);
2682 client_move_resize(self
, x
, y
, w
, h
);
2685 void client_shade(ObClient
*self
, gboolean shade
)
2687 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2688 shade
) || /* can't shade */
2689 self
->shaded
== shade
) return; /* already done */
2691 self
->shaded
= shade
;
2692 client_change_state(self
);
2693 client_change_wm_state(self
); /* the window is being hidden/shown */
2694 /* resize the frame to just the titlebar */
2695 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2698 void client_close(ObClient
*self
)
2702 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2704 /* in the case that the client provides no means to requesting that it
2705 close, we just kill it */
2706 if (!self
->delete_window
)
2710 XXX: itd be cool to do timeouts and shit here for killing the client's
2712 like... if the window is around after 5 seconds, then the close button
2713 turns a nice red, and if this function is called again, the client is
2717 ce
.xclient
.type
= ClientMessage
;
2718 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2719 ce
.xclient
.display
= ob_display
;
2720 ce
.xclient
.window
= self
->window
;
2721 ce
.xclient
.format
= 32;
2722 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2723 ce
.xclient
.data
.l
[1] = event_curtime
;
2724 ce
.xclient
.data
.l
[2] = 0l;
2725 ce
.xclient
.data
.l
[3] = 0l;
2726 ce
.xclient
.data
.l
[4] = 0l;
2727 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2730 void client_kill(ObClient
*self
)
2732 XKillClient(ob_display
, self
->window
);
2735 void client_hilite(ObClient
*self
, gboolean hilite
)
2737 if (self
->demands_attention
== hilite
)
2738 return; /* no change */
2740 /* don't allow focused windows to hilite */
2741 self
->demands_attention
= hilite
&& !client_focused(self
);
2742 if (self
->demands_attention
)
2743 frame_flash_start(self
->frame
);
2745 frame_flash_stop(self
->frame
);
2746 client_change_state(self
);
2749 void client_set_desktop_recursive(ObClient
*self
,
2750 guint target
, gboolean donthide
)
2755 if (target
!= self
->desktop
) {
2757 ob_debug("Setting desktop %u\n", target
+1);
2759 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2761 /* remove from the old desktop(s) */
2762 focus_order_remove(self
);
2764 old
= self
->desktop
;
2765 self
->desktop
= target
;
2766 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2767 /* the frame can display the current desktop state */
2768 frame_adjust_state(self
->frame
);
2769 /* 'move' the window to the new desktop */
2771 client_showhide(self
);
2772 /* raise if it was not already on the desktop */
2773 if (old
!= DESKTOP_ALL
)
2775 if (STRUT_EXISTS(self
->strut
))
2776 screen_update_areas();
2778 /* add to the new desktop(s) */
2779 if (config_focus_new
)
2780 focus_order_to_top(self
);
2782 focus_order_to_bottom(self
);
2785 /* move all transients */
2786 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2787 if (it
->data
!= self
)
2788 if (client_is_direct_child(self
, it
->data
))
2789 client_set_desktop_recursive(it
->data
, target
, donthide
);
2792 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2794 self
= client_search_top_parent(self
);
2795 client_set_desktop_recursive(self
, target
, donthide
);
2798 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2800 while (child
!= parent
&&
2801 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2802 child
= child
->transient_for
;
2803 return child
== parent
;
2806 ObClient
*client_search_modal_child(ObClient
*self
)
2811 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2812 ObClient
*c
= it
->data
;
2813 if ((ret
= client_search_modal_child(c
))) return ret
;
2814 if (c
->modal
) return c
;
2819 gboolean
client_validate(ObClient
*self
)
2823 XSync(ob_display
, FALSE
); /* get all events on the server */
2825 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2826 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2827 XPutBackEvent(ob_display
, &e
);
2834 void client_set_wm_state(ObClient
*self
, glong state
)
2836 if (state
== self
->wmstate
) return; /* no change */
2840 client_iconify(self
, TRUE
, TRUE
);
2843 client_iconify(self
, FALSE
, TRUE
);
2848 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2850 gboolean shaded
= self
->shaded
;
2851 gboolean fullscreen
= self
->fullscreen
;
2852 gboolean undecorated
= self
->undecorated
;
2853 gboolean max_horz
= self
->max_horz
;
2854 gboolean max_vert
= self
->max_vert
;
2855 gboolean modal
= self
->modal
;
2856 gboolean iconic
= self
->iconic
;
2857 gboolean demands_attention
= self
->demands_attention
;
2860 if (!(action
== prop_atoms
.net_wm_state_add
||
2861 action
== prop_atoms
.net_wm_state_remove
||
2862 action
== prop_atoms
.net_wm_state_toggle
))
2863 /* an invalid action was passed to the client message, ignore it */
2866 for (i
= 0; i
< 2; ++i
) {
2867 Atom state
= i
== 0 ? data1
: data2
;
2869 if (!state
) continue;
2871 /* if toggling, then pick whether we're adding or removing */
2872 if (action
== prop_atoms
.net_wm_state_toggle
) {
2873 if (state
== prop_atoms
.net_wm_state_modal
)
2874 action
= modal
? prop_atoms
.net_wm_state_remove
:
2875 prop_atoms
.net_wm_state_add
;
2876 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2877 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2878 prop_atoms
.net_wm_state_add
;
2879 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2880 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2881 prop_atoms
.net_wm_state_add
;
2882 else if (state
== prop_atoms
.net_wm_state_shaded
)
2883 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2884 prop_atoms
.net_wm_state_add
;
2885 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2886 action
= self
->skip_taskbar
?
2887 prop_atoms
.net_wm_state_remove
:
2888 prop_atoms
.net_wm_state_add
;
2889 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2890 action
= self
->skip_pager
?
2891 prop_atoms
.net_wm_state_remove
:
2892 prop_atoms
.net_wm_state_add
;
2893 else if (state
== prop_atoms
.net_wm_state_hidden
)
2894 action
= self
->iconic
?
2895 prop_atoms
.net_wm_state_remove
:
2896 prop_atoms
.net_wm_state_add
;
2897 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2898 action
= fullscreen
?
2899 prop_atoms
.net_wm_state_remove
:
2900 prop_atoms
.net_wm_state_add
;
2901 else if (state
== prop_atoms
.net_wm_state_above
)
2902 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2903 prop_atoms
.net_wm_state_add
;
2904 else if (state
== prop_atoms
.net_wm_state_below
)
2905 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2906 prop_atoms
.net_wm_state_add
;
2907 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2908 action
= self
->demands_attention
?
2909 prop_atoms
.net_wm_state_remove
:
2910 prop_atoms
.net_wm_state_add
;
2911 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2912 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2913 prop_atoms
.net_wm_state_add
;
2916 if (action
== prop_atoms
.net_wm_state_add
) {
2917 if (state
== prop_atoms
.net_wm_state_modal
) {
2919 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2921 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2923 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2925 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2926 self
->skip_taskbar
= TRUE
;
2927 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2928 self
->skip_pager
= TRUE
;
2929 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2931 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2933 } else if (state
== prop_atoms
.net_wm_state_above
) {
2935 self
->below
= FALSE
;
2936 } else if (state
== prop_atoms
.net_wm_state_below
) {
2937 self
->above
= FALSE
;
2939 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2940 demands_attention
= TRUE
;
2941 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2945 } else { /* action == prop_atoms.net_wm_state_remove */
2946 if (state
== prop_atoms
.net_wm_state_modal
) {
2948 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2950 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2952 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2954 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2955 self
->skip_taskbar
= FALSE
;
2956 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2957 self
->skip_pager
= FALSE
;
2958 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2960 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2962 } else if (state
== prop_atoms
.net_wm_state_above
) {
2963 self
->above
= FALSE
;
2964 } else if (state
== prop_atoms
.net_wm_state_below
) {
2965 self
->below
= FALSE
;
2966 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2967 demands_attention
= FALSE
;
2968 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2969 undecorated
= FALSE
;
2973 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2974 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2976 if (max_horz
== max_vert
) { /* both going the same way */
2977 client_maximize(self
, max_horz
, 0);
2979 client_maximize(self
, max_horz
, 1);
2980 client_maximize(self
, max_vert
, 2);
2984 if (max_horz
!= self
->max_horz
)
2985 client_maximize(self
, max_horz
, 1);
2987 client_maximize(self
, max_vert
, 2);
2990 /* change fullscreen state before shading, as it will affect if the window
2992 if (fullscreen
!= self
->fullscreen
)
2993 client_fullscreen(self
, fullscreen
);
2994 if (shaded
!= self
->shaded
)
2995 client_shade(self
, shaded
);
2996 if (undecorated
!= self
->undecorated
)
2997 client_set_undecorated(self
, undecorated
);
2998 if (modal
!= self
->modal
) {
2999 self
->modal
= modal
;
3000 /* when a window changes modality, then its stacking order with its
3001 transients needs to change */
3004 if (iconic
!= self
->iconic
)
3005 client_iconify(self
, iconic
, FALSE
);
3007 if (demands_attention
!= self
->demands_attention
)
3008 client_hilite(self
, demands_attention
);
3010 client_change_state(self
); /* change the hint to reflect these changes */
3013 ObClient
*client_focus_target(ObClient
*self
)
3015 ObClient
*child
= NULL
;
3017 child
= client_search_modal_child(self
);
3018 if (child
) return child
;
3022 gboolean
client_can_focus(ObClient
*self
)
3026 /* choose the correct target */
3027 self
= client_focus_target(self
);
3029 if (!self
->frame
->visible
)
3032 if (!(self
->can_focus
|| self
->focus_notify
))
3035 /* do a check to see if the window has already been unmapped or destroyed
3036 do this intelligently while watching out for unmaps we've generated
3037 (ignore_unmaps > 0) */
3038 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
3039 DestroyNotify
, &ev
)) {
3040 XPutBackEvent(ob_display
, &ev
);
3043 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
3044 UnmapNotify
, &ev
)) {
3045 if (self
->ignore_unmaps
) {
3046 self
->ignore_unmaps
--;
3048 XPutBackEvent(ob_display
, &ev
);
3056 gboolean
client_focus(ObClient
*self
)
3058 /* choose the correct target */
3059 self
= client_focus_target(self
);
3061 if (!client_can_focus(self
)) {
3062 if (!self
->frame
->visible
) {
3063 /* update the focus lists */
3064 focus_order_to_top(self
);
3069 ob_debug_type(OB_DEBUG_FOCUS
,
3070 "Focusing client \"%s\" at time %u\n",
3071 self
->title
, event_curtime
);
3073 if (self
->can_focus
) {
3074 /* This can cause a BadMatch error with CurrentTime, or if an app
3075 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3076 xerror_set_ignore(TRUE
);
3077 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3079 xerror_set_ignore(FALSE
);
3082 if (self
->focus_notify
) {
3084 ce
.xclient
.type
= ClientMessage
;
3085 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3086 ce
.xclient
.display
= ob_display
;
3087 ce
.xclient
.window
= self
->window
;
3088 ce
.xclient
.format
= 32;
3089 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3090 ce
.xclient
.data
.l
[1] = event_curtime
;
3091 ce
.xclient
.data
.l
[2] = 0l;
3092 ce
.xclient
.data
.l
[3] = 0l;
3093 ce
.xclient
.data
.l
[4] = 0l;
3094 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3098 ob_debug("%sively focusing %lx at %d\n",
3099 (self
->can_focus
? "act" : "pass"),
3100 self
->window
, (gint
) event_curtime
);
3103 /* Cause the FocusIn to come back to us. Important for desktop switches,
3104 since otherwise we'll have no FocusIn on the queue and send it off to
3105 the focus_backup. */
3106 XSync(ob_display
, FALSE
);
3110 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3112 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3114 /* XXX do some stuff here if user is false to determine if we really want
3115 to activate it or not (a parent or group member is currently
3118 ob_debug_type(OB_DEBUG_FOCUS
,
3119 "Want to activate window 0x%x with time %u (last time %u), "
3121 self
->window
, event_curtime
, last_time
,
3122 (user
? "user" : "application"));
3124 if (!user
&& event_curtime
&& last_time
&&
3125 !event_time_after(event_curtime
, last_time
))
3127 client_hilite(self
, TRUE
);
3129 if (event_curtime
!= CurrentTime
)
3130 self
->user_time
= event_curtime
;
3132 /* if using focus_delay, stop the timer now so that focus doesn't
3134 event_halt_focus_delay();
3136 if (client_normal(self
) && screen_showing_desktop
)
3137 screen_show_desktop(FALSE
);
3139 client_iconify(self
, FALSE
, here
);
3140 if (self
->desktop
!= DESKTOP_ALL
&&
3141 self
->desktop
!= screen_desktop
) {
3143 client_set_desktop(self
, screen_desktop
, FALSE
);
3145 screen_set_desktop(self
->desktop
);
3146 } else if (!self
->frame
->visible
)
3147 /* if its not visible for other reasons, then don't mess
3151 client_shade(self
, FALSE
);
3155 /* we do this as an action here. this is rather important. this is
3156 because we want the results from the focus change to take place
3157 BEFORE we go about raising the window. when a fullscreen window
3158 loses focus, we need this or else the raise wont be able to raise
3159 above the to-lose-focus fullscreen window. */
3164 void client_raise(ObClient
*self
)
3166 action_run_string("Raise", self
, CurrentTime
);
3169 void client_lower(ObClient
*self
)
3171 action_run_string("Lower", self
, CurrentTime
);
3174 gboolean
client_focused(ObClient
*self
)
3176 return self
== focus_client
;
3179 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3182 /* si is the smallest image >= req */
3183 /* li is the largest image < req */
3184 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3186 if (!self
->nicons
) {
3187 ObClientIcon
*parent
= NULL
;
3189 if (self
->transient_for
) {
3190 if (self
->transient_for
!= OB_TRAN_GROUP
)
3191 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3194 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3195 ObClient
*c
= it
->data
;
3196 if (c
!= self
&& !c
->transient_for
) {
3197 if ((parent
= client_icon_recursive(c
, w
, h
)))
3207 for (i
= 0; i
< self
->nicons
; ++i
) {
3208 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3209 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3213 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3218 if (largest
== 0) /* didnt find one smaller than the requested size */
3219 return &self
->icons
[si
];
3220 return &self
->icons
[li
];
3223 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3226 static ObClientIcon deficon
;
3228 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3229 deficon
.width
= deficon
.height
= 48;
3230 deficon
.data
= ob_rr_theme
->def_win_icon
;
3236 void client_set_layer(ObClient
*self
, gint layer
)
3240 self
->above
= FALSE
;
3241 } else if (layer
== 0) {
3242 self
->below
= self
->above
= FALSE
;
3244 self
->below
= FALSE
;
3247 client_calc_layer(self
);
3248 client_change_state(self
); /* reflect this in the state hints */
3251 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3253 if (self
->undecorated
!= undecorated
) {
3254 self
->undecorated
= undecorated
;
3255 client_setup_decor_and_functions(self
);
3256 /* Make sure the client knows it might have moved. Maybe there is a
3257 * better way of doing this so only one client_configure is sent, but
3258 * since 125 of these are sent per second when moving the window (with
3259 * user = FALSE) i doubt it matters much.
3261 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3262 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3263 client_change_state(self
); /* reflect this in the state hints */
3267 guint
client_monitor(ObClient
*self
)
3269 return screen_find_monitor(&self
->frame
->area
);
3272 ObClient
*client_search_top_parent(ObClient
*self
)
3274 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3275 client_normal(self
))
3276 self
= self
->transient_for
;
3280 GSList
*client_search_all_top_parents(ObClient
*self
)
3284 /* move up the direct transient chain as far as possible */
3285 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3286 self
= self
->transient_for
;
3288 if (!self
->transient_for
)
3289 ret
= g_slist_prepend(ret
, self
);
3293 g_assert(self
->group
);
3295 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3296 ObClient
*c
= it
->data
;
3298 if (!c
->transient_for
&& client_normal(c
))
3299 ret
= g_slist_prepend(ret
, c
);
3302 if (ret
== NULL
) /* no group parents */
3303 ret
= g_slist_prepend(ret
, self
);
3309 ObClient
*client_search_focus_parent(ObClient
*self
)
3311 if (self
->transient_for
) {
3312 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3313 if (client_focused(self
->transient_for
))
3314 return self
->transient_for
;
3318 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3319 ObClient
*c
= it
->data
;
3321 /* checking transient_for prevents infinate loops! */
3322 if (c
!= self
&& !c
->transient_for
)
3323 if (client_focused(c
))
3332 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3334 if (self
->transient_for
) {
3335 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3336 if (self
->transient_for
== search
)
3341 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3342 ObClient
*c
= it
->data
;
3344 /* checking transient_for prevents infinate loops! */
3345 if (c
!= self
&& !c
->transient_for
)
3355 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3359 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3360 if (sit
->data
== search
)
3362 if (client_search_transient(sit
->data
, search
))
3368 void client_update_sm_client_id(ObClient
*self
)
3370 g_free(self
->sm_client_id
);
3371 self
->sm_client_id
= NULL
;
3373 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3375 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3376 &self
->sm_client_id
);
3379 #define WANT_EDGE(cur, c) \
3382 if(!client_normal(cur)) \
3384 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3388 if(cur->layer < c->layer && !config_resist_layers_below) \
3391 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3392 if ((his_edge_start >= my_edge_start && \
3393 his_edge_start <= my_edge_end) || \
3394 (my_edge_start >= his_edge_start && \
3395 my_edge_start <= his_edge_end)) \
3398 /* finds the nearest edge in the given direction from the current client
3399 * note to self: the edge is the -frame- edge (the actual one), not the
3402 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3404 gint dest
, monitor_dest
;
3405 gint my_edge_start
, my_edge_end
, my_offset
;
3412 a
= screen_area(c
->desktop
);
3413 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3416 case OB_DIRECTION_NORTH
:
3417 my_edge_start
= c
->frame
->area
.x
;
3418 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3419 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3421 /* default: top of screen */
3422 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3423 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3424 /* if the monitor edge comes before the screen edge, */
3425 /* use that as the destination instead. (For xinerama) */
3426 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3427 dest
= monitor_dest
;
3429 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3430 gint his_edge_start
, his_edge_end
, his_offset
;
3431 ObClient
*cur
= it
->data
;
3435 his_edge_start
= cur
->frame
->area
.x
;
3436 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3437 his_offset
= cur
->frame
->area
.y
+
3438 (hang
? 0 : cur
->frame
->area
.height
);
3440 if(his_offset
+ 1 > my_offset
)
3443 if(his_offset
< dest
)
3446 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3449 case OB_DIRECTION_SOUTH
:
3450 my_edge_start
= c
->frame
->area
.x
;
3451 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3452 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3454 /* default: bottom of screen */
3455 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3456 monitor_dest
= monitor
->y
+ monitor
->height
-
3457 (hang
? c
->frame
->area
.height
: 0);
3458 /* if the monitor edge comes before the screen edge, */
3459 /* use that as the destination instead. (For xinerama) */
3460 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3461 dest
= monitor_dest
;
3463 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3464 gint his_edge_start
, his_edge_end
, his_offset
;
3465 ObClient
*cur
= it
->data
;
3469 his_edge_start
= cur
->frame
->area
.x
;
3470 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3471 his_offset
= cur
->frame
->area
.y
+
3472 (hang
? cur
->frame
->area
.height
: 0);
3475 if(his_offset
- 1 < my_offset
)
3478 if(his_offset
> dest
)
3481 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3484 case OB_DIRECTION_WEST
:
3485 my_edge_start
= c
->frame
->area
.y
;
3486 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3487 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3489 /* default: leftmost egde of screen */
3490 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3491 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3492 /* if the monitor edge comes before the screen edge, */
3493 /* use that as the destination instead. (For xinerama) */
3494 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3495 dest
= monitor_dest
;
3497 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3498 gint his_edge_start
, his_edge_end
, his_offset
;
3499 ObClient
*cur
= it
->data
;
3503 his_edge_start
= cur
->frame
->area
.y
;
3504 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3505 his_offset
= cur
->frame
->area
.x
+
3506 (hang
? 0 : cur
->frame
->area
.width
);
3508 if(his_offset
+ 1 > my_offset
)
3511 if(his_offset
< dest
)
3514 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3517 case OB_DIRECTION_EAST
:
3518 my_edge_start
= c
->frame
->area
.y
;
3519 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3520 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3522 /* default: rightmost edge of screen */
3523 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3524 monitor_dest
= monitor
->x
+ monitor
->width
-
3525 (hang
? c
->frame
->area
.width
: 0);
3526 /* if the monitor edge comes before the screen edge, */
3527 /* use that as the destination instead. (For xinerama) */
3528 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3529 dest
= monitor_dest
;
3531 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3532 gint his_edge_start
, his_edge_end
, his_offset
;
3533 ObClient
*cur
= it
->data
;
3537 his_edge_start
= cur
->frame
->area
.y
;
3538 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3539 his_offset
= cur
->frame
->area
.x
+
3540 (hang
? cur
->frame
->area
.width
: 0);
3542 if(his_offset
- 1 < my_offset
)
3545 if(his_offset
> dest
)
3548 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3551 case OB_DIRECTION_NORTHEAST
:
3552 case OB_DIRECTION_SOUTHEAST
:
3553 case OB_DIRECTION_NORTHWEST
:
3554 case OB_DIRECTION_SOUTHWEST
:
3555 /* not implemented */
3557 g_assert_not_reached();
3558 dest
= 0; /* suppress warning */
3563 ObClient
* client_under_pointer()
3567 ObClient
*ret
= NULL
;
3569 if (screen_pointer_pos(&x
, &y
)) {
3570 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3571 if (WINDOW_IS_CLIENT(it
->data
)) {
3572 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3573 if (c
->frame
->visible
&&
3574 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3584 gboolean
client_has_group_siblings(ObClient
*self
)
3586 return self
->group
&& self
->group
->members
->next
;