1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
25 #include "moveresize.h"
34 #include "focus_cycle.h"
39 #include "menuframe.h"
42 #include "obrender/render.h"
44 #include "obt/display.h"
45 #include "obt/xqueue.h"
53 # include <signal.h> /* for kill() */
57 #include <X11/Xutil.h>
59 /*! The event mask to grab on client windows */
60 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
63 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
68 ObClientCallback func;
72 GList *client_list = NULL;
74 static GSList *client_destroy_notifies = NULL;
75 static RrImage *client_default_icon = NULL;
77 static void client_get_all(ObClient *self, gboolean real);
78 static void client_get_startup_id(ObClient *self);
79 static void client_get_session_ids(ObClient *self);
80 static void client_save_app_rule_values(ObClient *self);
81 static void client_get_area(ObClient *self);
82 static void client_get_desktop(ObClient *self);
83 static void client_get_state(ObClient *self);
84 static void client_get_shaped(ObClient *self);
85 static void client_get_colormap(ObClient *self);
86 static void client_set_desktop_recursive(ObClient *self,
90 static void client_change_allowed_actions(ObClient *self);
91 static void client_change_state(ObClient *self);
92 static void client_change_wm_state(ObClient *self);
93 static void client_apply_startup_state(ObClient *self,
94 gint x, gint y, gint w, gint h);
95 static void client_restore_session_state(ObClient *self);
96 static gboolean client_restore_session_stacking(ObClient *self);
97 static ObAppSettings *client_get_settings_state(ObClient *self);
98 static void client_update_transient_tree(ObClient *self,
99 ObGroup *oldgroup, ObGroup *newgroup,
100 gboolean oldgtran, gboolean newgtran,
102 ObClient *newparent);
103 static void client_present(ObClient *self, gboolean here, gboolean raise,
105 static GSList *client_search_all_top_parents_internal(ObClient *self,
107 ObStackingLayer layer);
108 static void client_call_notifies(ObClient *self, GSList *list);
109 static void client_ping_event(ObClient *self, gboolean dead);
110 static void client_prompt_kill(ObClient *self);
111 static gboolean client_can_steal_focus(ObClient *self,
112 gboolean allow_other_desktop,
113 Time steal_time, Time launch_time);
115 void client_startup(gboolean reconfig)
117 if ((client_default_icon = RrImageCacheFind(ob_rr_icons,
118 ob_rr_theme->def_win_icon,
119 ob_rr_theme->def_win_icon_w,
120 ob_rr_theme->def_win_icon_h)))
121 RrImageRef(client_default_icon);
123 client_default_icon = RrImageNew(ob_rr_icons);
124 RrImageAddPicture(client_default_icon,
125 ob_rr_theme->def_win_icon,
126 ob_rr_theme->def_win_icon_w,
127 ob_rr_theme->def_win_icon_h);
130 if (reconfig) return;
135 void client_shutdown(gboolean reconfig)
137 RrImageUnref(client_default_icon);
138 client_default_icon = NULL;
140 if (reconfig) return;
143 static void client_call_notifies(ObClient *self, GSList *list)
147 for (it = list; it; it = g_slist_next(it)) {
148 ClientCallback *d = it->data;
149 d->func(self, d->data);
153 void client_add_destroy_notify(ObClientCallback func, gpointer data)
155 ClientCallback *d = g_slice_new(ClientCallback);
158 client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
161 void client_remove_destroy_notify(ObClientCallback func)
165 for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
166 ClientCallback *d = it->data;
167 if (d->func == func) {
168 g_slice_free(ClientCallback, d);
169 client_destroy_notifies =
170 g_slist_delete_link(client_destroy_notifies, it);
176 void client_set_list(void)
178 Window *windows, *win_it;
180 guint size = g_list_length(client_list);
182 /* create an array of the window ids */
184 windows = g_new(Window, size);
186 for (it = client_list; it; it = g_list_next(it), ++win_it)
187 *win_it = ((ObClient*)it->data)->window;
191 OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
192 (gulong*)windows, size);
200 void client_manage(Window window, ObPrompt *prompt)
203 XSetWindowAttributes attrib_set;
204 gboolean activate = FALSE;
205 ObAppSettings *settings;
206 gboolean transient = FALSE;
212 ob_debug("Managing window: 0x%lx", window);
214 /* choose the events we want to receive on the CLIENT window
215 (ObPrompt windows can request events too) */
216 attrib_set.event_mask = CLIENT_EVENTMASK |
217 (prompt ? prompt->event_mask : 0);
218 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
219 XChangeWindowAttributes(obt_display, window,
220 CWEventMask|CWDontPropagate, &attrib_set);
222 /* create the ObClient struct, and populate it from the hints on the
224 self = window_new(OB_WINDOW_CLASS_CLIENT, ObClient);
225 self->window = window;
226 self->prompt = prompt;
227 self->managed = TRUE;
229 /* non-zero defaults */
230 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
231 self->gravity = NorthWestGravity;
232 self->desktop = screen_num_desktops; /* always an invalid value */
234 /* get all the stuff off the window */
235 client_get_all(self, TRUE);
237 ob_debug("Window type: %d", self->type);
238 ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
239 ob_debug("Window name: %s class: %s role: %s title: %s",
240 self->name, self->class, self->role, self->title);
242 /* per-app settings override stuff from client_get_all, and return the
243 settings for other uses too. the returned settings is a shallow copy,
244 that needs to be freed with g_free(). */
245 settings = client_get_settings_state(self);
247 /* specify that if we exit, the window should not be destroyed and
248 should be reparented back to root automatically, unless we are managing
249 an internal ObPrompt window */
251 XChangeSaveSet(obt_display, window, SetModeInsert);
253 /* create the decoration frame for the client window */
254 self->frame = frame_new(self);
256 /* this must occur after the frame exists, since we copy the opacity value
258 client_update_opacity(self);
260 window_set_top_area(CLIENT_AS_WINDOW(self),
261 &self->area, self->border_width);
262 window_set_abstract(CLIENT_AS_WINDOW(self),
263 &self->frame->window, /* top level window */
264 &self->window, /* composite redir window */
265 &self->layer, /* stacking layer */
266 &self->frame->depth, /* window depth */
267 &self->alpha); /* opacity */
269 frame_grab_client(self->frame);
271 /* we've grabbed everything and set everything that we need to at mapping
275 /* the session should get the last say though */
276 client_restore_session_state(self);
278 /* tell startup notification that this app started */
279 launch_time = sn_app_started(self->startup_id, self->class, self->name);
281 if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
282 user_time = event_time();
284 /* do this after we have a frame.. it uses the frame to help determine the
285 WM_STATE to apply. */
286 client_change_state(self);
288 /* add ourselves to the focus order */
289 focus_order_add_new(self);
291 /* do this to add ourselves to the stacking list in a non-intrusive way */
292 client_calc_layer(self);
294 /* focus the new window? */
295 if (ob_state() != OB_STATE_STARTING &&
296 (!self->session || self->session->focused) &&
297 /* this means focus=true for window is same as config_focus_new=true */
298 ((config_focus_new || settings->focus == 1) ||
299 client_search_focus_tree_full(self)) &&
300 /* NET_WM_USER_TIME 0 when mapping means don't focus */
302 /* this checks for focus=false for the window */
303 settings->focus != 0 &&
304 focus_valid_target(self, self->desktop,
305 FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
306 settings->focus == 1))
311 /* remove the client's border */
312 XSetWindowBorderWidth(obt_display, self->window, 0);
314 /* set up the frame for its original dimensions before trying to place the
316 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
317 frame_adjust_client_area(self->frame);
319 /* where the frame was placed is where the window was originally */
322 /* but make sure the width/height are valid */
325 client_try_configure(self, &place.x, &place.y,
326 &place.width, &place.height, &l, &l, FALSE);
329 /* figure out placement for the window if the window is new */
330 if (ob_state() == OB_STATE_RUNNING) {
331 ob_debug("Positioned: %s @ %d %d",
332 (!self->positioned ? "no" :
333 (self->positioned == PPosition ? "program specified" :
334 (self->positioned == USPosition ? "user specified" :
335 (self->positioned == (PPosition | USPosition) ?
336 "program + user specified" :
337 "BADNESS !?")))), place.x, place.y);
339 ob_debug("Sized: %s @ %d %d",
340 (!self->sized ? "no" :
341 (self->sized == PSize ? "program specified" :
342 (self->sized == USSize ? "user specified" :
343 (self->sized == (PSize | USSize) ?
344 "program + user specified" :
345 "BADNESS !?")))), place.width, place.height);
347 obplaced = place_client(self, &place.x, &place.y,
348 place.width, place.height, settings);
350 /* watch for buggy apps that ask to be placed at (0,0) when there is
352 if (!obplaced && place.x == 0 && place.y == 0 &&
353 /* non-normal windows are allowed */
354 client_normal(self) &&
355 /* oldschool fullscreen windows are allowed */
356 !client_is_oldfullscreen(self, &place))
360 r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
364 ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
366 g_slice_free(Rect, r);
369 /* make sure the window is visible. */
370 client_find_onscreen(self, &place.x, &place.y,
371 place.width, place.height,
372 /* non-normal clients has less rules, and
373 windows that are being restored from a
374 session do also. we can assume you want
375 it back where you saved it. Clients saying
376 they placed themselves are subjected to
377 harder rules, ones that are placed by
378 place.c or by the user are allowed partially
379 off-screen and on xinerama divides (ie,
380 it is up to the placement routines to avoid
381 the xinerama divides)
383 children and splash screens are forced on
384 screen, but i don't remember why i decided to
387 ob_state() == OB_STATE_RUNNING &&
388 (self->type == OB_CLIENT_TYPE_DIALOG ||
389 self->type == OB_CLIENT_TYPE_SPLASH ||
390 (!((self->positioned & USPosition) ||
391 settings->pos_given) &&
392 client_normal(self) &&
394 /* don't move oldschool fullscreen windows to
395 fit inside the struts (fixes Acroread, which
396 makes its fullscreen window fit the screen
397 but it is not USSize'd or USPosition'd) */
398 !client_is_oldfullscreen(self, &place))));
401 /* if the window isn't user-sized, then make it fit inside
402 the visible screen area on its monitor. Use basically the same rules
403 for forcing the window on screen in the client_find_onscreen call.
405 do this after place_client, it chooses the monitor!
407 splash screens get "transient" set to TRUE by
408 the place_client call
410 if (ob_state() == OB_STATE_RUNNING &&
412 (!(self->sized & USSize || self->positioned & USPosition) &&
413 client_normal(self) &&
415 /* don't shrink oldschool fullscreen windows to fit inside the
416 struts (fixes Acroread, which makes its fullscreen window
417 fit the screen but it is not USSize'd or USPosition'd) */
418 !client_is_oldfullscreen(self, &place))))
420 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
422 /* get the size of the frame */
423 place.width += self->frame->size.left + self->frame->size.right;
424 place.height += self->frame->size.top + self->frame->size.bottom;
426 /* fit the window inside the area */
427 place.width = MIN(place.width, a->width);
428 place.height = MIN(place.height, a->height);
430 ob_debug("setting window size to %dx%d", place.width, place.height);
432 /* get the size of the client back */
433 place.width -= self->frame->size.left + self->frame->size.right;
434 place.height -= self->frame->size.top + self->frame->size.bottom;
436 g_slice_free(Rect, a);
439 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
440 "some restrictions may apply",
441 self->window, place.x, place.y, place.width, place.height);
443 ob_debug(" but session requested %d, %d %d x %d instead, "
445 self->session->x, self->session->y,
446 self->session->w, self->session->h);
448 /* do this after the window is placed, so the premax/prefullscreen numbers
451 this also places the window
453 client_apply_startup_state(self, place.x, place.y,
454 place.width, place.height);
456 ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
457 activate ? "yes" : "no");
459 activate = client_can_steal_focus(self, settings->focus,
460 event_time(), launch_time);
463 /* if the client isn't stealing focus, then hilite it so the user
464 knows it is there, but don't do this if we're restoring from a
466 if (!client_restore_session_stacking(self))
467 client_hilite(self, TRUE);
471 /* This may look rather odd. Well it's because new windows are added
472 to the stacking order non-intrusively. If we're not going to focus
473 the new window or hilite it, then we raise it to the top. This will
474 take affect for things that don't get focused like splash screens.
475 Also if you don't have focus_new enabled, then it's going to get
476 raised to the top. Legacy begets legacy I guess?
478 if (!client_restore_session_stacking(self))
479 stacking_raise(CLIENT_AS_WINDOW(self));
482 mouse_grab_for_client(self, TRUE);
484 /* this has to happen before we try focus the window, but we want it to
485 happen after the client's stacking has been determined or it looks bad
489 if (!config_focus_under_mouse)
490 ignore_start = event_start_ignore_all_enters();
494 if (!config_focus_under_mouse)
495 event_end_ignore_all_enters(ignore_start);
499 gboolean stacked = client_restore_session_stacking(self);
500 client_present(self, FALSE, !stacked, TRUE);
503 /* add to client list/map */
504 client_list = g_list_append(client_list, self);
505 window_add(&self->window, CLIENT_AS_WINDOW(self));
507 /* this has to happen after we're in the client_list */
508 if (STRUT_EXISTS(self->strut))
509 screen_update_areas();
511 /* update the list hints */
514 /* free the ObAppSettings shallow copy */
515 g_slice_free(ObAppSettings, settings);
517 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
518 window, self->frame->window, self->class);
521 ObClient *client_fake_manage(Window window)
524 ObAppSettings *settings;
526 ob_debug("Pretend-managing window: %lx", window);
528 /* do this minimal stuff to figure out the client's decorations */
530 self = window_new(OB_WINDOW_CLASS_CLIENT, ObClient);
531 self->window = window;
533 client_get_all(self, FALSE);
534 /* per-app settings override stuff, and return the settings for other
535 uses too. this returns a shallow copy that needs to be freed */
536 settings = client_get_settings_state(self);
538 /* create the decoration frame for the client window and adjust its size */
539 self->frame = frame_new(self);
541 client_apply_startup_state(self, self->area.x, self->area.y,
542 self->area.width, self->area.height);
544 window_set_abstract(CLIENT_AS_WINDOW(self),
545 &self->frame->window, /* top level window */
546 &self->window, /* composite redir window */
547 &self->layer, /* stacking layer */
548 &self->frame->depth, /* window depth */
551 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
553 ob_debug("gave extents left %d right %d top %d bottom %d",
554 self->frame->size.left, self->frame->size.right,
555 self->frame->size.top, self->frame->size.bottom);
557 /* free the ObAppSettings shallow copy */
558 g_slice_free(ObAppSettings, settings);
563 void client_unmanage_all(void)
566 client_unmanage(client_list->data);
569 void client_unmanage(ObClient *self)
574 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
575 self->window, self->frame->window,
576 self->class, self->title ? self->title : "");
578 g_assert(self != NULL);
580 /* we dont want events no more. do this before hiding the frame so we
581 don't generate more events */
582 XSelectInput(obt_display, self->window, NoEventMask);
584 window_cleanup(CLIENT_AS_WINDOW(self));
586 /* ignore enter events from the unmap so it doesnt mess with the focus */
587 if (!config_focus_under_mouse)
588 ignore_start = event_start_ignore_all_enters();
590 frame_hide(self->frame);
591 /* flush to send the hide to the server quickly */
594 if (!config_focus_under_mouse)
595 event_end_ignore_all_enters(ignore_start);
597 mouse_grab_for_client(self, FALSE);
599 self->managed = FALSE;
601 /* remove the window from our save set, unless we are managing an internal
604 XChangeSaveSet(obt_display, self->window, SetModeDelete);
606 /* update the focus lists */
607 focus_order_remove(self);
608 if (client_focused(self)) {
609 /* don't leave an invalid focus_client */
613 /* if we're prompting to kill the client, close that */
614 prompt_unref(self->kill_prompt);
615 self->kill_prompt = NULL;
617 client_list = g_list_remove(client_list, self);
618 stacking_remove(self);
619 window_remove(self->window);
621 /* once the client is out of the list, update the struts to remove its
623 if (STRUT_EXISTS(self->strut))
624 screen_update_areas();
626 client_call_notifies(self, client_destroy_notifies);
628 /* tell our parent(s) that we're gone */
629 for (it = self->parents; it; it = g_slist_next(it))
630 ((ObClient*)it->data)->transients =
631 g_slist_remove(((ObClient*)it->data)->transients,self);
633 /* tell our transients that we're gone */
634 for (it = self->transients; it; it = g_slist_next(it)) {
635 ((ObClient*)it->data)->parents =
636 g_slist_remove(((ObClient*)it->data)->parents, self);
637 /* we could be keeping our children in a higher layer */
638 client_calc_layer(it->data);
641 /* remove from its group */
643 group_remove(self->group, self);
647 /* restore the window's original geometry so it is not lost */
653 if (self->fullscreen)
654 a = self->pre_fullscreen_area;
655 else if (self->max_horz || self->max_vert) {
656 if (self->max_horz) {
657 a.x = self->pre_max_area.x;
658 a.width = self->pre_max_area.width;
660 if (self->max_vert) {
661 a.y = self->pre_max_area.y;
662 a.height = self->pre_max_area.height;
666 self->fullscreen = self->max_horz = self->max_vert = FALSE;
667 /* let it be moved and resized no matter what */
668 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
669 self->decorations = 0; /* unmanaged windows have no decor */
671 /* give the client its border back */
672 XSetWindowBorderWidth(obt_display, self->window, self->border_width);
674 client_move_resize(self, a.x, a.y, a.width, a.height);
677 /* reparent the window out of the frame, and free the frame */
678 frame_release_client(self->frame);
679 frame_free(self->frame);
682 if (ob_state() != OB_STATE_EXITING) {
683 /* these values should not be persisted across a window
685 OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
686 OBT_PROP_ERASE(self->window, NET_WM_STATE);
687 OBT_PROP_ERASE(self->window, WM_STATE);
689 /* if we're left in an unmapped state, the client wont be mapped.
690 this is bad, since we will no longer be managing the window on
692 XMapWindow(obt_display, self->window);
695 /* these should not be left on the window ever. other window managers
696 don't necessarily use them and it will mess them up (like compiz) */
697 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
698 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
700 /* update the list hints */
703 ob_debug("Unmanaged window 0x%lx", self->window);
705 /* free all data allocated in the client struct */
706 RrImageUnref(self->icon_set);
707 g_slist_free(self->transients);
708 g_free(self->startup_id);
709 g_free(self->wm_command);
711 g_free(self->icon_title);
712 g_free(self->original_title);
716 g_free(self->client_machine);
717 g_free(self->sm_client_id);
718 window_free(CLIENT_AS_WINDOW(self));
721 void client_fake_unmanage(ObClient *self)
723 /* this is all that got allocated to get the decorations */
725 window_cleanup(CLIENT_AS_WINDOW(self));
726 frame_free(self->frame);
727 window_free(CLIENT_AS_WINDOW(self));
730 static gboolean client_can_steal_focus(ObClient *self,
731 gboolean allow_other_desktop,
736 gboolean relative_focused;
737 gboolean parent_focused;
741 parent_focused = (focus_client != NULL &&
742 client_search_focus_parent(self));
743 relative_focused = (focus_client != NULL &&
744 (client_search_focus_tree_full(self) != NULL ||
745 client_search_focus_group_full(self) != NULL));
747 /* This is focus stealing prevention */
748 ob_debug_type(OB_DEBUG_FOCUS,
749 "Want to focus window 0x%x at time %u "
750 "launched at %u (last user interaction time %u)",
751 self->window, steal_time, launch_time,
752 event_last_user_time);
754 /* if it's on another desktop... */
755 if (!(self->desktop == screen_desktop ||
756 self->desktop == DESKTOP_ALL) &&
757 /* and (we dont know when it launched, and we don't want to allow
758 focus stealing from other desktops */
759 ((!launch_time && !allow_other_desktop) ||
760 /* or the timestamp is from before you changed desktops) */
761 (screen_desktop_user_time &&
762 !event_time_after(launch_time, screen_desktop_user_time))))
765 ob_debug_type(OB_DEBUG_FOCUS,
766 "Not focusing the window because its on another "
769 /* If something is focused... */
770 else if (focus_client) {
771 /* If the user is working in another window right now, then don't
773 if (!parent_focused &&
774 event_last_user_time && launch_time &&
775 event_time_after(event_last_user_time, launch_time) &&
776 event_last_user_time != launch_time &&
777 event_time_after(event_last_user_time,
778 steal_time - OB_EVENT_USER_TIME_DELAY))
781 ob_debug_type(OB_DEBUG_FOCUS,
782 "Not focusing the window because the user is "
783 "working in another window that is not "
786 /* If the new window is a transient (and its relatives aren't
788 else if (client_has_parent(self) && !relative_focused) {
790 ob_debug_type(OB_DEBUG_FOCUS,
791 "Not focusing the window because it is a "
792 "transient, and its relatives aren't focused");
794 /* Don't steal focus from globally active clients.
795 I stole this idea from KWin. It seems nice.
797 else if (!(focus_client->can_focus ||
798 focus_client->focus_notify))
801 ob_debug_type(OB_DEBUG_FOCUS,
802 "Not focusing the window because a globally "
803 "active client has focus");
805 /* Don't move focus if it's not going to go to this window
807 else if (client_focus_target(self) != self) {
809 ob_debug_type(OB_DEBUG_FOCUS,
810 "Not focusing the window because another window "
811 "would get the focus anyway");
813 /* Don't move focus if the window is not visible on the current
814 desktop and none of its relatives are focused */
815 else if (!(self->desktop == screen_desktop ||
816 self->desktop == DESKTOP_ALL) &&
820 ob_debug_type(OB_DEBUG_FOCUS,
821 "Not focusing the window because it is on "
822 "another desktop and no relatives are focused ");
827 ob_debug_type(OB_DEBUG_FOCUS,
828 "Focus stealing prevention activated for %s at "
829 "time %u (last user interaction time %u)",
830 self->title, steal_time, event_last_user_time);
834 /*! Returns a new structure containing the per-app settings for this client.
835 The returned structure needs to be freed with g_free. */
836 static ObAppSettings *client_get_settings_state(ObClient *self)
838 ObAppSettings *settings;
841 settings = config_create_app_settings();
843 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
844 ObAppSettings *app = it->data;
845 gboolean match = TRUE;
847 g_assert(app->name != NULL || app->class != NULL ||
848 app->role != NULL || app->title != NULL ||
849 (signed)app->type >= 0);
852 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
854 else if (app->class &&
855 !g_pattern_match(app->class,
856 strlen(self->class), self->class, NULL))
858 else if (app->role &&
859 !g_pattern_match(app->role,
860 strlen(self->role), self->role, NULL))
862 else if (app->title &&
863 !g_pattern_match(app->title,
864 strlen(self->title), self->title, NULL))
866 else if ((signed)app->type >= 0 && app->type != self->type) {
871 ob_debug("Window matching: %s", app->name);
873 /* copy the settings to our struct, overriding the existing
874 settings if they are not defaults */
875 config_app_settings_copy_non_defaults(app, settings);
879 if (settings->shade != -1)
880 self->shaded = !!settings->shade;
881 if (settings->decor != -1)
882 self->undecorated = !settings->decor;
883 if (settings->iconic != -1)
884 self->iconic = !!settings->iconic;
885 if (settings->skip_pager != -1)
886 self->skip_pager = !!settings->skip_pager;
887 if (settings->skip_taskbar != -1)
888 self->skip_taskbar = !!settings->skip_taskbar;
890 if (settings->max_vert != -1)
891 self->max_vert = !!settings->max_vert;
892 if (settings->max_horz != -1)
893 self->max_horz = !!settings->max_horz;
895 if (settings->fullscreen != -1)
896 self->fullscreen = !!settings->fullscreen;
898 if (settings->desktop) {
899 if (settings->desktop == DESKTOP_ALL)
900 self->desktop = settings->desktop;
901 else if (settings->desktop > 0 &&
902 settings->desktop <= screen_num_desktops)
903 self->desktop = settings->desktop - 1;
906 if (settings->layer == -1) {
910 else if (settings->layer == 0) {
914 else if (settings->layer == 1) {
921 static void client_restore_session_state(ObClient *self)
925 ob_debug_type(OB_DEBUG_SM,
926 "Restore session for client %s", self->title);
928 if (!(it = session_state_find(self))) {
929 ob_debug_type(OB_DEBUG_SM,
930 "Session data not found for client %s", self->title);
934 self->session = it->data;
936 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
939 RECT_SET_POINT(self->area, self->session->x, self->session->y);
940 self->positioned = USPosition;
941 self->sized = USSize;
942 if (self->session->w > 0)
943 self->area.width = self->session->w;
944 if (self->session->h > 0)
945 self->area.height = self->session->h;
946 XResizeWindow(obt_display, self->window,
947 self->area.width, self->area.height);
949 self->desktop = (self->session->desktop == DESKTOP_ALL ?
950 self->session->desktop :
951 MIN(screen_num_desktops - 1, self->session->desktop));
952 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
954 self->shaded = self->session->shaded;
955 self->iconic = self->session->iconic;
956 self->skip_pager = self->session->skip_pager;
957 self->skip_taskbar = self->session->skip_taskbar;
958 self->fullscreen = self->session->fullscreen;
959 self->above = self->session->above;
960 self->below = self->session->below;
961 self->max_horz = self->session->max_horz;
962 self->max_vert = self->session->max_vert;
963 self->undecorated = self->session->undecorated;
966 static gboolean client_restore_session_stacking(ObClient *self)
970 if (!self->session) return FALSE;
972 mypos = g_list_find(session_saved_state, self->session);
973 if (!mypos) return FALSE;
975 /* start above me and look for the first client */
976 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
979 for (cit = client_list; cit; cit = g_list_next(cit)) {
980 ObClient *c = cit->data;
981 /* found a client that was in the session, so go below it */
982 if (c->session == it->data) {
983 stacking_below(CLIENT_AS_WINDOW(self),
984 CLIENT_AS_WINDOW(cit->data));
992 void client_move_onscreen(ObClient *self, gboolean rude)
994 gint x = self->area.x;
995 gint y = self->area.y;
996 if (client_find_onscreen(self, &x, &y,
998 self->area.height, rude)) {
999 client_move(self, x, y);
1003 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
1006 gint ox = *x, oy = *y;
1007 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
1013 RECT_SET(desired, *x, *y, w, h);
1014 frame_rect_to_frame(self->frame, &desired);
1016 /* get where the frame would be */
1017 frame_client_gravity(self->frame, x, y);
1019 /* get the requested size of the window with decorations */
1020 fw = self->frame->size.left + w + self->frame->size.right;
1021 fh = self->frame->size.top + h + self->frame->size.bottom;
1023 /* If rudeness wasn't requested, then still be rude in a given direction
1024 if the client is not moving, only resizing in that direction */
1026 Point oldtl, oldtr, oldbl, oldbr;
1027 Point newtl, newtr, newbl, newbr;
1028 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
1030 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
1031 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
1032 self->frame->area.y + self->frame->area.height - 1);
1033 POINT_SET(oldtr, oldbr.x, oldtl.y);
1034 POINT_SET(oldbl, oldtl.x, oldbr.y);
1036 POINT_SET(newtl, *x, *y);
1037 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
1038 POINT_SET(newtr, newbr.x, newtl.y);
1039 POINT_SET(newbl, newtl.x, newbr.y);
1041 /* is it moving or just resizing from some corner? */
1042 stationary_l = oldtl.x == newtl.x;
1043 stationary_r = oldtr.x == newtr.x;
1044 stationary_t = oldtl.y == newtl.y;
1045 stationary_b = oldbl.y == newbl.y;
1047 /* if left edge is growing and didnt move right edge */
1048 if (stationary_r && newtl.x < oldtl.x)
1050 /* if right edge is growing and didnt move left edge */
1051 if (stationary_l && newtr.x > oldtr.x)
1053 /* if top edge is growing and didnt move bottom edge */
1054 if (stationary_b && newtl.y < oldtl.y)
1056 /* if bottom edge is growing and didnt move top edge */
1057 if (stationary_t && newbl.y > oldbl.y)
1061 /* we iterate through every monitor that the window is at least partially
1062 on, to make sure it is obeying the rules on them all
1064 if the window does not appear on any monitors, then use the first one
1067 for (i = 0; i < screen_num_monitors; ++i) {
1070 if (!screen_physical_area_monitor_contains(i, &desired)) {
1071 if (i < screen_num_monitors - 1 || found_mon)
1074 /* the window is not inside any monitor! so just use the first
1076 a = screen_area(self->desktop, 0, NULL);
1079 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1082 /* This makes sure windows aren't entirely outside of the screen so you
1083 can't see them at all.
1084 It makes sure 10% of the window is on the screen at least. And don't
1085 let it move itself off the top of the screen, which would hide the
1086 titlebar on you. (The user can still do this if they want too, it's
1087 only limiting the application.
1089 if (client_normal(self)) {
1090 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1091 *x = a->x + a->width - fw/10;
1092 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1093 *y = a->y + a->height - fh/10;
1094 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1095 *x = a->x - fw*9/10;
1096 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1097 *y = a->y - fh*9/10;
1100 /* This here doesn't let windows even a pixel outside the
1101 struts/screen. When called from client_manage, programs placing
1102 themselves are forced completely onscreen, while things like
1103 xterm -geometry resolution-width/2 will work fine. Trying to
1104 place it completely offscreen will be handled in the above code.
1105 Sorry for this confused comment, i am tired. */
1106 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1107 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1108 *x = a->x + MAX(0, a->width - fw);
1110 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1111 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1112 *y = a->y + MAX(0, a->height - fh);
1114 g_slice_free(Rect, a);
1117 /* get where the client should be */
1118 frame_frame_gravity(self->frame, x, y);
1120 return ox != *x || oy != *y;
1123 static void client_get_all(ObClient *self, gboolean real)
1125 /* this is needed for the frame to set itself up */
1126 client_get_area(self);
1128 /* these things can change the decor and functions of the window */
1130 client_get_mwm_hints(self);
1131 /* this can change the mwmhints for special cases */
1132 client_get_type_and_transientness(self);
1133 client_update_normal_hints(self);
1135 /* set up the decor/functions before getting the state. the states may
1136 affect which functions are available, but we want to know the maximum
1137 decor/functions are available to this window, so we can then apply them
1138 in client_apply_startup_state() */
1139 client_setup_decor_and_functions(self, FALSE);
1141 client_get_state(self);
1143 /* get the session related properties, these can change decorations
1144 from per-app settings */
1145 client_get_session_ids(self);
1147 /* now we got everything that can affect the decorations */
1151 /* get this early so we have it for debugging */
1152 client_update_title(self);
1154 /* save the values of the variables used for app rule matching */
1155 client_save_app_rule_values(self);
1157 client_update_protocols(self);
1159 client_update_wmhints(self);
1160 /* this may have already been called from client_update_wmhints */
1161 if (!self->parents && !self->transient_for_group)
1162 client_update_transient_for(self);
1164 client_get_startup_id(self);
1165 client_get_desktop(self);/* uses transient data/group/startup id if a
1166 desktop is not specified */
1167 client_get_shaped(self);
1170 /* a couple type-based defaults for new windows */
1172 /* this makes sure that these windows appear on all desktops */
1173 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1174 self->desktop = DESKTOP_ALL;
1178 client_update_sync_request_counter(self);
1181 client_get_colormap(self);
1182 client_update_strut(self);
1183 client_update_icons(self);
1184 client_update_icon_geometry(self);
1187 static void client_get_startup_id(ObClient *self)
1189 if (!(OBT_PROP_GETS(self->window, NET_STARTUP_ID, utf8,
1190 &self->startup_id)))
1192 OBT_PROP_GETS(self->group->leader,
1193 NET_STARTUP_ID, utf8, &self->startup_id);
1196 static void client_get_area(ObClient *self)
1198 XWindowAttributes wattrib;
1201 ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
1202 g_assert(ret != BadWindow);
1204 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1205 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1206 self->border_width = wattrib.border_width;
1208 ob_debug("client area: %d,%d %dx%d bw %d", wattrib.x, wattrib.y,
1209 wattrib.width, wattrib.height, wattrib.border_width);
1212 static void client_get_desktop(ObClient *self)
1214 guint32 d = screen_num_desktops; /* an always-invalid value */
1216 if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
1217 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1218 self->desktop = screen_num_desktops - 1;
1221 ob_debug("client requested desktop 0x%x", self->desktop);
1224 gboolean first = TRUE;
1225 guint all = screen_num_desktops; /* not a valid value */
1227 /* if they are all on one desktop, then open it on the
1229 for (it = self->parents; it; it = g_slist_next(it)) {
1230 ObClient *c = it->data;
1232 if (c->desktop == DESKTOP_ALL) continue;
1238 else if (all != c->desktop)
1239 all = screen_num_desktops; /* make it invalid */
1241 if (all != screen_num_desktops) {
1242 self->desktop = all;
1244 ob_debug("client desktop set from parents: 0x%x",
1247 /* try get from the startup-notification protocol */
1248 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1249 if (self->desktop >= screen_num_desktops &&
1250 self->desktop != DESKTOP_ALL)
1251 self->desktop = screen_num_desktops - 1;
1252 ob_debug("client desktop set from startup-notification: 0x%x",
1255 /* defaults to the current desktop */
1257 self->desktop = screen_desktop;
1258 ob_debug("client desktop set to the current desktop: %d",
1264 static void client_get_state(ObClient *self)
1269 if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
1271 for (i = 0; i < num; ++i) {
1272 if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1274 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1275 self->shaded = TRUE;
1276 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1277 self->iconic = TRUE;
1278 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1279 self->skip_taskbar = TRUE;
1280 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1281 self->skip_pager = TRUE;
1282 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1283 self->fullscreen = TRUE;
1284 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
1285 self->max_vert = TRUE;
1286 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
1287 self->max_horz = TRUE;
1288 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1290 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
1292 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
1293 self->demands_attention = TRUE;
1294 else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
1295 self->undecorated = TRUE;
1302 static void client_get_shaped(ObClient *self)
1304 self->shaped = FALSE;
1306 if (obt_display_extension_shape) {
1311 XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
1313 XShapeQueryExtents(obt_display, self->window, &s, &foo,
1314 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1321 void client_update_transient_for(ObClient *self)
1324 ObClient *target = NULL;
1325 gboolean trangroup = FALSE;
1327 if (XGetTransientForHint(obt_display, self->window, &t)) {
1328 if (t != self->window) { /* can't be transient to itself! */
1329 ObWindow *tw = window_find(t);
1330 /* if this happens then we need to check for it */
1331 g_assert(tw != CLIENT_AS_WINDOW(self));
1332 if (tw && WINDOW_IS_CLIENT(tw)) {
1333 /* watch out for windows with a parent that is something
1334 different, like a dockapp for example */
1335 target = WINDOW_AS_CLIENT(tw);
1339 /* Setting the transient_for to Root is actually illegal, however
1340 applications from time have done this to specify transient for
1342 if (!target && self->group && t == obt_root(ob_screen))
1344 } else if (self->group && self->transient)
1347 client_update_transient_tree(self, self->group, self->group,
1348 self->transient_for_group, trangroup,
1349 client_direct_parent(self), target);
1350 self->transient_for_group = trangroup;
1354 static void client_update_transient_tree(ObClient *self,
1355 ObGroup *oldgroup, ObGroup *newgroup,
1356 gboolean oldgtran, gboolean newgtran,
1357 ObClient* oldparent,
1358 ObClient *newparent)
1363 g_assert(!oldgtran || oldgroup);
1364 g_assert(!newgtran || newgroup);
1365 g_assert((!oldgtran && !oldparent) ||
1366 (oldgtran && !oldparent) ||
1367 (!oldgtran && oldparent));
1368 g_assert((!newgtran && !newparent) ||
1369 (newgtran && !newparent) ||
1370 (!newgtran && newparent));
1373 Group transient windows are not allowed to have other group
1374 transient windows as their children.
1377 /* No change has occured */
1378 if (oldgroup == newgroup &&
1379 oldgtran == newgtran &&
1380 oldparent == newparent) return;
1382 /** Remove the client from the transient tree **/
1384 for (it = self->transients; it; it = next) {
1385 next = g_slist_next(it);
1387 self->transients = g_slist_delete_link(self->transients, it);
1388 c->parents = g_slist_remove(c->parents, self);
1390 for (it = self->parents; it; it = next) {
1391 next = g_slist_next(it);
1393 self->parents = g_slist_delete_link(self->parents, it);
1394 c->transients = g_slist_remove(c->transients, self);
1397 /** Re-add the client to the transient tree **/
1399 /* If we're transient for a group then we need to add ourselves to all our
1402 for (it = newgroup->members; it; it = g_slist_next(it)) {
1405 !client_search_top_direct_parent(c)->transient_for_group &&
1408 c->transients = g_slist_prepend(c->transients, self);
1409 self->parents = g_slist_prepend(self->parents, c);
1414 /* If we are now transient for a single window we need to add ourselves to
1417 WARNING: Cyclical transient-ness is possible if two windows are
1418 transient for eachother.
1420 else if (newparent &&
1421 /* don't make ourself its child if it is already our child */
1422 !client_is_direct_child(self, newparent) &&
1423 client_normal(newparent))
1425 newparent->transients = g_slist_prepend(newparent->transients, self);
1426 self->parents = g_slist_prepend(self->parents, newparent);
1429 /* Add any group transient windows to our children. But if we're transient
1430 for the group, then other group transients are not our children.
1432 WARNING: Cyclical transient-ness is possible. For e.g. if:
1433 A is transient for the group
1434 B is transient for A
1435 C is transient for B
1436 A can't be transient for C or we have a cycle
1438 if (!newgtran && newgroup &&
1440 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1441 client_normal(self))
1443 for (it = newgroup->members; it; it = g_slist_next(it)) {
1445 if (c != self && c->transient_for_group &&
1446 /* Don't make it our child if it is already our parent */
1447 !client_is_direct_child(c, self))
1449 self->transients = g_slist_prepend(self->transients, c);
1450 c->parents = g_slist_prepend(c->parents, self);
1455 /** If we change our group transient-ness, our children change their
1456 effective group transient-ness, which affects how they relate to other
1459 for (it = self->transients; it; it = g_slist_next(it)) {
1461 if (!c->transient_for_group)
1462 client_update_transient_tree(c, c->group, c->group,
1463 c->transient_for_group,
1464 c->transient_for_group,
1465 client_direct_parent(c),
1466 client_direct_parent(c));
1470 void client_get_mwm_hints(ObClient *self)
1475 self->mwmhints.flags = 0; /* default to none */
1477 if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1479 if (num >= OB_MWM_ELEMENTS) {
1480 self->mwmhints.flags = hints[0];
1481 self->mwmhints.functions = hints[1];
1482 self->mwmhints.decorations = hints[2];
1488 void client_get_type_and_transientness(ObClient *self)
1495 self->transient = FALSE;
1497 if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1498 /* use the first value that we know about in the array */
1499 for (i = 0; i < num; ++i) {
1500 if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1501 self->type = OB_CLIENT_TYPE_DESKTOP;
1502 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1503 self->type = OB_CLIENT_TYPE_DOCK;
1504 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1505 self->type = OB_CLIENT_TYPE_TOOLBAR;
1506 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1507 self->type = OB_CLIENT_TYPE_MENU;
1508 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1509 self->type = OB_CLIENT_TYPE_UTILITY;
1510 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1511 self->type = OB_CLIENT_TYPE_SPLASH;
1512 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1513 self->type = OB_CLIENT_TYPE_DIALOG;
1514 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1515 self->type = OB_CLIENT_TYPE_NORMAL;
1516 else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1518 /* prevent this window from getting any decor or
1520 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1521 OB_MWM_FLAG_DECORATIONS);
1522 self->mwmhints.decorations = 0;
1523 self->mwmhints.functions = 0;
1525 if (self->type != (ObClientType) -1)
1526 break; /* grab the first legit type */
1531 if (XGetTransientForHint(obt_display, self->window, &t))
1532 self->transient = TRUE;
1534 if (self->type == (ObClientType) -1) {
1535 /*the window type hint was not set, which means we either classify
1536 ourself as a normal window or a dialog, depending on if we are a
1538 if (self->transient)
1539 self->type = OB_CLIENT_TYPE_DIALOG;
1541 self->type = OB_CLIENT_TYPE_NORMAL;
1544 /* then, based on our type, we can update our transientness.. */
1545 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1546 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1547 self->type == OB_CLIENT_TYPE_MENU ||
1548 self->type == OB_CLIENT_TYPE_UTILITY)
1550 self->transient = TRUE;
1554 void client_update_protocols(ObClient *self)
1559 self->focus_notify = FALSE;
1560 self->delete_window = FALSE;
1562 if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1563 for (i = 0; i < num_ret; ++i) {
1564 if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1565 /* this means we can request the window to close */
1566 self->delete_window = TRUE;
1567 else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1568 /* if this protocol is requested, then the window will be
1569 notified whenever we want it to receive focus */
1570 self->focus_notify = TRUE;
1571 else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1572 /* if this protocol is requested, then the window will allow
1573 pings to determine if it is still alive */
1576 else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1577 /* if this protocol is requested, then resizing the
1578 window will be synchronized between the frame and the
1580 self->sync_request = TRUE;
1588 void client_update_sync_request_counter(ObClient *self)
1592 if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1596 self->sync_counter = i;
1598 /* this must be set when managing a new window according to EWMH */
1599 XSyncIntToValue(&val, 0);
1600 XSyncSetCounter(obt_display, self->sync_counter, val);
1602 self->sync_counter = None;
1606 static void client_get_colormap(ObClient *self)
1608 XWindowAttributes wa;
1610 if (XGetWindowAttributes(obt_display, self->window, &wa))
1611 client_update_colormap(self, wa.colormap);
1614 void client_update_colormap(ObClient *self, Colormap colormap)
1616 if (colormap == self->colormap) return;
1618 ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1620 if (client_focused(self)) {
1621 screen_install_colormap(self, FALSE); /* uninstall old one */
1622 self->colormap = colormap;
1623 screen_install_colormap(self, TRUE); /* install new one */
1625 self->colormap = colormap;
1628 void client_update_normal_hints(ObClient *self)
1634 self->min_ratio = 0.0f;
1635 self->max_ratio = 0.0f;
1636 SIZE_SET(self->size_inc, 1, 1);
1637 SIZE_SET(self->base_size, -1, -1);
1638 SIZE_SET(self->min_size, 0, 0);
1639 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1641 /* get the hints from the window */
1642 if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
1643 /* normal windows can't request placement! har har
1644 if (!client_normal(self))
1646 self->positioned = (size.flags & (PPosition|USPosition));
1647 self->sized = (size.flags & (PSize|USSize));
1649 if (size.flags & PWinGravity)
1650 self->gravity = size.win_gravity;
1652 if (size.flags & PAspect) {
1653 if (size.min_aspect.y)
1655 (gfloat) size.min_aspect.x / size.min_aspect.y;
1656 if (size.max_aspect.y)
1658 (gfloat) size.max_aspect.x / size.max_aspect.y;
1661 if (size.flags & PMinSize)
1662 SIZE_SET(self->min_size, size.min_width, size.min_height);
1664 if (size.flags & PMaxSize)
1665 SIZE_SET(self->max_size, size.max_width, size.max_height);
1667 if (size.flags & PBaseSize)
1668 SIZE_SET(self->base_size, size.base_width, size.base_height);
1670 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1671 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1673 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1674 self->min_size.width, self->min_size.height,
1675 self->max_size.width, self->max_size.height);
1676 ob_debug("size inc (%d %d) base size (%d %d)",
1677 self->size_inc.width, self->size_inc.height,
1678 self->base_size.width, self->base_size.height);
1681 ob_debug("Normal hints: not set");
1684 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1686 /* start with everything (cept fullscreen) */
1688 (OB_FRAME_DECOR_TITLEBAR |
1689 OB_FRAME_DECOR_HANDLE |
1690 OB_FRAME_DECOR_GRIPS |
1691 OB_FRAME_DECOR_BORDER |
1692 OB_FRAME_DECOR_ICON |
1693 OB_FRAME_DECOR_ALLDESKTOPS |
1694 OB_FRAME_DECOR_ICONIFY |
1695 OB_FRAME_DECOR_MAXIMIZE |
1696 OB_FRAME_DECOR_SHADE |
1697 OB_FRAME_DECOR_CLOSE);
1699 (OB_CLIENT_FUNC_RESIZE |
1700 OB_CLIENT_FUNC_MOVE |
1701 OB_CLIENT_FUNC_ICONIFY |
1702 OB_CLIENT_FUNC_MAXIMIZE |
1703 OB_CLIENT_FUNC_SHADE |
1704 OB_CLIENT_FUNC_CLOSE |
1705 OB_CLIENT_FUNC_BELOW |
1706 OB_CLIENT_FUNC_ABOVE |
1707 OB_CLIENT_FUNC_UNDECORATE);
1709 if (!(self->min_size.width < self->max_size.width ||
1710 self->min_size.height < self->max_size.height))
1711 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1713 switch (self->type) {
1714 case OB_CLIENT_TYPE_NORMAL:
1715 /* normal windows retain all of the possible decorations and
1716 functionality, and can be fullscreen */
1717 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1720 case OB_CLIENT_TYPE_DIALOG:
1721 /* sometimes apps make dialog windows fullscreen for some reason (for
1722 e.g. kpdf does this..) */
1723 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1726 case OB_CLIENT_TYPE_UTILITY:
1727 /* these windows don't have anything added or removed by default */
1730 case OB_CLIENT_TYPE_MENU:
1731 case OB_CLIENT_TYPE_TOOLBAR:
1732 /* these windows can't iconify or maximize */
1733 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1734 OB_FRAME_DECOR_MAXIMIZE);
1735 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1736 OB_CLIENT_FUNC_MAXIMIZE);
1739 case OB_CLIENT_TYPE_SPLASH:
1740 /* these don't get get any decorations, and the only thing you can
1741 do with them is move them */
1742 self->decorations = 0;
1743 self->functions = OB_CLIENT_FUNC_MOVE;
1746 case OB_CLIENT_TYPE_DESKTOP:
1747 /* these windows are not manipulated by the window manager */
1748 self->decorations = 0;
1749 self->functions = 0;
1752 case OB_CLIENT_TYPE_DOCK:
1753 /* these windows are not manipulated by the window manager, but they
1754 can set below layer which has a special meaning */
1755 self->decorations = 0;
1756 self->functions = OB_CLIENT_FUNC_BELOW;
1760 /* If the client has no decor from its type (which never changes) then
1761 don't allow the user to "undecorate" the window. Otherwise, allow them
1762 to, even if there are motif hints removing the decor, because those
1763 may change these days (e.g. chromium) */
1764 if (self->decorations == 0)
1765 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1767 /* Mwm Hints are applied subtractively to what has already been chosen for
1768 decor and functionality */
1769 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1770 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1771 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1772 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1774 /* if the mwm hints request no handle or title, then all
1775 decorations are disabled, but keep the border if that's
1777 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1778 self->decorations = OB_FRAME_DECOR_BORDER;
1780 self->decorations = 0;
1785 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1786 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1787 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1788 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1789 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1790 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1791 /* dont let mwm hints kill any buttons
1792 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1793 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1794 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1795 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1797 /* dont let mwm hints kill the close button
1798 if (! (self->mwmhints.functions & MwmFunc_Close))
1799 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1803 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1804 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1805 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1806 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1807 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1808 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1810 /* can't maximize without moving/resizing */
1811 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1812 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1813 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1814 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1815 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1818 if (self->max_horz && self->max_vert) {
1819 /* once upon a time you couldn't resize maximized windows, that is not
1820 the case any more though !
1822 but do kill the handle on fully maxed windows */
1823 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1826 /* finally, the user can have requested no decorations, which overrides
1827 everything (but doesnt give it a border if it doesnt have one) */
1828 if (self->undecorated)
1829 self->decorations &= (config_theme_keepborder ?
1830 OB_FRAME_DECOR_BORDER : 0);
1832 /* if we don't have a titlebar, then we cannot shade! */
1833 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1834 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1836 /* now we need to check against rules for the client's current state */
1837 if (self->fullscreen) {
1838 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1839 OB_CLIENT_FUNC_FULLSCREEN |
1840 OB_CLIENT_FUNC_ICONIFY);
1841 self->decorations = 0;
1844 client_change_allowed_actions(self);
1847 /* reconfigure to make sure decorations are updated */
1848 client_reconfigure(self, FALSE);
1851 static void client_change_allowed_actions(ObClient *self)
1856 /* desktop windows are kept on all desktops */
1857 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1858 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1860 if (self->functions & OB_CLIENT_FUNC_SHADE)
1861 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1862 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1863 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1864 if (self->functions & OB_CLIENT_FUNC_MOVE)
1865 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1866 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1867 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1868 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1869 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1870 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1871 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1872 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1873 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1874 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1876 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1877 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1878 if (self->functions & OB_CLIENT_FUNC_BELOW)
1879 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1880 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1881 actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1883 OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1885 /* make sure the window isn't breaking any rules now
1887 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1888 it can't be iconified with its parent
1891 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1892 if (self->frame) client_shade(self, FALSE);
1893 else self->shaded = FALSE;
1895 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1896 if (self->frame) client_fullscreen(self, FALSE);
1897 else self->fullscreen = FALSE;
1899 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1901 if (self->frame) client_maximize(self, FALSE, 0);
1902 else self->max_vert = self->max_horz = FALSE;
1906 void client_update_wmhints(ObClient *self)
1910 /* assume a window takes input if it doesn't specify */
1911 self->can_focus = TRUE;
1913 if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
1916 if (hints->flags & InputHint)
1917 self->can_focus = hints->input;
1919 /* only do this when first managing the window *AND* when we aren't
1921 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1922 if (hints->flags & StateHint)
1923 self->iconic = hints->initial_state == IconicState;
1926 self->urgent = (hints->flags & XUrgencyHint);
1927 if (self->urgent && !ur)
1928 client_hilite(self, TRUE);
1929 else if (!self->urgent && ur && self->demands_attention)
1930 client_hilite(self, FALSE);
1932 if (!(hints->flags & WindowGroupHint))
1933 hints->window_group = None;
1935 /* did the group state change? */
1936 if (hints->window_group !=
1937 (self->group ? self->group->leader : None))
1939 ObGroup *oldgroup = self->group;
1941 /* remove from the old group if there was one */
1943 group_remove(self->group, self);
1947 /* add ourself to the group if we have one */
1948 if (hints->window_group != None) {
1949 self->group = group_add(hints->window_group, self);
1952 /* Put ourselves into the new group's transient tree, and remove
1953 ourselves from the old group's */
1954 client_update_transient_tree(self, oldgroup, self->group,
1955 self->transient_for_group,
1956 self->transient_for_group,
1957 client_direct_parent(self),
1958 client_direct_parent(self));
1960 /* Lastly, being in a group, or not, can change if the window is
1961 transient for anything.
1963 The logic for this is:
1964 self->transient = TRUE always if the window wants to be
1965 transient for something, even if transient_for was NULL because
1966 it wasn't in a group before.
1968 If parents was NULL and oldgroup was NULL we can assume
1969 that when we add the new group, it will become transient for
1972 If transient_for_group is TRUE, then it must have already
1973 had a group. If it is getting a new group, the above call to
1974 client_update_transient_tree has already taken care of
1975 everything ! If it is losing all group status then it will
1976 no longer be transient for anything and that needs to be
1979 if (self->transient &&
1980 ((self->parents == NULL && oldgroup == NULL) ||
1981 (self->transient_for_group && !self->group)))
1982 client_update_transient_for(self);
1985 /* the WM_HINTS can contain an icon */
1986 if (hints->flags & IconPixmapHint)
1987 client_update_icons(self);
1992 focus_cycle_addremove(self, TRUE);
1995 void client_update_title(ObClient *self)
1998 gchar *visible = NULL;
2000 g_free(self->title);
2001 g_free(self->original_title);
2004 if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) {
2005 /* try old x stuff */
2006 if (!(OBT_PROP_GETS(self->window, WM_NAME, locale, &data)
2007 || OBT_PROP_GETS(self->window, WM_NAME, utf8, &data))) {
2008 if (self->transient) {
2010 GNOME alert windows are not given titles:
2011 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
2013 data = g_strdup("");
2015 data = g_strdup(_("Unnamed Window"));
2018 self->original_title = g_strdup(data);
2020 if (self->client_machine) {
2021 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2026 if (self->not_responding) {
2028 if (self->kill_level > 0)
2029 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2031 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2035 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible);
2036 self->title = visible;
2039 frame_adjust_title(self->frame);
2041 /* update the icon title */
2043 g_free(self->icon_title);
2046 if (!OBT_PROP_GETS(self->window, NET_WM_ICON_NAME, utf8, &data))
2047 /* try old x stuff */
2048 if (!(OBT_PROP_GETS(self->window, WM_ICON_NAME, locale, &data) ||
2049 OBT_PROP_GETS(self->window, WM_ICON_NAME, utf8, &data)))
2050 data = g_strdup(self->title);
2052 if (self->client_machine) {
2053 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2058 if (self->not_responding) {
2060 if (self->kill_level > 0)
2061 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2063 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2067 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
2068 self->icon_title = visible;
2071 void client_update_strut(ObClient *self)
2075 gboolean got = FALSE;
2078 if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
2083 STRUT_PARTIAL_SET(strut,
2084 data[0], data[2], data[1], data[3],
2085 data[4], data[5], data[8], data[9],
2086 data[6], data[7], data[10], data[11]);
2092 OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
2098 /* use the screen's width/height */
2099 a = screen_physical_area_all_monitors();
2101 STRUT_PARTIAL_SET(strut,
2102 data[0], data[2], data[1], data[3],
2103 a->y, a->y + a->height - 1,
2104 a->x, a->x + a->width - 1,
2105 a->y, a->y + a->height - 1,
2106 a->x, a->x + a->width - 1);
2112 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2113 0, 0, 0, 0, 0, 0, 0, 0);
2115 if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
2116 self->strut = strut;
2118 /* updating here is pointless while we're being mapped cuz we're not in
2119 the client list yet */
2121 screen_update_areas();
2125 void client_update_icons(ObClient *self)
2130 guint num_seen; /* number of icons present */
2135 /* grab the server, because we might be setting the window's icon and
2136 we don't want them to set it in between and we overwrite their own
2140 if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
2141 /* figure out how many valid icons are in here */
2144 while (i + 2 < num) { /* +2 is to make sure there is a w and h */
2147 /* watch for the data being too small for the specified size,
2148 or for zero sized icons. */
2149 if (i + w*h > num || w == 0 || h == 0) break;
2151 /* convert it to the right bit order for ObRender */
2152 for (j = 0; j < w*h; ++j)
2154 (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2155 (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
2156 (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
2157 (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
2159 /* is it in the cache? */
2160 img = RrImageCacheFind(ob_rr_icons, &data[i], w, h);
2161 if (img) RrImageRef(img); /* own it */
2166 /* don't bother looping anymore if we already found it in the cache
2167 since we'll just use that! */
2171 /* if it's not in the cache yet, then add it to the cache now.
2172 we have already converted it to the correct bit order above */
2173 if (!img && num_seen > 0) {
2174 img = RrImageNew(ob_rr_icons);
2176 for (j = 0; j < num_seen; ++j) {
2179 RrImageAddPicture(img, &data[i], w, h);
2187 /* if we didn't find an image from the NET_WM_ICON stuff, then try the
2192 if ((hints = XGetWMHints(obt_display, self->window))) {
2193 if (hints->flags & IconPixmapHint) {
2195 obt_display_ignore_errors(TRUE);
2196 xicon = RrPixmapToRGBA(ob_rr_inst,
2198 (hints->flags & IconMaskHint ?
2199 hints->icon_mask : None),
2200 (gint*)&w, (gint*)&h, &data);
2201 obt_display_ignore_errors(FALSE);
2204 if (w > 0 && h > 0) {
2205 /* is this icon in the cache yet? */
2206 img = RrImageCacheFind(ob_rr_icons, data, w, h);
2207 if (img) RrImageRef(img); /* own it */
2209 /* if not, then add it */
2211 img = RrImageNew(ob_rr_icons);
2212 RrImageAddPicture(img, data, w, h);
2223 /* set the client's icons to be whatever we found */
2224 RrImageUnref(self->icon_set);
2225 self->icon_set = img;
2227 /* if the client has no icon at all, then we set a default icon onto it.
2228 but, if it has parents, then one of them will have an icon already
2230 if (!self->icon_set && !self->parents) {
2231 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2232 gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
2234 w = ob_rr_theme->def_win_icon_w;
2235 h = ob_rr_theme->def_win_icon_h;
2236 ldata = g_new(gulong, w*h+2);
2239 for (i = 0; i < w*h; ++i)
2240 ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2241 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2242 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2243 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2244 OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
2246 } else if (self->frame)
2247 /* don't draw the icon empty if we're just setting one now anyways,
2248 we'll get the property change any second */
2249 frame_adjust_icon(self->frame);
2254 void client_update_icon_geometry(ObClient *self)
2259 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2261 if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
2265 /* don't let them set it with an area < 0 */
2266 RECT_SET(self->icon_geometry, data[0], data[1],
2267 MAX(data[2],0), MAX(data[3],0));
2272 void client_update_opacity(ObClient *self)
2274 /* The alpha value is to be copied from the client onto its top-level
2275 window for third-party compositing managers to see */
2276 if (!OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL,
2279 self->alpha = 0xffffffff;
2280 OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY);
2283 OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL,
2287 static void client_get_session_ids(ObClient *self)
2294 if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
2297 /* get the SM_CLIENT_ID */
2300 got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
2302 OBT_PROP_GETS(self->window, SM_CLIENT_ID, locale, &self->sm_client_id);
2304 /* get the WM_CLASS (name and class). make them "" if they are not
2308 got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
2310 got = OBT_PROP_GETSS(self->window, WM_CLASS, locale, &ss);
2314 self->name = g_strdup(ss[0]);
2316 self->class = g_strdup(ss[1]);
2321 if (self->name == NULL) self->name = g_strdup("");
2322 if (self->class == NULL) self->class = g_strdup("");
2324 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2327 got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
2329 got = OBT_PROP_GETS(self->window, WM_WINDOW_ROLE, locale, &s);
2334 self->role = g_strdup("");
2336 /* get the WM_COMMAND */
2340 got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
2342 got = OBT_PROP_GETSS(self->window, WM_COMMAND, locale, &ss);
2345 /* merge/mash them all together */
2346 gchar *merge = NULL;
2349 for (i = 0; ss[i]; ++i) {
2352 merge = g_strconcat(merge, ss[i], NULL);
2354 merge = g_strconcat(ss[i], NULL);
2359 self->wm_command = merge;
2362 /* get the WM_CLIENT_MACHINE */
2365 got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
2367 got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, locale, &s);
2370 gchar localhost[128];
2373 gethostname(localhost, 127);
2374 localhost[127] = '\0';
2375 if (strcmp(localhost, s) != 0)
2376 self->client_machine = s;
2380 /* see if it has the PID set too (the PID requires that the
2381 WM_CLIENT_MACHINE be set) */
2382 if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
2387 /*! Save the properties used for app matching rules, as seen by Openbox when
2388 the window mapped, so that users can still access them later if the app
2390 static void client_save_app_rule_values(ObClient *self)
2394 OBT_PROP_SETS(self->window, OB_APP_ROLE, utf8, self->role);
2395 OBT_PROP_SETS(self->window, OB_APP_NAME, utf8, self->name);
2396 OBT_PROP_SETS(self->window, OB_APP_CLASS, utf8, self->class);
2397 OBT_PROP_SETS(self->window, OB_APP_TITLE, utf8, self->original_title);
2399 switch (self->type) {
2400 case OB_CLIENT_TYPE_NORMAL:
2401 type = "normal"; break;
2402 case OB_CLIENT_TYPE_DIALOG:
2403 type = "dialog"; break;
2404 case OB_CLIENT_TYPE_UTILITY:
2405 type = "utility"; break;
2406 case OB_CLIENT_TYPE_MENU:
2407 type = "menu"; break;
2408 case OB_CLIENT_TYPE_TOOLBAR:
2409 type = "toolbar"; break;
2410 case OB_CLIENT_TYPE_SPLASH:
2411 type = "splash"; break;
2412 case OB_CLIENT_TYPE_DESKTOP:
2413 type = "desktop"; break;
2414 case OB_CLIENT_TYPE_DOCK:
2415 type = "dock"; break;
2417 OBT_PROP_SETS(self->window, OB_APP_TYPE, utf8, type);
2420 static void client_change_wm_state(ObClient *self)
2425 old = self->wmstate;
2427 if (self->shaded || self->iconic ||
2428 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2430 self->wmstate = IconicState;
2432 self->wmstate = NormalState;
2434 if (old != self->wmstate) {
2435 OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
2436 self->wmstate, 1, 0, 0, 0);
2438 state[0] = self->wmstate;
2440 OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
2444 static void client_change_state(ObClient *self)
2446 gulong netstate[12];
2451 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2453 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2455 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2456 if (self->skip_taskbar)
2457 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2458 if (self->skip_pager)
2459 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2460 if (self->fullscreen)
2461 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2463 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2465 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2467 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2469 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2470 if (self->demands_attention)
2471 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2472 if (self->undecorated)
2473 netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2474 OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
2477 frame_adjust_state(self->frame);
2480 ObClient *client_search_focus_tree(ObClient *self)
2485 for (it = self->transients; it; it = g_slist_next(it)) {
2486 if (client_focused(it->data)) return it->data;
2487 if ((ret = client_search_focus_tree(it->data))) return ret;
2492 ObClient *client_search_focus_tree_full(ObClient *self)
2494 if (self->parents) {
2497 for (it = self->parents; it; it = g_slist_next(it)) {
2498 ObClient *c = it->data;
2499 if ((c = client_search_focus_tree_full(c))) return c;
2505 /* this function checks the whole tree, the client_search_focus_tree
2506 does not, so we need to check this window */
2507 if (client_focused(self))
2509 return client_search_focus_tree(self);
2513 ObClient *client_search_focus_group_full(ObClient *self)
2518 for (it = self->group->members; it; it = g_slist_next(it)) {
2519 ObClient *c = it->data;
2521 if (client_focused(c)) return c;
2522 if ((c = client_search_focus_tree(it->data))) return c;
2525 if (client_focused(self)) return self;
2529 gboolean client_has_parent(ObClient *self)
2531 return self->parents != NULL;
2534 gboolean client_is_oldfullscreen(const ObClient *self,
2537 const Rect *monitor, *allmonitors;
2539 /* No decorations and fills the monitor = oldskool fullscreen.
2540 But not for maximized windows.
2543 if (self->decorations || self->max_horz || self->max_vert) return FALSE;
2545 monitor = screen_physical_area_monitor(screen_find_monitor(area));
2546 allmonitors = screen_physical_area_all_monitors();
2548 return (RECT_EQUAL(*area, *monitor) ||
2549 RECT_EQUAL(*area, *allmonitors));
2552 static ObStackingLayer calc_layer(ObClient *self)
2556 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2557 l = OB_STACKING_LAYER_DESKTOP;
2558 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2559 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2560 else l = OB_STACKING_LAYER_ABOVE;
2562 else if ((self->fullscreen ||
2563 client_is_oldfullscreen(self, &self->area)) &&
2564 /* you are fullscreen while you or your children are focused.. */
2565 (client_focused(self) || client_search_focus_tree(self) ||
2566 /* you can be fullscreen if you're on another desktop */
2567 (self->desktop != screen_desktop &&
2568 self->desktop != DESKTOP_ALL) ||
2569 /* and you can also be fullscreen if the focused client is on
2570 another monitor, or nothing else is focused */
2572 client_monitor(focus_client) != client_monitor(self))))
2573 l = OB_STACKING_LAYER_FULLSCREEN;
2574 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2575 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2576 else l = OB_STACKING_LAYER_NORMAL;
2581 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2582 ObStackingLayer min)
2584 ObStackingLayer old, own;
2588 own = calc_layer(self);
2589 self->layer = MAX(own, min);
2591 if (self->layer != old) {
2592 stacking_remove(CLIENT_AS_WINDOW(self));
2593 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2596 /* we've been restacked */
2597 self->visited = TRUE;
2599 for (it = self->transients; it; it = g_slist_next(it))
2600 client_calc_layer_recursive(it->data, orig,
2604 static void client_calc_layer_internal(ObClient *self)
2608 /* transients take on the layer of their parents */
2609 sit = client_search_all_top_parents(self);
2611 for (; sit; sit = g_slist_next(sit))
2612 client_calc_layer_recursive(sit->data, self, 0);
2615 void client_calc_layer(ObClient *self)
2619 /* skip over stuff above fullscreen layer */
2620 for (it = stacking_list; it; it = g_list_next(it))
2621 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2623 /* find the windows in the fullscreen layer, and mark them not-visited */
2624 for (; it; it = g_list_next(it)) {
2625 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2626 else if (WINDOW_IS_CLIENT(it->data))
2627 WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2630 client_calc_layer_internal(self);
2632 /* skip over stuff above fullscreen layer */
2633 for (it = stacking_list; it; it = g_list_next(it))
2634 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2636 /* now recalc any windows in the fullscreen layer which have not
2637 had their layer recalced already */
2638 for (; it; it = g_list_next(it)) {
2639 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2640 else if (WINDOW_IS_CLIENT(it->data) &&
2641 !WINDOW_AS_CLIENT(it->data)->visited)
2642 client_calc_layer_internal(it->data);
2646 gboolean client_should_show(ObClient *self)
2650 if (client_normal(self) && screen_showing_desktop)
2652 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2658 gboolean client_show(ObClient *self)
2660 gboolean show = FALSE;
2662 if (client_should_show(self)) {
2663 /* replay pending pointer event before showing the window, in case it
2664 should be going to something under the window */
2665 mouse_replay_pointer();
2667 frame_show(self->frame);
2670 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2671 it needs to be in IconicState. This includes when it is on another
2674 client_change_wm_state(self);
2679 gboolean client_hide(ObClient *self)
2681 gboolean hide = FALSE;
2683 if (!client_should_show(self)) {
2684 /* We don't need to ignore enter events here.
2685 The window can hide/iconify in 3 different ways:
2686 1 - through an x message. in this case we ignore all enter events
2687 caused by responding to the x message (unless underMouse)
2688 2 - by a keyboard action. in this case we ignore all enter events
2689 caused by the action
2690 3 - by a mouse action. in this case they are doing stuff with the
2691 mouse and focus _should_ move.
2693 Also in action_end, we simulate an enter event that can't be ignored
2694 so trying to ignore them is futile in case 3 anyways
2697 /* replay pending pointer event before hiding the window, in case it
2698 should be going to the window */
2699 mouse_replay_pointer();
2701 frame_hide(self->frame);
2704 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2705 it needs to be in IconicState. This includes when it is on another
2708 client_change_wm_state(self);
2713 void client_showhide(ObClient *self)
2715 if (!client_show(self))
2719 gboolean client_normal(ObClient *self) {
2720 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2721 self->type == OB_CLIENT_TYPE_DOCK ||
2722 self->type == OB_CLIENT_TYPE_SPLASH);
2725 gboolean client_helper(ObClient *self)
2727 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2728 self->type == OB_CLIENT_TYPE_MENU ||
2729 self->type == OB_CLIENT_TYPE_TOOLBAR);
2732 gboolean client_mouse_focusable(ObClient *self)
2734 return !(self->type == OB_CLIENT_TYPE_MENU ||
2735 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2736 self->type == OB_CLIENT_TYPE_SPLASH ||
2737 self->type == OB_CLIENT_TYPE_DOCK);
2740 gboolean client_enter_focusable(ObClient *self)
2742 /* you can focus desktops but it shouldn't on enter */
2743 return (client_mouse_focusable(self) &&
2744 self->type != OB_CLIENT_TYPE_DESKTOP);
2747 static void client_apply_startup_state(ObClient *self,
2748 gint x, gint y, gint w, gint h)
2750 /* save the states that we are going to apply */
2751 gboolean iconic = self->iconic;
2752 gboolean fullscreen = self->fullscreen;
2753 gboolean undecorated = self->undecorated;
2754 gboolean shaded = self->shaded;
2755 gboolean demands_attention = self->demands_attention;
2756 gboolean max_horz = self->max_horz;
2757 gboolean max_vert = self->max_vert;
2761 /* turn them all off in the client, so they won't affect the window
2763 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2764 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2766 /* move the client to its placed position, or it it's already there,
2767 generate a ConfigureNotify telling the client where it is.
2769 do this after adjusting the frame. otherwise it gets all weird and
2770 clients don't work right
2772 do this before applying the states so they have the correct
2773 pre-max/pre-fullscreen values
2775 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2776 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2777 self->window, x, y, w, h);
2778 /* save the area, and make it where it should be for the premax stuff */
2779 oldarea = self->area;
2780 RECT_SET(self->area, x, y, w, h);
2782 /* apply the states. these are in a carefully crafted order.. */
2785 client_iconify(self, TRUE, FALSE, TRUE);
2787 client_set_undecorated(self, TRUE);
2789 client_shade(self, TRUE);
2790 if (demands_attention)
2791 client_hilite(self, TRUE);
2793 if (max_vert && max_horz)
2794 client_maximize(self, TRUE, 0);
2796 client_maximize(self, TRUE, 2);
2798 client_maximize(self, TRUE, 1);
2800 /* fullscreen removes the ability to apply other states */
2802 client_fullscreen(self, TRUE);
2804 /* if the window hasn't been configured yet, then do so now, in fact the
2805 x,y,w,h may _not_ be the same as the area rect, which can end up
2806 meaning that the client isn't properly moved/resized by the fullscreen
2808 pho can cause this because it maps at size of the screen but not 0,0
2809 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2810 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2811 cuz thats where the pre-fullscreen will be. however the actual area is
2812 not, so this needs to be called even if we have fullscreened/maxed
2814 self->area = oldarea;
2815 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2817 /* set the desktop hint, to make sure that it always exists */
2818 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
2820 /* nothing to do for the other states:
2829 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2831 /* these should be the current values. this is for when you're not moving,
2833 g_assert(*x == self->area.x);
2834 g_assert(oldw == self->area.width);
2837 switch (self->gravity) {
2839 case NorthWestGravity:
2841 case SouthWestGravity:
2848 *x -= (neww - oldw) / 2;
2850 case NorthEastGravity:
2852 case SouthEastGravity:
2858 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2860 /* these should be the current values. this is for when you're not moving,
2862 g_assert(*y == self->area.y);
2863 g_assert(oldh == self->area.height);
2866 switch (self->gravity) {
2868 case NorthWestGravity:
2870 case NorthEastGravity:
2877 *y -= (newh - oldh) / 2;
2879 case SouthWestGravity:
2881 case SouthEastGravity:
2887 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2888 gint *logicalw, gint *logicalh,
2891 Rect desired = {*x, *y, *w, *h};
2892 frame_rect_to_frame(self->frame, &desired);
2894 /* XXX make this call a different function that returns frame dimensions
2895 without changing the actual frame's state! Otherwise calling
2896 this function without calling client_configure leaves the frame in
2897 a totally bogus state ! */
2899 /* make the frame recalculate its dimensions n shit without changing
2900 anything visible for real, this way the constraints below can work with
2901 the updated frame dimensions. */
2902 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2904 /* gets the frame's position */
2905 frame_client_gravity(self->frame, x, y);
2907 /* these positions are frame positions, not client positions */
2909 /* set the size and position if fullscreen */
2910 if (self->fullscreen) {
2914 i = screen_find_monitor(&desired);
2915 a = screen_physical_area_monitor(i);
2922 user = FALSE; /* ignore if the client can't be moved/resized when it
2924 } else if (self->max_horz || self->max_vert) {
2928 /* use all possible struts when maximizing to the full screen */
2929 i = screen_find_monitor(&desired);
2930 a = screen_area(self->desktop, i,
2931 (self->max_horz && self->max_vert ? NULL : &desired));
2933 /* set the size and position if maximized */
2934 if (self->max_horz) {
2936 *w = a->width - self->frame->size.left - self->frame->size.right;
2938 if (self->max_vert) {
2940 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2943 user = FALSE; /* ignore if the client can't be moved/resized when it
2946 g_slice_free(Rect, a);
2949 /* gets the client's position */
2950 frame_frame_gravity(self->frame, x, y);
2952 /* work within the preferred sizes given by the window, these may have
2953 changed rather than it's requested width and height, so always run
2954 through this code */
2956 gint basew, baseh, minw, minh;
2957 gint incw, inch, maxw, maxh;
2958 gfloat minratio, maxratio;
2960 incw = self->size_inc.width;
2961 inch = self->size_inc.height;
2962 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2963 0 : self->min_ratio;
2964 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2965 0 : self->max_ratio;
2967 /* base size is substituted with min size if not specified */
2968 if (self->base_size.width >= 0 || self->base_size.height >= 0) {
2969 basew = self->base_size.width;
2970 baseh = self->base_size.height;
2972 basew = self->min_size.width;
2973 baseh = self->min_size.height;
2975 /* min size is substituted with base size if not specified */
2976 if (self->min_size.width || self->min_size.height) {
2977 minw = self->min_size.width;
2978 minh = self->min_size.height;
2980 minw = self->base_size.width;
2981 minh = self->base_size.height;
2984 /* This comment is no longer true */
2985 /* if this is a user-requested resize, then check against min/max
2988 /* smaller than min size or bigger than max size? */
2989 if (*w > self->max_size.width) *w = self->max_size.width;
2990 if (*w < minw) *w = minw;
2991 if (*h > self->max_size.height) *h = self->max_size.height;
2992 if (*h < minh) *h = minh;
2997 /* the sizes to used for maximized */
3001 /* keep to the increments */
3005 /* you cannot resize to nothing */
3006 if (basew + *w < 1) *w = 1 - basew;
3007 if (baseh + *h < 1) *h = 1 - baseh;
3009 /* save the logical size */
3010 *logicalw = incw > 1 ? *w : *w + basew;
3011 *logicalh = inch > 1 ? *h : *h + baseh;
3016 /* if maximized/fs then don't use the size increments */
3017 if (self->fullscreen || self->max_horz) *w = maxw;
3018 if (self->fullscreen || self->max_vert) *h = maxh;
3023 /* adjust the height to match the width for the aspect ratios.
3024 for this, min size is not substituted for base size ever. */
3025 *w -= self->base_size.width;
3026 *h -= self->base_size.height;
3029 if (*h * minratio > *w) {
3030 *h = (gint)(*w / minratio);
3032 /* you cannot resize to nothing */
3035 *w = (gint)(*h * minratio);
3039 if (*h * maxratio < *w) {
3040 *h = (gint)(*w / maxratio);
3042 /* you cannot resize to nothing */
3045 *w = (gint)(*h * minratio);
3049 *w += self->base_size.width;
3050 *h += self->base_size.height;
3053 /* these override the above states! if you cant move you can't move! */
3055 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3059 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3060 *w = self->area.width;
3061 *h = self->area.height;
3069 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3070 gboolean user, gboolean final, gboolean force_reply)
3072 Rect oldframe, oldclient;
3073 gboolean send_resize_client;
3074 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3075 gboolean fmoved, fresized;
3076 const guint fdecor = self->frame->decorations;
3077 const gboolean fhorz = self->frame->max_horz;
3078 const gboolean fvert = self->frame->max_vert;
3079 const gboolean fshaded = self->frame->shaded;
3080 gint logicalw, logicalh;
3082 /* find the new x, y, width, and height (and logical size) */
3083 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3085 /* set the logical size if things changed */
3086 if (!(w == self->area.width && h == self->area.height))
3087 SIZE_SET(self->logical_size, logicalw, logicalh);
3089 /* figure out if we moved or resized or what */
3090 moved = (x != self->area.x || y != self->area.y);
3091 resized = (w != self->area.width || h != self->area.height);
3093 oldframe = self->frame->area;
3094 oldclient = self->area;
3095 RECT_SET(self->area, x, y, w, h);
3097 /* for app-requested resizes, always resize if 'resized' is true.
3098 for user-requested ones, only resize if final is true, or when
3099 resizing in redraw mode */
3100 send_resize_client = ((!user && resized) ||
3102 (resized && config_resize_redraw))));
3104 /* if the client is enlarging, then resize the client before the frame */
3105 if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
3106 XMoveResizeWindow(obt_display, self->window,
3107 self->frame->size.left, self->frame->size.top,
3108 MAX(w, oldclient.width), MAX(h, oldclient.height));
3109 frame_adjust_client_area(self->frame);
3112 /* find the frame's dimensions and move/resize it */
3116 /* if decorations changed, then readjust everything for the frame */
3117 if (self->decorations != fdecor ||
3118 self->max_horz != fhorz || self->max_vert != fvert)
3120 fmoved = fresized = TRUE;
3122 if (self->shaded != fshaded)
3125 /* adjust the frame */
3126 if (fmoved || fresized) {
3127 gulong ignore_start;
3129 ignore_start = event_start_ignore_all_enters();
3131 /* replay pending pointer event before move the window, in case it
3132 would change what window gets the event */
3133 mouse_replay_pointer();
3135 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
3138 event_end_ignore_all_enters(ignore_start);
3141 if (!user || final) {
3142 gint oldrx = self->root_pos.x;
3143 gint oldry = self->root_pos.y;
3144 /* we have reset the client to 0 border width, so don't include
3145 it in these coords */
3146 POINT_SET(self->root_pos,
3147 self->frame->area.x + self->frame->size.left -
3149 self->frame->area.y + self->frame->size.top -
3150 self->border_width);
3151 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3155 /* This is kinda tricky and should not be changed.. let me explain!
3157 When user = FALSE, then the request is coming from the application
3158 itself, and we are more strict about when to send a synthetic
3159 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3160 in this case (or send one if force_reply is true)
3162 When user = TRUE, then the request is coming from "us", like when we
3163 maximize a window or something. In this case we are more lenient. We
3164 used to follow the same rules as above, but _Java_ Swing can't handle
3165 this. So just to appease Swing, when user = TRUE, we always send
3166 a synthetic ConfigureNotify to give the window its root coordinates.
3167 Lastly, if force_reply is TRUE, we always send a
3168 ConfigureNotify, which is needed during a resize with XSYNCronization.
3170 if ((!user && !resized && (rootmoved || force_reply)) ||
3171 (user && ((!resized && force_reply) || (final && rootmoved))))
3175 event.type = ConfigureNotify;
3176 event.xconfigure.display = obt_display;
3177 event.xconfigure.event = self->window;
3178 event.xconfigure.window = self->window;
3180 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3181 self->title, self->root_pos.x, self->root_pos.y, w, h);
3183 /* root window real coords */
3184 event.xconfigure.x = self->root_pos.x;
3185 event.xconfigure.y = self->root_pos.y;
3186 event.xconfigure.width = w;
3187 event.xconfigure.height = h;
3188 event.xconfigure.border_width = self->border_width;
3189 event.xconfigure.above = None;
3190 event.xconfigure.override_redirect = FALSE;
3191 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3192 FALSE, StructureNotifyMask, &event);
3195 /* if the client is shrinking, then resize the frame before the client.
3197 both of these resize sections may run, because the top one only resizes
3198 in the direction that is growing
3200 if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
3202 frame_adjust_client_area(self->frame);
3203 XMoveResizeWindow(obt_display, self->window,
3204 self->frame->size.left, self->frame->size.top, w, h);
3207 XFlush(obt_display);
3209 /* if it moved between monitors, then this can affect the stacking
3210 layer of this window or others - for fullscreen windows.
3211 also if it changed to/from oldschool fullscreen then its layer may
3214 watch out tho, don't try change stacking stuff if the window is no
3215 longer being managed !
3217 if (self->managed &&
3218 (screen_find_monitor(&self->frame->area) !=
3219 screen_find_monitor(&oldframe) ||
3220 (final && (client_is_oldfullscreen(self, &oldclient) !=
3221 client_is_oldfullscreen(self, &self->area)))))
3223 client_calc_layer(self);
3227 void client_fullscreen(ObClient *self, gboolean fs)
3231 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3232 self->fullscreen == fs) return; /* already done */
3234 self->fullscreen = fs;
3235 client_change_state(self); /* change the state hints on the client */
3238 self->pre_fullscreen_area = self->area;
3239 self->pre_fullscreen_max_horz = self->max_horz;
3240 self->pre_fullscreen_max_vert = self->max_vert;
3242 /* if the window is maximized, its area isn't all that meaningful.
3243 save its premax area instead. */
3244 if (self->max_horz) {
3245 self->pre_fullscreen_area.x = self->pre_max_area.x;
3246 self->pre_fullscreen_area.width = self->pre_max_area.width;
3248 if (self->max_vert) {
3249 self->pre_fullscreen_area.y = self->pre_max_area.y;
3250 self->pre_fullscreen_area.height = self->pre_max_area.height;
3253 /* these will help configure_full figure out where to fullscreen
3257 w = self->area.width;
3258 h = self->area.height;
3260 g_assert(self->pre_fullscreen_area.width > 0 &&
3261 self->pre_fullscreen_area.height > 0);
3263 self->max_horz = self->pre_fullscreen_max_horz;
3264 self->max_vert = self->pre_fullscreen_max_vert;
3265 if (self->max_horz) {
3266 self->pre_max_area.x = self->pre_fullscreen_area.x;
3267 self->pre_max_area.width = self->pre_fullscreen_area.width;
3269 if (self->max_vert) {
3270 self->pre_max_area.y = self->pre_fullscreen_area.y;
3271 self->pre_max_area.height = self->pre_fullscreen_area.height;
3274 x = self->pre_fullscreen_area.x;
3275 y = self->pre_fullscreen_area.y;
3276 w = self->pre_fullscreen_area.width;
3277 h = self->pre_fullscreen_area.height;
3278 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3281 ob_debug("Window %s going fullscreen (%d)",
3282 self->title, self->fullscreen);
3285 /* make sure the window is on some monitor */
3286 client_find_onscreen(self, &x, &y, w, h, FALSE);
3289 client_setup_decor_and_functions(self, FALSE);
3290 client_move_resize(self, x, y, w, h);
3292 /* and adjust our layer/stacking. do this after resizing the window,
3293 and applying decorations, because windows which fill the screen are
3294 considered "fullscreen" and it affects their layer */
3295 client_calc_layer(self);
3298 /* try focus us when we go into fullscreen mode */
3303 static void client_iconify_recursive(ObClient *self,
3304 gboolean iconic, gboolean curdesk,
3305 gboolean hide_animation)
3308 gboolean changed = FALSE;
3310 if (self->iconic != iconic) {
3311 ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3315 /* don't let non-normal windows iconify along with their parents
3317 if (client_normal(self)) {
3318 self->iconic = iconic;
3320 /* update the focus lists.. iconic windows go to the bottom of
3321 the list. this will also call focus_cycle_addremove(). */
3322 focus_order_to_bottom(self);
3327 self->iconic = iconic;
3329 if (curdesk && self->desktop != screen_desktop &&
3330 self->desktop != DESKTOP_ALL)
3331 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3333 /* this puts it after the current focused window, this will
3334 also cause focus_cycle_addremove() to be called for the
3336 focus_order_like_new(self);
3343 client_change_state(self);
3344 if (config_animate_iconify && !hide_animation)
3345 frame_begin_iconify_animation(self->frame, iconic);
3346 /* do this after starting the animation so it doesn't flash */
3347 client_showhide(self);
3350 /* iconify all direct transients, and deiconify all transients
3352 for (it = self->transients; it; it = g_slist_next(it))
3353 if (it->data != self)
3354 if (client_is_direct_child(self, it->data) || !iconic)
3355 client_iconify_recursive(it->data, iconic, curdesk,
3359 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3360 gboolean hide_animation)
3362 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3363 /* move up the transient chain as far as possible first */
3364 self = client_search_top_direct_parent(self);
3365 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3369 void client_maximize(ObClient *self, gboolean max, gint dir)
3373 g_assert(dir == 0 || dir == 1 || dir == 2);
3374 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
3376 /* check if already done */
3378 if (dir == 0 && self->max_horz && self->max_vert) return;
3379 if (dir == 1 && self->max_horz) return;
3380 if (dir == 2 && self->max_vert) return;
3382 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3383 if (dir == 1 && !self->max_horz) return;
3384 if (dir == 2 && !self->max_vert) return;
3387 /* these will help configure_full figure out which screen to fill with
3391 w = self->area.width;
3392 h = self->area.height;
3395 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3396 RECT_SET(self->pre_max_area,
3397 self->area.x, self->pre_max_area.y,
3398 self->area.width, self->pre_max_area.height);
3400 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3401 RECT_SET(self->pre_max_area,
3402 self->pre_max_area.x, self->area.y,
3403 self->pre_max_area.width, self->area.height);
3406 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3407 g_assert(self->pre_max_area.width > 0);
3409 x = self->pre_max_area.x;
3410 w = self->pre_max_area.width;
3412 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3413 0, self->pre_max_area.height);
3415 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3416 g_assert(self->pre_max_area.height > 0);
3418 y = self->pre_max_area.y;
3419 h = self->pre_max_area.height;
3421 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3422 self->pre_max_area.width, 0);
3426 if (dir == 0 || dir == 1) /* horz */
3427 self->max_horz = max;
3428 if (dir == 0 || dir == 2) /* vert */
3429 self->max_vert = max;
3432 /* make sure the window is on some monitor */
3433 client_find_onscreen(self, &x, &y, w, h, FALSE);
3436 client_change_state(self); /* change the state hints on the client */
3438 client_setup_decor_and_functions(self, FALSE);
3439 client_move_resize(self, x, y, w, h);
3442 void client_shade(ObClient *self, gboolean shade)
3444 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3445 shade) || /* can't shade */
3446 self->shaded == shade) return; /* already done */
3448 self->shaded = shade;
3449 client_change_state(self);
3450 client_change_wm_state(self); /* the window is being hidden/shown */
3451 /* resize the frame to just the titlebar */
3452 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3455 static void client_ping_event(ObClient *self, gboolean dead)
3457 if (self->not_responding != dead) {
3458 self->not_responding = dead;
3459 client_update_title(self);
3462 /* the client isn't responding, so ask to kill it */
3463 client_prompt_kill(self);
3465 /* it came back to life ! */
3467 if (self->kill_prompt) {
3468 prompt_unref(self->kill_prompt);
3469 self->kill_prompt = NULL;
3472 self->kill_level = 0;
3477 void client_close(ObClient *self)
3479 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3481 /* if closing an internal obprompt, that is just cancelling it */
3483 prompt_cancel(self->prompt);
3487 /* in the case that the client provides no means to requesting that it
3488 close, we just kill it */
3489 if (!self->delete_window)
3490 /* don't use client_kill(), we should only kill based on PID in
3491 response to a lack of PING replies */
3492 XKillClient(obt_display, self->window);
3494 /* request the client to close with WM_DELETE_WINDOW */
3495 OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
3496 OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
3497 0, 0, 0, NoEventMask);
3499 /* we're trying to close the window, so see if it is responding. if it
3500 is not, then we will let them kill the window */
3502 ping_start(self, client_ping_event);
3504 /* if we already know the window isn't responding (maybe they clicked
3505 no in the kill dialog but it hasn't come back to life), then show
3507 if (self->not_responding)
3508 client_prompt_kill(self);
3512 #define OB_KILL_RESULT_NO 0
3513 #define OB_KILL_RESULT_YES 1
3515 static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
3517 ObClient *self = data;
3519 if (result == OB_KILL_RESULT_YES)
3521 return TRUE; /* call the cleanup func */
3524 static void client_kill_cleanup(ObPrompt *p, gpointer data)
3526 ObClient *self = data;
3528 g_assert(p == self->kill_prompt);
3530 prompt_unref(self->kill_prompt);
3531 self->kill_prompt = NULL;
3534 static void client_prompt_kill(ObClient *self)
3536 /* check if we're already prompting */
3537 if (!self->kill_prompt) {
3538 ObPromptAnswer answers[] = {
3539 { 0, OB_KILL_RESULT_NO },
3540 { 0, OB_KILL_RESULT_YES }
3543 const gchar *y, *title;
3545 title = self->original_title;
3546 if (title[0] == '\0') {
3547 /* empty string, so use its parent */
3548 ObClient *p = client_search_top_direct_parent(self);
3549 if (p) title = p->original_title;
3552 if (client_on_localhost(self)) {
3555 if (self->kill_level == 0)
3561 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3563 y = _("End Process");
3567 (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3569 y = _("Disconnect");
3571 /* set the dialog buttons' text */
3572 answers[0].text = _("Cancel"); /* "no" */
3573 answers[1].text = y; /* "yes" */
3575 self->kill_prompt = prompt_new(m, NULL, answers,
3576 sizeof(answers)/sizeof(answers[0]),
3577 OB_KILL_RESULT_NO, /* default = no */
3578 OB_KILL_RESULT_NO, /* cancel = no */
3579 client_kill_requested,
3580 client_kill_cleanup,
3585 prompt_show(self->kill_prompt, self, TRUE);
3588 void client_kill(ObClient *self)
3590 /* don't kill our own windows */
3591 if (self->prompt) return;
3593 if (client_on_localhost(self) && self->pid) {
3594 /* running on the local host */
3595 if (self->kill_level == 0) {
3596 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3597 self->window, self->pid);
3598 kill(self->pid, SIGTERM);
3601 /* show that we're trying to kill it */
3602 client_update_title(self);
3605 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3606 self->window, self->pid);
3607 kill(self->pid, SIGKILL); /* kill -9 */
3611 /* running on a remote host */
3612 XKillClient(obt_display, self->window);
3616 void client_hilite(ObClient *self, gboolean hilite)
3618 if (self->demands_attention == hilite)
3619 return; /* no change */
3621 /* don't allow focused windows to hilite */
3622 self->demands_attention = hilite && !client_focused(self);
3623 if (self->frame != NULL) { /* if we're mapping, just set the state */
3624 if (self->demands_attention) {
3625 frame_flash_start(self->frame);
3627 /* if the window is on another desktop then raise it and make it
3628 the most recently used window */
3629 if (self->desktop != screen_desktop &&
3630 self->desktop != DESKTOP_ALL)
3632 stacking_raise(CLIENT_AS_WINDOW(self));
3633 focus_order_to_top(self);
3637 frame_flash_stop(self->frame);
3638 client_change_state(self);
3642 static void client_set_desktop_recursive(ObClient *self,
3650 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3652 ob_debug("Setting desktop %u", target+1);
3654 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3656 old = self->desktop;
3657 self->desktop = target;
3658 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
3659 /* the frame can display the current desktop state */
3660 frame_adjust_state(self->frame);
3661 /* 'move' the window to the new desktop */
3665 /* raise if it was not already on the desktop */
3666 if (old != DESKTOP_ALL && !dontraise)
3667 stacking_raise(CLIENT_AS_WINDOW(self));
3668 if (STRUT_EXISTS(self->strut))
3669 screen_update_areas();
3671 /* the new desktop's geometry may be different, so we may need to
3672 resize, for example if we are maximized */
3673 client_reconfigure(self, FALSE);
3675 focus_cycle_addremove(self, FALSE);
3678 /* move all transients */
3679 for (it = self->transients; it; it = g_slist_next(it))
3680 if (it->data != self)
3681 if (client_is_direct_child(self, it->data))
3682 client_set_desktop_recursive(it->data, target,
3683 donthide, dontraise);
3686 void client_set_desktop(ObClient *self, guint target,
3687 gboolean donthide, gboolean dontraise)
3689 self = client_search_top_direct_parent(self);
3690 client_set_desktop_recursive(self, target, donthide, dontraise);
3692 focus_cycle_addremove(NULL, TRUE);
3695 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3697 while (child != parent && (child = client_direct_parent(child)));
3698 return child == parent;
3701 ObClient *client_search_modal_child(ObClient *self)
3706 for (it = self->transients; it; it = g_slist_next(it)) {
3707 ObClient *c = it->data;
3708 if ((ret = client_search_modal_child(c))) return ret;
3709 if (c->modal) return c;
3714 struct ObClientFindDestroyUnmap {
3719 static gboolean find_destroy_unmap(XEvent *e, gpointer data)
3721 struct ObClientFindDestroyUnmap *find = data;
3722 if (e->type == DestroyNotify)
3723 return e->xdestroywindow.window == find->window;
3724 if (e->type == UnmapNotify && e->xunmap.window == find->window)
3725 /* ignore the first $find->ignore_unmaps$ many unmap events */
3726 return --find->ignore_unmaps < 0;
3730 gboolean client_validate(ObClient *self)
3732 struct ObClientFindDestroyUnmap find;
3734 XSync(obt_display, FALSE); /* get all events on the server */
3736 find.window = self->window;
3737 find.ignore_unmaps = self->ignore_unmaps;
3738 if (xqueue_exists_local(find_destroy_unmap, &find))
3744 void client_set_wm_state(ObClient *self, glong state)
3746 if (state == self->wmstate) return; /* no change */
3750 client_iconify(self, TRUE, TRUE, FALSE);
3753 client_iconify(self, FALSE, TRUE, FALSE);
3758 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3760 gboolean shaded = self->shaded;
3761 gboolean fullscreen = self->fullscreen;
3762 gboolean undecorated = self->undecorated;
3763 gboolean max_horz = self->max_horz;
3764 gboolean max_vert = self->max_vert;
3765 gboolean modal = self->modal;
3766 gboolean iconic = self->iconic;
3767 gboolean demands_attention = self->demands_attention;
3768 gboolean above = self->above;
3769 gboolean below = self->below;
3773 if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3774 action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3775 action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3776 /* an invalid action was passed to the client message, ignore it */
3779 for (i = 0; i < 2; ++i) {
3780 Atom state = i == 0 ? data1 : data2;
3782 if (!state) continue;
3784 /* if toggling, then pick whether we're adding or removing */
3785 if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3786 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3788 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3789 value = self->max_vert;
3790 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3791 value = self->max_horz;
3792 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3794 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3795 value = self->skip_taskbar;
3796 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3797 value = self->skip_pager;
3798 else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3799 value = self->iconic;
3800 else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3802 else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3803 value = self->above;
3804 else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3805 value = self->below;
3806 else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3807 value = self->demands_attention;
3808 else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3809 value = undecorated;
3811 g_assert_not_reached();
3812 action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3813 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3816 value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
3817 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3819 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3821 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3823 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3825 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3826 self->skip_taskbar = value;
3827 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3828 self->skip_pager = value;
3829 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3831 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3833 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3835 /* only unset below when setting above, otherwise you can't get to
3839 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3840 /* and vice versa */
3844 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3845 demands_attention = value;
3846 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3847 undecorated = value;
3851 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3852 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3854 if (max_horz == max_vert) { /* both going the same way */
3855 client_maximize(self, max_horz, 0);
3857 client_maximize(self, max_horz, 1);
3858 client_maximize(self, max_vert, 2);
3862 if (max_horz != self->max_horz)
3863 client_maximize(self, max_horz, 1);
3865 client_maximize(self, max_vert, 2);
3868 /* change fullscreen state before shading, as it will affect if the window
3870 if (fullscreen != self->fullscreen)
3871 client_fullscreen(self, fullscreen);
3872 if (shaded != self->shaded)
3873 client_shade(self, shaded);
3874 if (undecorated != self->undecorated)
3875 client_set_undecorated(self, undecorated);
3876 if (above != self->above || below != self->below) {
3877 self->above = above;
3878 self->below = below;
3879 client_calc_layer(self);
3882 if (modal != self->modal) {
3883 self->modal = modal;
3884 /* when a window changes modality, then its stacking order with its
3885 transients needs to change */
3886 stacking_raise(CLIENT_AS_WINDOW(self));
3888 /* it also may get focused. if something is focused that shouldn't
3889 be focused anymore, then move the focus */
3890 if (focus_client && client_focus_target(focus_client) != focus_client)
3891 client_focus(focus_client);
3894 if (iconic != self->iconic)
3895 client_iconify(self, iconic, FALSE, FALSE);
3897 if (demands_attention != self->demands_attention)
3898 client_hilite(self, demands_attention);
3900 client_change_state(self); /* change the hint to reflect these changes */
3902 focus_cycle_addremove(self, TRUE);
3905 ObClient *client_focus_target(ObClient *self)
3907 ObClient *child = NULL;
3909 child = client_search_modal_child(self);
3910 if (child) return child;
3914 gboolean client_can_focus(ObClient *self)
3916 /* choose the correct target */
3917 self = client_focus_target(self);
3919 if (!self->frame->visible)
3922 if (!(self->can_focus || self->focus_notify))
3928 gboolean client_focus(ObClient *self)
3930 if (!client_validate(self)) return FALSE;
3932 /* we might not focus this window, so if we have modal children which would
3933 be focused instead, bring them to this desktop */
3934 client_bring_modal_windows(self);
3936 /* choose the correct target */
3937 self = client_focus_target(self);
3939 if (!client_can_focus(self)) {
3940 ob_debug_type(OB_DEBUG_FOCUS,
3941 "Client %s can't be focused", self->title);
3945 ob_debug_type(OB_DEBUG_FOCUS,
3946 "Focusing client \"%s\" (0x%x) at time %u",
3947 self->title, self->window, event_time());
3949 /* if using focus_delay, stop the timer now so that focus doesn't
3951 event_halt_focus_delay();
3953 obt_display_ignore_errors(TRUE);
3955 if (self->can_focus) {
3956 /* This can cause a BadMatch error with CurrentTime, or if an app
3957 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3958 XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
3962 if (self->focus_notify) {
3964 ce.xclient.type = ClientMessage;
3965 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
3966 ce.xclient.display = obt_display;
3967 ce.xclient.window = self->window;
3968 ce.xclient.format = 32;
3969 ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
3970 ce.xclient.data.l[1] = event_time();
3971 ce.xclient.data.l[2] = 0l;
3972 ce.xclient.data.l[3] = 0l;
3973 ce.xclient.data.l[4] = 0l;
3974 XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
3977 obt_display_ignore_errors(FALSE);
3979 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
3980 obt_display_error_occured);
3981 return !obt_display_error_occured;
3984 static void client_present(ObClient *self, gboolean here, gboolean raise,
3987 if (client_normal(self) && screen_showing_desktop)
3988 screen_show_desktop(FALSE, self);
3990 client_iconify(self, FALSE, here, FALSE);
3991 if (self->desktop != DESKTOP_ALL &&
3992 self->desktop != screen_desktop)
3995 client_set_desktop(self, screen_desktop, FALSE, TRUE);
3997 screen_set_desktop(self->desktop, FALSE);
3998 } else if (!self->frame->visible)
3999 /* if its not visible for other reasons, then don't mess
4002 if (self->shaded && unshade)
4003 client_shade(self, FALSE);
4005 stacking_raise(CLIENT_AS_WINDOW(self));
4010 /* this function exists to map to the net_active_window message in the ewmh */
4011 void client_activate(ObClient *self, gboolean desktop,
4012 gboolean here, gboolean raise,
4013 gboolean unshade, gboolean user)
4015 if ((user && (desktop ||
4016 self->desktop == DESKTOP_ALL ||
4017 self->desktop == screen_desktop)) ||
4018 client_can_steal_focus(self, desktop, event_time(), CurrentTime))
4020 client_present(self, here, raise, unshade);
4023 client_hilite(self, TRUE);
4026 static void client_bring_windows_recursive(ObClient *self,
4034 for (it = self->transients; it; it = g_slist_next(it))
4035 client_bring_windows_recursive(it->data, desktop,
4036 helpers, modals, iconic);
4038 if (((helpers && client_helper(self)) ||
4039 (modals && self->modal)) &&
4040 ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
4041 (iconic && self->iconic)))
4043 if (iconic && self->iconic)
4044 client_iconify(self, FALSE, TRUE, FALSE);
4046 client_set_desktop(self, desktop, FALSE, FALSE);
4050 void client_bring_helper_windows(ObClient *self)
4052 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
4055 void client_bring_modal_windows(ObClient *self)
4057 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
4060 gboolean client_focused(ObClient *self)
4062 return self == focus_client;
4065 RrImage* client_icon(ObClient *self)
4067 RrImage *ret = NULL;
4070 ret = self->icon_set;
4071 else if (self->parents) {
4073 for (it = self->parents; it && !ret; it = g_slist_next(it))
4074 ret = client_icon(it->data);
4077 ret = client_default_icon;
4081 void client_set_layer(ObClient *self, gint layer)
4085 self->above = FALSE;
4086 } else if (layer == 0) {
4087 self->below = self->above = FALSE;
4089 self->below = FALSE;
4092 client_calc_layer(self);
4093 client_change_state(self); /* reflect this in the state hints */
4096 void client_set_undecorated(ObClient *self, gboolean undecorated)
4098 if (self->undecorated != undecorated &&
4099 /* don't let it undecorate if the function is missing, but let
4101 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4103 self->undecorated = undecorated;
4104 client_setup_decor_and_functions(self, TRUE);
4105 client_change_state(self); /* reflect this in the state hints */
4109 guint client_monitor(ObClient *self)
4111 return screen_find_monitor(&self->frame->area);
4114 ObClient *client_direct_parent(ObClient *self)
4116 if (!self->parents) return NULL;
4117 if (self->transient_for_group) return NULL;
4118 return self->parents->data;
4121 ObClient *client_search_top_direct_parent(ObClient *self)
4124 while ((p = client_direct_parent(self))) self = p;
4128 static GSList *client_search_all_top_parents_internal(ObClient *self,
4130 ObStackingLayer layer)
4135 /* move up the direct transient chain as far as possible */
4136 while ((p = client_direct_parent(self)) &&
4137 (!bylayer || p->layer == layer))
4141 ret = g_slist_prepend(NULL, self);
4143 ret = g_slist_copy(self->parents);
4148 GSList *client_search_all_top_parents(ObClient *self)
4150 return client_search_all_top_parents_internal(self, FALSE, 0);
4153 GSList *client_search_all_top_parents_layer(ObClient *self)
4155 return client_search_all_top_parents_internal(self, TRUE, self->layer);
4158 ObClient *client_search_focus_parent(ObClient *self)
4162 for (it = self->parents; it; it = g_slist_next(it))
4163 if (client_focused(it->data)) return it->data;
4168 ObClient *client_search_focus_parent_full(ObClient *self)
4171 ObClient *ret = NULL;
4173 for (it = self->parents; it; it = g_slist_next(it)) {
4174 if (client_focused(it->data))
4177 ret = client_search_focus_parent_full(it->data);
4183 ObClient *client_search_parent(ObClient *self, ObClient *search)
4187 for (it = self->parents; it; it = g_slist_next(it))
4188 if (it->data == search) return search;
4193 ObClient *client_search_transient(ObClient *self, ObClient *search)
4197 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4198 if (sit->data == search)
4200 if (client_search_transient(sit->data, search))
4206 static void detect_edge(Rect area, ObDirection dir,
4207 gint my_head, gint my_size,
4208 gint my_edge_start, gint my_edge_size,
4209 gint *dest, gboolean *near_edge)
4211 gint edge_start, edge_size, head, tail;
4212 gboolean skip_head = FALSE, skip_tail = FALSE;
4215 case OB_DIRECTION_NORTH:
4216 case OB_DIRECTION_SOUTH:
4217 edge_start = area.x;
4218 edge_size = area.width;
4220 case OB_DIRECTION_EAST:
4221 case OB_DIRECTION_WEST:
4222 edge_start = area.y;
4223 edge_size = area.height;
4226 g_assert_not_reached();
4229 /* do we collide with this window? */
4230 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4231 edge_start, edge_size))
4235 case OB_DIRECTION_NORTH:
4236 head = RECT_BOTTOM(area);
4237 tail = RECT_TOP(area);
4239 case OB_DIRECTION_SOUTH:
4240 head = RECT_TOP(area);
4241 tail = RECT_BOTTOM(area);
4243 case OB_DIRECTION_WEST:
4244 head = RECT_RIGHT(area);
4245 tail = RECT_LEFT(area);
4247 case OB_DIRECTION_EAST:
4248 head = RECT_LEFT(area);
4249 tail = RECT_RIGHT(area);
4252 g_assert_not_reached();
4255 case OB_DIRECTION_NORTH:
4256 case OB_DIRECTION_WEST:
4257 /* check if our window is past the head of this window */
4258 if (my_head <= head + 1)
4260 /* check if our window's tail is past the tail of this window */
4261 if (my_head + my_size - 1 <= tail)
4263 /* check if the head of this window is closer than the previously
4264 chosen edge (take into account that the previously chosen
4265 edge might have been a tail, not a head) */
4266 if (head + (*near_edge ? 0 : my_size) <= *dest)
4268 /* check if the tail of this window is closer than the previously
4269 chosen edge (take into account that the previously chosen
4270 edge might have been a head, not a tail) */
4271 if (tail - (!*near_edge ? 0 : my_size) <= *dest)
4274 case OB_DIRECTION_SOUTH:
4275 case OB_DIRECTION_EAST:
4276 /* check if our window is past the head of this window */
4277 if (my_head >= head - 1)
4279 /* check if our window's tail is past the tail of this window */
4280 if (my_head - my_size + 1 >= tail)
4282 /* check if the head of this window is closer than the previously
4283 chosen edge (take into account that the previously chosen
4284 edge might have been a tail, not a head) */
4285 if (head - (*near_edge ? 0 : my_size) >= *dest)
4287 /* check if the tail of this window is closer than the previously
4288 chosen edge (take into account that the previously chosen
4289 edge might have been a head, not a tail) */
4290 if (tail + (!*near_edge ? 0 : my_size) >= *dest)
4294 g_assert_not_reached();
4297 ob_debug("my head %d size %d", my_head, my_size);
4298 ob_debug("head %d tail %d dest %d", head, tail, *dest);
4300 ob_debug("using near edge %d", head);
4304 else if (!skip_tail) {
4305 ob_debug("using far edge %d", tail);
4311 void client_find_edge_directional(ObClient *self, ObDirection dir,
4312 gint my_head, gint my_size,
4313 gint my_edge_start, gint my_edge_size,
4314 gint *dest, gboolean *near_edge)
4322 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4323 &self->frame->area);
4326 case OB_DIRECTION_NORTH:
4327 edge = RECT_TOP(*a) - 1;
4329 case OB_DIRECTION_SOUTH:
4330 edge = RECT_BOTTOM(*a) + 1;
4332 case OB_DIRECTION_EAST:
4333 edge = RECT_RIGHT(*a) + 1;
4335 case OB_DIRECTION_WEST:
4336 edge = RECT_LEFT(*a) - 1;
4339 g_assert_not_reached();
4341 /* default to the far edge, then narrow it down */
4345 /* search for edges of monitors */
4346 for (i = 0; i < screen_num_monitors; ++i) {
4347 Rect *area = screen_area(self->desktop, i, NULL);
4348 detect_edge(*area, dir, my_head, my_size, my_edge_start,
4349 my_edge_size, dest, near_edge);
4350 g_slice_free(Rect, area);
4353 /* search for edges of clients */
4354 if (((dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH) &&
4356 ((dir == OB_DIRECTION_EAST || dir == OB_DIRECTION_WEST) &&
4359 for (it = client_list; it; it = g_list_next(it)) {
4360 ObClient *cur = it->data;
4362 /* skip windows to not bump into */
4367 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4368 cur->desktop != screen_desktop)
4371 ob_debug("trying window %s", cur->title);
4373 detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
4374 my_edge_size, dest, near_edge);
4376 dock_get_area(&dock_area);
4377 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4378 my_edge_size, dest, near_edge);
4381 g_slice_free(Rect, a);
4384 void client_find_move_directional(ObClient *self, ObDirection dir,
4388 gint e, e_start, e_size;
4392 case OB_DIRECTION_EAST:
4393 head = RECT_RIGHT(self->frame->area);
4394 size = self->frame->area.width;
4395 e_start = RECT_TOP(self->frame->area);
4396 e_size = self->frame->area.height;
4398 case OB_DIRECTION_WEST:
4399 head = RECT_LEFT(self->frame->area);
4400 size = self->frame->area.width;
4401 e_start = RECT_TOP(self->frame->area);
4402 e_size = self->frame->area.height;
4404 case OB_DIRECTION_NORTH:
4405 head = RECT_TOP(self->frame->area);
4406 size = self->frame->area.height;
4407 e_start = RECT_LEFT(self->frame->area);
4408 e_size = self->frame->area.width;
4410 case OB_DIRECTION_SOUTH:
4411 head = RECT_BOTTOM(self->frame->area);
4412 size = self->frame->area.height;
4413 e_start = RECT_LEFT(self->frame->area);
4414 e_size = self->frame->area.width;
4417 g_assert_not_reached();
4420 client_find_edge_directional(self, dir, head, size,
4421 e_start, e_size, &e, &near);
4422 *x = self->frame->area.x;
4423 *y = self->frame->area.y;
4425 case OB_DIRECTION_EAST:
4426 if (near) e -= self->frame->area.width;
4430 case OB_DIRECTION_WEST:
4432 else e -= self->frame->area.width;
4435 case OB_DIRECTION_NORTH:
4437 else e -= self->frame->area.height;
4440 case OB_DIRECTION_SOUTH:
4441 if (near) e -= self->frame->area.height;
4446 g_assert_not_reached();
4448 frame_frame_gravity(self->frame, x, y);
4451 void client_find_resize_directional(ObClient *self, ObDirection side,
4453 gint *x, gint *y, gint *w, gint *h)
4456 gint e, e_start, e_size, delta;
4461 case OB_DIRECTION_EAST:
4462 head = RECT_RIGHT(self->frame->area) +
4463 (self->size_inc.width - 1) * (grow ? 1 : 0);
4464 e_start = RECT_TOP(self->frame->area);
4465 e_size = self->frame->area.height;
4466 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4468 case OB_DIRECTION_WEST:
4469 head = RECT_LEFT(self->frame->area) -
4470 (self->size_inc.width - 1) * (grow ? 1 : 0);
4471 e_start = RECT_TOP(self->frame->area);
4472 e_size = self->frame->area.height;
4473 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4475 case OB_DIRECTION_NORTH:
4476 head = RECT_TOP(self->frame->area) -
4477 (self->size_inc.height - 1) * (grow ? 1 : 0);
4478 e_start = RECT_LEFT(self->frame->area);
4479 e_size = self->frame->area.width;
4480 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4482 case OB_DIRECTION_SOUTH:
4483 head = RECT_BOTTOM(self->frame->area) +
4484 (self->size_inc.height - 1) * (grow ? 1 : 0);
4485 e_start = RECT_LEFT(self->frame->area);
4486 e_size = self->frame->area.width;
4487 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4490 g_assert_not_reached();
4493 ob_debug("head %d dir %d", head, dir);
4494 client_find_edge_directional(self, dir, head, 1,
4495 e_start, e_size, &e, &near);
4496 ob_debug("edge %d", e);
4497 *x = self->frame->area.x;
4498 *y = self->frame->area.y;
4499 *w = self->frame->area.width;
4500 *h = self->frame->area.height;
4502 case OB_DIRECTION_EAST:
4503 if (grow == near) --e;
4504 delta = e - RECT_RIGHT(self->frame->area);
4507 case OB_DIRECTION_WEST:
4508 if (grow == near) ++e;
4509 delta = RECT_LEFT(self->frame->area) - e;
4513 case OB_DIRECTION_NORTH:
4514 if (grow == near) ++e;
4515 delta = RECT_TOP(self->frame->area) - e;
4519 case OB_DIRECTION_SOUTH:
4520 if (grow == near) --e;
4521 delta = e - RECT_BOTTOM(self->frame->area);
4525 g_assert_not_reached();
4527 frame_frame_gravity(self->frame, x, y);
4528 *w -= self->frame->size.left + self->frame->size.right;
4529 *h -= self->frame->size.top + self->frame->size.bottom;
4532 ObClient* client_under_pointer(void)
4536 ObClient *ret = NULL;
4538 if (screen_pointer_pos(&x, &y)) {
4539 for (it = stacking_list; it; it = g_list_next(it)) {
4540 if (WINDOW_IS_CLIENT(it->data)) {
4541 ObClient *c = WINDOW_AS_CLIENT(it->data);
4542 if (c->frame->visible &&
4543 /* check the desktop, this is done during desktop
4544 switching and windows are shown/hidden status is not
4546 (c->desktop == screen_desktop ||
4547 c->desktop == DESKTOP_ALL) &&
4548 /* ignore all animating windows */
4549 !frame_iconify_animating(c->frame) &&
4550 RECT_CONTAINS(c->frame->area, x, y))
4561 gboolean client_has_group_siblings(ObClient *self)
4563 return self->group && self->group->members->next;
4566 /*! Returns TRUE if the client is running on the same machine as Openbox */
4567 gboolean client_on_localhost(ObClient *self)
4569 return self->client_machine == NULL;