Don't make desktop switching interactive when no mods used. (Fix bug #5203)
[openbox.git] / openbox / client.c
blobf3b4bdac905aac72d2b163283258850b46d629de
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.
20 #include "client.h"
21 #include "debug.h"
22 #include "startupnotify.h"
23 #include "dock.h"
24 #include "screen.h"
25 #include "moveresize.h"
26 #include "ping.h"
27 #include "place.h"
28 #include "frame.h"
29 #include "session.h"
30 #include "event.h"
31 #include "grab.h"
32 #include "prompt.h"
33 #include "focus.h"
34 #include "focus_cycle.h"
35 #include "stacking.h"
36 #include "openbox.h"
37 #include "group.h"
38 #include "config.h"
39 #include "menuframe.h"
40 #include "keyboard.h"
41 #include "mouse.h"
42 #include "obrender/render.h"
43 #include "gettext.h"
44 #include "obt/display.h"
45 #include "obt/xqueue.h"
46 #include "obt/prop.h"
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif
52 #ifdef HAVE_SIGNAL_H
53 # include <signal.h> /* for kill() */
54 #endif
56 #include <glib.h>
57 #include <X11/Xutil.h>
59 /*! The event mask to grab on client windows */
60 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
61 ColormapChangeMask)
63 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
64 ButtonMotionMask)
66 typedef struct
68 ObClientCallback func;
69 gpointer data;
70 } ClientCallback;
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,
87 guint target,
88 gboolean donthide,
89 gboolean dontraise);
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,
101 ObClient* oldparent,
102 ObClient *newparent);
103 static void client_present(ObClient *self, gboolean here, gboolean raise,
104 gboolean unshade);
105 static GSList *client_search_all_top_parents_internal(ObClient *self,
106 gboolean bylayer,
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 gboolean request_from_user,
114 Time steal_time, Time launch_time);
115 static void client_setup_default_decor_and_functions(ObClient *self);
116 static void client_setup_decor_undecorated(ObClient *self);
118 void client_startup(gboolean reconfig)
120 client_default_icon = RrImageNewFromData(
121 ob_rr_icons, ob_rr_theme->def_win_icon,
122 ob_rr_theme->def_win_icon_w, ob_rr_theme->def_win_icon_h);
124 if (reconfig) return;
126 client_set_list();
129 void client_shutdown(gboolean reconfig)
131 RrImageUnref(client_default_icon);
132 client_default_icon = NULL;
134 if (reconfig) return;
137 static void client_call_notifies(ObClient *self, GSList *list)
139 GSList *it;
141 for (it = list; it; it = g_slist_next(it)) {
142 ClientCallback *d = it->data;
143 d->func(self, d->data);
147 void client_add_destroy_notify(ObClientCallback func, gpointer data)
149 ClientCallback *d = g_slice_new(ClientCallback);
150 d->func = func;
151 d->data = data;
152 client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
155 void client_remove_destroy_notify(ObClientCallback func)
157 GSList *it;
159 for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
160 ClientCallback *d = it->data;
161 if (d->func == func) {
162 g_slice_free(ClientCallback, d);
163 client_destroy_notifies =
164 g_slist_delete_link(client_destroy_notifies, it);
165 break;
170 void client_set_list(void)
172 Window *windows, *win_it;
173 GList *it;
174 guint size = g_list_length(client_list);
176 /* create an array of the window ids */
177 if (size > 0) {
178 windows = g_new(Window, size);
179 win_it = windows;
180 for (it = client_list; it; it = g_list_next(it), ++win_it)
181 *win_it = ((ObClient*)it->data)->window;
182 } else
183 windows = NULL;
185 OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
186 (gulong*)windows, size);
188 if (windows)
189 g_free(windows);
191 stacking_set_list();
194 void client_manage(Window window, ObPrompt *prompt)
196 ObClient *self;
197 XSetWindowAttributes attrib_set;
198 gboolean try_activate = FALSE;
199 gboolean do_activate;
200 ObAppSettings *settings;
201 gboolean transient = FALSE;
202 Rect place;
203 Time launch_time;
204 guint32 user_time;
205 gboolean obplaced;
207 ob_debug("Managing window: 0x%lx", window);
209 /* choose the events we want to receive on the CLIENT window
210 (ObPrompt windows can request events too) */
211 attrib_set.event_mask = CLIENT_EVENTMASK |
212 (prompt ? prompt->event_mask : 0);
213 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
214 XChangeWindowAttributes(obt_display, window,
215 CWEventMask|CWDontPropagate, &attrib_set);
217 /* create the ObClient struct, and populate it from the hints on the
218 window */
219 self = g_slice_new0(ObClient);
220 self->obwin.type = OB_WINDOW_CLASS_CLIENT;
221 self->window = window;
222 self->prompt = prompt;
223 self->managed = TRUE;
225 /* non-zero defaults */
226 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
227 self->gravity = NorthWestGravity;
228 self->desktop = screen_num_desktops; /* always an invalid value */
230 /* get all the stuff off the window */
231 client_get_all(self, TRUE);
233 ob_debug("Window type: %d", self->type);
234 ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
235 ob_debug("Window name: %s class: %s role: %s title: %s",
236 self->name, self->class, self->role, self->title);
238 /* per-app settings override stuff from client_get_all, and return the
239 settings for other uses too. the returned settings is a shallow copy,
240 that needs to be freed with g_free(). */
241 settings = client_get_settings_state(self);
243 /* the session should get the last say though */
244 client_restore_session_state(self);
246 /* the per-app settings/session may have changed the decorations for
247 the window, so we setup decorations for that here. this is a special
248 case because we want to place the window according to these decoration
249 changes.
250 we do this before setting up the frame so that it will reflect the
251 decorations of the window as it will be placed on screen.
253 client_setup_decor_undecorated(self);
255 /* specify that if we exit, the window should not be destroyed and
256 should be reparented back to root automatically, unless we are managing
257 an internal ObPrompt window */
258 if (!self->prompt)
259 XChangeSaveSet(obt_display, window, SetModeInsert);
261 /* create the decoration frame for the client window */
262 self->frame = frame_new(self);
264 frame_grab_client(self->frame);
266 /* we've grabbed everything and set everything that we need to at mapping
267 time now */
268 grab_server(FALSE);
270 /* this needs to occur once we have a frame, since it sets a property on
271 the frame */
272 client_update_opacity(self);
274 /* don't put helper/modal windows on a different desktop if they are
275 related to the focused window. */
276 if (!screen_compare_desktops(self->desktop, screen_desktop) &&
277 focus_client && client_search_transient(focus_client, self) &&
278 (client_helper(self) || self->modal))
280 self->desktop = screen_desktop;
283 /* tell startup notification that this app started */
284 launch_time = sn_app_started(self->startup_id, self->class, self->name);
286 if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
287 user_time = event_time();
289 /* do this after we have a frame.. it uses the frame to help determine the
290 WM_STATE to apply. */
291 client_change_state(self);
293 /* add ourselves to the focus order */
294 focus_order_add_new(self);
296 /* do this to add ourselves to the stacking list in a non-intrusive way */
297 client_calc_layer(self);
299 /* focus the new window? */
300 if (ob_state() != OB_STATE_STARTING &&
301 (!self->session || self->session->focused) &&
302 /* this means focus=true for window is same as config_focus_new=true */
303 ((config_focus_new || settings->focus == 1) ||
304 client_search_focus_tree_full(self)) &&
305 /* NET_WM_USER_TIME 0 when mapping means don't focus */
306 (user_time != 0) &&
307 /* this checks for focus=false for the window */
308 settings->focus != 0 &&
309 focus_valid_target(self, self->desktop,
310 FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
311 settings->focus == 1))
313 try_activate = TRUE;
316 /* remove the client's border */
317 XSetWindowBorderWidth(obt_display, self->window, 0);
319 /* adjust the frame to the client's size before showing or placing
320 the window */
321 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
322 frame_adjust_client_area(self->frame);
324 /* where the frame was placed is where the window was originally */
325 place = self->area;
327 ob_debug("Going to try activate new window? %s",
328 try_activate ? "yes" : "no");
329 if (try_activate)
330 do_activate = client_can_steal_focus(
331 self, settings->focus == 1,
332 (!!launch_time || settings->focus == 1),
333 event_time(), launch_time);
334 else
335 do_activate = FALSE;
337 /* figure out placement for the window if the window is new */
338 if (ob_state() == OB_STATE_RUNNING) {
339 ob_debug("Positioned: %s @ %d %d",
340 (!self->positioned ? "no" :
341 (self->positioned == PPosition ? "program specified" :
342 (self->positioned == USPosition ? "user specified" :
343 (self->positioned == (PPosition | USPosition) ?
344 "program + user specified" :
345 "BADNESS !?")))), place.x, place.y);
347 ob_debug("Sized: %s @ %d %d",
348 (!self->sized ? "no" :
349 (self->sized == PSize ? "program specified" :
350 (self->sized == USSize ? "user specified" :
351 (self->sized == (PSize | USSize) ?
352 "program + user specified" :
353 "BADNESS !?")))), place.width, place.height);
355 obplaced = place_client(self, do_activate, &place.x, &place.y,
356 settings);
358 /* watch for buggy apps that ask to be placed at (0,0) when there is
359 a strut there */
360 if (!obplaced && place.x == 0 && place.y == 0 &&
361 /* non-normal windows are allowed */
362 client_normal(self) &&
363 /* oldschool fullscreen windows are allowed */
364 !client_is_oldfullscreen(self, &place))
366 Rect *r;
368 r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
369 if (r->x || r->y) {
370 place.x = r->x;
371 place.y = r->y;
372 ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
374 g_slice_free(Rect, r);
377 /* make sure the window is visible. */
378 client_find_onscreen(self, &place.x, &place.y,
379 place.width, place.height,
380 /* non-normal clients has less rules, and
381 windows that are being restored from a
382 session do also. we can assume you want
383 it back where you saved it. Clients saying
384 they placed themselves are subjected to
385 harder rules, ones that are placed by
386 place.c or by the user are allowed partially
387 off-screen and on xinerama divides (ie,
388 it is up to the placement routines to avoid
389 the xinerama divides)
391 children and splash screens are forced on
392 screen, but i don't remember why i decided to
393 do that.
395 ob_state() == OB_STATE_RUNNING &&
396 (self->type == OB_CLIENT_TYPE_DIALOG ||
397 self->type == OB_CLIENT_TYPE_SPLASH ||
398 (!((self->positioned & USPosition) ||
399 settings->pos_given) &&
400 client_normal(self) &&
401 !self->session &&
402 /* don't move oldschool fullscreen windows to
403 fit inside the struts (fixes Acroread, which
404 makes its fullscreen window fit the screen
405 but it is not USSize'd or USPosition'd) */
406 !client_is_oldfullscreen(self, &place))));
409 /* if the window isn't user-sized, then make it fit inside
410 the visible screen area on its monitor. Use basically the same rules
411 for forcing the window on screen in the client_find_onscreen call.
413 do this after place_client, it chooses the monitor!
415 splash screens get "transient" set to TRUE by
416 the place_client call
418 if (ob_state() == OB_STATE_RUNNING &&
419 (transient ||
420 (!(self->sized & USSize || self->positioned & USPosition) &&
421 client_normal(self) &&
422 !self->session &&
423 /* don't shrink oldschool fullscreen windows to fit inside the
424 struts (fixes Acroread, which makes its fullscreen window
425 fit the screen but it is not USSize'd or USPosition'd) */
426 !client_is_oldfullscreen(self, &place))))
428 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
430 /* get the size of the frame */
431 place.width += self->frame->size.left + self->frame->size.right;
432 place.height += self->frame->size.top + self->frame->size.bottom;
434 /* fit the window inside the area */
435 place.width = MIN(place.width, a->width);
436 place.height = MIN(place.height, a->height);
438 ob_debug("setting window size to %dx%d", place.width, place.height);
440 /* get the size of the client back */
441 place.width -= self->frame->size.left + self->frame->size.right;
442 place.height -= self->frame->size.top + self->frame->size.bottom;
444 g_slice_free(Rect, a);
447 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
448 "some restrictions may apply",
449 self->window, place.x, place.y, place.width, place.height);
450 if (self->session)
451 ob_debug(" but session requested %d, %d %d x %d instead, "
452 "overriding",
453 self->session->x, self->session->y,
454 self->session->w, self->session->h);
456 /* do this after the window is placed, so the premax/prefullscreen numbers
457 won't be all wacko!!
459 this also places the window
461 client_apply_startup_state(self, place.x, place.y,
462 place.width, place.height);
464 /* set the initial value of the desktop hint, when one wasn't requested
465 on map. */
466 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
468 /* grab mouse bindings before showing the window */
469 mouse_grab_for_client(self, TRUE);
471 /* this has to happen before we try focus the window, but we want it to
472 happen after the client's stacking has been determined or it looks bad
475 gulong ignore_start;
476 if (!config_focus_under_mouse)
477 ignore_start = event_start_ignore_all_enters();
479 client_show(self);
481 if (!config_focus_under_mouse)
482 event_end_ignore_all_enters(ignore_start);
485 /* activate/hilight/raise the window */
486 if (try_activate) {
487 if (do_activate) {
488 gboolean stacked = client_restore_session_stacking(self);
489 client_present(self, FALSE, !stacked, TRUE);
491 else {
492 /* if the client isn't stealing focus, then hilite it so the user
493 knows it is there, but don't do this if we're restoring from a
494 session */
495 if (!client_restore_session_stacking(self))
496 client_hilite(self, TRUE);
499 else {
500 /* This may look rather odd. Well it's because new windows are added
501 to the stacking order non-intrusively. If we're not going to focus
502 the new window or hilite it, then we raise it to the top. This will
503 take affect for things that don't get focused like splash screens.
504 Also if you don't have focus_new enabled, then it's going to get
505 raised to the top. Legacy begets legacy I guess?
507 if (!client_restore_session_stacking(self))
508 stacking_raise(CLIENT_AS_WINDOW(self));
511 /* add to client list/map */
512 client_list = g_list_append(client_list, self);
513 window_add(&self->window, CLIENT_AS_WINDOW(self));
515 /* this has to happen after we're in the client_list */
516 if (STRUT_EXISTS(self->strut))
517 screen_update_areas();
519 /* update the list hints */
520 client_set_list();
522 /* free the ObAppSettings shallow copy */
523 g_slice_free(ObAppSettings, settings);
525 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
526 window, self->frame->window, self->class);
529 ObClient *client_fake_manage(Window window)
531 ObClient *self;
532 ObAppSettings *settings;
534 ob_debug("Pretend-managing window: %lx", window);
536 /* do this minimal stuff to figure out the client's decorations */
538 self = g_slice_new0(ObClient);
539 self->window = window;
541 client_get_all(self, FALSE);
542 /* per-app settings override stuff, and return the settings for other
543 uses too. this returns a shallow copy that needs to be freed */
544 settings = client_get_settings_state(self);
546 /* create the decoration frame for the client window and adjust its size */
547 self->frame = frame_new(self);
549 client_apply_startup_state(self, self->area.x, self->area.y,
550 self->area.width, self->area.height);
552 ob_debug("gave extents left %d right %d top %d bottom %d",
553 self->frame->size.left, self->frame->size.right,
554 self->frame->size.top, self->frame->size.bottom);
556 /* free the ObAppSettings shallow copy */
557 g_slice_free(ObAppSettings, settings);
559 return self;
562 void client_unmanage_all(void)
564 while (client_list)
565 client_unmanage(client_list->data);
568 void client_unmanage(ObClient *self)
570 GSList *it;
571 gulong ignore_start;
573 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
574 self->window, self->frame->window,
575 self->class, self->title ? self->title : "");
577 g_assert(self != NULL);
579 /* we dont want events no more. do this before hiding the frame so we
580 don't generate more events */
581 XSelectInput(obt_display, self->window, NoEventMask);
583 /* ignore enter events from the unmap so it doesnt mess with the focus */
584 if (!config_focus_under_mouse)
585 ignore_start = event_start_ignore_all_enters();
587 frame_hide(self->frame);
588 /* flush to send the hide to the server quickly */
589 XFlush(obt_display);
591 if (!config_focus_under_mouse)
592 event_end_ignore_all_enters(ignore_start);
594 mouse_grab_for_client(self, FALSE);
596 self->managed = FALSE;
598 /* remove the window from our save set, unless we are managing an internal
599 ObPrompt window */
600 if (!self->prompt)
601 XChangeSaveSet(obt_display, self->window, SetModeDelete);
603 /* update the focus lists */
604 focus_order_remove(self);
605 if (client_focused(self)) {
606 /* don't leave an invalid focus_client */
607 focus_client = NULL;
610 /* if we're prompting to kill the client, close that */
611 prompt_unref(self->kill_prompt);
612 self->kill_prompt = NULL;
614 client_list = g_list_remove(client_list, self);
615 stacking_remove(self);
616 window_remove(self->window);
618 /* once the client is out of the list, update the struts to remove its
619 influence */
620 if (STRUT_EXISTS(self->strut))
621 screen_update_areas();
623 client_call_notifies(self, client_destroy_notifies);
625 /* tell our parent(s) that we're gone */
626 for (it = self->parents; it; it = g_slist_next(it))
627 ((ObClient*)it->data)->transients =
628 g_slist_remove(((ObClient*)it->data)->transients,self);
630 /* tell our transients that we're gone */
631 for (it = self->transients; it; it = g_slist_next(it)) {
632 ((ObClient*)it->data)->parents =
633 g_slist_remove(((ObClient*)it->data)->parents, self);
634 /* we could be keeping our children in a higher layer */
635 client_calc_layer(it->data);
638 /* remove from its group */
639 if (self->group) {
640 group_remove(self->group, self);
641 self->group = NULL;
644 /* restore the window's original geometry so it is not lost */
646 Rect a;
648 a = self->area;
650 if (self->fullscreen)
651 a = self->pre_fullscreen_area;
652 else if (self->max_horz || self->max_vert) {
653 if (self->max_horz) {
654 a.x = self->pre_max_area.x;
655 a.width = self->pre_max_area.width;
657 if (self->max_vert) {
658 a.y = self->pre_max_area.y;
659 a.height = self->pre_max_area.height;
663 self->fullscreen = self->max_horz = self->max_vert = FALSE;
664 /* let it be moved and resized no matter what */
665 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
666 self->decorations = 0; /* unmanaged windows have no decor */
668 /* give the client its border back */
669 XSetWindowBorderWidth(obt_display, self->window, self->border_width);
671 client_move_resize(self, a.x, a.y, a.width, a.height);
674 /* reparent the window out of the frame, and free the frame */
675 frame_release_client(self->frame);
676 frame_free(self->frame);
677 self->frame = NULL;
679 if (ob_state() != OB_STATE_EXITING) {
680 /* these values should not be persisted across a window
681 unmapping/mapping */
682 OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
683 OBT_PROP_ERASE(self->window, NET_WM_STATE);
684 OBT_PROP_ERASE(self->window, WM_STATE);
685 } else {
686 /* if we're left in an unmapped state, the client wont be mapped.
687 this is bad, since we will no longer be managing the window on
688 restart */
689 XMapWindow(obt_display, self->window);
692 /* these should not be left on the window ever. other window managers
693 don't necessarily use them and it will mess them up (like compiz) */
694 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
695 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
697 /* update the list hints */
698 client_set_list();
700 ob_debug("Unmanaged window 0x%lx", self->window);
702 /* free all data allocated in the client struct */
703 RrImageUnref(self->icon_set);
704 g_slist_free(self->transients);
705 g_free(self->startup_id);
706 g_free(self->wm_command);
707 g_free(self->title);
708 g_free(self->icon_title);
709 g_free(self->original_title);
710 g_free(self->name);
711 g_free(self->class);
712 g_free(self->role);
713 g_free(self->client_machine);
714 g_free(self->sm_client_id);
715 g_slice_free(ObClient, self);
718 void client_fake_unmanage(ObClient *self)
720 /* this is all that got allocated to get the decorations */
722 frame_free(self->frame);
723 g_slice_free(ObClient, self);
726 static gboolean client_can_steal_focus(ObClient *self,
727 gboolean allow_other_desktop,
728 gboolean request_from_user,
729 Time steal_time,
730 Time launch_time)
732 gboolean steal;
733 gboolean relative_focused;
735 steal = TRUE;
737 relative_focused = (focus_client != NULL &&
738 (client_search_focus_tree_full(self) != NULL ||
739 client_search_focus_group_full(self) != NULL));
741 /* This is focus stealing prevention */
742 ob_debug("Want to focus window 0x%x at time %u "
743 "launched at %u (last user interaction time %u) "
744 "request from %s, allow other desktop: %s, "
745 "desktop switch time %u",
746 self->window, steal_time, launch_time,
747 event_last_user_time,
748 (request_from_user ? "user" : "other"),
749 (allow_other_desktop ? "yes" : "no"),
750 screen_desktop_user_time);
753 if no launch time is provided for an application, make one up.
755 if the window is related to other existing windows
756 and one of those windows was the last used
757 then we will give it a launch time equal to the last user time,
758 which will end up giving the window focus probably.
759 else
760 the window is related to other windows, but you are not working in
761 them?
762 seems suspicious, so we will give it a launch time of
763 NOW - STEAL_INTERVAL,
764 so it will be given focus only if we didn't use something else
765 during the steal interval.
766 else
767 the window is all on its own, so we can't judge it. give it a launch
768 time equal to the last user time, so it will probably take focus.
770 this way running things from a terminal will give them focus, but popups
771 without a launch time shouldn't steal focus so easily.
774 if (!launch_time) {
775 if (client_has_relative(self)) {
776 if (event_last_user_time && client_search_focus_group_full(self)) {
777 /* our relative is focused */
778 launch_time = event_last_user_time;
779 ob_debug("Unknown launch time, using %u - window in active "
780 "group", launch_time);
782 else if (!request_from_user) {
783 /* has relatives which are not being used. suspicious */
784 launch_time = event_time() - OB_EVENT_USER_TIME_DELAY;
785 ob_debug("Unknown launch time, using %u - window in inactive "
786 "group", launch_time);
788 else {
789 /* has relatives which are not being used, but the user seems
790 to want to go there! */
791 launch_time = event_last_user_time;
792 ob_debug("Unknown launch time, using %u - user request",
793 launch_time);
796 else {
797 /* the window is on its own, probably the user knows it is going
798 to appear */
799 launch_time = event_last_user_time;
800 ob_debug("Unknown launch time, using %u - independent window",
801 launch_time);
805 /* if it's on another desktop
806 and if allow_other_desktop is true, we generally let it steal focus.
807 but if it didn't come from the user, don't let it steal unless it was
808 launched before the user switched desktops.
809 focus, unless it was launched after we changed desktops and the request
810 came from the user
812 if (!screen_compare_desktops(screen_desktop, self->desktop)) {
813 /* must be allowed */
814 if (!allow_other_desktop) {
815 steal = FALSE;
816 ob_debug("Not focusing the window because its on another desktop");
818 /* if we don't know when the desktop changed, but request is from an
819 application, don't let it change desktop on you */
820 else if (!request_from_user) {
821 steal = FALSE;
822 ob_debug("Not focusing the window because non-user request");
825 /* If something is focused... */
826 else if (focus_client) {
827 /* If the user is working in another window right now, then don't
828 steal focus */
829 if (!relative_focused &&
830 event_last_user_time &&
831 /* last user time must be strictly > launch_time to block focus */
832 (event_time_after(event_last_user_time, launch_time) &&
833 event_last_user_time != launch_time) &&
834 event_time_after(event_last_user_time,
835 steal_time - OB_EVENT_USER_TIME_DELAY))
837 steal = FALSE;
838 ob_debug("Not focusing the window because the user is "
839 "working in another window that is not its relative");
841 /* Don't move focus if it's not going to go to this window
842 anyway */
843 else if (client_focus_target(self) != self) {
844 steal = FALSE;
845 ob_debug("Not focusing the window because another window "
846 "would get the focus anyway");
848 /* For requests that don't come from the user */
849 else if (!request_from_user) {
850 /* If the new window is a transient (and its relatives aren't
851 focused) */
852 if (client_has_parent(self) && !relative_focused) {
853 steal = FALSE;
854 ob_debug("Not focusing the window because it is a "
855 "transient, and its relatives aren't focused");
857 /* Don't steal focus from globally active clients.
858 I stole this idea from KWin. It seems nice.
860 else if (!(focus_client->can_focus || focus_client->focus_notify))
862 steal = FALSE;
863 ob_debug("Not focusing the window because a globally "
864 "active client has focus");
866 /* Don't move focus if the window is not visible on the current
867 desktop and none of its relatives are focused */
868 else if (!allow_other_desktop &&
869 !screen_compare_desktops(self->desktop, screen_desktop) &&
870 !relative_focused)
872 steal = FALSE;
873 ob_debug("Not focusing the window because it is on "
874 "another desktop and no relatives are focused ");
879 if (!steal)
880 ob_debug("Focus stealing prevention activated for %s at "
881 "time %u (last user interaction time %u)",
882 self->title, steal_time, event_last_user_time);
883 else
884 ob_debug("Allowing focus stealing for %s at time %u (last user "
885 "interaction time %u)",
886 self->title, steal_time, event_last_user_time);
887 return steal;
890 /*! Returns a new structure containing the per-app settings for this client.
891 The returned structure needs to be freed with g_free. */
892 static ObAppSettings *client_get_settings_state(ObClient *self)
894 ObAppSettings *settings;
895 GSList *it;
897 settings = config_create_app_settings();
899 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
900 ObAppSettings *app = it->data;
901 gboolean match = TRUE;
903 g_assert(app->name != NULL || app->class != NULL ||
904 app->role != NULL || app->title != NULL ||
905 (signed)app->type >= 0);
907 if (app->name &&
908 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
909 match = FALSE;
910 else if (app->class &&
911 !g_pattern_match(app->class,
912 strlen(self->class), self->class, NULL))
913 match = FALSE;
914 else if (app->role &&
915 !g_pattern_match(app->role,
916 strlen(self->role), self->role, NULL))
917 match = FALSE;
918 else if (app->title &&
919 !g_pattern_match(app->title,
920 strlen(self->title), self->title, NULL))
921 match = FALSE;
922 else if ((signed)app->type >= 0 && app->type != self->type) {
923 match = FALSE;
926 if (match) {
927 ob_debug("Window matching: %s", app->name);
929 /* copy the settings to our struct, overriding the existing
930 settings if they are not defaults */
931 config_app_settings_copy_non_defaults(app, settings);
935 if (settings->shade != -1)
936 self->shaded = !!settings->shade;
937 if (settings->decor != -1)
938 self->undecorated = !settings->decor;
939 if (settings->iconic != -1)
940 self->iconic = !!settings->iconic;
941 if (settings->skip_pager != -1)
942 self->skip_pager = !!settings->skip_pager;
943 if (settings->skip_taskbar != -1)
944 self->skip_taskbar = !!settings->skip_taskbar;
946 if (settings->max_vert != -1)
947 self->max_vert = !!settings->max_vert;
948 if (settings->max_horz != -1)
949 self->max_horz = !!settings->max_horz;
951 if (settings->fullscreen != -1)
952 self->fullscreen = !!settings->fullscreen;
954 if (settings->desktop) {
955 if (settings->desktop == DESKTOP_ALL)
956 self->desktop = settings->desktop;
957 else if (settings->desktop > 0 &&
958 settings->desktop <= screen_num_desktops)
959 self->desktop = settings->desktop - 1;
962 if (settings->layer == -1) {
963 self->below = TRUE;
964 self->above = FALSE;
966 else if (settings->layer == 0) {
967 self->below = FALSE;
968 self->above = FALSE;
970 else if (settings->layer == 1) {
971 self->below = FALSE;
972 self->above = TRUE;
974 return settings;
977 static void client_restore_session_state(ObClient *self)
979 GList *it;
981 ob_debug_type(OB_DEBUG_SM,
982 "Restore session for client %s", self->title);
984 if (!(it = session_state_find(self))) {
985 ob_debug_type(OB_DEBUG_SM,
986 "Session data not found for client %s", self->title);
987 return;
990 self->session = it->data;
992 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
993 self->title);
995 RECT_SET_POINT(self->area, self->session->x, self->session->y);
996 self->positioned = USPosition;
997 self->sized = USSize;
998 if (self->session->w > 0)
999 self->area.width = self->session->w;
1000 if (self->session->h > 0)
1001 self->area.height = self->session->h;
1002 XResizeWindow(obt_display, self->window,
1003 self->area.width, self->area.height);
1005 self->desktop = (self->session->desktop == DESKTOP_ALL ?
1006 self->session->desktop :
1007 MIN(screen_num_desktops - 1, self->session->desktop));
1008 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
1010 self->shaded = self->session->shaded;
1011 self->iconic = self->session->iconic;
1012 self->skip_pager = self->session->skip_pager;
1013 self->skip_taskbar = self->session->skip_taskbar;
1014 self->fullscreen = self->session->fullscreen;
1015 self->above = self->session->above;
1016 self->below = self->session->below;
1017 self->max_horz = self->session->max_horz;
1018 self->max_vert = self->session->max_vert;
1019 self->undecorated = self->session->undecorated;
1022 static gboolean client_restore_session_stacking(ObClient *self)
1024 GList *it, *mypos;
1026 if (!self->session) return FALSE;
1028 mypos = g_list_find(session_saved_state, self->session);
1029 if (!mypos) return FALSE;
1031 /* start above me and look for the first client */
1032 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
1033 GList *cit;
1035 for (cit = client_list; cit; cit = g_list_next(cit)) {
1036 ObClient *c = cit->data;
1037 /* found a client that was in the session, so go below it */
1038 if (c->session == it->data) {
1039 stacking_below(CLIENT_AS_WINDOW(self),
1040 CLIENT_AS_WINDOW(cit->data));
1041 return TRUE;
1045 return FALSE;
1048 void client_move_onscreen(ObClient *self, gboolean rude)
1050 gint x = self->area.x;
1051 gint y = self->area.y;
1052 if (client_find_onscreen(self, &x, &y,
1053 self->area.width,
1054 self->area.height, rude)) {
1055 client_move(self, x, y);
1059 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
1060 gboolean rude)
1062 gint ox = *x, oy = *y;
1063 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
1064 gint fw, fh;
1065 Rect desired;
1066 guint i;
1067 gboolean found_mon;
1069 RECT_SET(desired, *x, *y, w, h);
1070 frame_rect_to_frame(self->frame, &desired);
1072 /* get where the frame would be */
1073 frame_client_gravity(self->frame, x, y);
1075 /* get the requested size of the window with decorations */
1076 fw = self->frame->size.left + w + self->frame->size.right;
1077 fh = self->frame->size.top + h + self->frame->size.bottom;
1079 /* If rudeness wasn't requested, then still be rude in a given direction
1080 if the client is not moving, only resizing in that direction */
1081 if (!rude) {
1082 Point oldtl, oldtr, oldbl, oldbr;
1083 Point newtl, newtr, newbl, newbr;
1084 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
1086 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
1087 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
1088 self->frame->area.y + self->frame->area.height - 1);
1089 POINT_SET(oldtr, oldbr.x, oldtl.y);
1090 POINT_SET(oldbl, oldtl.x, oldbr.y);
1092 POINT_SET(newtl, *x, *y);
1093 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
1094 POINT_SET(newtr, newbr.x, newtl.y);
1095 POINT_SET(newbl, newtl.x, newbr.y);
1097 /* is it moving or just resizing from some corner? */
1098 stationary_l = oldtl.x == newtl.x;
1099 stationary_r = oldtr.x == newtr.x;
1100 stationary_t = oldtl.y == newtl.y;
1101 stationary_b = oldbl.y == newbl.y;
1103 /* if left edge is growing and didnt move right edge */
1104 if (stationary_r && newtl.x < oldtl.x)
1105 rudel = TRUE;
1106 /* if right edge is growing and didnt move left edge */
1107 if (stationary_l && newtr.x > oldtr.x)
1108 ruder = TRUE;
1109 /* if top edge is growing and didnt move bottom edge */
1110 if (stationary_b && newtl.y < oldtl.y)
1111 rudet = TRUE;
1112 /* if bottom edge is growing and didnt move top edge */
1113 if (stationary_t && newbl.y > oldbl.y)
1114 rudeb = TRUE;
1117 /* we iterate through every monitor that the window is at least partially
1118 on, to make sure it is obeying the rules on them all
1120 if the window does not appear on any monitors, then use the first one
1122 found_mon = FALSE;
1123 for (i = 0; i < screen_num_monitors; ++i) {
1124 Rect *a;
1126 if (!screen_physical_area_monitor_contains(i, &desired)) {
1127 if (i < screen_num_monitors - 1 || found_mon)
1128 continue;
1130 /* the window is not inside any monitor! so just use the first
1131 one */
1132 a = screen_area(self->desktop, 0, NULL);
1133 } else {
1134 found_mon = TRUE;
1135 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1138 /* This makes sure windows aren't entirely outside of the screen so you
1139 can't see them at all.
1140 It makes sure 10% of the window is on the screen at least. And don't
1141 let it move itself off the top of the screen, which would hide the
1142 titlebar on you. (The user can still do this if they want too, it's
1143 only limiting the application.
1145 if (client_normal(self)) {
1146 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1147 *x = a->x + a->width - fw/10;
1148 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1149 *y = a->y + a->height - fh/10;
1150 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1151 *x = a->x - fw*9/10;
1152 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1153 *y = a->y - fh*9/10;
1156 /* This here doesn't let windows even a pixel outside the
1157 struts/screen. When called from client_manage, programs placing
1158 themselves are forced completely onscreen, while things like
1159 xterm -geometry resolution-width/2 will work fine. Trying to
1160 place it completely offscreen will be handled in the above code.
1161 Sorry for this confused comment, i am tired. */
1162 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1163 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1164 *x = a->x + MAX(0, a->width - fw);
1166 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1167 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1168 *y = a->y + MAX(0, a->height - fh);
1170 g_slice_free(Rect, a);
1173 /* get where the client should be */
1174 frame_frame_gravity(self->frame, x, y);
1176 return ox != *x || oy != *y;
1179 static void client_get_all(ObClient *self, gboolean real)
1181 /* this is needed for the frame to set itself up */
1182 client_get_area(self);
1184 /* these things can change the decor and functions of the window */
1186 client_get_mwm_hints(self);
1187 /* this can change the mwmhints for special cases */
1188 client_get_type_and_transientness(self);
1189 client_update_normal_hints(self);
1191 /* set up the maximum possible decor/functions */
1192 client_setup_default_decor_and_functions(self);
1194 client_get_state(self);
1196 /* get the session related properties, these can change decorations
1197 from per-app settings */
1198 client_get_session_ids(self);
1200 /* now we got everything that can affect the decorations */
1201 if (!real)
1202 return;
1204 /* get this early so we have it for debugging */
1205 client_update_title(self);
1207 /* save the values of the variables used for app rule matching */
1208 client_save_app_rule_values(self);
1210 client_update_protocols(self);
1212 client_update_wmhints(self);
1213 /* this may have already been called from client_update_wmhints */
1214 if (!self->parents && !self->transient_for_group)
1215 client_update_transient_for(self);
1217 client_get_startup_id(self);
1218 client_get_desktop(self);/* uses transient data/group/startup id if a
1219 desktop is not specified */
1220 client_get_shaped(self);
1223 /* a couple type-based defaults for new windows */
1225 /* this makes sure that these windows appear on all desktops */
1226 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1227 self->desktop = DESKTOP_ALL;
1230 #ifdef SYNC
1231 client_update_sync_request_counter(self);
1232 #endif
1234 client_get_colormap(self);
1235 client_update_strut(self);
1236 client_update_icons(self);
1237 client_update_icon_geometry(self);
1240 static void client_get_startup_id(ObClient *self)
1242 if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id)))
1243 if (self->group)
1244 OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID,
1245 &self->startup_id);
1248 static void client_get_area(ObClient *self)
1250 XWindowAttributes wattrib;
1251 Status ret;
1253 ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
1254 g_assert(ret != BadWindow);
1256 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1257 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1258 self->border_width = wattrib.border_width;
1260 ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
1261 wattrib.width, wattrib.height, wattrib.border_width);
1264 static void client_get_desktop(ObClient *self)
1266 guint32 d = screen_num_desktops; /* an always-invalid value */
1268 if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
1269 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1270 self->desktop = screen_num_desktops - 1;
1271 else
1272 self->desktop = d;
1273 ob_debug("client requested desktop 0x%x", self->desktop);
1274 } else {
1275 GSList *it;
1276 gboolean first = TRUE;
1277 guint all = screen_num_desktops; /* not a valid value */
1279 /* if they are all on one desktop, then open it on the
1280 same desktop */
1281 for (it = self->parents; it; it = g_slist_next(it)) {
1282 ObClient *c = it->data;
1284 if (c->desktop == DESKTOP_ALL) continue;
1286 if (first) {
1287 all = c->desktop;
1288 first = FALSE;
1290 else if (all != c->desktop)
1291 all = screen_num_desktops; /* make it invalid */
1293 if (all != screen_num_desktops) {
1294 self->desktop = all;
1296 ob_debug("client desktop set from parents: 0x%x",
1297 self->desktop);
1299 /* try get from the startup-notification protocol */
1300 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1301 if (self->desktop >= screen_num_desktops &&
1302 self->desktop != DESKTOP_ALL)
1303 self->desktop = screen_num_desktops - 1;
1304 ob_debug("client desktop set from startup-notification: 0x%x",
1305 self->desktop);
1307 /* defaults to the current desktop */
1308 else {
1309 self->desktop = screen_desktop;
1310 ob_debug("client desktop set to the current desktop: %d",
1311 self->desktop);
1316 static void client_get_state(ObClient *self)
1318 guint32 *state;
1319 guint num;
1321 if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
1322 gulong i;
1323 for (i = 0; i < num; ++i) {
1324 if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1325 self->modal = TRUE;
1326 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1327 self->shaded = TRUE;
1328 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1329 self->iconic = TRUE;
1330 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1331 self->skip_taskbar = TRUE;
1332 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1333 self->skip_pager = TRUE;
1334 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1335 self->fullscreen = TRUE;
1336 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
1337 self->max_vert = TRUE;
1338 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
1339 self->max_horz = TRUE;
1340 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1341 self->above = TRUE;
1342 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
1343 self->below = TRUE;
1344 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
1345 self->demands_attention = TRUE;
1346 else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
1347 self->undecorated = TRUE;
1350 g_free(state);
1354 static void client_get_shaped(ObClient *self)
1356 self->shaped = FALSE;
1357 #ifdef SHAPE
1358 if (obt_display_extension_shape) {
1359 gint foo;
1360 guint ufoo;
1361 gint s;
1363 XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
1365 XShapeQueryExtents(obt_display, self->window, &s, &foo,
1366 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1367 &ufoo);
1368 self->shaped = !!s;
1370 #endif
1373 void client_update_transient_for(ObClient *self)
1375 Window t = None;
1376 ObClient *target = NULL;
1377 gboolean trangroup = FALSE;
1379 if (XGetTransientForHint(obt_display, self->window, &t)) {
1380 if (t != self->window) { /* can't be transient to itself! */
1381 ObWindow *tw = window_find(t);
1382 /* if this happens then we need to check for it */
1383 g_assert(tw != CLIENT_AS_WINDOW(self));
1384 if (tw && WINDOW_IS_CLIENT(tw)) {
1385 /* watch out for windows with a parent that is something
1386 different, like a dockapp for example */
1387 target = WINDOW_AS_CLIENT(tw);
1391 /* Setting the transient_for to Root is actually illegal, however
1392 applications from time have done this to specify transient for
1393 their group */
1394 if (!target && self->group && t == obt_root(ob_screen))
1395 trangroup = TRUE;
1396 } else if (self->group && self->transient)
1397 trangroup = TRUE;
1399 client_update_transient_tree(self, self->group, self->group,
1400 self->transient_for_group, trangroup,
1401 client_direct_parent(self), target);
1402 self->transient_for_group = trangroup;
1406 static void client_update_transient_tree(ObClient *self,
1407 ObGroup *oldgroup, ObGroup *newgroup,
1408 gboolean oldgtran, gboolean newgtran,
1409 ObClient* oldparent,
1410 ObClient *newparent)
1412 GSList *it, *next;
1413 ObClient *c;
1415 g_assert(!oldgtran || oldgroup);
1416 g_assert(!newgtran || newgroup);
1417 g_assert((!oldgtran && !oldparent) ||
1418 (oldgtran && !oldparent) ||
1419 (!oldgtran && oldparent));
1420 g_assert((!newgtran && !newparent) ||
1421 (newgtran && !newparent) ||
1422 (!newgtran && newparent));
1424 /* * *
1425 Group transient windows are not allowed to have other group
1426 transient windows as their children.
1427 * * */
1429 /* No change has occured */
1430 if (oldgroup == newgroup &&
1431 oldgtran == newgtran &&
1432 oldparent == newparent) return;
1434 /** Remove the client from the transient tree **/
1436 for (it = self->transients; it; it = next) {
1437 next = g_slist_next(it);
1438 c = it->data;
1439 self->transients = g_slist_delete_link(self->transients, it);
1440 c->parents = g_slist_remove(c->parents, self);
1442 for (it = self->parents; it; it = next) {
1443 next = g_slist_next(it);
1444 c = it->data;
1445 self->parents = g_slist_delete_link(self->parents, it);
1446 c->transients = g_slist_remove(c->transients, self);
1449 /** Re-add the client to the transient tree **/
1451 /* If we're transient for a group then we need to add ourselves to all our
1452 parents */
1453 if (newgtran) {
1454 for (it = newgroup->members; it; it = g_slist_next(it)) {
1455 c = it->data;
1456 if (c != self &&
1457 !client_search_top_direct_parent(c)->transient_for_group &&
1458 client_normal(c))
1460 c->transients = g_slist_prepend(c->transients, self);
1461 self->parents = g_slist_prepend(self->parents, c);
1466 /* If we are now transient for a single window we need to add ourselves to
1467 its children
1469 WARNING: Cyclical transient-ness is possible if two windows are
1470 transient for eachother.
1472 else if (newparent &&
1473 /* don't make ourself its child if it is already our child */
1474 !client_is_direct_child(self, newparent) &&
1475 client_normal(newparent))
1477 newparent->transients = g_slist_prepend(newparent->transients, self);
1478 self->parents = g_slist_prepend(self->parents, newparent);
1481 /* Add any group transient windows to our children. But if we're transient
1482 for the group, then other group transients are not our children.
1484 WARNING: Cyclical transient-ness is possible. For e.g. if:
1485 A is transient for the group
1486 B is transient for A
1487 C is transient for B
1488 A can't be transient for C or we have a cycle
1490 if (!newgtran && newgroup &&
1491 (!newparent ||
1492 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1493 client_normal(self))
1495 for (it = newgroup->members; it; it = g_slist_next(it)) {
1496 c = it->data;
1497 if (c != self && c->transient_for_group &&
1498 /* Don't make it our child if it is already our parent */
1499 !client_is_direct_child(c, self))
1501 self->transients = g_slist_prepend(self->transients, c);
1502 c->parents = g_slist_prepend(c->parents, self);
1507 /** If we change our group transient-ness, our children change their
1508 effective group transient-ness, which affects how they relate to other
1509 group windows **/
1511 for (it = self->transients; it; it = g_slist_next(it)) {
1512 c = it->data;
1513 if (!c->transient_for_group)
1514 client_update_transient_tree(c, c->group, c->group,
1515 c->transient_for_group,
1516 c->transient_for_group,
1517 client_direct_parent(c),
1518 client_direct_parent(c));
1522 void client_get_mwm_hints(ObClient *self)
1524 guint num;
1525 guint32 *hints;
1527 self->mwmhints.flags = 0; /* default to none */
1529 if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1530 &hints, &num)) {
1531 if (num >= OB_MWM_ELEMENTS) {
1532 self->mwmhints.flags = hints[0];
1533 self->mwmhints.functions = hints[1];
1534 self->mwmhints.decorations = hints[2];
1536 g_free(hints);
1540 void client_get_type_and_transientness(ObClient *self)
1542 guint num, i;
1543 guint32 *val;
1544 Window t;
1546 self->type = -1;
1547 self->transient = FALSE;
1549 if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1550 /* use the first value that we know about in the array */
1551 for (i = 0; i < num; ++i) {
1552 if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1553 self->type = OB_CLIENT_TYPE_DESKTOP;
1554 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1555 self->type = OB_CLIENT_TYPE_DOCK;
1556 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1557 self->type = OB_CLIENT_TYPE_TOOLBAR;
1558 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1559 self->type = OB_CLIENT_TYPE_MENU;
1560 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1561 self->type = OB_CLIENT_TYPE_UTILITY;
1562 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1563 self->type = OB_CLIENT_TYPE_SPLASH;
1564 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1565 self->type = OB_CLIENT_TYPE_DIALOG;
1566 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1567 self->type = OB_CLIENT_TYPE_NORMAL;
1568 else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1570 /* prevent this window from getting any decor or
1571 functionality */
1572 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1573 OB_MWM_FLAG_DECORATIONS);
1574 self->mwmhints.decorations = 0;
1575 self->mwmhints.functions = 0;
1577 if (self->type != (ObClientType) -1)
1578 break; /* grab the first legit type */
1580 g_free(val);
1583 if (XGetTransientForHint(obt_display, self->window, &t))
1584 self->transient = TRUE;
1586 if (self->type == (ObClientType) -1) {
1587 /*the window type hint was not set, which means we either classify
1588 ourself as a normal window or a dialog, depending on if we are a
1589 transient. */
1590 if (self->transient)
1591 self->type = OB_CLIENT_TYPE_DIALOG;
1592 else
1593 self->type = OB_CLIENT_TYPE_NORMAL;
1596 /* then, based on our type, we can update our transientness.. */
1597 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1598 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1599 self->type == OB_CLIENT_TYPE_MENU ||
1600 self->type == OB_CLIENT_TYPE_UTILITY)
1602 self->transient = TRUE;
1606 void client_update_protocols(ObClient *self)
1608 guint32 *proto;
1609 guint num_ret, i;
1611 self->focus_notify = FALSE;
1612 self->delete_window = FALSE;
1614 if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1615 for (i = 0; i < num_ret; ++i) {
1616 if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1617 /* this means we can request the window to close */
1618 self->delete_window = TRUE;
1619 else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1620 /* if this protocol is requested, then the window will be
1621 notified whenever we want it to receive focus */
1622 self->focus_notify = TRUE;
1623 else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1624 /* if this protocol is requested, then the window will allow
1625 pings to determine if it is still alive */
1626 self->ping = TRUE;
1627 #ifdef SYNC
1628 else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1629 /* if this protocol is requested, then resizing the
1630 window will be synchronized between the frame and the
1631 client */
1632 self->sync_request = TRUE;
1633 #endif
1635 g_free(proto);
1639 #ifdef SYNC
1640 void client_update_sync_request_counter(ObClient *self)
1642 guint32 i;
1644 if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1646 XSyncValue val;
1648 self->sync_counter = i;
1650 /* this must be set when managing a new window according to EWMH */
1651 XSyncIntToValue(&val, 0);
1652 XSyncSetCounter(obt_display, self->sync_counter, val);
1653 } else
1654 self->sync_counter = None;
1656 #endif
1658 static void client_get_colormap(ObClient *self)
1660 XWindowAttributes wa;
1662 if (XGetWindowAttributes(obt_display, self->window, &wa))
1663 client_update_colormap(self, wa.colormap);
1666 void client_update_colormap(ObClient *self, Colormap colormap)
1668 if (colormap == self->colormap) return;
1670 ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1672 if (client_focused(self)) {
1673 screen_install_colormap(self, FALSE); /* uninstall old one */
1674 self->colormap = colormap;
1675 screen_install_colormap(self, TRUE); /* install new one */
1676 } else
1677 self->colormap = colormap;
1680 void client_update_opacity(ObClient *self)
1682 guint32 o;
1684 if (OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, &o))
1685 OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL, o);
1686 else
1687 OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY);
1690 void client_update_normal_hints(ObClient *self)
1692 XSizeHints size;
1693 glong ret;
1695 /* defaults */
1696 self->min_ratio = 0.0f;
1697 self->max_ratio = 0.0f;
1698 SIZE_SET(self->size_inc, 1, 1);
1699 SIZE_SET(self->base_size, -1, -1);
1700 SIZE_SET(self->min_size, 0, 0);
1701 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1703 /* get the hints from the window */
1704 if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
1705 /* normal windows can't request placement! har har
1706 if (!client_normal(self))
1708 self->positioned = (size.flags & (PPosition|USPosition));
1709 self->sized = (size.flags & (PSize|USSize));
1711 if (size.flags & PWinGravity)
1712 self->gravity = size.win_gravity;
1714 if (size.flags & PAspect) {
1715 if (size.min_aspect.y)
1716 self->min_ratio =
1717 (gfloat) size.min_aspect.x / size.min_aspect.y;
1718 if (size.max_aspect.y)
1719 self->max_ratio =
1720 (gfloat) size.max_aspect.x / size.max_aspect.y;
1723 if (size.flags & PMinSize)
1724 SIZE_SET(self->min_size, size.min_width, size.min_height);
1726 if (size.flags & PMaxSize)
1727 SIZE_SET(self->max_size, size.max_width, size.max_height);
1729 if (size.flags & PBaseSize)
1730 SIZE_SET(self->base_size, size.base_width, size.base_height);
1732 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1733 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1735 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1736 self->min_size.width, self->min_size.height,
1737 self->max_size.width, self->max_size.height);
1738 ob_debug("size inc (%d %d) base size (%d %d)",
1739 self->size_inc.width, self->size_inc.height,
1740 self->base_size.width, self->base_size.height);
1742 else
1743 ob_debug("Normal hints: not set");
1746 static void client_setup_default_decor_and_functions(ObClient *self)
1748 /* start with everything (cept fullscreen) */
1749 self->decorations =
1750 (OB_FRAME_DECOR_TITLEBAR |
1751 OB_FRAME_DECOR_HANDLE |
1752 OB_FRAME_DECOR_GRIPS |
1753 OB_FRAME_DECOR_BORDER |
1754 OB_FRAME_DECOR_ICON |
1755 OB_FRAME_DECOR_ALLDESKTOPS |
1756 OB_FRAME_DECOR_ICONIFY |
1757 OB_FRAME_DECOR_MAXIMIZE |
1758 OB_FRAME_DECOR_SHADE |
1759 OB_FRAME_DECOR_CLOSE);
1760 self->functions =
1761 (OB_CLIENT_FUNC_RESIZE |
1762 OB_CLIENT_FUNC_MOVE |
1763 OB_CLIENT_FUNC_ICONIFY |
1764 OB_CLIENT_FUNC_MAXIMIZE |
1765 OB_CLIENT_FUNC_SHADE |
1766 OB_CLIENT_FUNC_CLOSE |
1767 OB_CLIENT_FUNC_BELOW |
1768 OB_CLIENT_FUNC_ABOVE |
1769 OB_CLIENT_FUNC_UNDECORATE);
1771 if (!(self->min_size.width < self->max_size.width ||
1772 self->min_size.height < self->max_size.height))
1773 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1775 switch (self->type) {
1776 case OB_CLIENT_TYPE_NORMAL:
1777 /* normal windows retain all of the possible decorations and
1778 functionality, and can be fullscreen */
1779 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1780 break;
1782 case OB_CLIENT_TYPE_DIALOG:
1783 /* sometimes apps make dialog windows fullscreen for some reason (for
1784 e.g. kpdf does this..) */
1785 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1786 break;
1788 case OB_CLIENT_TYPE_UTILITY:
1789 /* these windows don't have anything added or removed by default */
1790 break;
1792 case OB_CLIENT_TYPE_MENU:
1793 case OB_CLIENT_TYPE_TOOLBAR:
1794 /* these windows can't iconify or maximize */
1795 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1796 OB_FRAME_DECOR_MAXIMIZE);
1797 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1798 OB_CLIENT_FUNC_MAXIMIZE);
1799 break;
1801 case OB_CLIENT_TYPE_SPLASH:
1802 /* these don't get get any decorations, and the only thing you can
1803 do with them is move them */
1804 self->decorations = 0;
1805 self->functions = OB_CLIENT_FUNC_MOVE;
1806 break;
1808 case OB_CLIENT_TYPE_DESKTOP:
1809 /* these windows are not manipulated by the window manager */
1810 self->decorations = 0;
1811 self->functions = 0;
1812 break;
1814 case OB_CLIENT_TYPE_DOCK:
1815 /* these windows are not manipulated by the window manager, but they
1816 can set below layer which has a special meaning */
1817 self->decorations = 0;
1818 self->functions = OB_CLIENT_FUNC_BELOW;
1819 break;
1822 /* If the client has no decor from its type (which never changes) then
1823 don't allow the user to "undecorate" the window. Otherwise, allow them
1824 to, even if there are motif hints removing the decor, because those
1825 may change these days (e.g. chromium) */
1826 if (self->decorations == 0)
1827 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1829 /* Mwm Hints are applied subtractively to what has already been chosen for
1830 decor and functionality */
1831 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1832 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1833 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1834 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1836 /* if the mwm hints request no handle or title, then all
1837 decorations are disabled, but keep the border if that's
1838 specified */
1839 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1840 self->decorations = OB_FRAME_DECOR_BORDER;
1841 else
1842 self->decorations = 0;
1847 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1848 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1849 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1850 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1851 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1852 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1853 /* dont let mwm hints kill any buttons
1854 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1855 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1856 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1857 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1859 /* dont let mwm hints kill the close button
1860 if (! (self->mwmhints.functions & MwmFunc_Close))
1861 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1865 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1866 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1867 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1868 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1869 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1870 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1872 /* can't maximize without moving/resizing */
1873 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1874 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1875 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1876 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1877 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1881 /*! Set up decor for a client based on its undecorated state. */
1882 static void client_setup_decor_undecorated(ObClient *self)
1884 /* If the user requested no decorations, then remove all the decorations,
1885 except the border. But don't add a border if there wasn't one. */
1886 if (self->undecorated)
1887 self->decorations &= (config_theme_keepborder ?
1888 OB_FRAME_DECOR_BORDER : 0);
1891 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1893 client_setup_default_decor_and_functions(self);
1895 client_setup_decor_undecorated(self);
1897 if (self->max_horz && self->max_vert) {
1898 /* once upon a time you couldn't resize maximized windows, that is not
1899 the case any more though !
1901 but do kill the handle on fully maxed windows */
1902 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1905 /* if we don't have a titlebar, then we cannot shade! */
1906 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1907 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1909 /* now we need to check against rules for the client's current state */
1910 if (self->fullscreen) {
1911 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1912 OB_CLIENT_FUNC_FULLSCREEN |
1913 OB_CLIENT_FUNC_ICONIFY);
1914 self->decorations = 0;
1917 client_change_allowed_actions(self);
1919 if (reconfig)
1920 /* reconfigure to make sure decorations are updated */
1921 client_reconfigure(self, FALSE);
1924 static void client_change_allowed_actions(ObClient *self)
1926 gulong actions[12];
1927 gint num = 0;
1929 /* desktop windows are kept on all desktops */
1930 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1931 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1933 if (self->functions & OB_CLIENT_FUNC_SHADE)
1934 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1935 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1936 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1937 if (self->functions & OB_CLIENT_FUNC_MOVE)
1938 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1939 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1940 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1941 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1942 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1943 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1944 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1945 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1946 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1947 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1949 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1950 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1951 if (self->functions & OB_CLIENT_FUNC_BELOW)
1952 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1953 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1954 actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1956 OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1958 /* make sure the window isn't breaking any rules now
1960 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1961 it can't be iconified with its parent
1964 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1965 if (self->frame) client_shade(self, FALSE);
1966 else self->shaded = FALSE;
1968 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1969 if (self->frame) client_fullscreen(self, FALSE);
1970 else self->fullscreen = FALSE;
1972 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1973 self->max_vert)) {
1974 if (self->frame) client_maximize(self, FALSE, 0);
1975 else self->max_vert = self->max_horz = FALSE;
1979 void client_update_wmhints(ObClient *self)
1981 XWMHints *hints;
1983 /* assume a window takes input if it doesn't specify */
1984 self->can_focus = TRUE;
1986 if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
1987 gboolean ur;
1989 if (hints->flags & InputHint)
1990 self->can_focus = hints->input;
1992 /* only do this when first managing the window *AND* when we aren't
1993 starting up! */
1994 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1995 if (hints->flags & StateHint)
1996 self->iconic = hints->initial_state == IconicState;
1998 ur = self->urgent;
1999 self->urgent = (hints->flags & XUrgencyHint);
2000 if (self->urgent && !ur)
2001 client_hilite(self, TRUE);
2002 else if (!self->urgent && ur && self->demands_attention)
2003 client_hilite(self, FALSE);
2005 if (!(hints->flags & WindowGroupHint))
2006 hints->window_group = None;
2008 /* did the group state change? */
2009 if (hints->window_group !=
2010 (self->group ? self->group->leader : None))
2012 ObGroup *oldgroup = self->group;
2014 /* remove from the old group if there was one */
2015 if (self->group) {
2016 group_remove(self->group, self);
2017 self->group = NULL;
2020 /* add ourself to the group if we have one */
2021 if (hints->window_group != None) {
2022 self->group = group_add(hints->window_group, self);
2025 /* Put ourselves into the new group's transient tree, and remove
2026 ourselves from the old group's */
2027 client_update_transient_tree(self, oldgroup, self->group,
2028 self->transient_for_group,
2029 self->transient_for_group,
2030 client_direct_parent(self),
2031 client_direct_parent(self));
2033 /* Lastly, being in a group, or not, can change if the window is
2034 transient for anything.
2036 The logic for this is:
2037 self->transient = TRUE always if the window wants to be
2038 transient for something, even if transient_for was NULL because
2039 it wasn't in a group before.
2041 If parents was NULL and oldgroup was NULL we can assume
2042 that when we add the new group, it will become transient for
2043 something.
2045 If transient_for_group is TRUE, then it must have already
2046 had a group. If it is getting a new group, the above call to
2047 client_update_transient_tree has already taken care of
2048 everything ! If it is losing all group status then it will
2049 no longer be transient for anything and that needs to be
2050 updated.
2052 if (self->transient &&
2053 ((self->parents == NULL && oldgroup == NULL) ||
2054 (self->transient_for_group && !self->group)))
2055 client_update_transient_for(self);
2058 /* the WM_HINTS can contain an icon */
2059 if (hints->flags & IconPixmapHint)
2060 client_update_icons(self);
2062 XFree(hints);
2065 focus_cycle_addremove(self, TRUE);
2068 void client_update_title(ObClient *self)
2070 gchar *data = NULL;
2071 gchar *visible = NULL;
2073 g_free(self->title);
2074 g_free(self->original_title);
2076 /* try netwm */
2077 if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) {
2078 /* try old x stuff */
2079 if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) {
2080 if (self->transient) {
2082 GNOME alert windows are not given titles:
2083 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
2085 data = g_strdup("");
2086 } else
2087 data = g_strdup(_("Unnamed Window"));
2090 self->original_title = g_strdup(data);
2092 if (self->client_machine) {
2093 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2094 g_free(data);
2095 } else
2096 visible = data;
2098 if (self->not_responding) {
2099 data = visible;
2100 if (self->kill_level > 0)
2101 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2102 else
2103 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2104 g_free(data);
2107 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible);
2108 self->title = visible;
2110 if (self->frame)
2111 frame_adjust_title(self->frame);
2113 /* update the icon title */
2114 data = NULL;
2115 g_free(self->icon_title);
2117 /* try netwm */
2118 if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data))
2119 /* try old x stuff */
2120 if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data))
2121 data = g_strdup(self->title);
2123 if (self->client_machine) {
2124 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2125 g_free(data);
2126 } else
2127 visible = data;
2129 if (self->not_responding) {
2130 data = visible;
2131 if (self->kill_level > 0)
2132 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2133 else
2134 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2135 g_free(data);
2138 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible);
2139 self->icon_title = visible;
2142 void client_update_strut(ObClient *self)
2144 guint num;
2145 guint32 *data;
2146 gboolean got = FALSE;
2147 StrutPartial strut;
2149 if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
2150 &data, &num))
2152 if (num == 12) {
2153 got = TRUE;
2154 STRUT_PARTIAL_SET(strut,
2155 data[0], data[2], data[1], data[3],
2156 data[4], data[5], data[8], data[9],
2157 data[6], data[7], data[10], data[11]);
2159 g_free(data);
2162 if (!got &&
2163 OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
2164 if (num == 4) {
2165 const Rect *a;
2167 got = TRUE;
2169 /* use the screen's width/height */
2170 a = screen_physical_area_all_monitors();
2172 STRUT_PARTIAL_SET(strut,
2173 data[0], data[2], data[1], data[3],
2174 a->y, a->y + a->height - 1,
2175 a->x, a->x + a->width - 1,
2176 a->y, a->y + a->height - 1,
2177 a->x, a->x + a->width - 1);
2179 g_free(data);
2182 if (!got)
2183 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2184 0, 0, 0, 0, 0, 0, 0, 0);
2186 if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
2187 self->strut = strut;
2189 /* updating here is pointless while we're being mapped cuz we're not in
2190 the client list yet */
2191 if (self->frame)
2192 screen_update_areas();
2196 void client_update_icons(ObClient *self)
2198 guint num;
2199 guint32 *data;
2200 guint w, h, i, j;
2201 RrImage *img;
2203 img = NULL;
2205 /* grab the server, because we might be setting the window's icon and
2206 we don't want them to set it in between and we overwrite their own
2207 icon */
2208 grab_server(TRUE);
2210 if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
2211 /* figure out how many valid icons are in here */
2212 i = 0;
2213 while (i + 2 < num) { /* +2 is to make sure there is a w and h */
2214 w = data[i++];
2215 h = data[i++];
2216 /* watch for the data being too small for the specified size,
2217 or for zero sized icons. */
2218 if (i + w*h > num || w == 0 || h == 0) {
2219 i += w*h;
2220 continue;
2223 /* convert it to the right bit order for ObRender */
2224 for (j = 0; j < w*h; ++j)
2225 data[i+j] =
2226 (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2227 (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
2228 (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
2229 (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
2231 /* add it to the image cache as an original */
2232 if (!img)
2233 img = RrImageNewFromData(ob_rr_icons, &data[i], w, h);
2234 else
2235 RrImageAddFromData(img, &data[i], w, h);
2237 i += w*h;
2240 g_free(data);
2243 /* if we didn't find an image from the NET_WM_ICON stuff, then try the
2244 legacy X hints */
2245 if (!img) {
2246 XWMHints *hints;
2248 if ((hints = XGetWMHints(obt_display, self->window))) {
2249 if (hints->flags & IconPixmapHint) {
2250 gboolean xicon;
2251 obt_display_ignore_errors(TRUE);
2252 xicon = RrPixmapToRGBA(ob_rr_inst,
2253 hints->icon_pixmap,
2254 (hints->flags & IconMaskHint ?
2255 hints->icon_mask : None),
2256 (gint*)&w, (gint*)&h, &data);
2257 obt_display_ignore_errors(FALSE);
2259 if (xicon) {
2260 if (w > 0 && h > 0) {
2261 if (!img)
2262 img = RrImageNewFromData(ob_rr_icons, data, w, h);
2263 else
2264 RrImageAddFromData(img, data, w, h);
2267 g_free(data);
2270 XFree(hints);
2274 /* set the client's icons to be whatever we found */
2275 RrImageUnref(self->icon_set);
2276 self->icon_set = img;
2278 /* if the client has no icon at all, then we set a default icon onto it.
2279 but, if it has parents, then one of them will have an icon already
2281 if (!self->icon_set && !self->parents) {
2282 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2283 gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
2285 w = ob_rr_theme->def_win_icon_w;
2286 h = ob_rr_theme->def_win_icon_h;
2287 ldata = g_new(gulong, w*h+2);
2288 ldata[0] = w;
2289 ldata[1] = h;
2290 for (i = 0; i < w*h; ++i)
2291 ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2292 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2293 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2294 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2295 OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
2296 g_free(ldata);
2297 } else if (self->frame)
2298 /* don't draw the icon empty if we're just setting one now anyways,
2299 we'll get the property change any second */
2300 frame_adjust_icon(self->frame);
2302 grab_server(FALSE);
2305 void client_update_icon_geometry(ObClient *self)
2307 guint num;
2308 guint32 *data;
2310 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2312 if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
2313 &data, &num))
2315 if (num == 4)
2316 /* don't let them set it with an area < 0 */
2317 RECT_SET(self->icon_geometry, data[0], data[1],
2318 MAX(data[2],0), MAX(data[3],0));
2319 g_free(data);
2323 static void client_get_session_ids(ObClient *self)
2325 guint32 leader;
2326 gboolean got;
2327 gchar *s;
2328 gchar **ss;
2330 if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
2331 leader = None;
2333 /* get the SM_CLIENT_ID */
2334 if (leader && leader != self->window)
2335 OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id);
2336 else
2337 OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id);
2339 /* get the WM_CLASS (name and class). make them "" if they are not
2340 provided */
2341 got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss);
2343 if (got) {
2344 if (ss[0]) {
2345 self->name = g_strdup(ss[0]);
2346 if (ss[1])
2347 self->class = g_strdup(ss[1]);
2349 g_strfreev(ss);
2352 if (self->name == NULL) self->name = g_strdup("");
2353 if (self->class == NULL) self->class = g_strdup("");
2355 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2356 got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
2358 if (got)
2359 self->role = s;
2360 else
2361 self->role = g_strdup("");
2363 /* get the WM_COMMAND */
2364 got = FALSE;
2366 if (leader)
2367 got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss);
2368 if (!got)
2369 got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss);
2371 if (got) {
2372 /* merge/mash them all together */
2373 gchar *merge = NULL;
2374 gint i;
2376 for (i = 0; ss[i]; ++i) {
2377 gchar *tmp = merge;
2378 if (merge)
2379 merge = g_strconcat(merge, ss[i], NULL);
2380 else
2381 merge = g_strconcat(ss[i], NULL);
2382 g_free(tmp);
2384 g_strfreev(ss);
2386 self->wm_command = merge;
2389 /* get the WM_CLIENT_MACHINE */
2390 got = FALSE;
2391 if (leader)
2392 got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s);
2393 if (!got)
2394 got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s);
2396 if (got) {
2397 gchar localhost[128];
2398 guint32 pid;
2400 gethostname(localhost, 127);
2401 localhost[127] = '\0';
2402 if (strcmp(localhost, s) != 0)
2403 self->client_machine = s;
2404 else
2405 g_free(s);
2407 /* see if it has the PID set too (the PID requires that the
2408 WM_CLIENT_MACHINE be set) */
2409 if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
2410 self->pid = pid;
2414 /*! Save the properties used for app matching rules, as seen by Openbox when
2415 the window mapped, so that users can still access them later if the app
2416 changes them */
2417 static void client_save_app_rule_values(ObClient *self)
2419 const gchar *type;
2421 OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role);
2422 OBT_PROP_SETS(self->window, OB_APP_NAME, self->name);
2423 OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class);
2424 OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title);
2426 switch (self->type) {
2427 case OB_CLIENT_TYPE_NORMAL:
2428 type = "normal"; break;
2429 case OB_CLIENT_TYPE_DIALOG:
2430 type = "dialog"; break;
2431 case OB_CLIENT_TYPE_UTILITY:
2432 type = "utility"; break;
2433 case OB_CLIENT_TYPE_MENU:
2434 type = "menu"; break;
2435 case OB_CLIENT_TYPE_TOOLBAR:
2436 type = "toolbar"; break;
2437 case OB_CLIENT_TYPE_SPLASH:
2438 type = "splash"; break;
2439 case OB_CLIENT_TYPE_DESKTOP:
2440 type = "desktop"; break;
2441 case OB_CLIENT_TYPE_DOCK:
2442 type = "dock"; break;
2444 OBT_PROP_SETS(self->window, OB_APP_TYPE, type);
2447 static void client_change_wm_state(ObClient *self)
2449 gulong state[2];
2450 glong old;
2452 old = self->wmstate;
2454 if (self->shaded || self->iconic ||
2455 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2457 self->wmstate = IconicState;
2458 } else
2459 self->wmstate = NormalState;
2461 if (old != self->wmstate) {
2462 OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
2463 self->wmstate, 1, 0, 0, 0);
2465 state[0] = self->wmstate;
2466 state[1] = None;
2467 OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
2471 static void client_change_state(ObClient *self)
2473 gulong netstate[12];
2474 guint num;
2476 num = 0;
2477 if (self->modal)
2478 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2479 if (self->shaded)
2480 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2481 if (self->iconic)
2482 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2483 if (self->skip_taskbar)
2484 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2485 if (self->skip_pager)
2486 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2487 if (self->fullscreen)
2488 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2489 if (self->max_vert)
2490 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2491 if (self->max_horz)
2492 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2493 if (self->above)
2494 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2495 if (self->below)
2496 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2497 if (self->demands_attention)
2498 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2499 if (self->undecorated)
2500 netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2501 OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
2503 if (self->frame)
2504 frame_adjust_state(self->frame);
2507 ObClient *client_search_focus_tree(ObClient *self)
2509 GSList *it;
2510 ObClient *ret;
2512 for (it = self->transients; it; it = g_slist_next(it)) {
2513 if (client_focused(it->data)) return it->data;
2514 if ((ret = client_search_focus_tree(it->data))) return ret;
2516 return NULL;
2519 ObClient *client_search_focus_tree_full(ObClient *self)
2521 if (self->parents) {
2522 GSList *it;
2524 for (it = self->parents; it; it = g_slist_next(it)) {
2525 ObClient *c = it->data;
2526 if ((c = client_search_focus_tree_full(c))) return c;
2529 return NULL;
2531 else {
2532 /* this function checks the whole tree, the client_search_focus_tree
2533 does not, so we need to check this window */
2534 if (client_focused(self))
2535 return self;
2536 return client_search_focus_tree(self);
2540 ObClient *client_search_focus_group_full(ObClient *self)
2542 GSList *it;
2544 if (self->group) {
2545 for (it = self->group->members; it; it = g_slist_next(it)) {
2546 ObClient *c = it->data;
2548 if (client_focused(c)) return c;
2549 if ((c = client_search_focus_tree(it->data))) return c;
2551 } else
2552 if (client_focused(self)) return self;
2553 return NULL;
2556 gboolean client_has_parent(ObClient *self)
2558 return self->parents != NULL;
2561 gboolean client_has_children(ObClient *self)
2563 return self->transients != NULL;
2566 gboolean client_is_oldfullscreen(const ObClient *self,
2567 const Rect *area)
2569 const Rect *monitor, *allmonitors;
2571 /* No decorations and fills the monitor = oldskool fullscreen.
2572 But not for maximized windows.
2575 if (self->decorations || self->max_horz || self->max_vert) return FALSE;
2577 monitor = screen_physical_area_monitor(screen_find_monitor(area));
2578 allmonitors = screen_physical_area_all_monitors();
2580 return (RECT_EQUAL(*area, *monitor) ||
2581 RECT_EQUAL(*area, *allmonitors));
2584 static ObStackingLayer calc_layer(ObClient *self)
2586 ObStackingLayer l;
2588 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2589 l = OB_STACKING_LAYER_DESKTOP;
2590 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2591 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2592 else l = OB_STACKING_LAYER_ABOVE;
2594 else if ((self->fullscreen ||
2595 client_is_oldfullscreen(self, &self->area)) &&
2596 /* you are fullscreen while you or your children are focused.. */
2597 (client_focused(self) || client_search_focus_tree(self) ||
2598 /* you can be fullscreen if you're on another desktop */
2599 (self->desktop != screen_desktop &&
2600 self->desktop != DESKTOP_ALL) ||
2601 /* and you can also be fullscreen if the focused client is on
2602 another monitor, or nothing else is focused */
2603 (!focus_client ||
2604 client_monitor(focus_client) != client_monitor(self))))
2605 l = OB_STACKING_LAYER_FULLSCREEN;
2606 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2607 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2608 else l = OB_STACKING_LAYER_NORMAL;
2610 return l;
2613 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2614 ObStackingLayer min)
2616 ObStackingLayer old, own;
2617 GSList *it;
2619 old = self->layer;
2620 own = calc_layer(self);
2621 self->layer = MAX(own, min);
2623 if (self->layer != old) {
2624 stacking_remove(CLIENT_AS_WINDOW(self));
2625 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2628 /* we've been restacked */
2629 self->visited = TRUE;
2631 for (it = self->transients; it; it = g_slist_next(it))
2632 client_calc_layer_recursive(it->data, orig,
2633 self->layer);
2636 static void client_calc_layer_internal(ObClient *self)
2638 GSList *sit;
2640 /* transients take on the layer of their parents */
2641 sit = client_search_all_top_parents(self);
2643 for (; sit; sit = g_slist_next(sit))
2644 client_calc_layer_recursive(sit->data, self, 0);
2647 void client_calc_layer(ObClient *self)
2649 GList *it;
2651 /* skip over stuff above fullscreen layer */
2652 for (it = stacking_list; it; it = g_list_next(it))
2653 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2655 /* find the windows in the fullscreen layer, and mark them not-visited */
2656 for (; it; it = g_list_next(it)) {
2657 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2658 else if (WINDOW_IS_CLIENT(it->data))
2659 WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2662 client_calc_layer_internal(self);
2664 /* skip over stuff above fullscreen layer */
2665 for (it = stacking_list; it; it = g_list_next(it))
2666 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2668 /* now recalc any windows in the fullscreen layer which have not
2669 had their layer recalced already */
2670 for (; it; it = g_list_next(it)) {
2671 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2672 else if (WINDOW_IS_CLIENT(it->data) &&
2673 !WINDOW_AS_CLIENT(it->data)->visited)
2674 client_calc_layer_internal(it->data);
2678 gboolean client_should_show(ObClient *self)
2680 if (self->iconic)
2681 return FALSE;
2682 if (client_normal(self) && screen_showing_desktop)
2683 return FALSE;
2684 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2685 return TRUE;
2687 return FALSE;
2690 gboolean client_show(ObClient *self)
2692 gboolean show = FALSE;
2694 if (client_should_show(self)) {
2695 /* replay pending pointer event before showing the window, in case it
2696 should be going to something under the window */
2697 mouse_replay_pointer();
2699 frame_show(self->frame);
2700 show = TRUE;
2702 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2703 it needs to be in IconicState. This includes when it is on another
2704 desktop!
2706 client_change_wm_state(self);
2708 return show;
2711 gboolean client_hide(ObClient *self)
2713 gboolean hide = FALSE;
2715 if (!client_should_show(self)) {
2716 /* We don't need to ignore enter events here.
2717 The window can hide/iconify in 3 different ways:
2718 1 - through an x message. in this case we ignore all enter events
2719 caused by responding to the x message (unless underMouse)
2720 2 - by a keyboard action. in this case we ignore all enter events
2721 caused by the action
2722 3 - by a mouse action. in this case they are doing stuff with the
2723 mouse and focus _should_ move.
2725 Also in action_end, we simulate an enter event that can't be ignored
2726 so trying to ignore them is futile in case 3 anyways
2729 /* replay pending pointer event before hiding the window, in case it
2730 should be going to the window */
2731 mouse_replay_pointer();
2733 frame_hide(self->frame);
2734 hide = TRUE;
2736 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2737 it needs to be in IconicState. This includes when it is on another
2738 desktop!
2740 client_change_wm_state(self);
2742 return hide;
2745 void client_showhide(ObClient *self)
2747 if (!client_show(self))
2748 client_hide(self);
2751 gboolean client_normal(ObClient *self) {
2752 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2753 self->type == OB_CLIENT_TYPE_DOCK ||
2754 self->type == OB_CLIENT_TYPE_SPLASH);
2757 gboolean client_helper(ObClient *self)
2759 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2760 self->type == OB_CLIENT_TYPE_MENU ||
2761 self->type == OB_CLIENT_TYPE_TOOLBAR);
2764 gboolean client_mouse_focusable(ObClient *self)
2766 return !(self->type == OB_CLIENT_TYPE_MENU ||
2767 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2768 self->type == OB_CLIENT_TYPE_SPLASH ||
2769 self->type == OB_CLIENT_TYPE_DOCK);
2772 gboolean client_enter_focusable(ObClient *self)
2774 /* you can focus desktops but it shouldn't on enter */
2775 return (client_mouse_focusable(self) &&
2776 self->type != OB_CLIENT_TYPE_DESKTOP);
2779 static void client_apply_startup_state(ObClient *self,
2780 gint x, gint y, gint w, gint h)
2782 /* save the states that we are going to apply */
2783 gboolean iconic = self->iconic;
2784 gboolean fullscreen = self->fullscreen;
2785 gboolean undecorated = self->undecorated;
2786 gboolean shaded = self->shaded;
2787 gboolean demands_attention = self->demands_attention;
2788 gboolean max_horz = self->max_horz;
2789 gboolean max_vert = self->max_vert;
2790 Rect oldarea;
2791 gint l;
2793 /* turn them all off in the client, so they won't affect the window
2794 being placed */
2795 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2796 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2798 /* move the client to its placed position, or it it's already there,
2799 generate a ConfigureNotify telling the client where it is.
2801 do this after adjusting the frame. otherwise it gets all weird and
2802 clients don't work right
2804 do this before applying the states so they have the correct
2805 pre-max/pre-fullscreen values
2807 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2808 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2809 self->window, x, y, w, h);
2810 /* save the area, and make it where it should be for the premax stuff */
2811 oldarea = self->area;
2812 RECT_SET(self->area, x, y, w, h);
2814 /* apply the states. these are in a carefully crafted order.. */
2816 if (iconic)
2817 client_iconify(self, TRUE, FALSE, TRUE);
2818 if (undecorated)
2819 client_set_undecorated(self, TRUE);
2820 if (shaded)
2821 client_shade(self, TRUE);
2822 if (demands_attention)
2823 client_hilite(self, TRUE);
2825 if (max_vert && max_horz)
2826 client_maximize(self, TRUE, 0);
2827 else if (max_vert)
2828 client_maximize(self, TRUE, 2);
2829 else if (max_horz)
2830 client_maximize(self, TRUE, 1);
2832 /* fullscreen removes the ability to apply other states */
2833 if (fullscreen)
2834 client_fullscreen(self, TRUE);
2836 /* make sure client_setup_decor_and_functions() is called at least once */
2837 client_setup_decor_and_functions(self, FALSE);
2839 /* if the window hasn't been configured yet, then do so now, in fact the
2840 x,y,w,h may _not_ be the same as the area rect, which can end up
2841 meaning that the client isn't properly moved/resized by the fullscreen
2842 function
2843 pho can cause this because it maps at size of the screen but not 0,0
2844 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2845 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2846 cuz thats where the pre-fullscreen will be. however the actual area is
2847 not, so this needs to be called even if we have fullscreened/maxed
2849 self->area = oldarea;
2850 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2852 /* nothing to do for the other states:
2853 skip_taskbar
2854 skip_pager
2855 modal
2856 above
2857 below
2861 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2863 /* these should be the current values. this is for when you're not moving,
2864 just resizing */
2865 g_assert(*x == self->area.x);
2866 g_assert(oldw == self->area.width);
2868 /* horizontal */
2869 switch (self->gravity) {
2870 default:
2871 case NorthWestGravity:
2872 case WestGravity:
2873 case SouthWestGravity:
2874 case StaticGravity:
2875 case ForgetGravity:
2876 break;
2877 case NorthGravity:
2878 case CenterGravity:
2879 case SouthGravity:
2880 *x -= (neww - oldw) / 2;
2881 break;
2882 case NorthEastGravity:
2883 case EastGravity:
2884 case SouthEastGravity:
2885 *x -= neww - oldw;
2886 break;
2890 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2892 /* these should be the current values. this is for when you're not moving,
2893 just resizing */
2894 g_assert(*y == self->area.y);
2895 g_assert(oldh == self->area.height);
2897 /* vertical */
2898 switch (self->gravity) {
2899 default:
2900 case NorthWestGravity:
2901 case NorthGravity:
2902 case NorthEastGravity:
2903 case StaticGravity:
2904 case ForgetGravity:
2905 break;
2906 case WestGravity:
2907 case CenterGravity:
2908 case EastGravity:
2909 *y -= (newh - oldh) / 2;
2910 break;
2911 case SouthWestGravity:
2912 case SouthGravity:
2913 case SouthEastGravity:
2914 *y -= newh - oldh;
2915 break;
2919 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2920 gint *logicalw, gint *logicalh,
2921 gboolean user)
2923 Rect desired = {*x, *y, *w, *h};
2924 frame_rect_to_frame(self->frame, &desired);
2926 /* make the frame recalculate its dimensions n shit without changing
2927 anything visible for real, this way the constraints below can work with
2928 the updated frame dimensions. */
2929 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2931 /* cap any X windows at the size of an unsigned short */
2932 *w = MIN(*w,
2933 (gint)G_MAXUSHORT
2934 - self->frame->size.left - self->frame->size.right);
2935 *h = MIN(*h,
2936 (gint)G_MAXUSHORT
2937 - self->frame->size.top - self->frame->size.bottom);
2939 /* gets the frame's position */
2940 frame_client_gravity(self->frame, x, y);
2942 /* these positions are frame positions, not client positions */
2944 /* set the size and position if fullscreen */
2945 if (self->fullscreen) {
2946 const Rect *a;
2947 guint i;
2949 i = screen_find_monitor(&desired);
2950 a = screen_physical_area_monitor(i);
2952 *x = a->x;
2953 *y = a->y;
2954 *w = a->width;
2955 *h = a->height;
2957 user = FALSE; /* ignore if the client can't be moved/resized when it
2958 is fullscreening */
2959 } else if (self->max_horz || self->max_vert) {
2960 Rect *a;
2961 guint i;
2963 /* use all possible struts when maximizing to the full screen */
2964 i = screen_find_monitor(&desired);
2965 a = screen_area(self->desktop, i,
2966 (self->max_horz && self->max_vert ? NULL : &desired));
2968 /* set the size and position if maximized */
2969 if (self->max_horz) {
2970 *x = a->x;
2971 *w = a->width - self->frame->size.left - self->frame->size.right;
2973 if (self->max_vert) {
2974 *y = a->y;
2975 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2978 user = FALSE; /* ignore if the client can't be moved/resized when it
2979 is maximizing */
2981 g_slice_free(Rect, a);
2984 /* gets the client's position */
2985 frame_frame_gravity(self->frame, x, y);
2987 /* work within the preferred sizes given by the window, these may have
2988 changed rather than it's requested width and height, so always run
2989 through this code */
2991 gint basew, baseh, minw, minh;
2992 gint incw, inch, maxw, maxh;
2993 gfloat minratio, maxratio;
2995 incw = self->size_inc.width;
2996 inch = self->size_inc.height;
2997 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2998 0 : self->min_ratio;
2999 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
3000 0 : self->max_ratio;
3002 /* base size is substituted with min size if not specified */
3003 if (self->base_size.width >= 0 || self->base_size.height >= 0) {
3004 basew = self->base_size.width;
3005 baseh = self->base_size.height;
3006 } else {
3007 basew = self->min_size.width;
3008 baseh = self->min_size.height;
3010 /* min size is substituted with base size if not specified */
3011 if (self->min_size.width || self->min_size.height) {
3012 minw = self->min_size.width;
3013 minh = self->min_size.height;
3014 } else {
3015 minw = self->base_size.width;
3016 minh = self->base_size.height;
3019 /* This comment is no longer true */
3020 /* if this is a user-requested resize, then check against min/max
3021 sizes */
3023 /* smaller than min size or bigger than max size? */
3024 if (*w > self->max_size.width) *w = self->max_size.width;
3025 if (*w < minw) *w = minw;
3026 if (*h > self->max_size.height) *h = self->max_size.height;
3027 if (*h < minh) *h = minh;
3029 *w -= basew;
3030 *h -= baseh;
3032 /* the sizes to used for maximized */
3033 maxw = *w;
3034 maxh = *h;
3036 /* keep to the increments */
3037 *w /= incw;
3038 *h /= inch;
3040 /* you cannot resize to nothing */
3041 if (basew + *w < 1) *w = 1 - basew;
3042 if (baseh + *h < 1) *h = 1 - baseh;
3044 /* save the logical size */
3045 *logicalw = incw > 1 ? *w : *w + basew;
3046 *logicalh = inch > 1 ? *h : *h + baseh;
3048 *w *= incw;
3049 *h *= inch;
3051 /* if maximized/fs then don't use the size increments */
3052 if (self->fullscreen || self->max_horz) *w = maxw;
3053 if (self->fullscreen || self->max_vert) *h = maxh;
3055 *w += basew;
3056 *h += baseh;
3058 /* adjust the height to match the width for the aspect ratios.
3059 for this, min size is not substituted for base size ever. */
3060 *w -= self->base_size.width;
3061 *h -= self->base_size.height;
3063 if (minratio)
3064 if (*h * minratio > *w) {
3065 *h = (gint)(*w / minratio);
3067 /* you cannot resize to nothing */
3068 if (*h < 1) {
3069 *h = 1;
3070 *w = (gint)(*h * minratio);
3073 if (maxratio)
3074 if (*h * maxratio < *w) {
3075 *h = (gint)(*w / maxratio);
3077 /* you cannot resize to nothing */
3078 if (*h < 1) {
3079 *h = 1;
3080 *w = (gint)(*h * minratio);
3084 *w += self->base_size.width;
3085 *h += self->base_size.height;
3088 /* these override the above states! if you cant move you can't move! */
3089 if (user) {
3090 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3091 *x = self->area.x;
3092 *y = self->area.y;
3094 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3095 *w = self->area.width;
3096 *h = self->area.height;
3100 g_assert(*w > 0);
3101 g_assert(*h > 0);
3104 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3105 gboolean user, gboolean final, gboolean force_reply)
3107 Rect oldframe, oldclient;
3108 gboolean send_resize_client;
3109 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3110 gboolean fmoved, fresized;
3111 guint fdecor = self->frame->decorations;
3112 gboolean fhorz = self->frame->max_horz;
3113 gboolean fvert = self->frame->max_vert;
3114 gint logicalw, logicalh;
3116 /* find the new x, y, width, and height (and logical size) */
3117 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3119 /* set the logical size if things changed */
3120 if (!(w == self->area.width && h == self->area.height))
3121 SIZE_SET(self->logical_size, logicalw, logicalh);
3123 /* figure out if we moved or resized or what */
3124 moved = (x != self->area.x || y != self->area.y);
3125 resized = (w != self->area.width || h != self->area.height);
3127 oldframe = self->frame->area;
3128 oldclient = self->area;
3129 RECT_SET(self->area, x, y, w, h);
3131 /* for app-requested resizes, always resize if 'resized' is true.
3132 for user-requested ones, only resize if final is true, or when
3133 resizing in redraw mode */
3134 send_resize_client = ((!user && resized) ||
3135 (user && (final ||
3136 (resized && config_resize_redraw))));
3138 /* if the client is enlarging, then resize the client before the frame */
3139 if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
3140 XMoveResizeWindow(obt_display, self->window,
3141 self->frame->size.left, self->frame->size.top,
3142 MAX(w, oldclient.width), MAX(h, oldclient.height));
3143 frame_adjust_client_area(self->frame);
3146 /* find the frame's dimensions and move/resize it */
3147 fmoved = moved;
3148 fresized = resized;
3150 /* if decorations changed, then readjust everything for the frame */
3151 if (self->decorations != fdecor ||
3152 self->max_horz != fhorz || self->max_vert != fvert)
3154 fmoved = fresized = TRUE;
3157 /* adjust the frame */
3158 if (fmoved || fresized) {
3159 gulong ignore_start;
3160 if (!user)
3161 ignore_start = event_start_ignore_all_enters();
3163 /* replay pending pointer event before move the window, in case it
3164 would change what window gets the event */
3165 mouse_replay_pointer();
3167 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
3169 if (!user)
3170 event_end_ignore_all_enters(ignore_start);
3173 if (!user || final) {
3174 gint oldrx = self->root_pos.x;
3175 gint oldry = self->root_pos.y;
3176 /* we have reset the client to 0 border width, so don't include
3177 it in these coords */
3178 POINT_SET(self->root_pos,
3179 self->frame->area.x + self->frame->size.left -
3180 self->border_width,
3181 self->frame->area.y + self->frame->size.top -
3182 self->border_width);
3183 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3184 rootmoved = TRUE;
3187 /* This is kinda tricky and should not be changed.. let me explain!
3189 When user = FALSE, then the request is coming from the application
3190 itself, and we are more strict about when to send a synthetic
3191 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3192 in this case (or send one if force_reply is true)
3194 When user = TRUE, then the request is coming from "us", like when we
3195 maximize a window or something. In this case we are more lenient. We
3196 used to follow the same rules as above, but _Java_ Swing can't handle
3197 this. So just to appease Swing, when user = TRUE, we always send
3198 a synthetic ConfigureNotify to give the window its root coordinates.
3199 Lastly, if force_reply is TRUE, we always send a
3200 ConfigureNotify, which is needed during a resize with XSYNCronization.
3202 if ((!user && !resized && (rootmoved || force_reply)) ||
3203 (user && ((!resized && force_reply) || (final && rootmoved))))
3205 XEvent event;
3207 event.type = ConfigureNotify;
3208 event.xconfigure.display = obt_display;
3209 event.xconfigure.event = self->window;
3210 event.xconfigure.window = self->window;
3212 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3213 self->title, self->root_pos.x, self->root_pos.y, w, h);
3215 /* root window real coords */
3216 event.xconfigure.x = self->root_pos.x;
3217 event.xconfigure.y = self->root_pos.y;
3218 event.xconfigure.width = w;
3219 event.xconfigure.height = h;
3220 event.xconfigure.border_width = self->border_width;
3221 event.xconfigure.above = None;
3222 event.xconfigure.override_redirect = FALSE;
3223 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3224 FALSE, StructureNotifyMask, &event);
3227 /* if the client is shrinking, then resize the frame before the client.
3229 both of these resize sections may run, because the top one only resizes
3230 in the direction that is growing
3232 if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
3234 frame_adjust_client_area(self->frame);
3235 XMoveResizeWindow(obt_display, self->window,
3236 self->frame->size.left, self->frame->size.top, w, h);
3239 XFlush(obt_display);
3241 /* if it moved between monitors, then this can affect the stacking
3242 layer of this window or others - for fullscreen windows.
3243 also if it changed to/from oldschool fullscreen then its layer may
3244 change
3246 watch out tho, don't try change stacking stuff if the window is no
3247 longer being managed !
3249 if (self->managed &&
3250 (screen_find_monitor(&self->frame->area) !=
3251 screen_find_monitor(&oldframe) ||
3252 (final && (client_is_oldfullscreen(self, &oldclient) !=
3253 client_is_oldfullscreen(self, &self->area)))))
3255 client_calc_layer(self);
3259 void client_fullscreen(ObClient *self, gboolean fs)
3261 gint x, y, w, h;
3263 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3264 self->fullscreen == fs) return; /* already done */
3266 self->fullscreen = fs;
3267 client_change_state(self); /* change the state hints on the client */
3269 if (fs) {
3270 self->pre_fullscreen_area = self->area;
3271 self->pre_fullscreen_max_horz = self->max_horz;
3272 self->pre_fullscreen_max_vert = self->max_vert;
3274 /* if the window is maximized, its area isn't all that meaningful.
3275 save its premax area instead. */
3276 if (self->max_horz) {
3277 self->pre_fullscreen_area.x = self->pre_max_area.x;
3278 self->pre_fullscreen_area.width = self->pre_max_area.width;
3280 if (self->max_vert) {
3281 self->pre_fullscreen_area.y = self->pre_max_area.y;
3282 self->pre_fullscreen_area.height = self->pre_max_area.height;
3285 /* these will help configure_full figure out where to fullscreen
3286 the window */
3287 x = self->area.x;
3288 y = self->area.y;
3289 w = self->area.width;
3290 h = self->area.height;
3291 } else {
3292 g_assert(self->pre_fullscreen_area.width > 0 &&
3293 self->pre_fullscreen_area.height > 0);
3295 self->max_horz = self->pre_fullscreen_max_horz;
3296 self->max_vert = self->pre_fullscreen_max_vert;
3297 if (self->max_horz) {
3298 self->pre_max_area.x = self->pre_fullscreen_area.x;
3299 self->pre_max_area.width = self->pre_fullscreen_area.width;
3301 if (self->max_vert) {
3302 self->pre_max_area.y = self->pre_fullscreen_area.y;
3303 self->pre_max_area.height = self->pre_fullscreen_area.height;
3306 x = self->pre_fullscreen_area.x;
3307 y = self->pre_fullscreen_area.y;
3308 w = self->pre_fullscreen_area.width;
3309 h = self->pre_fullscreen_area.height;
3310 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3313 ob_debug("Window %s going fullscreen (%d)",
3314 self->title, self->fullscreen);
3316 if (fs) {
3317 /* make sure the window is on some monitor */
3318 client_find_onscreen(self, &x, &y, w, h, FALSE);
3321 client_setup_decor_and_functions(self, FALSE);
3322 client_move_resize(self, x, y, w, h);
3324 /* and adjust our layer/stacking. do this after resizing the window,
3325 and applying decorations, because windows which fill the screen are
3326 considered "fullscreen" and it affects their layer */
3327 client_calc_layer(self);
3329 if (fs) {
3330 /* try focus us when we go into fullscreen mode */
3331 client_focus(self);
3335 static void client_iconify_recursive(ObClient *self,
3336 gboolean iconic, gboolean curdesk,
3337 gboolean hide_animation)
3339 GSList *it;
3340 gboolean changed = FALSE;
3342 if (self->iconic != iconic) {
3343 ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3344 self->window);
3346 if (iconic) {
3347 /* don't let non-normal windows iconify along with their parents
3348 or whatever */
3349 if (client_normal(self)) {
3350 self->iconic = iconic;
3352 /* update the focus lists.. iconic windows go to the bottom of
3353 the list. this will also call focus_cycle_addremove(). */
3354 focus_order_to_bottom(self);
3356 changed = TRUE;
3358 } else {
3359 self->iconic = iconic;
3361 if (curdesk && self->desktop != screen_desktop &&
3362 self->desktop != DESKTOP_ALL)
3363 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3365 /* this puts it after the current focused window, this will
3366 also cause focus_cycle_addremove() to be called for the
3367 client */
3368 focus_order_like_new(self);
3370 changed = TRUE;
3374 if (changed) {
3375 client_change_state(self);
3376 if (config_animate_iconify && !hide_animation)
3377 frame_begin_iconify_animation(self->frame, iconic);
3378 /* do this after starting the animation so it doesn't flash */
3379 client_showhide(self);
3382 /* iconify all direct transients, and deiconify all transients
3383 (non-direct too) */
3384 for (it = self->transients; it; it = g_slist_next(it))
3385 if (it->data != self)
3386 if (client_is_direct_child(self, it->data) || !iconic)
3387 client_iconify_recursive(it->data, iconic, curdesk,
3388 hide_animation);
3391 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3392 gboolean hide_animation)
3394 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3395 /* move up the transient chain as far as possible first */
3396 self = client_search_top_direct_parent(self);
3397 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3401 void client_maximize(ObClient *self, gboolean max, gint dir)
3403 gint x, y, w, h;
3405 g_assert(dir == 0 || dir == 1 || dir == 2);
3406 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
3408 /* check if already done */
3409 if (max) {
3410 if (dir == 0 && self->max_horz && self->max_vert) return;
3411 if (dir == 1 && self->max_horz) return;
3412 if (dir == 2 && self->max_vert) return;
3413 } else {
3414 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3415 if (dir == 1 && !self->max_horz) return;
3416 if (dir == 2 && !self->max_vert) return;
3419 /* these will help configure_full figure out which screen to fill with
3420 the window */
3421 x = self->area.x;
3422 y = self->area.y;
3423 w = self->area.width;
3424 h = self->area.height;
3426 if (max) {
3427 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3428 RECT_SET(self->pre_max_area,
3429 self->area.x, self->pre_max_area.y,
3430 self->area.width, self->pre_max_area.height);
3432 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3433 RECT_SET(self->pre_max_area,
3434 self->pre_max_area.x, self->area.y,
3435 self->pre_max_area.width, self->area.height);
3437 } else {
3438 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3439 g_assert(self->pre_max_area.width > 0);
3441 x = self->pre_max_area.x;
3442 w = self->pre_max_area.width;
3444 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3445 0, self->pre_max_area.height);
3447 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3448 g_assert(self->pre_max_area.height > 0);
3450 y = self->pre_max_area.y;
3451 h = self->pre_max_area.height;
3453 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3454 self->pre_max_area.width, 0);
3458 if (dir == 0 || dir == 1) /* horz */
3459 self->max_horz = max;
3460 if (dir == 0 || dir == 2) /* vert */
3461 self->max_vert = max;
3463 if (max) {
3464 /* make sure the window is on some monitor */
3465 client_find_onscreen(self, &x, &y, w, h, FALSE);
3468 client_change_state(self); /* change the state hints on the client */
3470 client_setup_decor_and_functions(self, FALSE);
3471 client_move_resize(self, x, y, w, h);
3474 void client_shade(ObClient *self, gboolean shade)
3476 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3477 shade) || /* can't shade */
3478 self->shaded == shade) return; /* already done */
3480 self->shaded = shade;
3481 client_change_state(self);
3482 client_change_wm_state(self); /* the window is being hidden/shown */
3483 /* resize the frame to just the titlebar */
3484 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3487 static void client_ping_event(ObClient *self, gboolean dead)
3489 if (self->not_responding != dead) {
3490 self->not_responding = dead;
3491 client_update_title(self);
3493 if (dead)
3494 /* the client isn't responding, so ask to kill it */
3495 client_prompt_kill(self);
3496 else {
3497 /* it came back to life ! */
3499 if (self->kill_prompt) {
3500 prompt_unref(self->kill_prompt);
3501 self->kill_prompt = NULL;
3504 self->kill_level = 0;
3509 void client_close(ObClient *self)
3511 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3513 /* if closing an internal obprompt, that is just cancelling it */
3514 if (self->prompt) {
3515 prompt_cancel(self->prompt);
3516 return;
3519 /* in the case that the client provides no means to requesting that it
3520 close, we just kill it */
3521 if (!self->delete_window)
3522 /* don't use client_kill(), we should only kill based on PID in
3523 response to a lack of PING replies */
3524 XKillClient(obt_display, self->window);
3525 else {
3526 /* request the client to close with WM_DELETE_WINDOW */
3527 OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
3528 OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
3529 0, 0, 0, NoEventMask);
3531 /* we're trying to close the window, so see if it is responding. if it
3532 is not, then we will let them kill the window */
3533 if (self->ping)
3534 ping_start(self, client_ping_event);
3536 /* if we already know the window isn't responding (maybe they clicked
3537 no in the kill dialog but it hasn't come back to life), then show
3538 the kill dialog */
3539 if (self->not_responding)
3540 client_prompt_kill(self);
3544 #define OB_KILL_RESULT_NO 0
3545 #define OB_KILL_RESULT_YES 1
3547 static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
3549 ObClient *self = data;
3551 if (result == OB_KILL_RESULT_YES)
3552 client_kill(self);
3553 return TRUE; /* call the cleanup func */
3556 static void client_kill_cleanup(ObPrompt *p, gpointer data)
3558 ObClient *self = data;
3560 g_assert(p == self->kill_prompt);
3562 prompt_unref(self->kill_prompt);
3563 self->kill_prompt = NULL;
3566 static void client_prompt_kill(ObClient *self)
3568 /* check if we're already prompting */
3569 if (!self->kill_prompt) {
3570 ObPromptAnswer answers[] = {
3571 { 0, OB_KILL_RESULT_NO },
3572 { 0, OB_KILL_RESULT_YES }
3574 gchar *m;
3575 const gchar *y, *title;
3577 title = self->original_title;
3578 if (title[0] == '\0') {
3579 /* empty string, so use its parent */
3580 ObClient *p = client_search_top_direct_parent(self);
3581 if (p) title = p->original_title;
3584 if (client_on_localhost(self)) {
3585 const gchar *sig;
3587 if (self->kill_level == 0)
3588 sig = "terminate";
3589 else
3590 sig = "kill";
3592 m = g_strdup_printf
3593 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3594 title, sig);
3595 y = _("End Process");
3597 else {
3598 m = g_strdup_printf
3599 (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3600 title);
3601 y = _("Disconnect");
3603 /* set the dialog buttons' text */
3604 answers[0].text = _("Cancel"); /* "no" */
3605 answers[1].text = y; /* "yes" */
3607 self->kill_prompt = prompt_new(m, NULL, answers,
3608 sizeof(answers)/sizeof(answers[0]),
3609 OB_KILL_RESULT_NO, /* default = no */
3610 OB_KILL_RESULT_NO, /* cancel = no */
3611 client_kill_requested,
3612 client_kill_cleanup,
3613 self);
3614 g_free(m);
3617 prompt_show(self->kill_prompt, self, TRUE);
3620 void client_kill(ObClient *self)
3622 /* don't kill our own windows */
3623 if (self->prompt) return;
3625 if (client_on_localhost(self) && self->pid) {
3626 /* running on the local host */
3627 if (self->kill_level == 0) {
3628 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3629 self->window, self->pid);
3630 kill(self->pid, SIGTERM);
3631 ++self->kill_level;
3633 /* show that we're trying to kill it */
3634 client_update_title(self);
3636 else {
3637 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3638 self->window, self->pid);
3639 kill(self->pid, SIGKILL); /* kill -9 */
3642 else {
3643 /* running on a remote host */
3644 XKillClient(obt_display, self->window);
3648 void client_hilite(ObClient *self, gboolean hilite)
3650 if (self->demands_attention == hilite)
3651 return; /* no change */
3653 /* don't allow focused windows to hilite */
3654 self->demands_attention = hilite && !client_focused(self);
3655 if (self->frame != NULL) { /* if we're mapping, just set the state */
3656 if (self->demands_attention) {
3657 frame_flash_start(self->frame);
3659 /* if the window is on another desktop then raise it and make it
3660 the most recently used window */
3661 if (self->desktop != screen_desktop &&
3662 self->desktop != DESKTOP_ALL)
3664 stacking_raise(CLIENT_AS_WINDOW(self));
3665 focus_order_to_top(self);
3668 else
3669 frame_flash_stop(self->frame);
3670 client_change_state(self);
3674 static void client_set_desktop_recursive(ObClient *self,
3675 guint target,
3676 gboolean donthide,
3677 gboolean dontraise)
3679 guint old;
3680 GSList *it;
3682 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3684 ob_debug("Setting desktop %u", target+1);
3686 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3688 old = self->desktop;
3689 self->desktop = target;
3690 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
3691 /* the frame can display the current desktop state */
3692 frame_adjust_state(self->frame);
3693 /* 'move' the window to the new desktop */
3694 if (!donthide)
3695 client_hide(self);
3696 client_show(self);
3697 /* raise if it was not already on the desktop */
3698 if (old != DESKTOP_ALL && !dontraise)
3699 stacking_raise(CLIENT_AS_WINDOW(self));
3700 if (STRUT_EXISTS(self->strut))
3701 screen_update_areas();
3702 else
3703 /* the new desktop's geometry may be different, so we may need to
3704 resize, for example if we are maximized */
3705 client_reconfigure(self, FALSE);
3707 focus_cycle_addremove(self, FALSE);
3710 /* move all transients */
3711 for (it = self->transients; it; it = g_slist_next(it))
3712 if (it->data != self)
3713 if (client_is_direct_child(self, it->data))
3714 client_set_desktop_recursive(it->data, target,
3715 donthide, dontraise);
3718 void client_set_desktop(ObClient *self, guint target,
3719 gboolean donthide, gboolean dontraise)
3721 self = client_search_top_direct_parent(self);
3722 client_set_desktop_recursive(self, target, donthide, dontraise);
3724 focus_cycle_addremove(NULL, TRUE);
3727 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3729 while (child != parent && (child = client_direct_parent(child)));
3730 return child == parent;
3733 ObClient *client_search_modal_child(ObClient *self)
3735 GSList *it;
3736 ObClient *ret;
3738 for (it = self->transients; it; it = g_slist_next(it)) {
3739 ObClient *c = it->data;
3740 if ((ret = client_search_modal_child(c))) return ret;
3741 if (c->modal) return c;
3743 return NULL;
3746 struct ObClientFindDestroyUnmap {
3747 Window window;
3748 gint ignore_unmaps;
3751 static gboolean find_destroy_unmap(XEvent *e, gpointer data)
3753 struct ObClientFindDestroyUnmap *find = data;
3754 if (e->type == DestroyNotify)
3755 return e->xdestroywindow.window == find->window;
3756 if (e->type == UnmapNotify && e->xunmap.window == find->window)
3757 /* ignore the first $find->ignore_unmaps$ many unmap events */
3758 return --find->ignore_unmaps < 0;
3759 return FALSE;
3762 gboolean client_validate(ObClient *self)
3764 struct ObClientFindDestroyUnmap find;
3766 XSync(obt_display, FALSE); /* get all events on the server */
3768 find.window = self->window;
3769 find.ignore_unmaps = self->ignore_unmaps;
3770 if (xqueue_exists_local(find_destroy_unmap, &find))
3771 return FALSE;
3773 return TRUE;
3776 void client_set_wm_state(ObClient *self, glong state)
3778 if (state == self->wmstate) return; /* no change */
3780 switch (state) {
3781 case IconicState:
3782 client_iconify(self, TRUE, TRUE, FALSE);
3783 break;
3784 case NormalState:
3785 client_iconify(self, FALSE, TRUE, FALSE);
3786 break;
3790 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3792 gboolean shaded = self->shaded;
3793 gboolean fullscreen = self->fullscreen;
3794 gboolean undecorated = self->undecorated;
3795 gboolean max_horz = self->max_horz;
3796 gboolean max_vert = self->max_vert;
3797 gboolean modal = self->modal;
3798 gboolean iconic = self->iconic;
3799 gboolean demands_attention = self->demands_attention;
3800 gboolean above = self->above;
3801 gboolean below = self->below;
3802 gint i;
3803 gboolean value;
3805 if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3806 action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3807 action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3808 /* an invalid action was passed to the client message, ignore it */
3809 return;
3811 for (i = 0; i < 2; ++i) {
3812 Atom state = i == 0 ? data1 : data2;
3814 if (!state) continue;
3816 /* if toggling, then pick whether we're adding or removing */
3817 if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3818 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3819 value = modal;
3820 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3821 value = self->max_vert;
3822 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3823 value = self->max_horz;
3824 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3825 value = shaded;
3826 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3827 value = self->skip_taskbar;
3828 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3829 value = self->skip_pager;
3830 else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3831 value = self->iconic;
3832 else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3833 value = fullscreen;
3834 else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3835 value = self->above;
3836 else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3837 value = self->below;
3838 else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3839 value = self->demands_attention;
3840 else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3841 value = undecorated;
3842 else
3843 g_assert_not_reached();
3844 action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3845 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3848 value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
3849 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3850 modal = value;
3851 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3852 max_vert = value;
3853 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3854 max_horz = value;
3855 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3856 shaded = value;
3857 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3858 self->skip_taskbar = value;
3859 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3860 self->skip_pager = value;
3861 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3862 iconic = value;
3863 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3864 fullscreen = value;
3865 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3866 above = value;
3867 /* only unset below when setting above, otherwise you can't get to
3868 the normal layer */
3869 if (value)
3870 below = FALSE;
3871 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3872 /* and vice versa */
3873 if (value)
3874 above = FALSE;
3875 below = value;
3876 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3877 demands_attention = value;
3878 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3879 undecorated = value;
3883 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3884 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3885 /* toggling both */
3886 if (max_horz == max_vert) { /* both going the same way */
3887 client_maximize(self, max_horz, 0);
3888 } else {
3889 client_maximize(self, max_horz, 1);
3890 client_maximize(self, max_vert, 2);
3892 } else {
3893 /* toggling one */
3894 if (max_horz != self->max_horz)
3895 client_maximize(self, max_horz, 1);
3896 else
3897 client_maximize(self, max_vert, 2);
3900 /* change fullscreen state before shading, as it will affect if the window
3901 can shade or not */
3902 if (fullscreen != self->fullscreen)
3903 client_fullscreen(self, fullscreen);
3904 if (shaded != self->shaded)
3905 client_shade(self, shaded);
3906 if (undecorated != self->undecorated)
3907 client_set_undecorated(self, undecorated);
3908 if (above != self->above || below != self->below) {
3909 self->above = above;
3910 self->below = below;
3911 client_calc_layer(self);
3914 if (modal != self->modal) {
3915 self->modal = modal;
3916 /* when a window changes modality, then its stacking order with its
3917 transients needs to change */
3918 stacking_raise(CLIENT_AS_WINDOW(self));
3920 /* it also may get focused. if something is focused that shouldn't
3921 be focused anymore, then move the focus */
3922 if (focus_client && client_focus_target(focus_client) != focus_client)
3923 client_focus(focus_client);
3926 if (iconic != self->iconic)
3927 client_iconify(self, iconic, FALSE, FALSE);
3929 if (demands_attention != self->demands_attention)
3930 client_hilite(self, demands_attention);
3932 client_change_state(self); /* change the hint to reflect these changes */
3934 focus_cycle_addremove(self, TRUE);
3937 ObClient *client_focus_target(ObClient *self)
3939 ObClient *child = NULL;
3941 child = client_search_modal_child(self);
3942 if (child) return child;
3943 return self;
3946 gboolean client_can_focus(ObClient *self)
3948 /* choose the correct target */
3949 self = client_focus_target(self);
3951 if (!self->frame->visible)
3952 return FALSE;
3954 if (!(self->can_focus || self->focus_notify))
3955 return FALSE;
3957 return TRUE;
3960 gboolean client_focus(ObClient *self)
3962 if (!client_validate(self)) return FALSE;
3964 /* we might not focus this window, so if we have modal children which would
3965 be focused instead, bring them to this desktop */
3966 client_bring_modal_windows(self);
3968 /* choose the correct target */
3969 self = client_focus_target(self);
3971 if (!client_can_focus(self)) {
3972 ob_debug_type(OB_DEBUG_FOCUS,
3973 "Client %s can't be focused", self->title);
3974 return FALSE;
3977 /* if we have helper windows they should be there with the window */
3978 client_bring_helper_windows(self);
3980 ob_debug_type(OB_DEBUG_FOCUS,
3981 "Focusing client \"%s\" (0x%x) at time %u",
3982 self->title, self->window, event_time());
3984 /* if using focus_delay, stop the timer now so that focus doesn't
3985 go moving on us */
3986 event_halt_focus_delay();
3988 obt_display_ignore_errors(TRUE);
3990 if (self->can_focus) {
3991 /* This can cause a BadMatch error with CurrentTime, or if an app
3992 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3993 XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
3994 event_time());
3997 if (self->focus_notify) {
3998 XEvent ce;
3999 ce.xclient.type = ClientMessage;
4000 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
4001 ce.xclient.display = obt_display;
4002 ce.xclient.window = self->window;
4003 ce.xclient.format = 32;
4004 ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
4005 ce.xclient.data.l[1] = event_time();
4006 ce.xclient.data.l[2] = 0l;
4007 ce.xclient.data.l[3] = 0l;
4008 ce.xclient.data.l[4] = 0l;
4009 XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
4012 obt_display_ignore_errors(FALSE);
4014 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
4015 obt_display_error_occured);
4016 return !obt_display_error_occured;
4019 static void client_present(ObClient *self, gboolean here, gboolean raise,
4020 gboolean unshade)
4022 if (client_normal(self) && screen_showing_desktop)
4023 screen_show_desktop(FALSE, self);
4024 if (self->iconic)
4025 client_iconify(self, FALSE, here, FALSE);
4026 if (self->desktop != DESKTOP_ALL &&
4027 self->desktop != screen_desktop)
4029 if (here)
4030 client_set_desktop(self, screen_desktop, FALSE, TRUE);
4031 else
4032 screen_set_desktop(self->desktop, FALSE);
4033 } else if (!self->frame->visible)
4034 /* if its not visible for other reasons, then don't mess
4035 with it */
4036 return;
4037 if (self->shaded && unshade)
4038 client_shade(self, FALSE);
4039 if (raise)
4040 stacking_raise(CLIENT_AS_WINDOW(self));
4042 client_focus(self);
4045 /* this function exists to map to the net_active_window message in the ewmh */
4046 void client_activate(ObClient *self, gboolean desktop,
4047 gboolean here, gboolean raise,
4048 gboolean unshade, gboolean user)
4050 self = client_focus_target(self);
4052 if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
4053 client_present(self, here, raise, unshade);
4054 else
4055 client_hilite(self, TRUE);
4058 static void client_bring_windows_recursive(ObClient *self,
4059 guint desktop,
4060 gboolean helpers,
4061 gboolean modals,
4062 gboolean iconic)
4064 GSList *it;
4066 for (it = self->transients; it; it = g_slist_next(it))
4067 client_bring_windows_recursive(it->data, desktop,
4068 helpers, modals, iconic);
4070 if (((helpers && client_helper(self)) ||
4071 (modals && self->modal)) &&
4072 (!screen_compare_desktops(self->desktop, desktop) ||
4073 (iconic && self->iconic)))
4075 if (iconic && self->iconic)
4076 client_iconify(self, FALSE, TRUE, FALSE);
4077 else
4078 client_set_desktop(self, desktop, FALSE, FALSE);
4082 void client_bring_helper_windows(ObClient *self)
4084 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
4087 void client_bring_modal_windows(ObClient *self)
4089 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
4092 gboolean client_focused(ObClient *self)
4094 return self == focus_client;
4097 RrImage* client_icon(ObClient *self)
4099 RrImage *ret = NULL;
4101 if (self->icon_set)
4102 ret = self->icon_set;
4103 else if (self->parents) {
4104 GSList *it;
4105 for (it = self->parents; it && !ret; it = g_slist_next(it))
4106 ret = client_icon(it->data);
4108 if (!ret)
4109 ret = client_default_icon;
4110 return ret;
4113 void client_set_layer(ObClient *self, gint layer)
4115 if (layer < 0) {
4116 self->below = TRUE;
4117 self->above = FALSE;
4118 } else if (layer == 0) {
4119 self->below = self->above = FALSE;
4120 } else {
4121 self->below = FALSE;
4122 self->above = TRUE;
4124 client_calc_layer(self);
4125 client_change_state(self); /* reflect this in the state hints */
4128 void client_set_undecorated(ObClient *self, gboolean undecorated)
4130 if (self->undecorated != undecorated &&
4131 /* don't let it undecorate if the function is missing, but let
4132 it redecorate */
4133 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4135 self->undecorated = undecorated;
4136 client_setup_decor_and_functions(self, TRUE);
4137 client_change_state(self); /* reflect this in the state hints */
4141 guint client_monitor(ObClient *self)
4143 return screen_find_monitor(&self->frame->area);
4146 ObClient *client_direct_parent(ObClient *self)
4148 if (!self->parents) return NULL;
4149 if (self->transient_for_group) return NULL;
4150 return self->parents->data;
4153 ObClient *client_search_top_direct_parent(ObClient *self)
4155 ObClient *p;
4156 while ((p = client_direct_parent(self))) self = p;
4157 return self;
4160 static GSList *client_search_all_top_parents_internal(ObClient *self,
4161 gboolean bylayer,
4162 ObStackingLayer layer)
4164 GSList *ret;
4165 ObClient *p;
4167 /* move up the direct transient chain as far as possible */
4168 while ((p = client_direct_parent(self)) &&
4169 (!bylayer || p->layer == layer))
4170 self = p;
4172 if (!self->parents)
4173 ret = g_slist_prepend(NULL, self);
4174 else
4175 ret = g_slist_copy(self->parents);
4177 return ret;
4180 GSList *client_search_all_top_parents(ObClient *self)
4182 return client_search_all_top_parents_internal(self, FALSE, 0);
4185 GSList *client_search_all_top_parents_layer(ObClient *self)
4187 return client_search_all_top_parents_internal(self, TRUE, self->layer);
4190 ObClient *client_search_focus_parent(ObClient *self)
4192 GSList *it;
4194 for (it = self->parents; it; it = g_slist_next(it))
4195 if (client_focused(it->data)) return it->data;
4197 return NULL;
4200 ObClient *client_search_focus_parent_full(ObClient *self)
4202 GSList *it;
4203 ObClient *ret = NULL;
4205 for (it = self->parents; it; it = g_slist_next(it)) {
4206 if (client_focused(it->data))
4207 ret = it->data;
4208 else
4209 ret = client_search_focus_parent_full(it->data);
4210 if (ret) break;
4212 return ret;
4215 ObClient *client_search_parent(ObClient *self, ObClient *search)
4217 GSList *it;
4219 for (it = self->parents; it; it = g_slist_next(it))
4220 if (it->data == search) return search;
4222 return NULL;
4225 ObClient *client_search_transient(ObClient *self, ObClient *search)
4227 GSList *sit;
4229 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4230 if (sit->data == search)
4231 return search;
4232 if (client_search_transient(sit->data, search))
4233 return search;
4235 return NULL;
4238 static void detect_edge(Rect area, ObDirection dir,
4239 gint my_head, gint my_size,
4240 gint my_edge_start, gint my_edge_size,
4241 gint *dest, gboolean *near_edge)
4243 gint edge_start, edge_size, head, tail;
4244 gboolean skip_head = FALSE, skip_tail = FALSE;
4246 switch (dir) {
4247 case OB_DIRECTION_NORTH:
4248 case OB_DIRECTION_SOUTH:
4249 edge_start = area.x;
4250 edge_size = area.width;
4251 break;
4252 case OB_DIRECTION_EAST:
4253 case OB_DIRECTION_WEST:
4254 edge_start = area.y;
4255 edge_size = area.height;
4256 break;
4257 default:
4258 g_assert_not_reached();
4261 /* do we collide with this window? */
4262 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4263 edge_start, edge_size))
4264 return;
4266 switch (dir) {
4267 case OB_DIRECTION_NORTH:
4268 head = RECT_BOTTOM(area);
4269 tail = RECT_TOP(area);
4270 break;
4271 case OB_DIRECTION_SOUTH:
4272 head = RECT_TOP(area);
4273 tail = RECT_BOTTOM(area);
4274 break;
4275 case OB_DIRECTION_WEST:
4276 head = RECT_RIGHT(area);
4277 tail = RECT_LEFT(area);
4278 break;
4279 case OB_DIRECTION_EAST:
4280 head = RECT_LEFT(area);
4281 tail = RECT_RIGHT(area);
4282 break;
4283 default:
4284 g_assert_not_reached();
4286 switch (dir) {
4287 case OB_DIRECTION_NORTH:
4288 case OB_DIRECTION_WEST:
4289 /* check if our window is past the head of this window */
4290 if (my_head <= head + 1)
4291 skip_head = TRUE;
4292 /* check if our window's tail is past the tail of this window */
4293 if (my_head + my_size - 1 <= tail)
4294 skip_tail = TRUE;
4295 /* check if the head of this window is closer than the previously
4296 chosen edge (take into account that the previously chosen
4297 edge might have been a tail, not a head) */
4298 if (head + (*near_edge ? 0 : my_size) <= *dest)
4299 skip_head = TRUE;
4300 /* check if the tail of this window is closer than the previously
4301 chosen edge (take into account that the previously chosen
4302 edge might have been a head, not a tail) */
4303 if (tail - (!*near_edge ? 0 : my_size) <= *dest)
4304 skip_tail = TRUE;
4305 break;
4306 case OB_DIRECTION_SOUTH:
4307 case OB_DIRECTION_EAST:
4308 /* check if our window is past the head of this window */
4309 if (my_head >= head - 1)
4310 skip_head = TRUE;
4311 /* check if our window's tail is past the tail of this window */
4312 if (my_head - my_size + 1 >= tail)
4313 skip_tail = TRUE;
4314 /* check if the head of this window is closer than the previously
4315 chosen edge (take into account that the previously chosen
4316 edge might have been a tail, not a head) */
4317 if (head - (*near_edge ? 0 : my_size) >= *dest)
4318 skip_head = TRUE;
4319 /* check if the tail of this window is closer than the previously
4320 chosen edge (take into account that the previously chosen
4321 edge might have been a head, not a tail) */
4322 if (tail + (!*near_edge ? 0 : my_size) >= *dest)
4323 skip_tail = TRUE;
4324 break;
4325 default:
4326 g_assert_not_reached();
4329 ob_debug("my head %d size %d", my_head, my_size);
4330 ob_debug("head %d tail %d dest %d", head, tail, *dest);
4331 if (!skip_head) {
4332 ob_debug("using near edge %d", head);
4333 *dest = head;
4334 *near_edge = TRUE;
4336 else if (!skip_tail) {
4337 ob_debug("using far edge %d", tail);
4338 *dest = tail;
4339 *near_edge = FALSE;
4343 void client_find_edge_directional(ObClient *self, ObDirection dir,
4344 gint my_head, gint my_size,
4345 gint my_edge_start, gint my_edge_size,
4346 gint *dest, gboolean *near_edge)
4348 GList *it;
4349 Rect *a;
4350 Rect dock_area;
4351 gint edge;
4352 guint i;
4354 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4355 &self->frame->area);
4357 switch (dir) {
4358 case OB_DIRECTION_NORTH:
4359 edge = RECT_TOP(*a) - 1;
4360 break;
4361 case OB_DIRECTION_SOUTH:
4362 edge = RECT_BOTTOM(*a) + 1;
4363 break;
4364 case OB_DIRECTION_EAST:
4365 edge = RECT_RIGHT(*a) + 1;
4366 break;
4367 case OB_DIRECTION_WEST:
4368 edge = RECT_LEFT(*a) - 1;
4369 break;
4370 default:
4371 g_assert_not_reached();
4373 /* default to the far edge, then narrow it down */
4374 *dest = edge;
4375 *near_edge = TRUE;
4377 /* search for edges of monitors */
4378 for (i = 0; i < screen_num_monitors; ++i) {
4379 Rect *area = screen_area(self->desktop, i, NULL);
4380 detect_edge(*area, dir, my_head, my_size, my_edge_start,
4381 my_edge_size, dest, near_edge);
4382 g_slice_free(Rect, area);
4385 /* search for edges of clients */
4386 for (it = client_list; it; it = g_list_next(it)) {
4387 ObClient *cur = it->data;
4389 /* skip windows to not bump into */
4390 if (cur == self)
4391 continue;
4392 if (cur->iconic)
4393 continue;
4394 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4395 cur->desktop != screen_desktop)
4396 continue;
4398 ob_debug("trying window %s", cur->title);
4400 detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
4401 my_edge_size, dest, near_edge);
4403 dock_get_area(&dock_area);
4404 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4405 my_edge_size, dest, near_edge);
4407 g_slice_free(Rect, a);
4410 void client_find_move_directional(ObClient *self, ObDirection dir,
4411 gint *x, gint *y)
4413 gint head, size;
4414 gint e, e_start, e_size;
4415 gboolean near;
4417 switch (dir) {
4418 case OB_DIRECTION_EAST:
4419 head = RECT_RIGHT(self->frame->area);
4420 size = self->frame->area.width;
4421 e_start = RECT_TOP(self->frame->area);
4422 e_size = self->frame->area.height;
4423 break;
4424 case OB_DIRECTION_WEST:
4425 head = RECT_LEFT(self->frame->area);
4426 size = self->frame->area.width;
4427 e_start = RECT_TOP(self->frame->area);
4428 e_size = self->frame->area.height;
4429 break;
4430 case OB_DIRECTION_NORTH:
4431 head = RECT_TOP(self->frame->area);
4432 size = self->frame->area.height;
4433 e_start = RECT_LEFT(self->frame->area);
4434 e_size = self->frame->area.width;
4435 break;
4436 case OB_DIRECTION_SOUTH:
4437 head = RECT_BOTTOM(self->frame->area);
4438 size = self->frame->area.height;
4439 e_start = RECT_LEFT(self->frame->area);
4440 e_size = self->frame->area.width;
4441 break;
4442 default:
4443 g_assert_not_reached();
4446 client_find_edge_directional(self, dir, head, size,
4447 e_start, e_size, &e, &near);
4448 *x = self->frame->area.x;
4449 *y = self->frame->area.y;
4450 switch (dir) {
4451 case OB_DIRECTION_EAST:
4452 if (near) e -= self->frame->area.width;
4453 else e++;
4454 *x = e;
4455 break;
4456 case OB_DIRECTION_WEST:
4457 if (near) e++;
4458 else e -= self->frame->area.width;
4459 *x = e;
4460 break;
4461 case OB_DIRECTION_NORTH:
4462 if (near) e++;
4463 else e -= self->frame->area.height;
4464 *y = e;
4465 break;
4466 case OB_DIRECTION_SOUTH:
4467 if (near) e -= self->frame->area.height;
4468 else e++;
4469 *y = e;
4470 break;
4471 default:
4472 g_assert_not_reached();
4474 frame_frame_gravity(self->frame, x, y);
4477 void client_find_resize_directional(ObClient *self, ObDirection side,
4478 gboolean grow,
4479 gint *x, gint *y, gint *w, gint *h)
4481 gint head;
4482 gint e, e_start, e_size, delta;
4483 gboolean near;
4484 ObDirection dir;
4486 switch (side) {
4487 case OB_DIRECTION_EAST:
4488 head = RECT_RIGHT(self->frame->area) +
4489 (self->size_inc.width - 1) * (grow ? 1 : 0);
4490 e_start = RECT_TOP(self->frame->area);
4491 e_size = self->frame->area.height;
4492 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4493 break;
4494 case OB_DIRECTION_WEST:
4495 head = RECT_LEFT(self->frame->area) -
4496 (self->size_inc.width - 1) * (grow ? 1 : 0);
4497 e_start = RECT_TOP(self->frame->area);
4498 e_size = self->frame->area.height;
4499 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4500 break;
4501 case OB_DIRECTION_NORTH:
4502 head = RECT_TOP(self->frame->area) -
4503 (self->size_inc.height - 1) * (grow ? 1 : 0);
4504 e_start = RECT_LEFT(self->frame->area);
4505 e_size = self->frame->area.width;
4506 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4507 break;
4508 case OB_DIRECTION_SOUTH:
4509 head = RECT_BOTTOM(self->frame->area) +
4510 (self->size_inc.height - 1) * (grow ? 1 : 0);
4511 e_start = RECT_LEFT(self->frame->area);
4512 e_size = self->frame->area.width;
4513 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4514 break;
4515 default:
4516 g_assert_not_reached();
4519 ob_debug("head %d dir %d", head, dir);
4520 client_find_edge_directional(self, dir, head, 1,
4521 e_start, e_size, &e, &near);
4522 ob_debug("edge %d", e);
4523 *x = self->frame->area.x;
4524 *y = self->frame->area.y;
4525 *w = self->frame->area.width;
4526 *h = self->frame->area.height;
4527 switch (side) {
4528 case OB_DIRECTION_EAST:
4529 if (grow == near) --e;
4530 delta = e - RECT_RIGHT(self->frame->area);
4531 *w += delta;
4532 break;
4533 case OB_DIRECTION_WEST:
4534 if (grow == near) ++e;
4535 delta = RECT_LEFT(self->frame->area) - e;
4536 *x -= delta;
4537 *w += delta;
4538 break;
4539 case OB_DIRECTION_NORTH:
4540 if (grow == near) ++e;
4541 delta = RECT_TOP(self->frame->area) - e;
4542 *y -= delta;
4543 *h += delta;
4544 break;
4545 case OB_DIRECTION_SOUTH:
4546 if (grow == near) --e;
4547 delta = e - RECT_BOTTOM(self->frame->area);
4548 *h += delta;
4549 break;
4550 default:
4551 g_assert_not_reached();
4553 frame_frame_gravity(self->frame, x, y);
4554 *w -= self->frame->size.left + self->frame->size.right;
4555 *h -= self->frame->size.top + self->frame->size.bottom;
4558 ObClient* client_under_pointer(void)
4560 gint x, y;
4561 GList *it;
4562 ObClient *ret = NULL;
4564 if (screen_pointer_pos(&x, &y)) {
4565 for (it = stacking_list; it; it = g_list_next(it)) {
4566 if (WINDOW_IS_CLIENT(it->data)) {
4567 ObClient *c = WINDOW_AS_CLIENT(it->data);
4568 if (c->frame->visible &&
4569 /* check the desktop, this is done during desktop
4570 switching and windows are shown/hidden status is not
4571 reliable */
4572 (c->desktop == screen_desktop ||
4573 c->desktop == DESKTOP_ALL) &&
4574 /* ignore all animating windows */
4575 !frame_iconify_animating(c->frame) &&
4576 RECT_CONTAINS(c->frame->area, x, y))
4578 ret = c;
4579 break;
4584 return ret;
4587 gboolean client_has_group_siblings(ObClient *self)
4589 return self->group && self->group->members->next;
4592 gboolean client_has_relative(ObClient *self)
4594 return client_has_parent(self) ||
4595 client_has_group_siblings(self) ||
4596 client_has_children(self);
4599 /*! Returns TRUE if the client is running on the same machine as Openbox */
4600 gboolean client_on_localhost(ObClient *self)
4602 return self->client_machine == NULL;