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 window_set_abstract(CLIENT_AS_WINDOW(self),
257 &self->frame->window, /* top level window */
258 &self->layer, /* stacking layer */
259 &self->frame->depth); /* window depth */
261 frame_grab_client(self->frame);
263 /* we've grabbed everything and set everything that we need to at mapping
267 /* the session should get the last say though */
268 client_restore_session_state(self);
270 /* tell startup notification that this app started */
271 launch_time = sn_app_started(self->startup_id, self->class, self->name);
273 if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
274 user_time = event_time();
276 /* do this after we have a frame.. it uses the frame to help determine the
277 WM_STATE to apply. */
278 client_change_state(self);
280 /* add ourselves to the focus order */
281 focus_order_add_new(self);
283 /* do this to add ourselves to the stacking list in a non-intrusive way */
284 client_calc_layer(self);
286 /* focus the new window? */
287 if (ob_state() != OB_STATE_STARTING &&
288 (!self->session || self->session->focused) &&
289 /* this means focus=true for window is same as config_focus_new=true */
290 ((config_focus_new || settings->focus == 1) ||
291 client_search_focus_tree_full(self)) &&
292 /* NET_WM_USER_TIME 0 when mapping means don't focus */
294 /* this checks for focus=false for the window */
295 settings->focus != 0 &&
296 focus_valid_target(self, self->desktop,
297 FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
298 settings->focus == 1))
303 /* remove the client's border */
304 XSetWindowBorderWidth(obt_display, self->window, 0);
306 /* adjust the frame to the client's size before showing or placing
308 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
309 frame_adjust_client_area(self->frame);
311 /* where the frame was placed is where the window was originally */
314 /* figure out placement for the window if the window is new */
315 if (ob_state() == OB_STATE_RUNNING) {
316 ob_debug("Positioned: %s @ %d %d",
317 (!self->positioned ? "no" :
318 (self->positioned == PPosition ? "program specified" :
319 (self->positioned == USPosition ? "user specified" :
320 (self->positioned == (PPosition | USPosition) ?
321 "program + user specified" :
322 "BADNESS !?")))), place.x, place.y);
324 ob_debug("Sized: %s @ %d %d",
325 (!self->sized ? "no" :
326 (self->sized == PSize ? "program specified" :
327 (self->sized == USSize ? "user specified" :
328 (self->sized == (PSize | USSize) ?
329 "program + user specified" :
330 "BADNESS !?")))), place.width, place.height);
332 obplaced = place_client(self, &place.x, &place.y, settings);
334 /* watch for buggy apps that ask to be placed at (0,0) when there is
336 if (!obplaced && place.x == 0 && place.y == 0 &&
337 /* non-normal windows are allowed */
338 client_normal(self) &&
339 /* oldschool fullscreen windows are allowed */
340 !client_is_oldfullscreen(self, &place))
344 r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
348 ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
350 g_slice_free(Rect, r);
353 /* make sure the window is visible. */
354 client_find_onscreen(self, &place.x, &place.y,
355 place.width, place.height,
356 /* non-normal clients has less rules, and
357 windows that are being restored from a
358 session do also. we can assume you want
359 it back where you saved it. Clients saying
360 they placed themselves are subjected to
361 harder rules, ones that are placed by
362 place.c or by the user are allowed partially
363 off-screen and on xinerama divides (ie,
364 it is up to the placement routines to avoid
365 the xinerama divides)
367 children and splash screens are forced on
368 screen, but i don't remember why i decided to
371 ob_state() == OB_STATE_RUNNING &&
372 (self->type == OB_CLIENT_TYPE_DIALOG ||
373 self->type == OB_CLIENT_TYPE_SPLASH ||
374 (!((self->positioned & USPosition) ||
375 settings->pos_given) &&
376 client_normal(self) &&
378 /* don't move oldschool fullscreen windows to
379 fit inside the struts (fixes Acroread, which
380 makes its fullscreen window fit the screen
381 but it is not USSize'd or USPosition'd) */
382 !client_is_oldfullscreen(self, &place))));
385 /* if the window isn't user-sized, then make it fit inside
386 the visible screen area on its monitor. Use basically the same rules
387 for forcing the window on screen in the client_find_onscreen call.
389 do this after place_client, it chooses the monitor!
391 splash screens get "transient" set to TRUE by
392 the place_client call
394 if (ob_state() == OB_STATE_RUNNING &&
396 (!(self->sized & USSize || self->positioned & USPosition) &&
397 client_normal(self) &&
399 /* don't shrink oldschool fullscreen windows to fit inside the
400 struts (fixes Acroread, which makes its fullscreen window
401 fit the screen but it is not USSize'd or USPosition'd) */
402 !client_is_oldfullscreen(self, &place))))
404 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
406 /* get the size of the frame */
407 place.width += self->frame->size.left + self->frame->size.right;
408 place.height += self->frame->size.top + self->frame->size.bottom;
410 /* fit the window inside the area */
411 place.width = MIN(place.width, a->width);
412 place.height = MIN(place.height, a->height);
414 ob_debug("setting window size to %dx%d", place.width, place.height);
416 /* get the size of the client back */
417 place.width -= self->frame->size.left + self->frame->size.right;
418 place.height -= self->frame->size.top + self->frame->size.bottom;
420 g_slice_free(Rect, a);
423 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
424 "some restrictions may apply",
425 self->window, place.x, place.y, place.width, place.height);
427 ob_debug(" but session requested %d, %d %d x %d instead, "
429 self->session->x, self->session->y,
430 self->session->w, self->session->h);
432 /* do this after the window is placed, so the premax/prefullscreen numbers
435 this also places the window
437 client_apply_startup_state(self, place.x, place.y,
438 place.width, place.height);
440 ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
441 activate ? "yes" : "no");
443 activate = client_can_steal_focus(self, settings->focus,
444 event_time(), launch_time);
447 /* if the client isn't stealing focus, then hilite it so the user
448 knows it is there, but don't do this if we're restoring from a
450 if (!client_restore_session_stacking(self))
451 client_hilite(self, TRUE);
455 /* This may look rather odd. Well it's because new windows are added
456 to the stacking order non-intrusively. If we're not going to focus
457 the new window or hilite it, then we raise it to the top. This will
458 take affect for things that don't get focused like splash screens.
459 Also if you don't have focus_new enabled, then it's going to get
460 raised to the top. Legacy begets legacy I guess?
462 if (!client_restore_session_stacking(self))
463 stacking_raise(CLIENT_AS_WINDOW(self));
466 mouse_grab_for_client(self, TRUE);
468 /* this has to happen before we try focus the window, but we want it to
469 happen after the client's stacking has been determined or it looks bad
473 if (!config_focus_under_mouse)
474 ignore_start = event_start_ignore_all_enters();
478 if (!config_focus_under_mouse)
479 event_end_ignore_all_enters(ignore_start);
483 gboolean stacked = client_restore_session_stacking(self);
484 client_present(self, FALSE, !stacked, TRUE);
487 /* add to client list/map */
488 client_list = g_list_append(client_list, self);
489 window_add(&self->window, CLIENT_AS_WINDOW(self));
491 /* this has to happen after we're in the client_list */
492 if (STRUT_EXISTS(self->strut))
493 screen_update_areas();
495 /* update the list hints */
498 /* free the ObAppSettings shallow copy */
499 g_slice_free(ObAppSettings, settings);
501 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
502 window, self->frame->window, self->class);
505 ObClient *client_fake_manage(Window window)
508 ObAppSettings *settings;
510 ob_debug("Pretend-managing window: %lx", window);
512 /* do this minimal stuff to figure out the client's decorations */
514 self = window_new(OB_WINDOW_CLASS_CLIENT, ObClient);
515 self->window = window;
517 client_get_all(self, FALSE);
518 /* per-app settings override stuff, and return the settings for other
519 uses too. this returns a shallow copy that needs to be freed */
520 settings = client_get_settings_state(self);
522 /* create the decoration frame for the client window and adjust its size */
523 self->frame = frame_new(self);
525 client_apply_startup_state(self, self->area.x, self->area.y,
526 self->area.width, self->area.height);
528 window_set_abstract(CLIENT_AS_WINDOW(self),
529 &self->frame->window, /* top level window */
530 &self->layer, /* stacking layer */
531 &self->frame->depth); /* window depth */
533 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
535 ob_debug("gave extents left %d right %d top %d bottom %d",
536 self->frame->size.left, self->frame->size.right,
537 self->frame->size.top, self->frame->size.bottom);
539 /* free the ObAppSettings shallow copy */
540 g_slice_free(ObAppSettings, settings);
545 void client_unmanage_all(void)
548 client_unmanage(client_list->data);
551 void client_unmanage(ObClient *self)
556 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
557 self->window, self->frame->window,
558 self->class, self->title ? self->title : "");
560 g_assert(self != NULL);
562 /* we dont want events no more. do this before hiding the frame so we
563 don't generate more events */
564 XSelectInput(obt_display, self->window, NoEventMask);
566 /* ignore enter events from the unmap so it doesnt mess with the focus */
567 if (!config_focus_under_mouse)
568 ignore_start = event_start_ignore_all_enters();
570 frame_hide(self->frame);
571 /* flush to send the hide to the server quickly */
574 if (!config_focus_under_mouse)
575 event_end_ignore_all_enters(ignore_start);
577 mouse_grab_for_client(self, FALSE);
579 self->managed = FALSE;
581 /* remove the window from our save set, unless we are managing an internal
584 XChangeSaveSet(obt_display, self->window, SetModeDelete);
586 /* update the focus lists */
587 focus_order_remove(self);
588 if (client_focused(self)) {
589 /* don't leave an invalid focus_client */
593 /* if we're prompting to kill the client, close that */
594 prompt_unref(self->kill_prompt);
595 self->kill_prompt = NULL;
597 client_list = g_list_remove(client_list, self);
598 stacking_remove(self);
599 window_remove(self->window);
601 /* once the client is out of the list, update the struts to remove its
603 if (STRUT_EXISTS(self->strut))
604 screen_update_areas();
606 client_call_notifies(self, client_destroy_notifies);
608 /* tell our parent(s) that we're gone */
609 for (it = self->parents; it; it = g_slist_next(it))
610 ((ObClient*)it->data)->transients =
611 g_slist_remove(((ObClient*)it->data)->transients,self);
613 /* tell our transients that we're gone */
614 for (it = self->transients; it; it = g_slist_next(it)) {
615 ((ObClient*)it->data)->parents =
616 g_slist_remove(((ObClient*)it->data)->parents, self);
617 /* we could be keeping our children in a higher layer */
618 client_calc_layer(it->data);
621 /* remove from its group */
623 group_remove(self->group, self);
627 /* restore the window's original geometry so it is not lost */
633 if (self->fullscreen)
634 a = self->pre_fullscreen_area;
635 else if (self->max_horz || self->max_vert) {
636 if (self->max_horz) {
637 a.x = self->pre_max_area.x;
638 a.width = self->pre_max_area.width;
640 if (self->max_vert) {
641 a.y = self->pre_max_area.y;
642 a.height = self->pre_max_area.height;
646 self->fullscreen = self->max_horz = self->max_vert = FALSE;
647 /* let it be moved and resized no matter what */
648 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
649 self->decorations = 0; /* unmanaged windows have no decor */
651 /* give the client its border back */
652 XSetWindowBorderWidth(obt_display, self->window, self->border_width);
654 client_move_resize(self, a.x, a.y, a.width, a.height);
657 /* reparent the window out of the frame, and free the frame */
658 frame_release_client(self->frame);
659 frame_free(self->frame);
662 if (ob_state() != OB_STATE_EXITING) {
663 /* these values should not be persisted across a window
665 OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
666 OBT_PROP_ERASE(self->window, NET_WM_STATE);
667 OBT_PROP_ERASE(self->window, WM_STATE);
669 /* if we're left in an unmapped state, the client wont be mapped.
670 this is bad, since we will no longer be managing the window on
672 XMapWindow(obt_display, self->window);
675 /* these should not be left on the window ever. other window managers
676 don't necessarily use them and it will mess them up (like compiz) */
677 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
678 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
680 /* update the list hints */
683 ob_debug("Unmanaged window 0x%lx", self->window);
685 /* free all data allocated in the client struct */
686 RrImageUnref(self->icon_set);
687 g_slist_free(self->transients);
688 g_free(self->startup_id);
689 g_free(self->wm_command);
691 g_free(self->icon_title);
692 g_free(self->original_title);
696 g_free(self->client_machine);
697 g_free(self->sm_client_id);
698 window_free(CLIENT_AS_WINDOW(self));
701 void client_fake_unmanage(ObClient *self)
703 /* this is all that got allocated to get the decorations */
705 frame_free(self->frame);
706 window_free(CLIENT_AS_WINDOW(self));
709 static gboolean client_can_steal_focus(ObClient *self,
710 gboolean allow_other_desktop,
715 gboolean relative_focused;
716 gboolean parent_focused;
720 parent_focused = (focus_client != NULL &&
721 client_search_focus_parent(self));
722 relative_focused = (focus_client != NULL &&
723 (client_search_focus_tree_full(self) != NULL ||
724 client_search_focus_group_full(self) != NULL));
726 /* This is focus stealing prevention */
727 ob_debug_type(OB_DEBUG_FOCUS,
728 "Want to focus window 0x%x at time %u "
729 "launched at %u (last user interaction time %u)",
730 self->window, steal_time, launch_time,
731 event_last_user_time);
733 /* if it's on another desktop... */
734 if (!(self->desktop == screen_desktop ||
735 self->desktop == DESKTOP_ALL) &&
736 /* and (we dont know when it launched, and we don't want to allow
737 focus stealing from other desktops */
738 ((!launch_time && !allow_other_desktop) ||
739 /* or the timestamp is from before you changed desktops) */
740 (screen_desktop_user_time &&
741 !event_time_after(launch_time, screen_desktop_user_time))))
744 ob_debug_type(OB_DEBUG_FOCUS,
745 "Not focusing the window because its on another "
748 /* If something is focused... */
749 else if (focus_client) {
750 /* If the user is working in another window right now, then don't
752 if (!parent_focused &&
753 event_last_user_time && launch_time &&
754 event_time_after(event_last_user_time, launch_time) &&
755 event_last_user_time != launch_time &&
756 event_time_after(event_last_user_time,
757 steal_time - OB_EVENT_USER_TIME_DELAY))
760 ob_debug_type(OB_DEBUG_FOCUS,
761 "Not focusing the window because the user is "
762 "working in another window that is not "
765 /* If the new window is a transient (and its relatives aren't
767 else if (client_has_parent(self) && !relative_focused) {
769 ob_debug_type(OB_DEBUG_FOCUS,
770 "Not focusing the window because it is a "
771 "transient, and its relatives aren't focused");
773 /* Don't steal focus from globally active clients.
774 I stole this idea from KWin. It seems nice.
776 else if (!(focus_client->can_focus ||
777 focus_client->focus_notify))
780 ob_debug_type(OB_DEBUG_FOCUS,
781 "Not focusing the window because a globally "
782 "active client has focus");
784 /* Don't move focus if it's not going to go to this window
786 else if (client_focus_target(self) != self) {
788 ob_debug_type(OB_DEBUG_FOCUS,
789 "Not focusing the window because another window "
790 "would get the focus anyway");
792 /* Don't move focus if the window is not visible on the current
793 desktop and none of its relatives are focused */
794 else if (!(self->desktop == screen_desktop ||
795 self->desktop == DESKTOP_ALL) &&
799 ob_debug_type(OB_DEBUG_FOCUS,
800 "Not focusing the window because it is on "
801 "another desktop and no relatives are focused ");
806 ob_debug_type(OB_DEBUG_FOCUS,
807 "Focus stealing prevention activated for %s at "
808 "time %u (last user interaction time %u)",
809 self->title, steal_time, event_last_user_time);
813 /*! Returns a new structure containing the per-app settings for this client.
814 The returned structure needs to be freed with g_free. */
815 static ObAppSettings *client_get_settings_state(ObClient *self)
817 ObAppSettings *settings;
820 settings = config_create_app_settings();
822 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
823 ObAppSettings *app = it->data;
824 gboolean match = TRUE;
826 g_assert(app->name != NULL || app->class != NULL ||
827 app->role != NULL || app->title != NULL ||
828 (signed)app->type >= 0);
831 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
833 else if (app->class &&
834 !g_pattern_match(app->class,
835 strlen(self->class), self->class, NULL))
837 else if (app->role &&
838 !g_pattern_match(app->role,
839 strlen(self->role), self->role, NULL))
841 else if (app->title &&
842 !g_pattern_match(app->title,
843 strlen(self->title), self->title, NULL))
845 else if ((signed)app->type >= 0 && app->type != self->type) {
850 ob_debug("Window matching: %s", app->name);
852 /* copy the settings to our struct, overriding the existing
853 settings if they are not defaults */
854 config_app_settings_copy_non_defaults(app, settings);
858 if (settings->shade != -1)
859 self->shaded = !!settings->shade;
860 if (settings->decor != -1)
861 self->undecorated = !settings->decor;
862 if (settings->iconic != -1)
863 self->iconic = !!settings->iconic;
864 if (settings->skip_pager != -1)
865 self->skip_pager = !!settings->skip_pager;
866 if (settings->skip_taskbar != -1)
867 self->skip_taskbar = !!settings->skip_taskbar;
869 if (settings->max_vert != -1)
870 self->max_vert = !!settings->max_vert;
871 if (settings->max_horz != -1)
872 self->max_horz = !!settings->max_horz;
874 if (settings->fullscreen != -1)
875 self->fullscreen = !!settings->fullscreen;
877 if (settings->desktop) {
878 if (settings->desktop == DESKTOP_ALL)
879 self->desktop = settings->desktop;
880 else if (settings->desktop > 0 &&
881 settings->desktop <= screen_num_desktops)
882 self->desktop = settings->desktop - 1;
885 if (settings->layer == -1) {
889 else if (settings->layer == 0) {
893 else if (settings->layer == 1) {
900 static void client_restore_session_state(ObClient *self)
904 ob_debug_type(OB_DEBUG_SM,
905 "Restore session for client %s", self->title);
907 if (!(it = session_state_find(self))) {
908 ob_debug_type(OB_DEBUG_SM,
909 "Session data not found for client %s", self->title);
913 self->session = it->data;
915 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
918 RECT_SET_POINT(self->area, self->session->x, self->session->y);
919 self->positioned = USPosition;
920 self->sized = USSize;
921 if (self->session->w > 0)
922 self->area.width = self->session->w;
923 if (self->session->h > 0)
924 self->area.height = self->session->h;
925 XResizeWindow(obt_display, self->window,
926 self->area.width, self->area.height);
928 self->desktop = (self->session->desktop == DESKTOP_ALL ?
929 self->session->desktop :
930 MIN(screen_num_desktops - 1, self->session->desktop));
931 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
933 self->shaded = self->session->shaded;
934 self->iconic = self->session->iconic;
935 self->skip_pager = self->session->skip_pager;
936 self->skip_taskbar = self->session->skip_taskbar;
937 self->fullscreen = self->session->fullscreen;
938 self->above = self->session->above;
939 self->below = self->session->below;
940 self->max_horz = self->session->max_horz;
941 self->max_vert = self->session->max_vert;
942 self->undecorated = self->session->undecorated;
945 static gboolean client_restore_session_stacking(ObClient *self)
949 if (!self->session) return FALSE;
951 mypos = g_list_find(session_saved_state, self->session);
952 if (!mypos) return FALSE;
954 /* start above me and look for the first client */
955 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
958 for (cit = client_list; cit; cit = g_list_next(cit)) {
959 ObClient *c = cit->data;
960 /* found a client that was in the session, so go below it */
961 if (c->session == it->data) {
962 stacking_below(CLIENT_AS_WINDOW(self),
963 CLIENT_AS_WINDOW(cit->data));
971 void client_move_onscreen(ObClient *self, gboolean rude)
973 gint x = self->area.x;
974 gint y = self->area.y;
975 if (client_find_onscreen(self, &x, &y,
977 self->area.height, rude)) {
978 client_move(self, x, y);
982 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
985 gint ox = *x, oy = *y;
986 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
992 RECT_SET(desired, *x, *y, w, h);
993 frame_rect_to_frame(self->frame, &desired);
995 /* get where the frame would be */
996 frame_client_gravity(self->frame, x, y);
998 /* get the requested size of the window with decorations */
999 fw = self->frame->size.left + w + self->frame->size.right;
1000 fh = self->frame->size.top + h + self->frame->size.bottom;
1002 /* If rudeness wasn't requested, then still be rude in a given direction
1003 if the client is not moving, only resizing in that direction */
1005 Point oldtl, oldtr, oldbl, oldbr;
1006 Point newtl, newtr, newbl, newbr;
1007 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
1009 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
1010 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
1011 self->frame->area.y + self->frame->area.height - 1);
1012 POINT_SET(oldtr, oldbr.x, oldtl.y);
1013 POINT_SET(oldbl, oldtl.x, oldbr.y);
1015 POINT_SET(newtl, *x, *y);
1016 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
1017 POINT_SET(newtr, newbr.x, newtl.y);
1018 POINT_SET(newbl, newtl.x, newbr.y);
1020 /* is it moving or just resizing from some corner? */
1021 stationary_l = oldtl.x == newtl.x;
1022 stationary_r = oldtr.x == newtr.x;
1023 stationary_t = oldtl.y == newtl.y;
1024 stationary_b = oldbl.y == newbl.y;
1026 /* if left edge is growing and didnt move right edge */
1027 if (stationary_r && newtl.x < oldtl.x)
1029 /* if right edge is growing and didnt move left edge */
1030 if (stationary_l && newtr.x > oldtr.x)
1032 /* if top edge is growing and didnt move bottom edge */
1033 if (stationary_b && newtl.y < oldtl.y)
1035 /* if bottom edge is growing and didnt move top edge */
1036 if (stationary_t && newbl.y > oldbl.y)
1040 /* we iterate through every monitor that the window is at least partially
1041 on, to make sure it is obeying the rules on them all
1043 if the window does not appear on any monitors, then use the first one
1046 for (i = 0; i < screen_num_monitors; ++i) {
1049 if (!screen_physical_area_monitor_contains(i, &desired)) {
1050 if (i < screen_num_monitors - 1 || found_mon)
1053 /* the window is not inside any monitor! so just use the first
1055 a = screen_area(self->desktop, 0, NULL);
1058 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1061 /* This makes sure windows aren't entirely outside of the screen so you
1062 can't see them at all.
1063 It makes sure 10% of the window is on the screen at least. And don't
1064 let it move itself off the top of the screen, which would hide the
1065 titlebar on you. (The user can still do this if they want too, it's
1066 only limiting the application.
1068 if (client_normal(self)) {
1069 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1070 *x = a->x + a->width - fw/10;
1071 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1072 *y = a->y + a->height - fh/10;
1073 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1074 *x = a->x - fw*9/10;
1075 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1076 *y = a->y - fh*9/10;
1079 /* This here doesn't let windows even a pixel outside the
1080 struts/screen. When called from client_manage, programs placing
1081 themselves are forced completely onscreen, while things like
1082 xterm -geometry resolution-width/2 will work fine. Trying to
1083 place it completely offscreen will be handled in the above code.
1084 Sorry for this confused comment, i am tired. */
1085 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1086 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1087 *x = a->x + MAX(0, a->width - fw);
1089 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1090 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1091 *y = a->y + MAX(0, a->height - fh);
1093 g_slice_free(Rect, a);
1096 /* get where the client should be */
1097 frame_frame_gravity(self->frame, x, y);
1099 return ox != *x || oy != *y;
1102 static void client_get_all(ObClient *self, gboolean real)
1104 /* this is needed for the frame to set itself up */
1105 client_get_area(self);
1107 /* these things can change the decor and functions of the window */
1109 client_get_mwm_hints(self);
1110 /* this can change the mwmhints for special cases */
1111 client_get_type_and_transientness(self);
1112 client_update_normal_hints(self);
1114 /* set up the decor/functions before getting the state. the states may
1115 affect which functions are available, but we want to know the maximum
1116 decor/functions are available to this window, so we can then apply them
1117 in client_apply_startup_state() */
1118 client_setup_decor_and_functions(self, FALSE);
1120 client_get_state(self);
1122 /* get the session related properties, these can change decorations
1123 from per-app settings */
1124 client_get_session_ids(self);
1126 /* now we got everything that can affect the decorations */
1130 /* get this early so we have it for debugging */
1131 client_update_title(self);
1133 /* save the values of the variables used for app rule matching */
1134 client_save_app_rule_values(self);
1136 client_update_protocols(self);
1138 client_update_wmhints(self);
1139 /* this may have already been called from client_update_wmhints */
1140 if (!self->parents && !self->transient_for_group)
1141 client_update_transient_for(self);
1143 client_get_startup_id(self);
1144 client_get_desktop(self);/* uses transient data/group/startup id if a
1145 desktop is not specified */
1146 client_get_shaped(self);
1149 /* a couple type-based defaults for new windows */
1151 /* this makes sure that these windows appear on all desktops */
1152 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1153 self->desktop = DESKTOP_ALL;
1157 client_update_sync_request_counter(self);
1160 client_get_colormap(self);
1161 client_update_strut(self);
1162 client_update_icons(self);
1163 client_update_icon_geometry(self);
1166 static void client_get_startup_id(ObClient *self)
1168 if (!(OBT_PROP_GETS(self->window, NET_STARTUP_ID, utf8,
1169 &self->startup_id)))
1171 OBT_PROP_GETS(self->group->leader,
1172 NET_STARTUP_ID, utf8, &self->startup_id);
1175 static void client_get_area(ObClient *self)
1177 XWindowAttributes wattrib;
1180 ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
1181 g_assert(ret != BadWindow);
1183 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1184 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1185 self->border_width = wattrib.border_width;
1187 ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
1188 wattrib.width, wattrib.height, wattrib.border_width);
1191 static void client_get_desktop(ObClient *self)
1193 guint32 d = screen_num_desktops; /* an always-invalid value */
1195 if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
1196 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1197 self->desktop = screen_num_desktops - 1;
1200 ob_debug("client requested desktop 0x%x", self->desktop);
1203 gboolean first = TRUE;
1204 guint all = screen_num_desktops; /* not a valid value */
1206 /* if they are all on one desktop, then open it on the
1208 for (it = self->parents; it; it = g_slist_next(it)) {
1209 ObClient *c = it->data;
1211 if (c->desktop == DESKTOP_ALL) continue;
1217 else if (all != c->desktop)
1218 all = screen_num_desktops; /* make it invalid */
1220 if (all != screen_num_desktops) {
1221 self->desktop = all;
1223 ob_debug("client desktop set from parents: 0x%x",
1226 /* try get from the startup-notification protocol */
1227 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1228 if (self->desktop >= screen_num_desktops &&
1229 self->desktop != DESKTOP_ALL)
1230 self->desktop = screen_num_desktops - 1;
1231 ob_debug("client desktop set from startup-notification: 0x%x",
1234 /* defaults to the current desktop */
1236 self->desktop = screen_desktop;
1237 ob_debug("client desktop set to the current desktop: %d",
1243 static void client_get_state(ObClient *self)
1248 if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
1250 for (i = 0; i < num; ++i) {
1251 if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1253 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1254 self->shaded = TRUE;
1255 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1256 self->iconic = TRUE;
1257 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1258 self->skip_taskbar = TRUE;
1259 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1260 self->skip_pager = TRUE;
1261 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1262 self->fullscreen = TRUE;
1263 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
1264 self->max_vert = TRUE;
1265 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
1266 self->max_horz = TRUE;
1267 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1269 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
1271 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
1272 self->demands_attention = TRUE;
1273 else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
1274 self->undecorated = TRUE;
1281 static void client_get_shaped(ObClient *self)
1283 self->shaped = FALSE;
1285 if (obt_display_extension_shape) {
1290 XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
1292 XShapeQueryExtents(obt_display, self->window, &s, &foo,
1293 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1300 void client_update_transient_for(ObClient *self)
1303 ObClient *target = NULL;
1304 gboolean trangroup = FALSE;
1306 if (XGetTransientForHint(obt_display, self->window, &t)) {
1307 if (t != self->window) { /* can't be transient to itself! */
1308 ObWindow *tw = window_find(t);
1309 /* if this happens then we need to check for it */
1310 g_assert(tw != CLIENT_AS_WINDOW(self));
1311 if (tw && WINDOW_IS_CLIENT(tw)) {
1312 /* watch out for windows with a parent that is something
1313 different, like a dockapp for example */
1314 target = WINDOW_AS_CLIENT(tw);
1318 /* Setting the transient_for to Root is actually illegal, however
1319 applications from time have done this to specify transient for
1321 if (!target && self->group && t == obt_root(ob_screen))
1323 } else if (self->group && self->transient)
1326 client_update_transient_tree(self, self->group, self->group,
1327 self->transient_for_group, trangroup,
1328 client_direct_parent(self), target);
1329 self->transient_for_group = trangroup;
1333 static void client_update_transient_tree(ObClient *self,
1334 ObGroup *oldgroup, ObGroup *newgroup,
1335 gboolean oldgtran, gboolean newgtran,
1336 ObClient* oldparent,
1337 ObClient *newparent)
1342 g_assert(!oldgtran || oldgroup);
1343 g_assert(!newgtran || newgroup);
1344 g_assert((!oldgtran && !oldparent) ||
1345 (oldgtran && !oldparent) ||
1346 (!oldgtran && oldparent));
1347 g_assert((!newgtran && !newparent) ||
1348 (newgtran && !newparent) ||
1349 (!newgtran && newparent));
1352 Group transient windows are not allowed to have other group
1353 transient windows as their children.
1356 /* No change has occured */
1357 if (oldgroup == newgroup &&
1358 oldgtran == newgtran &&
1359 oldparent == newparent) return;
1361 /** Remove the client from the transient tree **/
1363 for (it = self->transients; it; it = next) {
1364 next = g_slist_next(it);
1366 self->transients = g_slist_delete_link(self->transients, it);
1367 c->parents = g_slist_remove(c->parents, self);
1369 for (it = self->parents; it; it = next) {
1370 next = g_slist_next(it);
1372 self->parents = g_slist_delete_link(self->parents, it);
1373 c->transients = g_slist_remove(c->transients, self);
1376 /** Re-add the client to the transient tree **/
1378 /* If we're transient for a group then we need to add ourselves to all our
1381 for (it = newgroup->members; it; it = g_slist_next(it)) {
1384 !client_search_top_direct_parent(c)->transient_for_group &&
1387 c->transients = g_slist_prepend(c->transients, self);
1388 self->parents = g_slist_prepend(self->parents, c);
1393 /* If we are now transient for a single window we need to add ourselves to
1396 WARNING: Cyclical transient-ness is possible if two windows are
1397 transient for eachother.
1399 else if (newparent &&
1400 /* don't make ourself its child if it is already our child */
1401 !client_is_direct_child(self, newparent) &&
1402 client_normal(newparent))
1404 newparent->transients = g_slist_prepend(newparent->transients, self);
1405 self->parents = g_slist_prepend(self->parents, newparent);
1408 /* Add any group transient windows to our children. But if we're transient
1409 for the group, then other group transients are not our children.
1411 WARNING: Cyclical transient-ness is possible. For e.g. if:
1412 A is transient for the group
1413 B is transient for A
1414 C is transient for B
1415 A can't be transient for C or we have a cycle
1417 if (!newgtran && newgroup &&
1419 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1420 client_normal(self))
1422 for (it = newgroup->members; it; it = g_slist_next(it)) {
1424 if (c != self && c->transient_for_group &&
1425 /* Don't make it our child if it is already our parent */
1426 !client_is_direct_child(c, self))
1428 self->transients = g_slist_prepend(self->transients, c);
1429 c->parents = g_slist_prepend(c->parents, self);
1434 /** If we change our group transient-ness, our children change their
1435 effective group transient-ness, which affects how they relate to other
1438 for (it = self->transients; it; it = g_slist_next(it)) {
1440 if (!c->transient_for_group)
1441 client_update_transient_tree(c, c->group, c->group,
1442 c->transient_for_group,
1443 c->transient_for_group,
1444 client_direct_parent(c),
1445 client_direct_parent(c));
1449 void client_get_mwm_hints(ObClient *self)
1454 self->mwmhints.flags = 0; /* default to none */
1456 if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1458 if (num >= OB_MWM_ELEMENTS) {
1459 self->mwmhints.flags = hints[0];
1460 self->mwmhints.functions = hints[1];
1461 self->mwmhints.decorations = hints[2];
1467 void client_get_type_and_transientness(ObClient *self)
1474 self->transient = FALSE;
1476 if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1477 /* use the first value that we know about in the array */
1478 for (i = 0; i < num; ++i) {
1479 if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1480 self->type = OB_CLIENT_TYPE_DESKTOP;
1481 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1482 self->type = OB_CLIENT_TYPE_DOCK;
1483 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1484 self->type = OB_CLIENT_TYPE_TOOLBAR;
1485 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1486 self->type = OB_CLIENT_TYPE_MENU;
1487 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1488 self->type = OB_CLIENT_TYPE_UTILITY;
1489 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1490 self->type = OB_CLIENT_TYPE_SPLASH;
1491 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1492 self->type = OB_CLIENT_TYPE_DIALOG;
1493 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1494 self->type = OB_CLIENT_TYPE_NORMAL;
1495 else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1497 /* prevent this window from getting any decor or
1499 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1500 OB_MWM_FLAG_DECORATIONS);
1501 self->mwmhints.decorations = 0;
1502 self->mwmhints.functions = 0;
1504 if (self->type != (ObClientType) -1)
1505 break; /* grab the first legit type */
1510 if (XGetTransientForHint(obt_display, self->window, &t))
1511 self->transient = TRUE;
1513 if (self->type == (ObClientType) -1) {
1514 /*the window type hint was not set, which means we either classify
1515 ourself as a normal window or a dialog, depending on if we are a
1517 if (self->transient)
1518 self->type = OB_CLIENT_TYPE_DIALOG;
1520 self->type = OB_CLIENT_TYPE_NORMAL;
1523 /* then, based on our type, we can update our transientness.. */
1524 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1525 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1526 self->type == OB_CLIENT_TYPE_MENU ||
1527 self->type == OB_CLIENT_TYPE_UTILITY)
1529 self->transient = TRUE;
1533 void client_update_protocols(ObClient *self)
1538 self->focus_notify = FALSE;
1539 self->delete_window = FALSE;
1541 if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1542 for (i = 0; i < num_ret; ++i) {
1543 if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1544 /* this means we can request the window to close */
1545 self->delete_window = TRUE;
1546 else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1547 /* if this protocol is requested, then the window will be
1548 notified whenever we want it to receive focus */
1549 self->focus_notify = TRUE;
1550 else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1551 /* if this protocol is requested, then the window will allow
1552 pings to determine if it is still alive */
1555 else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1556 /* if this protocol is requested, then resizing the
1557 window will be synchronized between the frame and the
1559 self->sync_request = TRUE;
1567 void client_update_sync_request_counter(ObClient *self)
1571 if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1575 self->sync_counter = i;
1577 /* this must be set when managing a new window according to EWMH */
1578 XSyncIntToValue(&val, 0);
1579 XSyncSetCounter(obt_display, self->sync_counter, val);
1581 self->sync_counter = None;
1585 static void client_get_colormap(ObClient *self)
1587 XWindowAttributes wa;
1589 if (XGetWindowAttributes(obt_display, self->window, &wa))
1590 client_update_colormap(self, wa.colormap);
1593 void client_update_colormap(ObClient *self, Colormap colormap)
1595 if (colormap == self->colormap) return;
1597 ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1599 if (client_focused(self)) {
1600 screen_install_colormap(self, FALSE); /* uninstall old one */
1601 self->colormap = colormap;
1602 screen_install_colormap(self, TRUE); /* install new one */
1604 self->colormap = colormap;
1607 void client_update_normal_hints(ObClient *self)
1613 self->min_ratio = 0.0f;
1614 self->max_ratio = 0.0f;
1615 SIZE_SET(self->size_inc, 1, 1);
1616 SIZE_SET(self->base_size, -1, -1);
1617 SIZE_SET(self->min_size, 0, 0);
1618 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1620 /* get the hints from the window */
1621 if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
1622 /* normal windows can't request placement! har har
1623 if (!client_normal(self))
1625 self->positioned = (size.flags & (PPosition|USPosition));
1626 self->sized = (size.flags & (PSize|USSize));
1628 if (size.flags & PWinGravity)
1629 self->gravity = size.win_gravity;
1631 if (size.flags & PAspect) {
1632 if (size.min_aspect.y)
1634 (gfloat) size.min_aspect.x / size.min_aspect.y;
1635 if (size.max_aspect.y)
1637 (gfloat) size.max_aspect.x / size.max_aspect.y;
1640 if (size.flags & PMinSize)
1641 SIZE_SET(self->min_size, size.min_width, size.min_height);
1643 if (size.flags & PMaxSize)
1644 SIZE_SET(self->max_size, size.max_width, size.max_height);
1646 if (size.flags & PBaseSize)
1647 SIZE_SET(self->base_size, size.base_width, size.base_height);
1649 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1650 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1652 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1653 self->min_size.width, self->min_size.height,
1654 self->max_size.width, self->max_size.height);
1655 ob_debug("size inc (%d %d) base size (%d %d)",
1656 self->size_inc.width, self->size_inc.height,
1657 self->base_size.width, self->base_size.height);
1660 ob_debug("Normal hints: not set");
1663 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1665 /* start with everything (cept fullscreen) */
1667 (OB_FRAME_DECOR_TITLEBAR |
1668 OB_FRAME_DECOR_HANDLE |
1669 OB_FRAME_DECOR_GRIPS |
1670 OB_FRAME_DECOR_BORDER |
1671 OB_FRAME_DECOR_ICON |
1672 OB_FRAME_DECOR_ALLDESKTOPS |
1673 OB_FRAME_DECOR_ICONIFY |
1674 OB_FRAME_DECOR_MAXIMIZE |
1675 OB_FRAME_DECOR_SHADE |
1676 OB_FRAME_DECOR_CLOSE);
1678 (OB_CLIENT_FUNC_RESIZE |
1679 OB_CLIENT_FUNC_MOVE |
1680 OB_CLIENT_FUNC_ICONIFY |
1681 OB_CLIENT_FUNC_MAXIMIZE |
1682 OB_CLIENT_FUNC_SHADE |
1683 OB_CLIENT_FUNC_CLOSE |
1684 OB_CLIENT_FUNC_BELOW |
1685 OB_CLIENT_FUNC_ABOVE |
1686 OB_CLIENT_FUNC_UNDECORATE);
1688 if (!(self->min_size.width < self->max_size.width ||
1689 self->min_size.height < self->max_size.height))
1690 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1692 switch (self->type) {
1693 case OB_CLIENT_TYPE_NORMAL:
1694 /* normal windows retain all of the possible decorations and
1695 functionality, and can be fullscreen */
1696 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1699 case OB_CLIENT_TYPE_DIALOG:
1700 /* sometimes apps make dialog windows fullscreen for some reason (for
1701 e.g. kpdf does this..) */
1702 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1705 case OB_CLIENT_TYPE_UTILITY:
1706 /* these windows don't have anything added or removed by default */
1709 case OB_CLIENT_TYPE_MENU:
1710 case OB_CLIENT_TYPE_TOOLBAR:
1711 /* these windows can't iconify or maximize */
1712 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1713 OB_FRAME_DECOR_MAXIMIZE);
1714 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1715 OB_CLIENT_FUNC_MAXIMIZE);
1718 case OB_CLIENT_TYPE_SPLASH:
1719 /* these don't get get any decorations, and the only thing you can
1720 do with them is move them */
1721 self->decorations = 0;
1722 self->functions = OB_CLIENT_FUNC_MOVE;
1725 case OB_CLIENT_TYPE_DESKTOP:
1726 /* these windows are not manipulated by the window manager */
1727 self->decorations = 0;
1728 self->functions = 0;
1731 case OB_CLIENT_TYPE_DOCK:
1732 /* these windows are not manipulated by the window manager, but they
1733 can set below layer which has a special meaning */
1734 self->decorations = 0;
1735 self->functions = OB_CLIENT_FUNC_BELOW;
1739 /* If the client has no decor from its type (which never changes) then
1740 don't allow the user to "undecorate" the window. Otherwise, allow them
1741 to, even if there are motif hints removing the decor, because those
1742 may change these days (e.g. chromium) */
1743 if (self->decorations == 0)
1744 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1746 /* Mwm Hints are applied subtractively to what has already been chosen for
1747 decor and functionality */
1748 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1749 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1750 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1751 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1753 /* if the mwm hints request no handle or title, then all
1754 decorations are disabled, but keep the border if that's
1756 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1757 self->decorations = OB_FRAME_DECOR_BORDER;
1759 self->decorations = 0;
1764 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1765 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1766 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1767 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1768 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1769 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1770 /* dont let mwm hints kill any buttons
1771 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1772 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1773 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1774 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1776 /* dont let mwm hints kill the close button
1777 if (! (self->mwmhints.functions & MwmFunc_Close))
1778 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1782 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1783 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1784 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1785 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1786 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1787 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1789 /* can't maximize without moving/resizing */
1790 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1791 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1792 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1793 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1794 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1797 if (self->max_horz && self->max_vert) {
1798 /* once upon a time you couldn't resize maximized windows, that is not
1799 the case any more though !
1801 but do kill the handle on fully maxed windows */
1802 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1805 /* finally, the user can have requested no decorations, which overrides
1806 everything (but doesnt give it a border if it doesnt have one) */
1807 if (self->undecorated)
1808 self->decorations &= (config_theme_keepborder ?
1809 OB_FRAME_DECOR_BORDER : 0);
1811 /* if we don't have a titlebar, then we cannot shade! */
1812 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1813 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1815 /* now we need to check against rules for the client's current state */
1816 if (self->fullscreen) {
1817 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1818 OB_CLIENT_FUNC_FULLSCREEN |
1819 OB_CLIENT_FUNC_ICONIFY);
1820 self->decorations = 0;
1823 client_change_allowed_actions(self);
1826 /* reconfigure to make sure decorations are updated */
1827 client_reconfigure(self, FALSE);
1830 static void client_change_allowed_actions(ObClient *self)
1835 /* desktop windows are kept on all desktops */
1836 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1837 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1839 if (self->functions & OB_CLIENT_FUNC_SHADE)
1840 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1841 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1842 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1843 if (self->functions & OB_CLIENT_FUNC_MOVE)
1844 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1845 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1846 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1847 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1848 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1849 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1850 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1851 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1852 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1853 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1855 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1856 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1857 if (self->functions & OB_CLIENT_FUNC_BELOW)
1858 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1859 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1860 actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1862 OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1864 /* make sure the window isn't breaking any rules now
1866 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1867 it can't be iconified with its parent
1870 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1871 if (self->frame) client_shade(self, FALSE);
1872 else self->shaded = FALSE;
1874 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1875 if (self->frame) client_fullscreen(self, FALSE);
1876 else self->fullscreen = FALSE;
1878 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1880 if (self->frame) client_maximize(self, FALSE, 0);
1881 else self->max_vert = self->max_horz = FALSE;
1885 void client_update_wmhints(ObClient *self)
1889 /* assume a window takes input if it doesn't specify */
1890 self->can_focus = TRUE;
1892 if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
1895 if (hints->flags & InputHint)
1896 self->can_focus = hints->input;
1898 /* only do this when first managing the window *AND* when we aren't
1900 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1901 if (hints->flags & StateHint)
1902 self->iconic = hints->initial_state == IconicState;
1905 self->urgent = (hints->flags & XUrgencyHint);
1906 if (self->urgent && !ur)
1907 client_hilite(self, TRUE);
1908 else if (!self->urgent && ur && self->demands_attention)
1909 client_hilite(self, FALSE);
1911 if (!(hints->flags & WindowGroupHint))
1912 hints->window_group = None;
1914 /* did the group state change? */
1915 if (hints->window_group !=
1916 (self->group ? self->group->leader : None))
1918 ObGroup *oldgroup = self->group;
1920 /* remove from the old group if there was one */
1922 group_remove(self->group, self);
1926 /* add ourself to the group if we have one */
1927 if (hints->window_group != None) {
1928 self->group = group_add(hints->window_group, self);
1931 /* Put ourselves into the new group's transient tree, and remove
1932 ourselves from the old group's */
1933 client_update_transient_tree(self, oldgroup, self->group,
1934 self->transient_for_group,
1935 self->transient_for_group,
1936 client_direct_parent(self),
1937 client_direct_parent(self));
1939 /* Lastly, being in a group, or not, can change if the window is
1940 transient for anything.
1942 The logic for this is:
1943 self->transient = TRUE always if the window wants to be
1944 transient for something, even if transient_for was NULL because
1945 it wasn't in a group before.
1947 If parents was NULL and oldgroup was NULL we can assume
1948 that when we add the new group, it will become transient for
1951 If transient_for_group is TRUE, then it must have already
1952 had a group. If it is getting a new group, the above call to
1953 client_update_transient_tree has already taken care of
1954 everything ! If it is losing all group status then it will
1955 no longer be transient for anything and that needs to be
1958 if (self->transient &&
1959 ((self->parents == NULL && oldgroup == NULL) ||
1960 (self->transient_for_group && !self->group)))
1961 client_update_transient_for(self);
1964 /* the WM_HINTS can contain an icon */
1965 if (hints->flags & IconPixmapHint)
1966 client_update_icons(self);
1971 focus_cycle_addremove(self, TRUE);
1974 void client_update_title(ObClient *self)
1977 gchar *visible = NULL;
1979 g_free(self->title);
1980 g_free(self->original_title);
1983 if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) {
1984 /* try old x stuff */
1985 if (!(OBT_PROP_GETS(self->window, WM_NAME, locale, &data)
1986 || OBT_PROP_GETS(self->window, WM_NAME, utf8, &data))) {
1987 if (self->transient) {
1989 GNOME alert windows are not given titles:
1990 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1992 data = g_strdup("");
1994 data = g_strdup(_("Unnamed Window"));
1997 self->original_title = g_strdup(data);
1999 if (self->client_machine) {
2000 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2005 if (self->not_responding) {
2007 if (self->kill_level > 0)
2008 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2010 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2014 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible);
2015 self->title = visible;
2018 frame_adjust_title(self->frame);
2020 /* update the icon title */
2022 g_free(self->icon_title);
2025 if (!OBT_PROP_GETS(self->window, NET_WM_ICON_NAME, utf8, &data))
2026 /* try old x stuff */
2027 if (!(OBT_PROP_GETS(self->window, WM_ICON_NAME, locale, &data) ||
2028 OBT_PROP_GETS(self->window, WM_ICON_NAME, utf8, &data)))
2029 data = g_strdup(self->title);
2031 if (self->client_machine) {
2032 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2037 if (self->not_responding) {
2039 if (self->kill_level > 0)
2040 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2042 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2046 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
2047 self->icon_title = visible;
2050 void client_update_strut(ObClient *self)
2054 gboolean got = FALSE;
2057 if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
2062 STRUT_PARTIAL_SET(strut,
2063 data[0], data[2], data[1], data[3],
2064 data[4], data[5], data[8], data[9],
2065 data[6], data[7], data[10], data[11]);
2071 OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
2077 /* use the screen's width/height */
2078 a = screen_physical_area_all_monitors();
2080 STRUT_PARTIAL_SET(strut,
2081 data[0], data[2], data[1], data[3],
2082 a->y, a->y + a->height - 1,
2083 a->x, a->x + a->width - 1,
2084 a->y, a->y + a->height - 1,
2085 a->x, a->x + a->width - 1);
2091 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2092 0, 0, 0, 0, 0, 0, 0, 0);
2094 if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
2095 self->strut = strut;
2097 /* updating here is pointless while we're being mapped cuz we're not in
2098 the client list yet */
2100 screen_update_areas();
2104 void client_update_icons(ObClient *self)
2109 guint num_seen; /* number of icons present */
2114 /* grab the server, because we might be setting the window's icon and
2115 we don't want them to set it in between and we overwrite their own
2119 if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
2120 /* figure out how many valid icons are in here */
2123 while (i + 2 < num) { /* +2 is to make sure there is a w and h */
2126 /* watch for the data being too small for the specified size,
2127 or for zero sized icons. */
2128 if (i + w*h > num || w == 0 || h == 0) break;
2130 /* convert it to the right bit order for ObRender */
2131 for (j = 0; j < w*h; ++j)
2133 (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2134 (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
2135 (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
2136 (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
2138 /* is it in the cache? */
2139 img = RrImageCacheFind(ob_rr_icons, &data[i], w, h);
2140 if (img) RrImageRef(img); /* own it */
2145 /* don't bother looping anymore if we already found it in the cache
2146 since we'll just use that! */
2150 /* if it's not in the cache yet, then add it to the cache now.
2151 we have already converted it to the correct bit order above */
2152 if (!img && num_seen > 0) {
2153 img = RrImageNew(ob_rr_icons);
2155 for (j = 0; j < num_seen; ++j) {
2158 RrImageAddPicture(img, &data[i], w, h);
2166 /* if we didn't find an image from the NET_WM_ICON stuff, then try the
2171 if ((hints = XGetWMHints(obt_display, self->window))) {
2172 if (hints->flags & IconPixmapHint) {
2174 obt_display_ignore_errors(TRUE);
2175 xicon = RrPixmapToRGBA(ob_rr_inst,
2177 (hints->flags & IconMaskHint ?
2178 hints->icon_mask : None),
2179 (gint*)&w, (gint*)&h, &data);
2180 obt_display_ignore_errors(FALSE);
2183 if (w > 0 && h > 0) {
2184 /* is this icon in the cache yet? */
2185 img = RrImageCacheFind(ob_rr_icons, data, w, h);
2186 if (img) RrImageRef(img); /* own it */
2188 /* if not, then add it */
2190 img = RrImageNew(ob_rr_icons);
2191 RrImageAddPicture(img, data, w, h);
2202 /* set the client's icons to be whatever we found */
2203 RrImageUnref(self->icon_set);
2204 self->icon_set = img;
2206 /* if the client has no icon at all, then we set a default icon onto it.
2207 but, if it has parents, then one of them will have an icon already
2209 if (!self->icon_set && !self->parents) {
2210 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2211 gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
2213 w = ob_rr_theme->def_win_icon_w;
2214 h = ob_rr_theme->def_win_icon_h;
2215 ldata = g_new(gulong, w*h+2);
2218 for (i = 0; i < w*h; ++i)
2219 ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2220 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2221 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2222 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2223 OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
2225 } else if (self->frame)
2226 /* don't draw the icon empty if we're just setting one now anyways,
2227 we'll get the property change any second */
2228 frame_adjust_icon(self->frame);
2233 void client_update_icon_geometry(ObClient *self)
2238 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2240 if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
2244 /* don't let them set it with an area < 0 */
2245 RECT_SET(self->icon_geometry, data[0], data[1],
2246 MAX(data[2],0), MAX(data[3],0));
2251 static void client_get_session_ids(ObClient *self)
2258 if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
2261 /* get the SM_CLIENT_ID */
2264 got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
2266 OBT_PROP_GETS(self->window, SM_CLIENT_ID, locale, &self->sm_client_id);
2268 /* get the WM_CLASS (name and class). make them "" if they are not
2272 got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
2274 got = OBT_PROP_GETSS(self->window, WM_CLASS, locale, &ss);
2278 self->name = g_strdup(ss[0]);
2280 self->class = g_strdup(ss[1]);
2285 if (self->name == NULL) self->name = g_strdup("");
2286 if (self->class == NULL) self->class = g_strdup("");
2288 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2291 got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
2293 got = OBT_PROP_GETS(self->window, WM_WINDOW_ROLE, locale, &s);
2298 self->role = g_strdup("");
2300 /* get the WM_COMMAND */
2304 got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
2306 got = OBT_PROP_GETSS(self->window, WM_COMMAND, locale, &ss);
2309 /* merge/mash them all together */
2310 gchar *merge = NULL;
2313 for (i = 0; ss[i]; ++i) {
2316 merge = g_strconcat(merge, ss[i], NULL);
2318 merge = g_strconcat(ss[i], NULL);
2323 self->wm_command = merge;
2326 /* get the WM_CLIENT_MACHINE */
2329 got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
2331 got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, locale, &s);
2334 gchar localhost[128];
2337 gethostname(localhost, 127);
2338 localhost[127] = '\0';
2339 if (strcmp(localhost, s) != 0)
2340 self->client_machine = s;
2344 /* see if it has the PID set too (the PID requires that the
2345 WM_CLIENT_MACHINE be set) */
2346 if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
2351 /*! Save the properties used for app matching rules, as seen by Openbox when
2352 the window mapped, so that users can still access them later if the app
2354 static void client_save_app_rule_values(ObClient *self)
2358 OBT_PROP_SETS(self->window, OB_APP_ROLE, utf8, self->role);
2359 OBT_PROP_SETS(self->window, OB_APP_NAME, utf8, self->name);
2360 OBT_PROP_SETS(self->window, OB_APP_CLASS, utf8, self->class);
2361 OBT_PROP_SETS(self->window, OB_APP_TITLE, utf8, self->original_title);
2363 switch (self->type) {
2364 case OB_CLIENT_TYPE_NORMAL:
2365 type = "normal"; break;
2366 case OB_CLIENT_TYPE_DIALOG:
2367 type = "dialog"; break;
2368 case OB_CLIENT_TYPE_UTILITY:
2369 type = "utility"; break;
2370 case OB_CLIENT_TYPE_MENU:
2371 type = "menu"; break;
2372 case OB_CLIENT_TYPE_TOOLBAR:
2373 type = "toolbar"; break;
2374 case OB_CLIENT_TYPE_SPLASH:
2375 type = "splash"; break;
2376 case OB_CLIENT_TYPE_DESKTOP:
2377 type = "desktop"; break;
2378 case OB_CLIENT_TYPE_DOCK:
2379 type = "dock"; break;
2381 OBT_PROP_SETS(self->window, OB_APP_TYPE, utf8, type);
2384 static void client_change_wm_state(ObClient *self)
2389 old = self->wmstate;
2391 if (self->shaded || self->iconic ||
2392 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2394 self->wmstate = IconicState;
2396 self->wmstate = NormalState;
2398 if (old != self->wmstate) {
2399 OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
2400 self->wmstate, 1, 0, 0, 0);
2402 state[0] = self->wmstate;
2404 OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
2408 static void client_change_state(ObClient *self)
2410 gulong netstate[12];
2415 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2417 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2419 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2420 if (self->skip_taskbar)
2421 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2422 if (self->skip_pager)
2423 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2424 if (self->fullscreen)
2425 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2427 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2429 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2431 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2433 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2434 if (self->demands_attention)
2435 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2436 if (self->undecorated)
2437 netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2438 OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
2441 frame_adjust_state(self->frame);
2444 ObClient *client_search_focus_tree(ObClient *self)
2449 for (it = self->transients; it; it = g_slist_next(it)) {
2450 if (client_focused(it->data)) return it->data;
2451 if ((ret = client_search_focus_tree(it->data))) return ret;
2456 ObClient *client_search_focus_tree_full(ObClient *self)
2458 if (self->parents) {
2461 for (it = self->parents; it; it = g_slist_next(it)) {
2462 ObClient *c = it->data;
2463 if ((c = client_search_focus_tree_full(c))) return c;
2469 /* this function checks the whole tree, the client_search_focus_tree
2470 does not, so we need to check this window */
2471 if (client_focused(self))
2473 return client_search_focus_tree(self);
2477 ObClient *client_search_focus_group_full(ObClient *self)
2482 for (it = self->group->members; it; it = g_slist_next(it)) {
2483 ObClient *c = it->data;
2485 if (client_focused(c)) return c;
2486 if ((c = client_search_focus_tree(it->data))) return c;
2489 if (client_focused(self)) return self;
2493 gboolean client_has_parent(ObClient *self)
2495 return self->parents != NULL;
2498 gboolean client_is_oldfullscreen(const ObClient *self,
2501 const Rect *monitor, *allmonitors;
2503 /* No decorations and fills the monitor = oldskool fullscreen.
2504 But not for maximized windows.
2507 if (self->decorations || self->max_horz || self->max_vert) return FALSE;
2509 monitor = screen_physical_area_monitor(screen_find_monitor(area));
2510 allmonitors = screen_physical_area_all_monitors();
2512 return (RECT_EQUAL(*area, *monitor) ||
2513 RECT_EQUAL(*area, *allmonitors));
2516 static ObStackingLayer calc_layer(ObClient *self)
2520 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2521 l = OB_STACKING_LAYER_DESKTOP;
2522 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2523 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2524 else l = OB_STACKING_LAYER_ABOVE;
2526 else if ((self->fullscreen ||
2527 client_is_oldfullscreen(self, &self->area)) &&
2528 /* you are fullscreen while you or your children are focused.. */
2529 (client_focused(self) || client_search_focus_tree(self) ||
2530 /* you can be fullscreen if you're on another desktop */
2531 (self->desktop != screen_desktop &&
2532 self->desktop != DESKTOP_ALL) ||
2533 /* and you can also be fullscreen if the focused client is on
2534 another monitor, or nothing else is focused */
2536 client_monitor(focus_client) != client_monitor(self))))
2537 l = OB_STACKING_LAYER_FULLSCREEN;
2538 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2539 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2540 else l = OB_STACKING_LAYER_NORMAL;
2545 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2546 ObStackingLayer min)
2548 ObStackingLayer old, own;
2552 own = calc_layer(self);
2553 self->layer = MAX(own, min);
2555 if (self->layer != old) {
2556 stacking_remove(CLIENT_AS_WINDOW(self));
2557 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2560 /* we've been restacked */
2561 self->visited = TRUE;
2563 for (it = self->transients; it; it = g_slist_next(it))
2564 client_calc_layer_recursive(it->data, orig,
2568 static void client_calc_layer_internal(ObClient *self)
2572 /* transients take on the layer of their parents */
2573 sit = client_search_all_top_parents(self);
2575 for (; sit; sit = g_slist_next(sit))
2576 client_calc_layer_recursive(sit->data, self, 0);
2579 void client_calc_layer(ObClient *self)
2583 /* skip over stuff above fullscreen layer */
2584 for (it = stacking_list; it; it = g_list_next(it))
2585 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2587 /* find the windows in the fullscreen layer, and mark them not-visited */
2588 for (; it; it = g_list_next(it)) {
2589 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2590 else if (WINDOW_IS_CLIENT(it->data))
2591 WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2594 client_calc_layer_internal(self);
2596 /* skip over stuff above fullscreen layer */
2597 for (it = stacking_list; it; it = g_list_next(it))
2598 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2600 /* now recalc any windows in the fullscreen layer which have not
2601 had their layer recalced already */
2602 for (; it; it = g_list_next(it)) {
2603 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2604 else if (WINDOW_IS_CLIENT(it->data) &&
2605 !WINDOW_AS_CLIENT(it->data)->visited)
2606 client_calc_layer_internal(it->data);
2610 gboolean client_should_show(ObClient *self)
2614 if (client_normal(self) && screen_showing_desktop)
2616 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2622 gboolean client_show(ObClient *self)
2624 gboolean show = FALSE;
2626 if (client_should_show(self)) {
2627 /* replay pending pointer event before showing the window, in case it
2628 should be going to something under the window */
2629 mouse_replay_pointer();
2631 frame_show(self->frame);
2634 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2635 it needs to be in IconicState. This includes when it is on another
2638 client_change_wm_state(self);
2643 gboolean client_hide(ObClient *self)
2645 gboolean hide = FALSE;
2647 if (!client_should_show(self)) {
2648 /* We don't need to ignore enter events here.
2649 The window can hide/iconify in 3 different ways:
2650 1 - through an x message. in this case we ignore all enter events
2651 caused by responding to the x message (unless underMouse)
2652 2 - by a keyboard action. in this case we ignore all enter events
2653 caused by the action
2654 3 - by a mouse action. in this case they are doing stuff with the
2655 mouse and focus _should_ move.
2657 Also in action_end, we simulate an enter event that can't be ignored
2658 so trying to ignore them is futile in case 3 anyways
2661 /* replay pending pointer event before hiding the window, in case it
2662 should be going to the window */
2663 mouse_replay_pointer();
2665 frame_hide(self->frame);
2668 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2669 it needs to be in IconicState. This includes when it is on another
2672 client_change_wm_state(self);
2677 void client_showhide(ObClient *self)
2679 if (!client_show(self))
2683 gboolean client_normal(ObClient *self) {
2684 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2685 self->type == OB_CLIENT_TYPE_DOCK ||
2686 self->type == OB_CLIENT_TYPE_SPLASH);
2689 gboolean client_helper(ObClient *self)
2691 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2692 self->type == OB_CLIENT_TYPE_MENU ||
2693 self->type == OB_CLIENT_TYPE_TOOLBAR);
2696 gboolean client_mouse_focusable(ObClient *self)
2698 return !(self->type == OB_CLIENT_TYPE_MENU ||
2699 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2700 self->type == OB_CLIENT_TYPE_SPLASH ||
2701 self->type == OB_CLIENT_TYPE_DOCK);
2704 gboolean client_enter_focusable(ObClient *self)
2706 /* you can focus desktops but it shouldn't on enter */
2707 return (client_mouse_focusable(self) &&
2708 self->type != OB_CLIENT_TYPE_DESKTOP);
2711 static void client_apply_startup_state(ObClient *self,
2712 gint x, gint y, gint w, gint h)
2714 /* save the states that we are going to apply */
2715 gboolean iconic = self->iconic;
2716 gboolean fullscreen = self->fullscreen;
2717 gboolean undecorated = self->undecorated;
2718 gboolean shaded = self->shaded;
2719 gboolean demands_attention = self->demands_attention;
2720 gboolean max_horz = self->max_horz;
2721 gboolean max_vert = self->max_vert;
2725 /* turn them all off in the client, so they won't affect the window
2727 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2728 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2730 /* move the client to its placed position, or it it's already there,
2731 generate a ConfigureNotify telling the client where it is.
2733 do this after adjusting the frame. otherwise it gets all weird and
2734 clients don't work right
2736 do this before applying the states so they have the correct
2737 pre-max/pre-fullscreen values
2739 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2740 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2741 self->window, x, y, w, h);
2742 /* save the area, and make it where it should be for the premax stuff */
2743 oldarea = self->area;
2744 RECT_SET(self->area, x, y, w, h);
2746 /* apply the states. these are in a carefully crafted order.. */
2749 client_iconify(self, TRUE, FALSE, TRUE);
2751 client_set_undecorated(self, TRUE);
2753 client_shade(self, TRUE);
2754 if (demands_attention)
2755 client_hilite(self, TRUE);
2757 if (max_vert && max_horz)
2758 client_maximize(self, TRUE, 0);
2760 client_maximize(self, TRUE, 2);
2762 client_maximize(self, TRUE, 1);
2764 /* fullscreen removes the ability to apply other states */
2766 client_fullscreen(self, TRUE);
2768 /* if the window hasn't been configured yet, then do so now, in fact the
2769 x,y,w,h may _not_ be the same as the area rect, which can end up
2770 meaning that the client isn't properly moved/resized by the fullscreen
2772 pho can cause this because it maps at size of the screen but not 0,0
2773 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2774 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2775 cuz thats where the pre-fullscreen will be. however the actual area is
2776 not, so this needs to be called even if we have fullscreened/maxed
2778 self->area = oldarea;
2779 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2781 /* set the desktop hint, to make sure that it always exists */
2782 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
2784 /* nothing to do for the other states:
2793 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2795 /* these should be the current values. this is for when you're not moving,
2797 g_assert(*x == self->area.x);
2798 g_assert(oldw == self->area.width);
2801 switch (self->gravity) {
2803 case NorthWestGravity:
2805 case SouthWestGravity:
2812 *x -= (neww - oldw) / 2;
2814 case NorthEastGravity:
2816 case SouthEastGravity:
2822 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2824 /* these should be the current values. this is for when you're not moving,
2826 g_assert(*y == self->area.y);
2827 g_assert(oldh == self->area.height);
2830 switch (self->gravity) {
2832 case NorthWestGravity:
2834 case NorthEastGravity:
2841 *y -= (newh - oldh) / 2;
2843 case SouthWestGravity:
2845 case SouthEastGravity:
2851 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2852 gint *logicalw, gint *logicalh,
2855 Rect desired = {*x, *y, *w, *h};
2856 frame_rect_to_frame(self->frame, &desired);
2858 /* XXX make this call a different function that returns frame dimensions
2859 without changing the actual frame's state! Otherwise calling
2860 this function without calling client_configure leaves the frame in
2861 a totally bogus state ! */
2863 /* make the frame recalculate its dimensions n shit without changing
2864 anything visible for real, this way the constraints below can work with
2865 the updated frame dimensions. */
2866 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2868 /* gets the frame's position */
2869 frame_client_gravity(self->frame, x, y);
2871 /* these positions are frame positions, not client positions */
2873 /* set the size and position if fullscreen */
2874 if (self->fullscreen) {
2878 i = screen_find_monitor(&desired);
2879 a = screen_physical_area_monitor(i);
2886 user = FALSE; /* ignore if the client can't be moved/resized when it
2888 } else if (self->max_horz || self->max_vert) {
2892 /* use all possible struts when maximizing to the full screen */
2893 i = screen_find_monitor(&desired);
2894 a = screen_area(self->desktop, i,
2895 (self->max_horz && self->max_vert ? NULL : &desired));
2897 /* set the size and position if maximized */
2898 if (self->max_horz) {
2900 *w = a->width - self->frame->size.left - self->frame->size.right;
2902 if (self->max_vert) {
2904 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2907 user = FALSE; /* ignore if the client can't be moved/resized when it
2910 g_slice_free(Rect, a);
2913 /* gets the client's position */
2914 frame_frame_gravity(self->frame, x, y);
2916 /* work within the preferred sizes given by the window, these may have
2917 changed rather than it's requested width and height, so always run
2918 through this code */
2920 gint basew, baseh, minw, minh;
2921 gint incw, inch, maxw, maxh;
2922 gfloat minratio, maxratio;
2924 incw = self->size_inc.width;
2925 inch = self->size_inc.height;
2926 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2927 0 : self->min_ratio;
2928 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2929 0 : self->max_ratio;
2931 /* base size is substituted with min size if not specified */
2932 if (self->base_size.width >= 0 || self->base_size.height >= 0) {
2933 basew = self->base_size.width;
2934 baseh = self->base_size.height;
2936 basew = self->min_size.width;
2937 baseh = self->min_size.height;
2939 /* min size is substituted with base size if not specified */
2940 if (self->min_size.width || self->min_size.height) {
2941 minw = self->min_size.width;
2942 minh = self->min_size.height;
2944 minw = self->base_size.width;
2945 minh = self->base_size.height;
2948 /* This comment is no longer true */
2949 /* if this is a user-requested resize, then check against min/max
2952 /* smaller than min size or bigger than max size? */
2953 if (*w > self->max_size.width) *w = self->max_size.width;
2954 if (*w < minw) *w = minw;
2955 if (*h > self->max_size.height) *h = self->max_size.height;
2956 if (*h < minh) *h = minh;
2961 /* the sizes to used for maximized */
2965 /* keep to the increments */
2969 /* you cannot resize to nothing */
2970 if (basew + *w < 1) *w = 1 - basew;
2971 if (baseh + *h < 1) *h = 1 - baseh;
2973 /* save the logical size */
2974 *logicalw = incw > 1 ? *w : *w + basew;
2975 *logicalh = inch > 1 ? *h : *h + baseh;
2980 /* if maximized/fs then don't use the size increments */
2981 if (self->fullscreen || self->max_horz) *w = maxw;
2982 if (self->fullscreen || self->max_vert) *h = maxh;
2987 /* adjust the height to match the width for the aspect ratios.
2988 for this, min size is not substituted for base size ever. */
2989 *w -= self->base_size.width;
2990 *h -= self->base_size.height;
2993 if (*h * minratio > *w) {
2994 *h = (gint)(*w / minratio);
2996 /* you cannot resize to nothing */
2999 *w = (gint)(*h * minratio);
3003 if (*h * maxratio < *w) {
3004 *h = (gint)(*w / maxratio);
3006 /* you cannot resize to nothing */
3009 *w = (gint)(*h * minratio);
3013 *w += self->base_size.width;
3014 *h += self->base_size.height;
3017 /* these override the above states! if you cant move you can't move! */
3019 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3023 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3024 *w = self->area.width;
3025 *h = self->area.height;
3033 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3034 gboolean user, gboolean final, gboolean force_reply)
3036 Rect oldframe, oldclient;
3037 gboolean send_resize_client;
3038 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3039 gboolean fmoved, fresized;
3040 const guint fdecor = self->frame->decorations;
3041 const gboolean fhorz = self->frame->max_horz;
3042 const gboolean fvert = self->frame->max_vert;
3043 const gboolean fshaded = self->frame->shaded;
3044 gint logicalw, logicalh;
3046 /* find the new x, y, width, and height (and logical size) */
3047 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3049 /* set the logical size if things changed */
3050 if (!(w == self->area.width && h == self->area.height))
3051 SIZE_SET(self->logical_size, logicalw, logicalh);
3053 /* figure out if we moved or resized or what */
3054 moved = (x != self->area.x || y != self->area.y);
3055 resized = (w != self->area.width || h != self->area.height);
3057 oldframe = self->frame->area;
3058 oldclient = self->area;
3059 RECT_SET(self->area, x, y, w, h);
3061 /* for app-requested resizes, always resize if 'resized' is true.
3062 for user-requested ones, only resize if final is true, or when
3063 resizing in redraw mode */
3064 send_resize_client = ((!user && resized) ||
3066 (resized && config_resize_redraw))));
3068 /* if the client is enlarging, then resize the client before the frame */
3069 if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
3070 XMoveResizeWindow(obt_display, self->window,
3071 self->frame->size.left, self->frame->size.top,
3072 MAX(w, oldclient.width), MAX(h, oldclient.height));
3073 frame_adjust_client_area(self->frame);
3076 /* find the frame's dimensions and move/resize it */
3080 /* if decorations changed, then readjust everything for the frame */
3081 if (self->decorations != fdecor ||
3082 self->max_horz != fhorz || self->max_vert != fvert)
3084 fmoved = fresized = TRUE;
3086 if (self->shaded != fshaded)
3089 /* adjust the frame */
3090 if (fmoved || fresized) {
3091 gulong ignore_start;
3093 ignore_start = event_start_ignore_all_enters();
3095 /* replay pending pointer event before move the window, in case it
3096 would change what window gets the event */
3097 mouse_replay_pointer();
3099 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
3102 event_end_ignore_all_enters(ignore_start);
3105 if (!user || final) {
3106 gint oldrx = self->root_pos.x;
3107 gint oldry = self->root_pos.y;
3108 /* we have reset the client to 0 border width, so don't include
3109 it in these coords */
3110 POINT_SET(self->root_pos,
3111 self->frame->area.x + self->frame->size.left -
3113 self->frame->area.y + self->frame->size.top -
3114 self->border_width);
3115 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3119 /* This is kinda tricky and should not be changed.. let me explain!
3121 When user = FALSE, then the request is coming from the application
3122 itself, and we are more strict about when to send a synthetic
3123 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3124 in this case (or send one if force_reply is true)
3126 When user = TRUE, then the request is coming from "us", like when we
3127 maximize a window or something. In this case we are more lenient. We
3128 used to follow the same rules as above, but _Java_ Swing can't handle
3129 this. So just to appease Swing, when user = TRUE, we always send
3130 a synthetic ConfigureNotify to give the window its root coordinates.
3131 Lastly, if force_reply is TRUE, we always send a
3132 ConfigureNotify, which is needed during a resize with XSYNCronization.
3134 if ((!user && !resized && (rootmoved || force_reply)) ||
3135 (user && ((!resized && force_reply) || (final && rootmoved))))
3139 event.type = ConfigureNotify;
3140 event.xconfigure.display = obt_display;
3141 event.xconfigure.event = self->window;
3142 event.xconfigure.window = self->window;
3144 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3145 self->title, self->root_pos.x, self->root_pos.y, w, h);
3147 /* root window real coords */
3148 event.xconfigure.x = self->root_pos.x;
3149 event.xconfigure.y = self->root_pos.y;
3150 event.xconfigure.width = w;
3151 event.xconfigure.height = h;
3152 event.xconfigure.border_width = self->border_width;
3153 event.xconfigure.above = None;
3154 event.xconfigure.override_redirect = FALSE;
3155 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3156 FALSE, StructureNotifyMask, &event);
3159 /* if the client is shrinking, then resize the frame before the client.
3161 both of these resize sections may run, because the top one only resizes
3162 in the direction that is growing
3164 if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
3166 frame_adjust_client_area(self->frame);
3167 XMoveResizeWindow(obt_display, self->window,
3168 self->frame->size.left, self->frame->size.top, w, h);
3171 XFlush(obt_display);
3173 /* if it moved between monitors, then this can affect the stacking
3174 layer of this window or others - for fullscreen windows.
3175 also if it changed to/from oldschool fullscreen then its layer may
3178 watch out tho, don't try change stacking stuff if the window is no
3179 longer being managed !
3181 if (self->managed &&
3182 (screen_find_monitor(&self->frame->area) !=
3183 screen_find_monitor(&oldframe) ||
3184 (final && (client_is_oldfullscreen(self, &oldclient) !=
3185 client_is_oldfullscreen(self, &self->area)))))
3187 client_calc_layer(self);
3191 void client_fullscreen(ObClient *self, gboolean fs)
3195 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3196 self->fullscreen == fs) return; /* already done */
3198 self->fullscreen = fs;
3199 client_change_state(self); /* change the state hints on the client */
3202 self->pre_fullscreen_area = self->area;
3203 self->pre_fullscreen_max_horz = self->max_horz;
3204 self->pre_fullscreen_max_vert = self->max_vert;
3206 /* if the window is maximized, its area isn't all that meaningful.
3207 save its premax area instead. */
3208 if (self->max_horz) {
3209 self->pre_fullscreen_area.x = self->pre_max_area.x;
3210 self->pre_fullscreen_area.width = self->pre_max_area.width;
3212 if (self->max_vert) {
3213 self->pre_fullscreen_area.y = self->pre_max_area.y;
3214 self->pre_fullscreen_area.height = self->pre_max_area.height;
3217 /* these will help configure_full figure out where to fullscreen
3221 w = self->area.width;
3222 h = self->area.height;
3224 g_assert(self->pre_fullscreen_area.width > 0 &&
3225 self->pre_fullscreen_area.height > 0);
3227 self->max_horz = self->pre_fullscreen_max_horz;
3228 self->max_vert = self->pre_fullscreen_max_vert;
3229 if (self->max_horz) {
3230 self->pre_max_area.x = self->pre_fullscreen_area.x;
3231 self->pre_max_area.width = self->pre_fullscreen_area.width;
3233 if (self->max_vert) {
3234 self->pre_max_area.y = self->pre_fullscreen_area.y;
3235 self->pre_max_area.height = self->pre_fullscreen_area.height;
3238 x = self->pre_fullscreen_area.x;
3239 y = self->pre_fullscreen_area.y;
3240 w = self->pre_fullscreen_area.width;
3241 h = self->pre_fullscreen_area.height;
3242 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3245 ob_debug("Window %s going fullscreen (%d)",
3246 self->title, self->fullscreen);
3249 /* make sure the window is on some monitor */
3250 client_find_onscreen(self, &x, &y, w, h, FALSE);
3253 client_setup_decor_and_functions(self, FALSE);
3254 client_move_resize(self, x, y, w, h);
3256 /* and adjust our layer/stacking. do this after resizing the window,
3257 and applying decorations, because windows which fill the screen are
3258 considered "fullscreen" and it affects their layer */
3259 client_calc_layer(self);
3262 /* try focus us when we go into fullscreen mode */
3267 static void client_iconify_recursive(ObClient *self,
3268 gboolean iconic, gboolean curdesk,
3269 gboolean hide_animation)
3272 gboolean changed = FALSE;
3274 if (self->iconic != iconic) {
3275 ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3279 /* don't let non-normal windows iconify along with their parents
3281 if (client_normal(self)) {
3282 self->iconic = iconic;
3284 /* update the focus lists.. iconic windows go to the bottom of
3285 the list. this will also call focus_cycle_addremove(). */
3286 focus_order_to_bottom(self);
3291 self->iconic = iconic;
3293 if (curdesk && self->desktop != screen_desktop &&
3294 self->desktop != DESKTOP_ALL)
3295 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3297 /* this puts it after the current focused window, this will
3298 also cause focus_cycle_addremove() to be called for the
3300 focus_order_like_new(self);
3307 client_change_state(self);
3308 if (config_animate_iconify && !hide_animation)
3309 frame_begin_iconify_animation(self->frame, iconic);
3310 /* do this after starting the animation so it doesn't flash */
3311 client_showhide(self);
3314 /* iconify all direct transients, and deiconify all transients
3316 for (it = self->transients; it; it = g_slist_next(it))
3317 if (it->data != self)
3318 if (client_is_direct_child(self, it->data) || !iconic)
3319 client_iconify_recursive(it->data, iconic, curdesk,
3323 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3324 gboolean hide_animation)
3326 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3327 /* move up the transient chain as far as possible first */
3328 self = client_search_top_direct_parent(self);
3329 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3333 void client_maximize(ObClient *self, gboolean max, gint dir)
3337 g_assert(dir == 0 || dir == 1 || dir == 2);
3338 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
3340 /* check if already done */
3342 if (dir == 0 && self->max_horz && self->max_vert) return;
3343 if (dir == 1 && self->max_horz) return;
3344 if (dir == 2 && self->max_vert) return;
3346 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3347 if (dir == 1 && !self->max_horz) return;
3348 if (dir == 2 && !self->max_vert) return;
3351 /* these will help configure_full figure out which screen to fill with
3355 w = self->area.width;
3356 h = self->area.height;
3359 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3360 RECT_SET(self->pre_max_area,
3361 self->area.x, self->pre_max_area.y,
3362 self->area.width, self->pre_max_area.height);
3364 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3365 RECT_SET(self->pre_max_area,
3366 self->pre_max_area.x, self->area.y,
3367 self->pre_max_area.width, self->area.height);
3370 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3371 g_assert(self->pre_max_area.width > 0);
3373 x = self->pre_max_area.x;
3374 w = self->pre_max_area.width;
3376 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3377 0, self->pre_max_area.height);
3379 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3380 g_assert(self->pre_max_area.height > 0);
3382 y = self->pre_max_area.y;
3383 h = self->pre_max_area.height;
3385 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3386 self->pre_max_area.width, 0);
3390 if (dir == 0 || dir == 1) /* horz */
3391 self->max_horz = max;
3392 if (dir == 0 || dir == 2) /* vert */
3393 self->max_vert = max;
3396 /* make sure the window is on some monitor */
3397 client_find_onscreen(self, &x, &y, w, h, FALSE);
3400 client_change_state(self); /* change the state hints on the client */
3402 client_setup_decor_and_functions(self, FALSE);
3403 client_move_resize(self, x, y, w, h);
3406 void client_shade(ObClient *self, gboolean shade)
3408 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3409 shade) || /* can't shade */
3410 self->shaded == shade) return; /* already done */
3412 self->shaded = shade;
3413 client_change_state(self);
3414 client_change_wm_state(self); /* the window is being hidden/shown */
3415 /* resize the frame to just the titlebar */
3416 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3419 static void client_ping_event(ObClient *self, gboolean dead)
3421 if (self->not_responding != dead) {
3422 self->not_responding = dead;
3423 client_update_title(self);
3426 /* the client isn't responding, so ask to kill it */
3427 client_prompt_kill(self);
3429 /* it came back to life ! */
3431 if (self->kill_prompt) {
3432 prompt_unref(self->kill_prompt);
3433 self->kill_prompt = NULL;
3436 self->kill_level = 0;
3441 void client_close(ObClient *self)
3443 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3445 /* if closing an internal obprompt, that is just cancelling it */
3447 prompt_cancel(self->prompt);
3451 /* in the case that the client provides no means to requesting that it
3452 close, we just kill it */
3453 if (!self->delete_window)
3454 /* don't use client_kill(), we should only kill based on PID in
3455 response to a lack of PING replies */
3456 XKillClient(obt_display, self->window);
3458 /* request the client to close with WM_DELETE_WINDOW */
3459 OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
3460 OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
3461 0, 0, 0, NoEventMask);
3463 /* we're trying to close the window, so see if it is responding. if it
3464 is not, then we will let them kill the window */
3466 ping_start(self, client_ping_event);
3468 /* if we already know the window isn't responding (maybe they clicked
3469 no in the kill dialog but it hasn't come back to life), then show
3471 if (self->not_responding)
3472 client_prompt_kill(self);
3476 #define OB_KILL_RESULT_NO 0
3477 #define OB_KILL_RESULT_YES 1
3479 static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
3481 ObClient *self = data;
3483 if (result == OB_KILL_RESULT_YES)
3485 return TRUE; /* call the cleanup func */
3488 static void client_kill_cleanup(ObPrompt *p, gpointer data)
3490 ObClient *self = data;
3492 g_assert(p == self->kill_prompt);
3494 prompt_unref(self->kill_prompt);
3495 self->kill_prompt = NULL;
3498 static void client_prompt_kill(ObClient *self)
3500 /* check if we're already prompting */
3501 if (!self->kill_prompt) {
3502 ObPromptAnswer answers[] = {
3503 { 0, OB_KILL_RESULT_NO },
3504 { 0, OB_KILL_RESULT_YES }
3507 const gchar *y, *title;
3509 title = self->original_title;
3510 if (title[0] == '\0') {
3511 /* empty string, so use its parent */
3512 ObClient *p = client_search_top_direct_parent(self);
3513 if (p) title = p->original_title;
3516 if (client_on_localhost(self)) {
3519 if (self->kill_level == 0)
3525 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3527 y = _("End Process");
3531 (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3533 y = _("Disconnect");
3535 /* set the dialog buttons' text */
3536 answers[0].text = _("Cancel"); /* "no" */
3537 answers[1].text = y; /* "yes" */
3539 self->kill_prompt = prompt_new(m, NULL, answers,
3540 sizeof(answers)/sizeof(answers[0]),
3541 OB_KILL_RESULT_NO, /* default = no */
3542 OB_KILL_RESULT_NO, /* cancel = no */
3543 client_kill_requested,
3544 client_kill_cleanup,
3549 prompt_show(self->kill_prompt, self, TRUE);
3552 void client_kill(ObClient *self)
3554 /* don't kill our own windows */
3555 if (self->prompt) return;
3557 if (client_on_localhost(self) && self->pid) {
3558 /* running on the local host */
3559 if (self->kill_level == 0) {
3560 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3561 self->window, self->pid);
3562 kill(self->pid, SIGTERM);
3565 /* show that we're trying to kill it */
3566 client_update_title(self);
3569 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3570 self->window, self->pid);
3571 kill(self->pid, SIGKILL); /* kill -9 */
3575 /* running on a remote host */
3576 XKillClient(obt_display, self->window);
3580 void client_hilite(ObClient *self, gboolean hilite)
3582 if (self->demands_attention == hilite)
3583 return; /* no change */
3585 /* don't allow focused windows to hilite */
3586 self->demands_attention = hilite && !client_focused(self);
3587 if (self->frame != NULL) { /* if we're mapping, just set the state */
3588 if (self->demands_attention) {
3589 frame_flash_start(self->frame);
3591 /* if the window is on another desktop then raise it and make it
3592 the most recently used window */
3593 if (self->desktop != screen_desktop &&
3594 self->desktop != DESKTOP_ALL)
3596 stacking_raise(CLIENT_AS_WINDOW(self));
3597 focus_order_to_top(self);
3601 frame_flash_stop(self->frame);
3602 client_change_state(self);
3606 static void client_set_desktop_recursive(ObClient *self,
3614 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3616 ob_debug("Setting desktop %u", target+1);
3618 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3620 old = self->desktop;
3621 self->desktop = target;
3622 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
3623 /* the frame can display the current desktop state */
3624 frame_adjust_state(self->frame);
3625 /* 'move' the window to the new desktop */
3629 /* raise if it was not already on the desktop */
3630 if (old != DESKTOP_ALL && !dontraise)
3631 stacking_raise(CLIENT_AS_WINDOW(self));
3632 if (STRUT_EXISTS(self->strut))
3633 screen_update_areas();
3635 /* the new desktop's geometry may be different, so we may need to
3636 resize, for example if we are maximized */
3637 client_reconfigure(self, FALSE);
3639 focus_cycle_addremove(self, FALSE);
3642 /* move all transients */
3643 for (it = self->transients; it; it = g_slist_next(it))
3644 if (it->data != self)
3645 if (client_is_direct_child(self, it->data))
3646 client_set_desktop_recursive(it->data, target,
3647 donthide, dontraise);
3650 void client_set_desktop(ObClient *self, guint target,
3651 gboolean donthide, gboolean dontraise)
3653 self = client_search_top_direct_parent(self);
3654 client_set_desktop_recursive(self, target, donthide, dontraise);
3656 focus_cycle_addremove(NULL, TRUE);
3659 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3661 while (child != parent && (child = client_direct_parent(child)));
3662 return child == parent;
3665 ObClient *client_search_modal_child(ObClient *self)
3670 for (it = self->transients; it; it = g_slist_next(it)) {
3671 ObClient *c = it->data;
3672 if ((ret = client_search_modal_child(c))) return ret;
3673 if (c->modal) return c;
3678 struct ObClientFindDestroyUnmap {
3683 static gboolean find_destroy_unmap(XEvent *e, gpointer data)
3685 struct ObClientFindDestroyUnmap *find = data;
3686 if (e->type == DestroyNotify)
3687 return e->xdestroywindow.window == find->window;
3688 if (e->type == UnmapNotify && e->xunmap.window == find->window)
3689 /* ignore the first $find->ignore_unmaps$ many unmap events */
3690 return --find->ignore_unmaps < 0;
3694 gboolean client_validate(ObClient *self)
3696 struct ObClientFindDestroyUnmap find;
3698 XSync(obt_display, FALSE); /* get all events on the server */
3700 find.window = self->window;
3701 find.ignore_unmaps = self->ignore_unmaps;
3702 if (xqueue_exists_local(find_destroy_unmap, &find))
3708 void client_set_wm_state(ObClient *self, glong state)
3710 if (state == self->wmstate) return; /* no change */
3714 client_iconify(self, TRUE, TRUE, FALSE);
3717 client_iconify(self, FALSE, TRUE, FALSE);
3722 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3724 gboolean shaded = self->shaded;
3725 gboolean fullscreen = self->fullscreen;
3726 gboolean undecorated = self->undecorated;
3727 gboolean max_horz = self->max_horz;
3728 gboolean max_vert = self->max_vert;
3729 gboolean modal = self->modal;
3730 gboolean iconic = self->iconic;
3731 gboolean demands_attention = self->demands_attention;
3732 gboolean above = self->above;
3733 gboolean below = self->below;
3737 if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3738 action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3739 action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3740 /* an invalid action was passed to the client message, ignore it */
3743 for (i = 0; i < 2; ++i) {
3744 Atom state = i == 0 ? data1 : data2;
3746 if (!state) continue;
3748 /* if toggling, then pick whether we're adding or removing */
3749 if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3750 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3752 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3753 value = self->max_vert;
3754 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3755 value = self->max_horz;
3756 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3758 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3759 value = self->skip_taskbar;
3760 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3761 value = self->skip_pager;
3762 else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3763 value = self->iconic;
3764 else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3766 else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3767 value = self->above;
3768 else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3769 value = self->below;
3770 else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3771 value = self->demands_attention;
3772 else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3773 value = undecorated;
3775 g_assert_not_reached();
3776 action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3777 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3780 value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
3781 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3783 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3785 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3787 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3789 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3790 self->skip_taskbar = value;
3791 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3792 self->skip_pager = value;
3793 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3795 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3797 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3799 /* only unset below when setting above, otherwise you can't get to
3803 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3804 /* and vice versa */
3808 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3809 demands_attention = value;
3810 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3811 undecorated = value;
3815 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3816 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3818 if (max_horz == max_vert) { /* both going the same way */
3819 client_maximize(self, max_horz, 0);
3821 client_maximize(self, max_horz, 1);
3822 client_maximize(self, max_vert, 2);
3826 if (max_horz != self->max_horz)
3827 client_maximize(self, max_horz, 1);
3829 client_maximize(self, max_vert, 2);
3832 /* change fullscreen state before shading, as it will affect if the window
3834 if (fullscreen != self->fullscreen)
3835 client_fullscreen(self, fullscreen);
3836 if (shaded != self->shaded)
3837 client_shade(self, shaded);
3838 if (undecorated != self->undecorated)
3839 client_set_undecorated(self, undecorated);
3840 if (above != self->above || below != self->below) {
3841 self->above = above;
3842 self->below = below;
3843 client_calc_layer(self);
3846 if (modal != self->modal) {
3847 self->modal = modal;
3848 /* when a window changes modality, then its stacking order with its
3849 transients needs to change */
3850 stacking_raise(CLIENT_AS_WINDOW(self));
3852 /* it also may get focused. if something is focused that shouldn't
3853 be focused anymore, then move the focus */
3854 if (focus_client && client_focus_target(focus_client) != focus_client)
3855 client_focus(focus_client);
3858 if (iconic != self->iconic)
3859 client_iconify(self, iconic, FALSE, FALSE);
3861 if (demands_attention != self->demands_attention)
3862 client_hilite(self, demands_attention);
3864 client_change_state(self); /* change the hint to reflect these changes */
3866 focus_cycle_addremove(self, TRUE);
3869 ObClient *client_focus_target(ObClient *self)
3871 ObClient *child = NULL;
3873 child = client_search_modal_child(self);
3874 if (child) return child;
3878 gboolean client_can_focus(ObClient *self)
3880 /* choose the correct target */
3881 self = client_focus_target(self);
3883 if (!self->frame->visible)
3886 if (!(self->can_focus || self->focus_notify))
3892 gboolean client_focus(ObClient *self)
3894 if (!client_validate(self)) return FALSE;
3896 /* we might not focus this window, so if we have modal children which would
3897 be focused instead, bring them to this desktop */
3898 client_bring_modal_windows(self);
3900 /* choose the correct target */
3901 self = client_focus_target(self);
3903 if (!client_can_focus(self)) {
3904 ob_debug_type(OB_DEBUG_FOCUS,
3905 "Client %s can't be focused", self->title);
3909 ob_debug_type(OB_DEBUG_FOCUS,
3910 "Focusing client \"%s\" (0x%x) at time %u",
3911 self->title, self->window, event_time());
3913 /* if using focus_delay, stop the timer now so that focus doesn't
3915 event_halt_focus_delay();
3917 obt_display_ignore_errors(TRUE);
3919 if (self->can_focus) {
3920 /* This can cause a BadMatch error with CurrentTime, or if an app
3921 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3922 XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
3926 if (self->focus_notify) {
3928 ce.xclient.type = ClientMessage;
3929 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
3930 ce.xclient.display = obt_display;
3931 ce.xclient.window = self->window;
3932 ce.xclient.format = 32;
3933 ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
3934 ce.xclient.data.l[1] = event_time();
3935 ce.xclient.data.l[2] = 0l;
3936 ce.xclient.data.l[3] = 0l;
3937 ce.xclient.data.l[4] = 0l;
3938 XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
3941 obt_display_ignore_errors(FALSE);
3943 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
3944 obt_display_error_occured);
3945 return !obt_display_error_occured;
3948 static void client_present(ObClient *self, gboolean here, gboolean raise,
3951 if (client_normal(self) && screen_showing_desktop)
3952 screen_show_desktop(FALSE, self);
3954 client_iconify(self, FALSE, here, FALSE);
3955 if (self->desktop != DESKTOP_ALL &&
3956 self->desktop != screen_desktop)
3959 client_set_desktop(self, screen_desktop, FALSE, TRUE);
3961 screen_set_desktop(self->desktop, FALSE);
3962 } else if (!self->frame->visible)
3963 /* if its not visible for other reasons, then don't mess
3966 if (self->shaded && unshade)
3967 client_shade(self, FALSE);
3969 stacking_raise(CLIENT_AS_WINDOW(self));
3974 /* this function exists to map to the net_active_window message in the ewmh */
3975 void client_activate(ObClient *self, gboolean desktop,
3976 gboolean here, gboolean raise,
3977 gboolean unshade, gboolean user)
3979 if ((user && (desktop ||
3980 self->desktop == DESKTOP_ALL ||
3981 self->desktop == screen_desktop)) ||
3982 client_can_steal_focus(self, desktop, event_time(), CurrentTime))
3984 client_present(self, here, raise, unshade);
3987 client_hilite(self, TRUE);
3990 static void client_bring_windows_recursive(ObClient *self,
3998 for (it = self->transients; it; it = g_slist_next(it))
3999 client_bring_windows_recursive(it->data, desktop,
4000 helpers, modals, iconic);
4002 if (((helpers && client_helper(self)) ||
4003 (modals && self->modal)) &&
4004 ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
4005 (iconic && self->iconic)))
4007 if (iconic && self->iconic)
4008 client_iconify(self, FALSE, TRUE, FALSE);
4010 client_set_desktop(self, desktop, FALSE, FALSE);
4014 void client_bring_helper_windows(ObClient *self)
4016 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
4019 void client_bring_modal_windows(ObClient *self)
4021 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
4024 gboolean client_focused(ObClient *self)
4026 return self == focus_client;
4029 RrImage* client_icon(ObClient *self)
4031 RrImage *ret = NULL;
4034 ret = self->icon_set;
4035 else if (self->parents) {
4037 for (it = self->parents; it && !ret; it = g_slist_next(it))
4038 ret = client_icon(it->data);
4041 ret = client_default_icon;
4045 void client_set_layer(ObClient *self, gint layer)
4049 self->above = FALSE;
4050 } else if (layer == 0) {
4051 self->below = self->above = FALSE;
4053 self->below = FALSE;
4056 client_calc_layer(self);
4057 client_change_state(self); /* reflect this in the state hints */
4060 void client_set_undecorated(ObClient *self, gboolean undecorated)
4062 if (self->undecorated != undecorated &&
4063 /* don't let it undecorate if the function is missing, but let
4065 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4067 self->undecorated = undecorated;
4068 client_setup_decor_and_functions(self, TRUE);
4069 client_change_state(self); /* reflect this in the state hints */
4073 guint client_monitor(ObClient *self)
4075 return screen_find_monitor(&self->frame->area);
4078 ObClient *client_direct_parent(ObClient *self)
4080 if (!self->parents) return NULL;
4081 if (self->transient_for_group) return NULL;
4082 return self->parents->data;
4085 ObClient *client_search_top_direct_parent(ObClient *self)
4088 while ((p = client_direct_parent(self))) self = p;
4092 static GSList *client_search_all_top_parents_internal(ObClient *self,
4094 ObStackingLayer layer)
4099 /* move up the direct transient chain as far as possible */
4100 while ((p = client_direct_parent(self)) &&
4101 (!bylayer || p->layer == layer))
4105 ret = g_slist_prepend(NULL, self);
4107 ret = g_slist_copy(self->parents);
4112 GSList *client_search_all_top_parents(ObClient *self)
4114 return client_search_all_top_parents_internal(self, FALSE, 0);
4117 GSList *client_search_all_top_parents_layer(ObClient *self)
4119 return client_search_all_top_parents_internal(self, TRUE, self->layer);
4122 ObClient *client_search_focus_parent(ObClient *self)
4126 for (it = self->parents; it; it = g_slist_next(it))
4127 if (client_focused(it->data)) return it->data;
4132 ObClient *client_search_focus_parent_full(ObClient *self)
4135 ObClient *ret = NULL;
4137 for (it = self->parents; it; it = g_slist_next(it)) {
4138 if (client_focused(it->data))
4141 ret = client_search_focus_parent_full(it->data);
4147 ObClient *client_search_parent(ObClient *self, ObClient *search)
4151 for (it = self->parents; it; it = g_slist_next(it))
4152 if (it->data == search) return search;
4157 ObClient *client_search_transient(ObClient *self, ObClient *search)
4161 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4162 if (sit->data == search)
4164 if (client_search_transient(sit->data, search))
4170 static void detect_edge(Rect area, ObDirection dir,
4171 gint my_head, gint my_size,
4172 gint my_edge_start, gint my_edge_size,
4173 gint *dest, gboolean *near_edge)
4175 gint edge_start, edge_size, head, tail;
4176 gboolean skip_head = FALSE, skip_tail = FALSE;
4179 case OB_DIRECTION_NORTH:
4180 case OB_DIRECTION_SOUTH:
4181 edge_start = area.x;
4182 edge_size = area.width;
4184 case OB_DIRECTION_EAST:
4185 case OB_DIRECTION_WEST:
4186 edge_start = area.y;
4187 edge_size = area.height;
4190 g_assert_not_reached();
4193 /* do we collide with this window? */
4194 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4195 edge_start, edge_size))
4199 case OB_DIRECTION_NORTH:
4200 head = RECT_BOTTOM(area);
4201 tail = RECT_TOP(area);
4203 case OB_DIRECTION_SOUTH:
4204 head = RECT_TOP(area);
4205 tail = RECT_BOTTOM(area);
4207 case OB_DIRECTION_WEST:
4208 head = RECT_RIGHT(area);
4209 tail = RECT_LEFT(area);
4211 case OB_DIRECTION_EAST:
4212 head = RECT_LEFT(area);
4213 tail = RECT_RIGHT(area);
4216 g_assert_not_reached();
4219 case OB_DIRECTION_NORTH:
4220 case OB_DIRECTION_WEST:
4221 /* check if our window is past the head of this window */
4222 if (my_head <= head + 1)
4224 /* check if our window's tail is past the tail of this window */
4225 if (my_head + my_size - 1 <= tail)
4227 /* check if the head of this window is closer than the previously
4228 chosen edge (take into account that the previously chosen
4229 edge might have been a tail, not a head) */
4230 if (head + (*near_edge ? 0 : my_size) <= *dest)
4232 /* check if the tail of this window is closer than the previously
4233 chosen edge (take into account that the previously chosen
4234 edge might have been a head, not a tail) */
4235 if (tail - (!*near_edge ? 0 : my_size) <= *dest)
4238 case OB_DIRECTION_SOUTH:
4239 case OB_DIRECTION_EAST:
4240 /* check if our window is past the head of this window */
4241 if (my_head >= head - 1)
4243 /* check if our window's tail is past the tail of this window */
4244 if (my_head - my_size + 1 >= tail)
4246 /* check if the head of this window is closer than the previously
4247 chosen edge (take into account that the previously chosen
4248 edge might have been a tail, not a head) */
4249 if (head - (*near_edge ? 0 : my_size) >= *dest)
4251 /* check if the tail of this window is closer than the previously
4252 chosen edge (take into account that the previously chosen
4253 edge might have been a head, not a tail) */
4254 if (tail + (!*near_edge ? 0 : my_size) >= *dest)
4258 g_assert_not_reached();
4261 ob_debug("my head %d size %d", my_head, my_size);
4262 ob_debug("head %d tail %d dest %d", head, tail, *dest);
4264 ob_debug("using near edge %d", head);
4268 else if (!skip_tail) {
4269 ob_debug("using far edge %d", tail);
4275 void client_find_edge_directional(ObClient *self, ObDirection dir,
4276 gint my_head, gint my_size,
4277 gint my_edge_start, gint my_edge_size,
4278 gint *dest, gboolean *near_edge)
4286 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4287 &self->frame->area);
4290 case OB_DIRECTION_NORTH:
4291 edge = RECT_TOP(*a) - 1;
4293 case OB_DIRECTION_SOUTH:
4294 edge = RECT_BOTTOM(*a) + 1;
4296 case OB_DIRECTION_EAST:
4297 edge = RECT_RIGHT(*a) + 1;
4299 case OB_DIRECTION_WEST:
4300 edge = RECT_LEFT(*a) - 1;
4303 g_assert_not_reached();
4305 /* default to the far edge, then narrow it down */
4309 /* search for edges of monitors */
4310 for (i = 0; i < screen_num_monitors; ++i) {
4311 Rect *area = screen_area(self->desktop, i, NULL);
4312 detect_edge(*area, dir, my_head, my_size, my_edge_start,
4313 my_edge_size, dest, near_edge);
4314 g_slice_free(Rect, area);
4317 /* search for edges of clients */
4318 if (((dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH) &&
4320 ((dir == OB_DIRECTION_EAST || dir == OB_DIRECTION_WEST) &&
4323 for (it = client_list; it; it = g_list_next(it)) {
4324 ObClient *cur = it->data;
4326 /* skip windows to not bump into */
4331 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4332 cur->desktop != screen_desktop)
4335 ob_debug("trying window %s", cur->title);
4337 detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
4338 my_edge_size, dest, near_edge);
4340 dock_get_area(&dock_area);
4341 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4342 my_edge_size, dest, near_edge);
4345 g_slice_free(Rect, a);
4348 void client_find_move_directional(ObClient *self, ObDirection dir,
4352 gint e, e_start, e_size;
4356 case OB_DIRECTION_EAST:
4357 head = RECT_RIGHT(self->frame->area);
4358 size = self->frame->area.width;
4359 e_start = RECT_TOP(self->frame->area);
4360 e_size = self->frame->area.height;
4362 case OB_DIRECTION_WEST:
4363 head = RECT_LEFT(self->frame->area);
4364 size = self->frame->area.width;
4365 e_start = RECT_TOP(self->frame->area);
4366 e_size = self->frame->area.height;
4368 case OB_DIRECTION_NORTH:
4369 head = RECT_TOP(self->frame->area);
4370 size = self->frame->area.height;
4371 e_start = RECT_LEFT(self->frame->area);
4372 e_size = self->frame->area.width;
4374 case OB_DIRECTION_SOUTH:
4375 head = RECT_BOTTOM(self->frame->area);
4376 size = self->frame->area.height;
4377 e_start = RECT_LEFT(self->frame->area);
4378 e_size = self->frame->area.width;
4381 g_assert_not_reached();
4384 client_find_edge_directional(self, dir, head, size,
4385 e_start, e_size, &e, &near);
4386 *x = self->frame->area.x;
4387 *y = self->frame->area.y;
4389 case OB_DIRECTION_EAST:
4390 if (near) e -= self->frame->area.width;
4394 case OB_DIRECTION_WEST:
4396 else e -= self->frame->area.width;
4399 case OB_DIRECTION_NORTH:
4401 else e -= self->frame->area.height;
4404 case OB_DIRECTION_SOUTH:
4405 if (near) e -= self->frame->area.height;
4410 g_assert_not_reached();
4412 frame_frame_gravity(self->frame, x, y);
4415 void client_find_resize_directional(ObClient *self, ObDirection side,
4417 gint *x, gint *y, gint *w, gint *h)
4420 gint e, e_start, e_size, delta;
4425 case OB_DIRECTION_EAST:
4426 head = RECT_RIGHT(self->frame->area) +
4427 (self->size_inc.width - 1) * (grow ? 1 : 0);
4428 e_start = RECT_TOP(self->frame->area);
4429 e_size = self->frame->area.height;
4430 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4432 case OB_DIRECTION_WEST:
4433 head = RECT_LEFT(self->frame->area) -
4434 (self->size_inc.width - 1) * (grow ? 1 : 0);
4435 e_start = RECT_TOP(self->frame->area);
4436 e_size = self->frame->area.height;
4437 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4439 case OB_DIRECTION_NORTH:
4440 head = RECT_TOP(self->frame->area) -
4441 (self->size_inc.height - 1) * (grow ? 1 : 0);
4442 e_start = RECT_LEFT(self->frame->area);
4443 e_size = self->frame->area.width;
4444 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4446 case OB_DIRECTION_SOUTH:
4447 head = RECT_BOTTOM(self->frame->area) +
4448 (self->size_inc.height - 1) * (grow ? 1 : 0);
4449 e_start = RECT_LEFT(self->frame->area);
4450 e_size = self->frame->area.width;
4451 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4454 g_assert_not_reached();
4457 ob_debug("head %d dir %d", head, dir);
4458 client_find_edge_directional(self, dir, head, 1,
4459 e_start, e_size, &e, &near);
4460 ob_debug("edge %d", e);
4461 *x = self->frame->area.x;
4462 *y = self->frame->area.y;
4463 *w = self->frame->area.width;
4464 *h = self->frame->area.height;
4466 case OB_DIRECTION_EAST:
4467 if (grow == near) --e;
4468 delta = e - RECT_RIGHT(self->frame->area);
4471 case OB_DIRECTION_WEST:
4472 if (grow == near) ++e;
4473 delta = RECT_LEFT(self->frame->area) - e;
4477 case OB_DIRECTION_NORTH:
4478 if (grow == near) ++e;
4479 delta = RECT_TOP(self->frame->area) - e;
4483 case OB_DIRECTION_SOUTH:
4484 if (grow == near) --e;
4485 delta = e - RECT_BOTTOM(self->frame->area);
4489 g_assert_not_reached();
4491 frame_frame_gravity(self->frame, x, y);
4492 *w -= self->frame->size.left + self->frame->size.right;
4493 *h -= self->frame->size.top + self->frame->size.bottom;
4496 ObClient* client_under_pointer(void)
4500 ObClient *ret = NULL;
4502 if (screen_pointer_pos(&x, &y)) {
4503 for (it = stacking_list; it; it = g_list_next(it)) {
4504 if (WINDOW_IS_CLIENT(it->data)) {
4505 ObClient *c = WINDOW_AS_CLIENT(it->data);
4506 if (c->frame->visible &&
4507 /* check the desktop, this is done during desktop
4508 switching and windows are shown/hidden status is not
4510 (c->desktop == screen_desktop ||
4511 c->desktop == DESKTOP_ALL) &&
4512 /* ignore all animating windows */
4513 !frame_iconify_animating(c->frame) &&
4514 RECT_CONTAINS(c->frame->area, x, y))
4525 gboolean client_has_group_siblings(ObClient *self)
4527 return self->group && self->group->members->next;
4530 /*! Returns TRUE if the client is running on the same machine as Openbox */
4531 gboolean client_on_localhost(ObClient *self)
4533 return self->client_machine == NULL;