1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
30 #include "extensions.h"
41 #include "menuframe.h"
44 #include "render/render.h"
52 # include <signal.h> /* for kill() */
56 #include <X11/Xutil.h>
58 /*! The event mask to grab on client windows */
59 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
62 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
67 ObClientCallback func;
71 GList *client_list = NULL;
73 static GSList *client_destroy_notifies = NULL;
74 static RrImage *client_default_icon = NULL;
76 static void client_get_all(ObClient *self, gboolean real);
77 static void client_get_startup_id(ObClient *self);
78 static void client_get_session_ids(ObClient *self);
79 static void client_save_session_ids(ObClient *self);
80 static void client_get_area(ObClient *self);
81 static void client_get_desktop(ObClient *self);
82 static void client_get_state(ObClient *self);
83 static void client_get_shaped(ObClient *self);
84 static void client_get_colormap(ObClient *self);
85 static void client_set_desktop_recursive(ObClient *self,
89 static void client_change_allowed_actions(ObClient *self);
90 static void client_change_state(ObClient *self);
91 static void client_change_wm_state(ObClient *self);
92 static void client_apply_startup_state(ObClient *self,
93 gint x, gint y, gint w, gint h);
94 static void client_restore_session_state(ObClient *self);
95 static gboolean client_restore_session_stacking(ObClient *self);
96 static ObAppSettings *client_get_settings_state(ObClient *self);
97 static void client_update_transient_tree(ObClient *self,
98 ObGroup *oldgroup, ObGroup *newgroup,
99 gboolean oldgtran, gboolean newgtran,
101 ObClient *newparent);
102 static void client_present(ObClient *self, gboolean here, gboolean raise,
104 static GSList *client_search_all_top_parents_internal(ObClient *self,
106 ObStackingLayer layer);
107 static void client_call_notifies(ObClient *self, GSList *list);
108 static void client_ping_event(ObClient *self, gboolean dead);
109 static void client_prompt_kill(ObClient *self);
110 static gboolean client_can_steal_focus(ObClient *self, Time steal_time,
113 void client_startup(gboolean reconfig)
115 if ((client_default_icon = RrImageCacheFind(ob_rr_icons,
116 ob_rr_theme->def_win_icon,
117 ob_rr_theme->def_win_icon_w,
118 ob_rr_theme->def_win_icon_h)))
119 RrImageRef(client_default_icon);
121 client_default_icon = RrImageNew(ob_rr_icons);
122 RrImageAddPicture(client_default_icon,
123 ob_rr_theme->def_win_icon,
124 ob_rr_theme->def_win_icon_w,
125 ob_rr_theme->def_win_icon_h);
128 if (reconfig) return;
133 void client_shutdown(gboolean reconfig)
135 RrImageUnref(client_default_icon);
136 client_default_icon = NULL;
138 if (reconfig) return;
141 static void client_call_notifies(ObClient *self, GSList *list)
145 for (it = list; it; it = g_slist_next(it)) {
146 ClientCallback *d = it->data;
147 d->func(self, d->data);
151 void client_add_destroy_notify(ObClientCallback func, gpointer data)
153 ClientCallback *d = g_new(ClientCallback, 1);
156 client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
159 void client_remove_destroy_notify(ObClientCallback func)
163 for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
164 ClientCallback *d = it->data;
165 if (d->func == func) {
167 client_destroy_notifies =
168 g_slist_delete_link(client_destroy_notifies, it);
174 void client_set_list(void)
176 Window *windows, *win_it;
178 guint size = g_list_length(client_list);
180 /* create an array of the window ids */
182 windows = g_new(Window, size);
184 for (it = client_list; it; it = g_list_next(it), ++win_it)
185 *win_it = ((ObClient*)it->data)->window;
189 PROP_SETA32(RootWindow(ob_display, ob_screen),
190 net_client_list, window, (gulong*)windows, size);
198 void client_manage_all(void)
203 XWindowAttributes attrib;
205 XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
206 &w, &w, &children, &nchild);
208 /* remove all icon windows from the list */
209 for (i = 0; i < nchild; i++) {
210 if (children[i] == None) continue;
211 wmhints = XGetWMHints(ob_display, children[i]);
213 if ((wmhints->flags & IconWindowHint) &&
214 (wmhints->icon_window != children[i]))
215 for (j = 0; j < nchild; j++)
216 if (children[j] == wmhints->icon_window) {
224 /* manage windows in reverse order from how they were originally mapped.
225 this is an attempt to manage children windows before their parents, so
226 that when the parent is mapped, it can find the child */
227 for (i = 0; i < nchild; ++i) {
228 if (children[i] == None)
230 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
231 if (attrib.override_redirect) continue;
233 if (attrib.map_state != IsUnmapped)
234 client_manage(children[i], NULL);
240 void client_manage(Window window, ObPrompt *prompt)
244 XWindowAttributes attrib;
245 XSetWindowAttributes attrib_set;
247 gboolean activate = FALSE;
248 ObAppSettings *settings;
249 gboolean transient = FALSE;
250 Rect place, *monitor;
251 Time launch_time, map_time;
256 /* check if it has already been unmapped by the time we started
257 mapping. the grab does a sync so we don't have to here */
258 if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
259 XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
261 XPutBackEvent(ob_display, &e);
263 ob_debug("Trying to manage unmapped window. Aborting that.\n");
265 return; /* don't manage it */
268 /* make sure it isn't an override-redirect window */
269 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
270 attrib.override_redirect)
273 return; /* don't manage it */
276 /* is the window a docking app */
277 if ((wmhint = XGetWMHints(ob_display, window))) {
278 if ((wmhint->flags & StateHint) &&
279 wmhint->initial_state == WithdrawnState)
281 dock_add(window, wmhint);
289 ob_debug("Managing window: 0x%lx\n", window);
291 map_time = event_get_server_time();
293 /* choose the events we want to receive on the CLIENT window
294 (ObPrompt windows can request events too) */
295 attrib_set.event_mask = CLIENT_EVENTMASK |
296 (prompt ? prompt->event_mask : 0);
297 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
298 XChangeWindowAttributes(ob_display, window,
299 CWEventMask|CWDontPropagate, &attrib_set);
301 /* create the ObClient struct, and populate it from the hints on the
303 self = g_new0(ObClient, 1);
304 self->obwin.type = Window_Client;
305 self->window = window;
306 self->prompt = prompt;
307 self->managed = TRUE;
309 /* non-zero defaults */
310 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
311 self->gravity = NorthWestGravity;
312 self->desktop = screen_num_desktops; /* always an invalid value */
314 /* get all the stuff off the window */
315 client_get_all(self, TRUE);
317 ob_debug("Window type: %d\n", self->type);
318 ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0);
319 ob_debug("Window name: %s class: %s role: %s\n", self->name, self->class, self->role);
321 /* per-app settings override stuff from client_get_all, and return the
322 settings for other uses too. the returned settings is a shallow copy,
323 that needs to be freed with g_free(). */
324 settings = client_get_settings_state(self);
326 /* now we have all of the window's information so we can set this up.
327 do this before creating the frame, so it can tell that we are still
328 mapping and doesn't go applying things right away */
329 client_setup_decor_and_functions(self, FALSE);
331 /* specify that if we exit, the window should not be destroyed and
332 should be reparented back to root automatically, unless we are managing
333 an internal ObPrompt window */
335 XChangeSaveSet(ob_display, window, SetModeInsert);
337 /* create the decoration frame for the client window */
338 self->frame = frame_new(self);
340 frame_grab_client(self->frame);
342 /* we've grabbed everything and set everything that we need to at mapping
346 /* the session should get the last say though */
347 client_restore_session_state(self);
349 /* tell startup notification that this app started */
350 launch_time = sn_app_started(self->startup_id, self->class, self->name);
352 if (!PROP_GET32(self->window, net_wm_user_time, cardinal, &user_time))
353 user_time = map_time;
355 /* do this after we have a frame.. it uses the frame to help determine the
356 WM_STATE to apply. */
357 client_change_state(self);
359 /* add ourselves to the focus order */
360 focus_order_add_new(self);
362 /* do this to add ourselves to the stacking list in a non-intrusive way */
363 client_calc_layer(self);
365 /* focus the new window? */
366 if (ob_state() != OB_STATE_STARTING &&
367 (!self->session || self->session->focused) &&
368 /* this means focus=true for window is same as config_focus_new=true */
369 ((config_focus_new || (settings && settings->focus == 1)) ||
370 client_search_focus_tree_full(self)) &&
371 /* NET_WM_USER_TIME 0 when mapping means don't focus */
373 /* this checks for focus=false for the window */
374 (!settings || settings->focus != 0) &&
375 focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE,
376 settings->focus == 1))
381 /* remove the client's border */
382 XSetWindowBorderWidth(ob_display, self->window, 0);
384 /* adjust the frame to the client's size before showing or placing
386 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
387 frame_adjust_client_area(self->frame);
389 /* where the frame was placed is where the window was originally */
391 monitor = screen_physical_area_monitor(screen_find_monitor(&place));
393 /* figure out placement for the window if the window is new */
394 if (ob_state() == OB_STATE_RUNNING) {
395 ob_debug("Positioned: %s @ %d %d\n",
396 (!self->positioned ? "no" :
397 (self->positioned == PPosition ? "program specified" :
398 (self->positioned == USPosition ? "user specified" :
399 (self->positioned == (PPosition | USPosition) ?
400 "program + user specified" :
401 "BADNESS !?")))), place.x, place.y);
403 ob_debug("Sized: %s @ %d %d\n",
404 (!self->sized ? "no" :
405 (self->sized == PSize ? "program specified" :
406 (self->sized == USSize ? "user specified" :
407 (self->sized == (PSize | USSize) ?
408 "program + user specified" :
409 "BADNESS !?")))), place.width, place.height);
411 /* splash screens are also returned as TRUE for transient,
412 and so will be forced on screen below */
413 transient = place_client(self, &place.x, &place.y, settings);
415 /* make sure the window is visible. */
416 client_find_onscreen(self, &place.x, &place.y,
417 place.width, place.height,
418 /* non-normal clients has less rules, and
419 windows that are being restored from a
420 session do also. we can assume you want
421 it back where you saved it. Clients saying
422 they placed themselves are subjected to
423 harder rules, ones that are placed by
424 place.c or by the user are allowed partially
425 off-screen and on xinerama divides (ie,
426 it is up to the placement routines to avoid
427 the xinerama divides)
429 splash screens get "transient" set to TRUE by
430 the place_client call
432 ob_state() == OB_STATE_RUNNING &&
434 (!((self->positioned & USPosition) ||
435 (settings && settings->pos_given)) &&
436 client_normal(self) &&
438 /* don't move oldschool fullscreen windows to
439 fit inside the struts (fixes Acroread, which
440 makes its fullscreen window fit the screen
441 but it is not USSize'd or USPosition'd) */
442 !(self->decorations == 0 &&
443 RECT_EQUAL(place, *monitor)))));
446 /* if the window isn't user-sized, then make it fit inside
447 the visible screen area on its monitor. Use basically the same rules
448 for forcing the window on screen in the client_find_onscreen call.
450 do this after place_client, it chooses the monitor!
452 splash screens get "transient" set to TRUE by
453 the place_client call
455 if (ob_state() == OB_STATE_RUNNING &&
457 (!(self->sized & USSize || self->positioned & USPosition) &&
458 client_normal(self) &&
460 /* don't shrink oldschool fullscreen windows to fit inside the
461 struts (fixes Acroread, which makes its fullscreen window
462 fit the screen but it is not USSize'd or USPosition'd) */
463 !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
465 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
467 /* get the size of the frame */
468 place.width += self->frame->size.left + self->frame->size.right;
469 place.height += self->frame->size.top + self->frame->size.bottom;
471 /* fit the window inside the area */
472 place.width = MIN(place.width, a->width);
473 place.height = MIN(place.height, a->height);
475 ob_debug("setting window size to %dx%d\n", place.width, place.height);
477 /* get the size of the client back */
478 place.width -= self->frame->size.left + self->frame->size.right;
479 place.height -= self->frame->size.top + self->frame->size.bottom;
484 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
485 "some restrictions may apply\n",
486 self->window, place.x, place.y, place.width, place.height);
488 ob_debug(" but session requested %d, %d %d x %d instead, "
490 self->session->x, self->session->y,
491 self->session->w, self->session->h);
493 /* do this after the window is placed, so the premax/prefullscreen numbers
496 this also places the window
498 client_apply_startup_state(self, place.x, place.y,
499 place.width, place.height);
504 ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n",
505 activate ? "yes" : "no");
507 activate = client_can_steal_focus(self, map_time, launch_time);
510 /* if the client isn't stealing focus, then hilite it so the user
511 knows it is there, but don't do this if we're restoring from a
513 if (!client_restore_session_stacking(self))
514 client_hilite(self, TRUE);
518 /* This may look rather odd. Well it's because new windows are added
519 to the stacking order non-intrusively. If we're not going to focus
520 the new window or hilite it, then we raise it to the top. This will
521 take affect for things that don't get focused like splash screens.
522 Also if you don't have focus_new enabled, then it's going to get
523 raised to the top. Legacy begets legacy I guess?
525 if (!client_restore_session_stacking(self))
526 stacking_raise(CLIENT_AS_WINDOW(self));
529 mouse_grab_for_client(self, TRUE);
531 /* this has to happen before we try focus the window, but we want it to
532 happen after the client's stacking has been determined or it looks bad
536 if (!config_focus_under_mouse)
537 ignore_start = event_start_ignore_all_enters();
541 if (!config_focus_under_mouse)
542 event_end_ignore_all_enters(ignore_start);
546 gboolean stacked = client_restore_session_stacking(self);
547 client_present(self, FALSE, !stacked, TRUE);
550 /* add to client list/map */
551 client_list = g_list_append(client_list, self);
552 g_hash_table_insert(window_map, &self->window, self);
554 /* this has to happen after we're in the client_list */
555 if (STRUT_EXISTS(self->strut))
556 screen_update_areas();
558 /* update the list hints */
561 /* free the ObAppSettings shallow copy */
564 ob_debug("Managed window 0x%lx plate 0x%x (%s)\n",
565 window, self->frame->window, self->class);
570 ObClient *client_fake_manage(Window window)
573 ObAppSettings *settings;
575 ob_debug("Pretend-managing window: %lx\n", window);
577 /* do this minimal stuff to figure out the client's decorations */
579 self = g_new0(ObClient, 1);
580 self->window = window;
582 client_get_all(self, FALSE);
583 /* per-app settings override stuff, and return the settings for other
584 uses too. this returns a shallow copy that needs to be freed */
585 settings = client_get_settings_state(self);
587 client_setup_decor_and_functions(self, FALSE);
589 /* create the decoration frame for the client window and adjust its size */
590 self->frame = frame_new(self);
591 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
593 ob_debug("gave extents left %d right %d top %d bottom %d\n",
594 self->frame->size.left, self->frame->size.right,
595 self->frame->size.top, self->frame->size.bottom);
597 /* free the ObAppSettings shallow copy */
603 void client_unmanage_all(void)
606 client_unmanage(client_list->data);
609 void client_unmanage(ObClient *self)
614 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
615 self->window, self->frame->window,
616 self->class, self->title ? self->title : "");
618 g_assert(self != NULL);
620 /* we dont want events no more. do this before hiding the frame so we
621 don't generate more events */
622 XSelectInput(ob_display, self->window, NoEventMask);
624 /* ignore enter events from the unmap so it doesnt mess with the focus */
625 if (!config_focus_under_mouse)
626 ignore_start = event_start_ignore_all_enters();
628 frame_hide(self->frame);
629 /* flush to send the hide to the server quickly */
632 if (!config_focus_under_mouse)
633 event_end_ignore_all_enters(ignore_start);
635 mouse_grab_for_client(self, FALSE);
637 self->managed = FALSE;
639 /* remove the window from our save set, unless we are managing an internal
642 XChangeSaveSet(ob_display, self->window, SetModeDelete);
644 /* update the focus lists */
645 focus_order_remove(self);
646 if (client_focused(self)) {
647 /* don't leave an invalid focus_client */
651 /* if we're prompting to kill the client, close that */
652 prompt_unref(self->kill_prompt);
653 self->kill_prompt = NULL;
655 client_list = g_list_remove(client_list, self);
656 stacking_remove(self);
657 g_hash_table_remove(window_map, &self->window);
659 /* once the client is out of the list, update the struts to remove its
661 if (STRUT_EXISTS(self->strut))
662 screen_update_areas();
664 client_call_notifies(self, client_destroy_notifies);
666 /* tell our parent(s) that we're gone */
667 for (it = self->parents; it; it = g_slist_next(it))
668 ((ObClient*)it->data)->transients =
669 g_slist_remove(((ObClient*)it->data)->transients,self);
671 /* tell our transients that we're gone */
672 for (it = self->transients; it; it = g_slist_next(it)) {
673 ((ObClient*)it->data)->parents =
674 g_slist_remove(((ObClient*)it->data)->parents, self);
675 /* we could be keeping our children in a higher layer */
676 client_calc_layer(it->data);
679 /* remove from its group */
681 group_remove(self->group, self);
685 /* restore the window's original geometry so it is not lost */
691 if (self->fullscreen)
692 a = self->pre_fullscreen_area;
693 else if (self->max_horz || self->max_vert) {
694 if (self->max_horz) {
695 a.x = self->pre_max_area.x;
696 a.width = self->pre_max_area.width;
698 if (self->max_vert) {
699 a.y = self->pre_max_area.y;
700 a.height = self->pre_max_area.height;
704 self->fullscreen = self->max_horz = self->max_vert = FALSE;
705 /* let it be moved and resized no matter what */
706 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
707 self->decorations = 0; /* unmanaged windows have no decor */
709 /* give the client its border back */
710 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
712 client_move_resize(self, a.x, a.y, a.width, a.height);
715 /* reparent the window out of the frame, and free the frame */
716 frame_release_client(self->frame);
717 frame_free(self->frame);
720 if (ob_state() != OB_STATE_EXITING) {
721 /* these values should not be persisted across a window
723 PROP_ERASE(self->window, net_wm_desktop);
724 PROP_ERASE(self->window, net_wm_state);
725 PROP_ERASE(self->window, wm_state);
727 /* if we're left in an unmapped state, the client wont be mapped.
728 this is bad, since we will no longer be managing the window on
730 XMapWindow(ob_display, self->window);
733 /* these should not be left on the window ever. other window managers
734 don't necessarily use them and it will mess them up (like compiz) */
735 PROP_ERASE(self->window, net_wm_visible_name);
736 PROP_ERASE(self->window, net_wm_visible_icon_name);
738 /* update the list hints */
741 ob_debug("Unmanaged window 0x%lx\n", self->window);
743 /* free all data allocated in the client struct */
744 RrImageUnref(self->icon_set);
745 g_slist_free(self->transients);
746 g_free(self->startup_id);
747 g_free(self->wm_command);
749 g_free(self->icon_title);
750 g_free(self->original_title);
754 g_free(self->client_machine);
755 g_free(self->sm_client_id);
759 void client_fake_unmanage(ObClient *self)
761 /* this is all that got allocated to get the decorations */
763 frame_free(self->frame);
767 static gboolean client_can_steal_focus(ObClient *self, Time steal_time,
771 gboolean relative_focused;
772 gboolean parent_focused;
776 parent_focused = (focus_client != NULL &&
777 client_search_focus_parent(self));
778 relative_focused = (focus_client != NULL &&
779 (client_search_focus_tree_full(self) != NULL ||
780 client_search_focus_group_full(self) != NULL));
782 /* This is focus stealing prevention */
783 ob_debug_type(OB_DEBUG_FOCUS,
784 "Want to focus new window 0x%x at time %u "
785 "launched at %u (last user interaction time %u)\n",
786 self->window, steal_time, launch_time,
787 event_last_user_time);
789 /* if it's on another desktop */
790 if (!(self->desktop == screen_desktop ||
791 self->desktop == DESKTOP_ALL) &&
792 /* the timestamp is from before you changed desktops */
793 launch_time && screen_desktop_user_time &&
794 !event_time_after(launch_time, screen_desktop_user_time))
797 ob_debug_type(OB_DEBUG_FOCUS,
798 "Not focusing the window because its on another "
801 /* If something is focused... */
802 else if (focus_client) {
803 /* If the user is working in another window right now, then don't
805 if (!parent_focused &&
806 event_last_user_time && launch_time &&
807 event_time_after(event_last_user_time, launch_time) &&
808 event_last_user_time != launch_time &&
809 event_time_after(event_last_user_time,
810 steal_time - OB_EVENT_USER_TIME_DELAY))
813 ob_debug_type(OB_DEBUG_FOCUS,
814 "Not focusing the window because the user is "
815 "working in another window that is not "
818 /* If the new window is a transient (and its relatives aren't
820 else if (client_has_parent(self) && !relative_focused) {
822 ob_debug_type(OB_DEBUG_FOCUS,
823 "Not focusing the window because it is a "
824 "transient, and its relatives aren't focused\n");
826 /* Don't steal focus from globally active clients.
827 I stole this idea from KWin. It seems nice.
829 else if (!(focus_client->can_focus ||
830 focus_client->focus_notify))
833 ob_debug_type(OB_DEBUG_FOCUS,
834 "Not focusing the window because a globally "
835 "active client has focus\n");
837 /* Don't move focus if it's not going to go to this window
839 else if (client_focus_target(self) != self) {
841 ob_debug_type(OB_DEBUG_FOCUS,
842 "Not focusing the window because another window "
843 "would get the focus anyway\n");
845 /* Don't move focus if the window is not visible on the current
846 desktop and none of its relatives are focused */
847 else if (!(self->desktop == screen_desktop ||
848 self->desktop == DESKTOP_ALL) &&
852 ob_debug_type(OB_DEBUG_FOCUS,
853 "Not focusing the window because it is on "
854 "another desktop and no relatives are focused ");
859 ob_debug_type(OB_DEBUG_FOCUS,
860 "Focus stealing prevention activated for %s at "
861 "time %u (last user interaction time %u)\n",
862 self->title, steal_time, event_last_user_time);
866 /*! Returns a new structure containing the per-app settings for this client.
867 The returned structure needs to be freed with g_free. */
868 static ObAppSettings *client_get_settings_state(ObClient *self)
870 ObAppSettings *settings;
873 settings = config_create_app_settings();
875 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
876 ObAppSettings *app = it->data;
877 gboolean match = TRUE;
879 g_assert(app->name != NULL || app->class != NULL);
881 /* we know that either name or class is not NULL so it will have to
882 match to use the rule */
884 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
886 else if (app->class &&
887 !g_pattern_match(app->class,
888 strlen(self->class), self->class, NULL))
890 else if (app->role &&
891 !g_pattern_match(app->role,
892 strlen(self->role), self->role, NULL))
894 else if ((signed)app->type >= 0 && app->type != self->type)
898 ob_debug("Window matching: %s\n", app->name);
900 /* copy the settings to our struct, overriding the existing
901 settings if they are not defaults */
902 config_app_settings_copy_non_defaults(app, settings);
906 if (settings->shade != -1)
907 self->shaded = !!settings->shade;
908 if (settings->decor != -1)
909 self->undecorated = !settings->decor;
910 if (settings->iconic != -1)
911 self->iconic = !!settings->iconic;
912 if (settings->skip_pager != -1)
913 self->skip_pager = !!settings->skip_pager;
914 if (settings->skip_taskbar != -1)
915 self->skip_taskbar = !!settings->skip_taskbar;
917 if (settings->max_vert != -1)
918 self->max_vert = !!settings->max_vert;
919 if (settings->max_horz != -1)
920 self->max_horz = !!settings->max_horz;
922 if (settings->fullscreen != -1)
923 self->fullscreen = !!settings->fullscreen;
925 if (settings->desktop) {
926 if (settings->desktop == DESKTOP_ALL)
927 self->desktop = settings->desktop;
928 else if (settings->desktop > 0 &&
929 settings->desktop <= screen_num_desktops)
930 self->desktop = settings->desktop - 1;
933 if (settings->layer == -1) {
937 else if (settings->layer == 0) {
941 else if (settings->layer == 1) {
948 static void client_restore_session_state(ObClient *self)
952 ob_debug_type(OB_DEBUG_SM,
953 "Restore session for client %s\n", self->title);
955 if (!(it = session_state_find(self))) {
956 ob_debug_type(OB_DEBUG_SM,
957 "Session data not found for client %s\n", self->title);
961 self->session = it->data;
963 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s\n",
966 RECT_SET_POINT(self->area, self->session->x, self->session->y);
967 self->positioned = USPosition;
968 self->sized = USSize;
969 if (self->session->w > 0)
970 self->area.width = self->session->w;
971 if (self->session->h > 0)
972 self->area.height = self->session->h;
973 XResizeWindow(ob_display, self->window,
974 self->area.width, self->area.height);
976 self->desktop = (self->session->desktop == DESKTOP_ALL ?
977 self->session->desktop :
978 MIN(screen_num_desktops - 1, self->session->desktop));
979 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
981 self->shaded = self->session->shaded;
982 self->iconic = self->session->iconic;
983 self->skip_pager = self->session->skip_pager;
984 self->skip_taskbar = self->session->skip_taskbar;
985 self->fullscreen = self->session->fullscreen;
986 self->above = self->session->above;
987 self->below = self->session->below;
988 self->max_horz = self->session->max_horz;
989 self->max_vert = self->session->max_vert;
990 self->undecorated = self->session->undecorated;
993 static gboolean client_restore_session_stacking(ObClient *self)
997 if (!self->session) return FALSE;
999 mypos = g_list_find(session_saved_state, self->session);
1000 if (!mypos) return FALSE;
1002 /* start above me and look for the first client */
1003 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
1006 for (cit = client_list; cit; cit = g_list_next(cit)) {
1007 ObClient *c = cit->data;
1008 /* found a client that was in the session, so go below it */
1009 if (c->session == it->data) {
1010 stacking_below(CLIENT_AS_WINDOW(self),
1011 CLIENT_AS_WINDOW(cit->data));
1019 void client_move_onscreen(ObClient *self, gboolean rude)
1021 gint x = self->area.x;
1022 gint y = self->area.y;
1023 if (client_find_onscreen(self, &x, &y,
1025 self->area.height, rude)) {
1026 client_move(self, x, y);
1030 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
1033 gint ox = *x, oy = *y;
1034 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
1040 RECT_SET(desired, *x, *y, w, h);
1041 frame_rect_to_frame(self->frame, &desired);
1043 /* get where the frame would be */
1044 frame_client_gravity(self->frame, x, y);
1046 /* get the requested size of the window with decorations */
1047 fw = self->frame->size.left + w + self->frame->size.right;
1048 fh = self->frame->size.top + h + self->frame->size.bottom;
1050 /* If rudeness wasn't requested, then still be rude in a given direction
1051 if the client is not moving, only resizing in that direction */
1053 Point oldtl, oldtr, oldbl, oldbr;
1054 Point newtl, newtr, newbl, newbr;
1055 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
1057 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
1058 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
1059 self->frame->area.y + self->frame->area.height - 1);
1060 POINT_SET(oldtr, oldbr.x, oldtl.y);
1061 POINT_SET(oldbl, oldtl.x, oldbr.y);
1063 POINT_SET(newtl, *x, *y);
1064 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
1065 POINT_SET(newtr, newbr.x, newtl.y);
1066 POINT_SET(newbl, newtl.x, newbr.y);
1068 /* is it moving or just resizing from some corner? */
1069 stationary_l = oldtl.x == newtl.x;
1070 stationary_r = oldtr.x == newtr.x;
1071 stationary_t = oldtl.y == newtl.y;
1072 stationary_b = oldbl.y == newbl.y;
1074 /* if left edge is growing and didnt move right edge */
1075 if (stationary_r && newtl.x < oldtl.x)
1077 /* if right edge is growing and didnt move left edge */
1078 if (stationary_l && newtr.x > oldtr.x)
1080 /* if top edge is growing and didnt move bottom edge */
1081 if (stationary_b && newtl.y < oldtl.y)
1083 /* if bottom edge is growing and didnt move top edge */
1084 if (stationary_t && newbl.y > oldbl.y)
1088 /* we iterate through every monitor that the window is at least partially
1089 on, to make sure it is obeying the rules on them all
1091 if the window does not appear on any monitors, then use the first one
1094 for (i = 0; i < screen_num_monitors; ++i) {
1097 if (!screen_physical_area_monitor_contains(i, &desired)) {
1098 if (i < screen_num_monitors - 1 || found_mon)
1101 /* the window is not inside any monitor! so just use the first
1103 a = screen_area(self->desktop, 0, NULL);
1106 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1109 /* This makes sure windows aren't entirely outside of the screen so you
1110 can't see them at all.
1111 It makes sure 10% of the window is on the screen at least. And don't
1112 let it move itself off the top of the screen, which would hide the
1113 titlebar on you. (The user can still do this if they want too, it's
1114 only limiting the application.
1116 if (client_normal(self)) {
1117 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1118 *x = a->x + a->width - fw/10;
1119 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1120 *y = a->y + a->height - fh/10;
1121 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1122 *x = a->x - fw*9/10;
1123 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1124 *y = a->y - fh*9/10;
1127 /* This here doesn't let windows even a pixel outside the
1128 struts/screen. When called from client_manage, programs placing
1129 themselves are forced completely onscreen, while things like
1130 xterm -geometry resolution-width/2 will work fine. Trying to
1131 place it completely offscreen will be handled in the above code.
1132 Sorry for this confused comment, i am tired. */
1133 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1134 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1135 *x = a->x + MAX(0, a->width - fw);
1137 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1138 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1139 *y = a->y + MAX(0, a->height - fh);
1144 /* get where the client should be */
1145 frame_frame_gravity(self->frame, x, y);
1147 return ox != *x || oy != *y;
1150 static void client_get_all(ObClient *self, gboolean real)
1152 /* this is needed for the frame to set itself up */
1153 client_get_area(self);
1155 /* these things can change the decor and functions of the window */
1157 client_get_mwm_hints(self);
1158 /* this can change the mwmhints for special cases */
1159 client_get_type_and_transientness(self);
1160 client_get_state(self);
1161 client_update_normal_hints(self);
1163 /* get the session related properties, these can change decorations
1164 from per-app settings */
1165 client_get_session_ids(self);
1166 client_save_session_ids(self);
1168 /* now we got everything that can affect the decorations */
1172 /* get this early so we have it for debugging */
1173 client_update_title(self);
1175 client_update_protocols(self);
1177 client_update_wmhints(self);
1178 /* this may have already been called from client_update_wmhints */
1179 if (!self->parents && !self->transient_for_group)
1180 client_update_transient_for(self);
1182 client_get_startup_id(self);
1183 client_get_desktop(self);/* uses transient data/group/startup id if a
1184 desktop is not specified */
1185 client_get_shaped(self);
1188 /* a couple type-based defaults for new windows */
1190 /* this makes sure that these windows appear on all desktops */
1191 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1192 self->desktop = DESKTOP_ALL;
1196 client_update_sync_request_counter(self);
1199 client_get_colormap(self);
1200 client_update_strut(self);
1201 client_update_icons(self);
1202 client_update_icon_geometry(self);
1205 static void client_get_startup_id(ObClient *self)
1207 if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
1209 PROP_GETS(self->group->leader,
1210 net_startup_id, utf8, &self->startup_id);
1213 static void client_get_area(ObClient *self)
1215 XWindowAttributes wattrib;
1218 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1219 g_assert(ret != BadWindow);
1221 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1222 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1223 self->border_width = wattrib.border_width;
1225 ob_debug("client area: %d %d %d %d bw %d\n", wattrib.x, wattrib.y,
1226 wattrib.width, wattrib.height, wattrib.border_width);
1229 static void client_get_desktop(ObClient *self)
1231 guint32 d = screen_num_desktops; /* an always-invalid value */
1233 if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
1234 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1235 self->desktop = screen_num_desktops - 1;
1238 ob_debug("client requested desktop 0x%x\n", self->desktop);
1241 gboolean first = TRUE;
1242 guint all = screen_num_desktops; /* not a valid value */
1244 /* if they are all on one desktop, then open it on the
1246 for (it = self->parents; it; it = g_slist_next(it)) {
1247 ObClient *c = it->data;
1249 if (c->desktop == DESKTOP_ALL) continue;
1255 else if (all != c->desktop)
1256 all = screen_num_desktops; /* make it invalid */
1258 if (all != screen_num_desktops) {
1259 self->desktop = all;
1261 ob_debug("client desktop set from parents: 0x%x\n",
1264 /* try get from the startup-notification protocol */
1265 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1266 if (self->desktop >= screen_num_desktops &&
1267 self->desktop != DESKTOP_ALL)
1268 self->desktop = screen_num_desktops - 1;
1269 ob_debug("client desktop set from startup-notification: 0x%x\n",
1272 /* defaults to the current desktop */
1274 self->desktop = screen_desktop;
1275 ob_debug("client desktop set to the current desktop: %d\n",
1281 static void client_get_state(ObClient *self)
1286 if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
1288 for (i = 0; i < num; ++i) {
1289 if (state[i] == prop_atoms.net_wm_state_modal)
1291 else if (state[i] == prop_atoms.net_wm_state_shaded)
1292 self->shaded = TRUE;
1293 else if (state[i] == prop_atoms.net_wm_state_hidden)
1294 self->iconic = TRUE;
1295 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
1296 self->skip_taskbar = TRUE;
1297 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
1298 self->skip_pager = TRUE;
1299 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
1300 self->fullscreen = TRUE;
1301 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
1302 self->max_vert = TRUE;
1303 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
1304 self->max_horz = TRUE;
1305 else if (state[i] == prop_atoms.net_wm_state_above)
1307 else if (state[i] == prop_atoms.net_wm_state_below)
1309 else if (state[i] == prop_atoms.net_wm_state_demands_attention)
1310 self->demands_attention = TRUE;
1311 else if (state[i] == prop_atoms.ob_wm_state_undecorated)
1312 self->undecorated = TRUE;
1319 static void client_get_shaped(ObClient *self)
1321 self->shaped = FALSE;
1323 if (extensions_shape) {
1328 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
1330 XShapeQueryExtents(ob_display, self->window, &s, &foo,
1331 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1338 void client_update_transient_for(ObClient *self)
1341 ObClient *target = NULL;
1342 gboolean trangroup = FALSE;
1344 if (XGetTransientForHint(ob_display, self->window, &t)) {
1345 if (t != self->window) { /* can't be transient to itself! */
1346 target = g_hash_table_lookup(window_map, &t);
1347 /* if this happens then we need to check for it */
1348 g_assert(target != self);
1349 if (target && !WINDOW_IS_CLIENT(target)) {
1350 /* watch out for windows with a parent that is something
1351 different, like a dockapp for example */
1356 /* Setting the transient_for to Root is actually illegal, however
1357 applications from time have done this to specify transient for
1359 if (!target && self->group && t == RootWindow(ob_display, ob_screen))
1361 } else if (self->group && self->transient)
1364 client_update_transient_tree(self, self->group, self->group,
1365 self->transient_for_group, trangroup,
1366 client_direct_parent(self), target);
1367 self->transient_for_group = trangroup;
1371 static void client_update_transient_tree(ObClient *self,
1372 ObGroup *oldgroup, ObGroup *newgroup,
1373 gboolean oldgtran, gboolean newgtran,
1374 ObClient* oldparent,
1375 ObClient *newparent)
1380 g_assert(!oldgtran || oldgroup);
1381 g_assert(!newgtran || newgroup);
1382 g_assert((!oldgtran && !oldparent) ||
1383 (oldgtran && !oldparent) ||
1384 (!oldgtran && oldparent));
1385 g_assert((!newgtran && !newparent) ||
1386 (newgtran && !newparent) ||
1387 (!newgtran && newparent));
1390 Group transient windows are not allowed to have other group
1391 transient windows as their children.
1394 /* No change has occured */
1395 if (oldgroup == newgroup &&
1396 oldgtran == newgtran &&
1397 oldparent == newparent) return;
1399 /** Remove the client from the transient tree **/
1401 for (it = self->transients; it; it = next) {
1402 next = g_slist_next(it);
1404 self->transients = g_slist_delete_link(self->transients, it);
1405 c->parents = g_slist_remove(c->parents, self);
1407 for (it = self->parents; it; it = next) {
1408 next = g_slist_next(it);
1410 self->parents = g_slist_delete_link(self->parents, it);
1411 c->transients = g_slist_remove(c->transients, self);
1414 /** Re-add the client to the transient tree **/
1416 /* If we're transient for a group then we need to add ourselves to all our
1419 for (it = newgroup->members; it; it = g_slist_next(it)) {
1422 !client_search_top_direct_parent(c)->transient_for_group &&
1425 c->transients = g_slist_prepend(c->transients, self);
1426 self->parents = g_slist_prepend(self->parents, c);
1431 /* If we are now transient for a single window we need to add ourselves to
1434 WARNING: Cyclical transient-ness is possible if two windows are
1435 transient for eachother.
1437 else if (newparent &&
1438 /* don't make ourself its child if it is already our child */
1439 !client_is_direct_child(self, newparent) &&
1440 client_normal(newparent))
1442 newparent->transients = g_slist_prepend(newparent->transients, self);
1443 self->parents = g_slist_prepend(self->parents, newparent);
1446 /* Add any group transient windows to our children. But if we're transient
1447 for the group, then other group transients are not our children.
1449 WARNING: Cyclical transient-ness is possible. For e.g. if:
1450 A is transient for the group
1451 B is transient for A
1452 C is transient for B
1453 A can't be transient for C or we have a cycle
1455 if (!newgtran && newgroup &&
1457 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1458 client_normal(self))
1460 for (it = newgroup->members; it; it = g_slist_next(it)) {
1462 if (c != self && c->transient_for_group &&
1463 /* Don't make it our child if it is already our parent */
1464 !client_is_direct_child(c, self))
1466 self->transients = g_slist_prepend(self->transients, c);
1467 c->parents = g_slist_prepend(c->parents, self);
1472 /** If we change our group transient-ness, our children change their
1473 effective group transient-ness, which affects how they relate to other
1476 for (it = self->transients; it; it = g_slist_next(it)) {
1478 if (!c->transient_for_group)
1479 client_update_transient_tree(c, c->group, c->group,
1480 c->transient_for_group,
1481 c->transient_for_group,
1482 client_direct_parent(c),
1483 client_direct_parent(c));
1487 void client_get_mwm_hints(ObClient *self)
1492 self->mwmhints.flags = 0; /* default to none */
1494 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
1496 if (num >= OB_MWM_ELEMENTS) {
1497 self->mwmhints.flags = hints[0];
1498 self->mwmhints.functions = hints[1];
1499 self->mwmhints.decorations = hints[2];
1505 void client_get_type_and_transientness(ObClient *self)
1512 self->transient = FALSE;
1514 if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1515 /* use the first value that we know about in the array */
1516 for (i = 0; i < num; ++i) {
1517 if (val[i] == prop_atoms.net_wm_window_type_desktop)
1518 self->type = OB_CLIENT_TYPE_DESKTOP;
1519 else if (val[i] == prop_atoms.net_wm_window_type_dock)
1520 self->type = OB_CLIENT_TYPE_DOCK;
1521 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1522 self->type = OB_CLIENT_TYPE_TOOLBAR;
1523 else if (val[i] == prop_atoms.net_wm_window_type_menu)
1524 self->type = OB_CLIENT_TYPE_MENU;
1525 else if (val[i] == prop_atoms.net_wm_window_type_utility)
1526 self->type = OB_CLIENT_TYPE_UTILITY;
1527 else if (val[i] == prop_atoms.net_wm_window_type_splash)
1528 self->type = OB_CLIENT_TYPE_SPLASH;
1529 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1530 self->type = OB_CLIENT_TYPE_DIALOG;
1531 else if (val[i] == prop_atoms.net_wm_window_type_normal)
1532 self->type = OB_CLIENT_TYPE_NORMAL;
1533 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1534 /* prevent this window from getting any decor or
1536 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1537 OB_MWM_FLAG_DECORATIONS);
1538 self->mwmhints.decorations = 0;
1539 self->mwmhints.functions = 0;
1541 if (self->type != (ObClientType) -1)
1542 break; /* grab the first legit type */
1547 if (XGetTransientForHint(ob_display, self->window, &t))
1548 self->transient = TRUE;
1550 if (self->type == (ObClientType) -1) {
1551 /*the window type hint was not set, which means we either classify
1552 ourself as a normal window or a dialog, depending on if we are a
1554 if (self->transient)
1555 self->type = OB_CLIENT_TYPE_DIALOG;
1557 self->type = OB_CLIENT_TYPE_NORMAL;
1560 /* then, based on our type, we can update our transientness.. */
1561 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1562 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1563 self->type == OB_CLIENT_TYPE_MENU ||
1564 self->type == OB_CLIENT_TYPE_UTILITY)
1566 self->transient = TRUE;
1570 void client_update_protocols(ObClient *self)
1573 guint num_return, i;
1575 self->focus_notify = FALSE;
1576 self->delete_window = FALSE;
1578 if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1579 for (i = 0; i < num_return; ++i) {
1580 if (proto[i] == prop_atoms.wm_delete_window)
1581 /* this means we can request the window to close */
1582 self->delete_window = TRUE;
1583 else if (proto[i] == prop_atoms.wm_take_focus)
1584 /* if this protocol is requested, then the window will be
1585 notified whenever we want it to receive focus */
1586 self->focus_notify = TRUE;
1587 else if (proto[i] == prop_atoms.net_wm_ping)
1588 /* if this protocol is requested, then the window will allow
1589 pings to determine if it is still alive */
1592 else if (proto[i] == prop_atoms.net_wm_sync_request)
1593 /* if this protocol is requested, then resizing the
1594 window will be synchronized between the frame and the
1596 self->sync_request = TRUE;
1604 void client_update_sync_request_counter(ObClient *self)
1608 if (PROP_GET32(self->window, net_wm_sync_request_counter, cardinal, &i)) {
1609 self->sync_counter = i;
1611 self->sync_counter = None;
1615 static void client_get_colormap(ObClient *self)
1617 XWindowAttributes wa;
1619 if (XGetWindowAttributes(ob_display, self->window, &wa))
1620 client_update_colormap(self, wa.colormap);
1623 void client_update_colormap(ObClient *self, Colormap colormap)
1625 if (colormap == self->colormap) return;
1627 ob_debug("Setting client %s colormap: 0x%x\n", self->title, colormap);
1629 if (client_focused(self)) {
1630 screen_install_colormap(self, FALSE); /* uninstall old one */
1631 self->colormap = colormap;
1632 screen_install_colormap(self, TRUE); /* install new one */
1634 self->colormap = colormap;
1637 void client_update_normal_hints(ObClient *self)
1643 self->min_ratio = 0.0f;
1644 self->max_ratio = 0.0f;
1645 SIZE_SET(self->size_inc, 1, 1);
1646 SIZE_SET(self->base_size, -1, -1);
1647 SIZE_SET(self->min_size, 0, 0);
1648 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1650 /* get the hints from the window */
1651 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1652 /* normal windows can't request placement! har har
1653 if (!client_normal(self))
1655 self->positioned = (size.flags & (PPosition|USPosition));
1656 self->sized = (size.flags & (PSize|USSize));
1658 if (size.flags & PWinGravity)
1659 self->gravity = size.win_gravity;
1661 if (size.flags & PAspect) {
1662 if (size.min_aspect.y)
1664 (gfloat) size.min_aspect.x / size.min_aspect.y;
1665 if (size.max_aspect.y)
1667 (gfloat) size.max_aspect.x / size.max_aspect.y;
1670 if (size.flags & PMinSize)
1671 SIZE_SET(self->min_size, size.min_width, size.min_height);
1673 if (size.flags & PMaxSize)
1674 SIZE_SET(self->max_size, size.max_width, size.max_height);
1676 if (size.flags & PBaseSize)
1677 SIZE_SET(self->base_size, size.base_width, size.base_height);
1679 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1680 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1682 ob_debug("Normal hints: min size (%d %d) max size (%d %d)\n "
1683 "size inc (%d %d) base size (%d %d)\n",
1684 self->min_size.width, self->min_size.height,
1685 self->max_size.width, self->max_size.height,
1686 self->size_inc.width, self->size_inc.height,
1687 self->base_size.width, self->base_size.height);
1690 ob_debug("Normal hints: not set\n");
1693 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1695 /* start with everything (cept fullscreen) */
1697 (OB_FRAME_DECOR_TITLEBAR |
1698 OB_FRAME_DECOR_HANDLE |
1699 OB_FRAME_DECOR_GRIPS |
1700 OB_FRAME_DECOR_BORDER |
1701 OB_FRAME_DECOR_ICON |
1702 OB_FRAME_DECOR_ALLDESKTOPS |
1703 OB_FRAME_DECOR_ICONIFY |
1704 OB_FRAME_DECOR_MAXIMIZE |
1705 OB_FRAME_DECOR_SHADE |
1706 OB_FRAME_DECOR_CLOSE);
1708 (OB_CLIENT_FUNC_RESIZE |
1709 OB_CLIENT_FUNC_MOVE |
1710 OB_CLIENT_FUNC_ICONIFY |
1711 OB_CLIENT_FUNC_MAXIMIZE |
1712 OB_CLIENT_FUNC_SHADE |
1713 OB_CLIENT_FUNC_CLOSE |
1714 OB_CLIENT_FUNC_BELOW |
1715 OB_CLIENT_FUNC_ABOVE |
1716 OB_CLIENT_FUNC_UNDECORATE);
1718 if (!(self->min_size.width < self->max_size.width ||
1719 self->min_size.height < self->max_size.height))
1720 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1722 switch (self->type) {
1723 case OB_CLIENT_TYPE_NORMAL:
1724 /* normal windows retain all of the possible decorations and
1725 functionality, and can be fullscreen */
1726 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1729 case OB_CLIENT_TYPE_DIALOG:
1730 /* sometimes apps make dialog windows fullscreen for some reason (for
1731 e.g. kpdf does this..) */
1732 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1735 case OB_CLIENT_TYPE_UTILITY:
1736 /* these windows don't have anything added or removed by default */
1739 case OB_CLIENT_TYPE_MENU:
1740 case OB_CLIENT_TYPE_TOOLBAR:
1741 /* these windows can't iconify or maximize */
1742 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1743 OB_FRAME_DECOR_MAXIMIZE);
1744 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1745 OB_CLIENT_FUNC_MAXIMIZE);
1748 case OB_CLIENT_TYPE_SPLASH:
1749 /* these don't get get any decorations, and the only thing you can
1750 do with them is move them */
1751 self->decorations = 0;
1752 self->functions = OB_CLIENT_FUNC_MOVE;
1755 case OB_CLIENT_TYPE_DESKTOP:
1756 /* these windows are not manipulated by the window manager */
1757 self->decorations = 0;
1758 self->functions = 0;
1761 case OB_CLIENT_TYPE_DOCK:
1762 /* these windows are not manipulated by the window manager, but they
1763 can set below layer which has a special meaning */
1764 self->decorations = 0;
1765 self->functions = OB_CLIENT_FUNC_BELOW;
1769 /* Mwm Hints are applied subtractively to what has already been chosen for
1770 decor and functionality */
1771 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1772 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1773 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1774 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1776 /* if the mwm hints request no handle or title, then all
1777 decorations are disabled, but keep the border if that's
1779 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1780 self->decorations = OB_FRAME_DECOR_BORDER;
1782 self->decorations = 0;
1787 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1788 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1789 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1790 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1791 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1792 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1793 /* dont let mwm hints kill any buttons
1794 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1795 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1796 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1797 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1799 /* dont let mwm hints kill the close button
1800 if (! (self->mwmhints.functions & MwmFunc_Close))
1801 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1805 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1806 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1807 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1808 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1809 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1810 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1812 /* can't maximize without moving/resizing */
1813 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1814 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1815 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1816 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1817 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1820 if (self->max_horz && self->max_vert) {
1821 /* you can't resize fully maximized windows */
1822 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1823 /* kill the handle on fully maxed windows */
1824 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1827 /* If there are no decorations to remove, don't allow the user to try
1829 if (self->decorations == 0)
1830 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1832 /* finally, the user can have requested no decorations, which overrides
1833 everything (but doesnt give it a border if it doesnt have one) */
1834 if (self->undecorated)
1835 self->decorations &= (config_theme_keepborder ?
1836 OB_FRAME_DECOR_BORDER : 0);
1838 /* if we don't have a titlebar, then we cannot shade! */
1839 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1840 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1842 /* now we need to check against rules for the client's current state */
1843 if (self->fullscreen) {
1844 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1845 OB_CLIENT_FUNC_FULLSCREEN |
1846 OB_CLIENT_FUNC_ICONIFY);
1847 self->decorations = 0;
1850 client_change_allowed_actions(self);
1853 /* force reconfigure to make sure decorations are updated */
1854 client_reconfigure(self, TRUE);
1857 static void client_change_allowed_actions(ObClient *self)
1862 /* desktop windows are kept on all desktops */
1863 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1864 actions[num++] = prop_atoms.net_wm_action_change_desktop;
1866 if (self->functions & OB_CLIENT_FUNC_SHADE)
1867 actions[num++] = prop_atoms.net_wm_action_shade;
1868 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1869 actions[num++] = prop_atoms.net_wm_action_close;
1870 if (self->functions & OB_CLIENT_FUNC_MOVE)
1871 actions[num++] = prop_atoms.net_wm_action_move;
1872 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1873 actions[num++] = prop_atoms.net_wm_action_minimize;
1874 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1875 actions[num++] = prop_atoms.net_wm_action_resize;
1876 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1877 actions[num++] = prop_atoms.net_wm_action_fullscreen;
1878 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1879 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1880 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1882 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1883 actions[num++] = prop_atoms.net_wm_action_above;
1884 if (self->functions & OB_CLIENT_FUNC_BELOW)
1885 actions[num++] = prop_atoms.net_wm_action_below;
1886 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1887 actions[num++] = prop_atoms.ob_wm_action_undecorate;
1889 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1891 /* make sure the window isn't breaking any rules now
1893 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1894 it can't be iconified with its parent
1897 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1898 if (self->frame) client_shade(self, FALSE);
1899 else self->shaded = FALSE;
1901 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1902 if (self->frame) client_fullscreen(self, FALSE);
1903 else self->fullscreen = FALSE;
1905 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1907 if (self->frame) client_maximize(self, FALSE, 0);
1908 else self->max_vert = self->max_horz = FALSE;
1912 void client_update_wmhints(ObClient *self)
1916 /* assume a window takes input if it doesn't specify */
1917 self->can_focus = TRUE;
1919 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1922 if (hints->flags & InputHint)
1923 self->can_focus = hints->input;
1925 /* only do this when first managing the window *AND* when we aren't
1927 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1928 if (hints->flags & StateHint)
1929 self->iconic = hints->initial_state == IconicState;
1932 self->urgent = (hints->flags & XUrgencyHint);
1933 if (self->urgent && !ur)
1934 client_hilite(self, TRUE);
1935 else if (!self->urgent && ur && self->demands_attention)
1936 client_hilite(self, FALSE);
1938 if (!(hints->flags & WindowGroupHint))
1939 hints->window_group = None;
1941 /* did the group state change? */
1942 if (hints->window_group !=
1943 (self->group ? self->group->leader : None))
1945 ObGroup *oldgroup = self->group;
1947 /* remove from the old group if there was one */
1949 group_remove(self->group, self);
1953 /* add ourself to the group if we have one */
1954 if (hints->window_group != None) {
1955 self->group = group_add(hints->window_group, self);
1958 /* Put ourselves into the new group's transient tree, and remove
1959 ourselves from the old group's */
1960 client_update_transient_tree(self, oldgroup, self->group,
1961 self->transient_for_group,
1962 self->transient_for_group,
1963 client_direct_parent(self),
1964 client_direct_parent(self));
1966 /* Lastly, being in a group, or not, can change if the window is
1967 transient for anything.
1969 The logic for this is:
1970 self->transient = TRUE always if the window wants to be
1971 transient for something, even if transient_for was NULL because
1972 it wasn't in a group before.
1974 If parents was NULL and oldgroup was NULL we can assume
1975 that when we add the new group, it will become transient for
1978 If transient_for_group is TRUE, then it must have already
1979 had a group. If it is getting a new group, the above call to
1980 client_update_transient_tree has already taken care of
1981 everything ! If it is losing all group status then it will
1982 no longer be transient for anything and that needs to be
1985 if (self->transient &&
1986 ((self->parents == NULL && oldgroup == NULL) ||
1987 (self->transient_for_group && !self->group)))
1988 client_update_transient_for(self);
1991 /* the WM_HINTS can contain an icon */
1992 if (hints->flags & IconPixmapHint)
1993 client_update_icons(self);
1999 void client_update_title(ObClient *self)
2002 gchar *visible = NULL;
2004 g_free(self->title);
2005 g_free(self->original_title);
2008 if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
2009 /* try old x stuff */
2010 if (!(PROP_GETS(self->window, wm_name, locale, &data)
2011 || PROP_GETS(self->window, wm_name, utf8, &data))) {
2012 if (self->transient) {
2014 GNOME alert windows are not given titles:
2015 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
2017 data = g_strdup("");
2019 data = g_strdup(_("Unnamed Window"));
2022 self->original_title = g_strdup(data);
2024 if (self->client_machine) {
2025 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2030 if (self->not_responding) {
2032 if (self->kill_level > 0)
2033 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2035 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2039 PROP_SETS(self->window, net_wm_visible_name, visible);
2040 self->title = visible;
2043 frame_adjust_title(self->frame);
2045 /* update the icon title */
2047 g_free(self->icon_title);
2050 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
2051 /* try old x stuff */
2052 if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) ||
2053 PROP_GETS(self->window, wm_icon_name, utf8, &data)))
2054 data = g_strdup(self->title);
2056 if (self->client_machine) {
2057 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2062 if (self->not_responding) {
2064 if (self->kill_level > 0)
2065 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2067 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2071 PROP_SETS(self->window, net_wm_visible_icon_name, visible);
2072 self->icon_title = visible;
2075 void client_update_strut(ObClient *self)
2079 gboolean got = FALSE;
2082 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
2086 STRUT_PARTIAL_SET(strut,
2087 data[0], data[2], data[1], data[3],
2088 data[4], data[5], data[8], data[9],
2089 data[6], data[7], data[10], data[11]);
2095 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
2101 /* use the screen's width/height */
2102 a = screen_physical_area_all_monitors();
2104 STRUT_PARTIAL_SET(strut,
2105 data[0], data[2], data[1], data[3],
2106 a->y, a->y + a->height - 1,
2107 a->x, a->x + a->width - 1,
2108 a->y, a->y + a->height - 1,
2109 a->x, a->x + a->width - 1);
2116 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2117 0, 0, 0, 0, 0, 0, 0, 0);
2119 if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
2120 self->strut = strut;
2122 /* updating here is pointless while we're being mapped cuz we're not in
2123 the client list yet */
2125 screen_update_areas();
2129 void client_update_icons(ObClient *self)
2134 guint num_seen; /* number of icons present */
2139 /* grab the server, because we might be setting the window's icon and
2140 we don't want them to set it in between and we overwrite their own
2144 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
2145 /* figure out how many valid icons are in here */
2148 while (i + 2 < num) { /* +2 is to make sure there is a w and h */
2151 /* watch for the data being too small for the specified size,
2152 or for zero sized icons. */
2153 if (i + w*h > num || w == 0 || h == 0) break;
2155 /* convert it to the right bit order for ObRender */
2156 for (j = 0; j < w*h; ++j)
2158 (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2159 (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
2160 (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
2161 (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
2163 /* is it in the cache? */
2164 img = RrImageCacheFind(ob_rr_icons, &data[i], w, h);
2165 if (img) RrImageRef(img); /* own it */
2170 /* don't bother looping anymore if we already found it in the cache
2171 since we'll just use that! */
2175 /* if it's not in the cache yet, then add it to the cache now.
2176 we have already converted it to the correct bit order above */
2177 if (!img && num_seen > 0) {
2178 img = RrImageNew(ob_rr_icons);
2180 for (j = 0; j < num_seen; ++j) {
2183 RrImageAddPicture(img, &data[i], w, h);
2191 /* if we didn't find an image from the NET_WM_ICON stuff, then try the
2196 if ((hints = XGetWMHints(ob_display, self->window))) {
2197 if (hints->flags & IconPixmapHint) {
2199 xerror_set_ignore(TRUE);
2200 xicon = RrPixmapToRGBA(ob_rr_inst,
2202 (hints->flags & IconMaskHint ?
2203 hints->icon_mask : None),
2204 (gint*)&w, (gint*)&h, &data);
2205 xerror_set_ignore(FALSE);
2208 if (w > 0 && h > 0) {
2209 /* is this icon in the cache yet? */
2210 img = RrImageCacheFind(ob_rr_icons, data, w, h);
2211 if (img) RrImageRef(img); /* own it */
2213 /* if not, then add it */
2215 img = RrImageNew(ob_rr_icons);
2216 RrImageAddPicture(img, data, w, h);
2227 /* set the client's icons to be whatever we found */
2228 RrImageUnref(self->icon_set);
2229 self->icon_set = img;
2231 /* if the client has no icon at all, then we set a default icon onto it.
2232 but, if it has parents, then one of them will have an icon already
2234 if (!self->icon_set && !self->parents) {
2235 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2236 gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
2238 w = ob_rr_theme->def_win_icon_w;
2239 h = ob_rr_theme->def_win_icon_h;
2240 ldata = g_new(gulong, w*h+2);
2243 for (i = 0; i < w*h; ++i)
2244 ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2245 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2246 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2247 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2248 PROP_SETA32(self->window, net_wm_icon, cardinal, ldata, w*h+2);
2250 } else if (self->frame)
2251 /* don't draw the icon empty if we're just setting one now anyways,
2252 we'll get the property change any second */
2253 frame_adjust_icon(self->frame);
2258 void client_update_icon_geometry(ObClient *self)
2263 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2265 if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num))
2268 /* don't let them set it with an area < 0 */
2269 RECT_SET(self->icon_geometry, data[0], data[1],
2270 MAX(data[2],0), MAX(data[3],0));
2275 static void client_get_session_ids(ObClient *self)
2282 if (!PROP_GET32(self->window, wm_client_leader, window, &leader))
2285 /* get the SM_CLIENT_ID */
2288 got = PROP_GETS(leader, sm_client_id, locale, &self->sm_client_id);
2290 PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id);
2292 /* get the WM_CLASS (name and class). make them "" if they are not
2296 got = PROP_GETSS(leader, wm_class, locale, &ss);
2298 got = PROP_GETSS(self->window, wm_class, locale, &ss);
2302 self->name = g_strdup(ss[0]);
2304 self->class = g_strdup(ss[1]);
2309 if (self->name == NULL) self->name = g_strdup("");
2310 if (self->class == NULL) self->class = g_strdup("");
2312 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2315 got = PROP_GETS(leader, wm_window_role, locale, &s);
2317 got = PROP_GETS(self->window, wm_window_role, locale, &s);
2322 self->role = g_strdup("");
2324 /* get the WM_COMMAND */
2328 got = PROP_GETSS(leader, wm_command, locale, &ss);
2330 got = PROP_GETSS(self->window, wm_command, locale, &ss);
2333 /* merge/mash them all together */
2334 gchar *merge = NULL;
2337 for (i = 0; ss[i]; ++i) {
2340 merge = g_strconcat(merge, ss[i], NULL);
2342 merge = g_strconcat(ss[i], NULL);
2347 self->wm_command = merge;
2350 /* get the WM_CLIENT_MACHINE */
2353 got = PROP_GETS(leader, wm_client_machine, locale, &s);
2355 got = PROP_GETS(self->window, wm_client_machine, locale, &s);
2358 gchar localhost[128];
2361 gethostname(localhost, 127);
2362 localhost[127] = '\0';
2363 if (strcmp(localhost, s) != 0)
2364 self->client_machine = s;
2368 /* see if it has the PID set too (the PID requires that the
2369 WM_CLIENT_MACHINE be set) */
2370 if (PROP_GET32(self->window, net_wm_pid, cardinal, &pid))
2375 /*! Save the session IDs as seen by Openbox when the window mapped, so that
2376 users can still access them later if the app changes them */
2377 static void client_save_session_ids(ObClient *self)
2379 PROP_SETS(self->window, ob_role, self->role);
2380 PROP_SETS(self->window, ob_name, self->name);
2381 PROP_SETS(self->window, ob_class, self->class);
2384 static void client_change_wm_state(ObClient *self)
2389 old = self->wmstate;
2391 if (self->shaded || self->iconic ||
2392 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2394 self->wmstate = IconicState;
2396 self->wmstate = NormalState;
2398 if (old != self->wmstate) {
2399 PROP_MSG(self->window, kde_wm_change_state,
2400 self->wmstate, 1, 0, 0);
2402 state[0] = self->wmstate;
2404 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
2408 static void client_change_state(ObClient *self)
2410 gulong netstate[12];
2415 netstate[num++] = prop_atoms.net_wm_state_modal;
2417 netstate[num++] = prop_atoms.net_wm_state_shaded;
2419 netstate[num++] = prop_atoms.net_wm_state_hidden;
2420 if (self->skip_taskbar)
2421 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
2422 if (self->skip_pager)
2423 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
2424 if (self->fullscreen)
2425 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
2427 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
2429 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
2431 netstate[num++] = prop_atoms.net_wm_state_above;
2433 netstate[num++] = prop_atoms.net_wm_state_below;
2434 if (self->demands_attention)
2435 netstate[num++] = prop_atoms.net_wm_state_demands_attention;
2436 if (self->undecorated)
2437 netstate[num++] = prop_atoms.ob_wm_state_undecorated;
2438 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
2441 frame_adjust_state(self->frame);
2444 ObClient *client_search_focus_tree(ObClient *self)
2449 for (it = self->transients; it; it = g_slist_next(it)) {
2450 if (client_focused(it->data)) return it->data;
2451 if ((ret = client_search_focus_tree(it->data))) return ret;
2456 ObClient *client_search_focus_tree_full(ObClient *self)
2458 if (self->parents) {
2461 for (it = self->parents; it; it = g_slist_next(it)) {
2462 ObClient *c = it->data;
2463 if ((c = client_search_focus_tree_full(it->data))) return c;
2469 /* this function checks the whole tree, the client_search_focus_tree
2470 does not, so we need to check this window */
2471 if (client_focused(self))
2473 return client_search_focus_tree(self);
2477 ObClient *client_search_focus_group_full(ObClient *self)
2482 for (it = self->group->members; it; it = g_slist_next(it)) {
2483 ObClient *c = it->data;
2485 if (client_focused(c)) return c;
2486 if ((c = client_search_focus_tree(it->data))) return c;
2489 if (client_focused(self)) return self;
2493 gboolean client_has_parent(ObClient *self)
2495 return self->parents != NULL;
2498 static ObStackingLayer calc_layer(ObClient *self)
2503 monitor = screen_physical_area_monitor(client_monitor(self));
2505 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2506 l = OB_STACKING_LAYER_DESKTOP;
2507 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2508 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2509 else l = OB_STACKING_LAYER_ABOVE;
2511 else if ((self->fullscreen ||
2512 /* No decorations and fills the monitor = oldskool fullscreen.
2513 But not for maximized windows.
2515 (self->decorations == 0 &&
2516 !(self->max_horz && self->max_vert) &&
2517 RECT_EQUAL(self->area, *monitor))) &&
2518 /* you are fullscreen while you or your children are focused.. */
2519 (client_focused(self) || client_search_focus_tree(self) ||
2520 /* you can be fullscreen if you're on another desktop */
2521 (self->desktop != screen_desktop &&
2522 self->desktop != DESKTOP_ALL) ||
2523 /* and you can also be fullscreen if the focused client is on
2524 another monitor, or nothing else is focused */
2526 client_monitor(focus_client) != client_monitor(self))))
2527 l = OB_STACKING_LAYER_FULLSCREEN;
2528 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2529 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2530 else l = OB_STACKING_LAYER_NORMAL;
2537 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2538 ObStackingLayer min)
2540 ObStackingLayer old, own;
2544 own = calc_layer(self);
2545 self->layer = MAX(own, min);
2547 if (self->layer != old) {
2548 stacking_remove(CLIENT_AS_WINDOW(self));
2549 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2552 /* we've been restacked */
2553 self->visited = TRUE;
2555 for (it = self->transients; it; it = g_slist_next(it))
2556 client_calc_layer_recursive(it->data, orig,
2560 static void client_calc_layer_internal(ObClient *self)
2564 /* transients take on the layer of their parents */
2565 sit = client_search_all_top_parents(self);
2567 for (; sit; sit = g_slist_next(sit))
2568 client_calc_layer_recursive(sit->data, self, 0);
2571 void client_calc_layer(ObClient *self)
2575 /* skip over stuff above fullscreen layer */
2576 for (it = stacking_list; it; it = g_list_next(it))
2577 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2579 /* find the windows in the fullscreen layer, and mark them not-visited */
2580 for (; it; it = g_list_next(it)) {
2581 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2582 else if (WINDOW_IS_CLIENT(it->data))
2583 WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2586 client_calc_layer_internal(self);
2588 /* skip over stuff above fullscreen layer */
2589 for (it = stacking_list; it; it = g_list_next(it))
2590 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2592 /* now recalc any windows in the fullscreen layer which have not
2593 had their layer recalced already */
2594 for (; it; it = g_list_next(it)) {
2595 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2596 else if (WINDOW_IS_CLIENT(it->data) &&
2597 !WINDOW_AS_CLIENT(it->data)->visited)
2598 client_calc_layer_internal(it->data);
2602 gboolean client_should_show(ObClient *self)
2606 if (client_normal(self) && screen_showing_desktop)
2608 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2614 gboolean client_show(ObClient *self)
2616 gboolean show = FALSE;
2618 if (client_should_show(self)) {
2619 /* replay pending pointer event before showing the window, in case it
2620 should be going to something under the window */
2621 mouse_replay_pointer();
2623 frame_show(self->frame);
2626 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2627 it needs to be in IconicState. This includes when it is on another
2630 client_change_wm_state(self);
2635 gboolean client_hide(ObClient *self)
2637 gboolean hide = FALSE;
2639 if (!client_should_show(self)) {
2640 /* We don't need to ignore enter events here.
2641 The window can hide/iconify in 3 different ways:
2642 1 - through an x message. in this case we ignore all enter events
2643 caused by responding to the x message (unless underMouse)
2644 2 - by a keyboard action. in this case we ignore all enter events
2645 caused by the action
2646 3 - by a mouse action. in this case they are doing stuff with the
2647 mouse and focus _should_ move.
2649 Also in action_end, we simulate an enter event that can't be ignored
2650 so trying to ignore them is futile in case 3 anyways
2653 /* replay pending pointer event before hiding the window, in case it
2654 should be going to the window */
2655 mouse_replay_pointer();
2657 frame_hide(self->frame);
2660 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2661 it needs to be in IconicState. This includes when it is on another
2664 client_change_wm_state(self);
2669 void client_showhide(ObClient *self)
2671 if (!client_show(self))
2675 gboolean client_normal(ObClient *self) {
2676 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2677 self->type == OB_CLIENT_TYPE_DOCK ||
2678 self->type == OB_CLIENT_TYPE_SPLASH);
2681 gboolean client_helper(ObClient *self)
2683 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2684 self->type == OB_CLIENT_TYPE_MENU ||
2685 self->type == OB_CLIENT_TYPE_TOOLBAR);
2688 gboolean client_mouse_focusable(ObClient *self)
2690 return !(self->type == OB_CLIENT_TYPE_MENU ||
2691 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2692 self->type == OB_CLIENT_TYPE_SPLASH ||
2693 self->type == OB_CLIENT_TYPE_DOCK);
2696 gboolean client_enter_focusable(ObClient *self)
2698 /* you can focus desktops but it shouldn't on enter */
2699 return (client_mouse_focusable(self) &&
2700 self->type != OB_CLIENT_TYPE_DESKTOP);
2703 static void client_apply_startup_state(ObClient *self,
2704 gint x, gint y, gint w, gint h)
2706 /* save the states that we are going to apply */
2707 gboolean iconic = self->iconic;
2708 gboolean fullscreen = self->fullscreen;
2709 gboolean undecorated = self->undecorated;
2710 gboolean shaded = self->shaded;
2711 gboolean demands_attention = self->demands_attention;
2712 gboolean max_horz = self->max_horz;
2713 gboolean max_vert = self->max_vert;
2717 /* turn them all off in the client, so they won't affect the window
2719 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2720 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2722 /* move the client to its placed position, or it it's already there,
2723 generate a ConfigureNotify telling the client where it is.
2725 do this after adjusting the frame. otherwise it gets all weird and
2726 clients don't work right
2728 do this before applying the states so they have the correct
2729 pre-max/pre-fullscreen values
2731 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2732 ob_debug("placed window 0x%x at %d, %d with size %d x %d\n",
2733 self->window, x, y, w, h);
2734 /* save the area, and make it where it should be for the premax stuff */
2735 oldarea = self->area;
2736 RECT_SET(self->area, x, y, w, h);
2738 /* apply the states. these are in a carefully crafted order.. */
2741 client_iconify(self, TRUE, FALSE, TRUE);
2743 client_fullscreen(self, TRUE);
2745 client_set_undecorated(self, TRUE);
2747 client_shade(self, TRUE);
2748 if (demands_attention)
2749 client_hilite(self, TRUE);
2751 if (max_vert && max_horz)
2752 client_maximize(self, TRUE, 0);
2754 client_maximize(self, TRUE, 2);
2756 client_maximize(self, TRUE, 1);
2758 /* if the window hasn't been configured yet, then do so now, in fact the
2759 x,y,w,h may _not_ be the same as the area rect, which can end up
2760 meaning that the client isn't properly moved/resized by the fullscreen
2762 pho can cause this because it maps at size of the screen but not 0,0
2763 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2764 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2765 cuz thats where the pre-fullscreen will be. however the actual area is
2766 not, so this needs to be called even if we have fullscreened/maxed
2768 self->area = oldarea;
2769 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2771 /* set the desktop hint, to make sure that it always exists */
2772 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
2774 /* nothing to do for the other states:
2783 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2785 /* these should be the current values. this is for when you're not moving,
2787 g_assert(*x == self->area.x);
2788 g_assert(oldw == self->area.width);
2791 switch (self->gravity) {
2793 case NorthWestGravity:
2795 case SouthWestGravity:
2802 *x -= (neww - oldw) / 2;
2804 case NorthEastGravity:
2806 case SouthEastGravity:
2812 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2814 /* these should be the current values. this is for when you're not moving,
2816 g_assert(*y == self->area.y);
2817 g_assert(oldh == self->area.height);
2820 switch (self->gravity) {
2822 case NorthWestGravity:
2824 case NorthEastGravity:
2831 *y -= (newh - oldh) / 2;
2833 case SouthWestGravity:
2835 case SouthEastGravity:
2841 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2842 gint *logicalw, gint *logicalh,
2845 Rect desired = {*x, *y, *w, *h};
2846 frame_rect_to_frame(self->frame, &desired);
2848 /* make the frame recalculate its dimensions n shit without changing
2849 anything visible for real, this way the constraints below can work with
2850 the updated frame dimensions. */
2851 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2853 /* gets the frame's position */
2854 frame_client_gravity(self->frame, x, y);
2856 /* these positions are frame positions, not client positions */
2858 /* set the size and position if fullscreen */
2859 if (self->fullscreen) {
2863 i = screen_find_monitor(&desired);
2864 a = screen_physical_area_monitor(i);
2871 user = FALSE; /* ignore if the client can't be moved/resized when it
2875 } else if (self->max_horz || self->max_vert) {
2879 /* use all possible struts when maximizing to the full screen */
2880 i = screen_find_monitor(&desired);
2881 a = screen_area(self->desktop, i,
2882 (self->max_horz && self->max_vert ? NULL : &desired));
2884 /* set the size and position if maximized */
2885 if (self->max_horz) {
2887 *w = a->width - self->frame->size.left - self->frame->size.right;
2889 if (self->max_vert) {
2891 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2894 user = FALSE; /* ignore if the client can't be moved/resized when it
2900 /* gets the client's position */
2901 frame_frame_gravity(self->frame, x, y);
2903 /* work within the preferred sizes given by the window, these may have
2904 changed rather than it's requested width and height, so always run
2905 through this code */
2907 gint basew, baseh, minw, minh;
2909 gfloat minratio, maxratio;
2911 incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
2912 inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
2913 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2914 0 : self->min_ratio;
2915 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2916 0 : self->max_ratio;
2918 /* base size is substituted with min size if not specified */
2919 if (self->base_size.width >= 0 || self->base_size.height >= 0) {
2920 basew = self->base_size.width;
2921 baseh = self->base_size.height;
2923 basew = self->min_size.width;
2924 baseh = self->min_size.height;
2926 /* min size is substituted with base size if not specified */
2927 if (self->min_size.width || self->min_size.height) {
2928 minw = self->min_size.width;
2929 minh = self->min_size.height;
2931 minw = self->base_size.width;
2932 minh = self->base_size.height;
2935 /* This comment is no longer true */
2936 /* if this is a user-requested resize, then check against min/max
2939 /* smaller than min size or bigger than max size? */
2940 if (*w > self->max_size.width) *w = self->max_size.width;
2941 if (*w < minw) *w = minw;
2942 if (*h > self->max_size.height) *h = self->max_size.height;
2943 if (*h < minh) *h = minh;
2948 /* keep to the increments */
2952 /* you cannot resize to nothing */
2953 if (basew + *w < 1) *w = 1 - basew;
2954 if (baseh + *h < 1) *h = 1 - baseh;
2956 /* save the logical size */
2957 *logicalw = incw > 1 ? *w : *w + basew;
2958 *logicalh = inch > 1 ? *h : *h + baseh;
2966 /* adjust the height to match the width for the aspect ratios.
2967 for this, min size is not substituted for base size ever. */
2968 *w -= self->base_size.width;
2969 *h -= self->base_size.height;
2972 if (*h * minratio > *w) {
2973 *h = (gint)(*w / minratio);
2975 /* you cannot resize to nothing */
2978 *w = (gint)(*h * minratio);
2982 if (*h * maxratio < *w) {
2983 *h = (gint)(*w / maxratio);
2985 /* you cannot resize to nothing */
2988 *w = (gint)(*h * minratio);
2992 *w += self->base_size.width;
2993 *h += self->base_size.height;
2996 /* these override the above states! if you cant move you can't move! */
2998 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3002 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3003 *w = self->area.width;
3004 *h = self->area.height;
3012 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3013 gboolean user, gboolean final, gboolean force_reply)
3017 gboolean send_resize_client;
3018 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3019 gboolean fmoved, fresized;
3020 guint fdecor = self->frame->decorations;
3021 gboolean fhorz = self->frame->max_horz;
3022 gboolean fvert = self->frame->max_vert;
3023 gint logicalw, logicalh;
3025 /* find the new x, y, width, and height (and logical size) */
3026 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3028 /* set the logical size if things changed */
3029 if (!(w == self->area.width && h == self->area.height))
3030 SIZE_SET(self->logical_size, logicalw, logicalh);
3032 /* figure out if we moved or resized or what */
3033 moved = (x != self->area.x || y != self->area.y);
3034 resized = (w != self->area.width || h != self->area.height);
3036 oldw = self->area.width;
3037 oldh = self->area.height;
3038 oldframe = self->frame->area;
3039 RECT_SET(self->area, x, y, w, h);
3041 /* for app-requested resizes, always resize if 'resized' is true.
3042 for user-requested ones, only resize if final is true, or when
3043 resizing in redraw mode */
3044 send_resize_client = ((!user && resized) ||
3046 (resized && config_resize_redraw))));
3048 /* if the client is enlarging, then resize the client before the frame */
3049 if (send_resize_client && (w > oldw || h > oldh)) {
3050 XMoveResizeWindow(ob_display, self->window,
3051 self->frame->size.left, self->frame->size.top,
3052 MAX(w, oldw), MAX(h, oldh));
3053 frame_adjust_client_area(self->frame);
3056 /* find the frame's dimensions and move/resize it */
3060 /* if decorations changed, then readjust everything for the frame */
3061 if (self->decorations != fdecor ||
3062 self->max_horz != fhorz || self->max_vert != fvert)
3064 fmoved = fresized = TRUE;
3067 /* adjust the frame */
3068 if (fmoved || fresized) {
3069 gulong ignore_start;
3071 ignore_start = event_start_ignore_all_enters();
3073 /* replay pending pointer event before move the window, in case it
3074 would change what window gets the event */
3075 mouse_replay_pointer();
3077 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
3080 event_end_ignore_all_enters(ignore_start);
3083 if (!user || final) {
3084 gint oldrx = self->root_pos.x;
3085 gint oldry = self->root_pos.y;
3086 /* we have reset the client to 0 border width, so don't include
3087 it in these coords */
3088 POINT_SET(self->root_pos,
3089 self->frame->area.x + self->frame->size.left -
3091 self->frame->area.y + self->frame->size.top -
3092 self->border_width);
3093 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3097 /* This is kinda tricky and should not be changed.. let me explain!
3099 When user = FALSE, then the request is coming from the application
3100 itself, and we are more strict about when to send a synthetic
3101 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3102 in this case (if force_reply is true)
3104 When user = TRUE, then the request is coming from "us", like when we
3105 maximize a window or something. In this case we are more lenient. We
3106 used to follow the same rules as above, but _Java_ Swing can't handle
3107 this. So just to appease Swing, when user = TRUE, we always send
3108 a synthetic ConfigureNotify to give the window its root coordinates.
3110 if ((!user && !resized && (rootmoved || force_reply)) ||
3111 (user && final && rootmoved))
3115 event.type = ConfigureNotify;
3116 event.xconfigure.display = ob_display;
3117 event.xconfigure.event = self->window;
3118 event.xconfigure.window = self->window;
3120 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n",
3121 self->title, self->root_pos.x, self->root_pos.y, w, h);
3123 /* root window real coords */
3124 event.xconfigure.x = self->root_pos.x;
3125 event.xconfigure.y = self->root_pos.y;
3126 event.xconfigure.width = w;
3127 event.xconfigure.height = h;
3128 event.xconfigure.border_width = self->border_width;
3129 event.xconfigure.above = None;
3130 event.xconfigure.override_redirect = FALSE;
3131 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3132 FALSE, StructureNotifyMask, &event);
3135 /* if the client is shrinking, then resize the frame before the client.
3137 both of these resize sections may run, because the top one only resizes
3138 in the direction that is growing
3140 if (send_resize_client && (w <= oldw || h <= oldh)) {
3141 frame_adjust_client_area(self->frame);
3142 XMoveResizeWindow(ob_display, self->window,
3143 self->frame->size.left, self->frame->size.top, w, h);
3148 /* if it moved between monitors, then this can affect the stacking
3149 layer of this window or others - for fullscreen windows */
3150 if (screen_find_monitor(&self->frame->area) !=
3151 screen_find_monitor(&oldframe))
3153 client_calc_layer(self);
3157 void client_fullscreen(ObClient *self, gboolean fs)
3161 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3162 self->fullscreen == fs) return; /* already done */
3164 self->fullscreen = fs;
3165 client_change_state(self); /* change the state hints on the client */
3168 self->pre_fullscreen_area = self->area;
3169 /* if the window is maximized, its area isn't all that meaningful.
3170 save its premax area instead. */
3171 if (self->max_horz) {
3172 self->pre_fullscreen_area.x = self->pre_max_area.x;
3173 self->pre_fullscreen_area.width = self->pre_max_area.width;
3175 if (self->max_vert) {
3176 self->pre_fullscreen_area.y = self->pre_max_area.y;
3177 self->pre_fullscreen_area.height = self->pre_max_area.height;
3180 /* these will help configure_full figure out where to fullscreen
3184 w = self->area.width;
3185 h = self->area.height;
3187 g_assert(self->pre_fullscreen_area.width > 0 &&
3188 self->pre_fullscreen_area.height > 0);
3190 x = self->pre_fullscreen_area.x;
3191 y = self->pre_fullscreen_area.y;
3192 w = self->pre_fullscreen_area.width;
3193 h = self->pre_fullscreen_area.height;
3194 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3197 ob_debug("Window %s going fullscreen (%d)\n",
3198 self->title, self->fullscreen);
3200 client_setup_decor_and_functions(self, FALSE);
3201 client_move_resize(self, x, y, w, h);
3203 /* and adjust our layer/stacking. do this after resizing the window,
3204 and applying decorations, because windows which fill the screen are
3205 considered "fullscreen" and it affects their layer */
3206 client_calc_layer(self);
3209 /* try focus us when we go into fullscreen mode */
3214 static void client_iconify_recursive(ObClient *self,
3215 gboolean iconic, gboolean curdesk,
3216 gboolean hide_animation)
3219 gboolean changed = FALSE;
3221 if (self->iconic != iconic) {
3222 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
3226 /* don't let non-normal windows iconify along with their parents
3228 if (client_normal(self)) {
3229 self->iconic = iconic;
3231 /* update the focus lists.. iconic windows go to the bottom of
3233 focus_order_to_bottom(self);
3238 self->iconic = iconic;
3240 if (curdesk && self->desktop != screen_desktop &&
3241 self->desktop != DESKTOP_ALL)
3242 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3244 /* this puts it after the current focused window */
3245 focus_order_remove(self);
3246 focus_order_add_new(self);
3253 client_change_state(self);
3254 if (config_animate_iconify && !hide_animation)
3255 frame_begin_iconify_animation(self->frame, iconic);
3256 /* do this after starting the animation so it doesn't flash */
3257 client_showhide(self);
3260 /* iconify all direct transients, and deiconify all transients
3262 for (it = self->transients; it; it = g_slist_next(it))
3263 if (it->data != self)
3264 if (client_is_direct_child(self, it->data) || !iconic)
3265 client_iconify_recursive(it->data, iconic, curdesk,
3269 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3270 gboolean hide_animation)
3272 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3273 /* move up the transient chain as far as possible first */
3274 self = client_search_top_direct_parent(self);
3275 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3279 void client_maximize(ObClient *self, gboolean max, gint dir)
3283 g_assert(dir == 0 || dir == 1 || dir == 2);
3284 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
3286 /* check if already done */
3288 if (dir == 0 && self->max_horz && self->max_vert) return;
3289 if (dir == 1 && self->max_horz) return;
3290 if (dir == 2 && self->max_vert) return;
3292 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3293 if (dir == 1 && !self->max_horz) return;
3294 if (dir == 2 && !self->max_vert) return;
3297 /* these will help configure_full figure out which screen to fill with
3301 w = self->area.width;
3302 h = self->area.height;
3305 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3306 RECT_SET(self->pre_max_area,
3307 self->area.x, self->pre_max_area.y,
3308 self->area.width, self->pre_max_area.height);
3310 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3311 RECT_SET(self->pre_max_area,
3312 self->pre_max_area.x, self->area.y,
3313 self->pre_max_area.width, self->area.height);
3316 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3317 g_assert(self->pre_max_area.width > 0);
3319 x = self->pre_max_area.x;
3320 w = self->pre_max_area.width;
3322 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3323 0, self->pre_max_area.height);
3325 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3326 g_assert(self->pre_max_area.height > 0);
3328 y = self->pre_max_area.y;
3329 h = self->pre_max_area.height;
3331 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3332 self->pre_max_area.width, 0);
3336 if (dir == 0 || dir == 1) /* horz */
3337 self->max_horz = max;
3338 if (dir == 0 || dir == 2) /* vert */
3339 self->max_vert = max;
3341 client_change_state(self); /* change the state hints on the client */
3343 client_setup_decor_and_functions(self, FALSE);
3344 client_move_resize(self, x, y, w, h);
3347 void client_shade(ObClient *self, gboolean shade)
3349 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3350 shade) || /* can't shade */
3351 self->shaded == shade) return; /* already done */
3353 self->shaded = shade;
3354 client_change_state(self);
3355 client_change_wm_state(self); /* the window is being hidden/shown */
3356 /* resize the frame to just the titlebar */
3357 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3360 static void client_ping_event(ObClient *self, gboolean dead)
3362 if (self->not_responding != dead) {
3363 self->not_responding = dead;
3364 client_update_title(self);
3367 /* the client isn't responding, so ask to kill it */
3368 client_prompt_kill(self);
3370 /* it came back to life ! */
3372 if (self->kill_prompt) {
3373 prompt_unref(self->kill_prompt);
3374 self->kill_prompt = NULL;
3377 self->kill_level = 0;
3382 void client_close(ObClient *self)
3384 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3386 /* if closing an internal obprompt, that is just cancelling it */
3388 prompt_cancel(self->prompt);
3392 /* in the case that the client provides no means to requesting that it
3393 close, we just kill it */
3394 if (!self->delete_window)
3395 /* don't use client_kill(), we should only kill based on PID in
3396 response to a lack of PING replies */
3397 XKillClient(ob_display, self->window);
3399 /* request the client to close with WM_DELETE_WINDOW */
3400 PROP_MSG_TO(self->window, self->window, wm_protocols,
3401 prop_atoms.wm_delete_window, event_curtime, 0, 0, 0,
3404 /* we're trying to close the window, so see if it is responding. if it
3405 is not, then we will let them kill the window */
3407 ping_start(self, client_ping_event);
3409 /* if we already know the window isn't responding (maybe they clicked
3410 no in the kill dialog but it hasn't come back to life), then show
3412 if (self->not_responding)
3413 client_prompt_kill(self);
3417 #define OB_KILL_RESULT_NO 0
3418 #define OB_KILL_RESULT_YES 1
3420 static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
3422 ObClient *self = data;
3424 if (result == OB_KILL_RESULT_YES)
3426 return TRUE; /* call the cleanup func */
3429 static void client_kill_cleanup(ObPrompt *p, gpointer data)
3431 ObClient *self = data;
3433 g_assert(p == self->kill_prompt);
3435 prompt_unref(self->kill_prompt);
3436 self->kill_prompt = NULL;
3439 static void client_prompt_kill(ObClient *self)
3441 /* check if we're already prompting */
3442 if (!self->kill_prompt) {
3443 ObPromptAnswer answers[] = {
3444 { 0, OB_KILL_RESULT_NO },
3445 { 0, OB_KILL_RESULT_YES }
3448 const gchar *y, *title;
3450 title = self->original_title;
3451 if (title[0] == '\0') {
3452 /* empty string, so use its parent */
3453 ObClient *p = client_search_top_direct_parent(self);
3454 if (p) title = p->original_title;
3457 if (client_on_localhost(self)) {
3460 if (self->kill_level == 0)
3466 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3468 y = _("End Process");
3472 (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3474 y = _("Disconnect");
3476 /* set the dialog buttons' text */
3477 answers[0].text = _("Cancel"); /* "no" */
3478 answers[1].text = y; /* "yes" */
3480 self->kill_prompt = prompt_new(m, NULL, answers,
3481 sizeof(answers)/sizeof(answers[0]),
3482 OB_KILL_RESULT_NO, /* default = no */
3483 OB_KILL_RESULT_NO, /* cancel = no */
3484 client_kill_requested,
3485 client_kill_cleanup,
3490 prompt_show(self->kill_prompt, self, TRUE);
3493 void client_kill(ObClient *self)
3495 /* don't kill our own windows */
3496 if (self->prompt) return;
3498 if (client_on_localhost(self) && self->pid) {
3499 /* running on the local host */
3500 if (self->kill_level == 0) {
3501 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3502 self->window, self->pid);
3503 kill(self->pid, SIGTERM);
3506 /* show that we're trying to kill it */
3507 client_update_title(self);
3510 ob_debug("killing window 0x%x with pid %lu, with SIGKILL\n",
3511 self->window, self->pid);
3512 kill(self->pid, SIGKILL); /* kill -9 */
3516 /* running on a remote host */
3517 XKillClient(ob_display, self->window);
3521 void client_hilite(ObClient *self, gboolean hilite)
3523 if (self->demands_attention == hilite)
3524 return; /* no change */
3526 /* don't allow focused windows to hilite */
3527 self->demands_attention = hilite && !client_focused(self);
3528 if (self->frame != NULL) { /* if we're mapping, just set the state */
3529 if (self->demands_attention) {
3530 frame_flash_start(self->frame);
3532 /* if the window is on another desktop then raise it and make it
3533 the most recently used window */
3534 if (self->desktop != screen_desktop &&
3535 self->desktop != DESKTOP_ALL)
3537 stacking_raise(CLIENT_AS_WINDOW(self));
3538 focus_order_to_top(self);
3542 frame_flash_stop(self->frame);
3543 client_change_state(self);
3547 static void client_set_desktop_recursive(ObClient *self,
3555 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3557 ob_debug("Setting desktop %u\n", target+1);
3559 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3561 old = self->desktop;
3562 self->desktop = target;
3563 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
3564 /* the frame can display the current desktop state */
3565 frame_adjust_state(self->frame);
3566 /* 'move' the window to the new desktop */
3570 /* raise if it was not already on the desktop */
3571 if (old != DESKTOP_ALL && !dontraise)
3572 stacking_raise(CLIENT_AS_WINDOW(self));
3573 if (STRUT_EXISTS(self->strut))
3574 screen_update_areas();
3576 /* the new desktop's geometry may be different, so we may need to
3577 resize, for example if we are maximized */
3578 client_reconfigure(self, FALSE);
3581 /* move all transients */
3582 for (it = self->transients; it; it = g_slist_next(it))
3583 if (it->data != self)
3584 if (client_is_direct_child(self, it->data))
3585 client_set_desktop_recursive(it->data, target,
3586 donthide, dontraise);
3589 void client_set_desktop(ObClient *self, guint target,
3590 gboolean donthide, gboolean dontraise)
3592 self = client_search_top_direct_parent(self);
3593 client_set_desktop_recursive(self, target, donthide, dontraise);
3596 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3598 while (child != parent && (child = client_direct_parent(child)));
3599 return child == parent;
3602 ObClient *client_search_modal_child(ObClient *self)
3607 for (it = self->transients; it; it = g_slist_next(it)) {
3608 ObClient *c = it->data;
3609 if ((ret = client_search_modal_child(c))) return ret;
3610 if (c->modal) return c;
3615 static gboolean client_validate_unmap(ObClient *self, int n)
3618 gboolean ret = TRUE;
3620 if (XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
3621 if (n < self->ignore_unmaps) // ignore this one, but look for more
3622 ret = client_validate_unmap(self, n+1);
3624 ret = FALSE; // the window is going to become unmanaged
3626 /* put them back on the event stack so they end up in the same order */
3627 XPutBackEvent(ob_display, &e);
3633 gboolean client_validate(ObClient *self)
3637 XSync(ob_display, FALSE); /* get all events on the server */
3639 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e)) {
3640 XPutBackEvent(ob_display, &e);
3644 if (!client_validate_unmap(self, 0))
3650 void client_set_wm_state(ObClient *self, glong state)
3652 if (state == self->wmstate) return; /* no change */
3656 client_iconify(self, TRUE, TRUE, FALSE);
3659 client_iconify(self, FALSE, TRUE, FALSE);
3664 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3666 gboolean shaded = self->shaded;
3667 gboolean fullscreen = self->fullscreen;
3668 gboolean undecorated = self->undecorated;
3669 gboolean max_horz = self->max_horz;
3670 gboolean max_vert = self->max_vert;
3671 gboolean modal = self->modal;
3672 gboolean iconic = self->iconic;
3673 gboolean demands_attention = self->demands_attention;
3674 gboolean above = self->above;
3675 gboolean below = self->below;
3678 if (!(action == prop_atoms.net_wm_state_add ||
3679 action == prop_atoms.net_wm_state_remove ||
3680 action == prop_atoms.net_wm_state_toggle))
3681 /* an invalid action was passed to the client message, ignore it */
3684 for (i = 0; i < 2; ++i) {
3685 Atom state = i == 0 ? data1 : data2;
3687 if (!state) continue;
3689 /* if toggling, then pick whether we're adding or removing */
3690 if (action == prop_atoms.net_wm_state_toggle) {
3691 if (state == prop_atoms.net_wm_state_modal)
3692 action = modal ? prop_atoms.net_wm_state_remove :
3693 prop_atoms.net_wm_state_add;
3694 else if (state == prop_atoms.net_wm_state_maximized_vert)
3695 action = self->max_vert ? prop_atoms.net_wm_state_remove :
3696 prop_atoms.net_wm_state_add;
3697 else if (state == prop_atoms.net_wm_state_maximized_horz)
3698 action = self->max_horz ? prop_atoms.net_wm_state_remove :
3699 prop_atoms.net_wm_state_add;
3700 else if (state == prop_atoms.net_wm_state_shaded)
3701 action = shaded ? prop_atoms.net_wm_state_remove :
3702 prop_atoms.net_wm_state_add;
3703 else if (state == prop_atoms.net_wm_state_skip_taskbar)
3704 action = self->skip_taskbar ?
3705 prop_atoms.net_wm_state_remove :
3706 prop_atoms.net_wm_state_add;
3707 else if (state == prop_atoms.net_wm_state_skip_pager)
3708 action = self->skip_pager ?
3709 prop_atoms.net_wm_state_remove :
3710 prop_atoms.net_wm_state_add;
3711 else if (state == prop_atoms.net_wm_state_hidden)
3712 action = self->iconic ?
3713 prop_atoms.net_wm_state_remove :
3714 prop_atoms.net_wm_state_add;
3715 else if (state == prop_atoms.net_wm_state_fullscreen)
3716 action = fullscreen ?
3717 prop_atoms.net_wm_state_remove :
3718 prop_atoms.net_wm_state_add;
3719 else if (state == prop_atoms.net_wm_state_above)
3720 action = self->above ? prop_atoms.net_wm_state_remove :
3721 prop_atoms.net_wm_state_add;
3722 else if (state == prop_atoms.net_wm_state_below)
3723 action = self->below ? prop_atoms.net_wm_state_remove :
3724 prop_atoms.net_wm_state_add;
3725 else if (state == prop_atoms.net_wm_state_demands_attention)
3726 action = self->demands_attention ?
3727 prop_atoms.net_wm_state_remove :
3728 prop_atoms.net_wm_state_add;
3729 else if (state == prop_atoms.ob_wm_state_undecorated)
3730 action = undecorated ? prop_atoms.net_wm_state_remove :
3731 prop_atoms.net_wm_state_add;
3734 if (action == prop_atoms.net_wm_state_add) {
3735 if (state == prop_atoms.net_wm_state_modal) {
3737 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
3739 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
3741 } else if (state == prop_atoms.net_wm_state_shaded) {
3743 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
3744 self->skip_taskbar = TRUE;
3745 } else if (state == prop_atoms.net_wm_state_skip_pager) {
3746 self->skip_pager = TRUE;
3747 } else if (state == prop_atoms.net_wm_state_hidden) {
3749 } else if (state == prop_atoms.net_wm_state_fullscreen) {
3751 } else if (state == prop_atoms.net_wm_state_above) {
3754 } else if (state == prop_atoms.net_wm_state_below) {
3757 } else if (state == prop_atoms.net_wm_state_demands_attention) {
3758 demands_attention = TRUE;
3759 } else if (state == prop_atoms.ob_wm_state_undecorated) {
3763 } else { /* action == prop_atoms.net_wm_state_remove */
3764 if (state == prop_atoms.net_wm_state_modal) {
3766 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
3768 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
3770 } else if (state == prop_atoms.net_wm_state_shaded) {
3772 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
3773 self->skip_taskbar = FALSE;
3774 } else if (state == prop_atoms.net_wm_state_skip_pager) {
3775 self->skip_pager = FALSE;
3776 } else if (state == prop_atoms.net_wm_state_hidden) {
3778 } else if (state == prop_atoms.net_wm_state_fullscreen) {
3780 } else if (state == prop_atoms.net_wm_state_above) {
3782 } else if (state == prop_atoms.net_wm_state_below) {
3784 } else if (state == prop_atoms.net_wm_state_demands_attention) {
3785 demands_attention = FALSE;
3786 } else if (state == prop_atoms.ob_wm_state_undecorated) {
3787 undecorated = FALSE;
3792 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3793 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3795 if (max_horz == max_vert) { /* both going the same way */
3796 client_maximize(self, max_horz, 0);
3798 client_maximize(self, max_horz, 1);
3799 client_maximize(self, max_vert, 2);
3803 if (max_horz != self->max_horz)
3804 client_maximize(self, max_horz, 1);
3806 client_maximize(self, max_vert, 2);
3809 /* change fullscreen state before shading, as it will affect if the window
3811 if (fullscreen != self->fullscreen)
3812 client_fullscreen(self, fullscreen);
3813 if (shaded != self->shaded)
3814 client_shade(self, shaded);
3815 if (undecorated != self->undecorated)
3816 client_set_undecorated(self, undecorated);
3817 if (above != self->above || below != self->below) {
3818 self->above = above;
3819 self->below = below;
3820 client_calc_layer(self);
3823 if (modal != self->modal) {
3824 self->modal = modal;
3825 /* when a window changes modality, then its stacking order with its
3826 transients needs to change */
3827 stacking_raise(CLIENT_AS_WINDOW(self));
3829 /* it also may get focused. if something is focused that shouldn't
3830 be focused anymore, then move the focus */
3831 if (focus_client && client_focus_target(focus_client) != focus_client)
3832 client_focus(focus_client);
3835 if (iconic != self->iconic)
3836 client_iconify(self, iconic, FALSE, FALSE);
3838 if (demands_attention != self->demands_attention)
3839 client_hilite(self, demands_attention);
3841 client_change_state(self); /* change the hint to reflect these changes */
3844 ObClient *client_focus_target(ObClient *self)
3846 ObClient *child = NULL;
3848 child = client_search_modal_child(self);
3849 if (child) return child;
3853 gboolean client_can_focus(ObClient *self)
3855 /* choose the correct target */
3856 self = client_focus_target(self);
3858 if (!self->frame->visible)
3861 if (!(self->can_focus || self->focus_notify))
3867 gboolean client_focus(ObClient *self)
3869 /* we might not focus this window, so if we have modal children which would
3870 be focused instead, bring them to this desktop */
3871 client_bring_modal_windows(self);
3873 /* choose the correct target */
3874 self = client_focus_target(self);
3876 if (!client_can_focus(self)) {
3877 ob_debug_type(OB_DEBUG_FOCUS,
3878 "Client %s can't be focused\n", self->title);
3882 ob_debug_type(OB_DEBUG_FOCUS,
3883 "Focusing client \"%s\" (0x%x) at time %u\n",
3884 self->title, self->window, event_curtime);
3886 /* if using focus_delay, stop the timer now so that focus doesn't
3888 event_halt_focus_delay();
3890 xerror_set_ignore(TRUE);
3891 xerror_occured = FALSE;
3893 if (self->can_focus) {
3894 /* This can cause a BadMatch error with CurrentTime, or if an app
3895 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3896 XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
3900 if (self->focus_notify) {
3902 ce.xclient.type = ClientMessage;
3903 ce.xclient.message_type = prop_atoms.wm_protocols;
3904 ce.xclient.display = ob_display;
3905 ce.xclient.window = self->window;
3906 ce.xclient.format = 32;
3907 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
3908 ce.xclient.data.l[1] = event_curtime;
3909 ce.xclient.data.l[2] = 0l;
3910 ce.xclient.data.l[3] = 0l;
3911 ce.xclient.data.l[4] = 0l;
3912 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
3915 xerror_set_ignore(FALSE);
3917 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d\n", xerror_occured);
3918 return !xerror_occured;
3921 static void client_present(ObClient *self, gboolean here, gboolean raise,
3924 if (client_normal(self) && screen_showing_desktop)
3925 screen_show_desktop(FALSE, self);
3927 client_iconify(self, FALSE, here, FALSE);
3928 if (self->desktop != DESKTOP_ALL &&
3929 self->desktop != screen_desktop)
3932 client_set_desktop(self, screen_desktop, FALSE, TRUE);
3934 screen_set_desktop(self->desktop, FALSE);
3935 } else if (!self->frame->visible)
3936 /* if its not visible for other reasons, then don't mess
3939 if (self->shaded && unshade)
3940 client_shade(self, FALSE);
3942 stacking_raise(CLIENT_AS_WINDOW(self));
3947 /* this function exists to map to the net_active_window message in the ewmh */
3948 void client_activate(ObClient *self, gboolean desktop,
3949 gboolean here, gboolean raise,
3950 gboolean unshade, gboolean user)
3952 if ((user && (desktop ||
3953 self->desktop == DESKTOP_ALL ||
3954 self->desktop == screen_desktop)) ||
3955 client_can_steal_focus(self, event_curtime, CurrentTime))
3957 client_present(self, here, raise, unshade);
3960 client_hilite(self, TRUE);
3963 static void client_bring_windows_recursive(ObClient *self,
3971 for (it = self->transients; it; it = g_slist_next(it))
3972 client_bring_windows_recursive(it->data, desktop,
3973 helpers, modals, iconic);
3975 if (((helpers && client_helper(self)) ||
3976 (modals && self->modal)) &&
3977 ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
3978 (iconic && self->iconic)))
3980 if (iconic && self->iconic)
3981 client_iconify(self, FALSE, TRUE, FALSE);
3983 client_set_desktop(self, desktop, FALSE, FALSE);
3987 void client_bring_helper_windows(ObClient *self)
3989 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
3992 void client_bring_modal_windows(ObClient *self)
3994 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
3997 gboolean client_focused(ObClient *self)
3999 return self == focus_client;
4002 RrImage* client_icon(ObClient *self)
4004 RrImage *ret = NULL;
4007 ret = self->icon_set;
4008 else if (self->parents) {
4010 for (it = self->parents; it && !ret; it = g_slist_next(it))
4011 ret = client_icon(it->data);
4014 ret = client_default_icon;
4018 void client_set_layer(ObClient *self, gint layer)
4022 self->above = FALSE;
4023 } else if (layer == 0) {
4024 self->below = self->above = FALSE;
4026 self->below = FALSE;
4029 client_calc_layer(self);
4030 client_change_state(self); /* reflect this in the state hints */
4033 void client_set_undecorated(ObClient *self, gboolean undecorated)
4035 if (self->undecorated != undecorated &&
4036 /* don't let it undecorate if the function is missing, but let
4038 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4040 self->undecorated = undecorated;
4041 client_setup_decor_and_functions(self, TRUE);
4042 client_change_state(self); /* reflect this in the state hints */
4046 guint client_monitor(ObClient *self)
4048 return screen_find_monitor(&self->frame->area);
4051 ObClient *client_direct_parent(ObClient *self)
4053 if (!self->parents) return NULL;
4054 if (self->transient_for_group) return NULL;
4055 return self->parents->data;
4058 ObClient *client_search_top_direct_parent(ObClient *self)
4061 while ((p = client_direct_parent(self))) self = p;
4065 static GSList *client_search_all_top_parents_internal(ObClient *self,
4067 ObStackingLayer layer)
4072 /* move up the direct transient chain as far as possible */
4073 while ((p = client_direct_parent(self)) &&
4074 (!bylayer || p->layer == layer))
4078 ret = g_slist_prepend(NULL, self);
4080 ret = g_slist_copy(self->parents);
4085 GSList *client_search_all_top_parents(ObClient *self)
4087 return client_search_all_top_parents_internal(self, FALSE, 0);
4090 GSList *client_search_all_top_parents_layer(ObClient *self)
4092 return client_search_all_top_parents_internal(self, TRUE, self->layer);
4095 ObClient *client_search_focus_parent(ObClient *self)
4099 for (it = self->parents; it; it = g_slist_next(it))
4100 if (client_focused(it->data)) return it->data;
4105 ObClient *client_search_focus_parent_full(ObClient *self)
4108 ObClient *ret = NULL;
4110 for (it = self->parents; it; it = g_slist_next(it)) {
4111 if (client_focused(it->data))
4114 ret = client_search_focus_parent_full(it->data);
4120 ObClient *client_search_parent(ObClient *self, ObClient *search)
4124 for (it = self->parents; it; it = g_slist_next(it))
4125 if (it->data == search) return search;
4130 ObClient *client_search_transient(ObClient *self, ObClient *search)
4134 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4135 if (sit->data == search)
4137 if (client_search_transient(sit->data, search))
4143 static void detect_edge(Rect area, ObDirection dir,
4144 gint my_head, gint my_size,
4145 gint my_edge_start, gint my_edge_size,
4146 gint *dest, gboolean *near_edge)
4148 gint edge_start, edge_size, head, tail;
4149 gboolean skip_head = FALSE, skip_tail = FALSE;
4152 case OB_DIRECTION_NORTH:
4153 case OB_DIRECTION_SOUTH:
4154 edge_start = area.x;
4155 edge_size = area.width;
4157 case OB_DIRECTION_EAST:
4158 case OB_DIRECTION_WEST:
4159 edge_start = area.y;
4160 edge_size = area.height;
4163 g_assert_not_reached();
4166 /* do we collide with this window? */
4167 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4168 edge_start, edge_size))
4172 case OB_DIRECTION_NORTH:
4173 head = RECT_BOTTOM(area);
4174 tail = RECT_TOP(area);
4176 case OB_DIRECTION_SOUTH:
4177 head = RECT_TOP(area);
4178 tail = RECT_BOTTOM(area);
4180 case OB_DIRECTION_WEST:
4181 head = RECT_RIGHT(area);
4182 tail = RECT_LEFT(area);
4184 case OB_DIRECTION_EAST:
4185 head = RECT_LEFT(area);
4186 tail = RECT_RIGHT(area);
4189 g_assert_not_reached();
4192 case OB_DIRECTION_NORTH:
4193 case OB_DIRECTION_WEST:
4194 /* check if our window is past the head of this window */
4195 if (my_head <= head + 1)
4197 /* check if our window's tail is past the tail of this window */
4198 if (my_head + my_size - 1 <= tail)
4200 /* check if the head of this window is closer than the previously
4201 chosen edge (take into account that the previously chosen
4202 edge might have been a tail, not a head) */
4203 if (head + (*near_edge ? 0 : my_size) <= *dest)
4205 /* check if the tail of this window is closer than the previously
4206 chosen edge (take into account that the previously chosen
4207 edge might have been a head, not a tail) */
4208 if (tail - (!*near_edge ? 0 : my_size) <= *dest)
4211 case OB_DIRECTION_SOUTH:
4212 case OB_DIRECTION_EAST:
4213 /* check if our window is past the head of this window */
4214 if (my_head >= head - 1)
4216 /* check if our window's tail is past the tail of this window */
4217 if (my_head - my_size + 1 >= tail)
4219 /* check if the head of this window is closer than the previously
4220 chosen edge (take into account that the previously chosen
4221 edge might have been a tail, not a head) */
4222 if (head - (*near_edge ? 0 : my_size) >= *dest)
4224 /* check if the tail of this window is closer than the previously
4225 chosen edge (take into account that the previously chosen
4226 edge might have been a head, not a tail) */
4227 if (tail + (!*near_edge ? 0 : my_size) >= *dest)
4231 g_assert_not_reached();
4234 ob_debug("my head %d size %d\n", my_head, my_size);
4235 ob_debug("head %d tail %d dest %d\n", head, tail, *dest);
4237 ob_debug("using near edge %d\n", head);
4241 else if (!skip_tail) {
4242 ob_debug("using far edge %d\n", tail);
4248 void client_find_edge_directional(ObClient *self, ObDirection dir,
4249 gint my_head, gint my_size,
4250 gint my_edge_start, gint my_edge_size,
4251 gint *dest, gboolean *near_edge)
4259 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4260 &self->frame->area);
4263 case OB_DIRECTION_NORTH:
4264 edge = RECT_TOP(*a) - 1;
4266 case OB_DIRECTION_SOUTH:
4267 edge = RECT_BOTTOM(*a) + 1;
4269 case OB_DIRECTION_EAST:
4270 edge = RECT_RIGHT(*a) + 1;
4272 case OB_DIRECTION_WEST:
4273 edge = RECT_LEFT(*a) - 1;
4276 g_assert_not_reached();
4278 /* default to the far edge, then narrow it down */
4282 /* search for edges of monitors */
4283 for (i = 0; i < screen_num_monitors; ++i) {
4284 Rect *area = screen_area(self->desktop, i, NULL);
4285 detect_edge(*area, dir, my_head, my_size, my_edge_start,
4286 my_edge_size, dest, near_edge);
4290 /* search for edges of clients */
4291 for (it = client_list; it; it = g_list_next(it)) {
4292 ObClient *cur = it->data;
4294 /* skip windows to not bump into */
4299 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4300 cur->desktop != screen_desktop)
4303 ob_debug("trying window %s\n", cur->title);
4305 detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
4306 my_edge_size, dest, near_edge);
4308 dock_get_area(&dock_area);
4309 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4310 my_edge_size, dest, near_edge);
4314 void client_find_move_directional(ObClient *self, ObDirection dir,
4318 gint e, e_start, e_size;
4322 case OB_DIRECTION_EAST:
4323 head = RECT_RIGHT(self->frame->area);
4324 size = self->frame->area.width;
4325 e_start = RECT_TOP(self->frame->area);
4326 e_size = self->frame->area.height;
4328 case OB_DIRECTION_WEST:
4329 head = RECT_LEFT(self->frame->area);
4330 size = self->frame->area.width;
4331 e_start = RECT_TOP(self->frame->area);
4332 e_size = self->frame->area.height;
4334 case OB_DIRECTION_NORTH:
4335 head = RECT_TOP(self->frame->area);
4336 size = self->frame->area.height;
4337 e_start = RECT_LEFT(self->frame->area);
4338 e_size = self->frame->area.width;
4340 case OB_DIRECTION_SOUTH:
4341 head = RECT_BOTTOM(self->frame->area);
4342 size = self->frame->area.height;
4343 e_start = RECT_LEFT(self->frame->area);
4344 e_size = self->frame->area.width;
4347 g_assert_not_reached();
4350 client_find_edge_directional(self, dir, head, size,
4351 e_start, e_size, &e, &near);
4352 *x = self->frame->area.x;
4353 *y = self->frame->area.y;
4355 case OB_DIRECTION_EAST:
4356 if (near) e -= self->frame->area.width;
4360 case OB_DIRECTION_WEST:
4362 else e -= self->frame->area.width;
4365 case OB_DIRECTION_NORTH:
4367 else e -= self->frame->area.height;
4370 case OB_DIRECTION_SOUTH:
4371 if (near) e -= self->frame->area.height;
4376 g_assert_not_reached();
4378 frame_frame_gravity(self->frame, x, y);
4381 void client_find_resize_directional(ObClient *self, ObDirection side,
4383 gint *x, gint *y, gint *w, gint *h)
4386 gint e, e_start, e_size, delta;
4391 case OB_DIRECTION_EAST:
4392 head = RECT_RIGHT(self->frame->area) +
4393 (self->size_inc.width - 1) * (grow ? 1 : 0);
4394 e_start = RECT_TOP(self->frame->area);
4395 e_size = self->frame->area.height;
4396 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4398 case OB_DIRECTION_WEST:
4399 head = RECT_LEFT(self->frame->area) -
4400 (self->size_inc.width - 1) * (grow ? 1 : 0);
4401 e_start = RECT_TOP(self->frame->area);
4402 e_size = self->frame->area.height;
4403 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4405 case OB_DIRECTION_NORTH:
4406 head = RECT_TOP(self->frame->area) -
4407 (self->size_inc.height - 1) * (grow ? 1 : 0);
4408 e_start = RECT_LEFT(self->frame->area);
4409 e_size = self->frame->area.width;
4410 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4412 case OB_DIRECTION_SOUTH:
4413 head = RECT_BOTTOM(self->frame->area) +
4414 (self->size_inc.height - 1) * (grow ? 1 : 0);
4415 e_start = RECT_LEFT(self->frame->area);
4416 e_size = self->frame->area.width;
4417 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4420 g_assert_not_reached();
4423 ob_debug("head %d dir %d\n", head, dir);
4424 client_find_edge_directional(self, dir, head, 1,
4425 e_start, e_size, &e, &near);
4426 ob_debug("edge %d\n", e);
4427 *x = self->frame->area.x;
4428 *y = self->frame->area.y;
4429 *w = self->frame->area.width;
4430 *h = self->frame->area.height;
4432 case OB_DIRECTION_EAST:
4433 if (grow == near) --e;
4434 delta = e - RECT_RIGHT(self->frame->area);
4437 case OB_DIRECTION_WEST:
4438 if (grow == near) ++e;
4439 delta = RECT_LEFT(self->frame->area) - e;
4443 case OB_DIRECTION_NORTH:
4444 if (grow == near) ++e;
4445 delta = RECT_TOP(self->frame->area) - e;
4449 case OB_DIRECTION_SOUTH:
4450 if (grow == near) --e;
4451 delta = e - RECT_BOTTOM(self->frame->area);
4455 g_assert_not_reached();
4457 frame_frame_gravity(self->frame, x, y);
4458 *w -= self->frame->size.left + self->frame->size.right;
4459 *h -= self->frame->size.top + self->frame->size.bottom;
4462 ObClient* client_under_pointer(void)
4466 ObClient *ret = NULL;
4468 if (screen_pointer_pos(&x, &y)) {
4469 for (it = stacking_list; it; it = g_list_next(it)) {
4470 if (WINDOW_IS_CLIENT(it->data)) {
4471 ObClient *c = WINDOW_AS_CLIENT(it->data);
4472 if (c->frame->visible &&
4473 /* check the desktop, this is done during desktop
4474 switching and windows are shown/hidden status is not
4476 (c->desktop == screen_desktop ||
4477 c->desktop == DESKTOP_ALL) &&
4478 /* ignore all animating windows */
4479 !frame_iconify_animating(c->frame) &&
4480 RECT_CONTAINS(c->frame->area, x, y))
4491 gboolean client_has_group_siblings(ObClient *self)
4493 return self->group && self->group->members->next;
4496 /*! Returns TRUE if the client is running on the same machine as Openbox */
4497 gboolean client_on_localhost(ObClient *self)
4499 return self->client_machine == NULL;