1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
25 #include "moveresize.h"
28 #include "engine_interface.h"
34 #include "focus_cycle.h"
39 #include "menuframe.h"
42 #include "render/render.h"
44 #include "obt/display.h"
53 # include <signal.h> /* for kill() */
57 #include <X11/Xutil.h>
59 /*! The event mask to grab on client windows */
60 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
63 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
66 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
67 ButtonPressMask | ButtonReleaseMask | \
68 SubstructureRedirectMask | FocusChangeMask)
72 ObClientCallback func;
76 GList *client_list = NULL;
78 static GSList *client_destroy_notifies = NULL;
80 static void client_get_all(ObClient *self, gboolean real);
81 static void client_get_startup_id(ObClient *self);
82 static void client_get_session_ids(ObClient *self);
83 static void client_get_area(ObClient *self);
84 static void client_get_desktop(ObClient *self);
85 static void client_get_state(ObClient *self);
86 static void client_get_shaped(ObClient *self);
87 static void client_get_mwm_hints(ObClient *self);
88 static void client_get_colormap(ObClient *self);
89 static void client_set_desktop_recursive(ObClient *self,
93 static void client_change_allowed_actions(ObClient *self);
94 static void client_change_state(ObClient *self);
95 static void client_change_wm_state(ObClient *self);
96 static void client_apply_startup_state(ObClient *self,
97 gint x, gint y, gint w, gint h);
98 static void client_restore_session_state(ObClient *self);
99 static gboolean client_restore_session_stacking(ObClient *self);
100 static ObAppSettings *client_get_settings_state(ObClient *self);
101 static void client_update_transient_tree(ObClient *self,
102 ObGroup *oldgroup, ObGroup *newgroup,
103 gboolean oldgtran, gboolean newgtran,
105 ObClient *newparent);
106 static void client_present(ObClient *self, gboolean here, gboolean raise,
108 static GSList *client_search_all_top_parents_internal(ObClient *self,
110 ObStackingLayer layer);
111 static void client_call_notifies(ObClient *self, GSList *list);
112 static void client_ping_event(ObClient *self, gboolean dead);
113 static void client_prompt_kill(ObClient *self);
115 Window createWindow(Window parent, Visual *visual, gulong mask,
116 XSetWindowAttributes *attrib)
118 return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0, (visual ? 32
119 : RrDepth(ob_rr_inst)), InputOutput, (visual ? visual
120 : RrVisual(ob_rr_inst)), mask, attrib);
124 Visual *check_32bit_client(ObClient *c)
126 XWindowAttributes wattrib;
129 /* we're already running at 32 bit depth, yay. we don't need to use their
131 if (RrDepth(ob_rr_inst) == 32)
134 ret = XGetWindowAttributes(obt_display, c->w_client, &wattrib);
135 g_assert(ret != BadDrawable);
136 g_assert(ret != BadWindow);
138 if (wattrib.depth == 32)
139 return wattrib.visual;
143 void client_startup(gboolean reconfig)
145 if (reconfig) return;
150 void client_shutdown(gboolean reconfig)
152 if (reconfig) return;
155 static void client_call_notifies(ObClient *self, GSList *list)
159 for (it = list; it; it = g_slist_next(it)) {
160 ClientCallback *d = it->data;
161 d->func(self, d->data);
165 void client_add_destroy_notify(ObClientCallback func, gpointer data)
167 ClientCallback *d = g_new(ClientCallback, 1);
170 client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
173 void client_remove_destroy_notify(ObClientCallback func)
177 for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
178 ClientCallback *d = it->data;
179 if (d->func == func) {
181 client_destroy_notifies =
182 g_slist_delete_link(client_destroy_notifies, it);
188 void client_set_list(void)
190 Window *windows, *win_it;
192 guint size = g_list_length(client_list);
194 /* create an array of the window ids */
196 windows = g_new(Window, size);
198 for (it = client_list; it; it = g_list_next(it), ++win_it)
199 *win_it = ((ObClient*)it->data)->w_client;
203 OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
204 (gulong*)windows, size);
212 void client_manage(Window window, ObPrompt *prompt)
215 XSetWindowAttributes attrib_set;
216 gboolean activate = FALSE;
217 ObAppSettings *settings;
218 gboolean transient = FALSE;
219 Rect place, *monitor;
220 Time launch_time, map_time;
222 ob_debug("Managing window: 0x%lx", window);
224 map_time = event_get_server_time();
226 /* choose the events we want to receive on the CLIENT window
227 (ObPrompt windows can request events too) */
228 attrib_set.event_mask = CLIENT_EVENTMASK |
229 (prompt ? prompt->event_mask : 0);
230 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
231 XChangeWindowAttributes(obt_display, window,
232 CWEventMask|CWDontPropagate, &attrib_set);
234 /* create the ObClient struct, and populate it from the hints on the
236 self = g_new0(ObClient, 1);
237 self->obwin.type = OB_WINDOW_CLASS_CLIENT;
238 self->w_client = window;
239 self->prompt = prompt;
241 /* non-zero defaults */
242 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
243 self->gravity = NorthWestGravity;
244 self->desktop = screen_num_desktops; /* always an invalid value */
246 /* get all the stuff off the window */
247 client_get_all(self, TRUE);
249 ob_debug("Window type: %d", self->type);
250 ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
252 /* now we have all of the window's information so we can set this up.
253 do this before creating the frame, so it can tell that we are still
254 mapping and doesn't go applying things right away */
255 client_setup_decor_and_functions(self, FALSE);
257 /* specify that if we exit, the window should not be destroyed and
258 should be reparented back to root automatically, unless we are managing
259 an internal ObPrompt window */
261 XChangeSaveSet(obt_display, window, SetModeInsert);
263 /* create the decoration frame for the client window */
264 Visual *visual = check_32bit_client(self);
266 XSetWindowAttributes attrib;
268 /* client has a 32-bit visual */
269 mask |= CWColormap | CWBackPixel | CWBorderPixel;
270 /* create a colormap with the visual */
271 attrib.colormap = XCreateColormap(
272 obt_display, RootWindow(obt_display, ob_screen), visual,
274 attrib.background_pixel = BlackPixel(obt_display, ob_screen);
275 attrib.border_pixel = BlackPixel(obt_display, ob_screen);
277 self->w_frame = createWindow(RootWindow(obt_display, ob_screen), visual,
279 self->frame = frame_engine->frame_new(self, self->w_client, self->w_frame);
280 /* reparent the client to the frame */
281 XReparentWindow(obt_display, self->w_client, self->w_frame, 0, 0);
284 When reparenting the client window, it is usually not mapped yet, since
285 this occurs from a MapRequest. However, in the case where Openbox is
286 starting up, the window is already mapped, so we'll see an unmap event
289 if (ob_state() == OB_STATE_STARTING)
290 ++self->ignore_unmaps;
292 /* select the event mask on the client's parent (to receive config/map
293 req's) the ButtonPress is to catch clicks on the client border */
294 XSelectInput(obt_display, self->w_frame, FRAME_EVENTMASK);
296 frame_engine->frame_grab(self->frame, window_map);
298 /* we've grabbed everything and set everything that we need to at mapping
302 /* per-app settings override stuff from client_get_all, and return the
303 settings for other uses too. the returned settings is a shallow copy,
304 that needs to be freed with g_free(). */
305 settings = client_get_settings_state(self);
306 /* the session should get the last say though */
307 client_restore_session_state(self);
309 /* tell startup notification that this app started */
310 launch_time = sn_app_started(self->startup_id, self->class, self->name);
312 /* do this after we have a frame.. it uses the frame to help determine the
313 WM_STATE to apply. */
314 client_change_state(self);
316 /* add ourselves to the focus order */
317 focus_order_add_new(self);
319 /* do this to add ourselves to the stacking list in a non-intrusive way */
320 client_calc_layer(self);
322 /* focus the new window? */
323 if (ob_state() != OB_STATE_STARTING &&
324 (!self->session || self->session->focused) &&
325 /* this means focus=true for window is same as config_focus_new=true */
326 ((config_focus_new || (settings && settings->focus == 1)) ||
327 client_search_focus_tree_full(self)) &&
328 /* this checks for focus=false for the window */
329 (!settings || settings->focus != 0) &&
330 focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
335 /* remove the client's border */
336 XSetWindowBorderWidth(obt_display, self->w_client, 0);
338 /* adjust the frame to the client's size before showing or placing
340 frame_engine->frame_set_hover_flag (self->frame, OB_BUTTON_NONE);
341 frame_engine->frame_set_press_flag (self->frame, OB_BUTTON_NONE);
342 frame_engine->frame_set_is_focus (self->frame, FALSE);
343 frame_engine->frame_set_decorations (self->frame, self->decorations);
344 frame_engine->frame_update_title (self->frame, self->title);
345 frame_engine->frame_update_layout (self->frame, self->area, FALSE, TRUE);
346 frame_engine->frame_update_skin (self->frame);
348 /* where the frame was placed is where the window was originally */
350 monitor = screen_physical_area_monitor(screen_find_monitor(&place));
352 /* figure out placement for the window if the window is new */
353 if (ob_state() == OB_STATE_RUNNING) {
354 ob_debug("Positioned: %s @ %d %d",
355 (!self->positioned ? "no" :
356 (self->positioned == PPosition ? "program specified" :
357 (self->positioned == USPosition ? "user specified" :
358 (self->positioned == (PPosition | USPosition) ?
359 "program + user specified" :
360 "BADNESS !?")))), place.x, place.y);
362 ob_debug("Sized: %s @ %d %d",
363 (!self->sized ? "no" :
364 (self->sized == PSize ? "program specified" :
365 (self->sized == USSize ? "user specified" :
366 (self->sized == (PSize | USSize) ?
367 "program + user specified" :
368 "BADNESS !?")))), place.width, place.height);
370 /* splash screens are also returned as TRUE for transient,
371 and so will be forced on screen below */
372 transient = place_client(self, &place.x, &place.y, settings);
374 /* make sure the window is visible. */
375 client_find_onscreen(self, &place.x, &place.y,
376 place.width, place.height,
377 /* non-normal clients has less rules, and
378 windows that are being restored from a
379 session do also. we can assume you want
380 it back where you saved it. Clients saying
381 they placed themselves are subjected to
382 harder rules, ones that are placed by
383 place.c or by the user are allowed partially
384 off-screen and on xinerama divides (ie,
385 it is up to the placement routines to avoid
386 the xinerama divides)
388 splash screens get "transient" set to TRUE by
389 the place_client call
391 ob_state() == OB_STATE_RUNNING &&
393 (!((self->positioned & USPosition) ||
394 (settings && settings->pos_given)) &&
395 client_normal(self) &&
397 /* don't move oldschool fullscreen windows to
398 fit inside the struts (fixes Acroread, which
399 makes its fullscreen window fit the screen
400 but it is not USSize'd or USPosition'd) */
401 !(self->decorations == 0 &&
402 RECT_EQUAL(place, *monitor)))));
405 /* if the window isn't user-sized, then make it fit inside
406 the visible screen area on its monitor. Use basically the same rules
407 for forcing the window on screen in the client_find_onscreen call.
409 do this after place_client, it chooses the monitor!
411 splash screens get "transient" set to TRUE by
412 the place_client call
414 if (ob_state() == OB_STATE_RUNNING &&
416 (!(self->sized & USSize || self->positioned & USPosition) &&
417 client_normal(self) &&
419 /* don't shrink oldschool fullscreen windows to fit inside the
420 struts (fixes Acroread, which makes its fullscreen window
421 fit the screen but it is not USSize'd or USPosition'd) */
422 !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
424 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
427 frame_engine->frame_get_size(self->frame, &size);
428 /* get the size of the frame */
429 place.width += size.left + size.right;
430 place.height += size.top + size.bottom;
432 /* fit the window inside the area */
433 place.width = MIN(place.width, a->width);
434 place.height = MIN(place.height, a->height);
436 ob_debug("setting window size to %dx%d", place.width, place.height);
438 /* get the size of the client back */
439 place.width -= size.left + size.right;
440 place.height -= size.top + size.bottom;
445 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
446 "some restrictions may apply",
447 self->w_client, place.x, place.y, place.width, place.height);
449 ob_debug(" but session requested %d, %d %d x %d instead, "
451 self->session->x, self->session->y,
452 self->session->w, self->session->h);
454 /* do this after the window is placed, so the premax/prefullscreen numbers
457 this also places the window
459 client_apply_startup_state(self, place.x, place.y,
460 place.width, place.height);
465 ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
466 activate ? "yes" : "no");
468 gboolean raise = FALSE;
470 /* This is focus stealing prevention */
471 ob_debug_type(OB_DEBUG_FOCUS,
472 "Want to focus new window 0x%x at time %u "
473 "launched at %u (last user interaction time %u)",
474 self->w_client, map_time, launch_time,
475 event_last_user_time);
477 if (menu_frame_visible || frame_engine->moveresize_in_progress) {
480 ob_debug_type(OB_DEBUG_FOCUS,
481 "Not focusing the window because the user is inside "
482 "an Openbox menu or is move/resizing a window and "
483 "we don't want to interrupt them");
486 /* if it's on another desktop */
487 else if (!(self->desktop == screen_desktop ||
488 self->desktop == DESKTOP_ALL) &&
489 /* the timestamp is from before you changed desktops */
490 launch_time && screen_desktop_user_time &&
491 !event_time_after(launch_time, screen_desktop_user_time))
495 ob_debug_type(OB_DEBUG_FOCUS,
496 "Not focusing the window because its on another "
499 /* If something is focused, and it's not our relative... */
500 else if (focus_client && client_search_focus_tree_full(self) == NULL &&
501 client_search_focus_group_full(self) == NULL)
503 /* If the user is working in another window right now, then don't
505 if (event_last_user_time && launch_time &&
506 event_time_after(event_last_user_time, launch_time) &&
507 event_last_user_time != launch_time &&
508 event_time_after(event_last_user_time,
509 map_time - OB_EVENT_USER_TIME_DELAY))
512 ob_debug_type(OB_DEBUG_FOCUS,
513 "Not focusing the window because the user is "
514 "working in another window");
516 /* If its a transient (and its parents aren't focused) */
517 else if (client_has_parent(self)) {
519 ob_debug_type(OB_DEBUG_FOCUS,
520 "Not focusing the window because it is a "
521 "transient, and its relatives aren't focused");
523 /* Don't steal focus from globally active clients.
524 I stole this idea from KWin. It seems nice.
526 else if (!(focus_client->can_focus ||
527 focus_client->focus_notify))
530 ob_debug_type(OB_DEBUG_FOCUS,
531 "Not focusing the window because a globally "
532 "active client has focus");
534 /* Don't move focus if it's not going to go to this window
536 else if (client_focus_target(self) != self) {
539 ob_debug_type(OB_DEBUG_FOCUS,
540 "Not focusing the window because another window "
541 "would get the focus anyway");
543 else if (!(self->desktop == screen_desktop ||
544 self->desktop == DESKTOP_ALL))
548 ob_debug_type(OB_DEBUG_FOCUS,
549 "Not focusing the window because it is on "
550 "another desktop and no relatives are focused ");
555 ob_debug_type(OB_DEBUG_FOCUS,
556 "Focus stealing prevention activated for %s at "
557 "time %u (last user interactioon time %u)",
558 self->title, map_time, event_last_user_time);
559 /* if the client isn't focused, then hilite it so the user
561 client_hilite(self, TRUE);
562 /* we may want to raise it even tho we're not activating it */
563 if (raise && !client_restore_session_stacking(self))
564 stacking_raise(CLIENT_AS_WINDOW(self));
568 /* This may look rather odd. Well it's because new windows are added
569 to the stacking order non-intrusively. If we're not going to focus
570 the new window or hilite it, then we raise it to the top. This will
571 take affect for things that don't get focused like splash screens.
572 Also if you don't have focus_new enabled, then it's going to get
573 raised to the top. Legacy begets legacy I guess?
575 if (!client_restore_session_stacking(self))
576 stacking_raise(CLIENT_AS_WINDOW(self));
579 mouse_grab_for_client(self, TRUE);
581 /* this has to happen before we try focus the window, but we want it to
582 happen after the client's stacking has been determined or it looks bad
586 if (!config_focus_under_mouse)
587 ignore_start = event_start_ignore_all_enters();
591 if (!config_focus_under_mouse)
592 event_end_ignore_all_enters(ignore_start);
596 gboolean stacked = client_restore_session_stacking(self);
597 client_present(self, FALSE, !stacked, TRUE);
600 /* add to client list/map */
601 client_list = g_list_append(client_list, self);
602 window_add(&self->w_client, CLIENT_AS_WINDOW(self));
604 /* this has to happen after we're in the client_list */
605 if (STRUT_EXISTS(self->strut))
606 screen_update_areas();
608 /* update the list hints */
611 /* watch for when the application stops responding. only do this for
612 normal windows, i.e. windows which have titlebars and close buttons
613 and things like that.
614 we don't need to stop pinging on unmanage, because it will be handled
615 automatically by the destroy callback!
617 if (self->ping && client_normal(self))
618 ping_start(self, client_ping_event);
620 /* free the ObAppSettings shallow copy */
623 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
624 window, self->w_frame, self->class);
628 ObClient *client_fake_manage(Window window)
631 ObAppSettings *settings;
633 ob_debug("Pretend-managing window: %lx", window);
635 /* do this minimal stuff to figure out the client's decorations */
637 self = g_new0(ObClient, 1);
638 self->w_client = window;
640 client_get_all(self, FALSE);
641 /* per-app settings override stuff, and return the settings for other
642 uses too. this returns a shallow copy that needs to be freed */
643 settings = client_get_settings_state(self);
645 client_setup_decor_and_functions(self, FALSE);
647 /* create the decoration frame for the client window and adjust its size */
648 Visual *visual = check_32bit_client(self);
650 XSetWindowAttributes attrib;
652 /* client has a 32-bit visual */
653 mask |= CWColormap | CWBackPixel | CWBorderPixel;
654 /* create a colormap with the visual */
655 attrib.colormap = XCreateColormap(
656 obt_display, RootWindow(obt_display, ob_screen), visual,
658 attrib.background_pixel = BlackPixel(obt_display, ob_screen);
659 attrib.border_pixel = BlackPixel(obt_display, ob_screen);
661 self->w_frame = createWindow(RootWindow(obt_display, ob_screen), visual,
663 self->frame = frame_engine->frame_new(self, self->w_client, self->w_frame);
664 frame_engine->frame_set_decorations (self->frame, self->decorations);
665 frame_engine->frame_update_title (self->frame, self->title);
666 frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
667 /* if this occurs while we are focus cycling, the indicator needs to
669 if (focus_cycle_target == self)
670 focus_cycle_draw_indicator(self);
671 frame_engine->frame_update_skin (self->frame);
674 frame_engine->frame_get_size(self->frame, &size);
675 ob_debug("gave extents left %d right %d top %d bottom %d",
676 size.left, size.right,
677 size.top, size.bottom);
679 /* free the ObAppSettings shallow copy */
685 void client_unmanage_all(void)
687 while (client_list != NULL)
688 client_unmanage(client_list->data);
691 void client_unmanage(ObClient *self)
697 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
698 self->w_client, self->w_frame,
699 self->class, self->title ? self->title : "");
701 g_assert(self != NULL);
703 /* we dont want events no more. do this before hiding the frame so we
704 don't generate more events */
705 XSelectInput(obt_display, self->w_client, NoEventMask);
707 /* ignore enter events from the unmap so it doesnt mess with the focus */
708 if (!config_focus_under_mouse)
709 ignore_start = event_start_ignore_all_enters();
711 client_hide_frame(self);
712 /* flush to send the hide to the server quickly */
715 if (!config_focus_under_mouse)
716 event_end_ignore_all_enters(ignore_start);
718 mouse_grab_for_client(self, FALSE);
720 /* remove the window from our save set, unless we are managing an internal
723 XChangeSaveSet(obt_display, self->w_client, SetModeDelete);
725 /* update the focus lists */
726 focus_order_remove(self);
727 if (client_focused(self)) {
728 /* don't leave an invalid focus_client */
732 /* if we're prompting to kill the client, close that */
733 prompt_unref(self->kill_prompt);
734 self->kill_prompt = NULL;
736 client_list = g_list_remove(client_list, self);
737 stacking_remove(self);
738 window_remove(self->w_client);
740 /* once the client is out of the list, update the struts to remove its
742 if (STRUT_EXISTS(self->strut))
743 screen_update_areas();
745 client_call_notifies(self, client_destroy_notifies);
747 /* tell our parent(s) that we're gone */
748 for (it = self->parents; it; it = g_slist_next(it))
749 ((ObClient*)it->data)->transients =
750 g_slist_remove(((ObClient*)it->data)->transients,self);
752 /* tell our transients that we're gone */
753 for (it = self->transients; it; it = g_slist_next(it)) {
754 ((ObClient*)it->data)->parents =
755 g_slist_remove(((ObClient*)it->data)->parents, self);
756 /* we could be keeping our children in a higher layer */
757 client_calc_layer(it->data);
760 /* remove from its group */
762 group_remove(self->group, self);
766 /* restore the window's original geometry so it is not lost */
772 if (self->fullscreen)
773 a = self->pre_fullscreen_area;
774 else if (self->max_horz || self->max_vert) {
775 if (self->max_horz) {
776 a.x = self->pre_max_area.x;
777 a.width = self->pre_max_area.width;
779 if (self->max_vert) {
780 a.y = self->pre_max_area.y;
781 a.height = self->pre_max_area.height;
785 self->fullscreen = self->max_horz = self->max_vert = FALSE;
786 frame_engine->frame_trigger(self, OB_TRIGGER_UNMAX_VERT);
787 frame_engine->frame_trigger(self, OB_TRIGGER_UNMAX_HORZ);
788 /* let it be moved and resized no matter what */
789 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
790 self->decorations = 0; /* unmanaged windows have no decor */
792 /* give the client its border back */
793 XSetWindowBorderWidth(obt_display, self->w_client, self->border_width);
795 client_move_resize(self, a.x, a.y, a.width, a.height);
798 /* reparent the window out of the frame, and free the frame */
800 gboolean reparent = TRUE;
801 /* check if the app has already reparented its window away */
802 while (XCheckTypedWindowEvent(obt_display, self->w_client,
803 ReparentNotify, &ev)) {
804 /* This check makes sure we don't catch our own reparent action to
805 our frame window. This doesn't count as the app reparenting itself
808 Reparent events that are generated by us are just discarded here.
809 They are of no consequence to us anyhow.
811 if (ev.xreparent.parent != self->w_frame) {
813 XPutBackEvent(obt_display, &ev);
819 /* according to the ICCCM - if the client doesn't reparent itself,
820 then we will reparent the window to root for them */
821 XReparentWindow(obt_display, self->w_client, RootWindow(
822 obt_display, ob_screen), self->area.x,
825 frame_engine->frame_ungrab(self->frame, window_map);
826 obt_main_loop_timeout_remove_data(ob_main_loop, client_flash_timeout, self, TRUE);
827 frame_engine->frame_free(self->frame);
830 if (ob_state() != OB_STATE_EXITING) {
831 /* these values should not be persisted across a window
833 OBT_PROP_ERASE(self->w_client, NET_WM_DESKTOP);
834 OBT_PROP_ERASE(self->w_client, NET_WM_STATE);
835 OBT_PROP_ERASE(self->w_client, WM_STATE);
837 /* if we're left in an unmapped state, the client wont be mapped.
838 this is bad, since we will no longer be managing the window on
840 XMapWindow(obt_display, self->w_client);
843 /* these should not be left on the window ever. other window managers
844 don't necessarily use them and it will mess them up (like compiz) */
845 OBT_PROP_ERASE(self->w_client, NET_WM_VISIBLE_NAME);
846 OBT_PROP_ERASE(self->w_client, NET_WM_VISIBLE_ICON_NAME);
848 /* update the list hints */
851 ob_debug("Unmanaged window 0x%lx", self->w_client);
853 /* free all data allocated in the client struct */
854 g_slist_free(self->transients);
855 for (j = 0; j < self->nicons; ++j)
856 g_free(self->icons[j].data);
857 if (self->nicons > 0)
859 g_free(self->startup_id);
860 g_free(self->wm_command);
862 g_free(self->icon_title);
863 g_free(self->original_title);
867 g_free(self->client_machine);
868 g_free(self->sm_client_id);
872 void client_fake_unmanage(ObClient *self)
874 /* this is all that got allocated to get the decorations */
876 frame_engine->frame_free(self->frame);
880 /*! Returns a new structure containing the per-app settings for this client.
881 The returned structure needs to be freed with g_free. */
882 static ObAppSettings *client_get_settings_state(ObClient *self)
884 ObAppSettings *settings;
887 settings = config_create_app_settings();
889 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
890 ObAppSettings *app = it->data;
891 gboolean match = TRUE;
893 g_assert(app->name != NULL || app->class != NULL);
895 /* we know that either name or class is not NULL so it will have to
896 match to use the rule */
898 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
900 else if (app->class &&
901 !g_pattern_match(app->class,
902 strlen(self->class), self->class, NULL))
904 else if (app->role &&
905 !g_pattern_match(app->role,
906 strlen(self->role), self->role, NULL))
910 ob_debug("Window matching: %s", app->name);
912 /* copy the settings to our struct, overriding the existing
913 settings if they are not defaults */
914 config_app_settings_copy_non_defaults(app, settings);
918 if (settings->shade != -1)
919 self->shaded = !!settings->shade;
920 if (settings->decor != -1)
921 self->undecorated = !settings->decor;
922 if (settings->iconic != -1)
923 self->iconic = !!settings->iconic;
924 if (settings->skip_pager != -1)
925 self->skip_pager = !!settings->skip_pager;
926 if (settings->skip_taskbar != -1)
927 self->skip_taskbar = !!settings->skip_taskbar;
929 if (settings->max_vert != -1)
930 self->max_vert = !!settings->max_vert;
931 if (settings->max_horz != -1)
932 self->max_horz = !!settings->max_horz;
934 if (settings->fullscreen != -1)
935 self->fullscreen = !!settings->fullscreen;
937 if (settings->desktop) {
938 if (settings->desktop == DESKTOP_ALL)
939 self->desktop = settings->desktop;
940 else if (settings->desktop > 0 &&
941 settings->desktop <= screen_num_desktops)
942 self->desktop = settings->desktop - 1;
945 if (settings->layer == -1) {
949 else if (settings->layer == 0) {
953 else if (settings->layer == 1) {
960 static void client_restore_session_state(ObClient *self)
964 ob_debug_type(OB_DEBUG_SM,
965 "Restore session for client %s", self->title);
967 if (!(it = session_state_find(self))) {
968 ob_debug_type(OB_DEBUG_SM,
969 "Session data not found for client %s", self->title);
973 self->session = it->data;
975 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
978 RECT_SET_POINT(self->area, self->session->x, self->session->y);
979 self->positioned = USPosition;
980 self->sized = USSize;
981 if (self->session->w > 0)
982 self->area.width = self->session->w;
983 if (self->session->h > 0)
984 self->area.height = self->session->h;
985 XResizeWindow(obt_display, self->w_client,
986 self->area.width, self->area.height);
988 self->desktop = (self->session->desktop == DESKTOP_ALL ?
989 self->session->desktop :
990 MIN(screen_num_desktops - 1, self->session->desktop));
991 OBT_PROP_SET32(self->w_client, NET_WM_DESKTOP, CARDINAL, self->desktop);
993 self->shaded = self->session->shaded;
994 self->iconic = self->session->iconic;
995 self->skip_pager = self->session->skip_pager;
996 self->skip_taskbar = self->session->skip_taskbar;
997 self->fullscreen = self->session->fullscreen;
998 self->above = self->session->above;
999 self->below = self->session->below;
1000 self->max_horz = self->session->max_horz;
1001 self->max_vert = self->session->max_vert;
1002 self->undecorated = self->session->undecorated;
1004 if (self->session->max_horz) {
1005 frame_engine->frame_trigger(self->frame, OB_TRIGGER_MAX_HORZ);
1008 frame_engine->frame_trigger(self->frame, OB_TRIGGER_UNMAX_HORZ);
1011 if (self->session->max_vert) {
1012 frame_engine->frame_trigger(self->frame, OB_TRIGGER_MAX_VERT);
1015 frame_engine->frame_trigger(self->frame, OB_TRIGGER_UNMAX_VERT);
1019 static gboolean client_restore_session_stacking(ObClient *self)
1023 if (!self->session) return FALSE;
1025 mypos = g_list_find(session_saved_state, self->session);
1026 if (!mypos) return FALSE;
1028 /* start above me and look for the first client */
1029 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
1032 for (cit = client_list; cit; cit = g_list_next(cit)) {
1033 ObClient *c = cit->data;
1034 /* found a client that was in the session, so go below it */
1035 if (c->session == it->data) {
1036 stacking_below(CLIENT_AS_WINDOW(self),
1037 CLIENT_AS_WINDOW(cit->data));
1045 void client_move_onscreen(ObClient *self, gboolean rude)
1047 gint x = self->area.x;
1048 gint y = self->area.y;
1049 if (client_find_onscreen(self, &x, &y,
1051 self->area.height, rude)) {
1052 client_move(self, x, y);
1056 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
1059 gint ox = *x, oy = *y;
1060 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
1066 RECT_SET(desired, *x, *y, w, h);
1067 frame_rect_to_frame(self, &desired);
1069 /* get where the frame would be */
1070 frame_client_gravity(self, x, y);
1073 frame_engine->frame_get_size(self->frame, &size);
1075 /* get the requested size of the window with decorations */
1076 fw = size.left + w + size.right;
1077 fh = size.top + h + 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 */
1082 Point oldtl, oldtr, oldbl, oldbr;
1083 Point newtl, newtr, newbl, newbr;
1084 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
1087 frame_engine->frame_get_window_area(self->frame, &area);
1088 POINT_SET(oldtl, area.x, area.y);
1089 POINT_SET(oldbr, area.x + area.width - 1,
1090 area.y + area.height - 1);
1091 POINT_SET(oldtr, oldbr.x, oldtl.y);
1092 POINT_SET(oldbl, oldtl.x, oldbr.y);
1094 POINT_SET(newtl, *x, *y);
1095 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
1096 POINT_SET(newtr, newbr.x, newtl.y);
1097 POINT_SET(newbl, newtl.x, newbr.y);
1099 /* is it moving or just resizing from some corner? */
1100 stationary_l = oldtl.x == newtl.x;
1101 stationary_r = oldtr.x == newtr.x;
1102 stationary_t = oldtl.y == newtl.y;
1103 stationary_b = oldbl.y == newbl.y;
1105 /* if left edge is growing and didnt move right edge */
1106 if (stationary_r && newtl.x < oldtl.x)
1108 /* if right edge is growing and didnt move left edge */
1109 if (stationary_l && newtr.x > oldtr.x)
1111 /* if top edge is growing and didnt move bottom edge */
1112 if (stationary_b && newtl.y < oldtl.y)
1114 /* if bottom edge is growing and didnt move top edge */
1115 if (stationary_t && newbl.y > oldbl.y)
1119 /* we iterate through every monitor that the window is at least partially
1120 on, to make sure it is obeying the rules on them all
1122 if the window does not appear on any monitors, then use the first one
1125 for (i = 0; i < screen_num_monitors; ++i) {
1128 if (!screen_physical_area_monitor_contains(i, &desired)) {
1129 if (i < screen_num_monitors - 1 || found_mon)
1132 /* the window is not inside any monitor! so just use the first
1134 a = screen_area(self->desktop, 0, NULL);
1137 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1140 /* This makes sure windows aren't entirely outside of the screen so you
1141 can't see them at all.
1142 It makes sure 10% of the window is on the screen at least. At don't
1143 let it move itself off the top of the screen, which would hide the
1144 titlebar on you. (The user can still do this if they want too, it's
1145 only limiting the application.
1147 if (client_normal(self)) {
1148 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1149 *x = a->x + a->width - fw/10;
1150 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1151 *y = a->y + a->height - fh/10;
1152 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1153 *x = a->x - fw*9/10;
1154 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1155 *y = a->y - fh*9/10;
1158 /* This here doesn't let windows even a pixel outside the
1159 struts/screen. When called from client_manage, programs placing
1160 themselves are forced completely onscreen, while things like
1161 xterm -geometry resolution-width/2 will work fine. Trying to
1162 place it completely offscreen will be handled in the above code.
1163 Sorry for this confused comment, i am tired. */
1164 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1165 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1166 *x = a->x + MAX(0, a->width - fw);
1168 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1169 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1170 *y = a->y + MAX(0, a->height - fh);
1175 /* get where the client should be */
1176 frame_frame_gravity(self, x, y);
1178 return ox != *x || oy != *y;
1181 static void client_get_all(ObClient *self, gboolean real)
1183 /* this is needed for the frame to set itself up */
1184 client_get_area(self);
1186 /* these things can change the decor and functions of the window */
1188 client_get_mwm_hints(self);
1189 /* this can change the mwmhints for special cases */
1190 client_get_type_and_transientness(self);
1191 client_get_state(self);
1192 client_update_normal_hints(self);
1194 /* get the session related properties, these can change decorations
1195 from per-app settings */
1196 client_get_session_ids(self);
1198 /* now we got everything that can affect the decorations */
1202 /* get this early so we have it for debugging */
1203 client_update_title(self);
1205 client_update_protocols(self);
1207 client_update_wmhints(self);
1208 /* this may have already been called from client_update_wmhints */
1209 if (!self->parents && !self->transient_for_group)
1210 client_update_transient_for(self);
1212 client_get_startup_id(self);
1213 client_get_desktop(self);/* uses transient data/group/startup id if a
1214 desktop is not specified */
1215 client_get_shaped(self);
1218 /* a couple type-based defaults for new windows */
1220 /* this makes sure that these windows appear on all desktops */
1221 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1222 self->desktop = DESKTOP_ALL;
1226 client_update_sync_request_counter(self);
1229 client_get_colormap(self);
1230 client_update_strut(self);
1231 client_update_icons(self);
1232 client_update_icon_geometry(self);
1235 static void client_get_startup_id(ObClient *self)
1237 if (!(OBT_PROP_GETS(self->w_client, NET_STARTUP_ID, utf8,
1238 &self->startup_id)))
1240 OBT_PROP_GETS(self->group->leader,
1241 NET_STARTUP_ID, utf8, &self->startup_id);
1244 static void client_get_area(ObClient *self)
1246 XWindowAttributes wattrib;
1249 ret = XGetWindowAttributes(obt_display, self->w_client, &wattrib);
1250 g_assert(ret != BadWindow);
1252 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1253 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1254 self->border_width = wattrib.border_width;
1256 ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
1257 wattrib.width, wattrib.height, wattrib.border_width);
1260 static void client_get_desktop(ObClient *self)
1262 guint32 d = screen_num_desktops; /* an always-invalid value */
1264 if (OBT_PROP_GET32(self->w_client, NET_WM_DESKTOP, CARDINAL, &d)) {
1265 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1266 self->desktop = screen_num_desktops - 1;
1269 ob_debug("client requested desktop 0x%x", self->desktop);
1272 gboolean first = TRUE;
1273 guint all = screen_num_desktops; /* not a valid value */
1275 /* if they are all on one desktop, then open it on the
1277 for (it = self->parents; it; it = g_slist_next(it)) {
1278 ObClient *c = it->data;
1280 if (c->desktop == DESKTOP_ALL) continue;
1286 else if (all != c->desktop)
1287 all = screen_num_desktops; /* make it invalid */
1289 if (all != screen_num_desktops) {
1290 self->desktop = all;
1292 ob_debug("client desktop set from parents: 0x%x",
1295 /* try get from the startup-notification protocol */
1296 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1297 if (self->desktop >= screen_num_desktops &&
1298 self->desktop != DESKTOP_ALL)
1299 self->desktop = screen_num_desktops - 1;
1300 ob_debug("client desktop set from startup-notification: 0x%x",
1303 /* defaults to the current desktop */
1305 self->desktop = screen_desktop;
1306 ob_debug("client desktop set to the current desktop: %d",
1312 static void client_get_state(ObClient *self)
1317 if (OBT_PROP_GETA32(self->w_client, NET_WM_STATE, ATOM, &state, &num)) {
1319 for (i = 0; i < num; ++i) {
1320 if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1322 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1323 self->shaded = TRUE;
1324 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1325 self->iconic = TRUE;
1326 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1327 self->skip_taskbar = TRUE;
1328 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1329 self->skip_pager = TRUE;
1330 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1331 self->fullscreen = TRUE;
1332 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
1333 self->max_vert = TRUE;
1334 frame_engine->frame_trigger(self->frame, OB_TRIGGER_MAX_VERT);
1336 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
1337 self->max_horz = TRUE;
1338 frame_engine->frame_trigger(self->frame, OB_TRIGGER_MAX_HORZ);
1340 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1342 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
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;
1354 static void client_get_shaped(ObClient *self)
1356 self->shaped = FALSE;
1358 if (obt_display_extension_shape) {
1363 XShapeSelectInput(obt_display, self->w_client, ShapeNotifyMask);
1365 XShapeQueryExtents(obt_display, self->w_client, &s, &foo,
1366 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1368 self->shaped = (s != 0);
1373 void client_update_transient_for(ObClient *self)
1376 ObClient *target = NULL;
1377 gboolean trangroup = FALSE;
1379 if (XGetTransientForHint(obt_display, self->w_client, &t)) {
1380 if (t != self->w_client) { /* cant 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
1394 if (!target && self->group && t == obt_root(ob_screen))
1396 } else if (self->group && self->transient)
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)
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));
1425 Group transient windows are not allowed to have other group
1426 transient windows as their children.
1430 /* No change has occured */
1431 if (oldgroup == newgroup &&
1432 oldgtran == newgtran &&
1433 oldparent == newparent) return;
1435 /** Remove the client from the transient tree **/
1437 for (it = self->transients; it; it = next) {
1438 next = g_slist_next(it);
1440 self->transients = g_slist_delete_link(self->transients, it);
1441 c->parents = g_slist_remove(c->parents, self);
1443 for (it = self->parents; it; it = next) {
1444 next = g_slist_next(it);
1446 self->parents = g_slist_delete_link(self->parents, it);
1447 c->transients = g_slist_remove(c->transients, self);
1450 /** Re-add the client to the transient tree **/
1452 /* If we're transient for a group then we need to add ourselves to all our
1455 for (it = newgroup->members; it; it = g_slist_next(it)) {
1458 !client_search_top_direct_parent(c)->transient_for_group &&
1461 c->transients = g_slist_prepend(c->transients, self);
1462 self->parents = g_slist_prepend(self->parents, c);
1467 /* If we are now transient for a single window we need to add ourselves to
1470 WARNING: Cyclical transient ness is possible if two windows are
1471 transient for eachother.
1473 else if (newparent &&
1474 /* don't make ourself its child if it is already our child */
1475 !client_is_direct_child(self, newparent) &&
1476 client_normal(newparent))
1478 newparent->transients = g_slist_prepend(newparent->transients, self);
1479 self->parents = g_slist_prepend(self->parents, newparent);
1482 /* Add any group transient windows to our children. But if we're transient
1483 for the group, then other group transients are not our children.
1485 WARNING: Cyclical transient-ness is possible. For e.g. if:
1486 A is transient for the group
1487 B is transient for A
1488 C is transient for B
1489 A can't be transient for C or we have a cycle
1491 if (!newgtran && newgroup &&
1493 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1494 client_normal(self))
1496 for (it = newgroup->members; it; it = g_slist_next(it)) {
1498 if (c != self && c->transient_for_group &&
1499 /* Don't make it our child if it is already our parent */
1500 !client_is_direct_child(c, self))
1502 self->transients = g_slist_prepend(self->transients, c);
1503 c->parents = g_slist_prepend(c->parents, self);
1508 /** If we change our group transient-ness, our children change their
1509 effect group transient-ness, which affects how they relate to other
1512 for (it = self->transients; it; it = g_slist_next(it)) {
1514 if (!c->transient_for_group)
1515 client_update_transient_tree(c, c->group, c->group,
1516 c->transient_for_group,
1517 c->transient_for_group,
1518 client_direct_parent(c),
1519 client_direct_parent(c));
1523 static void client_get_mwm_hints(ObClient *self)
1528 self->mwmhints.flags = 0; /* default to none */
1530 if (OBT_PROP_GETA32(self->w_client, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1532 if (num >= OB_MWM_ELEMENTS) {
1533 self->mwmhints.flags = hints[0];
1534 self->mwmhints.functions = hints[1];
1535 self->mwmhints.decorations = hints[2];
1541 void client_get_type_and_transientness(ObClient *self)
1548 self->transient = FALSE;
1550 if (OBT_PROP_GETA32(self->w_client, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1551 /* use the first value that we know about in the array */
1552 for (i = 0; i < num; ++i) {
1553 if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1554 self->type = OB_CLIENT_TYPE_DESKTOP;
1555 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1556 self->type = OB_CLIENT_TYPE_DOCK;
1557 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1558 self->type = OB_CLIENT_TYPE_TOOLBAR;
1559 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1560 self->type = OB_CLIENT_TYPE_MENU;
1561 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1562 self->type = OB_CLIENT_TYPE_UTILITY;
1563 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1564 self->type = OB_CLIENT_TYPE_SPLASH;
1565 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1566 self->type = OB_CLIENT_TYPE_DIALOG;
1567 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1568 self->type = OB_CLIENT_TYPE_NORMAL;
1569 else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1571 /* prevent this window from getting any decor or
1573 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1574 OB_MWM_FLAG_DECORATIONS);
1575 self->mwmhints.decorations = 0;
1576 self->mwmhints.functions = 0;
1578 if (self->type != (ObClientType) -1)
1579 break; /* grab the first legit type */
1584 if (XGetTransientForHint(obt_display, self->w_client, &t))
1585 self->transient = TRUE;
1587 if (self->type == (ObClientType) -1) {
1588 /*the window type hint was not set, which means we either classify
1589 ourself as a normal window or a dialog, depending on if we are a
1591 if (self->transient)
1592 self->type = OB_CLIENT_TYPE_DIALOG;
1594 self->type = OB_CLIENT_TYPE_NORMAL;
1597 /* then, based on our type, we can update our transientness.. */
1598 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1599 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1600 self->type == OB_CLIENT_TYPE_MENU ||
1601 self->type == OB_CLIENT_TYPE_UTILITY)
1603 self->transient = TRUE;
1607 void client_update_protocols(ObClient *self)
1612 self->focus_notify = FALSE;
1613 self->delete_window = FALSE;
1615 if (OBT_PROP_GETA32(self->w_client, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1616 for (i = 0; i < num_ret; ++i) {
1617 if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1618 /* this means we can request the window to close */
1619 self->delete_window = TRUE;
1620 else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1621 /* if this protocol is requested, then the window will be
1622 notified whenever we want it to receive focus */
1623 self->focus_notify = TRUE;
1624 else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1625 /* if this protocol is requested, then the window will allow
1626 pings to determine if it is still alive */
1629 else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1630 /* if this protocol is requested, then resizing the
1631 window will be synchronized between the frame and the
1633 self->sync_request = TRUE;
1641 void client_update_sync_request_counter(ObClient *self)
1645 if (OBT_PROP_GET32(self->w_client, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1647 self->sync_counter = i;
1649 self->sync_counter = None;
1653 static void client_get_colormap(ObClient *self)
1655 XWindowAttributes wa;
1657 if (XGetWindowAttributes(obt_display, self->w_client, &wa))
1658 client_update_colormap(self, wa.colormap);
1661 void client_update_colormap(ObClient *self, Colormap colormap)
1663 if (colormap == self->colormap) return;
1665 ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1667 if (client_focused(self)) {
1668 screen_install_colormap(self, FALSE); /* uninstall old one */
1669 self->colormap = colormap;
1670 screen_install_colormap(self, FALSE); /* install new one */
1672 self->colormap = colormap;
1675 void client_update_normal_hints(ObClient *self)
1681 self->min_ratio = 0.0f;
1682 self->max_ratio = 0.0f;
1683 SIZE_SET(self->size_inc, 1, 1);
1684 SIZE_SET(self->base_size, 0, 0);
1685 SIZE_SET(self->min_size, 0, 0);
1686 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1688 /* get the hints from the window */
1689 if (XGetWMNormalHints(obt_display, self->w_client, &size, &ret)) {
1690 /* normal windows can't request placement! har har
1691 if (!client_normal(self))
1693 self->positioned = (size.flags & (PPosition|USPosition));
1694 self->sized = (size.flags & (PSize|USSize));
1696 if (size.flags & PWinGravity)
1697 self->gravity = size.win_gravity;
1699 if (size.flags & PAspect) {
1700 if (size.min_aspect.y)
1702 (gfloat) size.min_aspect.x / size.min_aspect.y;
1703 if (size.max_aspect.y)
1705 (gfloat) size.max_aspect.x / size.max_aspect.y;
1708 if (size.flags & PMinSize)
1709 SIZE_SET(self->min_size, size.min_width, size.min_height);
1711 if (size.flags & PMaxSize)
1712 SIZE_SET(self->max_size, size.max_width, size.max_height);
1714 if (size.flags & PBaseSize)
1715 SIZE_SET(self->base_size, size.base_width, size.base_height);
1717 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1718 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1720 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1721 self->min_size.width, self->min_size.height,
1722 self->max_size.width, self->max_size.height);
1723 ob_debug("size inc (%d %d) base size (%d %d)",
1724 self->size_inc.width, self->size_inc.height,
1725 self->base_size.width, self->base_size.height);
1728 ob_debug("Normal hints: not set");
1731 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1733 /* start with everything (cept fullscreen) */
1735 (OB_FRAME_DECOR_TITLEBAR |
1736 OB_FRAME_DECOR_HANDLE |
1737 OB_FRAME_DECOR_GRIPS |
1738 OB_FRAME_DECOR_BORDER |
1739 OB_FRAME_DECOR_ICON |
1740 OB_FRAME_DECOR_ALLDESKTOPS |
1741 OB_FRAME_DECOR_ICONIFY |
1742 OB_FRAME_DECOR_MAXIMIZE |
1743 OB_FRAME_DECOR_SHADE |
1744 OB_FRAME_DECOR_CLOSE);
1746 (OB_CLIENT_FUNC_RESIZE |
1747 OB_CLIENT_FUNC_MOVE |
1748 OB_CLIENT_FUNC_ICONIFY |
1749 OB_CLIENT_FUNC_MAXIMIZE |
1750 OB_CLIENT_FUNC_SHADE |
1751 OB_CLIENT_FUNC_CLOSE |
1752 OB_CLIENT_FUNC_BELOW |
1753 OB_CLIENT_FUNC_ABOVE |
1754 OB_CLIENT_FUNC_UNDECORATE);
1756 if (!(self->min_size.width < self->max_size.width ||
1757 self->min_size.height < self->max_size.height))
1758 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1760 switch (self->type) {
1761 case OB_CLIENT_TYPE_NORMAL:
1762 /* normal windows retain all of the possible decorations and
1763 functionality, and can be fullscreen */
1764 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1767 case OB_CLIENT_TYPE_DIALOG:
1768 /* sometimes apps make dialog windows fullscreen for some reason (for
1769 e.g. kpdf does this..) */
1770 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1773 case OB_CLIENT_TYPE_UTILITY:
1774 /* these windows don't have anything added or removed by default */
1777 case OB_CLIENT_TYPE_MENU:
1778 case OB_CLIENT_TYPE_TOOLBAR:
1779 /* these windows can't iconify or maximize */
1780 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1781 OB_FRAME_DECOR_MAXIMIZE);
1782 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1783 OB_CLIENT_FUNC_MAXIMIZE);
1786 case OB_CLIENT_TYPE_SPLASH:
1787 /* these don't get get any decorations, and the only thing you can
1788 do with them is move them */
1789 self->decorations = 0;
1790 self->functions = OB_CLIENT_FUNC_MOVE;
1793 case OB_CLIENT_TYPE_DESKTOP:
1794 /* these windows are not manipulated by the window manager */
1795 self->decorations = 0;
1796 self->functions = 0;
1799 case OB_CLIENT_TYPE_DOCK:
1800 /* these windows are not manipulated by the window manager, but they
1801 can set below layer which has a special meaning */
1802 self->decorations = 0;
1803 self->functions = OB_CLIENT_FUNC_BELOW;
1807 /* Mwm Hints are applied subtractively to what has already been chosen for
1808 decor and functionality */
1809 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1810 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1811 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1812 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1814 /* if the mwm hints request no handle or title, then all
1815 decorations are disabled, but keep the border if that's
1817 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1818 self->decorations = OB_FRAME_DECOR_BORDER;
1820 self->decorations = 0;
1825 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1826 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1827 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1828 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1829 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1830 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1831 /* dont let mwm hints kill any buttons
1832 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1833 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1834 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1835 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1837 /* dont let mwm hints kill the close button
1838 if (! (self->mwmhints.functions & MwmFunc_Close))
1839 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1843 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1844 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1845 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1846 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1847 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1848 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1850 /* can't maximize without moving/resizing */
1851 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1852 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1853 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1854 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1855 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1858 if (self->max_horz && self->max_vert) {
1859 /* you can't resize fully maximized windows */
1860 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1861 /* kill the handle on fully maxed windows */
1862 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1865 /* If there are no decorations to remove, don't allow the user to try
1867 if (self->decorations == 0)
1868 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1870 /* finally, the user can have requested no decorations, which overrides
1871 everything (but doesnt give it a border if it doesnt have one) */
1872 if (self->undecorated)
1873 self->decorations = 0;
1875 /* if we don't have a titlebar, then we cannot shade! */
1876 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1877 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1879 /* now we need to check against rules for the client's current state */
1880 if (self->fullscreen) {
1881 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1882 OB_CLIENT_FUNC_FULLSCREEN |
1883 OB_CLIENT_FUNC_ICONIFY);
1884 self->decorations = 0;
1887 client_change_allowed_actions(self);
1890 /* force reconfigure to make sure decorations are updated */
1891 client_reconfigure(self, TRUE);
1894 static void client_change_allowed_actions(ObClient *self)
1899 /* desktop windows are kept on all desktops */
1900 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1901 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1903 if (self->functions & OB_CLIENT_FUNC_SHADE)
1904 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1905 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1906 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1907 if (self->functions & OB_CLIENT_FUNC_MOVE)
1908 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1909 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1910 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1911 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1912 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1913 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1914 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1915 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1916 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1917 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1919 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1920 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1921 if (self->functions & OB_CLIENT_FUNC_BELOW)
1922 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1923 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1924 actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1926 OBT_PROP_SETA32(self->w_client, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1928 /* make sure the window isn't breaking any rules now
1930 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1931 it can't be iconified with its parent
1934 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1935 if (self->frame) client_shade(self, FALSE);
1936 else self->shaded = FALSE;
1938 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1939 if (self->frame) client_fullscreen(self, FALSE);
1940 else self->fullscreen = FALSE;
1942 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1944 if (self->frame) client_maximize(self, FALSE, 0);
1946 self->max_vert = self->max_horz = FALSE;
1947 frame_engine->frame_trigger (self->frame, OB_TRIGGER_UNMAX_VERT);
1948 frame_engine->frame_trigger (self->frame, OB_TRIGGER_UNMAX_HORZ);
1954 void client_update_wmhints(ObClient *self)
1958 /* assume a window takes input if it doesnt specify */
1959 self->can_focus = TRUE;
1961 if ((hints = XGetWMHints(obt_display, self->w_client)) != NULL) {
1964 if (hints->flags & InputHint)
1965 self->can_focus = hints->input;
1967 /* only do this when first managing the window *AND* when we aren't
1969 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1970 if (hints->flags & StateHint)
1971 self->iconic = hints->initial_state == IconicState;
1974 self->urgent = (hints->flags & XUrgencyHint);
1975 if (self->urgent && !ur)
1976 client_hilite(self, TRUE);
1977 else if (!self->urgent && ur && self->demands_attention)
1978 client_hilite(self, FALSE);
1980 if (!(hints->flags & WindowGroupHint))
1981 hints->window_group = None;
1983 /* did the group state change? */
1984 if (hints->window_group !=
1985 (self->group ? self->group->leader : None))
1987 ObGroup *oldgroup = self->group;
1989 /* remove from the old group if there was one */
1990 if (self->group != NULL) {
1991 group_remove(self->group, self);
1995 /* add ourself to the group if we have one */
1996 if (hints->window_group != None) {
1997 self->group = group_add(hints->window_group, self);
2000 /* Put ourselves into the new group's transient tree, and remove
2001 ourselves from the old group's */
2002 client_update_transient_tree(self, oldgroup, self->group,
2003 self->transient_for_group,
2004 self->transient_for_group,
2005 client_direct_parent(self),
2006 client_direct_parent(self));
2008 /* Lastly, being in a group, or not, can change if the window is
2009 transient for anything.
2011 The logic for this is:
2012 self->transient = TRUE always if the window wants to be
2013 transient for something, even if transient_for was NULL because
2014 it wasn't in a group before.
2016 If parents was NULL and oldgroup was NULL we can assume
2017 that when we add the new group, it will become transient for
2020 If transient_for_group is TRUE, then it must have already
2021 had a group. If it is getting a new group, the above call to
2022 client_update_transient_tree has already taken care of
2023 everything ! If it is losing all group status then it will
2024 no longer be transient for anything and that needs to be
2027 if (self->transient &&
2028 ((self->parents == NULL && oldgroup == NULL) ||
2029 (self->transient_for_group && !self->group)))
2030 client_update_transient_for(self);
2033 /* the WM_HINTS can contain an icon */
2034 if (hints->flags & IconPixmapHint)
2035 client_update_icons(self);
2041 void client_update_title(ObClient *self)
2044 gchar *visible = NULL;
2046 g_free(self->title);
2047 g_free(self->original_title);
2050 if (!OBT_PROP_GETS(self->w_client, NET_WM_NAME, utf8, &data)) {
2051 /* try old x stuff */
2052 if (!(OBT_PROP_GETS(self->w_client, WM_NAME, locale, &data)
2053 || OBT_PROP_GETS(self->w_client, WM_NAME, utf8, &data))) {
2054 if (self->transient) {
2056 GNOME alert windows are not given titles:
2057 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
2059 data = g_strdup("");
2061 data = g_strdup("Unnamed Window");
2064 self->original_title = g_strdup(data);
2066 if (self->client_machine) {
2067 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2072 if (self->not_responding) {
2074 if (self->kill_level > 0)
2075 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2077 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2081 OBT_PROP_SETS(self->w_client, NET_WM_VISIBLE_NAME, utf8, visible);
2082 self->title = visible;
2085 /* update title render */
2086 frame_engine->frame_update_title (self->frame, self->title);
2087 frame_engine->frame_update_skin (self->frame);
2090 /* update the icon title */
2092 g_free(self->icon_title);
2095 if (!OBT_PROP_GETS(self->w_client, NET_WM_ICON_NAME, utf8, &data))
2096 /* try old x stuff */
2097 if (!(OBT_PROP_GETS(self->w_client, WM_ICON_NAME, locale, &data) ||
2098 OBT_PROP_GETS(self->w_client, WM_ICON_NAME, utf8, &data)))
2099 data = g_strdup(self->title);
2101 if (self->client_machine) {
2102 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2107 if (self->not_responding) {
2109 if (self->kill_level > 0)
2110 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2112 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2116 OBT_PROP_SETS(self->w_client, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
2117 self->icon_title = visible;
2120 void client_update_strut(ObClient *self)
2124 gboolean got = FALSE;
2127 if (OBT_PROP_GETA32(self->w_client, NET_WM_STRUT_PARTIAL, CARDINAL,
2132 STRUT_PARTIAL_SET(strut,
2133 data[0], data[2], data[1], data[3],
2134 data[4], data[5], data[8], data[9],
2135 data[6], data[7], data[10], data[11]);
2141 OBT_PROP_GETA32(self->w_client, NET_WM_STRUT, CARDINAL, &data, &num)) {
2147 /* use the screen's width/height */
2148 a = screen_physical_area_all_monitors();
2150 STRUT_PARTIAL_SET(strut,
2151 data[0], data[2], data[1], data[3],
2152 a->y, a->y + a->height - 1,
2153 a->x, a->x + a->width - 1,
2154 a->y, a->y + a->height - 1,
2155 a->x, a->x + a->width - 1);
2162 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2163 0, 0, 0, 0, 0, 0, 0, 0);
2165 if (!STRUT_EQUAL(strut, self->strut)) {
2166 self->strut = strut;
2168 /* updating here is pointless while we're being mapped cuz we're not in
2169 the client list yet */
2171 screen_update_areas();
2175 /* Avoid storing icons above this size if possible */
2176 #define AVOID_ABOVE 64
2178 void client_update_icons(ObClient *self)
2183 guint num_seen; /* number of icons present */
2184 guint num_small_seen; /* number of icons small enough present */
2185 guint smallest, smallest_area;
2187 for (i = 0; i < self->nicons; ++i)
2188 g_free(self->icons[i].data);
2189 if (self->nicons > 0)
2190 g_free(self->icons);
2193 if (OBT_PROP_GETA32(self->w_client, NET_WM_ICON, CARDINAL, &data, &num)) {
2194 /* figure out how many valid icons are in here */
2196 num_seen = num_small_seen = 0;
2197 smallest = smallest_area = 0;
2203 /* watch for it being too small for the specified size, or for
2204 zero sized icons. */
2205 if (i > num || w == 0 || h == 0) break;
2207 if (!smallest_area || w*h < smallest_area) {
2208 smallest = num_seen;
2209 smallest_area = w*h;
2212 if (w <= AVOID_ABOVE && h <= AVOID_ABOVE)
2215 if (num_small_seen > 0)
2216 self->nicons = num_small_seen;
2220 self->icons = g_new(ObClientIcon, self->nicons);
2222 /* store the icons */
2224 for (j = 0; j < self->nicons;) {
2227 w = self->icons[j].width = data[i++];
2228 h = self->icons[j].height = data[i++];
2230 /* if there are some icons smaller than the threshold, we're
2231 skipping all the ones above */
2232 if (num_small_seen > 0) {
2233 if (w > AVOID_ABOVE || h > AVOID_ABOVE) {
2238 /* if there were no icons smaller than the threshold, then we are
2239 only taking the smallest available one we saw */
2240 else if (j != smallest) {
2245 self->icons[j].data = g_new(RrPixel32, w * h);
2246 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
2251 self->icons[j].data[t] =
2252 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2253 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
2254 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
2255 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
2266 if ((hints = XGetWMHints(obt_display, self->w_client))) {
2267 if (hints->flags & IconPixmapHint) {
2269 self->icons = g_new(ObClientIcon, self->nicons);
2270 obt_display_ignore_errors(TRUE);
2271 if (!RrPixmapToRGBA(ob_rr_inst,
2273 (hints->flags & IconMaskHint ?
2274 hints->icon_mask : None),
2275 &self->icons[0].width,
2276 &self->icons[0].height,
2277 &self->icons[0].data))
2279 g_free(self->icons);
2282 obt_display_ignore_errors(FALSE);
2288 /* set the default icon onto the window
2289 in theory, this could be a race, but if a window doesn't set an icon
2290 or removes it entirely, it's not very likely it is going to set one
2291 right away afterwards
2293 if it has parents, then one of them will have an icon already
2295 if (self->nicons == 0 && !self->parents) {
2296 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2299 data = g_new(gulong, 48*48+2);
2300 data[0] = data[1] = 48;
2301 for (i = 0; i < 48*48; ++i)
2302 data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2303 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2304 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2305 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2306 OBT_PROP_SETA32(self->w_client, NET_WM_ICON, CARDINAL, data, 48*48+2);
2308 } else if (self->frame)
2309 frame_engine->frame_update_skin (self->frame);
2310 /* don't draw the icon empty if we're just setting one now anyways,
2311 we'll get the property change any second */
2312 //frame_adjust_icon(self->frame);
2315 void client_update_icon_geometry(ObClient *self)
2320 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2322 if (OBT_PROP_GETA32(self->w_client, NET_WM_ICON_GEOMETRY, CARDINAL,
2326 /* don't let them set it with an area < 0 */
2327 RECT_SET(self->icon_geometry, data[0], data[1],
2328 MAX(data[2],0), MAX(data[3],0));
2333 static void client_get_session_ids(ObClient *self)
2340 if (!OBT_PROP_GET32(self->w_client, WM_CLIENT_LEADER, WINDOW, &leader))
2343 /* get the SM_CLIENT_ID */
2346 got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
2348 OBT_PROP_GETS(self->w_client, SM_CLIENT_ID, locale, &self->sm_client_id);
2350 /* get the WM_CLASS (name and class). make them "" if they are not
2354 got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
2356 got = OBT_PROP_GETSS(self->w_client, WM_CLASS, locale, &ss);
2360 self->name = g_strdup(ss[0]);
2362 self->class = g_strdup(ss[1]);
2367 if (self->name == NULL) self->name = g_strdup("");
2368 if (self->class == NULL) self->class = g_strdup("");
2370 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2373 got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
2375 got = OBT_PROP_GETS(self->w_client, WM_WINDOW_ROLE, locale, &s);
2380 self->role = g_strdup("");
2382 /* get the WM_COMMAND */
2386 got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
2388 got = OBT_PROP_GETSS(self->w_client, WM_COMMAND, locale, &ss);
2391 /* merge/mash them all together */
2392 gchar *merge = NULL;
2395 for (i = 0; ss[i]; ++i) {
2398 merge = g_strconcat(merge, ss[i], NULL);
2400 merge = g_strconcat(ss[i], NULL);
2405 self->wm_command = merge;
2408 /* get the WM_CLIENT_MACHINE */
2411 got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
2413 got = OBT_PROP_GETS(self->w_client, WM_CLIENT_MACHINE, locale, &s);
2416 gchar localhost[128];
2419 gethostname(localhost, 127);
2420 localhost[127] = '\0';
2421 if (strcmp(localhost, s) != 0)
2422 self->client_machine = s;
2426 /* see if it has the PID set too (the PID requires that the
2427 WM_CLIENT_MACHINE be set) */
2428 if (OBT_PROP_GET32(self->w_client, NET_WM_PID, CARDINAL, &pid))
2433 static void client_change_wm_state(ObClient *self)
2438 old = self->wmstate;
2440 if (self->shaded || self->iconic ||
2441 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2443 self->wmstate = IconicState;
2445 self->wmstate = NormalState;
2447 if (old != self->wmstate) {
2448 OBT_PROP_MSG(ob_screen, self->w_client, KDE_WM_CHANGE_STATE,
2449 self->wmstate, 1, 0, 0, 0);
2451 state[0] = self->wmstate;
2453 OBT_PROP_SETA32(self->w_client, WM_STATE, WM_STATE, state, 2);
2457 static void client_change_state(ObClient *self)
2459 gulong netstate[12];
2464 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2466 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2468 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2469 if (self->skip_taskbar)
2470 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2471 if (self->skip_pager)
2472 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2473 if (self->fullscreen)
2474 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2476 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2478 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2480 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2482 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2483 if (self->demands_attention)
2484 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2485 if (self->undecorated)
2486 netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2487 OBT_PROP_SETA32(self->w_client, NET_WM_STATE, ATOM, netstate, num);
2491 frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
2492 /* if this occurs while we are focus cycling, the indicator needs to
2493 match the changes */
2494 if (focus_cycle_target == self)
2495 focus_cycle_draw_indicator(self);
2499 ObClient *client_search_focus_tree(ObClient *self)
2504 for (it = self->transients; it; it = g_slist_next(it)) {
2505 if (client_focused(it->data)) return it->data;
2506 if ((ret = client_search_focus_tree(it->data))) return ret;
2511 ObClient *client_search_focus_tree_full(ObClient *self)
2513 if (self->parents) {
2516 for (it = self->parents; it; it = g_slist_next(it)) {
2517 ObClient *c = it->data;
2518 if ((c = client_search_focus_tree_full(it->data))) return c;
2524 /* this function checks the whole tree, the client_search_focus_tree
2525 does not, so we need to check this window */
2526 if (client_focused(self))
2528 return client_search_focus_tree(self);
2532 ObClient *client_search_focus_group_full(ObClient *self)
2537 for (it = self->group->members; it; it = g_slist_next(it)) {
2538 ObClient *c = it->data;
2540 if (client_focused(c)) return c;
2541 if ((c = client_search_focus_tree(it->data))) return c;
2544 if (client_focused(self)) return self;
2548 gboolean client_has_parent(ObClient *self)
2550 return self->parents != NULL;
2553 static ObStackingLayer calc_layer(ObClient *self)
2558 monitor = screen_physical_area_monitor(client_monitor(self));
2560 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2561 l = OB_STACKING_LAYER_DESKTOP;
2562 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2563 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2564 else l = OB_STACKING_LAYER_ABOVE;
2566 else if ((self->fullscreen ||
2567 /* No decorations and fills the monitor = oldskool fullscreen.
2568 But not for maximized windows.
2570 (self->decorations == 0 &&
2571 !(self->max_horz && self->max_vert) &&
2572 RECT_EQUAL(self->area, *monitor))) &&
2573 /* you are fullscreen while you or your children are focused.. */
2574 (client_focused(self) || client_search_focus_tree(self) ||
2575 /* you can be fullscreen if you're on another desktop */
2576 (self->desktop != screen_desktop &&
2577 self->desktop != DESKTOP_ALL) ||
2578 /* and you can also be fullscreen if the focused client is on
2579 another monitor, or nothing else is focused */
2581 client_monitor(focus_client) != client_monitor(self))))
2582 l = OB_STACKING_LAYER_FULLSCREEN;
2583 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2584 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2585 else l = OB_STACKING_LAYER_NORMAL;
2592 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2593 ObStackingLayer min)
2595 ObStackingLayer old, own;
2599 own = calc_layer(self);
2600 self->layer = MAX(own, min);
2602 if (self->layer != old) {
2603 stacking_remove(CLIENT_AS_WINDOW(self));
2604 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2607 /* we've been restacked */
2608 self->visited = TRUE;
2610 for (it = self->transients; it; it = g_slist_next(it))
2611 client_calc_layer_recursive(it->data, orig,
2615 static void client_calc_layer_internal(ObClient *self)
2619 /* transients take on the layer of their parents */
2620 sit = client_search_all_top_parents(self);
2622 for (; sit; sit = g_slist_next(sit))
2623 client_calc_layer_recursive(sit->data, self, 0);
2626 void client_calc_layer(ObClient *self)
2630 /* skip over stuff above fullscreen layer */
2631 for (it = stacking_list; it; it = g_list_next(it))
2632 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2634 /* find the windows in the fullscreen layer, and mark them not-visited */
2635 for (; it; it = g_list_next(it)) {
2636 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2637 else if (WINDOW_IS_CLIENT(it->data))
2638 WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2641 client_calc_layer_internal(self);
2643 /* skip over stuff above fullscreen layer */
2644 for (it = stacking_list; it; it = g_list_next(it))
2645 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2647 /* now recalc any windows in the fullscreen layer which have not
2648 had their layer recalced already */
2649 for (; it; it = g_list_next(it)) {
2650 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2651 else if (WINDOW_IS_CLIENT(it->data) &&
2652 !WINDOW_AS_CLIENT(it->data)->visited)
2653 client_calc_layer_internal(it->data);
2657 gboolean client_should_show(ObClient *self)
2661 if (client_normal(self) && screen_showing_desktop)
2663 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2669 gboolean client_show(ObClient *self)
2671 gboolean show = FALSE;
2673 if (client_should_show(self)) {
2674 /* replay pending pointer event before showing the window, in case it
2675 should be going to something under the window */
2676 mouse_replay_pointer();
2678 client_show_frame(self);
2681 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2682 it needs to be in IconicState. This includes when it is on another
2685 client_change_wm_state(self);
2690 gboolean client_hide(ObClient *self)
2692 gboolean hide = FALSE;
2694 if (!client_should_show(self)) {
2695 if (self == focus_client) {
2696 /* if there is a grab going on, then we need to cancel it. if we
2697 move focus during the grab, applications will get
2698 NotifyWhileGrabbed events and ignore them !
2700 actions should not rely on being able to move focus during an
2703 event_cancel_all_key_grabs();
2706 /* We don't need to ignore enter events here.
2707 The window can hide/iconify in 3 different ways:
2708 1 - through an x message. in this case we ignore all enter events
2709 caused by responding to the x message (unless underMouse)
2710 2 - by a keyboard action. in this case we ignore all enter events
2711 caused by the action
2712 3 - by a mouse action. in this case they are doing stuff with the
2713 mouse and focus _should_ move.
2715 Also in action_end, we simulate an enter event that can't be ignored
2716 so trying to ignore them is futile in case 3 anyways
2719 /* replay pending pointer event before hiding the window, in case it
2720 should be going to the window */
2721 mouse_replay_pointer();
2723 client_hide_frame(self);
2726 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2727 it needs to be in IconicState. This includes when it is on another
2730 client_change_wm_state(self);
2735 void client_showhide(ObClient *self)
2737 if (!client_show(self))
2741 gboolean client_normal(ObClient *self) {
2742 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2743 self->type == OB_CLIENT_TYPE_DOCK ||
2744 self->type == OB_CLIENT_TYPE_SPLASH);
2747 gboolean client_helper(ObClient *self)
2749 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2750 self->type == OB_CLIENT_TYPE_MENU ||
2751 self->type == OB_CLIENT_TYPE_TOOLBAR);
2754 gboolean client_mouse_focusable(ObClient *self)
2756 return !(self->type == OB_CLIENT_TYPE_MENU ||
2757 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2758 self->type == OB_CLIENT_TYPE_SPLASH ||
2759 self->type == OB_CLIENT_TYPE_DOCK);
2762 gboolean client_enter_focusable(ObClient *self)
2764 /* you can focus desktops but it shouldn't on enter */
2765 return (client_mouse_focusable(self) &&
2766 self->type != OB_CLIENT_TYPE_DESKTOP);
2770 static void client_apply_startup_state(ObClient *self,
2771 gint x, gint y, gint w, gint h)
2773 /* save the states that we are going to apply */
2774 gboolean iconic = self->iconic;
2775 gboolean fullscreen = self->fullscreen;
2776 gboolean undecorated = self->undecorated;
2777 gboolean shaded = self->shaded;
2778 gboolean demands_attention = self->demands_attention;
2779 gboolean max_horz = self->max_horz;
2780 gboolean max_vert = self->max_vert;
2784 /* turn them all off in the client, so they won't affect the window
2786 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2787 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2789 /* move the client to its placed position, or it it's already there,
2790 generate a ConfigureNotify telling the client where it is.
2792 do this after adjusting the frame. otherwise it gets all weird and
2793 clients don't work right
2795 do this before applying the states so they have the correct
2796 pre-max/pre-fullscreen values
2798 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2799 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2800 self->w_client, x, y, w, h);
2801 /* save the area, and make it where it should be for the premax stuff */
2802 oldarea = self->area;
2803 RECT_SET(self->area, x, y, w, h);
2804 frame_engine->frame_set_client_area (self->frame, self->area);
2806 /* apply the states. these are in a carefully crafted order.. */
2809 client_iconify(self, TRUE, FALSE, TRUE);
2811 client_fullscreen(self, TRUE);
2813 client_set_undecorated(self, TRUE);
2815 client_shade(self, TRUE);
2816 if (demands_attention)
2817 client_hilite(self, TRUE);
2819 if (max_vert && max_horz)
2820 client_maximize(self, TRUE, 0);
2822 client_maximize(self, TRUE, 2);
2824 client_maximize(self, TRUE, 1);
2826 /* if the window hasn't been configured yet, then do so now, in fact the
2827 x,y,w,h may _not_ be the same as the area rect, which can end up
2828 meaning that the client isn't properly moved/resized by the fullscreen
2830 pho can cause this because it maps at size of the screen but not 0,0
2831 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2832 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2833 cuz thats where the pre-fullscreen will be. however the actual area is
2834 not, so this needs to be called even if we have fullscreened/maxed
2836 self->area = oldarea;
2837 frame_engine->frame_set_decorations (self->frame, self->decorations);
2838 frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
2839 /* if this occurs while we are focus cycling, the indicator needs to
2840 match the changes */
2841 if (focus_cycle_target == self)
2842 focus_cycle_draw_indicator(self);
2843 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2845 /* set the desktop hint, to make sure that it always exists */
2846 OBT_PROP_SET32(self->w_client, NET_WM_DESKTOP, CARDINAL, self->desktop);
2848 /* nothing to do for the other states:
2857 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2859 /* these should be the current values. this is for when you're not moving,
2861 g_assert(*x == self->area.x);
2862 g_assert(oldw == self->area.width);
2865 switch (self->gravity) {
2867 case NorthWestGravity:
2869 case SouthWestGravity:
2876 *x -= (neww - oldw) / 2;
2878 case NorthEastGravity:
2880 case SouthEastGravity:
2886 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2888 /* these should be the current values. this is for when you're not moving,
2890 g_assert(*y == self->area.y);
2891 g_assert(oldh == self->area.height);
2894 switch (self->gravity) {
2896 case NorthWestGravity:
2898 case NorthEastGravity:
2905 *y -= (newh - oldh) / 2;
2907 case SouthWestGravity:
2909 case SouthEastGravity:
2915 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2916 gint *logicalw, gint *logicalh,
2919 Rect desired = {*x, *y, *w, *h};
2920 frame_rect_to_frame(self, &desired);
2922 /* make the frame recalculate its dimentions n shit without changing
2923 anything visible for real, this way the constraints below can work with
2924 the updated frame dimensions. */
2925 frame_engine->frame_set_decorations (self->frame, self->decorations);
2926 frame_engine->frame_update_layout (self->frame, self->area, FALSE, TRUE);
2928 /* gets the frame's position */
2929 frame_client_gravity(self, x, y);
2931 /* these positions are frame positions, not client positions */
2933 /* set the size and position if fullscreen */
2934 if (self->fullscreen) {
2938 i = screen_find_monitor(&desired);
2939 a = screen_physical_area_monitor(i);
2946 user = FALSE; /* ignore if the client can't be moved/resized when it
2950 } else if (self->max_horz || self->max_vert) {
2954 /* use all possible struts when maximizing to the full screen */
2955 i = screen_find_monitor(&desired);
2956 a = screen_area(self->desktop, i,
2957 (self->max_horz && self->max_vert ? NULL : &desired));
2960 frame_engine->frame_get_size(self->frame, &size);
2961 /* set the size and position if maximized */
2962 if (self->max_horz) {
2964 *w = a->width - size.left - size.right;
2966 if (self->max_vert) {
2968 *h = a->height - size.top - size.bottom;
2971 user = FALSE; /* ignore if the client can't be moved/resized when it
2977 /* gets the client's position */
2978 frame_frame_gravity(self, x, y);
2980 /* work within the prefered sizes given by the window */
2981 if (!(*w == self->area.width && *h == self->area.height)) {
2982 gint basew, baseh, minw, minh;
2984 gfloat minratio, maxratio;
2986 incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
2987 inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
2988 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2989 0 : self->min_ratio;
2990 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2991 0 : self->max_ratio;
2993 /* base size is substituted with min size if not specified */
2994 if (self->base_size.width || self->base_size.height) {
2995 basew = self->base_size.width;
2996 baseh = self->base_size.height;
2998 basew = self->min_size.width;
2999 baseh = self->min_size.height;
3001 /* min size is substituted with base size if not specified */
3002 if (self->min_size.width || self->min_size.height) {
3003 minw = self->min_size.width;
3004 minh = self->min_size.height;
3006 minw = self->base_size.width;
3007 minh = self->base_size.height;
3010 /* if this is a user-requested resize, then check against min/max
3013 /* smaller than min size or bigger than max size? */
3014 if (*w > self->max_size.width) *w = self->max_size.width;
3015 if (*w < minw) *w = minw;
3016 if (*h > self->max_size.height) *h = self->max_size.height;
3017 if (*h < minh) *h = minh;
3022 /* keep to the increments */
3026 /* you cannot resize to nothing */
3027 if (basew + *w < 1) *w = 1 - basew;
3028 if (baseh + *h < 1) *h = 1 - baseh;
3030 /* save the logical size */
3031 *logicalw = incw > 1 ? *w : *w + basew;
3032 *logicalh = inch > 1 ? *h : *h + baseh;
3040 /* adjust the height to match the width for the aspect ratios.
3041 for this, min size is not substituted for base size ever. */
3042 *w -= self->base_size.width;
3043 *h -= self->base_size.height;
3046 if (*h * minratio > *w) {
3047 *h = (gint)(*w / minratio);
3049 /* you cannot resize to nothing */
3052 *w = (gint)(*h * minratio);
3056 if (*h * maxratio < *w) {
3057 *h = (gint)(*w / maxratio);
3059 /* you cannot resize to nothing */
3062 *w = (gint)(*h * minratio);
3066 *w += self->base_size.width;
3067 *h += self->base_size.height;
3070 /* these override the above states! if you cant move you can't move! */
3072 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3076 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3077 *w = self->area.width;
3078 *h = self->area.height;
3087 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3088 gboolean user, gboolean final, gboolean force_reply)
3092 gboolean send_resize_client;
3093 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3094 gboolean fmoved, fresized;
3095 guint fdecor = frame_engine->frame_get_decorations(self->frame);
3096 gboolean fhorz = frame_engine->frame_is_max_horz(self->frame);
3097 gboolean fvert = frame_engine->frame_is_max_vert(self->frame);
3098 gint logicalw, logicalh;
3101 frame_engine->frame_get_size(self->frame, &size);
3103 frame_engine->frame_get_window_area(self->frame, &area);
3105 /* find the new x, y, width, and height (and logical size) */
3106 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3108 /* set the logical size if things changed */
3109 if (!(w == self->area.width && h == self->area.height))
3110 SIZE_SET(self->logical_size, logicalw, logicalh);
3112 /* figure out if we moved or resized or what */
3113 moved = (x != self->area.x || y != self->area.y);
3114 resized = (w != self->area.width || h != self->area.height);
3116 oldw = self->area.width;
3117 oldh = self->area.height;
3118 frame_engine->frame_get_window_area(self->frame, &oldframe);
3119 RECT_SET(self->area, x, y, w, h);
3121 frame_engine->frame_set_client_area (self->frame, self->area);
3123 /* for app-requested resizes, always resize if 'resized' is true.
3124 for user-requested ones, only resize if final is true, or when
3125 resizing in redraw mode */
3126 send_resize_client = ((!user && resized) ||
3128 (resized && config_resize_redraw))));
3130 /* if the client is enlarging, then resize the client before the frame */
3131 if (send_resize_client && (w > oldw || h > oldh)) {
3132 frame_engine->frame_set_decorations (self->frame, self->decorations);
3133 frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
3134 /* if this occurs while we are focus cycling, the indicator needs to
3135 match the changes */
3136 if (focus_cycle_target == self)
3137 focus_cycle_draw_indicator(self);
3140 /* find the frame's dimensions and move/resize it */
3144 /* if decorations changed, then readjust everything for the frame */
3145 if (self->decorations != fdecor ||
3146 self->max_horz != fhorz || self->max_vert != fvert)
3148 fmoved = fresized = TRUE;
3151 /* adjust the frame */
3152 if (fmoved || fresized) {
3153 gulong ignore_start;
3155 ignore_start = event_start_ignore_all_enters();
3157 /* replay pending pointer event before move the window, in case it
3158 would change what window gets the event */
3159 mouse_replay_pointer();
3161 frame_engine->frame_set_decorations (self->frame, self->decorations);
3162 frame_engine->frame_update_layout (self->frame, self->area, TRUE, FALSE);
3163 /* if this occurs while we are focus cycling, the indicator needs to
3164 match the changes */
3165 if (focus_cycle_target == self)
3166 focus_cycle_draw_indicator(self);
3169 event_end_ignore_all_enters(ignore_start);
3172 if (!user || final) {
3173 gint oldrx = self->root_pos.x;
3174 gint oldry = self->root_pos.y;
3175 /* we have reset the client to 0 border width, so don't include
3176 it in these coords */
3177 POINT_SET(self->root_pos,
3178 area.x + size.left -
3181 self->border_width);
3182 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3186 /* This is kinda tricky and should not be changed.. let me explain!
3188 When user = FALSE, then the request is coming from the application
3189 itself, and we are more strict about when to send a synthetic
3190 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3191 in this case (if force_reply is true)
3193 When user = TRUE, then the request is coming from "us", like when we
3194 maximize a window or something. In this case we are more lenient. We
3195 used to follow the same rules as above, but _Java_ Swing can't handle
3196 this. So just to appease Swing, when user = TRUE, we always send
3197 a synthetic ConfigureNotify to give the window its root coordinates.
3199 if ((!user && !resized && (rootmoved || force_reply)) ||
3200 (user && final && rootmoved))
3204 event.type = ConfigureNotify;
3205 event.xconfigure.display = obt_display;
3206 event.xconfigure.event = self->w_client;
3207 event.xconfigure.window = self->w_client;
3209 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3210 self->title, self->root_pos.x, self->root_pos.y, w, h);
3212 /* root window real coords */
3213 event.xconfigure.x = self->root_pos.x;
3214 event.xconfigure.y = self->root_pos.y;
3215 event.xconfigure.width = w;
3216 event.xconfigure.height = h;
3217 event.xconfigure.border_width = self->border_width;
3218 event.xconfigure.above = None;
3219 event.xconfigure.override_redirect = FALSE;
3220 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3221 FALSE, StructureNotifyMask, &event);
3224 /* if the client is shrinking, then resize the frame before the client.
3226 both of these resize sections may run, because the top one only resizes
3227 in the direction that is growing
3229 if (send_resize_client && (w <= oldw || h <= oldh)) {
3230 frame_engine->frame_set_decorations (self->frame, self->decorations);
3231 frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
3232 /* if this occurs while we are focus cycling, the indicator needs to
3233 match the changes */
3234 if (focus_cycle_target == self)
3235 focus_cycle_draw_indicator(self);
3238 XFlush(obt_display);
3240 /* if it moved between monitors, then this can affect the stacking
3241 layer of this window or others - for fullscreen windows */
3243 frame_engine->frame_get_window_area(self->frame, ¤t_frame);
3244 if (screen_find_monitor(¤t_frame) !=
3245 screen_find_monitor(&oldframe))
3247 client_calc_layer(self);
3251 void client_fullscreen(ObClient *self, gboolean fs)
3255 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3256 self->fullscreen == fs) return; /* already done */
3258 self->fullscreen = fs;
3259 client_change_state(self); /* change the state hints on the client */
3262 self->pre_fullscreen_area = self->area;
3263 /* if the window is maximized, its area isn't all that meaningful.
3264 save it's premax area instead. */
3265 if (self->max_horz) {
3266 self->pre_fullscreen_area.x = self->pre_max_area.x;
3267 self->pre_fullscreen_area.width = self->pre_max_area.width;
3269 if (self->max_vert) {
3270 self->pre_fullscreen_area.y = self->pre_max_area.y;
3271 self->pre_fullscreen_area.height = self->pre_max_area.height;
3274 /* these will help configure_full figure out where to fullscreen
3278 w = self->area.width;
3279 h = self->area.height;
3281 g_assert(self->pre_fullscreen_area.width > 0 &&
3282 self->pre_fullscreen_area.height > 0);
3284 x = self->pre_fullscreen_area.x;
3285 y = self->pre_fullscreen_area.y;
3286 w = self->pre_fullscreen_area.width;
3287 h = self->pre_fullscreen_area.height;
3288 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3291 ob_debug("Window %s going fullscreen (%d)",
3292 self->title, self->fullscreen);
3294 client_setup_decor_and_functions(self, FALSE);
3295 client_move_resize(self, x, y, w, h);
3297 /* and adjust our layer/stacking. do this after resizing the window,
3298 and applying decorations, because windows which fill the screen are
3299 considered "fullscreen" and it affects their layer */
3300 client_calc_layer(self);
3303 /* try focus us when we go into fullscreen mode */
3308 static void client_iconify_recursive(ObClient *self,
3309 gboolean iconic, gboolean curdesk,
3310 gboolean hide_animation)
3313 gboolean changed = FALSE;
3316 if (self->iconic != iconic) {
3317 ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3321 /* don't let non-normal windows iconify along with their parents
3323 if (client_normal(self)) {
3324 self->iconic = iconic;
3326 /* update the focus lists.. iconic windows go to the bottom of
3328 focus_order_to_bottom(self);
3333 self->iconic = iconic;
3335 if (curdesk && self->desktop != screen_desktop &&
3336 self->desktop != DESKTOP_ALL)
3337 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3339 /* this puts it after the current focused window */
3340 focus_order_remove(self);
3341 focus_order_add_new(self);
3348 client_change_state(self);
3349 /* do this after starting the animation so it doesn't flash */
3350 client_showhide(self);
3353 /* iconify all direct transients, and deiconify all transients
3355 for (it = self->transients; it; it = g_slist_next(it))
3356 if (it->data != self)
3357 if (client_is_direct_child(self, it->data) || !iconic)
3358 client_iconify_recursive(it->data, iconic, curdesk,
3362 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3363 gboolean hide_animation)
3365 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3366 /* move up the transient chain as far as possible first */
3367 self = client_search_top_direct_parent(self);
3368 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3372 void client_maximize(ObClient *self, gboolean max, gint dir)
3376 g_assert(dir == 0 || dir == 1 || dir == 2);
3377 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
3379 /* check if already done */
3381 if (dir == 0 && self->max_horz && self->max_vert) return;
3382 if (dir == 1 && self->max_horz) return;
3383 if (dir == 2 && self->max_vert) return;
3385 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3386 if (dir == 1 && !self->max_horz) return;
3387 if (dir == 2 && !self->max_vert) return;
3390 /* these will help configure_full figure out which screen to fill with
3394 w = self->area.width;
3395 h = self->area.height;
3398 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3399 RECT_SET(self->pre_max_area,
3400 self->area.x, self->pre_max_area.y,
3401 self->area.width, self->pre_max_area.height);
3403 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3404 RECT_SET(self->pre_max_area,
3405 self->pre_max_area.x, self->area.y,
3406 self->pre_max_area.width, self->area.height);
3409 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3410 g_assert(self->pre_max_area.width > 0);
3412 x = self->pre_max_area.x;
3413 w = self->pre_max_area.width;
3415 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3416 0, self->pre_max_area.height);
3418 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3419 g_assert(self->pre_max_area.height > 0);
3421 y = self->pre_max_area.y;
3422 h = self->pre_max_area.height;
3424 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3425 self->pre_max_area.width, 0);
3429 if (dir == 0 || dir == 1) {
3430 self->max_horz = max; /* horz */
3431 frame_engine->frame_trigger(self->frame, max? OB_TRIGGER_MAX_HORZ : OB_TRIGGER_UNMAX_HORZ);
3434 if (dir == 0 || dir == 2){
3435 self->max_vert = max; /* vert */
3436 frame_engine->frame_trigger(self->frame, max? OB_TRIGGER_MAX_VERT : OB_TRIGGER_UNMAX_VERT);
3439 client_change_state(self); /* change the state hints on the client */
3441 client_setup_decor_and_functions(self, FALSE);
3442 client_move_resize(self, x, y, w, h);
3445 void client_shade(ObClient *self, gboolean shade)
3447 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3448 shade) || /* can't shade */
3449 self->shaded == shade) return; /* already done */
3451 self->shaded = shade;
3452 client_change_state(self);
3453 client_change_wm_state(self); /* the window is being hidden/shown */
3454 /* resize the frame to just the titlebar */
3455 frame_engine->frame_set_is_shaded (self->frame, self->shaded);
3456 frame_engine->frame_update_layout(self->frame, self->area, FALSE, FALSE);
3457 /* if this occurs while we are focus cycling, the indicator needs to
3458 match the changes */
3459 if (focus_cycle_target == self)
3460 focus_cycle_draw_indicator(self);
3463 static void client_ping_event(ObClient *self, gboolean dead)
3465 self->not_responding = dead;
3466 client_update_title(self);
3469 /* it came back to life ! */
3471 if (self->kill_prompt) {
3472 prompt_unref(self->kill_prompt);
3473 self->kill_prompt = NULL;
3476 self->kill_level = 0;
3480 void client_close(ObClient *self)
3482 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3485 prompt_cancel(self->prompt);
3489 /* in the case that the client provides no means to requesting that it
3490 close, we just kill it */
3491 if (!self->delete_window)
3492 /* don't use client_kill(), we should only kill based on PID in
3493 response to a lack of PING replies */
3494 XKillClient(obt_display, self->w_client);
3496 /* request the client to close with WM_DELETE_WINDOW */
3497 OBT_PROP_MSG_TO(self->w_client, self->w_client, WM_PROTOCOLS,
3498 OBT_PROP_ATOM(WM_DELETE_WINDOW), event_curtime,
3499 0, 0, 0, NoEventMask);
3501 if (self->not_responding)
3502 client_prompt_kill(self);
3506 #define OB_KILL_RESULT_NO 0
3507 #define OB_KILL_RESULT_YES 1
3509 static void client_kill_requested(ObPrompt *p, gint result, gpointer data)
3511 ObClient *self = data;
3513 if (result == OB_KILL_RESULT_YES)
3516 prompt_unref(self->kill_prompt);
3517 self->kill_prompt = NULL;
3520 static void client_prompt_kill(ObClient *self)
3522 /* check if we're already prompting */
3523 if (!self->kill_prompt) {
3524 ObPromptAnswer answers[] = {
3525 { _("No"), OB_KILL_RESULT_NO },
3526 { _("Yes"), OB_KILL_RESULT_YES }
3531 if (self->kill_level == 0)
3537 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"), self->original_title, sig);
3539 self->kill_prompt = prompt_new(m, answers,
3540 sizeof(answers)/sizeof(answers[0]),
3541 OB_KILL_RESULT_NO, /* default = no */
3542 OB_KILL_RESULT_NO, /* cancel = no */
3543 client_kill_requested, self);
3547 prompt_show(self->kill_prompt, self);
3550 void client_kill(ObClient *self)
3552 /* don't kill our own windows */
3553 if (self->prompt) return;
3555 if (!self->client_machine && self->pid) {
3556 /* running on the local host */
3557 if (self->kill_level == 0) {
3558 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3559 self->w_client, self->pid);
3560 kill(self->pid, SIGTERM);
3563 /* show that we're trying to kill it */
3564 client_update_title(self);
3567 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3568 self->w_client, self->pid);
3569 kill(self->pid, SIGKILL); /* kill -9 */
3573 /* running on a remote host */
3574 XKillClient(obt_display, self->w_client);
3578 void client_hilite(ObClient *self, gboolean hilite)
3580 if (self->demands_attention == hilite)
3581 return; /* no change */
3583 /* don't allow focused windows to hilite */
3584 self->demands_attention = hilite && !client_focused(self);
3585 if (self->frame != NULL) { /* if we're mapping, just set the state */
3586 if (self->demands_attention)
3587 client_flash_start(self);
3589 client_flash_stop(self);
3590 client_change_state(self);
3594 static void client_set_desktop_recursive(ObClient *self,
3602 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3604 ob_debug("Setting desktop %u", target+1);
3606 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3608 old = self->desktop;
3609 self->desktop = target;
3610 OBT_PROP_SET32(self->w_client, NET_WM_DESKTOP, CARDINAL, target);
3611 /* the frame can display the current desktop state */
3612 frame_engine->frame_set_decorations (self->frame, self->decorations);
3613 frame_engine->frame_update_layout(self->frame, self->area, FALSE, FALSE);
3614 /* if this occurs while we are focus cycling, the indicator needs to
3615 match the changes */
3616 if (focus_cycle_target == self)
3617 focus_cycle_draw_indicator(self);
3618 /* 'move' the window to the new desktop */
3622 /* raise if it was not already on the desktop */
3623 if (old != DESKTOP_ALL && !dontraise)
3624 stacking_raise(CLIENT_AS_WINDOW(self));
3625 if (STRUT_EXISTS(self->strut))
3626 screen_update_areas();
3628 /* the new desktop's geometry may be different, so we may need to
3629 resize, for example if we are maximized */
3630 client_reconfigure(self, FALSE);
3633 /* move all transients */
3634 for (it = self->transients; it; it = g_slist_next(it))
3635 if (it->data != self)
3636 if (client_is_direct_child(self, it->data))
3637 client_set_desktop_recursive(it->data, target,
3638 donthide, dontraise);
3641 void client_set_desktop(ObClient *self, guint target,
3642 gboolean donthide, gboolean dontraise)
3644 self = client_search_top_direct_parent(self);
3645 client_set_desktop_recursive(self, target, donthide, dontraise);
3648 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3650 while (child != parent && (child = client_direct_parent(child)));
3651 return child == parent;
3654 ObClient *client_search_modal_child(ObClient *self)
3659 for (it = self->transients; it; it = g_slist_next(it)) {
3660 ObClient *c = it->data;
3661 if ((ret = client_search_modal_child(c))) return ret;
3662 if (c->modal) return c;
3667 gboolean client_validate(ObClient *self)
3671 XSync(obt_display, FALSE); /* get all events on the server */
3673 if (XCheckTypedWindowEvent(obt_display, self->w_client, DestroyNotify, &e) ||
3674 XCheckTypedWindowEvent(obt_display, self->w_client, UnmapNotify, &e))
3676 XPutBackEvent(obt_display, &e);
3683 void client_set_wm_state(ObClient *self, glong state)
3685 if (state == self->wmstate) return; /* no change */
3689 client_iconify(self, TRUE, TRUE, FALSE);
3692 client_iconify(self, FALSE, TRUE, FALSE);
3697 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3699 gboolean shaded = self->shaded;
3700 gboolean fullscreen = self->fullscreen;
3701 gboolean undecorated = self->undecorated;
3702 gboolean max_horz = self->max_horz;
3703 gboolean max_vert = self->max_vert;
3704 gboolean modal = self->modal;
3705 gboolean iconic = self->iconic;
3706 gboolean demands_attention = self->demands_attention;
3707 gboolean above = self->above;
3708 gboolean below = self->below;
3711 if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3712 action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3713 action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3714 /* an invalid action was passed to the client message, ignore it */
3717 for (i = 0; i < 2; ++i) {
3718 Atom state = i == 0 ? data1 : data2;
3720 if (!state) continue;
3722 /* if toggling, then pick whether we're adding or removing */
3723 if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3724 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3725 action = modal ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3726 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3727 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3728 action = self->max_vert ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3729 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3730 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3731 action = self->max_horz ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3732 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3733 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3734 action = shaded ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3735 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3736 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3737 action = self->skip_taskbar ?
3738 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3739 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3740 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3741 action = self->skip_pager ?
3742 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3743 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3744 else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3745 action = self->iconic ?
3746 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3747 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3748 else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3749 action = fullscreen ?
3750 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3751 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3752 else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3753 action = self->above ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3754 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3755 else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3756 action = self->below ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3757 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3758 else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3759 action = self->demands_attention ?
3760 OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3761 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3762 else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3763 action = undecorated ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3764 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3767 if (action == OBT_PROP_ATOM(NET_WM_STATE_ADD)) {
3768 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3770 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3772 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3774 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3776 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3777 self->skip_taskbar = TRUE;
3778 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3779 self->skip_pager = TRUE;
3780 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3782 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3784 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3787 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3790 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3791 demands_attention = TRUE;
3792 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3796 } else { /* action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) */
3797 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3799 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3801 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3803 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3805 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3806 self->skip_taskbar = FALSE;
3807 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3808 self->skip_pager = FALSE;
3809 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3811 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3813 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3815 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3817 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3818 demands_attention = FALSE;
3819 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3820 undecorated = FALSE;
3825 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3826 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3828 if (max_horz == max_vert) { /* both going the same way */
3829 client_maximize(self, max_horz, 0);
3831 client_maximize(self, max_horz, 1);
3832 client_maximize(self, max_vert, 2);
3836 if (max_horz != self->max_horz)
3837 client_maximize(self, max_horz, 1);
3839 client_maximize(self, max_vert, 2);
3842 /* change fullscreen state before shading, as it will affect if the window
3844 if (fullscreen != self->fullscreen)
3845 client_fullscreen(self, fullscreen);
3846 if (shaded != self->shaded)
3847 client_shade(self, shaded);
3848 if (undecorated != self->undecorated)
3849 client_set_undecorated(self, undecorated);
3850 if (above != self->above || below != self->below) {
3851 self->above = above;
3852 self->below = below;
3853 client_calc_layer(self);
3856 if (modal != self->modal) {
3857 self->modal = modal;
3858 /* when a window changes modality, then its stacking order with its
3859 transients needs to change */
3860 stacking_raise(CLIENT_AS_WINDOW(self));
3862 /* it also may get focused. if something is focused that shouldn't
3863 be focused anymore, then move the focus */
3864 if (focus_client && client_focus_target(focus_client) != focus_client)
3865 client_focus(focus_client);
3868 if (iconic != self->iconic)
3869 client_iconify(self, iconic, FALSE, FALSE);
3871 if (demands_attention != self->demands_attention)
3872 client_hilite(self, demands_attention);
3874 client_change_state(self); /* change the hint to reflect these changes */
3877 ObClient *client_focus_target(ObClient *self)
3879 ObClient *child = NULL;
3881 child = client_search_modal_child(self);
3882 if (child) return child;
3886 gboolean client_can_focus(ObClient *self)
3888 /* choose the correct target */
3889 self = client_focus_target(self);
3891 if (!frame_engine->frame_is_visible(self->frame))
3894 if (!(self->can_focus || self->focus_notify))
3900 gboolean client_focus(ObClient *self)
3902 /* we might not focus this window, so if we have modal children which would
3903 be focused instead, bring them to this desktop */
3904 client_bring_modal_windows(self);
3906 /* choose the correct target */
3907 self = client_focus_target(self);
3909 if (!client_can_focus(self)) {
3910 ob_debug_type(OB_DEBUG_FOCUS,
3911 "Client %s can't be focused", self->title);
3915 ob_debug_type(OB_DEBUG_FOCUS,
3916 "Focusing client \"%s\" (0x%x) at time %u",
3917 self->title, self->w_client, event_curtime);
3919 /* if using focus_delay, stop the timer now so that focus doesn't
3921 event_halt_focus_delay();
3923 /* if there is a grab going on, then we need to cancel it. if we move
3924 focus during the grab, applications will get NotifyWhileGrabbed events
3927 actions should not rely on being able to move focus during an
3930 event_cancel_all_key_grabs();
3932 obt_display_ignore_errors(TRUE);
3934 if (self->can_focus) {
3935 /* This can cause a BadMatch error with CurrentTime, or if an app
3936 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3937 XSetInputFocus(obt_display, self->w_client, RevertToPointerRoot,
3941 if (self->focus_notify) {
3943 ce.xclient.type = ClientMessage;
3944 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
3945 ce.xclient.display = obt_display;
3946 ce.xclient.window = self->w_client;
3947 ce.xclient.format = 32;
3948 ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
3949 ce.xclient.data.l[1] = event_curtime;
3950 ce.xclient.data.l[2] = 0l;
3951 ce.xclient.data.l[3] = 0l;
3952 ce.xclient.data.l[4] = 0l;
3953 XSendEvent(obt_display, self->w_client, FALSE, NoEventMask, &ce);
3956 obt_display_ignore_errors(FALSE);
3958 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
3959 obt_display_error_occured);
3960 return !obt_display_error_occured;
3963 static void client_present(ObClient *self, gboolean here, gboolean raise,
3966 if (client_normal(self) && screen_showing_desktop)
3967 screen_show_desktop(FALSE, self);
3969 client_iconify(self, FALSE, here, FALSE);
3970 if (self->desktop != DESKTOP_ALL &&
3971 self->desktop != screen_desktop)
3974 client_set_desktop(self, screen_desktop, FALSE, TRUE);
3976 screen_set_desktop(self->desktop, FALSE);
3977 } else if (!frame_engine->frame_is_visible(self->frame))
3978 /* if its not visible for other reasons, then don't mess
3981 if (self->shaded && unshade)
3982 client_shade(self, FALSE);
3984 stacking_raise(CLIENT_AS_WINDOW(self));
3989 void client_activate(ObClient *self, gboolean here, gboolean raise,
3990 gboolean unshade, gboolean user)
3992 client_present(self, here, raise, unshade);
3995 static void client_bring_windows_recursive(ObClient *self,
4003 for (it = self->transients; it; it = g_slist_next(it))
4004 client_bring_windows_recursive(it->data, desktop,
4005 helpers, modals, iconic);
4007 if (((helpers && client_helper(self)) ||
4008 (modals && self->modal)) &&
4009 ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
4010 (iconic && self->iconic)))
4012 if (iconic && self->iconic)
4013 client_iconify(self, FALSE, TRUE, FALSE);
4015 client_set_desktop(self, desktop, FALSE, FALSE);
4019 void client_bring_helper_windows(ObClient *self)
4021 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
4024 void client_bring_modal_windows(ObClient *self)
4026 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
4029 gboolean client_focused(ObClient *self)
4031 return self == focus_client;
4034 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
4037 gulong min_diff, min_i;
4039 if (!self->nicons) {
4040 ObClientIcon *parent = NULL;
4043 for (it = self->parents; it; it = g_slist_next(it)) {
4044 ObClient *c = it->data;
4045 if ((parent = client_icon_recursive(c, w, h)))
4052 /* some kind of crappy approximation to find the icon closest in size to
4053 what we requested, but icons are generally all the same ratio as
4054 eachother so it's good enough. */
4056 min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
4059 for (i = 1; i < self->nicons; ++i) {
4062 diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h);
4063 if (diff < min_diff) {
4068 return &self->icons[min_i];
4071 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
4074 static ObClientIcon deficon;
4076 if (!(ret = client_icon_recursive(self, w, h))) {
4077 deficon.width = deficon.height = 48;
4078 deficon.data = ob_rr_theme->def_win_icon;
4084 void client_set_layer(ObClient *self, gint layer)
4088 self->above = FALSE;
4089 } else if (layer == 0) {
4090 self->below = self->above = FALSE;
4092 self->below = FALSE;
4095 client_calc_layer(self);
4096 client_change_state(self); /* reflect this in the state hints */
4099 void client_set_undecorated(ObClient *self, gboolean undecorated)
4101 if (self->undecorated != undecorated &&
4102 /* don't let it undecorate if the function is missing, but let
4104 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4106 self->undecorated = undecorated;
4107 client_setup_decor_and_functions(self, TRUE);
4108 client_change_state(self); /* reflect this in the state hints */
4112 guint client_monitor(ObClient *self)
4115 frame_engine->frame_get_window_area(self->frame, &area);
4116 return screen_find_monitor(&area);
4119 ObClient *client_direct_parent(ObClient *self)
4121 if (!self->parents) return NULL;
4122 if (self->transient_for_group) return NULL;
4123 return self->parents->data;
4126 ObClient *client_search_top_direct_parent(ObClient *self)
4129 while ((p = client_direct_parent(self))) self = p;
4133 static GSList *client_search_all_top_parents_internal(ObClient *self,
4135 ObStackingLayer layer)
4140 /* move up the direct transient chain as far as possible */
4141 while ((p = client_direct_parent(self)) &&
4142 (!bylayer || p->layer == layer))
4146 ret = g_slist_prepend(NULL, self);
4148 ret = g_slist_copy(self->parents);
4153 GSList *client_search_all_top_parents(ObClient *self)
4155 return client_search_all_top_parents_internal(self, FALSE, 0);
4158 GSList *client_search_all_top_parents_layer(ObClient *self)
4160 return client_search_all_top_parents_internal(self, TRUE, self->layer);
4163 ObClient *client_search_focus_parent(ObClient *self)
4167 for (it = self->parents; it; it = g_slist_next(it))
4168 if (client_focused(it->data)) return it->data;
4173 ObClient *client_search_parent(ObClient *self, ObClient *search)
4177 for (it = self->parents; it; it = g_slist_next(it))
4178 if (it->data == search) return search;
4183 ObClient *client_search_transient(ObClient *self, ObClient *search)
4187 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4188 if (sit->data == search)
4190 if (client_search_transient(sit->data, search))
4196 static void detect_edge(Rect area, ObDirection dir,
4197 gint my_head, gint my_size,
4198 gint my_edge_start, gint my_edge_size,
4199 gint *dest, gboolean *near_edge)
4201 gint edge_start, edge_size, head, tail;
4202 gboolean skip_head = FALSE, skip_tail = FALSE;
4205 case OB_DIRECTION_NORTH:
4206 case OB_DIRECTION_SOUTH:
4207 edge_start = area.x;
4208 edge_size = area.width;
4210 case OB_DIRECTION_EAST:
4211 case OB_DIRECTION_WEST:
4212 edge_start = area.y;
4213 edge_size = area.height;
4216 g_assert_not_reached();
4219 /* do we collide with this window? */
4220 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4221 edge_start, edge_size))
4225 case OB_DIRECTION_NORTH:
4226 head = RECT_BOTTOM(area);
4227 tail = RECT_TOP(area);
4229 case OB_DIRECTION_SOUTH:
4230 head = RECT_TOP(area);
4231 tail = RECT_BOTTOM(area);
4233 case OB_DIRECTION_WEST:
4234 head = RECT_RIGHT(area);
4235 tail = RECT_LEFT(area);
4237 case OB_DIRECTION_EAST:
4238 head = RECT_LEFT(area);
4239 tail = RECT_RIGHT(area);
4242 g_assert_not_reached();
4245 case OB_DIRECTION_NORTH:
4246 case OB_DIRECTION_WEST:
4247 /* check if our window is past the head of this window */
4248 if (my_head <= head + 1)
4250 /* check if our window's tail is past the tail of this window */
4251 if (my_head + my_size - 1 <= tail)
4253 /* check if the head of this window is closer than the previously
4254 chosen edge (take into account that the previously chosen
4255 edge might have been a tail, not a head) */
4256 if (head + (*near_edge ? 0 : my_size) < *dest)
4258 /* check if the tail of this window is closer than the previously
4259 chosen edge (take into account that the previously chosen
4260 edge might have been a head, not a tail) */
4261 if (tail - (!*near_edge ? 0 : my_size) < *dest)
4264 case OB_DIRECTION_SOUTH:
4265 case OB_DIRECTION_EAST:
4266 /* check if our window is past the head of this window */
4267 if (my_head >= head - 1)
4269 /* check if our window's tail is past the tail of this window */
4270 if (my_head - my_size + 1 >= tail)
4272 /* check if the head of this window is closer than the previously
4273 chosen edge (take into account that the previously chosen
4274 edge might have been a tail, not a head) */
4275 if (head - (*near_edge ? 0 : my_size) > *dest)
4277 /* check if the tail of this window is closer than the previously
4278 chosen edge (take into account that the previously chosen
4279 edge might have been a head, not a tail) */
4280 if (tail + (!*near_edge ? 0 : my_size) > *dest)
4284 g_assert_not_reached();
4287 ob_debug("my head %d size %d", my_head, my_size);
4288 ob_debug("head %d tail %d deest %d", head, tail, *dest);
4290 ob_debug("using near edge %d", head);
4294 else if (!skip_tail) {
4295 ob_debug("using far edge %d", tail);
4301 void client_find_edge_directional(ObClient *self, ObDirection dir,
4302 gint my_head, gint my_size,
4303 gint my_edge_start, gint my_edge_size,
4304 gint *dest, gboolean *near_edge)
4312 frame_engine->frame_get_size(self->frame, &size);
4314 frame_engine->frame_get_window_area(self->frame, &area);
4316 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4318 mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
4322 case OB_DIRECTION_NORTH:
4323 if (my_head >= RECT_TOP(*mon) + 1)
4324 edge = RECT_TOP(*mon) - 1;
4326 edge = RECT_TOP(*a) - 1;
4328 case OB_DIRECTION_SOUTH:
4329 if (my_head <= RECT_BOTTOM(*mon) - 1)
4330 edge = RECT_BOTTOM(*mon) + 1;
4332 edge = RECT_BOTTOM(*a) + 1;
4334 case OB_DIRECTION_EAST:
4335 if (my_head <= RECT_RIGHT(*mon) - 1)
4336 edge = RECT_RIGHT(*mon) + 1;
4338 edge = RECT_RIGHT(*a) + 1;
4340 case OB_DIRECTION_WEST:
4341 if (my_head >= RECT_LEFT(*mon) + 1)
4342 edge = RECT_LEFT(*mon) - 1;
4344 edge = RECT_LEFT(*a) - 1;
4347 g_assert_not_reached();
4349 /* default to the far edge, then narrow it down */
4353 for (it = client_list; it; it = g_list_next(it)) {
4354 ObClient *cur = it->data;
4356 /* skip windows to not bump into */
4361 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4362 cur->desktop != screen_desktop)
4365 ob_debug("trying window %s", cur->title);
4368 frame_engine->frame_get_window_area(self->frame, &area);
4369 detect_edge(area, dir, my_head, my_size, my_edge_start,
4370 my_edge_size, dest, near_edge);
4372 dock_get_area(&dock_area);
4373 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4374 my_edge_size, dest, near_edge);
4379 void client_find_move_directional(ObClient *self, ObDirection dir,
4383 gint e, e_start, e_size;
4388 frame_engine->frame_get_window_area(self->frame, &area);
4391 case OB_DIRECTION_EAST:
4392 head = RECT_RIGHT(area);
4394 e_start = RECT_TOP(area);
4395 e_size = area.height;
4397 case OB_DIRECTION_WEST:
4398 head = RECT_LEFT(area);
4400 e_start = RECT_TOP(area);
4401 e_size = area.height;
4403 case OB_DIRECTION_NORTH:
4404 head = RECT_TOP(area);
4406 e_start = RECT_LEFT(area);
4407 e_size = area.width;
4409 case OB_DIRECTION_SOUTH:
4410 head = RECT_BOTTOM(area);
4412 e_start = RECT_LEFT(area);
4413 e_size = area.width;
4416 g_assert_not_reached();
4419 client_find_edge_directional(self, dir, head, size,
4420 e_start, e_size, &e, &near);
4424 case OB_DIRECTION_EAST:
4425 if (near) e -= area.width;
4429 case OB_DIRECTION_WEST:
4431 else e -= area.width;
4434 case OB_DIRECTION_NORTH:
4436 else e -= area.height;
4439 case OB_DIRECTION_SOUTH:
4440 if (near) e -= area.height;
4445 g_assert_not_reached();
4447 frame_frame_gravity(self, x, y);
4450 void client_find_resize_directional(ObClient *self, ObDirection side,
4452 gint *x, gint *y, gint *w, gint *h)
4455 gint e, e_start, e_size, delta;
4460 frame_engine->frame_get_window_area(self->frame, &area);
4462 frame_engine->frame_get_size(self->frame, &size);
4465 case OB_DIRECTION_EAST:
4466 head = RECT_RIGHT(area) +
4467 (self->size_inc.width - 1) * (grow ? 1 : -1);
4468 e_start = RECT_TOP(area);
4469 e_size = area.height;
4470 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4472 case OB_DIRECTION_WEST:
4473 head = RECT_LEFT(area) -
4474 (self->size_inc.width - 1) * (grow ? 1 : -1);
4475 e_start = RECT_TOP(area);
4476 e_size = area.height;
4477 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4479 case OB_DIRECTION_NORTH:
4480 head = RECT_TOP(area) -
4481 (self->size_inc.height - 1) * (grow ? 1 : -1);
4482 e_start = RECT_LEFT(area);
4483 e_size = area.width;
4484 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4486 case OB_DIRECTION_SOUTH:
4487 head = RECT_BOTTOM(area) +
4488 (self->size_inc.height - 1) * (grow ? 1 : -1);
4489 e_start = RECT_LEFT(area);
4490 e_size = area.width;
4491 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4494 g_assert_not_reached();
4497 ob_debug("head %d dir %d", head, dir);
4498 client_find_edge_directional(self, dir, head, 1,
4499 e_start, e_size, &e, &near);
4500 ob_debug("edge %d", e);
4506 case OB_DIRECTION_EAST:
4507 if (grow == near) --e;
4508 delta = e - RECT_RIGHT(area);
4511 case OB_DIRECTION_WEST:
4512 if (grow == near) ++e;
4513 delta = RECT_LEFT(area) - e;
4517 case OB_DIRECTION_NORTH:
4518 if (grow == near) ++e;
4519 delta = RECT_TOP(area) - e;
4523 case OB_DIRECTION_SOUTH:
4524 if (grow == near) --e;
4525 delta = e - RECT_BOTTOM(area);
4529 g_assert_not_reached();
4531 frame_frame_gravity(self, x, y);
4532 *w -= size.left + size.right;
4533 *h -= size.top + size.bottom;
4536 ObClient* client_under_pointer(void)
4540 ObClient *ret = NULL;
4542 if (screen_pointer_pos(&x, &y)) {
4543 for (it = stacking_list; it; it = g_list_next(it)) {
4544 if (WINDOW_IS_CLIENT(it->data)) {
4545 ObClient *c = WINDOW_AS_CLIENT(it->data);
4547 frame_engine->frame_get_window_area(c->frame, &area);
4548 if (frame_engine->frame_is_visible(c->frame) &&
4549 /* check the desktop, this is done during desktop
4550 switching and windows are shown/hidden status is not
4552 (c->desktop == screen_desktop ||
4553 c->desktop == DESKTOP_ALL) &&
4554 RECT_CONTAINS(area, x, y))
4565 gboolean client_has_group_siblings(ObClient *self)
4567 return self->group && self->group->members->next;
4570 void client_show_frame(ObClient * self)
4572 frame_engine->frame_set_is_visible(self->frame, TRUE);
4573 frame_engine->frame_update_layout(self->frame, self->area, FALSE, FALSE);
4574 /* if this occurs while we are focus cycling, the indicator needs to
4575 match the changes */
4576 if (focus_cycle_target == self)
4577 focus_cycle_draw_indicator(self);
4578 frame_engine->frame_update_skin(self->frame);
4579 /* Grab the server to make sure that the frame window is mapped before
4580 the client gets its MapNotify, i.e. to make sure the client is
4581 _visible_ when it gets MapNotify. */
4583 XMapWindow(obt_display, self->w_client);
4584 XMapWindow(obt_display, self->w_frame);
4588 void client_hide_frame(ObClient * self)
4590 frame_engine->frame_set_is_visible(self->frame, FALSE);
4591 XUnmapWindow(obt_display, self->w_frame);
4592 /* we unmap the client itself so that we can get MapRequest
4593 events, and because the ICCCM tells us to! */
4594 XUnmapWindow(obt_display, self->w_client);
4595 self->ignore_unmaps++;
4598 gboolean client_flash_timeout(gpointer data)
4600 ObClient * self = (ObClient *) data;
4602 g_get_current_time(&now);
4604 if (now.tv_sec > self->flash_end.tv_sec
4605 || (now.tv_sec == self->flash_end.tv_sec && now.tv_usec
4606 >= self->flash_end.tv_usec))
4607 self->flashing = FALSE;
4609 if (!self->flashing)
4610 return FALSE; /* we are done */
4612 self->flash_on = !self->flash_on;
4613 if (!self->focused) {
4614 frame_engine->frame_set_is_focus(self->frame, self->flash_on);
4615 frame_engine->frame_update_skin (self->frame);
4616 self->focused = FALSE;
4618 return TRUE; /* go again */
4621 void client_flash_start(ObClient * self)
4623 self->flash_on = self->focused;
4624 if (!self->flashing)
4625 obt_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 0.6,
4626 client_flash_timeout, self, g_direct_equal, client_flash_done);
4627 g_get_current_time(&self->flash_end);
4628 g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
4630 self->flashing = TRUE;
4633 void client_flash_stop(ObClient * self)
4635 self->flashing = FALSE;
4638 void client_flash_done(gpointer data)
4640 ObClient * self = (ObClient *) data;
4641 if (self->focused != self->flash_on)
4643 frame_engine->frame_set_is_focus(self->frame, self->focused);
4644 frame_engine->frame_update_skin (self->frame);