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"
34 #include "focus_cycle.h"
39 #include "menuframe.h"
42 #include "obrender/render.h"
44 #include "obt/display.h"
45 #include "obt/xqueue.h"
53 # include <signal.h> /* for kill() */
57 #include <X11/Xutil.h>
59 /*! The event mask to grab on client windows */
60 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
63 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
68 ObClientCallback func
;
72 GList
*client_list
= NULL
;
74 static GSList
*client_destroy_notifies
= NULL
;
75 static RrImage
*client_default_icon
= NULL
;
77 static void client_get_all(ObClient
*self
, gboolean real
);
78 static void client_get_startup_id(ObClient
*self
);
79 static void client_get_session_ids(ObClient
*self
);
80 static void client_save_app_rule_values(ObClient
*self
);
81 static void client_get_area(ObClient
*self
);
82 static void client_get_desktop(ObClient
*self
);
83 static void client_get_state(ObClient
*self
);
84 static void client_get_shaped(ObClient
*self
);
85 static void client_get_colormap(ObClient
*self
);
86 static void client_set_desktop_recursive(ObClient
*self
,
90 static void client_change_allowed_actions(ObClient
*self
);
91 static void client_change_state(ObClient
*self
);
92 static void client_change_wm_state(ObClient
*self
);
93 static void client_apply_startup_state(ObClient
*self
,
94 gint x
, gint y
, gint w
, gint h
);
95 static void client_restore_session_state(ObClient
*self
);
96 static gboolean
client_restore_session_stacking(ObClient
*self
);
97 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
98 static void client_update_transient_tree(ObClient
*self
,
99 ObGroup
*oldgroup
, ObGroup
*newgroup
,
100 gboolean oldgtran
, gboolean newgtran
,
102 ObClient
*newparent
);
103 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
,
105 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
107 ObStackingLayer layer
);
108 static void client_call_notifies(ObClient
*self
, GSList
*list
);
109 static void client_ping_event(ObClient
*self
, gboolean dead
);
110 static void client_prompt_kill(ObClient
*self
);
111 static gboolean
client_can_steal_focus(ObClient
*self
,
112 gboolean allow_other_desktop
,
113 gboolean request_from_user
,
114 Time steal_time
, Time launch_time
);
116 void client_startup(gboolean reconfig
)
118 client_default_icon
= RrImageNewFromData(
119 ob_rr_icons
, ob_rr_theme
->def_win_icon
,
120 ob_rr_theme
->def_win_icon_w
, ob_rr_theme
->def_win_icon_h
);
122 if (reconfig
) return;
127 void client_shutdown(gboolean reconfig
)
129 RrImageUnref(client_default_icon
);
130 client_default_icon
= NULL
;
132 if (reconfig
) return;
135 static void client_call_notifies(ObClient
*self
, GSList
*list
)
139 for (it
= list
; it
; it
= g_slist_next(it
)) {
140 ClientCallback
*d
= it
->data
;
141 d
->func(self
, d
->data
);
145 void client_add_destroy_notify(ObClientCallback func
, gpointer data
)
147 ClientCallback
*d
= g_slice_new(ClientCallback
);
150 client_destroy_notifies
= g_slist_prepend(client_destroy_notifies
, d
);
153 void client_remove_destroy_notify(ObClientCallback func
)
157 for (it
= client_destroy_notifies
; it
; it
= g_slist_next(it
)) {
158 ClientCallback
*d
= it
->data
;
159 if (d
->func
== func
) {
160 g_slice_free(ClientCallback
, d
);
161 client_destroy_notifies
=
162 g_slist_delete_link(client_destroy_notifies
, it
);
168 void client_set_list(void)
170 Window
*windows
, *win_it
;
172 guint size
= g_list_length(client_list
);
174 /* create an array of the window ids */
176 windows
= g_new(Window
, size
);
178 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
179 *win_it
= ((ObClient
*)it
->data
)->window
;
183 OBT_PROP_SETA32(obt_root(ob_screen
), NET_CLIENT_LIST
, WINDOW
,
184 (gulong
*)windows
, size
);
192 void client_manage(Window window
, ObPrompt
*prompt
)
195 XSetWindowAttributes attrib_set
;
196 gboolean try_activate
= FALSE
;
197 gboolean do_activate
;
198 ObAppSettings
*settings
;
199 gboolean transient
= FALSE
;
205 ob_debug("Managing window: 0x%lx", window
);
207 /* choose the events we want to receive on the CLIENT window
208 (ObPrompt windows can request events too) */
209 attrib_set
.event_mask
= CLIENT_EVENTMASK
|
210 (prompt
? prompt
->event_mask
: 0);
211 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
212 XChangeWindowAttributes(obt_display
, window
,
213 CWEventMask
|CWDontPropagate
, &attrib_set
);
215 /* create the ObClient struct, and populate it from the hints on the
217 self
= g_slice_new0(ObClient
);
218 self
->obwin
.type
= OB_WINDOW_CLASS_CLIENT
;
219 self
->window
= window
;
220 self
->prompt
= prompt
;
221 self
->managed
= TRUE
;
223 /* non-zero defaults */
224 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
225 self
->gravity
= NorthWestGravity
;
226 self
->desktop
= screen_num_desktops
; /* always an invalid value */
228 /* get all the stuff off the window */
229 client_get_all(self
, TRUE
);
231 ob_debug("Window type: %d", self
->type
);
232 ob_debug("Window group: 0x%x", self
->group
?self
->group
->leader
:0);
233 ob_debug("Window name: %s class: %s role: %s title: %s",
234 self
->name
, self
->class, self
->role
, self
->title
);
236 /* per-app settings override stuff from client_get_all, and return the
237 settings for other uses too. the returned settings is a shallow copy,
238 that needs to be freed with g_free(). */
239 settings
= client_get_settings_state(self
);
241 /* specify that if we exit, the window should not be destroyed and
242 should be reparented back to root automatically, unless we are managing
243 an internal ObPrompt window */
245 XChangeSaveSet(obt_display
, window
, SetModeInsert
);
247 /* create the decoration frame for the client window */
248 self
->frame
= frame_new(self
);
250 frame_grab_client(self
->frame
);
252 /* we've grabbed everything and set everything that we need to at mapping
256 /* the session should get the last say though */
257 client_restore_session_state(self
);
259 /* don't put helper/modal windows on a different desktop if they are
260 related to the focused window. */
261 if (!screen_compare_desktops(self
->desktop
, screen_desktop
) &&
262 focus_client
&& client_search_transient(focus_client
, self
) &&
263 (client_helper(self
) || self
->modal
))
265 self
->desktop
= screen_desktop
;
268 /* tell startup notification that this app started */
269 launch_time
= sn_app_started(self
->startup_id
, self
->class, self
->name
);
271 if (!OBT_PROP_GET32(self
->window
, NET_WM_USER_TIME
, CARDINAL
, &user_time
))
272 user_time
= event_time();
274 /* do this after we have a frame.. it uses the frame to help determine the
275 WM_STATE to apply. */
276 client_change_state(self
);
278 /* add ourselves to the focus order */
279 focus_order_add_new(self
);
281 /* do this to add ourselves to the stacking list in a non-intrusive way */
282 client_calc_layer(self
);
284 /* focus the new window? */
285 if (ob_state() != OB_STATE_STARTING
&&
286 (!self
->session
|| self
->session
->focused
) &&
287 /* this means focus=true for window is same as config_focus_new=true */
288 ((config_focus_new
|| settings
->focus
== 1) ||
289 client_search_focus_tree_full(self
)) &&
290 /* NET_WM_USER_TIME 0 when mapping means don't focus */
292 /* this checks for focus=false for the window */
293 settings
->focus
!= 0 &&
294 focus_valid_target(self
, self
->desktop
,
295 FALSE
, FALSE
, TRUE
, TRUE
, FALSE
, FALSE
,
296 settings
->focus
== 1))
301 /* remove the client's border */
302 XSetWindowBorderWidth(obt_display
, self
->window
, 0);
304 /* adjust the frame to the client's size before showing or placing
306 frame_adjust_area(self
->frame
, FALSE
, TRUE
, FALSE
);
307 frame_adjust_client_area(self
->frame
);
309 /* where the frame was placed is where the window was originally */
312 ob_debug("Going to try activate new window? %s",
313 try_activate
? "yes" : "no");
315 do_activate
= client_can_steal_focus(
316 self
, settings
->focus
== 1,
317 (!!launch_time
|| settings
->focus
== 1),
318 event_time(), launch_time
);
322 /* figure out placement for the window if the window is new */
323 if (ob_state() == OB_STATE_RUNNING
) {
324 ob_debug("Positioned: %s @ %d %d",
325 (!self
->positioned
? "no" :
326 (self
->positioned
== PPosition
? "program specified" :
327 (self
->positioned
== USPosition
? "user specified" :
328 (self
->positioned
== (PPosition
| USPosition
) ?
329 "program + user specified" :
330 "BADNESS !?")))), place
.x
, place
.y
);
332 ob_debug("Sized: %s @ %d %d",
333 (!self
->sized
? "no" :
334 (self
->sized
== PSize
? "program specified" :
335 (self
->sized
== USSize
? "user specified" :
336 (self
->sized
== (PSize
| USSize
) ?
337 "program + user specified" :
338 "BADNESS !?")))), place
.width
, place
.height
);
340 obplaced
= place_client(self
, do_activate
, &place
.x
, &place
.y
,
343 /* watch for buggy apps that ask to be placed at (0,0) when there is
345 if (!obplaced
&& place
.x
== 0 && place
.y
== 0 &&
346 /* non-normal windows are allowed */
347 client_normal(self
) &&
348 /* oldschool fullscreen windows are allowed */
349 !client_is_oldfullscreen(self
, &place
))
353 r
= screen_area(self
->desktop
, SCREEN_AREA_ALL_MONITORS
, NULL
);
357 ob_debug("Moving buggy app from (0,0) to (%d,%d)", r
->x
, r
->y
);
359 g_slice_free(Rect
, r
);
362 /* make sure the window is visible. */
363 client_find_onscreen(self
, &place
.x
, &place
.y
,
364 place
.width
, place
.height
,
365 /* non-normal clients has less rules, and
366 windows that are being restored from a
367 session do also. we can assume you want
368 it back where you saved it. Clients saying
369 they placed themselves are subjected to
370 harder rules, ones that are placed by
371 place.c or by the user are allowed partially
372 off-screen and on xinerama divides (ie,
373 it is up to the placement routines to avoid
374 the xinerama divides)
376 children and splash screens are forced on
377 screen, but i don't remember why i decided to
380 ob_state() == OB_STATE_RUNNING
&&
381 (self
->type
== OB_CLIENT_TYPE_DIALOG
||
382 self
->type
== OB_CLIENT_TYPE_SPLASH
||
383 (!((self
->positioned
& USPosition
) ||
384 settings
->pos_given
) &&
385 client_normal(self
) &&
387 /* don't move oldschool fullscreen windows to
388 fit inside the struts (fixes Acroread, which
389 makes its fullscreen window fit the screen
390 but it is not USSize'd or USPosition'd) */
391 !client_is_oldfullscreen(self
, &place
))));
394 /* if the window isn't user-sized, then make it fit inside
395 the visible screen area on its monitor. Use basically the same rules
396 for forcing the window on screen in the client_find_onscreen call.
398 do this after place_client, it chooses the monitor!
400 splash screens get "transient" set to TRUE by
401 the place_client call
403 if (ob_state() == OB_STATE_RUNNING
&&
405 (!(self
->sized
& USSize
|| self
->positioned
& USPosition
) &&
406 client_normal(self
) &&
408 /* don't shrink oldschool fullscreen windows to fit inside the
409 struts (fixes Acroread, which makes its fullscreen window
410 fit the screen but it is not USSize'd or USPosition'd) */
411 !client_is_oldfullscreen(self
, &place
))))
413 Rect
*a
= screen_area(self
->desktop
, SCREEN_AREA_ONE_MONITOR
, &place
);
415 /* get the size of the frame */
416 place
.width
+= self
->frame
->size
.left
+ self
->frame
->size
.right
;
417 place
.height
+= self
->frame
->size
.top
+ self
->frame
->size
.bottom
;
419 /* fit the window inside the area */
420 place
.width
= MIN(place
.width
, a
->width
);
421 place
.height
= MIN(place
.height
, a
->height
);
423 ob_debug("setting window size to %dx%d", place
.width
, place
.height
);
425 /* get the size of the client back */
426 place
.width
-= self
->frame
->size
.left
+ self
->frame
->size
.right
;
427 place
.height
-= self
->frame
->size
.top
+ self
->frame
->size
.bottom
;
429 g_slice_free(Rect
, a
);
432 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
433 "some restrictions may apply",
434 self
->window
, place
.x
, place
.y
, place
.width
, place
.height
);
436 ob_debug(" but session requested %d, %d %d x %d instead, "
438 self
->session
->x
, self
->session
->y
,
439 self
->session
->w
, self
->session
->h
);
441 /* do this after the window is placed, so the premax/prefullscreen numbers
444 this also places the window
446 client_apply_startup_state(self
, place
.x
, place
.y
,
447 place
.width
, place
.height
);
449 /* set the initial value of the desktop hint, when one wasn't requested
451 OBT_PROP_SET32(self
->window
, NET_WM_DESKTOP
, CARDINAL
, self
->desktop
);
453 /* grab mouse bindings before showing the window */
454 mouse_grab_for_client(self
, TRUE
);
456 /* this has to happen before we try focus the window, but we want it to
457 happen after the client's stacking has been determined or it looks bad
461 if (!config_focus_under_mouse
)
462 ignore_start
= event_start_ignore_all_enters();
466 if (!config_focus_under_mouse
)
467 event_end_ignore_all_enters(ignore_start
);
470 /* activate/hilight/raise the window */
473 gboolean stacked
= client_restore_session_stacking(self
);
474 client_present(self
, FALSE
, !stacked
, TRUE
);
477 /* if the client isn't stealing focus, then hilite it so the user
478 knows it is there, but don't do this if we're restoring from a
480 if (!client_restore_session_stacking(self
))
481 client_hilite(self
, TRUE
);
485 /* This may look rather odd. Well it's because new windows are added
486 to the stacking order non-intrusively. If we're not going to focus
487 the new window or hilite it, then we raise it to the top. This will
488 take affect for things that don't get focused like splash screens.
489 Also if you don't have focus_new enabled, then it's going to get
490 raised to the top. Legacy begets legacy I guess?
492 if (!client_restore_session_stacking(self
))
493 stacking_raise(CLIENT_AS_WINDOW(self
));
496 /* add to client list/map */
497 client_list
= g_list_append(client_list
, self
);
498 window_add(&self
->window
, CLIENT_AS_WINDOW(self
));
500 /* this has to happen after we're in the client_list */
501 if (STRUT_EXISTS(self
->strut
))
502 screen_update_areas();
504 /* update the list hints */
507 /* free the ObAppSettings shallow copy */
508 g_slice_free(ObAppSettings
, settings
);
510 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
511 window
, self
->frame
->window
, self
->class);
514 ObClient
*client_fake_manage(Window window
)
517 ObAppSettings
*settings
;
519 ob_debug("Pretend-managing window: %lx", window
);
521 /* do this minimal stuff to figure out the client's decorations */
523 self
= g_slice_new0(ObClient
);
524 self
->window
= window
;
526 client_get_all(self
, FALSE
);
527 /* per-app settings override stuff, and return the settings for other
528 uses too. this returns a shallow copy that needs to be freed */
529 settings
= client_get_settings_state(self
);
531 /* create the decoration frame for the client window and adjust its size */
532 self
->frame
= frame_new(self
);
534 client_apply_startup_state(self
, self
->area
.x
, self
->area
.y
,
535 self
->area
.width
, self
->area
.height
);
537 ob_debug("gave extents left %d right %d top %d bottom %d",
538 self
->frame
->size
.left
, self
->frame
->size
.right
,
539 self
->frame
->size
.top
, self
->frame
->size
.bottom
);
541 /* free the ObAppSettings shallow copy */
542 g_slice_free(ObAppSettings
, settings
);
547 void client_unmanage_all(void)
550 client_unmanage(client_list
->data
);
553 void client_unmanage(ObClient
*self
)
558 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
559 self
->window
, self
->frame
->window
,
560 self
->class, self
->title
? self
->title
: "");
562 g_assert(self
!= NULL
);
564 /* we dont want events no more. do this before hiding the frame so we
565 don't generate more events */
566 XSelectInput(obt_display
, self
->window
, NoEventMask
);
568 /* ignore enter events from the unmap so it doesnt mess with the focus */
569 if (!config_focus_under_mouse
)
570 ignore_start
= event_start_ignore_all_enters();
572 frame_hide(self
->frame
);
573 /* flush to send the hide to the server quickly */
576 if (!config_focus_under_mouse
)
577 event_end_ignore_all_enters(ignore_start
);
579 mouse_grab_for_client(self
, FALSE
);
581 self
->managed
= FALSE
;
583 /* remove the window from our save set, unless we are managing an internal
586 XChangeSaveSet(obt_display
, self
->window
, SetModeDelete
);
588 /* update the focus lists */
589 focus_order_remove(self
);
590 if (client_focused(self
)) {
591 /* don't leave an invalid focus_client */
595 /* if we're prompting to kill the client, close that */
596 prompt_unref(self
->kill_prompt
);
597 self
->kill_prompt
= NULL
;
599 client_list
= g_list_remove(client_list
, self
);
600 stacking_remove(self
);
601 window_remove(self
->window
);
603 /* once the client is out of the list, update the struts to remove its
605 if (STRUT_EXISTS(self
->strut
))
606 screen_update_areas();
608 client_call_notifies(self
, client_destroy_notifies
);
610 /* tell our parent(s) that we're gone */
611 for (it
= self
->parents
; it
; it
= g_slist_next(it
))
612 ((ObClient
*)it
->data
)->transients
=
613 g_slist_remove(((ObClient
*)it
->data
)->transients
,self
);
615 /* tell our transients that we're gone */
616 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
617 ((ObClient
*)it
->data
)->parents
=
618 g_slist_remove(((ObClient
*)it
->data
)->parents
, self
);
619 /* we could be keeping our children in a higher layer */
620 client_calc_layer(it
->data
);
623 /* remove from its group */
625 group_remove(self
->group
, self
);
629 /* restore the window's original geometry so it is not lost */
635 if (self
->fullscreen
)
636 a
= self
->pre_fullscreen_area
;
637 else if (self
->max_horz
|| self
->max_vert
) {
638 if (self
->max_horz
) {
639 a
.x
= self
->pre_max_area
.x
;
640 a
.width
= self
->pre_max_area
.width
;
642 if (self
->max_vert
) {
643 a
.y
= self
->pre_max_area
.y
;
644 a
.height
= self
->pre_max_area
.height
;
648 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
649 /* let it be moved and resized no matter what */
650 self
->functions
= OB_CLIENT_FUNC_MOVE
| OB_CLIENT_FUNC_RESIZE
;
651 self
->decorations
= 0; /* unmanaged windows have no decor */
653 /* give the client its border back */
654 XSetWindowBorderWidth(obt_display
, self
->window
, self
->border_width
);
656 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
659 /* reparent the window out of the frame, and free the frame */
660 frame_release_client(self
->frame
);
661 frame_free(self
->frame
);
664 if (ob_state() != OB_STATE_EXITING
) {
665 /* these values should not be persisted across a window
667 OBT_PROP_ERASE(self
->window
, NET_WM_DESKTOP
);
668 OBT_PROP_ERASE(self
->window
, NET_WM_STATE
);
669 OBT_PROP_ERASE(self
->window
, WM_STATE
);
671 /* if we're left in an unmapped state, the client wont be mapped.
672 this is bad, since we will no longer be managing the window on
674 XMapWindow(obt_display
, self
->window
);
677 /* these should not be left on the window ever. other window managers
678 don't necessarily use them and it will mess them up (like compiz) */
679 OBT_PROP_ERASE(self
->window
, NET_WM_VISIBLE_NAME
);
680 OBT_PROP_ERASE(self
->window
, NET_WM_VISIBLE_ICON_NAME
);
682 /* update the list hints */
685 ob_debug("Unmanaged window 0x%lx", self
->window
);
687 /* free all data allocated in the client struct */
688 RrImageUnref(self
->icon_set
);
689 g_slist_free(self
->transients
);
690 g_free(self
->startup_id
);
691 g_free(self
->wm_command
);
693 g_free(self
->icon_title
);
694 g_free(self
->original_title
);
698 g_free(self
->client_machine
);
699 g_free(self
->sm_client_id
);
700 g_slice_free(ObClient
, self
);
703 void client_fake_unmanage(ObClient
*self
)
705 /* this is all that got allocated to get the decorations */
707 frame_free(self
->frame
);
708 g_slice_free(ObClient
, self
);
711 static gboolean
client_can_steal_focus(ObClient
*self
,
712 gboolean allow_other_desktop
,
713 gboolean request_from_user
,
718 gboolean relative_focused
;
722 relative_focused
= (focus_client
!= NULL
&&
723 (client_search_focus_tree_full(self
) != NULL
||
724 client_search_focus_group_full(self
) != NULL
));
726 /* This is focus stealing prevention */
727 ob_debug("Want to focus window 0x%x at time %u "
728 "launched at %u (last user interaction time %u) "
729 "request from %s, allow other desktop: %s, "
730 "desktop switch time %u",
731 self
->window
, steal_time
, launch_time
,
732 event_last_user_time
,
733 (request_from_user
? "user" : "other"),
734 (allow_other_desktop
? "yes" : "no"),
735 screen_desktop_user_time
);
738 if no launch time is provided for an application, make one up.
740 if the window is related to other existing windows
741 and one of those windows was the last used
742 then we will give it a launch time equal to the last user time,
743 which will end up giving the window focus probably.
745 the window is related to other windows, but you are not working in
747 seems suspicious, so we will give it a launch time of
748 NOW - STEAL_INTERVAL,
749 so it will be given focus only if we didn't use something else
750 during the steal interval.
752 the window is all on its own, so we can't judge it. give it a launch
753 time equal to the last user time, so it will probably take focus.
755 this way running things from a terminal will give them focus, but popups
756 without a launch time shouldn't steal focus so easily.
760 if (client_has_relative(self
)) {
761 if (event_last_user_time
&& client_search_focus_group_full(self
)) {
762 /* our relative is focused */
763 launch_time
= event_last_user_time
;
764 ob_debug("Unknown launch time, using %u - window in active "
765 "group", launch_time
);
767 else if (!request_from_user
) {
768 /* has relatives which are not being used. suspicious */
769 launch_time
= event_time() - OB_EVENT_USER_TIME_DELAY
;
770 ob_debug("Unknown launch time, using %u - window in inactive "
771 "group", launch_time
);
774 /* has relatives which are not being used, but the user seems
775 to want to go there! */
776 launch_time
= event_last_user_time
;
777 ob_debug("Unknown launch time, using %u - user request",
782 /* the window is on its own, probably the user knows it is going
784 launch_time
= event_last_user_time
;
785 ob_debug("Unknown launch time, using %u - independent window",
790 /* if it's on another desktop
791 and if allow_other_desktop is true, we generally let it steal focus.
792 but if it didn't come from the user, don't let it steal unless it was
793 launched before the user switched desktops.
794 focus, unless it was launched after we changed desktops and the request
797 if (!screen_compare_desktops(screen_desktop
, self
->desktop
)) {
798 /* must be allowed */
799 if (!allow_other_desktop
) {
801 ob_debug("Not focusing the window because its on another desktop");
803 /* if we don't know when the desktop changed, but request is from an
804 application, don't let it change desktop on you */
805 else if (!request_from_user
) {
807 ob_debug("Not focusing the window because non-user request");
810 /* If something is focused... */
811 else if (focus_client
) {
812 /* If the user is working in another window right now, then don't
814 if (!relative_focused
&&
815 event_last_user_time
&&
816 /* last user time must be strictly > launch_time to block focus */
817 (event_time_after(event_last_user_time
, launch_time
) &&
818 event_last_user_time
!= launch_time
) &&
819 event_time_after(event_last_user_time
,
820 steal_time
- OB_EVENT_USER_TIME_DELAY
))
823 ob_debug("Not focusing the window because the user is "
824 "working in another window that is not its relative");
826 /* Don't move focus if it's not going to go to this window
828 else if (client_focus_target(self
) != self
) {
830 ob_debug("Not focusing the window because another window "
831 "would get the focus anyway");
833 /* For requests that don't come from the user */
834 else if (!request_from_user
) {
835 /* If the new window is a transient (and its relatives aren't
837 if (client_has_parent(self
) && !relative_focused
) {
839 ob_debug("Not focusing the window because it is a "
840 "transient, and its relatives aren't focused");
842 /* Don't steal focus from globally active clients.
843 I stole this idea from KWin. It seems nice.
845 else if (!(focus_client
->can_focus
|| focus_client
->focus_notify
))
848 ob_debug("Not focusing the window because a globally "
849 "active client has focus");
851 /* Don't move focus if the window is not visible on the current
852 desktop and none of its relatives are focused */
853 else if (!allow_other_desktop
&&
854 !screen_compare_desktops(self
->desktop
, screen_desktop
) &&
858 ob_debug("Not focusing the window because it is on "
859 "another desktop and no relatives are focused ");
865 ob_debug("Focus stealing prevention activated for %s at "
866 "time %u (last user interaction time %u)",
867 self
->title
, steal_time
, event_last_user_time
);
869 ob_debug("Allowing focus stealing for %s at time %u (last user "
870 "interaction time %u)",
871 self
->title
, steal_time
, event_last_user_time
);
875 /*! Returns a new structure containing the per-app settings for this client.
876 The returned structure needs to be freed with g_free. */
877 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
879 ObAppSettings
*settings
;
882 settings
= config_create_app_settings();
884 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
885 ObAppSettings
*app
= it
->data
;
886 gboolean match
= TRUE
;
888 g_assert(app
->name
!= NULL
|| app
->class != NULL
||
889 app
->role
!= NULL
|| app
->title
!= NULL
||
890 (signed)app
->type
>= 0);
893 !g_pattern_match(app
->name
, strlen(self
->name
), self
->name
, NULL
))
895 else if (app
->class &&
896 !g_pattern_match(app
->class,
897 strlen(self
->class), self
->class, NULL
))
899 else if (app
->role
&&
900 !g_pattern_match(app
->role
,
901 strlen(self
->role
), self
->role
, NULL
))
903 else if (app
->title
&&
904 !g_pattern_match(app
->title
,
905 strlen(self
->title
), self
->title
, NULL
))
907 else if ((signed)app
->type
>= 0 && app
->type
!= self
->type
) {
912 ob_debug("Window matching: %s", app
->name
);
914 /* copy the settings to our struct, overriding the existing
915 settings if they are not defaults */
916 config_app_settings_copy_non_defaults(app
, settings
);
920 if (settings
->shade
!= -1)
921 self
->shaded
= !!settings
->shade
;
922 if (settings
->decor
!= -1)
923 self
->undecorated
= !settings
->decor
;
924 if (settings
->iconic
!= -1)
925 self
->iconic
= !!settings
->iconic
;
926 if (settings
->skip_pager
!= -1)
927 self
->skip_pager
= !!settings
->skip_pager
;
928 if (settings
->skip_taskbar
!= -1)
929 self
->skip_taskbar
= !!settings
->skip_taskbar
;
931 if (settings
->max_vert
!= -1)
932 self
->max_vert
= !!settings
->max_vert
;
933 if (settings
->max_horz
!= -1)
934 self
->max_horz
= !!settings
->max_horz
;
936 if (settings
->fullscreen
!= -1)
937 self
->fullscreen
= !!settings
->fullscreen
;
939 if (settings
->desktop
) {
940 if (settings
->desktop
== DESKTOP_ALL
)
941 self
->desktop
= settings
->desktop
;
942 else if (settings
->desktop
> 0 &&
943 settings
->desktop
<= screen_num_desktops
)
944 self
->desktop
= settings
->desktop
- 1;
947 if (settings
->layer
== -1) {
951 else if (settings
->layer
== 0) {
955 else if (settings
->layer
== 1) {
962 static void client_restore_session_state(ObClient
*self
)
966 ob_debug_type(OB_DEBUG_SM
,
967 "Restore session for client %s", self
->title
);
969 if (!(it
= session_state_find(self
))) {
970 ob_debug_type(OB_DEBUG_SM
,
971 "Session data not found for client %s", self
->title
);
975 self
->session
= it
->data
;
977 ob_debug_type(OB_DEBUG_SM
, "Session data loaded for client %s",
980 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
981 self
->positioned
= USPosition
;
982 self
->sized
= USSize
;
983 if (self
->session
->w
> 0)
984 self
->area
.width
= self
->session
->w
;
985 if (self
->session
->h
> 0)
986 self
->area
.height
= self
->session
->h
;
987 XResizeWindow(obt_display
, self
->window
,
988 self
->area
.width
, self
->area
.height
);
990 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
991 self
->session
->desktop
:
992 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
993 OBT_PROP_SET32(self
->window
, NET_WM_DESKTOP
, CARDINAL
, self
->desktop
);
995 self
->shaded
= self
->session
->shaded
;
996 self
->iconic
= self
->session
->iconic
;
997 self
->skip_pager
= self
->session
->skip_pager
;
998 self
->skip_taskbar
= self
->session
->skip_taskbar
;
999 self
->fullscreen
= self
->session
->fullscreen
;
1000 self
->above
= self
->session
->above
;
1001 self
->below
= self
->session
->below
;
1002 self
->max_horz
= self
->session
->max_horz
;
1003 self
->max_vert
= self
->session
->max_vert
;
1004 self
->undecorated
= self
->session
->undecorated
;
1007 static gboolean
client_restore_session_stacking(ObClient
*self
)
1011 if (!self
->session
) return FALSE
;
1013 mypos
= g_list_find(session_saved_state
, self
->session
);
1014 if (!mypos
) return FALSE
;
1016 /* start above me and look for the first client */
1017 for (it
= g_list_previous(mypos
); it
; it
= g_list_previous(it
)) {
1020 for (cit
= client_list
; cit
; cit
= g_list_next(cit
)) {
1021 ObClient
*c
= cit
->data
;
1022 /* found a client that was in the session, so go below it */
1023 if (c
->session
== it
->data
) {
1024 stacking_below(CLIENT_AS_WINDOW(self
),
1025 CLIENT_AS_WINDOW(cit
->data
));
1033 void client_move_onscreen(ObClient
*self
, gboolean rude
)
1035 gint x
= self
->area
.x
;
1036 gint y
= self
->area
.y
;
1037 if (client_find_onscreen(self
, &x
, &y
,
1039 self
->area
.height
, rude
)) {
1040 client_move(self
, x
, y
);
1044 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
1047 gint ox
= *x
, oy
= *y
;
1048 gboolean rudel
= rude
, ruder
= rude
, rudet
= rude
, rudeb
= rude
;
1054 RECT_SET(desired
, *x
, *y
, w
, h
);
1055 frame_rect_to_frame(self
->frame
, &desired
);
1057 /* get where the frame would be */
1058 frame_client_gravity(self
->frame
, x
, y
);
1060 /* get the requested size of the window with decorations */
1061 fw
= self
->frame
->size
.left
+ w
+ self
->frame
->size
.right
;
1062 fh
= self
->frame
->size
.top
+ h
+ self
->frame
->size
.bottom
;
1064 /* If rudeness wasn't requested, then still be rude in a given direction
1065 if the client is not moving, only resizing in that direction */
1067 Point oldtl
, oldtr
, oldbl
, oldbr
;
1068 Point newtl
, newtr
, newbl
, newbr
;
1069 gboolean stationary_l
, stationary_r
, stationary_t
, stationary_b
;
1071 POINT_SET(oldtl
, self
->frame
->area
.x
, self
->frame
->area
.y
);
1072 POINT_SET(oldbr
, self
->frame
->area
.x
+ self
->frame
->area
.width
- 1,
1073 self
->frame
->area
.y
+ self
->frame
->area
.height
- 1);
1074 POINT_SET(oldtr
, oldbr
.x
, oldtl
.y
);
1075 POINT_SET(oldbl
, oldtl
.x
, oldbr
.y
);
1077 POINT_SET(newtl
, *x
, *y
);
1078 POINT_SET(newbr
, *x
+ fw
- 1, *y
+ fh
- 1);
1079 POINT_SET(newtr
, newbr
.x
, newtl
.y
);
1080 POINT_SET(newbl
, newtl
.x
, newbr
.y
);
1082 /* is it moving or just resizing from some corner? */
1083 stationary_l
= oldtl
.x
== newtl
.x
;
1084 stationary_r
= oldtr
.x
== newtr
.x
;
1085 stationary_t
= oldtl
.y
== newtl
.y
;
1086 stationary_b
= oldbl
.y
== newbl
.y
;
1088 /* if left edge is growing and didnt move right edge */
1089 if (stationary_r
&& newtl
.x
< oldtl
.x
)
1091 /* if right edge is growing and didnt move left edge */
1092 if (stationary_l
&& newtr
.x
> oldtr
.x
)
1094 /* if top edge is growing and didnt move bottom edge */
1095 if (stationary_b
&& newtl
.y
< oldtl
.y
)
1097 /* if bottom edge is growing and didnt move top edge */
1098 if (stationary_t
&& newbl
.y
> oldbl
.y
)
1102 /* we iterate through every monitor that the window is at least partially
1103 on, to make sure it is obeying the rules on them all
1105 if the window does not appear on any monitors, then use the first one
1108 for (i
= 0; i
< screen_num_monitors
; ++i
) {
1111 if (!screen_physical_area_monitor_contains(i
, &desired
)) {
1112 if (i
< screen_num_monitors
- 1 || found_mon
)
1115 /* the window is not inside any monitor! so just use the first
1117 a
= screen_area(self
->desktop
, 0, NULL
);
1120 a
= screen_area(self
->desktop
, SCREEN_AREA_ONE_MONITOR
, &desired
);
1123 /* This makes sure windows aren't entirely outside of the screen so you
1124 can't see them at all.
1125 It makes sure 10% of the window is on the screen at least. And don't
1126 let it move itself off the top of the screen, which would hide the
1127 titlebar on you. (The user can still do this if they want too, it's
1128 only limiting the application.
1130 if (client_normal(self
)) {
1131 if (!self
->strut
.right
&& *x
+ fw
/10 >= a
->x
+ a
->width
- 1)
1132 *x
= a
->x
+ a
->width
- fw
/10;
1133 if (!self
->strut
.bottom
&& *y
+ fh
/10 >= a
->y
+ a
->height
- 1)
1134 *y
= a
->y
+ a
->height
- fh
/10;
1135 if (!self
->strut
.left
&& *x
+ fw
*9/10 - 1 < a
->x
)
1136 *x
= a
->x
- fw
*9/10;
1137 if (!self
->strut
.top
&& *y
+ fh
*9/10 - 1 < a
->y
)
1138 *y
= a
->y
- fh
*9/10;
1141 /* This here doesn't let windows even a pixel outside the
1142 struts/screen. When called from client_manage, programs placing
1143 themselves are forced completely onscreen, while things like
1144 xterm -geometry resolution-width/2 will work fine. Trying to
1145 place it completely offscreen will be handled in the above code.
1146 Sorry for this confused comment, i am tired. */
1147 if (rudel
&& !self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
1148 if (ruder
&& !self
->strut
.right
&& *x
+ fw
> a
->x
+ a
->width
)
1149 *x
= a
->x
+ MAX(0, a
->width
- fw
);
1151 if (rudet
&& !self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
1152 if (rudeb
&& !self
->strut
.bottom
&& *y
+ fh
> a
->y
+ a
->height
)
1153 *y
= a
->y
+ MAX(0, a
->height
- fh
);
1155 g_slice_free(Rect
, a
);
1158 /* get where the client should be */
1159 frame_frame_gravity(self
->frame
, x
, y
);
1161 return ox
!= *x
|| oy
!= *y
;
1164 static void client_get_all(ObClient
*self
, gboolean real
)
1166 /* this is needed for the frame to set itself up */
1167 client_get_area(self
);
1169 /* these things can change the decor and functions of the window */
1171 client_get_mwm_hints(self
);
1172 /* this can change the mwmhints for special cases */
1173 client_get_type_and_transientness(self
);
1174 client_update_normal_hints(self
);
1176 /* set up the decor/functions before getting the state. the states may
1177 affect which functions are available, but we want to know the maximum
1178 decor/functions are available to this window, so we can then apply them
1179 in client_apply_startup_state() */
1180 client_setup_decor_and_functions(self
, FALSE
);
1182 client_get_state(self
);
1184 /* get the session related properties, these can change decorations
1185 from per-app settings */
1186 client_get_session_ids(self
);
1188 /* now we got everything that can affect the decorations */
1192 /* get this early so we have it for debugging */
1193 client_update_title(self
);
1195 /* save the values of the variables used for app rule matching */
1196 client_save_app_rule_values(self
);
1198 client_update_protocols(self
);
1200 client_update_wmhints(self
);
1201 /* this may have already been called from client_update_wmhints */
1202 if (!self
->parents
&& !self
->transient_for_group
)
1203 client_update_transient_for(self
);
1205 client_get_startup_id(self
);
1206 client_get_desktop(self
);/* uses transient data/group/startup id if a
1207 desktop is not specified */
1208 client_get_shaped(self
);
1211 /* a couple type-based defaults for new windows */
1213 /* this makes sure that these windows appear on all desktops */
1214 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1215 self
->desktop
= DESKTOP_ALL
;
1219 client_update_sync_request_counter(self
);
1222 client_get_colormap(self
);
1223 client_update_strut(self
);
1224 client_update_icons(self
);
1225 client_update_icon_geometry(self
);
1228 static void client_get_startup_id(ObClient
*self
)
1230 if (!(OBT_PROP_GETS_UTF8(self
->window
, NET_STARTUP_ID
, &self
->startup_id
)))
1232 OBT_PROP_GETS_UTF8(self
->group
->leader
, NET_STARTUP_ID
,
1236 static void client_get_area(ObClient
*self
)
1238 XWindowAttributes wattrib
;
1241 ret
= XGetWindowAttributes(obt_display
, self
->window
, &wattrib
);
1242 g_assert(ret
!= BadWindow
);
1244 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
1245 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
1246 self
->border_width
= wattrib
.border_width
;
1248 ob_debug("client area: %d %d %d %d bw %d", wattrib
.x
, wattrib
.y
,
1249 wattrib
.width
, wattrib
.height
, wattrib
.border_width
);
1252 static void client_get_desktop(ObClient
*self
)
1254 guint32 d
= screen_num_desktops
; /* an always-invalid value */
1256 if (OBT_PROP_GET32(self
->window
, NET_WM_DESKTOP
, CARDINAL
, &d
)) {
1257 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
1258 self
->desktop
= screen_num_desktops
- 1;
1261 ob_debug("client requested desktop 0x%x", self
->desktop
);
1264 gboolean first
= TRUE
;
1265 guint all
= screen_num_desktops
; /* not a valid value */
1267 /* if they are all on one desktop, then open it on the
1269 for (it
= self
->parents
; it
; it
= g_slist_next(it
)) {
1270 ObClient
*c
= it
->data
;
1272 if (c
->desktop
== DESKTOP_ALL
) continue;
1278 else if (all
!= c
->desktop
)
1279 all
= screen_num_desktops
; /* make it invalid */
1281 if (all
!= screen_num_desktops
) {
1282 self
->desktop
= all
;
1284 ob_debug("client desktop set from parents: 0x%x",
1287 /* try get from the startup-notification protocol */
1288 else if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1289 if (self
->desktop
>= screen_num_desktops
&&
1290 self
->desktop
!= DESKTOP_ALL
)
1291 self
->desktop
= screen_num_desktops
- 1;
1292 ob_debug("client desktop set from startup-notification: 0x%x",
1295 /* defaults to the current desktop */
1297 self
->desktop
= screen_desktop
;
1298 ob_debug("client desktop set to the current desktop: %d",
1304 static void client_get_state(ObClient
*self
)
1309 if (OBT_PROP_GETA32(self
->window
, NET_WM_STATE
, ATOM
, &state
, &num
)) {
1311 for (i
= 0; i
< num
; ++i
) {
1312 if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_MODAL
))
1314 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_SHADED
))
1315 self
->shaded
= TRUE
;
1316 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
))
1317 self
->iconic
= TRUE
;
1318 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
))
1319 self
->skip_taskbar
= TRUE
;
1320 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
))
1321 self
->skip_pager
= TRUE
;
1322 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
))
1323 self
->fullscreen
= TRUE
;
1324 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
))
1325 self
->max_vert
= TRUE
;
1326 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
))
1327 self
->max_horz
= TRUE
;
1328 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE
))
1330 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_BELOW
))
1332 else if (state
[i
] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
))
1333 self
->demands_attention
= TRUE
;
1334 else if (state
[i
] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
))
1335 self
->undecorated
= TRUE
;
1342 static void client_get_shaped(ObClient
*self
)
1344 self
->shaped
= FALSE
;
1346 if (obt_display_extension_shape
) {
1351 XShapeSelectInput(obt_display
, self
->window
, ShapeNotifyMask
);
1353 XShapeQueryExtents(obt_display
, self
->window
, &s
, &foo
,
1354 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1361 void client_update_transient_for(ObClient
*self
)
1364 ObClient
*target
= NULL
;
1365 gboolean trangroup
= FALSE
;
1367 if (XGetTransientForHint(obt_display
, self
->window
, &t
)) {
1368 if (t
!= self
->window
) { /* can't be transient to itself! */
1369 ObWindow
*tw
= window_find(t
);
1370 /* if this happens then we need to check for it */
1371 g_assert(tw
!= CLIENT_AS_WINDOW(self
));
1372 if (tw
&& WINDOW_IS_CLIENT(tw
)) {
1373 /* watch out for windows with a parent that is something
1374 different, like a dockapp for example */
1375 target
= WINDOW_AS_CLIENT(tw
);
1379 /* Setting the transient_for to Root is actually illegal, however
1380 applications from time have done this to specify transient for
1382 if (!target
&& self
->group
&& t
== obt_root(ob_screen
))
1384 } else if (self
->group
&& self
->transient
)
1387 client_update_transient_tree(self
, self
->group
, self
->group
,
1388 self
->transient_for_group
, trangroup
,
1389 client_direct_parent(self
), target
);
1390 self
->transient_for_group
= trangroup
;
1394 static void client_update_transient_tree(ObClient
*self
,
1395 ObGroup
*oldgroup
, ObGroup
*newgroup
,
1396 gboolean oldgtran
, gboolean newgtran
,
1397 ObClient
* oldparent
,
1398 ObClient
*newparent
)
1403 g_assert(!oldgtran
|| oldgroup
);
1404 g_assert(!newgtran
|| newgroup
);
1405 g_assert((!oldgtran
&& !oldparent
) ||
1406 (oldgtran
&& !oldparent
) ||
1407 (!oldgtran
&& oldparent
));
1408 g_assert((!newgtran
&& !newparent
) ||
1409 (newgtran
&& !newparent
) ||
1410 (!newgtran
&& newparent
));
1413 Group transient windows are not allowed to have other group
1414 transient windows as their children.
1417 /* No change has occured */
1418 if (oldgroup
== newgroup
&&
1419 oldgtran
== newgtran
&&
1420 oldparent
== newparent
) return;
1422 /** Remove the client from the transient tree **/
1424 for (it
= self
->transients
; it
; it
= next
) {
1425 next
= g_slist_next(it
);
1427 self
->transients
= g_slist_delete_link(self
->transients
, it
);
1428 c
->parents
= g_slist_remove(c
->parents
, self
);
1430 for (it
= self
->parents
; it
; it
= next
) {
1431 next
= g_slist_next(it
);
1433 self
->parents
= g_slist_delete_link(self
->parents
, it
);
1434 c
->transients
= g_slist_remove(c
->transients
, self
);
1437 /** Re-add the client to the transient tree **/
1439 /* If we're transient for a group then we need to add ourselves to all our
1442 for (it
= newgroup
->members
; it
; it
= g_slist_next(it
)) {
1445 !client_search_top_direct_parent(c
)->transient_for_group
&&
1448 c
->transients
= g_slist_prepend(c
->transients
, self
);
1449 self
->parents
= g_slist_prepend(self
->parents
, c
);
1454 /* If we are now transient for a single window we need to add ourselves to
1457 WARNING: Cyclical transient-ness is possible if two windows are
1458 transient for eachother.
1460 else if (newparent
&&
1461 /* don't make ourself its child if it is already our child */
1462 !client_is_direct_child(self
, newparent
) &&
1463 client_normal(newparent
))
1465 newparent
->transients
= g_slist_prepend(newparent
->transients
, self
);
1466 self
->parents
= g_slist_prepend(self
->parents
, newparent
);
1469 /* Add any group transient windows to our children. But if we're transient
1470 for the group, then other group transients are not our children.
1472 WARNING: Cyclical transient-ness is possible. For e.g. if:
1473 A is transient for the group
1474 B is transient for A
1475 C is transient for B
1476 A can't be transient for C or we have a cycle
1478 if (!newgtran
&& newgroup
&&
1480 !client_search_top_direct_parent(newparent
)->transient_for_group
) &&
1481 client_normal(self
))
1483 for (it
= newgroup
->members
; it
; it
= g_slist_next(it
)) {
1485 if (c
!= self
&& c
->transient_for_group
&&
1486 /* Don't make it our child if it is already our parent */
1487 !client_is_direct_child(c
, self
))
1489 self
->transients
= g_slist_prepend(self
->transients
, c
);
1490 c
->parents
= g_slist_prepend(c
->parents
, self
);
1495 /** If we change our group transient-ness, our children change their
1496 effective group transient-ness, which affects how they relate to other
1499 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1501 if (!c
->transient_for_group
)
1502 client_update_transient_tree(c
, c
->group
, c
->group
,
1503 c
->transient_for_group
,
1504 c
->transient_for_group
,
1505 client_direct_parent(c
),
1506 client_direct_parent(c
));
1510 void client_get_mwm_hints(ObClient
*self
)
1515 self
->mwmhints
.flags
= 0; /* default to none */
1517 if (OBT_PROP_GETA32(self
->window
, MOTIF_WM_HINTS
, MOTIF_WM_HINTS
,
1519 if (num
>= OB_MWM_ELEMENTS
) {
1520 self
->mwmhints
.flags
= hints
[0];
1521 self
->mwmhints
.functions
= hints
[1];
1522 self
->mwmhints
.decorations
= hints
[2];
1528 void client_get_type_and_transientness(ObClient
*self
)
1535 self
->transient
= FALSE
;
1537 if (OBT_PROP_GETA32(self
->window
, NET_WM_WINDOW_TYPE
, ATOM
, &val
, &num
)) {
1538 /* use the first value that we know about in the array */
1539 for (i
= 0; i
< num
; ++i
) {
1540 if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP
))
1541 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1542 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK
))
1543 self
->type
= OB_CLIENT_TYPE_DOCK
;
1544 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR
))
1545 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1546 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU
))
1547 self
->type
= OB_CLIENT_TYPE_MENU
;
1548 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY
))
1549 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1550 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH
))
1551 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1552 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG
))
1553 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1554 else if (val
[i
] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL
))
1555 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1556 else if (val
[i
] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE
))
1558 /* prevent this window from getting any decor or
1560 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1561 OB_MWM_FLAG_DECORATIONS
);
1562 self
->mwmhints
.decorations
= 0;
1563 self
->mwmhints
.functions
= 0;
1565 if (self
->type
!= (ObClientType
) -1)
1566 break; /* grab the first legit type */
1571 if (XGetTransientForHint(obt_display
, self
->window
, &t
))
1572 self
->transient
= TRUE
;
1574 if (self
->type
== (ObClientType
) -1) {
1575 /*the window type hint was not set, which means we either classify
1576 ourself as a normal window or a dialog, depending on if we are a
1578 if (self
->transient
)
1579 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1581 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1584 /* then, based on our type, we can update our transientness.. */
1585 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1586 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1587 self
->type
== OB_CLIENT_TYPE_MENU
||
1588 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1590 self
->transient
= TRUE
;
1594 void client_update_protocols(ObClient
*self
)
1599 self
->focus_notify
= FALSE
;
1600 self
->delete_window
= FALSE
;
1602 if (OBT_PROP_GETA32(self
->window
, WM_PROTOCOLS
, ATOM
, &proto
, &num_ret
)) {
1603 for (i
= 0; i
< num_ret
; ++i
) {
1604 if (proto
[i
] == OBT_PROP_ATOM(WM_DELETE_WINDOW
))
1605 /* this means we can request the window to close */
1606 self
->delete_window
= TRUE
;
1607 else if (proto
[i
] == OBT_PROP_ATOM(WM_TAKE_FOCUS
))
1608 /* if this protocol is requested, then the window will be
1609 notified whenever we want it to receive focus */
1610 self
->focus_notify
= TRUE
;
1611 else if (proto
[i
] == OBT_PROP_ATOM(NET_WM_PING
))
1612 /* if this protocol is requested, then the window will allow
1613 pings to determine if it is still alive */
1616 else if (proto
[i
] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST
))
1617 /* if this protocol is requested, then resizing the
1618 window will be synchronized between the frame and the
1620 self
->sync_request
= TRUE
;
1628 void client_update_sync_request_counter(ObClient
*self
)
1632 if (OBT_PROP_GET32(self
->window
, NET_WM_SYNC_REQUEST_COUNTER
, CARDINAL
,&i
))
1636 self
->sync_counter
= i
;
1638 /* this must be set when managing a new window according to EWMH */
1639 XSyncIntToValue(&val
, 0);
1640 XSyncSetCounter(obt_display
, self
->sync_counter
, val
);
1642 self
->sync_counter
= None
;
1646 static void client_get_colormap(ObClient
*self
)
1648 XWindowAttributes wa
;
1650 if (XGetWindowAttributes(obt_display
, self
->window
, &wa
))
1651 client_update_colormap(self
, wa
.colormap
);
1654 void client_update_colormap(ObClient
*self
, Colormap colormap
)
1656 if (colormap
== self
->colormap
) return;
1658 ob_debug("Setting client %s colormap: 0x%x", self
->title
, colormap
);
1660 if (client_focused(self
)) {
1661 screen_install_colormap(self
, FALSE
); /* uninstall old one */
1662 self
->colormap
= colormap
;
1663 screen_install_colormap(self
, TRUE
); /* install new one */
1665 self
->colormap
= colormap
;
1668 void client_update_normal_hints(ObClient
*self
)
1674 self
->min_ratio
= 0.0f
;
1675 self
->max_ratio
= 0.0f
;
1676 SIZE_SET(self
->size_inc
, 1, 1);
1677 SIZE_SET(self
->base_size
, -1, -1);
1678 SIZE_SET(self
->min_size
, 0, 0);
1679 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1681 /* get the hints from the window */
1682 if (XGetWMNormalHints(obt_display
, self
->window
, &size
, &ret
)) {
1683 /* normal windows can't request placement! har har
1684 if (!client_normal(self))
1686 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1687 self
->sized
= (size
.flags
& (PSize
|USSize
));
1689 if (size
.flags
& PWinGravity
)
1690 self
->gravity
= size
.win_gravity
;
1692 if (size
.flags
& PAspect
) {
1693 if (size
.min_aspect
.y
)
1695 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1696 if (size
.max_aspect
.y
)
1698 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1701 if (size
.flags
& PMinSize
)
1702 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1704 if (size
.flags
& PMaxSize
)
1705 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1707 if (size
.flags
& PBaseSize
)
1708 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1710 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1711 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1713 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1714 self
->min_size
.width
, self
->min_size
.height
,
1715 self
->max_size
.width
, self
->max_size
.height
);
1716 ob_debug("size inc (%d %d) base size (%d %d)",
1717 self
->size_inc
.width
, self
->size_inc
.height
,
1718 self
->base_size
.width
, self
->base_size
.height
);
1721 ob_debug("Normal hints: not set");
1724 void client_setup_decor_and_functions(ObClient
*self
, gboolean reconfig
)
1726 /* start with everything (cept fullscreen) */
1728 (OB_FRAME_DECOR_TITLEBAR
|
1729 OB_FRAME_DECOR_HANDLE
|
1730 OB_FRAME_DECOR_GRIPS
|
1731 OB_FRAME_DECOR_BORDER
|
1732 OB_FRAME_DECOR_ICON
|
1733 OB_FRAME_DECOR_ALLDESKTOPS
|
1734 OB_FRAME_DECOR_ICONIFY
|
1735 OB_FRAME_DECOR_MAXIMIZE
|
1736 OB_FRAME_DECOR_SHADE
|
1737 OB_FRAME_DECOR_CLOSE
);
1739 (OB_CLIENT_FUNC_RESIZE
|
1740 OB_CLIENT_FUNC_MOVE
|
1741 OB_CLIENT_FUNC_ICONIFY
|
1742 OB_CLIENT_FUNC_MAXIMIZE
|
1743 OB_CLIENT_FUNC_SHADE
|
1744 OB_CLIENT_FUNC_CLOSE
|
1745 OB_CLIENT_FUNC_BELOW
|
1746 OB_CLIENT_FUNC_ABOVE
|
1747 OB_CLIENT_FUNC_UNDECORATE
);
1749 if (!(self
->min_size
.width
< self
->max_size
.width
||
1750 self
->min_size
.height
< self
->max_size
.height
))
1751 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1753 switch (self
->type
) {
1754 case OB_CLIENT_TYPE_NORMAL
:
1755 /* normal windows retain all of the possible decorations and
1756 functionality, and can be fullscreen */
1757 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1760 case OB_CLIENT_TYPE_DIALOG
:
1761 /* sometimes apps make dialog windows fullscreen for some reason (for
1762 e.g. kpdf does this..) */
1763 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1766 case OB_CLIENT_TYPE_UTILITY
:
1767 /* these windows don't have anything added or removed by default */
1770 case OB_CLIENT_TYPE_MENU
:
1771 case OB_CLIENT_TYPE_TOOLBAR
:
1772 /* these windows can't iconify or maximize */
1773 self
->decorations
&= ~(OB_FRAME_DECOR_ICONIFY
|
1774 OB_FRAME_DECOR_MAXIMIZE
);
1775 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
|
1776 OB_CLIENT_FUNC_MAXIMIZE
);
1779 case OB_CLIENT_TYPE_SPLASH
:
1780 /* these don't get get any decorations, and the only thing you can
1781 do with them is move them */
1782 self
->decorations
= 0;
1783 self
->functions
= OB_CLIENT_FUNC_MOVE
;
1786 case OB_CLIENT_TYPE_DESKTOP
:
1787 /* these windows are not manipulated by the window manager */
1788 self
->decorations
= 0;
1789 self
->functions
= 0;
1792 case OB_CLIENT_TYPE_DOCK
:
1793 /* these windows are not manipulated by the window manager, but they
1794 can set below layer which has a special meaning */
1795 self
->decorations
= 0;
1796 self
->functions
= OB_CLIENT_FUNC_BELOW
;
1800 /* If the client has no decor from its type (which never changes) then
1801 don't allow the user to "undecorate" the window. Otherwise, allow them
1802 to, even if there are motif hints removing the decor, because those
1803 may change these days (e.g. chromium) */
1804 if (self
->decorations
== 0)
1805 self
->functions
&= ~OB_CLIENT_FUNC_UNDECORATE
;
1807 /* Mwm Hints are applied subtractively to what has already been chosen for
1808 decor and functionality */
1809 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1810 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1811 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1812 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1814 /* if the mwm hints request no handle or title, then all
1815 decorations are disabled, but keep the border if that's
1817 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1818 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1820 self
->decorations
= 0;
1825 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1826 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1827 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1828 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1829 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1830 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1831 /* dont let mwm hints kill any buttons
1832 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1833 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1834 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1835 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1837 /* dont let mwm hints kill the close button
1838 if (! (self->mwmhints.functions & MwmFunc_Close))
1839 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1843 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1844 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1845 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1846 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1847 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1848 self
->decorations
&= ~(OB_FRAME_DECOR_GRIPS
| OB_FRAME_DECOR_HANDLE
);
1850 /* can't maximize without moving/resizing */
1851 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1852 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1853 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1854 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1855 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1858 if (self
->max_horz
&& self
->max_vert
) {
1859 /* once upon a time you couldn't resize maximized windows, that is not
1860 the case any more though !
1862 but do kill the handle on fully maxed windows */
1863 self
->decorations
&= ~(OB_FRAME_DECOR_HANDLE
| OB_FRAME_DECOR_GRIPS
);
1866 /* finally, the user can have requested no decorations, which overrides
1867 everything (but doesnt give it a border if it doesnt have one) */
1868 if (self
->undecorated
)
1869 self
->decorations
&= (config_theme_keepborder
?
1870 OB_FRAME_DECOR_BORDER
: 0);
1872 /* if we don't have a titlebar, then we cannot shade! */
1873 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1874 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1876 /* now we need to check against rules for the client's current state */
1877 if (self
->fullscreen
) {
1878 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1879 OB_CLIENT_FUNC_FULLSCREEN
|
1880 OB_CLIENT_FUNC_ICONIFY
);
1881 self
->decorations
= 0;
1884 client_change_allowed_actions(self
);
1887 /* reconfigure to make sure decorations are updated */
1888 client_reconfigure(self
, FALSE
);
1891 static void client_change_allowed_actions(ObClient
*self
)
1896 /* desktop windows are kept on all desktops */
1897 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1898 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP
);
1900 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1901 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE
);
1902 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1903 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE
);
1904 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1905 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE
);
1906 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1907 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE
);
1908 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1909 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE
);
1910 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1911 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN
);
1912 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1913 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ
);
1914 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT
);
1916 if (self
->functions
& OB_CLIENT_FUNC_ABOVE
)
1917 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE
);
1918 if (self
->functions
& OB_CLIENT_FUNC_BELOW
)
1919 actions
[num
++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW
);
1920 if (self
->functions
& OB_CLIENT_FUNC_UNDECORATE
)
1921 actions
[num
++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE
);
1923 OBT_PROP_SETA32(self
->window
, NET_WM_ALLOWED_ACTIONS
, ATOM
, actions
, num
);
1925 /* make sure the window isn't breaking any rules now
1927 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1928 it can't be iconified with its parent
1931 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1932 if (self
->frame
) client_shade(self
, FALSE
);
1933 else self
->shaded
= FALSE
;
1935 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1936 if (self
->frame
) client_fullscreen(self
, FALSE
);
1937 else self
->fullscreen
= FALSE
;
1939 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1941 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1942 else self
->max_vert
= self
->max_horz
= FALSE
;
1946 void client_update_wmhints(ObClient
*self
)
1950 /* assume a window takes input if it doesn't specify */
1951 self
->can_focus
= TRUE
;
1953 if ((hints
= XGetWMHints(obt_display
, self
->window
)) != NULL
) {
1956 if (hints
->flags
& InputHint
)
1957 self
->can_focus
= hints
->input
;
1959 /* only do this when first managing the window *AND* when we aren't
1961 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1962 if (hints
->flags
& StateHint
)
1963 self
->iconic
= hints
->initial_state
== IconicState
;
1966 self
->urgent
= (hints
->flags
& XUrgencyHint
);
1967 if (self
->urgent
&& !ur
)
1968 client_hilite(self
, TRUE
);
1969 else if (!self
->urgent
&& ur
&& self
->demands_attention
)
1970 client_hilite(self
, FALSE
);
1972 if (!(hints
->flags
& WindowGroupHint
))
1973 hints
->window_group
= None
;
1975 /* did the group state change? */
1976 if (hints
->window_group
!=
1977 (self
->group
? self
->group
->leader
: None
))
1979 ObGroup
*oldgroup
= self
->group
;
1981 /* remove from the old group if there was one */
1983 group_remove(self
->group
, self
);
1987 /* add ourself to the group if we have one */
1988 if (hints
->window_group
!= None
) {
1989 self
->group
= group_add(hints
->window_group
, self
);
1992 /* Put ourselves into the new group's transient tree, and remove
1993 ourselves from the old group's */
1994 client_update_transient_tree(self
, oldgroup
, self
->group
,
1995 self
->transient_for_group
,
1996 self
->transient_for_group
,
1997 client_direct_parent(self
),
1998 client_direct_parent(self
));
2000 /* Lastly, being in a group, or not, can change if the window is
2001 transient for anything.
2003 The logic for this is:
2004 self->transient = TRUE always if the window wants to be
2005 transient for something, even if transient_for was NULL because
2006 it wasn't in a group before.
2008 If parents was NULL and oldgroup was NULL we can assume
2009 that when we add the new group, it will become transient for
2012 If transient_for_group is TRUE, then it must have already
2013 had a group. If it is getting a new group, the above call to
2014 client_update_transient_tree has already taken care of
2015 everything ! If it is losing all group status then it will
2016 no longer be transient for anything and that needs to be
2019 if (self
->transient
&&
2020 ((self
->parents
== NULL
&& oldgroup
== NULL
) ||
2021 (self
->transient_for_group
&& !self
->group
)))
2022 client_update_transient_for(self
);
2025 /* the WM_HINTS can contain an icon */
2026 if (hints
->flags
& IconPixmapHint
)
2027 client_update_icons(self
);
2032 focus_cycle_addremove(self
, TRUE
);
2035 void client_update_title(ObClient
*self
)
2038 gchar
*visible
= NULL
;
2040 g_free(self
->title
);
2041 g_free(self
->original_title
);
2044 if (!OBT_PROP_GETS_UTF8(self
->window
, NET_WM_NAME
, &data
)) {
2045 /* try old x stuff */
2046 if (!OBT_PROP_GETS(self
->window
, WM_NAME
, &data
)) {
2047 if (self
->transient
) {
2049 GNOME alert windows are not given titles:
2050 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
2052 data
= g_strdup("");
2054 data
= g_strdup(_("Unnamed Window"));
2057 self
->original_title
= g_strdup(data
);
2059 if (self
->client_machine
) {
2060 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
2065 if (self
->not_responding
) {
2067 if (self
->kill_level
> 0)
2068 visible
= g_strdup_printf("%s - [%s]", data
, _("Killing..."));
2070 visible
= g_strdup_printf("%s - [%s]", data
, _("Not Responding"));
2074 OBT_PROP_SETS(self
->window
, NET_WM_VISIBLE_NAME
, visible
);
2075 self
->title
= visible
;
2078 frame_adjust_title(self
->frame
);
2080 /* update the icon title */
2082 g_free(self
->icon_title
);
2085 if (!OBT_PROP_GETS_UTF8(self
->window
, NET_WM_ICON_NAME
, &data
))
2086 /* try old x stuff */
2087 if (!OBT_PROP_GETS(self
->window
, WM_ICON_NAME
, &data
))
2088 data
= g_strdup(self
->title
);
2090 if (self
->client_machine
) {
2091 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
2096 if (self
->not_responding
) {
2098 if (self
->kill_level
> 0)
2099 visible
= g_strdup_printf("%s - [%s]", data
, _("Killing..."));
2101 visible
= g_strdup_printf("%s - [%s]", data
, _("Not Responding"));
2105 OBT_PROP_SETS(self
->window
, NET_WM_VISIBLE_ICON_NAME
, visible
);
2106 self
->icon_title
= visible
;
2109 void client_update_strut(ObClient
*self
)
2113 gboolean got
= FALSE
;
2116 if (OBT_PROP_GETA32(self
->window
, NET_WM_STRUT_PARTIAL
, CARDINAL
,
2121 STRUT_PARTIAL_SET(strut
,
2122 data
[0], data
[2], data
[1], data
[3],
2123 data
[4], data
[5], data
[8], data
[9],
2124 data
[6], data
[7], data
[10], data
[11]);
2130 OBT_PROP_GETA32(self
->window
, NET_WM_STRUT
, CARDINAL
, &data
, &num
)) {
2136 /* use the screen's width/height */
2137 a
= screen_physical_area_all_monitors();
2139 STRUT_PARTIAL_SET(strut
,
2140 data
[0], data
[2], data
[1], data
[3],
2141 a
->y
, a
->y
+ a
->height
- 1,
2142 a
->x
, a
->x
+ a
->width
- 1,
2143 a
->y
, a
->y
+ a
->height
- 1,
2144 a
->x
, a
->x
+ a
->width
- 1);
2150 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
2151 0, 0, 0, 0, 0, 0, 0, 0);
2153 if (!PARTIAL_STRUT_EQUAL(strut
, self
->strut
)) {
2154 self
->strut
= strut
;
2156 /* updating here is pointless while we're being mapped cuz we're not in
2157 the client list yet */
2159 screen_update_areas();
2163 void client_update_icons(ObClient
*self
)
2172 /* grab the server, because we might be setting the window's icon and
2173 we don't want them to set it in between and we overwrite their own
2177 if (OBT_PROP_GETA32(self
->window
, NET_WM_ICON
, CARDINAL
, &data
, &num
)) {
2178 /* figure out how many valid icons are in here */
2180 while (i
+ 2 < num
) { /* +2 is to make sure there is a w and h */
2183 /* watch for the data being too small for the specified size,
2184 or for zero sized icons. */
2185 if (i
+ w
*h
> num
|| w
== 0 || h
== 0) {
2190 /* convert it to the right bit order for ObRender */
2191 for (j
= 0; j
< w
*h
; ++j
)
2193 (((data
[i
+j
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
2194 (((data
[i
+j
] >> 16) & 0xff) << RrDefaultRedOffset
) +
2195 (((data
[i
+j
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
2196 (((data
[i
+j
] >> 0) & 0xff) << RrDefaultBlueOffset
);
2198 /* add it to the image cache as an original */
2200 img
= RrImageNewFromData(ob_rr_icons
, &data
[i
], w
, h
);
2202 RrImageAddFromData(img
, &data
[i
], w
, h
);
2210 /* if we didn't find an image from the NET_WM_ICON stuff, then try the
2215 if ((hints
= XGetWMHints(obt_display
, self
->window
))) {
2216 if (hints
->flags
& IconPixmapHint
) {
2218 obt_display_ignore_errors(TRUE
);
2219 xicon
= RrPixmapToRGBA(ob_rr_inst
,
2221 (hints
->flags
& IconMaskHint
?
2222 hints
->icon_mask
: None
),
2223 (gint
*)&w
, (gint
*)&h
, &data
);
2224 obt_display_ignore_errors(FALSE
);
2227 if (w
> 0 && h
> 0) {
2229 img
= RrImageNewFromData(ob_rr_icons
, data
, w
, h
);
2231 RrImageAddFromData(img
, data
, w
, h
);
2241 /* set the client's icons to be whatever we found */
2242 RrImageUnref(self
->icon_set
);
2243 self
->icon_set
= img
;
2245 /* if the client has no icon at all, then we set a default icon onto it.
2246 but, if it has parents, then one of them will have an icon already
2248 if (!self
->icon_set
&& !self
->parents
) {
2249 RrPixel32
*icon
= ob_rr_theme
->def_win_icon
;
2250 gulong
*ldata
; /* use a long here to satisfy OBT_PROP_SETA32 */
2252 w
= ob_rr_theme
->def_win_icon_w
;
2253 h
= ob_rr_theme
->def_win_icon_h
;
2254 ldata
= g_new(gulong
, w
*h
+2);
2257 for (i
= 0; i
< w
*h
; ++i
)
2258 ldata
[i
+2] = (((icon
[i
] >> RrDefaultAlphaOffset
) & 0xff) << 24) +
2259 (((icon
[i
] >> RrDefaultRedOffset
) & 0xff) << 16) +
2260 (((icon
[i
] >> RrDefaultGreenOffset
) & 0xff) << 8) +
2261 (((icon
[i
] >> RrDefaultBlueOffset
) & 0xff) << 0);
2262 OBT_PROP_SETA32(self
->window
, NET_WM_ICON
, CARDINAL
, ldata
, w
*h
+2);
2264 } else if (self
->frame
)
2265 /* don't draw the icon empty if we're just setting one now anyways,
2266 we'll get the property change any second */
2267 frame_adjust_icon(self
->frame
);
2272 void client_update_icon_geometry(ObClient
*self
)
2277 RECT_SET(self
->icon_geometry
, 0, 0, 0, 0);
2279 if (OBT_PROP_GETA32(self
->window
, NET_WM_ICON_GEOMETRY
, CARDINAL
,
2283 /* don't let them set it with an area < 0 */
2284 RECT_SET(self
->icon_geometry
, data
[0], data
[1],
2285 MAX(data
[2],0), MAX(data
[3],0));
2290 static void client_get_session_ids(ObClient
*self
)
2297 if (!OBT_PROP_GET32(self
->window
, WM_CLIENT_LEADER
, WINDOW
, &leader
))
2300 /* get the SM_CLIENT_ID */
2301 if (leader
&& leader
!= self
->window
)
2302 OBT_PROP_GETS_XPCS(leader
, SM_CLIENT_ID
, &self
->sm_client_id
);
2304 OBT_PROP_GETS_XPCS(self
->window
, SM_CLIENT_ID
, &self
->sm_client_id
);
2306 /* get the WM_CLASS (name and class). make them "" if they are not
2308 got
= OBT_PROP_GETSS_TYPE(self
->window
, WM_CLASS
, STRING_NO_CC
, &ss
);
2312 self
->name
= g_strdup(ss
[0]);
2314 self
->class = g_strdup(ss
[1]);
2319 if (self
->name
== NULL
) self
->name
= g_strdup("");
2320 if (self
->class == NULL
) self
->class = g_strdup("");
2322 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2323 got
= OBT_PROP_GETS_XPCS(self
->window
, WM_WINDOW_ROLE
, &s
);
2328 self
->role
= g_strdup("");
2330 /* get the WM_COMMAND */
2334 got
= OBT_PROP_GETSS(leader
, WM_COMMAND
, &ss
);
2336 got
= OBT_PROP_GETSS(self
->window
, WM_COMMAND
, &ss
);
2339 /* merge/mash them all together */
2340 gchar
*merge
= NULL
;
2343 for (i
= 0; ss
[i
]; ++i
) {
2346 merge
= g_strconcat(merge
, ss
[i
], NULL
);
2348 merge
= g_strconcat(ss
[i
], NULL
);
2353 self
->wm_command
= merge
;
2356 /* get the WM_CLIENT_MACHINE */
2359 got
= OBT_PROP_GETS(leader
, WM_CLIENT_MACHINE
, &s
);
2361 got
= OBT_PROP_GETS(self
->window
, WM_CLIENT_MACHINE
, &s
);
2364 gchar localhost
[128];
2367 gethostname(localhost
, 127);
2368 localhost
[127] = '\0';
2369 if (strcmp(localhost
, s
) != 0)
2370 self
->client_machine
= s
;
2374 /* see if it has the PID set too (the PID requires that the
2375 WM_CLIENT_MACHINE be set) */
2376 if (OBT_PROP_GET32(self
->window
, NET_WM_PID
, CARDINAL
, &pid
))
2381 /*! Save the properties used for app matching rules, as seen by Openbox when
2382 the window mapped, so that users can still access them later if the app
2384 static void client_save_app_rule_values(ObClient
*self
)
2388 OBT_PROP_SETS(self
->window
, OB_APP_ROLE
, self
->role
);
2389 OBT_PROP_SETS(self
->window
, OB_APP_NAME
, self
->name
);
2390 OBT_PROP_SETS(self
->window
, OB_APP_CLASS
, self
->class);
2391 OBT_PROP_SETS(self
->window
, OB_APP_TITLE
, self
->original_title
);
2393 switch (self
->type
) {
2394 case OB_CLIENT_TYPE_NORMAL
:
2395 type
= "normal"; break;
2396 case OB_CLIENT_TYPE_DIALOG
:
2397 type
= "dialog"; break;
2398 case OB_CLIENT_TYPE_UTILITY
:
2399 type
= "utility"; break;
2400 case OB_CLIENT_TYPE_MENU
:
2401 type
= "menu"; break;
2402 case OB_CLIENT_TYPE_TOOLBAR
:
2403 type
= "toolbar"; break;
2404 case OB_CLIENT_TYPE_SPLASH
:
2405 type
= "splash"; break;
2406 case OB_CLIENT_TYPE_DESKTOP
:
2407 type
= "desktop"; break;
2408 case OB_CLIENT_TYPE_DOCK
:
2409 type
= "dock"; break;
2411 OBT_PROP_SETS(self
->window
, OB_APP_TYPE
, type
);
2414 static void client_change_wm_state(ObClient
*self
)
2419 old
= self
->wmstate
;
2421 if (self
->shaded
|| self
->iconic
||
2422 (self
->desktop
!= DESKTOP_ALL
&& self
->desktop
!= screen_desktop
))
2424 self
->wmstate
= IconicState
;
2426 self
->wmstate
= NormalState
;
2428 if (old
!= self
->wmstate
) {
2429 OBT_PROP_MSG(ob_screen
, self
->window
, KDE_WM_CHANGE_STATE
,
2430 self
->wmstate
, 1, 0, 0, 0);
2432 state
[0] = self
->wmstate
;
2434 OBT_PROP_SETA32(self
->window
, WM_STATE
, WM_STATE
, state
, 2);
2438 static void client_change_state(ObClient
*self
)
2440 gulong netstate
[12];
2445 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL
);
2447 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED
);
2449 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
);
2450 if (self
->skip_taskbar
)
2451 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
);
2452 if (self
->skip_pager
)
2453 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
);
2454 if (self
->fullscreen
)
2455 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
);
2457 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
);
2459 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
);
2461 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE
);
2463 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW
);
2464 if (self
->demands_attention
)
2465 netstate
[num
++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
);
2466 if (self
->undecorated
)
2467 netstate
[num
++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
);
2468 OBT_PROP_SETA32(self
->window
, NET_WM_STATE
, ATOM
, netstate
, num
);
2471 frame_adjust_state(self
->frame
);
2474 ObClient
*client_search_focus_tree(ObClient
*self
)
2479 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2480 if (client_focused(it
->data
)) return it
->data
;
2481 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
2486 ObClient
*client_search_focus_tree_full(ObClient
*self
)
2488 if (self
->parents
) {
2491 for (it
= self
->parents
; it
; it
= g_slist_next(it
)) {
2492 ObClient
*c
= it
->data
;
2493 if ((c
= client_search_focus_tree_full(c
))) return c
;
2499 /* this function checks the whole tree, the client_search_focus_tree
2500 does not, so we need to check this window */
2501 if (client_focused(self
))
2503 return client_search_focus_tree(self
);
2507 ObClient
*client_search_focus_group_full(ObClient
*self
)
2512 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
2513 ObClient
*c
= it
->data
;
2515 if (client_focused(c
)) return c
;
2516 if ((c
= client_search_focus_tree(it
->data
))) return c
;
2519 if (client_focused(self
)) return self
;
2523 gboolean
client_has_parent(ObClient
*self
)
2525 return self
->parents
!= NULL
;
2528 gboolean
client_has_children(ObClient
*self
)
2530 return self
->transients
!= NULL
;
2533 gboolean
client_is_oldfullscreen(const ObClient
*self
,
2536 const Rect
*monitor
, *allmonitors
;
2538 /* No decorations and fills the monitor = oldskool fullscreen.
2539 But not for maximized windows.
2542 if (self
->decorations
|| self
->max_horz
|| self
->max_vert
) return FALSE
;
2544 monitor
= screen_physical_area_monitor(screen_find_monitor(area
));
2545 allmonitors
= screen_physical_area_all_monitors();
2547 return (RECT_EQUAL(*area
, *monitor
) ||
2548 RECT_EQUAL(*area
, *allmonitors
));
2551 static ObStackingLayer
calc_layer(ObClient
*self
)
2555 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2556 l
= OB_STACKING_LAYER_DESKTOP
;
2557 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2558 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2559 else l
= OB_STACKING_LAYER_ABOVE
;
2561 else if ((self
->fullscreen
||
2562 client_is_oldfullscreen(self
, &self
->area
)) &&
2563 /* you are fullscreen while you or your children are focused.. */
2564 (client_focused(self
) || client_search_focus_tree(self
) ||
2565 /* you can be fullscreen if you're on another desktop */
2566 (self
->desktop
!= screen_desktop
&&
2567 self
->desktop
!= DESKTOP_ALL
) ||
2568 /* and you can also be fullscreen if the focused client is on
2569 another monitor, or nothing else is focused */
2571 client_monitor(focus_client
) != client_monitor(self
))))
2572 l
= OB_STACKING_LAYER_FULLSCREEN
;
2573 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2574 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2575 else l
= OB_STACKING_LAYER_NORMAL
;
2580 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2581 ObStackingLayer min
)
2583 ObStackingLayer old
, own
;
2587 own
= calc_layer(self
);
2588 self
->layer
= MAX(own
, min
);
2590 if (self
->layer
!= old
) {
2591 stacking_remove(CLIENT_AS_WINDOW(self
));
2592 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
2595 /* we've been restacked */
2596 self
->visited
= TRUE
;
2598 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2599 client_calc_layer_recursive(it
->data
, orig
,
2603 static void client_calc_layer_internal(ObClient
*self
)
2607 /* transients take on the layer of their parents */
2608 sit
= client_search_all_top_parents(self
);
2610 for (; sit
; sit
= g_slist_next(sit
))
2611 client_calc_layer_recursive(sit
->data
, self
, 0);
2614 void client_calc_layer(ObClient
*self
)
2618 /* skip over stuff above fullscreen layer */
2619 for (it
= stacking_list
; it
; it
= g_list_next(it
))
2620 if (window_layer(it
->data
) <= OB_STACKING_LAYER_FULLSCREEN
) break;
2622 /* find the windows in the fullscreen layer, and mark them not-visited */
2623 for (; it
; it
= g_list_next(it
)) {
2624 if (window_layer(it
->data
) < OB_STACKING_LAYER_FULLSCREEN
) break;
2625 else if (WINDOW_IS_CLIENT(it
->data
))
2626 WINDOW_AS_CLIENT(it
->data
)->visited
= FALSE
;
2629 client_calc_layer_internal(self
);
2631 /* skip over stuff above fullscreen layer */
2632 for (it
= stacking_list
; it
; it
= g_list_next(it
))
2633 if (window_layer(it
->data
) <= OB_STACKING_LAYER_FULLSCREEN
) break;
2635 /* now recalc any windows in the fullscreen layer which have not
2636 had their layer recalced already */
2637 for (; it
; it
= g_list_next(it
)) {
2638 if (window_layer(it
->data
) < OB_STACKING_LAYER_FULLSCREEN
) break;
2639 else if (WINDOW_IS_CLIENT(it
->data
) &&
2640 !WINDOW_AS_CLIENT(it
->data
)->visited
)
2641 client_calc_layer_internal(it
->data
);
2645 gboolean
client_should_show(ObClient
*self
)
2649 if (client_normal(self
) && screen_showing_desktop
)
2651 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2657 gboolean
client_show(ObClient
*self
)
2659 gboolean show
= FALSE
;
2661 if (client_should_show(self
)) {
2662 /* replay pending pointer event before showing the window, in case it
2663 should be going to something under the window */
2664 mouse_replay_pointer();
2666 frame_show(self
->frame
);
2669 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2670 it needs to be in IconicState. This includes when it is on another
2673 client_change_wm_state(self
);
2678 gboolean
client_hide(ObClient
*self
)
2680 gboolean hide
= FALSE
;
2682 if (!client_should_show(self
)) {
2683 /* We don't need to ignore enter events here.
2684 The window can hide/iconify in 3 different ways:
2685 1 - through an x message. in this case we ignore all enter events
2686 caused by responding to the x message (unless underMouse)
2687 2 - by a keyboard action. in this case we ignore all enter events
2688 caused by the action
2689 3 - by a mouse action. in this case they are doing stuff with the
2690 mouse and focus _should_ move.
2692 Also in action_end, we simulate an enter event that can't be ignored
2693 so trying to ignore them is futile in case 3 anyways
2696 /* replay pending pointer event before hiding the window, in case it
2697 should be going to the window */
2698 mouse_replay_pointer();
2700 frame_hide(self
->frame
);
2703 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2704 it needs to be in IconicState. This includes when it is on another
2707 client_change_wm_state(self
);
2712 void client_showhide(ObClient
*self
)
2714 if (!client_show(self
))
2718 gboolean
client_normal(ObClient
*self
) {
2719 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2720 self
->type
== OB_CLIENT_TYPE_DOCK
||
2721 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2724 gboolean
client_helper(ObClient
*self
)
2726 return (self
->type
== OB_CLIENT_TYPE_UTILITY
||
2727 self
->type
== OB_CLIENT_TYPE_MENU
||
2728 self
->type
== OB_CLIENT_TYPE_TOOLBAR
);
2731 gboolean
client_mouse_focusable(ObClient
*self
)
2733 return !(self
->type
== OB_CLIENT_TYPE_MENU
||
2734 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
2735 self
->type
== OB_CLIENT_TYPE_SPLASH
||
2736 self
->type
== OB_CLIENT_TYPE_DOCK
);
2739 gboolean
client_enter_focusable(ObClient
*self
)
2741 /* you can focus desktops but it shouldn't on enter */
2742 return (client_mouse_focusable(self
) &&
2743 self
->type
!= OB_CLIENT_TYPE_DESKTOP
);
2746 static void client_apply_startup_state(ObClient
*self
,
2747 gint x
, gint y
, gint w
, gint h
)
2749 /* save the states that we are going to apply */
2750 gboolean iconic
= self
->iconic
;
2751 gboolean fullscreen
= self
->fullscreen
;
2752 gboolean undecorated
= self
->undecorated
;
2753 gboolean shaded
= self
->shaded
;
2754 gboolean demands_attention
= self
->demands_attention
;
2755 gboolean max_horz
= self
->max_horz
;
2756 gboolean max_vert
= self
->max_vert
;
2760 /* turn them all off in the client, so they won't affect the window
2762 self
->iconic
= self
->fullscreen
= self
->undecorated
= self
->shaded
=
2763 self
->demands_attention
= self
->max_horz
= self
->max_vert
= FALSE
;
2765 /* move the client to its placed position, or it it's already there,
2766 generate a ConfigureNotify telling the client where it is.
2768 do this after adjusting the frame. otherwise it gets all weird and
2769 clients don't work right
2771 do this before applying the states so they have the correct
2772 pre-max/pre-fullscreen values
2774 client_try_configure(self
, &x
, &y
, &w
, &h
, &l
, &l
, FALSE
);
2775 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2776 self
->window
, x
, y
, w
, h
);
2777 /* save the area, and make it where it should be for the premax stuff */
2778 oldarea
= self
->area
;
2779 RECT_SET(self
->area
, x
, y
, w
, h
);
2781 /* apply the states. these are in a carefully crafted order.. */
2784 client_iconify(self
, TRUE
, FALSE
, TRUE
);
2786 client_set_undecorated(self
, TRUE
);
2788 client_shade(self
, TRUE
);
2789 if (demands_attention
)
2790 client_hilite(self
, TRUE
);
2792 if (max_vert
&& max_horz
)
2793 client_maximize(self
, TRUE
, 0);
2795 client_maximize(self
, TRUE
, 2);
2797 client_maximize(self
, TRUE
, 1);
2799 /* fullscreen removes the ability to apply other states */
2801 client_fullscreen(self
, TRUE
);
2803 /* if the window hasn't been configured yet, then do so now, in fact the
2804 x,y,w,h may _not_ be the same as the area rect, which can end up
2805 meaning that the client isn't properly moved/resized by the fullscreen
2807 pho can cause this because it maps at size of the screen but not 0,0
2808 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2809 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2810 cuz thats where the pre-fullscreen will be. however the actual area is
2811 not, so this needs to be called even if we have fullscreened/maxed
2813 self
->area
= oldarea
;
2814 client_configure(self
, x
, y
, w
, h
, FALSE
, TRUE
, FALSE
);
2816 /* nothing to do for the other states:
2825 void client_gravity_resize_w(ObClient
*self
, gint
*x
, gint oldw
, gint neww
)
2827 /* these should be the current values. this is for when you're not moving,
2829 g_assert(*x
== self
->area
.x
);
2830 g_assert(oldw
== self
->area
.width
);
2833 switch (self
->gravity
) {
2835 case NorthWestGravity
:
2837 case SouthWestGravity
:
2844 *x
-= (neww
- oldw
) / 2;
2846 case NorthEastGravity
:
2848 case SouthEastGravity
:
2854 void client_gravity_resize_h(ObClient
*self
, gint
*y
, gint oldh
, gint newh
)
2856 /* these should be the current values. this is for when you're not moving,
2858 g_assert(*y
== self
->area
.y
);
2859 g_assert(oldh
== self
->area
.height
);
2862 switch (self
->gravity
) {
2864 case NorthWestGravity
:
2866 case NorthEastGravity
:
2873 *y
-= (newh
- oldh
) / 2;
2875 case SouthWestGravity
:
2877 case SouthEastGravity
:
2883 void client_try_configure(ObClient
*self
, gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2884 gint
*logicalw
, gint
*logicalh
,
2887 Rect desired
= {*x
, *y
, *w
, *h
};
2888 frame_rect_to_frame(self
->frame
, &desired
);
2890 /* make the frame recalculate its dimensions n shit without changing
2891 anything visible for real, this way the constraints below can work with
2892 the updated frame dimensions. */
2893 frame_adjust_area(self
->frame
, FALSE
, TRUE
, TRUE
);
2895 /* cap any X windows at the size of an unsigned short */
2897 G_MAXUSHORT
- self
->frame
->size
.left
- self
->frame
->size
.right
);
2899 G_MAXUSHORT
- self
->frame
->size
.top
- self
->frame
->size
.bottom
);
2902 /* gets the frame's position */
2903 frame_client_gravity(self
->frame
, x
, y
);
2905 /* these positions are frame positions, not client positions */
2907 /* set the size and position if fullscreen */
2908 if (self
->fullscreen
) {
2912 i
= screen_find_monitor(&desired
);
2913 a
= screen_physical_area_monitor(i
);
2920 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2922 } else if (self
->max_horz
|| self
->max_vert
) {
2926 /* use all possible struts when maximizing to the full screen */
2927 i
= screen_find_monitor(&desired
);
2928 a
= screen_area(self
->desktop
, i
,
2929 (self
->max_horz
&& self
->max_vert
? NULL
: &desired
));
2931 /* set the size and position if maximized */
2932 if (self
->max_horz
) {
2934 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2936 if (self
->max_vert
) {
2938 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2941 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2944 g_slice_free(Rect
, a
);
2947 /* gets the client's position */
2948 frame_frame_gravity(self
->frame
, x
, y
);
2950 /* work within the preferred sizes given by the window, these may have
2951 changed rather than it's requested width and height, so always run
2952 through this code */
2954 gint basew
, baseh
, minw
, minh
;
2955 gint incw
, inch
, maxw
, maxh
;
2956 gfloat minratio
, maxratio
;
2958 incw
= self
->size_inc
.width
;
2959 inch
= self
->size_inc
.height
;
2960 minratio
= self
->fullscreen
|| (self
->max_horz
&& self
->max_vert
) ?
2961 0 : self
->min_ratio
;
2962 maxratio
= self
->fullscreen
|| (self
->max_horz
&& self
->max_vert
) ?
2963 0 : self
->max_ratio
;
2965 /* base size is substituted with min size if not specified */
2966 if (self
->base_size
.width
>= 0 || self
->base_size
.height
>= 0) {
2967 basew
= self
->base_size
.width
;
2968 baseh
= self
->base_size
.height
;
2970 basew
= self
->min_size
.width
;
2971 baseh
= self
->min_size
.height
;
2973 /* min size is substituted with base size if not specified */
2974 if (self
->min_size
.width
|| self
->min_size
.height
) {
2975 minw
= self
->min_size
.width
;
2976 minh
= self
->min_size
.height
;
2978 minw
= self
->base_size
.width
;
2979 minh
= self
->base_size
.height
;
2982 /* This comment is no longer true */
2983 /* if this is a user-requested resize, then check against min/max
2986 /* smaller than min size or bigger than max size? */
2987 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2988 if (*w
< minw
) *w
= minw
;
2989 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2990 if (*h
< minh
) *h
= minh
;
2995 /* the sizes to used for maximized */
2999 /* keep to the increments */
3003 /* you cannot resize to nothing */
3004 if (basew
+ *w
< 1) *w
= 1 - basew
;
3005 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
3007 /* save the logical size */
3008 *logicalw
= incw
> 1 ? *w
: *w
+ basew
;
3009 *logicalh
= inch
> 1 ? *h
: *h
+ baseh
;
3014 /* if maximized/fs then don't use the size increments */
3015 if (self
->fullscreen
|| self
->max_horz
) *w
= maxw
;
3016 if (self
->fullscreen
|| self
->max_vert
) *h
= maxh
;
3021 /* adjust the height to match the width for the aspect ratios.
3022 for this, min size is not substituted for base size ever. */
3023 *w
-= self
->base_size
.width
;
3024 *h
-= self
->base_size
.height
;
3027 if (*h
* minratio
> *w
) {
3028 *h
= (gint
)(*w
/ minratio
);
3030 /* you cannot resize to nothing */
3033 *w
= (gint
)(*h
* minratio
);
3037 if (*h
* maxratio
< *w
) {
3038 *h
= (gint
)(*w
/ maxratio
);
3040 /* you cannot resize to nothing */
3043 *w
= (gint
)(*h
* minratio
);
3047 *w
+= self
->base_size
.width
;
3048 *h
+= self
->base_size
.height
;
3051 /* these override the above states! if you cant move you can't move! */
3053 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
3057 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
3058 *w
= self
->area
.width
;
3059 *h
= self
->area
.height
;
3067 void client_configure(ObClient
*self
, gint x
, gint y
, gint w
, gint h
,
3068 gboolean user
, gboolean final
, gboolean force_reply
)
3070 Rect oldframe
, oldclient
;
3071 gboolean send_resize_client
;
3072 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
3073 gboolean fmoved
, fresized
;
3074 guint fdecor
= self
->frame
->decorations
;
3075 gboolean fhorz
= self
->frame
->max_horz
;
3076 gboolean fvert
= self
->frame
->max_vert
;
3077 gint logicalw
, logicalh
;
3079 /* find the new x, y, width, and height (and logical size) */
3080 client_try_configure(self
, &x
, &y
, &w
, &h
, &logicalw
, &logicalh
, user
);
3082 /* set the logical size if things changed */
3083 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
3084 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
3086 /* figure out if we moved or resized or what */
3087 moved
= (x
!= self
->area
.x
|| y
!= self
->area
.y
);
3088 resized
= (w
!= self
->area
.width
|| h
!= self
->area
.height
);
3090 oldframe
= self
->frame
->area
;
3091 oldclient
= self
->area
;
3092 RECT_SET(self
->area
, x
, y
, w
, h
);
3094 /* for app-requested resizes, always resize if 'resized' is true.
3095 for user-requested ones, only resize if final is true, or when
3096 resizing in redraw mode */
3097 send_resize_client
= ((!user
&& resized
) ||
3099 (resized
&& config_resize_redraw
))));
3101 /* if the client is enlarging, then resize the client before the frame */
3102 if (send_resize_client
&& (w
> oldclient
.width
|| h
> oldclient
.height
)) {
3103 XMoveResizeWindow(obt_display
, self
->window
,
3104 self
->frame
->size
.left
, self
->frame
->size
.top
,
3105 MAX(w
, oldclient
.width
), MAX(h
, oldclient
.height
));
3106 frame_adjust_client_area(self
->frame
);
3109 /* find the frame's dimensions and move/resize it */
3113 /* if decorations changed, then readjust everything for the frame */
3114 if (self
->decorations
!= fdecor
||
3115 self
->max_horz
!= fhorz
|| self
->max_vert
!= fvert
)
3117 fmoved
= fresized
= TRUE
;
3120 /* adjust the frame */
3121 if (fmoved
|| fresized
) {
3122 gulong ignore_start
;
3124 ignore_start
= event_start_ignore_all_enters();
3126 /* replay pending pointer event before move the window, in case it
3127 would change what window gets the event */
3128 mouse_replay_pointer();
3130 frame_adjust_area(self
->frame
, fmoved
, fresized
, FALSE
);
3133 event_end_ignore_all_enters(ignore_start
);
3136 if (!user
|| final
) {
3137 gint oldrx
= self
->root_pos
.x
;
3138 gint oldry
= self
->root_pos
.y
;
3139 /* we have reset the client to 0 border width, so don't include
3140 it in these coords */
3141 POINT_SET(self
->root_pos
,
3142 self
->frame
->area
.x
+ self
->frame
->size
.left
-
3144 self
->frame
->area
.y
+ self
->frame
->size
.top
-
3145 self
->border_width
);
3146 if (self
->root_pos
.x
!= oldrx
|| self
->root_pos
.y
!= oldry
)
3150 /* This is kinda tricky and should not be changed.. let me explain!
3152 When user = FALSE, then the request is coming from the application
3153 itself, and we are more strict about when to send a synthetic
3154 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3155 in this case (or send one if force_reply is true)
3157 When user = TRUE, then the request is coming from "us", like when we
3158 maximize a window or something. In this case we are more lenient. We
3159 used to follow the same rules as above, but _Java_ Swing can't handle
3160 this. So just to appease Swing, when user = TRUE, we always send
3161 a synthetic ConfigureNotify to give the window its root coordinates.
3162 Lastly, if force_reply is TRUE, we always send a
3163 ConfigureNotify, which is needed during a resize with XSYNCronization.
3165 if ((!user
&& !resized
&& (rootmoved
|| force_reply
)) ||
3166 (user
&& ((!resized
&& force_reply
) || (final
&& rootmoved
))))
3170 event
.type
= ConfigureNotify
;
3171 event
.xconfigure
.display
= obt_display
;
3172 event
.xconfigure
.event
= self
->window
;
3173 event
.xconfigure
.window
= self
->window
;
3175 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3176 self
->title
, self
->root_pos
.x
, self
->root_pos
.y
, w
, h
);
3178 /* root window real coords */
3179 event
.xconfigure
.x
= self
->root_pos
.x
;
3180 event
.xconfigure
.y
= self
->root_pos
.y
;
3181 event
.xconfigure
.width
= w
;
3182 event
.xconfigure
.height
= h
;
3183 event
.xconfigure
.border_width
= self
->border_width
;
3184 event
.xconfigure
.above
= None
;
3185 event
.xconfigure
.override_redirect
= FALSE
;
3186 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
3187 FALSE
, StructureNotifyMask
, &event
);
3190 /* if the client is shrinking, then resize the frame before the client.
3192 both of these resize sections may run, because the top one only resizes
3193 in the direction that is growing
3195 if (send_resize_client
&& (w
<= oldclient
.width
|| h
<= oldclient
.height
))
3197 frame_adjust_client_area(self
->frame
);
3198 XMoveResizeWindow(obt_display
, self
->window
,
3199 self
->frame
->size
.left
, self
->frame
->size
.top
, w
, h
);
3202 XFlush(obt_display
);
3204 /* if it moved between monitors, then this can affect the stacking
3205 layer of this window or others - for fullscreen windows.
3206 also if it changed to/from oldschool fullscreen then its layer may
3209 watch out tho, don't try change stacking stuff if the window is no
3210 longer being managed !
3212 if (self
->managed
&&
3213 (screen_find_monitor(&self
->frame
->area
) !=
3214 screen_find_monitor(&oldframe
) ||
3215 (final
&& (client_is_oldfullscreen(self
, &oldclient
) !=
3216 client_is_oldfullscreen(self
, &self
->area
)))))
3218 client_calc_layer(self
);
3222 void client_fullscreen(ObClient
*self
, gboolean fs
)
3226 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
3227 self
->fullscreen
== fs
) return; /* already done */
3229 self
->fullscreen
= fs
;
3230 client_change_state(self
); /* change the state hints on the client */
3233 self
->pre_fullscreen_area
= self
->area
;
3234 self
->pre_fullscreen_max_horz
= self
->max_horz
;
3235 self
->pre_fullscreen_max_vert
= self
->max_vert
;
3237 /* if the window is maximized, its area isn't all that meaningful.
3238 save its premax area instead. */
3239 if (self
->max_horz
) {
3240 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
3241 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
3243 if (self
->max_vert
) {
3244 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
3245 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
3248 /* these will help configure_full figure out where to fullscreen
3252 w
= self
->area
.width
;
3253 h
= self
->area
.height
;
3255 g_assert(self
->pre_fullscreen_area
.width
> 0 &&
3256 self
->pre_fullscreen_area
.height
> 0);
3258 self
->max_horz
= self
->pre_fullscreen_max_horz
;
3259 self
->max_vert
= self
->pre_fullscreen_max_vert
;
3260 if (self
->max_horz
) {
3261 self
->pre_max_area
.x
= self
->pre_fullscreen_area
.x
;
3262 self
->pre_max_area
.width
= self
->pre_fullscreen_area
.width
;
3264 if (self
->max_vert
) {
3265 self
->pre_max_area
.y
= self
->pre_fullscreen_area
.y
;
3266 self
->pre_max_area
.height
= self
->pre_fullscreen_area
.height
;
3269 x
= self
->pre_fullscreen_area
.x
;
3270 y
= self
->pre_fullscreen_area
.y
;
3271 w
= self
->pre_fullscreen_area
.width
;
3272 h
= self
->pre_fullscreen_area
.height
;
3273 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
3276 ob_debug("Window %s going fullscreen (%d)",
3277 self
->title
, self
->fullscreen
);
3280 /* make sure the window is on some monitor */
3281 client_find_onscreen(self
, &x
, &y
, w
, h
, FALSE
);
3284 client_setup_decor_and_functions(self
, FALSE
);
3285 client_move_resize(self
, x
, y
, w
, h
);
3287 /* and adjust our layer/stacking. do this after resizing the window,
3288 and applying decorations, because windows which fill the screen are
3289 considered "fullscreen" and it affects their layer */
3290 client_calc_layer(self
);
3293 /* try focus us when we go into fullscreen mode */
3298 static void client_iconify_recursive(ObClient
*self
,
3299 gboolean iconic
, gboolean curdesk
,
3300 gboolean hide_animation
)
3303 gboolean changed
= FALSE
;
3305 if (self
->iconic
!= iconic
) {
3306 ob_debug("%sconifying window: 0x%lx", (iconic
? "I" : "Uni"),
3310 /* don't let non-normal windows iconify along with their parents
3312 if (client_normal(self
)) {
3313 self
->iconic
= iconic
;
3315 /* update the focus lists.. iconic windows go to the bottom of
3316 the list. this will also call focus_cycle_addremove(). */
3317 focus_order_to_bottom(self
);
3322 self
->iconic
= iconic
;
3324 if (curdesk
&& self
->desktop
!= screen_desktop
&&
3325 self
->desktop
!= DESKTOP_ALL
)
3326 client_set_desktop(self
, screen_desktop
, FALSE
, FALSE
);
3328 /* this puts it after the current focused window, this will
3329 also cause focus_cycle_addremove() to be called for the
3331 focus_order_like_new(self
);
3338 client_change_state(self
);
3339 if (config_animate_iconify
&& !hide_animation
)
3340 frame_begin_iconify_animation(self
->frame
, iconic
);
3341 /* do this after starting the animation so it doesn't flash */
3342 client_showhide(self
);
3345 /* iconify all direct transients, and deiconify all transients
3347 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3348 if (it
->data
!= self
)
3349 if (client_is_direct_child(self
, it
->data
) || !iconic
)
3350 client_iconify_recursive(it
->data
, iconic
, curdesk
,
3354 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
,
3355 gboolean hide_animation
)
3357 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
|| !iconic
) {
3358 /* move up the transient chain as far as possible first */
3359 self
= client_search_top_direct_parent(self
);
3360 client_iconify_recursive(self
, iconic
, curdesk
, hide_animation
);
3364 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
3368 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
3369 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && max
) return;/* can't */
3371 /* check if already done */
3373 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
3374 if (dir
== 1 && self
->max_horz
) return;
3375 if (dir
== 2 && self
->max_vert
) return;
3377 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
3378 if (dir
== 1 && !self
->max_horz
) return;
3379 if (dir
== 2 && !self
->max_vert
) return;
3382 /* these will help configure_full figure out which screen to fill with
3386 w
= self
->area
.width
;
3387 h
= self
->area
.height
;
3390 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
3391 RECT_SET(self
->pre_max_area
,
3392 self
->area
.x
, self
->pre_max_area
.y
,
3393 self
->area
.width
, self
->pre_max_area
.height
);
3395 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
3396 RECT_SET(self
->pre_max_area
,
3397 self
->pre_max_area
.x
, self
->area
.y
,
3398 self
->pre_max_area
.width
, self
->area
.height
);
3401 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
3402 g_assert(self
->pre_max_area
.width
> 0);
3404 x
= self
->pre_max_area
.x
;
3405 w
= self
->pre_max_area
.width
;
3407 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
3408 0, self
->pre_max_area
.height
);
3410 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
3411 g_assert(self
->pre_max_area
.height
> 0);
3413 y
= self
->pre_max_area
.y
;
3414 h
= self
->pre_max_area
.height
;
3416 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
3417 self
->pre_max_area
.width
, 0);
3421 if (dir
== 0 || dir
== 1) /* horz */
3422 self
->max_horz
= max
;
3423 if (dir
== 0 || dir
== 2) /* vert */
3424 self
->max_vert
= max
;
3427 /* make sure the window is on some monitor */
3428 client_find_onscreen(self
, &x
, &y
, w
, h
, FALSE
);
3431 client_change_state(self
); /* change the state hints on the client */
3433 client_setup_decor_and_functions(self
, FALSE
);
3434 client_move_resize(self
, x
, y
, w
, h
);
3437 void client_shade(ObClient
*self
, gboolean shade
)
3439 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
3440 shade
) || /* can't shade */
3441 self
->shaded
== shade
) return; /* already done */
3443 self
->shaded
= shade
;
3444 client_change_state(self
);
3445 client_change_wm_state(self
); /* the window is being hidden/shown */
3446 /* resize the frame to just the titlebar */
3447 frame_adjust_area(self
->frame
, FALSE
, TRUE
, FALSE
);
3450 static void client_ping_event(ObClient
*self
, gboolean dead
)
3452 if (self
->not_responding
!= dead
) {
3453 self
->not_responding
= dead
;
3454 client_update_title(self
);
3457 /* the client isn't responding, so ask to kill it */
3458 client_prompt_kill(self
);
3460 /* it came back to life ! */
3462 if (self
->kill_prompt
) {
3463 prompt_unref(self
->kill_prompt
);
3464 self
->kill_prompt
= NULL
;
3467 self
->kill_level
= 0;
3472 void client_close(ObClient
*self
)
3474 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
3476 /* if closing an internal obprompt, that is just cancelling it */
3478 prompt_cancel(self
->prompt
);
3482 /* in the case that the client provides no means to requesting that it
3483 close, we just kill it */
3484 if (!self
->delete_window
)
3485 /* don't use client_kill(), we should only kill based on PID in
3486 response to a lack of PING replies */
3487 XKillClient(obt_display
, self
->window
);
3489 /* request the client to close with WM_DELETE_WINDOW */
3490 OBT_PROP_MSG_TO(self
->window
, self
->window
, WM_PROTOCOLS
,
3491 OBT_PROP_ATOM(WM_DELETE_WINDOW
), event_time(),
3492 0, 0, 0, NoEventMask
);
3494 /* we're trying to close the window, so see if it is responding. if it
3495 is not, then we will let them kill the window */
3497 ping_start(self
, client_ping_event
);
3499 /* if we already know the window isn't responding (maybe they clicked
3500 no in the kill dialog but it hasn't come back to life), then show
3502 if (self
->not_responding
)
3503 client_prompt_kill(self
);
3507 #define OB_KILL_RESULT_NO 0
3508 #define OB_KILL_RESULT_YES 1
3510 static gboolean
client_kill_requested(ObPrompt
*p
, gint result
, gpointer data
)
3512 ObClient
*self
= data
;
3514 if (result
== OB_KILL_RESULT_YES
)
3516 return TRUE
; /* call the cleanup func */
3519 static void client_kill_cleanup(ObPrompt
*p
, gpointer data
)
3521 ObClient
*self
= data
;
3523 g_assert(p
== self
->kill_prompt
);
3525 prompt_unref(self
->kill_prompt
);
3526 self
->kill_prompt
= NULL
;
3529 static void client_prompt_kill(ObClient
*self
)
3531 /* check if we're already prompting */
3532 if (!self
->kill_prompt
) {
3533 ObPromptAnswer answers
[] = {
3534 { 0, OB_KILL_RESULT_NO
},
3535 { 0, OB_KILL_RESULT_YES
}
3538 const gchar
*y
, *title
;
3540 title
= self
->original_title
;
3541 if (title
[0] == '\0') {
3542 /* empty string, so use its parent */
3543 ObClient
*p
= client_search_top_direct_parent(self
);
3544 if (p
) title
= p
->original_title
;
3547 if (client_on_localhost(self
)) {
3550 if (self
->kill_level
== 0)
3556 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3558 y
= _("End Process");
3562 (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3564 y
= _("Disconnect");
3566 /* set the dialog buttons' text */
3567 answers
[0].text
= _("Cancel"); /* "no" */
3568 answers
[1].text
= y
; /* "yes" */
3570 self
->kill_prompt
= prompt_new(m
, NULL
, answers
,
3571 sizeof(answers
)/sizeof(answers
[0]),
3572 OB_KILL_RESULT_NO
, /* default = no */
3573 OB_KILL_RESULT_NO
, /* cancel = no */
3574 client_kill_requested
,
3575 client_kill_cleanup
,
3580 prompt_show(self
->kill_prompt
, self
, TRUE
);
3583 void client_kill(ObClient
*self
)
3585 /* don't kill our own windows */
3586 if (self
->prompt
) return;
3588 if (client_on_localhost(self
) && self
->pid
) {
3589 /* running on the local host */
3590 if (self
->kill_level
== 0) {
3591 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3592 self
->window
, self
->pid
);
3593 kill(self
->pid
, SIGTERM
);
3596 /* show that we're trying to kill it */
3597 client_update_title(self
);
3600 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3601 self
->window
, self
->pid
);
3602 kill(self
->pid
, SIGKILL
); /* kill -9 */
3606 /* running on a remote host */
3607 XKillClient(obt_display
, self
->window
);
3611 void client_hilite(ObClient
*self
, gboolean hilite
)
3613 if (self
->demands_attention
== hilite
)
3614 return; /* no change */
3616 /* don't allow focused windows to hilite */
3617 self
->demands_attention
= hilite
&& !client_focused(self
);
3618 if (self
->frame
!= NULL
) { /* if we're mapping, just set the state */
3619 if (self
->demands_attention
) {
3620 frame_flash_start(self
->frame
);
3622 /* if the window is on another desktop then raise it and make it
3623 the most recently used window */
3624 if (self
->desktop
!= screen_desktop
&&
3625 self
->desktop
!= DESKTOP_ALL
)
3627 stacking_raise(CLIENT_AS_WINDOW(self
));
3628 focus_order_to_top(self
);
3632 frame_flash_stop(self
->frame
);
3633 client_change_state(self
);
3637 static void client_set_desktop_recursive(ObClient
*self
,
3645 if (target
!= self
->desktop
&& self
->type
!= OB_CLIENT_TYPE_DESKTOP
) {
3647 ob_debug("Setting desktop %u", target
+1);
3649 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
3651 old
= self
->desktop
;
3652 self
->desktop
= target
;
3653 OBT_PROP_SET32(self
->window
, NET_WM_DESKTOP
, CARDINAL
, target
);
3654 /* the frame can display the current desktop state */
3655 frame_adjust_state(self
->frame
);
3656 /* 'move' the window to the new desktop */
3660 /* raise if it was not already on the desktop */
3661 if (old
!= DESKTOP_ALL
&& !dontraise
)
3662 stacking_raise(CLIENT_AS_WINDOW(self
));
3663 if (STRUT_EXISTS(self
->strut
))
3664 screen_update_areas();
3666 /* the new desktop's geometry may be different, so we may need to
3667 resize, for example if we are maximized */
3668 client_reconfigure(self
, FALSE
);
3670 focus_cycle_addremove(self
, FALSE
);
3673 /* move all transients */
3674 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3675 if (it
->data
!= self
)
3676 if (client_is_direct_child(self
, it
->data
))
3677 client_set_desktop_recursive(it
->data
, target
,
3678 donthide
, dontraise
);
3681 void client_set_desktop(ObClient
*self
, guint target
,
3682 gboolean donthide
, gboolean dontraise
)
3684 self
= client_search_top_direct_parent(self
);
3685 client_set_desktop_recursive(self
, target
, donthide
, dontraise
);
3687 focus_cycle_addremove(NULL
, TRUE
);
3690 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
3692 while (child
!= parent
&& (child
= client_direct_parent(child
)));
3693 return child
== parent
;
3696 ObClient
*client_search_modal_child(ObClient
*self
)
3701 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
3702 ObClient
*c
= it
->data
;
3703 if ((ret
= client_search_modal_child(c
))) return ret
;
3704 if (c
->modal
) return c
;
3709 struct ObClientFindDestroyUnmap
{
3714 static gboolean
find_destroy_unmap(XEvent
*e
, gpointer data
)
3716 struct ObClientFindDestroyUnmap
*find
= data
;
3717 if (e
->type
== DestroyNotify
)
3718 return e
->xdestroywindow
.window
== find
->window
;
3719 if (e
->type
== UnmapNotify
&& e
->xunmap
.window
== find
->window
)
3720 /* ignore the first $find->ignore_unmaps$ many unmap events */
3721 return --find
->ignore_unmaps
< 0;
3725 gboolean
client_validate(ObClient
*self
)
3727 struct ObClientFindDestroyUnmap find
;
3729 XSync(obt_display
, FALSE
); /* get all events on the server */
3731 find
.window
= self
->window
;
3732 find
.ignore_unmaps
= self
->ignore_unmaps
;
3733 if (xqueue_exists_local(find_destroy_unmap
, &find
))
3739 void client_set_wm_state(ObClient
*self
, glong state
)
3741 if (state
== self
->wmstate
) return; /* no change */
3745 client_iconify(self
, TRUE
, TRUE
, FALSE
);
3748 client_iconify(self
, FALSE
, TRUE
, FALSE
);
3753 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
3755 gboolean shaded
= self
->shaded
;
3756 gboolean fullscreen
= self
->fullscreen
;
3757 gboolean undecorated
= self
->undecorated
;
3758 gboolean max_horz
= self
->max_horz
;
3759 gboolean max_vert
= self
->max_vert
;
3760 gboolean modal
= self
->modal
;
3761 gboolean iconic
= self
->iconic
;
3762 gboolean demands_attention
= self
->demands_attention
;
3763 gboolean above
= self
->above
;
3764 gboolean below
= self
->below
;
3768 if (!(action
== OBT_PROP_ATOM(NET_WM_STATE_ADD
) ||
3769 action
== OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) ||
3770 action
== OBT_PROP_ATOM(NET_WM_STATE_TOGGLE
)))
3771 /* an invalid action was passed to the client message, ignore it */
3774 for (i
= 0; i
< 2; ++i
) {
3775 Atom state
= i
== 0 ? data1
: data2
;
3777 if (!state
) continue;
3779 /* if toggling, then pick whether we're adding or removing */
3780 if (action
== OBT_PROP_ATOM(NET_WM_STATE_TOGGLE
)) {
3781 if (state
== OBT_PROP_ATOM(NET_WM_STATE_MODAL
))
3783 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
))
3784 value
= self
->max_vert
;
3785 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
))
3786 value
= self
->max_horz
;
3787 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SHADED
))
3789 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
))
3790 value
= self
->skip_taskbar
;
3791 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
))
3792 value
= self
->skip_pager
;
3793 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
))
3794 value
= self
->iconic
;
3795 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
))
3797 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_ABOVE
))
3798 value
= self
->above
;
3799 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_BELOW
))
3800 value
= self
->below
;
3801 else if (state
== OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
))
3802 value
= self
->demands_attention
;
3803 else if (state
== OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
))
3804 value
= undecorated
;
3806 g_assert_not_reached();
3807 action
= value
? OBT_PROP_ATOM(NET_WM_STATE_REMOVE
) :
3808 OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3811 value
= action
== OBT_PROP_ATOM(NET_WM_STATE_ADD
);
3812 if (state
== OBT_PROP_ATOM(NET_WM_STATE_MODAL
)) {
3814 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT
)) {
3816 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ
)) {
3818 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SHADED
)) {
3820 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR
)) {
3821 self
->skip_taskbar
= value
;
3822 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER
)) {
3823 self
->skip_pager
= value
;
3824 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_HIDDEN
)) {
3826 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN
)) {
3828 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_ABOVE
)) {
3830 /* only unset below when setting above, otherwise you can't get to
3834 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_BELOW
)) {
3835 /* and vice versa */
3839 } else if (state
== OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION
)){
3840 demands_attention
= value
;
3841 } else if (state
== OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED
)) {
3842 undecorated
= value
;
3846 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
3847 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
3849 if (max_horz
== max_vert
) { /* both going the same way */
3850 client_maximize(self
, max_horz
, 0);
3852 client_maximize(self
, max_horz
, 1);
3853 client_maximize(self
, max_vert
, 2);
3857 if (max_horz
!= self
->max_horz
)
3858 client_maximize(self
, max_horz
, 1);
3860 client_maximize(self
, max_vert
, 2);
3863 /* change fullscreen state before shading, as it will affect if the window
3865 if (fullscreen
!= self
->fullscreen
)
3866 client_fullscreen(self
, fullscreen
);
3867 if (shaded
!= self
->shaded
)
3868 client_shade(self
, shaded
);
3869 if (undecorated
!= self
->undecorated
)
3870 client_set_undecorated(self
, undecorated
);
3871 if (above
!= self
->above
|| below
!= self
->below
) {
3872 self
->above
= above
;
3873 self
->below
= below
;
3874 client_calc_layer(self
);
3877 if (modal
!= self
->modal
) {
3878 self
->modal
= modal
;
3879 /* when a window changes modality, then its stacking order with its
3880 transients needs to change */
3881 stacking_raise(CLIENT_AS_WINDOW(self
));
3883 /* it also may get focused. if something is focused that shouldn't
3884 be focused anymore, then move the focus */
3885 if (focus_client
&& client_focus_target(focus_client
) != focus_client
)
3886 client_focus(focus_client
);
3889 if (iconic
!= self
->iconic
)
3890 client_iconify(self
, iconic
, FALSE
, FALSE
);
3892 if (demands_attention
!= self
->demands_attention
)
3893 client_hilite(self
, demands_attention
);
3895 client_change_state(self
); /* change the hint to reflect these changes */
3897 focus_cycle_addremove(self
, TRUE
);
3900 ObClient
*client_focus_target(ObClient
*self
)
3902 ObClient
*child
= NULL
;
3904 child
= client_search_modal_child(self
);
3905 if (child
) return child
;
3909 gboolean
client_can_focus(ObClient
*self
)
3911 /* choose the correct target */
3912 self
= client_focus_target(self
);
3914 if (!self
->frame
->visible
)
3917 if (!(self
->can_focus
|| self
->focus_notify
))
3923 gboolean
client_focus(ObClient
*self
)
3925 if (!client_validate(self
)) return FALSE
;
3927 /* we might not focus this window, so if we have modal children which would
3928 be focused instead, bring them to this desktop */
3929 client_bring_modal_windows(self
);
3931 /* choose the correct target */
3932 self
= client_focus_target(self
);
3934 if (!client_can_focus(self
)) {
3935 ob_debug_type(OB_DEBUG_FOCUS
,
3936 "Client %s can't be focused", self
->title
);
3940 /* if we have helper windows they should be there with the window */
3941 client_bring_helper_windows(self
);
3943 ob_debug_type(OB_DEBUG_FOCUS
,
3944 "Focusing client \"%s\" (0x%x) at time %u",
3945 self
->title
, self
->window
, event_time());
3947 /* if using focus_delay, stop the timer now so that focus doesn't
3949 event_halt_focus_delay();
3951 obt_display_ignore_errors(TRUE
);
3953 if (self
->can_focus
) {
3954 /* This can cause a BadMatch error with CurrentTime, or if an app
3955 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3956 XSetInputFocus(obt_display
, self
->window
, RevertToPointerRoot
,
3960 if (self
->focus_notify
) {
3962 ce
.xclient
.type
= ClientMessage
;
3963 ce
.xclient
.message_type
= OBT_PROP_ATOM(WM_PROTOCOLS
);
3964 ce
.xclient
.display
= obt_display
;
3965 ce
.xclient
.window
= self
->window
;
3966 ce
.xclient
.format
= 32;
3967 ce
.xclient
.data
.l
[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS
);
3968 ce
.xclient
.data
.l
[1] = event_time();
3969 ce
.xclient
.data
.l
[2] = 0l;
3970 ce
.xclient
.data
.l
[3] = 0l;
3971 ce
.xclient
.data
.l
[4] = 0l;
3972 XSendEvent(obt_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3975 obt_display_ignore_errors(FALSE
);
3977 ob_debug_type(OB_DEBUG_FOCUS
, "Error focusing? %d",
3978 obt_display_error_occured
);
3979 return !obt_display_error_occured
;
3982 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
,
3985 if (client_normal(self
) && screen_showing_desktop
)
3986 screen_show_desktop(FALSE
, self
);
3988 client_iconify(self
, FALSE
, here
, FALSE
);
3989 if (self
->desktop
!= DESKTOP_ALL
&&
3990 self
->desktop
!= screen_desktop
)
3993 client_set_desktop(self
, screen_desktop
, FALSE
, TRUE
);
3995 screen_set_desktop(self
->desktop
, FALSE
);
3996 } else if (!self
->frame
->visible
)
3997 /* if its not visible for other reasons, then don't mess
4000 if (self
->shaded
&& unshade
)
4001 client_shade(self
, FALSE
);
4003 stacking_raise(CLIENT_AS_WINDOW(self
));
4008 /* this function exists to map to the net_active_window message in the ewmh */
4009 void client_activate(ObClient
*self
, gboolean desktop
,
4010 gboolean here
, gboolean raise
,
4011 gboolean unshade
, gboolean user
)
4013 self
= client_focus_target(self
);
4015 if (client_can_steal_focus(self
, desktop
, user
, event_time(), CurrentTime
))
4016 client_present(self
, here
, raise
, unshade
);
4018 client_hilite(self
, TRUE
);
4021 static void client_bring_windows_recursive(ObClient
*self
,
4029 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
4030 client_bring_windows_recursive(it
->data
, desktop
,
4031 helpers
, modals
, iconic
);
4033 if (((helpers
&& client_helper(self
)) ||
4034 (modals
&& self
->modal
)) &&
4035 (!screen_compare_desktops(self
->desktop
, desktop
) ||
4036 (iconic
&& self
->iconic
)))
4038 if (iconic
&& self
->iconic
)
4039 client_iconify(self
, FALSE
, TRUE
, FALSE
);
4041 client_set_desktop(self
, desktop
, FALSE
, FALSE
);
4045 void client_bring_helper_windows(ObClient
*self
)
4047 client_bring_windows_recursive(self
, self
->desktop
, TRUE
, FALSE
, FALSE
);
4050 void client_bring_modal_windows(ObClient
*self
)
4052 client_bring_windows_recursive(self
, self
->desktop
, FALSE
, TRUE
, TRUE
);
4055 gboolean
client_focused(ObClient
*self
)
4057 return self
== focus_client
;
4060 RrImage
* client_icon(ObClient
*self
)
4062 RrImage
*ret
= NULL
;
4065 ret
= self
->icon_set
;
4066 else if (self
->parents
) {
4068 for (it
= self
->parents
; it
&& !ret
; it
= g_slist_next(it
))
4069 ret
= client_icon(it
->data
);
4072 ret
= client_default_icon
;
4076 void client_set_layer(ObClient
*self
, gint layer
)
4080 self
->above
= FALSE
;
4081 } else if (layer
== 0) {
4082 self
->below
= self
->above
= FALSE
;
4084 self
->below
= FALSE
;
4087 client_calc_layer(self
);
4088 client_change_state(self
); /* reflect this in the state hints */
4091 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
4093 if (self
->undecorated
!= undecorated
&&
4094 /* don't let it undecorate if the function is missing, but let
4096 (self
->functions
& OB_CLIENT_FUNC_UNDECORATE
|| !undecorated
))
4098 self
->undecorated
= undecorated
;
4099 client_setup_decor_and_functions(self
, TRUE
);
4100 client_change_state(self
); /* reflect this in the state hints */
4104 guint
client_monitor(ObClient
*self
)
4106 return screen_find_monitor(&self
->frame
->area
);
4109 ObClient
*client_direct_parent(ObClient
*self
)
4111 if (!self
->parents
) return NULL
;
4112 if (self
->transient_for_group
) return NULL
;
4113 return self
->parents
->data
;
4116 ObClient
*client_search_top_direct_parent(ObClient
*self
)
4119 while ((p
= client_direct_parent(self
))) self
= p
;
4123 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
4125 ObStackingLayer layer
)
4130 /* move up the direct transient chain as far as possible */
4131 while ((p
= client_direct_parent(self
)) &&
4132 (!bylayer
|| p
->layer
== layer
))
4136 ret
= g_slist_prepend(NULL
, self
);
4138 ret
= g_slist_copy(self
->parents
);
4143 GSList
*client_search_all_top_parents(ObClient
*self
)
4145 return client_search_all_top_parents_internal(self
, FALSE
, 0);
4148 GSList
*client_search_all_top_parents_layer(ObClient
*self
)
4150 return client_search_all_top_parents_internal(self
, TRUE
, self
->layer
);
4153 ObClient
*client_search_focus_parent(ObClient
*self
)
4157 for (it
= self
->parents
; it
; it
= g_slist_next(it
))
4158 if (client_focused(it
->data
)) return it
->data
;
4163 ObClient
*client_search_focus_parent_full(ObClient
*self
)
4166 ObClient
*ret
= NULL
;
4168 for (it
= self
->parents
; it
; it
= g_slist_next(it
)) {
4169 if (client_focused(it
->data
))
4172 ret
= client_search_focus_parent_full(it
->data
);
4178 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
4182 for (it
= self
->parents
; it
; it
= g_slist_next(it
))
4183 if (it
->data
== search
) return search
;
4188 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
4192 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
4193 if (sit
->data
== search
)
4195 if (client_search_transient(sit
->data
, search
))
4201 static void detect_edge(Rect area
, ObDirection dir
,
4202 gint my_head
, gint my_size
,
4203 gint my_edge_start
, gint my_edge_size
,
4204 gint
*dest
, gboolean
*near_edge
)
4206 gint edge_start
, edge_size
, head
, tail
;
4207 gboolean skip_head
= FALSE
, skip_tail
= FALSE
;
4210 case OB_DIRECTION_NORTH
:
4211 case OB_DIRECTION_SOUTH
:
4212 edge_start
= area
.x
;
4213 edge_size
= area
.width
;
4215 case OB_DIRECTION_EAST
:
4216 case OB_DIRECTION_WEST
:
4217 edge_start
= area
.y
;
4218 edge_size
= area
.height
;
4221 g_assert_not_reached();
4224 /* do we collide with this window? */
4225 if (!RANGES_INTERSECT(my_edge_start
, my_edge_size
,
4226 edge_start
, edge_size
))
4230 case OB_DIRECTION_NORTH
:
4231 head
= RECT_BOTTOM(area
);
4232 tail
= RECT_TOP(area
);
4234 case OB_DIRECTION_SOUTH
:
4235 head
= RECT_TOP(area
);
4236 tail
= RECT_BOTTOM(area
);
4238 case OB_DIRECTION_WEST
:
4239 head
= RECT_RIGHT(area
);
4240 tail
= RECT_LEFT(area
);
4242 case OB_DIRECTION_EAST
:
4243 head
= RECT_LEFT(area
);
4244 tail
= RECT_RIGHT(area
);
4247 g_assert_not_reached();
4250 case OB_DIRECTION_NORTH
:
4251 case OB_DIRECTION_WEST
:
4252 /* check if our window is past the head of this window */
4253 if (my_head
<= head
+ 1)
4255 /* check if our window's tail is past the tail of this window */
4256 if (my_head
+ my_size
- 1 <= tail
)
4258 /* check if the head of this window is closer than the previously
4259 chosen edge (take into account that the previously chosen
4260 edge might have been a tail, not a head) */
4261 if (head
+ (*near_edge
? 0 : my_size
) <= *dest
)
4263 /* check if the tail of this window is closer than the previously
4264 chosen edge (take into account that the previously chosen
4265 edge might have been a head, not a tail) */
4266 if (tail
- (!*near_edge
? 0 : my_size
) <= *dest
)
4269 case OB_DIRECTION_SOUTH
:
4270 case OB_DIRECTION_EAST
:
4271 /* check if our window is past the head of this window */
4272 if (my_head
>= head
- 1)
4274 /* check if our window's tail is past the tail of this window */
4275 if (my_head
- my_size
+ 1 >= tail
)
4277 /* check if the head of this window is closer than the previously
4278 chosen edge (take into account that the previously chosen
4279 edge might have been a tail, not a head) */
4280 if (head
- (*near_edge
? 0 : my_size
) >= *dest
)
4282 /* check if the tail of this window is closer than the previously
4283 chosen edge (take into account that the previously chosen
4284 edge might have been a head, not a tail) */
4285 if (tail
+ (!*near_edge
? 0 : my_size
) >= *dest
)
4289 g_assert_not_reached();
4292 ob_debug("my head %d size %d", my_head
, my_size
);
4293 ob_debug("head %d tail %d dest %d", head
, tail
, *dest
);
4295 ob_debug("using near edge %d", head
);
4299 else if (!skip_tail
) {
4300 ob_debug("using far edge %d", tail
);
4306 void client_find_edge_directional(ObClient
*self
, ObDirection dir
,
4307 gint my_head
, gint my_size
,
4308 gint my_edge_start
, gint my_edge_size
,
4309 gint
*dest
, gboolean
*near_edge
)
4317 a
= screen_area(self
->desktop
, SCREEN_AREA_ALL_MONITORS
,
4318 &self
->frame
->area
);
4321 case OB_DIRECTION_NORTH
:
4322 edge
= RECT_TOP(*a
) - 1;
4324 case OB_DIRECTION_SOUTH
:
4325 edge
= RECT_BOTTOM(*a
) + 1;
4327 case OB_DIRECTION_EAST
:
4328 edge
= RECT_RIGHT(*a
) + 1;
4330 case OB_DIRECTION_WEST
:
4331 edge
= RECT_LEFT(*a
) - 1;
4334 g_assert_not_reached();
4336 /* default to the far edge, then narrow it down */
4340 /* search for edges of monitors */
4341 for (i
= 0; i
< screen_num_monitors
; ++i
) {
4342 Rect
*area
= screen_area(self
->desktop
, i
, NULL
);
4343 detect_edge(*area
, dir
, my_head
, my_size
, my_edge_start
,
4344 my_edge_size
, dest
, near_edge
);
4345 g_slice_free(Rect
, area
);
4348 /* search for edges of clients */
4349 for (it
= client_list
; it
; it
= g_list_next(it
)) {
4350 ObClient
*cur
= it
->data
;
4352 /* skip windows to not bump into */
4357 if (self
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
&&
4358 cur
->desktop
!= screen_desktop
)
4361 ob_debug("trying window %s", cur
->title
);
4363 detect_edge(cur
->frame
->area
, dir
, my_head
, my_size
, my_edge_start
,
4364 my_edge_size
, dest
, near_edge
);
4366 dock_get_area(&dock_area
);
4367 detect_edge(dock_area
, dir
, my_head
, my_size
, my_edge_start
,
4368 my_edge_size
, dest
, near_edge
);
4370 g_slice_free(Rect
, a
);
4373 void client_find_move_directional(ObClient
*self
, ObDirection dir
,
4377 gint e
, e_start
, e_size
;
4381 case OB_DIRECTION_EAST
:
4382 head
= RECT_RIGHT(self
->frame
->area
);
4383 size
= self
->frame
->area
.width
;
4384 e_start
= RECT_TOP(self
->frame
->area
);
4385 e_size
= self
->frame
->area
.height
;
4387 case OB_DIRECTION_WEST
:
4388 head
= RECT_LEFT(self
->frame
->area
);
4389 size
= self
->frame
->area
.width
;
4390 e_start
= RECT_TOP(self
->frame
->area
);
4391 e_size
= self
->frame
->area
.height
;
4393 case OB_DIRECTION_NORTH
:
4394 head
= RECT_TOP(self
->frame
->area
);
4395 size
= self
->frame
->area
.height
;
4396 e_start
= RECT_LEFT(self
->frame
->area
);
4397 e_size
= self
->frame
->area
.width
;
4399 case OB_DIRECTION_SOUTH
:
4400 head
= RECT_BOTTOM(self
->frame
->area
);
4401 size
= self
->frame
->area
.height
;
4402 e_start
= RECT_LEFT(self
->frame
->area
);
4403 e_size
= self
->frame
->area
.width
;
4406 g_assert_not_reached();
4409 client_find_edge_directional(self
, dir
, head
, size
,
4410 e_start
, e_size
, &e
, &near
);
4411 *x
= self
->frame
->area
.x
;
4412 *y
= self
->frame
->area
.y
;
4414 case OB_DIRECTION_EAST
:
4415 if (near
) e
-= self
->frame
->area
.width
;
4419 case OB_DIRECTION_WEST
:
4421 else e
-= self
->frame
->area
.width
;
4424 case OB_DIRECTION_NORTH
:
4426 else e
-= self
->frame
->area
.height
;
4429 case OB_DIRECTION_SOUTH
:
4430 if (near
) e
-= self
->frame
->area
.height
;
4435 g_assert_not_reached();
4437 frame_frame_gravity(self
->frame
, x
, y
);
4440 void client_find_resize_directional(ObClient
*self
, ObDirection side
,
4442 gint
*x
, gint
*y
, gint
*w
, gint
*h
)
4445 gint e
, e_start
, e_size
, delta
;
4450 case OB_DIRECTION_EAST
:
4451 head
= RECT_RIGHT(self
->frame
->area
) +
4452 (self
->size_inc
.width
- 1) * (grow
? 1 : 0);
4453 e_start
= RECT_TOP(self
->frame
->area
);
4454 e_size
= self
->frame
->area
.height
;
4455 dir
= grow
? OB_DIRECTION_EAST
: OB_DIRECTION_WEST
;
4457 case OB_DIRECTION_WEST
:
4458 head
= RECT_LEFT(self
->frame
->area
) -
4459 (self
->size_inc
.width
- 1) * (grow
? 1 : 0);
4460 e_start
= RECT_TOP(self
->frame
->area
);
4461 e_size
= self
->frame
->area
.height
;
4462 dir
= grow
? OB_DIRECTION_WEST
: OB_DIRECTION_EAST
;
4464 case OB_DIRECTION_NORTH
:
4465 head
= RECT_TOP(self
->frame
->area
) -
4466 (self
->size_inc
.height
- 1) * (grow
? 1 : 0);
4467 e_start
= RECT_LEFT(self
->frame
->area
);
4468 e_size
= self
->frame
->area
.width
;
4469 dir
= grow
? OB_DIRECTION_NORTH
: OB_DIRECTION_SOUTH
;
4471 case OB_DIRECTION_SOUTH
:
4472 head
= RECT_BOTTOM(self
->frame
->area
) +
4473 (self
->size_inc
.height
- 1) * (grow
? 1 : 0);
4474 e_start
= RECT_LEFT(self
->frame
->area
);
4475 e_size
= self
->frame
->area
.width
;
4476 dir
= grow
? OB_DIRECTION_SOUTH
: OB_DIRECTION_NORTH
;
4479 g_assert_not_reached();
4482 ob_debug("head %d dir %d", head
, dir
);
4483 client_find_edge_directional(self
, dir
, head
, 1,
4484 e_start
, e_size
, &e
, &near
);
4485 ob_debug("edge %d", e
);
4486 *x
= self
->frame
->area
.x
;
4487 *y
= self
->frame
->area
.y
;
4488 *w
= self
->frame
->area
.width
;
4489 *h
= self
->frame
->area
.height
;
4491 case OB_DIRECTION_EAST
:
4492 if (grow
== near
) --e
;
4493 delta
= e
- RECT_RIGHT(self
->frame
->area
);
4496 case OB_DIRECTION_WEST
:
4497 if (grow
== near
) ++e
;
4498 delta
= RECT_LEFT(self
->frame
->area
) - e
;
4502 case OB_DIRECTION_NORTH
:
4503 if (grow
== near
) ++e
;
4504 delta
= RECT_TOP(self
->frame
->area
) - e
;
4508 case OB_DIRECTION_SOUTH
:
4509 if (grow
== near
) --e
;
4510 delta
= e
- RECT_BOTTOM(self
->frame
->area
);
4514 g_assert_not_reached();
4516 frame_frame_gravity(self
->frame
, x
, y
);
4517 *w
-= self
->frame
->size
.left
+ self
->frame
->size
.right
;
4518 *h
-= self
->frame
->size
.top
+ self
->frame
->size
.bottom
;
4521 ObClient
* client_under_pointer(void)
4525 ObClient
*ret
= NULL
;
4527 if (screen_pointer_pos(&x
, &y
)) {
4528 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
4529 if (WINDOW_IS_CLIENT(it
->data
)) {
4530 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
4531 if (c
->frame
->visible
&&
4532 /* check the desktop, this is done during desktop
4533 switching and windows are shown/hidden status is not
4535 (c
->desktop
== screen_desktop
||
4536 c
->desktop
== DESKTOP_ALL
) &&
4537 /* ignore all animating windows */
4538 !frame_iconify_animating(c
->frame
) &&
4539 RECT_CONTAINS(c
->frame
->area
, x
, y
))
4550 gboolean
client_has_group_siblings(ObClient
*self
)
4552 return self
->group
&& self
->group
->members
->next
;
4555 gboolean
client_has_relative(ObClient
*self
)
4557 return client_has_parent(self
) ||
4558 client_has_group_siblings(self
) ||
4559 client_has_children(self
);
4562 /*! Returns TRUE if the client is running on the same machine as Openbox */
4563 gboolean
client_on_localhost(ObClient
*self
)
4565 return self
->client_machine
== NULL
;