1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
40 #include "menuframe.h"
43 #include "render/render.h"
50 #include <X11/Xutil.h>
52 /*! The event mask to grab on client windows */
53 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
56 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
61 ObClientCallback func
;
65 GList
*client_list
= NULL
;
67 static GSList
*client_destroy_notifies
= NULL
;
69 static void client_get_all(ObClient
*self
, gboolean real
);
70 static void client_toggle_border(ObClient
*self
, gboolean show
);
71 static void client_get_startup_id(ObClient
*self
);
72 static void client_get_session_ids(ObClient
*self
);
73 static void client_get_area(ObClient
*self
);
74 static void client_get_desktop(ObClient
*self
);
75 static void client_get_state(ObClient
*self
);
76 static void client_get_shaped(ObClient
*self
);
77 static void client_get_mwm_hints(ObClient
*self
);
78 static void client_get_colormap(ObClient
*self
);
79 static void client_change_allowed_actions(ObClient
*self
);
80 static void client_change_state(ObClient
*self
);
81 static void client_change_wm_state(ObClient
*self
);
82 static void client_apply_startup_state(ObClient
*self
);
83 static void client_restore_session_state(ObClient
*self
);
84 static gboolean
client_restore_session_stacking(ObClient
*self
);
85 static ObAppSettings
*client_get_settings_state(ObClient
*self
);
86 static void client_update_transient_tree(ObClient
*self
,
87 ObGroup
*oldgroup
, ObGroup
*newgroup
,
90 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
);
91 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
93 ObStackingLayer layer
);
94 static void client_call_notifies(ObClient
*self
, GSList
*list
);
96 void client_startup(gboolean reconfig
)
103 void client_shutdown(gboolean reconfig
)
105 if (reconfig
) return;
108 static void client_call_notifies(ObClient
*self
, GSList
*list
)
112 for (it
= list
; it
; it
= g_slist_next(it
)) {
113 ClientCallback
*d
= it
->data
;
114 d
->func(self
, d
->data
);
118 void client_add_destroy_notify(ObClientCallback func
, gpointer data
)
120 ClientCallback
*d
= g_new(ClientCallback
, 1);
123 client_destroy_notifies
= g_slist_prepend(client_destroy_notifies
, d
);
126 void client_remove_destroy_notify(ObClientCallback func
)
130 for (it
= client_destroy_notifies
; it
; it
= g_slist_next(it
)) {
131 ClientCallback
*d
= it
->data
;
132 if (d
->func
== func
) {
134 client_destroy_notifies
=
135 g_slist_delete_link(client_destroy_notifies
, it
);
141 void client_set_list()
143 Window
*windows
, *win_it
;
145 guint size
= g_list_length(client_list
);
147 /* create an array of the window ids */
149 windows
= g_new(Window
, size
);
151 for (it
= client_list
; it
; it
= g_list_next(it
), ++win_it
)
152 *win_it
= ((ObClient
*)it
->data
)->window
;
156 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
157 net_client_list
, window
, (gulong
*)windows
, size
);
166 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
170 for (it = self->transients; it; it = g_slist_next(it)) {
171 if (!func(it->data, data)) return;
172 client_foreach_transient(it->data, func, data);
176 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
178 if (self->transient_for) {
179 if (self->transient_for != OB_TRAN_GROUP) {
180 if (!func(self->transient_for, data)) return;
181 client_foreach_ancestor(self->transient_for, func, data);
185 for (it = self->group->members; it; it = g_slist_next(it))
186 if (it->data != self &&
187 !((ObClient*)it->data)->transient_for) {
188 if (!func(it->data, data)) return;
189 client_foreach_ancestor(it->data, func, data);
196 void client_manage_all()
201 XWindowAttributes attrib
;
203 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
204 &w
, &w
, &children
, &nchild
);
206 /* remove all icon windows from the list */
207 for (i
= 0; i
< nchild
; i
++) {
208 if (children
[i
] == None
) continue;
209 wmhints
= XGetWMHints(ob_display
, children
[i
]);
211 if ((wmhints
->flags
& IconWindowHint
) &&
212 (wmhints
->icon_window
!= children
[i
]))
213 for (j
= 0; j
< nchild
; j
++)
214 if (children
[j
] == wmhints
->icon_window
) {
222 for (i
= 0; i
< nchild
; ++i
) {
223 if (children
[i
] == None
)
225 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
226 if (attrib
.override_redirect
) continue;
228 if (attrib
.map_state
!= IsUnmapped
)
229 client_manage(children
[i
]);
235 void client_manage(Window window
)
239 XWindowAttributes attrib
;
240 XSetWindowAttributes attrib_set
;
242 gboolean activate
= FALSE
;
243 ObAppSettings
*settings
;
247 /* check if it has already been unmapped by the time we started
248 mapping. the grab does a sync so we don't have to here */
249 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
250 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
))
252 XPutBackEvent(ob_display
, &e
);
254 ob_debug("Trying to manage unmapped window. Aborting that.\n");
256 return; /* don't manage it */
259 /* make sure it isn't an override-redirect window */
260 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
261 attrib
.override_redirect
)
264 return; /* don't manage it */
267 /* is the window a docking app */
268 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
269 if ((wmhint
->flags
& StateHint
) &&
270 wmhint
->initial_state
== WithdrawnState
)
272 dock_add(window
, wmhint
);
280 ob_debug("Managing window: %lx\n", window
);
282 /* choose the events we want to receive on the CLIENT window */
283 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
284 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
285 XChangeWindowAttributes(ob_display
, window
,
286 CWEventMask
|CWDontPropagate
, &attrib_set
);
288 /* create the ObClient struct, and populate it from the hints on the
290 self
= g_new0(ObClient
, 1);
291 self
->obwin
.type
= Window_Client
;
292 self
->window
= window
;
294 /* non-zero defaults */
295 self
->wmstate
= WithdrawnState
; /* make sure it gets updated first time */
296 self
->gravity
= NorthWestGravity
;
297 self
->desktop
= screen_num_desktops
; /* always an invalid value */
298 self
->user_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
300 /* get all the stuff off the window */
301 client_get_all(self
, TRUE
);
303 /* specify that if we exit, the window should not be destroyed and
304 should 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
);
312 /* we've grabbed everything and set everything that we need to at mapping
316 /* per-app settings override stuff from client_get_all, and return the
317 settings for other uses too */
318 settings
= client_get_settings_state(self
);
319 /* the session should get the last say thought */
320 client_restore_session_state(self
);
322 /* now we have all of the window's information so we can set this up */
323 client_setup_decor_and_functions(self
);
325 /* remove the client's border (and adjust re gravity) */
326 client_toggle_border(self
, FALSE
);
329 Time t
= sn_app_started(self
->startup_id
, self
->class);
330 if (t
) self
->user_time
= t
;
333 /* do this after we have a frame.. it uses the frame to help determine the
334 WM_STATE to apply. */
335 client_change_state(self
);
337 /* add ourselves to the focus order */
338 focus_order_add_new(self
);
340 /* do this to add ourselves to the stacking list in a non-intrusive way */
341 client_calc_layer(self
);
343 /* focus the new window? */
344 if (ob_state() != OB_STATE_STARTING
&&
345 (!self
->session
|| self
->session
->focused
) &&
347 /* this means focus=true for window is same as config_focus_new=true */
348 ((config_focus_new
|| (settings
&& settings
->focus
== 1)) ||
349 client_search_focus_parent(self
)) &&
350 /* this checks for focus=false for the window */
351 (!settings
|| settings
->focus
!= 0) &&
352 /* note the check against Type_Normal/Dialog, not client_normal(self),
353 which would also include other types. in this case we want more
354 strict rules for focus */
355 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
356 self
->type
== OB_CLIENT_TYPE_DIALOG
))
361 /* figure out placement for the window */
362 if (ob_state() == OB_STATE_RUNNING
) {
365 ob_debug("Positioned: %s @ %d %d\n",
366 (!self
->positioned
? "no" :
367 (self
->positioned
== PPosition
? "program specified" :
368 (self
->positioned
== USPosition
? "user specified" :
369 "BADNESS !?"))), self
->area
.x
, self
->area
.y
);
371 transient
= place_client(self
, &self
->area
.x
, &self
->area
.y
, settings
);
373 /* make sure the window is visible. */
374 client_find_onscreen(self
, &self
->area
.x
, &self
->area
.y
,
375 self
->area
.width
, self
->area
.height
,
376 /* non-normal clients has less rules, and
377 windows that are being restored from a
378 session do also. we can assume you want
379 it back where you saved it. Clients saying
380 they placed themselves are subjected to
381 harder rules, ones that are placed by
382 place.c or by the user are allowed partially
383 off-screen and on xinerama divides (ie,
384 it is up to the placement routines to avoid
385 the xinerama divides) */
387 (((self
->positioned
& PPosition
) &&
388 !(self
->positioned
& USPosition
)) &&
389 client_normal(self
) &&
393 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
394 self
->window
, self
->area
.x
, self
->area
.y
,
395 self
->area
.width
, self
->area
.height
);
397 ob_debug(" but session requested %d %d instead, overriding\n",
398 self
->session
->x
, self
->session
->y
);
400 /* adjust the frame to the client's size before showing the window */
401 frame_adjust_area(self
->frame
, FALSE
, TRUE
, FALSE
);
402 frame_adjust_client_area(self
->frame
);
405 /* move the client to its placed position, or it it's already there,
406 generate a ConfigureNotify telling the client where it is.
408 do this after adjusting the frame. otherwise it gets all weird and
409 clients don't work right */
410 client_configure(self
, self
->area
.x
, self
->area
.y
,
411 self
->area
.width
, self
->area
.height
,
414 /* do this after the window is placed, so the premax/prefullscreen numbers
416 also, this moves the window to the position where it has been placed
418 client_apply_startup_state(self
);
421 guint32 last_time
= focus_client
?
422 focus_client
->user_time
: CurrentTime
;
424 /* This is focus stealing prevention */
425 ob_debug_type(OB_DEBUG_FOCUS
,
426 "Want to focus new window 0x%x with time %u "
428 self
->window
, self
->user_time
, last_time
);
430 /* if it's on another desktop */
431 if (!(self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
432 && /* the timestamp is from before you changed desktops */
433 self
->user_time
&& screen_desktop_user_time
&&
434 !event_time_after(self
->user_time
, screen_desktop_user_time
))
437 ob_debug_type(OB_DEBUG_FOCUS
,
438 "Not focusing the window because its on another "
441 /* If something is focused, and it's not our parent... */
442 else if (focus_client
&& client_search_focus_parent(self
) == NULL
)
444 /* If time stamp is old, don't steal focus */
445 if (self
->user_time
&& last_time
&&
446 !event_time_after(self
->user_time
, last_time
))
449 ob_debug_type(OB_DEBUG_FOCUS
,
450 "Not focusing the window because the time is "
453 /* Don't steal focus from globally active clients.
454 I stole this idea from KWin. It seems nice.
456 if (!(focus_client
->can_focus
|| focus_client
->focus_notify
)) {
458 ob_debug_type(OB_DEBUG_FOCUS
,
459 "Not focusing the window because a globally "
460 "active client has focus\n");
465 ob_debug_type(OB_DEBUG_FOCUS
,
466 "Focus stealing prevention activated for %s with "
467 "time %u (last time %u)\n",
468 self
->title
, self
->user_time
, last_time
);
469 /* if the client isn't focused, then hilite it so the user
471 client_hilite(self
, TRUE
);
475 /* This may look rather odd. Well it's because new windows are added
476 to the stacking order non-intrusively. If we're not going to focus
477 the new window or hilite it, then we raise it to the top. This will
478 take affect for things that don't get focused like splash screens.
479 Also if you don't have focus_new enabled, then it's going to get
480 raised to the top. Legacy begets legacy I guess?
482 if (!client_restore_session_stacking(self
))
483 stacking_raise(CLIENT_AS_WINDOW(self
));
486 mouse_grab_for_client(self
, TRUE
);
488 /* this has to happen before we try focus the window, but we want it to
489 happen after the client's stacking has been determined or it looks bad
494 gboolean stacked
= client_restore_session_stacking(self
);
495 client_present(self
, FALSE
, !stacked
);
498 /* add to client list/map */
499 client_list
= g_list_append(client_list
, self
);
500 g_hash_table_insert(window_map
, &self
->window
, self
);
502 /* this has to happen after we're in the client_list */
503 if (STRUT_EXISTS(self
->strut
))
504 screen_update_areas();
506 /* update the list hints */
509 ob_debug("Managed window 0x%lx plate 0x%x (%s)\n",
510 window
, self
->frame
->plate
, self
->class);
516 ObClient
*client_fake_manage(Window window
)
519 ObAppSettings
*settings
;
521 ob_debug("Pretend-managing window: %lx\n", window
);
523 /* do this minimal stuff to figure out the client's decorations */
525 self
= g_new0(ObClient
, 1);
526 self
->window
= window
;
528 client_get_all(self
, FALSE
);
529 /* per-app settings override stuff, and return the settings for other
531 settings
= client_get_settings_state(self
);
533 client_setup_decor_and_functions(self
);
535 /* create the decoration frame for the client window and adjust its size */
536 self
->frame
= frame_new(self
);
537 frame_adjust_area(self
->frame
, FALSE
, TRUE
, TRUE
);
541 void client_unmanage_all()
543 while (client_list
!= NULL
)
544 client_unmanage(client_list
->data
);
547 void client_unmanage(ObClient
*self
)
552 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
553 self
->window
, self
->frame
->plate
,
554 self
->class, self
->title
? self
->title
: "");
556 g_assert(self
!= NULL
);
558 /* we dont want events no more. do this before hiding the frame so we
559 don't generate more events */
560 XSelectInput(ob_display
, self
->window
, NoEventMask
);
562 frame_hide(self
->frame
);
563 /* flush to send the hide to the server quickly */
566 /* ignore enter events from the unmap so it doesnt mess with the
568 event_ignore_all_queued_enters();
570 mouse_grab_for_client(self
, FALSE
);
572 /* remove the window from our save set */
573 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
575 /* kill the property windows */
576 propwin_remove(self
->user_time_window
, OB_PROPWIN_USER_TIME
, self
);
578 /* update the focus lists */
579 focus_order_remove(self
);
580 if (client_focused(self
)) {
581 /* don't leave an invalid focus_client */
585 client_list
= g_list_remove(client_list
, self
);
586 stacking_remove(self
);
587 g_hash_table_remove(window_map
, &self
->window
);
589 /* once the client is out of the list, update the struts to remove its
591 if (STRUT_EXISTS(self
->strut
))
592 screen_update_areas();
594 client_call_notifies(self
, client_destroy_notifies
);
596 /* tell our parent(s) that we're gone */
597 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
598 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
599 if (it
->data
!= self
)
600 ((ObClient
*)it
->data
)->transients
=
601 g_slist_remove(((ObClient
*)it
->data
)->transients
,self
);
602 } else if (self
->transient_for
) { /* transient of window */
603 self
->transient_for
->transients
=
604 g_slist_remove(self
->transient_for
->transients
, self
);
607 /* tell our transients that we're gone */
608 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
609 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
610 ((ObClient
*)it
->data
)->transient_for
= NULL
;
611 client_calc_layer(it
->data
);
615 /* remove from its group */
617 group_remove(self
->group
, self
);
621 /* restore the window's original geometry so it is not lost */
625 /* give the client its border back */
626 client_toggle_border(self
, TRUE
);
630 if (self
->fullscreen
)
631 a
= self
->pre_fullscreen_area
;
632 else if (self
->max_horz
|| self
->max_vert
) {
633 if (self
->max_horz
) {
634 a
.x
= self
->pre_max_area
.x
;
635 a
.width
= self
->pre_max_area
.width
;
637 if (self
->max_vert
) {
638 a
.y
= self
->pre_max_area
.y
;
639 a
.height
= self
->pre_max_area
.height
;
643 self
->fullscreen
= self
->max_horz
= self
->max_vert
= FALSE
;
644 self
->decorations
= 0; /* unmanaged windows have no decor */
646 client_move_resize(self
, a
.x
, a
.y
, a
.width
, a
.height
);
649 /* reparent the window out of the frame, and free the frame */
650 frame_release_client(self
->frame
);
651 frame_free(self
->frame
);
654 if (ob_state() != OB_STATE_EXITING
) {
655 /* these values should not be persisted across a window
657 PROP_ERASE(self
->window
, net_wm_desktop
);
658 PROP_ERASE(self
->window
, net_wm_state
);
659 PROP_ERASE(self
->window
, wm_state
);
661 /* if we're left in an unmapped state, the client wont be mapped.
662 this is bad, since we will no longer be managing the window on
664 XMapWindow(ob_display
, self
->window
);
667 /* update the list hints */
670 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
672 /* free all data allocated in the client struct */
673 g_slist_free(self
->transients
);
674 for (j
= 0; j
< self
->nicons
; ++j
)
675 g_free(self
->icons
[j
].data
);
676 if (self
->nicons
> 0)
678 g_free(self
->wm_command
);
680 g_free(self
->icon_title
);
684 g_free(self
->client_machine
);
685 g_free(self
->sm_client_id
);
689 void client_fake_unmanage(ObClient
*self
)
691 /* this is all that got allocated to get the decorations */
693 frame_free(self
->frame
);
697 static ObAppSettings
*client_get_settings_state(ObClient
*self
)
699 ObAppSettings
*settings
= NULL
;
702 for (it
= config_per_app_settings
; it
; it
= g_slist_next(it
)) {
703 ObAppSettings
*app
= it
->data
;
705 if ((app
->name
&& !app
->class && !strcmp(app
->name
, self
->name
))
706 || (app
->class && !app
->name
&& !strcmp(app
->class, self
->class))
707 || (app
->class && app
->name
&& !strcmp(app
->class, self
->class)
708 && !strcmp(app
->name
, self
->name
)))
710 /* Match if no role was specified in the per app setting, or if the
711 * string matches the beginning of the role, since apps like to set
712 * the role to things like browser-window-23c4b2f */
714 || !strncmp(app
->role
, self
->role
, strlen(app
->role
)))
716 ob_debug("Window matching: %s\n", app
->name
);
725 if (settings
->shade
!= -1)
726 self
->shaded
= !!settings
->shade
;
727 if (settings
->decor
!= -1)
728 self
->undecorated
= !settings
->decor
;
729 if (settings
->iconic
!= -1)
730 self
->iconic
= !!settings
->iconic
;
731 if (settings
->skip_pager
!= -1)
732 self
->skip_pager
= !!settings
->skip_pager
;
733 if (settings
->skip_taskbar
!= -1)
734 self
->skip_taskbar
= !!settings
->skip_taskbar
;
736 if (settings
->max_vert
!= -1)
737 self
->max_vert
= !!settings
->max_vert
;
738 if (settings
->max_horz
!= -1)
739 self
->max_horz
= !!settings
->max_horz
;
741 if (settings
->fullscreen
!= -1)
742 self
->fullscreen
= !!settings
->fullscreen
;
744 if (settings
->desktop
) {
745 if (settings
->desktop
== DESKTOP_ALL
)
746 self
->desktop
= settings
->desktop
;
747 else if (settings
->desktop
> 0 &&
748 settings
->desktop
<= screen_num_desktops
)
749 self
->desktop
= settings
->desktop
- 1;
752 if (settings
->layer
== -1) {
756 else if (settings
->layer
== 0) {
760 else if (settings
->layer
== 1) {
768 static void client_restore_session_state(ObClient
*self
)
772 ob_debug_type(OB_DEBUG_SM
,
773 "Restore session for client %s\n", self
->title
);
775 if (!(it
= session_state_find(self
))) {
776 ob_debug_type(OB_DEBUG_SM
,
777 "Session data not found for client %s\n", self
->title
);
781 self
->session
= it
->data
;
783 ob_debug_type(OB_DEBUG_SM
, "Session data loaded for client %s\n",
786 RECT_SET_POINT(self
->area
, self
->session
->x
, self
->session
->y
);
787 self
->positioned
= USPosition
;
788 if (self
->session
->w
> 0)
789 self
->area
.width
= self
->session
->w
;
790 if (self
->session
->h
> 0)
791 self
->area
.height
= self
->session
->h
;
792 XResizeWindow(ob_display
, self
->window
,
793 self
->area
.width
, self
->area
.height
);
795 self
->desktop
= (self
->session
->desktop
== DESKTOP_ALL
?
796 self
->session
->desktop
:
797 MIN(screen_num_desktops
- 1, self
->session
->desktop
));
798 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
800 self
->shaded
= self
->session
->shaded
;
801 self
->iconic
= self
->session
->iconic
;
802 self
->skip_pager
= self
->session
->skip_pager
;
803 self
->skip_taskbar
= self
->session
->skip_taskbar
;
804 self
->fullscreen
= self
->session
->fullscreen
;
805 self
->above
= self
->session
->above
;
806 self
->below
= self
->session
->below
;
807 self
->max_horz
= self
->session
->max_horz
;
808 self
->max_vert
= self
->session
->max_vert
;
809 self
->undecorated
= self
->session
->undecorated
;
812 static gboolean
client_restore_session_stacking(ObClient
*self
)
816 if (!self
->session
) return FALSE
;
818 mypos
= g_list_find(session_saved_state
, self
->session
);
819 if (!mypos
) return FALSE
;
821 /* start above me and look for the first client */
822 for (it
= g_list_previous(mypos
); it
; it
= g_list_previous(it
)) {
825 for (cit
= client_list
; cit
; cit
= g_list_next(cit
)) {
826 ObClient
*c
= cit
->data
;
827 /* found a client that was in the session, so go below it */
828 if (c
->session
== it
->data
) {
829 stacking_below(CLIENT_AS_WINDOW(self
),
830 CLIENT_AS_WINDOW(cit
->data
));
838 void client_move_onscreen(ObClient
*self
, gboolean rude
)
840 gint x
= self
->area
.x
;
841 gint y
= self
->area
.y
;
842 if (client_find_onscreen(self
, &x
, &y
,
844 self
->area
.height
, rude
)) {
845 client_move(self
, x
, y
);
849 gboolean
client_find_onscreen(ObClient
*self
, gint
*x
, gint
*y
, gint w
, gint h
,
853 gint ox
= *x
, oy
= *y
;
854 gboolean rudel
= rude
, ruder
= rude
, rudet
= rude
, rudeb
= rude
;
858 RECT_SET(desired
, *x
, *y
, w
, h
);
859 all_a
= screen_area(self
->desktop
);
860 mon_a
= screen_area_monitor(self
->desktop
, screen_find_monitor(&desired
));
862 /* get where the frame would be */
863 frame_client_gravity(self
->frame
, x
, y
, w
, h
);
865 /* get the requested size of the window with decorations */
866 fw
= self
->frame
->size
.left
+ w
+ self
->frame
->size
.right
;
867 fh
= self
->frame
->size
.top
+ h
+ self
->frame
->size
.bottom
;
869 /* This makes sure windows aren't entirely outside of the screen so you
870 can't see them at all.
871 It makes sure 10% of the window is on the screen at least. At don't let
872 it move itself off the top of the screen, which would hide the titlebar
873 on you. (The user can still do this if they want too, it's only limiting
876 XXX watch for xinerama dead areas...
878 if (client_normal(self
)) {
879 if (!self
->strut
.right
&& *x
+ fw
/10 >= all_a
->x
+ all_a
->width
- 1)
880 *x
= all_a
->x
+ all_a
->width
- fw
/10;
881 if (!self
->strut
.bottom
&& *y
+ fh
/10 >= all_a
->y
+ all_a
->height
- 1)
882 *y
= all_a
->y
+ all_a
->height
- fh
/10;
883 if (!self
->strut
.left
&& *x
+ fw
*9/10 - 1 < all_a
->x
)
884 *x
= all_a
->x
- fw
*9/10;
885 if (!self
->strut
.top
&& *y
+ fh
*9/10 - 1 < all_a
->y
)
886 *y
= all_a
->y
- fw
*9/10;
889 /* If rudeness wasn't requested, then figure out of the client is currently
890 entirely on the screen. If it is, and the position isn't changing by
891 request, and it is enlarging, then be rude even though it wasn't
894 Point oldtl
, oldtr
, oldbl
, oldbr
;
895 Point newtl
, newtr
, newbl
, newbr
;
896 gboolean stationary_l
, stationary_r
, stationary_t
, stationary_b
;
898 POINT_SET(oldtl
, self
->frame
->area
.x
, self
->frame
->area
.y
);
899 POINT_SET(oldbr
, self
->frame
->area
.x
+ self
->frame
->area
.width
- 1,
900 self
->frame
->area
.y
+ self
->frame
->area
.height
- 1);
901 POINT_SET(oldtr
, oldbr
.x
, oldtl
.y
);
902 POINT_SET(oldbl
, oldtl
.x
, oldbr
.y
);
904 POINT_SET(newtl
, *x
, *y
);
905 POINT_SET(newbr
, *x
+ fw
- 1, *y
+ fh
- 1);
906 POINT_SET(newtr
, newbr
.x
, newtl
.y
);
907 POINT_SET(newbl
, newtl
.x
, newbr
.y
);
909 /* is it moving or just resizing from some corner? */
910 stationary_l
= oldtl
.x
== oldtl
.x
;
911 stationary_r
= oldtr
.x
== oldtr
.x
;
912 stationary_t
= oldtl
.y
== oldtl
.y
;
913 stationary_b
= oldbl
.y
== oldbl
.y
;
915 /* if left edge is growing and didnt move right edge */
916 if (stationary_r
&& newtl
.x
< oldtl
.x
)
918 /* if right edge is growing and didnt move left edge */
919 if (stationary_l
&& newtr
.x
> oldtr
.x
)
921 /* if top edge is growing and didnt move bottom edge */
922 if (stationary_b
&& newtl
.y
< oldtl
.y
)
924 /* if bottom edge is growing and didnt move top edge */
925 if (stationary_t
&& newbl
.y
> oldbl
.y
)
929 /* This here doesn't let windows even a pixel outside the struts/screen.
930 * When called from client_manage, programs placing themselves are
931 * forced completely onscreen, while things like
932 * xterm -geometry resolution-width/2 will work fine. Trying to
933 * place it completely offscreen will be handled in the above code.
934 * Sorry for this confused comment, i am tired. */
935 if (fw
<= mon_a
->width
) {
936 if (rudel
&& !self
->strut
.left
&& *x
< mon_a
->x
) *x
= mon_a
->x
;
937 if (ruder
&& !self
->strut
.right
&& *x
+ fw
> mon_a
->x
+ mon_a
->width
)
938 *x
= mon_a
->x
+ mon_a
->width
- fw
;
940 if (fh
<= mon_a
->height
) {
941 if (rudet
&& !self
->strut
.top
&& *y
< mon_a
->y
) *y
= mon_a
->y
;
942 if (rudeb
&& !self
->strut
.bottom
&& *y
+ fh
> mon_a
->y
+ mon_a
->height
)
943 *y
= mon_a
->y
+ mon_a
->height
- fh
;
946 /* get where the client should be */
947 frame_frame_gravity(self
->frame
, x
, y
, w
, h
);
949 return ox
!= *x
|| oy
!= *y
;
952 static void client_toggle_border(ObClient
*self
, gboolean show
)
954 /* adjust our idea of where the client is, based on its border. When the
955 border is removed, the client should now be considered to be in a
957 when re-adding the border to the client, the same operation needs to be
959 gint oldx
= self
->area
.x
, oldy
= self
->area
.y
;
960 gint x
= oldx
, y
= oldy
;
961 switch(self
->gravity
) {
963 case NorthWestGravity
:
965 case SouthWestGravity
:
967 case NorthEastGravity
:
969 case SouthEastGravity
:
970 if (show
) x
-= self
->border_width
* 2;
971 else x
+= self
->border_width
* 2;
978 if (show
) x
-= self
->border_width
;
979 else x
+= self
->border_width
;
982 switch(self
->gravity
) {
984 case NorthWestGravity
:
986 case NorthEastGravity
:
988 case SouthWestGravity
:
990 case SouthEastGravity
:
991 if (show
) y
-= self
->border_width
* 2;
992 else y
+= self
->border_width
* 2;
999 if (show
) y
-= self
->border_width
;
1000 else y
+= self
->border_width
;
1007 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
1009 /* set border_width to 0 because there is no border to add into
1010 calculations anymore */
1011 self
->border_width
= 0;
1013 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
1017 static void client_get_all(ObClient
*self
, gboolean real
)
1019 /* this is needed for the frame to set itself up */
1020 client_get_area(self
);
1022 /* these things can change the decor and functions of the window */
1024 client_get_mwm_hints(self
);
1025 /* this can change the mwmhints for special cases */
1026 client_get_type_and_transientness(self
);
1027 client_get_state(self
);
1028 client_update_normal_hints(self
);
1030 /* get the session related properties, these can change decorations
1031 from per-app settings */
1032 client_get_session_ids(self
);
1034 /* now we got everything that can affect the decorations */
1038 /* get this early so we have it for debugging */
1039 client_update_title(self
);
1041 client_update_protocols(self
);
1043 client_update_wmhints(self
);
1044 /* this may have already been called from client_update_wmhints */
1045 if (self
->transient_for
== NULL
)
1046 client_update_transient_for(self
);
1048 client_get_startup_id(self
);
1049 client_get_desktop(self
);/* uses transient data/group/startup id if a
1050 desktop is not specified */
1051 client_get_shaped(self
);
1054 /* a couple type-based defaults for new windows */
1056 /* this makes sure that these windows appear on all desktops */
1057 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1058 self
->desktop
= DESKTOP_ALL
;
1062 client_update_sync_request_counter(self
);
1065 client_get_colormap(self
);
1066 client_update_strut(self
);
1067 client_update_icons(self
);
1068 client_update_user_time_window(self
);
1069 if (!self
->user_time_window
) /* check if this would have been called */
1070 client_update_user_time(self
);
1071 client_update_icon_geometry(self
);
1074 static void client_get_startup_id(ObClient
*self
)
1076 if (!(PROP_GETS(self
->window
, net_startup_id
, utf8
, &self
->startup_id
)))
1078 PROP_GETS(self
->group
->leader
,
1079 net_startup_id
, utf8
, &self
->startup_id
);
1082 static void client_get_area(ObClient
*self
)
1084 XWindowAttributes wattrib
;
1087 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
1088 g_assert(ret
!= BadWindow
);
1090 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
1091 POINT_SET(self
->root_pos
, wattrib
.x
, wattrib
.y
);
1092 self
->border_width
= wattrib
.border_width
;
1094 ob_debug("client area: %d %d %d %d\n", wattrib
.x
, wattrib
.y
,
1095 wattrib
.width
, wattrib
.height
);
1098 static void client_get_desktop(ObClient
*self
)
1100 guint32 d
= screen_num_desktops
; /* an always-invalid value */
1102 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
1103 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
1104 self
->desktop
= screen_num_desktops
- 1;
1108 gboolean trdesk
= FALSE
;
1110 if (self
->transient_for
) {
1111 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1112 self
->desktop
= self
->transient_for
->desktop
;
1117 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
1118 if (it
->data
!= self
&&
1119 !((ObClient
*)it
->data
)->transient_for
) {
1120 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
1127 /* try get from the startup-notification protocol */
1128 if (sn_get_desktop(self
->startup_id
, &self
->desktop
)) {
1129 if (self
->desktop
>= screen_num_desktops
&&
1130 self
->desktop
!= DESKTOP_ALL
)
1131 self
->desktop
= screen_num_desktops
- 1;
1133 /* defaults to the current desktop */
1134 self
->desktop
= screen_desktop
;
1139 static void client_get_state(ObClient
*self
)
1144 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
1146 for (i
= 0; i
< num
; ++i
) {
1147 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
1149 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
1150 self
->shaded
= TRUE
;
1151 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
1152 self
->iconic
= TRUE
;
1153 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
1154 self
->skip_taskbar
= TRUE
;
1155 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
1156 self
->skip_pager
= TRUE
;
1157 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
1158 self
->fullscreen
= TRUE
;
1159 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
1160 self
->max_vert
= TRUE
;
1161 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
1162 self
->max_horz
= TRUE
;
1163 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
1165 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
1167 else if (state
[i
] == prop_atoms
.net_wm_state_demands_attention
)
1168 self
->demands_attention
= TRUE
;
1169 else if (state
[i
] == prop_atoms
.ob_wm_state_undecorated
)
1170 self
->undecorated
= TRUE
;
1177 static void client_get_shaped(ObClient
*self
)
1179 self
->shaped
= FALSE
;
1181 if (extensions_shape
) {
1186 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
1188 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
1189 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
1191 self
->shaped
= (s
!= 0);
1196 void client_update_transient_for(ObClient
*self
)
1199 ObClient
*target
= NULL
;
1201 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
1202 if (t
!= self
->window
) { /* cant be transient to itself! */
1203 target
= g_hash_table_lookup(window_map
, &t
);
1204 /* if this happens then we need to check for it*/
1205 g_assert(target
!= self
);
1206 if (target
&& !WINDOW_IS_CLIENT(target
)) {
1207 /* this can happen when a dialog is a child of
1208 a dockapp, for example */
1212 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1214 Setting the transient_for to Root is actually illegal, however
1215 applications from time have done this to specify transient for
1218 Now you can do that by being a TYPE_DIALOG and not setting
1219 the transient_for hint at all on your window. But people still
1220 use Root, and Kwin is very strange in this regard.
1222 KWin 3.0 will not consider windows with transient_for set to
1223 Root as transient for their group *UNLESS* they are also modal.
1224 In that case, it will make them transient for the group. This
1225 leads to all sorts of weird behavior from KDE apps which are
1226 only tested in KWin. I'd like to follow their behavior just to
1227 make this work right with KDE stuff, but that seems wrong.
1229 if (!target
&& self
->group
) {
1230 /* not transient to a client, see if it is transient for a
1232 if (t
== RootWindow(ob_display
, ob_screen
)) {
1233 /* window is a transient for its group! */
1234 target
= OB_TRAN_GROUP
;
1238 } else if (self
->transient
&& self
->group
)
1239 target
= OB_TRAN_GROUP
;
1241 client_update_transient_tree(self
, self
->group
, self
->group
,
1242 self
->transient_for
, target
);
1243 self
->transient_for
= target
;
1247 static void client_update_transient_tree(ObClient
*self
,
1248 ObGroup
*oldgroup
, ObGroup
*newgroup
,
1249 ObClient
* oldparent
,
1250 ObClient
*newparent
)
1256 Group transient windows are not allowed to have other group
1257 transient windows as their children.
1261 /* No change has occured */
1262 if (oldgroup
== newgroup
&& oldparent
== newparent
) return;
1264 /** Remove the client from the transient tree wherever it has changed **/
1266 /* If the window is becoming a direct transient for a window in its group
1267 then any group transients which were our children and are now becoming
1268 our parents need to stop being our children.
1270 Group transients can't be children of group transients already, but
1271 we could have any number of direct parents above up, any of which could
1272 be transient for the group, and we need to remove it from our children.
1274 if (oldparent
!= newparent
&&
1275 newparent
!= NULL
&& newparent
!= OB_TRAN_GROUP
&&
1276 newgroup
!= NULL
&& newgroup
== oldgroup
)
1278 ObClient
*look
= newparent
;
1280 self
->transients
= g_slist_remove(self
->transients
, look
);
1281 look
= look
->transient_for
;
1282 } while (look
!= NULL
&& look
!= OB_TRAN_GROUP
);
1286 /* If the group changed, or if we are just becoming transient for the
1287 group, then we need to remove any old group transient windows
1288 from our children. But if we were already transient for the group, then
1289 other group transients are not our children. */
1290 if ((oldgroup
!= newgroup
||
1291 (newparent
== OB_TRAN_GROUP
&& oldparent
!= newparent
)) &&
1292 oldgroup
!= NULL
&& oldparent
!= OB_TRAN_GROUP
)
1294 for (it
= self
->transients
; it
; it
= next
) {
1295 next
= g_slist_next(it
);
1297 if (c
->group
== oldgroup
)
1298 self
->transients
= g_slist_delete_link(self
->transients
, it
);
1302 /* If we used to be transient for a group and now we are not, or we're
1303 transient for a new group, then we need to remove ourselves from all
1305 if (oldparent
== OB_TRAN_GROUP
&& (oldgroup
!= newgroup
||
1306 oldparent
!= newparent
))
1308 for (it
= oldgroup
->members
; it
; it
= g_slist_next(it
)) {
1310 if (c
!= self
&& (!c
->transient_for
||
1311 c
->transient_for
!= OB_TRAN_GROUP
))
1312 c
->transients
= g_slist_remove(c
->transients
, self
);
1315 /* If we used to be transient for a single window and we are no longer
1316 transient for it, then we need to remove ourself from its children */
1317 else if (oldparent
!= NULL
&& oldparent
!= OB_TRAN_GROUP
&&
1318 oldparent
!= newparent
)
1319 oldparent
->transients
= g_slist_remove(oldparent
->transients
, self
);
1322 /** Re-add the client to the transient tree wherever it has changed **/
1324 /* If we're now transient for a group and we weren't transient for it
1325 before then we need to add ourselves to all our new parents */
1326 if (newparent
== OB_TRAN_GROUP
&& (oldgroup
!= newgroup
||
1327 oldparent
!= newparent
))
1329 for (it
= oldgroup
->members
; it
; it
= g_slist_next(it
)) {
1331 if (c
!= self
&& (!c
->transient_for
||
1332 c
->transient_for
!= OB_TRAN_GROUP
))
1333 c
->transients
= g_slist_prepend(c
->transients
, self
);
1336 /* If we are now transient for a single window which we weren't before,
1337 we need to add ourselves to its children
1339 WARNING: Cyclical transient ness is possible if two windows are
1340 transient for eachother.
1342 else if (newparent
!= NULL
&& newparent
!= OB_TRAN_GROUP
&&
1343 newparent
!= oldparent
&&
1344 /* don't make ourself its child if it is already our child */
1345 !client_is_direct_child(self
, newparent
))
1346 newparent
->transients
= g_slist_prepend(newparent
->transients
, self
);
1348 /* If the group changed then we need to add any new group transient
1349 windows to our children. But if we're transient for the group, then
1350 other group transients are not our children.
1352 WARNING: Cyclical transient-ness is possible. For e.g. if:
1353 A is transient for the group
1354 B is transient for A
1355 C is transient for B
1356 A can't be transient for C or we have a cycle
1358 if (oldgroup
!= newgroup
&& newgroup
!= NULL
&&
1359 newparent
!= OB_TRAN_GROUP
)
1361 for (it
= newgroup
->members
; it
; it
= g_slist_next(it
)) {
1363 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
&&
1364 /* Don't make it our child if it is already our parent */
1365 !client_is_direct_child(c
, self
))
1367 self
->transients
= g_slist_prepend(self
->transients
, c
);
1373 static void client_get_mwm_hints(ObClient
*self
)
1378 self
->mwmhints
.flags
= 0; /* default to none */
1380 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
1382 if (num
>= OB_MWM_ELEMENTS
) {
1383 self
->mwmhints
.flags
= hints
[0];
1384 self
->mwmhints
.functions
= hints
[1];
1385 self
->mwmhints
.decorations
= hints
[2];
1391 void client_get_type_and_transientness(ObClient
*self
)
1398 self
->transient
= FALSE
;
1400 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
1401 /* use the first value that we know about in the array */
1402 for (i
= 0; i
< num
; ++i
) {
1403 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
1404 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
1405 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
1406 self
->type
= OB_CLIENT_TYPE_DOCK
;
1407 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
1408 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
1409 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
1410 self
->type
= OB_CLIENT_TYPE_MENU
;
1411 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
1412 self
->type
= OB_CLIENT_TYPE_UTILITY
;
1413 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
1414 self
->type
= OB_CLIENT_TYPE_SPLASH
;
1415 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
1416 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1417 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
1418 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1419 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
1420 /* prevent this window from getting any decor or
1422 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
1423 OB_MWM_FLAG_DECORATIONS
);
1424 self
->mwmhints
.decorations
= 0;
1425 self
->mwmhints
.functions
= 0;
1427 if (self
->type
!= (ObClientType
) -1)
1428 break; /* grab the first legit type */
1433 if (XGetTransientForHint(ob_display
, self
->window
, &t
))
1434 self
->transient
= TRUE
;
1436 if (self
->type
== (ObClientType
) -1) {
1437 /*the window type hint was not set, which means we either classify
1438 ourself as a normal window or a dialog, depending on if we are a
1440 if (self
->transient
)
1441 self
->type
= OB_CLIENT_TYPE_DIALOG
;
1443 self
->type
= OB_CLIENT_TYPE_NORMAL
;
1446 /* then, based on our type, we can update our transientness.. */
1447 if (self
->type
== OB_CLIENT_TYPE_DIALOG
||
1448 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
1449 self
->type
== OB_CLIENT_TYPE_MENU
||
1450 self
->type
== OB_CLIENT_TYPE_UTILITY
)
1452 self
->transient
= TRUE
;
1456 void client_update_protocols(ObClient
*self
)
1459 guint num_return
, i
;
1461 self
->focus_notify
= FALSE
;
1462 self
->delete_window
= FALSE
;
1464 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
1465 for (i
= 0; i
< num_return
; ++i
) {
1466 if (proto
[i
] == prop_atoms
.wm_delete_window
)
1467 /* this means we can request the window to close */
1468 self
->delete_window
= TRUE
;
1469 else if (proto
[i
] == prop_atoms
.wm_take_focus
)
1470 /* if this protocol is requested, then the window will be
1471 notified whenever we want it to receive focus */
1472 self
->focus_notify
= TRUE
;
1474 else if (proto
[i
] == prop_atoms
.net_wm_sync_request
)
1475 /* if this protocol is requested, then resizing the
1476 window will be synchronized between the frame and the
1478 self
->sync_request
= TRUE
;
1486 void client_update_sync_request_counter(ObClient
*self
)
1490 if (PROP_GET32(self
->window
, net_wm_sync_request_counter
, cardinal
, &i
)) {
1491 self
->sync_counter
= i
;
1493 self
->sync_counter
= None
;
1497 void client_get_colormap(ObClient
*self
)
1499 XWindowAttributes wa
;
1501 if (XGetWindowAttributes(ob_display
, self
->window
, &wa
))
1502 client_update_colormap(self
, wa
.colormap
);
1505 void client_update_colormap(ObClient
*self
, Colormap colormap
)
1507 self
->colormap
= colormap
;
1510 void client_update_normal_hints(ObClient
*self
)
1514 gint oldgravity
= self
->gravity
;
1517 self
->min_ratio
= 0.0f
;
1518 self
->max_ratio
= 0.0f
;
1519 SIZE_SET(self
->size_inc
, 1, 1);
1520 SIZE_SET(self
->base_size
, 0, 0);
1521 SIZE_SET(self
->min_size
, 0, 0);
1522 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
1524 /* get the hints from the window */
1525 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
1526 /* normal windows can't request placement! har har
1527 if (!client_normal(self))
1529 self
->positioned
= (size
.flags
& (PPosition
|USPosition
));
1531 if (size
.flags
& PWinGravity
) {
1532 self
->gravity
= size
.win_gravity
;
1534 /* if the client has a frame, i.e. has already been mapped and
1535 is changing its gravity */
1536 if (self
->frame
&& self
->gravity
!= oldgravity
) {
1537 /* move our idea of the client's position based on its new
1539 client_convert_gravity(self
, oldgravity
,
1540 &self
->area
.x
, &self
->area
.y
,
1541 self
->area
.width
, self
->area
.height
);
1545 if (size
.flags
& PAspect
) {
1546 if (size
.min_aspect
.y
)
1548 (gfloat
) size
.min_aspect
.x
/ size
.min_aspect
.y
;
1549 if (size
.max_aspect
.y
)
1551 (gfloat
) size
.max_aspect
.x
/ size
.max_aspect
.y
;
1554 if (size
.flags
& PMinSize
)
1555 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
1557 if (size
.flags
& PMaxSize
)
1558 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
1560 if (size
.flags
& PBaseSize
)
1561 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
1563 if (size
.flags
& PResizeInc
&& size
.width_inc
&& size
.height_inc
)
1564 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
1568 void client_setup_decor_and_functions(ObClient
*self
)
1570 /* start with everything (cept fullscreen) */
1572 (OB_FRAME_DECOR_TITLEBAR
|
1573 OB_FRAME_DECOR_HANDLE
|
1574 OB_FRAME_DECOR_GRIPS
|
1575 OB_FRAME_DECOR_BORDER
|
1576 OB_FRAME_DECOR_ICON
|
1577 OB_FRAME_DECOR_ALLDESKTOPS
|
1578 OB_FRAME_DECOR_ICONIFY
|
1579 OB_FRAME_DECOR_MAXIMIZE
|
1580 OB_FRAME_DECOR_SHADE
|
1581 OB_FRAME_DECOR_CLOSE
);
1583 (OB_CLIENT_FUNC_RESIZE
|
1584 OB_CLIENT_FUNC_MOVE
|
1585 OB_CLIENT_FUNC_ICONIFY
|
1586 OB_CLIENT_FUNC_MAXIMIZE
|
1587 OB_CLIENT_FUNC_SHADE
|
1588 OB_CLIENT_FUNC_CLOSE
|
1589 OB_CLIENT_FUNC_BELOW
|
1590 OB_CLIENT_FUNC_ABOVE
|
1591 OB_CLIENT_FUNC_UNDECORATE
);
1593 if (!(self
->min_size
.width
< self
->max_size
.width
||
1594 self
->min_size
.height
< self
->max_size
.height
))
1595 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1597 switch (self
->type
) {
1598 case OB_CLIENT_TYPE_NORMAL
:
1599 /* normal windows retain all of the possible decorations and
1600 functionality, and are the only windows that you can fullscreen */
1601 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1604 case OB_CLIENT_TYPE_DIALOG
:
1605 case OB_CLIENT_TYPE_UTILITY
:
1606 /* these windows cannot be maximized */
1607 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1610 case OB_CLIENT_TYPE_MENU
:
1611 case OB_CLIENT_TYPE_TOOLBAR
:
1612 /* these windows get less functionality */
1613 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1616 case OB_CLIENT_TYPE_SPLASH
:
1617 /* these don't get get any decorations, and the only thing you can
1618 do with them is move them */
1619 self
->decorations
= 0;
1620 self
->functions
= OB_CLIENT_FUNC_MOVE
;
1622 case OB_CLIENT_TYPE_DESKTOP
:
1623 /* these windows are not manipulated by the window manager */
1624 self
->decorations
= 0;
1625 self
->functions
= 0;
1627 case OB_CLIENT_TYPE_DOCK
:
1628 /* these windows are not manipulated by the window manager, but they
1629 can set below layer which has a special meaning */
1630 self
->decorations
= 0;
1631 self
->functions
= OB_CLIENT_FUNC_BELOW
;
1635 /* Mwm Hints are applied subtractively to what has already been chosen for
1636 decor and functionality */
1637 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1638 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1639 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1640 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1642 /* if the mwm hints request no handle or title, then all
1643 decorations are disabled, but keep the border if that's
1645 if (self
->mwmhints
.decorations
& OB_MWM_DECOR_BORDER
)
1646 self
->decorations
= OB_FRAME_DECOR_BORDER
;
1648 self
->decorations
= 0;
1653 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1654 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1655 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1656 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1657 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1658 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1659 /* dont let mwm hints kill any buttons
1660 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1661 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1662 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1663 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1665 /* dont let mwm hints kill the close button
1666 if (! (self->mwmhints.functions & MwmFunc_Close))
1667 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1671 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1672 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1673 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1674 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1675 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1676 self
->decorations
&= ~(OB_FRAME_DECOR_GRIPS
| OB_FRAME_DECOR_HANDLE
);
1678 /* can't maximize without moving/resizing */
1679 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1680 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1681 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1682 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1683 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1686 /* kill the handle on fully maxed windows */
1687 if (self
->max_vert
&& self
->max_horz
)
1688 self
->decorations
&= ~(OB_FRAME_DECOR_HANDLE
| OB_FRAME_DECOR_GRIPS
);
1690 /* If there are no decorations to remove, don't allow the user to try
1692 if (self
->decorations
== 0)
1693 self
->functions
&= ~OB_CLIENT_FUNC_UNDECORATE
;
1695 /* finally, the user can have requested no decorations, which overrides
1696 everything (but doesnt give it a border if it doesnt have one) */
1697 if (self
->undecorated
) {
1698 if (config_theme_keepborder
)
1699 self
->decorations
&= OB_FRAME_DECOR_BORDER
;
1701 self
->decorations
= 0;
1704 /* if we don't have a titlebar, then we cannot shade! */
1705 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1706 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1708 /* now we need to check against rules for the client's current state */
1709 if (self
->fullscreen
) {
1710 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1711 OB_CLIENT_FUNC_FULLSCREEN
|
1712 OB_CLIENT_FUNC_ICONIFY
);
1713 self
->decorations
= 0;
1716 client_change_allowed_actions(self
);
1719 /* adjust the client's decorations, etc. */
1720 client_reconfigure(self
);
1724 static void client_change_allowed_actions(ObClient
*self
)
1729 /* desktop windows are kept on all desktops */
1730 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1731 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1733 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1734 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1735 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1736 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1737 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1738 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1739 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1740 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1741 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1742 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1743 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1744 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1745 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1746 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1747 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1749 if (self
->functions
& OB_CLIENT_FUNC_ABOVE
)
1750 actions
[num
++] = prop_atoms
.net_wm_action_above
;
1751 if (self
->functions
& OB_CLIENT_FUNC_BELOW
)
1752 actions
[num
++] = prop_atoms
.net_wm_action_below
;
1753 if (self
->functions
& OB_CLIENT_FUNC_UNDECORATE
)
1754 actions
[num
++] = prop_atoms
.ob_wm_action_undecorate
;
1756 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1758 /* make sure the window isn't breaking any rules now */
1760 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1761 if (self
->frame
) client_shade(self
, FALSE
);
1762 else self
->shaded
= FALSE
;
1764 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1765 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
, FALSE
);
1766 else self
->iconic
= FALSE
;
1768 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1769 if (self
->frame
) client_fullscreen(self
, FALSE
);
1770 else self
->fullscreen
= FALSE
;
1772 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1774 if (self
->frame
) client_maximize(self
, FALSE
, 0);
1775 else self
->max_vert
= self
->max_horz
= FALSE
;
1779 void client_reconfigure(ObClient
*self
)
1781 /* by making this pass FALSE for user, we avoid the emacs event storm where
1782 every configurenotify causes an update in its normal hints, i think this
1783 is generally what we want anyways... */
1784 client_configure(self
, self
->area
.x
, self
->area
.y
,
1785 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1788 void client_update_wmhints(ObClient
*self
)
1792 /* assume a window takes input if it doesnt specify */
1793 self
->can_focus
= TRUE
;
1795 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1798 if (hints
->flags
& InputHint
)
1799 self
->can_focus
= hints
->input
;
1801 /* only do this when first managing the window *AND* when we aren't
1803 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1804 if (hints
->flags
& StateHint
)
1805 self
->iconic
= hints
->initial_state
== IconicState
;
1808 self
->urgent
= (hints
->flags
& XUrgencyHint
);
1809 if (self
->urgent
&& !ur
)
1810 client_hilite(self
, TRUE
);
1811 else if (!self
->urgent
&& ur
&& self
->demands_attention
)
1812 client_hilite(self
, FALSE
);
1814 if (!(hints
->flags
& WindowGroupHint
))
1815 hints
->window_group
= None
;
1817 /* did the group state change? */
1818 if (hints
->window_group
!=
1819 (self
->group
? self
->group
->leader
: None
))
1821 ObGroup
*oldgroup
= self
->group
;
1823 /* remove from the old group if there was one */
1824 if (self
->group
!= NULL
) {
1825 group_remove(self
->group
, self
);
1829 /* add ourself to the group if we have one */
1830 if (hints
->window_group
!= None
) {
1831 self
->group
= group_add(hints
->window_group
, self
);
1834 /* Put ourselves into the new group's transient tree, and remove
1835 ourselves from the old group's */
1836 client_update_transient_tree(self
, oldgroup
, self
->group
,
1837 self
->transient_for
,
1838 self
->transient_for
);
1840 /* Lastly, being in a group, or not, can change if the window is
1841 transient for anything.
1843 The logic for this is:
1844 self->transient = TRUE always if the window wants to be
1845 transient for something, even if transient_for was NULL because
1846 it wasn't in a group before.
1848 If transient_for was NULL and oldgroup was NULL we can assume
1849 that when we add the new group, it will become transient for
1852 If transient_for was OB_TRAN_GROUP, then it must have already
1853 had a group. If it is getting a new group, the above call to
1854 client_update_transient_tree has already taken care of
1855 everything ! If it is losing all group status then it will
1856 no longer be transient for anything and that needs to be
1859 if (self
->transient
&&
1860 ((self
->transient_for
== NULL
&& oldgroup
== NULL
) ||
1861 (self
->transient_for
== OB_TRAN_GROUP
&& !self
->group
)))
1862 client_update_transient_for(self
);
1865 /* the WM_HINTS can contain an icon */
1866 client_update_icons(self
);
1872 void client_update_title(ObClient
*self
)
1875 gchar
*visible
= NULL
;
1877 g_free(self
->title
);
1880 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
)) {
1881 /* try old x stuff */
1882 if (!(PROP_GETS(self
->window
, wm_name
, locale
, &data
)
1883 || PROP_GETS(self
->window
, wm_name
, utf8
, &data
))) {
1884 if (self
->transient
) {
1886 GNOME alert windows are not given titles:
1887 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1889 data
= g_strdup("");
1891 data
= g_strdup("Unnamed Window");
1895 if (self
->client_machine
) {
1896 visible
= g_strdup_printf("%s (%s)", data
, self
->client_machine
);
1901 PROP_SETS(self
->window
, net_wm_visible_name
, visible
);
1902 self
->title
= visible
;
1905 frame_adjust_title(self
->frame
);
1907 /* update the icon title */
1909 g_free(self
->icon_title
);
1912 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1913 /* try old x stuff */
1914 if (!(PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
) ||
1915 PROP_GETS(self
->window
, wm_icon_name
, utf8
, &data
)))
1916 data
= g_strdup(self
->title
);
1918 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1919 self
->icon_title
= data
;
1922 void client_update_strut(ObClient
*self
)
1926 gboolean got
= FALSE
;
1929 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1933 STRUT_PARTIAL_SET(strut
,
1934 data
[0], data
[2], data
[1], data
[3],
1935 data
[4], data
[5], data
[8], data
[9],
1936 data
[6], data
[7], data
[10], data
[11]);
1942 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1948 /* use the screen's width/height */
1949 a
= screen_physical_area();
1951 STRUT_PARTIAL_SET(strut
,
1952 data
[0], data
[2], data
[1], data
[3],
1953 a
->y
, a
->y
+ a
->height
- 1,
1954 a
->x
, a
->x
+ a
->width
- 1,
1955 a
->y
, a
->y
+ a
->height
- 1,
1956 a
->x
, a
->x
+ a
->width
- 1);
1962 STRUT_PARTIAL_SET(strut
, 0, 0, 0, 0,
1963 0, 0, 0, 0, 0, 0, 0, 0);
1965 if (!STRUT_EQUAL(strut
, self
->strut
)) {
1966 self
->strut
= strut
;
1968 /* updating here is pointless while we're being mapped cuz we're not in
1969 the client list yet */
1971 screen_update_areas();
1975 void client_update_icons(ObClient
*self
)
1981 for (i
= 0; i
< self
->nicons
; ++i
)
1982 g_free(self
->icons
[i
].data
);
1983 if (self
->nicons
> 0)
1984 g_free(self
->icons
);
1987 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1988 /* figure out how many valid icons are in here */
1990 while (num
- i
> 2) {
1994 if (i
> num
|| w
*h
== 0) break;
1998 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
2000 /* store the icons */
2002 for (j
= 0; j
< self
->nicons
; ++j
) {
2005 w
= self
->icons
[j
].width
= data
[i
++];
2006 h
= self
->icons
[j
].height
= data
[i
++];
2008 if (w
*h
== 0) continue;
2010 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
2011 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
2016 self
->icons
[j
].data
[t
] =
2017 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
2018 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
2019 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
2020 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
2029 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
2030 if (hints
->flags
& IconPixmapHint
) {
2032 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
2033 xerror_set_ignore(TRUE
);
2034 if (!RrPixmapToRGBA(ob_rr_inst
,
2036 (hints
->flags
& IconMaskHint
?
2037 hints
->icon_mask
: None
),
2038 &self
->icons
[self
->nicons
-1].width
,
2039 &self
->icons
[self
->nicons
-1].height
,
2040 &self
->icons
[self
->nicons
-1].data
)){
2041 g_free(&self
->icons
[self
->nicons
-1]);
2044 xerror_set_ignore(FALSE
);
2050 /* set the default icon onto the window
2051 in theory, this could be a race, but if a window doesn't set an icon
2052 or removes it entirely, it's not very likely it is going to set one
2053 right away afterwards */
2054 if (self
->nicons
== 0) {
2055 RrPixel32
*icon
= ob_rr_theme
->def_win_icon
;
2058 data
= g_new(gulong
, 48*48+2);
2059 data
[0] = data
[1] = 48;
2060 for (i
= 0; i
< 48*48; ++i
)
2061 data
[i
+2] = (((icon
[i
] >> RrDefaultAlphaOffset
) & 0xff) << 24) +
2062 (((icon
[i
] >> RrDefaultRedOffset
) & 0xff) << 16) +
2063 (((icon
[i
] >> RrDefaultGreenOffset
) & 0xff) << 8) +
2064 (((icon
[i
] >> RrDefaultBlueOffset
) & 0xff) << 0);
2065 PROP_SETA32(self
->window
, net_wm_icon
, cardinal
, data
, 48*48+2);
2067 } else if (self
->frame
)
2068 /* don't draw the icon empty if we're just setting one now anyways,
2069 we'll get the property change any second */
2070 frame_adjust_icon(self
->frame
);
2073 void client_update_user_time(ObClient
*self
)
2076 gboolean got
= FALSE
;
2078 if (self
->user_time_window
)
2079 got
= PROP_GET32(self
->user_time_window
,
2080 net_wm_user_time
, cardinal
, &time
);
2082 got
= PROP_GET32(self
->window
, net_wm_user_time
, cardinal
, &time
);
2085 /* we set this every time, not just when it grows, because in practice
2086 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
2087 backward we don't want all windows to stop focusing. we'll just
2088 assume noone is setting times older than the last one, cuz that
2089 would be pretty stupid anyways
2091 self
->user_time
= time
;
2093 /*ob_debug("window %s user time %u\n", self->title, time);*/
2097 void client_update_user_time_window(ObClient
*self
)
2101 if (!PROP_GET32(self
->window
, net_wm_user_time_window
, window
, &w
))
2104 if (w
!= self
->user_time_window
) {
2105 /* remove the old window */
2106 propwin_remove(self
->user_time_window
, OB_PROPWIN_USER_TIME
, self
);
2107 self
->user_time_window
= None
;
2109 if (self
->group
&& self
->group
->leader
== w
) {
2110 ob_debug_type(OB_DEBUG_APP_BUGS
, "Window is setting its "
2111 "_NET_WM_USER_TYPE_WINDOW to its group leader\n");
2112 /* do it anyways..? */
2114 else if (w
== self
->window
) {
2115 ob_debug_type(OB_DEBUG_APP_BUGS
, "Window is setting its "
2116 "_NET_WM_USER_TIME_WINDOW to itself\n");
2117 w
= None
; /* don't do it */
2120 /* add the new window */
2121 propwin_add(w
, OB_PROPWIN_USER_TIME
, self
);
2122 self
->user_time_window
= w
;
2124 /* and update from it */
2125 client_update_user_time(self
);
2129 void client_update_icon_geometry(ObClient
*self
)
2134 RECT_SET(self
->icon_geometry
, 0, 0, 0, 0);
2136 if (PROP_GETA32(self
->window
, net_wm_icon_geometry
, cardinal
, &data
, &num
)
2139 /* don't let them set it with an area < 0 */
2140 RECT_SET(self
->icon_geometry
, data
[0], data
[1],
2141 MAX(data
[2],0), MAX(data
[3],0));
2145 static void client_get_session_ids(ObClient
*self
)
2152 if (!PROP_GET32(self
->window
, wm_client_leader
, window
, &leader
))
2155 /* get the SM_CLIENT_ID */
2158 got
= PROP_GETS(leader
, sm_client_id
, locale
, &self
->sm_client_id
);
2160 PROP_GETS(self
->window
, sm_client_id
, locale
, &self
->sm_client_id
);
2162 /* get the WM_CLASS (name and class). make them "" if they are not
2166 got
= PROP_GETSS(leader
, wm_class
, locale
, &ss
);
2168 got
= PROP_GETSS(self
->window
, wm_class
, locale
, &ss
);
2172 self
->name
= g_strdup(ss
[0]);
2174 self
->class = g_strdup(ss
[1]);
2179 if (self
->name
== NULL
) self
->name
= g_strdup("");
2180 if (self
->class == NULL
) self
->class = g_strdup("");
2182 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2185 got
= PROP_GETS(leader
, wm_window_role
, locale
, &s
);
2187 got
= PROP_GETS(self
->window
, wm_window_role
, locale
, &s
);
2192 self
->role
= g_strdup("");
2194 /* get the WM_COMMAND */
2198 got
= PROP_GETSS(leader
, wm_command
, locale
, &ss
);
2200 got
= PROP_GETSS(self
->window
, wm_command
, locale
, &ss
);
2203 /* merge/mash them all together */
2204 gchar
*merge
= NULL
;
2207 for (i
= 0; ss
[i
]; ++i
) {
2210 merge
= g_strconcat(merge
, ss
[i
], NULL
);
2212 merge
= g_strconcat(ss
[i
], NULL
);
2217 self
->wm_command
= merge
;
2220 /* get the WM_CLIENT_MACHINE */
2223 got
= PROP_GETS(leader
, wm_client_machine
, locale
, &s
);
2225 got
= PROP_GETS(self
->window
, wm_client_machine
, locale
, &s
);
2228 gchar localhost
[128];
2230 gethostname(localhost
, 127);
2231 localhost
[127] = '\0';
2232 if (strcmp(localhost
, s
) != 0)
2233 self
->client_machine
= s
;
2239 static void client_change_wm_state(ObClient
*self
)
2244 old
= self
->wmstate
;
2246 if (self
->shaded
|| self
->iconic
||
2247 (self
->desktop
!= DESKTOP_ALL
&& self
->desktop
!= screen_desktop
))
2249 self
->wmstate
= IconicState
;
2251 self
->wmstate
= NormalState
;
2253 if (old
!= self
->wmstate
) {
2254 PROP_MSG(self
->window
, kde_wm_change_state
,
2255 self
->wmstate
, 1, 0, 0);
2257 state
[0] = self
->wmstate
;
2259 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
2263 static void client_change_state(ObClient
*self
)
2265 gulong netstate
[11];
2270 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
2272 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
2274 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
2275 if (self
->skip_taskbar
)
2276 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
2277 if (self
->skip_pager
)
2278 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
2279 if (self
->fullscreen
)
2280 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
2282 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
2284 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
2286 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
2288 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
2289 if (self
->demands_attention
)
2290 netstate
[num
++] = prop_atoms
.net_wm_state_demands_attention
;
2291 if (self
->undecorated
)
2292 netstate
[num
++] = prop_atoms
.ob_wm_state_undecorated
;
2293 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
2296 frame_adjust_state(self
->frame
);
2299 ObClient
*client_search_focus_tree(ObClient
*self
)
2304 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
2305 if (client_focused(it
->data
)) return it
->data
;
2306 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
2311 ObClient
*client_search_focus_tree_full(ObClient
*self
)
2313 if (self
->transient_for
) {
2314 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2315 return client_search_focus_tree_full(self
->transient_for
);
2318 gboolean recursed
= FALSE
;
2320 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
))
2321 if (!((ObClient
*)it
->data
)->transient_for
) {
2323 if ((c
= client_search_focus_tree_full(it
->data
)))
2332 /* this function checks the whole tree, the client_search_focus_tree~
2333 does not, so we need to check this window */
2334 if (client_focused(self
))
2336 return client_search_focus_tree(self
);
2339 static ObStackingLayer
calc_layer(ObClient
*self
)
2343 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
2344 l
= OB_STACKING_LAYER_DESKTOP
;
2345 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
2346 if (self
->below
) l
= OB_STACKING_LAYER_NORMAL
;
2347 else l
= OB_STACKING_LAYER_ABOVE
;
2349 else if ((self
->fullscreen
||
2350 /* No decorations and fills the monitor = oldskool fullscreen.
2351 But not for undecorated windows, because the user can do that
2353 (self
->decorations
== 0 &&
2354 !self
->undecorated
&&
2355 RECT_EQUAL(self
->area
,
2356 *screen_physical_area_monitor
2357 (client_monitor(self
))))) &&
2358 (client_focused(self
) || client_search_focus_tree(self
)))
2359 l
= OB_STACKING_LAYER_FULLSCREEN
;
2360 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
2361 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
2362 else l
= OB_STACKING_LAYER_NORMAL
;
2367 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
2368 ObStackingLayer min
)
2370 ObStackingLayer old
, own
;
2374 own
= calc_layer(self
);
2375 self
->layer
= MAX(own
, min
);
2377 if (self
->layer
!= old
) {
2378 stacking_remove(CLIENT_AS_WINDOW(self
));
2379 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self
));
2382 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2383 client_calc_layer_recursive(it
->data
, orig
,
2387 void client_calc_layer(ObClient
*self
)
2394 /* transients take on the layer of their parents */
2395 it
= client_search_all_top_parents(self
);
2397 for (; it
; it
= g_slist_next(it
))
2398 client_calc_layer_recursive(it
->data
, orig
, 0);
2401 gboolean
client_should_show(ObClient
*self
)
2405 if (client_normal(self
) && screen_showing_desktop
)
2407 if (self
->desktop
== screen_desktop
|| self
->desktop
== DESKTOP_ALL
)
2413 gboolean
client_show(ObClient
*self
)
2415 gboolean show
= FALSE
;
2417 if (client_should_show(self
)) {
2418 frame_show(self
->frame
);
2422 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2423 needs to be in IconicState. This includes when it is on another
2426 client_change_wm_state(self
);
2430 gboolean
client_hide(ObClient
*self
)
2432 gboolean hide
= FALSE
;
2434 if (!client_should_show(self
)) {
2435 if (self
== focus_client
) {
2436 /* if there is a grab going on, then we need to cancel it. if we
2437 move focus during the grab, applications will get
2438 NotifyWhileGrabbed events and ignore them !
2440 actions should not rely on being able to move focus during an
2443 if (keyboard_interactively_grabbed())
2444 keyboard_interactive_cancel();
2447 frame_hide(self
->frame
);
2451 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2452 needs to be in IconicState. This includes when it is on another
2455 client_change_wm_state(self
);
2459 void client_showhide(ObClient
*self
)
2461 if (!client_show(self
))
2464 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2465 needs to be in IconicState. This includes when it is on another
2468 client_change_wm_state(self
);
2471 gboolean
client_normal(ObClient
*self
) {
2472 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
2473 self
->type
== OB_CLIENT_TYPE_DOCK
||
2474 self
->type
== OB_CLIENT_TYPE_SPLASH
);
2477 gboolean
client_helper(ObClient
*self
)
2479 return (self
->type
== OB_CLIENT_TYPE_UTILITY
||
2480 self
->type
== OB_CLIENT_TYPE_MENU
||
2481 self
->type
== OB_CLIENT_TYPE_TOOLBAR
);
2484 gboolean
client_mouse_focusable(ObClient
*self
)
2486 return !(self
->type
== OB_CLIENT_TYPE_MENU
||
2487 self
->type
== OB_CLIENT_TYPE_TOOLBAR
||
2488 self
->type
== OB_CLIENT_TYPE_SPLASH
||
2489 self
->type
== OB_CLIENT_TYPE_DOCK
);
2492 gboolean
client_enter_focusable(ObClient
*self
)
2494 /* you can focus desktops but it shouldn't on enter */
2495 return (client_mouse_focusable(self
) &&
2496 self
->type
!= OB_CLIENT_TYPE_DESKTOP
);
2500 static void client_apply_startup_state(ObClient
*self
)
2502 /* set the desktop hint, to make sure that it always exists */
2503 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
2505 /* these are in a carefully crafted order.. */
2508 self
->iconic
= FALSE
;
2509 client_iconify(self
, TRUE
, FALSE
, TRUE
);
2511 if (self
->fullscreen
) {
2512 self
->fullscreen
= FALSE
;
2513 client_fullscreen(self
, TRUE
);
2515 if (self
->undecorated
) {
2516 self
->undecorated
= FALSE
;
2517 client_set_undecorated(self
, TRUE
);
2520 self
->shaded
= FALSE
;
2521 client_shade(self
, TRUE
);
2523 if (self
->demands_attention
) {
2524 self
->demands_attention
= FALSE
;
2525 client_hilite(self
, TRUE
);
2528 if (self
->max_vert
&& self
->max_horz
) {
2529 self
->max_vert
= self
->max_horz
= FALSE
;
2530 client_maximize(self
, TRUE
, 0);
2531 } else if (self
->max_vert
) {
2532 self
->max_vert
= FALSE
;
2533 client_maximize(self
, TRUE
, 2);
2534 } else if (self
->max_horz
) {
2535 self
->max_horz
= FALSE
;
2536 client_maximize(self
, TRUE
, 1);
2539 /* nothing to do for the other states:
2548 void client_convert_gravity(ObClient
*self
, gint gravity
, gint
*x
, gint
*y
,
2551 gint oldg
= self
->gravity
;
2553 /* get the frame's position from the requested stuff */
2554 self
->gravity
= gravity
;
2555 frame_client_gravity(self
->frame
, x
, y
, w
, h
);
2556 self
->gravity
= oldg
;
2558 /* get the client's position in its true gravity from that */
2559 frame_frame_gravity(self
->frame
, x
, y
, w
, h
);
2562 void client_try_configure(ObClient
*self
, gint
*x
, gint
*y
, gint
*w
, gint
*h
,
2563 gint
*logicalw
, gint
*logicalh
,
2566 Rect desired_area
= {*x
, *y
, *w
, *h
};
2568 /* make the frame recalculate its dimentions n shit without changing
2569 anything visible for real, this way the constraints below can work with
2570 the updated frame dimensions. */
2571 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
2573 /* work within the prefered sizes given by the window */
2574 if (!(*w
== self
->area
.width
&& *h
== self
->area
.height
)) {
2575 gint basew
, baseh
, minw
, minh
;
2577 /* base size is substituted with min size if not specified */
2578 if (self
->base_size
.width
|| self
->base_size
.height
) {
2579 basew
= self
->base_size
.width
;
2580 baseh
= self
->base_size
.height
;
2582 basew
= self
->min_size
.width
;
2583 baseh
= self
->min_size
.height
;
2585 /* min size is substituted with base size if not specified */
2586 if (self
->min_size
.width
|| self
->min_size
.height
) {
2587 minw
= self
->min_size
.width
;
2588 minh
= self
->min_size
.height
;
2590 minw
= self
->base_size
.width
;
2591 minh
= self
->base_size
.height
;
2594 /* if this is a user-requested resize, then check against min/max
2597 /* smaller than min size or bigger than max size? */
2598 if (*w
> self
->max_size
.width
) *w
= self
->max_size
.width
;
2599 if (*w
< minw
) *w
= minw
;
2600 if (*h
> self
->max_size
.height
) *h
= self
->max_size
.height
;
2601 if (*h
< minh
) *h
= minh
;
2606 /* keep to the increments */
2607 *w
/= self
->size_inc
.width
;
2608 *h
/= self
->size_inc
.height
;
2610 /* you cannot resize to nothing */
2611 if (basew
+ *w
< 1) *w
= 1 - basew
;
2612 if (baseh
+ *h
< 1) *h
= 1 - baseh
;
2614 /* save the logical size */
2615 *logicalw
= self
->size_inc
.width
> 1 ? *w
: *w
+ basew
;
2616 *logicalh
= self
->size_inc
.height
> 1 ? *h
: *h
+ baseh
;
2618 *w
*= self
->size_inc
.width
;
2619 *h
*= self
->size_inc
.height
;
2624 /* adjust the height to match the width for the aspect ratios.
2625 for this, min size is not substituted for base size ever. */
2626 *w
-= self
->base_size
.width
;
2627 *h
-= self
->base_size
.height
;
2629 if (!self
->fullscreen
) {
2630 if (self
->min_ratio
)
2631 if (*h
* self
->min_ratio
> *w
) {
2632 *h
= (gint
)(*w
/ self
->min_ratio
);
2634 /* you cannot resize to nothing */
2637 *w
= (gint
)(*h
* self
->min_ratio
);
2640 if (self
->max_ratio
)
2641 if (*h
* self
->max_ratio
< *w
) {
2642 *h
= (gint
)(*w
/ self
->max_ratio
);
2644 /* you cannot resize to nothing */
2647 *w
= (gint
)(*h
* self
->min_ratio
);
2652 *w
+= self
->base_size
.width
;
2653 *h
+= self
->base_size
.height
;
2656 /* gets the frame's position */
2657 frame_client_gravity(self
->frame
, x
, y
, *w
, *h
);
2659 /* these positions are frame positions, not client positions */
2661 /* set the size and position if fullscreen */
2662 if (self
->fullscreen
) {
2666 i
= screen_find_monitor(&desired_area
);
2667 a
= screen_physical_area_monitor(i
);
2674 user
= FALSE
; /* ignore if the client can't be moved/resized when it
2675 is entering fullscreen */
2676 } else if (self
->max_horz
|| self
->max_vert
) {
2680 i
= screen_find_monitor(&desired_area
);
2681 a
= screen_area_monitor(self
->desktop
, i
);
2683 /* set the size and position if maximized */
2684 if (self
->max_horz
) {
2686 *w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
2688 if (self
->max_vert
) {
2690 *h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
2693 /* maximizing is not allowed if the user can't move+resize the window
2697 /* gets the client's position */
2698 frame_frame_gravity(self
->frame
, x
, y
, *w
, *h
);
2700 /* these override the above states! if you cant move you can't move! */
2702 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
2706 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
2707 *w
= self
->area
.width
;
2708 *h
= self
->area
.height
;
2717 void client_configure(ObClient
*self
, gint x
, gint y
, gint w
, gint h
,
2718 gboolean user
, gboolean final
)
2721 gboolean send_resize_client
;
2722 gboolean moved
= FALSE
, resized
= FALSE
;
2723 gboolean fmoved
, fresized
;
2724 guint fdecor
= self
->frame
->decorations
;
2725 gboolean fhorz
= self
->frame
->max_horz
;
2726 gint logicalw
, logicalh
;
2728 /* find the new x, y, width, and height (and logical size) */
2729 client_try_configure(self
, &x
, &y
, &w
, &h
, &logicalw
, &logicalh
, user
);
2731 /* set the logical size if things changed */
2732 if (!(w
== self
->area
.width
&& h
== self
->area
.height
))
2733 SIZE_SET(self
->logical_size
, logicalw
, logicalh
);
2735 /* figure out if we moved or resized or what */
2736 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
2737 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
2739 oldw
= self
->area
.width
;
2740 oldh
= self
->area
.height
;
2741 RECT_SET(self
->area
, x
, y
, w
, h
);
2743 /* for app-requested resizes, always resize if 'resized' is true.
2744 for user-requested ones, only resize if final is true, or when
2745 resizing in redraw mode */
2746 send_resize_client
= ((!user
&& resized
) ||
2748 (resized
&& config_resize_redraw
))));
2750 /* if the client is enlarging, then resize the client before the frame */
2751 if (send_resize_client
&& (w
> oldw
|| h
> oldh
)) {
2752 XResizeWindow(ob_display
, self
->window
,
2753 MAX(w
, oldw
), MAX(h
, oldh
));
2754 /* resize the plate to show the client padding color underneath */
2755 frame_adjust_client_area(self
->frame
);
2758 /* find the frame's dimensions and move/resize it */
2761 if (self
->decorations
!= fdecor
|| self
->max_horz
!= fhorz
)
2762 fmoved
= fresized
= TRUE
;
2763 if (fmoved
|| fresized
)
2764 frame_adjust_area(self
->frame
, fmoved
, fresized
, FALSE
);
2766 if ((!user
|| (user
&& final
)) && !resized
)
2770 POINT_SET(self
->root_pos
,
2771 self
->frame
->area
.x
+ self
->frame
->size
.left
-
2773 self
->frame
->area
.y
+ self
->frame
->size
.top
-
2774 self
->border_width
);
2776 event
.type
= ConfigureNotify
;
2777 event
.xconfigure
.display
= ob_display
;
2778 event
.xconfigure
.event
= self
->window
;
2779 event
.xconfigure
.window
= self
->window
;
2781 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n",
2782 self
->title
, self
->root_pos
.x
, self
->root_pos
.y
, w
, h
);
2784 /* root window real coords */
2785 event
.xconfigure
.x
= self
->root_pos
.x
;
2786 event
.xconfigure
.y
= self
->root_pos
.y
;
2787 event
.xconfigure
.width
= w
;
2788 event
.xconfigure
.height
= h
;
2789 event
.xconfigure
.border_width
= 0;
2790 event
.xconfigure
.above
= self
->frame
->plate
;
2791 event
.xconfigure
.override_redirect
= FALSE
;
2792 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
2793 FALSE
, StructureNotifyMask
, &event
);
2796 /* if the client is shrinking, then resize the frame before the client */
2797 if (send_resize_client
&& (w
<= oldw
|| h
<= oldh
)) {
2798 /* resize the plate to show the client padding color underneath */
2799 frame_adjust_client_area(self
->frame
);
2801 if (send_resize_client
)
2802 XResizeWindow(ob_display
, self
->window
, w
, h
);
2808 void client_fullscreen(ObClient
*self
, gboolean fs
)
2812 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
2813 self
->fullscreen
== fs
) return; /* already done */
2815 self
->fullscreen
= fs
;
2816 client_change_state(self
); /* change the state hints on the client */
2819 self
->pre_fullscreen_area
= self
->area
;
2820 /* if the window is maximized, its area isn't all that meaningful.
2821 save it's premax area instead. */
2822 if (self
->max_horz
) {
2823 self
->pre_fullscreen_area
.x
= self
->pre_max_area
.x
;
2824 self
->pre_fullscreen_area
.width
= self
->pre_max_area
.width
;
2826 if (self
->max_vert
) {
2827 self
->pre_fullscreen_area
.y
= self
->pre_max_area
.y
;
2828 self
->pre_fullscreen_area
.height
= self
->pre_max_area
.height
;
2831 /* these will help configure_full figure out where to fullscreen
2835 w
= self
->area
.width
;
2836 h
= self
->area
.height
;
2838 g_assert(self
->pre_fullscreen_area
.width
> 0 &&
2839 self
->pre_fullscreen_area
.height
> 0);
2841 x
= self
->pre_fullscreen_area
.x
;
2842 y
= self
->pre_fullscreen_area
.y
;
2843 w
= self
->pre_fullscreen_area
.width
;
2844 h
= self
->pre_fullscreen_area
.height
;
2845 RECT_SET(self
->pre_fullscreen_area
, 0, 0, 0, 0);
2848 client_setup_decor_and_functions(self
);
2850 client_move_resize(self
, x
, y
, w
, h
);
2852 /* and adjust our layer/stacking. do this after resizing the window,
2853 and applying decorations, because windows which fill the screen are
2854 considered "fullscreen" and it affects their layer */
2855 client_calc_layer(self
);
2858 /* try focus us when we go into fullscreen mode */
2863 static void client_iconify_recursive(ObClient
*self
,
2864 gboolean iconic
, gboolean curdesk
,
2865 gboolean hide_animation
)
2868 gboolean changed
= FALSE
;
2871 if (self
->iconic
!= iconic
) {
2872 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2876 /* don't let non-normal windows iconify along with their parents
2878 if (client_normal(self
)) {
2879 self
->iconic
= iconic
;
2881 /* update the focus lists.. iconic windows go to the bottom of
2882 the list, put the new iconic window at the 'top of the
2884 focus_order_to_top(self
);
2889 self
->iconic
= iconic
;
2891 if (curdesk
&& self
->desktop
!= screen_desktop
&&
2892 self
->desktop
!= DESKTOP_ALL
)
2893 client_set_desktop(self
, screen_desktop
, FALSE
);
2895 /* this puts it after the current focused window */
2896 focus_order_remove(self
);
2897 focus_order_add_new(self
);
2904 client_change_state(self
);
2905 if (config_animate_iconify
&& !hide_animation
)
2906 frame_begin_iconify_animation(self
->frame
, iconic
);
2907 /* do this after starting the animation so it doesn't flash */
2908 client_showhide(self
);
2911 /* iconify all direct transients, and deiconify all transients
2913 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
2914 if (it
->data
!= self
)
2915 if (client_is_direct_child(self
, it
->data
) || !iconic
)
2916 client_iconify_recursive(it
->data
, iconic
, curdesk
,
2920 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
,
2921 gboolean hide_animation
)
2923 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
|| !iconic
) {
2924 /* move up the transient chain as far as possible first */
2925 self
= client_search_top_normal_parent(self
);
2926 client_iconify_recursive(self
, iconic
, curdesk
, hide_animation
);
2930 void client_maximize(ObClient
*self
, gboolean max
, gint dir
)
2934 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2935 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2937 /* check if already done */
2939 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2940 if (dir
== 1 && self
->max_horz
) return;
2941 if (dir
== 2 && self
->max_vert
) return;
2943 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2944 if (dir
== 1 && !self
->max_horz
) return;
2945 if (dir
== 2 && !self
->max_vert
) return;
2948 /* these will help configure_full figure out which screen to fill with
2952 w
= self
->area
.width
;
2953 h
= self
->area
.height
;
2956 if ((dir
== 0 || dir
== 1) && !self
->max_horz
) { /* horz */
2957 RECT_SET(self
->pre_max_area
,
2958 self
->area
.x
, self
->pre_max_area
.y
,
2959 self
->area
.width
, self
->pre_max_area
.height
);
2961 if ((dir
== 0 || dir
== 2) && !self
->max_vert
) { /* vert */
2962 RECT_SET(self
->pre_max_area
,
2963 self
->pre_max_area
.x
, self
->area
.y
,
2964 self
->pre_max_area
.width
, self
->area
.height
);
2967 if ((dir
== 0 || dir
== 1) && self
->max_horz
) { /* horz */
2968 g_assert(self
->pre_max_area
.width
> 0);
2970 x
= self
->pre_max_area
.x
;
2971 w
= self
->pre_max_area
.width
;
2973 RECT_SET(self
->pre_max_area
, 0, self
->pre_max_area
.y
,
2974 0, self
->pre_max_area
.height
);
2976 if ((dir
== 0 || dir
== 2) && self
->max_vert
) { /* vert */
2977 g_assert(self
->pre_max_area
.height
> 0);
2979 y
= self
->pre_max_area
.y
;
2980 h
= self
->pre_max_area
.height
;
2982 RECT_SET(self
->pre_max_area
, self
->pre_max_area
.x
, 0,
2983 self
->pre_max_area
.width
, 0);
2987 if (dir
== 0 || dir
== 1) /* horz */
2988 self
->max_horz
= max
;
2989 if (dir
== 0 || dir
== 2) /* vert */
2990 self
->max_vert
= max
;
2992 client_change_state(self
); /* change the state hints on the client */
2994 client_setup_decor_and_functions(self
);
2996 client_move_resize(self
, x
, y
, w
, h
);
2999 void client_shade(ObClient
*self
, gboolean shade
)
3001 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
3002 shade
) || /* can't shade */
3003 self
->shaded
== shade
) return; /* already done */
3005 self
->shaded
= shade
;
3006 client_change_state(self
);
3007 client_change_wm_state(self
); /* the window is being hidden/shown */
3008 /* resize the frame to just the titlebar */
3009 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
3012 void client_close(ObClient
*self
)
3016 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
3018 /* in the case that the client provides no means to requesting that it
3019 close, we just kill it */
3020 if (!self
->delete_window
)
3024 XXX: itd be cool to do timeouts and shit here for killing the client's
3026 like... if the window is around after 5 seconds, then the close button
3027 turns a nice red, and if this function is called again, the client is
3031 ce
.xclient
.type
= ClientMessage
;
3032 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3033 ce
.xclient
.display
= ob_display
;
3034 ce
.xclient
.window
= self
->window
;
3035 ce
.xclient
.format
= 32;
3036 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
3037 ce
.xclient
.data
.l
[1] = event_curtime
;
3038 ce
.xclient
.data
.l
[2] = 0l;
3039 ce
.xclient
.data
.l
[3] = 0l;
3040 ce
.xclient
.data
.l
[4] = 0l;
3041 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3044 void client_kill(ObClient
*self
)
3046 XKillClient(ob_display
, self
->window
);
3049 void client_hilite(ObClient
*self
, gboolean hilite
)
3051 if (self
->demands_attention
== hilite
)
3052 return; /* no change */
3054 /* don't allow focused windows to hilite */
3055 self
->demands_attention
= hilite
&& !client_focused(self
);
3056 if (self
->frame
!= NULL
) { /* if we're mapping, just set the state */
3057 if (self
->demands_attention
)
3058 frame_flash_start(self
->frame
);
3060 frame_flash_stop(self
->frame
);
3061 client_change_state(self
);
3065 void client_set_desktop_recursive(ObClient
*self
,
3072 if (target
!= self
->desktop
) {
3074 ob_debug("Setting desktop %u\n", target
+1);
3076 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
3078 old
= self
->desktop
;
3079 self
->desktop
= target
;
3080 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
3081 /* the frame can display the current desktop state */
3082 frame_adjust_state(self
->frame
);
3083 /* 'move' the window to the new desktop */
3085 client_showhide(self
);
3086 /* raise if it was not already on the desktop */
3087 if (old
!= DESKTOP_ALL
)
3088 stacking_raise(CLIENT_AS_WINDOW(self
));
3089 if (STRUT_EXISTS(self
->strut
))
3090 screen_update_areas();
3093 /* move all transients */
3094 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3095 if (it
->data
!= self
)
3096 if (client_is_direct_child(self
, it
->data
))
3097 client_set_desktop_recursive(it
->data
, target
, donthide
);
3100 void client_set_desktop(ObClient
*self
, guint target
,
3103 self
= client_search_top_normal_parent(self
);
3104 client_set_desktop_recursive(self
, target
, donthide
);
3107 gboolean
client_is_direct_child(ObClient
*parent
, ObClient
*child
)
3109 while (child
!= parent
&&
3110 child
->transient_for
&& child
->transient_for
!= OB_TRAN_GROUP
)
3111 child
= child
->transient_for
;
3112 return child
== parent
;
3115 ObClient
*client_search_modal_child(ObClient
*self
)
3120 for (it
= self
->transients
; it
; it
= g_slist_next(it
)) {
3121 ObClient
*c
= it
->data
;
3122 if ((ret
= client_search_modal_child(c
))) return ret
;
3123 if (c
->modal
) return c
;
3128 gboolean
client_validate(ObClient
*self
)
3132 XSync(ob_display
, FALSE
); /* get all events on the server */
3134 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
3135 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
3136 XPutBackEvent(ob_display
, &e
);
3143 void client_set_wm_state(ObClient
*self
, glong state
)
3145 if (state
== self
->wmstate
) return; /* no change */
3149 client_iconify(self
, TRUE
, TRUE
, FALSE
);
3152 client_iconify(self
, FALSE
, TRUE
, FALSE
);
3157 void client_set_state(ObClient
*self
, Atom action
, glong data1
, glong data2
)
3159 gboolean shaded
= self
->shaded
;
3160 gboolean fullscreen
= self
->fullscreen
;
3161 gboolean undecorated
= self
->undecorated
;
3162 gboolean max_horz
= self
->max_horz
;
3163 gboolean max_vert
= self
->max_vert
;
3164 gboolean modal
= self
->modal
;
3165 gboolean iconic
= self
->iconic
;
3166 gboolean demands_attention
= self
->demands_attention
;
3167 gboolean above
= self
->above
;
3168 gboolean below
= self
->below
;
3171 if (!(action
== prop_atoms
.net_wm_state_add
||
3172 action
== prop_atoms
.net_wm_state_remove
||
3173 action
== prop_atoms
.net_wm_state_toggle
))
3174 /* an invalid action was passed to the client message, ignore it */
3177 for (i
= 0; i
< 2; ++i
) {
3178 Atom state
= i
== 0 ? data1
: data2
;
3180 if (!state
) continue;
3182 /* if toggling, then pick whether we're adding or removing */
3183 if (action
== prop_atoms
.net_wm_state_toggle
) {
3184 if (state
== prop_atoms
.net_wm_state_modal
)
3185 action
= modal
? prop_atoms
.net_wm_state_remove
:
3186 prop_atoms
.net_wm_state_add
;
3187 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
3188 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
3189 prop_atoms
.net_wm_state_add
;
3190 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
3191 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
3192 prop_atoms
.net_wm_state_add
;
3193 else if (state
== prop_atoms
.net_wm_state_shaded
)
3194 action
= shaded
? prop_atoms
.net_wm_state_remove
:
3195 prop_atoms
.net_wm_state_add
;
3196 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
3197 action
= self
->skip_taskbar
?
3198 prop_atoms
.net_wm_state_remove
:
3199 prop_atoms
.net_wm_state_add
;
3200 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
3201 action
= self
->skip_pager
?
3202 prop_atoms
.net_wm_state_remove
:
3203 prop_atoms
.net_wm_state_add
;
3204 else if (state
== prop_atoms
.net_wm_state_hidden
)
3205 action
= self
->iconic
?
3206 prop_atoms
.net_wm_state_remove
:
3207 prop_atoms
.net_wm_state_add
;
3208 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
3209 action
= fullscreen
?
3210 prop_atoms
.net_wm_state_remove
:
3211 prop_atoms
.net_wm_state_add
;
3212 else if (state
== prop_atoms
.net_wm_state_above
)
3213 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
3214 prop_atoms
.net_wm_state_add
;
3215 else if (state
== prop_atoms
.net_wm_state_below
)
3216 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
3217 prop_atoms
.net_wm_state_add
;
3218 else if (state
== prop_atoms
.net_wm_state_demands_attention
)
3219 action
= self
->demands_attention
?
3220 prop_atoms
.net_wm_state_remove
:
3221 prop_atoms
.net_wm_state_add
;
3222 else if (state
== prop_atoms
.ob_wm_state_undecorated
)
3223 action
= undecorated
? prop_atoms
.net_wm_state_remove
:
3224 prop_atoms
.net_wm_state_add
;
3227 if (action
== prop_atoms
.net_wm_state_add
) {
3228 if (state
== prop_atoms
.net_wm_state_modal
) {
3230 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
3232 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
3234 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
3236 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
3237 self
->skip_taskbar
= TRUE
;
3238 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
3239 self
->skip_pager
= TRUE
;
3240 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
3242 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
3244 } else if (state
== prop_atoms
.net_wm_state_above
) {
3247 } else if (state
== prop_atoms
.net_wm_state_below
) {
3250 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
3251 demands_attention
= TRUE
;
3252 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
3256 } else { /* action == prop_atoms.net_wm_state_remove */
3257 if (state
== prop_atoms
.net_wm_state_modal
) {
3259 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
3261 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
3263 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
3265 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
3266 self
->skip_taskbar
= FALSE
;
3267 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
3268 self
->skip_pager
= FALSE
;
3269 } else if (state
== prop_atoms
.net_wm_state_hidden
) {
3271 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
3273 } else if (state
== prop_atoms
.net_wm_state_above
) {
3275 } else if (state
== prop_atoms
.net_wm_state_below
) {
3277 } else if (state
== prop_atoms
.net_wm_state_demands_attention
) {
3278 demands_attention
= FALSE
;
3279 } else if (state
== prop_atoms
.ob_wm_state_undecorated
) {
3280 undecorated
= FALSE
;
3285 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
3286 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
3288 if (max_horz
== max_vert
) { /* both going the same way */
3289 client_maximize(self
, max_horz
, 0);
3291 client_maximize(self
, max_horz
, 1);
3292 client_maximize(self
, max_vert
, 2);
3296 if (max_horz
!= self
->max_horz
)
3297 client_maximize(self
, max_horz
, 1);
3299 client_maximize(self
, max_vert
, 2);
3302 /* change fullscreen state before shading, as it will affect if the window
3304 if (fullscreen
!= self
->fullscreen
)
3305 client_fullscreen(self
, fullscreen
);
3306 if (shaded
!= self
->shaded
)
3307 client_shade(self
, shaded
);
3308 if (undecorated
!= self
->undecorated
)
3309 client_set_undecorated(self
, undecorated
);
3310 if (above
!= self
->above
|| below
!= self
->below
) {
3311 self
->above
= above
;
3312 self
->below
= below
;
3313 client_calc_layer(self
);
3316 if (modal
!= self
->modal
) {
3317 self
->modal
= modal
;
3318 /* when a window changes modality, then its stacking order with its
3319 transients needs to change */
3320 stacking_raise(CLIENT_AS_WINDOW(self
));
3322 /* it also may get focused. if something is focused that shouldn't
3323 be focused anymore, then move the focus */
3324 if (focus_client
&& client_focus_target(focus_client
) != focus_client
)
3325 client_focus(focus_client
);
3328 if (iconic
!= self
->iconic
)
3329 client_iconify(self
, iconic
, FALSE
, FALSE
);
3331 if (demands_attention
!= self
->demands_attention
)
3332 client_hilite(self
, demands_attention
);
3334 client_change_state(self
); /* change the hint to reflect these changes */
3337 ObClient
*client_focus_target(ObClient
*self
)
3339 ObClient
*child
= NULL
;
3341 child
= client_search_modal_child(self
);
3342 if (child
) return child
;
3346 gboolean
client_can_focus(ObClient
*self
)
3348 /* choose the correct target */
3349 self
= client_focus_target(self
);
3351 if (!self
->frame
->visible
)
3354 if (!(self
->can_focus
|| self
->focus_notify
))
3360 gboolean
client_focus(ObClient
*self
)
3362 /* choose the correct target */
3363 self
= client_focus_target(self
);
3365 if (!client_can_focus(self
)) {
3366 if (!self
->frame
->visible
) {
3367 /* update the focus lists */
3368 focus_order_to_top(self
);
3373 ob_debug_type(OB_DEBUG_FOCUS
,
3374 "Focusing client \"%s\" at time %u\n",
3375 self
->title
, event_curtime
);
3377 /* if there is a grab going on, then we need to cancel it. if we move
3378 focus during the grab, applications will get NotifyWhileGrabbed events
3381 actions should not rely on being able to move focus during an
3384 if (keyboard_interactively_grabbed())
3385 keyboard_interactive_cancel();
3387 xerror_set_ignore(TRUE
);
3388 xerror_occured
= FALSE
;
3390 if (self
->can_focus
) {
3391 /* This can cause a BadMatch error with CurrentTime, or if an app
3392 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3393 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
3397 if (self
->focus_notify
) {
3399 ce
.xclient
.type
= ClientMessage
;
3400 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
3401 ce
.xclient
.display
= ob_display
;
3402 ce
.xclient
.window
= self
->window
;
3403 ce
.xclient
.format
= 32;
3404 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
3405 ce
.xclient
.data
.l
[1] = event_curtime
;
3406 ce
.xclient
.data
.l
[2] = 0l;
3407 ce
.xclient
.data
.l
[3] = 0l;
3408 ce
.xclient
.data
.l
[4] = 0l;
3409 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
3412 xerror_set_ignore(FALSE
);
3414 return !xerror_occured
;
3417 /*! Present the client to the user.
3418 @param raise If the client should be raised or not. You should only set
3419 raise to false if you don't care if the window is completely
3422 static void client_present(ObClient
*self
, gboolean here
, gboolean raise
)
3424 /* if using focus_delay, stop the timer now so that focus doesn't
3426 event_halt_focus_delay();
3428 if (client_normal(self
) && screen_showing_desktop
)
3429 screen_show_desktop(FALSE
, self
);
3431 client_iconify(self
, FALSE
, here
, FALSE
);
3432 if (self
->desktop
!= DESKTOP_ALL
&&
3433 self
->desktop
!= screen_desktop
)
3436 client_set_desktop(self
, screen_desktop
, FALSE
);
3438 screen_set_desktop(self
->desktop
, FALSE
);
3439 } else if (!self
->frame
->visible
)
3440 /* if its not visible for other reasons, then don't mess
3444 client_shade(self
, FALSE
);
3446 stacking_raise(CLIENT_AS_WINDOW(self
));
3451 void client_activate(ObClient
*self
, gboolean here
, gboolean user
)
3453 guint32 last_time
= focus_client
? focus_client
->user_time
: CurrentTime
;
3454 gboolean allow
= FALSE
;
3456 /* if the request came from the user, or if nothing is focused, then grant
3458 if the currently focused app doesn't set a user_time, then it can't
3459 benefit from any focus stealing prevention.
3461 if (user
|| !focus_client
|| !last_time
)
3463 /* otherwise, if they didn't give a time stamp or if it is too old, they
3466 allow
= event_curtime
&& event_time_after(event_curtime
, last_time
);
3468 ob_debug_type(OB_DEBUG_FOCUS
,
3469 "Want to activate window 0x%x with time %u (last time %u), "
3470 "source=%s allowing? %d\n",
3471 self
->window
, event_curtime
, last_time
,
3472 (user
? "user" : "application"), allow
);
3475 if (event_curtime
!= CurrentTime
)
3476 self
->user_time
= event_curtime
;
3478 client_present(self
, here
, TRUE
);
3480 /* don't focus it but tell the user it wants attention */
3481 client_hilite(self
, TRUE
);
3484 static void client_bring_helper_windows_recursive(ObClient
*self
,
3489 for (it
= self
->transients
; it
; it
= g_slist_next(it
))
3490 client_bring_helper_windows_recursive(it
->data
, desktop
);
3492 if (client_helper(self
) &&
3493 self
->desktop
!= desktop
&& self
->desktop
!= DESKTOP_ALL
)
3495 client_set_desktop(self
, desktop
, FALSE
);
3499 void client_bring_helper_windows(ObClient
*self
)
3501 client_bring_helper_windows_recursive(self
, self
->desktop
);
3504 gboolean
client_focused(ObClient
*self
)
3506 return self
== focus_client
;
3509 static ObClientIcon
* client_icon_recursive(ObClient
*self
, gint w
, gint h
)
3512 gulong min_diff
, min_i
;
3514 if (!self
->nicons
) {
3515 ObClientIcon
*parent
= NULL
;
3517 if (self
->transient_for
) {
3518 if (self
->transient_for
!= OB_TRAN_GROUP
)
3519 parent
= client_icon_recursive(self
->transient_for
, w
, h
);
3522 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3523 ObClient
*c
= it
->data
;
3524 if (c
!= self
&& !c
->transient_for
) {
3525 if ((parent
= client_icon_recursive(c
, w
, h
)))
3535 /* some kind of crappy approximation to find the icon closest in size to
3536 what we requested, but icons are generally all the same ratio as
3537 eachother so it's good enough. */
3539 min_diff
= ABS(self
->icons
[0].width
- w
) + ABS(self
->icons
[0].height
- h
);
3542 for (i
= 1; i
< self
->nicons
; ++i
) {
3545 diff
= ABS(self
->icons
[0].width
- w
) + ABS(self
->icons
[0].height
- h
);
3546 if (diff
< min_diff
) {
3551 return &self
->icons
[min_i
];
3554 const ObClientIcon
* client_icon(ObClient
*self
, gint w
, gint h
)
3557 static ObClientIcon deficon
;
3559 if (!(ret
= client_icon_recursive(self
, w
, h
))) {
3560 deficon
.width
= deficon
.height
= 48;
3561 deficon
.data
= ob_rr_theme
->def_win_icon
;
3567 void client_set_layer(ObClient
*self
, gint layer
)
3571 self
->above
= FALSE
;
3572 } else if (layer
== 0) {
3573 self
->below
= self
->above
= FALSE
;
3575 self
->below
= FALSE
;
3578 client_calc_layer(self
);
3579 client_change_state(self
); /* reflect this in the state hints */
3582 void client_set_undecorated(ObClient
*self
, gboolean undecorated
)
3584 if (self
->undecorated
!= undecorated
&&
3585 /* don't let it undecorate if the function is missing, but let
3587 (self
->functions
& OB_CLIENT_FUNC_UNDECORATE
|| !undecorated
))
3589 self
->undecorated
= undecorated
;
3590 client_setup_decor_and_functions(self
);
3591 client_change_state(self
); /* reflect this in the state hints */
3595 guint
client_monitor(ObClient
*self
)
3597 return screen_find_monitor(&self
->frame
->area
);
3600 ObClient
*client_search_top_normal_parent(ObClient
*self
)
3602 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3603 client_normal(self
->transient_for
))
3604 self
= self
->transient_for
;
3608 static GSList
*client_search_all_top_parents_internal(ObClient
*self
,
3610 ObStackingLayer layer
)
3614 /* move up the direct transient chain as far as possible */
3615 while (self
->transient_for
&& self
->transient_for
!= OB_TRAN_GROUP
&&
3616 (!bylayer
|| self
->transient_for
->layer
== layer
) &&
3617 client_normal(self
->transient_for
))
3618 self
= self
->transient_for
;
3620 if (!self
->transient_for
)
3621 ret
= g_slist_prepend(ret
, self
);
3625 g_assert(self
->group
);
3627 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3628 ObClient
*c
= it
->data
;
3630 if (!c
->transient_for
&& client_normal(c
) &&
3631 (!bylayer
|| c
->layer
== layer
))
3633 ret
= g_slist_prepend(ret
, c
);
3637 if (ret
== NULL
) /* no group parents */
3638 ret
= g_slist_prepend(ret
, self
);
3644 GSList
*client_search_all_top_parents(ObClient
*self
)
3646 return client_search_all_top_parents_internal(self
, FALSE
, 0);
3649 GSList
*client_search_all_top_parents_layer(ObClient
*self
)
3651 return client_search_all_top_parents_internal(self
, TRUE
, self
->layer
);
3654 ObClient
*client_search_focus_parent(ObClient
*self
)
3656 if (self
->transient_for
) {
3657 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3658 if (client_focused(self
->transient_for
))
3659 return self
->transient_for
;
3663 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3664 ObClient
*c
= it
->data
;
3666 /* checking transient_for prevents infinate loops! */
3667 if (c
!= self
&& !c
->transient_for
)
3668 if (client_focused(c
))
3677 ObClient
*client_search_parent(ObClient
*self
, ObClient
*search
)
3679 if (self
->transient_for
) {
3680 if (self
->transient_for
!= OB_TRAN_GROUP
) {
3681 if (self
->transient_for
== search
)
3686 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
3687 ObClient
*c
= it
->data
;
3689 /* checking transient_for prevents infinate loops! */
3690 if (c
!= self
&& !c
->transient_for
)
3700 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
3704 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
3705 if (sit
->data
== search
)
3707 if (client_search_transient(sit
->data
, search
))
3713 #define WANT_EDGE(cur, c) \
3716 if(!client_normal(cur)) \
3718 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3723 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3724 if ((his_edge_start >= my_edge_start && \
3725 his_edge_start <= my_edge_end) || \
3726 (my_edge_start >= his_edge_start && \
3727 my_edge_start <= his_edge_end)) \
3730 /* finds the nearest edge in the given direction from the current client
3731 * note to self: the edge is the -frame- edge (the actual one), not the
3734 gint
client_directional_edge_search(ObClient
*c
, ObDirection dir
, gboolean hang
)
3736 gint dest
, monitor_dest
;
3737 gint my_edge_start
, my_edge_end
, my_offset
;
3744 a
= screen_area(c
->desktop
);
3745 monitor
= screen_area_monitor(c
->desktop
, client_monitor(c
));
3748 case OB_DIRECTION_NORTH
:
3749 my_edge_start
= c
->frame
->area
.x
;
3750 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3751 my_offset
= c
->frame
->area
.y
+ (hang
? c
->frame
->area
.height
: 0);
3753 /* default: top of screen */
3754 dest
= a
->y
+ (hang
? c
->frame
->area
.height
: 0);
3755 monitor_dest
= monitor
->y
+ (hang
? c
->frame
->area
.height
: 0);
3756 /* if the monitor edge comes before the screen edge, */
3757 /* use that as the destination instead. (For xinerama) */
3758 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3759 dest
= monitor_dest
;
3761 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3762 gint his_edge_start
, his_edge_end
, his_offset
;
3763 ObClient
*cur
= it
->data
;
3767 his_edge_start
= cur
->frame
->area
.x
;
3768 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3769 his_offset
= cur
->frame
->area
.y
+
3770 (hang
? 0 : cur
->frame
->area
.height
);
3772 if(his_offset
+ 1 > my_offset
)
3775 if(his_offset
< dest
)
3778 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3781 case OB_DIRECTION_SOUTH
:
3782 my_edge_start
= c
->frame
->area
.x
;
3783 my_edge_end
= c
->frame
->area
.x
+ c
->frame
->area
.width
;
3784 my_offset
= c
->frame
->area
.y
+ (hang
? 0 : c
->frame
->area
.height
);
3786 /* default: bottom of screen */
3787 dest
= a
->y
+ a
->height
- (hang
? c
->frame
->area
.height
: 0);
3788 monitor_dest
= monitor
->y
+ monitor
->height
-
3789 (hang
? c
->frame
->area
.height
: 0);
3790 /* if the monitor edge comes before the screen edge, */
3791 /* use that as the destination instead. (For xinerama) */
3792 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3793 dest
= monitor_dest
;
3795 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3796 gint his_edge_start
, his_edge_end
, his_offset
;
3797 ObClient
*cur
= it
->data
;
3801 his_edge_start
= cur
->frame
->area
.x
;
3802 his_edge_end
= cur
->frame
->area
.x
+ cur
->frame
->area
.width
;
3803 his_offset
= cur
->frame
->area
.y
+
3804 (hang
? cur
->frame
->area
.height
: 0);
3807 if(his_offset
- 1 < my_offset
)
3810 if(his_offset
> dest
)
3813 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3816 case OB_DIRECTION_WEST
:
3817 my_edge_start
= c
->frame
->area
.y
;
3818 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3819 my_offset
= c
->frame
->area
.x
+ (hang
? c
->frame
->area
.width
: 0);
3821 /* default: leftmost egde of screen */
3822 dest
= a
->x
+ (hang
? c
->frame
->area
.width
: 0);
3823 monitor_dest
= monitor
->x
+ (hang
? c
->frame
->area
.width
: 0);
3824 /* if the monitor edge comes before the screen edge, */
3825 /* use that as the destination instead. (For xinerama) */
3826 if (monitor_dest
!= dest
&& my_offset
> monitor_dest
)
3827 dest
= monitor_dest
;
3829 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3830 gint his_edge_start
, his_edge_end
, his_offset
;
3831 ObClient
*cur
= it
->data
;
3835 his_edge_start
= cur
->frame
->area
.y
;
3836 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3837 his_offset
= cur
->frame
->area
.x
+
3838 (hang
? 0 : cur
->frame
->area
.width
);
3840 if(his_offset
+ 1 > my_offset
)
3843 if(his_offset
< dest
)
3846 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3849 case OB_DIRECTION_EAST
:
3850 my_edge_start
= c
->frame
->area
.y
;
3851 my_edge_end
= c
->frame
->area
.y
+ c
->frame
->area
.height
;
3852 my_offset
= c
->frame
->area
.x
+ (hang
? 0 : c
->frame
->area
.width
);
3854 /* default: rightmost edge of screen */
3855 dest
= a
->x
+ a
->width
- (hang
? c
->frame
->area
.width
: 0);
3856 monitor_dest
= monitor
->x
+ monitor
->width
-
3857 (hang
? c
->frame
->area
.width
: 0);
3858 /* if the monitor edge comes before the screen edge, */
3859 /* use that as the destination instead. (For xinerama) */
3860 if (monitor_dest
!= dest
&& my_offset
< monitor_dest
)
3861 dest
= monitor_dest
;
3863 for(it
= client_list
; it
&& my_offset
!= dest
; it
= g_list_next(it
)) {
3864 gint his_edge_start
, his_edge_end
, his_offset
;
3865 ObClient
*cur
= it
->data
;
3869 his_edge_start
= cur
->frame
->area
.y
;
3870 his_edge_end
= cur
->frame
->area
.y
+ cur
->frame
->area
.height
;
3871 his_offset
= cur
->frame
->area
.x
+
3872 (hang
? cur
->frame
->area
.width
: 0);
3874 if(his_offset
- 1 < my_offset
)
3877 if(his_offset
> dest
)
3880 HIT_EDGE(my_edge_start
, my_edge_end
, his_edge_start
, his_edge_end
)
3883 case OB_DIRECTION_NORTHEAST
:
3884 case OB_DIRECTION_SOUTHEAST
:
3885 case OB_DIRECTION_NORTHWEST
:
3886 case OB_DIRECTION_SOUTHWEST
:
3887 /* not implemented */
3889 g_assert_not_reached();
3890 dest
= 0; /* suppress warning */
3895 ObClient
* client_under_pointer()
3899 ObClient
*ret
= NULL
;
3901 if (screen_pointer_pos(&x
, &y
)) {
3902 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
3903 if (WINDOW_IS_CLIENT(it
->data
)) {
3904 ObClient
*c
= WINDOW_AS_CLIENT(it
->data
);
3905 if (c
->frame
->visible
&&
3906 /* ignore all animating windows */
3907 !frame_iconify_animating(c
->frame
) &&
3908 RECT_CONTAINS(c
->frame
->area
, x
, y
))
3919 gboolean
client_has_group_siblings(ObClient
*self
)
3921 return self
->group
&& self
->group
->members
->next
;