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 /* XXX figure out if it is on screen now, and be rude if it is */
759 /* get where the frame would be */
760 frame_client_gravity(self
->frame
, x
, y
, w
, h
);
762 /* XXX watch for xinerama dead areas */
763 /* This makes sure windows aren't entirely outside of the screen so you
764 can't see them at all.
765 It makes sure 10% of the window is on the screen at least. At don't let
766 it move itself off the top of the screen, which would hide the titlebar
767 on you. (The user can still do this if they want too, it's only limiting
770 if (client_normal(self
)) {
771 a
= screen_area(self
->desktop
);
772 if (!self
->strut
.right
&&
773 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
774 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
775 if (!self
->strut
.bottom
&&
776 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
777 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
778 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
779 *x
= a
->x
- self
->frame
->area
.width
*9/10;
780 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
781 *y
= a
->y
- self
->frame
->area
.width
*9/10;
784 /* This here doesn't let windows even a pixel outside the screen,
785 * when called from client_manage, programs placing themselves are
786 * forced completely onscreen, while things like
787 * xterm -geometry resolution-width/2 will work fine. Trying to
788 * place it completely offscreen will be handled in the above code.
789 * Sorry for this confused comment, i am tired. */
791 /* avoid the xinerama monitor divide while we're at it,
792 * remember to fix the placement stuff to avoid it also and
793 * then remove this XXX */
794 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
795 /* dont let windows map into the strut unless they
796 are bigger than the available area */
798 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
799 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
800 *x
= a
->x
+ a
->width
- w
;
802 if (h
<= a
->height
) {
803 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
804 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
805 *y
= a
->y
+ a
->height
- h
;
809 /* get where the client should be */
810 frame_frame_gravity(self
->frame
, x
, y
, w
, h
);
812 return ox
!= *x
|| oy
!= *y
;
815 static void client_toggle_border(ObClient
*self
, gboolean show
)
817 /* adjust our idea of where the client is, based on its border. When the
818 border is removed, the client should now be considered to be in a
820 when re-adding the border to the client, the same operation needs to be
822 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
823 gint x
= oldx
, y
= oldy
;
824 switch(self
->gravity
) {
826 case NorthWestGravity
:
828 case SouthWestGravity
:
830 case NorthEastGravity
:
832 case SouthEastGravity
:
833 if (show
) x
-= self
->border_width
* 2;
834 else x
+= self
->border_width
* 2;
841 if (show
) x
-= self
->border_width
;
842 else x
+= self
->border_width
;
845 switch(self
->gravity
) {
847 case NorthWestGravity
:
849 case NorthEastGravity
:
851 case SouthWestGravity
:
853 case SouthEastGravity
:
854 if (show
) y
-= self
->border_width
* 2;
855 else y
+= self
->border_width
* 2;
862 if (show
) y
-= self
->border_width
;
863 else y
+= self
->border_width
;
870 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
872 /* set border_width to 0 because there is no border to add into
873 calculations anymore */
874 self
->border_width
= 0;
876 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
880 static void client_get_all(ObClient
*self
)
882 client_get_area(self
);
883 client_get_mwm_hints(self
);
885 /* The transient hint is used to pick a type, but the type can also affect
886 transiency (dialogs are always made transients of their group if they
887 have one). This is Havoc's idea, but it is needed to make some apps
888 work right (eg tsclient). */
889 client_update_transient_for(self
);
890 client_get_type(self
);/* this can change the mwmhints for special cases */
891 client_get_state(self
);
892 client_update_transient_for(self
);
894 client_update_wmhints(self
);
895 client_get_startup_id(self
);
896 client_get_desktop(self
);/* uses transient data/group/startup id if a
897 desktop is not specified */
898 client_get_shaped(self
);
900 client_get_layer(self
); /* if layer hasn't been specified, get it from
901 other sources if possible */
904 /* a couple type-based defaults for new windows */
906 /* this makes sure that these windows appear on all desktops */
907 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
908 self
->desktop
= DESKTOP_ALL
;
911 client_update_protocols(self
);
913 client_get_gravity(self
); /* get the attribute gravity */
914 client_update_normal_hints(self
); /* this may override the attribute
917 /* got the type, the mwmhints, the protocols, and the normal hints
918 (min/max sizes), so we're ready to set up the decorations/functions */
919 client_setup_decor_and_functions(self
);
922 client_update_sync_request_counter(self
);
924 client_get_client_machine(self
);
925 client_get_colormap(self
);
926 client_update_title(self
);
927 client_update_class(self
);
928 client_update_sm_client_id(self
);
929 client_update_strut(self
);
930 client_update_icons(self
);
931 client_update_user_time(self
);
934 static void client_get_startup_id(ObClient
*self
)
936 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
938 PROP_GETS(self
->group
->leader
,
939 net_startup_id
, utf8
, &self
->startup_id
);
942 static void client_get_area(ObClient
*self
)
944 XWindowAttributes wattrib
;
947 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
948 g_assert(ret
!= BadWindow
);
950 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
951 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
952 self
->border_width
= wattrib
.border_width
;
954 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
955 wattrib
.width
, wattrib
.height
);
958 static void client_get_desktop(ObClient
*self
)
960 guint32 d
= screen_num_desktops
; /* an always-invalid value */
962 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
963 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
964 self
->desktop
= screen_num_desktops
- 1;
968 gboolean trdesk
= FALSE
;
970 if (self
->transient_for
) {
971 if (self
->transient_for
!= OB_TRAN_GROUP
) {
972 self
->desktop
= self
->transient_for
->desktop
;
977 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
978 if (it
->data
!= self
&&
979 !((ObClient
*)it
->data
)->transient_for
) {
980 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
987 /* try get from the startup-notification protocol */
988 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
989 if (self
->desktop
>= screen_num_desktops
&&
990 self
->desktop
!= DESKTOP_ALL
)
991 self
->desktop
= screen_num_desktops
- 1;
993 /* defaults to the current desktop */
994 self
->desktop
= screen_desktop
;
999 static void client_get_layer(ObClient
*self
)
1001 if (!(self
->above
|| self
->below
)) {
1003 /* apply stuff from the group */
1007 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1008 ObClient
*c
= it
->data
;
1009 if (c
!= self
&& !client_search_transient(self
, c
) &&
1010 client_normal(self
) && client_normal(c
))
1013 (c
->above
? 1 : (c
->below
? -1 : 0)));
1027 g_assert_not_reached();
1034 static void client_get_state(ObClient
*self
)
1039 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1041 for (i
= 0; i
< num
; ++i
) {
1042 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1044 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1045 self
->shaded
= TRUE
;
1046 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1047 self
->iconic
= TRUE
;
1048 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1049 self
->skip_taskbar
= TRUE
;
1050 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1051 self
->skip_pager
= TRUE
;
1052 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1053 self
->fullscreen
= TRUE
;
1054 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1055 self
->max_vert
= TRUE
;
1056 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1057 self
->max_horz
= TRUE
;
1058 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1060 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1062 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1063 self
->demands_attention
= TRUE
;
1064 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1065 self
->undecorated
= TRUE
;
1072 static void client_get_shaped(ObClient
*self
)
1074 self
->shaped
= FALSE
;
1076 if (extensions_shape
) {
1081 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1083 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1084 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1086 self
->shaped
= (s
!= 0);
1091 void client_update_transient_for(ObClient
*self
)
1094 ObClient
*target
= NULL
;
1096 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1097 self
->transient
= TRUE
;
1098 if (t
!= self
->window
) { /* cant be transient to itself! */
1099 target
= g_hash_table_lookup(window_map
, &t
);
1100 /* if this happens then we need to check for it*/
1101 g_assert(target
!= self
);
1102 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1103 /* this can happen when a dialog is a child of
1104 a dockapp, for example */
1108 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1110 Setting the transient_for to Root is actually illegal, however
1111 applications from time have done this to specify transient for
1114 Now you can do that by being a TYPE_DIALOG and not setting
1115 the transient_for hint at all on your window. But people still
1116 use Root, and Kwin is very strange in this regard.
1118 KWin 3.0 will not consider windows with transient_for set to
1119 Root as transient for their group *UNLESS* they are also modal.
1120 In that case, it will make them transient for the group. This
1121 leads to all sorts of weird behavior from KDE apps which are
1122 only tested in KWin. I'd like to follow their behavior just to
1123 make this work right with KDE stuff, but that seems wrong.
1125 if (!target
&& self
->group
) {
1126 /* not transient to a client, see if it is transient for a
1128 if (t
== RootWindow(ob_display
, ob_screen
)) {
1129 /* window is a transient for its group! */
1130 target
= OB_TRAN_GROUP
;
1134 } else if (self
->group
) {
1135 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1136 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1137 self
->type
== OB_CLIENT_TYPE_MENU
||
1138 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1140 self
->transient
= TRUE
;
1141 target
= OB_TRAN_GROUP
;
1144 self
->transient
= FALSE
;
1146 /* if anything has changed... */
1147 if (target
!= self
->transient_for
) {
1148 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1151 /* remove from old parents */
1152 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1153 ObClient
*c
= it
->data
;
1154 if (c
!= self
&& (!c
->transient_for
||
1155 c
->transient_for
!= OB_TRAN_GROUP
))
1156 c
->transients
= g_slist_remove(c
->transients
, self
);
1158 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1159 /* remove from old parent */
1160 self
->transient_for
->transients
=
1161 g_slist_remove(self
->transient_for
->transients
, self
);
1163 self
->transient_for
= target
;
1164 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1167 /* add to new parents */
1168 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1169 ObClient
*c
= it
->data
;
1170 if (c
!= self
&& (!c
->transient_for
||
1171 c
->transient_for
!= OB_TRAN_GROUP
))
1172 c
->transients
= g_slist_append(c
->transients
, self
);
1175 /* remove all transients which are in the group, that causes
1176 circlular pointer hell of doom */
1177 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1179 for (sit
= self
->transients
; sit
; sit
= next
) {
1180 next
= g_slist_next(sit
);
1181 if (sit
->data
== it
->data
)
1183 g_slist_delete_link(self
->transients
, sit
);
1186 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1187 /* add to new parent */
1188 self
->transient_for
->transients
=
1189 g_slist_append(self
->transient_for
->transients
, self
);
1194 static void client_get_mwm_hints(ObClient
*self
)
1199 self
->mwmhints
.flags
= 0; /* default to none */
1201 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1203 if (num
>= OB_MWM_ELEMENTS
) {
1204 self
->mwmhints
.flags
= hints
[0];
1205 self
->mwmhints
.functions
= hints
[1];
1206 self
->mwmhints
.decorations
= hints
[2];
1212 void client_get_type(ObClient
*self
)
1219 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1220 /* use the first value that we know about in the array */
1221 for (i
= 0; i
< num
; ++i
) {
1222 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1223 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1224 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1225 self
->type
= OB_CLIENT_TYPE_DOCK
;
1226 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1227 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1228 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1229 self
->type
= OB_CLIENT_TYPE_MENU
;
1230 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1231 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1232 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1233 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1234 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1235 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1236 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1237 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1238 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1239 /* prevent this window from getting any decor or
1241 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1242 OB_MWM_FLAG_DECORATIONS
);
1243 self
->mwmhints
.decorations
= 0;
1244 self
->mwmhints
.functions
= 0;
1246 if (self
->type
!= (ObClientType
) -1)
1247 break; /* grab the first legit type */
1252 if (self
->type
== (ObClientType
) -1) {
1253 /*the window type hint was not set, which means we either classify
1254 ourself as a normal window or a dialog, depending on if we are a
1256 if (self
->transient
)
1257 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1259 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1263 void client_update_protocols(ObClient
*self
)
1266 guint num_return
, i
;
1268 self
->focus_notify
= FALSE
;
1269 self
->delete_window
= FALSE
;
1271 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1272 for (i
= 0; i
< num_return
; ++i
) {
1273 if (proto
[i
] == prop_atoms
.wm_delete_window
)
1274 /* this means we can request the window to close */
1275 self
->delete_window
= TRUE
;
1276 else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1277 /* if this protocol is requested, then the window will be
1278 notified whenever we want it to receive focus */
1279 self
->focus_notify
= TRUE
;
1281 else if (proto
[i
] == prop_atoms
.net_wm_sync_request
)
1282 /* if this protocol is requested, then resizing the
1283 window will be synchronized between the frame and the
1285 self
->sync_request
= TRUE
;
1293 void client_update_sync_request_counter(ObClient
*self
)
1297 if (PROP_GET32(self
->window
, net_wm_sync_request_counter
, cardinal
, &i
)) {
1298 self
->sync_counter
= i
;
1300 self
->sync_counter
= None
;
1304 static void client_get_gravity(ObClient
*self
)
1306 XWindowAttributes wattrib
;
1309 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1310 g_assert(ret
!= BadWindow
);
1311 self
->gravity
= wattrib
.win_gravity
;
1314 void client_get_colormap(ObClient
*self
)
1316 XWindowAttributes wa
;
1318 if (XGetWindowAttributes(ob_display
, self
->window
, &wa
))
1319 client_update_colormap(self
, wa
.colormap
);
1322 void client_update_colormap(ObClient
*self
, Colormap colormap
)
1324 self
->colormap
= colormap
;
1327 void client_update_normal_hints(ObClient
*self
)
1331 gint oldgravity
= self
->gravity
;
1334 self
->min_ratio
= 0.0f
;
1335 self
->max_ratio
= 0.0f
;
1336 SIZE_SET(self
->size_inc
, 1, 1);
1337 SIZE_SET(self
->base_size
, 0, 0);
1338 SIZE_SET(self
->min_size
, 0, 0);
1339 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1341 /* get the hints from the window */
1342 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1343 /* normal windows can't request placement! har har
1344 if (!client_normal(self))
1346 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1348 if (size
.flags
& PWinGravity
) {
1349 self
->gravity
= size
.win_gravity
;
1351 /* if the client has a frame, i.e. has already been mapped and
1352 is changing its gravity */
1353 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1354 /* move our idea of the client's position based on its new
1356 client_convert_gravity(self
, oldgravity
,
1357 &self
->area
.x
, &self
->area
.y
,
1358 self
->area
.width
, self
->area
.height
);
1362 if (size
.flags
& PAspect
) {
1363 if (size
.min_aspect
.y
)
1365 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1366 if (size
.max_aspect
.y
)
1368 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1371 if (size
.flags
& PMinSize
)
1372 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1374 if (size
.flags
& PMaxSize
)
1375 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1377 if (size
.flags
& PBaseSize
)
1378 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1380 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1381 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1385 void client_setup_decor_and_functions(ObClient
*self
)
1387 /* start with everything (cept fullscreen) */
1389 (OB_FRAME_DECOR_TITLEBAR
|
1390 OB_FRAME_DECOR_HANDLE
|
1391 OB_FRAME_DECOR_GRIPS
|
1392 OB_FRAME_DECOR_BORDER
|
1393 OB_FRAME_DECOR_ICON
|
1394 OB_FRAME_DECOR_ALLDESKTOPS
|
1395 OB_FRAME_DECOR_ICONIFY
|
1396 OB_FRAME_DECOR_MAXIMIZE
|
1397 OB_FRAME_DECOR_SHADE
|
1398 OB_FRAME_DECOR_CLOSE
);
1400 (OB_CLIENT_FUNC_RESIZE
|
1401 OB_CLIENT_FUNC_MOVE
|
1402 OB_CLIENT_FUNC_ICONIFY
|
1403 OB_CLIENT_FUNC_MAXIMIZE
|
1404 OB_CLIENT_FUNC_SHADE
|
1405 OB_CLIENT_FUNC_CLOSE
);
1407 if (!(self
->min_size
.width
< self
->max_size
.width
||
1408 self
->min_size
.height
< self
->max_size
.height
))
1409 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1411 switch (self
->type
) {
1412 case OB_CLIENT_TYPE_NORMAL
:
1413 /* normal windows retain all of the possible decorations and
1414 functionality, and are the only windows that you can fullscreen */
1415 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1418 case OB_CLIENT_TYPE_DIALOG
:
1419 case OB_CLIENT_TYPE_UTILITY
:
1420 /* these windows cannot be maximized */
1421 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1424 case OB_CLIENT_TYPE_MENU
:
1425 case OB_CLIENT_TYPE_TOOLBAR
:
1426 /* these windows get less functionality */
1427 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1430 case OB_CLIENT_TYPE_DESKTOP
:
1431 case OB_CLIENT_TYPE_DOCK
:
1432 case OB_CLIENT_TYPE_SPLASH
:
1433 /* none of these windows are manipulated by the window manager */
1434 self
->decorations
= 0;
1435 self
->functions
= 0;
1439 /* Mwm Hints are applied subtractively to what has already been chosen for
1440 decor and functionality */
1441 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1442 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1443 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1444 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1446 /* if the mwm hints request no handle or title, then all
1447 decorations are disabled, but keep the border if that's
1449 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1450 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1452 self
->decorations
= 0;
1457 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1458 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1459 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1460 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1461 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1462 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1463 /* dont let mwm hints kill any buttons
1464 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1465 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1466 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1467 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1469 /* dont let mwm hints kill the close button
1470 if (! (self->mwmhints.functions & MwmFunc_Close))
1471 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1475 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1476 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1477 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1478 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1479 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1480 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1482 /* can't maximize without moving/resizing */
1483 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1484 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1485 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1486 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1487 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1490 /* kill the handle on fully maxed windows */
1491 if (self
->max_vert
&& self
->max_horz
)
1492 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1494 /* finally, the user can have requested no decorations, which overrides
1495 everything (but doesnt give it a border if it doesnt have one) */
1496 if (self
->undecorated
) {
1497 if (config_theme_keepborder
)
1498 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1500 self
->decorations
= 0;
1503 /* if we don't have a titlebar, then we cannot shade! */
1504 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1505 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1507 /* now we need to check against rules for the client's current state */
1508 if (self
->fullscreen
) {
1509 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1510 OB_CLIENT_FUNC_FULLSCREEN
|
1511 OB_CLIENT_FUNC_ICONIFY
);
1512 self
->decorations
= 0;
1515 client_change_allowed_actions(self
);
1518 /* adjust the client's decorations, etc. */
1519 client_reconfigure(self
);
1523 static void client_change_allowed_actions(ObClient
*self
)
1528 /* desktop windows are kept on all desktops */
1529 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1530 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1532 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1533 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1534 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1535 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1536 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1537 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1538 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1539 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1540 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1541 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1542 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1543 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1544 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1545 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1546 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1549 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1551 /* make sure the window isn't breaking any rules now */
1553 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1554 if (self
->frame
) client_shade(self
, FALSE
);
1555 else self
->shaded
= FALSE
;
1557 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1558 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1559 else self
->iconic
= FALSE
;
1561 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1562 if (self
->frame
) client_fullscreen(self
, FALSE
);
1563 else self
->fullscreen
= FALSE
;
1565 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1567 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1568 else self
->max_vert
= self
->max_horz
= FALSE
;
1572 void client_reconfigure(ObClient
*self
)
1574 /* by making this pass FALSE for user, we avoid the emacs event storm where
1575 every configurenotify causes an update in its normal hints, i think this
1576 is generally what we want anyways... */
1577 client_configure(self
, self
->area
.x
, self
->area
.y
,
1578 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1581 void client_update_wmhints(ObClient
*self
)
1586 /* assume a window takes input if it doesnt specify */
1587 self
->can_focus
= TRUE
;
1589 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1590 if (hints
->flags
& InputHint
)
1591 self
->can_focus
= hints
->input
;
1593 /* only do this when first managing the window *AND* when we aren't
1595 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1596 if (hints
->flags
& StateHint
)
1597 self
->iconic
= hints
->initial_state
== IconicState
;
1599 if (!(hints
->flags
& WindowGroupHint
))
1600 hints
->window_group
= None
;
1602 /* did the group state change? */
1603 if (hints
->window_group
!=
1604 (self
->group
? self
->group
->leader
: None
)) {
1605 /* remove from the old group if there was one */
1606 if (self
->group
!= NULL
) {
1607 /* remove transients of the group */
1608 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1609 self
->transients
= g_slist_remove(self
->transients
,
1612 /* remove myself from parents in the group */
1613 if (self
->transient_for
== OB_TRAN_GROUP
) {
1614 for (it
= self
->group
->members
; it
;
1615 it
= g_slist_next(it
))
1617 ObClient
*c
= it
->data
;
1619 if (c
!= self
&& !c
->transient_for
)
1620 c
->transients
= g_slist_remove(c
->transients
,
1625 group_remove(self
->group
, self
);
1628 if (hints
->window_group
!= None
) {
1629 self
->group
= group_add(hints
->window_group
, self
);
1631 /* i can only have transients from the group if i am not
1633 if (!self
->transient_for
) {
1634 /* add other transients of the group that are already
1636 for (it
= self
->group
->members
; it
;
1637 it
= g_slist_next(it
))
1639 ObClient
*c
= it
->data
;
1640 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1642 g_slist_append(self
->transients
, c
);
1647 /* because the self->transient flag wont change from this call,
1648 we don't need to update the window's type and such, only its
1649 transient_for, and the transients lists of other windows in
1650 the group may be affected */
1651 client_update_transient_for(self
);
1654 /* the WM_HINTS can contain an icon */
1655 client_update_icons(self
);
1661 void client_update_title(ObClient
*self
)
1664 gchar
*visible
= NULL
;
1666 g_free(self
->title
);
1669 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1670 /* try old x stuff */
1671 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1672 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1673 if (self
->transient
) {
1675 GNOME alert windows are not given titles:
1676 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1678 data
= g_strdup("");
1680 data
= g_strdup("Unnamed Window");
1684 if (self
->client_machine
) {
1685 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1690 PROP_SETS(self
->window
, net_wm_visible_name
, visible
);
1691 self
->title
= visible
;
1694 frame_adjust_title(self
->frame
);
1696 /* update the icon title */
1698 g_free(self
->icon_title
);
1701 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1702 /* try old x stuff */
1703 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1704 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1705 data
= g_strdup(self
->title
);
1707 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1708 self
->icon_title
= data
;
1711 void client_update_class(ObClient
*self
)
1716 if (self
->name
) g_free(self
->name
);
1717 if (self
->class) g_free(self
->class);
1718 if (self
->role
) g_free(self
->role
);
1720 self
->name
= self
->class = self
->role
= NULL
;
1722 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1724 self
->name
= g_strdup(data
[0]);
1726 self
->class = g_strdup(data
[1]);
1731 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1734 if (self
->name
== NULL
) self
->name
= g_strdup("");
1735 if (self
->class == NULL
) self
->class = g_strdup("");
1736 if (self
->role
== NULL
) self
->role
= g_strdup("");
1739 void client_update_strut(ObClient
*self
)
1743 gboolean got
= FALSE
;
1746 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1750 STRUT_PARTIAL_SET(strut
,
1751 data
[0], data
[2], data
[1], data
[3],
1752 data
[4], data
[5], data
[8], data
[9],
1753 data
[6], data
[7], data
[10], data
[11]);
1759 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1765 /* use the screen's width/height */
1766 a
= screen_physical_area();
1768 STRUT_PARTIAL_SET(strut
,
1769 data
[0], data
[2], data
[1], data
[3],
1770 a
->y
, a
->y
+ a
->height
- 1,
1771 a
->x
, a
->x
+ a
->width
- 1,
1772 a
->y
, a
->y
+ a
->height
- 1,
1773 a
->x
, a
->x
+ a
->width
- 1);
1779 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1780 0, 0, 0, 0, 0, 0, 0, 0);
1782 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1783 self
->strut
= strut
;
1785 /* updating here is pointless while we're being mapped cuz we're not in
1786 the client list yet */
1788 screen_update_areas();
1792 void client_update_icons(ObClient
*self
)
1798 for (i
= 0; i
< self
->nicons
; ++i
)
1799 g_free(self
->icons
[i
].data
);
1800 if (self
->nicons
> 0)
1801 g_free(self
->icons
);
1804 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1805 /* figure out how many valid icons are in here */
1807 while (num
- i
> 2) {
1811 if (i
> num
|| w
*h
== 0) break;
1815 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1817 /* store the icons */
1819 for (j
= 0; j
< self
->nicons
; ++j
) {
1822 w
= self
->icons
[j
].width
= data
[i
++];
1823 h
= self
->icons
[j
].height
= data
[i
++];
1825 if (w
*h
== 0) continue;
1827 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1828 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1833 self
->icons
[j
].data
[t
] =
1834 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1835 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1836 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1837 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1846 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1847 if (hints
->flags
& IconPixmapHint
) {
1849 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1850 xerror_set_ignore(TRUE
);
1851 if (!RrPixmapToRGBA(ob_rr_inst
,
1853 (hints
->flags
& IconMaskHint
?
1854 hints
->icon_mask
: None
),
1855 &self
->icons
[self
->nicons
-1].width
,
1856 &self
->icons
[self
->nicons
-1].height
,
1857 &self
->icons
[self
->nicons
-1].data
)){
1858 g_free(&self
->icons
[self
->nicons
-1]);
1861 xerror_set_ignore(FALSE
);
1867 /* set the default icon onto the window
1868 in theory, this could be a race, but if a window doesn't set an icon
1869 or removes it entirely, it's not very likely it is going to set one
1870 right away afterwards */
1871 if (self
->nicons
== 0) {
1872 RrPixel32
*icon
= ob_rr_theme
->def_win_icon
;
1875 data
= g_new(gulong
, 48*48+2);
1876 data
[0] = data
[1] = 48;
1877 for (i
= 0; i
< 48*48; ++i
)
1878 data
[i
+2] = (((icon
[i
] >> RrDefaultAlphaOffset
) & 0xff) << 24) +
1879 (((icon
[i
] >> RrDefaultRedOffset
) & 0xff) << 16) +
1880 (((icon
[i
] >> RrDefaultGreenOffset
) & 0xff) << 8) +
1881 (((icon
[i
] >> RrDefaultBlueOffset
) & 0xff) << 0);
1882 PROP_SETA32(self
->window
, net_wm_icon
, cardinal
, data
, 48*48+2);
1884 } else if (self
->frame
)
1885 /* don't draw the icon empty if we're just setting one now anyways,
1886 we'll get the property change any second */
1887 frame_adjust_icon(self
->frame
);
1890 void client_update_user_time(ObClient
*self
)
1894 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1895 /* we set this every time, not just when it grows, because in practice
1896 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1897 backward we don't want all windows to stop focusing. we'll just
1898 assume noone is setting times older than the last one, cuz that
1899 would be pretty stupid anyways
1901 self
->user_time
= time
;
1904 ob_debug("window %s user time %u\n", self->title, time);
1909 static void client_get_client_machine(ObClient
*self
)
1912 gchar localhost
[128];
1914 g_free(self
->client_machine
);
1916 if (PROP_GETS(self
->window
, wm_client_machine
, locale
, &data
)) {
1917 gethostname(localhost
, 127);
1918 localhost
[127] = '\0';
1919 if (strcmp(localhost
, data
))
1920 self
->client_machine
= data
;
1924 static void client_change_wm_state(ObClient
*self
)
1929 old
= self
->wmstate
;
1931 if (self
->shaded
|| self
->iconic
|| !self
->frame
->visible
)
1932 self
->wmstate
= IconicState
;
1934 self
->wmstate
= NormalState
;
1936 if (old
!= self
->wmstate
) {
1937 PROP_MSG(self
->window
, kde_wm_change_state
,
1938 self
->wmstate
, 1, 0, 0);
1940 state
[0] = self
->wmstate
;
1942 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1946 static void client_change_state(ObClient
*self
)
1948 gulong netstate
[11];
1953 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1955 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1957 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1958 if (self
->skip_taskbar
)
1959 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1960 if (self
->skip_pager
)
1961 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1962 if (self
->fullscreen
)
1963 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1965 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1967 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1969 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1971 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1972 if (self
->demands_attention
)
1973 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1974 if (self
->undecorated
)
1975 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1976 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1979 frame_adjust_state(self
->frame
);
1982 ObClient
*client_search_focus_tree(ObClient
*self
)
1987 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1988 if (client_focused(it
->data
)) return it
->data
;
1989 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1994 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1996 if (self
->transient_for
) {
1997 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1998 return client_search_focus_tree_full(self
->transient_for
);
2001 gboolean recursed
= FALSE
;
2003 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
2004 if (!((ObClient
*)it
->data
)->transient_for
) {
2006 if ((c
= client_search_focus_tree_full(it
->data
)))
2015 /* this function checks the whole tree, the client_search_focus_tree~
2016 does not, so we need to check this window */
2017 if (client_focused(self
))
2019 return client_search_focus_tree(self
);
2022 static ObStackingLayer
calc_layer(ObClient
*self
)
2026 if (self
->fullscreen
&&
2027 (client_focused(self
) || client_search_focus_tree(self
)))
2028 l
= OB_STACKING_LAYER_FULLSCREEN
;
2029 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2030 l
= OB_STACKING_LAYER_DESKTOP
;
2031 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2032 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2033 else l
= OB_STACKING_LAYER_ABOVE
;
2035 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2036 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2037 else l
= OB_STACKING_LAYER_NORMAL
;
2042 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2043 ObStackingLayer min
, gboolean raised
)
2045 ObStackingLayer old
, own
;
2049 own
= calc_layer(self
);
2050 self
->layer
= MAX(own
, min
);
2052 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2053 client_calc_layer_recursive(it
->data
, orig
,
2055 raised
? raised
: self
->layer
!= old
);
2057 if (!raised
&& self
->layer
!= old
)
2058 if (orig
->frame
) { /* only restack if the original window is managed */
2059 stacking_remove(CLIENT_AS_WINDOW(self
));
2060 stacking_add(CLIENT_AS_WINDOW(self
));
2064 void client_calc_layer(ObClient
*self
)
2071 /* transients take on the layer of their parents */
2072 it
= client_search_all_top_parents(self
);
2074 for (; it
; it
= g_slist_next(it
))
2075 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2078 gboolean
client_should_show(ObClient
*self
)
2082 if (client_normal(self
) && screen_showing_desktop
)
2085 if (self->transient_for) {
2086 if (self->transient_for != OB_TRAN_GROUP)
2087 return client_should_show(self->transient_for);
2091 for (it = self->group->members; it; it = g_slist_next(it)) {
2092 ObClient *c = it->data;
2093 if (c != self && !c->transient_for) {
2094 if (client_should_show(c))
2101 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2107 void client_show(ObClient
*self
)
2110 if (client_should_show(self
)) {
2111 frame_show(self
->frame
);
2114 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2115 needs to be in IconicState. This includes when it is on another
2118 client_change_wm_state(self
);
2121 void client_hide(ObClient
*self
)
2123 if (!client_should_show(self
)) {
2124 frame_hide(self
->frame
);
2127 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2128 needs to be in IconicState. This includes when it is on another
2131 client_change_wm_state(self
);
2134 void client_showhide(ObClient
*self
)
2137 if (client_should_show(self
)) {
2138 frame_show(self
->frame
);
2141 frame_hide(self
->frame
);
2144 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2145 needs to be in IconicState. This includes when it is on another
2148 client_change_wm_state(self
);
2151 gboolean
client_normal(ObClient
*self
) {
2152 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2153 self
->type
== OB_CLIENT_TYPE_DOCK
||
2154 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2157 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2159 gboolean pos
= FALSE
; /* has the window's position been configured? */
2162 /* save the position, and set self->area for these to use */
2168 /* set the desktop hint, to make sure that it always exists */
2169 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
2171 /* these are in a carefully crafted order.. */
2174 self
->iconic
= FALSE
;
2175 client_iconify(self
, TRUE
, FALSE
);
2177 if (self
->fullscreen
) {
2178 self
->fullscreen
= FALSE
;
2179 client_fullscreen(self
, TRUE
);
2182 if (self
->undecorated
) {
2183 self
->undecorated
= FALSE
;
2184 client_set_undecorated(self
, TRUE
);
2187 self
->shaded
= FALSE
;
2188 client_shade(self
, TRUE
);
2190 if (self
->demands_attention
) {
2191 self
->demands_attention
= FALSE
;
2192 client_hilite(self
, TRUE
);
2195 if (self
->max_vert
&& self
->max_horz
) {
2196 self
->max_vert
= self
->max_horz
= FALSE
;
2197 client_maximize(self
, TRUE
, 0);
2199 } else if (self
->max_vert
) {
2200 self
->max_vert
= FALSE
;
2201 client_maximize(self
, TRUE
, 2);
2203 } else if (self
->max_horz
) {
2204 self
->max_horz
= FALSE
;
2205 client_maximize(self
, TRUE
, 1);
2209 /* if the client didn't get positioned yet, then do so now
2210 call client_move even if the window is not being moved anywhere, because
2211 when we reparent it and decorate it, it is getting moved and we need to
2212 be telling it so with a ConfigureNotify event.
2215 /* use the saved position */
2218 client_move(self
, x
, y
);
2221 /* nothing to do for the other states:
2230 void client_convert_gravity(ObClient
*self
, gint gravity
, gint
*x
, gint
*y
,
2233 gint oldg
= self
->gravity
;
2235 /* get the frame's position from the requested stuff */
2236 self
->gravity
= gravity
;
2237 frame_client_gravity(self
->frame
, x
, y
, w
, h
);
2238 self
->gravity
= oldg
;
2240 /* get the client's position in its true gravity from that */
2241 frame_frame_gravity(self
->frame
, x
, y
, w
, h
);
2244 void client_try_configure(ObClient
*self
, gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2245 gint
*logicalw
, gint
*logicalh
,
2248 Rect desired_area
= {*x
, *y
, *w
, *h
};
2250 /* make the frame recalculate its dimentions n shit without changing
2251 anything visible for real, this way the constraints below can work with
2252 the updated frame dimensions. */
2253 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2255 /* work within the prefered sizes given by the window */
2256 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2257 gint basew
, baseh
, minw
, minh
;
2259 /* base size is substituted with min size if not specified */
2260 if (self
->base_size
.width
|| self
->base_size
.height
) {
2261 basew
= self
->base_size
.width
;
2262 baseh
= self
->base_size
.height
;
2264 basew
= self
->min_size
.width
;
2265 baseh
= self
->min_size
.height
;
2267 /* min size is substituted with base size if not specified */
2268 if (self
->min_size
.width
|| self
->min_size
.height
) {
2269 minw
= self
->min_size
.width
;
2270 minh
= self
->min_size
.height
;
2272 minw
= self
->base_size
.width
;
2273 minh
= self
->base_size
.height
;
2276 /* if this is a user-requested resize, then check against min/max
2279 /* smaller than min size or bigger than max size? */
2280 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2281 if (*w
< minw
) *w
= minw
;
2282 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2283 if (*h
< minh
) *h
= minh
;
2288 /* keep to the increments */
2289 *w
/= self
->size_inc
.width
;
2290 *h
/= self
->size_inc
.height
;
2292 /* you cannot resize to nothing */
2293 if (basew
+ *w
< 1) *w
= 1 - basew
;
2294 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2296 /* save the logical size */
2297 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2298 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2300 *w
*= self
->size_inc
.width
;
2301 *h
*= self
->size_inc
.height
;
2306 /* adjust the height to match the width for the aspect ratios.
2307 for this, min size is not substituted for base size ever. */
2308 *w
-= self
->base_size
.width
;
2309 *h
-= self
->base_size
.height
;
2311 if (!self
->fullscreen
) {
2312 if (self
->min_ratio
)
2313 if (*h
* self
->min_ratio
> *w
) {
2314 *h
= (gint
)(*w
/ self
->min_ratio
);
2316 /* you cannot resize to nothing */
2319 *w
= (gint
)(*h
* self
->min_ratio
);
2322 if (self
->max_ratio
)
2323 if (*h
* self
->max_ratio
< *w
) {
2324 *h
= (gint
)(*w
/ self
->max_ratio
);
2326 /* you cannot resize to nothing */
2329 *w
= (gint
)(*h
* self
->min_ratio
);
2334 *w
+= self
->base_size
.width
;
2335 *h
+= self
->base_size
.height
;
2338 /* gets the frame's position */
2339 frame_client_gravity(self
->frame
, x
, y
, *w
, *h
);
2341 /* these positions are frame positions, not client positions */
2343 /* set the size and position if fullscreen */
2344 if (self
->fullscreen
) {
2348 i
= screen_find_monitor(&desired_area
);
2349 a
= screen_physical_area_monitor(i
);
2356 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2357 is entering fullscreen */
2358 } else if (self
->max_horz
|| self
->max_vert
) {
2362 i
= screen_find_monitor(&desired_area
);
2363 a
= screen_area_monitor(self
->desktop
, i
);
2365 /* set the size and position if maximized */
2366 if (self
->max_horz
) {
2368 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2370 if (self
->max_vert
) {
2372 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2375 /* maximizing is not allowed if the user can't move+resize the window
2379 /* gets the client's position */
2380 frame_frame_gravity(self
->frame
, x
, y
, *w
, *h
);
2382 /* these override the above states! if you cant move you can't move! */
2384 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2388 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2389 *w
= self
->area
.width
;
2390 *h
= self
->area
.height
;
2399 void client_configure_full(ObClient
*self
, gint x
, gint y
, gint w
, gint h
,
2400 gboolean user
, gboolean final
,
2401 gboolean force_reply
)
2403 gint oldw
, oldh
, oldrx
, oldry
;
2404 gboolean send_resize_client
;
2405 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2406 guint fdecor
= self
->frame
->decorations
;
2407 gboolean fhorz
= self
->frame
->max_horz
;
2408 gint logicalw
, logicalh
;
2410 /* find the new x, y, width, and height (and logical size) */
2411 client_try_configure(self
, &x
, &y
, &w
, &h
, &logicalw
, &logicalh
, user
);
2413 /* set the logical size if things changed */
2414 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2415 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2417 /* figure out if we moved or resized or what */
2418 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2419 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2421 oldw
= self
->area
.width
;
2422 oldh
= self
->area
.height
;
2423 RECT_SET(self
->area
, x
, y
, w
, h
);
2425 /* for app-requested resizes, always resize if 'resized' is true.
2426 for user-requested ones, only resize if final is true, or when
2427 resizing in redraw mode */
2428 send_resize_client
= ((!user
&& resized
) ||
2430 (resized
&& config_resize_redraw
))));
2432 /* if the client is enlarging, then resize the client before the frame */
2433 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2434 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2436 /* find the frame's dimensions and move/resize it */
2437 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2438 moved
= resized
= TRUE
;
2439 if (moved
|| resized
)
2440 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2442 /* find the client's position relative to the root window */
2443 oldrx
= self
->root_pos
.x
;
2444 oldry
= self
->root_pos
.y
;
2445 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2446 self
->frame
->size
.left
-
2447 self
->border_width
) ||
2448 oldry
!= (signed)(self
->frame
->area
.y
+
2449 self
->frame
->size
.top
-
2450 self
->border_width
));
2452 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2456 POINT_SET(self
->root_pos
,
2457 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2459 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2460 self
->border_width
);
2462 event
.type
= ConfigureNotify
;
2463 event
.xconfigure
.display
= ob_display
;
2464 event
.xconfigure
.event
= self
->window
;
2465 event
.xconfigure
.window
= self
->window
;
2467 /* root window real coords */
2468 event
.xconfigure
.x
= self
->root_pos
.x
;
2469 event
.xconfigure
.y
= self
->root_pos
.y
;
2470 event
.xconfigure
.width
= w
;
2471 event
.xconfigure
.height
= h
;
2472 event
.xconfigure
.border_width
= 0;
2473 event
.xconfigure
.above
= self
->frame
->plate
;
2474 event
.xconfigure
.override_redirect
= FALSE
;
2475 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2476 FALSE
, StructureNotifyMask
, &event
);
2479 /* if the client is shrinking, then resize the frame before the client */
2480 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2481 XResizeWindow(ob_display
, self
->window
, w
, h
);
2486 void client_fullscreen(ObClient
*self
, gboolean fs
)
2490 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2491 self
->fullscreen
== fs
) return; /* already done */
2493 self
->fullscreen
= fs
;
2494 client_change_state(self
); /* change the state hints on the client */
2495 client_calc_layer(self
); /* and adjust out layer/stacking */
2498 self
->pre_fullscreen_area
= self
->area
;
2499 /* if the window is maximized, its area isn't all that meaningful.
2500 save it's premax area instead. */
2501 if (self
->max_horz
) {
2502 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2503 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2505 if (self
->max_vert
) {
2506 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2507 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2510 /* these are not actually used cuz client_configure will set them
2511 as appropriate when the window is fullscreened */
2516 if (self
->pre_fullscreen_area
.width
> 0 &&
2517 self
->pre_fullscreen_area
.height
> 0)
2519 x
= self
->pre_fullscreen_area
.x
;
2520 y
= self
->pre_fullscreen_area
.y
;
2521 w
= self
->pre_fullscreen_area
.width
;
2522 h
= self
->pre_fullscreen_area
.height
;
2523 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2525 /* pick some fallbacks... */
2526 a
= screen_area_monitor(self
->desktop
, 0);
2527 x
= a
->x
+ a
->width
/ 4;
2528 y
= a
->y
+ a
->height
/ 4;
2534 client_setup_decor_and_functions(self
);
2536 client_move_resize(self
, x
, y
, w
, h
);
2538 /* try focus us when we go into fullscreen mode */
2542 static void client_iconify_recursive(ObClient
*self
,
2543 gboolean iconic
, gboolean curdesk
)
2546 gboolean changed
= FALSE
;
2549 if (self
->iconic
!= iconic
) {
2550 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2554 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2555 self
->iconic
= iconic
;
2557 /* update the focus lists.. iconic windows go to the bottom of
2558 the list, put the new iconic window at the 'top of the
2560 focus_order_to_top(self
);
2565 self
->iconic
= iconic
;
2568 client_set_desktop(self
, screen_desktop
, FALSE
);
2570 /* this puts it after the current focused window */
2571 focus_order_remove(self
);
2572 focus_order_add_new(self
);
2579 client_change_state(self
);
2580 client_showhide(self
);
2581 if (STRUT_EXISTS(self
->strut
))
2582 screen_update_areas();
2585 /* iconify all direct transients */
2586 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2587 if (it
->data
!= self
)
2588 if (client_is_direct_child(self
, it
->data
))
2589 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2592 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2594 /* move up the transient chain as far as possible first */
2595 self
= client_search_top_parent(self
);
2596 client_iconify_recursive(self
, iconic
, curdesk
);
2599 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2603 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2604 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2606 /* check if already done */
2608 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2609 if (dir
== 1 && self
->max_horz
) return;
2610 if (dir
== 2 && self
->max_vert
) return;
2612 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2613 if (dir
== 1 && !self
->max_horz
) return;
2614 if (dir
== 2 && !self
->max_vert
) return;
2617 /* we just tell it to configure in the same place and client_configure
2618 worries about filling the screen with the window */
2621 w
= self
->area
.width
;
2622 h
= self
->area
.height
;
2625 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2626 RECT_SET(self
->pre_max_area
,
2627 self
->area
.x
, self
->pre_max_area
.y
,
2628 self
->area
.width
, self
->pre_max_area
.height
);
2630 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2631 RECT_SET(self
->pre_max_area
,
2632 self
->pre_max_area
.x
, self
->area
.y
,
2633 self
->pre_max_area
.width
, self
->area
.height
);
2638 a
= screen_area_monitor(self
->desktop
, 0);
2639 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2640 if (self
->pre_max_area
.width
> 0) {
2641 x
= self
->pre_max_area
.x
;
2642 w
= self
->pre_max_area
.width
;
2644 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2645 0, self
->pre_max_area
.height
);
2647 /* pick some fallbacks... */
2648 x
= a
->x
+ a
->width
/ 4;
2652 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2653 if (self
->pre_max_area
.height
> 0) {
2654 y
= self
->pre_max_area
.y
;
2655 h
= self
->pre_max_area
.height
;
2657 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2658 self
->pre_max_area
.width
, 0);
2660 /* pick some fallbacks... */
2661 y
= a
->y
+ a
->height
/ 4;
2667 if (dir
== 0 || dir
== 1) /* horz */
2668 self
->max_horz
= max
;
2669 if (dir
== 0 || dir
== 2) /* vert */
2670 self
->max_vert
= max
;
2672 client_change_state(self
); /* change the state hints on the client */
2674 client_setup_decor_and_functions(self
);
2676 client_move_resize(self
, x
, y
, w
, h
);
2679 void client_shade(ObClient
*self
, gboolean shade
)
2681 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2682 shade
) || /* can't shade */
2683 self
->shaded
== shade
) return; /* already done */
2685 self
->shaded
= shade
;
2686 client_change_state(self
);
2687 client_change_wm_state(self
); /* the window is being hidden/shown */
2688 /* resize the frame to just the titlebar */
2689 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2692 void client_close(ObClient
*self
)
2696 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2698 /* in the case that the client provides no means to requesting that it
2699 close, we just kill it */
2700 if (!self
->delete_window
)
2704 XXX: itd be cool to do timeouts and shit here for killing the client's
2706 like... if the window is around after 5 seconds, then the close button
2707 turns a nice red, and if this function is called again, the client is
2711 ce
.xclient
.type
= ClientMessage
;
2712 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2713 ce
.xclient
.display
= ob_display
;
2714 ce
.xclient
.window
= self
->window
;
2715 ce
.xclient
.format
= 32;
2716 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2717 ce
.xclient
.data
.l
[1] = event_curtime
;
2718 ce
.xclient
.data
.l
[2] = 0l;
2719 ce
.xclient
.data
.l
[3] = 0l;
2720 ce
.xclient
.data
.l
[4] = 0l;
2721 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2724 void client_kill(ObClient
*self
)
2726 XKillClient(ob_display
, self
->window
);
2729 void client_hilite(ObClient
*self
, gboolean hilite
)
2731 if (self
->demands_attention
== hilite
)
2732 return; /* no change */
2734 /* don't allow focused windows to hilite */
2735 self
->demands_attention
= hilite
&& !client_focused(self
);
2736 if (self
->demands_attention
)
2737 frame_flash_start(self
->frame
);
2739 frame_flash_stop(self
->frame
);
2740 client_change_state(self
);
2743 void client_set_desktop_recursive(ObClient
*self
,
2744 guint target
, gboolean donthide
)
2749 if (target
!= self
->desktop
) {
2751 ob_debug("Setting desktop %u\n", target
+1);
2753 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2755 /* remove from the old desktop(s) */
2756 focus_order_remove(self
);
2758 old
= self
->desktop
;
2759 self
->desktop
= target
;
2760 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2761 /* the frame can display the current desktop state */
2762 frame_adjust_state(self
->frame
);
2763 /* 'move' the window to the new desktop */
2765 client_showhide(self
);
2766 /* raise if it was not already on the desktop */
2767 if (old
!= DESKTOP_ALL
)
2769 if (STRUT_EXISTS(self
->strut
))
2770 screen_update_areas();
2772 /* add to the new desktop(s) */
2773 if (config_focus_new
)
2774 focus_order_to_top(self
);
2776 focus_order_to_bottom(self
);
2779 /* move all transients */
2780 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2781 if (it
->data
!= self
)
2782 if (client_is_direct_child(self
, it
->data
))
2783 client_set_desktop_recursive(it
->data
, target
, donthide
);
2786 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2788 self
= client_search_top_parent(self
);
2789 client_set_desktop_recursive(self
, target
, donthide
);
2792 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2794 while (child
!= parent
&&
2795 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2796 child
= child
->transient_for
;
2797 return child
== parent
;
2800 ObClient
*client_search_modal_child(ObClient
*self
)
2805 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2806 ObClient
*c
= it
->data
;
2807 if ((ret
= client_search_modal_child(c
))) return ret
;
2808 if (c
->modal
) return c
;
2813 gboolean
client_validate(ObClient
*self
)
2817 XSync(ob_display
, FALSE
); /* get all events on the server */
2819 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2820 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2821 XPutBackEvent(ob_display
, &e
);
2828 void client_set_wm_state(ObClient
*self
, glong state
)
2830 if (state
== self
->wmstate
) return; /* no change */
2834 client_iconify(self
, TRUE
, TRUE
);
2837 client_iconify(self
, FALSE
, TRUE
);
2842 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2844 gboolean shaded
= self
->shaded
;
2845 gboolean fullscreen
= self
->fullscreen
;
2846 gboolean undecorated
= self
->undecorated
;
2847 gboolean max_horz
= self
->max_horz
;
2848 gboolean max_vert
= self
->max_vert
;
2849 gboolean modal
= self
->modal
;
2850 gboolean iconic
= self
->iconic
;
2851 gboolean demands_attention
= self
->demands_attention
;
2854 if (!(action
== prop_atoms
.net_wm_state_add
||
2855 action
== prop_atoms
.net_wm_state_remove
||
2856 action
== prop_atoms
.net_wm_state_toggle
))
2857 /* an invalid action was passed to the client message, ignore it */
2860 for (i
= 0; i
< 2; ++i
) {
2861 Atom state
= i
== 0 ? data1
: data2
;
2863 if (!state
) continue;
2865 /* if toggling, then pick whether we're adding or removing */
2866 if (action
== prop_atoms
.net_wm_state_toggle
) {
2867 if (state
== prop_atoms
.net_wm_state_modal
)
2868 action
= modal
? prop_atoms
.net_wm_state_remove
:
2869 prop_atoms
.net_wm_state_add
;
2870 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2871 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2872 prop_atoms
.net_wm_state_add
;
2873 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2874 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2875 prop_atoms
.net_wm_state_add
;
2876 else if (state
== prop_atoms
.net_wm_state_shaded
)
2877 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2878 prop_atoms
.net_wm_state_add
;
2879 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2880 action
= self
->skip_taskbar
?
2881 prop_atoms
.net_wm_state_remove
:
2882 prop_atoms
.net_wm_state_add
;
2883 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2884 action
= self
->skip_pager
?
2885 prop_atoms
.net_wm_state_remove
:
2886 prop_atoms
.net_wm_state_add
;
2887 else if (state
== prop_atoms
.net_wm_state_hidden
)
2888 action
= self
->iconic
?
2889 prop_atoms
.net_wm_state_remove
:
2890 prop_atoms
.net_wm_state_add
;
2891 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2892 action
= fullscreen
?
2893 prop_atoms
.net_wm_state_remove
:
2894 prop_atoms
.net_wm_state_add
;
2895 else if (state
== prop_atoms
.net_wm_state_above
)
2896 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2897 prop_atoms
.net_wm_state_add
;
2898 else if (state
== prop_atoms
.net_wm_state_below
)
2899 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2900 prop_atoms
.net_wm_state_add
;
2901 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2902 action
= self
->demands_attention
?
2903 prop_atoms
.net_wm_state_remove
:
2904 prop_atoms
.net_wm_state_add
;
2905 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2906 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2907 prop_atoms
.net_wm_state_add
;
2910 if (action
== prop_atoms
.net_wm_state_add
) {
2911 if (state
== prop_atoms
.net_wm_state_modal
) {
2913 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2915 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2917 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2919 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2920 self
->skip_taskbar
= TRUE
;
2921 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2922 self
->skip_pager
= TRUE
;
2923 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2925 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2927 } else if (state
== prop_atoms
.net_wm_state_above
) {
2929 self
->below
= FALSE
;
2930 } else if (state
== prop_atoms
.net_wm_state_below
) {
2931 self
->above
= FALSE
;
2933 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2934 demands_attention
= TRUE
;
2935 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2939 } else { /* action == prop_atoms.net_wm_state_remove */
2940 if (state
== prop_atoms
.net_wm_state_modal
) {
2942 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2944 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2946 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2948 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2949 self
->skip_taskbar
= FALSE
;
2950 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2951 self
->skip_pager
= FALSE
;
2952 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2954 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2956 } else if (state
== prop_atoms
.net_wm_state_above
) {
2957 self
->above
= FALSE
;
2958 } else if (state
== prop_atoms
.net_wm_state_below
) {
2959 self
->below
= FALSE
;
2960 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2961 demands_attention
= FALSE
;
2962 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2963 undecorated
= FALSE
;
2967 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2968 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2970 if (max_horz
== max_vert
) { /* both going the same way */
2971 client_maximize(self
, max_horz
, 0);
2973 client_maximize(self
, max_horz
, 1);
2974 client_maximize(self
, max_vert
, 2);
2978 if (max_horz
!= self
->max_horz
)
2979 client_maximize(self
, max_horz
, 1);
2981 client_maximize(self
, max_vert
, 2);
2984 /* change fullscreen state before shading, as it will affect if the window
2986 if (fullscreen
!= self
->fullscreen
)
2987 client_fullscreen(self
, fullscreen
);
2988 if (shaded
!= self
->shaded
)
2989 client_shade(self
, shaded
);
2990 if (undecorated
!= self
->undecorated
)
2991 client_set_undecorated(self
, undecorated
);
2992 if (modal
!= self
->modal
) {
2993 self
->modal
= modal
;
2994 /* when a window changes modality, then its stacking order with its
2995 transients needs to change */
2998 if (iconic
!= self
->iconic
)
2999 client_iconify(self
, iconic
, FALSE
);
3001 if (demands_attention
!= self
->demands_attention
)
3002 client_hilite(self
, demands_attention
);
3004 client_change_state(self
); /* change the hint to reflect these changes */
3007 ObClient
*client_focus_target(ObClient
*self
)
3009 ObClient
*child
= NULL
;
3011 child
= client_search_modal_child(self
);
3012 if (child
) return child
;
3016 gboolean
client_can_focus(ObClient
*self
)
3020 /* choose the correct target */
3021 self
= client_focus_target(self
);
3023 if (!self
->frame
->visible
)
3026 if (!(self
->can_focus
|| self
->focus_notify
))
3029 /* do a check to see if the window has already been unmapped or destroyed
3030 do this intelligently while watching out for unmaps we've generated
3031 (ignore_unmaps > 0) */
3032 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
3033 DestroyNotify
, &ev
)) {
3034 XPutBackEvent(ob_display
, &ev
);
3037 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
3038 UnmapNotify
, &ev
)) {
3039 if (self
->ignore_unmaps
) {
3040 self
->ignore_unmaps
--;
3042 XPutBackEvent(ob_display
, &ev
);
3050 gboolean
client_focus(ObClient
*self
)
3052 /* choose the correct target */
3053 self
= client_focus_target(self
);
3055 if (!client_can_focus(self
)) {
3056 if (!self
->frame
->visible
) {
3057 /* update the focus lists */
3058 focus_order_to_top(self
);
3063 ob_debug_type(OB_DEBUG_FOCUS
,
3064 "Focusing client \"%s\" at time %u\n",
3065 self
->title
, event_curtime
);
3067 if (self
->can_focus
) {
3068 /* This can cause a BadMatch error with CurrentTime, or if an app
3069 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3070 xerror_set_ignore(TRUE
);
3071 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3073 xerror_set_ignore(FALSE
);
3076 if (self
->focus_notify
) {
3078 ce
.xclient
.type
= ClientMessage
;
3079 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3080 ce
.xclient
.display
= ob_display
;
3081 ce
.xclient
.window
= self
->window
;
3082 ce
.xclient
.format
= 32;
3083 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3084 ce
.xclient
.data
.l
[1] = event_curtime
;
3085 ce
.xclient
.data
.l
[2] = 0l;
3086 ce
.xclient
.data
.l
[3] = 0l;
3087 ce
.xclient
.data
.l
[4] = 0l;
3088 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3092 ob_debug("%sively focusing %lx at %d\n",
3093 (self
->can_focus
? "act" : "pass"),
3094 self
->window
, (gint
) event_curtime
);
3097 /* Cause the FocusIn to come back to us. Important for desktop switches,
3098 since otherwise we'll have no FocusIn on the queue and send it off to
3099 the focus_backup. */
3100 XSync(ob_display
, FALSE
);
3104 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3106 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3108 /* XXX do some stuff here if user is false to determine if we really want
3109 to activate it or not (a parent or group member is currently
3112 ob_debug_type(OB_DEBUG_FOCUS
,
3113 "Want to activate window 0x%x with time %u (last time %u), "
3115 self
->window
, event_curtime
, last_time
,
3116 (user
? "user" : "application"));
3118 if (!user
&& event_curtime
&& last_time
&&
3119 !event_time_after(event_curtime
, last_time
))
3121 client_hilite(self
, TRUE
);
3123 if (event_curtime
!= CurrentTime
)
3124 self
->user_time
= event_curtime
;
3126 /* if using focus_delay, stop the timer now so that focus doesn't
3128 event_halt_focus_delay();
3130 if (client_normal(self
) && screen_showing_desktop
)
3131 screen_show_desktop(FALSE
);
3133 client_iconify(self
, FALSE
, here
);
3134 if (self
->desktop
!= DESKTOP_ALL
&&
3135 self
->desktop
!= screen_desktop
) {
3137 client_set_desktop(self
, screen_desktop
, FALSE
);
3139 screen_set_desktop(self
->desktop
);
3140 } else if (!self
->frame
->visible
)
3141 /* if its not visible for other reasons, then don't mess
3145 client_shade(self
, FALSE
);
3149 /* we do this as an action here. this is rather important. this is
3150 because we want the results from the focus change to take place
3151 BEFORE we go about raising the window. when a fullscreen window
3152 loses focus, we need this or else the raise wont be able to raise
3153 above the to-lose-focus fullscreen window. */
3158 void client_raise(ObClient
*self
)
3160 action_run_string("Raise", self
, CurrentTime
);
3163 void client_lower(ObClient
*self
)
3165 action_run_string("Lower", self
, CurrentTime
);
3168 gboolean
client_focused(ObClient
*self
)
3170 return self
== focus_client
;
3173 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3176 /* si is the smallest image >= req */
3177 /* li is the largest image < req */
3178 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3180 if (!self
->nicons
) {
3181 ObClientIcon
*parent
= NULL
;
3183 if (self
->transient_for
) {
3184 if (self
->transient_for
!= OB_TRAN_GROUP
)
3185 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3188 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3189 ObClient
*c
= it
->data
;
3190 if (c
!= self
&& !c
->transient_for
) {
3191 if ((parent
= client_icon_recursive(c
, w
, h
)))
3201 for (i
= 0; i
< self
->nicons
; ++i
) {
3202 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3203 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3207 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3212 if (largest
== 0) /* didnt find one smaller than the requested size */
3213 return &self
->icons
[si
];
3214 return &self
->icons
[li
];
3217 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3220 static ObClientIcon deficon
;
3222 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3223 deficon
.width
= deficon
.height
= 48;
3224 deficon
.data
= ob_rr_theme
->def_win_icon
;
3230 void client_set_layer(ObClient
*self
, gint layer
)
3234 self
->above
= FALSE
;
3235 } else if (layer
== 0) {
3236 self
->below
= self
->above
= FALSE
;
3238 self
->below
= FALSE
;
3241 client_calc_layer(self
);
3242 client_change_state(self
); /* reflect this in the state hints */
3245 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3247 if (self
->undecorated
!= undecorated
) {
3248 self
->undecorated
= undecorated
;
3249 client_setup_decor_and_functions(self
);
3250 /* Make sure the client knows it might have moved. Maybe there is a
3251 * better way of doing this so only one client_configure is sent, but
3252 * since 125 of these are sent per second when moving the window (with
3253 * user = FALSE) i doubt it matters much.
3255 client_configure(self
, self
->area
.x
, self
->area
.y
,
3256 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3257 client_change_state(self
); /* reflect this in the state hints */
3261 guint
client_monitor(ObClient
*self
)
3263 return screen_find_monitor(&self
->frame
->area
);
3266 ObClient
*client_search_top_parent(ObClient
*self
)
3268 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3269 client_normal(self
))
3270 self
= self
->transient_for
;
3274 GSList
*client_search_all_top_parents(ObClient
*self
)
3278 /* move up the direct transient chain as far as possible */
3279 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3280 self
= self
->transient_for
;
3282 if (!self
->transient_for
)
3283 ret
= g_slist_prepend(ret
, self
);
3287 g_assert(self
->group
);
3289 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3290 ObClient
*c
= it
->data
;
3292 if (!c
->transient_for
&& client_normal(c
))
3293 ret
= g_slist_prepend(ret
, c
);
3296 if (ret
== NULL
) /* no group parents */
3297 ret
= g_slist_prepend(ret
, self
);
3303 ObClient
*client_search_focus_parent(ObClient
*self
)
3305 if (self
->transient_for
) {
3306 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3307 if (client_focused(self
->transient_for
))
3308 return self
->transient_for
;
3312 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3313 ObClient
*c
= it
->data
;
3315 /* checking transient_for prevents infinate loops! */
3316 if (c
!= self
&& !c
->transient_for
)
3317 if (client_focused(c
))
3326 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3328 if (self
->transient_for
) {
3329 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3330 if (self
->transient_for
== search
)
3335 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3336 ObClient
*c
= it
->data
;
3338 /* checking transient_for prevents infinate loops! */
3339 if (c
!= self
&& !c
->transient_for
)
3349 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3353 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3354 if (sit
->data
== search
)
3356 if (client_search_transient(sit
->data
, search
))
3362 void client_update_sm_client_id(ObClient
*self
)
3364 g_free(self
->sm_client_id
);
3365 self
->sm_client_id
= NULL
;
3367 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3369 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3370 &self
->sm_client_id
);
3373 #define WANT_EDGE(cur, c) \
3376 if(!client_normal(cur)) \
3378 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3382 if(cur->layer < c->layer && !config_resist_layers_below) \
3385 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3386 if ((his_edge_start >= my_edge_start && \
3387 his_edge_start <= my_edge_end) || \
3388 (my_edge_start >= his_edge_start && \
3389 my_edge_start <= his_edge_end)) \
3392 /* finds the nearest edge in the given direction from the current client
3393 * note to self: the edge is the -frame- edge (the actual one), not the
3396 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3398 gint dest
, monitor_dest
;
3399 gint my_edge_start
, my_edge_end
, my_offset
;
3406 a
= screen_area(c
->desktop
);
3407 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3410 case OB_DIRECTION_NORTH
:
3411 my_edge_start
= c
->frame
->area
.x
;
3412 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3413 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3415 /* default: top of screen */
3416 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3417 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3418 /* if the monitor edge comes before the screen edge, */
3419 /* use that as the destination instead. (For xinerama) */
3420 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3421 dest
= monitor_dest
;
3423 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3424 gint his_edge_start
, his_edge_end
, his_offset
;
3425 ObClient
*cur
= it
->data
;
3429 his_edge_start
= cur
->frame
->area
.x
;
3430 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3431 his_offset
= cur
->frame
->area
.y
+
3432 (hang
? 0 : cur
->frame
->area
.height
);
3434 if(his_offset
+ 1 > my_offset
)
3437 if(his_offset
< dest
)
3440 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3443 case OB_DIRECTION_SOUTH
:
3444 my_edge_start
= c
->frame
->area
.x
;
3445 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3446 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3448 /* default: bottom of screen */
3449 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3450 monitor_dest
= monitor
->y
+ monitor
->height
-
3451 (hang
? c
->frame
->area
.height
: 0);
3452 /* if the monitor edge comes before the screen edge, */
3453 /* use that as the destination instead. (For xinerama) */
3454 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3455 dest
= monitor_dest
;
3457 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3458 gint his_edge_start
, his_edge_end
, his_offset
;
3459 ObClient
*cur
= it
->data
;
3463 his_edge_start
= cur
->frame
->area
.x
;
3464 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3465 his_offset
= cur
->frame
->area
.y
+
3466 (hang
? cur
->frame
->area
.height
: 0);
3469 if(his_offset
- 1 < my_offset
)
3472 if(his_offset
> dest
)
3475 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3478 case OB_DIRECTION_WEST
:
3479 my_edge_start
= c
->frame
->area
.y
;
3480 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3481 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3483 /* default: leftmost egde of screen */
3484 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3485 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3486 /* if the monitor edge comes before the screen edge, */
3487 /* use that as the destination instead. (For xinerama) */
3488 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3489 dest
= monitor_dest
;
3491 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3492 gint his_edge_start
, his_edge_end
, his_offset
;
3493 ObClient
*cur
= it
->data
;
3497 his_edge_start
= cur
->frame
->area
.y
;
3498 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3499 his_offset
= cur
->frame
->area
.x
+
3500 (hang
? 0 : cur
->frame
->area
.width
);
3502 if(his_offset
+ 1 > my_offset
)
3505 if(his_offset
< dest
)
3508 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3511 case OB_DIRECTION_EAST
:
3512 my_edge_start
= c
->frame
->area
.y
;
3513 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3514 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3516 /* default: rightmost edge of screen */
3517 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3518 monitor_dest
= monitor
->x
+ monitor
->width
-
3519 (hang
? c
->frame
->area
.width
: 0);
3520 /* if the monitor edge comes before the screen edge, */
3521 /* use that as the destination instead. (For xinerama) */
3522 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3523 dest
= monitor_dest
;
3525 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3526 gint his_edge_start
, his_edge_end
, his_offset
;
3527 ObClient
*cur
= it
->data
;
3531 his_edge_start
= cur
->frame
->area
.y
;
3532 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3533 his_offset
= cur
->frame
->area
.x
+
3534 (hang
? cur
->frame
->area
.width
: 0);
3536 if(his_offset
- 1 < my_offset
)
3539 if(his_offset
> dest
)
3542 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3545 case OB_DIRECTION_NORTHEAST
:
3546 case OB_DIRECTION_SOUTHEAST
:
3547 case OB_DIRECTION_NORTHWEST
:
3548 case OB_DIRECTION_SOUTHWEST
:
3549 /* not implemented */
3551 g_assert_not_reached();
3552 dest
= 0; /* suppress warning */
3557 ObClient
* client_under_pointer()
3561 ObClient
*ret
= NULL
;
3563 if (screen_pointer_pos(&x
, &y
)) {
3564 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3565 if (WINDOW_IS_CLIENT(it
->data
)) {
3566 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3567 if (c
->frame
->visible
&&
3568 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3578 gboolean
client_has_group_siblings(ObClient
*self
)
3580 return self
->group
&& self
->group
->members
->next
;