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
;
61 GSList
*client_destructors
= NULL
;
63 static void client_get_all(ObClient
*self
);
64 static void client_toggle_border(ObClient
*self
, gboolean show
);
65 static void client_get_startup_id(ObClient
*self
);
66 static void client_get_area(ObClient
*self
);
67 static void client_get_desktop(ObClient
*self
);
68 static void client_get_state(ObClient
*self
);
69 static void client_get_shaped(ObClient
*self
);
70 static void client_get_mwm_hints(ObClient
*self
);
71 static void client_get_gravity(ObClient
*self
);
72 static void client_showhide(ObClient
*self
);
73 static void client_change_allowed_actions(ObClient
*self
);
74 static void client_change_state(ObClient
*self
);
75 static void client_apply_startup_state(ObClient
*self
);
76 static void client_restore_session_state(ObClient
*self
);
77 static void client_restore_session_stacking(ObClient
*self
);
78 static void client_urgent_notify(ObClient
*self
);
80 void client_startup(gboolean reconfig
)
87 void client_shutdown(gboolean reconfig
)
91 void client_add_destructor(ObClientDestructor func
, gpointer data
)
93 Destructor
*d
= g_new(Destructor
, 1);
96 client_destructors
= g_slist_prepend(client_destructors
, d
);
99 void client_remove_destructor(ObClientDestructor func
)
103 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
104 Destructor
*d
= it
->data
;
105 if (d
->func
== func
) {
107 client_destructors
= g_slist_delete_link(client_destructors
, it
);
113 void client_set_list()
115 Window
*windows
, *win_it
;
117 guint size
= g_list_length(client_list
);
119 /* create an array of the window ids */
121 windows
= g_new(Window
, size
);
123 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
124 *win_it
= ((ObClient
*)it
->data
)->window
;
128 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
129 net_client_list
, window
, (gulong
*)windows
, size
);
138 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
142 for (it = self->transients; it; it = g_slist_next(it)) {
143 if (!func(it->data, data)) return;
144 client_foreach_transient(it->data, func, data);
148 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
150 if (self->transient_for) {
151 if (self->transient_for != OB_TRAN_GROUP) {
152 if (!func(self->transient_for, data)) return;
153 client_foreach_ancestor(self->transient_for, func, data);
157 for (it = self->group->members; it; it = g_slist_next(it))
158 if (it->data != self &&
159 !((ObClient*)it->data)->transient_for) {
160 if (!func(it->data, data)) return;
161 client_foreach_ancestor(it->data, func, data);
168 void client_manage_all()
173 XWindowAttributes attrib
;
175 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
176 &w
, &w
, &children
, &nchild
);
178 /* remove all icon windows from the list */
179 for (i
= 0; i
< nchild
; i
++) {
180 if (children
[i
] == None
) continue;
181 wmhints
= XGetWMHints(ob_display
, children
[i
]);
183 if ((wmhints
->flags
& IconWindowHint
) &&
184 (wmhints
->icon_window
!= children
[i
]))
185 for (j
= 0; j
< nchild
; j
++)
186 if (children
[j
] == wmhints
->icon_window
) {
194 for (i
= 0; i
< nchild
; ++i
) {
195 if (children
[i
] == None
)
197 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
198 if (attrib
.override_redirect
) continue;
200 if (attrib
.map_state
!= IsUnmapped
)
201 client_manage(children
[i
]);
207 static ObAppSettings
*get_settings(ObClient
*client
)
209 GSList
*a
= config_per_app_settings
;
212 ObAppSettings
*app
= (ObAppSettings
*) a
->data
;
215 (app
->name
&& !app
->class && !strcmp(app
->name
, client
->name
))
216 || (app
->class && !app
->name
&& !strcmp(app
->class, client
->class))
217 || (app
->class && app
->name
&& !strcmp(app
->class, client
->class)
218 && !strcmp(app
->name
, client
->name
))
220 ob_debug("Window matching: %s\n", app
->name
);
221 /* Match if no role was specified in the per app setting, or if the
222 * string matches the beginning of the role, since apps like to set
223 * the role to things like browser-window-23c4b2f */
225 || !strncmp(app
->role
, client
->role
, strlen(app
->role
)))
234 void client_manage(Window window
)
238 XWindowAttributes attrib
;
239 XSetWindowAttributes attrib_set
;
241 gboolean activate
= FALSE
;
242 ObAppSettings
*settings
;
246 /* check if it has already been unmapped by the time we started mapping
247 the grab does a sync so we don't have to here */
248 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
249 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
251 XPutBackEvent(ob_display
, &e
);
254 return; /* don't manage it */
257 /* make sure it isn't an override-redirect window */
258 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
259 attrib
.override_redirect
)
262 return; /* don't manage it */
265 /* is the window a docking app */
266 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
267 if ((wmhint
->flags
& StateHint
) &&
268 wmhint
->initial_state
== WithdrawnState
)
270 dock_add(window
, wmhint
);
278 ob_debug("Managing window: %lx\n", window
);
280 /* choose the events we want to receive on the CLIENT window */
281 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
282 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
283 XChangeWindowAttributes(ob_display
, window
,
284 CWEventMask
|CWDontPropagate
, &attrib_set
);
287 /* create the ObClient struct, and populate it from the hints on the
289 self
= g_new0(ObClient
, 1);
290 self
->obwin
.type
= Window_Client
;
291 self
->window
= window
;
293 /* non-zero defaults */
294 self
->title_count
= 1;
295 self
->wmstate
= NormalState
;
297 self
->desktop
= screen_num_desktops
; /* always an invalid value */
299 client_get_all(self
);
300 client_restore_session_state(self
);
302 sn_app_started(self
->class);
304 /* update the focus lists, do this before the call to change_state or
305 it can end up in the list twice! */
306 focus_order_add_new(self
);
308 client_change_state(self
);
310 /* remove the client's border (and adjust re gravity) */
311 client_toggle_border(self
, FALSE
);
313 /* specify that if we exit, the window should not be destroyed and should
314 be reparented back to root automatically */
315 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
317 /* create the decoration frame for the client window */
318 self
->frame
= frame_new(self
);
320 frame_grab_client(self
->frame
, self
);
324 client_apply_startup_state(self
);
326 /* get and set application level settings */
327 settings
= get_settings(self
);
329 stacking_add(CLIENT_AS_WINDOW(self
));
330 client_restore_session_stacking(self
);
333 /* Don't worry, we won't actually both shade and undecorate the
334 * window when push comes to shove. */
335 if (settings
->shade
!= -1)
336 client_shade(self
, !!settings
->shade
);
337 if (settings
->decor
!= -1)
338 client_set_undecorated(self
, !settings
->decor
);
339 if (settings
->iconic
!= -1)
340 client_iconify(self
, !!settings
->iconic
, FALSE
);
341 if (settings
->skip_pager
!= -1) {
342 self
->skip_pager
= !!settings
->skip_pager
;
343 client_change_state(self
);
345 if (settings
->skip_taskbar
!= -1) {
346 self
->skip_taskbar
= !!settings
->skip_taskbar
;
347 client_change_state(self
);
350 /* 1 && -1 shouldn't be possible by the code in config.c */
351 if (settings
->max_vert
== 1 && settings
->max_horz
== 1)
352 client_maximize(self
, TRUE
, 0, TRUE
);
353 else if (settings
->max_vert
== 0 && settings
->max_horz
== 0)
354 client_maximize(self
, FALSE
, 0, TRUE
);
355 else if (settings
->max_vert
== 1 && settings
->max_horz
== 0) {
356 client_maximize(self
, TRUE
, 2, TRUE
);
357 client_maximize(self
, FALSE
, 1, TRUE
);
358 } else if (settings
->max_vert
== 0 && settings
->max_horz
== 1) {
359 client_maximize(self
, TRUE
, 1, TRUE
);
360 client_maximize(self
, FALSE
, 2, TRUE
);
363 if (settings
->fullscreen
!= -1)
364 client_fullscreen(self
, !!settings
->fullscreen
, TRUE
);
366 if (settings
->desktop
< screen_num_desktops
367 || settings
->desktop
== DESKTOP_ALL
)
368 client_set_desktop(self
, settings
->desktop
, TRUE
);
370 if (settings
->layer
> -2 && settings
->layer
< 2)
371 client_set_layer(self
, settings
->layer
);
375 /* focus the new window? */
376 if (ob_state() != OB_STATE_STARTING
&&
377 /* this means focus=true for window is same as config_focus_new=true */
378 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
379 client_search_focus_parent(self
)) &&
380 /* this checks for focus=false for the window */
381 (!settings
|| settings
->focus
!= 0) &&
382 /* note the check against Type_Normal/Dialog, not client_normal(self),
383 which would also include other types. in this case we want more
384 strict rules for focus */
385 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
386 self
->type
== OB_CLIENT_TYPE_DIALOG
))
390 if (self
->desktop
!= screen_desktop
) {
391 /* activate the window */
394 gboolean group_foc
= FALSE
;
399 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
401 if (client_focused(it
->data
))
409 (!self
->transient_for
&& (!self
->group
||
410 !self
->group
->members
->next
))) ||
411 client_search_focus_tree_full(self
) ||
413 !client_normal(focus_client
))
415 /* activate the window */
422 if (ob_state() == OB_STATE_RUNNING
) {
423 gint x
= self
->area
.x
, ox
= x
;
424 gint y
= self
->area
.y
, oy
= y
;
427 transient
= place_client(self
, &x
, &y
, settings
);
429 /* make sure the window is visible. */
430 client_find_onscreen(self
, &x
, &y
,
431 self
->frame
->area
.width
,
432 self
->frame
->area
.height
,
433 /* non-normal clients has less rules, and
434 windows that are being restored from a
435 session do also. we can assume you want
436 it back where you saved it. Clients saying
437 they placed themselves are subjected to
438 harder rules, ones that are placed by
439 place.c or by the user are allowed partially
440 off-screen and on xinerama divides (ie,
441 it is up to the placement routines to avoid
442 the xinerama divides) */
444 (((self
->positioned
& PPosition
) &&
445 !(self
->positioned
& USPosition
)) &&
446 client_normal(self
) &&
448 if (x
!= ox
|| y
!= oy
)
449 client_move(self
, x
, y
);
452 keyboard_grab_for_client(self
, TRUE
);
453 mouse_grab_for_client(self
, TRUE
);
455 client_showhide(self
);
457 /* use client_focus instead of client_activate cuz client_activate does
458 stuff like switch desktops etc and I'm not interested in all that when
459 a window maps since its not based on an action from the user like
460 clicking a window to activate is. so keep the new window out of the way
463 /* if using focus_delay, stop the timer now so that focus doesn't go
465 event_halt_focus_delay();
468 /* since focus can change the stacking orders, if we focus the window
469 then the standard raise it gets is not enough, we need to queue one
470 for after the focus change takes place */
474 /* client_activate does this but we aret using it so we have to do it
476 if (screen_showing_desktop
)
477 screen_show_desktop(FALSE
);
479 /* add to client list/map */
480 client_list
= g_list_append(client_list
, self
);
481 g_hash_table_insert(window_map
, &self
->window
, self
);
483 /* this has to happen after we're in the client_list */
484 if (STRUT_EXISTS(self
->strut
))
485 screen_update_areas();
487 /* update the list hints */
490 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
493 void client_unmanage_all()
495 while (client_list
!= NULL
)
496 client_unmanage(client_list
->data
);
499 void client_unmanage(ObClient
*self
)
504 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
506 g_assert(self
!= NULL
);
508 keyboard_grab_for_client(self
, FALSE
);
509 mouse_grab_for_client(self
, FALSE
);
511 /* potentially fix focusLast */
512 if (config_focus_last
)
513 grab_pointer(TRUE
, OB_CURSOR_NONE
);
515 /* remove the window from our save set */
516 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
518 /* we dont want events no more */
519 XSelectInput(ob_display
, self
->window
, NoEventMask
);
521 frame_hide(self
->frame
);
523 client_list
= g_list_remove(client_list
, self
);
524 stacking_remove(self
);
525 g_hash_table_remove(window_map
, &self
->window
);
527 /* update the focus lists */
528 focus_order_remove(self
);
530 /* once the client is out of the list, update the struts to remove it's
532 if (STRUT_EXISTS(self
->strut
))
533 screen_update_areas();
535 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
536 Destructor
*d
= it
->data
;
537 d
->func(self
, d
->data
);
540 if (focus_client
== self
) {
543 /* focus the last focused window on the desktop, and ignore enter
544 events from the unmap so it doesnt mess with the focus */
545 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
546 /* remove these flags so we don't end up getting focused in the
548 self
->can_focus
= FALSE
;
549 self
->focus_notify
= FALSE
;
551 client_unfocus(self
);
554 /* tell our parent(s) that we're gone */
555 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
556 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
557 if (it
->data
!= self
)
558 ((ObClient
*)it
->data
)->transients
=
559 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
560 } else if (self
->transient_for
) { /* transient of window */
561 self
->transient_for
->transients
=
562 g_slist_remove(self
->transient_for
->transients
, self
);
565 /* tell our transients that we're gone */
566 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
567 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
568 ((ObClient
*)it
->data
)->transient_for
= NULL
;
569 client_calc_layer(it
->data
);
573 /* remove from its group */
575 group_remove(self
->group
, self
);
579 /* give the client its border back */
580 client_toggle_border(self
, TRUE
);
582 /* reparent the window out of the frame, and free the frame */
583 frame_release_client(self
->frame
, self
);
586 if (ob_state() != OB_STATE_EXITING
) {
587 /* these values should not be persisted across a window
589 PROP_ERASE(self
->window
, net_wm_desktop
);
590 PROP_ERASE(self
->window
, net_wm_state
);
591 PROP_ERASE(self
->window
, wm_state
);
593 /* if we're left in an unmapped state, the client wont be mapped. this
594 is bad, since we will no longer be managing the window on restart */
595 XMapWindow(ob_display
, self
->window
);
599 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
601 /* free all data allocated in the client struct */
602 g_slist_free(self
->transients
);
603 for (j
= 0; j
< self
->nicons
; ++j
)
604 g_free(self
->icons
[j
].data
);
605 if (self
->nicons
> 0)
608 g_free(self
->icon_title
);
612 g_free(self
->sm_client_id
);
615 /* update the list hints */
618 if (config_focus_last
)
619 grab_pointer(FALSE
, OB_CURSOR_NONE
);
622 static void client_urgent_notify(ObClient
*self
)
625 frame_flash_start(self
->frame
);
627 frame_flash_stop(self
->frame
);
630 static void client_restore_session_state(ObClient
*self
)
634 if (!(it
= session_state_find(self
)))
637 self
->session
= it
->data
;
639 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
640 self
->positioned
= PPosition
;
641 if (self
->session
->w
> 0)
642 self
->area
.width
= self
->session
->w
;
643 if (self
->session
->h
> 0)
644 self
->area
.height
= self
->session
->h
;
645 XResizeWindow(ob_display
, self
->window
,
646 self
->area
.width
, self
->area
.height
);
648 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
649 self
->session
->desktop
:
650 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
651 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
653 self
->shaded
= self
->session
->shaded
;
654 self
->iconic
= self
->session
->iconic
;
655 self
->skip_pager
= self
->session
->skip_pager
;
656 self
->skip_taskbar
= self
->session
->skip_taskbar
;
657 self
->fullscreen
= self
->session
->fullscreen
;
658 self
->above
= self
->session
->above
;
659 self
->below
= self
->session
->below
;
660 self
->max_horz
= self
->session
->max_horz
;
661 self
->max_vert
= self
->session
->max_vert
;
664 static void client_restore_session_stacking(ObClient
*self
)
668 if (!self
->session
) return;
670 it
= g_list_find(session_saved_state
, self
->session
);
671 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
674 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
675 if (session_state_cmp(it
->data
, cit
->data
))
678 client_calc_layer(self
);
679 stacking_below(CLIENT_AS_WINDOW(self
),
680 CLIENT_AS_WINDOW(cit
->data
));
686 void client_move_onscreen(ObClient
*self
, gboolean rude
)
688 gint x
= self
->area
.x
;
689 gint y
= self
->area
.y
;
690 if (client_find_onscreen(self
, &x
, &y
,
691 self
->frame
->area
.width
,
692 self
->frame
->area
.height
, rude
)) {
693 client_move(self
, x
, y
);
697 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
701 gint ox
= *x
, oy
= *y
;
703 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
706 /* XXX watch for xinerama dead areas */
707 /* This makes sure windows aren't entirely outside of the screen so you
708 can't see them at all.
709 It makes sure 10% of the window is on the screen at least. At don't let
710 it move itself off the top of the screen, which would hide the titlebar
711 on you. (The user can still do this if they want too, it's only limiting
714 if (client_normal(self
)) {
715 a
= screen_area(self
->desktop
);
716 if (!self
->strut
.right
&&
717 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
718 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
719 if (!self
->strut
.bottom
&&
720 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
721 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
722 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
723 *x
= a
->x
- self
->frame
->area
.width
*9/10;
724 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
725 *y
= a
->y
- self
->frame
->area
.width
*9/10;
728 /* This here doesn't let windows even a pixel outside the screen,
729 * when called from client_manage, programs placing themselves are
730 * forced completely onscreen, while things like
731 * xterm -geometry resolution-width/2 will work fine. Trying to
732 * place it completely offscreen will be handled in the above code.
733 * Sorry for this confused comment, i am tired. */
735 /* avoid the xinerama monitor divide while we're at it,
736 * remember to fix the placement stuff to avoid it also and
737 * then remove this XXX */
738 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
739 /* dont let windows map into the strut unless they
740 are bigger than the available area */
742 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
743 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
744 *x
= a
->x
+ a
->width
- w
;
746 if (h
<= a
->height
) {
747 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
748 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
749 *y
= a
->y
+ a
->height
- h
;
753 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
756 return ox
!= *x
|| oy
!= *y
;
759 static void client_toggle_border(ObClient
*self
, gboolean show
)
761 /* adjust our idea of where the client is, based on its border. When the
762 border is removed, the client should now be considered to be in a
764 when re-adding the border to the client, the same operation needs to be
766 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
767 gint x
= oldx
, y
= oldy
;
768 switch(self
->gravity
) {
770 case NorthWestGravity
:
772 case SouthWestGravity
:
774 case NorthEastGravity
:
776 case SouthEastGravity
:
777 if (show
) x
-= self
->border_width
* 2;
778 else x
+= self
->border_width
* 2;
785 if (show
) x
-= self
->border_width
;
786 else x
+= self
->border_width
;
789 switch(self
->gravity
) {
791 case NorthWestGravity
:
793 case NorthEastGravity
:
795 case SouthWestGravity
:
797 case SouthEastGravity
:
798 if (show
) y
-= self
->border_width
* 2;
799 else y
+= self
->border_width
* 2;
806 if (show
) y
-= self
->border_width
;
807 else y
+= self
->border_width
;
814 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
816 /* move the client so it is back it the right spot _with_ its
818 if (x
!= oldx
|| y
!= oldy
)
819 XMoveWindow(ob_display
, self
->window
, x
, y
);
821 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
825 static void client_get_all(ObClient
*self
)
827 client_get_area(self
);
828 client_get_mwm_hints(self
);
830 /* The transient hint is used to pick a type, but the type can also affect
831 transiency (dialogs are always made transients of their group if they
832 have one). This is Havoc's idea, but it is needed to make some apps
833 work right (eg tsclient). */
834 client_update_transient_for(self
);
835 client_get_type(self
);/* this can change the mwmhints for special cases */
836 client_update_transient_for(self
);
838 client_update_wmhints(self
);
839 client_get_startup_id(self
);
840 client_get_desktop(self
);/* uses transient data/group/startup id if a
841 desktop is not specified */
842 client_get_shaped(self
);
844 client_get_state(self
);
847 /* a couple type-based defaults for new windows */
849 /* this makes sure that these windows appear on all desktops */
850 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
851 self
->desktop
= DESKTOP_ALL
;
854 client_update_protocols(self
);
856 client_get_gravity(self
); /* get the attribute gravity */
857 client_update_normal_hints(self
); /* this may override the attribute
860 /* got the type, the mwmhints, the protocols, and the normal hints
861 (min/max sizes), so we're ready to set up the decorations/functions */
862 client_setup_decor_and_functions(self
);
864 client_update_title(self
);
865 client_update_class(self
);
866 client_update_sm_client_id(self
);
867 client_update_strut(self
);
868 client_update_icons(self
);
871 static void client_get_startup_id(ObClient
*self
)
873 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
875 PROP_GETS(self
->group
->leader
,
876 net_startup_id
, utf8
, &self
->startup_id
);
879 static void client_get_area(ObClient
*self
)
881 XWindowAttributes wattrib
;
884 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
885 g_assert(ret
!= BadWindow
);
887 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
888 self
->border_width
= wattrib
.border_width
;
891 static void client_get_desktop(ObClient
*self
)
893 guint32 d
= screen_num_desktops
; /* an always-invalid value */
895 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
896 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
897 self
->desktop
= screen_num_desktops
- 1;
901 gboolean trdesk
= FALSE
;
903 if (self
->transient_for
) {
904 if (self
->transient_for
!= OB_TRAN_GROUP
) {
905 self
->desktop
= self
->transient_for
->desktop
;
910 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
911 if (it
->data
!= self
&&
912 !((ObClient
*)it
->data
)->transient_for
) {
913 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
920 /* try get from the startup-notification protocol */
921 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
922 if (self
->desktop
>= screen_num_desktops
&&
923 self
->desktop
!= DESKTOP_ALL
)
924 self
->desktop
= screen_num_desktops
- 1;
926 /* defaults to the current desktop */
927 self
->desktop
= screen_desktop
;
930 if (self
->desktop
!= d
) {
931 /* set the desktop hint, to make sure that it always exists */
932 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
936 static void client_get_state(ObClient
*self
)
941 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
943 for (i
= 0; i
< num
; ++i
) {
944 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
946 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
948 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
950 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
951 self
->skip_taskbar
= TRUE
;
952 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
953 self
->skip_pager
= TRUE
;
954 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
955 self
->fullscreen
= TRUE
;
956 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
957 self
->max_vert
= TRUE
;
958 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
959 self
->max_horz
= TRUE
;
960 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
962 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
964 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
965 self
->undecorated
= TRUE
;
971 if (!(self
->above
|| self
->below
)) {
973 /* apply stuff from the group */
977 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
978 ObClient
*c
= it
->data
;
979 if (c
!= self
&& !client_search_transient(self
, c
) &&
980 client_normal(self
) && client_normal(c
))
983 (c
->above
? 1 : (c
->below
? -1 : 0)));
997 g_assert_not_reached();
1004 static void client_get_shaped(ObClient
*self
)
1006 self
->shaped
= FALSE
;
1008 if (extensions_shape
) {
1013 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1015 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1016 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1018 self
->shaped
= (s
!= 0);
1023 void client_update_transient_for(ObClient
*self
)
1026 ObClient
*target
= NULL
;
1028 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1029 self
->transient
= TRUE
;
1030 if (t
!= self
->window
) { /* cant be transient to itself! */
1031 target
= g_hash_table_lookup(window_map
, &t
);
1032 /* if this happens then we need to check for it*/
1033 g_assert(target
!= self
);
1034 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1035 /* this can happen when a dialog is a child of
1036 a dockapp, for example */
1041 /* we used to do this, but it violates the ICCCM and causes problems because
1042 toolkits seem to set transient_for = root rather arbitrarily (eg kicker's
1043 config dialogs), so it is being removed. the ewmh provides other ways to
1044 make things transient for their group. -dana
1046 if (!target
&& self
->group
) {
1047 /* not transient to a client, see if it is transient for a
1049 if (t
== self
->group
->leader
||
1051 t
== RootWindow(ob_display
, ob_screen
))
1053 /* window is a transient for its group! */
1054 target
= OB_TRAN_GROUP
;
1060 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
&& self
->group
) {
1061 self
->transient
= TRUE
;
1062 target
= OB_TRAN_GROUP
;
1064 self
->transient
= FALSE
;
1066 /* if anything has changed... */
1067 if (target
!= self
->transient_for
) {
1068 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1071 /* remove from old parents */
1072 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1073 ObClient
*c
= it
->data
;
1074 if (c
!= self
&& !c
->transient_for
)
1075 c
->transients
= g_slist_remove(c
->transients
, self
);
1077 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1078 /* remove from old parent */
1079 self
->transient_for
->transients
=
1080 g_slist_remove(self
->transient_for
->transients
, self
);
1082 self
->transient_for
= target
;
1083 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1086 /* add to new parents */
1087 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1088 ObClient
*c
= it
->data
;
1089 if (c
!= self
&& !c
->transient_for
)
1090 c
->transients
= g_slist_append(c
->transients
, self
);
1093 /* remove all transients which are in the group, that causes
1094 circlular pointer hell of doom */
1095 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1097 for (sit
= self
->transients
; sit
; sit
= next
) {
1098 next
= g_slist_next(sit
);
1099 if (sit
->data
== it
->data
)
1101 g_slist_delete_link(self
->transients
, sit
);
1104 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1105 /* add to new parent */
1106 self
->transient_for
->transients
=
1107 g_slist_append(self
->transient_for
->transients
, self
);
1112 static void client_get_mwm_hints(ObClient
*self
)
1117 self
->mwmhints
.flags
= 0; /* default to none */
1119 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1121 if (num
>= OB_MWM_ELEMENTS
) {
1122 self
->mwmhints
.flags
= hints
[0];
1123 self
->mwmhints
.functions
= hints
[1];
1124 self
->mwmhints
.decorations
= hints
[2];
1130 void client_get_type(ObClient
*self
)
1137 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1138 /* use the first value that we know about in the array */
1139 for (i
= 0; i
< num
; ++i
) {
1140 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1141 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1142 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1143 self
->type
= OB_CLIENT_TYPE_DOCK
;
1144 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1145 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1146 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1147 self
->type
= OB_CLIENT_TYPE_MENU
;
1148 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1149 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1150 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1151 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1152 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1153 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1154 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1155 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1156 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1157 /* prevent this window from getting any decor or
1159 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1160 OB_MWM_FLAG_DECORATIONS
);
1161 self
->mwmhints
.decorations
= 0;
1162 self
->mwmhints
.functions
= 0;
1164 if (self
->type
!= (ObClientType
) -1)
1165 break; /* grab the first legit type */
1170 if (self
->type
== (ObClientType
) -1) {
1171 /*the window type hint was not set, which means we either classify
1172 ourself as a normal window or a dialog, depending on if we are a
1174 if (self
->transient
)
1175 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1177 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1181 void client_update_protocols(ObClient
*self
)
1184 guint num_return
, i
;
1186 self
->focus_notify
= FALSE
;
1187 self
->delete_window
= FALSE
;
1189 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1190 for (i
= 0; i
< num_return
; ++i
) {
1191 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
1192 /* this means we can request the window to close */
1193 self
->delete_window
= TRUE
;
1194 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1195 /* if this protocol is requested, then the window will be
1196 notified whenever we want it to receive focus */
1197 self
->focus_notify
= TRUE
;
1203 static void client_get_gravity(ObClient
*self
)
1205 XWindowAttributes wattrib
;
1208 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1209 g_assert(ret
!= BadWindow
);
1210 self
->gravity
= wattrib
.win_gravity
;
1213 void client_update_normal_hints(ObClient
*self
)
1217 gint oldgravity
= self
->gravity
;
1220 self
->min_ratio
= 0.0f
;
1221 self
->max_ratio
= 0.0f
;
1222 SIZE_SET(self
->size_inc
, 1, 1);
1223 SIZE_SET(self
->base_size
, 0, 0);
1224 SIZE_SET(self
->min_size
, 0, 0);
1225 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1227 /* get the hints from the window */
1228 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1229 /* normal windows can't request placement! har har
1230 if (!client_normal(self))
1232 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1234 if (size
.flags
& PWinGravity
) {
1235 self
->gravity
= size
.win_gravity
;
1237 /* if the client has a frame, i.e. has already been mapped and
1238 is changing its gravity */
1239 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1240 /* move our idea of the client's position based on its new
1242 self
->area
.x
= self
->frame
->area
.x
;
1243 self
->area
.y
= self
->frame
->area
.y
;
1244 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1248 if (size
.flags
& PAspect
) {
1249 if (size
.min_aspect
.y
)
1251 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1252 if (size
.max_aspect
.y
)
1254 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1257 if (size
.flags
& PMinSize
)
1258 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1260 if (size
.flags
& PMaxSize
)
1261 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1263 if (size
.flags
& PBaseSize
)
1264 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1266 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1267 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1271 void client_setup_decor_and_functions(ObClient
*self
)
1273 /* start with everything (cept fullscreen) */
1275 (OB_FRAME_DECOR_TITLEBAR
|
1276 OB_FRAME_DECOR_HANDLE
|
1277 OB_FRAME_DECOR_GRIPS
|
1278 OB_FRAME_DECOR_BORDER
|
1279 OB_FRAME_DECOR_ICON
|
1280 OB_FRAME_DECOR_ALLDESKTOPS
|
1281 OB_FRAME_DECOR_ICONIFY
|
1282 OB_FRAME_DECOR_MAXIMIZE
|
1283 OB_FRAME_DECOR_SHADE
|
1284 OB_FRAME_DECOR_CLOSE
);
1286 (OB_CLIENT_FUNC_RESIZE
|
1287 OB_CLIENT_FUNC_MOVE
|
1288 OB_CLIENT_FUNC_ICONIFY
|
1289 OB_CLIENT_FUNC_MAXIMIZE
|
1290 OB_CLIENT_FUNC_SHADE
|
1291 OB_CLIENT_FUNC_CLOSE
);
1293 if (!(self
->min_size
.width
< self
->max_size
.width
||
1294 self
->min_size
.height
< self
->max_size
.height
))
1295 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1297 switch (self
->type
) {
1298 case OB_CLIENT_TYPE_NORMAL
:
1299 /* normal windows retain all of the possible decorations and
1300 functionality, and are the only windows that you can fullscreen */
1301 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1304 case OB_CLIENT_TYPE_DIALOG
:
1305 case OB_CLIENT_TYPE_UTILITY
:
1306 /* these windows cannot be maximized */
1307 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1310 case OB_CLIENT_TYPE_MENU
:
1311 case OB_CLIENT_TYPE_TOOLBAR
:
1312 /* these windows get less functionality */
1313 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1316 case OB_CLIENT_TYPE_DESKTOP
:
1317 case OB_CLIENT_TYPE_DOCK
:
1318 case OB_CLIENT_TYPE_SPLASH
:
1319 /* none of these windows are manipulated by the window manager */
1320 self
->decorations
= 0;
1321 self
->functions
= 0;
1325 /* Mwm Hints are applied subtractively to what has already been chosen for
1326 decor and functionality */
1327 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1328 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1329 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1330 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1332 /* if the mwm hints request no handle or title, then all
1333 decorations are disabled, but keep the border if that's
1335 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1336 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1338 self
->decorations
= 0;
1343 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1344 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1345 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1346 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1347 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1348 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1349 /* dont let mwm hints kill any buttons
1350 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1351 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1352 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1353 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1355 /* dont let mwm hints kill the close button
1356 if (! (self->mwmhints.functions & MwmFunc_Close))
1357 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1361 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1362 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1363 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1364 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1365 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1366 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1368 /* can't maximize without moving/resizing */
1369 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1370 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1371 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1372 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1373 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1376 /* kill the handle on fully maxed windows */
1377 if (self
->max_vert
&& self
->max_horz
)
1378 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1380 /* finally, the user can have requested no decorations, which overrides
1381 everything (but doesnt give it a border if it doesnt have one) */
1382 if (self
->undecorated
) {
1383 if (config_theme_keepborder
)
1384 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1386 self
->decorations
= 0;
1389 /* if we don't have a titlebar, then we cannot shade! */
1390 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1391 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1393 /* now we need to check against rules for the client's current state */
1394 if (self
->fullscreen
) {
1395 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1396 OB_CLIENT_FUNC_FULLSCREEN
|
1397 OB_CLIENT_FUNC_ICONIFY
);
1398 self
->decorations
= 0;
1401 client_change_allowed_actions(self
);
1404 /* adjust the client's decorations, etc. */
1405 client_reconfigure(self
);
1409 static void client_change_allowed_actions(ObClient
*self
)
1414 /* desktop windows are kept on all desktops */
1415 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1416 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1418 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1419 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1420 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1421 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1422 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1423 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1424 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1425 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1426 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1427 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1428 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1429 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1430 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1431 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1432 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1435 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1437 /* make sure the window isn't breaking any rules now */
1439 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1440 if (self
->frame
) client_shade(self
, FALSE
);
1441 else self
->shaded
= FALSE
;
1443 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1444 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1445 else self
->iconic
= FALSE
;
1447 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1448 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1449 else self
->fullscreen
= FALSE
;
1451 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1453 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1454 else self
->max_vert
= self
->max_horz
= FALSE
;
1458 void client_reconfigure(ObClient
*self
)
1460 /* by making this pass FALSE for user, we avoid the emacs event storm where
1461 every configurenotify causes an update in its normal hints, i think this
1462 is generally what we want anyways... */
1463 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1464 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1467 void client_update_wmhints(ObClient
*self
)
1470 gboolean ur
= FALSE
;
1473 /* assume a window takes input if it doesnt specify */
1474 self
->can_focus
= TRUE
;
1476 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1477 if (hints
->flags
& InputHint
)
1478 self
->can_focus
= hints
->input
;
1480 /* only do this when first managing the window *AND* when we aren't
1482 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1483 if (hints
->flags
& StateHint
)
1484 self
->iconic
= hints
->initial_state
== IconicState
;
1486 if (hints
->flags
& XUrgencyHint
)
1489 if (!(hints
->flags
& WindowGroupHint
))
1490 hints
->window_group
= None
;
1492 /* did the group state change? */
1493 if (hints
->window_group
!=
1494 (self
->group
? self
->group
->leader
: None
)) {
1495 /* remove from the old group if there was one */
1496 if (self
->group
!= NULL
) {
1497 /* remove transients of the group */
1498 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1499 self
->transients
= g_slist_remove(self
->transients
,
1502 /* remove myself from parents in the group */
1503 if (self
->transient_for
== OB_TRAN_GROUP
) {
1504 for (it
= self
->group
->members
; it
;
1505 it
= g_slist_next(it
))
1507 ObClient
*c
= it
->data
;
1509 if (c
!= self
&& !c
->transient_for
)
1510 c
->transients
= g_slist_remove(c
->transients
,
1515 group_remove(self
->group
, self
);
1518 if (hints
->window_group
!= None
) {
1519 self
->group
= group_add(hints
->window_group
, self
);
1521 /* i can only have transients from the group if i am not
1523 if (!self
->transient_for
) {
1524 /* add other transients of the group that are already
1526 for (it
= self
->group
->members
; it
;
1527 it
= g_slist_next(it
))
1529 ObClient
*c
= it
->data
;
1530 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1532 g_slist_append(self
->transients
, c
);
1537 /* because the self->transient flag wont change from this call,
1538 we don't need to update the window's type and such, only its
1539 transient_for, and the transients lists of other windows in
1540 the group may be affected */
1541 client_update_transient_for(self
);
1544 /* the WM_HINTS can contain an icon */
1545 client_update_icons(self
);
1550 if (ur
!= self
->urgent
) {
1552 /* fire the urgent callback if we're mapped, otherwise, wait until
1553 after we're mapped */
1555 client_urgent_notify(self
);
1559 void client_update_title(ObClient
*self
)
1565 gboolean read_title
;
1568 old_title
= self
->title
;
1571 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1572 /* try old x stuff */
1573 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1574 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1575 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1576 if (self
->transient
) {
1577 data
= g_strdup("");
1580 data
= g_strdup("Unnamed Window");
1584 if (config_title_number
) {
1586 /* did the title change? then reset the title_count */
1587 if (old_title
&& 0 != strncmp(old_title
, data
, strlen(data
)))
1588 self
->title_count
= 1;
1590 /* look for duplicates and append a number */
1592 for (it
= client_list
; it
; it
= g_list_next(it
))
1593 if (it
->data
!= self
) {
1594 ObClient
*c
= it
->data
;
1596 if (c
->title_count
== 1) {
1597 if (!strcmp(c
->title
, data
))
1598 nums
|= 1 << c
->title_count
;
1603 /* find the beginning of our " - [%u]", this relies on
1604 that syntax being used */
1605 end
= strrchr(c
->title
, '-') - 1;
1606 len
= end
- c
->title
;
1607 if (!strncmp(c
->title
, data
, len
))
1608 nums
|= 1 << c
->title_count
;
1611 /* find first free number */
1612 for (i
= 1; i
<= 32; ++i
)
1613 if (!(nums
& (1 << i
))) {
1614 if (self
->title_count
== 1 || i
== 1)
1615 self
->title_count
= i
;
1618 /* dont display the number for the first window */
1619 if (self
->title_count
> 1) {
1621 ndata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1626 self
->title_count
= 1;
1629 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1633 frame_adjust_title(self
->frame
);
1637 /* update the icon title */
1639 g_free(self
->icon_title
);
1643 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1644 /* try old x stuff */
1645 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)
1646 || PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
))) {
1647 data
= g_strdup(self
->title
);
1651 /* append the title count, dont display the number for the first window.
1652 * We don't need to check for config_title_number here since title_count
1653 * is not set above 1 then. */
1654 if (read_title
&& self
->title_count
> 1) {
1656 newdata
= g_strdup_printf("%s - [%u]", data
, self
->title_count
);
1661 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1663 self
->icon_title
= data
;
1666 void client_update_class(ObClient
*self
)
1671 if (self
->name
) g_free(self
->name
);
1672 if (self
->class) g_free(self
->class);
1673 if (self
->role
) g_free(self
->role
);
1675 self
->name
= self
->class = self
->role
= NULL
;
1677 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1679 self
->name
= g_strdup(data
[0]);
1681 self
->class = g_strdup(data
[1]);
1686 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1689 if (self
->name
== NULL
) self
->name
= g_strdup("");
1690 if (self
->class == NULL
) self
->class = g_strdup("");
1691 if (self
->role
== NULL
) self
->role
= g_strdup("");
1694 void client_update_strut(ObClient
*self
)
1698 gboolean got
= FALSE
;
1701 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1705 STRUT_PARTIAL_SET(strut
,
1706 data
[0], data
[2], data
[1], data
[3],
1707 data
[4], data
[5], data
[8], data
[9],
1708 data
[6], data
[7], data
[10], data
[11]);
1714 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1720 /* use the screen's width/height */
1721 a
= screen_physical_area();
1723 STRUT_PARTIAL_SET(strut
,
1724 data
[0], data
[2], data
[1], data
[3],
1725 a
->y
, a
->y
+ a
->height
- 1,
1726 a
->x
, a
->x
+ a
->width
- 1,
1727 a
->y
, a
->y
+ a
->height
- 1,
1728 a
->x
, a
->x
+ a
->width
- 1);
1734 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1735 0, 0, 0, 0, 0, 0, 0, 0);
1737 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1738 self
->strut
= strut
;
1740 /* updating here is pointless while we're being mapped cuz we're not in
1741 the client list yet */
1743 screen_update_areas();
1747 void client_update_icons(ObClient
*self
)
1753 for (i
= 0; i
< self
->nicons
; ++i
)
1754 g_free(self
->icons
[i
].data
);
1755 if (self
->nicons
> 0)
1756 g_free(self
->icons
);
1759 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1760 /* figure out how many valid icons are in here */
1762 while (num
- i
> 2) {
1766 if (i
> num
|| w
*h
== 0) break;
1770 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1772 /* store the icons */
1774 for (j
= 0; j
< self
->nicons
; ++j
) {
1777 w
= self
->icons
[j
].width
= data
[i
++];
1778 h
= self
->icons
[j
].height
= data
[i
++];
1780 if (w
*h
== 0) continue;
1782 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1783 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1788 self
->icons
[j
].data
[t
] =
1789 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1790 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1791 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1792 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1798 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1799 kwm_win_icon
, &data
, &num
)) {
1802 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1803 xerror_set_ignore(TRUE
);
1804 if (!RrPixmapToRGBA(ob_rr_inst
,
1806 &self
->icons
[self
->nicons
-1].width
,
1807 &self
->icons
[self
->nicons
-1].height
,
1808 &self
->icons
[self
->nicons
-1].data
)) {
1809 g_free(&self
->icons
[self
->nicons
-1]);
1812 xerror_set_ignore(FALSE
);
1818 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1819 if (hints
->flags
& IconPixmapHint
) {
1821 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1822 xerror_set_ignore(TRUE
);
1823 if (!RrPixmapToRGBA(ob_rr_inst
,
1825 (hints
->flags
& IconMaskHint
?
1826 hints
->icon_mask
: None
),
1827 &self
->icons
[self
->nicons
-1].width
,
1828 &self
->icons
[self
->nicons
-1].height
,
1829 &self
->icons
[self
->nicons
-1].data
)){
1830 g_free(&self
->icons
[self
->nicons
-1]);
1833 xerror_set_ignore(FALSE
);
1840 frame_adjust_icon(self
->frame
);
1843 static void client_change_state(ObClient
*self
)
1846 gulong netstate
[11];
1849 state
[0] = self
->wmstate
;
1851 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1855 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1857 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1859 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1860 if (self
->skip_taskbar
)
1861 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1862 if (self
->skip_pager
)
1863 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1864 if (self
->fullscreen
)
1865 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1867 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1869 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1871 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1873 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1874 if (self
->undecorated
)
1875 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1876 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1878 client_calc_layer(self
);
1881 frame_adjust_state(self
->frame
);
1884 ObClient
*client_search_focus_tree(ObClient
*self
)
1889 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1890 if (client_focused(it
->data
)) return it
->data
;
1891 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1896 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1898 if (self
->transient_for
) {
1899 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1900 return client_search_focus_tree_full(self
->transient_for
);
1903 gboolean recursed
= FALSE
;
1905 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1906 if (!((ObClient
*)it
->data
)->transient_for
) {
1908 if ((c
= client_search_focus_tree_full(it
->data
)))
1917 /* this function checks the whole tree, the client_search_focus_tree~
1918 does not, so we need to check this window */
1919 if (client_focused(self
))
1921 return client_search_focus_tree(self
);
1924 static ObStackingLayer
calc_layer(ObClient
*self
)
1928 if (self
->fullscreen
&&
1929 (client_focused(self
) || client_search_focus_tree(self
)))
1930 l
= OB_STACKING_LAYER_FULLSCREEN
;
1931 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1932 l
= OB_STACKING_LAYER_DESKTOP
;
1933 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1934 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
1935 else l
= OB_STACKING_LAYER_ABOVE
;
1937 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1938 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1939 else l
= OB_STACKING_LAYER_NORMAL
;
1944 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1945 ObStackingLayer l
, gboolean raised
)
1947 ObStackingLayer old
, own
;
1951 own
= calc_layer(self
);
1952 self
->layer
= l
> own
? l
: own
;
1954 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
1955 client_calc_layer_recursive(it
->data
, orig
,
1956 l
, raised
? raised
: l
!= old
);
1958 if (!raised
&& l
!= old
)
1959 if (orig
->frame
) { /* only restack if the original window is managed */
1960 stacking_remove(CLIENT_AS_WINDOW(self
));
1961 stacking_add(CLIENT_AS_WINDOW(self
));
1965 void client_calc_layer(ObClient
*self
)
1972 /* transients take on the layer of their parents */
1973 self
= client_search_top_transient(self
);
1975 l
= calc_layer(self
);
1977 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1980 gboolean
client_should_show(ObClient
*self
)
1984 if (client_normal(self
) && screen_showing_desktop
)
1987 if (self->transient_for) {
1988 if (self->transient_for != OB_TRAN_GROUP)
1989 return client_should_show(self->transient_for);
1993 for (it = self->group->members; it; it = g_slist_next(it)) {
1994 ObClient *c = it->data;
1995 if (c != self && !c->transient_for) {
1996 if (client_should_show(c))
2003 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2009 static void client_showhide(ObClient
*self
)
2012 if (client_should_show(self
))
2013 frame_show(self
->frame
);
2015 frame_hide(self
->frame
);
2018 gboolean
client_normal(ObClient
*self
) {
2019 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2020 self
->type
== OB_CLIENT_TYPE_DOCK
||
2021 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2024 static void client_apply_startup_state(ObClient
*self
)
2026 /* these are in a carefully crafted order.. */
2029 self
->iconic
= FALSE
;
2030 client_iconify(self
, TRUE
, FALSE
);
2032 if (self
->fullscreen
) {
2033 self
->fullscreen
= FALSE
;
2034 client_fullscreen(self
, TRUE
, FALSE
);
2036 if (self
->undecorated
) {
2037 self
->undecorated
= FALSE
;
2038 client_set_undecorated(self
, TRUE
);
2041 self
->shaded
= FALSE
;
2042 client_shade(self
, TRUE
);
2045 client_urgent_notify(self
);
2047 if (self
->max_vert
&& self
->max_horz
) {
2048 self
->max_vert
= self
->max_horz
= FALSE
;
2049 client_maximize(self
, TRUE
, 0, FALSE
);
2050 } else if (self
->max_vert
) {
2051 self
->max_vert
= FALSE
;
2052 client_maximize(self
, TRUE
, 2, FALSE
);
2053 } else if (self
->max_horz
) {
2054 self
->max_horz
= FALSE
;
2055 client_maximize(self
, TRUE
, 1, FALSE
);
2058 /* nothing to do for the other states:
2067 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2068 gint x
, gint y
, gint w
, gint h
,
2069 gboolean user
, gboolean final
,
2070 gboolean force_reply
)
2073 gboolean send_resize_client
;
2074 gboolean moved
= FALSE
, resized
= FALSE
;
2075 guint fdecor
= self
->frame
->decorations
;
2076 gboolean fhorz
= self
->frame
->max_horz
;
2078 /* make the frame recalculate its dimentions n shit without changing
2079 anything visible for real, this way the constraints below can work with
2080 the updated frame dimensions. */
2081 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2083 /* gets the frame's position */
2084 frame_client_gravity(self
->frame
, &x
, &y
);
2086 /* these positions are frame positions, not client positions */
2088 /* set the size and position if fullscreen */
2089 if (self
->fullscreen
) {
2093 i
= client_monitor(self
);
2094 a
= screen_physical_area_monitor(i
);
2101 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
2105 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
2107 /* set the size and position if maximized */
2108 if (self
->max_horz
) {
2110 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2112 if (self
->max_vert
) {
2114 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2118 /* gets the client's position */
2119 frame_frame_gravity(self
->frame
, &x
, &y
);
2121 /* these override the above states! if you cant move you can't move! */
2123 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2127 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2128 w
= self
->area
.width
;
2129 h
= self
->area
.height
;
2133 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
2134 gint basew
, baseh
, minw
, minh
;
2136 /* base size is substituted with min size if not specified */
2137 if (self
->base_size
.width
|| self
->base_size
.height
) {
2138 basew
= self
->base_size
.width
;
2139 baseh
= self
->base_size
.height
;
2141 basew
= self
->min_size
.width
;
2142 baseh
= self
->min_size
.height
;
2144 /* min size is substituted with base size if not specified */
2145 if (self
->min_size
.width
|| self
->min_size
.height
) {
2146 minw
= self
->min_size
.width
;
2147 minh
= self
->min_size
.height
;
2149 minw
= self
->base_size
.width
;
2150 minh
= self
->base_size
.height
;
2153 /* if this is a user-requested resize, then check against min/max
2156 /* smaller than min size or bigger than max size? */
2157 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
2158 if (w
< minw
) w
= minw
;
2159 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
2160 if (h
< minh
) h
= minh
;
2165 /* keep to the increments */
2166 w
/= self
->size_inc
.width
;
2167 h
/= self
->size_inc
.height
;
2169 /* you cannot resize to nothing */
2170 if (basew
+ w
< 1) w
= 1 - basew
;
2171 if (baseh
+ h
< 1) h
= 1 - baseh
;
2173 /* store the logical size */
2174 SIZE_SET(self
->logical_size
,
2175 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
2176 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
2178 w
*= self
->size_inc
.width
;
2179 h
*= self
->size_inc
.height
;
2184 /* adjust the height to match the width for the aspect ratios.
2185 for this, min size is not substituted for base size ever. */
2186 w
-= self
->base_size
.width
;
2187 h
-= self
->base_size
.height
;
2189 if (!self
->fullscreen
) {
2190 if (self
->min_ratio
)
2191 if (h
* self
->min_ratio
> w
) {
2192 h
= (gint
)(w
/ self
->min_ratio
);
2194 /* you cannot resize to nothing */
2197 w
= (gint
)(h
* self
->min_ratio
);
2200 if (self
->max_ratio
)
2201 if (h
* self
->max_ratio
< w
) {
2202 h
= (gint
)(w
/ self
->max_ratio
);
2204 /* you cannot resize to nothing */
2207 w
= (gint
)(h
* self
->min_ratio
);
2212 w
+= self
->base_size
.width
;
2213 h
+= self
->base_size
.height
;
2220 case OB_CORNER_TOPLEFT
:
2222 case OB_CORNER_TOPRIGHT
:
2223 x
-= w
- self
->area
.width
;
2225 case OB_CORNER_BOTTOMLEFT
:
2226 y
-= h
- self
->area
.height
;
2228 case OB_CORNER_BOTTOMRIGHT
:
2229 x
-= w
- self
->area
.width
;
2230 y
-= h
- self
->area
.height
;
2234 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2235 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2237 oldw
= self
->area
.width
;
2238 oldh
= self
->area
.height
;
2239 RECT_SET(self
->area
, x
, y
, w
, h
);
2241 /* for app-requested resizes, always resize if 'resized' is true.
2242 for user-requested ones, only resize if final is true, or when
2243 resizing in redraw mode */
2244 send_resize_client
= ((!user
&& resized
) ||
2246 (resized
&& config_resize_redraw
))));
2248 /* if the client is enlarging, then resize the client before the frame */
2249 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
))
2250 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2252 /* move/resize the frame to match the request */
2254 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2255 moved
= resized
= TRUE
;
2257 if (moved
|| resized
)
2258 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2260 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
2263 event
.type
= ConfigureNotify
;
2264 event
.xconfigure
.display
= ob_display
;
2265 event
.xconfigure
.event
= self
->window
;
2266 event
.xconfigure
.window
= self
->window
;
2268 /* root window real coords */
2269 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
2271 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
2273 event
.xconfigure
.width
= w
;
2274 event
.xconfigure
.height
= h
;
2275 event
.xconfigure
.border_width
= 0;
2276 event
.xconfigure
.above
= self
->frame
->plate
;
2277 event
.xconfigure
.override_redirect
= FALSE
;
2278 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2279 FALSE
, StructureNotifyMask
, &event
);
2283 /* if the client is shrinking, then resize the frame before the client */
2284 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
)))
2285 XResizeWindow(ob_display
, self
->window
, w
, h
);
2290 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
2294 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2295 self
->fullscreen
== fs
) return; /* already done */
2297 self
->fullscreen
= fs
;
2298 client_change_state(self
); /* change the state hints on the client,
2299 and adjust out layer/stacking */
2303 self
->pre_fullscreen_area
= self
->area
;
2305 /* these are not actually used cuz client_configure will set them
2306 as appropriate when the window is fullscreened */
2311 if (self
->pre_fullscreen_area
.width
> 0 &&
2312 self
->pre_fullscreen_area
.height
> 0)
2314 x
= self
->pre_fullscreen_area
.x
;
2315 y
= self
->pre_fullscreen_area
.y
;
2316 w
= self
->pre_fullscreen_area
.width
;
2317 h
= self
->pre_fullscreen_area
.height
;
2318 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2320 /* pick some fallbacks... */
2321 a
= screen_area_monitor(self
->desktop
, 0);
2322 x
= a
->x
+ a
->width
/ 4;
2323 y
= a
->y
+ a
->height
/ 4;
2329 client_setup_decor_and_functions(self
);
2331 client_move_resize(self
, x
, y
, w
, h
);
2333 /* try focus us when we go into fullscreen mode */
2337 static void client_iconify_recursive(ObClient
*self
,
2338 gboolean iconic
, gboolean curdesk
)
2341 gboolean changed
= FALSE
;
2344 if (self
->iconic
!= iconic
) {
2345 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2348 self
->iconic
= iconic
;
2351 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2354 old
= self
->wmstate
;
2355 self
->wmstate
= IconicState
;
2356 if (old
!= self
->wmstate
)
2357 PROP_MSG(self
->window
, kde_wm_change_state
,
2358 self
->wmstate
, 1, 0, 0);
2360 /* update the focus lists.. iconic windows go to the bottom of
2361 the list, put the new iconic window at the 'top of the
2363 focus_order_to_top(self
);
2371 client_set_desktop(self
, screen_desktop
, FALSE
);
2373 old
= self
->wmstate
;
2374 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2375 if (old
!= self
->wmstate
)
2376 PROP_MSG(self
->window
, kde_wm_change_state
,
2377 self
->wmstate
, 1, 0, 0);
2379 /* this puts it after the current focused window */
2380 focus_order_remove(self
);
2381 focus_order_add_new(self
);
2388 client_change_state(self
);
2389 client_showhide(self
);
2390 if (STRUT_EXISTS(self
->strut
))
2391 screen_update_areas();
2394 /* iconify all transients */
2395 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2396 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2400 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2402 /* move up the transient chain as far as possible first */
2403 self
= client_search_top_transient(self
);
2405 client_iconify_recursive(client_search_top_transient(self
),
2409 void client_maximize(ObClient
*self
, gboolean max
, gint dir
, gboolean savearea
)
2413 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2414 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2416 /* check if already done */
2418 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2419 if (dir
== 1 && self
->max_horz
) return;
2420 if (dir
== 2 && self
->max_vert
) return;
2422 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2423 if (dir
== 1 && !self
->max_horz
) return;
2424 if (dir
== 2 && !self
->max_vert
) return;
2427 /* we just tell it to configure in the same place and client_configure
2428 worries about filling the screen with the window */
2431 w
= self
->area
.width
;
2432 h
= self
->area
.height
;
2436 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2437 RECT_SET(self
->pre_max_area
,
2438 self
->area
.x
, self
->pre_max_area
.y
,
2439 self
->area
.width
, self
->pre_max_area
.height
);
2441 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2442 RECT_SET(self
->pre_max_area
,
2443 self
->pre_max_area
.x
, self
->area
.y
,
2444 self
->pre_max_area
.width
, self
->area
.height
);
2450 a
= screen_area_monitor(self
->desktop
, 0);
2451 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2452 if (self
->pre_max_area
.width
> 0) {
2453 x
= self
->pre_max_area
.x
;
2454 w
= self
->pre_max_area
.width
;
2456 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2457 0, self
->pre_max_area
.height
);
2459 /* pick some fallbacks... */
2460 x
= a
->x
+ a
->width
/ 4;
2464 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2465 if (self
->pre_max_area
.height
> 0) {
2466 y
= self
->pre_max_area
.y
;
2467 h
= self
->pre_max_area
.height
;
2469 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2470 self
->pre_max_area
.width
, 0);
2472 /* pick some fallbacks... */
2473 y
= a
->y
+ a
->height
/ 4;
2479 if (dir
== 0 || dir
== 1) /* horz */
2480 self
->max_horz
= max
;
2481 if (dir
== 0 || dir
== 2) /* vert */
2482 self
->max_vert
= max
;
2484 client_change_state(self
); /* change the state hints on the client */
2486 client_setup_decor_and_functions(self
);
2488 client_move_resize(self
, x
, y
, w
, h
);
2491 void client_shade(ObClient
*self
, gboolean shade
)
2493 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2494 shade
) || /* can't shade */
2495 self
->shaded
== shade
) return; /* already done */
2497 /* when we're iconic, don't change the wmstate */
2498 if (!self
->iconic
) {
2501 old
= self
->wmstate
;
2502 self
->wmstate
= shade
? IconicState
: NormalState
;
2503 if (old
!= self
->wmstate
)
2504 PROP_MSG(self
->window
, kde_wm_change_state
,
2505 self
->wmstate
, 1, 0, 0);
2508 self
->shaded
= shade
;
2509 client_change_state(self
);
2510 /* resize the frame to just the titlebar */
2511 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2514 void client_close(ObClient
*self
)
2518 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2520 /* in the case that the client provides no means to requesting that it
2521 close, we just kill it */
2522 if (!self
->delete_window
)
2526 XXX: itd be cool to do timeouts and shit here for killing the client's
2528 like... if the window is around after 5 seconds, then the close button
2529 turns a nice red, and if this function is called again, the client is
2533 ce
.xclient
.type
= ClientMessage
;
2534 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2535 ce
.xclient
.display
= ob_display
;
2536 ce
.xclient
.window
= self
->window
;
2537 ce
.xclient
.format
= 32;
2538 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2539 ce
.xclient
.data
.l
[1] = event_curtime
;
2540 ce
.xclient
.data
.l
[2] = 0l;
2541 ce
.xclient
.data
.l
[3] = 0l;
2542 ce
.xclient
.data
.l
[4] = 0l;
2543 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2546 void client_kill(ObClient
*self
)
2548 XKillClient(ob_display
, self
->window
);
2551 void client_set_desktop_recursive(ObClient
*self
,
2552 guint target
, gboolean donthide
)
2557 if (target
!= self
->desktop
) {
2559 ob_debug("Setting desktop %u\n", target
+1);
2561 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2563 /* remove from the old desktop(s) */
2564 focus_order_remove(self
);
2566 old
= self
->desktop
;
2567 self
->desktop
= target
;
2568 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2569 /* the frame can display the current desktop state */
2570 frame_adjust_state(self
->frame
);
2571 /* 'move' the window to the new desktop */
2573 client_showhide(self
);
2574 /* raise if it was not already on the desktop */
2575 if (old
!= DESKTOP_ALL
)
2577 if (STRUT_EXISTS(self
->strut
))
2578 screen_update_areas();
2580 /* add to the new desktop(s) */
2581 if (config_focus_new
)
2582 focus_order_to_top(self
);
2584 focus_order_to_bottom(self
);
2587 /* move all transients */
2588 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2589 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2593 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2595 client_set_desktop_recursive(client_search_top_transient(self
),
2599 ObClient
*client_search_modal_child(ObClient
*self
)
2604 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2605 ObClient
*c
= it
->data
;
2606 if ((ret
= client_search_modal_child(c
))) return ret
;
2607 if (c
->modal
) return c
;
2612 gboolean
client_validate(ObClient
*self
)
2616 XSync(ob_display
, FALSE
); /* get all events on the server */
2618 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2619 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2620 XPutBackEvent(ob_display
, &e
);
2627 void client_set_wm_state(ObClient
*self
, glong state
)
2629 if (state
== self
->wmstate
) return; /* no change */
2633 client_iconify(self
, TRUE
, TRUE
);
2636 client_iconify(self
, FALSE
, TRUE
);
2641 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2643 gboolean shaded
= self
->shaded
;
2644 gboolean fullscreen
= self
->fullscreen
;
2645 gboolean undecorated
= self
->undecorated
;
2646 gboolean max_horz
= self
->max_horz
;
2647 gboolean max_vert
= self
->max_vert
;
2648 gboolean modal
= self
->modal
;
2649 gboolean iconic
= self
->iconic
;
2652 if (!(action
== prop_atoms
.net_wm_state_add
||
2653 action
== prop_atoms
.net_wm_state_remove
||
2654 action
== prop_atoms
.net_wm_state_toggle
))
2655 /* an invalid action was passed to the client message, ignore it */
2658 for (i
= 0; i
< 2; ++i
) {
2659 Atom state
= i
== 0 ? data1
: data2
;
2661 if (!state
) continue;
2663 /* if toggling, then pick whether we're adding or removing */
2664 if (action
== prop_atoms
.net_wm_state_toggle
) {
2665 if (state
== prop_atoms
.net_wm_state_modal
)
2666 action
= modal
? prop_atoms
.net_wm_state_remove
:
2667 prop_atoms
.net_wm_state_add
;
2668 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2669 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2670 prop_atoms
.net_wm_state_add
;
2671 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2672 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2673 prop_atoms
.net_wm_state_add
;
2674 else if (state
== prop_atoms
.net_wm_state_shaded
)
2675 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2676 prop_atoms
.net_wm_state_add
;
2677 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2678 action
= self
->skip_taskbar
?
2679 prop_atoms
.net_wm_state_remove
:
2680 prop_atoms
.net_wm_state_add
;
2681 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2682 action
= self
->skip_pager
?
2683 prop_atoms
.net_wm_state_remove
:
2684 prop_atoms
.net_wm_state_add
;
2685 else if (state
== prop_atoms
.net_wm_state_hidden
)
2686 action
= self
->iconic
?
2687 prop_atoms
.net_wm_state_remove
:
2688 prop_atoms
.net_wm_state_add
;
2689 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2690 action
= fullscreen
?
2691 prop_atoms
.net_wm_state_remove
:
2692 prop_atoms
.net_wm_state_add
;
2693 else if (state
== prop_atoms
.net_wm_state_above
)
2694 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2695 prop_atoms
.net_wm_state_add
;
2696 else if (state
== prop_atoms
.net_wm_state_below
)
2697 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2698 prop_atoms
.net_wm_state_add
;
2699 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2700 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2701 prop_atoms
.net_wm_state_add
;
2704 if (action
== prop_atoms
.net_wm_state_add
) {
2705 if (state
== prop_atoms
.net_wm_state_modal
) {
2707 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2709 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2711 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2713 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2714 self
->skip_taskbar
= TRUE
;
2715 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2716 self
->skip_pager
= TRUE
;
2717 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2719 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2721 } else if (state
== prop_atoms
.net_wm_state_above
) {
2723 self
->below
= FALSE
;
2724 } else if (state
== prop_atoms
.net_wm_state_below
) {
2725 self
->above
= FALSE
;
2727 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2731 } else { /* action == prop_atoms.net_wm_state_remove */
2732 if (state
== prop_atoms
.net_wm_state_modal
) {
2734 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2736 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2738 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2740 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2741 self
->skip_taskbar
= FALSE
;
2742 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2743 self
->skip_pager
= FALSE
;
2744 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2746 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2748 } else if (state
== prop_atoms
.net_wm_state_above
) {
2749 self
->above
= FALSE
;
2750 } else if (state
== prop_atoms
.net_wm_state_below
) {
2751 self
->below
= FALSE
;
2752 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2753 undecorated
= FALSE
;
2757 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2758 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2760 if (max_horz
== max_vert
) { /* both going the same way */
2761 client_maximize(self
, max_horz
, 0, TRUE
);
2763 client_maximize(self
, max_horz
, 1, TRUE
);
2764 client_maximize(self
, max_vert
, 2, TRUE
);
2768 if (max_horz
!= self
->max_horz
)
2769 client_maximize(self
, max_horz
, 1, TRUE
);
2771 client_maximize(self
, max_vert
, 2, TRUE
);
2774 /* change fullscreen state before shading, as it will affect if the window
2776 if (fullscreen
!= self
->fullscreen
)
2777 client_fullscreen(self
, fullscreen
, TRUE
);
2778 if (shaded
!= self
->shaded
)
2779 client_shade(self
, shaded
);
2780 if (undecorated
!= self
->undecorated
)
2781 client_set_undecorated(self
, undecorated
);
2782 if (modal
!= self
->modal
) {
2783 self
->modal
= modal
;
2784 /* when a window changes modality, then its stacking order with its
2785 transients needs to change */
2788 if (iconic
!= self
->iconic
)
2789 client_iconify(self
, iconic
, FALSE
);
2791 client_calc_layer(self
);
2792 client_change_state(self
); /* change the hint to reflect these changes */
2795 ObClient
*client_focus_target(ObClient
*self
)
2799 /* if we have a modal child, then focus it, not us */
2800 child
= client_search_modal_child(client_search_top_transient(self
));
2801 if (child
) return child
;
2805 gboolean
client_can_focus(ObClient
*self
)
2809 /* choose the correct target */
2810 self
= client_focus_target(self
);
2812 if (!self
->frame
->visible
)
2815 if (!(self
->can_focus
|| self
->focus_notify
))
2818 /* do a check to see if the window has already been unmapped or destroyed
2819 do this intelligently while watching out for unmaps we've generated
2820 (ignore_unmaps > 0) */
2821 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2822 DestroyNotify
, &ev
)) {
2823 XPutBackEvent(ob_display
, &ev
);
2826 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2827 UnmapNotify
, &ev
)) {
2828 if (self
->ignore_unmaps
) {
2829 self
->ignore_unmaps
--;
2831 XPutBackEvent(ob_display
, &ev
);
2839 gboolean
client_focus(ObClient
*self
)
2841 /* choose the correct target */
2842 self
= client_focus_target(self
);
2844 if (!client_can_focus(self
)) {
2845 if (!self
->frame
->visible
) {
2846 /* update the focus lists */
2847 focus_order_to_top(self
);
2852 if (self
->can_focus
) {
2853 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2854 I choose to use it always, hopefully to find errors quicker, if any
2855 are left. (I hate X. I hate focus events.)
2857 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2858 #799. So now it is RevertToNone again.
2860 XSetInputFocus(ob_display
, self
->window
, RevertToNone
,
2864 if (self
->focus_notify
) {
2866 ce
.xclient
.type
= ClientMessage
;
2867 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2868 ce
.xclient
.display
= ob_display
;
2869 ce
.xclient
.window
= self
->window
;
2870 ce
.xclient
.format
= 32;
2871 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2872 ce
.xclient
.data
.l
[1] = event_curtime
;
2873 ce
.xclient
.data
.l
[2] = 0l;
2874 ce
.xclient
.data
.l
[3] = 0l;
2875 ce
.xclient
.data
.l
[4] = 0l;
2876 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2880 ob_debug("%sively focusing %lx at %d\n",
2881 (self
->can_focus
? "act" : "pass"),
2882 self
->window
, (gint
) event_curtime
);
2885 /* Cause the FocusIn to come back to us. Important for desktop switches,
2886 since otherwise we'll have no FocusIn on the queue and send it off to
2887 the focus_backup. */
2888 XSync(ob_display
, FALSE
);
2892 /* Used when the current client is closed, focus_last will then prevent
2893 * focus from going to the mouse pointer */
2894 void client_unfocus(ObClient
*self
)
2896 if (focus_client
== self
) {
2898 ob_debug("client_unfocus for %lx\n", self
->window
);
2900 focus_fallback(OB_FOCUS_FALLBACK_CLOSED
);
2904 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
2906 /* XXX do some stuff here if user is false to determine if we really want
2907 to activate it or not (a parent or group member is currently active) */
2909 if (client_normal(self
) && screen_showing_desktop
)
2910 screen_show_desktop(FALSE
);
2912 client_iconify(self
, FALSE
, here
);
2913 if (self
->desktop
!= DESKTOP_ALL
&&
2914 self
->desktop
!= screen_desktop
) {
2916 client_set_desktop(self
, screen_desktop
, FALSE
);
2918 screen_set_desktop(self
->desktop
);
2919 } else if (!self
->frame
->visible
)
2920 /* if its not visible for other reasons, then don't mess
2924 client_shade(self
, FALSE
);
2928 /* we do this an action here. this is rather important. this is because
2929 we want the results from the focus change to take place BEFORE we go
2930 about raising the window. when a fullscreen window loses focus, we need
2931 this or else the raise wont be able to raise above the to-lose-focus
2932 fullscreen window. */
2936 void client_raise(ObClient
*self
)
2938 action_run_string("Raise", self
);
2941 void client_lower(ObClient
*self
)
2943 action_run_string("Lower", self
);
2946 gboolean
client_focused(ObClient
*self
)
2948 return self
== focus_client
;
2951 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
2954 /* si is the smallest image >= req */
2955 /* li is the largest image < req */
2956 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2958 if (!self
->nicons
) {
2959 ObClientIcon
*parent
= NULL
;
2961 if (self
->transient_for
) {
2962 if (self
->transient_for
!= OB_TRAN_GROUP
)
2963 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
2966 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2967 ObClient
*c
= it
->data
;
2968 if (c
!= self
&& !c
->transient_for
) {
2969 if ((parent
= client_icon_recursive(c
, w
, h
)))
2979 for (i
= 0; i
< self
->nicons
; ++i
) {
2980 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2981 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2985 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2990 if (largest
== 0) /* didnt find one smaller than the requested size */
2991 return &self
->icons
[si
];
2992 return &self
->icons
[li
];
2995 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
2998 static ObClientIcon deficon
;
3000 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3001 deficon
.width
= deficon
.height
= 48;
3002 deficon
.data
= ob_rr_theme
->def_win_icon
;
3008 /* this be mostly ripped from fvwm */
3009 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
3011 gint my_cx
, my_cy
, his_cx
, his_cy
;
3014 gint score
, best_score
;
3015 ObClient
*best_client
, *cur
;
3021 /* first, find the centre coords of the currently focused window */
3022 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
3023 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
3028 for(it
= g_list_first(client_list
); it
; it
= g_list_next(it
)) {
3031 /* the currently selected window isn't interesting */
3034 if (!client_normal(cur
))
3036 /* using c->desktop instead of screen_desktop doesn't work if the
3037 * current window was omnipresent, hope this doesn't have any other
3039 if(screen_desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
3043 if(!(client_focus_target(cur
) == cur
&&
3044 client_can_focus(cur
)))
3047 /* find the centre coords of this window, from the
3048 * currently focused window's point of view */
3049 his_cx
= (cur
->frame
->area
.x
- my_cx
)
3050 + cur
->frame
->area
.width
/ 2;
3051 his_cy
= (cur
->frame
->area
.y
- my_cy
)
3052 + cur
->frame
->area
.height
/ 2;
3054 if(dir
== OB_DIRECTION_NORTHEAST
|| dir
== OB_DIRECTION_SOUTHEAST
||
3055 dir
== OB_DIRECTION_SOUTHWEST
|| dir
== OB_DIRECTION_NORTHWEST
) {
3057 /* Rotate the diagonals 45 degrees counterclockwise.
3058 * To do this, multiply the matrix /+h +h\ with the
3059 * vector (x y). \-h +h/
3060 * h = sqrt(0.5). We can set h := 1 since absolute
3061 * distance doesn't matter here. */
3062 tx
= his_cx
+ his_cy
;
3063 his_cy
= -his_cx
+ his_cy
;
3068 case OB_DIRECTION_NORTH
:
3069 case OB_DIRECTION_SOUTH
:
3070 case OB_DIRECTION_NORTHEAST
:
3071 case OB_DIRECTION_SOUTHWEST
:
3072 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
3073 distance
= ((dir
== OB_DIRECTION_NORTH
||
3074 dir
== OB_DIRECTION_NORTHEAST
) ?
3077 case OB_DIRECTION_EAST
:
3078 case OB_DIRECTION_WEST
:
3079 case OB_DIRECTION_SOUTHEAST
:
3080 case OB_DIRECTION_NORTHWEST
:
3081 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
3082 distance
= ((dir
== OB_DIRECTION_WEST
||
3083 dir
== OB_DIRECTION_NORTHWEST
) ?
3088 /* the target must be in the requested direction */
3092 /* Calculate score for this window. The smaller the better. */
3093 score
= distance
+ offset
;
3095 /* windows more than 45 degrees off the direction are
3096 * heavily penalized and will only be chosen if nothing
3097 * else within a million pixels */
3098 if(offset
> distance
)
3101 if(best_score
== -1 || score
< best_score
)
3109 void client_set_layer(ObClient
*self
, gint layer
)
3113 self
->above
= FALSE
;
3114 } else if (layer
== 0) {
3115 self
->below
= self
->above
= FALSE
;
3117 self
->below
= FALSE
;
3120 client_calc_layer(self
);
3121 client_change_state(self
); /* reflect this in the state hints */
3124 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3126 if (self
->undecorated
!= undecorated
) {
3127 self
->undecorated
= undecorated
;
3128 client_setup_decor_and_functions(self
);
3129 /* Make sure the client knows it might have moved. Maybe there is a
3130 * better way of doing this so only one client_configure is sent, but
3131 * since 125 of these are sent per second when moving the window (with
3132 * user = FALSE) i doubt it matters much.
3134 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3135 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3136 client_change_state(self
); /* reflect this in the state hints */
3140 /* Determines which physical monitor a client is on by calculating the
3141 area of the part of the client on each monitor. The number of the
3142 monitor containing the greatest area of the client is returned.*/
3143 guint
client_monitor(ObClient
*self
)
3149 for (i
= 0; i
< screen_num_monitors
; ++i
) {
3150 Rect
*area
= screen_physical_area_monitor(i
);
3151 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
)) {
3155 RECT_SET_INTERSECTION(r
, *area
, self
->frame
->area
);
3156 v
= r
.width
* r
.height
;
3167 ObClient
*client_search_top_transient(ObClient
*self
)
3169 /* move up the transient chain as far as possible */
3170 if (self
->transient_for
) {
3171 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3172 return client_search_top_transient(self
->transient_for
);
3176 g_assert(self
->group
);
3178 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3179 ObClient
*c
= it
->data
;
3181 /* checking transient_for prevents infinate loops! */
3182 if (c
!= self
&& !c
->transient_for
)
3193 ObClient
*client_search_focus_parent(ObClient
*self
)
3195 if (self
->transient_for
) {
3196 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3197 if (client_focused(self
->transient_for
))
3198 return self
->transient_for
;
3202 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3203 ObClient
*c
= it
->data
;
3205 /* checking transient_for prevents infinate loops! */
3206 if (c
!= self
&& !c
->transient_for
)
3207 if (client_focused(c
))
3216 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3218 if (self
->transient_for
) {
3219 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3220 if (self
->transient_for
== search
)
3225 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3226 ObClient
*c
= it
->data
;
3228 /* checking transient_for prevents infinate loops! */
3229 if (c
!= self
&& !c
->transient_for
)
3239 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3243 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3244 if (sit
->data
== search
)
3246 if (client_search_transient(sit
->data
, search
))
3252 void client_update_sm_client_id(ObClient
*self
)
3254 g_free(self
->sm_client_id
);
3255 self
->sm_client_id
= NULL
;
3257 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3259 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3260 &self
->sm_client_id
);
3263 #define WANT_EDGE(cur, c) \
3266 if(!client_normal(cur)) \
3268 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3272 if(cur->layer < c->layer && !config_resist_layers_below) \
3275 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3276 if ((his_edge_start >= my_edge_start && \
3277 his_edge_start <= my_edge_end) || \
3278 (my_edge_start >= his_edge_start && \
3279 my_edge_start <= his_edge_end)) \
3282 /* finds the nearest edge in the given direction from the current client
3283 * note to self: the edge is the -frame- edge (the actual one), not the
3286 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3288 gint dest
, monitor_dest
;
3289 gint my_edge_start
, my_edge_end
, my_offset
;
3296 a
= screen_area(c
->desktop
);
3297 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3300 case OB_DIRECTION_NORTH
:
3301 my_edge_start
= c
->frame
->area
.x
;
3302 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3303 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3305 /* default: top of screen */
3306 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3307 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3308 /* if the monitor edge comes before the screen edge, */
3309 /* use that as the destination instead. (For xinerama) */
3310 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3311 dest
= monitor_dest
;
3313 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3314 gint his_edge_start
, his_edge_end
, his_offset
;
3315 ObClient
*cur
= it
->data
;
3319 his_edge_start
= cur
->frame
->area
.x
;
3320 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3321 his_offset
= cur
->frame
->area
.y
+
3322 (hang
? 0 : cur
->frame
->area
.height
);
3324 if(his_offset
+ 1 > my_offset
)
3327 if(his_offset
< dest
)
3330 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3333 case OB_DIRECTION_SOUTH
:
3334 my_edge_start
= c
->frame
->area
.x
;
3335 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3336 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3338 /* default: bottom of screen */
3339 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3340 monitor_dest
= monitor
->y
+ monitor
->height
-
3341 (hang
? c
->frame
->area
.height
: 0);
3342 /* if the monitor edge comes before the screen edge, */
3343 /* use that as the destination instead. (For xinerama) */
3344 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3345 dest
= monitor_dest
;
3347 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3348 gint his_edge_start
, his_edge_end
, his_offset
;
3349 ObClient
*cur
= it
->data
;
3353 his_edge_start
= cur
->frame
->area
.x
;
3354 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3355 his_offset
= cur
->frame
->area
.y
+
3356 (hang
? cur
->frame
->area
.height
: 0);
3359 if(his_offset
- 1 < my_offset
)
3362 if(his_offset
> dest
)
3365 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3368 case OB_DIRECTION_WEST
:
3369 my_edge_start
= c
->frame
->area
.y
;
3370 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3371 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3373 /* default: leftmost egde of screen */
3374 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3375 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3376 /* if the monitor edge comes before the screen edge, */
3377 /* use that as the destination instead. (For xinerama) */
3378 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3379 dest
= monitor_dest
;
3381 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3382 gint his_edge_start
, his_edge_end
, his_offset
;
3383 ObClient
*cur
= it
->data
;
3387 his_edge_start
= cur
->frame
->area
.y
;
3388 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3389 his_offset
= cur
->frame
->area
.x
+
3390 (hang
? 0 : cur
->frame
->area
.width
);
3392 if(his_offset
+ 1 > my_offset
)
3395 if(his_offset
< dest
)
3398 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3401 case OB_DIRECTION_EAST
:
3402 my_edge_start
= c
->frame
->area
.y
;
3403 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3404 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3406 /* default: rightmost edge of screen */
3407 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3408 monitor_dest
= monitor
->x
+ monitor
->width
-
3409 (hang
? c
->frame
->area
.width
: 0);
3410 /* if the monitor edge comes before the screen edge, */
3411 /* use that as the destination instead. (For xinerama) */
3412 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3413 dest
= monitor_dest
;
3415 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3416 gint his_edge_start
, his_edge_end
, his_offset
;
3417 ObClient
*cur
= it
->data
;
3421 his_edge_start
= cur
->frame
->area
.y
;
3422 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3423 his_offset
= cur
->frame
->area
.x
+
3424 (hang
? cur
->frame
->area
.width
: 0);
3426 if(his_offset
- 1 < my_offset
)
3429 if(his_offset
> dest
)
3432 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3435 case OB_DIRECTION_NORTHEAST
:
3436 case OB_DIRECTION_SOUTHEAST
:
3437 case OB_DIRECTION_NORTHWEST
:
3438 case OB_DIRECTION_SOUTHWEST
:
3439 /* not implemented */
3441 g_assert_not_reached();
3442 dest
= 0; /* suppress warning */
3447 ObClient
* client_under_pointer()
3451 ObClient
*ret
= NULL
;
3453 if (screen_pointer_pos(&x
, &y
)) {
3454 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3455 if (WINDOW_IS_CLIENT(it
->data
)) {
3456 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3457 if (c
->frame
->visible
&&
3458 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3468 gboolean
client_has_group_siblings(ObClient
*self
)
3470 return self
->group
&& self
->group
->members
->next
;