]> Dogcows Code - chaz/openbox/blobdiff - openbox/client.c
no separator at the start of the menu
[chaz/openbox] / openbox / client.c
index bfb9f790169f6e39eccd05e634f92d3de0b8544b..c59baf96905d7d04036ed6b632b7b98b91ba0b7f 100644 (file)
@@ -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
@@ -68,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);
@@ -296,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);
 
-    self->user_time = 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! */
@@ -327,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) {
@@ -453,35 +460,70 @@ void client_manage(Window window)
     keyboard_grab_for_client(self, TRUE);
     mouse_grab_for_client(self, TRUE);
 
-    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
-       but do focus it. */
     if (activate) {
-        /* This is focus stealing prevention, if a user_time has been set */
-        if (self->user_time == CurrentTime ||
-            self->user_time > client_last_user_time)
+        /* 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 using focus_delay, stop the timer now so that focus doesn't
-               go moving on us */
-            event_halt_focus_delay();
+            /* 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;
+        }
 
-            client_focus(self);
+        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\n",
-                     self->title);
+            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 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 */
+        event_halt_focus_delay();
+        client_focus(self);
+    }
 
     /* client_activate does this but we aret using it so we have to do it
        here as well */
@@ -837,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);
@@ -845,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 */
@@ -938,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;
@@ -974,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)
@@ -1044,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;
@@ -1835,6 +1888,8 @@ void client_update_user_time(ObClient *self, gboolean new_event)
         */
         if (new_event)
             client_last_user_time = time;
+
+        /*ob_debug("window 0x%x user time %u\n", self->window, time);*/
     }
 }
 
@@ -1875,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);
 }
@@ -1942,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));
@@ -1964,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)
@@ -2076,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
@@ -2092,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;
@@ -2103,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) {
@@ -2297,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)
@@ -2401,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)
@@ -2605,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)
@@ -2813,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;
 }
@@ -2926,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)
@@ -3162,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);
@@ -3203,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)
This page took 0.041042 seconds and 4 git commands to generate.