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 Ben 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 | FocusChangeMask | \
51 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
56 ObClientDestructor func
;
60 GList
*client_list
= NULL
;
62 static GSList
*client_destructors
= NULL
;
63 static Time client_last_user_time
= CurrentTime
;
65 static void client_get_all(ObClient
*self
);
66 static void client_toggle_border(ObClient
*self
, gboolean show
);
67 static void client_get_startup_id(ObClient
*self
);
68 static void client_get_area(ObClient
*self
);
69 static void client_get_desktop(ObClient
*self
);
70 static void client_get_state(ObClient
*self
);
71 static void client_get_shaped(ObClient
*self
);
72 static void client_get_mwm_hints(ObClient
*self
);
73 static void client_get_gravity(ObClient
*self
);
74 static void client_showhide(ObClient
*self
);
75 static void client_change_allowed_actions(ObClient
*self
);
76 static void client_change_state(ObClient
*self
);
77 static void client_apply_startup_state(ObClient
*self
);
78 static void client_restore_session_state(ObClient
*self
);
79 static void client_restore_session_stacking(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 static ObAppSettings
*get_settings(ObClient
*client
)
210 GSList
*a
= config_per_app_settings
;
213 ObAppSettings
*app
= (ObAppSettings
*) a
->data
;
216 (app
->name
&& !app
->class && !strcmp(app
->name
, client
->name
))
217 || (app
->class && !app
->name
&& !strcmp(app
->class, client
->class))
218 || (app
->class && app
->name
&& !strcmp(app
->class, client
->class)
219 && !strcmp(app
->name
, client
->name
))
221 ob_debug("Window matching: %s\n", app
->name
);
222 /* Match if no role was specified in the per app setting, or if the
223 * string matches the beginning of the role, since apps like to set
224 * the role to things like browser-window-23c4b2f */
226 || !strncmp(app
->role
, client
->role
, strlen(app
->role
)))
235 void client_manage(Window window
)
239 XWindowAttributes attrib
;
240 XSetWindowAttributes attrib_set
;
242 gboolean activate
= FALSE
;
243 ObAppSettings
*settings
;
247 /* check if it has already been unmapped by the time we started mapping
248 the grab does a sync so we don't have to here */
249 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
250 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
252 XPutBackEvent(ob_display
, &e
);
255 return; /* don't manage it */
258 /* make sure it isn't an override-redirect window */
259 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
260 attrib
.override_redirect
)
263 return; /* don't manage it */
266 /* is the window a docking app */
267 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
268 if ((wmhint
->flags
& StateHint
) &&
269 wmhint
->initial_state
== WithdrawnState
)
271 dock_add(window
, wmhint
);
279 ob_debug("Managing window: %lx\n", window
);
281 /* choose the events we want to receive on the CLIENT window */
282 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
283 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
284 XChangeWindowAttributes(ob_display
, window
,
285 CWEventMask
|CWDontPropagate
, &attrib_set
);
288 /* create the ObClient struct, and populate it from the hints on the
290 self
= g_new0(ObClient
, 1);
291 self
->obwin
.type
= Window_Client
;
292 self
->window
= window
;
294 /* non-zero defaults */
295 self
->title_count
= 1;
296 self
->wmstate
= NormalState
;
298 self
->desktop
= screen_num_desktops
; /* always an invalid value */
299 self
->user_time
= ~0; /* maximum value, always newer than the real time */
301 client_get_all(self
);
302 client_restore_session_state(self
);
305 Time t
= sn_app_started(self
->startup_id
, self
->class);
306 if (t
) self
->user_time
= t
;
309 /* update the focus lists, do this before the call to change_state or
310 it can end up in the list twice! */
311 focus_order_add_new(self
);
313 client_change_state(self
);
315 /* remove the client's border (and adjust re gravity) */
316 client_toggle_border(self
, FALSE
);
318 /* specify that if we exit, the window should not be destroyed and should
319 be reparented back to root automatically */
320 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
322 /* create the decoration frame for the client window */
323 self
->frame
= frame_new(self
);
325 frame_grab_client(self
->frame
, self
);
329 client_apply_startup_state(self
);
331 /* get and set application level settings */
332 settings
= get_settings(self
);
334 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
335 client_restore_session_stacking(self
);
338 /* Don't worry, we won't actually both shade and undecorate the
339 * window when push comes to shove. */
340 if (settings
->shade
!= -1)
341 client_shade(self
, !!settings
->shade
);
342 if (settings
->decor
!= -1)
343 client_set_undecorated(self
, !settings
->decor
);
344 if (settings
->iconic
!= -1)
345 client_iconify(self
, !!settings
->iconic
, FALSE
);
346 if (settings
->skip_pager
!= -1) {
347 self
->skip_pager
= !!settings
->skip_pager
;
348 client_change_state(self
);
350 if (settings
->skip_taskbar
!= -1) {
351 self
->skip_taskbar
= !!settings
->skip_taskbar
;
352 client_change_state(self
);
355 /* 1 && -1 shouldn't be possible by the code in config.c */
356 if (settings
->max_vert
== 1 && settings
->max_horz
== 1)
357 client_maximize(self
, TRUE
, 0, TRUE
);
358 else if (settings
->max_vert
== 0 && settings
->max_horz
== 0)
359 client_maximize(self
, FALSE
, 0, TRUE
);
360 else if (settings
->max_vert
== 1 && settings
->max_horz
== 0) {
361 client_maximize(self
, TRUE
, 2, TRUE
);
362 client_maximize(self
, FALSE
, 1, TRUE
);
363 } else if (settings
->max_vert
== 0 && settings
->max_horz
== 1) {
364 client_maximize(self
, TRUE
, 1, TRUE
);
365 client_maximize(self
, FALSE
, 2, TRUE
);
368 if (settings
->fullscreen
!= -1)
369 client_fullscreen(self
, !!settings
->fullscreen
, TRUE
);
371 if (settings
->desktop
< screen_num_desktops
372 || settings
->desktop
== DESKTOP_ALL
)
373 client_set_desktop(self
, settings
->desktop
, TRUE
);
375 if (settings
->layer
> -2 && settings
->layer
< 2)
376 client_set_layer(self
, settings
->layer
);
380 /* focus the new window? */
381 if (ob_state() != OB_STATE_STARTING
&&
382 /* this means focus=true for window is same as config_focus_new=true */
383 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
384 client_search_focus_parent(self
)) &&
385 /* this checks for focus=false for the window */
386 (!settings
|| settings
->focus
!= 0) &&
387 /* note the check against Type_Normal/Dialog, not client_normal(self),
388 which would also include other types. in this case we want more
389 strict rules for focus */
390 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
391 self
->type
== OB_CLIENT_TYPE_DIALOG
))
395 if (self
->desktop
!= screen_desktop
) {
396 /* activate the window */
399 gboolean group_foc
= FALSE
;
404 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
406 if (client_focused(it
->data
))
414 (!self
->transient_for
&& (!self
->group
||
415 !self
->group
->members
->next
))) ||
416 client_search_focus_tree_full(self
) ||
418 !client_normal(focus_client
))
420 /* activate the window */
427 if (ob_state() == OB_STATE_RUNNING
) {
428 gint x
= self
->area
.x
, ox
= x
;
429 gint y
= self
->area
.y
, oy
= y
;
432 transient
= place_client(self
, &x
, &y
, settings
);
434 /* make sure the window is visible. */
435 client_find_onscreen(self
, &x
, &y
,
436 self
->frame
->area
.width
,
437 self
->frame
->area
.height
,
438 /* non-normal clients has less rules, and
439 windows that are being restored from a
440 session do also. we can assume you want
441 it back where you saved it. Clients saying
442 they placed themselves are subjected to
443 harder rules, ones that are placed by
444 place.c or by the user are allowed partially
445 off-screen and on xinerama divides (ie,
446 it is up to the placement routines to avoid
447 the xinerama divides) */
449 (((self
->positioned
& PPosition
) &&
450 !(self
->positioned
& USPosition
)) &&
451 client_normal(self
) &&
453 if (x
!= ox
|| y
!= oy
)
454 client_move(self
, x
, y
);
457 keyboard_grab_for_client(self
, TRUE
);
458 mouse_grab_for_client(self
, TRUE
);
461 /* This is focus stealing prevention, if a user_time has been set */
462 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
463 self
->window
, self
->user_time
, client_last_user_time
);
464 if (!self
->user_time
|| self
->user_time
>= client_last_user_time
)
466 /* since focus can change the stacking orders, if we focus the
467 window then the standard raise it gets is not enough, we need
468 to queue one for after the focus change takes place */
471 ob_debug("Focus stealing prevention activated for %s with time %u "
473 self
->title
, self
->user_time
, client_last_user_time
);
474 /* if the client isn't focused, then hilite it so the user
476 client_hilite(self
, TRUE
);
478 /* don't focus it ! (focus stealing prevention) */
483 /* This may look rather odd. Well it's because new windows are added
484 to the stacking order non-intrusively. If we're not going to focus
485 the new window or hilite it, then we raise it to the top. This will
486 take affect for things that don't get focused like splash screens.
487 Also if you don't have focus_new enabled, then it's going to get
488 raised to the top. Legacy begets legacy I guess?
493 /* this has to happen before we try focus the window, but we want it to
494 happen after the client's stacking has been determined or it looks bad
496 client_showhide(self
);
498 /* use client_focus instead of client_activate cuz client_activate does
499 stuff like switch desktops etc and I'm not interested in all that when
500 a window maps since its not based on an action from the user like
501 clicking a window to activate it. so keep the new window out of the way
504 /* if using focus_delay, stop the timer now so that focus doesn't
506 event_halt_focus_delay();
510 /* client_activate does this but we aret using it so we have to do it
512 if (screen_showing_desktop
)
513 screen_show_desktop(FALSE
);
515 /* add to client list/map */
516 client_list
= g_list_append(client_list
, self
);
517 g_hash_table_insert(window_map
, &self
->window
, self
);
519 /* this has to happen after we're in the client_list */
520 if (STRUT_EXISTS(self
->strut
))
521 screen_update_areas();
523 /* update the list hints */
526 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
529 void client_unmanage_all()
531 while (client_list
!= NULL
)
532 client_unmanage(client_list
->data
);
535 void client_unmanage(ObClient
*self
)
540 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
542 g_assert(self
!= NULL
);
544 keyboard_grab_for_client(self
, FALSE
);
545 mouse_grab_for_client(self
, FALSE
);
547 /* potentially fix focusLast */
548 if (config_focus_last
)
549 grab_pointer(TRUE
, OB_CURSOR_NONE
);
551 /* remove the window from our save set */
552 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
554 /* we dont want events no more */
555 XSelectInput(ob_display
, self
->window
, NoEventMask
);
557 frame_hide(self
->frame
);
559 client_list
= g_list_remove(client_list
, self
);
560 stacking_remove(self
);
561 g_hash_table_remove(window_map
, &self
->window
);
563 /* update the focus lists */
564 focus_order_remove(self
);
566 /* once the client is out of the list, update the struts to remove it's
568 if (STRUT_EXISTS(self
->strut
))
569 screen_update_areas();
571 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
572 Destructor
*d
= it
->data
;
573 d
->func(self
, d
->data
);
576 if (focus_client
== self
) {
579 /* focus the last focused window on the desktop, and ignore enter
580 events from the unmap so it doesnt mess with the focus */
581 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
582 /* remove these flags so we don't end up getting focused in the
584 self
->can_focus
= FALSE
;
585 self
->focus_notify
= FALSE
;
587 client_unfocus(self
);
590 /* tell our parent(s) that we're gone */
591 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
592 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
593 if (it
->data
!= self
)
594 ((ObClient
*)it
->data
)->transients
=
595 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
596 } else if (self
->transient_for
) { /* transient of window */
597 self
->transient_for
->transients
=
598 g_slist_remove(self
->transient_for
->transients
, self
);
601 /* tell our transients that we're gone */
602 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
603 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
604 ((ObClient
*)it
->data
)->transient_for
= NULL
;
605 client_calc_layer(it
->data
);
609 /* remove from its group */
611 group_remove(self
->group
, self
);
615 /* give the client its border back */
616 client_toggle_border(self
, TRUE
);
618 /* reparent the window out of the frame, and free the frame */
619 frame_release_client(self
->frame
, self
);
622 if (ob_state() != OB_STATE_EXITING
) {
623 /* these values should not be persisted across a window
625 PROP_ERASE(self
->window
, net_wm_desktop
);
626 PROP_ERASE(self
->window
, net_wm_state
);
627 PROP_ERASE(self
->window
, wm_state
);
629 /* if we're left in an unmapped state, the client wont be mapped. this
630 is bad, since we will no longer be managing the window on restart */
631 XMapWindow(ob_display
, self
->window
);
635 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
637 /* free all data allocated in the client struct */
638 g_slist_free(self
->transients
);
639 for (j
= 0; j
< self
->nicons
; ++j
)
640 g_free(self
->icons
[j
].data
);
641 if (self
->nicons
> 0)
644 g_free(self
->icon_title
);
648 g_free(self
->sm_client_id
);
651 /* update the list hints */
654 if (config_focus_last
)
655 grab_pointer(FALSE
, OB_CURSOR_NONE
);
658 static void client_restore_session_state(ObClient
*self
)
662 if (!(it
= session_state_find(self
)))
665 self
->session
= it
->data
;
667 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
668 self
->positioned
= PPosition
;
669 if (self
->session
->w
> 0)
670 self
->area
.width
= self
->session
->w
;
671 if (self
->session
->h
> 0)
672 self
->area
.height
= self
->session
->h
;
673 XResizeWindow(ob_display
, self
->window
,
674 self
->area
.width
, self
->area
.height
);
676 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
677 self
->session
->desktop
:
678 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
679 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
681 self
->shaded
= self
->session
->shaded
;
682 self
->iconic
= self
->session
->iconic
;
683 self
->skip_pager
= self
->session
->skip_pager
;
684 self
->skip_taskbar
= self
->session
->skip_taskbar
;
685 self
->fullscreen
= self
->session
->fullscreen
;
686 self
->above
= self
->session
->above
;
687 self
->below
= self
->session
->below
;
688 self
->max_horz
= self
->session
->max_horz
;
689 self
->max_vert
= self
->session
->max_vert
;
692 static void client_restore_session_stacking(ObClient
*self
)
696 if (!self
->session
) return;
698 it
= g_list_find(session_saved_state
, self
->session
);
699 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
702 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
703 if (session_state_cmp(it
->data
, cit
->data
))
706 client_calc_layer(self
);
707 stacking_below(CLIENT_AS_WINDOW(self
),
708 CLIENT_AS_WINDOW(cit
->data
));
714 void client_move_onscreen(ObClient
*self
, gboolean rude
)
716 gint x
= self
->area
.x
;
717 gint y
= self
->area
.y
;
718 if (client_find_onscreen(self
, &x
, &y
,
719 self
->frame
->area
.width
,
720 self
->frame
->area
.height
, rude
)) {
721 client_move(self
, x
, y
);
725 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
729 gint ox
= *x
, oy
= *y
;
731 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
734 /* XXX watch for xinerama dead areas */
735 /* This makes sure windows aren't entirely outside of the screen so you
736 can't see them at all.
737 It makes sure 10% of the window is on the screen at least. At don't let
738 it move itself off the top of the screen, which would hide the titlebar
739 on you. (The user can still do this if they want too, it's only limiting
742 if (client_normal(self
)) {
743 a
= screen_area(self
->desktop
);
744 if (!self
->strut
.right
&&
745 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
746 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
747 if (!self
->strut
.bottom
&&
748 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
749 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
750 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
751 *x
= a
->x
- self
->frame
->area
.width
*9/10;
752 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
753 *y
= a
->y
- self
->frame
->area
.width
*9/10;
756 /* This here doesn't let windows even a pixel outside the screen,
757 * when called from client_manage, programs placing themselves are
758 * forced completely onscreen, while things like
759 * xterm -geometry resolution-width/2 will work fine. Trying to
760 * place it completely offscreen will be handled in the above code.
761 * Sorry for this confused comment, i am tired. */
763 /* avoid the xinerama monitor divide while we're at it,
764 * remember to fix the placement stuff to avoid it also and
765 * then remove this XXX */
766 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
767 /* dont let windows map into the strut unless they
768 are bigger than the available area */
770 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
771 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
772 *x
= a
->x
+ a
->width
- w
;
774 if (h
<= a
->height
) {
775 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
776 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
777 *y
= a
->y
+ a
->height
- h
;
781 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
784 return ox
!= *x
|| oy
!= *y
;
787 static void client_toggle_border(ObClient
*self
, gboolean show
)
789 /* adjust our idea of where the client is, based on its border. When the
790 border is removed, the client should now be considered to be in a
792 when re-adding the border to the client, the same operation needs to be
794 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
795 gint x
= oldx
, y
= oldy
;
796 switch(self
->gravity
) {
798 case NorthWestGravity
:
800 case SouthWestGravity
:
802 case NorthEastGravity
:
804 case SouthEastGravity
:
805 if (show
) x
-= self
->border_width
* 2;
806 else x
+= self
->border_width
* 2;
813 if (show
) x
-= self
->border_width
;
814 else x
+= self
->border_width
;
817 switch(self
->gravity
) {
819 case NorthWestGravity
:
821 case NorthEastGravity
:
823 case SouthWestGravity
:
825 case SouthEastGravity
:
826 if (show
) y
-= self
->border_width
* 2;
827 else y
+= self
->border_width
* 2;
834 if (show
) y
-= self
->border_width
;
835 else y
+= self
->border_width
;
842 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
844 /* move the client so it is back it the right spot _with_ its
846 if (x
!= oldx
|| y
!= oldy
)
847 XMoveWindow(ob_display
, self
->window
, x
, y
);
849 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
853 static void client_get_all(ObClient
*self
)
855 client_get_area(self
);
856 client_get_mwm_hints(self
);
858 /* The transient hint is used to pick a type, but the type can also affect
859 transiency (dialogs are always made transients of their group if they
860 have one). This is Havoc's idea, but it is needed to make some apps
861 work right (eg tsclient). */
862 client_update_transient_for(self
);
863 client_get_type(self
);/* this can change the mwmhints for special cases */
864 client_update_transient_for(self
);
866 client_update_wmhints(self
);
867 client_get_startup_id(self
);
868 client_get_desktop(self
);/* uses transient data/group/startup id if a
869 desktop is not specified */
870 client_get_shaped(self
);
872 client_get_state(self
);
875 /* a couple type-based defaults for new windows */
877 /* this makes sure that these windows appear on all desktops */
878 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
879 self
->desktop
= DESKTOP_ALL
;
882 client_update_protocols(self
);
884 client_get_gravity(self
); /* get the attribute gravity */
885 client_update_normal_hints(self
); /* this may override the attribute
888 /* got the type, the mwmhints, the protocols, and the normal hints
889 (min/max sizes), so we're ready to set up the decorations/functions */
890 client_setup_decor_and_functions(self
);
892 client_update_title(self
);
893 client_update_class(self
);
894 client_update_sm_client_id(self
);
895 client_update_strut(self
);
896 client_update_icons(self
);
897 client_update_user_time(self
, FALSE
);
900 static void client_get_startup_id(ObClient
*self
)
902 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
904 PROP_GETS(self
->group
->leader
,
905 net_startup_id
, utf8
, &self
->startup_id
);
908 static void client_get_area(ObClient
*self
)
910 XWindowAttributes wattrib
;
913 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
914 g_assert(ret
!= BadWindow
);
916 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
917 self
->border_width
= wattrib
.border_width
;
920 static void client_get_desktop(ObClient
*self
)
922 guint32 d
= screen_num_desktops
; /* an always-invalid value */
924 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
925 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
926 self
->desktop
= screen_num_desktops
- 1;
930 gboolean trdesk
= FALSE
;
932 if (self
->transient_for
) {
933 if (self
->transient_for
!= OB_TRAN_GROUP
) {
934 self
->desktop
= self
->transient_for
->desktop
;
939 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
940 if (it
->data
!= self
&&
941 !((ObClient
*)it
->data
)->transient_for
) {
942 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
949 /* try get from the startup-notification protocol */
950 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
951 if (self
->desktop
>= screen_num_desktops
&&
952 self
->desktop
!= DESKTOP_ALL
)
953 self
->desktop
= screen_num_desktops
- 1;
955 /* defaults to the current desktop */
956 self
->desktop
= screen_desktop
;
959 if (self
->desktop
!= d
) {
960 /* set the desktop hint, to make sure that it always exists */
961 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
965 static void client_get_state(ObClient
*self
)
970 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
972 for (i
= 0; i
< num
; ++i
) {
973 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
975 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
977 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
979 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
980 self
->skip_taskbar
= TRUE
;
981 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
982 self
->skip_pager
= TRUE
;
983 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
984 self
->fullscreen
= TRUE
;
985 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
986 self
->max_vert
= TRUE
;
987 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
988 self
->max_horz
= TRUE
;
989 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
991 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
993 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
994 self
->demands_attention
= TRUE
;
995 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
996 self
->undecorated
= TRUE
;
1002 if (!(self
->above
|| self
->below
)) {
1004 /* apply stuff from the group */
1008 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1009 ObClient
*c
= it
->data
;
1010 if (c
!= self
&& !client_search_transient(self
, c
) &&
1011 client_normal(self
) && client_normal(c
))
1014 (c
->above
? 1 : (c
->below
? -1 : 0)));
1028 g_assert_not_reached();
1035 static void client_get_shaped(ObClient
*self
)
1037 self
->shaped
= FALSE
;
1039 if (extensions_shape
) {
1044 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1046 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1047 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1049 self
->shaped
= (s
!= 0);
1054 void client_update_transient_for(ObClient
*self
)
1057 ObClient
*target
= NULL
;
1059 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1060 self
->transient
= TRUE
;
1061 if (t
!= self
->window
) { /* cant be transient to itself! */
1062 target
= g_hash_table_lookup(window_map
, &t
);
1063 /* if this happens then we need to check for it*/
1064 g_assert(target
!= self
);
1065 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1066 /* this can happen when a dialog is a child of
1067 a dockapp, for example */
1072 /* we used to do this, but it violates the ICCCM and causes problems because
1073 toolkits seem to set transient_for = root rather arbitrarily (eg kicker's
1074 config dialogs), so it is being removed. the ewmh provides other ways to
1075 make things transient for their group. -dana
1077 if (!target
&& self
->group
) {
1078 /* not transient to a client, see if it is transient for a
1080 if (t
== self
->group
->leader
||
1082 t
== RootWindow(ob_display
, ob_screen
))
1084 /* window is a transient for its group! */
1085 target
= OB_TRAN_GROUP
;
1091 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
1092 self
->transient
= TRUE
;
1093 target
= OB_TRAN_GROUP
;
1095 self
->transient
= FALSE
;
1097 /* if anything has changed... */
1098 if (target
!= self
->transient_for
) {
1099 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1102 /* remove from old parents */
1103 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1104 ObClient
*c
= it
->data
;
1105 if (c
!= self
&& !c
->transient_for
)
1106 c
->transients
= g_slist_remove(c
->transients
, self
);
1108 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1109 /* remove from old parent */
1110 self
->transient_for
->transients
=
1111 g_slist_remove(self
->transient_for
->transients
, self
);
1113 self
->transient_for
= target
;
1114 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1117 /* add to new parents */
1118 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1119 ObClient
*c
= it
->data
;
1120 if (c
!= self
&& !c
->transient_for
)
1121 c
->transients
= g_slist_append(c
->transients
, self
);
1124 /* remove all transients which are in the group, that causes
1125 circlular pointer hell of doom */
1126 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1128 for (sit
= self
->transients
; sit
; sit
= next
) {
1129 next
= g_slist_next(sit
);
1130 if (sit
->data
== it
->data
)
1132 g_slist_delete_link(self
->transients
, sit
);
1135 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1136 /* add to new parent */
1137 self
->transient_for
->transients
=
1138 g_slist_append(self
->transient_for
->transients
, self
);
1143 static void client_get_mwm_hints(ObClient
*self
)
1148 self
->mwmhints
.flags
= 0; /* default to none */
1150 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1152 if (num
>= OB_MWM_ELEMENTS
) {
1153 self
->mwmhints
.flags
= hints
[0];
1154 self
->mwmhints
.functions
= hints
[1];
1155 self
->mwmhints
.decorations
= hints
[2];
1161 void client_get_type(ObClient
*self
)
1168 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1169 /* use the first value that we know about in the array */
1170 for (i
= 0; i
< num
; ++i
) {
1171 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1172 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1173 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1174 self
->type
= OB_CLIENT_TYPE_DOCK
;
1175 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1176 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1177 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1178 self
->type
= OB_CLIENT_TYPE_MENU
;
1179 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1180 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1181 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1182 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1183 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1184 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1185 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1186 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1187 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1188 /* prevent this window from getting any decor or
1190 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1191 OB_MWM_FLAG_DECORATIONS
);
1192 self
->mwmhints
.decorations
= 0;
1193 self
->mwmhints
.functions
= 0;
1195 if (self
->type
!= (ObClientType
) -1)
1196 break; /* grab the first legit type */
1201 if (self
->type
== (ObClientType
) -1) {
1202 /*the window type hint was not set, which means we either classify
1203 ourself as a normal window or a dialog, depending on if we are a
1205 if (self
->transient
)
1206 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1208 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1212 void client_update_protocols(ObClient
*self
)
1215 guint num_return
, i
;
1217 self
->focus_notify
= FALSE
;
1218 self
->delete_window
= FALSE
;
1220 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1221 for (i
= 0; i
< num_return
; ++i
) {
1222 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1223 /* this means we can request the window to close */
1224 self
->delete_window
= TRUE
;
1225 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1226 /* if this protocol is requested, then the window will be
1227 notified whenever we want it to receive focus */
1228 self
->focus_notify
= TRUE
;
1234 static void client_get_gravity(ObClient
*self
)
1236 XWindowAttributes wattrib
;
1239 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1240 g_assert(ret
!= BadWindow
);
1241 self
->gravity
= wattrib
.win_gravity
;
1244 void client_update_normal_hints(ObClient
*self
)
1248 gint oldgravity
= self
->gravity
;
1251 self
->min_ratio
= 0.0f
;
1252 self
->max_ratio
= 0.0f
;
1253 SIZE_SET(self
->size_inc
, 1, 1);
1254 SIZE_SET(self
->base_size
, 0, 0);
1255 SIZE_SET(self
->min_size
, 0, 0);
1256 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1258 /* get the hints from the window */
1259 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1260 /* normal windows can't request placement! har har
1261 if (!client_normal(self))
1263 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1265 if (size
.flags
& PWinGravity
) {
1266 self
->gravity
= size
.win_gravity
;
1268 /* if the client has a frame, i.e. has already been mapped and
1269 is changing its gravity */
1270 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1271 /* move our idea of the client's position based on its new
1273 self
->area
.x
= self
->frame
->area
.x
;
1274 self
->area
.y
= self
->frame
->area
.y
;
1275 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1279 if (size
.flags
& PAspect
) {
1280 if (size
.min_aspect
.y
)
1282 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1283 if (size
.max_aspect
.y
)
1285 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1288 if (size
.flags
& PMinSize
)
1289 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1291 if (size
.flags
& PMaxSize
)
1292 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1294 if (size
.flags
& PBaseSize
)
1295 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1297 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1298 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1302 void client_setup_decor_and_functions(ObClient
*self
)
1304 /* start with everything (cept fullscreen) */
1306 (OB_FRAME_DECOR_TITLEBAR
|
1307 OB_FRAME_DECOR_HANDLE
|
1308 OB_FRAME_DECOR_GRIPS
|
1309 OB_FRAME_DECOR_BORDER
|
1310 OB_FRAME_DECOR_ICON
|
1311 OB_FRAME_DECOR_ALLDESKTOPS
|
1312 OB_FRAME_DECOR_ICONIFY
|
1313 OB_FRAME_DECOR_MAXIMIZE
|
1314 OB_FRAME_DECOR_SHADE
|
1315 OB_FRAME_DECOR_CLOSE
);
1317 (OB_CLIENT_FUNC_RESIZE
|
1318 OB_CLIENT_FUNC_MOVE
|
1319 OB_CLIENT_FUNC_ICONIFY
|
1320 OB_CLIENT_FUNC_MAXIMIZE
|
1321 OB_CLIENT_FUNC_SHADE
|
1322 OB_CLIENT_FUNC_CLOSE
);
1324 if (!(self
->min_size
.width
< self
->max_size
.width
||
1325 self
->min_size
.height
< self
->max_size
.height
))
1326 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1328 switch (self
->type
) {
1329 case OB_CLIENT_TYPE_NORMAL
:
1330 /* normal windows retain all of the possible decorations and
1331 functionality, and are the only windows that you can fullscreen */
1332 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1335 case OB_CLIENT_TYPE_DIALOG
:
1336 case OB_CLIENT_TYPE_UTILITY
:
1337 /* these windows cannot be maximized */
1338 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1341 case OB_CLIENT_TYPE_MENU
:
1342 case OB_CLIENT_TYPE_TOOLBAR
:
1343 /* these windows get less functionality */
1344 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1347 case OB_CLIENT_TYPE_DESKTOP
:
1348 case OB_CLIENT_TYPE_DOCK
:
1349 case OB_CLIENT_TYPE_SPLASH
:
1350 /* none of these windows are manipulated by the window manager */
1351 self
->decorations
= 0;
1352 self
->functions
= 0;
1356 /* Mwm Hints are applied subtractively to what has already been chosen for
1357 decor and functionality */
1358 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1359 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1360 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1361 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1363 /* if the mwm hints request no handle or title, then all
1364 decorations are disabled, but keep the border if that's
1366 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1367 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1369 self
->decorations
= 0;
1374 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1375 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1376 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1377 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1378 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1379 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1380 /* dont let mwm hints kill any buttons
1381 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1382 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1383 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1384 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1386 /* dont let mwm hints kill the close button
1387 if (! (self->mwmhints.functions & MwmFunc_Close))
1388 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1392 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1393 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1394 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1395 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1396 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1397 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1399 /* can't maximize without moving/resizing */
1400 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1401 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1402 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1403 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1404 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1407 /* kill the handle on fully maxed windows */
1408 if (self
->max_vert
&& self
->max_horz
)
1409 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1411 /* finally, the user can have requested no decorations, which overrides
1412 everything (but doesnt give it a border if it doesnt have one) */
1413 if (self
->undecorated
) {
1414 if (config_theme_keepborder
)
1415 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1417 self
->decorations
= 0;
1420 /* if we don't have a titlebar, then we cannot shade! */
1421 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1422 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1424 /* now we need to check against rules for the client's current state */
1425 if (self
->fullscreen
) {
1426 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1427 OB_CLIENT_FUNC_FULLSCREEN
|
1428 OB_CLIENT_FUNC_ICONIFY
);
1429 self
->decorations
= 0;
1432 client_change_allowed_actions(self
);
1435 /* adjust the client's decorations, etc. */
1436 client_reconfigure(self
);
1440 static void client_change_allowed_actions(ObClient
*self
)
1445 /* desktop windows are kept on all desktops */
1446 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1447 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1449 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1450 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1451 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1452 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1453 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1454 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1455 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1456 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1457 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1458 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1459 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1460 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1461 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1462 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1463 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1466 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1468 /* make sure the window isn't breaking any rules now */
1470 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1471 if (self
->frame
) client_shade(self
, FALSE
);
1472 else self
->shaded
= FALSE
;
1474 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1475 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1476 else self
->iconic
= FALSE
;
1478 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1479 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1480 else self
->fullscreen
= FALSE
;
1482 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1484 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1485 else self
->max_vert
= self
->max_horz
= FALSE
;
1489 void client_reconfigure(ObClient
*self
)
1491 /* by making this pass FALSE for user, we avoid the emacs event storm where
1492 every configurenotify causes an update in its normal hints, i think this
1493 is generally what we want anyways... */
1494 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1495 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1498 void client_update_wmhints(ObClient
*self
)
1503 /* assume a window takes input if it doesnt specify */
1504 self
->can_focus
= TRUE
;
1506 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1507 if (hints
->flags
& InputHint
)
1508 self
->can_focus
= hints
->input
;
1510 /* only do this when first managing the window *AND* when we aren't
1512 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1513 if (hints
->flags
& StateHint
)
1514 self
->iconic
= hints
->initial_state
== IconicState
;
1516 if (!(hints
->flags
& WindowGroupHint
))
1517 hints
->window_group
= None
;
1519 /* did the group state change? */
1520 if (hints
->window_group
!=
1521 (self
->group
? self
->group
->leader
: None
)) {
1522 /* remove from the old group if there was one */
1523 if (self
->group
!= NULL
) {
1524 /* remove transients of the group */
1525 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1526 self
->transients
= g_slist_remove(self
->transients
,
1529 /* remove myself from parents in the group */
1530 if (self
->transient_for
== OB_TRAN_GROUP
) {
1531 for (it
= self
->group
->members
; it
;
1532 it
= g_slist_next(it
))
1534 ObClient
*c
= it
->data
;
1536 if (c
!= self
&& !c
->transient_for
)
1537 c
->transients
= g_slist_remove(c
->transients
,
1542 group_remove(self
->group
, self
);
1545 if (hints
->window_group
!= None
) {
1546 self
->group
= group_add(hints
->window_group
, self
);
1548 /* i can only have transients from the group if i am not
1550 if (!self
->transient_for
) {
1551 /* add other transients of the group that are already
1553 for (it
= self
->group
->members
; it
;
1554 it
= g_slist_next(it
))
1556 ObClient
*c
= it
->data
;
1557 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1559 g_slist_append(self
->transients
, c
);
1564 /* because the self->transient flag wont change from this call,
1565 we don't need to update the window's type and such, only its
1566 transient_for, and the transients lists of other windows in
1567 the group may be affected */
1568 client_update_transient_for(self
);
1571 /* the WM_HINTS can contain an icon */
1572 client_update_icons(self
);
1578 void client_update_title(ObClient
*self
)
1584 gboolean read_title
;
1587 old_title
= self
->title
;
1590 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1591 /* try old x stuff */
1592 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1593 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1594 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1595 if (self
->transient
) {
1596 data
= g_strdup("");
1599 data
= g_strdup("Unnamed Window");
1603 if (config_title_number
) {
1605 /* did the title change? then reset the title_count */
1606 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1607 self
->title_count
= 1;
1609 /* look for duplicates and append a number */
1611 for (it
= client_list
; it
; it
= g_list_next(it
))
1612 if (it
->data
!= self
) {
1613 ObClient
*c
= it
->data
;
1615 if (c
->title_count
== 1) {
1616 if (!strcmp(c
->title
, data
))
1617 nums
|= 1 << c
->title_count
;
1622 /* find the beginning of our " - [%u]", this relies on
1623 that syntax being used */
1624 end
= strrchr(c
->title
, '-') - 1;
1625 len
= end
- c
->title
;
1626 if (!strncmp(c
->title
, data
, len
))
1627 nums
|= 1 << c
->title_count
;
1630 /* find first free number */
1631 for (i
= 1; i
<= 32; ++i
)
1632 if (!(nums
& (1 << i
))) {
1633 if (self
->title_count
== 1 || i
== 1)
1634 self
->title_count
= i
;
1637 /* dont display the number for the first window */
1638 if (self
->title_count
> 1) {
1640 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1645 self
->title_count
= 1;
1648 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1652 frame_adjust_title(self
->frame
);
1656 /* update the icon title */
1658 g_free(self
->icon_title
);
1662 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1663 /* try old x stuff */
1664 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)
1665 || PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
))) {
1666 data
= g_strdup(self
->title
);
1670 /* append the title count, dont display the number for the first window.
1671 * We don't need to check for config_title_number here since title_count
1672 * is not set above 1 then. */
1673 if (read_title
&& self
->title_count
> 1) {
1675 newdata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1680 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1682 self
->icon_title
= data
;
1685 void client_update_class(ObClient
*self
)
1690 if (self
->name
) g_free(self
->name
);
1691 if (self
->class) g_free(self
->class);
1692 if (self
->role
) g_free(self
->role
);
1694 self
->name
= self
->class = self
->role
= NULL
;
1696 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1698 self
->name
= g_strdup(data
[0]);
1700 self
->class = g_strdup(data
[1]);
1705 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1708 if (self
->name
== NULL
) self
->name
= g_strdup("");
1709 if (self
->class == NULL
) self
->class = g_strdup("");
1710 if (self
->role
== NULL
) self
->role
= g_strdup("");
1713 void client_update_strut(ObClient
*self
)
1717 gboolean got
= FALSE
;
1720 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1724 STRUT_PARTIAL_SET(strut
,
1725 data
[0], data
[2], data
[1], data
[3],
1726 data
[4], data
[5], data
[8], data
[9],
1727 data
[6], data
[7], data
[10], data
[11]);
1733 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1739 /* use the screen's width/height */
1740 a
= screen_physical_area();
1742 STRUT_PARTIAL_SET(strut
,
1743 data
[0], data
[2], data
[1], data
[3],
1744 a
->y
, a
->y
+ a
->height
- 1,
1745 a
->x
, a
->x
+ a
->width
- 1,
1746 a
->y
, a
->y
+ a
->height
- 1,
1747 a
->x
, a
->x
+ a
->width
- 1);
1753 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1754 0, 0, 0, 0, 0, 0, 0, 0);
1756 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1757 self
->strut
= strut
;
1759 /* updating here is pointless while we're being mapped cuz we're not in
1760 the client list yet */
1762 screen_update_areas();
1766 void client_update_icons(ObClient
*self
)
1772 for (i
= 0; i
< self
->nicons
; ++i
)
1773 g_free(self
->icons
[i
].data
);
1774 if (self
->nicons
> 0)
1775 g_free(self
->icons
);
1778 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1779 /* figure out how many valid icons are in here */
1781 while (num
- i
> 2) {
1785 if (i
> num
|| w
*h
== 0) break;
1789 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1791 /* store the icons */
1793 for (j
= 0; j
< self
->nicons
; ++j
) {
1796 w
= self
->icons
[j
].width
= data
[i
++];
1797 h
= self
->icons
[j
].height
= data
[i
++];
1799 if (w
*h
== 0) continue;
1801 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1802 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1807 self
->icons
[j
].data
[t
] =
1808 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1809 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1810 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1811 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1820 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1821 if (hints
->flags
& IconPixmapHint
) {
1823 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1824 xerror_set_ignore(TRUE
);
1825 if (!RrPixmapToRGBA(ob_rr_inst
,
1827 (hints
->flags
& IconMaskHint
?
1828 hints
->icon_mask
: None
),
1829 &self
->icons
[self
->nicons
-1].width
,
1830 &self
->icons
[self
->nicons
-1].height
,
1831 &self
->icons
[self
->nicons
-1].data
)){
1832 g_free(&self
->icons
[self
->nicons
-1]);
1835 xerror_set_ignore(FALSE
);
1842 frame_adjust_icon(self
->frame
);
1845 void client_update_user_time(ObClient
*self
, gboolean new_event
)
1849 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1850 self
->user_time
= time
;
1851 /* we set this every time, not just when it grows, because in practice
1852 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1853 backward we don't want all windows to stop focusing. we'll just
1854 assume noone is setting times older than the last one, cuz that
1855 would be pretty stupid anyways
1856 However! This is called when a window is mapped to get its user time
1857 but it's an old number, it's not changing it from new user
1858 interaction, so in that case, don't change the last user time.
1861 client_last_user_time
= time
;
1865 static void client_change_state(ObClient
*self
)
1868 gulong netstate
[11];
1871 state
[0] = self
->wmstate
;
1873 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1877 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1879 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1881 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1882 if (self
->skip_taskbar
)
1883 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1884 if (self
->skip_pager
)
1885 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1886 if (self
->fullscreen
)
1887 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1889 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1891 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1893 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1895 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1896 if (self
->demands_attention
)
1897 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1898 if (self
->undecorated
)
1899 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1900 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1902 client_calc_layer(self
);
1905 frame_adjust_state(self
->frame
);
1908 ObClient
*client_search_focus_tree(ObClient
*self
)
1913 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1914 if (client_focused(it
->data
)) return it
->data
;
1915 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1920 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1922 if (self
->transient_for
) {
1923 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1924 return client_search_focus_tree_full(self
->transient_for
);
1927 gboolean recursed
= FALSE
;
1929 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1930 if (!((ObClient
*)it
->data
)->transient_for
) {
1932 if ((c
= client_search_focus_tree_full(it
->data
)))
1941 /* this function checks the whole tree, the client_search_focus_tree~
1942 does not, so we need to check this window */
1943 if (client_focused(self
))
1945 return client_search_focus_tree(self
);
1948 static ObStackingLayer
calc_layer(ObClient
*self
)
1952 if (self
->fullscreen
&&
1953 (client_focused(self
) || client_search_focus_tree(self
)))
1954 l
= OB_STACKING_LAYER_FULLSCREEN
;
1955 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1956 l
= OB_STACKING_LAYER_DESKTOP
;
1957 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1958 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1959 else l
= OB_STACKING_LAYER_ABOVE
;
1961 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1962 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1963 else l
= OB_STACKING_LAYER_NORMAL
;
1968 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1969 ObStackingLayer l
, gboolean raised
)
1971 ObStackingLayer old
, own
;
1975 own
= calc_layer(self
);
1976 self
->layer
= l
> own
? l
: own
;
1978 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1979 client_calc_layer_recursive(it
->data
, orig
,
1980 l
, raised
? raised
: l
!= old
);
1982 if (!raised
&& l
!= old
)
1983 if (orig
->frame
) { /* only restack if the original window is managed */
1984 stacking_remove(CLIENT_AS_WINDOW(self
));
1985 stacking_add(CLIENT_AS_WINDOW(self
));
1989 void client_calc_layer(ObClient
*self
)
1996 /* transients take on the layer of their parents */
1997 self
= client_search_top_transient(self
);
1999 l
= calc_layer(self
);
2001 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
2004 gboolean
client_should_show(ObClient
*self
)
2008 if (client_normal(self
) && screen_showing_desktop
)
2011 if (self->transient_for) {
2012 if (self->transient_for != OB_TRAN_GROUP)
2013 return client_should_show(self->transient_for);
2017 for (it = self->group->members; it; it = g_slist_next(it)) {
2018 ObClient *c = it->data;
2019 if (c != self && !c->transient_for) {
2020 if (client_should_show(c))
2027 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2033 static void client_showhide(ObClient
*self
)
2036 if (client_should_show(self
))
2037 frame_show(self
->frame
);
2039 frame_hide(self
->frame
);
2042 gboolean
client_normal(ObClient
*self
) {
2043 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2044 self
->type
== OB_CLIENT_TYPE_DOCK
||
2045 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2048 static void client_apply_startup_state(ObClient
*self
)
2050 /* these are in a carefully crafted order.. */
2053 self
->iconic
= FALSE
;
2054 client_iconify(self
, TRUE
, FALSE
);
2056 if (self
->fullscreen
) {
2057 self
->fullscreen
= FALSE
;
2058 client_fullscreen(self
, TRUE
, FALSE
);
2060 if (self
->undecorated
) {
2061 self
->undecorated
= FALSE
;
2062 client_set_undecorated(self
, TRUE
);
2065 self
->shaded
= FALSE
;
2066 client_shade(self
, TRUE
);
2068 if (self
->demands_attention
) {
2069 self
->demands_attention
= FALSE
;
2070 client_hilite(self
, TRUE
);
2073 if (self
->max_vert
&& self
->max_horz
) {
2074 self
->max_vert
= self
->max_horz
= FALSE
;
2075 client_maximize(self
, TRUE
, 0, FALSE
);
2076 } else if (self
->max_vert
) {
2077 self
->max_vert
= FALSE
;
2078 client_maximize(self
, TRUE
, 2, FALSE
);
2079 } else if (self
->max_horz
) {
2080 self
->max_horz
= FALSE
;
2081 client_maximize(self
, TRUE
, 1, FALSE
);
2084 /* nothing to do for the other states:
2093 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2094 gint x
, gint y
, gint w
, gint h
,
2095 gboolean user
, gboolean final
,
2096 gboolean force_reply
)
2099 gboolean send_resize_client
;
2100 gboolean moved
= FALSE
, resized
= FALSE
;
2101 guint fdecor
= self
->frame
->decorations
;
2102 gboolean fhorz
= self
->frame
->max_horz
;
2104 /* make the frame recalculate its dimentions n shit without changing
2105 anything visible for real, this way the constraints below can work with
2106 the updated frame dimensions. */
2107 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2109 /* gets the frame's position */
2110 frame_client_gravity(self
->frame
, &x
, &y
);
2112 /* these positions are frame positions, not client positions */
2114 /* set the size and position if fullscreen */
2115 if (self
->fullscreen
) {
2119 i
= client_monitor(self
);
2120 a
= screen_physical_area_monitor(i
);
2127 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2131 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
2133 /* set the size and position if maximized */
2134 if (self
->max_horz
) {
2136 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2138 if (self
->max_vert
) {
2140 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2144 /* gets the client's position */
2145 frame_frame_gravity(self
->frame
, &x
, &y
);
2147 /* these override the above states! if you cant move you can't move! */
2149 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2153 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2154 w
= self
->area
.width
;
2155 h
= self
->area
.height
;
2159 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2160 gint basew
, baseh
, minw
, minh
;
2162 /* base size is substituted with min size if not specified */
2163 if (self
->base_size
.width
|| self
->base_size
.height
) {
2164 basew
= self
->base_size
.width
;
2165 baseh
= self
->base_size
.height
;
2167 basew
= self
->min_size
.width
;
2168 baseh
= self
->min_size
.height
;
2170 /* min size is substituted with base size if not specified */
2171 if (self
->min_size
.width
|| self
->min_size
.height
) {
2172 minw
= self
->min_size
.width
;
2173 minh
= self
->min_size
.height
;
2175 minw
= self
->base_size
.width
;
2176 minh
= self
->base_size
.height
;
2179 /* if this is a user-requested resize, then check against min/max
2182 /* smaller than min size or bigger than max size? */
2183 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2184 if (w
< minw
) w
= minw
;
2185 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2186 if (h
< minh
) h
= minh
;
2191 /* keep to the increments */
2192 w
/= self
->size_inc
.width
;
2193 h
/= self
->size_inc
.height
;
2195 /* you cannot resize to nothing */
2196 if (basew
+ w
< 1) w
= 1 - basew
;
2197 if (baseh
+ h
< 1) h
= 1 - baseh
;
2199 /* store the logical size */
2200 SIZE_SET(self
->logical_size
,
2201 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2202 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2204 w
*= self
->size_inc
.width
;
2205 h
*= self
->size_inc
.height
;
2210 /* adjust the height to match the width for the aspect ratios.
2211 for this, min size is not substituted for base size ever. */
2212 w
-= self
->base_size
.width
;
2213 h
-= self
->base_size
.height
;
2215 if (!self
->fullscreen
) {
2216 if (self
->min_ratio
)
2217 if (h
* self
->min_ratio
> w
) {
2218 h
= (gint
)(w
/ self
->min_ratio
);
2220 /* you cannot resize to nothing */
2223 w
= (gint
)(h
* self
->min_ratio
);
2226 if (self
->max_ratio
)
2227 if (h
* self
->max_ratio
< w
) {
2228 h
= (gint
)(w
/ self
->max_ratio
);
2230 /* you cannot resize to nothing */
2233 w
= (gint
)(h
* self
->min_ratio
);
2238 w
+= self
->base_size
.width
;
2239 h
+= self
->base_size
.height
;
2246 case OB_CORNER_TOPLEFT
:
2248 case OB_CORNER_TOPRIGHT
:
2249 x
-= w
- self
->area
.width
;
2251 case OB_CORNER_BOTTOMLEFT
:
2252 y
-= h
- self
->area
.height
;
2254 case OB_CORNER_BOTTOMRIGHT
:
2255 x
-= w
- self
->area
.width
;
2256 y
-= h
- self
->area
.height
;
2260 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2261 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2263 oldw
= self
->area
.width
;
2264 oldh
= self
->area
.height
;
2265 RECT_SET(self
->area
, x
, y
, w
, h
);
2267 /* for app-requested resizes, always resize if 'resized' is true.
2268 for user-requested ones, only resize if final is true, or when
2269 resizing in redraw mode */
2270 send_resize_client
= ((!user
&& resized
) ||
2272 (resized
&& config_resize_redraw
))));
2274 /* if the client is enlarging, then resize the client before the frame */
2275 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2276 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2278 /* move/resize the frame to match the request */
2280 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2281 moved
= resized
= TRUE
;
2283 if (moved
|| resized
)
2284 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2286 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2289 event
.type
= ConfigureNotify
;
2290 event
.xconfigure
.display
= ob_display
;
2291 event
.xconfigure
.event
= self
->window
;
2292 event
.xconfigure
.window
= self
->window
;
2294 /* root window real coords */
2295 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2297 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2299 event
.xconfigure
.width
= w
;
2300 event
.xconfigure
.height
= h
;
2301 event
.xconfigure
.border_width
= 0;
2302 event
.xconfigure
.above
= self
->frame
->plate
;
2303 event
.xconfigure
.override_redirect
= FALSE
;
2304 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2305 FALSE
, StructureNotifyMask
, &event
);
2309 /* if the client is shrinking, then resize the frame before the client */
2310 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2311 XResizeWindow(ob_display
, self
->window
, w
, h
);
2316 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2320 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2321 self
->fullscreen
== fs
) return; /* already done */
2323 self
->fullscreen
= fs
;
2324 client_change_state(self
); /* change the state hints on the client,
2325 and adjust out layer/stacking */
2329 self
->pre_fullscreen_area
= self
->area
;
2331 /* these are not actually used cuz client_configure will set them
2332 as appropriate when the window is fullscreened */
2337 if (self
->pre_fullscreen_area
.width
> 0 &&
2338 self
->pre_fullscreen_area
.height
> 0)
2340 x
= self
->pre_fullscreen_area
.x
;
2341 y
= self
->pre_fullscreen_area
.y
;
2342 w
= self
->pre_fullscreen_area
.width
;
2343 h
= self
->pre_fullscreen_area
.height
;
2344 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2346 /* pick some fallbacks... */
2347 a
= screen_area_monitor(self
->desktop
, 0);
2348 x
= a
->x
+ a
->width
/ 4;
2349 y
= a
->y
+ a
->height
/ 4;
2355 client_setup_decor_and_functions(self
);
2357 client_move_resize(self
, x
, y
, w
, h
);
2359 /* try focus us when we go into fullscreen mode */
2363 static void client_iconify_recursive(ObClient
*self
,
2364 gboolean iconic
, gboolean curdesk
)
2367 gboolean changed
= FALSE
;
2370 if (self
->iconic
!= iconic
) {
2371 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2374 self
->iconic
= iconic
;
2377 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2380 old
= self
->wmstate
;
2381 self
->wmstate
= IconicState
;
2382 if (old
!= self
->wmstate
)
2383 PROP_MSG(self
->window
, kde_wm_change_state
,
2384 self
->wmstate
, 1, 0, 0);
2386 /* update the focus lists.. iconic windows go to the bottom of
2387 the list, put the new iconic window at the 'top of the
2389 focus_order_to_top(self
);
2397 client_set_desktop(self
, screen_desktop
, FALSE
);
2399 old
= self
->wmstate
;
2400 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2401 if (old
!= self
->wmstate
)
2402 PROP_MSG(self
->window
, kde_wm_change_state
,
2403 self
->wmstate
, 1, 0, 0);
2405 /* this puts it after the current focused window */
2406 focus_order_remove(self
);
2407 focus_order_add_new(self
);
2414 client_change_state(self
);
2415 client_showhide(self
);
2416 if (STRUT_EXISTS(self
->strut
))
2417 screen_update_areas();
2420 /* iconify all transients */
2421 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2422 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2426 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2428 /* move up the transient chain as far as possible first */
2429 self
= client_search_top_transient(self
);
2431 client_iconify_recursive(client_search_top_transient(self
),
2435 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2439 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2440 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2442 /* check if already done */
2444 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2445 if (dir
== 1 && self
->max_horz
) return;
2446 if (dir
== 2 && self
->max_vert
) return;
2448 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2449 if (dir
== 1 && !self
->max_horz
) return;
2450 if (dir
== 2 && !self
->max_vert
) return;
2453 /* we just tell it to configure in the same place and client_configure
2454 worries about filling the screen with the window */
2457 w
= self
->area
.width
;
2458 h
= self
->area
.height
;
2462 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2463 RECT_SET(self
->pre_max_area
,
2464 self
->area
.x
, self
->pre_max_area
.y
,
2465 self
->area
.width
, self
->pre_max_area
.height
);
2467 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2468 RECT_SET(self
->pre_max_area
,
2469 self
->pre_max_area
.x
, self
->area
.y
,
2470 self
->pre_max_area
.width
, self
->area
.height
);
2476 a
= screen_area_monitor(self
->desktop
, 0);
2477 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2478 if (self
->pre_max_area
.width
> 0) {
2479 x
= self
->pre_max_area
.x
;
2480 w
= self
->pre_max_area
.width
;
2482 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2483 0, self
->pre_max_area
.height
);
2485 /* pick some fallbacks... */
2486 x
= a
->x
+ a
->width
/ 4;
2490 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2491 if (self
->pre_max_area
.height
> 0) {
2492 y
= self
->pre_max_area
.y
;
2493 h
= self
->pre_max_area
.height
;
2495 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2496 self
->pre_max_area
.width
, 0);
2498 /* pick some fallbacks... */
2499 y
= a
->y
+ a
->height
/ 4;
2505 if (dir
== 0 || dir
== 1) /* horz */
2506 self
->max_horz
= max
;
2507 if (dir
== 0 || dir
== 2) /* vert */
2508 self
->max_vert
= max
;
2510 client_change_state(self
); /* change the state hints on the client */
2512 client_setup_decor_and_functions(self
);
2514 client_move_resize(self
, x
, y
, w
, h
);
2517 void client_shade(ObClient
*self
, gboolean shade
)
2519 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2520 shade
) || /* can't shade */
2521 self
->shaded
== shade
) return; /* already done */
2523 /* when we're iconic, don't change the wmstate */
2524 if (!self
->iconic
) {
2527 old
= self
->wmstate
;
2528 self
->wmstate
= shade
? IconicState
: NormalState
;
2529 if (old
!= self
->wmstate
)
2530 PROP_MSG(self
->window
, kde_wm_change_state
,
2531 self
->wmstate
, 1, 0, 0);
2534 self
->shaded
= shade
;
2535 client_change_state(self
);
2536 /* resize the frame to just the titlebar */
2537 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2540 void client_close(ObClient
*self
)
2544 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2546 /* in the case that the client provides no means to requesting that it
2547 close, we just kill it */
2548 if (!self
->delete_window
)
2552 XXX: itd be cool to do timeouts and shit here for killing the client's
2554 like... if the window is around after 5 seconds, then the close button
2555 turns a nice red, and if this function is called again, the client is
2559 ce
.xclient
.type
= ClientMessage
;
2560 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2561 ce
.xclient
.display
= ob_display
;
2562 ce
.xclient
.window
= self
->window
;
2563 ce
.xclient
.format
= 32;
2564 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2565 ce
.xclient
.data
.l
[1] = event_curtime
;
2566 ce
.xclient
.data
.l
[2] = 0l;
2567 ce
.xclient
.data
.l
[3] = 0l;
2568 ce
.xclient
.data
.l
[4] = 0l;
2569 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2572 void client_kill(ObClient
*self
)
2574 XKillClient(ob_display
, self
->window
);
2577 void client_hilite(ObClient
*self
, gboolean hilite
)
2579 /* don't allow focused windows to hilite */
2580 self
->demands_attention
= hilite
&& !client_focused(self
);
2581 if (self
->demands_attention
)
2582 frame_flash_start(self
->frame
);
2584 frame_flash_stop(self
->frame
);
2585 client_change_state(self
);
2588 void client_set_desktop_recursive(ObClient
*self
,
2589 guint target
, gboolean donthide
)
2594 if (target
!= self
->desktop
) {
2596 ob_debug("Setting desktop %u\n", target
+1);
2598 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2600 /* remove from the old desktop(s) */
2601 focus_order_remove(self
);
2603 old
= self
->desktop
;
2604 self
->desktop
= target
;
2605 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2606 /* the frame can display the current desktop state */
2607 frame_adjust_state(self
->frame
);
2608 /* 'move' the window to the new desktop */
2610 client_showhide(self
);
2611 /* raise if it was not already on the desktop */
2612 if (old
!= DESKTOP_ALL
)
2614 if (STRUT_EXISTS(self
->strut
))
2615 screen_update_areas();
2617 /* add to the new desktop(s) */
2618 if (config_focus_new
)
2619 focus_order_to_top(self
);
2621 focus_order_to_bottom(self
);
2624 /* move all transients */
2625 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2626 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2630 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2632 client_set_desktop_recursive(client_search_top_transient(self
),
2636 ObClient
*client_search_modal_child(ObClient
*self
)
2641 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2642 ObClient
*c
= it
->data
;
2643 if ((ret
= client_search_modal_child(c
))) return ret
;
2644 if (c
->modal
) return c
;
2649 gboolean
client_validate(ObClient
*self
)
2653 XSync(ob_display
, FALSE
); /* get all events on the server */
2655 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2656 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2657 XPutBackEvent(ob_display
, &e
);
2664 void client_set_wm_state(ObClient
*self
, glong state
)
2666 if (state
== self
->wmstate
) return; /* no change */
2670 client_iconify(self
, TRUE
, TRUE
);
2673 client_iconify(self
, FALSE
, TRUE
);
2678 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2680 gboolean shaded
= self
->shaded
;
2681 gboolean fullscreen
= self
->fullscreen
;
2682 gboolean undecorated
= self
->undecorated
;
2683 gboolean max_horz
= self
->max_horz
;
2684 gboolean max_vert
= self
->max_vert
;
2685 gboolean modal
= self
->modal
;
2686 gboolean iconic
= self
->iconic
;
2687 gboolean demands_attention
= self
->demands_attention
;
2690 if (!(action
== prop_atoms
.net_wm_state_add
||
2691 action
== prop_atoms
.net_wm_state_remove
||
2692 action
== prop_atoms
.net_wm_state_toggle
))
2693 /* an invalid action was passed to the client message, ignore it */
2696 for (i
= 0; i
< 2; ++i
) {
2697 Atom state
= i
== 0 ? data1
: data2
;
2699 if (!state
) continue;
2701 /* if toggling, then pick whether we're adding or removing */
2702 if (action
== prop_atoms
.net_wm_state_toggle
) {
2703 if (state
== prop_atoms
.net_wm_state_modal
)
2704 action
= modal
? prop_atoms
.net_wm_state_remove
:
2705 prop_atoms
.net_wm_state_add
;
2706 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2707 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2708 prop_atoms
.net_wm_state_add
;
2709 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2710 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2711 prop_atoms
.net_wm_state_add
;
2712 else if (state
== prop_atoms
.net_wm_state_shaded
)
2713 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2714 prop_atoms
.net_wm_state_add
;
2715 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2716 action
= self
->skip_taskbar
?
2717 prop_atoms
.net_wm_state_remove
:
2718 prop_atoms
.net_wm_state_add
;
2719 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2720 action
= self
->skip_pager
?
2721 prop_atoms
.net_wm_state_remove
:
2722 prop_atoms
.net_wm_state_add
;
2723 else if (state
== prop_atoms
.net_wm_state_hidden
)
2724 action
= self
->iconic
?
2725 prop_atoms
.net_wm_state_remove
:
2726 prop_atoms
.net_wm_state_add
;
2727 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2728 action
= fullscreen
?
2729 prop_atoms
.net_wm_state_remove
:
2730 prop_atoms
.net_wm_state_add
;
2731 else if (state
== prop_atoms
.net_wm_state_above
)
2732 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2733 prop_atoms
.net_wm_state_add
;
2734 else if (state
== prop_atoms
.net_wm_state_below
)
2735 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2736 prop_atoms
.net_wm_state_add
;
2737 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2738 action
= self
->demands_attention
?
2739 prop_atoms
.net_wm_state_remove
:
2740 prop_atoms
.net_wm_state_add
;
2741 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2742 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2743 prop_atoms
.net_wm_state_add
;
2746 if (action
== prop_atoms
.net_wm_state_add
) {
2747 if (state
== prop_atoms
.net_wm_state_modal
) {
2749 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2751 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2753 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2755 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2756 self
->skip_taskbar
= TRUE
;
2757 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2758 self
->skip_pager
= TRUE
;
2759 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2761 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2763 } else if (state
== prop_atoms
.net_wm_state_above
) {
2765 self
->below
= FALSE
;
2766 } else if (state
== prop_atoms
.net_wm_state_below
) {
2767 self
->above
= FALSE
;
2769 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2770 demands_attention
= TRUE
;
2771 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2775 } else { /* action == prop_atoms.net_wm_state_remove */
2776 if (state
== prop_atoms
.net_wm_state_modal
) {
2778 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2780 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2782 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2784 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2785 self
->skip_taskbar
= FALSE
;
2786 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2787 self
->skip_pager
= FALSE
;
2788 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2790 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2792 } else if (state
== prop_atoms
.net_wm_state_above
) {
2793 self
->above
= FALSE
;
2794 } else if (state
== prop_atoms
.net_wm_state_below
) {
2795 self
->below
= FALSE
;
2796 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2797 demands_attention
= FALSE
;
2798 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2799 undecorated
= FALSE
;
2803 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2804 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2806 if (max_horz
== max_vert
) { /* both going the same way */
2807 client_maximize(self
, max_horz
, 0, TRUE
);
2809 client_maximize(self
, max_horz
, 1, TRUE
);
2810 client_maximize(self
, max_vert
, 2, TRUE
);
2814 if (max_horz
!= self
->max_horz
)
2815 client_maximize(self
, max_horz
, 1, TRUE
);
2817 client_maximize(self
, max_vert
, 2, TRUE
);
2820 /* change fullscreen state before shading, as it will affect if the window
2822 if (fullscreen
!= self
->fullscreen
)
2823 client_fullscreen(self
, fullscreen
, TRUE
);
2824 if (shaded
!= self
->shaded
)
2825 client_shade(self
, shaded
);
2826 if (undecorated
!= self
->undecorated
)
2827 client_set_undecorated(self
, undecorated
);
2828 if (modal
!= self
->modal
) {
2829 self
->modal
= modal
;
2830 /* when a window changes modality, then its stacking order with its
2831 transients needs to change */
2834 if (iconic
!= self
->iconic
)
2835 client_iconify(self
, iconic
, FALSE
);
2837 if (demands_attention
!= self
->demands_attention
)
2838 client_hilite(self
, demands_attention
);
2840 client_calc_layer(self
);
2841 client_change_state(self
); /* change the hint to reflect these changes */
2844 ObClient
*client_focus_target(ObClient
*self
)
2848 /* if we have a modal child, then focus it, not us */
2849 child
= client_search_modal_child(client_search_top_transient(self
));
2850 if (child
) return child
;
2854 gboolean
client_can_focus(ObClient
*self
)
2858 /* choose the correct target */
2859 self
= client_focus_target(self
);
2861 if (!self
->frame
->visible
)
2864 if (!(self
->can_focus
|| self
->focus_notify
))
2867 /* do a check to see if the window has already been unmapped or destroyed
2868 do this intelligently while watching out for unmaps we've generated
2869 (ignore_unmaps > 0) */
2870 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2871 DestroyNotify
, &ev
)) {
2872 XPutBackEvent(ob_display
, &ev
);
2875 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2876 UnmapNotify
, &ev
)) {
2877 if (self
->ignore_unmaps
) {
2878 self
->ignore_unmaps
--;
2880 XPutBackEvent(ob_display
, &ev
);
2888 gboolean
client_focus(ObClient
*self
)
2890 /* choose the correct target */
2891 self
= client_focus_target(self
);
2893 if (!client_can_focus(self
)) {
2894 if (!self
->frame
->visible
) {
2895 /* update the focus lists */
2896 focus_order_to_top(self
);
2901 if (self
->can_focus
) {
2902 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2903 I choose to use it always, hopefully to find errors quicker, if any
2904 are left. (I hate X. I hate focus events.)
2906 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2907 #799. So now it is RevertToNone again.
2909 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2913 if (self
->focus_notify
) {
2915 ce
.xclient
.type
= ClientMessage
;
2916 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2917 ce
.xclient
.display
= ob_display
;
2918 ce
.xclient
.window
= self
->window
;
2919 ce
.xclient
.format
= 32;
2920 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2921 ce
.xclient
.data
.l
[1] = event_curtime
;
2922 ce
.xclient
.data
.l
[2] = 0l;
2923 ce
.xclient
.data
.l
[3] = 0l;
2924 ce
.xclient
.data
.l
[4] = 0l;
2925 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2929 ob_debug("%sively focusing %lx at %d\n",
2930 (self
->can_focus
? "act" : "pass"),
2931 self
->window
, (gint
) event_curtime
);
2934 /* Cause the FocusIn to come back to us. Important for desktop switches,
2935 since otherwise we'll have no FocusIn on the queue and send it off to
2936 the focus_backup. */
2937 XSync(ob_display
, FALSE
);
2941 /* Used when the current client is closed, focus_last will then prevent
2942 * focus from going to the mouse pointer */
2943 void client_unfocus(ObClient
*self
)
2945 if (focus_client
== self
) {
2947 ob_debug("client_unfocus for %lx\n", self
->window
);
2949 focus_fallback(OB_FOCUS_FALLBACK_CLOSED
);
2953 void client_activate(ObClient
*self
, gboolean here
, gboolean user
,
2956 /* XXX do some stuff here if user is false to determine if we really want
2957 to activate it or not (a parent or group member is currently
2961 client_hilite(self
, TRUE
);
2963 if (client_normal(self
) && screen_showing_desktop
)
2964 screen_show_desktop(FALSE
);
2966 client_iconify(self
, FALSE
, here
);
2967 if (self
->desktop
!= DESKTOP_ALL
&&
2968 self
->desktop
!= screen_desktop
) {
2970 client_set_desktop(self
, screen_desktop
, FALSE
);
2972 screen_set_desktop(self
->desktop
);
2973 } else if (!self
->frame
->visible
)
2974 /* if its not visible for other reasons, then don't mess
2978 client_shade(self
, FALSE
);
2982 /* we do this an action here. this is rather important. this is because
2983 we want the results from the focus change to take place BEFORE we go
2984 about raising the window. when a fullscreen window loses focus, we
2985 need this or else the raise wont be able to raise above the
2986 to-lose-focus fullscreen window. */
2991 void client_raise(ObClient
*self
)
2993 action_run_string("Raise", self
, CurrentTime
);
2996 void client_lower(ObClient
*self
)
2998 action_run_string("Lower", self
, CurrentTime
);
3001 gboolean
client_focused(ObClient
*self
)
3003 return self
== focus_client
;
3006 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3009 /* si is the smallest image >= req */
3010 /* li is the largest image < req */
3011 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3013 if (!self
->nicons
) {
3014 ObClientIcon
*parent
= NULL
;
3016 if (self
->transient_for
) {
3017 if (self
->transient_for
!= OB_TRAN_GROUP
)
3018 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3021 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3022 ObClient
*c
= it
->data
;
3023 if (c
!= self
&& !c
->transient_for
) {
3024 if ((parent
= client_icon_recursive(c
, w
, h
)))
3034 for (i
= 0; i
< self
->nicons
; ++i
) {
3035 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3036 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3040 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3045 if (largest
== 0) /* didnt find one smaller than the requested size */
3046 return &self
->icons
[si
];
3047 return &self
->icons
[li
];
3050 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3053 static ObClientIcon deficon
;
3055 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3056 deficon
.width
= deficon
.height
= 48;
3057 deficon
.data
= ob_rr_theme
->def_win_icon
;
3063 /* this be mostly ripped from fvwm */
3064 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3066 gint my_cx
, my_cy
, his_cx
, his_cy
;
3069 gint score
, best_score
;
3070 ObClient
*best_client
, *cur
;
3076 /* first, find the centre coords of the currently focused window */
3077 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3078 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3083 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3086 /* the currently selected window isn't interesting */
3089 if (!client_normal(cur
))
3091 /* using c->desktop instead of screen_desktop doesn't work if the
3092 * current window was omnipresent, hope this doesn't have any other
3094 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3098 if(!(client_focus_target(cur
) == cur
&&
3099 client_can_focus(cur
)))
3102 /* find the centre coords of this window, from the
3103 * currently focused window's point of view */
3104 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3105 + cur
->frame
->area
.width
/ 2;
3106 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3107 + cur
->frame
->area
.height
/ 2;
3109 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3110 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3112 /* Rotate the diagonals 45 degrees counterclockwise.
3113 * To do this, multiply the matrix /+h +h\ with the
3114 * vector (x y). \-h +h/
3115 * h = sqrt(0.5). We can set h := 1 since absolute
3116 * distance doesn't matter here. */
3117 tx
= his_cx
+ his_cy
;
3118 his_cy
= -his_cx
+ his_cy
;
3123 case OB_DIRECTION_NORTH
:
3124 case OB_DIRECTION_SOUTH
:
3125 case OB_DIRECTION_NORTHEAST
:
3126 case OB_DIRECTION_SOUTHWEST
:
3127 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3128 distance
= ((dir
== OB_DIRECTION_NORTH
||
3129 dir
== OB_DIRECTION_NORTHEAST
) ?
3132 case OB_DIRECTION_EAST
:
3133 case OB_DIRECTION_WEST
:
3134 case OB_DIRECTION_SOUTHEAST
:
3135 case OB_DIRECTION_NORTHWEST
:
3136 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3137 distance
= ((dir
== OB_DIRECTION_WEST
||
3138 dir
== OB_DIRECTION_NORTHWEST
) ?
3143 /* the target must be in the requested direction */
3147 /* Calculate score for this window. The smaller the better. */
3148 score
= distance
+ offset
;
3150 /* windows more than 45 degrees off the direction are
3151 * heavily penalized and will only be chosen if nothing
3152 * else within a million pixels */
3153 if(offset
> distance
)
3156 if(best_score
== -1 || score
< best_score
)
3164 void client_set_layer(ObClient
*self
, gint layer
)
3168 self
->above
= FALSE
;
3169 } else if (layer
== 0) {
3170 self
->below
= self
->above
= FALSE
;
3172 self
->below
= FALSE
;
3175 client_calc_layer(self
);
3176 client_change_state(self
); /* reflect this in the state hints */
3179 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3181 if (self
->undecorated
!= undecorated
) {
3182 self
->undecorated
= undecorated
;
3183 client_setup_decor_and_functions(self
);
3184 /* Make sure the client knows it might have moved. Maybe there is a
3185 * better way of doing this so only one client_configure is sent, but
3186 * since 125 of these are sent per second when moving the window (with
3187 * user = FALSE) i doubt it matters much.
3189 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3190 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3191 client_change_state(self
); /* reflect this in the state hints */
3195 /* Determines which physical monitor a client is on by calculating the
3196 area of the part of the client on each monitor. The number of the
3197 monitor containing the greatest area of the client is returned.*/
3198 guint
client_monitor(ObClient
*self
)
3204 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3205 Rect
*area
= screen_physical_area_monitor(i
);
3206 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3210 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3211 v
= r
.width
* r
.height
;
3222 ObClient
*client_search_top_transient(ObClient
*self
)
3224 /* move up the transient chain as far as possible */
3225 if (self
->transient_for
) {
3226 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3227 return client_search_top_transient(self
->transient_for
);
3231 g_assert(self
->group
);
3233 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3234 ObClient
*c
= it
->data
;
3236 /* checking transient_for prevents infinate loops! */
3237 if (c
!= self
&& !c
->transient_for
)
3248 ObClient
*client_search_focus_parent(ObClient
*self
)
3250 if (self
->transient_for
) {
3251 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3252 if (client_focused(self
->transient_for
))
3253 return self
->transient_for
;
3257 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3258 ObClient
*c
= it
->data
;
3260 /* checking transient_for prevents infinate loops! */
3261 if (c
!= self
&& !c
->transient_for
)
3262 if (client_focused(c
))
3271 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3273 if (self
->transient_for
) {
3274 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3275 if (self
->transient_for
== search
)
3280 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3281 ObClient
*c
= it
->data
;
3283 /* checking transient_for prevents infinate loops! */
3284 if (c
!= self
&& !c
->transient_for
)
3294 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3298 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3299 if (sit
->data
== search
)
3301 if (client_search_transient(sit
->data
, search
))
3307 void client_update_sm_client_id(ObClient
*self
)
3309 g_free(self
->sm_client_id
);
3310 self
->sm_client_id
= NULL
;
3312 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3314 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3315 &self
->sm_client_id
);
3318 #define WANT_EDGE(cur, c) \
3321 if(!client_normal(cur)) \
3323 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3327 if(cur->layer < c->layer && !config_resist_layers_below) \
3330 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3331 if ((his_edge_start >= my_edge_start && \
3332 his_edge_start <= my_edge_end) || \
3333 (my_edge_start >= his_edge_start && \
3334 my_edge_start <= his_edge_end)) \
3337 /* finds the nearest edge in the given direction from the current client
3338 * note to self: the edge is the -frame- edge (the actual one), not the
3341 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3343 gint dest
, monitor_dest
;
3344 gint my_edge_start
, my_edge_end
, my_offset
;
3351 a
= screen_area(c
->desktop
);
3352 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3355 case OB_DIRECTION_NORTH
:
3356 my_edge_start
= c
->frame
->area
.x
;
3357 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3358 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3360 /* default: top of screen */
3361 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3362 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3363 /* if the monitor edge comes before the screen edge, */
3364 /* use that as the destination instead. (For xinerama) */
3365 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3366 dest
= monitor_dest
;
3368 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3369 gint his_edge_start
, his_edge_end
, his_offset
;
3370 ObClient
*cur
= it
->data
;
3374 his_edge_start
= cur
->frame
->area
.x
;
3375 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3376 his_offset
= cur
->frame
->area
.y
+
3377 (hang
? 0 : cur
->frame
->area
.height
);
3379 if(his_offset
+ 1 > my_offset
)
3382 if(his_offset
< dest
)
3385 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3388 case OB_DIRECTION_SOUTH
:
3389 my_edge_start
= c
->frame
->area
.x
;
3390 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3391 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3393 /* default: bottom of screen */
3394 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3395 monitor_dest
= monitor
->y
+ monitor
->height
-
3396 (hang
? c
->frame
->area
.height
: 0);
3397 /* if the monitor edge comes before the screen edge, */
3398 /* use that as the destination instead. (For xinerama) */
3399 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3400 dest
= monitor_dest
;
3402 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3403 gint his_edge_start
, his_edge_end
, his_offset
;
3404 ObClient
*cur
= it
->data
;
3408 his_edge_start
= cur
->frame
->area
.x
;
3409 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3410 his_offset
= cur
->frame
->area
.y
+
3411 (hang
? cur
->frame
->area
.height
: 0);
3414 if(his_offset
- 1 < my_offset
)
3417 if(his_offset
> dest
)
3420 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3423 case OB_DIRECTION_WEST
:
3424 my_edge_start
= c
->frame
->area
.y
;
3425 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3426 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3428 /* default: leftmost egde of screen */
3429 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3430 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3431 /* if the monitor edge comes before the screen edge, */
3432 /* use that as the destination instead. (For xinerama) */
3433 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3434 dest
= monitor_dest
;
3436 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3437 gint his_edge_start
, his_edge_end
, his_offset
;
3438 ObClient
*cur
= it
->data
;
3442 his_edge_start
= cur
->frame
->area
.y
;
3443 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3444 his_offset
= cur
->frame
->area
.x
+
3445 (hang
? 0 : cur
->frame
->area
.width
);
3447 if(his_offset
+ 1 > my_offset
)
3450 if(his_offset
< dest
)
3453 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3456 case OB_DIRECTION_EAST
:
3457 my_edge_start
= c
->frame
->area
.y
;
3458 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3459 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3461 /* default: rightmost edge of screen */
3462 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3463 monitor_dest
= monitor
->x
+ monitor
->width
-
3464 (hang
? c
->frame
->area
.width
: 0);
3465 /* if the monitor edge comes before the screen edge, */
3466 /* use that as the destination instead. (For xinerama) */
3467 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3468 dest
= monitor_dest
;
3470 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3471 gint his_edge_start
, his_edge_end
, his_offset
;
3472 ObClient
*cur
= it
->data
;
3476 his_edge_start
= cur
->frame
->area
.y
;
3477 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3478 his_offset
= cur
->frame
->area
.x
+
3479 (hang
? cur
->frame
->area
.width
: 0);
3481 if(his_offset
- 1 < my_offset
)
3484 if(his_offset
> dest
)
3487 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3490 case OB_DIRECTION_NORTHEAST
:
3491 case OB_DIRECTION_SOUTHEAST
:
3492 case OB_DIRECTION_NORTHWEST
:
3493 case OB_DIRECTION_SOUTHWEST
:
3494 /* not implemented */
3496 g_assert_not_reached();
3497 dest
= 0; /* suppress warning */
3502 ObClient
* client_under_pointer()
3506 ObClient
*ret
= NULL
;
3508 if (screen_pointer_pos(&x
, &y
)) {
3509 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3510 if (WINDOW_IS_CLIENT(it
->data
)) {
3511 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3512 if (c
->frame
->visible
&&
3513 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3523 gboolean
client_has_group_siblings(ObClient
*self
)
3525 return self
->group
&& self
->group
->members
->next
;