1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
49 #include <X11/Xutil.h>
51 /*! The event mask to grab on client windows */
52 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
55 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
60 ObClientDestructor func
;
64 GList
*client_list
= NULL
;
66 static GSList
*client_destructors
= NULL
;
68 static void client_get_all(ObClient
*self
);
69 static void client_toggle_border(ObClient
*self
, gboolean show
);
70 static void client_get_startup_id(ObClient
*self
);
71 static void client_get_area(ObClient
*self
);
72 static void client_get_desktop(ObClient
*self
);
73 static void client_get_state(ObClient
*self
);
74 static void client_get_layer(ObClient
*self
);
75 static void client_get_shaped(ObClient
*self
);
76 static void client_get_mwm_hints(ObClient
*self
);
77 static void client_get_gravity(ObClient
*self
);
78 static void client_get_client_machine(ObClient
*self
);
79 static void client_get_colormap(ObClient
*self
);
80 static void client_get_transientness(ObClient
*self
);
81 static void client_change_allowed_actions(ObClient
*self
);
82 static void client_change_state(ObClient
*self
);
83 static void client_change_wm_state(ObClient
*self
);
84 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
85 static void client_restore_session_state(ObClient
*self
);
86 static gboolean
client_restore_session_stacking(ObClient
*self
);
87 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
88 static void client_update_transient_tree(ObClient
*self
,
89 ObGroup
*oldgroup
, ObGroup
*newgroup
,
92 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
);
93 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
95 ObStackingLayer layer
);
97 void client_startup(gboolean reconfig
)
104 void client_shutdown(gboolean reconfig
)
108 void client_add_destructor(ObClientDestructor func
, gpointer data
)
110 Destructor
*d
= g_new(Destructor
, 1);
113 client_destructors
= g_slist_prepend(client_destructors
, d
);
116 void client_remove_destructor(ObClientDestructor func
)
120 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
121 Destructor
*d
= it
->data
;
122 if (d
->func
== func
) {
124 client_destructors
= g_slist_delete_link(client_destructors
, it
);
130 void client_set_list()
132 Window
*windows
, *win_it
;
134 guint size
= g_list_length(client_list
);
136 /* create an array of the window ids */
138 windows
= g_new(Window
, size
);
140 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
141 *win_it
= ((ObClient
*)it
->data
)->window
;
145 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
146 net_client_list
, window
, (gulong
*)windows
, size
);
155 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
159 for (it = self->transients; it; it = g_slist_next(it)) {
160 if (!func(it->data, data)) return;
161 client_foreach_transient(it->data, func, data);
165 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
167 if (self->transient_for) {
168 if (self->transient_for != OB_TRAN_GROUP) {
169 if (!func(self->transient_for, data)) return;
170 client_foreach_ancestor(self->transient_for, func, data);
174 for (it = self->group->members; it; it = g_slist_next(it))
175 if (it->data != self &&
176 !((ObClient*)it->data)->transient_for) {
177 if (!func(it->data, data)) return;
178 client_foreach_ancestor(it->data, func, data);
185 void client_manage_all()
190 XWindowAttributes attrib
;
192 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
193 &w
, &w
, &children
, &nchild
);
195 /* remove all icon windows from the list */
196 for (i
= 0; i
< nchild
; i
++) {
197 if (children
[i
] == None
) continue;
198 wmhints
= XGetWMHints(ob_display
, children
[i
]);
200 if ((wmhints
->flags
& IconWindowHint
) &&
201 (wmhints
->icon_window
!= children
[i
]))
202 for (j
= 0; j
< nchild
; j
++)
203 if (children
[j
] == wmhints
->icon_window
) {
211 for (i
= 0; i
< nchild
; ++i
) {
212 if (children
[i
] == None
)
214 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
215 if (attrib
.override_redirect
) continue;
217 if (attrib
.map_state
!= IsUnmapped
)
218 client_manage(children
[i
]);
224 void client_manage(Window window
)
228 XWindowAttributes attrib
;
229 XSetWindowAttributes attrib_set
;
231 gboolean activate
= FALSE
;
232 ObAppSettings
*settings
;
237 /* check if it has already been unmapped by the time we started mapping.
238 the grab does a sync so we don't have to here */
239 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
240 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
242 XPutBackEvent(ob_display
, &e
);
244 ob_debug("Trying to manage unmapped window. Aborting that.\n");
246 return; /* don't manage it */
249 /* make sure it isn't an override-redirect window */
250 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
251 attrib
.override_redirect
)
254 return; /* don't manage it */
257 /* is the window a docking app */
258 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
259 if ((wmhint
->flags
& StateHint
) &&
260 wmhint
->initial_state
== WithdrawnState
)
262 dock_add(window
, wmhint
);
270 ob_debug("Managing window: %lx\n", window
);
272 /* choose the events we want to receive on the CLIENT window */
273 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
274 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
275 XChangeWindowAttributes(ob_display
, window
,
276 CWEventMask
|CWDontPropagate
, &attrib_set
);
279 /* create the ObClient struct, and populate it from the hints on the
281 self
= g_new0(ObClient
, 1);
282 self
->obwin
.type
= Window_Client
;
283 self
->window
= window
;
285 /* non-zero defaults */
286 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
288 self
->desktop
= screen_num_desktops
; /* always an invalid value */
289 self
->user_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
291 client_get_all(self
);
292 /* per-app settings override stuff, and return the settings for other
294 settings
= client_get_settings_state(self
);
295 /* the session should get the last say */
296 client_restore_session_state(self
);
298 client_calc_layer(self
);
301 Time t
= sn_app_started(self
->startup_id
, self
->class);
302 if (t
) self
->user_time
= t
;
305 /* update the focus lists, do this before the call to change_state or
306 it can end up in the list twice! */
307 focus_order_add_new(self
);
309 /* remove the client's border (and adjust re gravity) */
310 client_toggle_border(self
, FALSE
);
312 /* specify that if we exit, the window should not be destroyed and should
313 be reparented back to root automatically */
314 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
316 /* create the decoration frame for the client window */
317 self
->frame
= frame_new(self
);
319 frame_grab_client(self
->frame
, self
);
321 /* do this after we have a frame.. it uses the frame to help determine the
322 WM_STATE to apply. */
323 client_change_state(self
);
327 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
329 /* focus the new window? */
330 if (ob_state() != OB_STATE_STARTING
&&
331 (!self
->session
|| self
->session
->focused
) &&
333 /* this means focus=true for window is same as config_focus_new=true */
334 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
335 client_search_focus_parent(self
)) &&
336 /* this checks for focus=false for the window */
337 (!settings
|| settings
->focus
!= 0) &&
338 /* note the check against Type_Normal/Dialog, not client_normal(self),
339 which would also include other types. in this case we want more
340 strict rules for focus */
341 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
342 self
->type
== OB_CLIENT_TYPE_DIALOG
))
347 /* get the current position */
351 /* figure out placement for the window */
352 if (ob_state() == OB_STATE_RUNNING
) {
355 transient
= place_client(self
, &newx
, &newy
, settings
);
357 /* make sure the window is visible. */
358 client_find_onscreen(self
, &newx
, &newy
,
361 /* non-normal clients has less rules, and
362 windows that are being restored from a
363 session do also. we can assume you want
364 it back where you saved it. Clients saying
365 they placed themselves are subjected to
366 harder rules, ones that are placed by
367 place.c or by the user are allowed partially
368 off-screen and on xinerama divides (ie,
369 it is up to the placement routines to avoid
370 the xinerama divides) */
372 (((self
->positioned
& PPosition
) &&
373 !(self
->positioned
& USPosition
)) &&
374 client_normal(self
) &&
378 /* do this after the window is placed, so the premax/prefullscreen numbers
380 also, this moves the window to the position where it has been placed
382 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
383 self
->window
, newx
, newy
, self
->area
.width
, self
->area
.height
);
385 ob_debug("session requested %d %d\n",
386 self
->session
->x
, self
->session
->y
);
388 client_apply_startup_state(self
, newx
, newy
);
390 mouse_grab_for_client(self
, TRUE
);
393 guint32 last_time
= focus_client
?
394 focus_client
->user_time
: CurrentTime
;
396 /* This is focus stealing prevention */
397 ob_debug_type(OB_DEBUG_FOCUS
,
398 "Want to focus new window 0x%x with time %u "
400 self
->window
, self
->user_time
, last_time
);
402 /* if it's on another desktop */
403 if (!(self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
404 && /* the timestamp is from before you changed desktops */
405 self
->user_time
&& screen_desktop_user_time
&&
406 !event_time_after(self
->user_time
, screen_desktop_user_time
))
409 ob_debug_type(OB_DEBUG_FOCUS
,
410 "Not focusing the window because its on another "
413 /* If something is focused, and it's not our parent... */
414 else if (focus_client
&& client_search_focus_parent(self
) == NULL
)
416 /* If time stamp is old, don't steal focus */
417 if (self
->user_time
&& last_time
&&
418 !event_time_after(self
->user_time
, last_time
))
421 ob_debug_type(OB_DEBUG_FOCUS
,
422 "Not focusing the window because the time is "
425 /* Don't steal focus from globally active clients.
426 I stole this idea from KWin. It seems nice.
428 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
)) {
430 ob_debug_type(OB_DEBUG_FOCUS
,
431 "Not focusing the window because a globally "
432 "active client has focus\n");
437 ob_debug_type(OB_DEBUG_FOCUS
,
438 "Focus stealing prevention activated for %s with "
439 "time %u (last time %u)\n",
440 self
->title
, self
->user_time
, last_time
);
441 /* if the client isn't focused, then hilite it so the user
443 client_hilite(self
, TRUE
);
447 /* This may look rather odd. Well it's because new windows are added
448 to the stacking order non-intrusively. If we're not going to focus
449 the new window or hilite it, then we raise it to the top. This will
450 take affect for things that don't get focused like splash screens.
451 Also if you don't have focus_new enabled, then it's going to get
452 raised to the top. Legacy begets legacy I guess?
454 if (!client_restore_session_stacking(self
))
458 /* this has to happen before we try focus the window, but we want it to
459 happen after the client's stacking has been determined or it looks bad
463 /* use client_focus instead of client_activate cuz client_activate does
464 stuff like switch desktops etc and I'm not interested in all that when
465 a window maps since its not based on an action from the user like
466 clicking a window to activate it. so keep the new window out of the way
469 gboolean stacked
= client_restore_session_stacking(self
);
470 client_present(self
, FALSE
, !stacked
);
473 /* add to client list/map */
474 client_list
= g_list_append(client_list
, self
);
475 g_hash_table_insert(window_map
, &self
->window
, self
);
477 /* this has to happen after we're in the client_list */
478 if (STRUT_EXISTS(self
->strut
))
479 screen_update_areas();
481 /* update the list hints */
484 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
487 void client_unmanage_all()
489 while (client_list
!= NULL
)
490 client_unmanage(client_list
->data
);
493 void client_unmanage(ObClient
*self
)
498 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
499 self
->title
? self
->title
: "");
501 g_assert(self
!= NULL
);
503 /* we dont want events no more. do this before hiding the frame so we
504 don't generate more events */
505 XSelectInput(ob_display
, self
->window
, NoEventMask
);
507 frame_hide(self
->frame
);
508 /* flush to send the hide to the server quickly */
511 /* ignore enter events from the unmap so it doesnt mess with the focus */
512 event_ignore_queued_enters();
514 mouse_grab_for_client(self
, FALSE
);
516 /* remove the window from our save set */
517 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
519 /* update the focus lists */
520 focus_order_remove(self
);
521 if (client_focused(self
)) {
522 /* don't leave an invalid focus_client */
526 client_list
= g_list_remove(client_list
, self
);
527 stacking_remove(self
);
528 g_hash_table_remove(window_map
, &self
->window
);
530 /* once the client is out of the list, update the struts to remove its
532 if (STRUT_EXISTS(self
->strut
))
533 screen_update_areas();
535 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
536 Destructor
*d
= it
->data
;
537 d
->func(self
, d
->data
);
540 /* tell our parent(s) that we're gone */
541 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
542 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
543 if (it
->data
!= self
)
544 ((ObClient
*)it
->data
)->transients
=
545 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
546 } else if (self
->transient_for
) { /* transient of window */
547 self
->transient_for
->transients
=
548 g_slist_remove(self
->transient_for
->transients
, self
);
551 /* tell our transients that we're gone */
552 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
553 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
554 ((ObClient
*)it
->data
)->transient_for
= NULL
;
555 client_calc_layer(it
->data
);
559 /* remove from its group */
561 group_remove(self
->group
, self
);
565 /* restore the window's original geometry so it is not lost */
569 if (self
->fullscreen
)
570 a
= self
->pre_fullscreen_area
;
571 else if (self
->max_horz
|| self
->max_vert
) {
572 if (self
->max_horz
) {
573 a
.x
= self
->pre_max_area
.x
;
574 a
.width
= self
->pre_max_area
.width
;
576 if (self
->max_vert
) {
577 a
.y
= self
->pre_max_area
.y
;
578 a
.height
= self
->pre_max_area
.height
;
582 /* give the client its border back */
583 client_toggle_border(self
, TRUE
);
585 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
586 self
->decorations
= 0; /* unmanaged windows have no decor */
588 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
591 /* reparent the window out of the frame, and free the frame */
592 frame_release_client(self
->frame
, self
);
595 if (ob_state() != OB_STATE_EXITING
) {
596 /* these values should not be persisted across a window
598 PROP_ERASE(self
->window
, net_wm_desktop
);
599 PROP_ERASE(self
->window
, net_wm_state
);
600 PROP_ERASE(self
->window
, wm_state
);
602 /* if we're left in an unmapped state, the client wont be mapped. this
603 is bad, since we will no longer be managing the window on restart */
604 XMapWindow(ob_display
, self
->window
);
607 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
609 /* free all data allocated in the client struct */
610 g_slist_free(self
->transients
);
611 for (j
= 0; j
< self
->nicons
; ++j
)
612 g_free(self
->icons
[j
].data
);
613 if (self
->nicons
> 0)
616 g_free(self
->icon_title
);
620 g_free(self
->client_machine
);
621 g_free(self
->sm_client_id
);
624 /* update the list hints */
628 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
630 ObAppSettings
*settings
= NULL
;
633 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
634 ObAppSettings
*app
= it
->data
;
636 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
637 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
638 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
639 && !strcmp(app
->name
, self
->name
)))
641 ob_debug("Window matching: %s\n", app
->name
);
642 /* Match if no role was specified in the per app setting, or if the
643 * string matches the beginning of the role, since apps like to set
644 * the role to things like browser-window-23c4b2f */
646 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
656 if (settings
->shade
!= -1)
657 self
->shaded
= !!settings
->shade
;
658 if (settings
->decor
!= -1)
659 self
->undecorated
= !settings
->decor
;
660 if (settings
->iconic
!= -1)
661 self
->iconic
= !!settings
->iconic
;
662 if (settings
->skip_pager
!= -1)
663 self
->skip_pager
= !!settings
->skip_pager
;
664 if (settings
->skip_taskbar
!= -1)
665 self
->skip_taskbar
= !!settings
->skip_taskbar
;
667 if (settings
->max_vert
!= -1)
668 self
->max_vert
= !!settings
->max_vert
;
669 if (settings
->max_horz
!= -1)
670 self
->max_horz
= !!settings
->max_horz
;
672 if (settings
->fullscreen
!= -1)
673 self
->fullscreen
= !!settings
->fullscreen
;
675 if (settings
->desktop
< screen_num_desktops
676 || settings
->desktop
== DESKTOP_ALL
)
677 self
->desktop
= settings
->desktop
;
679 if (settings
->layer
== -1) {
683 else if (settings
->layer
== 0) {
687 else if (settings
->layer
== 1) {
695 static void client_restore_session_state(ObClient
*self
)
699 ob_debug_type(OB_DEBUG_SM
,
700 "Restore session for client %s\n", self
->title
);
702 if (!(it
= session_state_find(self
))) {
703 ob_debug_type(OB_DEBUG_SM
,
704 "Session data not found for client %s\n", self
->title
);
708 self
->session
= it
->data
;
710 ob_debug_type(OB_DEBUG_SM
, "Session data loaded for client %s\n",
713 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
714 self
->positioned
= USPosition
;
715 if (self
->session
->w
> 0)
716 self
->area
.width
= self
->session
->w
;
717 if (self
->session
->h
> 0)
718 self
->area
.height
= self
->session
->h
;
719 XResizeWindow(ob_display
, self
->window
,
720 self
->area
.width
, self
->area
.height
);
722 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
723 self
->session
->desktop
:
724 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
725 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
727 self
->shaded
= self
->session
->shaded
;
728 self
->iconic
= self
->session
->iconic
;
729 self
->skip_pager
= self
->session
->skip_pager
;
730 self
->skip_taskbar
= self
->session
->skip_taskbar
;
731 self
->fullscreen
= self
->session
->fullscreen
;
732 self
->above
= self
->session
->above
;
733 self
->below
= self
->session
->below
;
734 self
->max_horz
= self
->session
->max_horz
;
735 self
->max_vert
= self
->session
->max_vert
;
736 self
->undecorated
= self
->session
->undecorated
;
739 static gboolean
client_restore_session_stacking(ObClient
*self
)
743 if (!self
->session
) return FALSE
;
745 mypos
= g_list_find(session_saved_state
, self
->session
);
746 if (!mypos
) return FALSE
;
748 /* start above me and look for the first client */
749 for (it
= g_list_previous(mypos
); it
; it
= g_list_previous(it
)) {
752 for (cit
= client_list
; cit
; cit
= g_list_next(cit
)) {
753 ObClient
*c
= cit
->data
;
754 /* found a client that was in the session, so go below it */
755 if (c
->session
== it
->data
) {
756 stacking_below(CLIENT_AS_WINDOW(self
),
757 CLIENT_AS_WINDOW(cit
->data
));
765 void client_move_onscreen(ObClient
*self
, gboolean rude
)
767 gint x
= self
->area
.x
;
768 gint y
= self
->area
.y
;
769 if (client_find_onscreen(self
, &x
, &y
,
771 self
->area
.height
, rude
)) {
772 client_move(self
, x
, y
);
776 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
780 gint ox
= *x
, oy
= *y
;
781 gboolean rudel
= rude
, ruder
= rude
, rudet
= rude
, rudeb
= rude
;
784 all_a
= screen_area(self
->desktop
);
785 mon_a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
787 /* get where the frame would be */
788 frame_client_gravity(self
->frame
, x
, y
, w
, h
);
790 /* get the requested size of the window with decorations */
791 fw
= self
->frame
->size
.left
+ w
+ self
->frame
->size
.right
;
792 fh
= self
->frame
->size
.top
+ h
+ self
->frame
->size
.bottom
;
794 /* This makes sure windows aren't entirely outside of the screen so you
795 can't see them at all.
796 It makes sure 10% of the window is on the screen at least. At don't let
797 it move itself off the top of the screen, which would hide the titlebar
798 on you. (The user can still do this if they want too, it's only limiting
801 XXX watch for xinerama dead areas...
803 if (client_normal(self
)) {
804 if (!self
->strut
.right
&& *x
+ fw
/10 >= all_a
->x
+ all_a
->width
- 1)
805 *x
= all_a
->x
+ all_a
->width
- fw
/10;
806 if (!self
->strut
.bottom
&& *y
+ fh
/10 >= all_a
->y
+ all_a
->height
- 1)
807 *y
= all_a
->y
+ all_a
->height
- fh
/10;
808 if (!self
->strut
.left
&& *x
+ fw
*9/10 - 1 < all_a
->x
)
809 *x
= all_a
->x
- fw
*9/10;
810 if (!self
->strut
.top
&& *y
+ fh
*9/10 - 1 < all_a
->y
)
811 *y
= all_a
->y
- fw
*9/10;
814 /* If rudeness wasn't requested, then figure out of the client is currently
815 entirely on the screen. If it is, and the position isn't changing by
816 request, and it is enlarging, then be rude even though it wasn't
819 Point oldtl
, oldtr
, oldbl
, oldbr
;
820 Point newtl
, newtr
, newbl
, newbr
;
821 gboolean stationary_l
, stationary_r
, stationary_t
, stationary_b
;
823 POINT_SET(oldtl
, self
->frame
->area
.x
, self
->frame
->area
.y
);
824 POINT_SET(oldbr
, self
->frame
->area
.x
+ self
->frame
->area
.width
- 1,
825 self
->frame
->area
.y
+ self
->frame
->area
.height
- 1);
826 POINT_SET(oldtr
, oldbr
.x
, oldtl
.y
);
827 POINT_SET(oldbl
, oldtl
.x
, oldbr
.y
);
829 POINT_SET(newtl
, *x
, *y
);
830 POINT_SET(newbr
, *x
+ fw
- 1, *y
+ fh
- 1);
831 POINT_SET(newtr
, newbr
.x
, newtl
.y
);
832 POINT_SET(newbl
, newtl
.x
, newbr
.y
);
834 /* is it moving or just resizing from some corner? */
835 stationary_l
= oldtl
.x
== oldtl
.x
;
836 stationary_r
= oldtr
.x
== oldtr
.x
;
837 stationary_t
= oldtl
.y
== oldtl
.y
;
838 stationary_b
= oldbl
.y
== oldbl
.y
;
840 /* if left edge is growing and didnt move right edge */
841 if (stationary_r
&& newtl
.x
< oldtl
.x
)
843 /* if right edge is growing and didnt move left edge */
844 if (stationary_l
&& newtr
.x
> oldtr
.x
)
846 /* if top edge is growing and didnt move bottom edge */
847 if (stationary_b
&& newtl
.y
< oldtl
.y
)
849 /* if bottom edge is growing and didnt move top edge */
850 if (stationary_t
&& newbl
.y
> oldbl
.y
)
854 /* This here doesn't let windows even a pixel outside the struts/screen.
855 * When called from client_manage, programs placing themselves are
856 * forced completely onscreen, while things like
857 * xterm -geometry resolution-width/2 will work fine. Trying to
858 * place it completely offscreen will be handled in the above code.
859 * Sorry for this confused comment, i am tired. */
860 if (fw
<= mon_a
->width
) {
861 if (rudel
&& !self
->strut
.left
&& *x
< mon_a
->x
) *x
= mon_a
->x
;
862 if (ruder
&& !self
->strut
.right
&& *x
+ fw
> mon_a
->x
+ mon_a
->width
)
863 *x
= mon_a
->x
+ mon_a
->width
- fw
;
865 if (fh
<= mon_a
->height
) {
866 if (rudet
&& !self
->strut
.top
&& *y
< mon_a
->y
) *y
= mon_a
->y
;
867 if (rudeb
&& !self
->strut
.bottom
&& *y
+ fh
> mon_a
->y
+ mon_a
->height
)
868 *y
= mon_a
->y
+ mon_a
->height
- fh
;
871 /* get where the client should be */
872 frame_frame_gravity(self
->frame
, x
, y
, w
, h
);
874 return ox
!= *x
|| oy
!= *y
;
877 static void client_toggle_border(ObClient
*self
, gboolean show
)
879 /* adjust our idea of where the client is, based on its border. When the
880 border is removed, the client should now be considered to be in a
882 when re-adding the border to the client, the same operation needs to be
884 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
885 gint x
= oldx
, y
= oldy
;
886 switch(self
->gravity
) {
888 case NorthWestGravity
:
890 case SouthWestGravity
:
892 case NorthEastGravity
:
894 case SouthEastGravity
:
895 if (show
) x
-= self
->border_width
* 2;
896 else x
+= self
->border_width
* 2;
903 if (show
) x
-= self
->border_width
;
904 else x
+= self
->border_width
;
907 switch(self
->gravity
) {
909 case NorthWestGravity
:
911 case NorthEastGravity
:
913 case SouthWestGravity
:
915 case SouthEastGravity
:
916 if (show
) y
-= self
->border_width
* 2;
917 else y
+= self
->border_width
* 2;
924 if (show
) y
-= self
->border_width
;
925 else y
+= self
->border_width
;
932 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
934 /* set border_width to 0 because there is no border to add into
935 calculations anymore */
936 self
->border_width
= 0;
938 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
942 static void client_get_all(ObClient
*self
)
944 client_get_area(self
);
945 client_get_mwm_hints(self
);
947 /* The transient-ness of a window is used to pick a type, but the type can
948 also affect transiency.
950 Dialogs are always made transients for their group if they have one.
952 I also have made non-application type windows be transients for their
953 group (eg utility windows).
955 client_get_transientness(self
);
956 client_get_type(self
);/* this can change the mwmhints for special cases */
957 client_get_state(self
);
959 client_update_wmhints(self
);
960 /* this may have already been called from client_update_wmhints */
961 if (self
->transient_for
== NULL
)
962 client_update_transient_for(self
);
963 client_get_startup_id(self
);
964 client_get_desktop(self
);/* uses transient data/group/startup id if a
965 desktop is not specified */
966 client_get_shaped(self
);
968 client_get_layer(self
); /* if layer hasn't been specified, get it from
969 other sources if possible */
972 /* a couple type-based defaults for new windows */
974 /* this makes sure that these windows appear on all desktops */
975 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
976 self
->desktop
= DESKTOP_ALL
;
979 client_update_protocols(self
);
981 client_get_gravity(self
); /* get the attribute gravity */
982 client_update_normal_hints(self
); /* this may override the attribute
985 /* got the type, the mwmhints, the protocols, and the normal hints
986 (min/max sizes), so we're ready to set up the decorations/functions */
987 client_setup_decor_and_functions(self
);
990 client_update_sync_request_counter(self
);
992 client_get_client_machine(self
);
993 client_get_colormap(self
);
994 client_update_title(self
);
995 client_update_class(self
);
996 client_update_sm_client_id(self
);
997 client_update_strut(self
);
998 client_update_icons(self
);
999 client_update_user_time(self
);
1000 client_update_icon_geometry(self
);
1003 static void client_get_startup_id(ObClient
*self
)
1005 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
1007 PROP_GETS(self
->group
->leader
,
1008 net_startup_id
, utf8
, &self
->startup_id
);
1011 static void client_get_area(ObClient
*self
)
1013 XWindowAttributes wattrib
;
1016 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1017 g_assert(ret
!= BadWindow
);
1019 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
1020 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
1021 self
->border_width
= wattrib
.border_width
;
1023 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
1024 wattrib
.width
, wattrib
.height
);
1027 static void client_get_desktop(ObClient
*self
)
1029 guint32 d
= screen_num_desktops
; /* an always-invalid value */
1031 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
1032 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
1033 self
->desktop
= screen_num_desktops
- 1;
1037 gboolean trdesk
= FALSE
;
1039 if (self
->transient_for
) {
1040 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1041 self
->desktop
= self
->transient_for
->desktop
;
1046 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1047 if (it
->data
!= self
&&
1048 !((ObClient
*)it
->data
)->transient_for
) {
1049 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
1056 /* try get from the startup-notification protocol */
1057 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1058 if (self
->desktop
>= screen_num_desktops
&&
1059 self
->desktop
!= DESKTOP_ALL
)
1060 self
->desktop
= screen_num_desktops
- 1;
1062 /* defaults to the current desktop */
1063 self
->desktop
= screen_desktop
;
1068 static void client_get_layer(ObClient
*self
)
1070 if (!(self
->above
|| self
->below
)) {
1072 /* apply stuff from the group */
1076 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1077 ObClient
*c
= it
->data
;
1078 if (c
!= self
&& !client_search_transient(self
, c
) &&
1079 client_normal(self
) && client_normal(c
))
1082 (c
->above
? 1 : (c
->below
? -1 : 0)));
1096 g_assert_not_reached();
1103 static void client_get_state(ObClient
*self
)
1108 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1110 for (i
= 0; i
< num
; ++i
) {
1111 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1113 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1114 self
->shaded
= TRUE
;
1115 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1116 self
->iconic
= TRUE
;
1117 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1118 self
->skip_taskbar
= TRUE
;
1119 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1120 self
->skip_pager
= TRUE
;
1121 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1122 self
->fullscreen
= TRUE
;
1123 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1124 self
->max_vert
= TRUE
;
1125 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1126 self
->max_horz
= TRUE
;
1127 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1129 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1131 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1132 self
->demands_attention
= TRUE
;
1133 else if (state
[i
] == prop_atoms
.openbox_wm_state_undecorated
)
1134 self
->undecorated
= TRUE
;
1141 static void client_get_shaped(ObClient
*self
)
1143 self
->shaped
= FALSE
;
1145 if (extensions_shape
) {
1150 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1152 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1153 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1155 self
->shaped
= (s
!= 0);
1160 void client_get_transientness(ObClient
*self
)
1163 if (XGetTransientForHint(ob_display
, self
->window
, &t
))
1164 self
->transient
= TRUE
;
1167 void client_update_transient_for(ObClient
*self
)
1170 ObClient
*target
= NULL
;
1172 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1173 self
->transient
= TRUE
;
1174 if (t
!= self
->window
) { /* cant be transient to itself! */
1175 target
= g_hash_table_lookup(window_map
, &t
);
1176 /* if this happens then we need to check for it*/
1177 g_assert(target
!= self
);
1178 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1179 /* this can happen when a dialog is a child of
1180 a dockapp, for example */
1184 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1186 Setting the transient_for to Root is actually illegal, however
1187 applications from time have done this to specify transient for
1190 Now you can do that by being a TYPE_DIALOG and not setting
1191 the transient_for hint at all on your window. But people still
1192 use Root, and Kwin is very strange in this regard.
1194 KWin 3.0 will not consider windows with transient_for set to
1195 Root as transient for their group *UNLESS* they are also modal.
1196 In that case, it will make them transient for the group. This
1197 leads to all sorts of weird behavior from KDE apps which are
1198 only tested in KWin. I'd like to follow their behavior just to
1199 make this work right with KDE stuff, but that seems wrong.
1201 if (!target
&& self
->group
) {
1202 /* not transient to a client, see if it is transient for a
1204 if (t
== RootWindow(ob_display
, ob_screen
)) {
1205 /* window is a transient for its group! */
1206 target
= OB_TRAN_GROUP
;
1210 } else if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1211 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1212 self
->type
== OB_CLIENT_TYPE_MENU
||
1213 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1215 self
->transient
= TRUE
;
1217 target
= OB_TRAN_GROUP
;
1219 self
->transient
= FALSE
;
1221 client_update_transient_tree(self
, self
->group
, self
->group
,
1222 self
->transient_for
, target
);
1223 self
->transient_for
= target
;
1227 static void client_update_transient_tree(ObClient
*self
,
1228 ObGroup
*oldgroup
, ObGroup
*newgroup
,
1229 ObClient
* oldparent
,
1230 ObClient
*newparent
)
1235 /* No change has occured */
1236 if (oldgroup
== newgroup
&& oldparent
== newparent
) return;
1238 /** Remove the client from the transient tree wherever it has changed **/
1240 /* If the window is becoming a direct transient for a window in its group
1241 then that window can't be a child of this window anymore */
1242 if (oldparent
!= newparent
&&
1243 newparent
!= NULL
&& newparent
!= OB_TRAN_GROUP
&&
1244 newparent
->transient_for
== OB_TRAN_GROUP
&&
1245 newgroup
!= NULL
&& newgroup
== oldgroup
)
1247 self
->transients
= g_slist_remove(self
->transients
, newparent
);
1251 /* If the group changed then we need to remove any old group transient
1252 windows from our children. But if we're transient for the group, then
1253 other group transients are not our children. */
1254 if (oldgroup
!= newgroup
&& oldgroup
!= NULL
&&
1255 oldparent
!= OB_TRAN_GROUP
)
1257 for (it
= self
->transients
; it
; it
= next
) {
1258 next
= g_slist_next(it
);
1260 if (c
->group
== oldgroup
)
1261 self
->transients
= g_slist_delete_link(self
->transients
, it
);
1265 /* If we used to be transient for a group and now we are not, or we're
1266 transient for a new group, then we need to remove ourselves from all
1268 if (oldparent
== OB_TRAN_GROUP
&& (oldgroup
!= newgroup
||
1269 oldparent
!= newparent
))
1271 for (it
= oldgroup
->members
; it
; it
= g_slist_next(it
)) {
1273 if (c
!= self
&& (!c
->transient_for
||
1274 c
->transient_for
!= OB_TRAN_GROUP
))
1275 c
->transients
= g_slist_remove(c
->transients
, self
);
1278 /* If we used to be transient for a single window and we are no longer
1279 transient for it, then we need to remove ourself from its children */
1280 else if (oldparent
!= NULL
&& oldparent
!= OB_TRAN_GROUP
&&
1281 oldparent
!= newparent
)
1282 oldparent
->transients
= g_slist_remove(oldparent
->transients
, self
);
1285 /** Re-add the client to the transient tree wherever it has changed **/
1287 /* If we're now transient for a group and we weren't transient for it
1288 before then we need to add ourselves to all our new parents */
1289 if (newparent
== OB_TRAN_GROUP
&& (oldgroup
!= newgroup
||
1290 oldparent
!= newparent
))
1292 for (it
= oldgroup
->members
; it
; it
= g_slist_next(it
)) {
1294 if (c
!= self
&& (!c
->transient_for
||
1295 c
->transient_for
!= OB_TRAN_GROUP
))
1296 c
->transients
= g_slist_prepend(c
->transients
, self
);
1299 /* If we are now transient for a single window which we weren't before,
1300 we need to add ourselves to its children
1302 WARNING: Cyclical transient ness is possible if two windows are
1303 transient for eachother.
1305 else if (newparent
!= NULL
&& newparent
!= OB_TRAN_GROUP
&&
1306 newparent
!= oldparent
&&
1307 /* don't make ourself its child if it is already our child */
1308 !client_is_direct_child(self
, newparent
))
1309 newparent
->transients
= g_slist_prepend(newparent
->transients
, self
);
1311 /* If the group changed then we need to add any new group transient
1312 windows to our children. But if we're transient for the group, then
1313 other group transients are not our children.
1315 WARNING: Cyclical transient-ness is possible. For e.g. if:
1316 A is transient for the group
1317 B is a member of the group and transient for A
1319 if (oldgroup
!= newgroup
&& newgroup
!= NULL
&&
1320 newparent
!= OB_TRAN_GROUP
)
1322 for (it
= newgroup
->members
; it
; it
= g_slist_next(it
)) {
1324 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
&&
1325 /* Don't make it our child if it is already our parent */
1326 !client_is_direct_child(c
, self
))
1328 self
->transients
= g_slist_prepend(self
->transients
, c
);
1334 static void client_get_mwm_hints(ObClient
*self
)
1339 self
->mwmhints
.flags
= 0; /* default to none */
1341 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1343 if (num
>= OB_MWM_ELEMENTS
) {
1344 self
->mwmhints
.flags
= hints
[0];
1345 self
->mwmhints
.functions
= hints
[1];
1346 self
->mwmhints
.decorations
= hints
[2];
1352 void client_get_type(ObClient
*self
)
1359 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1360 /* use the first value that we know about in the array */
1361 for (i
= 0; i
< num
; ++i
) {
1362 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1363 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1364 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1365 self
->type
= OB_CLIENT_TYPE_DOCK
;
1366 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1367 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1368 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1369 self
->type
= OB_CLIENT_TYPE_MENU
;
1370 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1371 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1372 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1373 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1374 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1375 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1376 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1377 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1378 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1379 /* prevent this window from getting any decor or
1381 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1382 OB_MWM_FLAG_DECORATIONS
);
1383 self
->mwmhints
.decorations
= 0;
1384 self
->mwmhints
.functions
= 0;
1386 if (self
->type
!= (ObClientType
) -1)
1387 break; /* grab the first legit type */
1392 if (self
->type
== (ObClientType
) -1) {
1393 /*the window type hint was not set, which means we either classify
1394 ourself as a normal window or a dialog, depending on if we are a
1396 if (self
->transient
)
1397 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1399 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1403 void client_update_protocols(ObClient
*self
)
1406 guint num_return
, i
;
1408 self
->focus_notify
= FALSE
;
1409 self
->delete_window
= FALSE
;
1411 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1412 for (i
= 0; i
< num_return
; ++i
) {
1413 if (proto
[i
] == prop_atoms
.wm_delete_window
)
1414 /* this means we can request the window to close */
1415 self
->delete_window
= TRUE
;
1416 else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1417 /* if this protocol is requested, then the window will be
1418 notified whenever we want it to receive focus */
1419 self
->focus_notify
= TRUE
;
1421 else if (proto
[i
] == prop_atoms
.net_wm_sync_request
)
1422 /* if this protocol is requested, then resizing the
1423 window will be synchronized between the frame and the
1425 self
->sync_request
= TRUE
;
1433 void client_update_sync_request_counter(ObClient
*self
)
1437 if (PROP_GET32(self
->window
, net_wm_sync_request_counter
, cardinal
, &i
)) {
1438 self
->sync_counter
= i
;
1440 self
->sync_counter
= None
;
1444 static void client_get_gravity(ObClient
*self
)
1446 XWindowAttributes wattrib
;
1449 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1450 g_assert(ret
!= BadWindow
);
1451 self
->gravity
= wattrib
.win_gravity
;
1454 void client_get_colormap(ObClient
*self
)
1456 XWindowAttributes wa
;
1458 if (XGetWindowAttributes(ob_display
, self
->window
, &wa
))
1459 client_update_colormap(self
, wa
.colormap
);
1462 void client_update_colormap(ObClient
*self
, Colormap colormap
)
1464 self
->colormap
= colormap
;
1467 void client_update_normal_hints(ObClient
*self
)
1471 gint oldgravity
= self
->gravity
;
1474 self
->min_ratio
= 0.0f
;
1475 self
->max_ratio
= 0.0f
;
1476 SIZE_SET(self
->size_inc
, 1, 1);
1477 SIZE_SET(self
->base_size
, 0, 0);
1478 SIZE_SET(self
->min_size
, 0, 0);
1479 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1481 /* get the hints from the window */
1482 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1483 /* normal windows can't request placement! har har
1484 if (!client_normal(self))
1486 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1488 if (size
.flags
& PWinGravity
) {
1489 self
->gravity
= size
.win_gravity
;
1491 /* if the client has a frame, i.e. has already been mapped and
1492 is changing its gravity */
1493 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1494 /* move our idea of the client's position based on its new
1496 client_convert_gravity(self
, oldgravity
,
1497 &self
->area
.x
, &self
->area
.y
,
1498 self
->area
.width
, self
->area
.height
);
1502 if (size
.flags
& PAspect
) {
1503 if (size
.min_aspect
.y
)
1505 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1506 if (size
.max_aspect
.y
)
1508 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1511 if (size
.flags
& PMinSize
)
1512 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1514 if (size
.flags
& PMaxSize
)
1515 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1517 if (size
.flags
& PBaseSize
)
1518 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1520 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1521 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1525 void client_setup_decor_and_functions(ObClient
*self
)
1527 /* start with everything (cept fullscreen) */
1529 (OB_FRAME_DECOR_TITLEBAR
|
1530 OB_FRAME_DECOR_HANDLE
|
1531 OB_FRAME_DECOR_GRIPS
|
1532 OB_FRAME_DECOR_BORDER
|
1533 OB_FRAME_DECOR_ICON
|
1534 OB_FRAME_DECOR_ALLDESKTOPS
|
1535 OB_FRAME_DECOR_ICONIFY
|
1536 OB_FRAME_DECOR_MAXIMIZE
|
1537 OB_FRAME_DECOR_SHADE
|
1538 OB_FRAME_DECOR_CLOSE
);
1540 (OB_CLIENT_FUNC_RESIZE
|
1541 OB_CLIENT_FUNC_MOVE
|
1542 OB_CLIENT_FUNC_ICONIFY
|
1543 OB_CLIENT_FUNC_MAXIMIZE
|
1544 OB_CLIENT_FUNC_SHADE
|
1545 OB_CLIENT_FUNC_CLOSE
);
1547 if (!(self
->min_size
.width
< self
->max_size
.width
||
1548 self
->min_size
.height
< self
->max_size
.height
))
1549 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1551 switch (self
->type
) {
1552 case OB_CLIENT_TYPE_NORMAL
:
1553 /* normal windows retain all of the possible decorations and
1554 functionality, and are the only windows that you can fullscreen */
1555 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1558 case OB_CLIENT_TYPE_DIALOG
:
1559 case OB_CLIENT_TYPE_UTILITY
:
1560 /* these windows cannot be maximized */
1561 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1564 case OB_CLIENT_TYPE_MENU
:
1565 case OB_CLIENT_TYPE_TOOLBAR
:
1566 /* these windows get less functionality */
1567 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1570 case OB_CLIENT_TYPE_DESKTOP
:
1571 case OB_CLIENT_TYPE_DOCK
:
1572 case OB_CLIENT_TYPE_SPLASH
:
1573 /* none of these windows are manipulated by the window manager */
1574 self
->decorations
= 0;
1575 self
->functions
= 0;
1579 /* Mwm Hints are applied subtractively to what has already been chosen for
1580 decor and functionality */
1581 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1582 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1583 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1584 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1586 /* if the mwm hints request no handle or title, then all
1587 decorations are disabled, but keep the border if that's
1589 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1590 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1592 self
->decorations
= 0;
1597 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1598 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1599 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1600 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1601 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1602 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1603 /* dont let mwm hints kill any buttons
1604 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1605 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1606 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1607 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1609 /* dont let mwm hints kill the close button
1610 if (! (self->mwmhints.functions & MwmFunc_Close))
1611 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1615 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1616 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1617 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1618 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1619 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1620 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1622 /* can't maximize without moving/resizing */
1623 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1624 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1625 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1626 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1627 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1630 /* kill the handle on fully maxed windows */
1631 if (self
->max_vert
&& self
->max_horz
)
1632 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1634 /* finally, the user can have requested no decorations, which overrides
1635 everything (but doesnt give it a border if it doesnt have one) */
1636 if (self
->undecorated
) {
1637 if (config_theme_keepborder
)
1638 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1640 self
->decorations
= 0;
1643 /* if we don't have a titlebar, then we cannot shade! */
1644 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1645 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1647 /* now we need to check against rules for the client's current state */
1648 if (self
->fullscreen
) {
1649 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1650 OB_CLIENT_FUNC_FULLSCREEN
|
1651 OB_CLIENT_FUNC_ICONIFY
);
1652 self
->decorations
= 0;
1655 client_change_allowed_actions(self
);
1658 /* adjust the client's decorations, etc. */
1659 client_reconfigure(self
);
1663 static void client_change_allowed_actions(ObClient
*self
)
1668 /* desktop windows are kept on all desktops */
1669 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1670 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1672 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1673 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1674 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1675 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1676 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1677 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1678 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1679 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1680 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1681 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1682 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1683 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1684 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1685 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1686 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1689 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1691 /* make sure the window isn't breaking any rules now */
1693 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1694 if (self
->frame
) client_shade(self
, FALSE
);
1695 else self
->shaded
= FALSE
;
1697 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1698 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1699 else self
->iconic
= FALSE
;
1701 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1702 if (self
->frame
) client_fullscreen(self
, FALSE
);
1703 else self
->fullscreen
= FALSE
;
1705 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1707 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1708 else self
->max_vert
= self
->max_horz
= FALSE
;
1712 void client_reconfigure(ObClient
*self
)
1714 /* by making this pass FALSE for user, we avoid the emacs event storm where
1715 every configurenotify causes an update in its normal hints, i think this
1716 is generally what we want anyways... */
1717 client_configure(self
, self
->area
.x
, self
->area
.y
,
1718 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1721 void client_update_wmhints(ObClient
*self
)
1725 /* assume a window takes input if it doesnt specify */
1726 self
->can_focus
= TRUE
;
1728 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1731 if (hints
->flags
& InputHint
)
1732 self
->can_focus
= hints
->input
;
1734 /* only do this when first managing the window *AND* when we aren't
1736 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1737 if (hints
->flags
& StateHint
)
1738 self
->iconic
= hints
->initial_state
== IconicState
;
1741 self
->urgent
= (hints
->flags
& XUrgencyHint
);
1742 if (self
->urgent
&& !ur
)
1743 client_hilite(self
, TRUE
);
1744 else if (!self
->urgent
&& ur
&& self
->demands_attention
)
1745 client_hilite(self
, FALSE
);
1747 if (!(hints
->flags
& WindowGroupHint
))
1748 hints
->window_group
= None
;
1750 /* did the group state change? */
1751 if (hints
->window_group
!=
1752 (self
->group
? self
->group
->leader
: None
))
1754 ObGroup
*oldgroup
= self
->group
;
1756 /* remove from the old group if there was one */
1757 if (self
->group
!= NULL
) {
1758 group_remove(self
->group
, self
);
1762 /* add ourself to the group if we have one */
1763 if (hints
->window_group
!= None
) {
1764 self
->group
= group_add(hints
->window_group
, self
);
1767 /* Put ourselves into the new group's transient tree, and remove
1768 ourselves from the old group's */
1769 client_update_transient_tree(self
, oldgroup
, self
->group
,
1770 self
->transient_for
,
1771 self
->transient_for
);
1773 /* Lastly, being in a group, or not, can change if the window is
1774 transient for anything.
1776 The logic for this is:
1777 self->transient = TRUE always if the window wants to be
1778 transient for something, even if transient_for was NULL because
1779 it wasn't in a group before.
1781 If transient_for was NULL and oldgroup was NULL we can assume
1782 that when we add the new group, it will become transient for
1785 If transient_for was OB_TRAN_GROUP, then it must have already
1786 had a group. If it is getting a new group, the above call to
1787 client_update_transient_tree has already taken care of
1788 everything ! If it is losing all group status then it will
1789 no longer be transient for anything and that needs to be
1792 if (self
->transient
&&
1793 ((self
->transient_for
== NULL
&& oldgroup
== NULL
) ||
1794 (self
->transient_for
== OB_TRAN_GROUP
&& !self
->group
)))
1795 client_update_transient_for(self
);
1798 /* the WM_HINTS can contain an icon */
1799 client_update_icons(self
);
1805 void client_update_title(ObClient
*self
)
1808 gchar
*visible
= NULL
;
1810 g_free(self
->title
);
1813 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1814 /* try old x stuff */
1815 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1816 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1817 if (self
->transient
) {
1819 GNOME alert windows are not given titles:
1820 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1822 data
= g_strdup("");
1824 data
= g_strdup("Unnamed Window");
1828 if (self
->client_machine
) {
1829 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1834 PROP_SETS(self
->window
, net_wm_visible_name
, visible
);
1835 self
->title
= visible
;
1838 frame_adjust_title(self
->frame
);
1840 /* update the icon title */
1842 g_free(self
->icon_title
);
1845 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1846 /* try old x stuff */
1847 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1848 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1849 data
= g_strdup(self
->title
);
1851 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1852 self
->icon_title
= data
;
1855 void client_update_class(ObClient
*self
)
1860 if (self
->name
) g_free(self
->name
);
1861 if (self
->class) g_free(self
->class);
1862 if (self
->role
) g_free(self
->role
);
1864 self
->name
= self
->class = self
->role
= NULL
;
1866 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1868 self
->name
= g_strdup(data
[0]);
1870 self
->class = g_strdup(data
[1]);
1875 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1878 if (self
->name
== NULL
) self
->name
= g_strdup("");
1879 if (self
->class == NULL
) self
->class = g_strdup("");
1880 if (self
->role
== NULL
) self
->role
= g_strdup("");
1883 void client_update_strut(ObClient
*self
)
1887 gboolean got
= FALSE
;
1890 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1894 STRUT_PARTIAL_SET(strut
,
1895 data
[0], data
[2], data
[1], data
[3],
1896 data
[4], data
[5], data
[8], data
[9],
1897 data
[6], data
[7], data
[10], data
[11]);
1903 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1909 /* use the screen's width/height */
1910 a
= screen_physical_area();
1912 STRUT_PARTIAL_SET(strut
,
1913 data
[0], data
[2], data
[1], data
[3],
1914 a
->y
, a
->y
+ a
->height
- 1,
1915 a
->x
, a
->x
+ a
->width
- 1,
1916 a
->y
, a
->y
+ a
->height
- 1,
1917 a
->x
, a
->x
+ a
->width
- 1);
1923 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1924 0, 0, 0, 0, 0, 0, 0, 0);
1926 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1927 self
->strut
= strut
;
1929 /* updating here is pointless while we're being mapped cuz we're not in
1930 the client list yet */
1932 screen_update_areas();
1936 void client_update_icons(ObClient
*self
)
1942 for (i
= 0; i
< self
->nicons
; ++i
)
1943 g_free(self
->icons
[i
].data
);
1944 if (self
->nicons
> 0)
1945 g_free(self
->icons
);
1948 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1949 /* figure out how many valid icons are in here */
1951 while (num
- i
> 2) {
1955 if (i
> num
|| w
*h
== 0) break;
1959 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1961 /* store the icons */
1963 for (j
= 0; j
< self
->nicons
; ++j
) {
1966 w
= self
->icons
[j
].width
= data
[i
++];
1967 h
= self
->icons
[j
].height
= data
[i
++];
1969 if (w
*h
== 0) continue;
1971 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1972 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1977 self
->icons
[j
].data
[t
] =
1978 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1979 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1980 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1981 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1990 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1991 if (hints
->flags
& IconPixmapHint
) {
1993 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1994 xerror_set_ignore(TRUE
);
1995 if (!RrPixmapToRGBA(ob_rr_inst
,
1997 (hints
->flags
& IconMaskHint
?
1998 hints
->icon_mask
: None
),
1999 &self
->icons
[self
->nicons
-1].width
,
2000 &self
->icons
[self
->nicons
-1].height
,
2001 &self
->icons
[self
->nicons
-1].data
)){
2002 g_free(&self
->icons
[self
->nicons
-1]);
2005 xerror_set_ignore(FALSE
);
2011 /* set the default icon onto the window
2012 in theory, this could be a race, but if a window doesn't set an icon
2013 or removes it entirely, it's not very likely it is going to set one
2014 right away afterwards */
2015 if (self
->nicons
== 0) {
2016 RrPixel32
*icon
= ob_rr_theme
->def_win_icon
;
2019 data
= g_new(gulong
, 48*48+2);
2020 data
[0] = data
[1] = 48;
2021 for (i
= 0; i
< 48*48; ++i
)
2022 data
[i
+2] = (((icon
[i
] >> RrDefaultAlphaOffset
) & 0xff) << 24) +
2023 (((icon
[i
] >> RrDefaultRedOffset
) & 0xff) << 16) +
2024 (((icon
[i
] >> RrDefaultGreenOffset
) & 0xff) << 8) +
2025 (((icon
[i
] >> RrDefaultBlueOffset
) & 0xff) << 0);
2026 PROP_SETA32(self
->window
, net_wm_icon
, cardinal
, data
, 48*48+2);
2028 } else if (self
->frame
)
2029 /* don't draw the icon empty if we're just setting one now anyways,
2030 we'll get the property change any second */
2031 frame_adjust_icon(self
->frame
);
2034 void client_update_user_time(ObClient
*self
)
2038 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
2039 /* we set this every time, not just when it grows, because in practice
2040 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
2041 backward we don't want all windows to stop focusing. we'll just
2042 assume noone is setting times older than the last one, cuz that
2043 would be pretty stupid anyways
2045 self
->user_time
= time
;
2048 ob_debug("window %s user time %u\n", self->title, time);
2053 void client_update_icon_geometry(ObClient
*self
)
2058 RECT_SET(self
->icon_geometry
, 0, 0, 0, 0);
2060 if (PROP_GETA32(self
->window
, net_wm_icon_geometry
, cardinal
, &data
, &num
)
2063 /* don't let them set it with an area < 0 */
2064 RECT_SET(self
->icon_geometry
, data
[0], data
[1],
2065 MAX(data
[2],0), MAX(data
[3],0));
2069 static void client_get_client_machine(ObClient
*self
)
2072 gchar localhost
[128];
2074 g_free(self
->client_machine
);
2076 if (PROP_GETS(self
->window
, wm_client_machine
, locale
, &data
)) {
2077 gethostname(localhost
, 127);
2078 localhost
[127] = '\0';
2079 if (strcmp(localhost
, data
))
2080 self
->client_machine
= data
;
2084 static void client_change_wm_state(ObClient
*self
)
2089 old
= self
->wmstate
;
2091 if (self
->shaded
|| !self
->frame
->visible
)
2092 self
->wmstate
= IconicState
;
2094 self
->wmstate
= NormalState
;
2096 if (old
!= self
->wmstate
) {
2097 PROP_MSG(self
->window
, kde_wm_change_state
,
2098 self
->wmstate
, 1, 0, 0);
2100 state
[0] = self
->wmstate
;
2102 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
2106 static void client_change_state(ObClient
*self
)
2108 gulong netstate
[11];
2113 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
2115 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
2117 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
2118 if (self
->skip_taskbar
)
2119 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
2120 if (self
->skip_pager
)
2121 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
2122 if (self
->fullscreen
)
2123 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
2125 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
2127 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
2129 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
2131 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
2132 if (self
->demands_attention
)
2133 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
2134 if (self
->undecorated
)
2135 netstate
[num
++] = prop_atoms
.openbox_wm_state_undecorated
;
2136 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
2139 frame_adjust_state(self
->frame
);
2142 ObClient
*client_search_focus_tree(ObClient
*self
)
2147 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2148 if (client_focused(it
->data
)) return it
->data
;
2149 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
2154 ObClient
*client_search_focus_tree_full(ObClient
*self
)
2156 if (self
->transient_for
) {
2157 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2158 return client_search_focus_tree_full(self
->transient_for
);
2161 gboolean recursed
= FALSE
;
2163 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
2164 if (!((ObClient
*)it
->data
)->transient_for
) {
2166 if ((c
= client_search_focus_tree_full(it
->data
)))
2175 /* this function checks the whole tree, the client_search_focus_tree~
2176 does not, so we need to check this window */
2177 if (client_focused(self
))
2179 return client_search_focus_tree(self
);
2182 static ObStackingLayer
calc_layer(ObClient
*self
)
2186 if ((self
->fullscreen
||
2187 /* no decorations and fills the monitor means oldskool fullscreen */
2188 (self
->frame
!= NULL
&&
2189 (self
->frame
->size
.top
== 0 && self
->frame
->size
.left
== 0 &&
2190 self
->frame
->size
.bottom
== 0 && self
->frame
->size
.right
== 0 &&
2191 RECT_EQUAL(self
->area
,
2192 *screen_physical_area_monitor(client_monitor(self
))))))
2194 (client_focused(self
) || client_search_focus_tree(self
)))
2195 l
= OB_STACKING_LAYER_FULLSCREEN
;
2196 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2197 l
= OB_STACKING_LAYER_DESKTOP
;
2198 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2199 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2200 else l
= OB_STACKING_LAYER_ABOVE
;
2202 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2203 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2204 else l
= OB_STACKING_LAYER_NORMAL
;
2209 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2210 ObStackingLayer min
, gboolean raised
)
2212 ObStackingLayer old
, own
;
2216 own
= calc_layer(self
);
2217 self
->layer
= MAX(own
, min
);
2219 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2220 client_calc_layer_recursive(it
->data
, orig
,
2222 raised
? raised
: self
->layer
!= old
);
2224 if (!raised
&& self
->layer
!= old
)
2225 if (orig
->frame
) { /* only restack if the original window is managed */
2226 stacking_remove(CLIENT_AS_WINDOW(self
));
2227 stacking_add(CLIENT_AS_WINDOW(self
));
2231 void client_calc_layer(ObClient
*self
)
2238 /* transients take on the layer of their parents */
2239 it
= client_search_all_top_parents(self
);
2241 for (; it
; it
= g_slist_next(it
))
2242 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2245 gboolean
client_should_show(ObClient
*self
)
2249 if (client_normal(self
) && screen_showing_desktop
)
2252 if (self->transient_for) {
2253 if (self->transient_for != OB_TRAN_GROUP)
2254 return client_should_show(self->transient_for);
2258 for (it = self->group->members; it; it = g_slist_next(it)) {
2259 ObClient *c = it->data;
2260 if (c != self && !c->transient_for) {
2261 if (client_should_show(c))
2268 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2274 void client_show(ObClient
*self
)
2277 if (client_should_show(self
)) {
2278 frame_show(self
->frame
);
2281 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2282 needs to be in IconicState. This includes when it is on another
2285 client_change_wm_state(self
);
2288 void client_hide(ObClient
*self
)
2290 if (!client_should_show(self
)) {
2291 frame_hide(self
->frame
);
2294 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2295 needs to be in IconicState. This includes when it is on another
2298 client_change_wm_state(self
);
2301 void client_showhide(ObClient
*self
)
2304 if (client_should_show(self
)) {
2305 frame_show(self
->frame
);
2308 frame_hide(self
->frame
);
2311 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2312 needs to be in IconicState. This includes when it is on another
2315 client_change_wm_state(self
);
2318 gboolean
client_normal(ObClient
*self
) {
2319 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2320 self
->type
== OB_CLIENT_TYPE_DOCK
||
2321 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2324 gboolean
client_application(ObClient
*self
)
2326 return (self
->type
== OB_CLIENT_TYPE_NORMAL
||
2327 self
->type
== OB_CLIENT_TYPE_DIALOG
);
2330 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2332 gboolean pos
= FALSE
; /* has the window's position been configured? */
2335 /* save the position, and set self->area for these to use */
2341 /* set the desktop hint, to make sure that it always exists */
2342 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
2344 /* these are in a carefully crafted order.. */
2347 self
->iconic
= FALSE
;
2348 client_iconify(self
, TRUE
, FALSE
);
2350 if (self
->fullscreen
) {
2351 self
->fullscreen
= FALSE
;
2352 client_fullscreen(self
, TRUE
);
2355 if (self
->undecorated
) {
2356 self
->undecorated
= FALSE
;
2357 client_set_undecorated(self
, TRUE
);
2360 self
->shaded
= FALSE
;
2361 client_shade(self
, TRUE
);
2363 if (self
->demands_attention
) {
2364 self
->demands_attention
= FALSE
;
2365 client_hilite(self
, TRUE
);
2368 if (self
->max_vert
&& self
->max_horz
) {
2369 self
->max_vert
= self
->max_horz
= FALSE
;
2370 client_maximize(self
, TRUE
, 0);
2372 } else if (self
->max_vert
) {
2373 self
->max_vert
= FALSE
;
2374 client_maximize(self
, TRUE
, 2);
2376 } else if (self
->max_horz
) {
2377 self
->max_horz
= FALSE
;
2378 client_maximize(self
, TRUE
, 1);
2382 /* if the client didn't get positioned yet, then do so now
2383 call client_move even if the window is not being moved anywhere, because
2384 when we reparent it and decorate it, it is getting moved and we need to
2385 be telling it so with a ConfigureNotify event.
2388 /* use the saved position */
2391 client_move(self
, x
, y
);
2394 /* nothing to do for the other states:
2403 void client_convert_gravity(ObClient
*self
, gint gravity
, gint
*x
, gint
*y
,
2406 gint oldg
= self
->gravity
;
2408 /* get the frame's position from the requested stuff */
2409 self
->gravity
= gravity
;
2410 frame_client_gravity(self
->frame
, x
, y
, w
, h
);
2411 self
->gravity
= oldg
;
2413 /* get the client's position in its true gravity from that */
2414 frame_frame_gravity(self
->frame
, x
, y
, w
, h
);
2417 void client_try_configure(ObClient
*self
, gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2418 gint
*logicalw
, gint
*logicalh
,
2421 Rect desired_area
= {*x
, *y
, *w
, *h
};
2423 /* make the frame recalculate its dimentions n shit without changing
2424 anything visible for real, this way the constraints below can work with
2425 the updated frame dimensions. */
2426 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2428 /* work within the prefered sizes given by the window */
2429 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2430 gint basew
, baseh
, minw
, minh
;
2432 /* base size is substituted with min size if not specified */
2433 if (self
->base_size
.width
|| self
->base_size
.height
) {
2434 basew
= self
->base_size
.width
;
2435 baseh
= self
->base_size
.height
;
2437 basew
= self
->min_size
.width
;
2438 baseh
= self
->min_size
.height
;
2440 /* min size is substituted with base size if not specified */
2441 if (self
->min_size
.width
|| self
->min_size
.height
) {
2442 minw
= self
->min_size
.width
;
2443 minh
= self
->min_size
.height
;
2445 minw
= self
->base_size
.width
;
2446 minh
= self
->base_size
.height
;
2449 /* if this is a user-requested resize, then check against min/max
2452 /* smaller than min size or bigger than max size? */
2453 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2454 if (*w
< minw
) *w
= minw
;
2455 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2456 if (*h
< minh
) *h
= minh
;
2461 /* keep to the increments */
2462 *w
/= self
->size_inc
.width
;
2463 *h
/= self
->size_inc
.height
;
2465 /* you cannot resize to nothing */
2466 if (basew
+ *w
< 1) *w
= 1 - basew
;
2467 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2469 /* save the logical size */
2470 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2471 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2473 *w
*= self
->size_inc
.width
;
2474 *h
*= self
->size_inc
.height
;
2479 /* adjust the height to match the width for the aspect ratios.
2480 for this, min size is not substituted for base size ever. */
2481 *w
-= self
->base_size
.width
;
2482 *h
-= self
->base_size
.height
;
2484 if (!self
->fullscreen
) {
2485 if (self
->min_ratio
)
2486 if (*h
* self
->min_ratio
> *w
) {
2487 *h
= (gint
)(*w
/ self
->min_ratio
);
2489 /* you cannot resize to nothing */
2492 *w
= (gint
)(*h
* self
->min_ratio
);
2495 if (self
->max_ratio
)
2496 if (*h
* self
->max_ratio
< *w
) {
2497 *h
= (gint
)(*w
/ self
->max_ratio
);
2499 /* you cannot resize to nothing */
2502 *w
= (gint
)(*h
* self
->min_ratio
);
2507 *w
+= self
->base_size
.width
;
2508 *h
+= self
->base_size
.height
;
2511 /* gets the frame's position */
2512 frame_client_gravity(self
->frame
, x
, y
, *w
, *h
);
2514 /* these positions are frame positions, not client positions */
2516 /* set the size and position if fullscreen */
2517 if (self
->fullscreen
) {
2521 i
= screen_find_monitor(&desired_area
);
2522 a
= screen_physical_area_monitor(i
);
2529 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2530 is entering fullscreen */
2531 } else if (self
->max_horz
|| self
->max_vert
) {
2535 i
= screen_find_monitor(&desired_area
);
2536 a
= screen_area_monitor(self
->desktop
, i
);
2538 /* set the size and position if maximized */
2539 if (self
->max_horz
) {
2541 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2543 if (self
->max_vert
) {
2545 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2548 /* maximizing is not allowed if the user can't move+resize the window
2552 /* gets the client's position */
2553 frame_frame_gravity(self
->frame
, x
, y
, *w
, *h
);
2555 /* these override the above states! if you cant move you can't move! */
2557 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2561 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2562 *w
= self
->area
.width
;
2563 *h
= self
->area
.height
;
2572 void client_configure_full(ObClient
*self
, gint x
, gint y
, gint w
, gint h
,
2573 gboolean user
, gboolean final
,
2574 gboolean force_reply
)
2576 gint oldw
, oldh
, oldrx
, oldry
;
2577 gboolean send_resize_client
;
2578 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2579 guint fdecor
= self
->frame
->decorations
;
2580 gboolean fhorz
= self
->frame
->max_horz
;
2581 gint logicalw
, logicalh
;
2583 /* find the new x, y, width, and height (and logical size) */
2584 client_try_configure(self
, &x
, &y
, &w
, &h
, &logicalw
, &logicalh
, user
);
2586 /* set the logical size if things changed */
2587 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2588 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2590 /* figure out if we moved or resized or what */
2591 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2592 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2594 oldw
= self
->area
.width
;
2595 oldh
= self
->area
.height
;
2596 RECT_SET(self
->area
, x
, y
, w
, h
);
2598 /* for app-requested resizes, always resize if 'resized' is true.
2599 for user-requested ones, only resize if final is true, or when
2600 resizing in redraw mode */
2601 send_resize_client
= ((!user
&& resized
) ||
2603 (resized
&& config_resize_redraw
))));
2605 /* if the client is enlarging, then resize the client before the frame */
2606 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
)) {
2607 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2608 /* resize the plate to show the client padding color underneath */
2609 frame_adjust_client_area(self
->frame
);
2612 /* find the frame's dimensions and move/resize it */
2613 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2614 moved
= resized
= TRUE
;
2615 if (moved
|| resized
)
2616 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2618 /* find the client's position relative to the root window */
2619 oldrx
= self
->root_pos
.x
;
2620 oldry
= self
->root_pos
.y
;
2621 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2622 self
->frame
->size
.left
-
2623 self
->border_width
) ||
2624 oldry
!= (signed)(self
->frame
->area
.y
+
2625 self
->frame
->size
.top
-
2626 self
->border_width
));
2628 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2632 POINT_SET(self
->root_pos
,
2633 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2635 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2636 self
->border_width
);
2638 event
.type
= ConfigureNotify
;
2639 event
.xconfigure
.display
= ob_display
;
2640 event
.xconfigure
.event
= self
->window
;
2641 event
.xconfigure
.window
= self
->window
;
2643 /* root window real coords */
2644 event
.xconfigure
.x
= self
->root_pos
.x
;
2645 event
.xconfigure
.y
= self
->root_pos
.y
;
2646 event
.xconfigure
.width
= w
;
2647 event
.xconfigure
.height
= h
;
2648 event
.xconfigure
.border_width
= 0;
2649 event
.xconfigure
.above
= self
->frame
->plate
;
2650 event
.xconfigure
.override_redirect
= FALSE
;
2651 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2652 FALSE
, StructureNotifyMask
, &event
);
2655 /* if the client is shrinking, then resize the frame before the client */
2656 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
))) {
2657 /* resize the plate to show the client padding color underneath */
2658 frame_adjust_client_area(self
->frame
);
2660 XResizeWindow(ob_display
, self
->window
, w
, h
);
2666 void client_fullscreen(ObClient
*self
, gboolean fs
)
2670 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2671 self
->fullscreen
== fs
) return; /* already done */
2673 self
->fullscreen
= fs
;
2674 client_change_state(self
); /* change the state hints on the client */
2675 client_calc_layer(self
); /* and adjust out layer/stacking */
2678 self
->pre_fullscreen_area
= self
->area
;
2679 /* if the window is maximized, its area isn't all that meaningful.
2680 save it's premax area instead. */
2681 if (self
->max_horz
) {
2682 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2683 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2685 if (self
->max_vert
) {
2686 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2687 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2690 /* these are not actually used cuz client_configure will set them
2691 as appropriate when the window is fullscreened */
2696 if (self
->pre_fullscreen_area
.width
> 0 &&
2697 self
->pre_fullscreen_area
.height
> 0)
2699 x
= self
->pre_fullscreen_area
.x
;
2700 y
= self
->pre_fullscreen_area
.y
;
2701 w
= self
->pre_fullscreen_area
.width
;
2702 h
= self
->pre_fullscreen_area
.height
;
2703 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2705 /* pick some fallbacks... */
2706 a
= screen_area_monitor(self
->desktop
, 0);
2707 x
= a
->x
+ a
->width
/ 4;
2708 y
= a
->y
+ a
->height
/ 4;
2714 client_setup_decor_and_functions(self
);
2716 client_move_resize(self
, x
, y
, w
, h
);
2718 /* try focus us when we go into fullscreen mode */
2722 static void client_iconify_recursive(ObClient
*self
,
2723 gboolean iconic
, gboolean curdesk
)
2726 gboolean changed
= FALSE
;
2729 if (self
->iconic
!= iconic
) {
2730 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2734 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2735 self
->iconic
= iconic
;
2737 /* update the focus lists.. iconic windows go to the bottom of
2738 the list, put the new iconic window at the 'top of the
2740 focus_order_to_top(self
);
2745 self
->iconic
= iconic
;
2747 if (curdesk
&& self
->desktop
!= screen_desktop
&&
2748 self
->desktop
!= DESKTOP_ALL
)
2749 client_set_desktop(self
, screen_desktop
, FALSE
);
2751 /* this puts it after the current focused window */
2752 focus_order_remove(self
);
2753 focus_order_add_new(self
);
2760 client_change_state(self
);
2761 if (ob_state() != OB_STATE_STARTING
&& config_animate_iconify
)
2762 frame_begin_iconify_animation(self
->frame
, iconic
);
2763 /* do this after starting the animation so it doesn't flash */
2764 client_showhide(self
);
2767 /* iconify all direct transients, and deiconify all transients
2769 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2770 if (it
->data
!= self
)
2771 if (client_is_direct_child(self
, it
->data
) || !iconic
)
2772 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2775 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2777 /* move up the transient chain as far as possible first */
2778 self
= client_search_top_normal_parent(self
);
2779 client_iconify_recursive(self
, iconic
, curdesk
);
2782 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2786 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2787 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2789 /* check if already done */
2791 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2792 if (dir
== 1 && self
->max_horz
) return;
2793 if (dir
== 2 && self
->max_vert
) return;
2795 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2796 if (dir
== 1 && !self
->max_horz
) return;
2797 if (dir
== 2 && !self
->max_vert
) return;
2800 /* we just tell it to configure in the same place and client_configure
2801 worries about filling the screen with the window */
2804 w
= self
->area
.width
;
2805 h
= self
->area
.height
;
2808 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2809 RECT_SET(self
->pre_max_area
,
2810 self
->area
.x
, self
->pre_max_area
.y
,
2811 self
->area
.width
, self
->pre_max_area
.height
);
2813 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2814 RECT_SET(self
->pre_max_area
,
2815 self
->pre_max_area
.x
, self
->area
.y
,
2816 self
->pre_max_area
.width
, self
->area
.height
);
2821 a
= screen_area_monitor(self
->desktop
, 0);
2822 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2823 if (self
->pre_max_area
.width
> 0) {
2824 x
= self
->pre_max_area
.x
;
2825 w
= self
->pre_max_area
.width
;
2827 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2828 0, self
->pre_max_area
.height
);
2830 /* pick some fallbacks... */
2831 x
= a
->x
+ a
->width
/ 4;
2835 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2836 if (self
->pre_max_area
.height
> 0) {
2837 y
= self
->pre_max_area
.y
;
2838 h
= self
->pre_max_area
.height
;
2840 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2841 self
->pre_max_area
.width
, 0);
2843 /* pick some fallbacks... */
2844 y
= a
->y
+ a
->height
/ 4;
2850 if (dir
== 0 || dir
== 1) /* horz */
2851 self
->max_horz
= max
;
2852 if (dir
== 0 || dir
== 2) /* vert */
2853 self
->max_vert
= max
;
2855 client_change_state(self
); /* change the state hints on the client */
2857 client_setup_decor_and_functions(self
);
2859 client_move_resize(self
, x
, y
, w
, h
);
2862 void client_shade(ObClient
*self
, gboolean shade
)
2864 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2865 shade
) || /* can't shade */
2866 self
->shaded
== shade
) return; /* already done */
2868 self
->shaded
= shade
;
2869 client_change_state(self
);
2870 client_change_wm_state(self
); /* the window is being hidden/shown */
2871 /* resize the frame to just the titlebar */
2872 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2875 void client_close(ObClient
*self
)
2879 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2881 /* in the case that the client provides no means to requesting that it
2882 close, we just kill it */
2883 if (!self
->delete_window
)
2887 XXX: itd be cool to do timeouts and shit here for killing the client's
2889 like... if the window is around after 5 seconds, then the close button
2890 turns a nice red, and if this function is called again, the client is
2894 ce
.xclient
.type
= ClientMessage
;
2895 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2896 ce
.xclient
.display
= ob_display
;
2897 ce
.xclient
.window
= self
->window
;
2898 ce
.xclient
.format
= 32;
2899 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2900 ce
.xclient
.data
.l
[1] = event_curtime
;
2901 ce
.xclient
.data
.l
[2] = 0l;
2902 ce
.xclient
.data
.l
[3] = 0l;
2903 ce
.xclient
.data
.l
[4] = 0l;
2904 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2907 void client_kill(ObClient
*self
)
2909 XKillClient(ob_display
, self
->window
);
2912 void client_hilite(ObClient
*self
, gboolean hilite
)
2914 if (self
->demands_attention
== hilite
)
2915 return; /* no change */
2917 /* don't allow focused windows to hilite */
2918 self
->demands_attention
= hilite
&& !client_focused(self
);
2919 if (self
->frame
!= NULL
) { /* if we're mapping, just set the state */
2920 if (self
->demands_attention
)
2921 frame_flash_start(self
->frame
);
2923 frame_flash_stop(self
->frame
);
2924 client_change_state(self
);
2928 void client_set_desktop_recursive(ObClient
*self
,
2929 guint target
, gboolean donthide
)
2934 if (target
!= self
->desktop
) {
2936 ob_debug("Setting desktop %u\n", target
+1);
2938 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2940 /* remove from the old desktop(s) */
2941 focus_order_remove(self
);
2943 old
= self
->desktop
;
2944 self
->desktop
= target
;
2945 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2946 /* the frame can display the current desktop state */
2947 frame_adjust_state(self
->frame
);
2948 /* 'move' the window to the new desktop */
2950 client_showhide(self
);
2951 /* raise if it was not already on the desktop */
2952 if (old
!= DESKTOP_ALL
)
2954 if (STRUT_EXISTS(self
->strut
))
2955 screen_update_areas();
2957 /* add to the new desktop(s) */
2958 if (config_focus_new
)
2959 focus_order_to_top(self
);
2961 focus_order_to_bottom(self
);
2964 /* move all transients */
2965 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2966 if (it
->data
!= self
)
2967 if (client_is_direct_child(self
, it
->data
))
2968 client_set_desktop_recursive(it
->data
, target
, donthide
);
2971 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2973 self
= client_search_top_normal_parent(self
);
2974 client_set_desktop_recursive(self
, target
, donthide
);
2977 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2979 while (child
!= parent
&&
2980 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2981 child
= child
->transient_for
;
2982 return child
== parent
;
2985 ObClient
*client_search_modal_child(ObClient
*self
)
2990 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2991 ObClient
*c
= it
->data
;
2992 if ((ret
= client_search_modal_child(c
))) return ret
;
2993 if (c
->modal
) return c
;
2998 gboolean
client_validate(ObClient
*self
)
3002 XSync(ob_display
, FALSE
); /* get all events on the server */
3004 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
3005 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
3006 XPutBackEvent(ob_display
, &e
);
3013 void client_set_wm_state(ObClient
*self
, glong state
)
3015 if (state
== self
->wmstate
) return; /* no change */
3019 client_iconify(self
, TRUE
, TRUE
);
3022 client_iconify(self
, FALSE
, TRUE
);
3027 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
3029 gboolean shaded
= self
->shaded
;
3030 gboolean fullscreen
= self
->fullscreen
;
3031 gboolean undecorated
= self
->undecorated
;
3032 gboolean max_horz
= self
->max_horz
;
3033 gboolean max_vert
= self
->max_vert
;
3034 gboolean modal
= self
->modal
;
3035 gboolean iconic
= self
->iconic
;
3036 gboolean demands_attention
= self
->demands_attention
;
3039 if (!(action
== prop_atoms
.net_wm_state_add
||
3040 action
== prop_atoms
.net_wm_state_remove
||
3041 action
== prop_atoms
.net_wm_state_toggle
))
3042 /* an invalid action was passed to the client message, ignore it */
3045 for (i
= 0; i
< 2; ++i
) {
3046 Atom state
= i
== 0 ? data1
: data2
;
3048 if (!state
) continue;
3050 /* if toggling, then pick whether we're adding or removing */
3051 if (action
== prop_atoms
.net_wm_state_toggle
) {
3052 if (state
== prop_atoms
.net_wm_state_modal
)
3053 action
= modal
? prop_atoms
.net_wm_state_remove
:
3054 prop_atoms
.net_wm_state_add
;
3055 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
3056 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
3057 prop_atoms
.net_wm_state_add
;
3058 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
3059 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
3060 prop_atoms
.net_wm_state_add
;
3061 else if (state
== prop_atoms
.net_wm_state_shaded
)
3062 action
= shaded
? prop_atoms
.net_wm_state_remove
:
3063 prop_atoms
.net_wm_state_add
;
3064 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
3065 action
= self
->skip_taskbar
?
3066 prop_atoms
.net_wm_state_remove
:
3067 prop_atoms
.net_wm_state_add
;
3068 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
3069 action
= self
->skip_pager
?
3070 prop_atoms
.net_wm_state_remove
:
3071 prop_atoms
.net_wm_state_add
;
3072 else if (state
== prop_atoms
.net_wm_state_hidden
)
3073 action
= self
->iconic
?
3074 prop_atoms
.net_wm_state_remove
:
3075 prop_atoms
.net_wm_state_add
;
3076 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
3077 action
= fullscreen
?
3078 prop_atoms
.net_wm_state_remove
:
3079 prop_atoms
.net_wm_state_add
;
3080 else if (state
== prop_atoms
.net_wm_state_above
)
3081 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
3082 prop_atoms
.net_wm_state_add
;
3083 else if (state
== prop_atoms
.net_wm_state_below
)
3084 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
3085 prop_atoms
.net_wm_state_add
;
3086 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
3087 action
= self
->demands_attention
?
3088 prop_atoms
.net_wm_state_remove
:
3089 prop_atoms
.net_wm_state_add
;
3090 else if (state
== prop_atoms
.openbox_wm_state_undecorated
)
3091 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
3092 prop_atoms
.net_wm_state_add
;
3095 if (action
== prop_atoms
.net_wm_state_add
) {
3096 if (state
== prop_atoms
.net_wm_state_modal
) {
3098 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
3100 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
3102 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
3104 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
3105 self
->skip_taskbar
= TRUE
;
3106 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
3107 self
->skip_pager
= TRUE
;
3108 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
3110 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
3112 } else if (state
== prop_atoms
.net_wm_state_above
) {
3114 self
->below
= FALSE
;
3115 } else if (state
== prop_atoms
.net_wm_state_below
) {
3116 self
->above
= FALSE
;
3118 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
3119 demands_attention
= TRUE
;
3120 } else if (state
== prop_atoms
.openbox_wm_state_undecorated
) {
3124 } else { /* action == prop_atoms.net_wm_state_remove */
3125 if (state
== prop_atoms
.net_wm_state_modal
) {
3127 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
3129 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
3131 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
3133 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
3134 self
->skip_taskbar
= FALSE
;
3135 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
3136 self
->skip_pager
= FALSE
;
3137 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
3139 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
3141 } else if (state
== prop_atoms
.net_wm_state_above
) {
3142 self
->above
= FALSE
;
3143 } else if (state
== prop_atoms
.net_wm_state_below
) {
3144 self
->below
= FALSE
;
3145 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
3146 demands_attention
= FALSE
;
3147 } else if (state
== prop_atoms
.openbox_wm_state_undecorated
) {
3148 undecorated
= FALSE
;
3152 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
3153 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
3155 if (max_horz
== max_vert
) { /* both going the same way */
3156 client_maximize(self
, max_horz
, 0);
3158 client_maximize(self
, max_horz
, 1);
3159 client_maximize(self
, max_vert
, 2);
3163 if (max_horz
!= self
->max_horz
)
3164 client_maximize(self
, max_horz
, 1);
3166 client_maximize(self
, max_vert
, 2);
3169 /* change fullscreen state before shading, as it will affect if the window
3171 if (fullscreen
!= self
->fullscreen
)
3172 client_fullscreen(self
, fullscreen
);
3173 if (shaded
!= self
->shaded
)
3174 client_shade(self
, shaded
);
3175 if (undecorated
!= self
->undecorated
)
3176 client_set_undecorated(self
, undecorated
);
3177 if (modal
!= self
->modal
) {
3178 self
->modal
= modal
;
3179 /* when a window changes modality, then its stacking order with its
3180 transients needs to change */
3183 if (iconic
!= self
->iconic
)
3184 client_iconify(self
, iconic
, FALSE
);
3186 if (demands_attention
!= self
->demands_attention
)
3187 client_hilite(self
, demands_attention
);
3189 client_change_state(self
); /* change the hint to reflect these changes */
3192 ObClient
*client_focus_target(ObClient
*self
)
3194 ObClient
*child
= NULL
;
3196 child
= client_search_modal_child(self
);
3197 if (child
) return child
;
3201 gboolean
client_can_focus(ObClient
*self
)
3205 /* choose the correct target */
3206 self
= client_focus_target(self
);
3208 if (!self
->frame
->visible
)
3211 if (!(self
->can_focus
|| self
->focus_notify
))
3214 /* do a check to see if the window has already been unmapped or destroyed
3215 do this intelligently while watching out for unmaps we've generated
3216 (ignore_unmaps > 0) */
3217 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
3218 DestroyNotify
, &ev
)) {
3219 XPutBackEvent(ob_display
, &ev
);
3222 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
3223 UnmapNotify
, &ev
)) {
3224 if (self
->ignore_unmaps
) {
3225 self
->ignore_unmaps
--;
3227 XPutBackEvent(ob_display
, &ev
);
3235 gboolean
client_focus(ObClient
*self
)
3237 /* choose the correct target */
3238 self
= client_focus_target(self
);
3240 if (!client_can_focus(self
)) {
3241 if (!self
->frame
->visible
) {
3242 /* update the focus lists */
3243 focus_order_to_top(self
);
3248 ob_debug_type(OB_DEBUG_FOCUS
,
3249 "Focusing client \"%s\" at time %u\n",
3250 self
->title
, event_curtime
);
3252 if (self
->can_focus
) {
3253 /* This can cause a BadMatch error with CurrentTime, or if an app
3254 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3255 xerror_set_ignore(TRUE
);
3256 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3258 xerror_set_ignore(FALSE
);
3261 if (self
->focus_notify
) {
3263 ce
.xclient
.type
= ClientMessage
;
3264 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3265 ce
.xclient
.display
= ob_display
;
3266 ce
.xclient
.window
= self
->window
;
3267 ce
.xclient
.format
= 32;
3268 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3269 ce
.xclient
.data
.l
[1] = event_curtime
;
3270 ce
.xclient
.data
.l
[2] = 0l;
3271 ce
.xclient
.data
.l
[3] = 0l;
3272 ce
.xclient
.data
.l
[4] = 0l;
3273 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3277 ob_debug("%sively focusing %lx at %d\n",
3278 (self
->can_focus
? "act" : "pass"),
3279 self
->window
, (gint
) event_curtime
);
3282 /* Cause the FocusIn to come back to us. Important for desktop switches,
3283 since otherwise we'll have no FocusIn on the queue and send it off to
3284 the focus_backup. */
3285 XSync(ob_display
, FALSE
);
3289 /*! Present the client to the user.
3290 @param raise If the client should be raised or not. You should only set
3291 raise to false if you don't care if the window is completely
3294 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
)
3296 /* if using focus_delay, stop the timer now so that focus doesn't
3298 event_halt_focus_delay();
3300 if (client_normal(self
) && screen_showing_desktop
)
3301 screen_show_desktop(FALSE
, FALSE
);
3303 client_iconify(self
, FALSE
, here
);
3304 if (self
->desktop
!= DESKTOP_ALL
&&
3305 self
->desktop
!= screen_desktop
)
3308 client_set_desktop(self
, screen_desktop
, FALSE
);
3310 screen_set_desktop(self
->desktop
, FALSE
);
3311 } else if (!self
->frame
->visible
)
3312 /* if its not visible for other reasons, then don't mess
3316 client_shade(self
, FALSE
);
3321 /* we do this as an action here. this is rather important. this is
3322 because we want the results from the focus change to take place
3323 BEFORE we go about raising the window. when a fullscreen window
3324 loses focus, we need this or else the raise wont be able to raise
3325 above the to-lose-focus fullscreen window. */
3330 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3332 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3334 /* XXX do some stuff here if user is false to determine if we really want
3335 to activate it or not (a parent or group member is currently
3338 ob_debug_type(OB_DEBUG_FOCUS
,
3339 "Want to activate window 0x%x with time %u (last time %u), "
3341 self
->window
, event_curtime
, last_time
,
3342 (user
? "user" : "application"));
3344 if (!user
&& event_curtime
&& last_time
&&
3345 !event_time_after(event_curtime
, last_time
))
3347 client_hilite(self
, TRUE
);
3349 if (event_curtime
!= CurrentTime
)
3350 self
->user_time
= event_curtime
;
3352 client_present(self
, here
, TRUE
);
3356 void client_raise(ObClient
*self
)
3358 action_run_string("Raise", self
, CurrentTime
);
3361 void client_lower(ObClient
*self
)
3363 action_run_string("Lower", self
, CurrentTime
);
3366 gboolean
client_focused(ObClient
*self
)
3368 return self
== focus_client
;
3371 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3374 /* si is the smallest image >= req */
3375 /* li is the largest image < req */
3376 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3378 if (!self
->nicons
) {
3379 ObClientIcon
*parent
= NULL
;
3381 if (self
->transient_for
) {
3382 if (self
->transient_for
!= OB_TRAN_GROUP
)
3383 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3386 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3387 ObClient
*c
= it
->data
;
3388 if (c
!= self
&& !c
->transient_for
) {
3389 if ((parent
= client_icon_recursive(c
, w
, h
)))
3399 for (i
= 0; i
< self
->nicons
; ++i
) {
3400 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3401 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3405 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3410 if (largest
== 0) /* didnt find one smaller than the requested size */
3411 return &self
->icons
[si
];
3412 return &self
->icons
[li
];
3415 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3418 static ObClientIcon deficon
;
3420 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3421 deficon
.width
= deficon
.height
= 48;
3422 deficon
.data
= ob_rr_theme
->def_win_icon
;
3428 void client_set_layer(ObClient
*self
, gint layer
)
3432 self
->above
= FALSE
;
3433 } else if (layer
== 0) {
3434 self
->below
= self
->above
= FALSE
;
3436 self
->below
= FALSE
;
3439 client_calc_layer(self
);
3440 client_change_state(self
); /* reflect this in the state hints */
3443 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3445 if (self
->undecorated
!= undecorated
) {
3446 self
->undecorated
= undecorated
;
3447 client_setup_decor_and_functions(self
);
3448 /* Make sure the client knows it might have moved. Maybe there is a
3449 * better way of doing this so only one client_configure is sent, but
3450 * since 125 of these are sent per second when moving the window (with
3451 * user = FALSE) i doubt it matters much.
3453 client_configure(self
, self
->area
.x
, self
->area
.y
,
3454 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3455 client_change_state(self
); /* reflect this in the state hints */
3459 guint
client_monitor(ObClient
*self
)
3461 return screen_find_monitor(&self
->frame
->area
);
3464 ObClient
*client_search_top_normal_parent(ObClient
*self
)
3466 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3467 client_normal(self
->transient_for
))
3468 self
= self
->transient_for
;
3472 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
3474 ObStackingLayer layer
)
3478 /* move up the direct transient chain as far as possible */
3479 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3480 (!bylayer
|| self
->transient_for
->layer
== layer
) &&
3481 client_normal(self
->transient_for
))
3482 self
= self
->transient_for
;
3484 if (!self
->transient_for
)
3485 ret
= g_slist_prepend(ret
, self
);
3489 g_assert(self
->group
);
3491 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3492 ObClient
*c
= it
->data
;
3494 if (!c
->transient_for
&& client_normal(c
) &&
3495 (!bylayer
|| c
->layer
== layer
))
3497 ret
= g_slist_prepend(ret
, c
);
3501 if (ret
== NULL
) /* no group parents */
3502 ret
= g_slist_prepend(ret
, self
);
3508 GSList
*client_search_all_top_parents(ObClient
*self
)
3510 return client_search_all_top_parents_internal(self
, FALSE
, 0);
3513 GSList
*client_search_all_top_parents_layer(ObClient
*self
)
3515 return client_search_all_top_parents_internal(self
, TRUE
, self
->layer
);
3518 ObClient
*client_search_focus_parent(ObClient
*self
)
3520 if (self
->transient_for
) {
3521 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3522 if (client_focused(self
->transient_for
))
3523 return self
->transient_for
;
3527 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3528 ObClient
*c
= it
->data
;
3530 /* checking transient_for prevents infinate loops! */
3531 if (c
!= self
&& !c
->transient_for
)
3532 if (client_focused(c
))
3541 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3543 if (self
->transient_for
) {
3544 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3545 if (self
->transient_for
== search
)
3550 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3551 ObClient
*c
= it
->data
;
3553 /* checking transient_for prevents infinate loops! */
3554 if (c
!= self
&& !c
->transient_for
)
3564 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3568 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3569 if (sit
->data
== search
)
3571 if (client_search_transient(sit
->data
, search
))
3577 void client_update_sm_client_id(ObClient
*self
)
3579 g_free(self
->sm_client_id
);
3580 self
->sm_client_id
= NULL
;
3582 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3584 ob_debug_type(OB_DEBUG_SM
, "Client %s does not have session id\n",
3586 if (!PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3587 &self
->sm_client_id
)) {
3588 ob_debug_type(OB_DEBUG_SM
, "Client %s does not have session id on "
3589 "group window\n", self
->title
);
3591 ob_debug_type(OB_DEBUG_SM
, "Client %s has session id on "
3592 "group window\n", self
->title
);
3594 ob_debug_type(OB_DEBUG_SM
, "Client %s has session id\n",
3598 #define WANT_EDGE(cur, c) \
3601 if(!client_normal(cur)) \
3603 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3607 if(cur->layer < c->layer && !config_resist_layers_below) \
3610 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3611 if ((his_edge_start >= my_edge_start && \
3612 his_edge_start <= my_edge_end) || \
3613 (my_edge_start >= his_edge_start && \
3614 my_edge_start <= his_edge_end)) \
3617 /* finds the nearest edge in the given direction from the current client
3618 * note to self: the edge is the -frame- edge (the actual one), not the
3621 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3623 gint dest
, monitor_dest
;
3624 gint my_edge_start
, my_edge_end
, my_offset
;
3631 a
= screen_area(c
->desktop
);
3632 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3635 case OB_DIRECTION_NORTH
:
3636 my_edge_start
= c
->frame
->area
.x
;
3637 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3638 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3640 /* default: top of screen */
3641 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3642 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3643 /* if the monitor edge comes before the screen edge, */
3644 /* use that as the destination instead. (For xinerama) */
3645 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3646 dest
= monitor_dest
;
3648 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3649 gint his_edge_start
, his_edge_end
, his_offset
;
3650 ObClient
*cur
= it
->data
;
3654 his_edge_start
= cur
->frame
->area
.x
;
3655 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3656 his_offset
= cur
->frame
->area
.y
+
3657 (hang
? 0 : cur
->frame
->area
.height
);
3659 if(his_offset
+ 1 > my_offset
)
3662 if(his_offset
< dest
)
3665 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3668 case OB_DIRECTION_SOUTH
:
3669 my_edge_start
= c
->frame
->area
.x
;
3670 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3671 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3673 /* default: bottom of screen */
3674 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3675 monitor_dest
= monitor
->y
+ monitor
->height
-
3676 (hang
? c
->frame
->area
.height
: 0);
3677 /* if the monitor edge comes before the screen edge, */
3678 /* use that as the destination instead. (For xinerama) */
3679 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3680 dest
= monitor_dest
;
3682 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3683 gint his_edge_start
, his_edge_end
, his_offset
;
3684 ObClient
*cur
= it
->data
;
3688 his_edge_start
= cur
->frame
->area
.x
;
3689 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3690 his_offset
= cur
->frame
->area
.y
+
3691 (hang
? cur
->frame
->area
.height
: 0);
3694 if(his_offset
- 1 < my_offset
)
3697 if(his_offset
> dest
)
3700 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3703 case OB_DIRECTION_WEST
:
3704 my_edge_start
= c
->frame
->area
.y
;
3705 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3706 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3708 /* default: leftmost egde of screen */
3709 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3710 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3711 /* if the monitor edge comes before the screen edge, */
3712 /* use that as the destination instead. (For xinerama) */
3713 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3714 dest
= monitor_dest
;
3716 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3717 gint his_edge_start
, his_edge_end
, his_offset
;
3718 ObClient
*cur
= it
->data
;
3722 his_edge_start
= cur
->frame
->area
.y
;
3723 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3724 his_offset
= cur
->frame
->area
.x
+
3725 (hang
? 0 : cur
->frame
->area
.width
);
3727 if(his_offset
+ 1 > my_offset
)
3730 if(his_offset
< dest
)
3733 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3736 case OB_DIRECTION_EAST
:
3737 my_edge_start
= c
->frame
->area
.y
;
3738 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3739 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3741 /* default: rightmost edge of screen */
3742 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3743 monitor_dest
= monitor
->x
+ monitor
->width
-
3744 (hang
? c
->frame
->area
.width
: 0);
3745 /* if the monitor edge comes before the screen edge, */
3746 /* use that as the destination instead. (For xinerama) */
3747 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3748 dest
= monitor_dest
;
3750 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3751 gint his_edge_start
, his_edge_end
, his_offset
;
3752 ObClient
*cur
= it
->data
;
3756 his_edge_start
= cur
->frame
->area
.y
;
3757 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3758 his_offset
= cur
->frame
->area
.x
+
3759 (hang
? cur
->frame
->area
.width
: 0);
3761 if(his_offset
- 1 < my_offset
)
3764 if(his_offset
> dest
)
3767 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3770 case OB_DIRECTION_NORTHEAST
:
3771 case OB_DIRECTION_SOUTHEAST
:
3772 case OB_DIRECTION_NORTHWEST
:
3773 case OB_DIRECTION_SOUTHWEST
:
3774 /* not implemented */
3776 g_assert_not_reached();
3777 dest
= 0; /* suppress warning */
3782 ObClient
* client_under_pointer()
3786 ObClient
*ret
= NULL
;
3788 if (screen_pointer_pos(&x
, &y
)) {
3789 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3790 if (WINDOW_IS_CLIENT(it
->data
)) {
3791 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3792 if (c
->frame
->visible
&&
3793 /* ignore all animating windows */
3794 !frame_iconify_animating(c
->frame
) &&
3795 RECT_CONTAINS(c
->frame
->area
, x
, y
))
3806 gboolean
client_has_group_siblings(ObClient
*self
)
3808 return self
->group
&& self
->group
->members
->next
;
3811 gboolean
client_has_application_group_siblings(ObClient
*self
)
3815 if (!self
->group
) return FALSE
;
3817 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3818 ObClient
*c
= it
->data
;
3819 if (c
!= self
&& client_application(c
))