1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
25 #include "moveresize.h"
38 #include "menuframe.h"
41 #include "render/render.h"
43 #include "obt/display.h"
51 # include <signal.h> /* for kill() */
55 #include <X11/Xutil.h>
57 /*! The event mask to grab on client windows */
58 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
61 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
66 ObClientCallback func
;
70 GList
*client_list
= NULL
;
72 static GSList
*client_destroy_notifies
= NULL
;
74 static void client_get_all(ObClient
*self
, gboolean real
);
75 static void client_get_startup_id(ObClient
*self
);
76 static void client_get_session_ids(ObClient
*self
);
77 static void client_get_area(ObClient
*self
);
78 static void client_get_desktop(ObClient
*self
);
79 static void client_get_state(ObClient
*self
);
80 static void client_get_shaped(ObClient
*self
);
81 static void client_get_mwm_hints(ObClient
*self
);
82 static void client_get_colormap(ObClient
*self
);
83 static void client_set_desktop_recursive(ObClient
*self
,
87 static void client_change_allowed_actions(ObClient
*self
);
88 static void client_change_state(ObClient
*self
);
89 static void client_change_wm_state(ObClient
*self
);
90 static void client_apply_startup_state(ObClient
*self
,
91 gint x
, gint y
, gint w
, gint h
);
92 static void client_restore_session_state(ObClient
*self
);
93 static gboolean
client_restore_session_stacking(ObClient
*self
);
94 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
95 static void client_update_transient_tree(ObClient
*self
,
96 ObGroup
*oldgroup
, ObGroup
*newgroup
,
97 gboolean oldgtran
, gboolean newgtran
,
100 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
,
102 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
104 ObStackingLayer layer
);
105 static void client_call_notifies(ObClient
*self
, GSList
*list
);
106 static void client_ping_event(ObClient
*self
, gboolean dead
);
107 static void client_prompt_kill(ObClient
*self
);
110 void client_startup(gboolean reconfig
)
112 if (reconfig
) return;
117 void client_shutdown(gboolean reconfig
)
119 if (reconfig
) return;
122 static void client_call_notifies(ObClient
*self
, GSList
*list
)
126 for (it
= list
; it
; it
= g_slist_next(it
)) {
127 ClientCallback
*d
= it
->data
;
128 d
->func(self
, d
->data
);
132 void client_add_destroy_notify(ObClientCallback func
, gpointer data
)
134 ClientCallback
*d
= g_new(ClientCallback
, 1);
137 client_destroy_notifies
= g_slist_prepend(client_destroy_notifies
, d
);
140 void client_remove_destroy_notify(ObClientCallback func
)
144 for (it
= client_destroy_notifies
; it
; it
= g_slist_next(it
)) {
145 ClientCallback
*d
= it
->data
;
146 if (d
->func
== func
) {
148 client_destroy_notifies
=
149 g_slist_delete_link(client_destroy_notifies
, it
);
155 void client_set_list(void)
157 Window
*windows
, *win_it
;
159 guint size
= g_list_length(client_list
);
161 /* create an array of the window ids */
163 windows
= g_new(Window
, size
);
165 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
166 *win_it
= ((ObClient
*)it
->data
)->window
;
170 OBT_PROP_SETA32(obt_root(ob_screen
), NET_CLIENT_LIST
, WINDOW
,
171 (gulong
*)windows
, size
);
179 void client_manage(Window window
, ObPrompt
*prompt
)
182 XSetWindowAttributes attrib_set
;
183 gboolean activate
= FALSE
;
184 ObAppSettings
*settings
;
185 gboolean transient
= FALSE
;
186 Rect place
, *monitor
;
187 Time launch_time
, map_time
;
189 ob_debug("Managing window: 0x%lx", window
);
191 map_time
= event_get_server_time();
193 /* choose the events we want to receive on the CLIENT window
194 (ObPrompt windows can request events too) */
195 attrib_set
.event_mask
= CLIENT_EVENTMASK
|
196 (prompt
? prompt
->event_mask
: 0);
197 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
198 XChangeWindowAttributes(obt_display
, window
,
199 CWEventMask
|CWDontPropagate
, &attrib_set
);
201 /* create the ObClient struct, and populate it from the hints on the
203 self
= g_new0(ObClient
, 1);
204 self
->obwin
.type
= OB_WINDOW_CLASS_CLIENT
;
205 self
->window
= window
;
206 self
->prompt
= prompt
;
208 /* non-zero defaults */
209 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
210 self
->gravity
= NorthWestGravity
;
211 self
->desktop
= screen_num_desktops
; /* always an invalid value */
213 /* get all the stuff off the window */
214 client_get_all(self
, TRUE
);
216 ob_debug("Window type: %d", self
->type
);
217 ob_debug("Window group: 0x%x", self
->group
?self
->group
->leader
:0);
219 /* now we have all of the window's information so we can set this up.
220 do this before creating the frame, so it can tell that we are still
221 mapping and doesn't go applying things right away */
222 client_setup_decor_and_functions(self
, FALSE
);
224 /* specify that if we exit, the window should not be destroyed and
225 should be reparented back to root automatically, unless we are managing
226 an internal ObPrompt window */
228 XChangeSaveSet(obt_display
, window
, SetModeInsert
);
230 /* create the decoration frame for the client window */
231 self
->frame
= frame_new(self
);
233 frame_grab_client(self
->frame
);
235 /* we've grabbed everything and set everything that we need to at mapping
239 /* per-app settings override stuff from client_get_all, and return the
240 settings for other uses too. the returned settings is a shallow copy,
241 that needs to be freed with g_free(). */
242 settings
= client_get_settings_state(self
);
243 /* the session should get the last say though */
244 client_restore_session_state(self
);
246 /* tell startup notification that this app started */
247 launch_time
= sn_app_started(self
->startup_id
, self
->class, self
->name
);
249 /* do this after we have a frame.. it uses the frame to help determine the
250 WM_STATE to apply. */
251 client_change_state(self
);
253 /* add ourselves to the focus order */
254 focus_order_add_new(self
);
256 /* do this to add ourselves to the stacking list in a non-intrusive way */
257 client_calc_layer(self
);
259 /* focus the new window? */
260 if (ob_state() != OB_STATE_STARTING
&&
261 (!self
->session
|| self
->session
->focused
) &&
262 /* this means focus=true for window is same as config_focus_new=true */
263 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
264 client_search_focus_tree_full(self
)) &&
265 /* this checks for focus=false for the window */
266 (!settings
|| settings
->focus
!= 0) &&
267 focus_valid_target(self
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
))
272 /* remove the client's border */
273 XSetWindowBorderWidth(obt_display
, self
->window
, 0);
275 /* adjust the frame to the client's size before showing or placing
277 frame_adjust_area(self
->frame
, FALSE
, TRUE
, FALSE
);
278 frame_adjust_client_area(self
->frame
);
280 /* where the frame was placed is where the window was originally */
282 monitor
= screen_physical_area_monitor(screen_find_monitor(&place
));
284 /* figure out placement for the window if the window is new */
285 if (ob_state() == OB_STATE_RUNNING
) {
286 ob_debug("Positioned: %s @ %d %d",
287 (!self
->positioned
? "no" :
288 (self
->positioned
== PPosition
? "program specified" :
289 (self
->positioned
== USPosition
? "user specified" :
290 (self
->positioned
== (PPosition
| USPosition
) ?
291 "program + user specified" :
292 "BADNESS !?")))), place
.x
, place
.y
);
294 ob_debug("Sized: %s @ %d %d",
295 (!self
->sized
? "no" :
296 (self
->sized
== PSize
? "program specified" :
297 (self
->sized
== USSize
? "user specified" :
298 (self
->sized
== (PSize
| USSize
) ?
299 "program + user specified" :
300 "BADNESS !?")))), place
.width
, place
.height
);
302 /* splash screens are also returned as TRUE for transient,
303 and so will be forced on screen below */
304 transient
= place_client(self
, &place
.x
, &place
.y
, settings
);
306 /* make sure the window is visible. */
307 client_find_onscreen(self
, &place
.x
, &place
.y
,
308 place
.width
, place
.height
,
309 /* non-normal clients has less rules, and
310 windows that are being restored from a
311 session do also. we can assume you want
312 it back where you saved it. Clients saying
313 they placed themselves are subjected to
314 harder rules, ones that are placed by
315 place.c or by the user are allowed partially
316 off-screen and on xinerama divides (ie,
317 it is up to the placement routines to avoid
318 the xinerama divides)
320 splash screens get "transient" set to TRUE by
321 the place_client call
323 ob_state() == OB_STATE_RUNNING
&&
325 (!((self
->positioned
& USPosition
) ||
326 (settings
&& settings
->pos_given
)) &&
327 client_normal(self
) &&
329 /* don't move oldschool fullscreen windows to
330 fit inside the struts (fixes Acroread, which
331 makes its fullscreen window fit the screen
332 but it is not USSize'd or USPosition'd) */
333 !(self
->decorations
== 0 &&
334 RECT_EQUAL(place
, *monitor
)))));
337 /* if the window isn't user-sized, then make it fit inside
338 the visible screen area on its monitor. Use basically the same rules
339 for forcing the window on screen in the client_find_onscreen call.
341 do this after place_client, it chooses the monitor!
343 splash screens get "transient" set to TRUE by
344 the place_client call
346 if (ob_state() == OB_STATE_RUNNING
&&
348 (!(self
->sized
& USSize
|| self
->positioned
& USPosition
) &&
349 client_normal(self
) &&
351 /* don't shrink oldschool fullscreen windows to fit inside the
352 struts (fixes Acroread, which makes its fullscreen window
353 fit the screen but it is not USSize'd or USPosition'd) */
354 !(self
->decorations
== 0 && RECT_EQUAL(place
, *monitor
)))))
356 Rect
*a
= screen_area(self
->desktop
, SCREEN_AREA_ONE_MONITOR
, &place
);
358 /* get the size of the frame */
359 place
.width
+= self
->frame
->size
.left
+ self
->frame
->size
.right
;
360 place
.height
+= self
->frame
->size
.top
+ self
->frame
->size
.bottom
;
362 /* fit the window inside the area */
363 place
.width
= MIN(place
.width
, a
->width
);
364 place
.height
= MIN(place
.height
, a
->height
);
366 ob_debug("setting window size to %dx%d", place
.width
, place
.height
);
368 /* get the size of the client back */
369 place
.width
-= self
->frame
->size
.left
+ self
->frame
->size
.right
;
370 place
.height
-= self
->frame
->size
.top
+ self
->frame
->size
.bottom
;
375 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
376 "some restrictions may apply",
377 self
->window
, place
.x
, place
.y
, place
.width
, place
.height
);
379 ob_debug(" but session requested %d, %d %d x %d instead, "
381 self
->session
->x
, self
->session
->y
,
382 self
->session
->w
, self
->session
->h
);
384 /* do this after the window is placed, so the premax/prefullscreen numbers
387 this also places the window
389 client_apply_startup_state(self
, place
.x
, place
.y
,
390 place
.width
, place
.height
);
395 ob_debug_type(OB_DEBUG_FOCUS
, "Going to try activate new window? %s",
396 activate
? "yes" : "no");
398 gboolean raise
= FALSE
;
400 /* This is focus stealing prevention */
401 ob_debug_type(OB_DEBUG_FOCUS
,
402 "Want to focus new window 0x%x at time %u "
403 "launched at %u (last user interaction time %u)",
404 self
->window
, map_time
, launch_time
,
405 event_last_user_time
);
407 if (menu_frame_visible
|| moveresize_in_progress
) {
410 ob_debug_type(OB_DEBUG_FOCUS
,
411 "Not focusing the window because the user is inside "
412 "an Openbox menu or is move/resizing a window and "
413 "we don't want to interrupt them");
416 /* if it's on another desktop */
417 else if (!(self
->desktop
== screen_desktop
||
418 self
->desktop
== DESKTOP_ALL
) &&
419 /* the timestamp is from before you changed desktops */
420 launch_time
&& screen_desktop_user_time
&&
421 !event_time_after(launch_time
, screen_desktop_user_time
))
425 ob_debug_type(OB_DEBUG_FOCUS
,
426 "Not focusing the window because its on another "
429 /* If something is focused, and it's not our relative... */
430 else if (focus_client
&& client_search_focus_tree_full(self
) == NULL
&&
431 client_search_focus_group_full(self
) == NULL
)
433 /* If the user is working in another window right now, then don't
435 if (event_last_user_time
&& launch_time
&&
436 event_time_after(event_last_user_time
, launch_time
) &&
437 event_last_user_time
!= launch_time
&&
438 event_time_after(event_last_user_time
,
439 map_time
- OB_EVENT_USER_TIME_DELAY
))
442 ob_debug_type(OB_DEBUG_FOCUS
,
443 "Not focusing the window because the user is "
444 "working in another window");
446 /* If its a transient (and its parents aren't focused) */
447 else if (client_has_parent(self
)) {
449 ob_debug_type(OB_DEBUG_FOCUS
,
450 "Not focusing the window because it is a "
451 "transient, and its relatives aren't focused");
453 /* Don't steal focus from globally active clients.
454 I stole this idea from KWin. It seems nice.
456 else if (!(focus_client
->can_focus
||
457 focus_client
->focus_notify
))
460 ob_debug_type(OB_DEBUG_FOCUS
,
461 "Not focusing the window because a globally "
462 "active client has focus");
464 /* Don't move focus if it's not going to go to this window
466 else if (client_focus_target(self
) != self
) {
469 ob_debug_type(OB_DEBUG_FOCUS
,
470 "Not focusing the window because another window "
471 "would get the focus anyway");
473 else if (!(self
->desktop
== screen_desktop
||
474 self
->desktop
== DESKTOP_ALL
))
478 ob_debug_type(OB_DEBUG_FOCUS
,
479 "Not focusing the window because it is on "
480 "another desktop and no relatives are focused ");
485 ob_debug_type(OB_DEBUG_FOCUS
,
486 "Focus stealing prevention activated for %s at "
487 "time %u (last user interactioon time %u)",
488 self
->title
, map_time
, event_last_user_time
);
489 /* if the client isn't focused, then hilite it so the user
491 client_hilite(self
, TRUE
);
492 /* we may want to raise it even tho we're not activating it */
493 if (raise
&& !client_restore_session_stacking(self
))
494 stacking_raise(CLIENT_AS_WINDOW(self
));
498 /* This may look rather odd. Well it's because new windows are added
499 to the stacking order non-intrusively. If we're not going to focus
500 the new window or hilite it, then we raise it to the top. This will
501 take affect for things that don't get focused like splash screens.
502 Also if you don't have focus_new enabled, then it's going to get
503 raised to the top. Legacy begets legacy I guess?
505 if (!client_restore_session_stacking(self
))
506 stacking_raise(CLIENT_AS_WINDOW(self
));
509 mouse_grab_for_client(self
, TRUE
);
511 /* this has to happen before we try focus the window, but we want it to
512 happen after the client's stacking has been determined or it looks bad
516 if (!config_focus_under_mouse
)
517 ignore_start
= event_start_ignore_all_enters();
521 if (!config_focus_under_mouse
)
522 event_end_ignore_all_enters(ignore_start
);
526 gboolean stacked
= client_restore_session_stacking(self
);
527 client_present(self
, FALSE
, !stacked
, TRUE
);
530 /* add to client list/map */
531 client_list
= g_list_append(client_list
, self
);
532 window_add(&self
->window
, CLIENT_AS_WINDOW(self
));
534 /* this has to happen after we're in the client_list */
535 if (STRUT_EXISTS(self
->strut
))
536 screen_update_areas();
538 /* update the list hints */
541 /* watch for when the application stops responding. only do this for
542 normal windows, i.e. windows which have titlebars and close buttons
543 and things like that.
544 we don't need to stop pinging on unmanage, because it will be handled
545 automatically by the destroy callback!
547 if (self
->ping
&& client_normal(self
))
548 ping_start(self
, client_ping_event
);
550 /* free the ObAppSettings shallow copy */
553 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
554 window
, self
->frame
->window
, self
->class);
558 ObClient
*client_fake_manage(Window window
)
561 ObAppSettings
*settings
;
563 ob_debug("Pretend-managing window: %lx", window
);
565 /* do this minimal stuff to figure out the client's decorations */
567 self
= g_new0(ObClient
, 1);
568 self
->window
= window
;
570 client_get_all(self
, FALSE
);
571 /* per-app settings override stuff, and return the settings for other
572 uses too. this returns a shallow copy that needs to be freed */
573 settings
= client_get_settings_state(self
);
575 client_setup_decor_and_functions(self
, FALSE
);
577 /* create the decoration frame for the client window and adjust its size */
578 self
->frame
= frame_new(self
);
579 frame_adjust_area(self
->frame
, FALSE
, TRUE
, TRUE
);
581 ob_debug("gave extents left %d right %d top %d bottom %d",
582 self
->frame
->size
.left
, self
->frame
->size
.right
,
583 self
->frame
->size
.top
, self
->frame
->size
.bottom
);
585 /* free the ObAppSettings shallow copy */
591 void client_unmanage_all(void)
593 while (client_list
!= NULL
)
594 client_unmanage(client_list
->data
);
597 void client_unmanage(ObClient
*self
)
603 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
604 self
->window
, self
->frame
->window
,
605 self
->class, self
->title
? self
->title
: "");
607 g_assert(self
!= NULL
);
609 /* we dont want events no more. do this before hiding the frame so we
610 don't generate more events */
611 XSelectInput(obt_display
, self
->window
, NoEventMask
);
613 /* ignore enter events from the unmap so it doesnt mess with the focus */
614 if (!config_focus_under_mouse
)
615 ignore_start
= event_start_ignore_all_enters();
617 frame_hide(self
->frame
);
618 /* flush to send the hide to the server quickly */
621 if (!config_focus_under_mouse
)
622 event_end_ignore_all_enters(ignore_start
);
624 mouse_grab_for_client(self
, FALSE
);
626 /* remove the window from our save set, unless we are managing an internal
629 XChangeSaveSet(obt_display
, self
->window
, SetModeDelete
);
631 /* update the focus lists */
632 focus_order_remove(self
);
633 if (client_focused(self
)) {
634 /* don't leave an invalid focus_client */
638 /* if we're prompting to kill the client, close that */
639 prompt_unref(self
->kill_prompt
);
640 self
->kill_prompt
= NULL
;
642 client_list
= g_list_remove(client_list
, self
);
643 stacking_remove(self
);
644 window_remove(self
->window
);
646 /* once the client is out of the list, update the struts to remove its
648 if (STRUT_EXISTS(self
->strut
))
649 screen_update_areas();
651 client_call_notifies(self
, client_destroy_notifies
);
653 /* tell our parent(s) that we're gone */
654 for (it
= self
->parents
; it
; it
= g_slist_next(it
))
655 ((ObClient
*)it
->data
)->transients
=
656 g_slist_remove(((ObClient
*)it
->data
)->transients
,self
);
658 /* tell our transients that we're gone */
659 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
660 ((ObClient
*)it
->data
)->parents
=
661 g_slist_remove(((ObClient
*)it
->data
)->parents
, self
);
662 /* we could be keeping our children in a higher layer */
663 client_calc_layer(it
->data
);
666 /* remove from its group */
668 group_remove(self
->group
, self
);
672 /* restore the window's original geometry so it is not lost */
678 if (self
->fullscreen
)
679 a
= self
->pre_fullscreen_area
;
680 else if (self
->max_horz
|| self
->max_vert
) {
681 if (self
->max_horz
) {
682 a
.x
= self
->pre_max_area
.x
;
683 a
.width
= self
->pre_max_area
.width
;
685 if (self
->max_vert
) {
686 a
.y
= self
->pre_max_area
.y
;
687 a
.height
= self
->pre_max_area
.height
;
691 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
692 /* let it be moved and resized no matter what */
693 self
->functions
= OB_CLIENT_FUNC_MOVE
| OB_CLIENT_FUNC_RESIZE
;
694 self
->decorations
= 0; /* unmanaged windows have no decor */
696 /* give the client its border back */
697 XSetWindowBorderWidth(obt_display
, self
->window
, self
->border_width
);
699 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
702 /* reparent the window out of the frame, and free the frame */
703 frame_release_client(self
->frame
);
704 frame_free(self
->frame
);
707 if (ob_state() != OB_STATE_EXITING
) {
708 /* these values should not be persisted across a window
710 OBT_PROP_ERASE(self
->window
, NET_WM_DESKTOP
);
711 OBT_PROP_ERASE(self
->window
, NET_WM_STATE
);
712 OBT_PROP_ERASE(self
->window
, WM_STATE
);
714 /* if we're left in an unmapped state, the client wont be mapped.
715 this is bad, since we will no longer be managing the window on
717 XMapWindow(obt_display
, self
->window
);
720 /* these should not be left on the window ever. other window managers
721 don't necessarily use them and it will mess them up (like compiz) */
722 OBT_PROP_ERASE(self
->window
, NET_WM_VISIBLE_NAME
);
723 OBT_PROP_ERASE(self
->window
, NET_WM_VISIBLE_ICON_NAME
);
725 /* update the list hints */
728 ob_debug("Unmanaged window 0x%lx", self
->window
);
730 /* free all data allocated in the client struct */
731 g_slist_free(self
->transients
);
732 for (j
= 0; j
< self
->nicons
; ++j
)
733 g_free(self
->icons
[j
].data
);
734 if (self
->nicons
> 0)
736 g_free(self
->startup_id
);
737 g_free(self
->wm_command
);
739 g_free(self
->icon_title
);
743 g_free(self
->client_machine
);
744 g_free(self
->sm_client_id
);
748 void client_fake_unmanage(ObClient
*self
)
750 /* this is all that got allocated to get the decorations */
752 frame_free(self
->frame
);
756 /*! Returns a new structure containing the per-app settings for this client.
757 The returned structure needs to be freed with g_free. */
758 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
760 ObAppSettings
*settings
;
763 settings
= config_create_app_settings();
765 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
766 ObAppSettings
*app
= it
->data
;
767 gboolean match
= TRUE
;
769 g_assert(app
->name
!= NULL
|| app
->class != NULL
);
771 /* we know that either name or class is not NULL so it will have to
772 match to use the rule */
774 !g_pattern_match(app
->name
, strlen(self
->name
), self
->name
, NULL
))
776 else if (app
->class &&
777 !g_pattern_match(app
->class,
778 strlen(self
->class), self
->class, NULL
))
780 else if (app
->role
&&
781 !g_pattern_match(app
->role
,
782 strlen(self
->role
), self
->role
, NULL
))
786 ob_debug("Window matching: %s", app
->name
);
788 /* copy the settings to our struct, overriding the existing
789 settings if they are not defaults */
790 config_app_settings_copy_non_defaults(app
, settings
);
794 if (settings
->shade
!= -1)
795 self
->shaded
= !!settings
->shade
;
796 if (settings
->decor
!= -1)
797 self
->undecorated
= !settings
->decor
;
798 if (settings
->iconic
!= -1)
799 self
->iconic
= !!settings
->iconic
;
800 if (settings
->skip_pager
!= -1)
801 self
->skip_pager
= !!settings
->skip_pager
;
802 if (settings
->skip_taskbar
!= -1)
803 self
->skip_taskbar
= !!settings
->skip_taskbar
;
805 if (settings
->max_vert
!= -1)
806 self
->max_vert
= !!settings
->max_vert
;
807 if (settings
->max_horz
!= -1)
808 self
->max_horz
= !!settings
->max_horz
;
810 if (settings
->fullscreen
!= -1)
811 self
->fullscreen
= !!settings
->fullscreen
;
813 if (settings
->desktop
) {
814 if (settings
->desktop
== DESKTOP_ALL
)
815 self
->desktop
= settings
->desktop
;
816 else if (settings
->desktop
> 0 &&
817 settings
->desktop
<= screen_num_desktops
)
818 self
->desktop
= settings
->desktop
- 1;
821 if (settings
->layer
== -1) {
825 else if (settings
->layer
== 0) {
829 else if (settings
->layer
== 1) {
836 static void client_restore_session_state(ObClient
*self
)
840 ob_debug_type(OB_DEBUG_SM
,
841 "Restore session for client %s", self
->title
);
843 if (!(it
= session_state_find(self
))) {
844 ob_debug_type(OB_DEBUG_SM
,
845 "Session data not found for client %s", self
->title
);
849 self
->session
= it
->data
;
851 ob_debug_type(OB_DEBUG_SM
, "Session data loaded for client %s",
854 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
855 self
->positioned
= USPosition
;
856 self
->sized
= USSize
;
857 if (self
->session
->w
> 0)
858 self
->area
.width
= self
->session
->w
;
859 if (self
->session
->h
> 0)
860 self
->area
.height
= self
->session
->h
;
861 XResizeWindow(obt_display
, self
->window
,
862 self
->area
.width
, self
->area
.height
);
864 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
865 self
->session
->desktop
:
866 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
867 OBT_PROP_SET32(self
->window
, NET_WM_DESKTOP
, CARDINAL
, self
->desktop
);
869 self
->shaded
= self
->session
->shaded
;
870 self
->iconic
= self
->session
->iconic
;
871 self
->skip_pager
= self
->session
->skip_pager
;
872 self
->skip_taskbar
= self
->session
->skip_taskbar
;
873 self
->fullscreen
= self
->session
->fullscreen
;
874 self
->above
= self
->session
->above
;
875 self
->below
= self
->session
->below
;
876 self
->max_horz
= self
->session
->max_horz
;
877 self
->max_vert
= self
->session
->max_vert
;
878 self
->undecorated
= self
->session
->undecorated
;
881 static gboolean
client_restore_session_stacking(ObClient
*self
)
885 if (!self
->session
) return FALSE
;
887 mypos
= g_list_find(session_saved_state
, self
->session
);
888 if (!mypos
) return FALSE
;
890 /* start above me and look for the first client */
891 for (it
= g_list_previous(mypos
); it
; it
= g_list_previous(it
)) {
894 for (cit
= client_list
; cit
; cit
= g_list_next(cit
)) {
895 ObClient
*c
= cit
->data
;
896 /* found a client that was in the session, so go below it */
897 if (c
->session
== it
->data
) {
898 stacking_below(CLIENT_AS_WINDOW(self
),
899 CLIENT_AS_WINDOW(cit
->data
));
907 void client_move_onscreen(ObClient
*self
, gboolean rude
)
909 gint x
= self
->area
.x
;
910 gint y
= self
->area
.y
;
911 if (client_find_onscreen(self
, &x
, &y
,
913 self
->area
.height
, rude
)) {
914 client_move(self
, x
, y
);
918 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
921 gint ox
= *x
, oy
= *y
;
922 gboolean rudel
= rude
, ruder
= rude
, rudet
= rude
, rudeb
= rude
;
928 RECT_SET(desired
, *x
, *y
, w
, h
);
929 frame_rect_to_frame(self
->frame
, &desired
);
931 /* get where the frame would be */
932 frame_client_gravity(self
->frame
, x
, y
);
934 /* get the requested size of the window with decorations */
935 fw
= self
->frame
->size
.left
+ w
+ self
->frame
->size
.right
;
936 fh
= self
->frame
->size
.top
+ h
+ self
->frame
->size
.bottom
;
938 /* If rudeness wasn't requested, then still be rude in a given direction
939 if the client is not moving, only resizing in that direction */
941 Point oldtl
, oldtr
, oldbl
, oldbr
;
942 Point newtl
, newtr
, newbl
, newbr
;
943 gboolean stationary_l
, stationary_r
, stationary_t
, stationary_b
;
945 POINT_SET(oldtl
, self
->frame
->area
.x
, self
->frame
->area
.y
);
946 POINT_SET(oldbr
, self
->frame
->area
.x
+ self
->frame
->area
.width
- 1,
947 self
->frame
->area
.y
+ self
->frame
->area
.height
- 1);
948 POINT_SET(oldtr
, oldbr
.x
, oldtl
.y
);
949 POINT_SET(oldbl
, oldtl
.x
, oldbr
.y
);
951 POINT_SET(newtl
, *x
, *y
);
952 POINT_SET(newbr
, *x
+ fw
- 1, *y
+ fh
- 1);
953 POINT_SET(newtr
, newbr
.x
, newtl
.y
);
954 POINT_SET(newbl
, newtl
.x
, newbr
.y
);
956 /* is it moving or just resizing from some corner? */
957 stationary_l
= oldtl
.x
== newtl
.x
;
958 stationary_r
= oldtr
.x
== newtr
.x
;
959 stationary_t
= oldtl
.y
== newtl
.y
;
960 stationary_b
= oldbl
.y
== newbl
.y
;
962 /* if left edge is growing and didnt move right edge */
963 if (stationary_r
&& newtl
.x
< oldtl
.x
)
965 /* if right edge is growing and didnt move left edge */
966 if (stationary_l
&& newtr
.x
> oldtr
.x
)
968 /* if top edge is growing and didnt move bottom edge */
969 if (stationary_b
&& newtl
.y
< oldtl
.y
)
971 /* if bottom edge is growing and didnt move top edge */
972 if (stationary_t
&& newbl
.y
> oldbl
.y
)
976 /* we iterate through every monitor that the window is at least partially
977 on, to make sure it is obeying the rules on them all
979 if the window does not appear on any monitors, then use the first one
982 for (i
= 0; i
< screen_num_monitors
; ++i
) {
985 if (!screen_physical_area_monitor_contains(i
, &desired
)) {
986 if (i
< screen_num_monitors
- 1 || found_mon
)
989 /* the window is not inside any monitor! so just use the first
991 a
= screen_area(self
->desktop
, 0, NULL
);
994 a
= screen_area(self
->desktop
, SCREEN_AREA_ONE_MONITOR
, &desired
);
997 /* This makes sure windows aren't entirely outside of the screen so you
998 can't see them at all.
999 It makes sure 10% of the window is on the screen at least. At don't
1000 let it move itself off the top of the screen, which would hide the
1001 titlebar on you. (The user can still do this if they want too, it's
1002 only limiting the application.
1004 if (client_normal(self
)) {
1005 if (!self
->strut
.right
&& *x
+ fw
/10 >= a
->x
+ a
->width
- 1)
1006 *x
= a
->x
+ a
->width
- fw
/10;
1007 if (!self
->strut
.bottom
&& *y
+ fh
/10 >= a
->y
+ a
->height
- 1)
1008 *y
= a
->y
+ a
->height
- fh
/10;
1009 if (!self
->strut
.left
&& *x
+ fw
*9/10 - 1 < a
->x
)
1010 *x
= a
->x
- fw
*9/10;
1011 if (!self
->strut
.top
&& *y
+ fh
*9/10 - 1 < a
->y
)
1012 *y
= a
->y
- fh
*9/10;
1015 /* This here doesn't let windows even a pixel outside the
1016 struts/screen. When called from client_manage, programs placing
1017 themselves are forced completely onscreen, while things like
1018 xterm -geometry resolution-width/2 will work fine. Trying to
1019 place it completely offscreen will be handled in the above code.
1020 Sorry for this confused comment, i am tired. */
1021 if (rudel
&& !self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
1022 if (ruder
&& !self
->strut
.right
&& *x
+ fw
> a
->x
+ a
->width
)
1023 *x
= a
->x
+ MAX(0, a
->width
- fw
);
1025 if (rudet
&& !self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
1026 if (rudeb
&& !self
->strut
.bottom
&& *y
+ fh
> a
->y
+ a
->height
)
1027 *y
= a
->y
+ MAX(0, a
->height
- fh
);
1032 /* get where the client should be */
1033 frame_frame_gravity(self
->frame
, x
, y
);
1035 return ox
!= *x
|| oy
!= *y
;
1038 static void client_get_all(ObClient
*self
, gboolean real
)
1040 /* this is needed for the frame to set itself up */
1041 client_get_area(self
);
1043 /* these things can change the decor and functions of the window */
1045 client_get_mwm_hints(self
);
1046 /* this can change the mwmhints for special cases */
1047 client_get_type_and_transientness(self
);
1048 client_get_state(self
);
1049 client_update_normal_hints(self
);
1051 /* get the session related properties, these can change decorations
1052 from per-app settings */
1053 client_get_session_ids(self
);
1055 /* now we got everything that can affect the decorations */
1059 /* get this early so we have it for debugging */
1060 client_update_title(self
);
1062 client_update_protocols(self
);
1064 client_update_wmhints(self
);
1065 /* this may have already been called from client_update_wmhints */
1066 if (!self
->parents
&& !self
->transient_for_group
)
1067 client_update_transient_for(self
);
1069 client_get_startup_id(self
);
1070 client_get_desktop(self
);/* uses transient data/group/startup id if a
1071 desktop is not specified */
1072 client_get_shaped(self
);
1075 /* a couple type-based defaults for new windows */
1077 /* this makes sure that these windows appear on all desktops */
1078 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1079 self
->desktop
= DESKTOP_ALL
;
1083 client_update_sync_request_counter(self
);
1086 client_get_colormap(self
);
1087 client_update_strut(self
);
1088 client_update_icons(self
);
1089 client_update_icon_geometry(self
);
1092 static void client_get_startup_id(ObClient
*self
)
1094 if (!(OBT_PROP_GETS(self
->window
, NET_STARTUP_ID
, utf8
,
1095 &self
->startup_id
)))
1097 OBT_PROP_GETS(self
->group
->leader
,
1098 NET_STARTUP_ID
, utf8
, &self
->startup_id
);
1101 static void client_get_area(ObClient
*self
)
1103 XWindowAttributes wattrib
;
1106 ret
= XGetWindowAttributes(obt_display
, self
->window
, &wattrib
);
1107 g_assert(ret
!= BadWindow
);
1109 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
1110 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
1111 self
->border_width
= wattrib
.border_width
;
1113 ob_debug("client area: %d %d %d %d bw %d", wattrib
.x
, wattrib
.y
,
1114 wattrib
.width
, wattrib
.height
, wattrib
.border_width
);
1117 static void client_get_desktop(ObClient
*self
)
1119 guint32 d
= screen_num_desktops
; /* an always-invalid value */
1121 if (OBT_PROP_GET32(self
->window
, NET_WM_DESKTOP
, CARDINAL
, &d
)) {
1122 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
1123 self
->desktop
= screen_num_desktops
- 1;
1126 ob_debug("client requested desktop 0x%x", self
->desktop
);
1129 gboolean first
= TRUE
;
1130 guint all
= screen_num_desktops
; /* not a valid value */
1132 /* if they are all on one desktop, then open it on the
1134 for (it
= self
->parents
; it
; it
= g_slist_next(it
)) {
1135 ObClient
*c
= it
->data
;
1137 if (c
->desktop
== DESKTOP_ALL
) continue;
1143 else if (all
!= c
->desktop
)
1144 all
= screen_num_desktops
; /* make it invalid */
1146 if (all
!= screen_num_desktops
) {
1147 self
->desktop
= all
;
1149 ob_debug("client desktop set from parents: 0x%x",
1152 /* try get from the startup-notification protocol */
1153 else if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1154 if (self
->desktop
>= screen_num_desktops
&&
1155 self
->desktop
!= DESKTOP_ALL
)
1156 self
->desktop
= screen_num_desktops
- 1;
1157 ob_debug("client desktop set from startup-notification: 0x%x",
1160 /* defaults to the current desktop */
1162 self
->desktop
= screen_desktop
;
1163 ob_debug("client desktop set to the current desktop: %d",
1169 static void client_get_state(ObClient
*self
)
1174 if (OBT_PROP_GETA32(self
->window
, NET_WM_STATE
, ATOM
, &state
, &num
)) {
1176 for (i
= 0; i
< num
; ++i
) {
1177 if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_MODAL
))
1179 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_SHADED
))
1180 self
->shaded
= TRUE
;
1181 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
))
1182 self
->iconic
= TRUE
;
1183 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
))
1184 self
->skip_taskbar
= TRUE
;
1185 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
))
1186 self
->skip_pager
= TRUE
;
1187 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
))
1188 self
->fullscreen
= TRUE
;
1189 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
))
1190 self
->max_vert
= TRUE
;
1191 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
))
1192 self
->max_horz
= TRUE
;
1193 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE
))
1195 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_BELOW
))
1197 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
))
1198 self
->demands_attention
= TRUE
;
1199 else if (state
[i
] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
))
1200 self
->undecorated
= TRUE
;
1207 static void client_get_shaped(ObClient
*self
)
1209 self
->shaped
= FALSE
;
1211 if (obt_display_extension_shape
) {
1216 XShapeSelectInput(obt_display
, self
->window
, ShapeNotifyMask
);
1218 XShapeQueryExtents(obt_display
, self
->window
, &s
, &foo
,
1219 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1221 self
->shaped
= (s
!= 0);
1226 void client_update_transient_for(ObClient
*self
)
1229 ObClient
*target
= NULL
;
1230 gboolean trangroup
= FALSE
;
1232 if (XGetTransientForHint(obt_display
, self
->window
, &t
)) {
1233 if (t
!= self
->window
) { /* cant be transient to itself! */
1234 ObWindow
*tw
= window_find(t
);
1235 /* if this happens then we need to check for it*/
1236 g_assert(tw
!= CLIENT_AS_WINDOW(self
));
1237 if (tw
&& WINDOW_IS_CLIENT(tw
)) {
1238 /* watch out for windows with a parent that is something
1239 different, like a dockapp for example */
1240 target
= WINDOW_AS_CLIENT(tw
);
1244 /* Setting the transient_for to Root is actually illegal, however
1245 applications from time have done this to specify transient for
1247 if (!target
&& self
->group
&& t
== obt_root(ob_screen
))
1249 } else if (self
->group
&& self
->transient
)
1252 client_update_transient_tree(self
, self
->group
, self
->group
,
1253 self
->transient_for_group
, trangroup
,
1254 client_direct_parent(self
), target
);
1255 self
->transient_for_group
= trangroup
;
1259 static void client_update_transient_tree(ObClient
*self
,
1260 ObGroup
*oldgroup
, ObGroup
*newgroup
,
1261 gboolean oldgtran
, gboolean newgtran
,
1262 ObClient
* oldparent
,
1263 ObClient
*newparent
)
1268 g_assert(!oldgtran
|| oldgroup
);
1269 g_assert(!newgtran
|| newgroup
);
1270 g_assert((!oldgtran
&& !oldparent
) ||
1271 (oldgtran
&& !oldparent
) ||
1272 (!oldgtran
&& oldparent
));
1273 g_assert((!newgtran
&& !newparent
) ||
1274 (newgtran
&& !newparent
) ||
1275 (!newgtran
&& newparent
));
1278 Group transient windows are not allowed to have other group
1279 transient windows as their children.
1283 /* No change has occured */
1284 if (oldgroup
== newgroup
&&
1285 oldgtran
== newgtran
&&
1286 oldparent
== newparent
) return;
1288 /** Remove the client from the transient tree **/
1290 for (it
= self
->transients
; it
; it
= next
) {
1291 next
= g_slist_next(it
);
1293 self
->transients
= g_slist_delete_link(self
->transients
, it
);
1294 c
->parents
= g_slist_remove(c
->parents
, self
);
1296 for (it
= self
->parents
; it
; it
= next
) {
1297 next
= g_slist_next(it
);
1299 self
->parents
= g_slist_delete_link(self
->parents
, it
);
1300 c
->transients
= g_slist_remove(c
->transients
, self
);
1303 /** Re-add the client to the transient tree **/
1305 /* If we're transient for a group then we need to add ourselves to all our
1308 for (it
= newgroup
->members
; it
; it
= g_slist_next(it
)) {
1311 !client_search_top_direct_parent(c
)->transient_for_group
&&
1314 c
->transients
= g_slist_prepend(c
->transients
, self
);
1315 self
->parents
= g_slist_prepend(self
->parents
, c
);
1320 /* If we are now transient for a single window we need to add ourselves to
1323 WARNING: Cyclical transient ness is possible if two windows are
1324 transient for eachother.
1326 else if (newparent
&&
1327 /* don't make ourself its child if it is already our child */
1328 !client_is_direct_child(self
, newparent
) &&
1329 client_normal(newparent
))
1331 newparent
->transients
= g_slist_prepend(newparent
->transients
, self
);
1332 self
->parents
= g_slist_prepend(self
->parents
, newparent
);
1335 /* Add any group transient windows to our children. But if we're transient
1336 for the group, then other group transients are not our children.
1338 WARNING: Cyclical transient-ness is possible. For e.g. if:
1339 A is transient for the group
1340 B is transient for A
1341 C is transient for B
1342 A can't be transient for C or we have a cycle
1344 if (!newgtran
&& newgroup
&&
1346 !client_search_top_direct_parent(newparent
)->transient_for_group
) &&
1347 client_normal(self
))
1349 for (it
= newgroup
->members
; it
; it
= g_slist_next(it
)) {
1351 if (c
!= self
&& c
->transient_for_group
&&
1352 /* Don't make it our child if it is already our parent */
1353 !client_is_direct_child(c
, self
))
1355 self
->transients
= g_slist_prepend(self
->transients
, c
);
1356 c
->parents
= g_slist_prepend(c
->parents
, self
);
1361 /** If we change our group transient-ness, our children change their
1362 effect group transient-ness, which affects how they relate to other
1365 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1367 if (!c
->transient_for_group
)
1368 client_update_transient_tree(c
, c
->group
, c
->group
,
1369 c
->transient_for_group
,
1370 c
->transient_for_group
,
1371 client_direct_parent(c
),
1372 client_direct_parent(c
));
1376 static void client_get_mwm_hints(ObClient
*self
)
1381 self
->mwmhints
.flags
= 0; /* default to none */
1383 if (OBT_PROP_GETA32(self
->window
, MOTIF_WM_HINTS
, MOTIF_WM_HINTS
,
1385 if (num
>= OB_MWM_ELEMENTS
) {
1386 self
->mwmhints
.flags
= hints
[0];
1387 self
->mwmhints
.functions
= hints
[1];
1388 self
->mwmhints
.decorations
= hints
[2];
1394 void client_get_type_and_transientness(ObClient
*self
)
1401 self
->transient
= FALSE
;
1403 if (OBT_PROP_GETA32(self
->window
, NET_WM_WINDOW_TYPE
, ATOM
, &val
, &num
)) {
1404 /* use the first value that we know about in the array */
1405 for (i
= 0; i
< num
; ++i
) {
1406 if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP
))
1407 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1408 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK
))
1409 self
->type
= OB_CLIENT_TYPE_DOCK
;
1410 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR
))
1411 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1412 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU
))
1413 self
->type
= OB_CLIENT_TYPE_MENU
;
1414 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY
))
1415 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1416 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH
))
1417 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1418 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG
))
1419 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1420 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL
))
1421 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1422 else if (val
[i
] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE
))
1424 /* prevent this window from getting any decor or
1426 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1427 OB_MWM_FLAG_DECORATIONS
);
1428 self
->mwmhints
.decorations
= 0;
1429 self
->mwmhints
.functions
= 0;
1431 if (self
->type
!= (ObClientType
) -1)
1432 break; /* grab the first legit type */
1437 if (XGetTransientForHint(obt_display
, self
->window
, &t
))
1438 self
->transient
= TRUE
;
1440 if (self
->type
== (ObClientType
) -1) {
1441 /*the window type hint was not set, which means we either classify
1442 ourself as a normal window or a dialog, depending on if we are a
1444 if (self
->transient
)
1445 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1447 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1450 /* then, based on our type, we can update our transientness.. */
1451 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1452 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1453 self
->type
== OB_CLIENT_TYPE_MENU
||
1454 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1456 self
->transient
= TRUE
;
1460 void client_update_protocols(ObClient
*self
)
1465 self
->focus_notify
= FALSE
;
1466 self
->delete_window
= FALSE
;
1468 if (OBT_PROP_GETA32(self
->window
, WM_PROTOCOLS
, ATOM
, &proto
, &num_ret
)) {
1469 for (i
= 0; i
< num_ret
; ++i
) {
1470 if (proto
[i
] == OBT_PROP_ATOM(WM_DELETE_WINDOW
))
1471 /* this means we can request the window to close */
1472 self
->delete_window
= TRUE
;
1473 else if (proto
[i
] == OBT_PROP_ATOM(WM_TAKE_FOCUS
))
1474 /* if this protocol is requested, then the window will be
1475 notified whenever we want it to receive focus */
1476 self
->focus_notify
= TRUE
;
1477 else if (proto
[i
] == OBT_PROP_ATOM(NET_WM_PING
))
1478 /* if this protocol is requested, then the window will allow
1479 pings to determine if it is still alive */
1482 else if (proto
[i
] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST
))
1483 /* if this protocol is requested, then resizing the
1484 window will be synchronized between the frame and the
1486 self
->sync_request
= TRUE
;
1494 void client_update_sync_request_counter(ObClient
*self
)
1498 if (OBT_PROP_GET32(self
->window
, NET_WM_SYNC_REQUEST_COUNTER
, CARDINAL
,&i
))
1500 self
->sync_counter
= i
;
1502 self
->sync_counter
= None
;
1506 static void client_get_colormap(ObClient
*self
)
1508 XWindowAttributes wa
;
1510 if (XGetWindowAttributes(obt_display
, self
->window
, &wa
))
1511 client_update_colormap(self
, wa
.colormap
);
1514 void client_update_colormap(ObClient
*self
, Colormap colormap
)
1516 if (colormap
== self
->colormap
) return;
1518 ob_debug("Setting client %s colormap: 0x%x", self
->title
, colormap
);
1520 if (client_focused(self
)) {
1521 screen_install_colormap(self
, FALSE
); /* uninstall old one */
1522 self
->colormap
= colormap
;
1523 screen_install_colormap(self
, FALSE
); /* install new one */
1525 self
->colormap
= colormap
;
1528 void client_update_normal_hints(ObClient
*self
)
1534 self
->min_ratio
= 0.0f
;
1535 self
->max_ratio
= 0.0f
;
1536 SIZE_SET(self
->size_inc
, 1, 1);
1537 SIZE_SET(self
->base_size
, 0, 0);
1538 SIZE_SET(self
->min_size
, 0, 0);
1539 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1541 /* get the hints from the window */
1542 if (XGetWMNormalHints(obt_display
, self
->window
, &size
, &ret
)) {
1543 /* normal windows can't request placement! har har
1544 if (!client_normal(self))
1546 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1547 self
->sized
= (size
.flags
& (PSize
|USSize
));
1549 if (size
.flags
& PWinGravity
)
1550 self
->gravity
= size
.win_gravity
;
1552 if (size
.flags
& PAspect
) {
1553 if (size
.min_aspect
.y
)
1555 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1556 if (size
.max_aspect
.y
)
1558 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1561 if (size
.flags
& PMinSize
)
1562 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1564 if (size
.flags
& PMaxSize
)
1565 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1567 if (size
.flags
& PBaseSize
)
1568 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1570 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1571 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1573 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1574 self
->min_size
.width
, self
->min_size
.height
,
1575 self
->max_size
.width
, self
->max_size
.height
);
1576 ob_debug("size inc (%d %d) base size (%d %d)",
1577 self
->size_inc
.width
, self
->size_inc
.height
,
1578 self
->base_size
.width
, self
->base_size
.height
);
1581 ob_debug("Normal hints: not set");
1584 void client_setup_decor_and_functions(ObClient
*self
, gboolean reconfig
)
1586 /* start with everything (cept fullscreen) */
1588 (OB_FRAME_DECOR_TITLEBAR
|
1589 OB_FRAME_DECOR_HANDLE
|
1590 OB_FRAME_DECOR_GRIPS
|
1591 OB_FRAME_DECOR_BORDER
|
1592 OB_FRAME_DECOR_ICON
|
1593 OB_FRAME_DECOR_ALLDESKTOPS
|
1594 OB_FRAME_DECOR_ICONIFY
|
1595 OB_FRAME_DECOR_MAXIMIZE
|
1596 OB_FRAME_DECOR_SHADE
|
1597 OB_FRAME_DECOR_CLOSE
);
1599 (OB_CLIENT_FUNC_RESIZE
|
1600 OB_CLIENT_FUNC_MOVE
|
1601 OB_CLIENT_FUNC_ICONIFY
|
1602 OB_CLIENT_FUNC_MAXIMIZE
|
1603 OB_CLIENT_FUNC_SHADE
|
1604 OB_CLIENT_FUNC_CLOSE
|
1605 OB_CLIENT_FUNC_BELOW
|
1606 OB_CLIENT_FUNC_ABOVE
|
1607 OB_CLIENT_FUNC_UNDECORATE
);
1609 if (!(self
->min_size
.width
< self
->max_size
.width
||
1610 self
->min_size
.height
< self
->max_size
.height
))
1611 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1613 switch (self
->type
) {
1614 case OB_CLIENT_TYPE_NORMAL
:
1615 /* normal windows retain all of the possible decorations and
1616 functionality, and can be fullscreen */
1617 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1620 case OB_CLIENT_TYPE_DIALOG
:
1621 /* sometimes apps make dialog windows fullscreen for some reason (for
1622 e.g. kpdf does this..) */
1623 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1626 case OB_CLIENT_TYPE_UTILITY
:
1627 /* these windows don't have anything added or removed by default */
1630 case OB_CLIENT_TYPE_MENU
:
1631 case OB_CLIENT_TYPE_TOOLBAR
:
1632 /* these windows can't iconify or maximize */
1633 self
->decorations
&= ~(OB_FRAME_DECOR_ICONIFY
|
1634 OB_FRAME_DECOR_MAXIMIZE
);
1635 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
|
1636 OB_CLIENT_FUNC_MAXIMIZE
);
1639 case OB_CLIENT_TYPE_SPLASH
:
1640 /* these don't get get any decorations, and the only thing you can
1641 do with them is move them */
1642 self
->decorations
= 0;
1643 self
->functions
= OB_CLIENT_FUNC_MOVE
;
1646 case OB_CLIENT_TYPE_DESKTOP
:
1647 /* these windows are not manipulated by the window manager */
1648 self
->decorations
= 0;
1649 self
->functions
= 0;
1652 case OB_CLIENT_TYPE_DOCK
:
1653 /* these windows are not manipulated by the window manager, but they
1654 can set below layer which has a special meaning */
1655 self
->decorations
= 0;
1656 self
->functions
= OB_CLIENT_FUNC_BELOW
;
1660 /* Mwm Hints are applied subtractively to what has already been chosen for
1661 decor and functionality */
1662 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1663 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1664 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1665 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1667 /* if the mwm hints request no handle or title, then all
1668 decorations are disabled, but keep the border if that's
1670 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1671 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1673 self
->decorations
= 0;
1678 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1679 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1680 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1681 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1682 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1683 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1684 /* dont let mwm hints kill any buttons
1685 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1686 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1687 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1688 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1690 /* dont let mwm hints kill the close button
1691 if (! (self->mwmhints.functions & MwmFunc_Close))
1692 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1696 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1697 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1698 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1699 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1700 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1701 self
->decorations
&= ~(OB_FRAME_DECOR_GRIPS
| OB_FRAME_DECOR_HANDLE
);
1703 /* can't maximize without moving/resizing */
1704 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1705 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1706 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1707 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1708 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1711 if (self
->max_horz
&& self
->max_vert
) {
1712 /* you can't resize fully maximized windows */
1713 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1714 /* kill the handle on fully maxed windows */
1715 self
->decorations
&= ~(OB_FRAME_DECOR_HANDLE
| OB_FRAME_DECOR_GRIPS
);
1718 /* If there are no decorations to remove, don't allow the user to try
1720 if (self
->decorations
== 0)
1721 self
->functions
&= ~OB_CLIENT_FUNC_UNDECORATE
;
1723 /* finally, the user can have requested no decorations, which overrides
1724 everything (but doesnt give it a border if it doesnt have one) */
1725 if (self
->undecorated
)
1726 self
->decorations
= 0;
1728 /* if we don't have a titlebar, then we cannot shade! */
1729 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1730 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1732 /* now we need to check against rules for the client's current state */
1733 if (self
->fullscreen
) {
1734 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1735 OB_CLIENT_FUNC_FULLSCREEN
|
1736 OB_CLIENT_FUNC_ICONIFY
);
1737 self
->decorations
= 0;
1740 client_change_allowed_actions(self
);
1743 /* force reconfigure to make sure decorations are updated */
1744 client_reconfigure(self
, TRUE
);
1747 static void client_change_allowed_actions(ObClient
*self
)
1752 /* desktop windows are kept on all desktops */
1753 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1754 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP
);
1756 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1757 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE
);
1758 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1759 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE
);
1760 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1761 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE
);
1762 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1763 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE
);
1764 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1765 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE
);
1766 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1767 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN
);
1768 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1769 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ
);
1770 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT
);
1772 if (self
->functions
& OB_CLIENT_FUNC_ABOVE
)
1773 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE
);
1774 if (self
->functions
& OB_CLIENT_FUNC_BELOW
)
1775 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW
);
1776 if (self
->functions
& OB_CLIENT_FUNC_UNDECORATE
)
1777 actions
[num
++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE
);
1779 OBT_PROP_SETA32(self
->window
, NET_WM_ALLOWED_ACTIONS
, ATOM
, actions
, num
);
1781 /* make sure the window isn't breaking any rules now
1783 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1784 it can't be iconified with its parent
1787 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1788 if (self
->frame
) client_shade(self
, FALSE
);
1789 else self
->shaded
= FALSE
;
1791 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1792 if (self
->frame
) client_fullscreen(self
, FALSE
);
1793 else self
->fullscreen
= FALSE
;
1795 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1797 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1798 else self
->max_vert
= self
->max_horz
= FALSE
;
1802 void client_update_wmhints(ObClient
*self
)
1806 /* assume a window takes input if it doesnt specify */
1807 self
->can_focus
= TRUE
;
1809 if ((hints
= XGetWMHints(obt_display
, self
->window
)) != NULL
) {
1812 if (hints
->flags
& InputHint
)
1813 self
->can_focus
= hints
->input
;
1815 /* only do this when first managing the window *AND* when we aren't
1817 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1818 if (hints
->flags
& StateHint
)
1819 self
->iconic
= hints
->initial_state
== IconicState
;
1822 self
->urgent
= (hints
->flags
& XUrgencyHint
);
1823 if (self
->urgent
&& !ur
)
1824 client_hilite(self
, TRUE
);
1825 else if (!self
->urgent
&& ur
&& self
->demands_attention
)
1826 client_hilite(self
, FALSE
);
1828 if (!(hints
->flags
& WindowGroupHint
))
1829 hints
->window_group
= None
;
1831 /* did the group state change? */
1832 if (hints
->window_group
!=
1833 (self
->group
? self
->group
->leader
: None
))
1835 ObGroup
*oldgroup
= self
->group
;
1837 /* remove from the old group if there was one */
1838 if (self
->group
!= NULL
) {
1839 group_remove(self
->group
, self
);
1843 /* add ourself to the group if we have one */
1844 if (hints
->window_group
!= None
) {
1845 self
->group
= group_add(hints
->window_group
, self
);
1848 /* Put ourselves into the new group's transient tree, and remove
1849 ourselves from the old group's */
1850 client_update_transient_tree(self
, oldgroup
, self
->group
,
1851 self
->transient_for_group
,
1852 self
->transient_for_group
,
1853 client_direct_parent(self
),
1854 client_direct_parent(self
));
1856 /* Lastly, being in a group, or not, can change if the window is
1857 transient for anything.
1859 The logic for this is:
1860 self->transient = TRUE always if the window wants to be
1861 transient for something, even if transient_for was NULL because
1862 it wasn't in a group before.
1864 If parents was NULL and oldgroup was NULL we can assume
1865 that when we add the new group, it will become transient for
1868 If transient_for_group is TRUE, then it must have already
1869 had a group. If it is getting a new group, the above call to
1870 client_update_transient_tree has already taken care of
1871 everything ! If it is losing all group status then it will
1872 no longer be transient for anything and that needs to be
1875 if (self
->transient
&&
1876 ((self
->parents
== NULL
&& oldgroup
== NULL
) ||
1877 (self
->transient_for_group
&& !self
->group
)))
1878 client_update_transient_for(self
);
1881 /* the WM_HINTS can contain an icon */
1882 if (hints
->flags
& IconPixmapHint
)
1883 client_update_icons(self
);
1889 void client_update_title(ObClient
*self
)
1892 gchar
*visible
= NULL
;
1894 g_free(self
->title
);
1897 if (!OBT_PROP_GETS(self
->window
, NET_WM_NAME
, utf8
, &data
)) {
1898 /* try old x stuff */
1899 if (!(OBT_PROP_GETS(self
->window
, WM_NAME
, locale
, &data
)
1900 || OBT_PROP_GETS(self
->window
, WM_NAME
, utf8
, &data
))) {
1901 if (self
->transient
) {
1903 GNOME alert windows are not given titles:
1904 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1906 data
= g_strdup("");
1908 data
= g_strdup("Unnamed Window");
1912 if (self
->client_machine
) {
1913 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1918 if (self
->not_responding
) {
1920 if (self
->kill_level
> 0)
1921 visible
= g_strdup_printf("%s - [%s]", data
, _("Killing..."));
1923 visible
= g_strdup_printf("%s - [%s]", data
, _("Not Responding"));
1927 OBT_PROP_SETS(self
->window
, NET_WM_VISIBLE_NAME
, utf8
, visible
);
1928 self
->title
= visible
;
1931 frame_adjust_title(self
->frame
);
1933 /* update the icon title */
1935 g_free(self
->icon_title
);
1938 if (!OBT_PROP_GETS(self
->window
, NET_WM_ICON_NAME
, utf8
, &data
))
1939 /* try old x stuff */
1940 if (!(OBT_PROP_GETS(self
->window
, WM_ICON_NAME
, locale
, &data
) ||
1941 OBT_PROP_GETS(self
->window
, WM_ICON_NAME
, utf8
, &data
)))
1942 data
= g_strdup(self
->title
);
1944 if (self
->client_machine
) {
1945 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1950 if (self
->not_responding
) {
1952 if (self
->kill_level
> 0)
1953 visible
= g_strdup_printf("%s - [%s]", data
, _("Killing..."));
1955 visible
= g_strdup_printf("%s - [%s]", data
, _("Not Responding"));
1959 OBT_PROP_SETS(self
->window
, NET_WM_VISIBLE_ICON_NAME
, utf8
, visible
);
1960 self
->icon_title
= visible
;
1963 void client_update_strut(ObClient
*self
)
1967 gboolean got
= FALSE
;
1970 if (OBT_PROP_GETA32(self
->window
, NET_WM_STRUT_PARTIAL
, CARDINAL
,
1975 STRUT_PARTIAL_SET(strut
,
1976 data
[0], data
[2], data
[1], data
[3],
1977 data
[4], data
[5], data
[8], data
[9],
1978 data
[6], data
[7], data
[10], data
[11]);
1984 OBT_PROP_GETA32(self
->window
, NET_WM_STRUT
, CARDINAL
, &data
, &num
)) {
1990 /* use the screen's width/height */
1991 a
= screen_physical_area_all_monitors();
1993 STRUT_PARTIAL_SET(strut
,
1994 data
[0], data
[2], data
[1], data
[3],
1995 a
->y
, a
->y
+ a
->height
- 1,
1996 a
->x
, a
->x
+ a
->width
- 1,
1997 a
->y
, a
->y
+ a
->height
- 1,
1998 a
->x
, a
->x
+ a
->width
- 1);
2005 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
2006 0, 0, 0, 0, 0, 0, 0, 0);
2008 if (!STRUT_EQUAL(strut
, self
->strut
)) {
2009 self
->strut
= strut
;
2011 /* updating here is pointless while we're being mapped cuz we're not in
2012 the client list yet */
2014 screen_update_areas();
2018 /* Avoid storing icons above this size if possible */
2019 #define AVOID_ABOVE 64
2021 void client_update_icons(ObClient
*self
)
2026 guint num_seen
; /* number of icons present */
2027 guint num_small_seen
; /* number of icons small enough present */
2028 guint smallest
, smallest_area
;
2030 for (i
= 0; i
< self
->nicons
; ++i
)
2031 g_free(self
->icons
[i
].data
);
2032 if (self
->nicons
> 0)
2033 g_free(self
->icons
);
2036 if (OBT_PROP_GETA32(self
->window
, NET_WM_ICON
, CARDINAL
, &data
, &num
)) {
2037 /* figure out how many valid icons are in here */
2039 num_seen
= num_small_seen
= 0;
2040 smallest
= smallest_area
= 0;
2046 /* watch for it being too small for the specified size, or for
2047 zero sized icons. */
2048 if (i
> num
|| w
== 0 || h
== 0) break;
2050 if (!smallest_area
|| w
*h
< smallest_area
) {
2051 smallest
= num_seen
;
2052 smallest_area
= w
*h
;
2055 if (w
<= AVOID_ABOVE
&& h
<= AVOID_ABOVE
)
2058 if (num_small_seen
> 0)
2059 self
->nicons
= num_small_seen
;
2063 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
2065 /* store the icons */
2067 for (j
= 0; j
< self
->nicons
;) {
2070 w
= self
->icons
[j
].width
= data
[i
++];
2071 h
= self
->icons
[j
].height
= data
[i
++];
2073 /* if there are some icons smaller than the threshold, we're
2074 skipping all the ones above */
2075 if (num_small_seen
> 0) {
2076 if (w
> AVOID_ABOVE
|| h
> AVOID_ABOVE
) {
2081 /* if there were no icons smaller than the threshold, then we are
2082 only taking the smallest available one we saw */
2083 else if (j
!= smallest
) {
2088 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
2089 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
2094 self
->icons
[j
].data
[t
] =
2095 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
2096 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
2097 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
2098 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
2109 if ((hints
= XGetWMHints(obt_display
, self
->window
))) {
2110 if (hints
->flags
& IconPixmapHint
) {
2112 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
2113 obt_display_ignore_errors(TRUE
);
2114 if (!RrPixmapToRGBA(ob_rr_inst
,
2116 (hints
->flags
& IconMaskHint
?
2117 hints
->icon_mask
: None
),
2118 &self
->icons
[0].width
,
2119 &self
->icons
[0].height
,
2120 &self
->icons
[0].data
))
2122 g_free(self
->icons
);
2125 obt_display_ignore_errors(FALSE
);
2131 /* set the default icon onto the window
2132 in theory, this could be a race, but if a window doesn't set an icon
2133 or removes it entirely, it's not very likely it is going to set one
2134 right away afterwards
2136 if it has parents, then one of them will have an icon already
2138 if (self
->nicons
== 0 && !self
->parents
) {
2139 RrPixel32
*icon
= ob_rr_theme
->def_win_icon
;
2142 data
= g_new(gulong
, 48*48+2);
2143 data
[0] = data
[1] = 48;
2144 for (i
= 0; i
< 48*48; ++i
)
2145 data
[i
+2] = (((icon
[i
] >> RrDefaultAlphaOffset
) & 0xff) << 24) +
2146 (((icon
[i
] >> RrDefaultRedOffset
) & 0xff) << 16) +
2147 (((icon
[i
] >> RrDefaultGreenOffset
) & 0xff) << 8) +
2148 (((icon
[i
] >> RrDefaultBlueOffset
) & 0xff) << 0);
2149 OBT_PROP_SETA32(self
->window
, NET_WM_ICON
, CARDINAL
, data
, 48*48+2);
2151 } else if (self
->frame
)
2152 /* don't draw the icon empty if we're just setting one now anyways,
2153 we'll get the property change any second */
2154 frame_adjust_icon(self
->frame
);
2157 void client_update_icon_geometry(ObClient
*self
)
2162 RECT_SET(self
->icon_geometry
, 0, 0, 0, 0);
2164 if (OBT_PROP_GETA32(self
->window
, NET_WM_ICON_GEOMETRY
, CARDINAL
,
2168 /* don't let them set it with an area < 0 */
2169 RECT_SET(self
->icon_geometry
, data
[0], data
[1],
2170 MAX(data
[2],0), MAX(data
[3],0));
2175 static void client_get_session_ids(ObClient
*self
)
2182 if (!OBT_PROP_GET32(self
->window
, WM_CLIENT_LEADER
, WINDOW
, &leader
))
2185 /* get the SM_CLIENT_ID */
2188 got
= OBT_PROP_GETS(leader
, SM_CLIENT_ID
, locale
, &self
->sm_client_id
);
2190 OBT_PROP_GETS(self
->window
, SM_CLIENT_ID
, locale
, &self
->sm_client_id
);
2192 /* get the WM_CLASS (name and class). make them "" if they are not
2196 got
= OBT_PROP_GETSS(leader
, WM_CLASS
, locale
, &ss
);
2198 got
= OBT_PROP_GETSS(self
->window
, WM_CLASS
, locale
, &ss
);
2202 self
->name
= g_strdup(ss
[0]);
2204 self
->class = g_strdup(ss
[1]);
2209 if (self
->name
== NULL
) self
->name
= g_strdup("");
2210 if (self
->class == NULL
) self
->class = g_strdup("");
2212 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2215 got
= OBT_PROP_GETS(leader
, WM_WINDOW_ROLE
, locale
, &s
);
2217 got
= OBT_PROP_GETS(self
->window
, WM_WINDOW_ROLE
, locale
, &s
);
2222 self
->role
= g_strdup("");
2224 /* get the WM_COMMAND */
2228 got
= OBT_PROP_GETSS(leader
, WM_COMMAND
, locale
, &ss
);
2230 got
= OBT_PROP_GETSS(self
->window
, WM_COMMAND
, locale
, &ss
);
2233 /* merge/mash them all together */
2234 gchar
*merge
= NULL
;
2237 for (i
= 0; ss
[i
]; ++i
) {
2240 merge
= g_strconcat(merge
, ss
[i
], NULL
);
2242 merge
= g_strconcat(ss
[i
], NULL
);
2247 self
->wm_command
= merge
;
2250 /* get the WM_CLIENT_MACHINE */
2253 got
= OBT_PROP_GETS(leader
, WM_CLIENT_MACHINE
, locale
, &s
);
2255 got
= OBT_PROP_GETS(self
->window
, WM_CLIENT_MACHINE
, locale
, &s
);
2258 gchar localhost
[128];
2261 gethostname(localhost
, 127);
2262 localhost
[127] = '\0';
2263 if (strcmp(localhost
, s
) != 0)
2264 self
->client_machine
= s
;
2268 /* see if it has the PID set too (the PID requires that the
2269 WM_CLIENT_MACHINE be set) */
2270 if (OBT_PROP_GET32(self
->window
, NET_WM_PID
, CARDINAL
, &pid
))
2275 static void client_change_wm_state(ObClient
*self
)
2280 old
= self
->wmstate
;
2282 if (self
->shaded
|| self
->iconic
||
2283 (self
->desktop
!= DESKTOP_ALL
&& self
->desktop
!= screen_desktop
))
2285 self
->wmstate
= IconicState
;
2287 self
->wmstate
= NormalState
;
2289 if (old
!= self
->wmstate
) {
2290 OBT_PROP_MSG(ob_screen
, self
->window
, KDE_WM_CHANGE_STATE
,
2291 self
->wmstate
, 1, 0, 0, 0);
2293 state
[0] = self
->wmstate
;
2295 OBT_PROP_SETA32(self
->window
, WM_STATE
, WM_STATE
, state
, 2);
2299 static void client_change_state(ObClient
*self
)
2301 gulong netstate
[12];
2306 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL
);
2308 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED
);
2310 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
);
2311 if (self
->skip_taskbar
)
2312 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
);
2313 if (self
->skip_pager
)
2314 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
);
2315 if (self
->fullscreen
)
2316 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
);
2318 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
);
2320 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
);
2322 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE
);
2324 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW
);
2325 if (self
->demands_attention
)
2326 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
);
2327 if (self
->undecorated
)
2328 netstate
[num
++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
);
2329 OBT_PROP_SETA32(self
->window
, NET_WM_STATE
, ATOM
, netstate
, num
);
2332 frame_adjust_state(self
->frame
);
2335 ObClient
*client_search_focus_tree(ObClient
*self
)
2340 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2341 if (client_focused(it
->data
)) return it
->data
;
2342 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
2347 ObClient
*client_search_focus_tree_full(ObClient
*self
)
2349 if (self
->parents
) {
2352 for (it
= self
->parents
; it
; it
= g_slist_next(it
)) {
2353 ObClient
*c
= it
->data
;
2354 if ((c
= client_search_focus_tree_full(it
->data
))) return c
;
2360 /* this function checks the whole tree, the client_search_focus_tree
2361 does not, so we need to check this window */
2362 if (client_focused(self
))
2364 return client_search_focus_tree(self
);
2368 ObClient
*client_search_focus_group_full(ObClient
*self
)
2373 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2374 ObClient
*c
= it
->data
;
2376 if (client_focused(c
)) return c
;
2377 if ((c
= client_search_focus_tree(it
->data
))) return c
;
2380 if (client_focused(self
)) return self
;
2384 gboolean
client_has_parent(ObClient
*self
)
2386 return self
->parents
!= NULL
;
2389 static ObStackingLayer
calc_layer(ObClient
*self
)
2394 monitor
= screen_physical_area_monitor(client_monitor(self
));
2396 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2397 l
= OB_STACKING_LAYER_DESKTOP
;
2398 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2399 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2400 else l
= OB_STACKING_LAYER_ABOVE
;
2402 else if ((self
->fullscreen
||
2403 /* No decorations and fills the monitor = oldskool fullscreen.
2404 But not for maximized windows.
2406 (self
->decorations
== 0 &&
2407 !(self
->max_horz
&& self
->max_vert
) &&
2408 RECT_EQUAL(self
->area
, *monitor
))) &&
2409 /* you are fullscreen while you or your children are focused.. */
2410 (client_focused(self
) || client_search_focus_tree(self
) ||
2411 /* you can be fullscreen if you're on another desktop */
2412 (self
->desktop
!= screen_desktop
&&
2413 self
->desktop
!= DESKTOP_ALL
) ||
2414 /* and you can also be fullscreen if the focused client is on
2415 another monitor, or nothing else is focused */
2417 client_monitor(focus_client
) != client_monitor(self
))))
2418 l
= OB_STACKING_LAYER_FULLSCREEN
;
2419 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2420 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2421 else l
= OB_STACKING_LAYER_NORMAL
;
2428 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2429 ObStackingLayer min
)
2431 ObStackingLayer old
, own
;
2435 own
= calc_layer(self
);
2436 self
->layer
= MAX(own
, min
);
2438 if (self
->layer
!= old
) {
2439 stacking_remove(CLIENT_AS_WINDOW(self
));
2440 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
2443 /* we've been restacked */
2444 self
->visited
= TRUE
;
2446 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2447 client_calc_layer_recursive(it
->data
, orig
,
2451 static void client_calc_layer_internal(ObClient
*self
)
2455 /* transients take on the layer of their parents */
2456 sit
= client_search_all_top_parents(self
);
2458 for (; sit
; sit
= g_slist_next(sit
))
2459 client_calc_layer_recursive(sit
->data
, self
, 0);
2462 void client_calc_layer(ObClient
*self
)
2466 /* skip over stuff above fullscreen layer */
2467 for (it
= stacking_list
; it
; it
= g_list_next(it
))
2468 if (window_layer(it
->data
) <= OB_STACKING_LAYER_FULLSCREEN
) break;
2470 /* find the windows in the fullscreen layer, and mark them not-visited */
2471 for (; it
; it
= g_list_next(it
)) {
2472 if (window_layer(it
->data
) < OB_STACKING_LAYER_FULLSCREEN
) break;
2473 else if (WINDOW_IS_CLIENT(it
->data
))
2474 WINDOW_AS_CLIENT(it
->data
)->visited
= FALSE
;
2477 client_calc_layer_internal(self
);
2479 /* skip over stuff above fullscreen layer */
2480 for (it
= stacking_list
; it
; it
= g_list_next(it
))
2481 if (window_layer(it
->data
) <= OB_STACKING_LAYER_FULLSCREEN
) break;
2483 /* now recalc any windows in the fullscreen layer which have not
2484 had their layer recalced already */
2485 for (; it
; it
= g_list_next(it
)) {
2486 if (window_layer(it
->data
) < OB_STACKING_LAYER_FULLSCREEN
) break;
2487 else if (WINDOW_IS_CLIENT(it
->data
) &&
2488 !WINDOW_AS_CLIENT(it
->data
)->visited
)
2489 client_calc_layer_internal(it
->data
);
2493 gboolean
client_should_show(ObClient
*self
)
2497 if (client_normal(self
) && screen_showing_desktop
)
2499 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2505 gboolean
client_show(ObClient
*self
)
2507 gboolean show
= FALSE
;
2509 if (client_should_show(self
)) {
2510 frame_show(self
->frame
);
2513 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2514 it needs to be in IconicState. This includes when it is on another
2517 client_change_wm_state(self
);
2522 gboolean
client_hide(ObClient
*self
)
2524 gboolean hide
= FALSE
;
2526 if (!client_should_show(self
)) {
2527 if (self
== focus_client
) {
2528 /* if there is a grab going on, then we need to cancel it. if we
2529 move focus during the grab, applications will get
2530 NotifyWhileGrabbed events and ignore them !
2532 actions should not rely on being able to move focus during an
2535 event_cancel_all_key_grabs();
2538 /* We don't need to ignore enter events here.
2539 The window can hide/iconify in 3 different ways:
2540 1 - through an x message. in this case we ignore all enter events
2541 caused by responding to the x message (unless underMouse)
2542 2 - by a keyboard action. in this case we ignore all enter events
2543 caused by the action
2544 3 - by a mouse action. in this case they are doing stuff with the
2545 mouse and focus _should_ move.
2547 Also in action_end, we simulate an enter event that can't be ignored
2548 so trying to ignore them is futile in case 3 anyways
2551 frame_hide(self
->frame
);
2554 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2555 it needs to be in IconicState. This includes when it is on another
2558 client_change_wm_state(self
);
2563 void client_showhide(ObClient
*self
)
2565 if (!client_show(self
))
2569 gboolean
client_normal(ObClient
*self
) {
2570 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2571 self
->type
== OB_CLIENT_TYPE_DOCK
||
2572 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2575 gboolean
client_helper(ObClient
*self
)
2577 return (self
->type
== OB_CLIENT_TYPE_UTILITY
||
2578 self
->type
== OB_CLIENT_TYPE_MENU
||
2579 self
->type
== OB_CLIENT_TYPE_TOOLBAR
);
2582 gboolean
client_mouse_focusable(ObClient
*self
)
2584 return !(self
->type
== OB_CLIENT_TYPE_MENU
||
2585 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
2586 self
->type
== OB_CLIENT_TYPE_SPLASH
||
2587 self
->type
== OB_CLIENT_TYPE_DOCK
);
2590 gboolean
client_enter_focusable(ObClient
*self
)
2592 /* you can focus desktops but it shouldn't on enter */
2593 return (client_mouse_focusable(self
) &&
2594 self
->type
!= OB_CLIENT_TYPE_DESKTOP
);
2598 static void client_apply_startup_state(ObClient
*self
,
2599 gint x
, gint y
, gint w
, gint h
)
2601 /* save the states that we are going to apply */
2602 gboolean iconic
= self
->iconic
;
2603 gboolean fullscreen
= self
->fullscreen
;
2604 gboolean undecorated
= self
->undecorated
;
2605 gboolean shaded
= self
->shaded
;
2606 gboolean demands_attention
= self
->demands_attention
;
2607 gboolean max_horz
= self
->max_horz
;
2608 gboolean max_vert
= self
->max_vert
;
2612 /* turn them all off in the client, so they won't affect the window
2614 self
->iconic
= self
->fullscreen
= self
->undecorated
= self
->shaded
=
2615 self
->demands_attention
= self
->max_horz
= self
->max_vert
= FALSE
;
2617 /* move the client to its placed position, or it it's already there,
2618 generate a ConfigureNotify telling the client where it is.
2620 do this after adjusting the frame. otherwise it gets all weird and
2621 clients don't work right
2623 do this before applying the states so they have the correct
2624 pre-max/pre-fullscreen values
2626 client_try_configure(self
, &x
, &y
, &w
, &h
, &l
, &l
, FALSE
);
2627 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2628 self
->window
, x
, y
, w
, h
);
2629 /* save the area, and make it where it should be for the premax stuff */
2630 oldarea
= self
->area
;
2631 RECT_SET(self
->area
, x
, y
, w
, h
);
2633 /* apply the states. these are in a carefully crafted order.. */
2636 client_iconify(self
, TRUE
, FALSE
, TRUE
);
2638 client_fullscreen(self
, TRUE
);
2640 client_set_undecorated(self
, TRUE
);
2642 client_shade(self
, TRUE
);
2643 if (demands_attention
)
2644 client_hilite(self
, TRUE
);
2646 if (max_vert
&& max_horz
)
2647 client_maximize(self
, TRUE
, 0);
2649 client_maximize(self
, TRUE
, 2);
2651 client_maximize(self
, TRUE
, 1);
2653 /* if the window hasn't been configured yet, then do so now, in fact the
2654 x,y,w,h may _not_ be the same as the area rect, which can end up
2655 meaning that the client isn't properly moved/resized by the fullscreen
2657 pho can cause this because it maps at size of the screen but not 0,0
2658 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2659 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2660 cuz thats where the pre-fullscreen will be. however the actual area is
2661 not, so this needs to be called even if we have fullscreened/maxed
2663 self
->area
= oldarea
;
2664 client_configure(self
, x
, y
, w
, h
, FALSE
, TRUE
, FALSE
);
2666 /* set the desktop hint, to make sure that it always exists */
2667 OBT_PROP_SET32(self
->window
, NET_WM_DESKTOP
, CARDINAL
, self
->desktop
);
2669 /* nothing to do for the other states:
2678 void client_gravity_resize_w(ObClient
*self
, gint
*x
, gint oldw
, gint neww
)
2680 /* these should be the current values. this is for when you're not moving,
2682 g_assert(*x
== self
->area
.x
);
2683 g_assert(oldw
== self
->area
.width
);
2686 switch (self
->gravity
) {
2688 case NorthWestGravity
:
2690 case SouthWestGravity
:
2697 *x
-= (neww
- oldw
) / 2;
2699 case NorthEastGravity
:
2701 case SouthEastGravity
:
2707 void client_gravity_resize_h(ObClient
*self
, gint
*y
, gint oldh
, gint newh
)
2709 /* these should be the current values. this is for when you're not moving,
2711 g_assert(*y
== self
->area
.y
);
2712 g_assert(oldh
== self
->area
.height
);
2715 switch (self
->gravity
) {
2717 case NorthWestGravity
:
2719 case NorthEastGravity
:
2726 *y
-= (newh
- oldh
) / 2;
2728 case SouthWestGravity
:
2730 case SouthEastGravity
:
2736 void client_try_configure(ObClient
*self
, gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2737 gint
*logicalw
, gint
*logicalh
,
2740 Rect desired
= {*x
, *y
, *w
, *h
};
2741 frame_rect_to_frame(self
->frame
, &desired
);
2743 /* make the frame recalculate its dimentions n shit without changing
2744 anything visible for real, this way the constraints below can work with
2745 the updated frame dimensions. */
2746 frame_adjust_area(self
->frame
, FALSE
, TRUE
, TRUE
);
2748 /* gets the frame's position */
2749 frame_client_gravity(self
->frame
, x
, y
);
2751 /* these positions are frame positions, not client positions */
2753 /* set the size and position if fullscreen */
2754 if (self
->fullscreen
) {
2758 i
= screen_find_monitor(&desired
);
2759 a
= screen_physical_area_monitor(i
);
2766 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2770 } else if (self
->max_horz
|| self
->max_vert
) {
2774 /* use all possible struts when maximizing to the full screen */
2775 i
= screen_find_monitor(&desired
);
2776 a
= screen_area(self
->desktop
, i
,
2777 (self
->max_horz
&& self
->max_vert
? NULL
: &desired
));
2779 /* set the size and position if maximized */
2780 if (self
->max_horz
) {
2782 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2784 if (self
->max_vert
) {
2786 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2789 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2795 /* gets the client's position */
2796 frame_frame_gravity(self
->frame
, x
, y
);
2798 /* work within the prefered sizes given by the window */
2799 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2800 gint basew
, baseh
, minw
, minh
;
2802 gfloat minratio
, maxratio
;
2804 incw
= self
->fullscreen
|| self
->max_horz
? 1 : self
->size_inc
.width
;
2805 inch
= self
->fullscreen
|| self
->max_vert
? 1 : self
->size_inc
.height
;
2806 minratio
= self
->fullscreen
|| (self
->max_horz
&& self
->max_vert
) ?
2807 0 : self
->min_ratio
;
2808 maxratio
= self
->fullscreen
|| (self
->max_horz
&& self
->max_vert
) ?
2809 0 : self
->max_ratio
;
2811 /* base size is substituted with min size if not specified */
2812 if (self
->base_size
.width
|| self
->base_size
.height
) {
2813 basew
= self
->base_size
.width
;
2814 baseh
= self
->base_size
.height
;
2816 basew
= self
->min_size
.width
;
2817 baseh
= self
->min_size
.height
;
2819 /* min size is substituted with base size if not specified */
2820 if (self
->min_size
.width
|| self
->min_size
.height
) {
2821 minw
= self
->min_size
.width
;
2822 minh
= self
->min_size
.height
;
2824 minw
= self
->base_size
.width
;
2825 minh
= self
->base_size
.height
;
2828 /* if this is a user-requested resize, then check against min/max
2831 /* smaller than min size or bigger than max size? */
2832 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2833 if (*w
< minw
) *w
= minw
;
2834 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2835 if (*h
< minh
) *h
= minh
;
2840 /* keep to the increments */
2844 /* you cannot resize to nothing */
2845 if (basew
+ *w
< 1) *w
= 1 - basew
;
2846 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2848 /* save the logical size */
2849 *logicalw
= incw
> 1 ? *w
: *w
+ basew
;
2850 *logicalh
= inch
> 1 ? *h
: *h
+ baseh
;
2858 /* adjust the height to match the width for the aspect ratios.
2859 for this, min size is not substituted for base size ever. */
2860 *w
-= self
->base_size
.width
;
2861 *h
-= self
->base_size
.height
;
2864 if (*h
* minratio
> *w
) {
2865 *h
= (gint
)(*w
/ minratio
);
2867 /* you cannot resize to nothing */
2870 *w
= (gint
)(*h
* minratio
);
2874 if (*h
* maxratio
< *w
) {
2875 *h
= (gint
)(*w
/ maxratio
);
2877 /* you cannot resize to nothing */
2880 *w
= (gint
)(*h
* minratio
);
2884 *w
+= self
->base_size
.width
;
2885 *h
+= self
->base_size
.height
;
2888 /* these override the above states! if you cant move you can't move! */
2890 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2894 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2895 *w
= self
->area
.width
;
2896 *h
= self
->area
.height
;
2905 void client_configure(ObClient
*self
, gint x
, gint y
, gint w
, gint h
,
2906 gboolean user
, gboolean final
, gboolean force_reply
)
2910 gboolean send_resize_client
;
2911 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2912 gboolean fmoved
, fresized
;
2913 guint fdecor
= self
->frame
->decorations
;
2914 gboolean fhorz
= self
->frame
->max_horz
;
2915 gboolean fvert
= self
->frame
->max_vert
;
2916 gint logicalw
, logicalh
;
2918 /* find the new x, y, width, and height (and logical size) */
2919 client_try_configure(self
, &x
, &y
, &w
, &h
, &logicalw
, &logicalh
, user
);
2921 /* set the logical size if things changed */
2922 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2923 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2925 /* figure out if we moved or resized or what */
2926 moved
= (x
!= self
->area
.x
|| y
!= self
->area
.y
);
2927 resized
= (w
!= self
->area
.width
|| h
!= self
->area
.height
);
2929 oldw
= self
->area
.width
;
2930 oldh
= self
->area
.height
;
2931 oldframe
= self
->frame
->area
;
2932 RECT_SET(self
->area
, x
, y
, w
, h
);
2934 /* for app-requested resizes, always resize if 'resized' is true.
2935 for user-requested ones, only resize if final is true, or when
2936 resizing in redraw mode */
2937 send_resize_client
= ((!user
&& resized
) ||
2939 (resized
&& config_resize_redraw
))));
2941 /* if the client is enlarging, then resize the client before the frame */
2942 if (send_resize_client
&& (w
> oldw
|| h
> oldh
)) {
2943 XMoveResizeWindow(obt_display
, self
->window
,
2944 self
->frame
->size
.left
, self
->frame
->size
.top
,
2945 MAX(w
, oldw
), MAX(h
, oldh
));
2946 frame_adjust_client_area(self
->frame
);
2949 /* find the frame's dimensions and move/resize it */
2953 /* if decorations changed, then readjust everything for the frame */
2954 if (self
->decorations
!= fdecor
||
2955 self
->max_horz
!= fhorz
|| self
->max_vert
!= fvert
)
2957 fmoved
= fresized
= TRUE
;
2960 /* adjust the frame */
2961 if (fmoved
|| fresized
) {
2962 gulong ignore_start
;
2964 ignore_start
= event_start_ignore_all_enters();
2966 frame_adjust_area(self
->frame
, fmoved
, fresized
, FALSE
);
2969 event_end_ignore_all_enters(ignore_start
);
2972 if (!user
|| final
) {
2973 gint oldrx
= self
->root_pos
.x
;
2974 gint oldry
= self
->root_pos
.y
;
2975 /* we have reset the client to 0 border width, so don't include
2976 it in these coords */
2977 POINT_SET(self
->root_pos
,
2978 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2980 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2981 self
->border_width
);
2982 if (self
->root_pos
.x
!= oldrx
|| self
->root_pos
.y
!= oldry
)
2986 /* This is kinda tricky and should not be changed.. let me explain!
2988 When user = FALSE, then the request is coming from the application
2989 itself, and we are more strict about when to send a synthetic
2990 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
2991 in this case (if force_reply is true)
2993 When user = TRUE, then the request is coming from "us", like when we
2994 maximize a window or something. In this case we are more lenient. We
2995 used to follow the same rules as above, but _Java_ Swing can't handle
2996 this. So just to appease Swing, when user = TRUE, we always send
2997 a synthetic ConfigureNotify to give the window its root coordinates.
2999 if ((!user
&& !resized
&& (rootmoved
|| force_reply
)) ||
3000 (user
&& final
&& rootmoved
))
3004 event
.type
= ConfigureNotify
;
3005 event
.xconfigure
.display
= obt_display
;
3006 event
.xconfigure
.event
= self
->window
;
3007 event
.xconfigure
.window
= self
->window
;
3009 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3010 self
->title
, self
->root_pos
.x
, self
->root_pos
.y
, w
, h
);
3012 /* root window real coords */
3013 event
.xconfigure
.x
= self
->root_pos
.x
;
3014 event
.xconfigure
.y
= self
->root_pos
.y
;
3015 event
.xconfigure
.width
= w
;
3016 event
.xconfigure
.height
= h
;
3017 event
.xconfigure
.border_width
= self
->border_width
;
3018 event
.xconfigure
.above
= None
;
3019 event
.xconfigure
.override_redirect
= FALSE
;
3020 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
3021 FALSE
, StructureNotifyMask
, &event
);
3024 /* if the client is shrinking, then resize the frame before the client.
3026 both of these resize sections may run, because the top one only resizes
3027 in the direction that is growing
3029 if (send_resize_client
&& (w
<= oldw
|| h
<= oldh
)) {
3030 frame_adjust_client_area(self
->frame
);
3031 XMoveResizeWindow(obt_display
, self
->window
,
3032 self
->frame
->size
.left
, self
->frame
->size
.top
, w
, h
);
3035 XFlush(obt_display
);
3037 /* if it moved between monitors, then this can affect the stacking
3038 layer of this window or others - for fullscreen windows */
3039 if (screen_find_monitor(&self
->frame
->area
) !=
3040 screen_find_monitor(&oldframe
))
3042 client_calc_layer(self
);
3046 void client_fullscreen(ObClient
*self
, gboolean fs
)
3050 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
3051 self
->fullscreen
== fs
) return; /* already done */
3053 self
->fullscreen
= fs
;
3054 client_change_state(self
); /* change the state hints on the client */
3057 self
->pre_fullscreen_area
= self
->area
;
3058 /* if the window is maximized, its area isn't all that meaningful.
3059 save it's premax area instead. */
3060 if (self
->max_horz
) {
3061 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
3062 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
3064 if (self
->max_vert
) {
3065 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
3066 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
3069 /* these will help configure_full figure out where to fullscreen
3073 w
= self
->area
.width
;
3074 h
= self
->area
.height
;
3076 g_assert(self
->pre_fullscreen_area
.width
> 0 &&
3077 self
->pre_fullscreen_area
.height
> 0);
3079 x
= self
->pre_fullscreen_area
.x
;
3080 y
= self
->pre_fullscreen_area
.y
;
3081 w
= self
->pre_fullscreen_area
.width
;
3082 h
= self
->pre_fullscreen_area
.height
;
3083 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
3086 ob_debug("Window %s going fullscreen (%d)",
3087 self
->title
, self
->fullscreen
);
3089 client_setup_decor_and_functions(self
, FALSE
);
3090 client_move_resize(self
, x
, y
, w
, h
);
3092 /* and adjust our layer/stacking. do this after resizing the window,
3093 and applying decorations, because windows which fill the screen are
3094 considered "fullscreen" and it affects their layer */
3095 client_calc_layer(self
);
3098 /* try focus us when we go into fullscreen mode */
3103 static void client_iconify_recursive(ObClient
*self
,
3104 gboolean iconic
, gboolean curdesk
,
3105 gboolean hide_animation
)
3108 gboolean changed
= FALSE
;
3111 if (self
->iconic
!= iconic
) {
3112 ob_debug("%sconifying window: 0x%lx", (iconic
? "I" : "Uni"),
3116 /* don't let non-normal windows iconify along with their parents
3118 if (client_normal(self
)) {
3119 self
->iconic
= iconic
;
3121 /* update the focus lists.. iconic windows go to the bottom of
3123 focus_order_to_bottom(self
);
3128 self
->iconic
= iconic
;
3130 if (curdesk
&& self
->desktop
!= screen_desktop
&&
3131 self
->desktop
!= DESKTOP_ALL
)
3132 client_set_desktop(self
, screen_desktop
, FALSE
, FALSE
);
3134 /* this puts it after the current focused window */
3135 focus_order_remove(self
);
3136 focus_order_add_new(self
);
3143 client_change_state(self
);
3144 if (config_animate_iconify
&& !hide_animation
)
3145 frame_begin_iconify_animation(self
->frame
, iconic
);
3146 /* do this after starting the animation so it doesn't flash */
3147 client_showhide(self
);
3150 /* iconify all direct transients, and deiconify all transients
3152 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3153 if (it
->data
!= self
)
3154 if (client_is_direct_child(self
, it
->data
) || !iconic
)
3155 client_iconify_recursive(it
->data
, iconic
, curdesk
,
3159 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
,
3160 gboolean hide_animation
)
3162 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
|| !iconic
) {
3163 /* move up the transient chain as far as possible first */
3164 self
= client_search_top_direct_parent(self
);
3165 client_iconify_recursive(self
, iconic
, curdesk
, hide_animation
);
3169 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
3173 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
3174 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
3176 /* check if already done */
3178 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
3179 if (dir
== 1 && self
->max_horz
) return;
3180 if (dir
== 2 && self
->max_vert
) return;
3182 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
3183 if (dir
== 1 && !self
->max_horz
) return;
3184 if (dir
== 2 && !self
->max_vert
) return;
3187 /* these will help configure_full figure out which screen to fill with
3191 w
= self
->area
.width
;
3192 h
= self
->area
.height
;
3195 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
3196 RECT_SET(self
->pre_max_area
,
3197 self
->area
.x
, self
->pre_max_area
.y
,
3198 self
->area
.width
, self
->pre_max_area
.height
);
3200 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
3201 RECT_SET(self
->pre_max_area
,
3202 self
->pre_max_area
.x
, self
->area
.y
,
3203 self
->pre_max_area
.width
, self
->area
.height
);
3206 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
3207 g_assert(self
->pre_max_area
.width
> 0);
3209 x
= self
->pre_max_area
.x
;
3210 w
= self
->pre_max_area
.width
;
3212 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
3213 0, self
->pre_max_area
.height
);
3215 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
3216 g_assert(self
->pre_max_area
.height
> 0);
3218 y
= self
->pre_max_area
.y
;
3219 h
= self
->pre_max_area
.height
;
3221 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
3222 self
->pre_max_area
.width
, 0);
3226 if (dir
== 0 || dir
== 1) /* horz */
3227 self
->max_horz
= max
;
3228 if (dir
== 0 || dir
== 2) /* vert */
3229 self
->max_vert
= max
;
3231 client_change_state(self
); /* change the state hints on the client */
3233 client_setup_decor_and_functions(self
, FALSE
);
3234 client_move_resize(self
, x
, y
, w
, h
);
3237 void client_shade(ObClient
*self
, gboolean shade
)
3239 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
3240 shade
) || /* can't shade */
3241 self
->shaded
== shade
) return; /* already done */
3243 self
->shaded
= shade
;
3244 client_change_state(self
);
3245 client_change_wm_state(self
); /* the window is being hidden/shown */
3246 /* resize the frame to just the titlebar */
3247 frame_adjust_area(self
->frame
, FALSE
, TRUE
, FALSE
);
3250 static void client_ping_event(ObClient
*self
, gboolean dead
)
3252 self
->not_responding
= dead
;
3253 client_update_title(self
);
3256 /* it came back to life ! */
3258 if (self
->kill_prompt
) {
3259 prompt_unref(self
->kill_prompt
);
3260 self
->kill_prompt
= NULL
;
3263 self
->kill_level
= 0;
3267 void client_close(ObClient
*self
)
3269 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
3272 prompt_cancel(self
->prompt
);
3276 /* in the case that the client provides no means to requesting that it
3277 close, we just kill it */
3278 if (!self
->delete_window
)
3279 /* don't use client_kill(), we should only kill based on PID in
3280 response to a lack of PING replies */
3281 XKillClient(obt_display
, self
->window
);
3283 /* request the client to close with WM_DELETE_WINDOW */
3284 OBT_PROP_MSG_TO(self
->window
, self
->window
, WM_PROTOCOLS
,
3285 OBT_PROP_ATOM(WM_DELETE_WINDOW
), event_curtime
,
3286 0, 0, 0, NoEventMask
);
3288 if (self
->not_responding
)
3289 client_prompt_kill(self
);
3293 #define OB_KILL_RESULT_NO 0
3294 #define OB_KILL_RESULT_YES 1
3296 static void client_kill_requested(ObPrompt
*p
, gint result
, gpointer data
)
3298 ObClient
*self
= data
;
3300 if (result
== OB_KILL_RESULT_YES
)
3303 prompt_unref(self
->kill_prompt
);
3304 self
->kill_prompt
= NULL
;
3307 static void client_prompt_kill(ObClient
*self
)
3309 ObPromptAnswer answers
[] = {
3310 { _("No"), OB_KILL_RESULT_NO
},
3311 { _("Yes"), OB_KILL_RESULT_YES
}
3315 /* check if we're already prompting */
3316 if (self
->kill_prompt
) return;
3319 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit?"), self
->title
);
3321 self
->kill_prompt
= prompt_new(m
, answers
,
3322 sizeof(answers
)/sizeof(answers
[0]),
3323 OB_KILL_RESULT_NO
, /* default = no */
3324 OB_KILL_RESULT_NO
, /* cancel = no */
3325 client_kill_requested
, self
);
3326 prompt_show(self
->kill_prompt
, self
);
3331 void client_kill(ObClient
*self
)
3333 if (!self
->client_machine
&& self
->pid
) {
3334 /* running on the local host */
3335 if (self
->kill_level
== 0) {
3336 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3337 self
->window
, self
->pid
);
3338 kill(self
->pid
, SIGTERM
);
3341 /* show that we're trying to kill it */
3342 client_update_title(self
);
3345 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3346 self
->window
, self
->pid
);
3347 kill(self
->pid
, SIGKILL
); /* kill -9 */
3351 /* running on a remote host */
3352 XKillClient(obt_display
, self
->window
);
3356 void client_hilite(ObClient
*self
, gboolean hilite
)
3358 if (self
->demands_attention
== hilite
)
3359 return; /* no change */
3361 /* don't allow focused windows to hilite */
3362 self
->demands_attention
= hilite
&& !client_focused(self
);
3363 if (self
->frame
!= NULL
) { /* if we're mapping, just set the state */
3364 if (self
->demands_attention
)
3365 frame_flash_start(self
->frame
);
3367 frame_flash_stop(self
->frame
);
3368 client_change_state(self
);
3372 static void client_set_desktop_recursive(ObClient
*self
,
3380 if (target
!= self
->desktop
&& self
->type
!= OB_CLIENT_TYPE_DESKTOP
) {
3382 ob_debug("Setting desktop %u", target
+1);
3384 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
3386 old
= self
->desktop
;
3387 self
->desktop
= target
;
3388 OBT_PROP_SET32(self
->window
, NET_WM_DESKTOP
, CARDINAL
, target
);
3389 /* the frame can display the current desktop state */
3390 frame_adjust_state(self
->frame
);
3391 /* 'move' the window to the new desktop */
3395 /* raise if it was not already on the desktop */
3396 if (old
!= DESKTOP_ALL
&& !dontraise
)
3397 stacking_raise(CLIENT_AS_WINDOW(self
));
3398 if (STRUT_EXISTS(self
->strut
))
3399 screen_update_areas();
3401 /* the new desktop's geometry may be different, so we may need to
3402 resize, for example if we are maximized */
3403 client_reconfigure(self
, FALSE
);
3406 /* move all transients */
3407 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3408 if (it
->data
!= self
)
3409 if (client_is_direct_child(self
, it
->data
))
3410 client_set_desktop_recursive(it
->data
, target
,
3411 donthide
, dontraise
);
3414 void client_set_desktop(ObClient
*self
, guint target
,
3415 gboolean donthide
, gboolean dontraise
)
3417 self
= client_search_top_direct_parent(self
);
3418 client_set_desktop_recursive(self
, target
, donthide
, dontraise
);
3421 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
3423 while (child
!= parent
&& (child
= client_direct_parent(child
)));
3424 return child
== parent
;
3427 ObClient
*client_search_modal_child(ObClient
*self
)
3432 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
3433 ObClient
*c
= it
->data
;
3434 if ((ret
= client_search_modal_child(c
))) return ret
;
3435 if (c
->modal
) return c
;
3440 gboolean
client_validate(ObClient
*self
)
3444 XSync(obt_display
, FALSE
); /* get all events on the server */
3446 if (XCheckTypedWindowEvent(obt_display
, self
->window
, DestroyNotify
, &e
) ||
3447 XCheckTypedWindowEvent(obt_display
, self
->window
, UnmapNotify
, &e
))
3449 XPutBackEvent(obt_display
, &e
);
3456 void client_set_wm_state(ObClient
*self
, glong state
)
3458 if (state
== self
->wmstate
) return; /* no change */
3462 client_iconify(self
, TRUE
, TRUE
, FALSE
);
3465 client_iconify(self
, FALSE
, TRUE
, FALSE
);
3470 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
3472 gboolean shaded
= self
->shaded
;
3473 gboolean fullscreen
= self
->fullscreen
;
3474 gboolean undecorated
= self
->undecorated
;
3475 gboolean max_horz
= self
->max_horz
;
3476 gboolean max_vert
= self
->max_vert
;
3477 gboolean modal
= self
->modal
;
3478 gboolean iconic
= self
->iconic
;
3479 gboolean demands_attention
= self
->demands_attention
;
3480 gboolean above
= self
->above
;
3481 gboolean below
= self
->below
;
3484 if (!(action
== OBT_PROP_ATOM(NET_WM_STATE_ADD
) ||
3485 action
== OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) ||
3486 action
== OBT_PROP_ATOM(NET_WM_STATE_TOGGLE
)))
3487 /* an invalid action was passed to the client message, ignore it */
3490 for (i
= 0; i
< 2; ++i
) {
3491 Atom state
= i
== 0 ? data1
: data2
;
3493 if (!state
) continue;
3495 /* if toggling, then pick whether we're adding or removing */
3496 if (action
== OBT_PROP_ATOM(NET_WM_STATE_TOGGLE
)) {
3497 if (state
== OBT_PROP_ATOM(NET_WM_STATE_MODAL
))
3498 action
= modal
? OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3499 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3500 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
))
3501 action
= self
->max_vert
? OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3502 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3503 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
))
3504 action
= self
->max_horz
? OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3505 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3506 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SHADED
))
3507 action
= shaded
? OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3508 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3509 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
))
3510 action
= self
->skip_taskbar
?
3511 OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3512 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3513 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
))
3514 action
= self
->skip_pager
?
3515 OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3516 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3517 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
))
3518 action
= self
->iconic
?
3519 OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3520 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3521 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
))
3522 action
= fullscreen
?
3523 OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3524 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3525 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_ABOVE
))
3526 action
= self
->above
? OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3527 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3528 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_BELOW
))
3529 action
= self
->below
? OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3530 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3531 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
))
3532 action
= self
->demands_attention
?
3533 OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3534 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3535 else if (state
== OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
))
3536 action
= undecorated
? OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3537 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3540 if (action
== OBT_PROP_ATOM(NET_WM_STATE_ADD
)) {
3541 if (state
== OBT_PROP_ATOM(NET_WM_STATE_MODAL
)) {
3543 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
)) {
3545 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
)) {
3547 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SHADED
)) {
3549 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
)) {
3550 self
->skip_taskbar
= TRUE
;
3551 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
)) {
3552 self
->skip_pager
= TRUE
;
3553 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
)) {
3555 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
)) {
3557 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_ABOVE
)) {
3560 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_BELOW
)) {
3563 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
)){
3564 demands_attention
= TRUE
;
3565 } else if (state
== OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
)) {
3569 } else { /* action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) */
3570 if (state
== OBT_PROP_ATOM(NET_WM_STATE_MODAL
)) {
3572 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
)) {
3574 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
)) {
3576 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SHADED
)) {
3578 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
)) {
3579 self
->skip_taskbar
= FALSE
;
3580 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
)) {
3581 self
->skip_pager
= FALSE
;
3582 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
)) {
3584 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
)) {
3586 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_ABOVE
)) {
3588 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_BELOW
)) {
3590 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
)){
3591 demands_attention
= FALSE
;
3592 } else if (state
== OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
)) {
3593 undecorated
= FALSE
;
3598 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
3599 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
3601 if (max_horz
== max_vert
) { /* both going the same way */
3602 client_maximize(self
, max_horz
, 0);
3604 client_maximize(self
, max_horz
, 1);
3605 client_maximize(self
, max_vert
, 2);
3609 if (max_horz
!= self
->max_horz
)
3610 client_maximize(self
, max_horz
, 1);
3612 client_maximize(self
, max_vert
, 2);
3615 /* change fullscreen state before shading, as it will affect if the window
3617 if (fullscreen
!= self
->fullscreen
)
3618 client_fullscreen(self
, fullscreen
);
3619 if (shaded
!= self
->shaded
)
3620 client_shade(self
, shaded
);
3621 if (undecorated
!= self
->undecorated
)
3622 client_set_undecorated(self
, undecorated
);
3623 if (above
!= self
->above
|| below
!= self
->below
) {
3624 self
->above
= above
;
3625 self
->below
= below
;
3626 client_calc_layer(self
);
3629 if (modal
!= self
->modal
) {
3630 self
->modal
= modal
;
3631 /* when a window changes modality, then its stacking order with its
3632 transients needs to change */
3633 stacking_raise(CLIENT_AS_WINDOW(self
));
3635 /* it also may get focused. if something is focused that shouldn't
3636 be focused anymore, then move the focus */
3637 if (focus_client
&& client_focus_target(focus_client
) != focus_client
)
3638 client_focus(focus_client
);
3641 if (iconic
!= self
->iconic
)
3642 client_iconify(self
, iconic
, FALSE
, FALSE
);
3644 if (demands_attention
!= self
->demands_attention
)
3645 client_hilite(self
, demands_attention
);
3647 client_change_state(self
); /* change the hint to reflect these changes */
3650 ObClient
*client_focus_target(ObClient
*self
)
3652 ObClient
*child
= NULL
;
3654 child
= client_search_modal_child(self
);
3655 if (child
) return child
;
3659 gboolean
client_can_focus(ObClient
*self
)
3661 /* choose the correct target */
3662 self
= client_focus_target(self
);
3664 if (!self
->frame
->visible
)
3667 if (!(self
->can_focus
|| self
->focus_notify
))
3673 gboolean
client_focus(ObClient
*self
)
3675 /* we might not focus this window, so if we have modal children which would
3676 be focused instead, bring them to this desktop */
3677 client_bring_modal_windows(self
);
3679 /* choose the correct target */
3680 self
= client_focus_target(self
);
3682 if (!client_can_focus(self
)) {
3683 ob_debug_type(OB_DEBUG_FOCUS
,
3684 "Client %s can't be focused", self
->title
);
3688 ob_debug_type(OB_DEBUG_FOCUS
,
3689 "Focusing client \"%s\" (0x%x) at time %u",
3690 self
->title
, self
->window
, event_curtime
);
3692 /* if using focus_delay, stop the timer now so that focus doesn't
3694 event_halt_focus_delay();
3696 /* if there is a grab going on, then we need to cancel it. if we move
3697 focus during the grab, applications will get NotifyWhileGrabbed events
3700 actions should not rely on being able to move focus during an
3703 event_cancel_all_key_grabs();
3705 obt_display_ignore_errors(TRUE
);
3707 if (self
->can_focus
) {
3708 /* This can cause a BadMatch error with CurrentTime, or if an app
3709 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3710 XSetInputFocus(obt_display
, self
->window
, RevertToPointerRoot
,
3714 if (self
->focus_notify
) {
3716 ce
.xclient
.type
= ClientMessage
;
3717 ce
.xclient
.message_type
= OBT_PROP_ATOM(WM_PROTOCOLS
);
3718 ce
.xclient
.display
= obt_display
;
3719 ce
.xclient
.window
= self
->window
;
3720 ce
.xclient
.format
= 32;
3721 ce
.xclient
.data
.l
[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS
);
3722 ce
.xclient
.data
.l
[1] = event_curtime
;
3723 ce
.xclient
.data
.l
[2] = 0l;
3724 ce
.xclient
.data
.l
[3] = 0l;
3725 ce
.xclient
.data
.l
[4] = 0l;
3726 XSendEvent(obt_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3729 obt_display_ignore_errors(FALSE
);
3731 ob_debug_type(OB_DEBUG_FOCUS
, "Error focusing? %d",
3732 obt_display_error_occured
);
3733 return !obt_display_error_occured
;
3736 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
,
3739 if (client_normal(self
) && screen_showing_desktop
)
3740 screen_show_desktop(FALSE
, self
);
3742 client_iconify(self
, FALSE
, here
, FALSE
);
3743 if (self
->desktop
!= DESKTOP_ALL
&&
3744 self
->desktop
!= screen_desktop
)
3747 client_set_desktop(self
, screen_desktop
, FALSE
, TRUE
);
3749 screen_set_desktop(self
->desktop
, FALSE
);
3750 } else if (!self
->frame
->visible
)
3751 /* if its not visible for other reasons, then don't mess
3754 if (self
->shaded
&& unshade
)
3755 client_shade(self
, FALSE
);
3757 stacking_raise(CLIENT_AS_WINDOW(self
));
3762 void client_activate(ObClient
*self
, gboolean here
, gboolean raise
,
3763 gboolean unshade
, gboolean user
)
3765 client_present(self
, here
, raise
, unshade
);
3768 static void client_bring_windows_recursive(ObClient
*self
,
3776 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3777 client_bring_windows_recursive(it
->data
, desktop
,
3778 helpers
, modals
, iconic
);
3780 if (((helpers
&& client_helper(self
)) ||
3781 (modals
&& self
->modal
)) &&
3782 ((self
->desktop
!= desktop
&& self
->desktop
!= DESKTOP_ALL
) ||
3783 (iconic
&& self
->iconic
)))
3785 if (iconic
&& self
->iconic
)
3786 client_iconify(self
, FALSE
, TRUE
, FALSE
);
3788 client_set_desktop(self
, desktop
, FALSE
, FALSE
);
3792 void client_bring_helper_windows(ObClient
*self
)
3794 client_bring_windows_recursive(self
, self
->desktop
, TRUE
, FALSE
, FALSE
);
3797 void client_bring_modal_windows(ObClient
*self
)
3799 client_bring_windows_recursive(self
, self
->desktop
, FALSE
, TRUE
, TRUE
);
3802 gboolean
client_focused(ObClient
*self
)
3804 return self
== focus_client
;
3807 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3810 gulong min_diff
, min_i
;
3812 if (!self
->nicons
) {
3813 ObClientIcon
*parent
= NULL
;
3816 for (it
= self
->parents
; it
; it
= g_slist_next(it
)) {
3817 ObClient
*c
= it
->data
;
3818 if ((parent
= client_icon_recursive(c
, w
, h
)))
3825 /* some kind of crappy approximation to find the icon closest in size to
3826 what we requested, but icons are generally all the same ratio as
3827 eachother so it's good enough. */
3829 min_diff
= ABS(self
->icons
[0].width
- w
) + ABS(self
->icons
[0].height
- h
);
3832 for (i
= 1; i
< self
->nicons
; ++i
) {
3835 diff
= ABS(self
->icons
[i
].width
- w
) + ABS(self
->icons
[i
].height
- h
);
3836 if (diff
< min_diff
) {
3841 return &self
->icons
[min_i
];
3844 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3847 static ObClientIcon deficon
;
3849 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3850 deficon
.width
= deficon
.height
= 48;
3851 deficon
.data
= ob_rr_theme
->def_win_icon
;
3857 void client_set_layer(ObClient
*self
, gint layer
)
3861 self
->above
= FALSE
;
3862 } else if (layer
== 0) {
3863 self
->below
= self
->above
= FALSE
;
3865 self
->below
= FALSE
;
3868 client_calc_layer(self
);
3869 client_change_state(self
); /* reflect this in the state hints */
3872 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3874 if (self
->undecorated
!= undecorated
&&
3875 /* don't let it undecorate if the function is missing, but let
3877 (self
->functions
& OB_CLIENT_FUNC_UNDECORATE
|| !undecorated
))
3879 self
->undecorated
= undecorated
;
3880 client_setup_decor_and_functions(self
, TRUE
);
3881 client_change_state(self
); /* reflect this in the state hints */
3885 guint
client_monitor(ObClient
*self
)
3887 return screen_find_monitor(&self
->frame
->area
);
3890 ObClient
*client_direct_parent(ObClient
*self
)
3892 if (!self
->parents
) return NULL
;
3893 if (self
->transient_for_group
) return NULL
;
3894 return self
->parents
->data
;
3897 ObClient
*client_search_top_direct_parent(ObClient
*self
)
3900 while ((p
= client_direct_parent(self
))) self
= p
;
3904 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
3906 ObStackingLayer layer
)
3911 /* move up the direct transient chain as far as possible */
3912 while ((p
= client_direct_parent(self
)) &&
3913 (!bylayer
|| p
->layer
== layer
))
3917 ret
= g_slist_prepend(NULL
, self
);
3919 ret
= g_slist_copy(self
->parents
);
3924 GSList
*client_search_all_top_parents(ObClient
*self
)
3926 return client_search_all_top_parents_internal(self
, FALSE
, 0);
3929 GSList
*client_search_all_top_parents_layer(ObClient
*self
)
3931 return client_search_all_top_parents_internal(self
, TRUE
, self
->layer
);
3934 ObClient
*client_search_focus_parent(ObClient
*self
)
3938 for (it
= self
->parents
; it
; it
= g_slist_next(it
))
3939 if (client_focused(it
->data
)) return it
->data
;
3944 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3948 for (it
= self
->parents
; it
; it
= g_slist_next(it
))
3949 if (it
->data
== search
) return search
;
3954 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3958 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3959 if (sit
->data
== search
)
3961 if (client_search_transient(sit
->data
, search
))
3967 static void detect_edge(Rect area
, ObDirection dir
,
3968 gint my_head
, gint my_size
,
3969 gint my_edge_start
, gint my_edge_size
,
3970 gint
*dest
, gboolean
*near_edge
)
3972 gint edge_start
, edge_size
, head
, tail
;
3973 gboolean skip_head
= FALSE
, skip_tail
= FALSE
;
3976 case OB_DIRECTION_NORTH
:
3977 case OB_DIRECTION_SOUTH
:
3978 edge_start
= area
.x
;
3979 edge_size
= area
.width
;
3981 case OB_DIRECTION_EAST
:
3982 case OB_DIRECTION_WEST
:
3983 edge_start
= area
.y
;
3984 edge_size
= area
.height
;
3987 g_assert_not_reached();
3990 /* do we collide with this window? */
3991 if (!RANGES_INTERSECT(my_edge_start
, my_edge_size
,
3992 edge_start
, edge_size
))
3996 case OB_DIRECTION_NORTH
:
3997 head
= RECT_BOTTOM(area
);
3998 tail
= RECT_TOP(area
);
4000 case OB_DIRECTION_SOUTH
:
4001 head
= RECT_TOP(area
);
4002 tail
= RECT_BOTTOM(area
);
4004 case OB_DIRECTION_WEST
:
4005 head
= RECT_RIGHT(area
);
4006 tail
= RECT_LEFT(area
);
4008 case OB_DIRECTION_EAST
:
4009 head
= RECT_LEFT(area
);
4010 tail
= RECT_RIGHT(area
);
4013 g_assert_not_reached();
4016 case OB_DIRECTION_NORTH
:
4017 case OB_DIRECTION_WEST
:
4018 /* check if our window is past the head of this window */
4019 if (my_head
<= head
+ 1)
4021 /* check if our window's tail is past the tail of this window */
4022 if (my_head
+ my_size
- 1 <= tail
)
4024 /* check if the head of this window is closer than the previously
4025 chosen edge (take into account that the previously chosen
4026 edge might have been a tail, not a head) */
4027 if (head
+ (*near_edge
? 0 : my_size
) < *dest
)
4029 /* check if the tail of this window is closer than the previously
4030 chosen edge (take into account that the previously chosen
4031 edge might have been a head, not a tail) */
4032 if (tail
- (!*near_edge
? 0 : my_size
) < *dest
)
4035 case OB_DIRECTION_SOUTH
:
4036 case OB_DIRECTION_EAST
:
4037 /* check if our window is past the head of this window */
4038 if (my_head
>= head
- 1)
4040 /* check if our window's tail is past the tail of this window */
4041 if (my_head
- my_size
+ 1 >= tail
)
4043 /* check if the head of this window is closer than the previously
4044 chosen edge (take into account that the previously chosen
4045 edge might have been a tail, not a head) */
4046 if (head
- (*near_edge
? 0 : my_size
) > *dest
)
4048 /* check if the tail of this window is closer than the previously
4049 chosen edge (take into account that the previously chosen
4050 edge might have been a head, not a tail) */
4051 if (tail
+ (!*near_edge
? 0 : my_size
) > *dest
)
4055 g_assert_not_reached();
4058 ob_debug("my head %d size %d", my_head
, my_size
);
4059 ob_debug("head %d tail %d deest %d", head
, tail
, *dest
);
4061 ob_debug("using near edge %d", head
);
4065 else if (!skip_tail
) {
4066 ob_debug("using far edge %d", tail
);
4072 void client_find_edge_directional(ObClient
*self
, ObDirection dir
,
4073 gint my_head
, gint my_size
,
4074 gint my_edge_start
, gint my_edge_size
,
4075 gint
*dest
, gboolean
*near_edge
)
4082 a
= screen_area(self
->desktop
, SCREEN_AREA_ALL_MONITORS
,
4083 &self
->frame
->area
);
4084 mon
= screen_area(self
->desktop
, SCREEN_AREA_ONE_MONITOR
,
4085 &self
->frame
->area
);
4088 case OB_DIRECTION_NORTH
:
4089 if (my_head
>= RECT_TOP(*mon
) + 1)
4090 edge
= RECT_TOP(*mon
) - 1;
4092 edge
= RECT_TOP(*a
) - 1;
4094 case OB_DIRECTION_SOUTH
:
4095 if (my_head
<= RECT_BOTTOM(*mon
) - 1)
4096 edge
= RECT_BOTTOM(*mon
) + 1;
4098 edge
= RECT_BOTTOM(*a
) + 1;
4100 case OB_DIRECTION_EAST
:
4101 if (my_head
<= RECT_RIGHT(*mon
) - 1)
4102 edge
= RECT_RIGHT(*mon
) + 1;
4104 edge
= RECT_RIGHT(*a
) + 1;
4106 case OB_DIRECTION_WEST
:
4107 if (my_head
>= RECT_LEFT(*mon
) + 1)
4108 edge
= RECT_LEFT(*mon
) - 1;
4110 edge
= RECT_LEFT(*a
) - 1;
4113 g_assert_not_reached();
4115 /* default to the far edge, then narrow it down */
4119 for (it
= client_list
; it
; it
= g_list_next(it
)) {
4120 ObClient
*cur
= it
->data
;
4122 /* skip windows to not bump into */
4127 if (self
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
&&
4128 cur
->desktop
!= screen_desktop
)
4131 ob_debug("trying window %s", cur
->title
);
4133 detect_edge(cur
->frame
->area
, dir
, my_head
, my_size
, my_edge_start
,
4134 my_edge_size
, dest
, near_edge
);
4136 dock_get_area(&dock_area
);
4137 detect_edge(dock_area
, dir
, my_head
, my_size
, my_edge_start
,
4138 my_edge_size
, dest
, near_edge
);
4143 void client_find_move_directional(ObClient
*self
, ObDirection dir
,
4147 gint e
, e_start
, e_size
;
4151 case OB_DIRECTION_EAST
:
4152 head
= RECT_RIGHT(self
->frame
->area
);
4153 size
= self
->frame
->area
.width
;
4154 e_start
= RECT_TOP(self
->frame
->area
);
4155 e_size
= self
->frame
->area
.height
;
4157 case OB_DIRECTION_WEST
:
4158 head
= RECT_LEFT(self
->frame
->area
);
4159 size
= self
->frame
->area
.width
;
4160 e_start
= RECT_TOP(self
->frame
->area
);
4161 e_size
= self
->frame
->area
.height
;
4163 case OB_DIRECTION_NORTH
:
4164 head
= RECT_TOP(self
->frame
->area
);
4165 size
= self
->frame
->area
.height
;
4166 e_start
= RECT_LEFT(self
->frame
->area
);
4167 e_size
= self
->frame
->area
.width
;
4169 case OB_DIRECTION_SOUTH
:
4170 head
= RECT_BOTTOM(self
->frame
->area
);
4171 size
= self
->frame
->area
.height
;
4172 e_start
= RECT_LEFT(self
->frame
->area
);
4173 e_size
= self
->frame
->area
.width
;
4176 g_assert_not_reached();
4179 client_find_edge_directional(self
, dir
, head
, size
,
4180 e_start
, e_size
, &e
, &near
);
4181 *x
= self
->frame
->area
.x
;
4182 *y
= self
->frame
->area
.y
;
4184 case OB_DIRECTION_EAST
:
4185 if (near
) e
-= self
->frame
->area
.width
;
4189 case OB_DIRECTION_WEST
:
4191 else e
-= self
->frame
->area
.width
;
4194 case OB_DIRECTION_NORTH
:
4196 else e
-= self
->frame
->area
.height
;
4199 case OB_DIRECTION_SOUTH
:
4200 if (near
) e
-= self
->frame
->area
.height
;
4205 g_assert_not_reached();
4207 frame_frame_gravity(self
->frame
, x
, y
);
4210 void client_find_resize_directional(ObClient
*self
, ObDirection side
,
4212 gint
*x
, gint
*y
, gint
*w
, gint
*h
)
4215 gint e
, e_start
, e_size
, delta
;
4220 case OB_DIRECTION_EAST
:
4221 head
= RECT_RIGHT(self
->frame
->area
) +
4222 (self
->size_inc
.width
- 1) * (grow
? 1 : -1);
4223 e_start
= RECT_TOP(self
->frame
->area
);
4224 e_size
= self
->frame
->area
.height
;
4225 dir
= grow
? OB_DIRECTION_EAST
: OB_DIRECTION_WEST
;
4227 case OB_DIRECTION_WEST
:
4228 head
= RECT_LEFT(self
->frame
->area
) -
4229 (self
->size_inc
.width
- 1) * (grow
? 1 : -1);
4230 e_start
= RECT_TOP(self
->frame
->area
);
4231 e_size
= self
->frame
->area
.height
;
4232 dir
= grow
? OB_DIRECTION_WEST
: OB_DIRECTION_EAST
;
4234 case OB_DIRECTION_NORTH
:
4235 head
= RECT_TOP(self
->frame
->area
) -
4236 (self
->size_inc
.height
- 1) * (grow
? 1 : -1);
4237 e_start
= RECT_LEFT(self
->frame
->area
);
4238 e_size
= self
->frame
->area
.width
;
4239 dir
= grow
? OB_DIRECTION_NORTH
: OB_DIRECTION_SOUTH
;
4241 case OB_DIRECTION_SOUTH
:
4242 head
= RECT_BOTTOM(self
->frame
->area
) +
4243 (self
->size_inc
.height
- 1) * (grow
? 1 : -1);
4244 e_start
= RECT_LEFT(self
->frame
->area
);
4245 e_size
= self
->frame
->area
.width
;
4246 dir
= grow
? OB_DIRECTION_SOUTH
: OB_DIRECTION_NORTH
;
4249 g_assert_not_reached();
4252 ob_debug("head %d dir %d", head
, dir
);
4253 client_find_edge_directional(self
, dir
, head
, 1,
4254 e_start
, e_size
, &e
, &near
);
4255 ob_debug("edge %d", e
);
4256 *x
= self
->frame
->area
.x
;
4257 *y
= self
->frame
->area
.y
;
4258 *w
= self
->frame
->area
.width
;
4259 *h
= self
->frame
->area
.height
;
4261 case OB_DIRECTION_EAST
:
4262 if (grow
== near
) --e
;
4263 delta
= e
- RECT_RIGHT(self
->frame
->area
);
4266 case OB_DIRECTION_WEST
:
4267 if (grow
== near
) ++e
;
4268 delta
= RECT_LEFT(self
->frame
->area
) - e
;
4272 case OB_DIRECTION_NORTH
:
4273 if (grow
== near
) ++e
;
4274 delta
= RECT_TOP(self
->frame
->area
) - e
;
4278 case OB_DIRECTION_SOUTH
:
4279 if (grow
== near
) --e
;
4280 delta
= e
- RECT_BOTTOM(self
->frame
->area
);
4284 g_assert_not_reached();
4286 frame_frame_gravity(self
->frame
, x
, y
);
4287 *w
-= self
->frame
->size
.left
+ self
->frame
->size
.right
;
4288 *h
-= self
->frame
->size
.top
+ self
->frame
->size
.bottom
;
4291 ObClient
* client_under_pointer(void)
4295 ObClient
*ret
= NULL
;
4297 if (screen_pointer_pos(&x
, &y
)) {
4298 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
4299 if (WINDOW_IS_CLIENT(it
->data
)) {
4300 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
4301 if (c
->frame
->visible
&&
4302 /* check the desktop, this is done during desktop
4303 switching and windows are shown/hidden status is not
4305 (c
->desktop
== screen_desktop
||
4306 c
->desktop
== DESKTOP_ALL
) &&
4307 /* ignore all animating windows */
4308 !frame_iconify_animating(c
->frame
) &&
4309 RECT_CONTAINS(c
->frame
->area
, x
, y
))
4320 gboolean
client_has_group_siblings(ObClient
*self
)
4322 return self
->group
&& self
->group
->members
->next
;