1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
25 #include "moveresize.h"
38 #include "menuframe.h"
41 #include "render/render.h"
43 #include "obt/display.h"
51 # include <signal.h> /* for kill() */
55 #include <X11/Xutil.h>
57 /*! The event mask to grab on client windows */
58 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
61 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
66 ObClientCallback func;
70 GList *client_list = NULL;
72 static GSList *client_destroy_notifies = NULL;
74 static void client_get_all(ObClient *self, gboolean real);
75 static void client_get_startup_id(ObClient *self);
76 static void client_get_session_ids(ObClient *self);
77 static void client_get_area(ObClient *self);
78 static void client_get_desktop(ObClient *self);
79 static void client_get_state(ObClient *self);
80 static void client_get_shaped(ObClient *self);
81 static void client_get_mwm_hints(ObClient *self);
82 static void client_get_colormap(ObClient *self);
83 static void client_set_desktop_recursive(ObClient *self,
87 static void client_change_allowed_actions(ObClient *self);
88 static void client_change_state(ObClient *self);
89 static void client_change_wm_state(ObClient *self);
90 static void client_apply_startup_state(ObClient *self,
91 gint x, gint y, gint w, gint h);
92 static void client_restore_session_state(ObClient *self);
93 static gboolean client_restore_session_stacking(ObClient *self);
94 static ObAppSettings *client_get_settings_state(ObClient *self);
95 static void client_update_transient_tree(ObClient *self,
96 ObGroup *oldgroup, ObGroup *newgroup,
97 gboolean oldgtran, gboolean newgtran,
100 static void client_present(ObClient *self, gboolean here, gboolean raise,
102 static GSList *client_search_all_top_parents_internal(ObClient *self,
104 ObStackingLayer layer);
105 static void client_call_notifies(ObClient *self, GSList *list);
106 static void client_ping_event(ObClient *self, gboolean dead);
109 void client_startup(gboolean reconfig)
111 if (reconfig) return;
116 void client_shutdown(gboolean reconfig)
118 if (reconfig) return;
121 static void client_call_notifies(ObClient *self, GSList *list)
125 for (it = list; it; it = g_slist_next(it)) {
126 ClientCallback *d = it->data;
127 d->func(self, d->data);
131 void client_add_destroy_notify(ObClientCallback func, gpointer data)
133 ClientCallback *d = g_new(ClientCallback, 1);
136 client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
139 void client_remove_destroy_notify(ObClientCallback func)
143 for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
144 ClientCallback *d = it->data;
145 if (d->func == func) {
147 client_destroy_notifies =
148 g_slist_delete_link(client_destroy_notifies, it);
154 void client_set_list(void)
156 Window *windows, *win_it;
158 guint size = g_list_length(client_list);
160 /* create an array of the window ids */
162 windows = g_new(Window, size);
164 for (it = client_list; it; it = g_list_next(it), ++win_it)
165 *win_it = ((ObClient*)it->data)->window;
169 OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
170 (gulong*)windows, size);
178 void client_manage(Window window, ObPrompt *prompt)
181 XSetWindowAttributes attrib_set;
182 gboolean activate = FALSE;
183 ObAppSettings *settings;
184 gboolean transient = FALSE;
185 Rect place, *monitor;
186 Time launch_time, map_time;
188 ob_debug("Managing window: 0x%lx", window);
190 map_time = event_get_server_time();
192 /* choose the events we want to receive on the CLIENT window */
193 attrib_set.event_mask = CLIENT_EVENTMASK;
194 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
195 XChangeWindowAttributes(obt_display, window,
196 CWEventMask|CWDontPropagate, &attrib_set);
198 /* create the ObClient struct, and populate it from the hints on the
200 self = g_new0(ObClient, 1);
201 self->obwin.type = OB_WINDOW_CLASS_CLIENT;
202 self->window = window;
203 self->prompt = prompt;
205 /* non-zero defaults */
206 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
207 self->gravity = NorthWestGravity;
208 self->desktop = screen_num_desktops; /* always an invalid value */
210 /* get all the stuff off the window */
211 client_get_all(self, TRUE);
213 ob_debug("Window type: %d", self->type);
214 ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
216 /* now we have all of the window's information so we can set this up.
217 do this before creating the frame, so it can tell that we are still
218 mapping and doesn't go applying things right away */
219 client_setup_decor_and_functions(self, FALSE);
221 /* specify that if we exit, the window should not be destroyed and
222 should be reparented back to root automatically */
224 XChangeSaveSet(obt_display, window, SetModeInsert);
226 /* create the decoration frame for the client window */
227 self->frame = frame_new(self);
229 frame_grab_client(self->frame);
231 /* we've grabbed everything and set everything that we need to at mapping
235 /* per-app settings override stuff from client_get_all, and return the
236 settings for other uses too. the returned settings is a shallow copy,
237 that needs to be freed with g_free(). */
238 settings = client_get_settings_state(self);
239 /* the session should get the last say though */
240 client_restore_session_state(self);
242 /* tell startup notification that this app started */
243 launch_time = sn_app_started(self->startup_id, self->class, self->name);
245 /* do this after we have a frame.. it uses the frame to help determine the
246 WM_STATE to apply. */
247 client_change_state(self);
249 /* add ourselves to the focus order */
250 focus_order_add_new(self);
252 /* do this to add ourselves to the stacking list in a non-intrusive way */
253 client_calc_layer(self);
255 /* focus the new window? */
256 if (ob_state() != OB_STATE_STARTING &&
257 (!self->session || self->session->focused) &&
258 /* this means focus=true for window is same as config_focus_new=true */
259 ((config_focus_new || (settings && settings->focus == 1)) ||
260 client_search_focus_tree_full(self)) &&
261 /* this checks for focus=false for the window */
262 (!settings || settings->focus != 0) &&
263 focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
268 /* remove the client's border */
269 XSetWindowBorderWidth(obt_display, self->window, 0);
271 /* adjust the frame to the client's size before showing or placing
273 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
274 frame_adjust_client_area(self->frame);
276 /* where the frame was placed is where the window was originally */
278 monitor = screen_physical_area_monitor(screen_find_monitor(&place));
280 /* figure out placement for the window if the window is new */
281 if (ob_state() == OB_STATE_RUNNING) {
282 ob_debug("Positioned: %s @ %d %d",
283 (!self->positioned ? "no" :
284 (self->positioned == PPosition ? "program specified" :
285 (self->positioned == USPosition ? "user specified" :
286 (self->positioned == (PPosition | USPosition) ?
287 "program + user specified" :
288 "BADNESS !?")))), place.x, place.y);
290 ob_debug("Sized: %s @ %d %d",
291 (!self->sized ? "no" :
292 (self->sized == PSize ? "program specified" :
293 (self->sized == USSize ? "user specified" :
294 (self->sized == (PSize | USSize) ?
295 "program + user specified" :
296 "BADNESS !?")))), place.width, place.height);
298 /* splash screens are also returned as TRUE for transient,
299 and so will be forced on screen below */
300 transient = place_client(self, &place.x, &place.y, settings);
302 /* make sure the window is visible. */
303 client_find_onscreen(self, &place.x, &place.y,
304 place.width, place.height,
305 /* non-normal clients has less rules, and
306 windows that are being restored from a
307 session do also. we can assume you want
308 it back where you saved it. Clients saying
309 they placed themselves are subjected to
310 harder rules, ones that are placed by
311 place.c or by the user are allowed partially
312 off-screen and on xinerama divides (ie,
313 it is up to the placement routines to avoid
314 the xinerama divides)
316 splash screens get "transient" set to TRUE by
317 the place_client call
319 ob_state() == OB_STATE_RUNNING &&
321 (!((self->positioned & USPosition) ||
322 (settings && settings->pos_given)) &&
323 client_normal(self) &&
325 /* don't move oldschool fullscreen windows to
326 fit inside the struts (fixes Acroread, which
327 makes its fullscreen window fit the screen
328 but it is not USSize'd or USPosition'd) */
329 !(self->decorations == 0 &&
330 RECT_EQUAL(place, *monitor)))));
333 /* if the window isn't user-sized, then make it fit inside
334 the visible screen area on its monitor. Use basically the same rules
335 for forcing the window on screen in the client_find_onscreen call.
337 do this after place_client, it chooses the monitor!
339 splash screens get "transient" set to TRUE by
340 the place_client call
342 if (ob_state() == OB_STATE_RUNNING &&
344 (!(self->sized & USSize || self->positioned & USPosition) &&
345 client_normal(self) &&
347 /* don't shrink oldschool fullscreen windows to fit inside the
348 struts (fixes Acroread, which makes its fullscreen window
349 fit the screen but it is not USSize'd or USPosition'd) */
350 !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
352 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
354 /* get the size of the frame */
355 place.width += self->frame->size.left + self->frame->size.right;
356 place.height += self->frame->size.top + self->frame->size.bottom;
358 /* fit the window inside the area */
359 place.width = MIN(place.width, a->width);
360 place.height = MIN(place.height, a->height);
362 ob_debug("setting window size to %dx%d", place.width, place.height);
364 /* get the size of the client back */
365 place.width -= self->frame->size.left + self->frame->size.right;
366 place.height -= self->frame->size.top + self->frame->size.bottom;
371 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
372 "some restrictions may apply",
373 self->window, place.x, place.y, place.width, place.height);
375 ob_debug(" but session requested %d, %d %d x %d instead, "
377 self->session->x, self->session->y,
378 self->session->w, self->session->h);
380 /* do this after the window is placed, so the premax/prefullscreen numbers
383 this also places the window
385 client_apply_startup_state(self, place.x, place.y,
386 place.width, place.height);
391 ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
392 activate ? "yes" : "no");
394 gboolean raise = FALSE;
396 /* This is focus stealing prevention */
397 ob_debug_type(OB_DEBUG_FOCUS,
398 "Want to focus new window 0x%x at time %u "
399 "launched at %u (last user interaction time %u)",
400 self->window, map_time, launch_time,
401 event_last_user_time);
403 if (menu_frame_visible || moveresize_in_progress) {
406 ob_debug_type(OB_DEBUG_FOCUS,
407 "Not focusing the window because the user is inside "
408 "an Openbox menu or is move/resizing a window and "
409 "we don't want to interrupt them");
412 /* if it's on another desktop */
413 else if (!(self->desktop == screen_desktop ||
414 self->desktop == DESKTOP_ALL) &&
415 /* the timestamp is from before you changed desktops */
416 launch_time && screen_desktop_user_time &&
417 !event_time_after(launch_time, screen_desktop_user_time))
421 ob_debug_type(OB_DEBUG_FOCUS,
422 "Not focusing the window because its on another "
425 /* If something is focused, and it's not our relative... */
426 else if (focus_client && client_search_focus_tree_full(self) == NULL &&
427 client_search_focus_group_full(self) == NULL)
429 /* If the user is working in another window right now, then don't
431 if (event_last_user_time && launch_time &&
432 event_time_after(event_last_user_time, launch_time) &&
433 event_last_user_time != launch_time &&
434 event_time_after(event_last_user_time,
435 map_time - OB_EVENT_USER_TIME_DELAY))
438 ob_debug_type(OB_DEBUG_FOCUS,
439 "Not focusing the window because the user is "
440 "working in another window");
442 /* If its a transient (and its parents aren't focused) */
443 else if (client_has_parent(self)) {
445 ob_debug_type(OB_DEBUG_FOCUS,
446 "Not focusing the window because it is a "
447 "transient, and its relatives aren't focused");
449 /* Don't steal focus from globally active clients.
450 I stole this idea from KWin. It seems nice.
452 else if (!(focus_client->can_focus ||
453 focus_client->focus_notify))
456 ob_debug_type(OB_DEBUG_FOCUS,
457 "Not focusing the window because a globally "
458 "active client has focus");
460 /* Don't move focus if it's not going to go to this window
462 else if (client_focus_target(self) != self) {
465 ob_debug_type(OB_DEBUG_FOCUS,
466 "Not focusing the window because another window "
467 "would get the focus anyway");
469 else if (!(self->desktop == screen_desktop ||
470 self->desktop == DESKTOP_ALL))
474 ob_debug_type(OB_DEBUG_FOCUS,
475 "Not focusing the window because it is on "
476 "another desktop and no relatives are focused ");
481 ob_debug_type(OB_DEBUG_FOCUS,
482 "Focus stealing prevention activated for %s at "
483 "time %u (last user interactioon time %u)",
484 self->title, map_time, event_last_user_time);
485 /* if the client isn't focused, then hilite it so the user
487 client_hilite(self, TRUE);
488 /* we may want to raise it even tho we're not activating it */
489 if (raise && !client_restore_session_stacking(self))
490 stacking_raise(CLIENT_AS_WINDOW(self));
494 /* This may look rather odd. Well it's because new windows are added
495 to the stacking order non-intrusively. If we're not going to focus
496 the new window or hilite it, then we raise it to the top. This will
497 take affect for things that don't get focused like splash screens.
498 Also if you don't have focus_new enabled, then it's going to get
499 raised to the top. Legacy begets legacy I guess?
501 if (!client_restore_session_stacking(self))
502 stacking_raise(CLIENT_AS_WINDOW(self));
505 mouse_grab_for_client(self, TRUE);
507 /* this has to happen before we try focus the window, but we want it to
508 happen after the client's stacking has been determined or it looks bad
512 if (!config_focus_under_mouse)
513 ignore_start = event_start_ignore_all_enters();
517 if (!config_focus_under_mouse)
518 event_end_ignore_all_enters(ignore_start);
522 gboolean stacked = client_restore_session_stacking(self);
523 client_present(self, FALSE, !stacked, TRUE);
526 /* add to client list/map */
527 client_list = g_list_append(client_list, self);
528 window_add(&self->window, CLIENT_AS_WINDOW(self));
530 /* this has to happen after we're in the client_list */
531 if (STRUT_EXISTS(self->strut))
532 screen_update_areas();
534 /* update the list hints */
537 /* watch for when the application stops responding. only do this for
538 normal windows, i.e. windows which have titlebars and close buttons
539 and things like that.
540 we don't need to stop pinging on unmanage, because it will be handled
541 automatically by the destroy callback!
543 if (self->ping && client_normal(self))
544 ping_start(self, client_ping_event);
546 /* free the ObAppSettings shallow copy */
549 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
550 window, self->frame->window, self->class);
554 ObClient *client_fake_manage(Window window)
557 ObAppSettings *settings;
559 ob_debug("Pretend-managing window: %lx", window);
561 /* do this minimal stuff to figure out the client's decorations */
563 self = g_new0(ObClient, 1);
564 self->window = window;
566 client_get_all(self, FALSE);
567 /* per-app settings override stuff, and return the settings for other
568 uses too. this returns a shallow copy that needs to be freed */
569 settings = client_get_settings_state(self);
571 client_setup_decor_and_functions(self, FALSE);
573 /* create the decoration frame for the client window and adjust its size */
574 self->frame = frame_new(self);
575 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
577 ob_debug("gave extents left %d right %d top %d bottom %d",
578 self->frame->size.left, self->frame->size.right,
579 self->frame->size.top, self->frame->size.bottom);
581 /* free the ObAppSettings shallow copy */
587 void client_unmanage_all(void)
589 while (client_list != NULL)
590 client_unmanage(client_list->data);
593 void client_unmanage(ObClient *self)
599 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
600 self->window, self->frame->window,
601 self->class, self->title ? self->title : "");
603 g_assert(self != NULL);
605 /* we dont want events no more. do this before hiding the frame so we
606 don't generate more events */
607 XSelectInput(obt_display, self->window, NoEventMask);
609 /* ignore enter events from the unmap so it doesnt mess with the focus */
610 if (!config_focus_under_mouse)
611 ignore_start = event_start_ignore_all_enters();
613 frame_hide(self->frame);
614 /* flush to send the hide to the server quickly */
617 if (!config_focus_under_mouse)
618 event_end_ignore_all_enters(ignore_start);
620 mouse_grab_for_client(self, FALSE);
622 /* remove the window from our save set */
624 XChangeSaveSet(obt_display, self->window, SetModeDelete);
626 /* update the focus lists */
627 focus_order_remove(self);
628 if (client_focused(self)) {
629 /* don't leave an invalid focus_client */
633 client_list = g_list_remove(client_list, self);
634 stacking_remove(self);
635 window_remove(self->window);
637 /* once the client is out of the list, update the struts to remove its
639 if (STRUT_EXISTS(self->strut))
640 screen_update_areas();
642 client_call_notifies(self, client_destroy_notifies);
644 /* tell our parent(s) that we're gone */
645 for (it = self->parents; it; it = g_slist_next(it))
646 ((ObClient*)it->data)->transients =
647 g_slist_remove(((ObClient*)it->data)->transients,self);
649 /* tell our transients that we're gone */
650 for (it = self->transients; it; it = g_slist_next(it)) {
651 ((ObClient*)it->data)->parents =
652 g_slist_remove(((ObClient*)it->data)->parents, self);
653 /* we could be keeping our children in a higher layer */
654 client_calc_layer(it->data);
657 /* remove from its group */
659 group_remove(self->group, self);
663 /* restore the window's original geometry so it is not lost */
669 if (self->fullscreen)
670 a = self->pre_fullscreen_area;
671 else if (self->max_horz || self->max_vert) {
672 if (self->max_horz) {
673 a.x = self->pre_max_area.x;
674 a.width = self->pre_max_area.width;
676 if (self->max_vert) {
677 a.y = self->pre_max_area.y;
678 a.height = self->pre_max_area.height;
682 self->fullscreen = self->max_horz = self->max_vert = FALSE;
683 /* let it be moved and resized no matter what */
684 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
685 self->decorations = 0; /* unmanaged windows have no decor */
687 /* give the client its border back */
688 XSetWindowBorderWidth(obt_display, self->window, self->border_width);
690 client_move_resize(self, a.x, a.y, a.width, a.height);
693 /* reparent the window out of the frame, and free the frame */
694 frame_release_client(self->frame);
695 frame_free(self->frame);
698 if (ob_state() != OB_STATE_EXITING) {
699 /* these values should not be persisted across a window
701 OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
702 OBT_PROP_ERASE(self->window, NET_WM_STATE);
703 OBT_PROP_ERASE(self->window, WM_STATE);
705 /* if we're left in an unmapped state, the client wont be mapped.
706 this is bad, since we will no longer be managing the window on
708 XMapWindow(obt_display, self->window);
711 /* these should not be left on the window ever. other window managers
712 don't necessarily use them and it will mess them up (like compiz) */
713 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
714 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
716 /* update the list hints */
719 ob_debug("Unmanaged window 0x%lx", self->window);
721 /* free all data allocated in the client struct */
722 g_slist_free(self->transients);
723 for (j = 0; j < self->nicons; ++j)
724 g_free(self->icons[j].data);
725 if (self->nicons > 0)
727 g_free(self->startup_id);
728 g_free(self->wm_command);
730 g_free(self->icon_title);
734 g_free(self->client_machine);
735 g_free(self->sm_client_id);
739 void client_fake_unmanage(ObClient *self)
741 /* this is all that got allocated to get the decorations */
743 frame_free(self->frame);
747 /*! Returns a new structure containing the per-app settings for this client.
748 The returned structure needs to be freed with g_free. */
749 static ObAppSettings *client_get_settings_state(ObClient *self)
751 ObAppSettings *settings;
754 settings = config_create_app_settings();
756 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
757 ObAppSettings *app = it->data;
758 gboolean match = TRUE;
760 g_assert(app->name != NULL || app->class != NULL);
762 /* we know that either name or class is not NULL so it will have to
763 match to use the rule */
765 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
767 else if (app->class &&
768 !g_pattern_match(app->class,
769 strlen(self->class), self->class, NULL))
771 else if (app->role &&
772 !g_pattern_match(app->role,
773 strlen(self->role), self->role, NULL))
777 ob_debug("Window matching: %s", app->name);
779 /* copy the settings to our struct, overriding the existing
780 settings if they are not defaults */
781 config_app_settings_copy_non_defaults(app, settings);
785 if (settings->shade != -1)
786 self->shaded = !!settings->shade;
787 if (settings->decor != -1)
788 self->undecorated = !settings->decor;
789 if (settings->iconic != -1)
790 self->iconic = !!settings->iconic;
791 if (settings->skip_pager != -1)
792 self->skip_pager = !!settings->skip_pager;
793 if (settings->skip_taskbar != -1)
794 self->skip_taskbar = !!settings->skip_taskbar;
796 if (settings->max_vert != -1)
797 self->max_vert = !!settings->max_vert;
798 if (settings->max_horz != -1)
799 self->max_horz = !!settings->max_horz;
801 if (settings->fullscreen != -1)
802 self->fullscreen = !!settings->fullscreen;
804 if (settings->desktop) {
805 if (settings->desktop == DESKTOP_ALL)
806 self->desktop = settings->desktop;
807 else if (settings->desktop > 0 &&
808 settings->desktop <= screen_num_desktops)
809 self->desktop = settings->desktop - 1;
812 if (settings->layer == -1) {
816 else if (settings->layer == 0) {
820 else if (settings->layer == 1) {
827 static void client_restore_session_state(ObClient *self)
831 ob_debug_type(OB_DEBUG_SM,
832 "Restore session for client %s", self->title);
834 if (!(it = session_state_find(self))) {
835 ob_debug_type(OB_DEBUG_SM,
836 "Session data not found for client %s", self->title);
840 self->session = it->data;
842 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
845 RECT_SET_POINT(self->area, self->session->x, self->session->y);
846 self->positioned = USPosition;
847 self->sized = USSize;
848 if (self->session->w > 0)
849 self->area.width = self->session->w;
850 if (self->session->h > 0)
851 self->area.height = self->session->h;
852 XResizeWindow(obt_display, self->window,
853 self->area.width, self->area.height);
855 self->desktop = (self->session->desktop == DESKTOP_ALL ?
856 self->session->desktop :
857 MIN(screen_num_desktops - 1, self->session->desktop));
858 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
860 self->shaded = self->session->shaded;
861 self->iconic = self->session->iconic;
862 self->skip_pager = self->session->skip_pager;
863 self->skip_taskbar = self->session->skip_taskbar;
864 self->fullscreen = self->session->fullscreen;
865 self->above = self->session->above;
866 self->below = self->session->below;
867 self->max_horz = self->session->max_horz;
868 self->max_vert = self->session->max_vert;
869 self->undecorated = self->session->undecorated;
872 static gboolean client_restore_session_stacking(ObClient *self)
876 if (!self->session) return FALSE;
878 mypos = g_list_find(session_saved_state, self->session);
879 if (!mypos) return FALSE;
881 /* start above me and look for the first client */
882 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
885 for (cit = client_list; cit; cit = g_list_next(cit)) {
886 ObClient *c = cit->data;
887 /* found a client that was in the session, so go below it */
888 if (c->session == it->data) {
889 stacking_below(CLIENT_AS_WINDOW(self),
890 CLIENT_AS_WINDOW(cit->data));
898 void client_move_onscreen(ObClient *self, gboolean rude)
900 gint x = self->area.x;
901 gint y = self->area.y;
902 if (client_find_onscreen(self, &x, &y,
904 self->area.height, rude)) {
905 client_move(self, x, y);
909 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
912 gint ox = *x, oy = *y;
913 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
919 RECT_SET(desired, *x, *y, w, h);
920 frame_rect_to_frame(self->frame, &desired);
922 /* get where the frame would be */
923 frame_client_gravity(self->frame, x, y);
925 /* get the requested size of the window with decorations */
926 fw = self->frame->size.left + w + self->frame->size.right;
927 fh = self->frame->size.top + h + self->frame->size.bottom;
929 /* If rudeness wasn't requested, then still be rude in a given direction
930 if the client is not moving, only resizing in that direction */
932 Point oldtl, oldtr, oldbl, oldbr;
933 Point newtl, newtr, newbl, newbr;
934 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
936 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
937 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
938 self->frame->area.y + self->frame->area.height - 1);
939 POINT_SET(oldtr, oldbr.x, oldtl.y);
940 POINT_SET(oldbl, oldtl.x, oldbr.y);
942 POINT_SET(newtl, *x, *y);
943 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
944 POINT_SET(newtr, newbr.x, newtl.y);
945 POINT_SET(newbl, newtl.x, newbr.y);
947 /* is it moving or just resizing from some corner? */
948 stationary_l = oldtl.x == newtl.x;
949 stationary_r = oldtr.x == newtr.x;
950 stationary_t = oldtl.y == newtl.y;
951 stationary_b = oldbl.y == newbl.y;
953 /* if left edge is growing and didnt move right edge */
954 if (stationary_r && newtl.x < oldtl.x)
956 /* if right edge is growing and didnt move left edge */
957 if (stationary_l && newtr.x > oldtr.x)
959 /* if top edge is growing and didnt move bottom edge */
960 if (stationary_b && newtl.y < oldtl.y)
962 /* if bottom edge is growing and didnt move top edge */
963 if (stationary_t && newbl.y > oldbl.y)
967 /* we iterate through every monitor that the window is at least partially
968 on, to make sure it is obeying the rules on them all
970 if the window does not appear on any monitors, then use the first one
973 for (i = 0; i < screen_num_monitors; ++i) {
976 if (!screen_physical_area_monitor_contains(i, &desired)) {
977 if (i < screen_num_monitors - 1 || found_mon)
980 /* the window is not inside any monitor! so just use the first
982 a = screen_area(self->desktop, 0, NULL);
985 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
988 /* This makes sure windows aren't entirely outside of the screen so you
989 can't see them at all.
990 It makes sure 10% of the window is on the screen at least. At don't
991 let it move itself off the top of the screen, which would hide the
992 titlebar on you. (The user can still do this if they want too, it's
993 only limiting the application.
995 if (client_normal(self)) {
996 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
997 *x = a->x + a->width - fw/10;
998 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
999 *y = a->y + a->height - fh/10;
1000 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1001 *x = a->x - fw*9/10;
1002 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1003 *y = a->y - fh*9/10;
1006 /* This here doesn't let windows even a pixel outside the
1007 struts/screen. When called from client_manage, programs placing
1008 themselves are forced completely onscreen, while things like
1009 xterm -geometry resolution-width/2 will work fine. Trying to
1010 place it completely offscreen will be handled in the above code.
1011 Sorry for this confused comment, i am tired. */
1012 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1013 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1014 *x = a->x + MAX(0, a->width - fw);
1016 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1017 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1018 *y = a->y + MAX(0, a->height - fh);
1023 /* get where the client should be */
1024 frame_frame_gravity(self->frame, x, y);
1026 return ox != *x || oy != *y;
1029 static void client_get_all(ObClient *self, gboolean real)
1031 /* this is needed for the frame to set itself up */
1032 client_get_area(self);
1034 /* these things can change the decor and functions of the window */
1036 client_get_mwm_hints(self);
1037 /* this can change the mwmhints for special cases */
1038 client_get_type_and_transientness(self);
1039 client_get_state(self);
1040 client_update_normal_hints(self);
1042 /* get the session related properties, these can change decorations
1043 from per-app settings */
1044 client_get_session_ids(self);
1046 /* now we got everything that can affect the decorations */
1050 /* get this early so we have it for debugging */
1051 client_update_title(self);
1053 client_update_protocols(self);
1055 client_update_wmhints(self);
1056 /* this may have already been called from client_update_wmhints */
1057 if (!self->parents && !self->transient_for_group)
1058 client_update_transient_for(self);
1060 client_get_startup_id(self);
1061 client_get_desktop(self);/* uses transient data/group/startup id if a
1062 desktop is not specified */
1063 client_get_shaped(self);
1066 /* a couple type-based defaults for new windows */
1068 /* this makes sure that these windows appear on all desktops */
1069 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1070 self->desktop = DESKTOP_ALL;
1074 client_update_sync_request_counter(self);
1077 client_get_colormap(self);
1078 client_update_strut(self);
1079 client_update_icons(self);
1080 client_update_icon_geometry(self);
1083 static void client_get_startup_id(ObClient *self)
1085 if (!(OBT_PROP_GETS(self->window, NET_STARTUP_ID, utf8,
1086 &self->startup_id)))
1088 OBT_PROP_GETS(self->group->leader,
1089 NET_STARTUP_ID, utf8, &self->startup_id);
1092 static void client_get_area(ObClient *self)
1094 XWindowAttributes wattrib;
1097 ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
1098 g_assert(ret != BadWindow);
1100 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1101 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1102 self->border_width = wattrib.border_width;
1104 ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
1105 wattrib.width, wattrib.height, wattrib.border_width);
1108 static void client_get_desktop(ObClient *self)
1110 guint32 d = screen_num_desktops; /* an always-invalid value */
1112 if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
1113 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1114 self->desktop = screen_num_desktops - 1;
1117 ob_debug("client requested desktop 0x%x", self->desktop);
1120 gboolean first = TRUE;
1121 guint all = screen_num_desktops; /* not a valid value */
1123 /* if they are all on one desktop, then open it on the
1125 for (it = self->parents; it; it = g_slist_next(it)) {
1126 ObClient *c = it->data;
1128 if (c->desktop == DESKTOP_ALL) continue;
1134 else if (all != c->desktop)
1135 all = screen_num_desktops; /* make it invalid */
1137 if (all != screen_num_desktops) {
1138 self->desktop = all;
1140 ob_debug("client desktop set from parents: 0x%x",
1143 /* try get from the startup-notification protocol */
1144 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1145 if (self->desktop >= screen_num_desktops &&
1146 self->desktop != DESKTOP_ALL)
1147 self->desktop = screen_num_desktops - 1;
1148 ob_debug("client desktop set from startup-notification: 0x%x",
1151 /* defaults to the current desktop */
1153 self->desktop = screen_desktop;
1154 ob_debug("client desktop set to the current desktop: %d",
1160 static void client_get_state(ObClient *self)
1165 if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
1167 for (i = 0; i < num; ++i) {
1168 if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1170 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1171 self->shaded = TRUE;
1172 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1173 self->iconic = TRUE;
1174 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1175 self->skip_taskbar = TRUE;
1176 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1177 self->skip_pager = TRUE;
1178 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1179 self->fullscreen = TRUE;
1180 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
1181 self->max_vert = TRUE;
1182 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
1183 self->max_horz = TRUE;
1184 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1186 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
1188 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
1189 self->demands_attention = TRUE;
1190 else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
1191 self->undecorated = TRUE;
1198 static void client_get_shaped(ObClient *self)
1200 self->shaped = FALSE;
1202 if (obt_display_extension_shape) {
1207 XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
1209 XShapeQueryExtents(obt_display, self->window, &s, &foo,
1210 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1212 self->shaped = (s != 0);
1217 void client_update_transient_for(ObClient *self)
1220 ObClient *target = NULL;
1221 gboolean trangroup = FALSE;
1223 if (XGetTransientForHint(obt_display, self->window, &t)) {
1224 if (t != self->window) { /* cant be transient to itself! */
1225 ObWindow *tw = window_find(t);
1226 /* if this happens then we need to check for it*/
1227 g_assert(tw != CLIENT_AS_WINDOW(self));
1228 if (tw && WINDOW_IS_CLIENT(tw)) {
1229 /* watch out for windows with a parent that is something
1230 different, like a dockapp for example */
1231 target = WINDOW_AS_CLIENT(tw);
1235 /* Setting the transient_for to Root is actually illegal, however
1236 applications from time have done this to specify transient for
1238 if (!target && self->group && t == obt_root(ob_screen))
1240 } else if (self->group && self->transient)
1243 client_update_transient_tree(self, self->group, self->group,
1244 self->transient_for_group, trangroup,
1245 client_direct_parent(self), target);
1246 self->transient_for_group = trangroup;
1250 static void client_update_transient_tree(ObClient *self,
1251 ObGroup *oldgroup, ObGroup *newgroup,
1252 gboolean oldgtran, gboolean newgtran,
1253 ObClient* oldparent,
1254 ObClient *newparent)
1259 g_assert(!oldgtran || oldgroup);
1260 g_assert(!newgtran || newgroup);
1261 g_assert((!oldgtran && !oldparent) ||
1262 (oldgtran && !oldparent) ||
1263 (!oldgtran && oldparent));
1264 g_assert((!newgtran && !newparent) ||
1265 (newgtran && !newparent) ||
1266 (!newgtran && newparent));
1269 Group transient windows are not allowed to have other group
1270 transient windows as their children.
1274 /* No change has occured */
1275 if (oldgroup == newgroup &&
1276 oldgtran == newgtran &&
1277 oldparent == newparent) return;
1279 /** Remove the client from the transient tree **/
1281 for (it = self->transients; it; it = next) {
1282 next = g_slist_next(it);
1284 self->transients = g_slist_delete_link(self->transients, it);
1285 c->parents = g_slist_remove(c->parents, self);
1287 for (it = self->parents; it; it = next) {
1288 next = g_slist_next(it);
1290 self->parents = g_slist_delete_link(self->parents, it);
1291 c->transients = g_slist_remove(c->transients, self);
1294 /** Re-add the client to the transient tree **/
1296 /* If we're transient for a group then we need to add ourselves to all our
1299 for (it = newgroup->members; it; it = g_slist_next(it)) {
1302 !client_search_top_direct_parent(c)->transient_for_group &&
1305 c->transients = g_slist_prepend(c->transients, self);
1306 self->parents = g_slist_prepend(self->parents, c);
1311 /* If we are now transient for a single window we need to add ourselves to
1314 WARNING: Cyclical transient ness is possible if two windows are
1315 transient for eachother.
1317 else if (newparent &&
1318 /* don't make ourself its child if it is already our child */
1319 !client_is_direct_child(self, newparent) &&
1320 client_normal(newparent))
1322 newparent->transients = g_slist_prepend(newparent->transients, self);
1323 self->parents = g_slist_prepend(self->parents, newparent);
1326 /* Add any group transient windows to our children. But if we're transient
1327 for the group, then other group transients are not our children.
1329 WARNING: Cyclical transient-ness is possible. For e.g. if:
1330 A is transient for the group
1331 B is transient for A
1332 C is transient for B
1333 A can't be transient for C or we have a cycle
1335 if (!newgtran && newgroup &&
1337 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1338 client_normal(self))
1340 for (it = newgroup->members; it; it = g_slist_next(it)) {
1342 if (c != self && c->transient_for_group &&
1343 /* Don't make it our child if it is already our parent */
1344 !client_is_direct_child(c, self))
1346 self->transients = g_slist_prepend(self->transients, c);
1347 c->parents = g_slist_prepend(c->parents, self);
1352 /** If we change our group transient-ness, our children change their
1353 effect group transient-ness, which affects how they relate to other
1356 for (it = self->transients; it; it = g_slist_next(it)) {
1358 if (!c->transient_for_group)
1359 client_update_transient_tree(c, c->group, c->group,
1360 c->transient_for_group,
1361 c->transient_for_group,
1362 client_direct_parent(c),
1363 client_direct_parent(c));
1367 static void client_get_mwm_hints(ObClient *self)
1372 self->mwmhints.flags = 0; /* default to none */
1374 if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1376 if (num >= OB_MWM_ELEMENTS) {
1377 self->mwmhints.flags = hints[0];
1378 self->mwmhints.functions = hints[1];
1379 self->mwmhints.decorations = hints[2];
1385 void client_get_type_and_transientness(ObClient *self)
1392 self->transient = FALSE;
1394 if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1395 /* use the first value that we know about in the array */
1396 for (i = 0; i < num; ++i) {
1397 if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1398 self->type = OB_CLIENT_TYPE_DESKTOP;
1399 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1400 self->type = OB_CLIENT_TYPE_DOCK;
1401 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1402 self->type = OB_CLIENT_TYPE_TOOLBAR;
1403 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1404 self->type = OB_CLIENT_TYPE_MENU;
1405 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1406 self->type = OB_CLIENT_TYPE_UTILITY;
1407 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1408 self->type = OB_CLIENT_TYPE_SPLASH;
1409 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1410 self->type = OB_CLIENT_TYPE_DIALOG;
1411 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1412 self->type = OB_CLIENT_TYPE_NORMAL;
1413 else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1415 /* prevent this window from getting any decor or
1417 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1418 OB_MWM_FLAG_DECORATIONS);
1419 self->mwmhints.decorations = 0;
1420 self->mwmhints.functions = 0;
1422 if (self->type != (ObClientType) -1)
1423 break; /* grab the first legit type */
1428 if (XGetTransientForHint(obt_display, self->window, &t))
1429 self->transient = TRUE;
1431 if (self->type == (ObClientType) -1) {
1432 /*the window type hint was not set, which means we either classify
1433 ourself as a normal window or a dialog, depending on if we are a
1435 if (self->transient)
1436 self->type = OB_CLIENT_TYPE_DIALOG;
1438 self->type = OB_CLIENT_TYPE_NORMAL;
1441 /* then, based on our type, we can update our transientness.. */
1442 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1443 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1444 self->type == OB_CLIENT_TYPE_MENU ||
1445 self->type == OB_CLIENT_TYPE_UTILITY)
1447 self->transient = TRUE;
1451 void client_update_protocols(ObClient *self)
1456 self->focus_notify = FALSE;
1457 self->delete_window = FALSE;
1459 if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1460 for (i = 0; i < num_ret; ++i) {
1461 if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1462 /* this means we can request the window to close */
1463 self->delete_window = TRUE;
1464 else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1465 /* if this protocol is requested, then the window will be
1466 notified whenever we want it to receive focus */
1467 self->focus_notify = TRUE;
1468 else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1469 /* if this protocol is requested, then the window will allow
1470 pings to determine if it is still alive */
1473 else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1474 /* if this protocol is requested, then resizing the
1475 window will be synchronized between the frame and the
1477 self->sync_request = TRUE;
1485 void client_update_sync_request_counter(ObClient *self)
1489 if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1491 self->sync_counter = i;
1493 self->sync_counter = None;
1497 static void client_get_colormap(ObClient *self)
1499 XWindowAttributes wa;
1501 if (XGetWindowAttributes(obt_display, self->window, &wa))
1502 client_update_colormap(self, wa.colormap);
1505 void client_update_colormap(ObClient *self, Colormap colormap)
1507 if (colormap == self->colormap) return;
1509 ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1511 if (client_focused(self)) {
1512 screen_install_colormap(self, FALSE); /* uninstall old one */
1513 self->colormap = colormap;
1514 screen_install_colormap(self, FALSE); /* install new one */
1516 self->colormap = colormap;
1519 void client_update_normal_hints(ObClient *self)
1525 self->min_ratio = 0.0f;
1526 self->max_ratio = 0.0f;
1527 SIZE_SET(self->size_inc, 1, 1);
1528 SIZE_SET(self->base_size, 0, 0);
1529 SIZE_SET(self->min_size, 0, 0);
1530 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1532 /* get the hints from the window */
1533 if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
1534 /* normal windows can't request placement! har har
1535 if (!client_normal(self))
1537 self->positioned = (size.flags & (PPosition|USPosition));
1538 self->sized = (size.flags & (PSize|USSize));
1540 if (size.flags & PWinGravity)
1541 self->gravity = size.win_gravity;
1543 if (size.flags & PAspect) {
1544 if (size.min_aspect.y)
1546 (gfloat) size.min_aspect.x / size.min_aspect.y;
1547 if (size.max_aspect.y)
1549 (gfloat) size.max_aspect.x / size.max_aspect.y;
1552 if (size.flags & PMinSize)
1553 SIZE_SET(self->min_size, size.min_width, size.min_height);
1555 if (size.flags & PMaxSize)
1556 SIZE_SET(self->max_size, size.max_width, size.max_height);
1558 if (size.flags & PBaseSize)
1559 SIZE_SET(self->base_size, size.base_width, size.base_height);
1561 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1562 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1564 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1565 self->min_size.width, self->min_size.height,
1566 self->max_size.width, self->max_size.height);
1567 ob_debug("size inc (%d %d) base size (%d %d)",
1568 self->size_inc.width, self->size_inc.height,
1569 self->base_size.width, self->base_size.height);
1572 ob_debug("Normal hints: not set");
1575 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1577 /* start with everything (cept fullscreen) */
1579 (OB_FRAME_DECOR_TITLEBAR |
1580 OB_FRAME_DECOR_HANDLE |
1581 OB_FRAME_DECOR_GRIPS |
1582 OB_FRAME_DECOR_BORDER |
1583 OB_FRAME_DECOR_ICON |
1584 OB_FRAME_DECOR_ALLDESKTOPS |
1585 OB_FRAME_DECOR_ICONIFY |
1586 OB_FRAME_DECOR_MAXIMIZE |
1587 OB_FRAME_DECOR_SHADE |
1588 OB_FRAME_DECOR_CLOSE);
1590 (OB_CLIENT_FUNC_RESIZE |
1591 OB_CLIENT_FUNC_MOVE |
1592 OB_CLIENT_FUNC_ICONIFY |
1593 OB_CLIENT_FUNC_MAXIMIZE |
1594 OB_CLIENT_FUNC_SHADE |
1595 OB_CLIENT_FUNC_CLOSE |
1596 OB_CLIENT_FUNC_BELOW |
1597 OB_CLIENT_FUNC_ABOVE |
1598 OB_CLIENT_FUNC_UNDECORATE);
1600 if (!(self->min_size.width < self->max_size.width ||
1601 self->min_size.height < self->max_size.height))
1602 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1604 switch (self->type) {
1605 case OB_CLIENT_TYPE_NORMAL:
1606 /* normal windows retain all of the possible decorations and
1607 functionality, and can be fullscreen */
1608 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1611 case OB_CLIENT_TYPE_DIALOG:
1612 /* sometimes apps make dialog windows fullscreen for some reason (for
1613 e.g. kpdf does this..) */
1614 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1617 case OB_CLIENT_TYPE_UTILITY:
1618 /* these windows don't have anything added or removed by default */
1621 case OB_CLIENT_TYPE_MENU:
1622 case OB_CLIENT_TYPE_TOOLBAR:
1623 /* these windows can't iconify or maximize */
1624 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1625 OB_FRAME_DECOR_MAXIMIZE);
1626 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1627 OB_CLIENT_FUNC_MAXIMIZE);
1630 case OB_CLIENT_TYPE_SPLASH:
1631 /* these don't get get any decorations, and the only thing you can
1632 do with them is move them */
1633 self->decorations = 0;
1634 self->functions = OB_CLIENT_FUNC_MOVE;
1637 case OB_CLIENT_TYPE_DESKTOP:
1638 /* these windows are not manipulated by the window manager */
1639 self->decorations = 0;
1640 self->functions = 0;
1643 case OB_CLIENT_TYPE_DOCK:
1644 /* these windows are not manipulated by the window manager, but they
1645 can set below layer which has a special meaning */
1646 self->decorations = 0;
1647 self->functions = OB_CLIENT_FUNC_BELOW;
1651 /* Mwm Hints are applied subtractively to what has already been chosen for
1652 decor and functionality */
1653 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1654 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1655 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1656 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1658 /* if the mwm hints request no handle or title, then all
1659 decorations are disabled, but keep the border if that's
1661 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1662 self->decorations = OB_FRAME_DECOR_BORDER;
1664 self->decorations = 0;
1669 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1670 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1671 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1672 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1673 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1674 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1675 /* dont let mwm hints kill any buttons
1676 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1677 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1678 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1679 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1681 /* dont let mwm hints kill the close button
1682 if (! (self->mwmhints.functions & MwmFunc_Close))
1683 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1687 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1688 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1689 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1690 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1691 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1692 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1694 /* can't maximize without moving/resizing */
1695 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1696 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1697 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1698 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1699 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1702 if (self->max_horz && self->max_vert) {
1703 /* you can't resize fully maximized windows */
1704 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1705 /* kill the handle on fully maxed windows */
1706 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1709 /* If there are no decorations to remove, don't allow the user to try
1711 if (self->decorations == 0)
1712 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1714 /* finally, the user can have requested no decorations, which overrides
1715 everything (but doesnt give it a border if it doesnt have one) */
1716 if (self->undecorated)
1717 self->decorations = 0;
1719 /* if we don't have a titlebar, then we cannot shade! */
1720 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1721 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1723 /* now we need to check against rules for the client's current state */
1724 if (self->fullscreen) {
1725 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1726 OB_CLIENT_FUNC_FULLSCREEN |
1727 OB_CLIENT_FUNC_ICONIFY);
1728 self->decorations = 0;
1731 client_change_allowed_actions(self);
1734 /* force reconfigure to make sure decorations are updated */
1735 client_reconfigure(self, TRUE);
1738 static void client_change_allowed_actions(ObClient *self)
1743 /* desktop windows are kept on all desktops */
1744 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1745 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1747 if (self->functions & OB_CLIENT_FUNC_SHADE)
1748 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1749 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1750 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1751 if (self->functions & OB_CLIENT_FUNC_MOVE)
1752 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1753 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1754 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1755 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1756 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1757 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1758 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1759 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1760 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1761 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1763 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1764 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1765 if (self->functions & OB_CLIENT_FUNC_BELOW)
1766 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1767 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1768 actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1770 OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1772 /* make sure the window isn't breaking any rules now
1774 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1775 it can't be iconified with its parent
1778 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1779 if (self->frame) client_shade(self, FALSE);
1780 else self->shaded = FALSE;
1782 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1783 if (self->frame) client_fullscreen(self, FALSE);
1784 else self->fullscreen = FALSE;
1786 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1788 if (self->frame) client_maximize(self, FALSE, 0);
1789 else self->max_vert = self->max_horz = FALSE;
1793 void client_update_wmhints(ObClient *self)
1797 /* assume a window takes input if it doesnt specify */
1798 self->can_focus = TRUE;
1800 if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
1803 if (hints->flags & InputHint)
1804 self->can_focus = hints->input;
1806 /* only do this when first managing the window *AND* when we aren't
1808 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1809 if (hints->flags & StateHint)
1810 self->iconic = hints->initial_state == IconicState;
1813 self->urgent = (hints->flags & XUrgencyHint);
1814 if (self->urgent && !ur)
1815 client_hilite(self, TRUE);
1816 else if (!self->urgent && ur && self->demands_attention)
1817 client_hilite(self, FALSE);
1819 if (!(hints->flags & WindowGroupHint))
1820 hints->window_group = None;
1822 /* did the group state change? */
1823 if (hints->window_group !=
1824 (self->group ? self->group->leader : None))
1826 ObGroup *oldgroup = self->group;
1828 /* remove from the old group if there was one */
1829 if (self->group != NULL) {
1830 group_remove(self->group, self);
1834 /* add ourself to the group if we have one */
1835 if (hints->window_group != None) {
1836 self->group = group_add(hints->window_group, self);
1839 /* Put ourselves into the new group's transient tree, and remove
1840 ourselves from the old group's */
1841 client_update_transient_tree(self, oldgroup, self->group,
1842 self->transient_for_group,
1843 self->transient_for_group,
1844 client_direct_parent(self),
1845 client_direct_parent(self));
1847 /* Lastly, being in a group, or not, can change if the window is
1848 transient for anything.
1850 The logic for this is:
1851 self->transient = TRUE always if the window wants to be
1852 transient for something, even if transient_for was NULL because
1853 it wasn't in a group before.
1855 If parents was NULL and oldgroup was NULL we can assume
1856 that when we add the new group, it will become transient for
1859 If transient_for_group is TRUE, then it must have already
1860 had a group. If it is getting a new group, the above call to
1861 client_update_transient_tree has already taken care of
1862 everything ! If it is losing all group status then it will
1863 no longer be transient for anything and that needs to be
1866 if (self->transient &&
1867 ((self->parents == NULL && oldgroup == NULL) ||
1868 (self->transient_for_group && !self->group)))
1869 client_update_transient_for(self);
1872 /* the WM_HINTS can contain an icon */
1873 if (hints->flags & IconPixmapHint)
1874 client_update_icons(self);
1880 void client_update_title(ObClient *self)
1883 gchar *visible = NULL;
1885 g_free(self->title);
1888 if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) {
1889 /* try old x stuff */
1890 if (!(OBT_PROP_GETS(self->window, WM_NAME, locale, &data)
1891 || OBT_PROP_GETS(self->window, WM_NAME, utf8, &data))) {
1892 if (self->transient) {
1894 GNOME alert windows are not given titles:
1895 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1897 data = g_strdup("");
1899 data = g_strdup("Unnamed Window");
1903 if (self->client_machine) {
1904 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
1909 if (self->not_responding) {
1911 if (self->close_tried_term)
1912 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
1914 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
1918 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible);
1919 self->title = visible;
1922 frame_adjust_title(self->frame);
1924 /* update the icon title */
1926 g_free(self->icon_title);
1929 if (!OBT_PROP_GETS(self->window, NET_WM_ICON_NAME, utf8, &data))
1930 /* try old x stuff */
1931 if (!(OBT_PROP_GETS(self->window, WM_ICON_NAME, locale, &data) ||
1932 OBT_PROP_GETS(self->window, WM_ICON_NAME, utf8, &data)))
1933 data = g_strdup(self->title);
1935 if (self->client_machine) {
1936 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
1941 if (self->not_responding) {
1943 if (self->close_tried_term)
1944 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
1946 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
1950 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
1951 self->icon_title = visible;
1954 void client_update_strut(ObClient *self)
1958 gboolean got = FALSE;
1961 if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
1966 STRUT_PARTIAL_SET(strut,
1967 data[0], data[2], data[1], data[3],
1968 data[4], data[5], data[8], data[9],
1969 data[6], data[7], data[10], data[11]);
1975 OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
1981 /* use the screen's width/height */
1982 a = screen_physical_area_all_monitors();
1984 STRUT_PARTIAL_SET(strut,
1985 data[0], data[2], data[1], data[3],
1986 a->y, a->y + a->height - 1,
1987 a->x, a->x + a->width - 1,
1988 a->y, a->y + a->height - 1,
1989 a->x, a->x + a->width - 1);
1996 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1997 0, 0, 0, 0, 0, 0, 0, 0);
1999 if (!STRUT_EQUAL(strut, self->strut)) {
2000 self->strut = strut;
2002 /* updating here is pointless while we're being mapped cuz we're not in
2003 the client list yet */
2005 screen_update_areas();
2009 /* Avoid storing icons above this size if possible */
2010 #define AVOID_ABOVE 64
2012 void client_update_icons(ObClient *self)
2017 guint num_seen; /* number of icons present */
2018 guint num_small_seen; /* number of icons small enough present */
2019 guint smallest, smallest_area;
2021 for (i = 0; i < self->nicons; ++i)
2022 g_free(self->icons[i].data);
2023 if (self->nicons > 0)
2024 g_free(self->icons);
2027 if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
2028 /* figure out how many valid icons are in here */
2030 num_seen = num_small_seen = 0;
2031 smallest = smallest_area = 0;
2037 /* watch for it being too small for the specified size, or for
2038 zero sized icons. */
2039 if (i > num || w == 0 || h == 0) break;
2041 if (!smallest_area || w*h < smallest_area) {
2042 smallest = num_seen;
2043 smallest_area = w*h;
2046 if (w <= AVOID_ABOVE && h <= AVOID_ABOVE)
2049 if (num_small_seen > 0)
2050 self->nicons = num_small_seen;
2054 self->icons = g_new(ObClientIcon, self->nicons);
2056 /* store the icons */
2058 for (j = 0; j < self->nicons;) {
2061 w = self->icons[j].width = data[i++];
2062 h = self->icons[j].height = data[i++];
2064 /* if there are some icons smaller than the threshold, we're
2065 skipping all the ones above */
2066 if (num_small_seen > 0) {
2067 if (w > AVOID_ABOVE || h > AVOID_ABOVE) {
2072 /* if there were no icons smaller than the threshold, then we are
2073 only taking the smallest available one we saw */
2074 else if (j != smallest) {
2079 self->icons[j].data = g_new(RrPixel32, w * h);
2080 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
2085 self->icons[j].data[t] =
2086 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2087 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
2088 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
2089 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
2100 if ((hints = XGetWMHints(obt_display, self->window))) {
2101 if (hints->flags & IconPixmapHint) {
2103 self->icons = g_new(ObClientIcon, self->nicons);
2104 obt_display_ignore_errors(TRUE);
2105 if (!RrPixmapToRGBA(ob_rr_inst,
2107 (hints->flags & IconMaskHint ?
2108 hints->icon_mask : None),
2109 &self->icons[0].width,
2110 &self->icons[0].height,
2111 &self->icons[0].data))
2113 g_free(self->icons);
2116 obt_display_ignore_errors(FALSE);
2122 /* set the default icon onto the window
2123 in theory, this could be a race, but if a window doesn't set an icon
2124 or removes it entirely, it's not very likely it is going to set one
2125 right away afterwards
2127 if it has parents, then one of them will have an icon already
2129 if (self->nicons == 0 && !self->parents) {
2130 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2133 data = g_new(gulong, 48*48+2);
2134 data[0] = data[1] = 48;
2135 for (i = 0; i < 48*48; ++i)
2136 data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2137 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2138 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2139 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2140 OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, data, 48*48+2);
2142 } else if (self->frame)
2143 /* don't draw the icon empty if we're just setting one now anyways,
2144 we'll get the property change any second */
2145 frame_adjust_icon(self->frame);
2148 void client_update_icon_geometry(ObClient *self)
2153 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2155 if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
2159 /* don't let them set it with an area < 0 */
2160 RECT_SET(self->icon_geometry, data[0], data[1],
2161 MAX(data[2],0), MAX(data[3],0));
2166 static void client_get_session_ids(ObClient *self)
2173 if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
2176 /* get the SM_CLIENT_ID */
2179 got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
2181 OBT_PROP_GETS(self->window, SM_CLIENT_ID, locale, &self->sm_client_id);
2183 /* get the WM_CLASS (name and class). make them "" if they are not
2187 got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
2189 got = OBT_PROP_GETSS(self->window, WM_CLASS, locale, &ss);
2193 self->name = g_strdup(ss[0]);
2195 self->class = g_strdup(ss[1]);
2200 if (self->name == NULL) self->name = g_strdup("");
2201 if (self->class == NULL) self->class = g_strdup("");
2203 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2206 got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
2208 got = OBT_PROP_GETS(self->window, WM_WINDOW_ROLE, locale, &s);
2213 self->role = g_strdup("");
2215 /* get the WM_COMMAND */
2219 got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
2221 got = OBT_PROP_GETSS(self->window, WM_COMMAND, locale, &ss);
2224 /* merge/mash them all together */
2225 gchar *merge = NULL;
2228 for (i = 0; ss[i]; ++i) {
2231 merge = g_strconcat(merge, ss[i], NULL);
2233 merge = g_strconcat(ss[i], NULL);
2238 self->wm_command = merge;
2241 /* get the WM_CLIENT_MACHINE */
2244 got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
2246 got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, locale, &s);
2249 gchar localhost[128];
2252 gethostname(localhost, 127);
2253 localhost[127] = '\0';
2254 if (strcmp(localhost, s) != 0)
2255 self->client_machine = s;
2259 /* see if it has the PID set too (the PID requires that the
2260 WM_CLIENT_MACHINE be set) */
2261 if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
2266 static void client_change_wm_state(ObClient *self)
2271 old = self->wmstate;
2273 if (self->shaded || self->iconic ||
2274 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2276 self->wmstate = IconicState;
2278 self->wmstate = NormalState;
2280 if (old != self->wmstate) {
2281 OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
2282 self->wmstate, 1, 0, 0, 0);
2284 state[0] = self->wmstate;
2286 OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
2290 static void client_change_state(ObClient *self)
2292 gulong netstate[12];
2297 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2299 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2301 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2302 if (self->skip_taskbar)
2303 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2304 if (self->skip_pager)
2305 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2306 if (self->fullscreen)
2307 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2309 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2311 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2313 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2315 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2316 if (self->demands_attention)
2317 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2318 if (self->undecorated)
2319 netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2320 OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
2323 frame_adjust_state(self->frame);
2326 ObClient *client_search_focus_tree(ObClient *self)
2331 for (it = self->transients; it; it = g_slist_next(it)) {
2332 if (client_focused(it->data)) return it->data;
2333 if ((ret = client_search_focus_tree(it->data))) return ret;
2338 ObClient *client_search_focus_tree_full(ObClient *self)
2340 if (self->parents) {
2343 for (it = self->parents; it; it = g_slist_next(it)) {
2344 ObClient *c = it->data;
2345 if ((c = client_search_focus_tree_full(it->data))) return c;
2351 /* this function checks the whole tree, the client_search_focus_tree
2352 does not, so we need to check this window */
2353 if (client_focused(self))
2355 return client_search_focus_tree(self);
2359 ObClient *client_search_focus_group_full(ObClient *self)
2364 for (it = self->group->members; it; it = g_slist_next(it)) {
2365 ObClient *c = it->data;
2367 if (client_focused(c)) return c;
2368 if ((c = client_search_focus_tree(it->data))) return c;
2371 if (client_focused(self)) return self;
2375 gboolean client_has_parent(ObClient *self)
2377 return self->parents != NULL;
2380 static ObStackingLayer calc_layer(ObClient *self)
2385 monitor = screen_physical_area_monitor(client_monitor(self));
2387 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2388 l = OB_STACKING_LAYER_DESKTOP;
2389 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2390 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2391 else l = OB_STACKING_LAYER_ABOVE;
2393 else if ((self->fullscreen ||
2394 /* No decorations and fills the monitor = oldskool fullscreen.
2395 But not for maximized windows.
2397 (self->decorations == 0 &&
2398 !(self->max_horz && self->max_vert) &&
2399 RECT_EQUAL(self->area, *monitor))) &&
2400 /* you are fullscreen while you or your children are focused.. */
2401 (client_focused(self) || client_search_focus_tree(self) ||
2402 /* you can be fullscreen if you're on another desktop */
2403 (self->desktop != screen_desktop &&
2404 self->desktop != DESKTOP_ALL) ||
2405 /* and you can also be fullscreen if the focused client is on
2406 another monitor, or nothing else is focused */
2408 client_monitor(focus_client) != client_monitor(self))))
2409 l = OB_STACKING_LAYER_FULLSCREEN;
2410 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2411 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2412 else l = OB_STACKING_LAYER_NORMAL;
2419 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2420 ObStackingLayer min)
2422 ObStackingLayer old, own;
2426 own = calc_layer(self);
2427 self->layer = MAX(own, min);
2429 if (self->layer != old) {
2430 stacking_remove(CLIENT_AS_WINDOW(self));
2431 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2434 /* we've been restacked */
2435 self->visited = TRUE;
2437 for (it = self->transients; it; it = g_slist_next(it))
2438 client_calc_layer_recursive(it->data, orig,
2442 static void client_calc_layer_internal(ObClient *self)
2446 /* transients take on the layer of their parents */
2447 sit = client_search_all_top_parents(self);
2449 for (; sit; sit = g_slist_next(sit))
2450 client_calc_layer_recursive(sit->data, self, 0);
2453 void client_calc_layer(ObClient *self)
2457 /* skip over stuff above fullscreen layer */
2458 for (it = stacking_list; it; it = g_list_next(it))
2459 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2461 /* find the windows in the fullscreen layer, and mark them not-visited */
2462 for (; it; it = g_list_next(it)) {
2463 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2464 else if (WINDOW_IS_CLIENT(it->data))
2465 WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2468 client_calc_layer_internal(self);
2470 /* skip over stuff above fullscreen layer */
2471 for (it = stacking_list; it; it = g_list_next(it))
2472 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2474 /* now recalc any windows in the fullscreen layer which have not
2475 had their layer recalced already */
2476 for (; it; it = g_list_next(it)) {
2477 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2478 else if (WINDOW_IS_CLIENT(it->data) &&
2479 !WINDOW_AS_CLIENT(it->data)->visited)
2480 client_calc_layer_internal(it->data);
2484 gboolean client_should_show(ObClient *self)
2488 if (client_normal(self) && screen_showing_desktop)
2490 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2496 gboolean client_show(ObClient *self)
2498 gboolean show = FALSE;
2500 if (client_should_show(self)) {
2501 frame_show(self->frame);
2504 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2505 it needs to be in IconicState. This includes when it is on another
2508 client_change_wm_state(self);
2513 gboolean client_hide(ObClient *self)
2515 gboolean hide = FALSE;
2517 if (!client_should_show(self)) {
2518 if (self == focus_client) {
2519 /* if there is a grab going on, then we need to cancel it. if we
2520 move focus during the grab, applications will get
2521 NotifyWhileGrabbed events and ignore them !
2523 actions should not rely on being able to move focus during an
2526 event_cancel_all_key_grabs();
2529 /* We don't need to ignore enter events here.
2530 The window can hide/iconify in 3 different ways:
2531 1 - through an x message. in this case we ignore all enter events
2532 caused by responding to the x message (unless underMouse)
2533 2 - by a keyboard action. in this case we ignore all enter events
2534 caused by the action
2535 3 - by a mouse action. in this case they are doing stuff with the
2536 mouse and focus _should_ move.
2538 Also in action_end, we simulate an enter event that can't be ignored
2539 so trying to ignore them is futile in case 3 anyways
2542 frame_hide(self->frame);
2545 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2546 it needs to be in IconicState. This includes when it is on another
2549 client_change_wm_state(self);
2554 void client_showhide(ObClient *self)
2556 if (!client_show(self))
2560 gboolean client_normal(ObClient *self) {
2561 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2562 self->type == OB_CLIENT_TYPE_DOCK ||
2563 self->type == OB_CLIENT_TYPE_SPLASH);
2566 gboolean client_helper(ObClient *self)
2568 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2569 self->type == OB_CLIENT_TYPE_MENU ||
2570 self->type == OB_CLIENT_TYPE_TOOLBAR);
2573 gboolean client_mouse_focusable(ObClient *self)
2575 return !(self->type == OB_CLIENT_TYPE_MENU ||
2576 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2577 self->type == OB_CLIENT_TYPE_SPLASH ||
2578 self->type == OB_CLIENT_TYPE_DOCK);
2581 gboolean client_enter_focusable(ObClient *self)
2583 /* you can focus desktops but it shouldn't on enter */
2584 return (client_mouse_focusable(self) &&
2585 self->type != OB_CLIENT_TYPE_DESKTOP);
2589 static void client_apply_startup_state(ObClient *self,
2590 gint x, gint y, gint w, gint h)
2592 /* save the states that we are going to apply */
2593 gboolean iconic = self->iconic;
2594 gboolean fullscreen = self->fullscreen;
2595 gboolean undecorated = self->undecorated;
2596 gboolean shaded = self->shaded;
2597 gboolean demands_attention = self->demands_attention;
2598 gboolean max_horz = self->max_horz;
2599 gboolean max_vert = self->max_vert;
2603 /* turn them all off in the client, so they won't affect the window
2605 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2606 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2608 /* move the client to its placed position, or it it's already there,
2609 generate a ConfigureNotify telling the client where it is.
2611 do this after adjusting the frame. otherwise it gets all weird and
2612 clients don't work right
2614 do this before applying the states so they have the correct
2615 pre-max/pre-fullscreen values
2617 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2618 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2619 self->window, x, y, w, h);
2620 /* save the area, and make it where it should be for the premax stuff */
2621 oldarea = self->area;
2622 RECT_SET(self->area, x, y, w, h);
2624 /* apply the states. these are in a carefully crafted order.. */
2627 client_iconify(self, TRUE, FALSE, TRUE);
2629 client_fullscreen(self, TRUE);
2631 client_set_undecorated(self, TRUE);
2633 client_shade(self, TRUE);
2634 if (demands_attention)
2635 client_hilite(self, TRUE);
2637 if (max_vert && max_horz)
2638 client_maximize(self, TRUE, 0);
2640 client_maximize(self, TRUE, 2);
2642 client_maximize(self, TRUE, 1);
2644 /* if the window hasn't been configured yet, then do so now, in fact the
2645 x,y,w,h may _not_ be the same as the area rect, which can end up
2646 meaning that the client isn't properly moved/resized by the fullscreen
2648 pho can cause this because it maps at size of the screen but not 0,0
2649 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2650 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2651 cuz thats where the pre-fullscreen will be. however the actual area is
2652 not, so this needs to be called even if we have fullscreened/maxed
2654 self->area = oldarea;
2655 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2657 /* set the desktop hint, to make sure that it always exists */
2658 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
2660 /* nothing to do for the other states:
2669 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2671 /* these should be the current values. this is for when you're not moving,
2673 g_assert(*x == self->area.x);
2674 g_assert(oldw == self->area.width);
2677 switch (self->gravity) {
2679 case NorthWestGravity:
2681 case SouthWestGravity:
2688 *x -= (neww - oldw) / 2;
2690 case NorthEastGravity:
2692 case SouthEastGravity:
2698 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2700 /* these should be the current values. this is for when you're not moving,
2702 g_assert(*y == self->area.y);
2703 g_assert(oldh == self->area.height);
2706 switch (self->gravity) {
2708 case NorthWestGravity:
2710 case NorthEastGravity:
2717 *y -= (newh - oldh) / 2;
2719 case SouthWestGravity:
2721 case SouthEastGravity:
2727 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2728 gint *logicalw, gint *logicalh,
2731 Rect desired = {*x, *y, *w, *h};
2732 frame_rect_to_frame(self->frame, &desired);
2734 /* make the frame recalculate its dimentions n shit without changing
2735 anything visible for real, this way the constraints below can work with
2736 the updated frame dimensions. */
2737 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2739 /* gets the frame's position */
2740 frame_client_gravity(self->frame, x, y);
2742 /* these positions are frame positions, not client positions */
2744 /* set the size and position if fullscreen */
2745 if (self->fullscreen) {
2749 i = screen_find_monitor(&desired);
2750 a = screen_physical_area_monitor(i);
2757 user = FALSE; /* ignore if the client can't be moved/resized when it
2761 } else if (self->max_horz || self->max_vert) {
2765 /* use all possible struts when maximizing to the full screen */
2766 i = screen_find_monitor(&desired);
2767 a = screen_area(self->desktop, i,
2768 (self->max_horz && self->max_vert ? NULL : &desired));
2770 /* set the size and position if maximized */
2771 if (self->max_horz) {
2773 *w = a->width - self->frame->size.left - self->frame->size.right;
2775 if (self->max_vert) {
2777 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2780 user = FALSE; /* ignore if the client can't be moved/resized when it
2786 /* gets the client's position */
2787 frame_frame_gravity(self->frame, x, y);
2789 /* work within the prefered sizes given by the window */
2790 if (!(*w == self->area.width && *h == self->area.height)) {
2791 gint basew, baseh, minw, minh;
2793 gfloat minratio, maxratio;
2795 incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
2796 inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
2797 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2798 0 : self->min_ratio;
2799 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2800 0 : self->max_ratio;
2802 /* base size is substituted with min size if not specified */
2803 if (self->base_size.width || self->base_size.height) {
2804 basew = self->base_size.width;
2805 baseh = self->base_size.height;
2807 basew = self->min_size.width;
2808 baseh = self->min_size.height;
2810 /* min size is substituted with base size if not specified */
2811 if (self->min_size.width || self->min_size.height) {
2812 minw = self->min_size.width;
2813 minh = self->min_size.height;
2815 minw = self->base_size.width;
2816 minh = self->base_size.height;
2819 /* if this is a user-requested resize, then check against min/max
2822 /* smaller than min size or bigger than max size? */
2823 if (*w > self->max_size.width) *w = self->max_size.width;
2824 if (*w < minw) *w = minw;
2825 if (*h > self->max_size.height) *h = self->max_size.height;
2826 if (*h < minh) *h = minh;
2831 /* keep to the increments */
2835 /* you cannot resize to nothing */
2836 if (basew + *w < 1) *w = 1 - basew;
2837 if (baseh + *h < 1) *h = 1 - baseh;
2839 /* save the logical size */
2840 *logicalw = incw > 1 ? *w : *w + basew;
2841 *logicalh = inch > 1 ? *h : *h + baseh;
2849 /* adjust the height to match the width for the aspect ratios.
2850 for this, min size is not substituted for base size ever. */
2851 *w -= self->base_size.width;
2852 *h -= self->base_size.height;
2855 if (*h * minratio > *w) {
2856 *h = (gint)(*w / minratio);
2858 /* you cannot resize to nothing */
2861 *w = (gint)(*h * minratio);
2865 if (*h * maxratio < *w) {
2866 *h = (gint)(*w / maxratio);
2868 /* you cannot resize to nothing */
2871 *w = (gint)(*h * minratio);
2875 *w += self->base_size.width;
2876 *h += self->base_size.height;
2879 /* these override the above states! if you cant move you can't move! */
2881 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2885 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2886 *w = self->area.width;
2887 *h = self->area.height;
2896 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
2897 gboolean user, gboolean final, gboolean force_reply)
2901 gboolean send_resize_client;
2902 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
2903 gboolean fmoved, fresized;
2904 guint fdecor = self->frame->decorations;
2905 gboolean fhorz = self->frame->max_horz;
2906 gboolean fvert = self->frame->max_vert;
2907 gint logicalw, logicalh;
2909 /* find the new x, y, width, and height (and logical size) */
2910 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
2912 /* set the logical size if things changed */
2913 if (!(w == self->area.width && h == self->area.height))
2914 SIZE_SET(self->logical_size, logicalw, logicalh);
2916 /* figure out if we moved or resized or what */
2917 moved = (x != self->area.x || y != self->area.y);
2918 resized = (w != self->area.width || h != self->area.height);
2920 oldw = self->area.width;
2921 oldh = self->area.height;
2922 oldframe = self->frame->area;
2923 RECT_SET(self->area, x, y, w, h);
2925 /* for app-requested resizes, always resize if 'resized' is true.
2926 for user-requested ones, only resize if final is true, or when
2927 resizing in redraw mode */
2928 send_resize_client = ((!user && resized) ||
2930 (resized && config_resize_redraw))));
2932 /* if the client is enlarging, then resize the client before the frame */
2933 if (send_resize_client && (w > oldw || h > oldh)) {
2934 XMoveResizeWindow(obt_display, self->window,
2935 self->frame->size.left, self->frame->size.top,
2936 MAX(w, oldw), MAX(h, oldh));
2937 frame_adjust_client_area(self->frame);
2940 /* find the frame's dimensions and move/resize it */
2944 /* if decorations changed, then readjust everything for the frame */
2945 if (self->decorations != fdecor ||
2946 self->max_horz != fhorz || self->max_vert != fvert)
2948 fmoved = fresized = TRUE;
2951 /* adjust the frame */
2952 if (fmoved || fresized) {
2953 gulong ignore_start;
2955 ignore_start = event_start_ignore_all_enters();
2957 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
2960 event_end_ignore_all_enters(ignore_start);
2963 if (!user || final) {
2964 gint oldrx = self->root_pos.x;
2965 gint oldry = self->root_pos.y;
2966 /* we have reset the client to 0 border width, so don't include
2967 it in these coords */
2968 POINT_SET(self->root_pos,
2969 self->frame->area.x + self->frame->size.left -
2971 self->frame->area.y + self->frame->size.top -
2972 self->border_width);
2973 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
2977 /* This is kinda tricky and should not be changed.. let me explain!
2979 When user = FALSE, then the request is coming from the application
2980 itself, and we are more strict about when to send a synthetic
2981 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
2982 in this case (if force_reply is true)
2984 When user = TRUE, then the request is coming from "us", like when we
2985 maximize a window or something. In this case we are more lenient. We
2986 used to follow the same rules as above, but _Java_ Swing can't handle
2987 this. So just to appease Swing, when user = TRUE, we always send
2988 a synthetic ConfigureNotify to give the window its root coordinates.
2990 if ((!user && !resized && (rootmoved || force_reply)) ||
2991 (user && final && rootmoved))
2995 event.type = ConfigureNotify;
2996 event.xconfigure.display = obt_display;
2997 event.xconfigure.event = self->window;
2998 event.xconfigure.window = self->window;
3000 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3001 self->title, self->root_pos.x, self->root_pos.y, w, h);
3003 /* root window real coords */
3004 event.xconfigure.x = self->root_pos.x;
3005 event.xconfigure.y = self->root_pos.y;
3006 event.xconfigure.width = w;
3007 event.xconfigure.height = h;
3008 event.xconfigure.border_width = self->border_width;
3009 event.xconfigure.above = None;
3010 event.xconfigure.override_redirect = FALSE;
3011 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3012 FALSE, StructureNotifyMask, &event);
3015 /* if the client is shrinking, then resize the frame before the client.
3017 both of these resize sections may run, because the top one only resizes
3018 in the direction that is growing
3020 if (send_resize_client && (w <= oldw || h <= oldh)) {
3021 frame_adjust_client_area(self->frame);
3022 XMoveResizeWindow(obt_display, self->window,
3023 self->frame->size.left, self->frame->size.top, w, h);
3026 XFlush(obt_display);
3028 /* if it moved between monitors, then this can affect the stacking
3029 layer of this window or others - for fullscreen windows */
3030 if (screen_find_monitor(&self->frame->area) !=
3031 screen_find_monitor(&oldframe))
3033 client_calc_layer(self);
3037 void client_fullscreen(ObClient *self, gboolean fs)
3041 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3042 self->fullscreen == fs) return; /* already done */
3044 self->fullscreen = fs;
3045 client_change_state(self); /* change the state hints on the client */
3048 self->pre_fullscreen_area = self->area;
3049 /* if the window is maximized, its area isn't all that meaningful.
3050 save it's premax area instead. */
3051 if (self->max_horz) {
3052 self->pre_fullscreen_area.x = self->pre_max_area.x;
3053 self->pre_fullscreen_area.width = self->pre_max_area.width;
3055 if (self->max_vert) {
3056 self->pre_fullscreen_area.y = self->pre_max_area.y;
3057 self->pre_fullscreen_area.height = self->pre_max_area.height;
3060 /* these will help configure_full figure out where to fullscreen
3064 w = self->area.width;
3065 h = self->area.height;
3067 g_assert(self->pre_fullscreen_area.width > 0 &&
3068 self->pre_fullscreen_area.height > 0);
3070 x = self->pre_fullscreen_area.x;
3071 y = self->pre_fullscreen_area.y;
3072 w = self->pre_fullscreen_area.width;
3073 h = self->pre_fullscreen_area.height;
3074 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3077 ob_debug("Window %s going fullscreen (%d)",
3078 self->title, self->fullscreen);
3080 client_setup_decor_and_functions(self, FALSE);
3081 client_move_resize(self, x, y, w, h);
3083 /* and adjust our layer/stacking. do this after resizing the window,
3084 and applying decorations, because windows which fill the screen are
3085 considered "fullscreen" and it affects their layer */
3086 client_calc_layer(self);
3089 /* try focus us when we go into fullscreen mode */
3094 static void client_iconify_recursive(ObClient *self,
3095 gboolean iconic, gboolean curdesk,
3096 gboolean hide_animation)
3099 gboolean changed = FALSE;
3102 if (self->iconic != iconic) {
3103 ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3107 /* don't let non-normal windows iconify along with their parents
3109 if (client_normal(self)) {
3110 self->iconic = iconic;
3112 /* update the focus lists.. iconic windows go to the bottom of
3114 focus_order_to_bottom(self);
3119 self->iconic = iconic;
3121 if (curdesk && self->desktop != screen_desktop &&
3122 self->desktop != DESKTOP_ALL)
3123 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3125 /* this puts it after the current focused window */
3126 focus_order_remove(self);
3127 focus_order_add_new(self);
3134 client_change_state(self);
3135 if (config_animate_iconify && !hide_animation)
3136 frame_begin_iconify_animation(self->frame, iconic);
3137 /* do this after starting the animation so it doesn't flash */
3138 client_showhide(self);
3141 /* iconify all direct transients, and deiconify all transients
3143 for (it = self->transients; it; it = g_slist_next(it))
3144 if (it->data != self)
3145 if (client_is_direct_child(self, it->data) || !iconic)
3146 client_iconify_recursive(it->data, iconic, curdesk,
3150 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3151 gboolean hide_animation)
3153 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3154 /* move up the transient chain as far as possible first */
3155 self = client_search_top_direct_parent(self);
3156 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3160 void client_maximize(ObClient *self, gboolean max, gint dir)
3164 g_assert(dir == 0 || dir == 1 || dir == 2);
3165 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
3167 /* check if already done */
3169 if (dir == 0 && self->max_horz && self->max_vert) return;
3170 if (dir == 1 && self->max_horz) return;
3171 if (dir == 2 && self->max_vert) return;
3173 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3174 if (dir == 1 && !self->max_horz) return;
3175 if (dir == 2 && !self->max_vert) return;
3178 /* these will help configure_full figure out which screen to fill with
3182 w = self->area.width;
3183 h = self->area.height;
3186 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3187 RECT_SET(self->pre_max_area,
3188 self->area.x, self->pre_max_area.y,
3189 self->area.width, self->pre_max_area.height);
3191 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3192 RECT_SET(self->pre_max_area,
3193 self->pre_max_area.x, self->area.y,
3194 self->pre_max_area.width, self->area.height);
3197 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3198 g_assert(self->pre_max_area.width > 0);
3200 x = self->pre_max_area.x;
3201 w = self->pre_max_area.width;
3203 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3204 0, self->pre_max_area.height);
3206 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3207 g_assert(self->pre_max_area.height > 0);
3209 y = self->pre_max_area.y;
3210 h = self->pre_max_area.height;
3212 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3213 self->pre_max_area.width, 0);
3217 if (dir == 0 || dir == 1) /* horz */
3218 self->max_horz = max;
3219 if (dir == 0 || dir == 2) /* vert */
3220 self->max_vert = max;
3222 client_change_state(self); /* change the state hints on the client */
3224 client_setup_decor_and_functions(self, FALSE);
3225 client_move_resize(self, x, y, w, h);
3228 void client_shade(ObClient *self, gboolean shade)
3230 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3231 shade) || /* can't shade */
3232 self->shaded == shade) return; /* already done */
3234 self->shaded = shade;
3235 client_change_state(self);
3236 client_change_wm_state(self); /* the window is being hidden/shown */
3237 /* resize the frame to just the titlebar */
3238 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3241 static void client_ping_event(ObClient *self, gboolean dead)
3243 self->not_responding = dead;
3244 client_update_title(self);
3247 /* try kill it nicely the first time again, if it started responding
3249 self->close_tried_term = FALSE;
3253 void client_close(ObClient *self)
3255 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3262 /* in the case that the client provides no means to requesting that it
3263 close, we just kill it */
3264 if (!self->delete_window)
3265 /* don't use client_kill(), we should only kill based on PID in
3266 response to a lack of PING replies */
3267 XKillClient(obt_display, self->window);
3268 else if (self->not_responding)
3271 /* request the client to close with WM_DELETE_WINDOW */
3272 OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
3273 OBT_PROP_ATOM(WM_DELETE_WINDOW), event_curtime,
3274 0, 0, 0, NoEventMask);
3277 void client_kill(ObClient *self)
3279 if (!self->client_machine && self->pid) {
3280 /* running on the local host */
3281 if (!self->close_tried_term) {
3282 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3283 self->window, self->pid);
3284 kill(self->pid, SIGTERM);
3285 self->close_tried_term = TRUE;
3287 /* show that we're trying to kill it */
3288 client_update_title(self);
3291 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3292 self->window, self->pid);
3293 kill(self->pid, SIGKILL); /* kill -9 */
3297 XKillClient(obt_display, self->window);
3300 void client_hilite(ObClient *self, gboolean hilite)
3302 if (self->demands_attention == hilite)
3303 return; /* no change */
3305 /* don't allow focused windows to hilite */
3306 self->demands_attention = hilite && !client_focused(self);
3307 if (self->frame != NULL) { /* if we're mapping, just set the state */
3308 if (self->demands_attention)
3309 frame_flash_start(self->frame);
3311 frame_flash_stop(self->frame);
3312 client_change_state(self);
3316 static void client_set_desktop_recursive(ObClient *self,
3324 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3326 ob_debug("Setting desktop %u", target+1);
3328 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3330 old = self->desktop;
3331 self->desktop = target;
3332 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
3333 /* the frame can display the current desktop state */
3334 frame_adjust_state(self->frame);
3335 /* 'move' the window to the new desktop */
3339 /* raise if it was not already on the desktop */
3340 if (old != DESKTOP_ALL && !dontraise)
3341 stacking_raise(CLIENT_AS_WINDOW(self));
3342 if (STRUT_EXISTS(self->strut))
3343 screen_update_areas();
3345 /* the new desktop's geometry may be different, so we may need to
3346 resize, for example if we are maximized */
3347 client_reconfigure(self, FALSE);
3350 /* move all transients */
3351 for (it = self->transients; it; it = g_slist_next(it))
3352 if (it->data != self)
3353 if (client_is_direct_child(self, it->data))
3354 client_set_desktop_recursive(it->data, target,
3355 donthide, dontraise);
3358 void client_set_desktop(ObClient *self, guint target,
3359 gboolean donthide, gboolean dontraise)
3361 self = client_search_top_direct_parent(self);
3362 client_set_desktop_recursive(self, target, donthide, dontraise);
3365 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3367 while (child != parent && (child = client_direct_parent(child)));
3368 return child == parent;
3371 ObClient *client_search_modal_child(ObClient *self)
3376 for (it = self->transients; it; it = g_slist_next(it)) {
3377 ObClient *c = it->data;
3378 if ((ret = client_search_modal_child(c))) return ret;
3379 if (c->modal) return c;
3384 gboolean client_validate(ObClient *self)
3388 XSync(obt_display, FALSE); /* get all events on the server */
3390 if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e) ||
3391 XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e))
3393 XPutBackEvent(obt_display, &e);
3400 void client_set_wm_state(ObClient *self, glong state)
3402 if (state == self->wmstate) return; /* no change */
3406 client_iconify(self, TRUE, TRUE, FALSE);
3409 client_iconify(self, FALSE, TRUE, FALSE);
3414 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3416 gboolean shaded = self->shaded;
3417 gboolean fullscreen = self->fullscreen;
3418 gboolean undecorated = self->undecorated;
3419 gboolean max_horz = self->max_horz;
3420 gboolean max_vert = self->max_vert;
3421 gboolean modal = self->modal;
3422 gboolean iconic = self->iconic;
3423 gboolean demands_attention = self->demands_attention;
3424 gboolean above = self->above;
3425 gboolean below = self->below;
3428 if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3429 action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3430 action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3431 /* an invalid action was passed to the client message, ignore it */
3434 for (i = 0; i < 2; ++i) {
3435 Atom state = i == 0 ? data1 : data2;
3437 if (!state) continue;
3439 /* if toggling, then pick whether we're adding or removing */
3440 if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3441 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3442 action = modal ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3443 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3444 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3445 action = self->max_vert ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3446 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3447 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3448 action = self->max_horz ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3449 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3450 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3451 action = shaded ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3452 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3453 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3454 action = self->skip_taskbar ?
3455 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3456 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3457 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3458 action = self->skip_pager ?
3459 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3460 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3461 else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3462 action = self->iconic ?
3463 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3464 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3465 else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3466 action = fullscreen ?
3467 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3468 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3469 else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3470 action = self->above ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3471 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3472 else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3473 action = self->below ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3474 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3475 else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3476 action = self->demands_attention ?
3477 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3478 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3479 else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3480 action = undecorated ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3481 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3484 if (action == OBT_PROP_ATOM(NET_WM_STATE_ADD)) {
3485 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3487 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3489 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3491 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3493 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3494 self->skip_taskbar = TRUE;
3495 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3496 self->skip_pager = TRUE;
3497 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3499 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3501 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3504 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3507 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3508 demands_attention = TRUE;
3509 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3513 } else { /* action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) */
3514 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3516 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3518 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3520 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3522 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3523 self->skip_taskbar = FALSE;
3524 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3525 self->skip_pager = FALSE;
3526 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3528 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3530 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3532 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3534 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3535 demands_attention = FALSE;
3536 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3537 undecorated = FALSE;
3542 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3543 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3545 if (max_horz == max_vert) { /* both going the same way */
3546 client_maximize(self, max_horz, 0);
3548 client_maximize(self, max_horz, 1);
3549 client_maximize(self, max_vert, 2);
3553 if (max_horz != self->max_horz)
3554 client_maximize(self, max_horz, 1);
3556 client_maximize(self, max_vert, 2);
3559 /* change fullscreen state before shading, as it will affect if the window
3561 if (fullscreen != self->fullscreen)
3562 client_fullscreen(self, fullscreen);
3563 if (shaded != self->shaded)
3564 client_shade(self, shaded);
3565 if (undecorated != self->undecorated)
3566 client_set_undecorated(self, undecorated);
3567 if (above != self->above || below != self->below) {
3568 self->above = above;
3569 self->below = below;
3570 client_calc_layer(self);
3573 if (modal != self->modal) {
3574 self->modal = modal;
3575 /* when a window changes modality, then its stacking order with its
3576 transients needs to change */
3577 stacking_raise(CLIENT_AS_WINDOW(self));
3579 /* it also may get focused. if something is focused that shouldn't
3580 be focused anymore, then move the focus */
3581 if (focus_client && client_focus_target(focus_client) != focus_client)
3582 client_focus(focus_client);
3585 if (iconic != self->iconic)
3586 client_iconify(self, iconic, FALSE, FALSE);
3588 if (demands_attention != self->demands_attention)
3589 client_hilite(self, demands_attention);
3591 client_change_state(self); /* change the hint to reflect these changes */
3594 ObClient *client_focus_target(ObClient *self)
3596 ObClient *child = NULL;
3598 child = client_search_modal_child(self);
3599 if (child) return child;
3603 gboolean client_can_focus(ObClient *self)
3605 /* choose the correct target */
3606 self = client_focus_target(self);
3608 if (!self->frame->visible)
3611 if (!(self->can_focus || self->focus_notify))
3617 gboolean client_focus(ObClient *self)
3619 /* we might not focus this window, so if we have modal children which would
3620 be focused instead, bring them to this desktop */
3621 client_bring_modal_windows(self);
3623 /* choose the correct target */
3624 self = client_focus_target(self);
3626 if (!client_can_focus(self)) {
3627 ob_debug_type(OB_DEBUG_FOCUS,
3628 "Client %s can't be focused", self->title);
3632 ob_debug_type(OB_DEBUG_FOCUS,
3633 "Focusing client \"%s\" (0x%x) at time %u",
3634 self->title, self->window, event_curtime);
3636 /* if using focus_delay, stop the timer now so that focus doesn't
3638 event_halt_focus_delay();
3640 /* if there is a grab going on, then we need to cancel it. if we move
3641 focus during the grab, applications will get NotifyWhileGrabbed events
3644 actions should not rely on being able to move focus during an
3647 event_cancel_all_key_grabs();
3649 obt_display_ignore_errors(TRUE);
3651 if (self->can_focus) {
3652 /* This can cause a BadMatch error with CurrentTime, or if an app
3653 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3654 XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
3658 if (self->focus_notify) {
3660 ce.xclient.type = ClientMessage;
3661 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
3662 ce.xclient.display = obt_display;
3663 ce.xclient.window = self->window;
3664 ce.xclient.format = 32;
3665 ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
3666 ce.xclient.data.l[1] = event_curtime;
3667 ce.xclient.data.l[2] = 0l;
3668 ce.xclient.data.l[3] = 0l;
3669 ce.xclient.data.l[4] = 0l;
3670 XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
3673 obt_display_ignore_errors(FALSE);
3675 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
3676 obt_display_error_occured);
3677 return !obt_display_error_occured;
3680 static void client_present(ObClient *self, gboolean here, gboolean raise,
3683 if (client_normal(self) && screen_showing_desktop)
3684 screen_show_desktop(FALSE, self);
3686 client_iconify(self, FALSE, here, FALSE);
3687 if (self->desktop != DESKTOP_ALL &&
3688 self->desktop != screen_desktop)
3691 client_set_desktop(self, screen_desktop, FALSE, TRUE);
3693 screen_set_desktop(self->desktop, FALSE);
3694 } else if (!self->frame->visible)
3695 /* if its not visible for other reasons, then don't mess
3698 if (self->shaded && unshade)
3699 client_shade(self, FALSE);
3701 stacking_raise(CLIENT_AS_WINDOW(self));
3706 void client_activate(ObClient *self, gboolean here, gboolean raise,
3707 gboolean unshade, gboolean user)
3709 client_present(self, here, raise, unshade);
3712 static void client_bring_windows_recursive(ObClient *self,
3720 for (it = self->transients; it; it = g_slist_next(it))
3721 client_bring_windows_recursive(it->data, desktop,
3722 helpers, modals, iconic);
3724 if (((helpers && client_helper(self)) ||
3725 (modals && self->modal)) &&
3726 ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
3727 (iconic && self->iconic)))
3729 if (iconic && self->iconic)
3730 client_iconify(self, FALSE, TRUE, FALSE);
3732 client_set_desktop(self, desktop, FALSE, FALSE);
3736 void client_bring_helper_windows(ObClient *self)
3738 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
3741 void client_bring_modal_windows(ObClient *self)
3743 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
3746 gboolean client_focused(ObClient *self)
3748 return self == focus_client;
3751 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
3754 gulong min_diff, min_i;
3756 if (!self->nicons) {
3757 ObClientIcon *parent = NULL;
3760 for (it = self->parents; it; it = g_slist_next(it)) {
3761 ObClient *c = it->data;
3762 if ((parent = client_icon_recursive(c, w, h)))
3769 /* some kind of crappy approximation to find the icon closest in size to
3770 what we requested, but icons are generally all the same ratio as
3771 eachother so it's good enough. */
3773 min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
3776 for (i = 1; i < self->nicons; ++i) {
3779 diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h);
3780 if (diff < min_diff) {
3785 return &self->icons[min_i];
3788 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
3791 static ObClientIcon deficon;
3793 if (!(ret = client_icon_recursive(self, w, h))) {
3794 deficon.width = deficon.height = 48;
3795 deficon.data = ob_rr_theme->def_win_icon;
3801 void client_set_layer(ObClient *self, gint layer)
3805 self->above = FALSE;
3806 } else if (layer == 0) {
3807 self->below = self->above = FALSE;
3809 self->below = FALSE;
3812 client_calc_layer(self);
3813 client_change_state(self); /* reflect this in the state hints */
3816 void client_set_undecorated(ObClient *self, gboolean undecorated)
3818 if (self->undecorated != undecorated &&
3819 /* don't let it undecorate if the function is missing, but let
3821 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
3823 self->undecorated = undecorated;
3824 client_setup_decor_and_functions(self, TRUE);
3825 client_change_state(self); /* reflect this in the state hints */
3829 guint client_monitor(ObClient *self)
3831 return screen_find_monitor(&self->frame->area);
3834 ObClient *client_direct_parent(ObClient *self)
3836 if (!self->parents) return NULL;
3837 if (self->transient_for_group) return NULL;
3838 return self->parents->data;
3841 ObClient *client_search_top_direct_parent(ObClient *self)
3844 while ((p = client_direct_parent(self))) self = p;
3848 static GSList *client_search_all_top_parents_internal(ObClient *self,
3850 ObStackingLayer layer)
3855 /* move up the direct transient chain as far as possible */
3856 while ((p = client_direct_parent(self)) &&
3857 (!bylayer || p->layer == layer))
3861 ret = g_slist_prepend(NULL, self);
3863 ret = g_slist_copy(self->parents);
3868 GSList *client_search_all_top_parents(ObClient *self)
3870 return client_search_all_top_parents_internal(self, FALSE, 0);
3873 GSList *client_search_all_top_parents_layer(ObClient *self)
3875 return client_search_all_top_parents_internal(self, TRUE, self->layer);
3878 ObClient *client_search_focus_parent(ObClient *self)
3882 for (it = self->parents; it; it = g_slist_next(it))
3883 if (client_focused(it->data)) return it->data;
3888 ObClient *client_search_parent(ObClient *self, ObClient *search)
3892 for (it = self->parents; it; it = g_slist_next(it))
3893 if (it->data == search) return search;
3898 ObClient *client_search_transient(ObClient *self, ObClient *search)
3902 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3903 if (sit->data == search)
3905 if (client_search_transient(sit->data, search))
3911 static void detect_edge(Rect area, ObDirection dir,
3912 gint my_head, gint my_size,
3913 gint my_edge_start, gint my_edge_size,
3914 gint *dest, gboolean *near_edge)
3916 gint edge_start, edge_size, head, tail;
3917 gboolean skip_head = FALSE, skip_tail = FALSE;
3920 case OB_DIRECTION_NORTH:
3921 case OB_DIRECTION_SOUTH:
3922 edge_start = area.x;
3923 edge_size = area.width;
3925 case OB_DIRECTION_EAST:
3926 case OB_DIRECTION_WEST:
3927 edge_start = area.y;
3928 edge_size = area.height;
3931 g_assert_not_reached();
3934 /* do we collide with this window? */
3935 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
3936 edge_start, edge_size))
3940 case OB_DIRECTION_NORTH:
3941 head = RECT_BOTTOM(area);
3942 tail = RECT_TOP(area);
3944 case OB_DIRECTION_SOUTH:
3945 head = RECT_TOP(area);
3946 tail = RECT_BOTTOM(area);
3948 case OB_DIRECTION_WEST:
3949 head = RECT_RIGHT(area);
3950 tail = RECT_LEFT(area);
3952 case OB_DIRECTION_EAST:
3953 head = RECT_LEFT(area);
3954 tail = RECT_RIGHT(area);
3957 g_assert_not_reached();
3960 case OB_DIRECTION_NORTH:
3961 case OB_DIRECTION_WEST:
3962 /* check if our window is past the head of this window */
3963 if (my_head <= head + 1)
3965 /* check if our window's tail is past the tail of this window */
3966 if (my_head + my_size - 1 <= tail)
3968 /* check if the head of this window is closer than the previously
3969 chosen edge (take into account that the previously chosen
3970 edge might have been a tail, not a head) */
3971 if (head + (*near_edge ? 0 : my_size) < *dest)
3973 /* check if the tail of this window is closer than the previously
3974 chosen edge (take into account that the previously chosen
3975 edge might have been a head, not a tail) */
3976 if (tail - (!*near_edge ? 0 : my_size) < *dest)
3979 case OB_DIRECTION_SOUTH:
3980 case OB_DIRECTION_EAST:
3981 /* check if our window is past the head of this window */
3982 if (my_head >= head - 1)
3984 /* check if our window's tail is past the tail of this window */
3985 if (my_head - my_size + 1 >= tail)
3987 /* check if the head of this window is closer than the previously
3988 chosen edge (take into account that the previously chosen
3989 edge might have been a tail, not a head) */
3990 if (head - (*near_edge ? 0 : my_size) > *dest)
3992 /* check if the tail of this window is closer than the previously
3993 chosen edge (take into account that the previously chosen
3994 edge might have been a head, not a tail) */
3995 if (tail + (!*near_edge ? 0 : my_size) > *dest)
3999 g_assert_not_reached();
4002 ob_debug("my head %d size %d", my_head, my_size);
4003 ob_debug("head %d tail %d deest %d", head, tail, *dest);
4005 ob_debug("using near edge %d", head);
4009 else if (!skip_tail) {
4010 ob_debug("using far edge %d", tail);
4016 void client_find_edge_directional(ObClient *self, ObDirection dir,
4017 gint my_head, gint my_size,
4018 gint my_edge_start, gint my_edge_size,
4019 gint *dest, gboolean *near_edge)
4026 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4027 &self->frame->area);
4028 mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
4029 &self->frame->area);
4032 case OB_DIRECTION_NORTH:
4033 if (my_head >= RECT_TOP(*mon) + 1)
4034 edge = RECT_TOP(*mon) - 1;
4036 edge = RECT_TOP(*a) - 1;
4038 case OB_DIRECTION_SOUTH:
4039 if (my_head <= RECT_BOTTOM(*mon) - 1)
4040 edge = RECT_BOTTOM(*mon) + 1;
4042 edge = RECT_BOTTOM(*a) + 1;
4044 case OB_DIRECTION_EAST:
4045 if (my_head <= RECT_RIGHT(*mon) - 1)
4046 edge = RECT_RIGHT(*mon) + 1;
4048 edge = RECT_RIGHT(*a) + 1;
4050 case OB_DIRECTION_WEST:
4051 if (my_head >= RECT_LEFT(*mon) + 1)
4052 edge = RECT_LEFT(*mon) - 1;
4054 edge = RECT_LEFT(*a) - 1;
4057 g_assert_not_reached();
4059 /* default to the far edge, then narrow it down */
4063 for (it = client_list; it; it = g_list_next(it)) {
4064 ObClient *cur = it->data;
4066 /* skip windows to not bump into */
4071 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4072 cur->desktop != screen_desktop)
4075 ob_debug("trying window %s", cur->title);
4077 detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
4078 my_edge_size, dest, near_edge);
4080 dock_get_area(&dock_area);
4081 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4082 my_edge_size, dest, near_edge);
4087 void client_find_move_directional(ObClient *self, ObDirection dir,
4091 gint e, e_start, e_size;
4095 case OB_DIRECTION_EAST:
4096 head = RECT_RIGHT(self->frame->area);
4097 size = self->frame->area.width;
4098 e_start = RECT_TOP(self->frame->area);
4099 e_size = self->frame->area.height;
4101 case OB_DIRECTION_WEST:
4102 head = RECT_LEFT(self->frame->area);
4103 size = self->frame->area.width;
4104 e_start = RECT_TOP(self->frame->area);
4105 e_size = self->frame->area.height;
4107 case OB_DIRECTION_NORTH:
4108 head = RECT_TOP(self->frame->area);
4109 size = self->frame->area.height;
4110 e_start = RECT_LEFT(self->frame->area);
4111 e_size = self->frame->area.width;
4113 case OB_DIRECTION_SOUTH:
4114 head = RECT_BOTTOM(self->frame->area);
4115 size = self->frame->area.height;
4116 e_start = RECT_LEFT(self->frame->area);
4117 e_size = self->frame->area.width;
4120 g_assert_not_reached();
4123 client_find_edge_directional(self, dir, head, size,
4124 e_start, e_size, &e, &near);
4125 *x = self->frame->area.x;
4126 *y = self->frame->area.y;
4128 case OB_DIRECTION_EAST:
4129 if (near) e -= self->frame->area.width;
4133 case OB_DIRECTION_WEST:
4135 else e -= self->frame->area.width;
4138 case OB_DIRECTION_NORTH:
4140 else e -= self->frame->area.height;
4143 case OB_DIRECTION_SOUTH:
4144 if (near) e -= self->frame->area.height;
4149 g_assert_not_reached();
4151 frame_frame_gravity(self->frame, x, y);
4154 void client_find_resize_directional(ObClient *self, ObDirection side,
4156 gint *x, gint *y, gint *w, gint *h)
4159 gint e, e_start, e_size, delta;
4164 case OB_DIRECTION_EAST:
4165 head = RECT_RIGHT(self->frame->area) +
4166 (self->size_inc.width - 1) * (grow ? 1 : -1);
4167 e_start = RECT_TOP(self->frame->area);
4168 e_size = self->frame->area.height;
4169 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4171 case OB_DIRECTION_WEST:
4172 head = RECT_LEFT(self->frame->area) -
4173 (self->size_inc.width - 1) * (grow ? 1 : -1);
4174 e_start = RECT_TOP(self->frame->area);
4175 e_size = self->frame->area.height;
4176 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4178 case OB_DIRECTION_NORTH:
4179 head = RECT_TOP(self->frame->area) -
4180 (self->size_inc.height - 1) * (grow ? 1 : -1);
4181 e_start = RECT_LEFT(self->frame->area);
4182 e_size = self->frame->area.width;
4183 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4185 case OB_DIRECTION_SOUTH:
4186 head = RECT_BOTTOM(self->frame->area) +
4187 (self->size_inc.height - 1) * (grow ? 1 : -1);
4188 e_start = RECT_LEFT(self->frame->area);
4189 e_size = self->frame->area.width;
4190 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4193 g_assert_not_reached();
4196 ob_debug("head %d dir %d", head, dir);
4197 client_find_edge_directional(self, dir, head, 1,
4198 e_start, e_size, &e, &near);
4199 ob_debug("edge %d", e);
4200 *x = self->frame->area.x;
4201 *y = self->frame->area.y;
4202 *w = self->frame->area.width;
4203 *h = self->frame->area.height;
4205 case OB_DIRECTION_EAST:
4206 if (grow == near) --e;
4207 delta = e - RECT_RIGHT(self->frame->area);
4210 case OB_DIRECTION_WEST:
4211 if (grow == near) ++e;
4212 delta = RECT_LEFT(self->frame->area) - e;
4216 case OB_DIRECTION_NORTH:
4217 if (grow == near) ++e;
4218 delta = RECT_TOP(self->frame->area) - e;
4222 case OB_DIRECTION_SOUTH:
4223 if (grow == near) --e;
4224 delta = e - RECT_BOTTOM(self->frame->area);
4228 g_assert_not_reached();
4230 frame_frame_gravity(self->frame, x, y);
4231 *w -= self->frame->size.left + self->frame->size.right;
4232 *h -= self->frame->size.top + self->frame->size.bottom;
4235 ObClient* client_under_pointer(void)
4239 ObClient *ret = NULL;
4241 if (screen_pointer_pos(&x, &y)) {
4242 for (it = stacking_list; it; it = g_list_next(it)) {
4243 if (WINDOW_IS_CLIENT(it->data)) {
4244 ObClient *c = WINDOW_AS_CLIENT(it->data);
4245 if (c->frame->visible &&
4246 /* check the desktop, this is done during desktop
4247 switching and windows are shown/hidden status is not
4249 (c->desktop == screen_desktop ||
4250 c->desktop == DESKTOP_ALL) &&
4251 /* ignore all animating windows */
4252 !frame_iconify_animating(c->frame) &&
4253 RECT_CONTAINS(c->frame->area, x, y))
4264 gboolean client_has_group_siblings(ObClient *self)
4266 return self->group && self->group->members->next;