]> Dogcows Code - chaz/openbox/blobdiff - openbox/client.c
when restoring from show-desktop mode because a window is going to be focused, then...
[chaz/openbox] / openbox / client.c
index 5e1a78513992929a178fb871bfbffa0b89a1b3b2..3ebcc69ae8f568a0d6c5deafb129b464f8763ec8 100644 (file)
@@ -77,6 +77,7 @@ static void client_get_mwm_hints(ObClient *self);
 static void client_get_gravity(ObClient *self);
 static void client_get_client_machine(ObClient *self);
 static void client_get_colormap(ObClient *self);
+static void client_get_transientness(ObClient *self);
 static void client_change_allowed_actions(ObClient *self);
 static void client_change_state(ObClient *self);
 static void client_change_wm_state(ObClient *self);
@@ -84,6 +85,10 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y);
 static void client_restore_session_state(ObClient *self);
 static void client_restore_session_stacking(ObClient *self);
 static ObAppSettings *client_get_settings_state(ObClient *self);
+static void client_update_transient_tree(ObClient *self,
+                                         ObGroup *oldgroup, ObGroup *newgroup,
+                                         ObClient* oldparent,
+                                         ObClient *newparent);
 
 void client_startup(gboolean reconfig)
 {
@@ -277,7 +282,7 @@ void client_manage(Window window)
     self->wmstate = WithdrawnState; /* make sure it gets updated first time */
     self->layer = -1;
     self->desktop = screen_num_desktops; /* always an invalid value */
-    self->user_time = CurrentTime;
+    self->user_time = focus_client ? focus_client->user_time : CurrentTime;
 
     client_get_all(self);
     /* per-app settings override stuff, and return the settings for other
@@ -347,8 +352,8 @@ void client_manage(Window window)
 
         /* make sure the window is visible. */
         client_find_onscreen(self, &newx, &newy,
-                             self->frame->area.width,
-                             self->frame->area.height,
+                             self->area.width,
+                             self->area.height,
                              /* non-normal clients has less rules, and
                                 windows that are being restored from a
                                 session do also. we can assume you want
@@ -381,8 +386,10 @@ void client_manage(Window window)
             focus_client->user_time : CurrentTime;
 
         /* 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, last_time);
+        ob_debug_type(OB_DEBUG_FOCUS,
+                      "Want to focus new window 0x%x with time %u "
+                      "(last time %u)\n",
+                      self->window, self->user_time, last_time);
 
         /* if it's on another desktop */
         if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
@@ -391,31 +398,38 @@ void client_manage(Window window)
             !event_time_after(self->user_time, screen_desktop_user_time))
         {
             activate = FALSE;
+            ob_debug_type(OB_DEBUG_FOCUS,
+                          "Not focusing the window because its on another "
+                          "desktop\n");
         }
-        /* If nothing is focused, or a parent was focused, then focus this
-           always
-        */
-        else if (!focus_client || client_search_focus_parent(self) != NULL)
-            activate = TRUE;
-        else
+        /* If something is focused, and it's not our parent... */
+        else if (focus_client && client_search_focus_parent(self) == NULL)
         {
             /* If time stamp is old, don't steal focus */
             if (self->user_time && last_time &&
                 !event_time_after(self->user_time, last_time))
             {
                 activate = FALSE;
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Not focusing the window because the time is "
+                              "too old\n");
             }
             /* 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))
+            if (!(focus_client->can_focus || focus_client->focus_notify)) {
                 activate = FALSE;
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Not focusing the window because a globally "
+                              "active client has focus\n");
+            }
         }
 
         if (!activate) {
-            ob_debug("Focus stealing prevention activated for %s with time %u "
-                     "(last time %u)\n",
-                     self->title, self->user_time, last_time);
+            ob_debug_type(OB_DEBUG_FOCUS,
+                          "Focus stealing prevention activated for %s with "
+                          "time %u (last time %u)\n",
+                          self->title, self->user_time, last_time);
             /* if the client isn't focused, then hilite it so the user
                knows it is there */
             client_hilite(self, TRUE);
@@ -494,22 +508,8 @@ void client_unmanage(ObClient *self)
     /* update the focus lists */
     focus_order_remove(self);
     if (client_focused(self)) {
-        /* we have to fall back here because we might not get a focus out.
-           1. we need to xselectinput off the window before we unmap it because
-           otherwise we end up getting unmapnotifies we don't want and they
-           can mess up mapping it again quickly
-           2. this means that if we unmanage from a synthetic unmapnotify, we
-              are the ones unmapped it, and causing the focusout. so we won't
-              get the focusout event.
-           3. we can't handle focusin events on the root window because they
-              come from all screens, so the focus change gets lost
-
-           if this ever gets removed in the future MAKE SURE to replace it
-           with:
-           /- don't leave an invalid focus_client -/
-           focus_client = NULL;
-        */
-        focus_fallback(FALSE);
+        /* don't leave an invalid focus_client */
+        focus_client = NULL;
     }
 
     client_list = g_list_remove(client_list, self);
@@ -742,8 +742,8 @@ void client_move_onscreen(ObClient *self, gboolean rude)
     gint x = self->area.x;
     gint y = self->area.y;
     if (client_find_onscreen(self, &x, &y,
-                             self->frame->area.width,
-                             self->frame->area.height, rude)) {
+                             self->area.width,
+                             self->area.height, rude)) {
         client_move(self, x, y);
     }
 }
