1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
49 #include <X11/Xutil.h>
51 /*! The event mask to grab on client windows */
52 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
55 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
60 ObClientDestructor func
;
64 GList
*client_list
= NULL
;
66 static GSList
*client_destructors
= NULL
;
68 static void client_get_all(ObClient
*self
);
69 static void client_toggle_border(ObClient
*self
, gboolean show
);
70 static void client_get_startup_id(ObClient
*self
);
71 static void client_get_area(ObClient
*self
);
72 static void client_get_desktop(ObClient
*self
);
73 static void client_get_state(ObClient
*self
);
74 static void client_get_layer(ObClient
*self
);
75 static void client_get_shaped(ObClient
*self
);
76 static void client_get_mwm_hints(ObClient
*self
);
77 static void client_get_gravity(ObClient
*self
);
78 static void client_get_client_machine(ObClient
*self
);
79 static void client_get_colormap(ObClient
*self
);
80 static void client_change_allowed_actions(ObClient
*self
);
81 static void client_change_state(ObClient
*self
);
82 static void client_change_wm_state(ObClient
*self
);
83 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
);
84 static void client_restore_session_state(ObClient
*self
);
85 static void client_restore_session_stacking(ObClient
*self
);
86 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
88 void client_startup(gboolean reconfig
)
95 void client_shutdown(gboolean reconfig
)
99 void client_add_destructor(ObClientDestructor func
, gpointer data
)
101 Destructor
*d
= g_new(Destructor
, 1);
104 client_destructors
= g_slist_prepend(client_destructors
, d
);
107 void client_remove_destructor(ObClientDestructor func
)
111 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
112 Destructor
*d
= it
->data
;
113 if (d
->func
== func
) {
115 client_destructors
= g_slist_delete_link(client_destructors
, it
);
121 void client_set_list()
123 Window
*windows
, *win_it
;
125 guint size
= g_list_length(client_list
);
127 /* create an array of the window ids */
129 windows
= g_new(Window
, size
);
131 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
132 *win_it
= ((ObClient
*)it
->data
)->window
;
136 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
137 net_client_list
, window
, (gulong
*)windows
, size
);
146 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
150 for (it = self->transients; it; it = g_slist_next(it)) {
151 if (!func(it->data, data)) return;
152 client_foreach_transient(it->data, func, data);
156 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
158 if (self->transient_for) {
159 if (self->transient_for != OB_TRAN_GROUP) {
160 if (!func(self->transient_for, data)) return;
161 client_foreach_ancestor(self->transient_for, func, data);
165 for (it = self->group->members; it; it = g_slist_next(it))
166 if (it->data != self &&
167 !((ObClient*)it->data)->transient_for) {
168 if (!func(it->data, data)) return;
169 client_foreach_ancestor(it->data, func, data);
176 void client_manage_all()
181 XWindowAttributes attrib
;
183 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
184 &w
, &w
, &children
, &nchild
);
186 /* remove all icon windows from the list */
187 for (i
= 0; i
< nchild
; i
++) {
188 if (children
[i
] == None
) continue;
189 wmhints
= XGetWMHints(ob_display
, children
[i
]);
191 if ((wmhints
->flags
& IconWindowHint
) &&
192 (wmhints
->icon_window
!= children
[i
]))
193 for (j
= 0; j
< nchild
; j
++)
194 if (children
[j
] == wmhints
->icon_window
) {
202 for (i
= 0; i
< nchild
; ++i
) {
203 if (children
[i
] == None
)
205 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
206 if (attrib
.override_redirect
) continue;
208 if (attrib
.map_state
!= IsUnmapped
)
209 client_manage(children
[i
]);
215 void client_manage(Window window
)
219 XWindowAttributes attrib
;
220 XSetWindowAttributes attrib_set
;
222 gboolean activate
= FALSE
;
223 ObAppSettings
*settings
;
228 /* check if it has already been unmapped by the time we started mapping.
229 the grab does a sync so we don't have to here */
230 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
231 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
233 XPutBackEvent(ob_display
, &e
);
235 ob_debug("Trying to manage unmapped window. Aborting that.\n");
237 return; /* don't manage it */
240 /* make sure it isn't an override-redirect window */
241 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
242 attrib
.override_redirect
)
245 return; /* don't manage it */
248 /* is the window a docking app */
249 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
250 if ((wmhint
->flags
& StateHint
) &&
251 wmhint
->initial_state
== WithdrawnState
)
253 dock_add(window
, wmhint
);
261 ob_debug("Managing window: %lx\n", window
);
263 /* choose the events we want to receive on the CLIENT window */
264 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
265 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
266 XChangeWindowAttributes(ob_display
, window
,
267 CWEventMask
|CWDontPropagate
, &attrib_set
);
270 /* create the ObClient struct, and populate it from the hints on the
272 self
= g_new0(ObClient
, 1);
273 self
->obwin
.type
= Window_Client
;
274 self
->window
= window
;
276 /* non-zero defaults */
277 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
279 self
->desktop
= screen_num_desktops
; /* always an invalid value */
280 self
->user_time
= CurrentTime
;
282 client_get_all(self
);
283 /* per-app settings override stuff, and return the settings for other
285 settings
= client_get_settings_state(self
);
286 /* the session should get the last say */
287 client_restore_session_state(self
);
289 client_calc_layer(self
);
292 Time t
= sn_app_started(self
->startup_id
, self
->class);
293 if (t
) self
->user_time
= t
;
296 /* update the focus lists, do this before the call to change_state or
297 it can end up in the list twice! */
298 focus_order_add_new(self
);
300 /* remove the client's border (and adjust re gravity) */
301 client_toggle_border(self
, FALSE
);
303 /* specify that if we exit, the window should not be destroyed and should
304 be reparented back to root automatically */
305 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
307 /* create the decoration frame for the client window */
308 self
->frame
= frame_new(self
);
310 frame_grab_client(self
->frame
, self
);
312 /* do this after we have a frame.. it uses the frame to help determine the
313 WM_STATE to apply. */
314 client_change_state(self
);
318 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
319 client_restore_session_stacking(self
);
321 /* focus the new window? */
322 if (ob_state() != OB_STATE_STARTING
&&
324 /* this means focus=true for window is same as config_focus_new=true */
325 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
326 client_search_focus_parent(self
)) &&
327 /* this checks for focus=false for the window */
328 (!settings
|| settings
->focus
!= 0) &&
329 /* note the check against Type_Normal/Dialog, not client_normal(self),
330 which would also include other types. in this case we want more
331 strict rules for focus */
332 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
333 self
->type
== OB_CLIENT_TYPE_DIALOG
))
338 /* get the current position */
342 /* figure out placement for the window */
343 if (ob_state() == OB_STATE_RUNNING
) {
346 transient
= place_client(self
, &newx
, &newy
, settings
);
348 /* make sure the window is visible. */
349 client_find_onscreen(self
, &newx
, &newy
,
350 self
->frame
->area
.width
,
351 self
->frame
->area
.height
,
352 /* non-normal clients has less rules, and
353 windows that are being restored from a
354 session do also. we can assume you want
355 it back where you saved it. Clients saying
356 they placed themselves are subjected to
357 harder rules, ones that are placed by
358 place.c or by the user are allowed partially
359 off-screen and on xinerama divides (ie,
360 it is up to the placement routines to avoid
361 the xinerama divides) */
363 (((self
->positioned
& PPosition
) &&
364 !(self
->positioned
& USPosition
)) &&
365 client_normal(self
) &&
369 /* do this after the window is placed, so the premax/prefullscreen numbers
371 also, this moves the window to the position where it has been placed
373 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
374 self
->window
, newx
, newy
, self
->area
.width
, self
->area
.height
);
375 client_apply_startup_state(self
, newx
, newy
);
377 mouse_grab_for_client(self
, TRUE
);
380 guint32 last_time
= focus_client
?
381 focus_client
->user_time
: CurrentTime
;
383 /* This is focus stealing prevention */
384 ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
385 self
->window
, self
->user_time
, last_time
);
387 /* if it's on another desktop */
388 if (!(self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
389 && /* the timestamp is from before you changed desktops */
390 self
->user_time
&& screen_desktop_user_time
&&
391 !event_time_after(self
->user_time
, screen_desktop_user_time
))
395 /* If nothing is focused, or a parent was focused, then focus this
398 else if (!focus_client
|| client_search_focus_parent(self
) != NULL
)
402 /* If time stamp is old, don't steal focus */
403 if (self
->user_time
&& last_time
&&
404 !event_time_after(self
->user_time
, last_time
))
408 /* Don't steal focus from globally active clients.
409 I stole this idea from KWin. It seems nice.
411 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
))
416 ob_debug("Focus stealing prevention activated for %s with time %u "
418 self
->title
, self
->user_time
, last_time
);
419 /* if the client isn't focused, then hilite it so the user
421 client_hilite(self
, TRUE
);
425 /* This may look rather odd. Well it's because new windows are added
426 to the stacking order non-intrusively. If we're not going to focus
427 the new window or hilite it, then we raise it to the top. This will
428 take affect for things that don't get focused like splash screens.
429 Also if you don't have focus_new enabled, then it's going to get
430 raised to the top. Legacy begets legacy I guess?
435 /* this has to happen before we try focus the window, but we want it to
436 happen after the client's stacking has been determined or it looks bad
440 /* use client_focus instead of client_activate cuz client_activate does
441 stuff like switch desktops etc and I'm not interested in all that when
442 a window maps since its not based on an action from the user like
443 clicking a window to activate it. so keep the new window out of the way
446 client_activate(self
, FALSE
, TRUE
);
448 /* add to client list/map */
449 client_list
= g_list_append(client_list
, self
);
450 g_hash_table_insert(window_map
, &self
->window
, self
);
452 /* this has to happen after we're in the client_list */
453 if (STRUT_EXISTS(self
->strut
))
454 screen_update_areas();
456 /* update the list hints */
459 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
462 void client_unmanage_all()
464 while (client_list
!= NULL
)
465 client_unmanage(client_list
->data
);
468 void client_unmanage(ObClient
*self
)
473 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self
->window
, self
->class,
474 self
->title
? self
->title
: "");
476 g_assert(self
!= NULL
);
478 /* we dont want events no more. do this before hiding the frame so we
479 don't generate more events */
480 XSelectInput(ob_display
, self
->window
, NoEventMask
);
482 frame_hide(self
->frame
);
483 /* flush to send the hide to the server quickly */
486 /* ignore enter events from the unmap so it doesnt mess with the focus */
487 event_ignore_queued_enters();
489 mouse_grab_for_client(self
, FALSE
);
491 /* remove the window from our save set */
492 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
494 /* update the focus lists */
495 focus_order_remove(self
);
496 if (self
== focus_client
) {
497 /* we have to fall back here because we might not get a focus out.
498 1. we need to xselectinput off the window before we unmap it because
499 otherwise we end up getting unmapnotifies we don't want and they
500 can mess up mapping it again quickly
501 2. this means that if we unmanage from a synthetic unmapnotify, we
502 are the ones unmapped it, and causing the focusout. so we won't
503 get the focusout event.
504 3. we can't handle focusin events on the root window because they
505 come from all screens, so the focus change gets lost
507 focus_fallback(FALSE
);
509 /* don't leave an invalid focus_client */
513 client_list
= g_list_remove(client_list
, self
);
514 stacking_remove(self
);
515 g_hash_table_remove(window_map
, &self
->window
);
517 /* once the client is out of the list, update the struts to remove its
519 if (STRUT_EXISTS(self
->strut
))
520 screen_update_areas();
522 for (it
= client_destructors
; it
; it
= g_slist_next(it
)) {
523 Destructor
*d
= it
->data
;
524 d
->func(self
, d
->data
);
527 /* tell our parent(s) that we're gone */
528 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
529 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
530 if (it
->data
!= self
)
531 ((ObClient
*)it
->data
)->transients
=
532 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
533 } else if (self
->transient_for
) { /* transient of window */
534 self
->transient_for
->transients
=
535 g_slist_remove(self
->transient_for
->transients
, self
);
538 /* tell our transients that we're gone */
539 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
540 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
541 ((ObClient
*)it
->data
)->transient_for
= NULL
;
542 client_calc_layer(it
->data
);
546 /* remove from its group */
548 group_remove(self
->group
, self
);
552 /* restore the window's original geometry so it is not lost */
556 if (self
->fullscreen
)
557 a
= self
->pre_fullscreen_area
;
558 else if (self
->max_horz
|| self
->max_vert
) {
559 if (self
->max_horz
) {
560 a
.x
= self
->pre_max_area
.x
;
561 a
.width
= self
->pre_max_area
.width
;
563 if (self
->max_vert
) {
564 a
.y
= self
->pre_max_area
.y
;
565 a
.height
= self
->pre_max_area
.height
;
569 /* give the client its border back */
570 client_toggle_border(self
, TRUE
);
572 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
573 self
->decorations
= 0; /* unmanaged windows have no decor */
575 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
578 /* reparent the window out of the frame, and free the frame */
579 frame_release_client(self
->frame
, self
);
582 if (ob_state() != OB_STATE_EXITING
) {
583 /* these values should not be persisted across a window
585 PROP_ERASE(self
->window
, net_wm_desktop
);
586 PROP_ERASE(self
->window
, net_wm_state
);
587 PROP_ERASE(self
->window
, wm_state
);
589 /* if we're left in an unmapped state, the client wont be mapped. this
590 is bad, since we will no longer be managing the window on restart */
591 XMapWindow(ob_display
, self
->window
);
594 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
596 /* free all data allocated in the client struct */
597 g_slist_free(self
->transients
);
598 for (j
= 0; j
< self
->nicons
; ++j
)
599 g_free(self
->icons
[j
].data
);
600 if (self
->nicons
> 0)
603 g_free(self
->icon_title
);
607 g_free(self
->client_machine
);
608 g_free(self
->sm_client_id
);
611 /* update the list hints */
615 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
617 ObAppSettings
*settings
= NULL
;
620 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
621 ObAppSettings
*app
= it
->data
;
623 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
624 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
625 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
626 && !strcmp(app
->name
, self
->name
)))
628 ob_debug("Window matching: %s\n", app
->name
);
629 /* Match if no role was specified in the per app setting, or if the
630 * string matches the beginning of the role, since apps like to set
631 * the role to things like browser-window-23c4b2f */
633 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
643 if (settings
->shade
!= -1)
644 self
->shaded
= !!settings
->shade
;
645 if (settings
->decor
!= -1)
646 self
->undecorated
= !settings
->decor
;
647 if (settings
->iconic
!= -1)
648 self
->iconic
= !!settings
->iconic
;
649 if (settings
->skip_pager
!= -1)
650 self
->skip_pager
= !!settings
->skip_pager
;
651 if (settings
->skip_taskbar
!= -1)
652 self
->skip_taskbar
= !!settings
->skip_taskbar
;
654 if (settings
->max_vert
!= -1)
655 self
->max_vert
= !!settings
->max_vert
;
656 if (settings
->max_horz
!= -1)
657 self
->max_horz
= !!settings
->max_horz
;
659 if (settings
->fullscreen
!= -1)
660 self
->fullscreen
= !!settings
->fullscreen
;
662 if (settings
->desktop
< screen_num_desktops
663 || settings
->desktop
== DESKTOP_ALL
)
664 self
->desktop
= settings
->desktop
;
666 if (settings
->layer
== -1) {
670 else if (settings
->layer
== 0) {
674 else if (settings
->layer
== 1) {
682 static void client_restore_session_state(ObClient
*self
)
686 if (!(it
= session_state_find(self
)))
689 self
->session
= it
->data
;
691 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
692 self
->positioned
= PPosition
;
693 if (self
->session
->w
> 0)
694 self
->area
.width
= self
->session
->w
;
695 if (self
->session
->h
> 0)
696 self
->area
.height
= self
->session
->h
;
697 XResizeWindow(ob_display
, self
->window
,
698 self
->area
.width
, self
->area
.height
);
700 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
701 self
->session
->desktop
:
702 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
703 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
705 self
->shaded
= self
->session
->shaded
;
706 self
->iconic
= self
->session
->iconic
;
707 self
->skip_pager
= self
->session
->skip_pager
;
708 self
->skip_taskbar
= self
->session
->skip_taskbar
;
709 self
->fullscreen
= self
->session
->fullscreen
;
710 self
->above
= self
->session
->above
;
711 self
->below
= self
->session
->below
;
712 self
->max_horz
= self
->session
->max_horz
;
713 self
->max_vert
= self
->session
->max_vert
;
716 static void client_restore_session_stacking(ObClient
*self
)
720 if (!self
->session
) return;
722 it
= g_list_find(session_saved_state
, self
->session
);
723 for (it
= g_list_previous(it
); it
; it
= g_list_previous(it
)) {
726 for (cit
= client_list
; cit
; cit
= g_list_next(cit
))
727 if (session_state_cmp(it
->data
, cit
->data
))
730 client_calc_layer(self
);
731 stacking_below(CLIENT_AS_WINDOW(self
),
732 CLIENT_AS_WINDOW(cit
->data
));
738 void client_move_onscreen(ObClient
*self
, gboolean rude
)
740 gint x
= self
->area
.x
;
741 gint y
= self
->area
.y
;
742 if (client_find_onscreen(self
, &x
, &y
,
743 self
->frame
->area
.width
,
744 self
->frame
->area
.height
, rude
)) {
745 client_move(self
, x
, y
);
749 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
753 gint ox
= *x
, oy
= *y
;
755 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
758 /* XXX watch for xinerama dead areas */
759 /* This makes sure windows aren't entirely outside of the screen so you
760 can't see them at all.
761 It makes sure 10% of the window is on the screen at least. At don't let
762 it move itself off the top of the screen, which would hide the titlebar
763 on you. (The user can still do this if they want too, it's only limiting
766 if (client_normal(self
)) {
767 a
= screen_area(self
->desktop
);
768 if (!self
->strut
.right
&&
769 *x
+ self
->frame
->area
.width
/10 >= a
->x
+ a
->width
- 1)
770 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
/10;
771 if (!self
->strut
.bottom
&&
772 *y
+ self
->frame
->area
.height
/10 >= a
->y
+ a
->height
- 1)
773 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
/10;
774 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
*9/10 - 1 < a
->x
)
775 *x
= a
->x
- self
->frame
->area
.width
*9/10;
776 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
*9/10 - 1 < a
->y
)
777 *y
= a
->y
- self
->frame
->area
.width
*9/10;
780 /* This here doesn't let windows even a pixel outside the screen,
781 * when called from client_manage, programs placing themselves are
782 * forced completely onscreen, while things like
783 * xterm -geometry resolution-width/2 will work fine. Trying to
784 * place it completely offscreen will be handled in the above code.
785 * Sorry for this confused comment, i am tired. */
787 /* avoid the xinerama monitor divide while we're at it,
788 * remember to fix the placement stuff to avoid it also and
789 * then remove this XXX */
790 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
791 /* dont let windows map into the strut unless they
792 are bigger than the available area */
794 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
795 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
796 *x
= a
->x
+ a
->width
- w
;
798 if (h
<= a
->height
) {
799 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
800 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
801 *y
= a
->y
+ a
->height
- h
;
805 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
808 return ox
!= *x
|| oy
!= *y
;
811 static void client_toggle_border(ObClient
*self
, gboolean show
)
813 /* adjust our idea of where the client is, based on its border. When the
814 border is removed, the client should now be considered to be in a
816 when re-adding the border to the client, the same operation needs to be
818 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
819 gint x
= oldx
, y
= oldy
;
820 switch(self
->gravity
) {
822 case NorthWestGravity
:
824 case SouthWestGravity
:
826 case NorthEastGravity
:
828 case SouthEastGravity
:
829 if (show
) x
-= self
->border_width
* 2;
830 else x
+= self
->border_width
* 2;
837 if (show
) x
-= self
->border_width
;
838 else x
+= self
->border_width
;
841 switch(self
->gravity
) {
843 case NorthWestGravity
:
845 case NorthEastGravity
:
847 case SouthWestGravity
:
849 case SouthEastGravity
:
850 if (show
) y
-= self
->border_width
* 2;
851 else y
+= self
->border_width
* 2;
858 if (show
) y
-= self
->border_width
;
859 else y
+= self
->border_width
;
866 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
868 /* set border_width to 0 because there is no border to add into
869 calculations anymore */
870 self
->border_width
= 0;
872 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
876 static void client_get_all(ObClient
*self
)
878 client_get_area(self
);
879 client_get_mwm_hints(self
);
881 /* The transient hint is used to pick a type, but the type can also affect
882 transiency (dialogs are always made transients of their group if they
883 have one). This is Havoc's idea, but it is needed to make some apps
884 work right (eg tsclient). */
885 client_update_transient_for(self
);
886 client_get_type(self
);/* this can change the mwmhints for special cases */
887 client_get_state(self
);
888 client_update_transient_for(self
);
890 client_update_wmhints(self
);
891 client_get_startup_id(self
);
892 client_get_desktop(self
);/* uses transient data/group/startup id if a
893 desktop is not specified */
894 client_get_shaped(self
);
896 client_get_layer(self
); /* if layer hasn't been specified, get it from
897 other sources if possible */
900 /* a couple type-based defaults for new windows */
902 /* this makes sure that these windows appear on all desktops */
903 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
904 self
->desktop
= DESKTOP_ALL
;
907 client_update_protocols(self
);
909 client_get_gravity(self
); /* get the attribute gravity */
910 client_update_normal_hints(self
); /* this may override the attribute
913 /* got the type, the mwmhints, the protocols, and the normal hints
914 (min/max sizes), so we're ready to set up the decorations/functions */
915 client_setup_decor_and_functions(self
);
918 client_update_sync_request_counter(self
);
920 client_get_client_machine(self
);
921 client_get_colormap(self
);
922 client_update_title(self
);
923 client_update_class(self
);
924 client_update_sm_client_id(self
);
925 client_update_strut(self
);
926 client_update_icons(self
);
927 client_update_user_time(self
);
930 static void client_get_startup_id(ObClient
*self
)
932 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
934 PROP_GETS(self
->group
->leader
,
935 net_startup_id
, utf8
, &self
->startup_id
);
938 static void client_get_area(ObClient
*self
)
940 XWindowAttributes wattrib
;
943 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
944 g_assert(ret
!= BadWindow
);
946 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
947 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
948 self
->border_width
= wattrib
.border_width
;
950 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
951 wattrib
.width
, wattrib
.height
);
954 static void client_get_desktop(ObClient
*self
)
956 guint32 d
= screen_num_desktops
; /* an always-invalid value */
958 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
959 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
960 self
->desktop
= screen_num_desktops
- 1;
964 gboolean trdesk
= FALSE
;
966 if (self
->transient_for
) {
967 if (self
->transient_for
!= OB_TRAN_GROUP
) {
968 self
->desktop
= self
->transient_for
->desktop
;
973 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
974 if (it
->data
!= self
&&
975 !((ObClient
*)it
->data
)->transient_for
) {
976 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
983 /* try get from the startup-notification protocol */
984 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
985 if (self
->desktop
>= screen_num_desktops
&&
986 self
->desktop
!= DESKTOP_ALL
)
987 self
->desktop
= screen_num_desktops
- 1;
989 /* defaults to the current desktop */
990 self
->desktop
= screen_desktop
;
995 static void client_get_layer(ObClient
*self
)
997 if (!(self
->above
|| self
->below
)) {
999 /* apply stuff from the group */
1003 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1004 ObClient
*c
= it
->data
;
1005 if (c
!= self
&& !client_search_transient(self
, c
) &&
1006 client_normal(self
) && client_normal(c
))
1009 (c
->above
? 1 : (c
->below
? -1 : 0)));
1023 g_assert_not_reached();
1030 static void client_get_state(ObClient
*self
)
1035 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1037 for (i
= 0; i
< num
; ++i
) {
1038 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1040 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1041 self
->shaded
= TRUE
;
1042 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1043 self
->iconic
= TRUE
;
1044 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1045 self
->skip_taskbar
= TRUE
;
1046 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1047 self
->skip_pager
= TRUE
;
1048 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1049 self
->fullscreen
= TRUE
;
1050 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1051 self
->max_vert
= TRUE
;
1052 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1053 self
->max_horz
= TRUE
;
1054 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1056 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1058 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1059 self
->demands_attention
= TRUE
;
1060 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1061 self
->undecorated
= TRUE
;
1068 static void client_get_shaped(ObClient
*self
)
1070 self
->shaped
= FALSE
;
1072 if (extensions_shape
) {
1077 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1079 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1080 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1082 self
->shaped
= (s
!= 0);
1087 void client_update_transient_for(ObClient
*self
)
1090 ObClient
*target
= NULL
;
1092 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1093 self
->transient
= TRUE
;
1094 if (t
!= self
->window
) { /* cant be transient to itself! */
1095 target
= g_hash_table_lookup(window_map
, &t
);
1096 /* if this happens then we need to check for it*/
1097 g_assert(target
!= self
);
1098 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1099 /* this can happen when a dialog is a child of
1100 a dockapp, for example */
1104 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1106 Setting the transient_for to Root is actually illegal, however
1107 applications from time have done this to specify transient for
1110 Now you can do that by being a TYPE_DIALOG and not setting
1111 the transient_for hint at all on your window. But people still
1112 use Root, and Kwin is very strange in this regard.
1114 KWin 3.0 will not consider windows with transient_for set to
1115 Root as transient for their group *UNLESS* they are also modal.
1116 In that case, it will make them transient for the group. This
1117 leads to all sorts of weird behavior from KDE apps which are
1118 only tested in KWin. I'd like to follow their behavior just to
1119 make this work right with KDE stuff, but that seems wrong.
1121 if (!target
&& self
->group
) {
1122 /* not transient to a client, see if it is transient for a
1124 if (t
== RootWindow(ob_display
, ob_screen
)) {
1125 /* window is a transient for its group! */
1126 target
= OB_TRAN_GROUP
;
1130 } else if (self
->group
) {
1131 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1132 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1133 self
->type
== OB_CLIENT_TYPE_MENU
||
1134 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1136 self
->transient
= TRUE
;
1137 target
= OB_TRAN_GROUP
;
1140 self
->transient
= FALSE
;
1142 /* if anything has changed... */
1143 if (target
!= self
->transient_for
) {
1144 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1147 /* remove from old parents */
1148 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1149 ObClient
*c
= it
->data
;
1150 if (c
!= self
&& (!c
->transient_for
||
1151 c
->transient_for
!= OB_TRAN_GROUP
))
1152 c
->transients
= g_slist_remove(c
->transients
, self
);
1154 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1155 /* remove from old parent */
1156 self
->transient_for
->transients
=
1157 g_slist_remove(self
->transient_for
->transients
, self
);
1159 self
->transient_for
= target
;
1160 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
1163 /* add to new parents */
1164 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1165 ObClient
*c
= it
->data
;
1166 if (c
!= self
&& (!c
->transient_for
||
1167 c
->transient_for
!= OB_TRAN_GROUP
))
1168 c
->transients
= g_slist_append(c
->transients
, self
);
1171 /* remove all transients which are in the group, that causes
1172 circlular pointer hell of doom */
1173 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
1175 for (sit
= self
->transients
; sit
; sit
= next
) {
1176 next
= g_slist_next(sit
);
1177 if (sit
->data
== it
->data
)
1179 g_slist_delete_link(self
->transients
, sit
);
1182 } else if (self
->transient_for
!= NULL
) { /* transient of window */
1183 /* add to new parent */
1184 self
->transient_for
->transients
=
1185 g_slist_append(self
->transient_for
->transients
, self
);
1190 static void client_get_mwm_hints(ObClient
*self
)
1195 self
->mwmhints
.flags
= 0; /* default to none */
1197 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1199 if (num
>= OB_MWM_ELEMENTS
) {
1200 self
->mwmhints
.flags
= hints
[0];
1201 self
->mwmhints
.functions
= hints
[1];
1202 self
->mwmhints
.decorations
= hints
[2];
1208 void client_get_type(ObClient
*self
)
1215 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1216 /* use the first value that we know about in the array */
1217 for (i
= 0; i
< num
; ++i
) {
1218 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1219 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1220 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1221 self
->type
= OB_CLIENT_TYPE_DOCK
;
1222 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1223 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1224 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1225 self
->type
= OB_CLIENT_TYPE_MENU
;
1226 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1227 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1228 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1229 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1230 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1231 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1232 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1233 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1234 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1235 /* prevent this window from getting any decor or
1237 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1238 OB_MWM_FLAG_DECORATIONS
);
1239 self
->mwmhints
.decorations
= 0;
1240 self
->mwmhints
.functions
= 0;
1242 if (self
->type
!= (ObClientType
) -1)
1243 break; /* grab the first legit type */
1248 if (self
->type
== (ObClientType
) -1) {
1249 /*the window type hint was not set, which means we either classify
1250 ourself as a normal window or a dialog, depending on if we are a
1252 if (self
->transient
)
1253 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1255 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1259 void client_update_protocols(ObClient
*self
)
1262 guint num_return
, i
;
1264 self
->focus_notify
= FALSE
;
1265 self
->delete_window
= FALSE
;
1267 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1268 for (i
= 0; i
< num_return
; ++i
) {
1269 if (proto
[i
] == prop_atoms
.wm_delete_window
)
1270 /* this means we can request the window to close */
1271 self
->delete_window
= TRUE
;
1272 else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1273 /* if this protocol is requested, then the window will be
1274 notified whenever we want it to receive focus */
1275 self
->focus_notify
= TRUE
;
1277 else if (proto
[i
] == prop_atoms
.net_wm_sync_request
)
1278 /* if this protocol is requested, then resizing the
1279 window will be synchronized between the frame and the
1281 self
->sync_request
= TRUE
;
1289 void client_update_sync_request_counter(ObClient
*self
)
1293 if (PROP_GET32(self
->window
, net_wm_sync_request_counter
, cardinal
, &i
)) {
1294 self
->sync_counter
= i
;
1296 self
->sync_counter
= None
;
1300 static void client_get_gravity(ObClient
*self
)
1302 XWindowAttributes wattrib
;
1305 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1306 g_assert(ret
!= BadWindow
);
1307 self
->gravity
= wattrib
.win_gravity
;
1310 void client_get_colormap(ObClient
*self
)
1312 XWindowAttributes wa
;
1314 if (XGetWindowAttributes(ob_display
, self
->window
, &wa
))
1315 client_update_colormap(self
, wa
.colormap
);
1318 void client_update_colormap(ObClient
*self
, Colormap colormap
)
1320 self
->colormap
= colormap
;
1323 void client_update_normal_hints(ObClient
*self
)
1327 gint oldgravity
= self
->gravity
;
1330 self
->min_ratio
= 0.0f
;
1331 self
->max_ratio
= 0.0f
;
1332 SIZE_SET(self
->size_inc
, 1, 1);
1333 SIZE_SET(self
->base_size
, 0, 0);
1334 SIZE_SET(self
->min_size
, 0, 0);
1335 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1337 /* get the hints from the window */
1338 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1339 /* normal windows can't request placement! har har
1340 if (!client_normal(self))
1342 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1344 if (size
.flags
& PWinGravity
) {
1345 self
->gravity
= size
.win_gravity
;
1347 /* if the client has a frame, i.e. has already been mapped and
1348 is changing its gravity */
1349 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1350 /* move our idea of the client's position based on its new
1352 self
->area
.x
= self
->frame
->area
.x
;
1353 self
->area
.y
= self
->frame
->area
.y
;
1354 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
1358 if (size
.flags
& PAspect
) {
1359 if (size
.min_aspect
.y
)
1361 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1362 if (size
.max_aspect
.y
)
1364 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1367 if (size
.flags
& PMinSize
)
1368 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1370 if (size
.flags
& PMaxSize
)
1371 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1373 if (size
.flags
& PBaseSize
)
1374 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1376 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1377 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1381 void client_setup_decor_and_functions(ObClient
*self
)
1383 /* start with everything (cept fullscreen) */
1385 (OB_FRAME_DECOR_TITLEBAR
|
1386 OB_FRAME_DECOR_HANDLE
|
1387 OB_FRAME_DECOR_GRIPS
|
1388 OB_FRAME_DECOR_BORDER
|
1389 OB_FRAME_DECOR_ICON
|
1390 OB_FRAME_DECOR_ALLDESKTOPS
|
1391 OB_FRAME_DECOR_ICONIFY
|
1392 OB_FRAME_DECOR_MAXIMIZE
|
1393 OB_FRAME_DECOR_SHADE
|
1394 OB_FRAME_DECOR_CLOSE
);
1396 (OB_CLIENT_FUNC_RESIZE
|
1397 OB_CLIENT_FUNC_MOVE
|
1398 OB_CLIENT_FUNC_ICONIFY
|
1399 OB_CLIENT_FUNC_MAXIMIZE
|
1400 OB_CLIENT_FUNC_SHADE
|
1401 OB_CLIENT_FUNC_CLOSE
);
1403 if (!(self
->min_size
.width
< self
->max_size
.width
||
1404 self
->min_size
.height
< self
->max_size
.height
))
1405 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1407 switch (self
->type
) {
1408 case OB_CLIENT_TYPE_NORMAL
:
1409 /* normal windows retain all of the possible decorations and
1410 functionality, and are the only windows that you can fullscreen */
1411 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1414 case OB_CLIENT_TYPE_DIALOG
:
1415 case OB_CLIENT_TYPE_UTILITY
:
1416 /* these windows cannot be maximized */
1417 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1420 case OB_CLIENT_TYPE_MENU
:
1421 case OB_CLIENT_TYPE_TOOLBAR
:
1422 /* these windows get less functionality */
1423 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1426 case OB_CLIENT_TYPE_DESKTOP
:
1427 case OB_CLIENT_TYPE_DOCK
:
1428 case OB_CLIENT_TYPE_SPLASH
:
1429 /* none of these windows are manipulated by the window manager */
1430 self
->decorations
= 0;
1431 self
->functions
= 0;
1435 /* Mwm Hints are applied subtractively to what has already been chosen for
1436 decor and functionality */
1437 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1438 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1439 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1440 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1442 /* if the mwm hints request no handle or title, then all
1443 decorations are disabled, but keep the border if that's
1445 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1446 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1448 self
->decorations
= 0;
1453 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1454 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1455 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1456 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1457 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1458 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1459 /* dont let mwm hints kill any buttons
1460 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1461 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1462 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1463 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1465 /* dont let mwm hints kill the close button
1466 if (! (self->mwmhints.functions & MwmFunc_Close))
1467 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1471 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1472 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1473 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1474 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1475 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1476 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1478 /* can't maximize without moving/resizing */
1479 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1480 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1481 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1482 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1483 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1486 /* kill the handle on fully maxed windows */
1487 if (self
->max_vert
&& self
->max_horz
)
1488 self
->decorations
&= ~OB_FRAME_DECOR_HANDLE
;
1490 /* finally, the user can have requested no decorations, which overrides
1491 everything (but doesnt give it a border if it doesnt have one) */
1492 if (self
->undecorated
) {
1493 if (config_theme_keepborder
)
1494 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1496 self
->decorations
= 0;
1499 /* if we don't have a titlebar, then we cannot shade! */
1500 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1501 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1503 /* now we need to check against rules for the client's current state */
1504 if (self
->fullscreen
) {
1505 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1506 OB_CLIENT_FUNC_FULLSCREEN
|
1507 OB_CLIENT_FUNC_ICONIFY
);
1508 self
->decorations
= 0;
1511 client_change_allowed_actions(self
);
1514 /* adjust the client's decorations, etc. */
1515 client_reconfigure(self
);
1519 static void client_change_allowed_actions(ObClient
*self
)
1524 /* desktop windows are kept on all desktops */
1525 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1526 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1528 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1529 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1530 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1531 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1532 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1533 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1534 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1535 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1536 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1537 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1538 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1539 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1540 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1541 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1542 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1545 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1547 /* make sure the window isn't breaking any rules now */
1549 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1550 if (self
->frame
) client_shade(self
, FALSE
);
1551 else self
->shaded
= FALSE
;
1553 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1554 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1555 else self
->iconic
= FALSE
;
1557 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1558 if (self
->frame
) client_fullscreen(self
, FALSE
);
1559 else self
->fullscreen
= FALSE
;
1561 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1563 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1564 else self
->max_vert
= self
->max_horz
= FALSE
;
1568 void client_reconfigure(ObClient
*self
)
1570 /* by making this pass FALSE for user, we avoid the emacs event storm where
1571 every configurenotify causes an update in its normal hints, i think this
1572 is generally what we want anyways... */
1573 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1574 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1577 void client_update_wmhints(ObClient
*self
)
1582 /* assume a window takes input if it doesnt specify */
1583 self
->can_focus
= TRUE
;
1585 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1586 if (hints
->flags
& InputHint
)
1587 self
->can_focus
= hints
->input
;
1589 /* only do this when first managing the window *AND* when we aren't
1591 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1592 if (hints
->flags
& StateHint
)
1593 self
->iconic
= hints
->initial_state
== IconicState
;
1595 if (!(hints
->flags
& WindowGroupHint
))
1596 hints
->window_group
= None
;
1598 /* did the group state change? */
1599 if (hints
->window_group
!=
1600 (self
->group
? self
->group
->leader
: None
)) {
1601 /* remove from the old group if there was one */
1602 if (self
->group
!= NULL
) {
1603 /* remove transients of the group */
1604 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1605 self
->transients
= g_slist_remove(self
->transients
,
1608 /* remove myself from parents in the group */
1609 if (self
->transient_for
== OB_TRAN_GROUP
) {
1610 for (it
= self
->group
->members
; it
;
1611 it
= g_slist_next(it
))
1613 ObClient
*c
= it
->data
;
1615 if (c
!= self
&& !c
->transient_for
)
1616 c
->transients
= g_slist_remove(c
->transients
,
1621 group_remove(self
->group
, self
);
1624 if (hints
->window_group
!= None
) {
1625 self
->group
= group_add(hints
->window_group
, self
);
1627 /* i can only have transients from the group if i am not
1629 if (!self
->transient_for
) {
1630 /* add other transients of the group that are already
1632 for (it
= self
->group
->members
; it
;
1633 it
= g_slist_next(it
))
1635 ObClient
*c
= it
->data
;
1636 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1638 g_slist_append(self
->transients
, c
);
1643 /* because the self->transient flag wont change from this call,
1644 we don't need to update the window's type and such, only its
1645 transient_for, and the transients lists of other windows in
1646 the group may be affected */
1647 client_update_transient_for(self
);
1650 /* the WM_HINTS can contain an icon */
1651 client_update_icons(self
);
1657 void client_update_title(ObClient
*self
)
1660 gchar
*visible
= NULL
;
1662 g_free(self
->title
);
1665 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1666 /* try old x stuff */
1667 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1668 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1669 if (self
->transient
) {
1671 GNOME alert windows are not given titles:
1672 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1674 data
= g_strdup("");
1676 data
= g_strdup("Unnamed Window");
1680 if (self
->client_machine
) {
1681 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1686 PROP_SETS(self
->window
, net_wm_visible_name
, visible
);
1687 self
->title
= visible
;
1690 frame_adjust_title(self
->frame
);
1692 /* update the icon title */
1694 g_free(self
->icon_title
);
1697 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1698 /* try old x stuff */
1699 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1700 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1701 data
= g_strdup(self
->title
);
1703 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1704 self
->icon_title
= data
;
1707 void client_update_class(ObClient
*self
)
1712 if (self
->name
) g_free(self
->name
);
1713 if (self
->class) g_free(self
->class);
1714 if (self
->role
) g_free(self
->role
);
1716 self
->name
= self
->class = self
->role
= NULL
;
1718 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1720 self
->name
= g_strdup(data
[0]);
1722 self
->class = g_strdup(data
[1]);
1727 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1730 if (self
->name
== NULL
) self
->name
= g_strdup("");
1731 if (self
->class == NULL
) self
->class = g_strdup("");
1732 if (self
->role
== NULL
) self
->role
= g_strdup("");
1735 void client_update_strut(ObClient
*self
)
1739 gboolean got
= FALSE
;
1742 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1746 STRUT_PARTIAL_SET(strut
,
1747 data
[0], data
[2], data
[1], data
[3],
1748 data
[4], data
[5], data
[8], data
[9],
1749 data
[6], data
[7], data
[10], data
[11]);
1755 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1761 /* use the screen's width/height */
1762 a
= screen_physical_area();
1764 STRUT_PARTIAL_SET(strut
,
1765 data
[0], data
[2], data
[1], data
[3],
1766 a
->y
, a
->y
+ a
->height
- 1,
1767 a
->x
, a
->x
+ a
->width
- 1,
1768 a
->y
, a
->y
+ a
->height
- 1,
1769 a
->x
, a
->x
+ a
->width
- 1);
1775 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1776 0, 0, 0, 0, 0, 0, 0, 0);
1778 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1779 self
->strut
= strut
;
1781 /* updating here is pointless while we're being mapped cuz we're not in
1782 the client list yet */
1784 screen_update_areas();
1788 void client_update_icons(ObClient
*self
)
1794 for (i
= 0; i
< self
->nicons
; ++i
)
1795 g_free(self
->icons
[i
].data
);
1796 if (self
->nicons
> 0)
1797 g_free(self
->icons
);
1800 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1801 /* figure out how many valid icons are in here */
1803 while (num
- i
> 2) {
1807 if (i
> num
|| w
*h
== 0) break;
1811 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1813 /* store the icons */
1815 for (j
= 0; j
< self
->nicons
; ++j
) {
1818 w
= self
->icons
[j
].width
= data
[i
++];
1819 h
= self
->icons
[j
].height
= data
[i
++];
1821 if (w
*h
== 0) continue;
1823 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1824 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1829 self
->icons
[j
].data
[t
] =
1830 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1831 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1832 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1833 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1842 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1843 if (hints
->flags
& IconPixmapHint
) {
1845 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1846 xerror_set_ignore(TRUE
);
1847 if (!RrPixmapToRGBA(ob_rr_inst
,
1849 (hints
->flags
& IconMaskHint
?
1850 hints
->icon_mask
: None
),
1851 &self
->icons
[self
->nicons
-1].width
,
1852 &self
->icons
[self
->nicons
-1].height
,
1853 &self
->icons
[self
->nicons
-1].data
)){
1854 g_free(&self
->icons
[self
->nicons
-1]);
1857 xerror_set_ignore(FALSE
);
1863 /* set the default icon onto the window
1864 in theory, this could be a race, but if a window doesn't set an icon
1865 or removes it entirely, it's not very likely it is going to set one
1866 right away afterwards */
1867 if (self
->nicons
== 0) {
1868 RrPixel32
*icon
= ob_rr_theme
->def_win_icon
;
1871 data
= g_new(gulong
, 48*48+2);
1872 data
[0] = data
[1] = 48;
1873 for (i
= 0; i
< 48*48; ++i
)
1874 data
[i
+2] = (((icon
[i
] >> RrDefaultAlphaOffset
) & 0xff) << 24) +
1875 (((icon
[i
] >> RrDefaultRedOffset
) & 0xff) << 16) +
1876 (((icon
[i
] >> RrDefaultGreenOffset
) & 0xff) << 8) +
1877 (((icon
[i
] >> RrDefaultBlueOffset
) & 0xff) << 0);
1878 PROP_SETA32(self
->window
, net_wm_icon
, cardinal
, data
, 48*48+2);
1880 } else if (self
->frame
)
1881 /* don't draw the icon empty if we're just setting one now anyways,
1882 we'll get the property change any second */
1883 frame_adjust_icon(self
->frame
);
1886 void client_update_user_time(ObClient
*self
)
1890 if (PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
)) {
1891 /* we set this every time, not just when it grows, because in practice
1892 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1893 backward we don't want all windows to stop focusing. we'll just
1894 assume noone is setting times older than the last one, cuz that
1895 would be pretty stupid anyways
1897 self
->user_time
= time
;
1900 ob_debug("window %s user time %u\n", self->title, time);
1905 static void client_get_client_machine(ObClient
*self
)
1908 gchar localhost
[128];
1910 g_free(self
->client_machine
);
1912 if (PROP_GETS(self
->window
, wm_client_machine
, locale
, &data
)) {
1913 gethostname(localhost
, 127);
1914 localhost
[127] = '\0';
1915 if (strcmp(localhost
, data
))
1916 self
->client_machine
= data
;
1920 static void client_change_wm_state(ObClient
*self
)
1925 old
= self
->wmstate
;
1927 if (self
->shaded
|| self
->iconic
|| !self
->frame
->visible
)
1928 self
->wmstate
= IconicState
;
1930 self
->wmstate
= NormalState
;
1932 if (old
!= self
->wmstate
) {
1933 PROP_MSG(self
->window
, kde_wm_change_state
,
1934 self
->wmstate
, 1, 0, 0);
1936 state
[0] = self
->wmstate
;
1938 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1942 static void client_change_state(ObClient
*self
)
1944 gulong netstate
[11];
1949 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1951 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1953 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1954 if (self
->skip_taskbar
)
1955 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1956 if (self
->skip_pager
)
1957 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1958 if (self
->fullscreen
)
1959 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1961 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1963 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1965 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1967 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1968 if (self
->demands_attention
)
1969 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
1970 if (self
->undecorated
)
1971 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
1972 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1975 frame_adjust_state(self
->frame
);
1978 ObClient
*client_search_focus_tree(ObClient
*self
)
1983 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
1984 if (client_focused(it
->data
)) return it
->data
;
1985 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1990 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1992 if (self
->transient_for
) {
1993 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1994 return client_search_focus_tree_full(self
->transient_for
);
1997 gboolean recursed
= FALSE
;
1999 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
2000 if (!((ObClient
*)it
->data
)->transient_for
) {
2002 if ((c
= client_search_focus_tree_full(it
->data
)))
2011 /* this function checks the whole tree, the client_search_focus_tree~
2012 does not, so we need to check this window */
2013 if (client_focused(self
))
2015 return client_search_focus_tree(self
);
2018 static ObStackingLayer
calc_layer(ObClient
*self
)
2022 if (self
->fullscreen
&&
2023 (client_focused(self
) || client_search_focus_tree(self
)))
2024 l
= OB_STACKING_LAYER_FULLSCREEN
;
2025 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2026 l
= OB_STACKING_LAYER_DESKTOP
;
2027 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2028 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2029 else l
= OB_STACKING_LAYER_ABOVE
;
2031 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2032 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2033 else l
= OB_STACKING_LAYER_NORMAL
;
2038 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2039 ObStackingLayer min
, gboolean raised
)
2041 ObStackingLayer old
, own
;
2045 own
= calc_layer(self
);
2046 self
->layer
= MAX(own
, min
);
2048 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2049 client_calc_layer_recursive(it
->data
, orig
,
2051 raised
? raised
: self
->layer
!= old
);
2053 if (!raised
&& self
->layer
!= old
)
2054 if (orig
->frame
) { /* only restack if the original window is managed */
2055 stacking_remove(CLIENT_AS_WINDOW(self
));
2056 stacking_add(CLIENT_AS_WINDOW(self
));
2060 void client_calc_layer(ObClient
*self
)
2067 /* transients take on the layer of their parents */
2068 it
= client_search_all_top_parents(self
);
2070 for (; it
; it
= g_slist_next(it
))
2071 client_calc_layer_recursive(it
->data
, orig
, 0, FALSE
);
2074 gboolean
client_should_show(ObClient
*self
)
2078 if (client_normal(self
) && screen_showing_desktop
)
2081 if (self->transient_for) {
2082 if (self->transient_for != OB_TRAN_GROUP)
2083 return client_should_show(self->transient_for);
2087 for (it = self->group->members; it; it = g_slist_next(it)) {
2088 ObClient *c = it->data;
2089 if (c != self && !c->transient_for) {
2090 if (client_should_show(c))
2097 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2103 void client_show(ObClient
*self
)
2106 if (client_should_show(self
)) {
2107 frame_show(self
->frame
);
2110 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2111 needs to be in IconicState. This includes when it is on another
2114 client_change_wm_state(self
);
2117 void client_hide(ObClient
*self
)
2119 if (!client_should_show(self
)) {
2120 frame_hide(self
->frame
);
2123 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2124 needs to be in IconicState. This includes when it is on another
2127 client_change_wm_state(self
);
2130 void client_showhide(ObClient
*self
)
2133 if (client_should_show(self
)) {
2134 frame_show(self
->frame
);
2137 frame_hide(self
->frame
);
2140 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2141 needs to be in IconicState. This includes when it is on another
2144 client_change_wm_state(self
);
2147 gboolean
client_normal(ObClient
*self
) {
2148 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2149 self
->type
== OB_CLIENT_TYPE_DOCK
||
2150 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2153 static void client_apply_startup_state(ObClient
*self
, gint x
, gint y
)
2155 gboolean pos
= FALSE
; /* has the window's position been configured? */
2158 /* save the position, and set self->area for these to use */
2164 /* set the desktop hint, to make sure that it always exists */
2165 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
2167 /* these are in a carefully crafted order.. */
2170 self
->iconic
= FALSE
;
2171 client_iconify(self
, TRUE
, FALSE
);
2173 if (self
->fullscreen
) {
2174 self
->fullscreen
= FALSE
;
2175 client_fullscreen(self
, TRUE
);
2178 if (self
->undecorated
) {
2179 self
->undecorated
= FALSE
;
2180 client_set_undecorated(self
, TRUE
);
2183 self
->shaded
= FALSE
;
2184 client_shade(self
, TRUE
);
2186 if (self
->demands_attention
) {
2187 self
->demands_attention
= FALSE
;
2188 client_hilite(self
, TRUE
);
2191 if (self
->max_vert
&& self
->max_horz
) {
2192 self
->max_vert
= self
->max_horz
= FALSE
;
2193 client_maximize(self
, TRUE
, 0);
2195 } else if (self
->max_vert
) {
2196 self
->max_vert
= FALSE
;
2197 client_maximize(self
, TRUE
, 2);
2199 } else if (self
->max_horz
) {
2200 self
->max_horz
= FALSE
;
2201 client_maximize(self
, TRUE
, 1);
2205 /* if the client didn't get positioned yet, then do so now
2206 call client_move even if the window is not being moved anywhere, because
2207 when we reparent it and decorate it, it is getting moved and we need to
2208 be telling it so with a ConfigureNotify event.
2211 /* use the saved position */
2214 client_move(self
, x
, y
);
2217 /* nothing to do for the other states:
2226 void client_try_configure(ObClient
*self
, ObCorner anchor
,
2227 gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2228 gint
*logicalw
, gint
*logicalh
,
2231 Rect desired_area
= {*x
, *y
, *w
, *h
};
2233 /* make the frame recalculate its dimentions n shit without changing
2234 anything visible for real, this way the constraints below can work with
2235 the updated frame dimensions. */
2236 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2238 /* work within the prefered sizes given by the window */
2239 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2240 gint basew
, baseh
, minw
, minh
;
2242 /* base size is substituted with min size if not specified */
2243 if (self
->base_size
.width
|| self
->base_size
.height
) {
2244 basew
= self
->base_size
.width
;
2245 baseh
= self
->base_size
.height
;
2247 basew
= self
->min_size
.width
;
2248 baseh
= self
->min_size
.height
;
2250 /* min size is substituted with base size if not specified */
2251 if (self
->min_size
.width
|| self
->min_size
.height
) {
2252 minw
= self
->min_size
.width
;
2253 minh
= self
->min_size
.height
;
2255 minw
= self
->base_size
.width
;
2256 minh
= self
->base_size
.height
;
2259 /* if this is a user-requested resize, then check against min/max
2262 /* smaller than min size or bigger than max size? */
2263 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2264 if (*w
< minw
) *w
= minw
;
2265 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2266 if (*h
< minh
) *h
= minh
;
2271 /* keep to the increments */
2272 *w
/= self
->size_inc
.width
;
2273 *h
/= self
->size_inc
.height
;
2275 /* you cannot resize to nothing */
2276 if (basew
+ *w
< 1) *w
= 1 - basew
;
2277 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2279 /* save the logical size */
2280 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2281 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2283 *w
*= self
->size_inc
.width
;
2284 *h
*= self
->size_inc
.height
;
2289 /* adjust the height to match the width for the aspect ratios.
2290 for this, min size is not substituted for base size ever. */
2291 *w
-= self
->base_size
.width
;
2292 *h
-= self
->base_size
.height
;
2294 if (!self
->fullscreen
) {
2295 if (self
->min_ratio
)
2296 if (*h
* self
->min_ratio
> *w
) {
2297 *h
= (gint
)(*w
/ self
->min_ratio
);
2299 /* you cannot resize to nothing */
2302 *w
= (gint
)(*h
* self
->min_ratio
);
2305 if (self
->max_ratio
)
2306 if (*h
* self
->max_ratio
< *w
) {
2307 *h
= (gint
)(*w
/ self
->max_ratio
);
2309 /* you cannot resize to nothing */
2312 *w
= (gint
)(*h
* self
->min_ratio
);
2317 *w
+= self
->base_size
.width
;
2318 *h
+= self
->base_size
.height
;
2321 /* gets the frame's position */
2322 frame_client_gravity(self
->frame
, x
, y
);
2324 /* these positions are frame positions, not client positions */
2326 /* set the size and position if fullscreen */
2327 if (self
->fullscreen
) {
2331 i
= screen_find_monitor(&desired_area
);
2332 a
= screen_physical_area_monitor(i
);
2339 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2340 is entering fullscreen */
2341 } else if (self
->max_horz
|| self
->max_vert
) {
2345 i
= screen_find_monitor(&desired_area
);
2346 a
= screen_area_monitor(self
->desktop
, i
);
2348 /* set the size and position if maximized */
2349 if (self
->max_horz
) {
2351 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2353 if (self
->max_vert
) {
2355 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2358 /* maximizing is not allowed if the user can't move+resize the window
2362 /* gets the client's position */
2363 frame_frame_gravity(self
->frame
, x
, y
);
2365 /* these override the above states! if you cant move you can't move! */
2367 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2371 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2372 *w
= self
->area
.width
;
2373 *h
= self
->area
.height
;
2381 case OB_CORNER_TOPLEFT
:
2383 case OB_CORNER_TOPRIGHT
:
2384 *x
-= *w
- self
->area
.width
;
2386 case OB_CORNER_BOTTOMLEFT
:
2387 *y
-= *h
- self
->area
.height
;
2389 case OB_CORNER_BOTTOMRIGHT
:
2390 *x
-= *w
- self
->area
.width
;
2391 *y
-= *h
- self
->area
.height
;
2397 void client_configure_full(ObClient
*self
, ObCorner anchor
,
2398 gint x
, gint y
, gint w
, gint h
,
2399 gboolean user
, gboolean final
,
2400 gboolean force_reply
)
2402 gint oldw
, oldh
, oldrx
, oldry
;
2403 gboolean send_resize_client
;
2404 gboolean moved
= FALSE
, resized
= FALSE
, rootmoved
= FALSE
;
2405 guint fdecor
= self
->frame
->decorations
;
2406 gboolean fhorz
= self
->frame
->max_horz
;
2407 gint logicalw
, logicalh
;
2409 /* find the new x, y, width, and height (and logical size) */
2410 client_try_configure(self
, anchor
, &x
, &y
, &w
, &h
,
2411 &logicalw
, &logicalh
, user
);
2413 /* set the logical size if things changed */
2414 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2415 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2417 /* figure out if we moved or resized or what */
2418 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2419 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2421 oldw
= self
->area
.width
;
2422 oldh
= self
->area
.height
;
2423 RECT_SET(self
->area
, x
, y
, w
, h
);
2425 /* for app-requested resizes, always resize if 'resized' is true.
2426 for user-requested ones, only resize if final is true, or when
2427 resizing in redraw mode */
2428 send_resize_client
= ((!user
&& resized
) ||
2430 (resized
&& config_resize_redraw
))));
2432 /* if the client is enlarging, then resize the client before the frame */
2433 if (send_resize_client
&& user
&& (w
> oldw
|| h
> oldh
)) {
2434 XResizeWindow(ob_display
, self
->window
, MAX(w
, oldw
), MAX(h
, oldh
));
2435 frame_adjust_client_area(self
->frame
);
2438 /* find the frame's dimensions and move/resize it */
2439 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2440 moved
= resized
= TRUE
;
2441 if (moved
|| resized
)
2442 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
2444 /* find the client's position relative to the root window */
2445 oldrx
= self
->root_pos
.x
;
2446 oldry
= self
->root_pos
.y
;
2447 rootmoved
= (oldrx
!= (signed)(self
->frame
->area
.x
+
2448 self
->frame
->size
.left
-
2449 self
->border_width
) ||
2450 oldry
!= (signed)(self
->frame
->area
.y
+
2451 self
->frame
->size
.top
-
2452 self
->border_width
));
2454 if (force_reply
|| ((!user
|| (user
&& final
)) && rootmoved
))
2458 POINT_SET(self
->root_pos
,
2459 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2461 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2462 self
->border_width
);
2464 event
.type
= ConfigureNotify
;
2465 event
.xconfigure
.display
= ob_display
;
2466 event
.xconfigure
.event
= self
->window
;
2467 event
.xconfigure
.window
= self
->window
;
2469 /* root window real coords */
2470 event
.xconfigure
.x
= self
->root_pos
.x
;
2471 event
.xconfigure
.y
= self
->root_pos
.y
;
2472 event
.xconfigure
.width
= w
;
2473 event
.xconfigure
.height
= h
;
2474 event
.xconfigure
.border_width
= 0;
2475 event
.xconfigure
.above
= self
->frame
->plate
;
2476 event
.xconfigure
.override_redirect
= FALSE
;
2477 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2478 FALSE
, StructureNotifyMask
, &event
);
2481 /* if the client is shrinking, then resize the frame before the client */
2482 if (send_resize_client
&& (!user
|| (w
<= oldw
|| h
<= oldh
))) {
2483 frame_adjust_client_area(self
->frame
);
2484 XResizeWindow(ob_display
, self
->window
, w
, h
);
2490 void client_fullscreen(ObClient
*self
, gboolean fs
)
2494 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2495 self
->fullscreen
== fs
) return; /* already done */
2497 self
->fullscreen
= fs
;
2498 client_change_state(self
); /* change the state hints on the client */
2499 client_calc_layer(self
); /* and adjust out layer/stacking */
2502 self
->pre_fullscreen_area
= self
->area
;
2503 /* if the window is maximized, its area isn't all that meaningful.
2504 save it's premax area instead. */
2505 if (self
->max_horz
) {
2506 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2507 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2509 if (self
->max_vert
) {
2510 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2511 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2514 /* these are not actually used cuz client_configure will set them
2515 as appropriate when the window is fullscreened */
2520 if (self
->pre_fullscreen_area
.width
> 0 &&
2521 self
->pre_fullscreen_area
.height
> 0)
2523 x
= self
->pre_fullscreen_area
.x
;
2524 y
= self
->pre_fullscreen_area
.y
;
2525 w
= self
->pre_fullscreen_area
.width
;
2526 h
= self
->pre_fullscreen_area
.height
;
2527 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2529 /* pick some fallbacks... */
2530 a
= screen_area_monitor(self
->desktop
, 0);
2531 x
= a
->x
+ a
->width
/ 4;
2532 y
= a
->y
+ a
->height
/ 4;
2538 client_setup_decor_and_functions(self
);
2540 client_move_resize(self
, x
, y
, w
, h
);
2542 /* try focus us when we go into fullscreen mode */
2546 static void client_iconify_recursive(ObClient
*self
,
2547 gboolean iconic
, gboolean curdesk
)
2550 gboolean changed
= FALSE
;
2553 if (self
->iconic
!= iconic
) {
2554 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2558 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2559 self
->iconic
= iconic
;
2561 /* update the focus lists.. iconic windows go to the bottom of
2562 the list, put the new iconic window at the 'top of the
2564 focus_order_to_top(self
);
2569 self
->iconic
= iconic
;
2572 client_set_desktop(self
, screen_desktop
, FALSE
);
2574 /* this puts it after the current focused window */
2575 focus_order_remove(self
);
2576 focus_order_add_new(self
);
2583 client_change_state(self
);
2584 client_showhide(self
);
2585 if (STRUT_EXISTS(self
->strut
))
2586 screen_update_areas();
2589 /* iconify all direct transients */
2590 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2591 if (it
->data
!= self
)
2592 if (client_is_direct_child(self
, it
->data
))
2593 client_iconify_recursive(it
->data
, iconic
, curdesk
);
2596 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2598 /* move up the transient chain as far as possible first */
2599 self
= client_search_top_parent(self
);
2600 client_iconify_recursive(self
, iconic
, curdesk
);
2603 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2607 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2608 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2610 /* check if already done */
2612 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2613 if (dir
== 1 && self
->max_horz
) return;
2614 if (dir
== 2 && self
->max_vert
) return;
2616 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2617 if (dir
== 1 && !self
->max_horz
) return;
2618 if (dir
== 2 && !self
->max_vert
) return;
2621 /* we just tell it to configure in the same place and client_configure
2622 worries about filling the screen with the window */
2625 w
= self
->area
.width
;
2626 h
= self
->area
.height
;
2629 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2630 RECT_SET(self
->pre_max_area
,
2631 self
->area
.x
, self
->pre_max_area
.y
,
2632 self
->area
.width
, self
->pre_max_area
.height
);
2634 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2635 RECT_SET(self
->pre_max_area
,
2636 self
->pre_max_area
.x
, self
->area
.y
,
2637 self
->pre_max_area
.width
, self
->area
.height
);
2642 a
= screen_area_monitor(self
->desktop
, 0);
2643 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2644 if (self
->pre_max_area
.width
> 0) {
2645 x
= self
->pre_max_area
.x
;
2646 w
= self
->pre_max_area
.width
;
2648 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2649 0, self
->pre_max_area
.height
);
2651 /* pick some fallbacks... */
2652 x
= a
->x
+ a
->width
/ 4;
2656 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2657 if (self
->pre_max_area
.height
> 0) {
2658 y
= self
->pre_max_area
.y
;
2659 h
= self
->pre_max_area
.height
;
2661 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2662 self
->pre_max_area
.width
, 0);
2664 /* pick some fallbacks... */
2665 y
= a
->y
+ a
->height
/ 4;
2671 if (dir
== 0 || dir
== 1) /* horz */
2672 self
->max_horz
= max
;
2673 if (dir
== 0 || dir
== 2) /* vert */
2674 self
->max_vert
= max
;
2676 client_change_state(self
); /* change the state hints on the client */
2678 client_setup_decor_and_functions(self
);
2680 client_move_resize(self
, x
, y
, w
, h
);
2683 void client_shade(ObClient
*self
, gboolean shade
)
2685 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2686 shade
) || /* can't shade */
2687 self
->shaded
== shade
) return; /* already done */
2689 self
->shaded
= shade
;
2690 client_change_state(self
);
2691 client_change_wm_state(self
); /* the window is being hidden/shown */
2692 /* resize the frame to just the titlebar */
2693 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2696 void client_close(ObClient
*self
)
2700 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2702 /* in the case that the client provides no means to requesting that it
2703 close, we just kill it */
2704 if (!self
->delete_window
)
2708 XXX: itd be cool to do timeouts and shit here for killing the client's
2710 like... if the window is around after 5 seconds, then the close button
2711 turns a nice red, and if this function is called again, the client is
2715 ce
.xclient
.type
= ClientMessage
;
2716 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2717 ce
.xclient
.display
= ob_display
;
2718 ce
.xclient
.window
= self
->window
;
2719 ce
.xclient
.format
= 32;
2720 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2721 ce
.xclient
.data
.l
[1] = event_curtime
;
2722 ce
.xclient
.data
.l
[2] = 0l;
2723 ce
.xclient
.data
.l
[3] = 0l;
2724 ce
.xclient
.data
.l
[4] = 0l;
2725 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2728 void client_kill(ObClient
*self
)
2730 XKillClient(ob_display
, self
->window
);
2733 void client_hilite(ObClient
*self
, gboolean hilite
)
2735 if (self
->demands_attention
== hilite
)
2736 return; /* no change */
2738 /* don't allow focused windows to hilite */
2739 self
->demands_attention
= hilite
&& !client_focused(self
);
2740 if (self
->demands_attention
)
2741 frame_flash_start(self
->frame
);
2743 frame_flash_stop(self
->frame
);
2744 client_change_state(self
);
2747 void client_set_desktop_recursive(ObClient
*self
,
2748 guint target
, gboolean donthide
)
2753 if (target
!= self
->desktop
) {
2755 ob_debug("Setting desktop %u\n", target
+1);
2757 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2759 /* remove from the old desktop(s) */
2760 focus_order_remove(self
);
2762 old
= self
->desktop
;
2763 self
->desktop
= target
;
2764 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2765 /* the frame can display the current desktop state */
2766 frame_adjust_state(self
->frame
);
2767 /* 'move' the window to the new desktop */
2769 client_showhide(self
);
2770 /* raise if it was not already on the desktop */
2771 if (old
!= DESKTOP_ALL
)
2773 if (STRUT_EXISTS(self
->strut
))
2774 screen_update_areas();
2776 /* add to the new desktop(s) */
2777 if (config_focus_new
)
2778 focus_order_to_top(self
);
2780 focus_order_to_bottom(self
);
2783 /* move all transients */
2784 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2785 if (it
->data
!= self
)
2786 if (client_is_direct_child(self
, it
->data
))
2787 client_set_desktop_recursive(it
->data
, target
, donthide
);
2790 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2792 self
= client_search_top_parent(self
);
2793 client_set_desktop_recursive(self
, target
, donthide
);
2796 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
2798 while (child
!= parent
&&
2799 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
2800 child
= child
->transient_for
;
2801 return child
== parent
;
2804 ObClient
*client_search_modal_child(ObClient
*self
)
2809 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2810 ObClient
*c
= it
->data
;
2811 if ((ret
= client_search_modal_child(c
))) return ret
;
2812 if (c
->modal
) return c
;
2817 gboolean
client_validate(ObClient
*self
)
2821 XSync(ob_display
, FALSE
); /* get all events on the server */
2823 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2824 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2825 XPutBackEvent(ob_display
, &e
);
2832 void client_set_wm_state(ObClient
*self
, glong state
)
2834 if (state
== self
->wmstate
) return; /* no change */
2838 client_iconify(self
, TRUE
, TRUE
);
2841 client_iconify(self
, FALSE
, TRUE
);
2846 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
2848 gboolean shaded
= self
->shaded
;
2849 gboolean fullscreen
= self
->fullscreen
;
2850 gboolean undecorated
= self
->undecorated
;
2851 gboolean max_horz
= self
->max_horz
;
2852 gboolean max_vert
= self
->max_vert
;
2853 gboolean modal
= self
->modal
;
2854 gboolean iconic
= self
->iconic
;
2855 gboolean demands_attention
= self
->demands_attention
;
2858 if (!(action
== prop_atoms
.net_wm_state_add
||
2859 action
== prop_atoms
.net_wm_state_remove
||
2860 action
== prop_atoms
.net_wm_state_toggle
))
2861 /* an invalid action was passed to the client message, ignore it */
2864 for (i
= 0; i
< 2; ++i
) {
2865 Atom state
= i
== 0 ? data1
: data2
;
2867 if (!state
) continue;
2869 /* if toggling, then pick whether we're adding or removing */
2870 if (action
== prop_atoms
.net_wm_state_toggle
) {
2871 if (state
== prop_atoms
.net_wm_state_modal
)
2872 action
= modal
? prop_atoms
.net_wm_state_remove
:
2873 prop_atoms
.net_wm_state_add
;
2874 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2875 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2876 prop_atoms
.net_wm_state_add
;
2877 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2878 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2879 prop_atoms
.net_wm_state_add
;
2880 else if (state
== prop_atoms
.net_wm_state_shaded
)
2881 action
= shaded
? prop_atoms
.net_wm_state_remove
:
2882 prop_atoms
.net_wm_state_add
;
2883 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2884 action
= self
->skip_taskbar
?
2885 prop_atoms
.net_wm_state_remove
:
2886 prop_atoms
.net_wm_state_add
;
2887 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2888 action
= self
->skip_pager
?
2889 prop_atoms
.net_wm_state_remove
:
2890 prop_atoms
.net_wm_state_add
;
2891 else if (state
== prop_atoms
.net_wm_state_hidden
)
2892 action
= self
->iconic
?
2893 prop_atoms
.net_wm_state_remove
:
2894 prop_atoms
.net_wm_state_add
;
2895 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2896 action
= fullscreen
?
2897 prop_atoms
.net_wm_state_remove
:
2898 prop_atoms
.net_wm_state_add
;
2899 else if (state
== prop_atoms
.net_wm_state_above
)
2900 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2901 prop_atoms
.net_wm_state_add
;
2902 else if (state
== prop_atoms
.net_wm_state_below
)
2903 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2904 prop_atoms
.net_wm_state_add
;
2905 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
2906 action
= self
->demands_attention
?
2907 prop_atoms
.net_wm_state_remove
:
2908 prop_atoms
.net_wm_state_add
;
2909 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
2910 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
2911 prop_atoms
.net_wm_state_add
;
2914 if (action
== prop_atoms
.net_wm_state_add
) {
2915 if (state
== prop_atoms
.net_wm_state_modal
) {
2917 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2919 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2921 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2923 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2924 self
->skip_taskbar
= TRUE
;
2925 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2926 self
->skip_pager
= TRUE
;
2927 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2929 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2931 } else if (state
== prop_atoms
.net_wm_state_above
) {
2933 self
->below
= FALSE
;
2934 } else if (state
== prop_atoms
.net_wm_state_below
) {
2935 self
->above
= FALSE
;
2937 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2938 demands_attention
= TRUE
;
2939 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2943 } else { /* action == prop_atoms.net_wm_state_remove */
2944 if (state
== prop_atoms
.net_wm_state_modal
) {
2946 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2948 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2950 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2952 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2953 self
->skip_taskbar
= FALSE
;
2954 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2955 self
->skip_pager
= FALSE
;
2956 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
2958 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2960 } else if (state
== prop_atoms
.net_wm_state_above
) {
2961 self
->above
= FALSE
;
2962 } else if (state
== prop_atoms
.net_wm_state_below
) {
2963 self
->below
= FALSE
;
2964 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
2965 demands_attention
= FALSE
;
2966 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
2967 undecorated
= FALSE
;
2971 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2972 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2974 if (max_horz
== max_vert
) { /* both going the same way */
2975 client_maximize(self
, max_horz
, 0);
2977 client_maximize(self
, max_horz
, 1);
2978 client_maximize(self
, max_vert
, 2);
2982 if (max_horz
!= self
->max_horz
)
2983 client_maximize(self
, max_horz
, 1);
2985 client_maximize(self
, max_vert
, 2);
2988 /* change fullscreen state before shading, as it will affect if the window
2990 if (fullscreen
!= self
->fullscreen
)
2991 client_fullscreen(self
, fullscreen
);
2992 if (shaded
!= self
->shaded
)
2993 client_shade(self
, shaded
);
2994 if (undecorated
!= self
->undecorated
)
2995 client_set_undecorated(self
, undecorated
);
2996 if (modal
!= self
->modal
) {
2997 self
->modal
= modal
;
2998 /* when a window changes modality, then its stacking order with its
2999 transients needs to change */
3002 if (iconic
!= self
->iconic
)
3003 client_iconify(self
, iconic
, FALSE
);
3005 if (demands_attention
!= self
->demands_attention
)
3006 client_hilite(self
, demands_attention
);
3008 client_change_state(self
); /* change the hint to reflect these changes */
3011 ObClient
*client_focus_target(ObClient
*self
)
3013 ObClient
*child
= NULL
;
3015 child
= client_search_modal_child(self
);
3016 if (child
) return child
;
3020 gboolean
client_can_focus(ObClient
*self
)
3024 /* choose the correct target */
3025 self
= client_focus_target(self
);
3027 if (!self
->frame
->visible
)
3030 if (!(self
->can_focus
|| self
->focus_notify
))
3033 /* do a check to see if the window has already been unmapped or destroyed
3034 do this intelligently while watching out for unmaps we've generated
3035 (ignore_unmaps > 0) */
3036 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
3037 DestroyNotify
, &ev
)) {
3038 XPutBackEvent(ob_display
, &ev
);
3041 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
3042 UnmapNotify
, &ev
)) {
3043 if (self
->ignore_unmaps
) {
3044 self
->ignore_unmaps
--;
3046 XPutBackEvent(ob_display
, &ev
);
3054 gboolean
client_focus(ObClient
*self
)
3056 /* choose the correct target */
3057 self
= client_focus_target(self
);
3059 if (!client_can_focus(self
)) {
3060 if (!self
->frame
->visible
) {
3061 /* update the focus lists */
3062 focus_order_to_top(self
);
3067 ob_debug_type(OB_DEBUG_FOCUS
,
3068 "Focusing client \"%s\" at time %u\n",
3069 self
->title
, event_curtime
);
3071 if (self
->can_focus
) {
3072 /* This can cause a BadMatch error with CurrentTime, or if an app
3073 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3074 xerror_set_ignore(TRUE
);
3075 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3077 xerror_set_ignore(FALSE
);
3080 if (self
->focus_notify
) {
3082 ce
.xclient
.type
= ClientMessage
;
3083 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3084 ce
.xclient
.display
= ob_display
;
3085 ce
.xclient
.window
= self
->window
;
3086 ce
.xclient
.format
= 32;
3087 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3088 ce
.xclient
.data
.l
[1] = event_curtime
;
3089 ce
.xclient
.data
.l
[2] = 0l;
3090 ce
.xclient
.data
.l
[3] = 0l;
3091 ce
.xclient
.data
.l
[4] = 0l;
3092 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3096 ob_debug("%sively focusing %lx at %d\n",
3097 (self
->can_focus
? "act" : "pass"),
3098 self
->window
, (gint
) event_curtime
);
3101 /* Cause the FocusIn to come back to us. Important for desktop switches,
3102 since otherwise we'll have no FocusIn on the queue and send it off to
3103 the focus_backup. */
3104 XSync(ob_display
, FALSE
);
3108 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3110 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3112 /* XXX do some stuff here if user is false to determine if we really want
3113 to activate it or not (a parent or group member is currently
3116 ob_debug_type(OB_DEBUG_FOCUS
,
3117 "Want to activate window 0x%x with time %u (last time %u), "
3119 self
->window
, event_curtime
, last_time
,
3120 (user
? "user" : "application"));
3122 if (!user
&& event_curtime
&& last_time
&&
3123 !event_time_after(event_curtime
, last_time
))
3125 client_hilite(self
, TRUE
);
3127 if (event_curtime
!= CurrentTime
)
3128 self
->user_time
= event_curtime
;
3130 /* if using focus_delay, stop the timer now so that focus doesn't
3132 event_halt_focus_delay();
3134 if (client_normal(self
) && screen_showing_desktop
)
3135 screen_show_desktop(FALSE
);
3137 client_iconify(self
, FALSE
, here
);
3138 if (self
->desktop
!= DESKTOP_ALL
&&
3139 self
->desktop
!= screen_desktop
) {
3141 client_set_desktop(self
, screen_desktop
, FALSE
);
3143 screen_set_desktop(self
->desktop
);
3144 } else if (!self
->frame
->visible
)
3145 /* if its not visible for other reasons, then don't mess
3149 client_shade(self
, FALSE
);
3153 /* we do this as an action here. this is rather important. this is
3154 because we want the results from the focus change to take place
3155 BEFORE we go about raising the window. when a fullscreen window
3156 loses focus, we need this or else the raise wont be able to raise
3157 above the to-lose-focus fullscreen window. */
3162 void client_raise(ObClient
*self
)
3164 action_run_string("Raise", self
, CurrentTime
);
3167 void client_lower(ObClient
*self
)
3169 action_run_string("Lower", self
, CurrentTime
);
3172 gboolean
client_focused(ObClient
*self
)
3174 return self
== focus_client
;
3177 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3180 /* si is the smallest image >= req */
3181 /* li is the largest image < req */
3182 gulong size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
3184 if (!self
->nicons
) {
3185 ObClientIcon
*parent
= NULL
;
3187 if (self
->transient_for
) {
3188 if (self
->transient_for
!= OB_TRAN_GROUP
)
3189 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3192 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3193 ObClient
*c
= it
->data
;
3194 if (c
!= self
&& !c
->transient_for
) {
3195 if ((parent
= client_icon_recursive(c
, w
, h
)))
3205 for (i
= 0; i
< self
->nicons
; ++i
) {
3206 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
3207 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
3211 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
3216 if (largest
== 0) /* didnt find one smaller than the requested size */
3217 return &self
->icons
[si
];
3218 return &self
->icons
[li
];
3221 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3224 static ObClientIcon deficon
;
3226 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3227 deficon
.width
= deficon
.height
= 48;
3228 deficon
.data
= ob_rr_theme
->def_win_icon
;
3234 void client_set_layer(ObClient
*self
, gint layer
)
3238 self
->above
= FALSE
;
3239 } else if (layer
== 0) {
3240 self
->below
= self
->above
= FALSE
;
3242 self
->below
= FALSE
;
3245 client_calc_layer(self
);
3246 client_change_state(self
); /* reflect this in the state hints */
3249 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3251 if (self
->undecorated
!= undecorated
) {
3252 self
->undecorated
= undecorated
;
3253 client_setup_decor_and_functions(self
);
3254 /* Make sure the client knows it might have moved. Maybe there is a
3255 * better way of doing this so only one client_configure is sent, but
3256 * since 125 of these are sent per second when moving the window (with
3257 * user = FALSE) i doubt it matters much.
3259 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
3260 self
->area
.width
, self
->area
.height
, TRUE
, TRUE
);
3261 client_change_state(self
); /* reflect this in the state hints */
3265 guint
client_monitor(ObClient
*self
)
3267 return screen_find_monitor(&self
->frame
->area
);
3270 ObClient
*client_search_top_parent(ObClient
*self
)
3272 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3273 client_normal(self
))
3274 self
= self
->transient_for
;
3278 GSList
*client_search_all_top_parents(ObClient
*self
)
3282 /* move up the direct transient chain as far as possible */
3283 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
)
3284 self
= self
->transient_for
;
3286 if (!self
->transient_for
)
3287 ret
= g_slist_prepend(ret
, self
);
3291 g_assert(self
->group
);
3293 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3294 ObClient
*c
= it
->data
;
3296 if (!c
->transient_for
&& client_normal(c
))
3297 ret
= g_slist_prepend(ret
, c
);
3300 if (ret
== NULL
) /* no group parents */
3301 ret
= g_slist_prepend(ret
, self
);
3307 ObClient
*client_search_focus_parent(ObClient
*self
)
3309 if (self
->transient_for
) {
3310 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3311 if (client_focused(self
->transient_for
))
3312 return self
->transient_for
;
3316 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3317 ObClient
*c
= it
->data
;
3319 /* checking transient_for prevents infinate loops! */
3320 if (c
!= self
&& !c
->transient_for
)
3321 if (client_focused(c
))
3330 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3332 if (self
->transient_for
) {
3333 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3334 if (self
->transient_for
== search
)
3339 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3340 ObClient
*c
= it
->data
;
3342 /* checking transient_for prevents infinate loops! */
3343 if (c
!= self
&& !c
->transient_for
)
3353 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3357 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3358 if (sit
->data
== search
)
3360 if (client_search_transient(sit
->data
, search
))
3366 void client_update_sm_client_id(ObClient
*self
)
3368 g_free(self
->sm_client_id
);
3369 self
->sm_client_id
= NULL
;
3371 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
) &&
3373 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
,
3374 &self
->sm_client_id
);
3377 #define WANT_EDGE(cur, c) \
3380 if(!client_normal(cur)) \
3382 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3386 if(cur->layer < c->layer && !config_resist_layers_below) \
3389 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3390 if ((his_edge_start >= my_edge_start && \
3391 his_edge_start <= my_edge_end) || \
3392 (my_edge_start >= his_edge_start && \
3393 my_edge_start <= his_edge_end)) \
3396 /* finds the nearest edge in the given direction from the current client
3397 * note to self: the edge is the -frame- edge (the actual one), not the
3400 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3402 gint dest
, monitor_dest
;
3403 gint my_edge_start
, my_edge_end
, my_offset
;
3410 a
= screen_area(c
->desktop
);
3411 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3414 case OB_DIRECTION_NORTH
:
3415 my_edge_start
= c
->frame
->area
.x
;
3416 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3417 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3419 /* default: top of screen */
3420 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3421 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3422 /* if the monitor edge comes before the screen edge, */
3423 /* use that as the destination instead. (For xinerama) */
3424 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3425 dest
= monitor_dest
;
3427 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3428 gint his_edge_start
, his_edge_end
, his_offset
;
3429 ObClient
*cur
= it
->data
;
3433 his_edge_start
= cur
->frame
->area
.x
;
3434 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3435 his_offset
= cur
->frame
->area
.y
+
3436 (hang
? 0 : cur
->frame
->area
.height
);
3438 if(his_offset
+ 1 > my_offset
)
3441 if(his_offset
< dest
)
3444 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3447 case OB_DIRECTION_SOUTH
:
3448 my_edge_start
= c
->frame
->area
.x
;
3449 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3450 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3452 /* default: bottom of screen */
3453 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3454 monitor_dest
= monitor
->y
+ monitor
->height
-
3455 (hang
? c
->frame
->area
.height
: 0);
3456 /* if the monitor edge comes before the screen edge, */
3457 /* use that as the destination instead. (For xinerama) */
3458 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3459 dest
= monitor_dest
;
3461 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3462 gint his_edge_start
, his_edge_end
, his_offset
;
3463 ObClient
*cur
= it
->data
;
3467 his_edge_start
= cur
->frame
->area
.x
;
3468 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3469 his_offset
= cur
->frame
->area
.y
+
3470 (hang
? cur
->frame
->area
.height
: 0);
3473 if(his_offset
- 1 < my_offset
)
3476 if(his_offset
> dest
)
3479 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3482 case OB_DIRECTION_WEST
:
3483 my_edge_start
= c
->frame
->area
.y
;
3484 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3485 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3487 /* default: leftmost egde of screen */
3488 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3489 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3490 /* if the monitor edge comes before the screen edge, */
3491 /* use that as the destination instead. (For xinerama) */
3492 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3493 dest
= monitor_dest
;
3495 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3496 gint his_edge_start
, his_edge_end
, his_offset
;
3497 ObClient
*cur
= it
->data
;
3501 his_edge_start
= cur
->frame
->area
.y
;
3502 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3503 his_offset
= cur
->frame
->area
.x
+
3504 (hang
? 0 : cur
->frame
->area
.width
);
3506 if(his_offset
+ 1 > my_offset
)
3509 if(his_offset
< dest
)
3512 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3515 case OB_DIRECTION_EAST
:
3516 my_edge_start
= c
->frame
->area
.y
;
3517 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3518 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3520 /* default: rightmost edge of screen */
3521 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3522 monitor_dest
= monitor
->x
+ monitor
->width
-
3523 (hang
? c
->frame
->area
.width
: 0);
3524 /* if the monitor edge comes before the screen edge, */
3525 /* use that as the destination instead. (For xinerama) */
3526 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3527 dest
= monitor_dest
;
3529 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3530 gint his_edge_start
, his_edge_end
, his_offset
;
3531 ObClient
*cur
= it
->data
;
3535 his_edge_start
= cur
->frame
->area
.y
;
3536 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3537 his_offset
= cur
->frame
->area
.x
+
3538 (hang
? cur
->frame
->area
.width
: 0);
3540 if(his_offset
- 1 < my_offset
)
3543 if(his_offset
> dest
)
3546 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3549 case OB_DIRECTION_NORTHEAST
:
3550 case OB_DIRECTION_SOUTHEAST
:
3551 case OB_DIRECTION_NORTHWEST
:
3552 case OB_DIRECTION_SOUTHWEST
:
3553 /* not implemented */
3555 g_assert_not_reached();
3556 dest
= 0; /* suppress warning */
3561 ObClient
* client_under_pointer()
3565 ObClient
*ret
= NULL
;
3567 if (screen_pointer_pos(&x
, &y
)) {
3568 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3569 if (WINDOW_IS_CLIENT(it
->data
)) {
3570 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3571 if (c
->frame
->visible
&&
3572 RECT_CONTAINS(c
->frame
->area
, x
, y
)) {
3582 gboolean
client_has_group_siblings(ObClient
*self
)
3584 return self
->group
&& self
->group
->members
->next
;