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"
34 #include "focus_cycle.h"
40 #include "menuframe.h"
43 #include "render/render.h"
45 #include "obt/display.h"
53 # include <signal.h> /* for kill() */
57 #include <X11/Xutil.h>
59 extern guint config_window_margin;
61 /*! The event mask to grab on client windows */
62 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
65 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
70 ObClientCallback func;
74 GList *client_list = NULL;
76 static GSList *client_destroy_notifies = NULL;
77 static RrImage *client_default_icon = NULL;
79 static void client_get_all(ObClient *self, gboolean real);
80 static void client_get_startup_id(ObClient *self);
81 static void client_get_session_ids(ObClient *self);
82 static void client_get_area(ObClient *self);
83 static void client_get_desktop(ObClient *self);
84 static void client_get_state(ObClient *self);
85 static void client_get_shaped(ObClient *self);
86 static void client_get_mwm_hints(ObClient *self);
87 static void client_get_colormap(ObClient *self);
88 static void client_set_desktop_recursive(ObClient *self,
92 static void client_change_allowed_actions(ObClient *self);
93 static void client_change_state(ObClient *self);
94 static void client_change_wm_state(ObClient *self);
95 static void client_apply_startup_state(ObClient *self,
96 gint x, gint y, gint w, gint h);
97 static void client_restore_session_state(ObClient *self);
98 static gboolean client_restore_session_stacking(ObClient *self);
99 static ObAppSettings *client_get_settings_state(ObClient *self);
100 static void client_update_transient_tree(ObClient *self,
101 ObGroup *oldgroup, ObGroup *newgroup,
102 gboolean oldgtran, gboolean newgtran,
104 ObClient *newparent);
105 static void client_present(ObClient *self, gboolean here, gboolean raise,
107 static GSList *client_search_all_top_parents_internal(ObClient *self,
109 ObStackingLayer layer);
110 static void client_call_notifies(ObClient *self, GSList *list);
111 static void client_ping_event(ObClient *self, gboolean dead);
112 static void client_prompt_kill(ObClient *self);
114 void client_startup(gboolean reconfig)
116 if ((client_default_icon = RrImageCacheFind(ob_rr_icons,
117 ob_rr_theme->def_win_icon,
118 ob_rr_theme->def_win_icon_w,
119 ob_rr_theme->def_win_icon_h)))
120 RrImageRef(client_default_icon);
122 client_default_icon = RrImageNew(ob_rr_icons);
123 RrImageAddPicture(client_default_icon,
124 ob_rr_theme->def_win_icon,
125 ob_rr_theme->def_win_icon_w,
126 ob_rr_theme->def_win_icon_h);
129 if (reconfig) return;
134 void client_shutdown(gboolean reconfig)
136 RrImageUnref(client_default_icon);
137 client_default_icon = NULL;
139 if (reconfig) return;
142 static void client_call_notifies(ObClient *self, GSList *list)
146 for (it = list; it; it = g_slist_next(it)) {
147 ClientCallback *d = it->data;
148 d->func(self, d->data);
152 void client_add_destroy_notify(ObClientCallback func, gpointer data)
154 ClientCallback *d = g_new(ClientCallback, 1);
157 client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
160 void client_remove_destroy_notify(ObClientCallback func)
164 for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
165 ClientCallback *d = it->data;
166 if (d->func == func) {
168 client_destroy_notifies =
169 g_slist_delete_link(client_destroy_notifies, it);
175 void client_set_list(void)
177 Window *windows, *win_it;
179 guint size = g_list_length(client_list);
181 /* create an array of the window ids */
183 windows = g_new(Window, size);
185 for (it = client_list; it; it = g_list_next(it), ++win_it)
186 *win_it = ((ObClient*)it->data)->window;
190 OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
191 (gulong*)windows, size);
199 void client_manage(Window window, ObPrompt *prompt)
202 XSetWindowAttributes attrib_set;
203 gboolean activate = FALSE;
204 ObAppSettings *settings;
205 gboolean transient = FALSE;
206 Rect place, *monitor;
207 Time launch_time, map_time;
209 ob_debug("Managing window: 0x%lx", window);
211 map_time = event_get_server_time();
213 /* choose the events we want to receive on the CLIENT window
214 (ObPrompt windows can request events too) */
215 attrib_set.event_mask = CLIENT_EVENTMASK |
216 (prompt ? prompt->event_mask : 0);
217 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
218 XChangeWindowAttributes(obt_display, window,
219 CWEventMask|CWDontPropagate, &attrib_set);
221 /* create the ObClient struct, and populate it from the hints on the
223 self = g_new0(ObClient, 1);
224 self->obwin.type = OB_WINDOW_CLASS_CLIENT;
225 self->window = window;
226 self->prompt = prompt;
228 /* non-zero defaults */
229 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
230 self->gravity = NorthWestGravity;
231 self->desktop = screen_num_desktops; /* always an invalid value */
233 /* get all the stuff off the window */
234 client_get_all(self, TRUE);
236 ob_debug("Window type: %d", self->type);
237 ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
238 ob_debug("Window name: %s class: %s", self->name, self->class);
240 /* now we have all of the window's information so we can set this up.
241 do this before creating the frame, so it can tell that we are still
242 mapping and doesn't go applying things right away */
243 client_setup_decor_and_functions(self, FALSE);
245 /* specify that if we exit, the window should not be destroyed and
246 should be reparented back to root automatically, unless we are managing
247 an internal ObPrompt window */
249 XChangeSaveSet(obt_display, window, SetModeInsert);
251 /* create the decoration frame for the client window */
252 self->frame = frame_new(self);
254 frame_grab_client(self->frame);
256 /* we've grabbed everything and set everything that we need to at mapping
260 /* per-app settings override stuff from client_get_all, and return the
261 settings for other uses too. the returned settings is a shallow copy,
262 that needs to be freed with g_free(). */
263 settings = client_get_settings_state(self);
264 /* the session should get the last say though */
265 client_restore_session_state(self);
267 /* tell startup notification that this app started */
268 launch_time = sn_app_started(self->startup_id, self->class, self->name);
270 /* do this after we have a frame.. it uses the frame to help determine the
271 WM_STATE to apply. */
272 client_change_state(self);
274 /* add ourselves to the focus order */
275 focus_order_add_new(self);
277 /* do this to add ourselves to the stacking list in a non-intrusive way */
278 client_calc_layer(self);
280 /* focus the new window? */
281 if (ob_state() != OB_STATE_STARTING &&
282 (!self->session || self->session->focused) &&
283 /* this means focus=true for window is same as config_focus_new=true */
284 ((config_focus_new || (settings && settings->focus == 1)) ||
285 client_search_focus_tree_full(self)) &&
286 /* this checks for focus=false for the window */
287 (!settings || settings->focus != 0) &&
288 focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE) &&
289 (!focus_cycle_target || !config_focus_dontstop))
294 /* remove the client's border */
295 XSetWindowBorderWidth(obt_display, self->window, 0);
297 /* adjust the frame to the client's size before showing or placing
299 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
300 frame_adjust_client_area(self->frame);
302 /* where the frame was placed is where the window was originally */
304 monitor = screen_physical_area_monitor(screen_find_monitor(&place));
306 /* figure out placement for the window if the window is new */
307 if (ob_state() == OB_STATE_RUNNING) {
308 ob_debug("Positioned: %s @ %d %d",
309 (!self->positioned ? "no" :
310 (self->positioned == PPosition ? "program specified" :
311 (self->positioned == USPosition ? "user specified" :
312 (self->positioned == (PPosition | USPosition) ?
313 "program + user specified" :
314 "BADNESS !?")))), place.x, place.y);
316 ob_debug("Sized: %s @ %d %d",
317 (!self->sized ? "no" :
318 (self->sized == PSize ? "program specified" :
319 (self->sized == USSize ? "user specified" :
320 (self->sized == (PSize | USSize) ?
321 "program + user specified" :
322 "BADNESS !?")))), place.width, place.height);
324 /* splash screens are also returned as TRUE for transient,
325 and so will be forced on screen below */
326 transient = place_client(self, &place.x, &place.y, settings);
328 /* make sure the window is visible. */
329 client_find_onscreen(self, &place.x, &place.y,
330 place.width, place.height,
331 /* non-normal clients has less rules, and
332 windows that are being restored from a
333 session do also. we can assume you want
334 it back where you saved it. Clients saying
335 they placed themselves are subjected to
336 harder rules, ones that are placed by
337 place.c or by the user are allowed partially
338 off-screen and on xinerama divides (ie,
339 it is up to the placement routines to avoid
340 the xinerama divides)
342 splash screens get "transient" set to TRUE by
343 the place_client call
345 ob_state() == OB_STATE_RUNNING &&
347 (!((self->positioned & USPosition) ||
348 (settings && settings->pos_given)) &&
349 client_normal(self) &&
351 /* don't move oldschool fullscreen windows to
352 fit inside the struts (fixes Acroread, which
353 makes its fullscreen window fit the screen
354 but it is not USSize'd or USPosition'd) */
355 !(self->decorations == 0 &&
356 RECT_EQUAL(place, *monitor)))));
359 /* if the window isn't user-sized, then make it fit inside
360 the visible screen area on its monitor. Use basically the same rules
361 for forcing the window on screen in the client_find_onscreen call.
363 do this after place_client, it chooses the monitor!
365 splash screens get "transient" set to TRUE by
366 the place_client call
368 if (ob_state() == OB_STATE_RUNNING &&
370 (!(self->sized & USSize || self->positioned & USPosition) &&
371 client_normal(self) &&
373 /* don't shrink oldschool fullscreen windows to fit inside the
374 struts (fixes Acroread, which makes its fullscreen window
375 fit the screen but it is not USSize'd or USPosition'd) */
376 !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
378 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
380 /* get the size of the frame */
381 place.width += self->frame->size.left + self->frame->size.right;
382 place.height += self->frame->size.top + self->frame->size.bottom;
384 /* fit the window inside the area */
385 place.width = MIN(place.width, a->width);
386 place.height = MIN(place.height, a->height);
388 ob_debug("setting window size to %dx%d", place.width, place.height);
390 /* get the size of the client back */
391 place.width -= self->frame->size.left + self->frame->size.right;
392 place.height -= self->frame->size.top + self->frame->size.bottom;
397 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
398 "some restrictions may apply",
399 self->window, place.x, place.y, place.width, place.height);
401 ob_debug(" but session requested %d, %d %d x %d instead, "
403 self->session->x, self->session->y,
404 self->session->w, self->session->h);
406 /* do this after the window is placed, so the premax/prefullscreen numbers
409 this also places the window
411 client_apply_startup_state(self, place.x, place.y,
412 place.width, place.height);
417 ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
418 activate ? "yes" : "no");
420 gboolean raise = FALSE;
421 gboolean relative_focused;
422 gboolean parent_focused;
424 parent_focused = (focus_client != NULL &&
425 client_search_focus_parent(self));
426 relative_focused = (focus_client != NULL &&
427 (client_search_focus_tree_full(self) != NULL ||
428 client_search_focus_group_full(self) != NULL));
430 /* This is focus stealing prevention */
431 ob_debug_type(OB_DEBUG_FOCUS,
432 "Want to focus new window 0x%x at time %u "
433 "launched at %u (last user interaction time %u)",
434 self->window, map_time, launch_time,
435 event_last_user_time);
436 ob_debug_type(OB_DEBUG_FOCUS,
437 "Current focus_client: %s",
438 (focus_client ? focus_client->title : "(none)"));
439 ob_debug_type(OB_DEBUG_FOCUS,
440 "parent focuesed: %d relative focused: %d",
441 parent_focused, relative_focused);
443 /* if it's on another desktop */
444 if (!(self->desktop == screen_desktop ||
445 self->desktop == DESKTOP_ALL) &&
446 /* the timestamp is from before you changed desktops */
447 launch_time && screen_desktop_user_time &&
448 !event_time_after(launch_time, screen_desktop_user_time))
452 ob_debug_type(OB_DEBUG_FOCUS,
453 "Not focusing the window because its on another "
456 /* If something is focused... */
457 else if (focus_client) {
458 /* If the user is working in another window right now, then don't
460 if (!parent_focused &&
461 event_last_user_time && launch_time &&
462 event_time_after(event_last_user_time, launch_time) &&
463 event_last_user_time != launch_time &&
464 event_time_after(event_last_user_time,
465 map_time - OB_EVENT_USER_TIME_DELAY))
468 ob_debug_type(OB_DEBUG_FOCUS,
469 "Not focusing the window because the user is "
470 "working in another window that is not "
473 /* If the new window is a transient (and its relatives aren't
475 else if (client_has_parent(self) && !relative_focused) {
477 ob_debug_type(OB_DEBUG_FOCUS,
478 "Not focusing the window because it is a "
479 "transient, and its relatives aren't focused");
481 /* Don't steal focus from globally active clients.
482 I stole this idea from KWin. It seems nice.
484 else if (!(focus_client->can_focus ||
485 focus_client->focus_notify))
488 ob_debug_type(OB_DEBUG_FOCUS,
489 "Not focusing the window because a globally "
490 "active client has focus");
492 /* Don't move focus if it's not going to go to this window
494 else if (client_focus_target(self) != self) {
497 ob_debug_type(OB_DEBUG_FOCUS,
498 "Not focusing the window because another window "
499 "would get the focus anyway");
501 /* Don't move focus if the window is not visible on the current
502 desktop and none of its relatives are focused */
503 else if (!(self->desktop == screen_desktop ||
504 self->desktop == DESKTOP_ALL) &&
509 ob_debug_type(OB_DEBUG_FOCUS,
510 "Not focusing the window because it is on "
511 "another desktop and no relatives are focused ");
516 ob_debug_type(OB_DEBUG_FOCUS,
517 "Focus stealing prevention activated for %s at "
518 "time %u (last user interaction time %u)",
519 self->title, map_time, event_last_user_time);
520 /* if the client isn't focused, then hilite it so the user
522 client_hilite(self, TRUE);
523 /* we may want to raise it even tho we're not activating it */
524 if (raise && !client_restore_session_stacking(self))
525 stacking_raise(CLIENT_AS_WINDOW(self));
529 /* This may look rather odd. Well it's because new windows are added
530 to the stacking order non-intrusively. If we're not going to focus
531 the new window or hilite it, then we raise it to the top. This will
532 take affect for things that don't get focused like splash screens.
533 Also if you don't have focus_new enabled, then it's going to get
534 raised to the top. Legacy begets legacy I guess?
536 if (!client_restore_session_stacking(self))
537 stacking_raise(CLIENT_AS_WINDOW(self));
540 mouse_grab_for_client(self, TRUE);
542 /* this has to happen before we try focus the window, but we want it to
543 happen after the client's stacking has been determined or it looks bad
547 if (!config_focus_under_mouse)
548 ignore_start = event_start_ignore_all_enters();
552 if (!config_focus_under_mouse)
553 event_end_ignore_all_enters(ignore_start);
557 gboolean stacked = client_restore_session_stacking(self);
558 client_present(self, FALSE, !stacked, TRUE);
561 /* add to client list/map */
562 client_list = g_list_append(client_list, self);
563 window_add(&self->window, CLIENT_AS_WINDOW(self));
565 /* this has to happen after we're in the client_list */
566 if (STRUT_EXISTS(self->strut))
567 screen_update_areas();
569 /* update the list hints */
572 /* free the ObAppSettings shallow copy */
575 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
576 window, self->frame->window, self->class);
578 hooks_queue(OB_HOOK_WIN_NEW, self);
581 ObClient *client_fake_manage(Window window)
584 ObAppSettings *settings;
586 ob_debug("Pretend-managing window: %lx", window);
588 /* do this minimal stuff to figure out the client's decorations */
590 self = g_new0(ObClient, 1);
591 self->window = window;
593 client_get_all(self, FALSE);
594 /* per-app settings override stuff, and return the settings for other
595 uses too. this returns a shallow copy that needs to be freed */
596 settings = client_get_settings_state(self);
598 client_setup_decor_and_functions(self, FALSE);
600 /* create the decoration frame for the client window and adjust its size */
601 self->frame = frame_new(self);
602 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
604 ob_debug("gave extents left %d right %d top %d bottom %d",
605 self->frame->size.left, self->frame->size.right,
606 self->frame->size.top, self->frame->size.bottom);
608 /* free the ObAppSettings shallow copy */
614 void client_unmanage_all(void)
617 client_unmanage(client_list->data);
620 void client_unmanage(ObClient *self)
625 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
626 self->window, self->frame->window,
627 self->class, self->title ? self->title : "");
629 g_assert(self != NULL);
631 /* we dont want events no more. do this before hiding the frame so we
632 don't generate more events */
633 XSelectInput(obt_display, self->window, NoEventMask);
635 /* ignore enter events from the unmap so it doesnt mess with the focus */
636 if (!config_focus_under_mouse)
637 ignore_start = event_start_ignore_all_enters();
639 frame_hide(self->frame);
640 /* flush to send the hide to the server quickly */
643 if (!config_focus_under_mouse)
644 event_end_ignore_all_enters(ignore_start);
646 mouse_grab_for_client(self, FALSE);
648 /* remove the window from our save set, unless we are managing an internal
651 XChangeSaveSet(obt_display, self->window, SetModeDelete);
653 /* this can't be queued to run later */
654 hooks_run(OB_HOOK_WIN_CLOSE, self);
656 /* update the focus lists */
657 focus_order_remove(self);
658 if (client_focused(self)) {
659 /* don't leave an invalid focus_client */
663 /* if we're prompting to kill the client, close that */
664 prompt_unref(self->kill_prompt);
665 self->kill_prompt = NULL;
667 client_list = g_list_remove(client_list, self);
668 stacking_remove(self);
669 window_remove(self->window);
671 /* once the client is out of the list, update the struts to remove its
673 if (STRUT_EXISTS(self->strut))
674 screen_update_areas();
676 client_call_notifies(self, client_destroy_notifies);
678 /* tell our parent(s) that we're gone */
679 for (it = self->parents; it; it = g_slist_next(it))
680 ((ObClient*)it->data)->transients =
681 g_slist_remove(((ObClient*)it->data)->transients,self);
683 /* tell our transients that we're gone */
684 for (it = self->transients; it; it = g_slist_next(it)) {
685 ((ObClient*)it->data)->parents =
686 g_slist_remove(((ObClient*)it->data)->parents, self);
687 /* we could be keeping our children in a higher layer */
688 client_calc_layer(it->data);
691 /* remove from its group */
693 group_remove(self->group, self);
697 /* restore the window's original geometry so it is not lost */
703 if (self->fullscreen)
704 a = self->pre_fullscreen_area;
705 else if (self->max_horz || self->max_vert) {
706 if (self->max_horz) {
707 a.x = self->pre_max_area.x;
708 a.width = self->pre_max_area.width;
710 if (self->max_vert) {
711 a.y = self->pre_max_area.y;
712 a.height = self->pre_max_area.height;
716 self->fullscreen = self->max_horz = self->max_vert = FALSE;
717 /* let it be moved and resized no matter what */
718 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
719 self->decorations = 0; /* unmanaged windows have no decor */
721 /* give the client its border back */
722 XSetWindowBorderWidth(obt_display, self->window, self->border_width);
724 client_move_resize(self, a.x, a.y, a.width, a.height);
727 /* reparent the window out of the frame, and free the frame */
728 frame_release_client(self->frame);
729 frame_free(self->frame);
732 if (ob_state() != OB_STATE_EXITING) {
733 /* these values should not be persisted across a window
735 OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
736 OBT_PROP_ERASE(self->window, NET_WM_STATE);
737 OBT_PROP_ERASE(self->window, WM_STATE);
739 /* if we're left in an unmapped state, the client wont be mapped.
740 this is bad, since we will no longer be managing the window on
742 XMapWindow(obt_display, self->window);
745 /* these should not be left on the window ever. other window managers
746 don't necessarily use them and it will mess them up (like compiz) */
747 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
748 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
750 /* update the list hints */
753 ob_debug("Unmanaged window 0x%lx", self->window);
755 /* free all data allocated in the client struct */
756 RrImageUnref(self->icon_set);
757 g_slist_free(self->transients);
758 g_free(self->startup_id);
759 g_free(self->wm_command);
761 g_free(self->icon_title);
762 g_free(self->original_title);
766 g_free(self->client_machine);
767 g_free(self->sm_client_id);
771 void client_fake_unmanage(ObClient *self)
773 /* this is all that got allocated to get the decorations */
775 frame_free(self->frame);
779 /*! Returns a new structure containing the per-app settings for this client.
780 The returned structure needs to be freed with g_free. */
781 static ObAppSettings *client_get_settings_state(ObClient *self)
783 ObAppSettings *settings;
786 settings = config_create_app_settings();
788 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
789 ObAppSettings *app = it->data;
790 gboolean match = TRUE;
792 g_assert(app->name != NULL || app->class != NULL);
794 /* we know that either name or class is not NULL so it will have to
795 match to use the rule */
797 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
799 else if (app->class &&
800 !g_pattern_match(app->class,
801 strlen(self->class), self->class, NULL))
803 else if (app->role &&
804 !g_pattern_match(app->role,
805 strlen(self->role), self->role, NULL))
807 else if ((signed)app->type >= 0 && app->type != self->type)
811 ob_debug("Window matching: %s", app->name);
813 /* copy the settings to our struct, overriding the existing
814 settings if they are not defaults */
815 config_app_settings_copy_non_defaults(app, settings);
819 if (settings->shade != -1)
820 self->shaded = !!settings->shade;
821 if (settings->decor != -1)
822 self->undecorated = !settings->decor;
823 if (settings->iconic != -1)
824 self->iconic = !!settings->iconic;
825 if (settings->skip_pager != -1)
826 self->skip_pager = !!settings->skip_pager;
827 if (settings->skip_taskbar != -1)
828 self->skip_taskbar = !!settings->skip_taskbar;
830 if (settings->max_vert != -1)
831 self->max_vert = !!settings->max_vert;
832 if (settings->max_horz != -1)
833 self->max_horz = !!settings->max_horz;
835 if (settings->fullscreen != -1)
836 self->fullscreen = !!settings->fullscreen;
838 if (settings->desktop) {
839 if (settings->desktop == DESKTOP_ALL)
840 self->desktop = settings->desktop;
841 else if (settings->desktop > 0 &&
842 settings->desktop <= screen_num_desktops)
843 self->desktop = settings->desktop - 1;
846 if (settings->layer == -1) {
850 else if (settings->layer == 0) {
854 else if (settings->layer == 1) {
861 static void client_restore_session_state(ObClient *self)
865 ob_debug_type(OB_DEBUG_SM,
866 "Restore session for client %s", self->title);
868 if (!(it = session_state_find(self))) {
869 ob_debug_type(OB_DEBUG_SM,
870 "Session data not found for client %s", self->title);
874 self->session = it->data;
876 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
879 RECT_SET_POINT(self->area, self->session->x, self->session->y);
880 self->positioned = USPosition;
881 self->sized = USSize;
882 if (self->session->w > 0)
883 self->area.width = self->session->w;
884 if (self->session->h > 0)
885 self->area.height = self->session->h;
886 XResizeWindow(obt_display, self->window,
887 self->area.width, self->area.height);
889 self->desktop = (self->session->desktop == DESKTOP_ALL ?
890 self->session->desktop :
891 MIN(screen_num_desktops - 1, self->session->desktop));
892 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
894 self->shaded = self->session->shaded;
895 self->iconic = self->session->iconic;
896 self->skip_pager = self->session->skip_pager;
897 self->skip_taskbar = self->session->skip_taskbar;
898 self->fullscreen = self->session->fullscreen;
899 self->above = self->session->above;
900 self->below = self->session->below;
901 self->max_horz = self->session->max_horz;
902 self->max_vert = self->session->max_vert;
903 self->undecorated = self->session->undecorated;
906 static gboolean client_restore_session_stacking(ObClient *self)
910 if (!self->session) return FALSE;
912 mypos = g_list_find(session_saved_state, self->session);
913 if (!mypos) return FALSE;
915 /* start above me and look for the first client */
916 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
919 for (cit = client_list; cit; cit = g_list_next(cit)) {
920 ObClient *c = cit->data;
921 /* found a client that was in the session, so go below it */
922 if (c->session == it->data) {
923 stacking_below(CLIENT_AS_WINDOW(self),
924 CLIENT_AS_WINDOW(cit->data));
932 void client_move_onscreen(ObClient *self, gboolean rude)
934 gint x = self->area.x;
935 gint y = self->area.y;
936 if (client_find_onscreen(self, &x, &y,
938 self->area.height, rude)) {
939 client_move(self, x, y);
943 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
946 gint ox = *x, oy = *y;
947 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
953 RECT_SET(desired, *x, *y, w, h);
954 frame_rect_to_frame(self->frame, &desired);
956 /* get where the frame would be */
957 frame_client_gravity(self->frame, x, y);
959 /* get the requested size of the window with decorations */
960 fw = self->frame->size.left + w + self->frame->size.right;
961 fh = self->frame->size.top + h + self->frame->size.bottom;
963 /* If rudeness wasn't requested, then still be rude in a given direction
964 if the client is not moving, only resizing in that direction */
966 Point oldtl, oldtr, oldbl, oldbr;
967 Point newtl, newtr, newbl, newbr;
968 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
970 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
971 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
972 self->frame->area.y + self->frame->area.height - 1);
973 POINT_SET(oldtr, oldbr.x, oldtl.y);
974 POINT_SET(oldbl, oldtl.x, oldbr.y);
976 POINT_SET(newtl, *x, *y);
977 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
978 POINT_SET(newtr, newbr.x, newtl.y);
979 POINT_SET(newbl, newtl.x, newbr.y);
981 /* is it moving or just resizing from some corner? */
982 stationary_l = oldtl.x == newtl.x;
983 stationary_r = oldtr.x == newtr.x;
984 stationary_t = oldtl.y == newtl.y;
985 stationary_b = oldbl.y == newbl.y;
987 /* if left edge is growing and didnt move right edge */
988 if (stationary_r && newtl.x < oldtl.x)
990 /* if right edge is growing and didnt move left edge */
991 if (stationary_l && newtr.x > oldtr.x)
993 /* if top edge is growing and didnt move bottom edge */
994 if (stationary_b && newtl.y < oldtl.y)
996 /* if bottom edge is growing and didnt move top edge */
997 if (stationary_t && newbl.y > oldbl.y)
1001 /* we iterate through every monitor that the window is at least partially
1002 on, to make sure it is obeying the rules on them all
1004 if the window does not appear on any monitors, then use the first one
1007 for (i = 0; i < screen_num_monitors; ++i) {
1010 if (!screen_physical_area_monitor_contains(i, &desired)) {
1011 if (i < screen_num_monitors - 1 || found_mon)
1014 /* the window is not inside any monitor! so just use the first
1016 a = screen_area(self->desktop, 0, NULL);
1019 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1022 /* This makes sure windows aren't entirely outside of the screen so you
1023 can't see them at all.
1024 It makes sure 10% of the window is on the screen at least. And don't
1025 let it move itself off the top of the screen, which would hide the
1026 titlebar on you. (The user can still do this if they want too, it's
1027 only limiting the application.
1029 if (client_normal(self)) {
1030 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1031 *x = a->x + a->width - fw/10;
1032 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1033 *y = a->y + a->height - fh/10;
1034 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1035 *x = a->x - fw*9/10;
1036 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1037 *y = a->y - fh*9/10;
1040 /* This here doesn't let windows even a pixel outside the
1041 struts/screen. When called from client_manage, programs placing
1042 themselves are forced completely onscreen, while things like
1043 xterm -geometry resolution-width/2 will work fine. Trying to
1044 place it completely offscreen will be handled in the above code.
1045 Sorry for this confused comment, i am tired. */
1046 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1047 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1048 *x = a->x + MAX(0, a->width - fw);
1050 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1051 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1052 *y = a->y + MAX(0, a->height - fh);
1057 /* get where the client should be */
1058 frame_frame_gravity(self->frame, x, y);
1060 return ox != *x || oy != *y;
1063 static void client_get_all(ObClient *self, gboolean real)
1065 /* this is needed for the frame to set itself up */
1066 client_get_area(self);
1068 /* these things can change the decor and functions of the window */
1070 client_get_mwm_hints(self);
1071 /* this can change the mwmhints for special cases */
1072 client_get_type_and_transientness(self);
1073 client_get_state(self);
1074 client_update_normal_hints(self);
1076 /* get the session related properties, these can change decorations
1077 from per-app settings */
1078 client_get_session_ids(self);
1080 /* now we got everything that can affect the decorations */
1084 /* get this early so we have it for debugging */
1085 client_update_title(self);
1087 client_update_protocols(self);
1089 client_update_wmhints(self);
1090 /* this may have already been called from client_update_wmhints */
1091 if (!self->parents && !self->transient_for_group)
1092 client_update_transient_for(self);
1094 client_get_startup_id(self);
1095 client_get_desktop(self);/* uses transient data/group/startup id if a
1096 desktop is not specified */
1097 client_get_shaped(self);
1100 /* a couple type-based defaults for new windows */
1102 /* this makes sure that these windows appear on all desktops */
1103 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1104 self->desktop = DESKTOP_ALL;
1108 client_update_sync_request_counter(self);
1111 client_get_colormap(self);
1112 client_update_strut(self);
1113 client_update_icons(self);
1114 client_update_icon_geometry(self);
1117 static void client_get_startup_id(ObClient *self)
1119 if (!(OBT_PROP_GETS(self->window, NET_STARTUP_ID, utf8,
1120 &self->startup_id)))
1122 OBT_PROP_GETS(self->group->leader,
1123 NET_STARTUP_ID, utf8, &self->startup_id);
1126 static void client_get_area(ObClient *self)
1128 XWindowAttributes wattrib;
1131 ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
1132 g_assert(ret != BadWindow);
1134 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1135 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1136 self->border_width = wattrib.border_width;
1138 ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
1139 wattrib.width, wattrib.height, wattrib.border_width);
1142 static void client_get_desktop(ObClient *self)
1144 guint32 d = screen_num_desktops; /* an always-invalid value */
1146 if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
1147 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1148 self->desktop = screen_num_desktops - 1;
1151 ob_debug("client requested desktop 0x%x", self->desktop);
1154 gboolean first = TRUE;
1155 guint all = screen_num_desktops; /* not a valid value */
1157 /* if they are all on one desktop, then open it on the
1159 for (it = self->parents; it; it = g_slist_next(it)) {
1160 ObClient *c = it->data;
1162 if (c->desktop == DESKTOP_ALL) continue;
1168 else if (all != c->desktop)
1169 all = screen_num_desktops; /* make it invalid */
1171 if (all != screen_num_desktops) {
1172 self->desktop = all;
1174 ob_debug("client desktop set from parents: 0x%x",
1177 /* try get from the startup-notification protocol */
1178 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1179 if (self->desktop >= screen_num_desktops &&
1180 self->desktop != DESKTOP_ALL)
1181 self->desktop = screen_num_desktops - 1;
1182 ob_debug("client desktop set from startup-notification: 0x%x",
1185 /* defaults to the current desktop */
1187 self->desktop = screen_desktop;
1188 ob_debug("client desktop set to the current desktop: %d",
1194 static void client_get_state(ObClient *self)
1199 if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
1201 for (i = 0; i < num; ++i) {
1202 if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1204 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1205 self->shaded = TRUE;
1206 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1207 self->iconic = TRUE;
1208 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1209 self->skip_taskbar = TRUE;
1210 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1211 self->skip_pager = TRUE;
1212 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1213 self->fullscreen = TRUE;
1214 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
1215 self->max_vert = TRUE;
1216 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
1217 self->max_horz = TRUE;
1218 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1220 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
1222 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
1223 self->demands_attention = TRUE;
1224 else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
1225 self->undecorated = TRUE;
1226 else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_LOCKED))
1227 self->locked = TRUE;
1234 static void client_get_shaped(ObClient *self)
1236 self->shaped = FALSE;
1238 if (obt_display_extension_shape) {
1243 XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
1245 XShapeQueryExtents(obt_display, self->window, &s, &foo,
1246 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1253 void client_update_transient_for(ObClient *self)
1256 ObClient *target = NULL;
1257 gboolean trangroup = FALSE;
1259 if (XGetTransientForHint(obt_display, self->window, &t)) {
1260 if (t != self->window) { /* can't be transient to itself! */
1261 ObWindow *tw = window_find(t);
1262 /* if this happens then we need to check for it */
1263 g_assert(tw != CLIENT_AS_WINDOW(self));
1264 if (tw && WINDOW_IS_CLIENT(tw)) {
1265 /* watch out for windows with a parent that is something
1266 different, like a dockapp for example */
1267 target = WINDOW_AS_CLIENT(tw);
1271 /* Setting the transient_for to Root is actually illegal, however
1272 applications from time have done this to specify transient for
1274 if (!target && self->group && t == obt_root(ob_screen))
1276 } else if (self->group && self->transient)
1279 client_update_transient_tree(self, self->group, self->group,
1280 self->transient_for_group, trangroup,
1281 client_direct_parent(self), target);
1282 self->transient_for_group = trangroup;
1286 static void client_update_transient_tree(ObClient *self,
1287 ObGroup *oldgroup, ObGroup *newgroup,
1288 gboolean oldgtran, gboolean newgtran,
1289 ObClient* oldparent,
1290 ObClient *newparent)
1295 g_assert(!oldgtran || oldgroup);
1296 g_assert(!newgtran || newgroup);
1297 g_assert((!oldgtran && !oldparent) ||
1298 (oldgtran && !oldparent) ||
1299 (!oldgtran && oldparent));
1300 g_assert((!newgtran && !newparent) ||
1301 (newgtran && !newparent) ||
1302 (!newgtran && newparent));
1305 Group transient windows are not allowed to have other group
1306 transient windows as their children.
1309 /* No change has occured */
1310 if (oldgroup == newgroup &&
1311 oldgtran == newgtran &&
1312 oldparent == newparent) return;
1314 /** Remove the client from the transient tree **/
1316 for (it = self->transients; it; it = next) {
1317 next = g_slist_next(it);
1319 self->transients = g_slist_delete_link(self->transients, it);
1320 c->parents = g_slist_remove(c->parents, self);
1322 for (it = self->parents; it; it = next) {
1323 next = g_slist_next(it);
1325 self->parents = g_slist_delete_link(self->parents, it);
1326 c->transients = g_slist_remove(c->transients, self);
1329 /** Re-add the client to the transient tree **/
1331 /* If we're transient for a group then we need to add ourselves to all our
1334 for (it = newgroup->members; it; it = g_slist_next(it)) {
1337 !client_search_top_direct_parent(c)->transient_for_group &&
1340 c->transients = g_slist_prepend(c->transients, self);
1341 self->parents = g_slist_prepend(self->parents, c);
1346 /* If we are now transient for a single window we need to add ourselves to
1349 WARNING: Cyclical transient-ness is possible if two windows are
1350 transient for eachother.
1352 else if (newparent &&
1353 /* don't make ourself its child if it is already our child */
1354 !client_is_direct_child(self, newparent) &&
1355 client_normal(newparent))
1357 newparent->transients = g_slist_prepend(newparent->transients, self);
1358 self->parents = g_slist_prepend(self->parents, newparent);
1361 /* Add any group transient windows to our children. But if we're transient
1362 for the group, then other group transients are not our children.
1364 WARNING: Cyclical transient-ness is possible. For e.g. if:
1365 A is transient for the group
1366 B is transient for A
1367 C is transient for B
1368 A can't be transient for C or we have a cycle
1370 if (!newgtran && newgroup &&
1372 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1373 client_normal(self))
1375 for (it = newgroup->members; it; it = g_slist_next(it)) {
1377 if (c != self && c->transient_for_group &&
1378 /* Don't make it our child if it is already our parent */
1379 !client_is_direct_child(c, self))
1381 self->transients = g_slist_prepend(self->transients, c);
1382 c->parents = g_slist_prepend(c->parents, self);
1387 /** If we change our group transient-ness, our children change their
1388 effective group transient-ness, which affects how they relate to other
1391 for (it = self->transients; it; it = g_slist_next(it)) {
1393 if (!c->transient_for_group)
1394 client_update_transient_tree(c, c->group, c->group,
1395 c->transient_for_group,
1396 c->transient_for_group,
1397 client_direct_parent(c),
1398 client_direct_parent(c));
1402 static void client_get_mwm_hints(ObClient *self)
1407 self->mwmhints.flags = 0; /* default to none */
1409 if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1411 if (num >= OB_MWM_ELEMENTS) {
1412 self->mwmhints.flags = hints[0];
1413 self->mwmhints.functions = hints[1];
1414 self->mwmhints.decorations = hints[2];
1420 void client_get_type_and_transientness(ObClient *self)
1427 self->transient = FALSE;
1429 if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1430 /* use the first value that we know about in the array */
1431 for (i = 0; i < num; ++i) {
1432 if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1433 self->type = OB_CLIENT_TYPE_DESKTOP;
1434 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1435 self->type = OB_CLIENT_TYPE_DOCK;
1436 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1437 self->type = OB_CLIENT_TYPE_TOOLBAR;
1438 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1439 self->type = OB_CLIENT_TYPE_MENU;
1440 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1441 self->type = OB_CLIENT_TYPE_UTILITY;
1442 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1443 self->type = OB_CLIENT_TYPE_SPLASH;
1444 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1445 self->type = OB_CLIENT_TYPE_DIALOG;
1446 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1447 self->type = OB_CLIENT_TYPE_NORMAL;
1448 else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1450 /* prevent this window from getting any decor or
1452 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1453 OB_MWM_FLAG_DECORATIONS);
1454 self->mwmhints.decorations = 0;
1455 self->mwmhints.functions = 0;
1457 if (self->type != (ObClientType) -1)
1458 break; /* grab the first legit type */
1463 if (XGetTransientForHint(obt_display, self->window, &t))
1464 self->transient = TRUE;
1466 if (self->type == (ObClientType) -1) {
1467 /*the window type hint was not set, which means we either classify
1468 ourself as a normal window or a dialog, depending on if we are a
1470 if (self->transient)
1471 self->type = OB_CLIENT_TYPE_DIALOG;
1473 self->type = OB_CLIENT_TYPE_NORMAL;
1476 /* then, based on our type, we can update our transientness.. */
1477 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1478 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1479 self->type == OB_CLIENT_TYPE_MENU ||
1480 self->type == OB_CLIENT_TYPE_UTILITY)
1482 self->transient = TRUE;
1486 void client_update_protocols(ObClient *self)
1491 self->focus_notify = FALSE;
1492 self->delete_window = FALSE;
1494 if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1495 for (i = 0; i < num_ret; ++i) {
1496 if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1497 /* this means we can request the window to close */
1498 self->delete_window = TRUE;
1499 else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1500 /* if this protocol is requested, then the window will be
1501 notified whenever we want it to receive focus */
1502 self->focus_notify = TRUE;
1503 else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1504 /* if this protocol is requested, then the window will allow
1505 pings to determine if it is still alive */
1508 else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1509 /* if this protocol is requested, then resizing the
1510 window will be synchronized between the frame and the
1512 self->sync_request = TRUE;
1520 void client_update_sync_request_counter(ObClient *self)
1524 if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1526 self->sync_counter = i;
1528 self->sync_counter = None;
1532 static void client_get_colormap(ObClient *self)
1534 XWindowAttributes wa;
1536 if (XGetWindowAttributes(obt_display, self->window, &wa))
1537 client_update_colormap(self, wa.colormap);
1540 void client_update_colormap(ObClient *self, Colormap colormap)
1542 if (colormap == self->colormap) return;
1544 ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1546 if (client_focused(self)) {
1547 screen_install_colormap(self, FALSE); /* uninstall old one */
1548 self->colormap = colormap;
1549 screen_install_colormap(self, TRUE); /* install new one */
1551 self->colormap = colormap;
1554 void client_update_normal_hints(ObClient *self)
1560 self->min_ratio = 0.0f;
1561 self->max_ratio = 0.0f;
1562 SIZE_SET(self->size_inc, 1, 1);
1563 SIZE_SET(self->base_size, -1, -1);
1564 SIZE_SET(self->min_size, 0, 0);
1565 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1567 /* get the hints from the window */
1568 if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
1569 /* normal windows can't request placement! har har
1570 if (!client_normal(self))
1572 self->positioned = (size.flags & (PPosition|USPosition));
1573 self->sized = (size.flags & (PSize|USSize));
1575 if (size.flags & PWinGravity)
1576 self->gravity = size.win_gravity;
1578 if (size.flags & PAspect) {
1579 if (size.min_aspect.y)
1581 (gfloat) size.min_aspect.x / size.min_aspect.y;
1582 if (size.max_aspect.y)
1584 (gfloat) size.max_aspect.x / size.max_aspect.y;
1587 if (size.flags & PMinSize)
1588 SIZE_SET(self->min_size, size.min_width, size.min_height);
1590 if (size.flags & PMaxSize)
1591 SIZE_SET(self->max_size, size.max_width, size.max_height);
1593 if (size.flags & PBaseSize)
1594 SIZE_SET(self->base_size, size.base_width, size.base_height);
1596 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1597 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1599 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1600 self->min_size.width, self->min_size.height,
1601 self->max_size.width, self->max_size.height);
1602 ob_debug("size inc (%d %d) base size (%d %d)",
1603 self->size_inc.width, self->size_inc.height,
1604 self->base_size.width, self->base_size.height);
1607 ob_debug("Normal hints: not set");
1610 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1612 /* start with everything (cept fullscreen) */
1614 (OB_FRAME_DECOR_TITLEBAR |
1615 OB_FRAME_DECOR_HANDLE |
1616 OB_FRAME_DECOR_GRIPS |
1617 OB_FRAME_DECOR_BORDER |
1618 OB_FRAME_DECOR_ICON |
1619 OB_FRAME_DECOR_ALLDESKTOPS |
1620 OB_FRAME_DECOR_ICONIFY |
1621 OB_FRAME_DECOR_MAXIMIZE |
1622 OB_FRAME_DECOR_SHADE |
1623 OB_FRAME_DECOR_CLOSE);
1625 (OB_CLIENT_FUNC_RESIZE |
1626 OB_CLIENT_FUNC_MOVE |
1627 OB_CLIENT_FUNC_ICONIFY |
1628 OB_CLIENT_FUNC_MAXIMIZE |
1629 OB_CLIENT_FUNC_SHADE |
1630 OB_CLIENT_FUNC_CLOSE |
1631 OB_CLIENT_FUNC_BELOW |
1632 OB_CLIENT_FUNC_ABOVE |
1633 OB_CLIENT_FUNC_UNDECORATE);
1635 if (!(self->min_size.width < self->max_size.width ||
1636 self->min_size.height < self->max_size.height))
1637 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1639 switch (self->type) {
1640 case OB_CLIENT_TYPE_NORMAL:
1641 /* normal windows retain all of the possible decorations and
1642 functionality, and can be fullscreen */
1643 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1646 case OB_CLIENT_TYPE_DIALOG:
1647 /* sometimes apps make dialog windows fullscreen for some reason (for
1648 e.g. kpdf does this..) */
1649 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1652 case OB_CLIENT_TYPE_UTILITY:
1653 /* these windows don't have anything added or removed by default */
1656 case OB_CLIENT_TYPE_MENU:
1657 case OB_CLIENT_TYPE_TOOLBAR:
1658 /* these windows can't iconify or maximize */
1659 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1660 OB_FRAME_DECOR_MAXIMIZE);
1661 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1662 OB_CLIENT_FUNC_MAXIMIZE);
1665 case OB_CLIENT_TYPE_SPLASH:
1666 /* these don't get get any decorations, and the only thing you can
1667 do with them is move them */
1668 self->decorations = 0;
1669 self->functions = OB_CLIENT_FUNC_MOVE;
1672 case OB_CLIENT_TYPE_DESKTOP:
1673 /* these windows are not manipulated by the window manager */
1674 self->decorations = 0;
1675 self->functions = 0;
1678 case OB_CLIENT_TYPE_DOCK:
1679 /* these windows are not manipulated by the window manager, but they
1680 can set below layer which has a special meaning */
1681 self->decorations = 0;
1682 self->functions = OB_CLIENT_FUNC_BELOW;
1686 /* Mwm Hints are applied subtractively to what has already been chosen for
1687 decor and functionality */
1688 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1689 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1690 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1691 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1693 /* if the mwm hints request no handle or title, then all
1694 decorations are disabled, but keep the border if that's
1696 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1697 self->decorations = OB_FRAME_DECOR_BORDER;
1699 self->decorations = 0;
1704 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1705 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1706 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1707 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1708 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1709 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1710 /* dont let mwm hints kill any buttons
1711 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1712 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1713 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1714 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1716 /* dont let mwm hints kill the close button
1717 if (! (self->mwmhints.functions & MwmFunc_Close))
1718 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1722 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1723 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1724 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1725 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1726 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1727 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1729 /* can't maximize without moving/resizing */
1730 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1731 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1732 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1733 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1734 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1737 if (self->max_horz && self->max_vert) {
1738 /* you can't resize fully maximized windows */
1739 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1740 /* kill the handle on fully maxed windows */
1741 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1744 /* If there are no decorations to remove, don't allow the user to try
1746 if (self->decorations == 0)
1747 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1749 /* finally, the user can have requested no decorations, which overrides
1750 everything (but doesnt give it a border if it doesnt have one) */
1751 if (self->undecorated)
1752 self->decorations = 0;
1754 /* if we don't have a titlebar, then we cannot shade! */
1755 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1756 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1758 /* now we need to check against rules for the client's current state */
1759 if (self->fullscreen) {
1760 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1761 OB_CLIENT_FUNC_FULLSCREEN |
1762 OB_CLIENT_FUNC_ICONIFY);
1763 self->decorations = 0;
1766 client_change_allowed_actions(self);
1769 /* force reconfigure to make sure decorations are updated */
1770 client_reconfigure(self, TRUE);
1773 static void client_change_allowed_actions(ObClient *self)
1778 /* desktop windows are kept on all desktops */
1779 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1780 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1782 if (self->functions & OB_CLIENT_FUNC_SHADE)
1783 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1784 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1785 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1786 if (self->functions & OB_CLIENT_FUNC_MOVE)
1787 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1788 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1789 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1790 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1791 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1792 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1793 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1794 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1795 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1796 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1798 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1799 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1800 if (self->functions & OB_CLIENT_FUNC_BELOW)
1801 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1802 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1803 actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1805 OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1807 /* make sure the window isn't breaking any rules now
1809 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1810 it can't be iconified with its parent
1813 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1814 if (self->frame) client_shade(self, FALSE);
1815 else self->shaded = FALSE;
1817 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1818 if (self->frame) client_fullscreen(self, FALSE);
1819 else self->fullscreen = FALSE;
1821 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1823 if (self->frame) client_maximize(self, FALSE, 0);
1824 else self->max_vert = self->max_horz = FALSE;
1828 void client_update_wmhints(ObClient *self)
1832 /* assume a window takes input if it doesn't specify */
1833 self->can_focus = TRUE;
1835 if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
1838 if (hints->flags & InputHint)
1839 self->can_focus = hints->input;
1841 /* only do this when first managing the window *AND* when we aren't
1843 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1844 if (hints->flags & StateHint)
1845 self->iconic = hints->initial_state == IconicState;
1848 self->urgent = (hints->flags & XUrgencyHint);
1849 if (self->urgent && !ur)
1850 client_hilite(self, TRUE);
1851 else if (!self->urgent && ur && self->demands_attention)
1852 client_hilite(self, FALSE);
1854 if (!(hints->flags & WindowGroupHint))
1855 hints->window_group = None;
1857 /* did the group state change? */
1858 if (hints->window_group !=
1859 (self->group ? self->group->leader : None))
1861 ObGroup *oldgroup = self->group;
1863 /* remove from the old group if there was one */
1865 group_remove(self->group, self);
1869 /* add ourself to the group if we have one */
1870 if (hints->window_group != None) {
1871 self->group = group_add(hints->window_group, self);
1874 /* Put ourselves into the new group's transient tree, and remove
1875 ourselves from the old group's */
1876 client_update_transient_tree(self, oldgroup, self->group,
1877 self->transient_for_group,
1878 self->transient_for_group,
1879 client_direct_parent(self),
1880 client_direct_parent(self));
1882 /* Lastly, being in a group, or not, can change if the window is
1883 transient for anything.
1885 The logic for this is:
1886 self->transient = TRUE always if the window wants to be
1887 transient for something, even if transient_for was NULL because
1888 it wasn't in a group before.
1890 If parents was NULL and oldgroup was NULL we can assume
1891 that when we add the new group, it will become transient for
1894 If transient_for_group is TRUE, then it must have already
1895 had a group. If it is getting a new group, the above call to
1896 client_update_transient_tree has already taken care of
1897 everything ! If it is losing all group status then it will
1898 no longer be transient for anything and that needs to be
1901 if (self->transient &&
1902 ((self->parents == NULL && oldgroup == NULL) ||
1903 (self->transient_for_group && !self->group)))
1904 client_update_transient_for(self);
1907 /* the WM_HINTS can contain an icon */
1908 if (hints->flags & IconPixmapHint)
1909 client_update_icons(self);
1915 void client_update_title(ObClient *self)
1918 gchar *visible = NULL;
1920 g_free(self->title);
1921 g_free(self->original_title);
1924 if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) {
1925 /* try old x stuff */
1926 if (!(OBT_PROP_GETS(self->window, WM_NAME, locale, &data)
1927 || OBT_PROP_GETS(self->window, WM_NAME, utf8, &data))) {
1928 if (self->transient) {
1930 GNOME alert windows are not given titles:
1931 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1933 data = g_strdup("");
1935 data = g_strdup(_("Unnamed Window"));
1938 self->original_title = g_strdup(data);
1940 if (self->client_machine) {
1941 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
1946 if (self->not_responding) {
1948 if (self->kill_level > 0)
1949 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
1951 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
1955 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible);
1956 self->title = visible;
1959 frame_adjust_title(self->frame);
1961 /* update the icon title */
1963 g_free(self->icon_title);
1966 if (!OBT_PROP_GETS(self->window, NET_WM_ICON_NAME, utf8, &data))
1967 /* try old x stuff */
1968 if (!(OBT_PROP_GETS(self->window, WM_ICON_NAME, locale, &data) ||
1969 OBT_PROP_GETS(self->window, WM_ICON_NAME, utf8, &data)))
1970 data = g_strdup(self->title);
1972 if (self->client_machine) {
1973 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
1978 if (self->not_responding) {
1980 if (self->kill_level > 0)
1981 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
1983 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
1987 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
1988 self->icon_title = visible;
1991 void client_update_strut(ObClient *self)
1995 gboolean got = FALSE;
1998 if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
2003 STRUT_PARTIAL_SET(strut,
2004 data[0], data[2], data[1], data[3],
2005 data[4], data[5], data[8], data[9],
2006 data[6], data[7], data[10], data[11]);
2012 OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
2018 /* use the screen's width/height */
2019 a = screen_physical_area_all_monitors();
2021 STRUT_PARTIAL_SET(strut,
2022 data[0], data[2], data[1], data[3],
2023 a->y, a->y + a->height - 1,
2024 a->x, a->x + a->width - 1,
2025 a->y, a->y + a->height - 1,
2026 a->x, a->x + a->width - 1);
2033 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2034 0, 0, 0, 0, 0, 0, 0, 0);
2036 if (!STRUT_EQUAL(strut, self->strut)) {
2037 self->strut = strut;
2039 /* updating here is pointless while we're being mapped cuz we're not in
2040 the client list yet */
2042 screen_update_areas();
2046 void client_update_icons(ObClient *self)
2051 guint num_seen; /* number of icons present */
2056 /* grab the server, because we might be setting the window's icon and
2057 we don't want them to set it in between and we overwrite their own
2061 if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
2062 /* figure out how many valid icons are in here */
2065 while (i + 2 < num) { /* +2 is to make sure there is a w and h */
2068 /* watch for the data being too small for the specified size,
2069 or for zero sized icons. */
2070 if (i + w*h > num || w == 0 || h == 0) break;
2072 /* convert it to the right bit order for ObRender */
2073 for (j = 0; j < w*h; ++j)
2075 (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2076 (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
2077 (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
2078 (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
2080 /* is it in the cache? */
2081 img = RrImageCacheFind(ob_rr_icons, &data[i], w, h);
2082 if (img) RrImageRef(img); /* own it */
2087 /* don't bother looping anymore if we already found it in the cache
2088 since we'll just use that! */
2092 /* if it's not in the cache yet, then add it to the cache now.
2093 we have already converted it to the correct bit order above */
2094 if (!img && num_seen > 0) {
2095 img = RrImageNew(ob_rr_icons);
2097 for (j = 0; j < num_seen; ++j) {
2100 RrImageAddPicture(img, &data[i], w, h);
2108 /* if we didn't find an image from the NET_WM_ICON stuff, then try the
2113 if ((hints = XGetWMHints(obt_display, self->window))) {
2114 if (hints->flags & IconPixmapHint) {
2116 obt_display_ignore_errors(TRUE);
2117 xicon = RrPixmapToRGBA(ob_rr_inst,
2119 (hints->flags & IconMaskHint ?
2120 hints->icon_mask : None),
2121 (gint*)&w, (gint*)&h, &data);
2122 obt_display_ignore_errors(FALSE);
2125 if (w > 0 && h > 0) {
2126 /* is this icon in the cache yet? */
2127 img = RrImageCacheFind(ob_rr_icons, data, w, h);
2128 if (img) RrImageRef(img); /* own it */
2130 /* if not, then add it */
2132 img = RrImageNew(ob_rr_icons);
2133 RrImageAddPicture(img, data, w, h);
2144 /* set the client's icons to be whatever we found */
2145 RrImageUnref(self->icon_set);
2146 self->icon_set = img;
2148 /* if the client has no icon at all, then we set a default icon onto it.
2149 but, if it has parents, then one of them will have an icon already
2151 if (!self->icon_set && !self->parents) {
2152 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2153 gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
2155 r = g_random_int_range(0,255);
2156 g = g_random_int_range(0,255);
2157 b = g_random_int_range(0,255);
2159 w = ob_rr_theme->def_win_icon_w;
2160 h = ob_rr_theme->def_win_icon_h;
2161 ldata = g_new(gulong, w*h+2);
2164 for (i = 0; i < w*h; ++i)
2165 ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2166 ((((icon[i] >> RrDefaultRedOffset) & 0xff)*r/255) << 16) +
2167 ((((icon[i] >> RrDefaultGreenOffset) & 0xff)*g/255) << 8) +
2168 ((((icon[i] >> RrDefaultBlueOffset) & 0xff)*b/255) << 0);
2169 OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
2171 } else if (self->frame)
2172 /* don't draw the icon empty if we're just setting one now anyways,
2173 we'll get the property change any second */
2174 frame_adjust_icon(self->frame);
2179 void client_update_icon_geometry(ObClient *self)
2184 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2186 if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
2190 /* don't let them set it with an area < 0 */
2191 RECT_SET(self->icon_geometry, data[0], data[1],
2192 MAX(data[2],0), MAX(data[3],0));
2197 static void client_get_session_ids(ObClient *self)
2204 if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
2207 /* get the SM_CLIENT_ID */
2210 got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
2212 OBT_PROP_GETS(self->window, SM_CLIENT_ID, locale, &self->sm_client_id);
2214 /* get the WM_CLASS (name and class). make them "" if they are not
2218 got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
2220 got = OBT_PROP_GETSS(self->window, WM_CLASS, locale, &ss);
2224 self->name = g_strdup(ss[0]);
2226 self->class = g_strdup(ss[1]);
2231 if (self->name == NULL) self->name = g_strdup("");
2232 if (self->class == NULL) self->class = g_strdup("");
2234 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2237 got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
2239 got = OBT_PROP_GETS(self->window, WM_WINDOW_ROLE, locale, &s);
2244 self->role = g_strdup("");
2246 /* get the WM_COMMAND */
2250 got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
2252 got = OBT_PROP_GETSS(self->window, WM_COMMAND, locale, &ss);
2255 /* merge/mash them all together */
2256 gchar *merge = NULL;
2259 for (i = 0; ss[i]; ++i) {
2262 merge = g_strconcat(merge, ss[i], NULL);
2264 merge = g_strconcat(ss[i], NULL);
2269 self->wm_command = merge;
2272 /* get the WM_CLIENT_MACHINE */
2275 got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
2277 got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, locale, &s);
2280 gchar localhost[128];
2283 gethostname(localhost, 127);
2284 localhost[127] = '\0';
2285 if (strcmp(localhost, s) != 0)
2286 self->client_machine = s;
2290 /* see if it has the PID set too (the PID requires that the
2291 WM_CLIENT_MACHINE be set) */
2292 if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
2297 static void client_change_wm_state(ObClient *self)
2302 old = self->wmstate;
2304 if (self->shaded || self->iconic ||
2305 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2307 self->wmstate = IconicState;
2309 self->wmstate = NormalState;
2311 if (old != self->wmstate) {
2312 OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
2313 self->wmstate, 1, 0, 0, 0);
2315 state[0] = self->wmstate;
2317 OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
2321 static void client_change_state(ObClient *self)
2323 gulong netstate[13];
2328 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2330 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2332 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2333 if (self->skip_taskbar)
2334 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2335 if (self->skip_pager)
2336 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2337 if (self->fullscreen)
2338 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2340 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2342 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2344 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2346 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2347 if (self->demands_attention)
2348 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2349 if (self->undecorated)
2350 netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2352 netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_LOCKED);
2353 OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
2356 frame_adjust_state(self->frame);
2359 ObClient *client_search_focus_tree(ObClient *self)
2364 for (it = self->transients; it; it = g_slist_next(it)) {
2365 if (client_focused(it->data)) return it->data;
2366 if ((ret = client_search_focus_tree(it->data))) return ret;
2371 ObClient *client_search_focus_tree_full(ObClient *self)
2373 if (self->parents) {
2376 for (it = self->parents; it; it = g_slist_next(it)) {
2377 ObClient *c = it->data;
2378 if ((c = client_search_focus_tree_full(it->data))) return c;
2384 /* this function checks the whole tree, the client_search_focus_tree
2385 does not, so we need to check this window */
2386 if (client_focused(self))
2388 return client_search_focus_tree(self);
2392 ObClient *client_search_focus_group_full(ObClient *self)
2397 for (it = self->group->members; it; it = g_slist_next(it)) {
2398 ObClient *c = it->data;
2400 if (client_focused(c)) return c;
2401 if ((c = client_search_focus_tree(it->data))) return c;
2404 if (client_focused(self)) return self;
2408 gboolean client_has_parent(ObClient *self)
2410 return self->parents != NULL;
2413 static ObStackingLayer calc_layer(ObClient *self)
2418 monitor = screen_physical_area_monitor(client_monitor(self));
2420 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2421 l = OB_STACKING_LAYER_DESKTOP;
2422 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2423 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2424 else l = OB_STACKING_LAYER_ABOVE;
2426 else if ((self->fullscreen ||
2427 /* No decorations and fills the monitor = oldskool fullscreen.
2428 But not for maximized windows.
2430 (self->decorations == 0 &&
2431 !(self->max_horz && self->max_vert) &&
2432 RECT_EQUAL(self->area, *monitor))) &&
2433 /* you are fullscreen while you or your children are focused.. */
2434 (client_focused(self) || client_search_focus_tree(self) ||
2435 /* you can be fullscreen if you're on another desktop */
2436 (self->desktop != screen_desktop &&
2437 self->desktop != DESKTOP_ALL) ||
2438 /* and you can also be fullscreen if the focused client is on
2439 another monitor, or nothing else is focused */
2441 client_monitor(focus_client) != client_monitor(self))))
2442 l = OB_STACKING_LAYER_FULLSCREEN;
2443 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2444 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2445 else l = OB_STACKING_LAYER_NORMAL;
2452 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2453 ObStackingLayer min)
2455 ObStackingLayer old, own;
2459 own = calc_layer(self);
2460 self->layer = MAX(own, min);
2462 if (self->layer != old) {
2463 stacking_remove(CLIENT_AS_WINDOW(self));
2464 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2467 /* we've been restacked */
2468 self->visited = TRUE;
2470 for (it = self->transients; it; it = g_slist_next(it))
2471 client_calc_layer_recursive(it->data, orig,
2475 static void client_calc_layer_internal(ObClient *self)
2479 /* transients take on the layer of their parents */
2480 sit = client_search_all_top_parents(self);
2482 for (; sit; sit = g_slist_next(sit))
2483 client_calc_layer_recursive(sit->data, self, 0);
2486 void client_calc_layer(ObClient *self)
2490 /* skip over stuff above fullscreen layer */
2491 for (it = stacking_list; it; it = g_list_next(it))
2492 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2494 /* find the windows in the fullscreen layer, and mark them not-visited */
2495 for (; it; it = g_list_next(it)) {
2496 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2497 else if (WINDOW_IS_CLIENT(it->data))
2498 WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2501 client_calc_layer_internal(self);
2503 /* skip over stuff above fullscreen layer */
2504 for (it = stacking_list; it; it = g_list_next(it))
2505 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2507 /* now recalc any windows in the fullscreen layer which have not
2508 had their layer recalced already */
2509 for (; it; it = g_list_next(it)) {
2510 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2511 else if (WINDOW_IS_CLIENT(it->data) &&
2512 !WINDOW_AS_CLIENT(it->data)->visited)
2513 client_calc_layer_internal(it->data);
2517 gboolean client_should_show(ObClient *self)
2521 if (client_normal(self) && screen_showing_desktop)
2523 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2529 gboolean client_show(ObClient *self)
2531 gboolean show = FALSE;
2533 if (client_should_show(self)) {
2534 /* replay pending pointer event before showing the window, in case it
2535 should be going to something under the window */
2536 mouse_replay_pointer();
2538 frame_show(self->frame);
2541 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2542 it needs to be in IconicState. This includes when it is on another
2545 client_change_wm_state(self);
2547 hooks_queue(OB_HOOK_WIN_VISIBLE, self);
2552 gboolean client_hide(ObClient *self)
2554 gboolean hide = FALSE;
2556 if (!client_should_show(self)) {
2557 if (self == focus_client) {
2558 event_cancel_all_key_grabs();
2561 /* We don't need to ignore enter events here.
2562 The window can hide/iconify in 3 different ways:
2563 1 - through an x message. in this case we ignore all enter events
2564 caused by responding to the x message (unless underMouse)
2565 2 - by a keyboard action. in this case we ignore all enter events
2566 caused by the action
2567 3 - by a mouse action. in this case they are doing stuff with the
2568 mouse and focus _should_ move.
2570 Also in action_end, we simulate an enter event that can't be ignored
2571 so trying to ignore them is futile in case 3 anyways
2574 /* replay pending pointer event before hiding the window, in case it
2575 should be going to the window */
2576 mouse_replay_pointer();
2578 frame_hide(self->frame);
2581 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2582 it needs to be in IconicState. This includes when it is on another
2585 client_change_wm_state(self);
2587 hooks_queue(OB_HOOK_WIN_INVISIBLE, self);
2592 void client_showhide(ObClient *self)
2594 if (!client_show(self))
2598 gboolean client_normal(ObClient *self) {
2599 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2600 self->type == OB_CLIENT_TYPE_DOCK ||
2601 self->type == OB_CLIENT_TYPE_SPLASH);
2604 gboolean client_helper(ObClient *self)
2606 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2607 self->type == OB_CLIENT_TYPE_MENU ||
2608 self->type == OB_CLIENT_TYPE_TOOLBAR);
2611 gboolean client_mouse_focusable(ObClient *self)
2613 return !(/*self->type == OB_CLIENT_TYPE_MENU ||*/
2614 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2615 self->type == OB_CLIENT_TYPE_SPLASH ||
2616 self->type == OB_CLIENT_TYPE_DOCK);
2619 gboolean client_enter_focusable(ObClient *self)
2621 /* you can focus desktops but it shouldn't on enter */
2622 return (client_mouse_focusable(self) &&
2623 self->type != OB_CLIENT_TYPE_DESKTOP);
2626 static void client_apply_startup_state(ObClient *self,
2627 gint x, gint y, gint w, gint h)
2629 /* save the states that we are going to apply */
2630 gboolean iconic = self->iconic;
2631 gboolean fullscreen = self->fullscreen;
2632 gboolean undecorated = self->undecorated;
2633 gboolean shaded = self->shaded;
2634 gboolean demands_attention = self->demands_attention;
2635 gboolean max_horz = self->max_horz;
2636 gboolean max_vert = self->max_vert;
2640 /* turn them all off in the client, so they won't affect the window
2642 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2643 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2645 /* move the client to its placed position, or it it's already there,
2646 generate a ConfigureNotify telling the client where it is.
2648 do this after adjusting the frame. otherwise it gets all weird and
2649 clients don't work right
2651 do this before applying the states so they have the correct
2652 pre-max/pre-fullscreen values
2654 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2655 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2656 self->window, x, y, w, h);
2657 /* save the area, and make it where it should be for the premax stuff */
2658 oldarea = self->area;
2659 RECT_SET(self->area, x, y, w, h);
2661 /* apply the states. these are in a carefully crafted order.. */
2664 client_iconify(self, TRUE, FALSE, TRUE);
2666 client_fullscreen(self, TRUE);
2668 client_set_undecorated(self, TRUE);
2670 client_shade(self, TRUE);
2671 if (demands_attention)
2672 client_hilite(self, TRUE);
2674 if (max_vert && max_horz)
2675 client_maximize(self, TRUE, 0);
2677 client_maximize(self, TRUE, 2);
2679 client_maximize(self, TRUE, 1);
2681 /* if the window hasn't been configured yet, then do so now, in fact the
2682 x,y,w,h may _not_ be the same as the area rect, which can end up
2683 meaning that the client isn't properly moved/resized by the fullscreen
2685 pho can cause this because it maps at size of the screen but not 0,0
2686 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2687 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2688 cuz thats where the pre-fullscreen will be. however the actual area is
2689 not, so this needs to be called even if we have fullscreened/maxed
2691 self->area = oldarea;
2692 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2694 /* set the desktop hint, to make sure that it always exists */
2695 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
2697 /* nothing to do for the other states:
2706 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2708 /* these should be the current values. this is for when you're not moving,
2710 g_assert(*x == self->area.x);
2711 g_assert(oldw == self->area.width);
2714 switch (self->gravity) {
2716 case NorthWestGravity:
2718 case SouthWestGravity:
2725 *x -= (neww - oldw) / 2;
2727 case NorthEastGravity:
2729 case SouthEastGravity:
2735 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2737 /* these should be the current values. this is for when you're not moving,
2739 g_assert(*y == self->area.y);
2740 g_assert(oldh == self->area.height);
2743 switch (self->gravity) {
2745 case NorthWestGravity:
2747 case NorthEastGravity:
2754 *y -= (newh - oldh) / 2;
2756 case SouthWestGravity:
2758 case SouthEastGravity:
2764 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2765 gint *logicalw, gint *logicalh,
2768 Rect desired = {*x, *y, *w, *h};
2769 frame_rect_to_frame(self->frame, &desired);
2771 /* make the frame recalculate its dimensions n shit without changing
2772 anything visible for real, this way the constraints below can work with
2773 the updated frame dimensions. */
2774 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2776 /* gets the frame's position */
2777 frame_client_gravity(self->frame, x, y);
2779 /* these positions are frame positions, not client positions */
2781 /* set the size and position if fullscreen */
2782 if (self->fullscreen) {
2786 i = screen_find_monitor(&desired);
2787 a = screen_physical_area_monitor(i);
2794 user = FALSE; /* ignore if the client can't be moved/resized when it
2798 } else if (self->max_horz || self->max_vert) {
2802 /* use all possible struts when maximizing to the full screen */
2803 i = screen_find_monitor(&desired);
2804 a = screen_area(self->desktop, i,
2805 (self->max_horz && self->max_vert ? NULL : &desired));
2807 /* set the size and position if maximized */
2808 if (self->max_horz) {
2810 *w = a->width - self->frame->size.left - self->frame->size.right;
2812 if (self->max_vert) {
2814 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2817 user = FALSE; /* ignore if the client can't be moved/resized when it
2823 /* gets the client's position */
2824 frame_frame_gravity(self->frame, x, y);
2826 /* work within the preferred sizes given by the window, these may have
2827 changed rather than it's requested width and height, so always run
2828 through this code */
2830 gint basew, baseh, minw, minh;
2832 gfloat minratio, maxratio;
2834 incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
2835 inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
2836 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2837 0 : self->min_ratio;
2838 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2839 0 : self->max_ratio;
2841 /* base size is substituted with min size if not specified */
2842 if (self->base_size.width >= 0 || self->base_size.height >= 0) {
2843 basew = self->base_size.width;
2844 baseh = self->base_size.height;
2846 basew = self->min_size.width;
2847 baseh = self->min_size.height;
2849 /* min size is substituted with base size if not specified */
2850 if (self->min_size.width || self->min_size.height) {
2851 minw = self->min_size.width;
2852 minh = self->min_size.height;
2854 minw = self->base_size.width;
2855 minh = self->base_size.height;
2858 /* This comment is no longer true */
2859 /* if this is a user-requested resize, then check against min/max
2862 /* smaller than min size or bigger than max size? */
2863 if (*w > self->max_size.width) *w = self->max_size.width;
2864 if (*w < minw) *w = minw;
2865 if (*h > self->max_size.height) *h = self->max_size.height;
2866 if (*h < minh) *h = minh;
2871 /* keep to the increments */
2875 /* you cannot resize to nothing */
2876 if (basew + *w < 1) *w = 1 - basew;
2877 if (baseh + *h < 1) *h = 1 - baseh;
2879 /* save the logical size */
2880 *logicalw = incw > 1 ? *w : *w + basew;
2881 *logicalh = inch > 1 ? *h : *h + baseh;
2889 /* adjust the height to match the width for the aspect ratios.
2890 for this, min size is not substituted for base size ever. */
2891 *w -= self->base_size.width;
2892 *h -= self->base_size.height;
2895 if (*h * minratio > *w) {
2896 *h = (gint)(*w / minratio);
2898 /* you cannot resize to nothing */
2901 *w = (gint)(*h * minratio);
2905 if (*h * maxratio < *w) {
2906 *h = (gint)(*w / maxratio);
2908 /* you cannot resize to nothing */
2911 *w = (gint)(*h * minratio);
2915 *w += self->base_size.width;
2916 *h += self->base_size.height;
2919 /* these override the above states! if you cant move you can't move! */
2921 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2925 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2926 *w = self->area.width;
2927 *h = self->area.height;
2935 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
2936 gboolean user, gboolean final, gboolean force_reply)
2940 gboolean send_resize_client;
2941 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
2942 gboolean fmoved, fresized;
2943 guint fdecor = self->frame->decorations;
2944 gboolean fhorz = self->frame->max_horz;
2945 gboolean fvert = self->frame->max_vert;
2946 gint logicalw, logicalh;
2948 /* find the new x, y, width, and height (and logical size) */
2949 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
2951 /* set the logical size if things changed */
2952 if (!(w == self->area.width && h == self->area.height))
2953 SIZE_SET(self->logical_size, logicalw, logicalh);
2955 /* figure out if we moved or resized or what */
2956 moved = (x != self->area.x || y != self->area.y);
2957 resized = (w != self->area.width || h != self->area.height);
2959 oldw = self->area.width;
2960 oldh = self->area.height;
2961 oldframe = self->frame->area;
2962 RECT_SET(self->area, x, y, w, h);
2964 /* for app-requested resizes, always resize if 'resized' is true.
2965 for user-requested ones, only resize if final is true, or when
2966 resizing in redraw mode */
2967 send_resize_client = ((!user && resized) ||
2969 (resized && config_resize_redraw))));
2971 /* if the client is enlarging, then resize the client before the frame */
2972 if (send_resize_client && (w > oldw || h > oldh)) {
2973 XMoveResizeWindow(obt_display, self->window,
2974 self->frame->size.left, self->frame->size.top,
2975 MAX(w, oldw), MAX(h, oldh));
2976 frame_adjust_client_area(self->frame);
2979 /* find the frame's dimensions and move/resize it */
2983 /* if decorations changed, then readjust everything for the frame */
2984 if (self->decorations != fdecor ||
2985 self->max_horz != fhorz || self->max_vert != fvert)
2987 fmoved = fresized = TRUE;
2990 /* adjust the frame */
2991 if (fmoved || fresized) {
2992 gulong ignore_start;
2994 ignore_start = event_start_ignore_all_enters();
2996 /* replay pending pointer event before move the window, in case it
2997 would change what window gets the event */
2998 mouse_replay_pointer();
3000 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
3003 event_end_ignore_all_enters(ignore_start);
3006 if (!user || final) {
3007 gint oldrx = self->root_pos.x;
3008 gint oldry = self->root_pos.y;
3009 /* we have reset the client to 0 border width, so don't include
3010 it in these coords */
3011 POINT_SET(self->root_pos,
3012 self->frame->area.x + self->frame->size.left -
3014 self->frame->area.y + self->frame->size.top -
3015 self->border_width);
3016 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3020 /* This is kinda tricky and should not be changed.. let me explain!
3022 When user = FALSE, then the request is coming from the application
3023 itself, and we are more strict about when to send a synthetic
3024 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3025 in this case (if force_reply is true)
3027 When user = TRUE, then the request is coming from "us", like when we
3028 maximize a window or something. In this case we are more lenient. We
3029 used to follow the same rules as above, but _Java_ Swing can't handle
3030 this. So just to appease Swing, when user = TRUE, we always send
3031 a synthetic ConfigureNotify to give the window its root coordinates.
3033 if ((!user && !resized && (rootmoved || force_reply)) ||
3034 (user && final && rootmoved))
3038 event.type = ConfigureNotify;
3039 event.xconfigure.display = obt_display;
3040 event.xconfigure.event = self->window;
3041 event.xconfigure.window = self->window;
3043 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3044 self->title, self->root_pos.x, self->root_pos.y, w, h);
3046 /* root window real coords */
3047 event.xconfigure.x = self->root_pos.x;
3048 event.xconfigure.y = self->root_pos.y;
3049 event.xconfigure.width = w;
3050 event.xconfigure.height = h;
3051 event.xconfigure.border_width = self->border_width;
3052 event.xconfigure.above = None;
3053 event.xconfigure.override_redirect = FALSE;
3054 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3055 FALSE, StructureNotifyMask, &event);
3058 /* if the client is shrinking, then resize the frame before the client.
3060 both of these resize sections may run, because the top one only resizes
3061 in the direction that is growing
3063 if (send_resize_client && (w <= oldw || h <= oldh)) {
3064 frame_adjust_client_area(self->frame);
3065 XMoveResizeWindow(obt_display, self->window,
3066 self->frame->size.left, self->frame->size.top, w, h);
3069 XFlush(obt_display);
3071 /* if it moved between monitors, then this can affect the stacking
3072 layer of this window or others - for fullscreen windows */
3073 if (screen_find_monitor(&self->frame->area) !=
3074 screen_find_monitor(&oldframe))
3076 client_calc_layer(self);
3080 void client_fullscreen(ObClient *self, gboolean fs)
3084 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3085 self->fullscreen == fs) return; /* already done */
3087 self->fullscreen = fs;
3088 client_change_state(self); /* change the state hints on the client */
3091 self->pre_fullscreen_area = self->area;
3092 /* if the window is maximized, its area isn't all that meaningful.
3093 save its premax area instead. */
3094 if (self->max_horz) {
3095 self->pre_fullscreen_area.x = self->pre_max_area.x;
3096 self->pre_fullscreen_area.width = self->pre_max_area.width;
3098 if (self->max_vert) {
3099 self->pre_fullscreen_area.y = self->pre_max_area.y;
3100 self->pre_fullscreen_area.height = self->pre_max_area.height;
3103 /* these will help configure_full figure out where to fullscreen
3107 w = self->area.width;
3108 h = self->area.height;
3110 g_assert(self->pre_fullscreen_area.width > 0 &&
3111 self->pre_fullscreen_area.height > 0);
3113 x = self->pre_fullscreen_area.x;
3114 y = self->pre_fullscreen_area.y;
3115 w = self->pre_fullscreen_area.width;
3116 h = self->pre_fullscreen_area.height;
3117 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3120 ob_debug("Window %s going fullscreen (%d)",
3121 self->title, self->fullscreen);
3123 client_setup_decor_and_functions(self, FALSE);
3124 client_move_resize(self, x, y, w, h);
3126 /* and adjust our layer/stacking. do this after resizing the window,
3127 and applying decorations, because windows which fill the screen are
3128 considered "fullscreen" and it affects their layer */
3129 client_calc_layer(self);
3132 /* try focus us when we go into fullscreen mode */
3137 static void client_iconify_recursive(ObClient *self,
3138 gboolean iconic, gboolean curdesk,
3139 gboolean hide_animation)
3142 gboolean changed = FALSE;
3144 if (self->iconic != iconic) {
3145 ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3149 /* don't let non-normal windows iconify along with their parents
3151 if (client_normal(self)) {
3152 self->iconic = iconic;
3154 /* update the focus lists.. iconic windows go to the bottom of
3156 focus_order_to_bottom(self);
3161 self->iconic = iconic;
3163 if (curdesk && self->desktop != screen_desktop &&
3164 self->desktop != DESKTOP_ALL)
3165 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3167 /* this puts it after the current focused window */
3168 focus_order_remove(self);
3169 focus_order_add_new(self);
3176 client_change_state(self);
3177 if (config_animate_iconify && !hide_animation)
3178 frame_begin_iconify_animation(self->frame, iconic);
3179 /* do this after starting the animation so it doesn't flash */
3180 client_showhide(self);
3182 hooks_queue((iconic ? OB_HOOK_WIN_ICONIC : OB_HOOK_WIN_UNICONIC),
3186 /* iconify all direct transients, and deiconify all transients
3188 for (it = self->transients; it; it = g_slist_next(it))
3189 if (it->data != self)
3190 if (client_is_direct_child(self, it->data) || !iconic)
3191 client_iconify_recursive(it->data, iconic, curdesk,
3195 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3196 gboolean hide_animation)
3198 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3199 /* move up the transient chain as far as possible first */
3200 self = client_search_top_direct_parent(self);
3201 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3205 void client_maximize(ObClient *self, gboolean max, gint dir)
3209 g_assert(dir == 0 || dir == 1 || dir == 2);
3210 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
3212 /* check if already done */
3214 if (dir == 0 && self->max_horz && self->max_vert) return;
3215 if (dir == 1 && self->max_horz) return;
3216 if (dir == 2 && self->max_vert) return;
3218 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3219 if (dir == 1 && !self->max_horz) return;
3220 if (dir == 2 && !self->max_vert) return;
3223 /* these will help configure_full figure out which screen to fill with
3227 w = self->area.width;
3228 h = self->area.height;
3231 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3232 RECT_SET(self->pre_max_area,
3233 self->area.x, self->pre_max_area.y,
3234 self->area.width, self->pre_max_area.height);
3236 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3237 RECT_SET(self->pre_max_area,
3238 self->pre_max_area.x, self->area.y,
3239 self->pre_max_area.width, self->area.height);
3242 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3243 g_assert(self->pre_max_area.width > 0);
3245 x = self->pre_max_area.x;
3246 w = self->pre_max_area.width;
3248 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3249 0, self->pre_max_area.height);
3251 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3252 g_assert(self->pre_max_area.height > 0);
3254 y = self->pre_max_area.y;
3255 h = self->pre_max_area.height;
3257 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3258 self->pre_max_area.width, 0);
3262 if (dir == 0 || dir == 1) /* horz */
3263 self->max_horz = max;
3264 if (dir == 0 || dir == 2) /* vert */
3265 self->max_vert = max;
3267 client_change_state(self); /* change the state hints on the client */
3269 client_setup_decor_and_functions(self, FALSE);
3270 client_move_resize(self, x, y, w, h);
3272 hooks_queue((max ? OB_HOOK_WIN_MAX : OB_HOOK_WIN_UNMAX), self);
3275 void client_shade(ObClient *self, gboolean shade)
3277 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3278 shade) || /* can't shade */
3279 self->shaded == shade) return; /* already done */
3281 self->shaded = shade;
3282 client_change_state(self);
3283 client_change_wm_state(self); /* the window is being hidden/shown */
3284 /* resize the frame to just the titlebar */
3285 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3287 hooks_queue((shade ? OB_HOOK_WIN_SHADE : OB_HOOK_WIN_UNSHADE), self);
3290 static void client_ping_event(ObClient *self, gboolean dead)
3292 if (self->not_responding != dead) {
3293 self->not_responding = dead;
3294 client_update_title(self);
3297 /* the client isn't responding, so ask to kill it */
3298 client_prompt_kill(self);
3300 /* it came back to life ! */
3302 if (self->kill_prompt) {
3303 prompt_unref(self->kill_prompt);
3304 self->kill_prompt = NULL;
3307 self->kill_level = 0;
3312 void client_close(ObClient *self)
3314 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3316 /* if closing an internal obprompt, that is just cancelling it */
3318 prompt_cancel(self->prompt);
3322 /* in the case that the client provides no means to requesting that it
3323 close, we just kill it */
3324 if (!self->delete_window)
3325 /* don't use client_kill(), we should only kill based on PID in
3326 response to a lack of PING replies */
3327 XKillClient(obt_display, self->window);
3329 /* request the client to close with WM_DELETE_WINDOW */
3330 OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
3331 OBT_PROP_ATOM(WM_DELETE_WINDOW), event_curtime,
3332 0, 0, 0, NoEventMask);
3334 /* we're trying to close the window, so see if it is responding. if it
3335 is not, then we will let them kill the window */
3337 ping_start(self, client_ping_event);
3339 /* if we already know the window isn't responding (maybe they clicked
3340 no in the kill dialog but it hasn't come back to life), then show
3342 if (self->not_responding)
3343 client_prompt_kill(self);
3347 #define OB_KILL_RESULT_NO 0
3348 #define OB_KILL_RESULT_YES 1
3350 static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
3352 ObClient *self = data;
3354 if (result == OB_KILL_RESULT_YES)
3356 return TRUE; /* call the cleanup func */
3359 static void client_kill_cleanup(ObPrompt *p, gpointer data)
3361 ObClient *self = data;
3363 g_assert(p == self->kill_prompt);
3365 prompt_unref(self->kill_prompt);
3366 self->kill_prompt = NULL;
3369 static void client_prompt_kill(ObClient *self)
3371 /* check if we're already prompting */
3372 if (!self->kill_prompt) {
3373 ObPromptAnswer answers[] = {
3374 { 0, OB_KILL_RESULT_NO },
3375 { 0, OB_KILL_RESULT_YES }
3378 const gchar *y, *title;
3380 title = self->original_title;
3381 if (title[0] == '\0') {
3382 /* empty string, so use its parent */
3383 ObClient *p = client_search_top_direct_parent(self);
3384 if (p) title = p->original_title;
3387 if (client_on_localhost(self)) {
3390 if (self->kill_level == 0)
3396 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3398 y = _("End Process");
3402 (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3404 y = _("Disconnect");
3406 /* set the dialog buttons' text */
3407 answers[0].text = _("Cancel"); /* "no" */
3408 answers[1].text = y; /* "yes" */
3410 self->kill_prompt = prompt_new(m, NULL, answers,
3411 sizeof(answers)/sizeof(answers[0]),
3412 OB_KILL_RESULT_NO, /* default = no */
3413 OB_KILL_RESULT_NO, /* cancel = no */
3414 client_kill_requested,
3415 client_kill_cleanup,
3420 prompt_show(self->kill_prompt, self, TRUE);
3423 void client_kill(ObClient *self)
3425 /* don't kill our own windows */
3426 if (self->prompt) return;
3428 if (client_on_localhost(self) && self->pid) {
3429 /* running on the local host */
3430 if (self->kill_level == 0) {
3431 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3432 self->window, self->pid);
3433 kill(self->pid, SIGTERM);
3436 /* show that we're trying to kill it */
3437 client_update_title(self);
3440 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3441 self->window, self->pid);
3442 kill(self->pid, SIGKILL); /* kill -9 */
3446 /* running on a remote host */
3447 XKillClient(obt_display, self->window);
3451 void client_hilite(ObClient *self, gboolean hilite)
3453 if (self->demands_attention == hilite)
3454 return; /* no change */
3456 /* don't allow focused windows to hilite */
3457 self->demands_attention = hilite && !client_focused(self);
3458 if (self->frame != NULL) { /* if we're mapping, just set the state */
3459 if (self->demands_attention)
3460 frame_flash_start(self->frame);
3462 frame_flash_stop(self->frame);
3463 client_change_state(self);
3467 static void client_set_desktop_recursive(ObClient *self,
3475 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3477 ob_debug("Setting desktop %u", target+1);
3479 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3481 old = self->desktop;
3482 self->desktop = target;
3483 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
3484 /* the frame can display the current desktop state */
3485 frame_adjust_state(self->frame);
3486 /* 'move' the window to the new desktop */
3490 /* raise if it was not already on the desktop */
3491 if (old != DESKTOP_ALL && !dontraise)
3492 stacking_raise(CLIENT_AS_WINDOW(self));
3493 if (STRUT_EXISTS(self->strut))
3494 screen_update_areas();
3496 /* the new desktop's geometry may be different, so we may need to
3497 resize, for example if we are maximized */
3498 client_reconfigure(self, FALSE);
3500 if (old != self->desktop)
3501 hooks_queue(OB_HOOK_WIN_DESK_CHANGE, self);
3504 /* move all transients */
3505 for (it = self->transients; it; it = g_slist_next(it))
3506 if (it->data != self)
3507 if (client_is_direct_child(self, it->data))
3508 client_set_desktop_recursive(it->data, target,
3509 donthide, dontraise);
3512 void client_set_desktop(ObClient *self, guint target,
3513 gboolean donthide, gboolean dontraise)
3515 self = client_search_top_direct_parent(self);
3516 client_set_desktop_recursive(self, target, donthide, dontraise);
3519 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3521 while (child != parent && (child = client_direct_parent(child)));
3522 return child == parent;
3525 ObClient *client_search_modal_child(ObClient *self)
3530 for (it = self->transients; it; it = g_slist_next(it)) {
3531 ObClient *c = it->data;
3532 if ((ret = client_search_modal_child(c))) return ret;
3533 if (c->modal) return c;
3538 gboolean client_validate(ObClient *self)
3542 XSync(obt_display, FALSE); /* get all events on the server */
3544 if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e) ||
3545 XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e))
3547 XPutBackEvent(obt_display, &e);
3554 void client_set_wm_state(ObClient *self, glong state)
3556 if (state == self->wmstate) return; /* no change */
3560 client_iconify(self, TRUE, TRUE, FALSE);
3563 client_iconify(self, FALSE, TRUE, FALSE);
3568 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3570 gboolean shaded = self->shaded;
3571 gboolean fullscreen = self->fullscreen;
3572 gboolean undecorated = self->undecorated;
3573 gboolean locked = self->locked;
3574 gboolean max_horz = self->max_horz;
3575 gboolean max_vert = self->max_vert;
3576 gboolean modal = self->modal;
3577 gboolean iconic = self->iconic;
3578 gboolean demands_attention = self->demands_attention;
3579 gboolean above = self->above;
3580 gboolean below = self->below;
3584 if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3585 action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3586 action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3587 /* an invalid action was passed to the client message, ignore it */
3590 for (i = 0; i < 2; ++i) {
3591 Atom state = i == 0 ? data1 : data2;
3593 if (!state) continue;
3595 /* if toggling, then pick whether we're adding or removing */
3596 if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3597 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3599 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3600 value = self->max_vert;
3601 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3602 value = self->max_horz;
3603 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3605 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3606 value = self->skip_taskbar;
3607 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3608 value = self->skip_pager;
3609 else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3610 value = self->iconic;
3611 else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3613 else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3614 value = self->above;
3615 else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3616 value = self->below;
3617 else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3618 value = self->demands_attention;
3619 else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3620 value = undecorated;
3621 else if (state == OBT_PROP_ATOM(OB_WM_STATE_LOCKED))
3623 action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3624 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3627 value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
3628 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3630 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3632 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3634 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3636 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3637 self->skip_taskbar = value;
3638 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3639 self->skip_pager = value;
3640 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3642 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3644 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3646 /* only unset below when setting above, otherwise you can't get to
3650 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3651 /* and vice versa */
3655 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3656 demands_attention = value;
3657 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3658 undecorated = value;
3659 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_LOCKED)) {
3664 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3665 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3667 if (max_horz == max_vert) { /* both going the same way */
3668 client_maximize(self, max_horz, 0);
3670 client_maximize(self, max_horz, 1);
3671 client_maximize(self, max_vert, 2);
3675 if (max_horz != self->max_horz)
3676 client_maximize(self, max_horz, 1);
3678 client_maximize(self, max_vert, 2);
3681 /* change fullscreen state before shading, as it will affect if the window
3683 if (fullscreen != self->fullscreen)
3684 client_fullscreen(self, fullscreen);
3685 if (shaded != self->shaded)
3686 client_shade(self, shaded);
3687 if (undecorated != self->undecorated)
3688 client_set_undecorated(self, undecorated);
3689 if (locked != self->locked)
3690 client_set_locked(self, locked);
3691 if (above != self->above || below != self->below) {
3692 self->above = above;
3693 self->below = below;
3694 client_calc_layer(self);
3697 if (modal != self->modal) {
3698 self->modal = modal;
3699 /* when a window changes modality, then its stacking order with its
3700 transients needs to change */
3701 stacking_raise(CLIENT_AS_WINDOW(self));
3703 /* it also may get focused. if something is focused that shouldn't
3704 be focused anymore, then move the focus */
3705 if (focus_client && client_focus_target(focus_client) != focus_client)
3706 client_focus(focus_client);
3709 if (iconic != self->iconic)
3710 client_iconify(self, iconic, FALSE, FALSE);
3712 if (demands_attention != self->demands_attention)
3713 client_hilite(self, demands_attention);
3715 client_change_state(self); /* change the hint to reflect these changes */
3718 ObClient *client_focus_target(ObClient *self)
3720 ObClient *child = NULL;
3722 child = client_search_modal_child(self);
3723 if (child) return child;
3727 gboolean client_can_focus(ObClient *self)
3729 /* choose the correct target */
3730 self = client_focus_target(self);
3732 if (!self->frame->visible)
3735 if (!(self->can_focus || self->focus_notify))
3741 gboolean client_focus(ObClient *self)
3745 XkbGetState(obt_display, XkbUseCoreKbd, &state);
3746 if (state.locked_mods & 128)
3750 /* we might not focus this window, so if we have modal children which would
3751 be focused instead, bring them to this desktop */
3752 client_bring_modal_windows(self);
3754 /* choose the correct target */
3755 self = client_focus_target(self);
3757 if (!client_can_focus(self)) {
3758 ob_debug_type(OB_DEBUG_FOCUS,
3759 "Client %s can't be focused", self->title);
3763 ob_debug_type(OB_DEBUG_FOCUS,
3764 "Focusing client \"%s\" (0x%x) at time %u",
3765 self->title, self->window, event_curtime);
3767 /* if using focus_delay, stop the timer now so that focus doesn't
3769 event_halt_focus_delay();
3771 event_cancel_all_key_grabs();
3773 obt_display_ignore_errors(TRUE);
3775 if (self->can_focus) {
3776 /* This can cause a BadMatch error with CurrentTime, or if an app
3777 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3778 XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
3782 if (self->focus_notify) {
3784 ce.xclient.type = ClientMessage;
3785 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
3786 ce.xclient.display = obt_display;
3787 ce.xclient.window = self->window;
3788 ce.xclient.format = 32;
3789 ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
3790 ce.xclient.data.l[1] = event_curtime;
3791 ce.xclient.data.l[2] = 0l;
3792 ce.xclient.data.l[3] = 0l;
3793 ce.xclient.data.l[4] = 0l;
3794 XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
3797 obt_display_ignore_errors(FALSE);
3799 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
3800 obt_display_error_occured);
3801 return !obt_display_error_occured;
3804 static void client_present(ObClient *self, gboolean here, gboolean raise,
3807 if (client_normal(self) && screen_showing_desktop)
3808 screen_show_desktop(FALSE, self);
3810 client_iconify(self, FALSE, here, FALSE);
3811 if (self->desktop != DESKTOP_ALL &&
3812 self->desktop != screen_desktop)
3815 client_set_desktop(self, screen_desktop, FALSE, TRUE);
3817 screen_set_desktop(self->desktop, FALSE);
3818 } else if (!self->frame->visible)
3819 /* if its not visible for other reasons, then don't mess
3822 if (self->shaded && unshade)
3823 client_shade(self, FALSE);
3825 stacking_raise(CLIENT_AS_WINDOW(self));
3830 /* this function exists to map to the client_activate message in the ewmh,
3831 the user arg is unused because nobody uses it correctly anyway. */
3832 void client_activate(ObClient *self, gboolean here, gboolean raise,
3833 gboolean unshade, gboolean user)
3835 client_present(self, here, raise, unshade);
3838 static void client_bring_windows_recursive(ObClient *self,
3846 for (it = self->transients; it; it = g_slist_next(it))
3847 client_bring_windows_recursive(it->data, desktop,
3848 helpers, modals, iconic);
3850 if (((helpers && client_helper(self)) ||
3851 (modals && self->modal)) &&
3852 ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
3853 (iconic && self->iconic)))
3855 if (iconic && self->iconic)
3856 client_iconify(self, FALSE, TRUE, FALSE);
3858 client_set_desktop(self, desktop, FALSE, FALSE);
3862 void client_bring_helper_windows(ObClient *self)
3864 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
3867 void client_bring_modal_windows(ObClient *self)
3869 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
3872 gboolean client_focused(ObClient *self)
3874 return self == focus_client;
3877 RrImage* client_icon(ObClient *self)
3879 RrImage *ret = NULL;
3882 ret = self->icon_set;
3883 else if (self->parents) {
3885 for (it = self->parents; it && !ret; it = g_slist_next(it))
3886 ret = client_icon(it->data);
3889 ret = client_default_icon;
3893 void client_set_layer(ObClient *self, gint layer)
3897 self->above = FALSE;
3898 } else if (layer == 0) {
3899 self->below = self->above = FALSE;
3901 self->below = FALSE;
3904 client_calc_layer(self);
3905 client_change_state(self); /* reflect this in the state hints */
3908 void client_set_locked(ObClient *self, gboolean locked)
3910 if (self->locked != locked) {
3911 self->locked = locked;
3912 client_change_state(self);
3916 void client_set_undecorated(ObClient *self, gboolean undecorated)
3918 if (self->undecorated != undecorated &&
3919 /* don't let it undecorate if the function is missing, but let
3921 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
3923 self->undecorated = undecorated;
3924 client_setup_decor_and_functions(self, TRUE);
3925 client_change_state(self); /* reflect this in the state hints */
3927 hooks_queue((undecorated ?
3928 OB_HOOK_WIN_UNDECORATED : OB_HOOK_WIN_DECORATED), self);
3932 guint client_monitor(ObClient *self)
3934 return screen_find_monitor(&self->frame->area);
3937 ObClient *client_direct_parent(ObClient *self)
3939 if (!self->parents) return NULL;
3940 if (self->transient_for_group) return NULL;
3941 return self->parents->data;
3944 ObClient *client_search_top_direct_parent(ObClient *self)
3947 while ((p = client_direct_parent(self))) self = p;
3951 static GSList *client_search_all_top_parents_internal(ObClient *self,
3953 ObStackingLayer layer)
3958 /* move up the direct transient chain as far as possible */
3959 while ((p = client_direct_parent(self)) &&
3960 (!bylayer || p->layer == layer))
3964 ret = g_slist_prepend(NULL, self);
3966 ret = g_slist_copy(self->parents);
3971 GSList *client_search_all_top_parents(ObClient *self)
3973 return client_search_all_top_parents_internal(self, FALSE, 0);
3976 GSList *client_search_all_top_parents_layer(ObClient *self)
3978 return client_search_all_top_parents_internal(self, TRUE, self->layer);
3981 ObClient *client_search_focus_parent(ObClient *self)
3985 for (it = self->parents; it; it = g_slist_next(it))
3986 if (client_focused(it->data)) return it->data;
3991 ObClient *client_search_focus_parent_full(ObClient *self)
3994 ObClient *ret = NULL;
3996 for (it = self->parents; it; it = g_slist_next(it)) {
3997 if (client_focused(it->data))
4000 ret = client_search_focus_parent_full(it->data);
4006 ObClient *client_search_parent(ObClient *self, ObClient *search)
4010 for (it = self->parents; it; it = g_slist_next(it))
4011 if (it->data == search) return search;
4016 ObClient *client_search_transient(ObClient *self, ObClient *search)
4020 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4021 if (sit->data == search)
4023 if (client_search_transient(sit->data, search))
4029 static void detect_edge(Rect area, ObDirection dir,
4030 gint my_head, gint my_size,
4031 gint my_edge_start, gint my_edge_size,
4032 gint *dest, gboolean *near_edge)
4034 gint edge_start, edge_size, head, tail;
4035 gboolean skip_head = FALSE, skip_tail = FALSE;
4038 case OB_DIRECTION_NORTH:
4039 case OB_DIRECTION_SOUTH:
4040 edge_start = area.x;
4041 edge_size = area.width;
4043 case OB_DIRECTION_EAST:
4044 case OB_DIRECTION_WEST:
4045 edge_start = area.y;
4046 edge_size = area.height;
4049 g_assert_not_reached();
4052 /* do we collide with this window? */
4053 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4054 edge_start, edge_size))
4058 case OB_DIRECTION_NORTH:
4059 head = RECT_BOTTOM(area);
4060 tail = RECT_TOP(area);
4062 case OB_DIRECTION_SOUTH:
4063 head = RECT_TOP(area);
4064 tail = RECT_BOTTOM(area);
4066 case OB_DIRECTION_WEST:
4067 head = RECT_RIGHT(area);
4068 tail = RECT_LEFT(area);
4070 case OB_DIRECTION_EAST:
4071 head = RECT_LEFT(area);
4072 tail = RECT_RIGHT(area);
4075 g_assert_not_reached();
4078 case OB_DIRECTION_NORTH:
4079 case OB_DIRECTION_WEST:
4080 /* check if our window is past the head of this window */
4081 if (my_head <= head + 1)
4083 /* check if our window's tail is past the tail of this window */
4084 if (my_head + my_size - 1 <= tail)
4086 /* check if the head of this window is closer than the previously
4087 chosen edge (take into account that the previously chosen
4088 edge might have been a tail, not a head) */
4089 if (head + (*near_edge ? 0 : my_size) <= *dest)
4091 /* check if the tail of this window is closer than the previously
4092 chosen edge (take into account that the previously chosen
4093 edge might have been a head, not a tail) */
4094 if (tail - (!*near_edge ? 0 : my_size) <= *dest)
4097 case OB_DIRECTION_SOUTH:
4098 case OB_DIRECTION_EAST:
4099 /* check if our window is past the head of this window */
4100 if (my_head >= head - 1)
4102 /* check if our window's tail is past the tail of this window */
4103 if (my_head - my_size + 1 >= tail)
4105 /* check if the head of this window is closer than the previously
4106 chosen edge (take into account that the previously chosen
4107 edge might have been a tail, not a head) */
4108 if (head - (*near_edge ? 0 : my_size) >= *dest)
4110 /* check if the tail of this window is closer than the previously
4111 chosen edge (take into account that the previously chosen
4112 edge might have been a head, not a tail) */
4113 if (tail + (!*near_edge ? 0 : my_size) >= *dest)
4117 g_assert_not_reached();
4120 ob_debug("my head %d size %d", my_head, my_size);
4121 ob_debug("head %d tail %d dest %d", head, tail, *dest);
4123 ob_debug("using near edge %d", head);
4127 else if (!skip_tail) {
4128 ob_debug("using far edge %d", tail);
4134 void client_find_edge_directional(ObClient *self, ObDirection dir,
4135 gint my_head, gint my_size,
4136 gint my_edge_start, gint my_edge_size,
4137 gint *dest, gboolean *near_edge)
4145 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4146 &self->frame->area);
4147 mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
4148 &self->frame->area);
4151 case OB_DIRECTION_NORTH:
4152 if (my_head >= RECT_TOP(*mon) + 1)
4153 edge = RECT_TOP(*mon) - 1;
4155 edge = RECT_TOP(*a) - 1;
4157 case OB_DIRECTION_SOUTH:
4158 if (my_head <= RECT_BOTTOM(*mon) - 1)
4159 edge = RECT_BOTTOM(*mon) + 1;
4161 edge = RECT_BOTTOM(*a) + 1;
4163 case OB_DIRECTION_EAST:
4164 if (my_head <= RECT_RIGHT(*mon) - 1)
4165 edge = RECT_RIGHT(*mon) + 1;
4167 edge = RECT_RIGHT(*a) + 1;
4169 case OB_DIRECTION_WEST:
4170 if (my_head >= RECT_LEFT(*mon) + 1)
4171 edge = RECT_LEFT(*mon) - 1;
4173 edge = RECT_LEFT(*a) - 1;
4176 g_assert_not_reached();
4178 /* default to the far edge, then narrow it down */
4182 for (it = client_list; it; it = g_list_next(it)) {
4183 ObClient *cur = it->data;
4185 /* skip windows to not bump into */
4190 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4191 cur->desktop != screen_desktop)
4194 ob_debug("trying window %s", cur->title);
4196 RECT_SET(expand, cur->frame->area.x - config_window_margin,
4197 cur->frame->area.y - config_window_margin,
4198 cur->frame->area.width + config_window_margin * 2,
4199 cur->frame->area.height + config_window_margin * 2);
4200 detect_edge(expand, dir, my_head, my_size, my_edge_start,
4201 my_edge_size, dest, near_edge);
4203 dock_get_area(&dock_area);
4204 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4205 my_edge_size, dest, near_edge);
4210 void client_find_move_directional(ObClient *self, ObDirection dir,
4214 gint e, e_start, e_size;
4218 case OB_DIRECTION_EAST:
4219 head = RECT_RIGHT(self->frame->area);
4220 size = self->frame->area.width;
4221 e_start = RECT_TOP(self->frame->area);
4222 e_size = self->frame->area.height;
4224 case OB_DIRECTION_WEST:
4225 head = RECT_LEFT(self->frame->area);
4226 size = self->frame->area.width;
4227 e_start = RECT_TOP(self->frame->area);
4228 e_size = self->frame->area.height;
4230 case OB_DIRECTION_NORTH:
4231 head = RECT_TOP(self->frame->area);
4232 size = self->frame->area.height;
4233 e_start = RECT_LEFT(self->frame->area);
4234 e_size = self->frame->area.width;
4236 case OB_DIRECTION_SOUTH:
4237 head = RECT_BOTTOM(self->frame->area);
4238 size = self->frame->area.height;
4239 e_start = RECT_LEFT(self->frame->area);
4240 e_size = self->frame->area.width;
4243 g_assert_not_reached();
4246 client_find_edge_directional(self, dir, head, size,
4247 e_start, e_size, &e, &near);
4248 *x = self->frame->area.x;
4249 *y = self->frame->area.y;
4251 case OB_DIRECTION_EAST:
4252 if (near) e -= self->frame->area.width;
4256 case OB_DIRECTION_WEST:
4258 else e -= self->frame->area.width;
4261 case OB_DIRECTION_NORTH:
4263 else e -= self->frame->area.height;
4266 case OB_DIRECTION_SOUTH:
4267 if (near) e -= self->frame->area.height;
4272 g_assert_not_reached();
4274 frame_frame_gravity(self->frame, x, y);
4277 void client_find_resize_directional(ObClient *self, ObDirection side,
4279 gint *x, gint *y, gint *w, gint *h)
4282 gint e, e_start, e_size, delta;
4287 case OB_DIRECTION_EAST:
4288 head = RECT_RIGHT(self->frame->area) +
4289 (self->size_inc.width - 1) * (grow ? 1 : 0);
4290 e_start = RECT_TOP(self->frame->area);
4291 e_size = self->frame->area.height;
4292 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4294 case OB_DIRECTION_WEST:
4295 head = RECT_LEFT(self->frame->area) -
4296 (self->size_inc.width - 1) * (grow ? 1 : 0);
4297 e_start = RECT_TOP(self->frame->area);
4298 e_size = self->frame->area.height;
4299 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4301 case OB_DIRECTION_NORTH:
4302 head = RECT_TOP(self->frame->area) -
4303 (self->size_inc.height - 1) * (grow ? 1 : 0);
4304 e_start = RECT_LEFT(self->frame->area);
4305 e_size = self->frame->area.width;
4306 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4308 case OB_DIRECTION_SOUTH:
4309 head = RECT_BOTTOM(self->frame->area) +
4310 (self->size_inc.height - 1) * (grow ? 1 : 0);
4311 e_start = RECT_LEFT(self->frame->area);
4312 e_size = self->frame->area.width;
4313 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4316 g_assert_not_reached();
4319 ob_debug("head %d dir %d", head, dir);
4320 client_find_edge_directional(self, dir, head, 1,
4321 e_start, e_size, &e, &near);
4322 ob_debug("edge %d", e);
4323 *x = self->frame->area.x;
4324 *y = self->frame->area.y;
4325 *w = self->frame->area.width;
4326 *h = self->frame->area.height;
4328 case OB_DIRECTION_EAST:
4329 if (grow == near) --e;
4330 delta = e - RECT_RIGHT(self->frame->area);
4333 case OB_DIRECTION_WEST:
4334 if (grow == near) ++e;
4335 delta = RECT_LEFT(self->frame->area) - e;
4339 case OB_DIRECTION_NORTH:
4340 if (grow == near) ++e;
4341 delta = RECT_TOP(self->frame->area) - e;
4345 case OB_DIRECTION_SOUTH:
4346 if (grow == near) --e;
4347 delta = e - RECT_BOTTOM(self->frame->area);
4351 g_assert_not_reached();
4353 frame_frame_gravity(self->frame, x, y);
4354 *w -= self->frame->size.left + self->frame->size.right;
4355 *h -= self->frame->size.top + self->frame->size.bottom;
4358 ObClient* client_under_pointer(void)
4362 ObClient *ret = NULL;
4364 if (screen_pointer_pos(&x, &y)) {
4365 for (it = stacking_list; it; it = g_list_next(it)) {
4366 if (WINDOW_IS_CLIENT(it->data)) {
4367 ObClient *c = WINDOW_AS_CLIENT(it->data);
4368 if (c->frame->visible &&
4369 /* check the desktop, this is done during desktop
4370 switching and windows are shown/hidden status is not
4372 (c->desktop == screen_desktop ||
4373 c->desktop == DESKTOP_ALL) &&
4374 /* ignore all animating windows */
4375 !frame_iconify_animating(c->frame) &&
4376 RECT_CONTAINS(c->frame->area, x, y))
4387 gboolean client_has_group_siblings(ObClient *self)
4389 return self->group && self->group->members->next;
4392 /*! Returns TRUE if the client is running on the same machine as Openbox */
4393 gboolean client_on_localhost(ObClient *self)
4395 return self->client_machine == NULL;