@@ -917,16 +917,22 @@ static void client_get_all(ObClient *self)
     client_get_area(self);
     client_get_mwm_hints(self);
 
-    /* The transient hint is used to pick a type, but the type can also affect
-       transiency (dialogs are always made transients of their group if they
-       have one). This is Havoc's idea, but it is needed to make some apps
-       work right (eg tsclient). */
-    client_update_transient_for(self);
+    /* The transient-ness of a window is used to pick a type, but the type can
+       also affect transiency.
+
+       Dialogs are always made transients for their group if they have one.
+
+       I also have made non-application type windows be transients for their
+       group (eg utility windows).
+    */
+    client_get_transientness(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);
+    /* this may have already been called from client_update_wmhints */
+    if (self->transient_for == NULL)
+        client_update_transient_for(self);
     client_get_startup_id(self);
     client_get_desktop(self);/* uses transient data/group/startup id if a
                                 desktop is not specified */
@@ -1123,6 +1129,13 @@ static void client_get_shaped(ObClient *self)
 #endif
 }
 
+void client_get_transientness(ObClient *self)
+{
+    Window t;
+    if (XGetTransientForHint(ob_display, self->window, &t))
+        self->transient = TRUE;
+}
+
 void client_update_transient_for(ObClient *self)
 {
     Window t = None;
@@ -1166,62 +1179,126 @@ void client_update_transient_for(ObClient *self)
                 }
             }
         }
-    } else if (self->group) {
-        if (self->type == OB_CLIENT_TYPE_DIALOG ||
-            self->type == OB_CLIENT_TYPE_TOOLBAR ||
-            self->type == OB_CLIENT_TYPE_MENU ||
-            self->type == OB_CLIENT_TYPE_UTILITY)
-        {
-            self->transient = TRUE;
+    } else if (self->type == OB_CLIENT_TYPE_DIALOG ||
+               self->type == OB_CLIENT_TYPE_TOOLBAR ||
+               self->type == OB_CLIENT_TYPE_MENU ||
+               self->type == OB_CLIENT_TYPE_UTILITY)
+    {
+        self->transient = TRUE;
+        if (self->group)
             target = OB_TRAN_GROUP;
-        }
     } else
         self->transient = FALSE;
 
-    /* if anything has changed... */
-    if (target != self->transient_for) {
-        if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
-            GSList *it;
+    client_update_transient_tree(self, self->group, self->group,
+                                 self->transient_for, target);
+    self->transient_for = target;
+                          
+}
 
