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"
40 #include "menuframe.h"
43 #include "render/render.h"
50 #include <X11/Xutil.h>
52 /*! The event mask to grab on client windows */
53 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
56 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
61 ObClientCallback func
;
65 GList
*client_list
= NULL
;
67 static GSList
*client_destructors
= NULL
;
69 static void client_get_all(ObClient
*self
, gboolean real
);
70 static void client_toggle_border(ObClient
*self
, gboolean show
);
71 static void client_get_startup_id(ObClient
*self
);
72 static void client_get_session_ids(ObClient
*self
);
73 static void client_get_area(ObClient
*self
);
74 static void client_get_desktop(ObClient
*self
);
75 static void client_get_state(ObClient
*self
);
76 static void client_get_shaped(ObClient
*self
);
77 static void client_get_mwm_hints(ObClient
*self
);
78 static void client_get_colormap(ObClient
*self
);
79 static void client_change_allowed_actions(ObClient
*self
);
80 static void client_change_state(ObClient
*self
);
81 static void client_change_wm_state(ObClient
*self
);
82 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
83 static void client_restore_session_state(ObClient
*self
);
84 static gboolean
client_restore_session_stacking(ObClient
*self
);
85 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
86 static void client_update_transient_tree(ObClient
*self
,
87 ObGroup
*oldgroup
, ObGroup
*newgroup
,
90 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
);
91 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
93 ObStackingLayer layer
);
95 void client_startup(gboolean reconfig
)
102 void client_shutdown(gboolean reconfig
)
104 if (reconfig
) return;
107 void client_add_destructor(ObClientCallback func
, gpointer data
)
109 ClientCallback
*d
= g_new(ClientCallback
, 1);
112 client_destructors
= g_slist_prepend(client_destructors
, d
);
115 void client_remove_destructor(ObClientCallback func
)
119 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
120 ClientCallback
*d
= it
->data
;
121 if (d
->func
== func
) {
123 client_destructors
= g_slist_delete_link(client_destructors
, it
);
129 void client_set_list()
131 Window
*windows
, *win_it
;
133 guint size
= g_list_length(client_list
);
135 /* create an array of the window ids */
137 windows
= g_new(Window
, size
);
139 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
140 *win_it
= ((ObClient
*)it
->data
)->window
;
144 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
145 net_client_list
, window
, (gulong
*)windows
, size
);
154 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
158 for (it = self->transients; it; it = g_slist_next(it)) {
159 if (!func(it->data, data)) return;
160 client_foreach_transient(it->data, func, data);
164 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
166 if (self->transient_for) {
167 if (self->transient_for != OB_TRAN_GROUP) {
168 if (!func(self->transient_for, data)) return;
169 client_foreach_ancestor(self->transient_for, func, data);
173 for (it = self->group->members; it; it = g_slist_next(it))
174 if (it->data != self &&
175 !((ObClient*)it->data)->transient_for) {
176 if (!func(it->data, data)) return;
177 client_foreach_ancestor(it->data, func, data);
184 void client_manage_all()
189 XWindowAttributes attrib
;
191 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
192 &w
, &w
, &children
, &nchild
);
194 /* remove all icon windows from the list */
195 for (i
= 0; i
< nchild
; i
++) {
196 if (children
[i
] == None
) continue;
197 wmhints
= XGetWMHints(ob_display
, children
[i
]);
199 if ((wmhints
->flags
& IconWindowHint
) &&
200 (wmhints
->icon_window
!= children
[i
]))
201 for (j
= 0; j
< nchild
; j
++)
202 if (children
[j
] == wmhints
->icon_window
) {
210 for (i
= 0; i
< nchild
; ++i
) {
211 if (children
[i
] == None
)
213 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
214 if (attrib
.override_redirect
) continue;
216 if (attrib
.map_state
!= IsUnmapped
)
217 client_manage(children
[i
]);
223 void client_manage(Window window
)
227 XWindowAttributes attrib
;
228 XSetWindowAttributes attrib_set
;
230 gboolean activate
= FALSE
;
231 ObAppSettings
*settings
;
236 /* check if it has already been unmapped by the time we started
237 mapping. the grab does a sync so we don't have to here */
238 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
239 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
241 XPutBackEvent(ob_display
, &e
);
243 ob_debug("Trying to manage unmapped window. Aborting that.\n");
245 return; /* don't manage it */
248 /* make sure it isn't an override-redirect window */
249 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
250 attrib
.override_redirect
)
253 return; /* don't manage it */
256 /* is the window a docking app */
257 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
258 if ((wmhint
->flags
& StateHint
) &&
259 wmhint
->initial_state
== WithdrawnState
)
261 dock_add(window
, wmhint
);
269 ob_debug("Managing window: %lx\n", window
);
271 /* choose the events we want to receive on the CLIENT window */
272 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
273 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
274 XChangeWindowAttributes(ob_display
, window
,
275 CWEventMask
|CWDontPropagate
, &attrib_set
);
277 /* create the ObClient struct, and populate it from the hints on the
279 self
= g_new0(ObClient
, 1);
280 self
->obwin
.type
= Window_Client
;
281 self
->window
= window
;
283 /* non-zero defaults */
284 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
286 self
->desktop
= screen_num_desktops
; /* always an invalid value */
287 self
->user_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
289 client_get_all(self
, TRUE
);
290 /* per-app settings override stuff, and return the settings for other
292 settings
= client_get_settings_state(self
);
293 /* the session should get the last say */
294 client_restore_session_state(self
);
296 client_setup_decor_and_functions(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
313 should 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
);
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
))
455 stacking_raise(CLIENT_AS_WINDOW(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);
490 ObClient
*client_fake_manage(Window window
)
493 ObAppSettings
*settings
;
495 ob_debug("Pretend-managing window: %lx\n", window
);
497 /* do this minimal stuff to figure out the client's decorations */
499 self
= g_new0(ObClient
, 1);
500 self
->window
= window
;
502 client_get_all(self
, FALSE
);
503 /* per-app settings override stuff, and return the settings for other
505 settings
= client_get_settings_state(self
);
507 client_setup_decor_and_functions(self
);
509 /* create the decoration frame for the client window and adjust its size */
510 self
->frame
= frame_new(self
);
511 frame_adjust_area(self
->frame
, FALSE
, TRUE
, TRUE
);
515 void client_unmanage_all()
517 while (client_list
!= NULL
)
518 client_unmanage(client_list
->data
);
521 void client_unmanage(ObClient
*self
)
526 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
,
527 self
->class, self
->title
? self
->title
: "");
529 g_assert(self
!= NULL
);
531 /* we dont want events no more. do this before hiding the frame so we
532 don't generate more events */
533 XSelectInput(ob_display
, self
->window
, NoEventMask
);
535 frame_hide(self
->frame
);
536 /* flush to send the hide to the server quickly */
539 /* ignore enter events from the unmap so it doesnt mess with the
541 event_ignore_queued_enters();
543 mouse_grab_for_client(self
, FALSE
);
545 /* remove the window from our save set */
546 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
548 /* kill the property windows */
549 propwin_remove(self
->user_time_window
, OB_PROPWIN_USER_TIME
, self
);
551 /* update the focus lists */
552 focus_order_remove(self
);
553 if (client_focused(self
)) {
554 /* don't leave an invalid focus_client */
558 client_list
= g_list_remove(client_list
, self
);
559 stacking_remove(self
);
560 g_hash_table_remove(window_map
, &self
->window
);
562 /* once the client is out of the list, update the struts to remove its
564 if (STRUT_EXISTS(self
->strut
))
565 screen_update_areas();
567 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
568 ClientCallback
*d
= it
->data
;
569 d
->func(self
, d
->data
);
572 /* tell our parent(s) that we're gone */
573 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
574 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
575 if (it
->data
!= self
)
576 ((ObClient
*)it
->data
)->transients
=
577 g_slist_remove(((ObClient
*)it
->data
)->transients
,self
);
578 } else if (self
->transient_for
) { /* transient of window */
579 self
->transient_for
->transients
=
580 g_slist_remove(self
->transient_for
->transients
, self
);
583 /* tell our transients that we're gone */
584 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
585 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
586 ((ObClient
*)it
->data
)->transient_for
= NULL
;
587 client_calc_layer(it
->data
);
591 /* remove from its group */
593 group_remove(self
->group
, self
);
597 /* restore the window's original geometry so it is not lost */
601 if (self
->fullscreen
)
602 a
= self
->pre_fullscreen_area
;
603 else if (self
->max_horz
|| self
->max_vert
) {
604 if (self
->max_horz
) {
605 a
.x
= self
->pre_max_area
.x
;
606 a
.width
= self
->pre_max_area
.width
;
608 if (self
->max_vert
) {
609 a
.y
= self
->pre_max_area
.y
;
610 a
.height
= self
->pre_max_area
.height
;
614 /* give the client its border back */
615 client_toggle_border(self
, TRUE
);
617 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
618 self
->decorations
= 0; /* unmanaged windows have no decor */
620 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
623 /* reparent the window out of the frame, and free the frame */
624 frame_release_client(self
->frame
);
625 frame_free(self
->frame
);
628 if (ob_state() != OB_STATE_EXITING
) {
629 /* these values should not be persisted across a window
631 PROP_ERASE(self
->window
, net_wm_desktop
);
632 PROP_ERASE(self
->window
, net_wm_state
);
633 PROP_ERASE(self
->window
, wm_state
);
635 /* if we're left in an unmapped state, the client wont be mapped.
636 this is bad, since we will no longer be managing the window on
638 XMapWindow(ob_display
, self
->window
);
641 /* update the list hints */
644 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
646 /* free all data allocated in the client struct */
647 g_slist_free(self
->transients
);
648 for (j
= 0; j
< self
->nicons
; ++j
)
649 g_free(self
->icons
[j
].data
);
650 if (self
->nicons
> 0)
652 g_free(self
->wm_command
);
654 g_free(self
->icon_title
);
658 g_free(self
->client_machine
);
659 g_free(self
->sm_client_id
);
663 void client_fake_unmanage(ObClient
*self
)
665 /* this is all that got allocated to get the decorations */
667 frame_free(self
->frame
);
671 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
673 ObAppSettings
*settings
= NULL
;
676 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
677 ObAppSettings
*app
= it
->data
;
679 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
680 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
681 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
682 && !strcmp(app
->name
, self
->name
)))
684 /* Match if no role was specified in the per app setting, or if the
685 * string matches the beginning of the role, since apps like to set
686 * the role to things like browser-window-23c4b2f */
688 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
690 ob_debug("Window matching: %s\n", app
->name
);
699 if (settings
->shade
!= -1)
700 self
->shaded
= !!settings
->shade
;
701 if (settings
->decor
!= -1)
702 self
->undecorated
= !settings
->decor
;
703 if (settings
->iconic
!= -1)
704 self
->iconic
= !!settings
->iconic
;
705 if (settings
->skip_pager
!= -1)
706 self
->skip_pager
= !!settings
->skip_pager
;
707 if (settings
->skip_taskbar
!= -1)
708 self
->skip_taskbar
= !!settings
->skip_taskbar
;
710 if (settings
->max_vert
!= -1)
711 self
->max_vert
= !!settings
->max_vert
;
712 if (settings
->max_horz
!= -1)
713 self
->max_horz
= !!settings
->max_horz
;
715 if (settings
->fullscreen
!= -1)
716 self
->fullscreen
= !!settings
->fullscreen
;
718 if (settings
->desktop
) {
719 if (settings
->desktop
== DESKTOP_ALL
)
720 self
->desktop
= settings
->desktop
;
721 else if (settings
->desktop
> 0 &&
722 settings
->desktop
<= screen_num_desktops
)
723 self
->desktop
= settings
->desktop
- 1;
726 if (settings
->layer
== -1) {
730 else if (settings
->layer
== 0) {
734 else if (settings
->layer
== 1) {
742 static void client_restore_session_state(ObClient
*self
)
746 ob_debug_type(OB_DEBUG_SM
,
747 "Restore session for client %s\n", self
->title
);
749 if (!(it
= session_state_find(self
))) {
750 ob_debug_type(OB_DEBUG_SM
,
751 "Session data not found for client %s\n", self
->title
);
755 self
->session
= it
->data
;
757 ob_debug_type(OB_DEBUG_SM
, "Session data loaded for client %s\n",
760 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
761 self
->positioned
= USPosition
;
762 if (self
->session
->w
> 0)
763 self
->area
.width
= self
->session
->w
;
764 if (self
->session
->h
> 0)
765 self
->area
.height
= self
->session
->h
;
766 XResizeWindow(ob_display
, self
->window
,
767 self
->area
.width
, self
->area
.height
);
769 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
770 self
->session
->desktop
:
771 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
772 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
774 self
->shaded
= self
->session
->shaded
;
775 self
->iconic
= self
->session
->iconic
;
776 self
->skip_pager
= self
->session
->skip_pager
;
777 self
->skip_taskbar
= self
->session
->skip_taskbar
;
778 self
->fullscreen
= self
->session
->fullscreen
;
779 self
->above
= self
->session
->above
;
780 self
->below
= self
->session
->below
;
781 self
->max_horz
= self
->session
->max_horz
;
782 self
->max_vert
= self
->session
->max_vert
;
783 self
->undecorated
= self
->session
->undecorated
;
786 static gboolean
client_restore_session_stacking(ObClient
*self
)
790 if (!self
->session
) return FALSE
;
792 mypos
= g_list_find(session_saved_state
, self
->session
);
793 if (!mypos
) return FALSE
;
795 /* start above me and look for the first client */
796 for (it
= g_list_previous(mypos
); it
; it
= g_list_previous(it
)) {
799 for (cit
= client_list
; cit
; cit
= g_list_next(cit
)) {
800 ObClient
*c
= cit
->data
;
801 /* found a client that was in the session, so go below it */
802 if (c
->session
== it
->data
) {
803 stacking_below(CLIENT_AS_WINDOW(self
),
804 CLIENT_AS_WINDOW(cit
->data
));
812 void client_move_onscreen(ObClient
*self
, gboolean rude
)
814 gint x
= self
->area
.x
;
815 gint y
= self
->area
.y
;
816 if (client_find_onscreen(self
, &x
, &y
,
818 self
->area
.height
, rude
)) {
819 client_move(self
, x
, y
);
823 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
827 gint ox
= *x
, oy
= *y
;
828 gboolean rudel
= rude
, ruder
= rude
, rudet
= rude
, rudeb
= rude
;
831 all_a
= screen_area(self
->desktop
);
832 mon_a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
834 /* get where the frame would be */
835 frame_client_gravity(self
->frame
, x
, y
, w
, h
);
837 /* get the requested size of the window with decorations */
838 fw
= self
->frame
->size
.left
+ w
+ self
->frame
->size
.right
;
839 fh
= self
->frame
->size
.top
+ h
+ self
->frame
->size
.bottom
;
841 /* This makes sure windows aren't entirely outside of the screen so you
842 can't see them at all.
843 It makes sure 10% of the window is on the screen at least. At don't let
844 it move itself off the top of the screen, which would hide the titlebar
845 on you. (The user can still do this if they want too, it's only limiting
848 XXX watch for xinerama dead areas...
850 if (client_normal(self
)) {
851 if (!self
->strut
.right
&& *x
+ fw
/10 >= all_a
->x
+ all_a
->width
- 1)
852 *x
= all_a
->x
+ all_a
->width
- fw
/10;
853 if (!self
->strut
.bottom
&& *y
+ fh
/10 >= all_a
->y
+ all_a
->height
- 1)
854 *y
= all_a
->y
+ all_a
->height
- fh
/10;
855 if (!self
->strut
.left
&& *x
+ fw
*9/10 - 1 < all_a
->x
)
856 *x
= all_a
->x
- fw
*9/10;
857 if (!self
->strut
.top
&& *y
+ fh
*9/10 - 1 < all_a
->y
)
858 *y
= all_a
->y
- fw
*9/10;
861 /* If rudeness wasn't requested, then figure out of the client is currently
862 entirely on the screen. If it is, and the position isn't changing by
863 request, and it is enlarging, then be rude even though it wasn't
866 Point oldtl
, oldtr
, oldbl
, oldbr
;
867 Point newtl
, newtr
, newbl
, newbr
;
868 gboolean stationary_l
, stationary_r
, stationary_t
, stationary_b
;
870 POINT_SET(oldtl
, self
->frame
->area
.x
, self
->frame
->area
.y
);
871 POINT_SET(oldbr
, self
->frame
->area
.x
+ self
->frame
->area
.width
- 1,
872 self
->frame
->area
.y
+ self
->frame
->area
.height
- 1);
873 POINT_SET(oldtr
, oldbr
.x
, oldtl
.y
);
874 POINT_SET(oldbl
, oldtl
.x
, oldbr
.y
);
876 POINT_SET(newtl
, *x
, *y
);
877 POINT_SET(newbr
, *x
+ fw
- 1, *y
+ fh
- 1);
878 POINT_SET(newtr
, newbr
.x
, newtl
.y
);
879 POINT_SET(newbl
, newtl
.x
, newbr
.y
);
881 /* is it moving or just resizing from some corner? */
882 stationary_l
= oldtl
.x
== oldtl
.x
;
883 stationary_r
= oldtr
.x
== oldtr
.x
;
884 stationary_t
= oldtl
.y
== oldtl
.y
;
885 stationary_b
= oldbl
.y
== oldbl
.y
;
887 /* if left edge is growing and didnt move right edge */
888 if (stationary_r
&& newtl
.x
< oldtl
.x
)
890 /* if right edge is growing and didnt move left edge */
891 if (stationary_l
&& newtr
.x
> oldtr
.x
)
893 /* if top edge is growing and didnt move bottom edge */
894 if (stationary_b
&& newtl
.y
< oldtl
.y
)
896 /* if bottom edge is growing and didnt move top edge */
897 if (stationary_t
&& newbl
.y
> oldbl
.y
)
901 /* This here doesn't let windows even a pixel outside the struts/screen.
902 * When called from client_manage, programs placing themselves are
903 * forced completely onscreen, while things like
904 * xterm -geometry resolution-width/2 will work fine. Trying to
905 * place it completely offscreen will be handled in the above code.
906 * Sorry for this confused comment, i am tired. */
907 if (fw
<= mon_a
->width
) {
908 if (rudel
&& !self
->strut
.left
&& *x
< mon_a
->x
) *x
= mon_a
->x
;
909 if (ruder
&& !self
->strut
.right
&& *x
+ fw
> mon_a
->x
+ mon_a
->width
)
910 *x
= mon_a
->x
+ mon_a
->width
- fw
;
912 if (fh
<= mon_a
->height
) {
913 if (rudet
&& !self
->strut
.top
&& *y
< mon_a
->y
) *y
= mon_a
->y
;
914 if (rudeb
&& !self
->strut
.bottom
&& *y
+ fh
> mon_a
->y
+ mon_a
->height
)
915 *y
= mon_a
->y
+ mon_a
->height
- fh
;
918 /* get where the client should be */
919 frame_frame_gravity(self
->frame
, x
, y
, w
, h
);
921 return ox
!= *x
|| oy
!= *y
;
924 static void client_toggle_border(ObClient
*self
, gboolean show
)
926 /* adjust our idea of where the client is, based on its border. When the
927 border is removed, the client should now be considered to be in a
929 when re-adding the border to the client, the same operation needs to be
931 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
932 gint x
= oldx
, y
= oldy
;
933 switch(self
->gravity
) {
935 case NorthWestGravity
:
937 case SouthWestGravity
:
939 case NorthEastGravity
:
941 case SouthEastGravity
:
942 if (show
) x
-= self
->border_width
* 2;
943 else x
+= self
->border_width
* 2;
950 if (show
) x
-= self
->border_width
;
951 else x
+= self
->border_width
;
954 switch(self
->gravity
) {
956 case NorthWestGravity
:
958 case NorthEastGravity
:
960 case SouthWestGravity
:
962 case SouthEastGravity
:
963 if (show
) y
-= self
->border_width
* 2;
964 else y
+= self
->border_width
* 2;
971 if (show
) y
-= self
->border_width
;
972 else y
+= self
->border_width
;
979 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
981 /* set border_width to 0 because there is no border to add into
982 calculations anymore */
983 self
->border_width
= 0;
985 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
989 static void client_get_all(ObClient
*self
, gboolean real
)
991 /* this is needed for the frame to set itself up */
992 client_get_area(self
);
994 /* these things can change the decor and functions of the window */
996 client_get_mwm_hints(self
);
997 /* this can change the mwmhints for special cases */
998 client_get_type_and_transientness(self
);
999 client_get_state(self
);
1000 client_update_normal_hints(self
);
1002 /* get the session related properties, these can change decorations
1003 from per-app settings */
1004 client_get_session_ids(self
);
1006 /* now we got everything that can affect the decorations */
1010 client_update_protocols(self
);
1012 client_update_wmhints(self
);
1013 /* this may have already been called from client_update_wmhints */
1014 if (self
->transient_for
== NULL
)
1015 client_update_transient_for(self
);
1017 client_get_startup_id(self
);
1018 client_get_desktop(self
);/* uses transient data/group/startup id if a
1019 desktop is not specified */
1020 client_get_shaped(self
);
1023 /* a couple type-based defaults for new windows */
1025 /* this makes sure that these windows appear on all desktops */
1026 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1027 self
->desktop
= DESKTOP_ALL
;
1031 client_update_sync_request_counter(self
);
1034 client_get_colormap(self
);
1035 client_update_title(self
);
1036 client_update_strut(self
);
1037 client_update_icons(self
);
1038 client_update_user_time_window(self
);
1039 if (!self
->user_time_window
) /* check if this would have been called */
1040 client_update_user_time(self
);
1041 client_update_icon_geometry(self
);
1044 static void client_get_startup_id(ObClient
*self
)
1046 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
1048 PROP_GETS(self
->group
->leader
,
1049 net_startup_id
, utf8
, &self
->startup_id
);
1052 static void client_get_area(ObClient
*self
)
1054 XWindowAttributes wattrib
;
1057 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1058 g_assert(ret
!= BadWindow
);
1060 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
1061 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
1062 self
->border_width
= wattrib
.border_width
;
1064 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
1065 wattrib
.width
, wattrib
.height
);
1068 static void client_get_desktop(ObClient
*self
)
1070 guint32 d
= screen_num_desktops
; /* an always-invalid value */
1072 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
1073 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
1074 self
->desktop
= screen_num_desktops
- 1;
1078 gboolean trdesk
= FALSE
;
1080 if (self
->transient_for
) {
1081 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1082 self
->desktop
= self
->transient_for
->desktop
;
1087 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1088 if (it
->data
!= self
&&
1089 !((ObClient
*)it
->data
)->transient_for
) {
1090 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
1097 /* try get from the startup-notification protocol */
1098 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1099 if (self
->desktop
>= screen_num_desktops
&&
1100 self
->desktop
!= DESKTOP_ALL
)
1101 self
->desktop
= screen_num_desktops
- 1;
1103 /* defaults to the current desktop */
1104 self
->desktop
= screen_desktop
;
1109 static void client_get_state(ObClient
*self
)
1114 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1116 for (i
= 0; i
< num
; ++i
) {
1117 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1119 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1120 self
->shaded
= TRUE
;
1121 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1122 self
->iconic
= TRUE
;
1123 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1124 self
->skip_taskbar
= TRUE
;
1125 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1126 self
->skip_pager
= TRUE
;
1127 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1128 self
->fullscreen
= TRUE
;
1129 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1130 self
->max_vert
= TRUE
;
1131 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1132 self
->max_horz
= TRUE
;
1133 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1135 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1137 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1138 self
->demands_attention
= TRUE
;
1139 else if (state
[i
] == prop_atoms
.openbox_wm_state_undecorated
)
1140 self
->undecorated
= TRUE
;
1147 static void client_get_shaped(ObClient
*self
)
1149 self
->shaped
= FALSE
;
1151 if (extensions_shape
) {
1156 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1158 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1159 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1161 self
->shaped
= (s
!= 0);
1166 void client_update_transient_for(ObClient
*self
)
1169 ObClient
*target
= NULL
;
1171 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1172 if (t
!= self
->window
) { /* cant be transient to itself! */
1173 target
= g_hash_table_lookup(window_map
, &t
);
1174 /* if this happens then we need to check for it*/
1175 g_assert(target
!= self
);
1176 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1177 /* this can happen when a dialog is a child of
1178 a dockapp, for example */
1182 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1184 Setting the transient_for to Root is actually illegal, however
1185 applications from time have done this to specify transient for
1188 Now you can do that by being a TYPE_DIALOG and not setting
1189 the transient_for hint at all on your window. But people still
1190 use Root, and Kwin is very strange in this regard.
1192 KWin 3.0 will not consider windows with transient_for set to
1193 Root as transient for their group *UNLESS* they are also modal.
1194 In that case, it will make them transient for the group. This
1195 leads to all sorts of weird behavior from KDE apps which are
1196 only tested in KWin. I'd like to follow their behavior just to
1197 make this work right with KDE stuff, but that seems wrong.
1199 if (!target
&& self
->group
) {
1200 /* not transient to a client, see if it is transient for a
1202 if (t
== RootWindow(ob_display
, ob_screen
)) {
1203 /* window is a transient for its group! */
1204 target
= OB_TRAN_GROUP
;
1208 } else if (self
->transient
&& self
->group
)
1209 target
= OB_TRAN_GROUP
;
1211 client_update_transient_tree(self
, self
->group
, self
->group
,
1212 self
->transient_for
, target
);
1213 self
->transient_for
= target
;
1217 static void client_update_transient_tree(ObClient
*self
,
1218 ObGroup
*oldgroup
, ObGroup
*newgroup
,
1219 ObClient
* oldparent
,
1220 ObClient
*newparent
)
1225 /* No change has occured */
1226 if (oldgroup
== newgroup
&& oldparent
== newparent
) return;
1228 /** Remove the client from the transient tree wherever it has changed **/
1230 /* If the window is becoming a direct transient for a window in its group
1231 then that window can't be a child of this window anymore */
1232 if (oldparent
!= newparent
&&
1233 newparent
!= NULL
&& newparent
!= OB_TRAN_GROUP
&&
1234 newparent
->transient_for
== OB_TRAN_GROUP
&&
1235 newgroup
!= NULL
&& newgroup
== oldgroup
)
1237 self
->transients
= g_slist_remove(self
->transients
, newparent
);
1241 /* If the group changed, or if we are just becoming transient for the
1242 group, then we need to remove any old group transient windows
1243 from our children. But if we were already transient for the group, then
1244 other group transients are not our children. */
1245 if ((oldgroup
!= newgroup
||
1246 (newparent
== OB_TRAN_GROUP
&& oldparent
!= newparent
)) &&
1247 oldgroup
!= NULL
&& oldparent
!= OB_TRAN_GROUP
)
1249 for (it
= self
->transients
; it
; it
= next
) {
1250 next
= g_slist_next(it
);
1252 if (c
->group
== oldgroup
)
1253 self
->transients
= g_slist_delete_link(self
->transients
, it
);
1257 /* If we used to be transient for a group and now we are not, or we're
1258 transient for a new group, then we need to remove ourselves from all
1260 if (oldparent
== OB_TRAN_GROUP
&& (oldgroup
!= newgroup
||
1261 oldparent
!= newparent
))
1263 for (it
= oldgroup
->members
; it
; it
= g_slist_next(it
)) {
1265 if (c
!= self
&& (!c
->transient_for
||
1266 c
->transient_for
!= OB_TRAN_GROUP
))
1267 c
->transients
= g_slist_remove(c
->transients
, self
);
1270 /* If we used to be transient for a single window and we are no longer
1271 transient for it, then we need to remove ourself from its children */
1272 else if (oldparent
!= NULL
&& oldparent
!= OB_TRAN_GROUP
&&
1273 oldparent
!= newparent
)
1274 oldparent
->transients
= g_slist_remove(oldparent
->transients
, self
);
1277 /** Re-add the client to the transient tree wherever it has changed **/
1279 /* If we're now transient for a group and we weren't transient for it
1280 before then we need to add ourselves to all our new parents */
1281 if (newparent
== OB_TRAN_GROUP
&& (oldgroup
!= newgroup
||
1282 oldparent
!= newparent
))
1284 for (it
= oldgroup
->members
; it
; it
= g_slist_next(it
)) {
1286 if (c
!= self
&& (!c
->transient_for
||
1287 c
->transient_for
!= OB_TRAN_GROUP
))
1288 c
->transients
= g_slist_prepend(c
->transients
, self
);
1291 /* If we are now transient for a single window which we weren't before,
1292 we need to add ourselves to its children
1294 WARNING: Cyclical transient ness is possible if two windows are
1295 transient for eachother.
1297 else if (newparent
!= NULL
&& newparent
!= OB_TRAN_GROUP
&&
1298 newparent
!= oldparent
&&
1299 /* don't make ourself its child if it is already our child */
1300 !client_is_direct_child(self
, newparent
))
1301 newparent
->transients
= g_slist_prepend(newparent
->transients
, self
);
1303 /* If the group changed then we need to add any new group transient
1304 windows to our children. But if we're transient for the group, then
1305 other group transients are not our children.
1307 WARNING: Cyclical transient-ness is possible. For e.g. if:
1308 A is transient for the group
1309 B is a member of the group and transient for A
1311 if (oldgroup
!= newgroup
&& newgroup
!= NULL
&&
1312 newparent
!= OB_TRAN_GROUP
)
1314 for (it
= newgroup
->members
; it
; it
= g_slist_next(it
)) {
1316 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
&&
1317 /* Don't make it our child if it is already our parent */
1318 !client_is_direct_child(c
, self
))
1320 self
->transients
= g_slist_prepend(self
->transients
, c
);
1326 static void client_get_mwm_hints(ObClient
*self
)
1331 self
->mwmhints
.flags
= 0; /* default to none */
1333 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1335 if (num
>= OB_MWM_ELEMENTS
) {
1336 self
->mwmhints
.flags
= hints
[0];
1337 self
->mwmhints
.functions
= hints
[1];
1338 self
->mwmhints
.decorations
= hints
[2];
1344 void client_get_type_and_transientness(ObClient
*self
)
1351 self
->transient
= FALSE
;
1353 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1354 /* use the first value that we know about in the array */
1355 for (i
= 0; i
< num
; ++i
) {
1356 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1357 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1358 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1359 self
->type
= OB_CLIENT_TYPE_DOCK
;
1360 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1361 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1362 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1363 self
->type
= OB_CLIENT_TYPE_MENU
;
1364 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1365 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1366 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1367 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1368 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1369 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1370 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1371 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1372 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1373 /* prevent this window from getting any decor or
1375 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1376 OB_MWM_FLAG_DECORATIONS
);
1377 self
->mwmhints
.decorations
= 0;
1378 self
->mwmhints
.functions
= 0;
1380 if (self
->type
!= (ObClientType
) -1)
1381 break; /* grab the first legit type */
1386 if (XGetTransientForHint(ob_display
, self
->window
, &t
))
1387 self
->transient
= TRUE
;
1389 if (self
->type
== (ObClientType
) -1) {
1390 /*the window type hint was not set, which means we either classify
1391 ourself as a normal window or a dialog, depending on if we are a
1393 if (self
->transient
)
1394 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1396 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1399 /* then, based on our type, we can update our transientness.. */
1400 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1401 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1402 self
->type
== OB_CLIENT_TYPE_MENU
||
1403 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1405 self
->transient
= TRUE
;
1409 void client_update_protocols(ObClient
*self
)
1412 guint num_return
, i
;
1414 self
->focus_notify
= FALSE
;
1415 self
->delete_window
= FALSE
;
1417 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1418 for (i
= 0; i
< num_return
; ++i
) {
1419 if (proto
[i
] == prop_atoms
.wm_delete_window
)
1420 /* this means we can request the window to close */
1421 self
->delete_window
= TRUE
;
1422 else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1423 /* if this protocol is requested, then the window will be
1424 notified whenever we want it to receive focus */
1425 self
->focus_notify
= TRUE
;
1427 else if (proto
[i
] == prop_atoms
.net_wm_sync_request
)
1428 /* if this protocol is requested, then resizing the
1429 window will be synchronized between the frame and the
1431 self
->sync_request
= TRUE
;
1439 void client_update_sync_request_counter(ObClient
*self
)
1443 if (PROP_GET32(self
->window
, net_wm_sync_request_counter
, cardinal
, &i
)) {
1444 self
->sync_counter
= i
;
1446 self
->sync_counter
= None
;
1450 void client_get_colormap(ObClient
*self
)
1452 XWindowAttributes wa
;
1454 if (XGetWindowAttributes(ob_display
, self
->window
, &wa
))
1455 client_update_colormap(self
, wa
.colormap
);
1458 void client_update_colormap(ObClient
*self
, Colormap colormap
)
1460 self
->colormap
= colormap
;
1463 void client_update_normal_hints(ObClient
*self
)
1467 gint oldgravity
= self
->gravity
;
1470 self
->min_ratio
= 0.0f
;
1471 self
->max_ratio
= 0.0f
;
1472 SIZE_SET(self
->size_inc
, 1, 1);
1473 SIZE_SET(self
->base_size
, 0, 0);
1474 SIZE_SET(self
->min_size
, 0, 0);
1475 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1477 /* get the hints from the window */
1478 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1479 /* normal windows can't request placement! har har
1480 if (!client_normal(self))
1482 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1484 if (size
.flags
& PWinGravity
) {
1485 self
->gravity
= size
.win_gravity
;
1487 /* if the client has a frame, i.e. has already been mapped and
1488 is changing its gravity */
1489 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1490 /* move our idea of the client's position based on its new
1492 client_convert_gravity(self
, oldgravity
,
1493 &self
->area
.x
, &self
->area
.y
,
1494 self
->area
.width
, self
->area
.height
);
1498 if (size
.flags
& PAspect
) {
1499 if (size
.min_aspect
.y
)
1501 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1502 if (size
.max_aspect
.y
)
1504 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1507 if (size
.flags
& PMinSize
)
1508 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1510 if (size
.flags
& PMaxSize
)
1511 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1513 if (size
.flags
& PBaseSize
)
1514 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1516 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1517 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1521 void client_setup_decor_and_functions(ObClient
*self
)
1523 /* start with everything (cept fullscreen) */
1525 (OB_FRAME_DECOR_TITLEBAR
|
1526 OB_FRAME_DECOR_HANDLE
|
1527 OB_FRAME_DECOR_GRIPS
|
1528 OB_FRAME_DECOR_BORDER
|
1529 OB_FRAME_DECOR_ICON
|
1530 OB_FRAME_DECOR_ALLDESKTOPS
|
1531 OB_FRAME_DECOR_ICONIFY
|
1532 OB_FRAME_DECOR_MAXIMIZE
|
1533 OB_FRAME_DECOR_SHADE
|
1534 OB_FRAME_DECOR_CLOSE
);
1536 (OB_CLIENT_FUNC_RESIZE
|
1537 OB_CLIENT_FUNC_MOVE
|
1538 OB_CLIENT_FUNC_ICONIFY
|
1539 OB_CLIENT_FUNC_MAXIMIZE
|
1540 OB_CLIENT_FUNC_SHADE
|
1541 OB_CLIENT_FUNC_CLOSE
);
1543 if (!(self
->min_size
.width
< self
->max_size
.width
||
1544 self
->min_size
.height
< self
->max_size
.height
))
1545 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1547 switch (self
->type
) {
1548 case OB_CLIENT_TYPE_NORMAL
:
1549 /* normal windows retain all of the possible decorations and
1550 functionality, and are the only windows that you can fullscreen */
1551 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1554 case OB_CLIENT_TYPE_DIALOG
:
1555 case OB_CLIENT_TYPE_UTILITY
:
1556 /* these windows cannot be maximized */
1557 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1560 case OB_CLIENT_TYPE_MENU
:
1561 case OB_CLIENT_TYPE_TOOLBAR
:
1562 /* these windows get less functionality */
1563 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1566 case OB_CLIENT_TYPE_SPLASH
:
1567 /* these don't get get any decorations, and the only thing you can
1568 do with them is move them */
1569 self
->decorations
= 0;
1570 self
->functions
= OB_CLIENT_FUNC_MOVE
;
1572 case OB_CLIENT_TYPE_DESKTOP
:
1573 case OB_CLIENT_TYPE_DOCK
:
1574 /* these windows are not manipulated by the window manager */
1575 self
->decorations
= 0;
1576 self
->functions
= 0;
1580 /* Mwm Hints are applied subtractively to what has already been chosen for
1581 decor and functionality */
1582 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1583 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1584 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1585 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1587 /* if the mwm hints request no handle or title, then all
1588 decorations are disabled, but keep the border if that's
1590 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1591 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1593 self
->decorations
= 0;
1598 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1599 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1600 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1601 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1602 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1603 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1604 /* dont let mwm hints kill any buttons
1605 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1606 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1607 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1608 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1610 /* dont let mwm hints kill the close button
1611 if (! (self->mwmhints.functions & MwmFunc_Close))
1612 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1616 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1617 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1618 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1619 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1620 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1621 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1623 /* can't maximize without moving/resizing */
1624 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1625 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1626 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1627 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1628 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1631 /* kill the handle on fully maxed windows */
1632 if (self
->max_vert
&& self
->max_horz
)
1633 self
->decorations
&= ~(OB_FRAME_DECOR_HANDLE
| OB_FRAME_DECOR_GRIPS
);
1635 /* finally, the user can have requested no decorations, which overrides
1636 everything (but doesnt give it a border if it doesnt have one) */
1637 if (self
->undecorated
) {
1638 if (config_theme_keepborder
)
1639 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1641 self
->decorations
= 0;
1644 /* if we don't have a titlebar, then we cannot shade! */
1645 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1646 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1648 /* now we need to check against rules for the client's current state */
1649 if (self
->fullscreen
) {
1650 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1651 OB_CLIENT_FUNC_FULLSCREEN
|
1652 OB_CLIENT_FUNC_ICONIFY
);
1653 self
->decorations
= 0;
1656 client_change_allowed_actions(self
);
1659 /* adjust the client's decorations, etc. */
1660 client_reconfigure(self
);
1664 static void client_change_allowed_actions(ObClient
*self
)
1669 /* desktop windows are kept on all desktops */
1670 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1671 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1673 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1674 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1675 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1676 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1677 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1678 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1679 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1680 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1681 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1682 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1683 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1684 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1685 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1686 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1687 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1690 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1692 /* make sure the window isn't breaking any rules now */
1694 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1695 if (self
->frame
) client_shade(self
, FALSE
);
1696 else self
->shaded
= FALSE
;
1698 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1699 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1700 else self
->iconic
= FALSE
;
1702 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1703 if (self
->frame
) client_fullscreen(self
, FALSE
);
1704 else self
->fullscreen
= FALSE
;
1706 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1708 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1709 else self
->max_vert
= self
->max_horz
= FALSE
;
1713 void client_reconfigure(ObClient
*self
)
1715 /* by making this pass FALSE for user, we avoid the emacs event storm where
1716 every configurenotify causes an update in its normal hints, i think this
1717 is generally what we want anyways... */
1718 client_configure(self
, self
->area
.x
, self
->area
.y
,
1719 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1722 void client_update_wmhints(ObClient
*self
)
1726 /* assume a window takes input if it doesnt specify */
1727 self
->can_focus
= TRUE
;
1729 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1732 if (hints
->flags
& InputHint
)
1733 self
->can_focus
= hints
->input
;
1735 /* only do this when first managing the window *AND* when we aren't
1737 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1738 if (hints
->flags
& StateHint
)
1739 self
->iconic
= hints
->initial_state
== IconicState
;
1742 self
->urgent
= (hints
->flags
& XUrgencyHint
);
1743 if (self
->urgent
&& !ur
)
1744 client_hilite(self
, TRUE
);
1745 else if (!self
->urgent
&& ur
&& self
->demands_attention
)
1746 client_hilite(self
, FALSE
);
1748 if (!(hints
->flags
& WindowGroupHint
))
1749 hints
->window_group
= None
;
1751 /* did the group state change? */
1752 if (hints
->window_group
!=
1753 (self
->group
? self
->group
->leader
: None
))
1755 ObGroup
*oldgroup
= self
->group
;
1757 /* remove from the old group if there was one */
1758 if (self
->group
!= NULL
) {
1759 group_remove(self
->group
, self
);
1763 /* add ourself to the group if we have one */
1764 if (hints
->window_group
!= None
) {
1765 self
->group
= group_add(hints
->window_group
, self
);
1768 /* Put ourselves into the new group's transient tree, and remove
1769 ourselves from the old group's */
1770 client_update_transient_tree(self
, oldgroup
, self
->group
,
1771 self
->transient_for
,
1772 self
->transient_for
);
1774 /* Lastly, being in a group, or not, can change if the window is
1775 transient for anything.
1777 The logic for this is:
1778 self->transient = TRUE always if the window wants to be
1779 transient for something, even if transient_for was NULL because
1780 it wasn't in a group before.
1782 If transient_for was NULL and oldgroup was NULL we can assume
1783 that when we add the new group, it will become transient for
1786 If transient_for was OB_TRAN_GROUP, then it must have already
1787 had a group. If it is getting a new group, the above call to
1788 client_update_transient_tree has already taken care of
1789 everything ! If it is losing all group status then it will
1790 no longer be transient for anything and that needs to be
1793 if (self
->transient
&&
1794 ((self
->transient_for
== NULL
&& oldgroup
== NULL
) ||
1795 (self
->transient_for
== OB_TRAN_GROUP
&& !self
->group
)))
1796 client_update_transient_for(self
);
1799 /* the WM_HINTS can contain an icon */
1800 client_update_icons(self
);
1806 void client_update_title(ObClient
*self
)
1809 gchar
*visible
= NULL
;
1811 g_free(self
->title
);
1814 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1815 /* try old x stuff */
1816 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1817 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1818 if (self
->transient
) {
1820 GNOME alert windows are not given titles:
1821 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1823 data
= g_strdup("");
1825 data
= g_strdup("Unnamed Window");
1829 if (self
->client_machine
) {
1830 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1835 PROP_SETS(self
->window
, net_wm_visible_name
, visible
);
1836 self
->title
= visible
;
1839 frame_adjust_title(self
->frame
);
1841 /* update the icon title */
1843 g_free(self
->icon_title
);
1846 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1847 /* try old x stuff */
1848 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1849 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1850 data
= g_strdup(self
->title
);
1852 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1853 self
->icon_title
= data
;
1856 void client_update_strut(ObClient
*self
)
1860 gboolean got
= FALSE
;
1863 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1867 STRUT_PARTIAL_SET(strut
,
1868 data
[0], data
[2], data
[1], data
[3],
1869 data
[4], data
[5], data
[8], data
[9],
1870 data
[6], data
[7], data
[10], data
[11]);
1876 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1882 /* use the screen's width/height */
1883 a
= screen_physical_area();
1885 STRUT_PARTIAL_SET(strut
,
1886 data
[0], data
[2], data
[1], data
[3],
1887 a
->y
, a
->y
+ a
->height
- 1,
1888 a
->x
, a
->x
+ a
->width
- 1,
1889 a
->y
, a
->y
+ a
->height
- 1,
1890 a
->x
, a
->x
+ a
->width
- 1);
1896 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1897 0, 0, 0, 0, 0, 0, 0, 0);
1899 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1900 self
->strut
= strut
;
1902 /* updating here is pointless while we're being mapped cuz we're not in
1903 the client list yet */
1905 screen_update_areas();
1909 void client_update_icons(ObClient
*self
)
1915 for (i
= 0; i
< self
->nicons
; ++i
)
1916 g_free(self
->icons
[i
].data
);
1917 if (self
->nicons
> 0)
1918 g_free(self
->icons
);
1921 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1922 /* figure out how many valid icons are in here */
1924 while (num
- i
> 2) {
1928 if (i
> num
|| w
*h
== 0) break;
1932 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1934 /* store the icons */
1936 for (j
= 0; j
< self
->nicons
; ++j
) {
1939 w
= self
->icons
[j
].width
= data
[i
++];
1940 h
= self
->icons
[j
].height
= data
[i
++];
1942 if (w
*h
== 0) continue;
1944 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1945 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1950 self
->icons
[j
].data
[t
] =
1951 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1952 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1953 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1954 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1963 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1964 if (hints
->flags
& IconPixmapHint
) {
1966 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1967 xerror_set_ignore(TRUE
);
1968 if (!RrPixmapToRGBA(ob_rr_inst
,
1970 (hints
->flags
& IconMaskHint
?
1971 hints
->icon_mask
: None
),
1972 &self
->icons
[self
->nicons
-1].width
,
1973 &self
->icons
[self
->nicons
-1].height
,
1974 &self
->icons
[self
->nicons
-1].data
)){
1975 g_free(&self
->icons
[self
->nicons
-1]);
1978 xerror_set_ignore(FALSE
);
1984 /* set the default icon onto the window
1985 in theory, this could be a race, but if a window doesn't set an icon
1986 or removes it entirely, it's not very likely it is going to set one
1987 right away afterwards */
1988 if (self
->nicons
== 0) {
1989 RrPixel32
*icon
= ob_rr_theme
->def_win_icon
;
1992 data
= g_new(gulong
, 48*48+2);
1993 data
[0] = data
[1] = 48;
1994 for (i
= 0; i
< 48*48; ++i
)
1995 data
[i
+2] = (((icon
[i
] >> RrDefaultAlphaOffset
) & 0xff) << 24) +
1996 (((icon
[i
] >> RrDefaultRedOffset
) & 0xff) << 16) +
1997 (((icon
[i
] >> RrDefaultGreenOffset
) & 0xff) << 8) +
1998 (((icon
[i
] >> RrDefaultBlueOffset
) & 0xff) << 0);
1999 PROP_SETA32(self
->window
, net_wm_icon
, cardinal
, data
, 48*48+2);
2001 } else if (self
->frame
)
2002 /* don't draw the icon empty if we're just setting one now anyways,
2003 we'll get the property change any second */
2004 frame_adjust_icon(self
->frame
);
2007 void client_update_user_time(ObClient
*self
)
2010 gboolean got
= FALSE
;
2012 if (self
->user_time_window
)
2013 got
= PROP_GET32(self
->user_time_window
,
2014 net_wm_user_time
, cardinal
, &time
);
2016 got
= PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
);
2019 /* we set this every time, not just when it grows, because in practice
2020 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
2021 backward we don't want all windows to stop focusing. we'll just
2022 assume noone is setting times older than the last one, cuz that
2023 would be pretty stupid anyways
2025 self
->user_time
= time
;
2027 /*ob_debug("window %s user time %u\n", self->title, time);*/
2031 void client_update_user_time_window(ObClient
*self
)
2035 if (!PROP_GET32(self
->window
, net_wm_user_time_window
, window
, &w
))
2038 if (w
!= self
->user_time_window
) {
2039 /* remove the old window */
2040 propwin_remove(self
->user_time_window
, OB_PROPWIN_USER_TIME
, self
);
2041 self
->user_time_window
= None
;
2043 if (self
->group
&& self
->group
->leader
== w
) {
2044 ob_debug_type(OB_DEBUG_APP_BUGS
, "Window is setting its "
2045 "_NET_WM_USER_TYPE_WINDOW to its group leader\n");
2046 /* do it anyways..? */
2048 else if (w
== self
->window
) {
2049 ob_debug_type(OB_DEBUG_APP_BUGS
, "Window is setting its "
2050 "_NET_WM_USER_TIME_WINDOW to itself\n");
2051 w
= None
; /* don't do it */
2054 /* add the new window */
2055 propwin_add(w
, OB_PROPWIN_USER_TIME
, self
);
2056 self
->user_time_window
= w
;
2058 /* and update from it */
2059 client_update_user_time(self
);
2063 void client_update_icon_geometry(ObClient
*self
)
2068 RECT_SET(self
->icon_geometry
, 0, 0, 0, 0);
2070 if (PROP_GETA32(self
->window
, net_wm_icon_geometry
, cardinal
, &data
, &num
)
2073 /* don't let them set it with an area < 0 */
2074 RECT_SET(self
->icon_geometry
, data
[0], data
[1],
2075 MAX(data
[2],0), MAX(data
[3],0));
2079 static void client_get_session_ids(ObClient
*self
)
2086 if (!PROP_GET32(self
->window
, wm_client_leader
, window
, &leader
))
2089 /* get the SM_CLIENT_ID */
2092 got
= PROP_GETS(leader
, sm_client_id
, locale
, &self
->sm_client_id
);
2094 PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
);
2096 /* get the WM_CLASS (name and class). make them "" if they are not
2100 got
= PROP_GETSS(leader
, wm_class
, locale
, &ss
);
2102 got
= PROP_GETSS(self
->window
, wm_class
, locale
, &ss
);
2106 self
->name
= g_strdup(ss
[0]);
2108 self
->class = g_strdup(ss
[1]);
2113 if (self
->name
== NULL
) self
->name
= g_strdup("");
2114 if (self
->class == NULL
) self
->class = g_strdup("");
2116 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2119 got
= PROP_GETS(leader
, wm_window_role
, locale
, &s
);
2121 got
= PROP_GETS(self
->window
, wm_window_role
, locale
, &s
);
2126 self
->role
= g_strdup("");
2128 /* get the WM_COMMAND */
2132 got
= PROP_GETSS(leader
, wm_command
, locale
, &ss
);
2134 got
= PROP_GETSS(self
->window
, wm_command
, locale
, &ss
);
2137 /* merge/mash them all together */
2138 gchar
*merge
= NULL
;
2141 for (i
= 0; ss
[i
]; ++i
) {
2144 merge
= g_strconcat(merge
, ss
[i
], NULL
);
2146 merge
= g_strconcat(ss
[i
], NULL
);
2151 self
->wm_command
= merge
;
2154 /* get the WM_CLIENT_MACHINE */
2157 got
= PROP_GETS(leader
, wm_client_machine
, locale
, &s
);
2159 got
= PROP_GETS(self
->window
, wm_client_machine
, locale
, &s
);
2162 gchar localhost
[128];
2164 gethostname(localhost
, 127);
2165 localhost
[127] = '\0';
2166 if (strcmp(localhost
, s
) != 0)
2167 self
->client_machine
= s
;
2173 static void client_change_wm_state(ObClient
*self
)
2178 old
= self
->wmstate
;
2180 if (self
->shaded
|| self
->iconic
||
2181 (self
->desktop
!= DESKTOP_ALL
&& self
->desktop
!= screen_desktop
))
2183 self
->wmstate
= IconicState
;
2185 self
->wmstate
= NormalState
;
2187 if (old
!= self
->wmstate
) {
2188 PROP_MSG(self
->window
, kde_wm_change_state
,
2189 self
->wmstate
, 1, 0, 0);
2191 state
[0] = self
->wmstate
;
2193 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
2197 static void client_change_state(ObClient
*self
)
2199 gulong netstate
[11];
2204 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
2206 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
2208 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
2209 if (self
->skip_taskbar
)
2210 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
2211 if (self
->skip_pager
)
2212 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
2213 if (self
->fullscreen
)
2214 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
2216 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
2218 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
2220 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
2222 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
2223 if (self
->demands_attention
)
2224 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
2225 if (self
->undecorated
)
2226 netstate
[num
++] = prop_atoms
.openbox_wm_state_undecorated
;
2227 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
2230 frame_adjust_state(self
->frame
);
2233 ObClient
*client_search_focus_tree(ObClient
*self
)
2238 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2239 if (client_focused(it
->data
)) return it
->data
;
2240 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
2245 ObClient
*client_search_focus_tree_full(ObClient
*self
)
2247 if (self
->transient_for
) {
2248 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2249 return client_search_focus_tree_full(self
->transient_for
);
2252 gboolean recursed
= FALSE
;
2254 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
2255 if (!((ObClient
*)it
->data
)->transient_for
) {
2257 if ((c
= client_search_focus_tree_full(it
->data
)))
2266 /* this function checks the whole tree, the client_search_focus_tree~
2267 does not, so we need to check this window */
2268 if (client_focused(self
))
2270 return client_search_focus_tree(self
);
2273 static ObStackingLayer
calc_layer(ObClient
*self
)
2277 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2278 l
= OB_STACKING_LAYER_DESKTOP
;
2279 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2280 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2281 else l
= OB_STACKING_LAYER_ABOVE
;
2283 else if ((self
->fullscreen
||
2284 /* no decorations and fills the monitor = oldskool fullscreen */
2285 (self
->frame
!= NULL
&&
2286 (self
->frame
->size
.right
== 0 && self
->frame
->size
.left
== 0 &&
2287 self
->frame
->size
.bottom
== 0 && self
->frame
->size
.top
== 0 &&
2288 RECT_EQUAL(self
->area
,
2289 *screen_physical_area_monitor
2290 (client_monitor(self
)))))) &&
2291 (client_focused(self
) || client_search_focus_tree(self
)))
2292 l
= OB_STACKING_LAYER_FULLSCREEN
;
2293 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2294 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2295 else l
= OB_STACKING_LAYER_NORMAL
;
2300 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2301 ObStackingLayer min
, gboolean raised
)
2303 ObStackingLayer old
, own
;
2307 own
= calc_layer(self
);
2308 self
->layer
= MAX(own
, min
);
2310 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2311 client_calc_layer_recursive(it
->data
, orig
,
2313 raised
? raised
: self
->layer
> old
);
2315 /* restack. but only if the original window is managed.
2317 raised is used so that only the bottom-most window in the stacking
2318 order is raised, the others will automatically come with it.
2320 also only the highest windows in the stacking order (no transients)
2321 are lowered, cuz the rest come for free
2323 if (!raised
&& orig
->frame
) {
2324 if (self
->layer
> old
) {
2325 stacking_remove(CLIENT_AS_WINDOW(self
));
2326 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
2327 } else if (self
->layer
< old
&& self
->transients
== NULL
) {
2328 stacking_remove(CLIENT_AS_WINDOW(self
));
2329 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
2334 void client_calc_layer(ObClient
*self
)
2341 /* transients take on the layer of their parents */
2342 it
= client_search_all_top_parents(self
);
2344 for (; it
; it
= g_slist_next(it
))
2345 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2348 gboolean
client_should_show(ObClient
*self
)
2352 if (client_normal(self
) && screen_showing_desktop
)
2354 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2360 void client_show(ObClient
*self
)
2363 if (client_should_show(self
)) {
2364 frame_show(self
->frame
);
2367 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2368 needs to be in IconicState. This includes when it is on another
2371 client_change_wm_state(self
);
2374 void client_hide(ObClient
*self
)
2376 if (!client_should_show(self
)) {
2377 frame_hide(self
->frame
);
2380 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2381 needs to be in IconicState. This includes when it is on another
2384 client_change_wm_state(self
);
2387 void client_showhide(ObClient
*self
)
2390 if (client_should_show(self
)) {
2391 frame_show(self
->frame
);
2394 frame_hide(self
->frame
);
2397 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2398 needs to be in IconicState. This includes when it is on another
2401 client_change_wm_state(self
);
2404 gboolean
client_normal(ObClient
*self
) {
2405 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2406 self
->type
== OB_CLIENT_TYPE_DOCK
||
2407 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2410 gboolean
client_helper(ObClient
*self
)
2412 return (self
->type
== OB_CLIENT_TYPE_UTILITY
||
2413 self
->type
== OB_CLIENT_TYPE_MENU
||
2414 self
->type
== OB_CLIENT_TYPE_TOOLBAR
);
2417 gboolean
client_mouse_focusable(ObClient
*self
)
2419 return !(self
->type
== OB_CLIENT_TYPE_MENU
||
2420 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
2421 self
->type
== OB_CLIENT_TYPE_SPLASH
||
2422 self
->type
== OB_CLIENT_TYPE_DOCK
);
2425 gboolean
client_enter_focusable(ObClient
*self
)
2427 /* you can focus desktops but it shouldn't on enter */
2428 return (client_mouse_focusable(self
) &&
2429 self
->type
!= OB_CLIENT_TYPE_DESKTOP
);
2433 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2435 gboolean pos
= FALSE
; /* has the window's position been configured? */
2438 /* save the position, and set self->area for these to use */
2444 /* set the desktop hint, to make sure that it always exists */
2445 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
2447 /* these are in a carefully crafted order.. */
2450 self
->iconic
= FALSE
;
2451 client_iconify(self
, TRUE
, FALSE
);
2453 if (self
->fullscreen
) {
2454 self
->fullscreen
= FALSE
;
2455 client_fullscreen(self
, TRUE
);
2458 if (self
->undecorated
) {
2459 self
->undecorated
= FALSE
;
2460 client_set_undecorated(self
, TRUE
);
2463 self
->shaded
= FALSE
;
2464 client_shade(self
, TRUE
);
2466 if (self
->demands_attention
) {
2467 self
->demands_attention
= FALSE
;
2468 client_hilite(self
, TRUE
);
2471 if (self
->max_vert
&& self
->max_horz
) {
2472 self
->max_vert
= self
->max_horz
= FALSE
;
2473 client_maximize(self
, TRUE
, 0);
2475 } else if (self
->max_vert
) {
2476 self
->max_vert
= FALSE
;
2477 client_maximize(self
, TRUE
, 2);
2479 } else if (self
->max_horz
) {
2480 self
->max_horz
= FALSE
;
2481 client_maximize(self
, TRUE
, 1);
2485 /* if the client didn't get positioned yet, then do so now.
2486 call client_move even if the window is not being moved anywhere, because
2487 when we reparent it and decorate it, it is getting moved and we need to
2488 be telling it so with a ConfigureNotify event.
2491 /* use the saved position */
2494 client_move(self
, x
, y
);
2497 /* nothing to do for the other states:
2506 void client_convert_gravity(ObClient
*self
, gint gravity
, gint
*x
, gint
*y
,
2509 gint oldg
= self
->gravity
;
2511 /* get the frame's position from the requested stuff */
2512 self
->gravity
= gravity
;
2513 frame_client_gravity(self
->frame
, x
, y
, w
, h
);
2514 self
->gravity
= oldg
;
2516 /* get the client's position in its true gravity from that */
2517 frame_frame_gravity(self
->frame
, x
, y
, w
, h
);
2520 void client_try_configure(ObClient
*self
, gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2521 gint
*logicalw
, gint
*logicalh
,
2524 Rect desired_area
= {*x
, *y
, *w
, *h
};
2526 /* make the frame recalculate its dimentions n shit without changing
2527 anything visible for real, this way the constraints below can work with
2528 the updated frame dimensions. */
2529 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2531 /* work within the prefered sizes given by the window */
2532 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2533 gint basew
, baseh
, minw
, minh
;
2535 /* base size is substituted with min size if not specified */
2536 if (self
->base_size
.width
|| self
->base_size
.height
) {
2537 basew
= self
->base_size
.width
;
2538 baseh
= self
->base_size
.height
;
2540 basew
= self
->min_size
.width
;
2541 baseh
= self
->min_size
.height
;
2543 /* min size is substituted with base size if not specified */
2544 if (self
->min_size
.width
|| self
->min_size
.height
) {
2545 minw
= self
->min_size
.width
;
2546 minh
= self
->min_size
.height
;
2548 minw
= self
->base_size
.width
;
2549 minh
= self
->base_size
.height
;
2552 /* if this is a user-requested resize, then check against min/max
2555 /* smaller than min size or bigger than max size? */
2556 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2557 if (*w
< minw
) *w
= minw
;
2558 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2559 if (*h
< minh
) *h
= minh
;
2564 /* keep to the increments */
2565 *w
/= self
->size_inc
.width
;
2566 *h
/= self
->size_inc
.height
;
2568 /* you cannot resize to nothing */
2569 if (basew
+ *w
< 1) *w
= 1 - basew
;
2570 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2572 /* save the logical size */
2573 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2574 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2576 *w
*= self
->size_inc
.width
;
2577 *h
*= self
->size_inc
.height
;
2582 /* adjust the height to match the width for the aspect ratios.
2583 for this, min size is not substituted for base size ever. */
2584 *w
-= self
->base_size
.width
;
2585 *h
-= self
->base_size
.height
;
2587 if (!self
->fullscreen
) {
2588 if (self
->min_ratio
)
2589 if (*h
* self
->min_ratio
> *w
) {
2590 *h
= (gint
)(*w
/ self
->min_ratio
);
2592 /* you cannot resize to nothing */
2595 *w
= (gint
)(*h
* self
->min_ratio
);
2598 if (self
->max_ratio
)
2599 if (*h
* self
->max_ratio
< *w
) {
2600 *h
= (gint
)(*w
/ self
->max_ratio
);
2602 /* you cannot resize to nothing */
2605 *w
= (gint
)(*h
* self
->min_ratio
);
2610 *w
+= self
->base_size
.width
;
2611 *h
+= self
->base_size
.height
;
2614 /* gets the frame's position */
2615 frame_client_gravity(self
->frame
, x
, y
, *w
, *h
);
2617 /* these positions are frame positions, not client positions */
2619 /* set the size and position if fullscreen */
2620 if (self
->fullscreen
) {
2624 i
= screen_find_monitor(&desired_area
);
2625 a
= screen_physical_area_monitor(i
);
2632 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2633 is entering fullscreen */
2634 } else if (self
->max_horz
|| self
->max_vert
) {
2638 i
= screen_find_monitor(&desired_area
);
2639 a
= screen_area_monitor(self
->desktop
, i
);
2641 /* set the size and position if maximized */
2642 if (self
->max_horz
) {
2644 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2646 if (self
->max_vert
) {
2648 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2651 /* maximizing is not allowed if the user can't move+resize the window
2655 /* gets the client's position */
2656 frame_frame_gravity(self
->frame
, x
, y
, *w
, *h
);
2658 /* these override the above states! if you cant move you can't move! */
2660 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2664 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2665 *w
= self
->area
.width
;
2666 *h
= self
->area
.height
;
2675 void client_configure_full(ObClient
*self
, gint x
, gint y
, gint w
, gint h
,
2676 gboolean user
, gboolean final
,
2677 gboolean force_reply
)
2679 gint oldw
, oldh
, oldrx
, oldry
;
2680 gboolean send_resize_client
;
2681 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2682 guint fdecor
= self
->frame
->decorations
;
2683 gboolean fhorz
= self
->frame
->max_horz
;
2684 gint logicalw
, logicalh
;
2686 /* find the new x, y, width, and height (and logical size) */
2687 client_try_configure(self
, &x
, &y
, &w
, &h
, &logicalw
, &logicalh
, user
);
2689 /* set the logical size if things changed */
2690 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2691 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2693 /* figure out if we moved or resized or what */
2694 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2695 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2697 oldw
= self
->area
.width
;
2698 oldh
= self
->area
.height
;
2699 RECT_SET(self
->area
, x
, y
, w
, h
);
2701 /* for app-requested resizes, always resize if 'resized' is true.
2702 for user-requested ones, only resize if final is true, or when
2703 resizing in redraw mode */
2704 send_resize_client
= ((!user
&& resized
) ||
2706 (resized
&& config_resize_redraw
))));
2708 /* if the client is enlarging, then resize the client before the frame */
2709 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
)) {
2710 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2711 /* resize the plate to show the client padding color underneath */
2712 frame_adjust_client_area(self
->frame
);
2715 /* find the frame's dimensions and move/resize it */
2716 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2717 moved
= resized
= TRUE
;
2718 if (moved
|| resized
)
2719 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2721 /* find the client's position relative to the root window */
2722 oldrx
= self
->root_pos
.x
;
2723 oldry
= self
->root_pos
.y
;
2724 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2725 self
->frame
->size
.left
-
2726 self
->border_width
) ||
2727 oldry
!= (signed)(self
->frame
->area
.y
+
2728 self
->frame
->size
.top
-
2729 self
->border_width
));
2731 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2735 POINT_SET(self
->root_pos
,
2736 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2738 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2739 self
->border_width
);
2741 event
.type
= ConfigureNotify
;
2742 event
.xconfigure
.display
= ob_display
;
2743 event
.xconfigure
.event
= self
->window
;
2744 event
.xconfigure
.window
= self
->window
;
2746 /* root window real coords */
2747 event
.xconfigure
.x
= self
->root_pos
.x
;
2748 event
.xconfigure
.y
= self
->root_pos
.y
;
2749 event
.xconfigure
.width
= w
;
2750 event
.xconfigure
.height
= h
;
2751 event
.xconfigure
.border_width
= 0;
2752 event
.xconfigure
.above
= self
->frame
->plate
;
2753 event
.xconfigure
.override_redirect
= FALSE
;
2754 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2755 FALSE
, StructureNotifyMask
, &event
);
2758 /* if the client is shrinking, then resize the frame before the client */
2759 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
))) {
2760 /* resize the plate to show the client padding color underneath */
2761 frame_adjust_client_area(self
->frame
);
2763 XResizeWindow(ob_display
, self
->window
, w
, h
);
2769 void client_fullscreen(ObClient
*self
, gboolean fs
)
2773 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2774 self
->fullscreen
== fs
) return; /* already done */
2776 self
->fullscreen
= fs
;
2777 client_change_state(self
); /* change the state hints on the client */
2778 client_calc_layer(self
); /* and adjust out layer/stacking */
2781 self
->pre_fullscreen_area
= self
->area
;
2782 /* if the window is maximized, its area isn't all that meaningful.
2783 save it's premax area instead. */
2784 if (self
->max_horz
) {
2785 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2786 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2788 if (self
->max_vert
) {
2789 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2790 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2793 /* these are not actually used cuz client_configure will set them
2794 as appropriate when the window is fullscreened */
2799 if (self
->pre_fullscreen_area
.width
> 0 &&
2800 self
->pre_fullscreen_area
.height
> 0)
2802 x
= self
->pre_fullscreen_area
.x
;
2803 y
= self
->pre_fullscreen_area
.y
;
2804 w
= self
->pre_fullscreen_area
.width
;
2805 h
= self
->pre_fullscreen_area
.height
;
2806 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2808 /* pick some fallbacks... */
2809 a
= screen_area_monitor(self
->desktop
, 0);
2810 x
= a
->x
+ a
->width
/ 4;
2811 y
= a
->y
+ a
->height
/ 4;
2817 client_setup_decor_and_functions(self
);
2819 client_move_resize(self
, x
, y
, w
, h
);
2821 /* try focus us when we go into fullscreen mode */
2825 static void client_iconify_recursive(ObClient
*self
,
2826 gboolean iconic
, gboolean curdesk
)
2829 gboolean changed
= FALSE
;
2832 if (self
->iconic
!= iconic
) {
2833 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2837 /* don't let non-normal windows iconify along with their parents
2839 if (client_normal(self
)) {
2840 self
->iconic
= iconic
;
2842 /* update the focus lists.. iconic windows go to the bottom of
2843 the list, put the new iconic window at the 'top of the
2845 focus_order_to_top(self
);
2850 self
->iconic
= iconic
;
2852 if (curdesk
&& self
->desktop
!= screen_desktop
&&
2853 self
->desktop
!= DESKTOP_ALL
)
2854 client_set_desktop(self
, screen_desktop
, FALSE
);
2856 /* this puts it after the current focused window */
2857 focus_order_remove(self
);
2858 focus_order_add_new(self
);
2865 client_change_state(self
);
2866 if (ob_state() != OB_STATE_STARTING
&& config_animate_iconify
)
2867 frame_begin_iconify_animation(self
->frame
, iconic
);
2868 /* do this after starting the animation so it doesn't flash */
2869 client_showhide(self
);
2872 /* iconify all direct transients, and deiconify all transients
2874 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2875 if (it
->data
!= self
)
2876 if (client_is_direct_child(self
, it
->data
) || !iconic
)
2877 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2880 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2882 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
|| !iconic
) {
2883 /* move up the transient chain as far as possible first */
2884 self
= client_search_top_normal_parent(self
);
2885 client_iconify_recursive(self
, iconic
, curdesk
);
2889 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2893 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2894 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2896 /* check if already done */
2898 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2899 if (dir
== 1 && self
->max_horz
) return;
2900 if (dir
== 2 && self
->max_vert
) return;
2902 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2903 if (dir
== 1 && !self
->max_horz
) return;
2904 if (dir
== 2 && !self
->max_vert
) return;
2907 /* we just tell it to configure in the same place and client_configure
2908 worries about filling the screen with the window */
2911 w
= self
->area
.width
;
2912 h
= self
->area
.height
;
2915 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2916 RECT_SET(self
->pre_max_area
,
2917 self
->area
.x
, self
->pre_max_area
.y
,
2918 self
->area
.width
, self
->pre_max_area
.height
);
2920 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2921 RECT_SET(self
->pre_max_area
,
2922 self
->pre_max_area
.x
, self
->area
.y
,
2923 self
->pre_max_area
.width
, self
->area
.height
);
2928 a
= screen_area_monitor(self
->desktop
, 0);
2929 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2930 if (self
->pre_max_area
.width
> 0) {
2931 x
= self
->pre_max_area
.x
;
2932 w
= self
->pre_max_area
.width
;
2934 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2935 0, self
->pre_max_area
.height
);
2937 /* pick some fallbacks... */
2938 x
= a
->x
+ a
->width
/ 4;
2942 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2943 if (self
->pre_max_area
.height
> 0) {
2944 y
= self
->pre_max_area
.y
;
2945 h
= self
->pre_max_area
.height
;
2947 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2948 self
->pre_max_area
.width
, 0);
2950 /* pick some fallbacks... */
2951 y
= a
->y
+ a
->height
/ 4;
2957 if (dir
== 0 || dir
== 1) /* horz */
2958 self
->max_horz
= max
;
2959 if (dir
== 0 || dir
== 2) /* vert */
2960 self
->max_vert
= max
;
2962 client_change_state(self
); /* change the state hints on the client */
2964 client_setup_decor_and_functions(self
);
2966 client_move_resize(self
, x
, y
, w
, h
);
2969 void client_shade(ObClient
*self
, gboolean shade
)
2971 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2972 shade
) || /* can't shade */
2973 self
->shaded
== shade
) return; /* already done */
2975 self
->shaded
= shade
;
2976 client_change_state(self
);
2977 client_change_wm_state(self
); /* the window is being hidden/shown */
2978 /* resize the frame to just the titlebar */
2979 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2982 void client_close(ObClient
*self
)
2986 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2988 /* in the case that the client provides no means to requesting that it
2989 close, we just kill it */
2990 if (!self
->delete_window
)
2994 XXX: itd be cool to do timeouts and shit here for killing the client's
2996 like... if the window is around after 5 seconds, then the close button
2997 turns a nice red, and if this function is called again, the client is
3001 ce
.xclient
.type
= ClientMessage
;
3002 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3003 ce
.xclient
.display
= ob_display
;
3004 ce
.xclient
.window
= self
->window
;
3005 ce
.xclient
.format
= 32;
3006 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
3007 ce
.xclient
.data
.l
[1] = event_curtime
;
3008 ce
.xclient
.data
.l
[2] = 0l;
3009 ce
.xclient
.data
.l
[3] = 0l;
3010 ce
.xclient
.data
.l
[4] = 0l;
3011 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3014 void client_kill(ObClient
*self
)
3016 XKillClient(ob_display
, self
->window
);
3019 void client_hilite(ObClient
*self
, gboolean hilite
)
3021 if (self
->demands_attention
== hilite
)
3022 return; /* no change */
3024 /* don't allow focused windows to hilite */
3025 self
->demands_attention
= hilite
&& !client_focused(self
);
3026 if (self
->frame
!= NULL
) { /* if we're mapping, just set the state */
3027 if (self
->demands_attention
)
3028 frame_flash_start(self
->frame
);
3030 frame_flash_stop(self
->frame
);
3031 client_change_state(self
);
3035 void client_set_desktop_recursive(ObClient
*self
,
3042 if (target
!= self
->desktop
) {
3044 ob_debug("Setting desktop %u\n", target
+1);
3046 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
3048 old
= self
->desktop
;
3049 self
->desktop
= target
;
3050 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
3051 /* the frame can display the current desktop state */
3052 frame_adjust_state(self
->frame
);
3053 /* 'move' the window to the new desktop */
3055 client_showhide(self
);
3056 /* raise if it was not already on the desktop */
3057 if (old
!= DESKTOP_ALL
)
3058 stacking_raise(CLIENT_AS_WINDOW(self
));
3059 if (STRUT_EXISTS(self
->strut
))
3060 screen_update_areas();
3063 /* move all transients */
3064 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3065 if (it
->data
!= self
)
3066 if (client_is_direct_child(self
, it
->data
))
3067 client_set_desktop_recursive(it
->data
, target
, donthide
);
3070 void client_set_desktop(ObClient
*self
, guint target
,
3073 self
= client_search_top_normal_parent(self
);
3074 client_set_desktop_recursive(self
, target
, donthide
);
3077 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
3079 while (child
!= parent
&&
3080 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
3081 child
= child
->transient_for
;
3082 return child
== parent
;
3085 ObClient
*client_search_modal_child(ObClient
*self
)
3090 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
3091 ObClient
*c
= it
->data
;
3092 if ((ret
= client_search_modal_child(c
))) return ret
;
3093 if (c
->modal
) return c
;
3098 gboolean
client_validate(ObClient
*self
)
3102 XSync(ob_display
, FALSE
); /* get all events on the server */
3104 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
3105 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
3106 XPutBackEvent(ob_display
, &e
);
3113 void client_set_wm_state(ObClient
*self
, glong state
)
3115 if (state
== self
->wmstate
) return; /* no change */
3119 client_iconify(self
, TRUE
, TRUE
);
3122 client_iconify(self
, FALSE
, TRUE
);
3127 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
3129 gboolean shaded
= self
->shaded
;
3130 gboolean fullscreen
= self
->fullscreen
;
3131 gboolean undecorated
= self
->undecorated
;
3132 gboolean max_horz
= self
->max_horz
;
3133 gboolean max_vert
= self
->max_vert
;
3134 gboolean modal
= self
->modal
;
3135 gboolean iconic
= self
->iconic
;
3136 gboolean demands_attention
= self
->demands_attention
;
3137 gboolean above
= self
->above
;
3138 gboolean below
= self
->below
;
3141 if (!(action
== prop_atoms
.net_wm_state_add
||
3142 action
== prop_atoms
.net_wm_state_remove
||
3143 action
== prop_atoms
.net_wm_state_toggle
))
3144 /* an invalid action was passed to the client message, ignore it */
3147 for (i
= 0; i
< 2; ++i
) {
3148 Atom state
= i
== 0 ? data1
: data2
;
3150 if (!state
) continue;
3152 /* if toggling, then pick whether we're adding or removing */
3153 if (action
== prop_atoms
.net_wm_state_toggle
) {
3154 if (state
== prop_atoms
.net_wm_state_modal
)
3155 action
= modal
? prop_atoms
.net_wm_state_remove
:
3156 prop_atoms
.net_wm_state_add
;
3157 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
3158 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
3159 prop_atoms
.net_wm_state_add
;
3160 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
3161 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
3162 prop_atoms
.net_wm_state_add
;
3163 else if (state
== prop_atoms
.net_wm_state_shaded
)
3164 action
= shaded
? prop_atoms
.net_wm_state_remove
:
3165 prop_atoms
.net_wm_state_add
;
3166 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
3167 action
= self
->skip_taskbar
?
3168 prop_atoms
.net_wm_state_remove
:
3169 prop_atoms
.net_wm_state_add
;
3170 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
3171 action
= self
->skip_pager
?
3172 prop_atoms
.net_wm_state_remove
:
3173 prop_atoms
.net_wm_state_add
;
3174 else if (state
== prop_atoms
.net_wm_state_hidden
)
3175 action
= self
->iconic
?
3176 prop_atoms
.net_wm_state_remove
:
3177 prop_atoms
.net_wm_state_add
;
3178 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
3179 action
= fullscreen
?
3180 prop_atoms
.net_wm_state_remove
:
3181 prop_atoms
.net_wm_state_add
;
3182 else if (state
== prop_atoms
.net_wm_state_above
)
3183 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
3184 prop_atoms
.net_wm_state_add
;
3185 else if (state
== prop_atoms
.net_wm_state_below
)
3186 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
3187 prop_atoms
.net_wm_state_add
;
3188 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
3189 action
= self
->demands_attention
?
3190 prop_atoms
.net_wm_state_remove
:
3191 prop_atoms
.net_wm_state_add
;
3192 else if (state
== prop_atoms
.openbox_wm_state_undecorated
)
3193 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
3194 prop_atoms
.net_wm_state_add
;
3197 if (action
== prop_atoms
.net_wm_state_add
) {
3198 if (state
== prop_atoms
.net_wm_state_modal
) {
3200 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
3202 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
3204 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
3206 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
3207 self
->skip_taskbar
= TRUE
;
3208 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
3209 self
->skip_pager
= TRUE
;
3210 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
3212 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
3214 } else if (state
== prop_atoms
.net_wm_state_above
) {
3217 } else if (state
== prop_atoms
.net_wm_state_below
) {
3220 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
3221 demands_attention
= TRUE
;
3222 } else if (state
== prop_atoms
.openbox_wm_state_undecorated
) {
3226 } else { /* action == prop_atoms.net_wm_state_remove */
3227 if (state
== prop_atoms
.net_wm_state_modal
) {
3229 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
3231 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
3233 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
3235 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
3236 self
->skip_taskbar
= FALSE
;
3237 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
3238 self
->skip_pager
= FALSE
;
3239 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
3241 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
3243 } else if (state
== prop_atoms
.net_wm_state_above
) {
3245 } else if (state
== prop_atoms
.net_wm_state_below
) {
3247 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
3248 demands_attention
= FALSE
;
3249 } else if (state
== prop_atoms
.openbox_wm_state_undecorated
) {
3250 undecorated
= FALSE
;
3254 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
3255 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
3257 if (max_horz
== max_vert
) { /* both going the same way */
3258 client_maximize(self
, max_horz
, 0);
3260 client_maximize(self
, max_horz
, 1);
3261 client_maximize(self
, max_vert
, 2);
3265 if (max_horz
!= self
->max_horz
)
3266 client_maximize(self
, max_horz
, 1);
3268 client_maximize(self
, max_vert
, 2);
3271 /* change fullscreen state before shading, as it will affect if the window
3273 if (fullscreen
!= self
->fullscreen
)
3274 client_fullscreen(self
, fullscreen
);
3275 if (shaded
!= self
->shaded
)
3276 client_shade(self
, shaded
);
3277 if (undecorated
!= self
->undecorated
)
3278 client_set_undecorated(self
, undecorated
);
3279 if (modal
!= self
->modal
) {
3280 self
->modal
= modal
;
3281 /* when a window changes modality, then its stacking order with its
3282 transients needs to change */
3283 stacking_raise(CLIENT_AS_WINDOW(self
));
3285 if (iconic
!= self
->iconic
)
3286 client_iconify(self
, iconic
, FALSE
);
3288 if (demands_attention
!= self
->demands_attention
)
3289 client_hilite(self
, demands_attention
);
3291 if (above
!= self
->above
|| below
!= self
->below
) {
3292 self
->above
= above
;
3293 self
->below
= below
;
3294 client_calc_layer(self
);
3297 client_change_state(self
); /* change the hint to reflect these changes */
3300 ObClient
*client_focus_target(ObClient
*self
)
3302 ObClient
*child
= NULL
;
3304 child
= client_search_modal_child(self
);
3305 if (child
) return child
;
3309 gboolean
client_can_focus(ObClient
*self
)
3313 /* choose the correct target */
3314 self
= client_focus_target(self
);
3316 if (!self
->frame
->visible
)
3319 if (!(self
->can_focus
|| self
->focus_notify
))
3322 /* do a check to see if the window has already been unmapped or destroyed
3323 do this intelligently while watching out for unmaps we've generated
3324 (ignore_unmaps > 0) */
3325 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
3326 DestroyNotify
, &ev
)) {
3327 XPutBackEvent(ob_display
, &ev
);
3330 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
3331 UnmapNotify
, &ev
)) {
3332 if (self
->ignore_unmaps
) {
3333 self
->ignore_unmaps
--;
3335 XPutBackEvent(ob_display
, &ev
);
3343 gboolean
client_focus(ObClient
*self
)
3345 /* choose the correct target */
3346 self
= client_focus_target(self
);
3348 if (!client_can_focus(self
)) {
3349 if (!self
->frame
->visible
) {
3350 /* update the focus lists */
3351 focus_order_to_top(self
);
3356 ob_debug_type(OB_DEBUG_FOCUS
,
3357 "Focusing client \"%s\" at time %u\n",
3358 self
->title
, event_curtime
);
3360 if (self
->can_focus
) {
3361 /* This can cause a BadMatch error with CurrentTime, or if an app
3362 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3363 xerror_set_ignore(TRUE
);
3364 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3366 xerror_set_ignore(FALSE
);
3369 if (self
->focus_notify
) {
3371 ce
.xclient
.type
= ClientMessage
;
3372 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3373 ce
.xclient
.display
= ob_display
;
3374 ce
.xclient
.window
= self
->window
;
3375 ce
.xclient
.format
= 32;
3376 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3377 ce
.xclient
.data
.l
[1] = event_curtime
;
3378 ce
.xclient
.data
.l
[2] = 0l;
3379 ce
.xclient
.data
.l
[3] = 0l;
3380 ce
.xclient
.data
.l
[4] = 0l;
3381 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3385 ob_debug("%sively focusing %lx at %d\n",
3386 (self
->can_focus
? "act" : "pass"),
3387 self
->window
, (gint
) event_curtime
);
3390 /* Cause the FocusIn to come back to us. Important for desktop switches,
3391 since otherwise we'll have no FocusIn on the queue and send it off to
3392 the focus_backup. */
3393 XSync(ob_display
, FALSE
);
3397 /*! Present the client to the user.
3398 @param raise If the client should be raised or not. You should only set
3399 raise to false if you don't care if the window is completely
3402 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
)
3404 /* if using focus_delay, stop the timer now so that focus doesn't
3406 event_halt_focus_delay();
3408 if (client_normal(self
) && screen_showing_desktop
)
3409 screen_show_desktop(FALSE
, FALSE
);
3411 client_iconify(self
, FALSE
, here
);
3412 if (self
->desktop
!= DESKTOP_ALL
&&
3413 self
->desktop
!= screen_desktop
)
3416 client_set_desktop(self
, screen_desktop
, FALSE
);
3418 screen_set_desktop(self
->desktop
, FALSE
);
3419 } else if (!self
->frame
->visible
)
3420 /* if its not visible for other reasons, then don't mess
3424 client_shade(self
, FALSE
);
3426 stacking_raise(CLIENT_AS_WINDOW(self
));
3431 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3433 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3434 gboolean allow
= FALSE
;
3436 /* if the request came from the user, or if nothing is focused, then grant
3438 if the currently focused app doesn't set a user_time, then it can't
3439 benefit from any focus stealing prevention.
3441 if (user
|| !focus_client
|| !last_time
)
3443 /* otherwise, if they didn't give a time stamp or if it is too old, they
3446 allow
= event_curtime
&& event_time_after(event_curtime
, last_time
);
3448 ob_debug_type(OB_DEBUG_FOCUS
,
3449 "Want to activate window 0x%x with time %u (last time %u), "
3450 "source=%s allowing? %d\n",
3451 self
->window
, event_curtime
, last_time
,
3452 (user
? "user" : "application"), allow
);
3455 if (event_curtime
!= CurrentTime
)
3456 self
->user_time
= event_curtime
;
3458 client_present(self
, here
, TRUE
);
3460 /* don't focus it but tell the user it wants attention */
3461 client_hilite(self
, TRUE
);
3464 static void client_bring_helper_windows_recursive(ObClient
*self
,
3469 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3470 client_bring_helper_windows_recursive(it
->data
, desktop
);
3472 if (client_helper(self
) &&
3473 self
->desktop
!= desktop
&& self
->desktop
!= DESKTOP_ALL
)
3475 client_set_desktop(self
, desktop
, FALSE
);
3479 void client_bring_helper_windows(ObClient
*self
)
3481 client_bring_helper_windows_recursive(self
, self
->desktop
);
3484 gboolean
client_focused(ObClient
*self
)
3486 return self
== focus_client
;
3489 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3492 /* si is the smallest image >= req */
3493 /* li is the largest image < req */
3494 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3496 if (!self
->nicons
) {
3497 ObClientIcon
*parent
= NULL
;
3499 if (self
->transient_for
) {
3500 if (self
->transient_for
!= OB_TRAN_GROUP
)
3501 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3504 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3505 ObClient
*c
= it
->data
;
3506 if (c
!= self
&& !c
->transient_for
) {
3507 if ((parent
= client_icon_recursive(c
, w
, h
)))
3517 for (i
= 0; i
< self
->nicons
; ++i
) {
3518 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3519 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3523 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3528 if (largest
== 0) /* didnt find one smaller than the requested size */
3529 return &self
->icons
[si
];
3530 return &self
->icons
[li
];
3533 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3536 static ObClientIcon deficon
;
3538 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3539 deficon
.width
= deficon
.height
= 48;
3540 deficon
.data
= ob_rr_theme
->def_win_icon
;
3546 void client_set_layer(ObClient
*self
, gint layer
)
3550 self
->above
= FALSE
;
3551 } else if (layer
== 0) {
3552 self
->below
= self
->above
= FALSE
;
3554 self
->below
= FALSE
;
3557 client_calc_layer(self
);
3558 client_change_state(self
); /* reflect this in the state hints */
3561 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3563 if (self
->undecorated
!= undecorated
) {
3564 self
->undecorated
= undecorated
;
3565 client_setup_decor_and_functions(self
);
3566 /* Make sure the client knows it might have moved. Maybe there is a
3567 * better way of doing this so only one client_configure is sent, but
3568 * since 125 of these are sent per second when moving the window (with
3569 * user = FALSE) i doubt it matters much.
3571 client_configure(self
, self
->area
.x
, self
->area
.y
,
3572 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3573 client_change_state(self
); /* reflect this in the state hints */
3577 guint
client_monitor(ObClient
*self
)
3579 return screen_find_monitor(&self
->frame
->area
);
3582 ObClient
*client_search_top_normal_parent(ObClient
*self
)
3584 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3585 client_normal(self
->transient_for
))
3586 self
= self
->transient_for
;
3590 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
3592 ObStackingLayer layer
)
3596 /* move up the direct transient chain as far as possible */
3597 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3598 (!bylayer
|| self
->transient_for
->layer
== layer
) &&
3599 client_normal(self
->transient_for
))
3600 self
= self
->transient_for
;
3602 if (!self
->transient_for
)
3603 ret
= g_slist_prepend(ret
, self
);
3607 g_assert(self
->group
);
3609 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3610 ObClient
*c
= it
->data
;
3612 if (!c
->transient_for
&& client_normal(c
) &&
3613 (!bylayer
|| c
->layer
== layer
))
3615 ret
= g_slist_prepend(ret
, c
);
3619 if (ret
== NULL
) /* no group parents */
3620 ret
= g_slist_prepend(ret
, self
);
3626 GSList
*client_search_all_top_parents(ObClient
*self
)
3628 return client_search_all_top_parents_internal(self
, FALSE
, 0);
3631 GSList
*client_search_all_top_parents_layer(ObClient
*self
)
3633 return client_search_all_top_parents_internal(self
, TRUE
, self
->layer
);
3636 ObClient
*client_search_focus_parent(ObClient
*self
)
3638 if (self
->transient_for
) {
3639 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3640 if (client_focused(self
->transient_for
))
3641 return self
->transient_for
;
3645 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3646 ObClient
*c
= it
->data
;
3648 /* checking transient_for prevents infinate loops! */
3649 if (c
!= self
&& !c
->transient_for
)
3650 if (client_focused(c
))
3659 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3661 if (self
->transient_for
) {
3662 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3663 if (self
->transient_for
== search
)
3668 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3669 ObClient
*c
= it
->data
;
3671 /* checking transient_for prevents infinate loops! */
3672 if (c
!= self
&& !c
->transient_for
)
3682 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3686 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3687 if (sit
->data
== search
)
3689 if (client_search_transient(sit
->data
, search
))
3695 #define WANT_EDGE(cur, c) \
3698 if(!client_normal(cur)) \
3700 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3704 if(cur->layer < c->layer && !config_resist_layers_below) \
3707 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3708 if ((his_edge_start >= my_edge_start && \
3709 his_edge_start <= my_edge_end) || \
3710 (my_edge_start >= his_edge_start && \
3711 my_edge_start <= his_edge_end)) \
3714 /* finds the nearest edge in the given direction from the current client
3715 * note to self: the edge is the -frame- edge (the actual one), not the
3718 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3720 gint dest
, monitor_dest
;
3721 gint my_edge_start
, my_edge_end
, my_offset
;
3728 a
= screen_area(c
->desktop
);
3729 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3732 case OB_DIRECTION_NORTH
:
3733 my_edge_start
= c
->frame
->area
.x
;
3734 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3735 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3737 /* default: top of screen */
3738 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3739 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3740 /* if the monitor edge comes before the screen edge, */
3741 /* use that as the destination instead. (For xinerama) */
3742 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3743 dest
= monitor_dest
;
3745 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3746 gint his_edge_start
, his_edge_end
, his_offset
;
3747 ObClient
*cur
= it
->data
;
3751 his_edge_start
= cur
->frame
->area
.x
;
3752 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3753 his_offset
= cur
->frame
->area
.y
+
3754 (hang
? 0 : cur
->frame
->area
.height
);
3756 if(his_offset
+ 1 > my_offset
)
3759 if(his_offset
< dest
)
3762 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3765 case OB_DIRECTION_SOUTH
:
3766 my_edge_start
= c
->frame
->area
.x
;
3767 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3768 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3770 /* default: bottom of screen */
3771 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3772 monitor_dest
= monitor
->y
+ monitor
->height
-
3773 (hang
? c
->frame
->area
.height
: 0);
3774 /* if the monitor edge comes before the screen edge, */
3775 /* use that as the destination instead. (For xinerama) */
3776 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3777 dest
= monitor_dest
;
3779 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3780 gint his_edge_start
, his_edge_end
, his_offset
;
3781 ObClient
*cur
= it
->data
;
3785 his_edge_start
= cur
->frame
->area
.x
;
3786 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3787 his_offset
= cur
->frame
->area
.y
+
3788 (hang
? cur
->frame
->area
.height
: 0);
3791 if(his_offset
- 1 < my_offset
)
3794 if(his_offset
> dest
)
3797 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3800 case OB_DIRECTION_WEST
:
3801 my_edge_start
= c
->frame
->area
.y
;
3802 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3803 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3805 /* default: leftmost egde of screen */
3806 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3807 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3808 /* if the monitor edge comes before the screen edge, */
3809 /* use that as the destination instead. (For xinerama) */
3810 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3811 dest
= monitor_dest
;
3813 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3814 gint his_edge_start
, his_edge_end
, his_offset
;
3815 ObClient
*cur
= it
->data
;
3819 his_edge_start
= cur
->frame
->area
.y
;
3820 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3821 his_offset
= cur
->frame
->area
.x
+
3822 (hang
? 0 : cur
->frame
->area
.width
);
3824 if(his_offset
+ 1 > my_offset
)
3827 if(his_offset
< dest
)
3830 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3833 case OB_DIRECTION_EAST
:
3834 my_edge_start
= c
->frame
->area
.y
;
3835 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3836 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3838 /* default: rightmost edge of screen */
3839 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3840 monitor_dest
= monitor
->x
+ monitor
->width
-
3841 (hang
? c
->frame
->area
.width
: 0);
3842 /* if the monitor edge comes before the screen edge, */
3843 /* use that as the destination instead. (For xinerama) */
3844 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3845 dest
= monitor_dest
;
3847 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3848 gint his_edge_start
, his_edge_end
, his_offset
;
3849 ObClient
*cur
= it
->data
;
3853 his_edge_start
= cur
->frame
->area
.y
;
3854 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3855 his_offset
= cur
->frame
->area
.x
+
3856 (hang
? cur
->frame
->area
.width
: 0);
3858 if(his_offset
- 1 < my_offset
)
3861 if(his_offset
> dest
)
3864 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3867 case OB_DIRECTION_NORTHEAST
:
3868 case OB_DIRECTION_SOUTHEAST
:
3869 case OB_DIRECTION_NORTHWEST
:
3870 case OB_DIRECTION_SOUTHWEST
:
3871 /* not implemented */
3873 g_assert_not_reached();
3874 dest
= 0; /* suppress warning */
3879 ObClient
* client_under_pointer()
3883 ObClient
*ret
= NULL
;
3885 if (screen_pointer_pos(&x
, &y
)) {
3886 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3887 if (WINDOW_IS_CLIENT(it
->data
)) {
3888 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3889 if (c
->frame
->visible
&&
3890 /* ignore all animating windows */
3891 !frame_iconify_animating(c
->frame
) &&
3892 RECT_CONTAINS(c
->frame
->area
, x
, y
))
3903 gboolean
client_has_group_siblings(ObClient
*self
)
3905 return self
->group
&& self
->group
->members
->next
;