X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;ds=sidebyside;f=openbox%2Fclient.c;h=c59baf96905d7d04036ed6b632b7b98b91ba0b7f;hb=baa4a878cda78cf7ef9a894b8937c72fa50c897a;hp=27e63292931b0ed422149c7a40e900f0f02f2aaa;hpb=bd19fd06db8905ef835109414314ef928370a7d7;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 27e63292..c59baf96 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -2,7 +2,7 @@ client.c for the Openbox window manager Copyright (c) 2006 Mikael Magnusson - Copyright (c) 2003 Ben Jansens + Copyright (c) 2003-2007 Dana Jansens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -57,8 +57,10 @@ typedef struct gpointer data; } Destructor; -GList *client_list = NULL; -GSList *client_destructors = NULL; +GList *client_list = NULL; + +static GSList *client_destructors = NULL; +static Time client_last_user_time = CurrentTime; static void client_get_all(ObClient *self); static void client_toggle_border(ObClient *self, gboolean show); @@ -66,6 +68,7 @@ static void client_get_startup_id(ObClient *self); static void client_get_area(ObClient *self); static void client_get_desktop(ObClient *self); static void client_get_state(ObClient *self); +static void client_get_layer(ObClient *self); static void client_get_shaped(ObClient *self); static void client_get_mwm_hints(ObClient *self); static void client_get_gravity(ObClient *self); @@ -294,11 +297,17 @@ void client_manage(Window window) self->wmstate = NormalState; self->layer = -1; self->desktop = screen_num_desktops; /* always an invalid value */ + self->user_time = ~0; /* maximum value, always newer than the real time */ client_get_all(self); client_restore_session_state(self); - sn_app_started(self->startup_id, self->class); + client_calc_layer(self); + + { + Time t = sn_app_started(self->startup_id, self->class); + if (t) self->user_time = t; + } /* update the focus lists, do this before the call to change_state or it can end up in the list twice! */ @@ -325,7 +334,7 @@ void client_manage(Window window) /* get and set application level settings */ settings = get_settings(self); - stacking_add(CLIENT_AS_WINDOW(self)); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); client_restore_session_stacking(self); if (settings) { @@ -451,23 +460,69 @@ void client_manage(Window window) keyboard_grab_for_client(self, TRUE); mouse_grab_for_client(self, TRUE); + if (activate) { + /* This is focus stealing prevention */ + ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n", + self->window, self->user_time, client_last_user_time); + + /* If a nothing at all, or a parent was focused, then focus this + always + */ + if (!focus_client || client_search_focus_parent(self) != NULL) + activate = TRUE; + else + { + /* If time stamp is old, don't steal focus */ + if (self->user_time && self->user_time < client_last_user_time) + activate = FALSE; + /* Don't steal focus from globally active clients. + I stole this idea from KWin. It seems nice. + */ + if (!focus_client->can_focus && focus_client->focus_notify) + activate = FALSE; + } + + if (activate) + { + /* since focus can change the stacking orders, if we focus the + window then the standard raise it gets is not enough, we need + to queue one for after the focus change takes place */ + client_raise(self); + } else { + ob_debug("Focus stealing prevention activated for %s with time %u " + "(last time %u)\n", + self->title, self->user_time, client_last_user_time); + /* if the client isn't focused, then hilite it so the user + knows it is there */ + client_hilite(self, TRUE); + } + } + else { + /* This may look rather odd. Well it's because new windows are added + to the stacking order non-intrusively. If we're not going to focus + the new window or hilite it, then we raise it to the top. This will + take affect for things that don't get focused like splash screens. + Also if you don't have focus_new enabled, then it's going to get + raised to the top. Legacy begets legacy I guess? + */ + client_raise(self); + } + + /* this has to happen before we try focus the window, but we want it to + happen after the client's stacking has been determined or it looks bad + */ client_showhide(self); /* use client_focus instead of client_activate cuz client_activate does stuff like switch desktops etc and I'm not interested in all that when a window maps since its not based on an action from the user like - clicking a window to activate is. so keep the new window out of the way + clicking a window to activate it. so keep the new window out of the way but do focus it. */ if (activate) { - /* if using focus_delay, stop the timer now so that focus doesn't go - moving on us */ + /* if using focus_delay, stop the timer now so that focus doesn't + go moving on us */ event_halt_focus_delay(); - client_focus(self); - /* since focus can change the stacking orders, if we focus the window - then the standard raise it gets is not enough, we need to queue one - for after the focus change takes place */ - client_raise(self); } /* client_activate does this but we aret using it so we have to do it @@ -824,6 +879,7 @@ static void client_get_all(ObClient *self) work right (eg tsclient). */ client_update_transient_for(self); client_get_type(self);/* this can change the mwmhints for special cases */ + client_get_state(self); client_update_transient_for(self); client_update_wmhints(self); @@ -832,7 +888,8 @@ static void client_get_all(ObClient *self) desktop is not specified */ client_get_shaped(self); - client_get_state(self); + client_get_layer(self); /* if layer hasn't been specified, get it from + other sources if possible */ { /* a couple type-based defaults for new windows */ @@ -857,6 +914,7 @@ static void client_get_all(ObClient *self) client_update_sm_client_id(self); client_update_strut(self); client_update_icons(self); + client_update_user_time(self, FALSE); } static void client_get_startup_id(ObClient *self) @@ -924,6 +982,41 @@ static void client_get_desktop(ObClient *self) } } +static void client_get_layer(ObClient *self) +{ + if (!(self->above || self->below)) { + if (self->group) { + /* apply stuff from the group */ + GSList *it; + gint layer = -2; + + for (it = self->group->members; it; it = g_slist_next(it)) { + ObClient *c = it->data; + if (c != self && !client_search_transient(self, c) && + client_normal(self) && client_normal(c)) + { + layer = MAX(layer, + (c->above ? 1 : (c->below ? -1 : 0))); + } + } + switch (layer) { + case -1: + self->below = TRUE; + break; + case -2: + case 0: + break; + case 1: + self->above = TRUE; + break; + default: + g_assert_not_reached(); + break; + } + } + } +} + static void client_get_state(ObClient *self) { guint32 *state; @@ -960,38 +1053,6 @@ static void client_get_state(ObClient *self) g_free(state); } - - if (!(self->above || self->below)) { - if (self->group) { - /* apply stuff from the group */ - GSList *it; - gint layer = -2; - - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if (c != self && !client_search_transient(self, c) && - client_normal(self) && client_normal(c)) - { - layer = MAX(layer, - (c->above ? 1 : (c->below ? -1 : 0))); - } - } - switch (layer) { - case -1: - self->below = TRUE; - break; - case -2: - case 0: - break; - case 1: - self->above = TRUE; - break; - default: - g_assert_not_reached(); - break; - } - } - } } static void client_get_shaped(ObClient *self) @@ -1030,25 +1091,31 @@ void client_update_transient_for(ObClient *self) target = NULL; } -#if 0 -/* we used to do this, but it violates the ICCCM and causes problems because - toolkits seem to set transient_for = root rather arbitrarily (eg kicker's - config dialogs), so it is being removed. the ewmh provides other ways to - make things transient for their group. -dana -*/ + /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat.. + + Setting the transient_for to Root is actually illegal, however + applications from time have done this to specify transient for + their group. + + Now you can do that by being a TYPE_DIALOG and not setting + the transient_for hint at all on your window. But people still + use Root, and Kwin is very strange in this regard. + + KWin 3.0 will not consider windows with transient_for set to + Root as transient for their group *UNLESS* they are also modal. + In that case, it will make them transient for the group. This + leads to all sorts of weird behavior from KDE apps which are + only tested in KWin. I'd like to follow their behavior just to + make this work right with KDE stuff, but that seems wrong. + */ if (!target && self->group) { /* not transient to a client, see if it is transient for a group */ - if (t == self->group->leader || - t == None || - t == RootWindow(ob_display, ob_screen)) - { + if (t == RootWindow(ob_display, ob_screen)) { /* window is a transient for its group! */ target = OB_TRAN_GROUP; } } -#endif - } } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) { self->transient = TRUE; @@ -1804,6 +1871,28 @@ void client_update_icons(ObClient *self) frame_adjust_icon(self->frame); } +void client_update_user_time(ObClient *self, gboolean new_event) +{ + guint32 time; + + if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) { + self->user_time = time; + /* we set this every time, not just when it grows, because in practice + sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes + backward we don't want all windows to stop focusing. we'll just + assume noone is setting times older than the last one, cuz that + would be pretty stupid anyways + However! This is called when a window is mapped to get its user time + but it's an old number, it's not changing it from new user + interaction, so in that case, don't change the last user time. + */ + if (new_event) + client_last_user_time = time; + + /*ob_debug("window 0x%x user time %u\n", self->window, time);*/ + } +} + static void client_change_state(ObClient *self) { gulong state[2]; @@ -1841,8 +1930,6 @@ static void client_change_state(ObClient *self) netstate[num++] = prop_atoms.ob_wm_state_undecorated; PROP_SETA32(self->window, net_wm_state, atom, netstate, num); - client_calc_layer(self); - if (self->frame) frame_adjust_state(self->frame); } @@ -1908,20 +1995,21 @@ static ObStackingLayer calc_layer(ObClient *self) } static void client_calc_layer_recursive(ObClient *self, ObClient *orig, - ObStackingLayer l, gboolean raised) + ObStackingLayer min, gboolean raised) { ObStackingLayer old, own; GSList *it; old = self->layer; own = calc_layer(self); - self->layer = l > own ? l : own; + self->layer = MAX(own, min); for (it = self->transients; it; it = g_slist_next(it)) client_calc_layer_recursive(it->data, orig, - l, raised ? raised : l != old); + self->layer, + raised ? raised : self->layer != old); - if (!raised && l != old) + if (!raised && self->layer != old) if (orig->frame) { /* only restack if the original window is managed */ stacking_remove(CLIENT_AS_WINDOW(self)); stacking_add(CLIENT_AS_WINDOW(self)); @@ -1930,17 +2018,16 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig, void client_calc_layer(ObClient *self) { - ObStackingLayer l; ObClient *orig; + GSList *it; orig = self; /* transients take on the layer of their parents */ - self = client_search_top_transient(self); - - l = calc_layer(self); + it = client_search_top_transients(self); - client_calc_layer_recursive(self, orig, l, FALSE); + for (; it; it = g_slist_next(it)) + client_calc_layer_recursive(it->data, orig, 0, FALSE); } gboolean client_should_show(ObClient *self) @@ -2042,6 +2129,7 @@ void client_configure_full(ObClient *self, ObCorner anchor, gboolean moved = FALSE, resized = FALSE; guint fdecor = self->frame->decorations; gboolean fhorz = self->frame->max_horz; + Rect desired_area = {x, y, w, h}; /* make the frame recalculate its dimentions n shit without changing anything visible for real, this way the constraints below can work with @@ -2058,7 +2146,7 @@ void client_configure_full(ObClient *self, ObCorner anchor, Rect *a; guint i; - i = client_monitor(self); + i = screen_find_monitor(&desired_area); a = screen_physical_area_monitor(i); x = a->x; @@ -2069,8 +2157,10 @@ void client_configure_full(ObClient *self, ObCorner anchor, user = FALSE; /* ignore that increment etc shit when in fullscreen */ } else { Rect *a; + guint i; - a = screen_area_monitor(self->desktop, client_monitor(self)); + i = screen_find_monitor(&desired_area); + a = screen_area_monitor(self->desktop, i); /* set the size and position if maximized */ if (self->max_horz) { @@ -2263,8 +2353,8 @@ void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea) self->fullscreen == fs) return; /* already done */ self->fullscreen = fs; - client_change_state(self); /* change the state hints on the client, - and adjust out layer/stacking */ + client_change_state(self); /* change the state hints on the client */ + client_calc_layer(self); /* and adjust out layer/stacking */ if (fs) { if (savearea) @@ -2367,11 +2457,13 @@ static void client_iconify_recursive(ObClient *self, void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk) { + GSList *it; + /* move up the transient chain as far as possible first */ - self = client_search_top_transient(self); + it = client_search_top_transients(self); - client_iconify_recursive(client_search_top_transient(self), - iconic, curdesk); + for (; it; it = g_slist_next(it)) + client_iconify_recursive(it->data, iconic, curdesk); } void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea) @@ -2571,8 +2663,12 @@ void client_set_desktop_recursive(ObClient *self, void client_set_desktop(ObClient *self, guint target, gboolean donthide) { - client_set_desktop_recursive(client_search_top_transient(self), - target, donthide); + GSList *it; + + it = client_search_top_transients(self); + + for(; it; it = g_slist_next(it)) + client_set_desktop_recursive(it->data, target, donthide); } ObClient *client_search_modal_child(ObClient *self) @@ -2779,16 +2875,14 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) if (demands_attention != self->demands_attention) client_hilite(self, demands_attention); - client_calc_layer(self); client_change_state(self); /* change the hint to reflect these changes */ } ObClient *client_focus_target(ObClient *self) { - ObClient *child; - - /* if we have a modal child, then focus it, not us */ - child = client_search_modal_child(client_search_top_transient(self)); + ObClient *child = NULL; + + child = client_search_modal_child(self); if (child) return child; return self; } @@ -2892,46 +2986,55 @@ void client_unfocus(ObClient *self) } } -void client_activate(ObClient *self, gboolean here, gboolean user) +void client_activate(ObClient *self, gboolean here, gboolean user, Time time) { /* XXX do some stuff here if user is false to determine if we really want - to activate it or not (a parent or group member is currently active) */ - - if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE); - if (self->iconic) - client_iconify(self, FALSE, here); - if (self->desktop != DESKTOP_ALL && - self->desktop != screen_desktop) { - if (here) - client_set_desktop(self, screen_desktop, FALSE); - else - screen_set_desktop(self->desktop); - } else if (!self->frame->visible) - /* if its not visible for other reasons, then don't mess - with it */ - return; - if (self->shaded) - client_shade(self, FALSE); + to activate it or not (a parent or group member is currently + active)? + */ + ob_debug("Want to activate window 0x%x with time %u (last time %u), " + "source=%s\n", + self->window, time, client_last_user_time, + (user ? "user" : "application")); + if (!user && time && time < client_last_user_time) + client_hilite(self, TRUE); + else { + if (client_normal(self) && screen_showing_desktop) + screen_show_desktop(FALSE); + if (self->iconic) + client_iconify(self, FALSE, here); + if (self->desktop != DESKTOP_ALL && + self->desktop != screen_desktop) { + if (here) + client_set_desktop(self, screen_desktop, FALSE); + else + screen_set_desktop(self->desktop); + } else if (!self->frame->visible) + /* if its not visible for other reasons, then don't mess + with it */ + return; + if (self->shaded) + client_shade(self, FALSE); - client_focus(self); + client_focus(self); - /* we do this an action here. this is rather important. this is because - we want the results from the focus change to take place BEFORE we go - about raising the window. when a fullscreen window loses focus, we need - this or else the raise wont be able to raise above the to-lose-focus - fullscreen window. */ - client_raise(self); + /* we do this an action here. this is rather important. this is because + we want the results from the focus change to take place BEFORE we go + about raising the window. when a fullscreen window loses focus, we + need this or else the raise wont be able to raise above the + to-lose-focus fullscreen window. */ + client_raise(self); + } } void client_raise(ObClient *self) { - action_run_string("Raise", self); + action_run_string("Raise", self, CurrentTime); } void client_lower(ObClient *self) { - action_run_string("Lower", self); + action_run_string("Lower", self, CurrentTime); } gboolean client_focused(ObClient *self) @@ -3128,40 +3231,22 @@ void client_set_undecorated(ObClient *self, gboolean undecorated) } } -/* Determines which physical monitor a client is on by calculating the - area of the part of the client on each monitor. The number of the - monitor containing the greatest area of the client is returned.*/ guint client_monitor(ObClient *self) { - guint i; - guint most = 0; - guint mostv = 0; - - for (i = 0; i < screen_num_monitors; ++i) { - Rect *area = screen_physical_area_monitor(i); - if (RECT_INTERSECTS_RECT(*area, self->frame->area)) { - Rect r; - guint v; - - RECT_SET_INTERSECTION(r, *area, self->frame->area); - v = r.width * r.height; - - if (v > mostv) { - mostv = v; - most = i; - } - } - } - return most; + return screen_find_monitor(&self->frame->area); } -ObClient *client_search_top_transient(ObClient *self) +GSList *client_search_top_transients(ObClient *self) { - /* move up the transient chain as far as possible */ - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) { - return client_search_top_transient(self->transient_for); - } else { + GSList *ret = NULL; + + /* move up the direct transient chain as far as possible */ + while (self->transient_for && self->transient_for != OB_TRAN_GROUP) + self = self->transient_for; + + if (!self->transient_for) + ret = g_slist_prepend(ret, self); + else { GSList *it; g_assert(self->group); @@ -3169,16 +3254,15 @@ ObClient *client_search_top_transient(ObClient *self) for (it = self->group->members; it; it = g_slist_next(it)) { ObClient *c = it->data; - /* checking transient_for prevents infinate loops! */ - if (c != self && !c->transient_for) - break; + if (!c->transient_for) + ret = g_slist_prepend(ret, c); } - if (it) - return it->data; - } + + if (ret == NULL) /* no group parents */ + ret = g_slist_prepend(ret, self); } - return self; + return ret; } ObClient *client_search_focus_parent(ObClient *self)