1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
45 #include <X11/Xutil.h>
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask)
50 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
55 ObClientDestructor func
;
59 GList
*client_list
= NULL
;
61 static GSList
*client_destructors
= NULL
;
63 static void client_get_all(ObClient
*self
);
64 static void client_toggle_border(ObClient
*self
, gboolean show
);
65 static void client_get_startup_id(ObClient
*self
);
66 static void client_get_area(ObClient
*self
);
67 static void client_get_desktop(ObClient
*self
);
68 static void client_get_state(ObClient
*self
);
69 static void client_get_layer(ObClient
*self
);
70 static void client_get_shaped(ObClient
*self
);
71 static void client_get_mwm_hints(ObClient
*self
);
72 static void client_get_gravity(ObClient
*self
);
73 static void client_change_allowed_actions(ObClient
*self
);
74 static void client_change_state(ObClient
*self
);
75 static void client_change_wm_state(ObClient
*self
);
76 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
77 static void client_restore_session_state(ObClient
*self
);
78 static void client_restore_session_stacking(ObClient
*self
);
79 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
81 void client_startup(gboolean reconfig
)
88 void client_shutdown(gboolean reconfig
)
92 void client_add_destructor(ObClientDestructor func
, gpointer data
)
94 Destructor
*d
= g_new(Destructor
, 1);
97 client_destructors
= g_slist_prepend(client_destructors
, d
);
100 void client_remove_destructor(ObClientDestructor func
)
104 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
105 Destructor
*d
= it
->data
;
106 if (d
->func
== func
) {
108 client_destructors
= g_slist_delete_link(client_destructors
, it
);
114 void client_set_list()
116 Window
*windows
, *win_it
;
118 guint size
= g_list_length(client_list
);
120 /* create an array of the window ids */
122 windows
= g_new(Window
, size
);
124 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
125 *win_it
= ((ObClient
*)it
->data
)->window
;
129 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
130 net_client_list
, window
, (gulong
*)windows
, size
);
139 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
143 for (it = self->transients; it; it = g_slist_next(it)) {
144 if (!func(it->data, data)) return;
145 client_foreach_transient(it->data, func, data);
149 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
151 if (self->transient_for) {
152 if (self->transient_for != OB_TRAN_GROUP) {
153 if (!func(self->transient_for, data)) return;
154 client_foreach_ancestor(self->transient_for, func, data);
158 for (it = self->group->members; it; it = g_slist_next(it))
159 if (it->data != self &&
160 !((ObClient*)it->data)->transient_for) {
161 if (!func(it->data, data)) return;
162 client_foreach_ancestor(it->data, func, data);
169 void client_manage_all()
174 XWindowAttributes attrib
;
176 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
177 &w
, &w
, &children
, &nchild
);
179 /* remove all icon windows from the list */
180 for (i
= 0; i
< nchild
; i
++) {
181 if (children
[i
] == None
) continue;
182 wmhints
= XGetWMHints(ob_display
, children
[i
]);
184 if ((wmhints
->flags
& IconWindowHint
) &&
185 (wmhints
->icon_window
!= children
[i
]))
186 for (j
= 0; j
< nchild
; j
++)
187 if (children
[j
] == wmhints
->icon_window
) {
195 for (i
= 0; i
< nchild
; ++i
) {
196 if (children
[i
] == None
)
198 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
199 if (attrib
.override_redirect
) continue;
201 if (attrib
.map_state
!= IsUnmapped
)
202 client_manage(children
[i
]);
208 void client_manage(Window window
)
212 XWindowAttributes attrib
;
213 XSetWindowAttributes attrib_set
;
215 gboolean activate
= FALSE
;
216 ObAppSettings
*settings
;
221 /* check if it has already been unmapped by the time we started mapping.
222 the grab does a sync so we don't have to here */
223 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
224 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
226 XPutBackEvent(ob_display
, &e
);
228 ob_debug("Trying to manage unmapped window. Aborting that.\n");
230 return; /* don't manage it */
233 /* make sure it isn't an override-redirect window */
234 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
235 attrib
.override_redirect
)
238 return; /* don't manage it */
241 /* is the window a docking app */
242 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
243 if ((wmhint
->flags
& StateHint
) &&
244 wmhint
->initial_state
== WithdrawnState
)
246 dock_add(window
, wmhint
);
254 ob_debug("Managing window: %lx\n", window
);
256 /* choose the events we want to receive on the CLIENT window */
257 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
258 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
259 XChangeWindowAttributes(ob_display
, window
,
260 CWEventMask
|CWDontPropagate
, &attrib_set
);
263 /* create the ObClient struct, and populate it from the hints on the
265 self
= g_new0(ObClient
, 1);
266 self
->obwin
.type
= Window_Client
;
267 self
->window
= window
;
269 /* non-zero defaults */
270 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
272 self
->desktop
= screen_num_desktops
; /* always an invalid value */
273 self
->user_time
= CurrentTime
;
275 client_get_all(self
);
276 /* per-app settings override stuff, and return the settings for other
278 settings
= client_get_settings_state(self
);
279 /* the session should get the last say */
280 client_restore_session_state(self
);
282 client_calc_layer(self
);
285 Time t
= sn_app_started(self
->startup_id
, self
->class);
286 if (t
) self
->user_time
= t
;
289 /* update the focus lists, do this before the call to change_state or
290 it can end up in the list twice! */
291 focus_order_add_new(self
);
293 /* remove the client's border (and adjust re gravity) */
294 client_toggle_border(self
, FALSE
);
296 /* specify that if we exit, the window should not be destroyed and should
297 be reparented back to root automatically */
298 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
300 /* create the decoration frame for the client window */
301 self
->frame
= frame_new(self
);
303 frame_grab_client(self
->frame
, self
);
305 /* do this after we have a frame.. it uses the frame to help determine the
306 WM_STATE to apply. */
307 client_change_state(self
);
311 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
312 client_restore_session_stacking(self
);
314 /* focus the new window? */
315 if (ob_state() != OB_STATE_STARTING
&&
316 /* this means focus=true for window is same as config_focus_new=true */
317 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
318 client_search_focus_parent(self
)) &&
319 /* this checks for focus=false for the window */
320 (!settings
|| settings
->focus
!= 0) &&
321 /* note the check against Type_Normal/Dialog, not client_normal(self),
322 which would also include other types. in this case we want more
323 strict rules for focus */
324 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
325 self
->type
== OB_CLIENT_TYPE_DIALOG
))
329 if (self
->desktop
!= screen_desktop
) {
330 /* activate the window */
333 gboolean group_foc
= FALSE
;
338 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
340 if (client_focused(it
->data
))
348 (!self
->transient_for
&& (!self
->group
||
349 !self
->group
->members
->next
))) ||
350 client_search_focus_tree_full(self
) ||
352 !client_normal(focus_client
))
354 /* activate the window */
361 /* get the current position */
365 /* figure out placement for the window */
366 if (ob_state() == OB_STATE_RUNNING
) {
369 transient
= place_client(self
, &newx
, &newy
, settings
);
371 /* make sure the window is visible. */
372 client_find_onscreen(self
, &newx
, &newy
,
373 self
->frame
->area
.width
,
374 self
->frame
->area
.height
,
375 /* non-normal clients has less rules, and
376 windows that are being restored from a
377 session do also. we can assume you want
378 it back where you saved it. Clients saying
379 they placed themselves are subjected to
380 harder rules, ones that are placed by
381 place.c or by the user are allowed partially
382 off-screen and on xinerama divides (ie,
383 it is up to the placement routines to avoid
384 the xinerama divides) */
386 (((self
->positioned
& PPosition
) &&
387 !(self
->positioned
& USPosition
)) &&
388 client_normal(self
) &&
392 /* do this after the window is placed, so the premax/prefullscreen numbers
394 also, this moves the window to the position where it has been placed
396 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
397 self
->window
, newx
, newy
, self
->area
.width
, self
->area
.height
);
398 client_apply_startup_state(self
, newx
, newy
);
400 keyboard_grab_for_client(self
, TRUE
);
401 mouse_grab_for_client(self
, TRUE
);
404 guint32 last_time
= focus_client
?
405 focus_client
->user_time
: CurrentTime
;
407 /* This is focus stealing prevention */
408 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
409 self
->window
, self
->user_time
, last_time
);
411 /* If a nothing at all, or a parent was focused, then focus this
414 if (!focus_client
|| client_search_focus_parent(self
) != NULL
)
418 /* If time stamp is old, don't steal focus */
419 if (self
->user_time
&& last_time
&&
420 !event_time_after(self
->user_time
, last_time
))
424 /* Don't steal focus from globally active clients.
425 I stole this idea from KWin. It seems nice.
427 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
))
433 /* since focus can change the stacking orders, if we focus the
434 window then the standard raise it gets is not enough, we need
435 to queue one for after the focus change takes place */
438 ob_debug("Focus stealing prevention activated for %s with time %u "
440 self
->title
, self
->user_time
, last_time
);
441 /* if the client isn't focused, then hilite it so the user
443 client_hilite(self
, TRUE
);
447 /* This may look rather odd. Well it's because new windows are added
448 to the stacking order non-intrusively. If we're not going to focus
449 the new window or hilite it, then we raise it to the top. This will
450 take affect for things that don't get focused like splash screens.
451 Also if you don't have focus_new enabled, then it's going to get
452 raised to the top. Legacy begets legacy I guess?
457 /* this has to happen before we try focus the window, but we want it to
458 happen after the client's stacking has been determined or it looks bad
462 /* use client_focus instead of client_activate cuz client_activate does
463 stuff like switch desktops etc and I'm not interested in all that when
464 a window maps since its not based on an action from the user like
465 clicking a window to activate it. so keep the new window out of the way
468 /* if using focus_delay, stop the timer now so that focus doesn't
470 event_halt_focus_delay();
474 /* client_activate does this but we aren't using it so we have to do it
476 if (screen_showing_desktop
)
477 screen_show_desktop(FALSE
);
479 /* add to client list/map */
480 client_list
= g_list_append(client_list
, self
);
481 g_hash_table_insert(window_map
, &self
->window
, self
);
483 /* this has to happen after we're in the client_list */
484 if (STRUT_EXISTS(self
->strut
))
485 screen_update_areas();
487 /* update the list hints */
490 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
493 void client_unmanage_all()
495 while (client_list
!= NULL
)
496 client_unmanage(client_list
->data
);
499 void client_unmanage(ObClient
*self
)
504 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
505 self
->title
? self
->title
: "");
507 g_assert(self
!= NULL
);
509 frame_hide(self
->frame
);
510 /* sync to send the hide to the server quickly, and to get back the enter
512 XSync(ob_display
, FALSE
);
514 if (focus_client
== self
) {
515 /* ignore enter events from the unmap so it doesnt mess with the focus
517 event_ignore_queued_enters();
521 keyboard_grab_for_client(self
, FALSE
);
522 mouse_grab_for_client(self
, FALSE
);
524 /* remove the window from our save set */
525 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
527 /* we dont want events no more */
528 XSelectInput(ob_display
, self
->window
, NoEventMask
);
530 /* update the focus lists */
531 focus_order_remove(self
);
533 client_list
= g_list_remove(client_list
, self
);
534 stacking_remove(self
);
535 g_hash_table_remove(window_map
, &self
->window
);
537 /* once the client is out of the list, update the struts to remove its
539 if (STRUT_EXISTS(self
->strut
))
540 screen_update_areas();
542 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
543 Destructor
*d
= it
->data
;
544 d
->func(self
, d
->data
);
547 /* tell our parent(s) that we're gone */
548 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
549 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
550 if (it
->data
!= self
)
551 ((ObClient
*)it
->data
)->transients
=
552 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
553 } else if (self
->transient_for
) { /* transient of window */
554 self
->transient_for
->transients
=
555 g_slist_remove(self
->transient_for
->transients
, self
);
558 /* tell our transients that we're gone */
559 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
560 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
561 ((ObClient
*)it
->data
)->transient_for
= NULL
;
562 client_calc_layer(it
->data
);
566 /* remove from its group */
568 group_remove(self
->group
, self
);
572 /* restore the window's original geometry so it is not lost */
576 if (self
->fullscreen
)
577 a
= self
->pre_fullscreen_area
;
578 else if (self
->max_horz
|| self
->max_vert
) {
579 if (self
->max_horz
) {
580 a
.x
= self
->pre_max_area
.x
;
581 a
.width
= self
->pre_max_area
.width
;
583 if (self
->max_vert
) {
584 a
.y
= self
->pre_max_area
.y
;
585 a
.height
= self
->pre_max_area
.height
;
589 /* give the client its border back */
590 client_toggle_border(self
, TRUE
);
592 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
593 self
->decorations
= 0; /* unmanaged windows have no decor */
595 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
598 /* reparent the window out of the frame, and free the frame */
599 frame_release_client(self
->frame
, self
);
602 if (ob_state() != OB_STATE_EXITING
) {
603 /* these values should not be persisted across a window
605 PROP_ERASE(self
->window
, net_wm_desktop
);
606 PROP_ERASE(self
->window
, net_wm_state
);
607 PROP_ERASE(self
->window
, wm_state
);
609 /* if we're left in an unmapped state, the client wont be mapped. this
610 is bad, since we will no longer be managing the window on restart */
611 XMapWindow(ob_display
, self
->window
);
614 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
616 /* free all data allocated in the client struct */
617 g_slist_free(self
->transients
);
618 for (j
= 0; j
< self
->nicons
; ++j
)
619 g_free(self
->icons
[j
].data
);
620 if (self
->nicons
> 0)
623 g_free(self
->icon_title
);
627 g_free(self
->sm_client_id
);
630 /* update the list hints */
634 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
636 ObAppSettings
*settings
= NULL
;
639 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
640 ObAppSettings
*app
= it
->data
;
642 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
643 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
644 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
645 && !strcmp(app
->name
, self
->name
)))
647 ob_debug("Window matching: %s\n", app
->name
);
648 /* Match if no role was specified in the per app setting, or if the
649 * string matches the beginning of the role, since apps like to set
650 * the role to things like browser-window-23c4b2f */
652 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
662 if (settings
->shade
!= -1)
663 self
->shaded
= !!settings
->shade
;
664 if (settings
->decor
!= -1)
665 self
->undecorated
= !settings
->decor
;
666 if (settings
->iconic
!= -1)
667 self
->iconic
= !!settings
->iconic
;
668 if (settings
->skip_pager
!= -1)
669 self
->skip_pager
= !!settings
->skip_pager
;
670 if (settings
->skip_taskbar
!= -1)
671 self
->skip_taskbar
= !!settings
->skip_taskbar
;
673 if (settings
->max_vert
!= -1)
674 self
->max_vert
= !!settings
->max_vert
;
675 if (settings
->max_horz
!= -1)
676 self
->max_vert
= !!settings
->max_horz
;
678 if (settings
->fullscreen
!= -1)
679 self
->fullscreen
= !!settings
->fullscreen
;
681 if (settings
->desktop
< screen_num_desktops
682 || settings
->desktop
== DESKTOP_ALL
)
683 self
->desktop
= settings
->desktop
;
685 if (settings
->layer
== -1) {
689 else if (settings
->layer
== 0) {
693 else if (settings
->layer
== 1) {
701 static void client_restore_session_state(ObClient
*self
)
705 if (!(it
= session_state_find(self
)))
708 self
->session
= it
->data
;
710 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
711 self
->positioned
= PPosition
;
712 if (self
->session
->w
> 0)
713 self
->area
.width
= self
->session
->w
;
714 if (self
->session
->h
> 0)
715 self
->area
.height
= self
->session
->h
;
716 XResizeWindow(ob_display
, self
->window
,
717 self
->area
.width
, self
->area
.height
);
719 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
720 self
->session
->desktop
:
721 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
722 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
724 self
->shaded
= self
->session
->shaded
;
725 self
->iconic
= self
->session
->iconic
;
726 self
->skip_pager
= self
->session
->skip_pager
;
727 self
->skip_taskbar
= self
->session
->skip_taskbar
;
728 self
->fullscreen
= self
->session
->fullscreen
;
729 self
->above
= self
->session
->above
;
730 self
->below
= self
->session
->below
;
731 self
->max_horz
= self
->session
->max_horz
;
732 self
->max_vert
= self
->session
->max_vert
;
735 static void client_restore_session_stacking(ObClient
*self
)
739 if (!self
->session
) return;
741 it
= g_list_find(session_saved_state
, self
->session
);
742 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
745 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
746 if (session_state_cmp(it
->data
, cit
->data
))
749 client_calc_layer(self
);
750 stacking_below(CLIENT_AS_WINDOW(self
),
751 CLIENT_AS_WINDOW(cit
->data
));
757 void client_move_onscreen(ObClient
*self
, gboolean rude
)
759 gint x
= self
->area
.x
;
760 gint y
= self
->area
.y
;
761 if (client_find_onscreen(self
, &x
, &y
,
762 self
->frame
->area
.width
,
763 self
->frame
->area
.height
, rude
)) {
764 client_move(self
, x
, y
);
768 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
772 gint ox
= *x
, oy
= *y
;
774 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
777 /* XXX watch for xinerama dead areas */
778 /* This makes sure windows aren't entirely outside of the screen so you
779 can't see them at all.
780 It makes sure 10% of the window is on the screen at least. At don't let
781 it move itself off the top of the screen, which would hide the titlebar
782 on you. (The user can still do this if they want too, it's only limiting
785 if (client_normal(self
)) {
786 a
= screen_area(self
->desktop
);
787 if (!self
->strut
.right
&&
788 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
789 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
790 if (!self
->strut
.bottom
&&
791 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
792 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
793 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
794 *x
= a
->x
- self
->frame
->area
.width
*9/10;
795 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
796 *y
= a
->y
- self
->frame
->area
.width
*9/10;
799 /* This here doesn't let windows even a pixel outside the screen,
800 * when called from client_manage, programs placing themselves are
801 * forced completely onscreen, while things like
802 * xterm -geometry resolution-width/2 will work fine. Trying to
803 * place it completely offscreen will be handled in the above code.
804 * Sorry for this confused comment, i am tired. */
806 /* avoid the xinerama monitor divide while we're at it,
807 * remember to fix the placement stuff to avoid it also and
808 * then remove this XXX */
809 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
810 /* dont let windows map into the strut unless they
811 are bigger than the available area */
813 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
814 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
815 *x
= a
->x
+ a
->width
- w
;
817 if (h
<= a
->height
) {
818 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
819 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
820 *y
= a
->y
+ a
->height
- h
;
824 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
827 return ox
!= *x
|| oy
!= *y
;
830 static void client_toggle_border(ObClient
*self
, gboolean show
)
832 /* adjust our idea of where the client is, based on its border. When the
833 border is removed, the client should now be considered to be in a
835 when re-adding the border to the client, the same operation needs to be
837 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
838 gint x
= oldx
, y
= oldy
;
839 switch(self
->gravity
) {
841 case NorthWestGravity
:
843 case SouthWestGravity
:
845 case NorthEastGravity
:
847 case SouthEastGravity
:
848 if (show
) x
-= self
->border_width
* 2;
849 else x
+= self
->border_width
* 2;
856 if (show
) x
-= self
->border_width
;
857 else x
+= self
->border_width
;
860 switch(self
->gravity
) {
862 case NorthWestGravity
:
864 case NorthEastGravity
:
866 case SouthWestGravity
:
868 case SouthEastGravity
:
869 if (show
) y
-= self
->border_width
* 2;
870 else y
+= self
->border_width
* 2;
877 if (show
) y
-= self
->border_width
;
878 else y
+= self
->border_width
;
885 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
887 /* set border_width to 0 because there is no border to add into
888 calculations anymore */
889 self
->border_width
= 0;
891 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
895 static void client_get_all(ObClient
*self
)
897 client_get_area(self
);
898 client_get_mwm_hints(self
);
900 /* The transient hint is used to pick a type, but the type can also affect
901 transiency (dialogs are always made transients of their group if they
902 have one). This is Havoc's idea, but it is needed to make some apps
903 work right (eg tsclient). */
904 client_update_transient_for(self
);
905 client_get_type(self
);/* this can change the mwmhints for special cases */
906 client_get_state(self
);
907 client_update_transient_for(self
);
909 client_update_wmhints(self
);
910 client_get_startup_id(self
);
911 client_get_desktop(self
);/* uses transient data/group/startup id if a
912 desktop is not specified */
913 client_get_shaped(self
);
915 client_get_layer(self
); /* if layer hasn't been specified, get it from
916 other sources if possible */
919 /* a couple type-based defaults for new windows */
921 /* this makes sure that these windows appear on all desktops */
922 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
923 self
->desktop
= DESKTOP_ALL
;
926 client_update_protocols(self
);
928 client_get_gravity(self
); /* get the attribute gravity */
929 client_update_normal_hints(self
); /* this may override the attribute
932 /* got the type, the mwmhints, the protocols, and the normal hints
933 (min/max sizes), so we're ready to set up the decorations/functions */
934 client_setup_decor_and_functions(self
);
936 client_update_title(self
);
937 client_update_class(self
);
938 client_update_sm_client_id(self
);
939 client_update_strut(self
);
940 client_update_icons(self
);
941 client_update_user_time(self
);
944 static void client_get_startup_id(ObClient
*self
)
946 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
948 PROP_GETS(self
->group
->leader
,
949 net_startup_id
, utf8
, &self
->startup_id
);
952 static void client_get_area(ObClient
*self
)
954 XWindowAttributes wattrib
;
957 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
958 g_assert(ret
!= BadWindow
);
960 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
961 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
962 self
->border_width
= wattrib
.border_width
;
964 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
965 wattrib
.width
, wattrib
.height
);
968 static void client_get_desktop(ObClient
*self
)
970 guint32 d
= screen_num_desktops
; /* an always-invalid value */
972 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
973 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
974 self
->desktop
= screen_num_desktops
- 1;
978 gboolean trdesk
= FALSE
;
980 if (self
->transient_for
) {
981 if (self
->transient_for
!= OB_TRAN_GROUP
) {
982 self
->desktop
= self
->transient_for
->desktop
;
987 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
988 if (it
->data
!= self
&&
989 !((ObClient
*)it
->data
)->transient_for
) {
990 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
997 /* try get from the startup-notification protocol */
998 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
999 if (self
->desktop
>= screen_num_desktops
&&
1000 self
->desktop
!= DESKTOP_ALL
)
1001 self
->desktop
= screen_num_desktops
- 1;
1003 /* defaults to the current desktop */
1004 self
->desktop
= screen_desktop
;
1007 if (self
->desktop
!= d
) {
1008 /* set the desktop hint, to make sure that it always exists */
1009 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
1013 static void client_get_layer(ObClient
*self
)
1015 if (!(self
->above
|| self
->below
)) {
1017 /* apply stuff from the group */
1021 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1022 ObClient
*c
= it
->data
;
1023 if (c
!= self
&& !client_search_transient(self
, c
) &&
1024 client_normal(self
) && client_normal(c
))
1027 (c
->above
? 1 : (c
->below
? -1 : 0)));
1041 g_assert_not_reached();
1048 static void client_get_state(ObClient
*self
)
1053 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1055 for (i
= 0; i
< num
; ++i
) {
1056 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1058 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1059 self
->shaded
= TRUE
;
1060 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1061 self
->iconic
= TRUE
;
1062 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1063 self
->skip_taskbar
= TRUE
;
1064 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1065 self
->skip_pager
= TRUE
;
1066 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1067 self
->fullscreen
= TRUE
;
1068 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1069 self
->max_vert
= TRUE
;
1070 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1071 self
->max_horz
= TRUE
;
1072 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1074 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1076 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1077 self
->demands_attention
= TRUE
;
1078 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1079 self
->undecorated
= TRUE
;
1086 static void client_get_shaped(ObClient
*self
)
1088 self
->shaped
= FALSE
;
1090 if (extensions_shape
) {
1095 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1097 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1098 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1100 self
->shaped
= (s
!= 0);
1105 void client_update_transient_for(ObClient
*self
)
1108 ObClient
*target
= NULL
;
1110 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1111 self
->transient
= TRUE
;
1112 if (t
!= self
->window
) { /* cant be transient to itself! */
1113 target
= g_hash_table_lookup(window_map
, &t
);
1114 /* if this happens then we need to check for it*/
1115 g_assert(target
!= self
);
1116 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1117 /* this can happen when a dialog is a child of
1118 a dockapp, for example */
1122 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1124 Setting the transient_for to Root is actually illegal, however
1125 applications from time have done this to specify transient for
1128 Now you can do that by being a TYPE_DIALOG and not setting
1129 the transient_for hint at all on your window. But people still
1130 use Root, and Kwin is very strange in this regard.
1132 KWin 3.0 will not consider windows with transient_for set to
1133 Root as transient for their group *UNLESS* they are also modal.
1134 In that case, it will make them transient for the group. This
1135 leads to all sorts of weird behavior from KDE apps which are
1136 only tested in KWin. I'd like to follow their behavior just to
1137 make this work right with KDE stuff, but that seems wrong.
1139 if (!target
&& self
->group
) {
1140 /* not transient to a client, see if it is transient for a
1142 if (t
== RootWindow(ob_display
, ob_screen
)) {
1143 /* window is a transient for its group! */
1144 target
= OB_TRAN_GROUP
;
1148 } else if (self
->group
) {
1149 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1150 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1151 self
->type
== OB_CLIENT_TYPE_MENU
||
1152 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1154 self
->transient
= TRUE
;
1155 target
= OB_TRAN_GROUP
;
1158 self
->transient
= FALSE
;
1160 /* if anything has changed... */
1161 if (target
!= self
->transient_for
) {
1162 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1165 /* remove from old parents */
1166 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1167 ObClient
*c
= it
->data
;
1168 if (c
!= self
&& !c
->transient_for
)
1169 c
->transients
= g_slist_remove(c
->transients
, self
);
1171 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1172 /* remove from old parent */
1173 self
->transient_for
->transients
=
1174 g_slist_remove(self
->transient_for
->transients
, self
);
1176 self
->transient_for
= target
;
1177 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1180 /* add to new parents */
1181 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1182 ObClient
*c
= it
->data
;
1183 if (c
!= self
&& !c
->transient_for
)
1184 c
->transients
= g_slist_append(c
->transients
, self
);
1187 /* remove all transients which are in the group, that causes
1188 circlular pointer hell of doom */
1189 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1191 for (sit
= self
->transients
; sit
; sit
= next
) {
1192 next
= g_slist_next(sit
);
1193 if (sit
->data
== it
->data
)
1195 g_slist_delete_link(self
->transients
, sit
);
1198 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1199 /* add to new parent */
1200 self
->transient_for
->transients
=
1201 g_slist_append(self
->transient_for
->transients
, self
);
1206 static void client_get_mwm_hints(ObClient
*self
)
1211 self
->mwmhints
.flags
= 0; /* default to none */
1213 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1215 if (num
>= OB_MWM_ELEMENTS
) {
1216 self
->mwmhints
.flags
= hints
[0];
1217 self
->mwmhints
.functions
= hints
[1];
1218 self
->mwmhints
.decorations
= hints
[2];
1224 void client_get_type(ObClient
*self
)
1231 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1232 /* use the first value that we know about in the array */
1233 for (i
= 0; i
< num
; ++i
) {
1234 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1235 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1236 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1237 self
->type
= OB_CLIENT_TYPE_DOCK
;
1238 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1239 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1240 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1241 self
->type
= OB_CLIENT_TYPE_MENU
;
1242 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1243 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1244 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1245 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1246 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1247 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1248 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1249 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1250 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1251 /* prevent this window from getting any decor or
1253 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1254 OB_MWM_FLAG_DECORATIONS
);
1255 self
->mwmhints
.decorations
= 0;
1256 self
->mwmhints
.functions
= 0;
1258 if (self
->type
!= (ObClientType
) -1)
1259 break; /* grab the first legit type */
1264 if (self
->type
== (ObClientType
) -1) {
1265 /*the window type hint was not set, which means we either classify
1266 ourself as a normal window or a dialog, depending on if we are a
1268 if (self
->transient
)
1269 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1271 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1275 void client_update_protocols(ObClient
*self
)
1278 guint num_return
, i
;
1280 self
->focus_notify
= FALSE
;
1281 self
->delete_window
= FALSE
;
1283 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1284 for (i
= 0; i
< num_return
; ++i
) {
1285 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1286 /* this means we can request the window to close */
1287 self
->delete_window
= TRUE
;
1288 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1289 /* if this protocol is requested, then the window will be
1290 notified whenever we want it to receive focus */
1291 self
->focus_notify
= TRUE
;
1297 static void client_get_gravity(ObClient
*self
)
1299 XWindowAttributes wattrib
;
1302 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1303 g_assert(ret
!= BadWindow
);
1304 self
->gravity
= wattrib
.win_gravity
;
1307 void client_update_normal_hints(ObClient
*self
)
1311 gint oldgravity
= self
->gravity
;
1314 self
->min_ratio
= 0.0f
;
1315 self
->max_ratio
= 0.0f
;
1316 SIZE_SET(self
->size_inc
, 1, 1);
1317 SIZE_SET(self
->base_size
, 0, 0);
1318 SIZE_SET(self
->min_size
, 0, 0);
1319 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1321 /* get the hints from the window */
1322 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1323 /* normal windows can't request placement! har har
1324 if (!client_normal(self))
1326 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1328 if (size
.flags
& PWinGravity
) {
1329 self
->gravity
= size
.win_gravity
;
1331 /* if the client has a frame, i.e. has already been mapped and
1332 is changing its gravity */
1333 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1334 /* move our idea of the client's position based on its new
1336 self
->area
.x
= self
->frame
->area
.x
;
1337 self
->area
.y
= self
->frame
->area
.y
;
1338 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1342 if (size
.flags
& PAspect
) {
1343 if (size
.min_aspect
.y
)
1345 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1346 if (size
.max_aspect
.y
)
1348 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1351 if (size
.flags
& PMinSize
)
1352 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1354 if (size
.flags
& PMaxSize
)
1355 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1357 if (size
.flags
& PBaseSize
)
1358 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1360 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1361 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1365 void client_setup_decor_and_functions(ObClient
*self
)
1367 /* start with everything (cept fullscreen) */
1369 (OB_FRAME_DECOR_TITLEBAR
|
1370 OB_FRAME_DECOR_HANDLE
|
1371 OB_FRAME_DECOR_GRIPS
|
1372 OB_FRAME_DECOR_BORDER
|
1373 OB_FRAME_DECOR_ICON
|
1374 OB_FRAME_DECOR_ALLDESKTOPS
|
1375 OB_FRAME_DECOR_ICONIFY
|
1376 OB_FRAME_DECOR_MAXIMIZE
|
1377 OB_FRAME_DECOR_SHADE
|
1378 OB_FRAME_DECOR_CLOSE
);
1380 (OB_CLIENT_FUNC_RESIZE
|
1381 OB_CLIENT_FUNC_MOVE
|
1382 OB_CLIENT_FUNC_ICONIFY
|
1383 OB_CLIENT_FUNC_MAXIMIZE
|
1384 OB_CLIENT_FUNC_SHADE
|
1385 OB_CLIENT_FUNC_CLOSE
);
1387 if (!(self
->min_size
.width
< self
->max_size
.width
||
1388 self
->min_size
.height
< self
->max_size
.height
))
1389 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1391 switch (self
->type
) {
1392 case OB_CLIENT_TYPE_NORMAL
:
1393 /* normal windows retain all of the possible decorations and
1394 functionality, and are the only windows that you can fullscreen */
1395 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1398 case OB_CLIENT_TYPE_DIALOG
:
1399 case OB_CLIENT_TYPE_UTILITY
:
1400 /* these windows cannot be maximized */
1401 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1404 case OB_CLIENT_TYPE_MENU
:
1405 case OB_CLIENT_TYPE_TOOLBAR
:
1406 /* these windows get less functionality */
1407 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1410 case OB_CLIENT_TYPE_DESKTOP
:
1411 case OB_CLIENT_TYPE_DOCK
:
1412 case OB_CLIENT_TYPE_SPLASH
:
1413 /* none of these windows are manipulated by the window manager */
1414 self
->decorations
= 0;
1415 self
->functions
= 0;
1419 /* Mwm Hints are applied subtractively to what has already been chosen for
1420 decor and functionality */
1421 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1422 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1423 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1424 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1426 /* if the mwm hints request no handle or title, then all
1427 decorations are disabled, but keep the border if that's
1429 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1430 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1432 self
->decorations
= 0;
1437 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1438 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1439 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1440 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1441 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1442 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1443 /* dont let mwm hints kill any buttons
1444 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1445 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1446 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1447 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1449 /* dont let mwm hints kill the close button
1450 if (! (self->mwmhints.functions & MwmFunc_Close))
1451 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1455 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1456 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1457 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1458 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1459 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1460 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1462 /* can't maximize without moving/resizing */
1463 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1464 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1465 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1466 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1467 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1470 /* kill the handle on fully maxed windows */
1471 if (self
->max_vert
&& self
->max_horz
)
1472 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1474 /* finally, the user can have requested no decorations, which overrides
1475 everything (but doesnt give it a border if it doesnt have one) */
1476 if (self
->undecorated
) {
1477 if (config_theme_keepborder
)
1478 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1480 self
->decorations
= 0;
1483 /* if we don't have a titlebar, then we cannot shade! */
1484 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1485 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1487 /* now we need to check against rules for the client's current state */
1488 if (self
->fullscreen
) {
1489 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1490 OB_CLIENT_FUNC_FULLSCREEN
|
1491 OB_CLIENT_FUNC_ICONIFY
);
1492 self
->decorations
= 0;
1495 client_change_allowed_actions(self
);
1498 /* adjust the client's decorations, etc. */
1499 client_reconfigure(self
);
1503 static void client_change_allowed_actions(ObClient
*self
)
1508 /* desktop windows are kept on all desktops */
1509 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1510 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1512 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1513 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1514 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1515 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1516 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1517 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1518 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1519 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1520 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1521 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1522 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1523 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1524 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1525 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1526 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1529 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1531 /* make sure the window isn't breaking any rules now */
1533 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1534 if (self
->frame
) client_shade(self
, FALSE
);
1535 else self
->shaded
= FALSE
;
1537 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1538 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1539 else self
->iconic
= FALSE
;
1541 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1542 if (self
->frame
) client_fullscreen(self
, FALSE
);
1543 else self
->fullscreen
= FALSE
;
1545 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1547 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1548 else self
->max_vert
= self
->max_horz
= FALSE
;
1552 void client_reconfigure(ObClient
*self
)
1554 /* by making this pass FALSE for user, we avoid the emacs event storm where
1555 every configurenotify causes an update in its normal hints, i think this
1556 is generally what we want anyways... */
1557 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1558 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1561 void client_update_wmhints(ObClient
*self
)
1566 /* assume a window takes input if it doesnt specify */
1567 self
->can_focus
= TRUE
;
1569 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1570 if (hints
->flags
& InputHint
)
1571 self
->can_focus
= hints
->input
;
1573 /* only do this when first managing the window *AND* when we aren't
1575 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1576 if (hints
->flags
& StateHint
)
1577 self
->iconic
= hints
->initial_state
== IconicState
;
1579 if (!(hints
->flags
& WindowGroupHint
))
1580 hints
->window_group
= None
;
1582 /* did the group state change? */
1583 if (hints
->window_group
!=
1584 (self
->group
? self
->group
->leader
: None
)) {
1585 /* remove from the old group if there was one */
1586 if (self
->group
!= NULL
) {
1587 /* remove transients of the group */
1588 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1589 self
->transients
= g_slist_remove(self
->transients
,
1592 /* remove myself from parents in the group */
1593 if (self
->transient_for
== OB_TRAN_GROUP
) {
1594 for (it
= self
->group
->members
; it
;
1595 it
= g_slist_next(it
))
1597 ObClient
*c
= it
->data
;
1599 if (c
!= self
&& !c
->transient_for
)
1600 c
->transients
= g_slist_remove(c
->transients
,
1605 group_remove(self
->group
, self
);
1608 if (hints
->window_group
!= None
) {
1609 self
->group
= group_add(hints
->window_group
, self
);
1611 /* i can only have transients from the group if i am not
1613 if (!self
->transient_for
) {
1614 /* add other transients of the group that are already
1616 for (it
= self
->group
->members
; it
;
1617 it
= g_slist_next(it
))
1619 ObClient
*c
= it
->data
;
1620 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1622 g_slist_append(self
->transients
, c
);
1627 /* because the self->transient flag wont change from this call,
1628 we don't need to update the window's type and such, only its
1629 transient_for, and the transients lists of other windows in
1630 the group may be affected */
1631 client_update_transient_for(self
);
1634 /* the WM_HINTS can contain an icon */
1635 client_update_icons(self
);
1641 void client_update_title(ObClient
*self
)
1645 g_free(self
->title
);
1648 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1649 /* try old x stuff */
1650 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1651 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1652 if (self
->transient
) {
1654 GNOME alert windows are not given titles:
1655 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1657 data
= g_strdup("");
1659 data
= g_strdup("Unnamed Window");
1663 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1667 frame_adjust_title(self
->frame
);
1669 /* update the icon title */
1671 g_free(self
->icon_title
);
1674 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1675 /* try old x stuff */
1676 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1677 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1678 data
= g_strdup(self
->title
);
1680 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1681 self
->icon_title
= data
;
1684 void client_update_class(ObClient
*self
)
1689 if (self
->name
) g_free(self
->name
);
1690 if (self
->class) g_free(self
->class);
1691 if (self
->role
) g_free(self
->role
);
1693 self
->name
= self
->class = self
->role
= NULL
;
1695 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1697 self
->name
= g_strdup(data
[0]);
1699 self
->class = g_strdup(data
[1]);
1704 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1707 if (self
->name
== NULL
) self
->name
= g_strdup("");
1708 if (self
->class == NULL
) self
->class = g_strdup("");
1709 if (self
->role
== NULL
) self
->role
= g_strdup("");
1712 void client_update_strut(ObClient
*self
)
1716 gboolean got
= FALSE
;
1719 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1723 STRUT_PARTIAL_SET(strut
,
1724 data
[0], data
[2], data
[1], data
[3],
1725 data
[4], data
[5], data
[8], data
[9],
1726 data
[6], data
[7], data
[10], data
[11]);
1732 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1738 /* use the screen's width/height */
1739 a
= screen_physical_area();
1741 STRUT_PARTIAL_SET(strut
,
1742 data
[0], data
[2], data
[1], data
[3],
1743 a
->y
, a
->y
+ a
->height
- 1,
1744 a
->x
, a
->x
+ a
->width
- 1,
1745 a
->y
, a
->y
+ a
->height
- 1,
1746 a
->x
, a
->x
+ a
->width
- 1);
1752 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1753 0, 0, 0, 0, 0, 0, 0, 0);
1755 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1756 self
->strut
= strut
;
1758 /* updating here is pointless while we're being mapped cuz we're not in
1759 the client list yet */
1761 screen_update_areas();
1765 void client_update_icons(ObClient
*self
)
1771 for (i
= 0; i
< self
->nicons
; ++i
)
1772 g_free(self
->icons
[i
].data
);
1773 if (self
->nicons
> 0)
1774 g_free(self
->icons
);
1777 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1778 /* figure out how many valid icons are in here */
1780 while (num
- i
> 2) {
1784 if (i
> num
|| w
*h
== 0) break;
1788 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1790 /* store the icons */
1792 for (j
= 0; j
< self
->nicons
; ++j
) {
1795 w
= self
->icons
[j
].width
= data
[i
++];
1796 h
= self
->icons
[j
].height
= data
[i
++];
1798 if (w
*h
== 0) continue;
1800 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1801 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1806 self
->icons
[j
].data
[t
] =
1807 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1808 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1809 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1810 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1819 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1820 if (hints
->flags
& IconPixmapHint
) {
1822 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1823 xerror_set_ignore(TRUE
);
1824 if (!RrPixmapToRGBA(ob_rr_inst
,
1826 (hints
->flags
& IconMaskHint
?
1827 hints
->icon_mask
: None
),
1828 &self
->icons
[self
->nicons
-1].width
,
1829 &self
->icons
[self
->nicons
-1].height
,
1830 &self
->icons
[self
->nicons
-1].data
)){
1831 g_free(&self
->icons
[self
->nicons
-1]);
1834 xerror_set_ignore(FALSE
);
1841 frame_adjust_icon(self
->frame
);
1844 void client_update_user_time(ObClient
*self
)
1848 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1849 /* we set this every time, not just when it grows, because in practice
1850 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1851 backward we don't want all windows to stop focusing. we'll just
1852 assume noone is setting times older than the last one, cuz that
1853 would be pretty stupid anyways
1855 self
->user_time
= time
;
1858 ob_debug("window %s user time %u\n", self->title, time);
1863 static void client_change_wm_state(ObClient
*self
)
1868 old
= self
->wmstate
;
1870 if (self
->shaded
|| self
->iconic
|| !self
->frame
->visible
)
1871 self
->wmstate
= IconicState
;
1873 self
->wmstate
= NormalState
;
1875 if (old
!= self
->wmstate
) {
1876 PROP_MSG(self
->window
, kde_wm_change_state
,
1877 self
->wmstate
, 1, 0, 0);
1879 state
[0] = self
->wmstate
;
1881 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1885 static void client_change_state(ObClient
*self
)
1887 gulong netstate
[11];
1892 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1894 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1896 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1897 if (self
->skip_taskbar
)
1898 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1899 if (self
->skip_pager
)
1900 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1901 if (self
->fullscreen
)
1902 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1904 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1906 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1908 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1910 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1911 if (self
->demands_attention
)
1912 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1913 if (self
->undecorated
)
1914 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1915 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1918 frame_adjust_state(self
->frame
);
1921 ObClient
*client_search_focus_tree(ObClient
*self
)
1926 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1927 if (client_focused(it
->data
)) return it
->data
;
1928 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1933 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1935 if (self
->transient_for
) {
1936 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1937 return client_search_focus_tree_full(self
->transient_for
);
1940 gboolean recursed
= FALSE
;
1942 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1943 if (!((ObClient
*)it
->data
)->transient_for
) {
1945 if ((c
= client_search_focus_tree_full(it
->data
)))
1954 /* this function checks the whole tree, the client_search_focus_tree~
1955 does not, so we need to check this window */
1956 if (client_focused(self
))
1958 return client_search_focus_tree(self
);
1961 static ObStackingLayer
calc_layer(ObClient
*self
)
1965 if (self
->fullscreen
&&
1966 (client_focused(self
) || client_search_focus_tree(self
)))
1967 l
= OB_STACKING_LAYER_FULLSCREEN
;
1968 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1969 l
= OB_STACKING_LAYER_DESKTOP
;
1970 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1971 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1972 else l
= OB_STACKING_LAYER_ABOVE
;
1974 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1975 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1976 else l
= OB_STACKING_LAYER_NORMAL
;
1981 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1982 ObStackingLayer min
, gboolean raised
)
1984 ObStackingLayer old
, own
;
1988 own
= calc_layer(self
);
1989 self
->layer
= MAX(own
, min
);
1991 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1992 client_calc_layer_recursive(it
->data
, orig
,
1994 raised
? raised
: self
->layer
!= old
);
1996 if (!raised
&& self
->layer
!= old
)
1997 if (orig
->frame
) { /* only restack if the original window is managed */
1998 stacking_remove(CLIENT_AS_WINDOW(self
));
1999 stacking_add(CLIENT_AS_WINDOW(self
));
2003 void client_calc_layer(ObClient
*self
)
2010 /* transients take on the layer of their parents */
2011 it
= client_search_all_top_parents(self
);
2013 for (; it
; it
= g_slist_next(it
))
2014 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2017 gboolean
client_should_show(ObClient
*self
)
2021 if (client_normal(self
) && screen_showing_desktop
)
2024 if (self->transient_for) {
2025 if (self->transient_for != OB_TRAN_GROUP)
2026 return client_should_show(self->transient_for);
2030 for (it = self->group->members; it; it = g_slist_next(it)) {
2031 ObClient *c = it->data;
2032 if (c != self && !c->transient_for) {
2033 if (client_should_show(c))
2040 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2046 void client_show(ObClient
*self
)
2049 if (client_should_show(self
)) {
2050 frame_show(self
->frame
);
2053 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2054 needs to be in IconicState. This includes when it is on another
2057 client_change_wm_state(self
);
2060 void client_hide(ObClient
*self
)
2062 if (!client_should_show(self
)) {
2063 frame_hide(self
->frame
);
2066 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2067 needs to be in IconicState. This includes when it is on another
2070 client_change_wm_state(self
);
2073 void client_showhide(ObClient
*self
)
2076 if (client_should_show(self
)) {
2077 frame_show(self
->frame
);
2080 frame_hide(self
->frame
);
2083 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2084 needs to be in IconicState. This includes when it is on another
2087 client_change_wm_state(self
);
2090 gboolean
client_normal(ObClient
*self
) {
2091 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2092 self
->type
== OB_CLIENT_TYPE_DOCK
||
2093 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2096 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2098 gboolean pos
= FALSE
; /* has the window's position been configured? */
2101 /* save the position, and set self->area for these to use */
2107 /* these are in a carefully crafted order.. */
2110 self
->iconic
= FALSE
;
2111 client_iconify(self
, TRUE
, FALSE
);
2113 if (self
->fullscreen
) {
2114 self
->fullscreen
= FALSE
;
2115 client_fullscreen(self
, TRUE
);
2118 if (self
->undecorated
) {
2119 self
->undecorated
= FALSE
;
2120 client_set_undecorated(self
, TRUE
);
2123 self
->shaded
= FALSE
;
2124 client_shade(self
, TRUE
);
2126 if (self
->demands_attention
) {
2127 self
->demands_attention
= FALSE
;
2128 client_hilite(self
, TRUE
);
2131 if (self
->max_vert
&& self
->max_horz
) {
2132 self
->max_vert
= self
->max_horz
= FALSE
;
2133 client_maximize(self
, TRUE
, 0);
2135 } else if (self
->max_vert
) {
2136 self
->max_vert
= FALSE
;
2137 client_maximize(self
, TRUE
, 2);
2139 } else if (self
->max_horz
) {
2140 self
->max_horz
= FALSE
;
2141 client_maximize(self
, TRUE
, 1);
2145 /* if the client didn't get positioned yet, then do so now
2146 call client_move even if the window is not being moved anywhere, because
2147 when we reparent it and decorate it, it is getting moved and we need to
2148 be telling it so with a ConfigureNotify event.
2151 /* use the saved position */
2154 client_move(self
, x
, y
);
2157 /* nothing to do for the other states:
2166 void client_try_configure(ObClient
*self
, ObCorner anchor
,
2167 gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2168 gint
*logicalw
, gint
*logicalh
,
2171 Rect desired_area
= {*x
, *y
, *w
, *h
};
2173 /* make the frame recalculate its dimentions n shit without changing
2174 anything visible for real, this way the constraints below can work with
2175 the updated frame dimensions. */
2176 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2178 /* work within the prefered sizes given by the window */
2179 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2180 gint basew
, baseh
, minw
, minh
;
2182 /* base size is substituted with min size if not specified */
2183 if (self
->base_size
.width
|| self
->base_size
.height
) {
2184 basew
= self
->base_size
.width
;
2185 baseh
= self
->base_size
.height
;
2187 basew
= self
->min_size
.width
;
2188 baseh
= self
->min_size
.height
;
2190 /* min size is substituted with base size if not specified */
2191 if (self
->min_size
.width
|| self
->min_size
.height
) {
2192 minw
= self
->min_size
.width
;
2193 minh
= self
->min_size
.height
;
2195 minw
= self
->base_size
.width
;
2196 minh
= self
->base_size
.height
;
2199 /* if this is a user-requested resize, then check against min/max
2202 /* smaller than min size or bigger than max size? */
2203 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2204 if (*w
< minw
) *w
= minw
;
2205 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2206 if (*h
< minh
) *h
= minh
;
2211 /* keep to the increments */
2212 *w
/= self
->size_inc
.width
;
2213 *h
/= self
->size_inc
.height
;
2215 /* you cannot resize to nothing */
2216 if (basew
+ *w
< 1) *w
= 1 - basew
;
2217 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2219 /* save the logical size */
2220 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2221 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2223 *w
*= self
->size_inc
.width
;
2224 *h
*= self
->size_inc
.height
;
2229 /* adjust the height to match the width for the aspect ratios.
2230 for this, min size is not substituted for base size ever. */
2231 *w
-= self
->base_size
.width
;
2232 *h
-= self
->base_size
.height
;
2234 if (!self
->fullscreen
) {
2235 if (self
->min_ratio
)
2236 if (*h
* self
->min_ratio
> *w
) {
2237 *h
= (gint
)(*w
/ self
->min_ratio
);
2239 /* you cannot resize to nothing */
2242 *w
= (gint
)(*h
* self
->min_ratio
);
2245 if (self
->max_ratio
)
2246 if (*h
* self
->max_ratio
< *w
) {
2247 *h
= (gint
)(*w
/ self
->max_ratio
);
2249 /* you cannot resize to nothing */
2252 *w
= (gint
)(*h
* self
->min_ratio
);
2257 *w
+= self
->base_size
.width
;
2258 *h
+= self
->base_size
.height
;
2261 /* gets the frame's position */
2262 frame_client_gravity(self
->frame
, x
, y
);
2264 /* these positions are frame positions, not client positions */
2266 /* set the size and position if fullscreen */
2267 if (self
->fullscreen
) {
2271 i
= screen_find_monitor(&desired_area
);
2272 a
= screen_physical_area_monitor(i
);
2279 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2280 is entering fullscreen */
2281 } else if (self
->max_horz
|| self
->max_vert
) {
2285 i
= screen_find_monitor(&desired_area
);
2286 a
= screen_area_monitor(self
->desktop
, i
);
2288 /* set the size and position if maximized */
2289 if (self
->max_horz
) {
2291 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2293 if (self
->max_vert
) {
2295 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2298 /* maximizing is not allowed if the user can't move+resize the window
2302 /* gets the client's position */
2303 frame_frame_gravity(self
->frame
, x
, y
);
2305 /* these override the above states! if you cant move you can't move! */
2307 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2311 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2312 *w
= self
->area
.width
;
2313 *h
= self
->area
.height
;
2321 case OB_CORNER_TOPLEFT
:
2323 case OB_CORNER_TOPRIGHT
:
2324 *x
-= *w
- self
->area
.width
;
2326 case OB_CORNER_BOTTOMLEFT
:
2327 *y
-= *h
- self
->area
.height
;
2329 case OB_CORNER_BOTTOMRIGHT
:
2330 *x
-= *w
- self
->area
.width
;
2331 *y
-= *h
- self
->area
.height
;
2337 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2338 gint x
, gint y
, gint w
, gint h
,
2339 gboolean user
, gboolean final
,
2340 gboolean force_reply
)
2342 gint oldw
, oldh
, oldrx
, oldry
;
2343 gboolean send_resize_client
;
2344 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2345 guint fdecor
= self
->frame
->decorations
;
2346 gboolean fhorz
= self
->frame
->max_horz
;
2347 gint logicalw
, logicalh
;
2349 /* find the new x, y, width, and height (and logical size) */
2350 client_try_configure(self
, anchor
, &x
, &y
, &w
, &h
,
2351 &logicalw
, &logicalh
, user
);
2353 /* set the logical size if things changed */
2354 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2355 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2357 /* figure out if we moved or resized or what */
2358 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2359 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2361 oldw
= self
->area
.width
;
2362 oldh
= self
->area
.height
;
2363 RECT_SET(self
->area
, x
, y
, w
, h
);
2365 /* for app-requested resizes, always resize if 'resized' is true.
2366 for user-requested ones, only resize if final is true, or when
2367 resizing in redraw mode */
2368 send_resize_client
= ((!user
&& resized
) ||
2370 (resized
&& config_resize_redraw
))));
2372 /* if the client is enlarging, then resize the client before the frame */
2373 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2374 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2376 /* find the frame's dimensions and move/resize it */
2377 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2378 moved
= resized
= TRUE
;
2379 if (moved
|| resized
)
2380 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2382 /* find the client's position relative to the root window */
2383 oldrx
= self
->root_pos
.x
;
2384 oldry
= self
->root_pos
.y
;
2385 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2386 self
->frame
->size
.left
-
2387 self
->border_width
) ||
2388 oldry
!= (signed)(self
->frame
->area
.y
+
2389 self
->frame
->size
.top
-
2390 self
->border_width
));
2392 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2396 POINT_SET(self
->root_pos
,
2397 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2399 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2400 self
->border_width
);
2402 event
.type
= ConfigureNotify
;
2403 event
.xconfigure
.display
= ob_display
;
2404 event
.xconfigure
.event
= self
->window
;
2405 event
.xconfigure
.window
= self
->window
;
2407 /* root window real coords */
2408 event
.xconfigure
.x
= self
->root_pos
.x
;
2409 event
.xconfigure
.y
= self
->root_pos
.y
;
2410 event
.xconfigure
.width
= w
;
2411 event
.xconfigure
.height
= h
;
2412 event
.xconfigure
.border_width
= 0;
2413 event
.xconfigure
.above
= self
->frame
->plate
;
2414 event
.xconfigure
.override_redirect
= FALSE
;
2415 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2416 FALSE
, StructureNotifyMask
, &event
);
2419 /* if the client is shrinking, then resize the frame before the client */
2420 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2421 XResizeWindow(ob_display
, self
->window
, w
, h
);
2426 void client_fullscreen(ObClient
*self
, gboolean fs
)
2430 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2431 self
->fullscreen
== fs
) return; /* already done */
2433 self
->fullscreen
= fs
;
2434 client_change_state(self
); /* change the state hints on the client */
2435 client_calc_layer(self
); /* and adjust out layer/stacking */
2438 self
->pre_fullscreen_area
= self
->area
;
2439 /* if the window is maximized, its area isn't all that meaningful.
2440 save it's premax area instead. */
2441 if (self
->max_horz
) {
2442 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2443 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2445 if (self
->max_vert
) {
2446 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2447 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2450 /* these are not actually used cuz client_configure will set them
2451 as appropriate when the window is fullscreened */
2456 if (self
->pre_fullscreen_area
.width
> 0 &&
2457 self
->pre_fullscreen_area
.height
> 0)
2459 x
= self
->pre_fullscreen_area
.x
;
2460 y
= self
->pre_fullscreen_area
.y
;
2461 w
= self
->pre_fullscreen_area
.width
;
2462 h
= self
->pre_fullscreen_area
.height
;
2463 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2465 /* pick some fallbacks... */
2466 a
= screen_area_monitor(self
->desktop
, 0);
2467 x
= a
->x
+ a
->width
/ 4;
2468 y
= a
->y
+ a
->height
/ 4;
2474 client_setup_decor_and_functions(self
);
2476 client_move_resize(self
, x
, y
, w
, h
);
2478 /* try focus us when we go into fullscreen mode */
2482 static void client_iconify_recursive(ObClient
*self
,
2483 gboolean iconic
, gboolean curdesk
)
2486 gboolean changed
= FALSE
;
2489 if (self
->iconic
!= iconic
) {
2490 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2494 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2495 self
->iconic
= iconic
;
2497 /* update the focus lists.. iconic windows go to the bottom of
2498 the list, put the new iconic window at the 'top of the
2500 focus_order_to_top(self
);
2505 self
->iconic
= iconic
;
2508 client_set_desktop(self
, screen_desktop
, FALSE
);
2510 /* this puts it after the current focused window */
2511 focus_order_remove(self
);
2512 focus_order_add_new(self
);
2519 client_change_state(self
);
2520 client_showhide(self
);
2521 if (STRUT_EXISTS(self
->strut
))
2522 screen_update_areas();
2525 /* iconify all direct transients */
2526 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2527 if (it
->data
!= self
)
2528 if (client_is_direct_child(self
, it
->data
))
2529 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2532 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2534 /* move up the transient chain as far as possible first */
2535 self
= client_search_top_parent(self
);
2536 client_iconify_recursive(self
, iconic
, curdesk
);
2539 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2543 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2544 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2546 /* check if already done */
2548 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2549 if (dir
== 1 && self
->max_horz
) return;
2550 if (dir
== 2 && self
->max_vert
) return;
2552 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2553 if (dir
== 1 && !self
->max_horz
) return;
2554 if (dir
== 2 && !self
->max_vert
) return;
2557 /* we just tell it to configure in the same place and client_configure
2558 worries about filling the screen with the window */
2561 w
= self
->area
.width
;
2562 h
= self
->area
.height
;
2565 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2566 RECT_SET(self
->pre_max_area
,
2567 self
->area
.x
, self
->pre_max_area
.y
,
2568 self
->area
.width
, self
->pre_max_area
.height
);
2570 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2571 RECT_SET(self
->pre_max_area
,
2572 self
->pre_max_area
.x
, self
->area
.y
,
2573 self
->pre_max_area
.width
, self
->area
.height
);
2578 a
= screen_area_monitor(self
->desktop
, 0);
2579 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2580 if (self
->pre_max_area
.width
> 0) {
2581 x
= self
->pre_max_area
.x
;
2582 w
= self
->pre_max_area
.width
;
2584 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2585 0, self
->pre_max_area
.height
);
2587 /* pick some fallbacks... */
2588 x
= a
->x
+ a
->width
/ 4;
2592 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2593 if (self
->pre_max_area
.height
> 0) {
2594 y
= self
->pre_max_area
.y
;
2595 h
= self
->pre_max_area
.height
;
2597 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2598 self
->pre_max_area
.width
, 0);
2600 /* pick some fallbacks... */
2601 y
= a
->y
+ a
->height
/ 4;
2607 if (dir
== 0 || dir
== 1) /* horz */
2608 self
->max_horz
= max
;
2609 if (dir
== 0 || dir
== 2) /* vert */
2610 self
->max_vert
= max
;
2612 client_change_state(self
); /* change the state hints on the client */
2614 client_setup_decor_and_functions(self
);
2616 client_move_resize(self
, x
, y
, w
, h
);
2619 void client_shade(ObClient
*self
, gboolean shade
)
2621 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2622 shade
) || /* can't shade */
2623 self
->shaded
== shade
) return; /* already done */
2625 self
->shaded
= shade
;
2626 client_change_state(self
);
2627 client_change_wm_state(self
); /* the window is being hidden/shown */
2628 /* resize the frame to just the titlebar */
2629 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2632 void client_close(ObClient
*self
)
2636 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2638 /* in the case that the client provides no means to requesting that it
2639 close, we just kill it */
2640 if (!self
->delete_window
)
2644 XXX: itd be cool to do timeouts and shit here for killing the client's
2646 like... if the window is around after 5 seconds, then the close button
2647 turns a nice red, and if this function is called again, the client is
2651 ce
.xclient
.type
= ClientMessage
;
2652 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2653 ce
.xclient
.display
= ob_display
;
2654 ce
.xclient
.window
= self
->window
;
2655 ce
.xclient
.format
= 32;
2656 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2657 ce
.xclient
.data
.l
[1] = event_curtime
;
2658 ce
.xclient
.data
.l
[2] = 0l;
2659 ce
.xclient
.data
.l
[3] = 0l;
2660 ce
.xclient
.data
.l
[4] = 0l;
2661 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2664 void client_kill(ObClient
*self
)
2666 XKillClient(ob_display
, self
->window
);
2669 void client_hilite(ObClient
*self
, gboolean hilite
)
2671 if (self
->demands_attention
== hilite
)
2672 return; /* no change */
2674 /* don't allow focused windows to hilite */
2675 self
->demands_attention
= hilite
&& !client_focused(self
);
2676 if (self
->demands_attention
)
2677 frame_flash_start(self
->frame
);
2679 frame_flash_stop(self
->frame
);
2680 client_change_state(self
);
2683 void client_set_desktop_recursive(ObClient
*self
,
2684 guint target
, gboolean donthide
)
2689 if (target
!= self
->desktop
) {
2691 ob_debug("Setting desktop %u\n", target
+1);
2693 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2695 /* remove from the old desktop(s) */
2696 focus_order_remove(self
);
2698 old
= self
->desktop
;
2699 self
->desktop
= target
;
2700 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2701 /* the frame can display the current desktop state */
2702 frame_adjust_state(self
->frame
);
2703 /* 'move' the window to the new desktop */
2705 client_showhide(self
);
2706 /* raise if it was not already on the desktop */
2707 if (old
!= DESKTOP_ALL
)
2709 if (STRUT_EXISTS(self
->strut
))
2710 screen_update_areas();
2712 /* add to the new desktop(s) */
2713 if (config_focus_new
)
2714 focus_order_to_top(self
);
2716 focus_order_to_bottom(self
);
2719 /* move all transients */
2720 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2721 if (it
->data
!= self
)
2722 if (client_is_direct_child(self
, it
->data
))
2723 client_set_desktop_recursive(it
->data
, target
, donthide
);
2726 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2728 self
= client_search_top_parent(self
);
2729 client_set_desktop_recursive(self
, target
, donthide
);
2732 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2734 while (child
!= parent
&&
2735 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2736 child
= child
->transient_for
;
2737 return child
== parent
;
2740 ObClient
*client_search_modal_child(ObClient
*self
)
2745 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2746 ObClient
*c
= it
->data
;
2747 if ((ret
= client_search_modal_child(c
))) return ret
;
2748 if (c
->modal
) return c
;
2753 gboolean
client_validate(ObClient
*self
)
2757 XSync(ob_display
, FALSE
); /* get all events on the server */
2759 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2760 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2761 XPutBackEvent(ob_display
, &e
);
2768 void client_set_wm_state(ObClient
*self
, glong state
)
2770 if (state
== self
->wmstate
) return; /* no change */
2774 client_iconify(self
, TRUE
, TRUE
);
2777 client_iconify(self
, FALSE
, TRUE
);
2782 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2784 gboolean shaded
= self
->shaded
;
2785 gboolean fullscreen
= self
->fullscreen
;
2786 gboolean undecorated
= self
->undecorated
;
2787 gboolean max_horz
= self
->max_horz
;
2788 gboolean max_vert
= self
->max_vert
;
2789 gboolean modal
= self
->modal
;
2790 gboolean iconic
= self
->iconic
;
2791 gboolean demands_attention
= self
->demands_attention
;
2794 if (!(action
== prop_atoms
.net_wm_state_add
||
2795 action
== prop_atoms
.net_wm_state_remove
||
2796 action
== prop_atoms
.net_wm_state_toggle
))
2797 /* an invalid action was passed to the client message, ignore it */
2800 for (i
= 0; i
< 2; ++i
) {
2801 Atom state
= i
== 0 ? data1
: data2
;
2803 if (!state
) continue;
2805 /* if toggling, then pick whether we're adding or removing */
2806 if (action
== prop_atoms
.net_wm_state_toggle
) {
2807 if (state
== prop_atoms
.net_wm_state_modal
)
2808 action
= modal
? prop_atoms
.net_wm_state_remove
:
2809 prop_atoms
.net_wm_state_add
;
2810 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2811 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2812 prop_atoms
.net_wm_state_add
;
2813 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2814 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2815 prop_atoms
.net_wm_state_add
;
2816 else if (state
== prop_atoms
.net_wm_state_shaded
)
2817 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2818 prop_atoms
.net_wm_state_add
;
2819 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2820 action
= self
->skip_taskbar
?
2821 prop_atoms
.net_wm_state_remove
:
2822 prop_atoms
.net_wm_state_add
;
2823 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2824 action
= self
->skip_pager
?
2825 prop_atoms
.net_wm_state_remove
:
2826 prop_atoms
.net_wm_state_add
;
2827 else if (state
== prop_atoms
.net_wm_state_hidden
)
2828 action
= self
->iconic
?
2829 prop_atoms
.net_wm_state_remove
:
2830 prop_atoms
.net_wm_state_add
;
2831 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2832 action
= fullscreen
?
2833 prop_atoms
.net_wm_state_remove
:
2834 prop_atoms
.net_wm_state_add
;
2835 else if (state
== prop_atoms
.net_wm_state_above
)
2836 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2837 prop_atoms
.net_wm_state_add
;
2838 else if (state
== prop_atoms
.net_wm_state_below
)
2839 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2840 prop_atoms
.net_wm_state_add
;
2841 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2842 action
= self
->demands_attention
?
2843 prop_atoms
.net_wm_state_remove
:
2844 prop_atoms
.net_wm_state_add
;
2845 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2846 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2847 prop_atoms
.net_wm_state_add
;
2850 if (action
== prop_atoms
.net_wm_state_add
) {
2851 if (state
== prop_atoms
.net_wm_state_modal
) {
2853 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2855 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2857 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2859 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2860 self
->skip_taskbar
= TRUE
;
2861 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2862 self
->skip_pager
= TRUE
;
2863 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2865 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2867 } else if (state
== prop_atoms
.net_wm_state_above
) {
2869 self
->below
= FALSE
;
2870 } else if (state
== prop_atoms
.net_wm_state_below
) {
2871 self
->above
= FALSE
;
2873 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2874 demands_attention
= TRUE
;
2875 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2879 } else { /* action == prop_atoms.net_wm_state_remove */
2880 if (state
== prop_atoms
.net_wm_state_modal
) {
2882 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2884 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2886 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2888 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2889 self
->skip_taskbar
= FALSE
;
2890 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2891 self
->skip_pager
= FALSE
;
2892 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2894 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2896 } else if (state
== prop_atoms
.net_wm_state_above
) {
2897 self
->above
= FALSE
;
2898 } else if (state
== prop_atoms
.net_wm_state_below
) {
2899 self
->below
= FALSE
;
2900 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2901 demands_attention
= FALSE
;
2902 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2903 undecorated
= FALSE
;
2907 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2908 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2910 if (max_horz
== max_vert
) { /* both going the same way */
2911 client_maximize(self
, max_horz
, 0);
2913 client_maximize(self
, max_horz
, 1);
2914 client_maximize(self
, max_vert
, 2);
2918 if (max_horz
!= self
->max_horz
)
2919 client_maximize(self
, max_horz
, 1);
2921 client_maximize(self
, max_vert
, 2);
2924 /* change fullscreen state before shading, as it will affect if the window
2926 if (fullscreen
!= self
->fullscreen
)
2927 client_fullscreen(self
, fullscreen
);
2928 if (shaded
!= self
->shaded
)
2929 client_shade(self
, shaded
);
2930 if (undecorated
!= self
->undecorated
)
2931 client_set_undecorated(self
, undecorated
);
2932 if (modal
!= self
->modal
) {
2933 self
->modal
= modal
;
2934 /* when a window changes modality, then its stacking order with its
2935 transients needs to change */
2938 if (iconic
!= self
->iconic
)
2939 client_iconify(self
, iconic
, FALSE
);
2941 if (demands_attention
!= self
->demands_attention
)
2942 client_hilite(self
, demands_attention
);
2944 client_change_state(self
); /* change the hint to reflect these changes */
2947 ObClient
*client_focus_target(ObClient
*self
)
2949 ObClient
*child
= NULL
;
2951 child
= client_search_modal_child(self
);
2952 if (child
) return child
;
2956 gboolean
client_can_focus(ObClient
*self
)
2960 /* choose the correct target */
2961 self
= client_focus_target(self
);
2963 if (!self
->frame
->visible
)
2966 if (!(self
->can_focus
|| self
->focus_notify
))
2969 /* do a check to see if the window has already been unmapped or destroyed
2970 do this intelligently while watching out for unmaps we've generated
2971 (ignore_unmaps > 0) */
2972 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2973 DestroyNotify
, &ev
)) {
2974 XPutBackEvent(ob_display
, &ev
);
2977 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2978 UnmapNotify
, &ev
)) {
2979 if (self
->ignore_unmaps
) {
2980 self
->ignore_unmaps
--;
2982 XPutBackEvent(ob_display
, &ev
);
2990 gboolean
client_focus(ObClient
*self
)
2992 /* choose the correct target */
2993 self
= client_focus_target(self
);
2995 if (!client_can_focus(self
)) {
2996 if (!self
->frame
->visible
) {
2997 /* update the focus lists */
2998 focus_order_to_top(self
);
3003 ob_debug_type(OB_DEBUG_FOCUS
,
3004 "Focusing client \"%s\" at time %u\n",
3005 self
->title
, event_curtime
);
3007 if (self
->can_focus
) {
3008 /* This can cause a BadMatch error with CurrentTime, or if an app
3009 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3010 xerror_set_ignore(TRUE
);
3011 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3013 xerror_set_ignore(FALSE
);
3016 if (self
->focus_notify
) {
3018 ce
.xclient
.type
= ClientMessage
;
3019 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3020 ce
.xclient
.display
= ob_display
;
3021 ce
.xclient
.window
= self
->window
;
3022 ce
.xclient
.format
= 32;
3023 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3024 ce
.xclient
.data
.l
[1] = event_curtime
;
3025 ce
.xclient
.data
.l
[2] = 0l;
3026 ce
.xclient
.data
.l
[3] = 0l;
3027 ce
.xclient
.data
.l
[4] = 0l;
3028 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3032 ob_debug("%sively focusing %lx at %d\n",
3033 (self
->can_focus
? "act" : "pass"),
3034 self
->window
, (gint
) event_curtime
);
3037 /* Cause the FocusIn to come back to us. Important for desktop switches,
3038 since otherwise we'll have no FocusIn on the queue and send it off to
3039 the focus_backup. */
3040 XSync(ob_display
, FALSE
);
3044 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3046 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3048 /* XXX do some stuff here if user is false to determine if we really want
3049 to activate it or not (a parent or group member is currently
3052 ob_debug("Want to activate window 0x%x with time %u (last time %u), "
3054 self
->window
, event_curtime
, last_time
,
3055 (user
? "user" : "application"));
3057 if (!user
&& event_curtime
&& last_time
&&
3058 !event_time_after(event_curtime
, last_time
))
3060 client_hilite(self
, TRUE
);
3062 if (client_normal(self
) && screen_showing_desktop
)
3063 screen_show_desktop(FALSE
);
3065 client_iconify(self
, FALSE
, here
);
3066 if (self
->desktop
!= DESKTOP_ALL
&&
3067 self
->desktop
!= screen_desktop
) {
3069 client_set_desktop(self
, screen_desktop
, FALSE
);
3071 screen_set_desktop(self
->desktop
);
3072 } else if (!self
->frame
->visible
)
3073 /* if its not visible for other reasons, then don't mess
3077 client_shade(self
, FALSE
);
3081 /* we do this an action here. this is rather important. this is because
3082 we want the results from the focus change to take place BEFORE we go
3083 about raising the window. when a fullscreen window loses focus, we
3084 need this or else the raise wont be able to raise above the
3085 to-lose-focus fullscreen window. */
3090 void client_raise(ObClient
*self
)
3092 action_run_string("Raise", self
, CurrentTime
);
3095 void client_lower(ObClient
*self
)
3097 action_run_string("Lower", self
, CurrentTime
);
3100 gboolean
client_focused(ObClient
*self
)
3102 return self
== focus_client
;
3105 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3108 /* si is the smallest image >= req */
3109 /* li is the largest image < req */
3110 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3112 if (!self
->nicons
) {
3113 ObClientIcon
*parent
= NULL
;
3115 if (self
->transient_for
) {
3116 if (self
->transient_for
!= OB_TRAN_GROUP
)
3117 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3120 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3121 ObClient
*c
= it
->data
;
3122 if (c
!= self
&& !c
->transient_for
) {
3123 if ((parent
= client_icon_recursive(c
, w
, h
)))
3133 for (i
= 0; i
< self
->nicons
; ++i
) {
3134 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3135 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3139 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3144 if (largest
== 0) /* didnt find one smaller than the requested size */
3145 return &self
->icons
[si
];
3146 return &self
->icons
[li
];
3149 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3152 static ObClientIcon deficon
;
3154 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3155 deficon
.width
= deficon
.height
= 48;
3156 deficon
.data
= ob_rr_theme
->def_win_icon
;
3162 /* this be mostly ripped from fvwm */
3163 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3165 gint my_cx
, my_cy
, his_cx
, his_cy
;
3168 gint score
, best_score
;
3169 ObClient
*best_client
, *cur
;
3175 /* first, find the centre coords of the currently focused window */
3176 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3177 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3182 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3185 /* the currently selected window isn't interesting */
3188 if (!client_normal(cur
))
3190 /* using c->desktop instead of screen_desktop doesn't work if the
3191 * current window was omnipresent, hope this doesn't have any other
3193 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3197 if(!(client_focus_target(cur
) == cur
&&
3198 client_can_focus(cur
)))
3201 /* find the centre coords of this window, from the
3202 * currently focused window's point of view */
3203 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3204 + cur
->frame
->area
.width
/ 2;
3205 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3206 + cur
->frame
->area
.height
/ 2;
3208 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3209 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3211 /* Rotate the diagonals 45 degrees counterclockwise.
3212 * To do this, multiply the matrix /+h +h\ with the
3213 * vector (x y). \-h +h/
3214 * h = sqrt(0.5). We can set h := 1 since absolute
3215 * distance doesn't matter here. */
3216 tx
= his_cx
+ his_cy
;
3217 his_cy
= -his_cx
+ his_cy
;
3222 case OB_DIRECTION_NORTH
:
3223 case OB_DIRECTION_SOUTH
:
3224 case OB_DIRECTION_NORTHEAST
:
3225 case OB_DIRECTION_SOUTHWEST
:
3226 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3227 distance
= ((dir
== OB_DIRECTION_NORTH
||
3228 dir
== OB_DIRECTION_NORTHEAST
) ?
3231 case OB_DIRECTION_EAST
:
3232 case OB_DIRECTION_WEST
:
3233 case OB_DIRECTION_SOUTHEAST
:
3234 case OB_DIRECTION_NORTHWEST
:
3235 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3236 distance
= ((dir
== OB_DIRECTION_WEST
||
3237 dir
== OB_DIRECTION_NORTHWEST
) ?
3242 /* the target must be in the requested direction */
3246 /* Calculate score for this window. The smaller the better. */
3247 score
= distance
+ offset
;
3249 /* windows more than 45 degrees off the direction are
3250 * heavily penalized and will only be chosen if nothing
3251 * else within a million pixels */
3252 if(offset
> distance
)
3255 if(best_score
== -1 || score
< best_score
)
3263 void client_set_layer(ObClient
*self
, gint layer
)
3267 self
->above
= FALSE
;
3268 } else if (layer
== 0) {
3269 self
->below
= self
->above
= FALSE
;
3271 self
->below
= FALSE
;
3274 client_calc_layer(self
);
3275 client_change_state(self
); /* reflect this in the state hints */
3278 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3280 if (self
->undecorated
!= undecorated
) {
3281 self
->undecorated
= undecorated
;
3282 client_setup_decor_and_functions(self
);
3283 /* Make sure the client knows it might have moved. Maybe there is a
3284 * better way of doing this so only one client_configure is sent, but
3285 * since 125 of these are sent per second when moving the window (with
3286 * user = FALSE) i doubt it matters much.
3288 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3289 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3290 client_change_state(self
); /* reflect this in the state hints */
3294 guint
client_monitor(ObClient
*self
)
3296 return screen_find_monitor(&self
->frame
->area
);
3299 ObClient
*client_search_top_parent(ObClient
*self
)
3301 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3302 self
= self
->transient_for
;
3306 GSList
*client_search_all_top_parents(ObClient
*self
)
3310 /* move up the direct transient chain as far as possible */
3311 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3312 self
= self
->transient_for
;
3314 if (!self
->transient_for
)
3315 ret
= g_slist_prepend(ret
, self
);
3319 g_assert(self
->group
);
3321 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3322 ObClient
*c
= it
->data
;
3324 if (!c
->transient_for
&& client_normal(c
))
3325 ret
= g_slist_prepend(ret
, c
);
3328 if (ret
== NULL
) /* no group parents */
3329 ret
= g_slist_prepend(ret
, self
);
3335 ObClient
*client_search_focus_parent(ObClient
*self
)
3337 if (self
->transient_for
) {
3338 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3339 if (client_focused(self
->transient_for
))
3340 return self
->transient_for
;
3344 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3345 ObClient
*c
= it
->data
;
3347 /* checking transient_for prevents infinate loops! */
3348 if (c
!= self
&& !c
->transient_for
)
3349 if (client_focused(c
))
3358 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3360 if (self
->transient_for
) {
3361 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3362 if (self
->transient_for
== search
)
3367 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3368 ObClient
*c
= it
->data
;
3370 /* checking transient_for prevents infinate loops! */
3371 if (c
!= self
&& !c
->transient_for
)
3381 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3385 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3386 if (sit
->data
== search
)
3388 if (client_search_transient(sit
->data
, search
))
3394 void client_update_sm_client_id(ObClient
*self
)
3396 g_free(self
->sm_client_id
);
3397 self
->sm_client_id
= NULL
;
3399 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3401 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3402 &self
->sm_client_id
);
3405 #define WANT_EDGE(cur, c) \
3408 if(!client_normal(cur)) \
3410 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3414 if(cur->layer < c->layer && !config_resist_layers_below) \
3417 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3418 if ((his_edge_start >= my_edge_start && \
3419 his_edge_start <= my_edge_end) || \
3420 (my_edge_start >= his_edge_start && \
3421 my_edge_start <= his_edge_end)) \
3424 /* finds the nearest edge in the given direction from the current client
3425 * note to self: the edge is the -frame- edge (the actual one), not the
3428 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3430 gint dest
, monitor_dest
;
3431 gint my_edge_start
, my_edge_end
, my_offset
;
3438 a
= screen_area(c
->desktop
);
3439 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3442 case OB_DIRECTION_NORTH
:
3443 my_edge_start
= c
->frame
->area
.x
;
3444 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3445 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3447 /* default: top of screen */
3448 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3449 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3450 /* if the monitor edge comes before the screen edge, */
3451 /* use that as the destination instead. (For xinerama) */
3452 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3453 dest
= monitor_dest
;
3455 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3456 gint his_edge_start
, his_edge_end
, his_offset
;
3457 ObClient
*cur
= it
->data
;
3461 his_edge_start
= cur
->frame
->area
.x
;
3462 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3463 his_offset
= cur
->frame
->area
.y
+
3464 (hang
? 0 : cur
->frame
->area
.height
);
3466 if(his_offset
+ 1 > my_offset
)
3469 if(his_offset
< dest
)
3472 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3475 case OB_DIRECTION_SOUTH
:
3476 my_edge_start
= c
->frame
->area
.x
;
3477 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3478 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3480 /* default: bottom of screen */
3481 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3482 monitor_dest
= monitor
->y
+ monitor
->height
-
3483 (hang
? c
->frame
->area
.height
: 0);
3484 /* if the monitor edge comes before the screen edge, */
3485 /* use that as the destination instead. (For xinerama) */
3486 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3487 dest
= monitor_dest
;
3489 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3490 gint his_edge_start
, his_edge_end
, his_offset
;
3491 ObClient
*cur
= it
->data
;
3495 his_edge_start
= cur
->frame
->area
.x
;
3496 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3497 his_offset
= cur
->frame
->area
.y
+
3498 (hang
? cur
->frame
->area
.height
: 0);
3501 if(his_offset
- 1 < my_offset
)
3504 if(his_offset
> dest
)
3507 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3510 case OB_DIRECTION_WEST
:
3511 my_edge_start
= c
->frame
->area
.y
;
3512 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3513 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3515 /* default: leftmost egde of screen */
3516 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3517 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3518 /* if the monitor edge comes before the screen edge, */
3519 /* use that as the destination instead. (For xinerama) */
3520 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3521 dest
= monitor_dest
;
3523 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3524 gint his_edge_start
, his_edge_end
, his_offset
;
3525 ObClient
*cur
= it
->data
;
3529 his_edge_start
= cur
->frame
->area
.y
;
3530 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3531 his_offset
= cur
->frame
->area
.x
+
3532 (hang
? 0 : cur
->frame
->area
.width
);
3534 if(his_offset
+ 1 > my_offset
)
3537 if(his_offset
< dest
)
3540 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3543 case OB_DIRECTION_EAST
:
3544 my_edge_start
= c
->frame
->area
.y
;
3545 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3546 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3548 /* default: rightmost edge of screen */
3549 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3550 monitor_dest
= monitor
->x
+ monitor
->width
-
3551 (hang
? c
->frame
->area
.width
: 0);
3552 /* if the monitor edge comes before the screen edge, */
3553 /* use that as the destination instead. (For xinerama) */
3554 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3555 dest
= monitor_dest
;
3557 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3558 gint his_edge_start
, his_edge_end
, his_offset
;
3559 ObClient
*cur
= it
->data
;
3563 his_edge_start
= cur
->frame
->area
.y
;
3564 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3565 his_offset
= cur
->frame
->area
.x
+
3566 (hang
? cur
->frame
->area
.width
: 0);
3568 if(his_offset
- 1 < my_offset
)
3571 if(his_offset
> dest
)
3574 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3577 case OB_DIRECTION_NORTHEAST
:
3578 case OB_DIRECTION_SOUTHEAST
:
3579 case OB_DIRECTION_NORTHWEST
:
3580 case OB_DIRECTION_SOUTHWEST
:
3581 /* not implemented */
3583 g_assert_not_reached();
3584 dest
= 0; /* suppress warning */
3589 ObClient
* client_under_pointer()
3593 ObClient
*ret
= NULL
;
3595 if (screen_pointer_pos(&x
, &y
)) {
3596 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3597 if (WINDOW_IS_CLIENT(it
->data
)) {
3598 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3599 if (c
->frame
->visible
&&
3600 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3610 gboolean
client_has_group_siblings(ObClient
*self
)
3612 return self
->group
&& self
->group
->members
->next
;