-            /* remove from old parents */
-            for (it = self->group->members; it; it = g_slist_next(it)) {
-                ObClient *c = it->data;
-                if (c != self && (!c->transient_for ||
-                                  c->transient_for != OB_TRAN_GROUP))
-                    c->transients = g_slist_remove(c->transients, self);
-            }
-        } else if (self->transient_for != NULL) { /* transient of window */
-            /* remove from old parent */
-            self->transient_for->transients =
-                g_slist_remove(self->transient_for->transients, self);
+static void client_update_transient_tree(ObClient *self,
+                                         ObGroup *oldgroup, ObGroup *newgroup,
+                                         ObClient* oldparent,
+                                         ObClient *newparent)
+{
+    GSList *it, *next;
+    ObClient *c;
+
+    /* No change has occured */
+    if (oldgroup == newgroup && oldparent == newparent) return;
+
+    /** Remove the client from the transient tree wherever it has changed **/
+
+    /* If the window is becoming a direct transient for a window in its group
+       then that window can't be a child of this window anymore */
+    if (oldparent != newparent &&
+        newparent != NULL && newparent != OB_TRAN_GROUP &&
+        newparent->transient_for == OB_TRAN_GROUP &&
+        newgroup != NULL && newgroup == oldgroup)
+    {
+        self->transients = g_slist_remove(self->transients, newparent);
+    }
+            
+
+    /* If the group changed then we need to remove any old group transient
+       windows from our children. But if we're transient for the group, then
+       other group transients are not our children. */
+    if (oldgroup != newgroup && oldgroup != NULL &&
+        oldparent != OB_TRAN_GROUP)
+    {
+        for (it = self->transients; it; it = next) {
+            next = g_slist_next(it);
+            c = it->data;
+            if (c->group == oldgroup)
+                self->transients = g_slist_delete_link(self->transients, it);
         }
-        self->transient_for = target;
-        if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
-            GSList *it;
+    }
 
-            /* add to new parents */
-            for (it = self->group->members; it; it = g_slist_next(it)) {
-                ObClient *c = it->data;
-                if (c != self && (!c->transient_for ||
-                                  c->transient_for != OB_TRAN_GROUP))
-                    c->transients = g_slist_append(c->transients, self);
-            }
+    /* If we used to be transient for a group and now we are not, or we're
+       transient for a new group, then we need to remove ourselves from all
+       our ex-parents */
+    if (oldparent == OB_TRAN_GROUP && (oldgroup != newgroup ||
+                                       oldparent != newparent))
+    {
+        for (it = oldgroup->members; it; it = g_slist_next(it)) {
+            c = it->data;
+            if (c != self && (!c->transient_for ||
+                              c->transient_for != OB_TRAN_GROUP))
+                c->transients = g_slist_remove(c->transients, self);
+        }
+    }
+    /* If we used to be transient for a single window and we are no longer
+       transient for it, then we need to remove ourself from its children */
+    else if (oldparent != NULL && oldparent != OB_TRAN_GROUP &&
+             oldparent != newparent)
+        oldparent->transients = g_slist_remove(oldparent->transients, self);
 
-            /* remove all transients which are in the group, that causes
-               circlular pointer hell of doom */
-            for (it = self->group->members; it; it = g_slist_next(it)) {
-                GSList *sit, *next;
-                for (sit = self->transients; sit; sit = next) {
-                    next = g_slist_next(sit);
-                    if (sit->data == it->data)
-                        self->transients =
-                            g_slist_delete_link(self->transients, sit);
-                }
+
+    /** Re-add the client to the transient tree wherever it has changed **/
+
+    /* If we're now transient for a group and we weren't transient for it
+       before then we need to add ourselves to all our new parents */
+    if (newparent == OB_TRAN_GROUP && (oldgroup != newgroup ||
+                                       oldparent != newparent))
+    {
+        for (it = oldgroup->members; it; it = g_slist_next(it)) {
+            c = it->data;
+            if (c != self && (!c->transient_for ||
+                              c->transient_for != OB_TRAN_GROUP))
+                c->transients = g_slist_append(c->transients, self);
+        }
+    }
+    /* If we are now transient for a single window which we weren't before,
+       we need to add ourselves to its children
+
+       WARNING: Cyclical transient ness is possible if two windows are
+       transient for eachother.
+    */
+    else if (newparent != NULL && newparent != OB_TRAN_GROUP &&
+             newparent != oldparent &&
+             /* don't make ourself its child if it is already our child */
+             !client_is_direct_child(self, newparent))
+        newparent->transients = g_slist_append(newparent->transients, self);
+
+    /* If the group changed then we need to add any new group transient
+       windows to our children. But if we're transient for the group, then
+       other group transients are not our children.
+
+       WARNING: Cyclical transient-ness is possible. For e.g. if:
+       A is transient for the group
+       B is a member of the group and transient for A
+    */
+    if (oldgroup != newgroup && newgroup != NULL &&
+        newparent != OB_TRAN_GROUP)
+    {
+        for (it = newgroup->members; it; it = g_slist_next(it)) {
+            c = it->data;
+            if (c != self && c->transient_for == OB_TRAN_GROUP &&
+                /* Don't make it our child if it is already our parent */
+                !client_is_direct_child(c, self))
+            {
+                self->transients = g_slist_append(self->transients, c);
             }
-        } else if (self->transient_for != NULL) { /* transient of window */
-            /* add to new parent */
-            self->transient_for->transients =
-                g_slist_append(self->transient_for->transients, self);
         }
     }
 }
@@ -1616,12 +1693,13 @@ void client_reconfigure(ObClient *self)
 void client_update_wmhints(ObClient *self)
 {
     XWMHints *hints;
-    GSList *it;
 
     /* assume a window takes input if it doesnt specify */
     self->can_focus = TRUE;
   
     if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
+        gboolean ur;
+
         if (hints->flags & InputHint)
             self->can_focus = hints->input;
 
@@ -1631,59 +1709,62 @@ void client_update_wmhints(ObClient *self)
             if (hints->flags & StateHint)
                 self->iconic = hints->initial_state == IconicState;
 
+        ur = self->urgent;
+        self->urgent = (hints->flags & XUrgencyHint);
+        if (self->urgent && !ur)
+            client_hilite(self, TRUE);
+        else if (!self->urgent && ur && self->demands_attention)
+            client_hilite(self, FALSE);
+
         if (!(hints->flags & WindowGroupHint))
             hints->window_group = None;
 
         /* did the group state change? */
         if (hints->window_group !=
-            (self->group ? self->group->leader : None)) {
+            (self->group ? self->group->leader : None))
+        {
+            ObGroup *oldgroup = self->group;
+
             /* remove from the old group if there was one */
             if (self->group != NULL) {
-                /* remove transients of the group */
-                for (it = self->group->members; it; it = g_slist_next(it))
-                    self->transients = g_slist_remove(self->transients,
-                                                      it->data);
-
-                /* remove myself from parents in the group */
-                if (self->transient_for == OB_TRAN_GROUP) {
-                    for (it = self->group->members; it;
-                         it = g_slist_next(it))
-                    {
-                        ObClient *c = it->data;
-
-                        if (c != self && !c->transient_for)
-                            c->transients = g_slist_remove(c->transients,
-                                                           self);
-                    }
-                }
-
                 group_remove(self->group, self);
                 self->group = NULL;
             }
+
+            /* add ourself to the group if we have one */
             if (hints->window_group != None) {
                 self->group = group_add(hints->window_group, self);
-
-                /* i can only have transients from the group if i am not
-                   transient myself */
-                if (!self->transient_for) {
-                    /* add other transients of the group that are already
-                       set up */
-                    for (it = self->group->members; it;
-                         it = g_slist_next(it))
-                    {
-                        ObClient *c = it->data;
-                        if (c != self && c->transient_for == OB_TRAN_GROUP)
-                            self->transients =
-                                g_slist_append(self->transients, c);
-                    }
-                }
             }
 
-            /* because the self->transient flag wont change from this call,
-               we don't need to update the window's type and such, only its
-               transient_for, and the transients lists of other windows in
-               the group may be affected */
-            client_update_transient_for(self);
+            /* Put ourselves into the new group's transient tree, and remove
+               ourselves from the old group's */
+            client_update_transient_tree(self, oldgroup, self->group,
+                                         self->transient_for,
+                                         self->transient_for);
+
+            /* Lastly, being in a group, or not, can change if the window is
+               transient for anything.
+
+               The logic for this is:
+               self->transient = TRUE always if the window wants to be
+               transient for something, even if transient_for was NULL because
+               it wasn't in a group before.
+
+               If transient_for was NULL and oldgroup was NULL we can assume
+               that when we add the new group, it will become transient for
+               something.
+
+               If transient_for was OB_TRAN_GROUP, then it must have already
+               had a group. If it is getting a new group, the above call to
+               client_update_transient_tree has already taken care of
+               everything ! If it is losing all group status then it will
+               no longer be transient for anything and that needs to be
+               updated.
+            */
+            if (self->transient &&
+                ((self->transient_for == NULL && oldgroup == NULL) ||
+                 (self->transient_for == OB_TRAN_GROUP && !self->group)))
+                client_update_transient_for(self);
         }
 
         /* the WM_HINTS can contain an icon */
@@ -2189,6 +2270,12 @@ gboolean client_normal(ObClient *self) {
               self->type == OB_CLIENT_TYPE_SPLASH);
 }
 
+gboolean client_application(ObClient *self)
+{
+    return (self->type == OB_CLIENT_TYPE_NORMAL ||
+            self->type == OB_CLIENT_TYPE_DIALOG);
+}
+
 static void client_apply_startup_state(ObClient *self, gint x, gint y)
 {
     gboolean pos = FALSE; /* has the window's position been configured? */
@@ -2606,7 +2693,8 @@ static void client_iconify_recursive(ObClient *self,
         } else {
             self->iconic = iconic;
 
-            if (curdesk)
+            if (curdesk && self->desktop != screen_desktop &&
+                self->desktop != DESKTOP_ALL)
                 client_set_desktop(self, screen_desktop, FALSE);
 
             /* this puts it after the current focused window */
@@ -2624,10 +2712,11 @@ static void client_iconify_recursive(ObClient *self,
             screen_update_areas();
     }
 
-    /* iconify all direct transients */
+    /* iconify all direct transients, and deiconify all transients
+       (non-direct too) */
     for (it = self->transients; it; it = g_slist_next(it))
         if (it->data != self)
-            if (client_is_direct_child(self, it->data))
+            if (client_is_direct_child(self, it->data) || !iconic)
                 client_iconify_recursive(it->data, iconic, curdesk);
 }
 
@@ -2775,11 +2864,13 @@ void client_hilite(ObClient *self, gboolean hilite)
 
     /* don't allow focused windows to hilite */
     self->demands_attention = hilite && !client_focused(self);
-    if (self->demands_attention)
-        frame_flash_start(self->frame);
-    else
-        frame_flash_stop(self->frame);
-    client_change_state(self);
+    if (self->frame != NULL) { /* if we're mapping, just set the state */
+        if (self->demands_attention)
+            frame_flash_start(self->frame);
+        else
+            frame_flash_stop(self->frame);
+        client_change_state(self);
+    }
 }
 
 void client_set_desktop_recursive(ObClient *self,
@@ -3170,11 +3261,12 @@ void client_activate(ObClient *self, gboolean here, gboolean user)
         event_halt_focus_delay();
 
         if (client_normal(self) && screen_showing_desktop)
-            screen_show_desktop(FALSE);
+            screen_show_desktop(FALSE, FALSE);
         if (self->iconic)
             client_iconify(self, FALSE, here);
         if (self->desktop != DESKTOP_ALL &&
-            self->desktop != screen_desktop) {
+            self->desktop != screen_desktop)
+        {
             if (here)
                 client_set_desktop(self, screen_desktop, FALSE);
             else
@@ -3621,3 +3713,17 @@ gboolean client_has_group_siblings(ObClient *self)
 {
     return self->group && self->group->members->next;
 }
+
+gboolean client_has_application_group_siblings(ObClient *self)
+{
+    GSList *it;
+
+    if (!self->group) return FALSE;
+
+    for (it = self->group->members; it; it = g_slist_next(it)) {
+        ObClient *c = it->data;
+        if (c != self && client_application(c))
+            return TRUE;
+    }
+    return FALSE;
+}
This page took 0.038841 seconds and 4 git commands to generate.