diff --git a/openbox/client.c b/openbox/client.c index 0c4cce1f..4c88daf7 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -451,7 +451,7 @@ void client_manage(Window window) raised to the top. Legacy begets legacy I guess? */ if (!client_restore_session_stacking(self)) - client_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); } /* this has to happen before we try focus the window, but we want it to @@ -2344,13 +2344,25 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig, for (it = self->transients; it; it = g_slist_next(it)) client_calc_layer_recursive(it->data, orig, self->layer, - raised ? raised : self->layer != old); + raised ? raised : self->layer > old); - if (!raised && self->layer != old) - if (orig->frame) { /* only restack if the original window is managed */ + /* restack. but only if the original window is managed. + + raised is used so that only the bottom-most window in the stacking + order is raised, the others will automatically come with it. + + also only the highest windows in the stacking order (no transients) + are lowered, cuz the rest come for free + */ + if (!raised && orig->frame) { + if (self->layer > old) { stacking_remove(CLIENT_AS_WINDOW(self)); - stacking_add(CLIENT_AS_WINDOW(self)); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); + } else if (self->layer < old && self->transients == NULL) { + stacking_remove(CLIENT_AS_WINDOW(self)); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); } + } } void client_calc_layer(ObClient *self) @@ -3077,7 +3089,7 @@ void client_set_desktop_recursive(ObClient *self, client_showhide(self); /* raise if it was not already on the desktop */ if (old != DESKTOP_ALL) - client_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); if (STRUT_EXISTS(self->strut)) screen_update_areas(); } @@ -3302,7 +3314,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) self->modal = modal; /* when a window changes modality, then its stacking order with its transients needs to change */ - client_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); } if (iconic != self->iconic) client_iconify(self, iconic, FALSE); @@ -3444,17 +3456,10 @@ static void client_present(ObClient *self, gboolean here, gboolean raise) return; if (self->shaded) client_shade(self, FALSE); + if (raise) + stacking_raise(CLIENT_AS_WINDOW(self)); client_focus(self); - - if (raise) { - /* we do this as 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_activate(ObClient *self, gboolean here, gboolean user) @@ -3510,16 +3515,6 @@ void client_bring_helper_windows(ObClient *self) client_bring_helper_windows_recursive(self, self->desktop); } -void client_raise(ObClient *self) -{ - action_run_string("Raise", self, CurrentTime); -} - -void client_lower(ObClient *self) -{ - action_run_string("Lower", self, CurrentTime); -} - gboolean client_focused(ObClient *self) { return self == focus_client; diff --git a/openbox/client.h b/openbox/client.h index 30d40510..5a837391 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -550,26 +550,6 @@ void client_bring_helper_windows(ObClient *self); /*! Calculates the stacking layer for the client window */ void client_calc_layer(ObClient *self); -/*! Raises the client to the top of its stacking layer - Normally actions call to the client_* functions to make stuff go, but this - one is an exception. It just fires off an action, which will be queued. - This is because stacking order rules can be changed by focus state, and so - any time focus changes you have to wait for it to complete before you can - properly restart windows. As such, this only queues an action for later - execution, once the focus change has gone through. -*/ -void client_raise(ObClient *self); - -/*! Lowers the client to the bottom of its stacking layer - Normally actions call to the client_* functions to make stuff go, but this - one is an exception. It just fires off an action, which will be queued. - This is because stacking order rules can be changed by focus state, and so - any time focus changes you have to wait for it to complete before you can - properly restart windows. As such, this only queues an action for later - execution, once the focus change has gone through. -*/ -void client_lower(ObClient *self); - /*! Updates the window's transient status, and any parents of it */ void client_update_transient_for(ObClient *self); /*! Update the protocols that the window supports and adjusts things if they diff --git a/openbox/event.c b/openbox/event.c index 566739c9..0c594d6b 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -1019,20 +1019,55 @@ static void event_handle_client(ObClient *client, XEvent *e) } if (e->xconfigurerequest.value_mask & CWStackMode) { + ObClient *sibling = NULL; + + /* get the sibling */ + if (e->xconfigurerequest.value_mask & CWSibling) { + ObWindow *win; + win = g_hash_table_lookup(window_map, + &e->xconfigurerequest.above); + if (WINDOW_IS_CLIENT(win)) + sibling = WINDOW_AS_CLIENT(win); + } + switch (e->xconfigurerequest.detail) { case Below: - case BottomIf: - /* Apps are so rude. And this is totally disconnected from - activation/focus. Bleh. */ - /*client_lower(client);*/ + ob_debug("ConfigureRequest Below for client %s sibling %s\n", + client->title, sibling ? sibling->title : "(all)"); + /* just lower it */ + stacking_lower(CLIENT_AS_WINDOW(client)); + break; + case BottomIf: + ob_debug("ConfigureRequest BottomIf for client %s sibling " + "%s\n", + client->title, sibling ? sibling->title : "(all)"); + /* if this client occludes sibling (or anything if NULL), then + lower it to the bottom */ + if (stacking_occluded(sibling, client)) + stacking_lower(CLIENT_AS_WINDOW(client)); break; - case Above: + ob_debug("ConfigureRequest Above for client %s sibling %s\n", + client->title, sibling ? sibling->title : "(all)"); + /* activate it rather than just focus it */ + client_activate(client, FALSE, FALSE); + break; case TopIf: + ob_debug("ConfigureRequest TopIf for client %s sibling %s\n", + client->title, sibling ? sibling->title : "(all)"); + if (stacking_occluded(client, sibling)) + /* activate it rather than just focus it */ + client_activate(client, FALSE, FALSE); + case Opposite: + ob_debug("ConfigureRequest Opposite for client %s sibling " + "%s\n", + client->title, sibling ? sibling->title : "(all)"); + if (stacking_occluded(client, sibling)) + /* activate it rather than just focus it */ + client_activate(client, FALSE, FALSE); + else if (stacking_occluded(sibling, client)) + stacking_lower(CLIENT_AS_WINDOW(client)); default: - /* Apps are so rude. And this is totally disconnected from - activation/focus. Bleh. */ - /*client_raise(client);*/ break; } } @@ -1601,7 +1636,7 @@ static gboolean focus_delay_func(gpointer data) event_curtime = d->time; if (focus_client != d->client) { if (client_focus(d->client) && config_focus_raise) - client_raise(d->client); + stacking_raise(CLIENT_AS_WINDOW(d->client)); } event_curtime = old; return FALSE; /* no repeat */ diff --git a/openbox/stacking.c b/openbox/stacking.c index 18747b9c..afad8ad0 100644 --- a/openbox/stacking.c +++ b/openbox/stacking.c @@ -416,7 +416,7 @@ void stacking_add_nonintrusive(ObWindow *win) /* insert above its highest parent (or its highest child !) */ it_below = find_highest_relative(client); - if (!it_below) { + if (!it_below && client != focus_client) { /* nothing to put it directly above, so try find the focused client to put it underneath it */ if (focus_client && focus_client->layer == client->layer) { @@ -425,10 +425,16 @@ void stacking_add_nonintrusive(ObWindow *win) } } if (!it_below) { - /* there is no window to put this directly above, so put it at the - bottom */ - stacking_list = g_list_prepend(stacking_list, win); - stacking_lower(win); + if (client == focus_client) { + /* it's focused so put it at the top */ + stacking_list = g_list_append(stacking_list, win); + stacking_raise(win); + } else { + /* there is no window to put this directly above, so put it at the + bottom */ + stacking_list = g_list_prepend(stacking_list, win); + stacking_lower(win); + } } else { /* make sure it's not in the wrong layer though ! */ for (; it_below; it_below = g_list_next(it_below)) @@ -453,3 +459,36 @@ void stacking_add_nonintrusive(ObWindow *win) g_list_free(wins); } } + +gboolean stacking_occluded(ObClient *client, ObClient *sibling) +{ + GList *it; + gboolean obscured = FALSE; + gboolean found = FALSE; + + /* no need for any looping in this case */ + if (sibling && client->layer != sibling->layer) + return obscured; + + for (it = stacking_list; it; it = g_list_next(it)) + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + if (found) { + if (sibling != NULL) { + if (c == sibling) { + obscured = TRUE; + break; + } + } + else if (c->layer == client->layer) { + obscured = TRUE; + break; + } + else if (c->layer > client->layer) + break; /* we past its layer */ + } + else if (c == client) + found = TRUE; + } + return obscured; +} diff --git a/openbox/stacking.h b/openbox/stacking.h index 2391f650..70e4b2d4 100644 --- a/openbox/stacking.h +++ b/openbox/stacking.h @@ -59,4 +59,9 @@ void stacking_lower(ObWindow *window); */ void stacking_below(ObWindow *window, ObWindow *below); +/*! Returns TRUE if client is occluded by sibling. If sibling is NULL it tries + against all other clients. Otherwise, it returns FALSE. +*/ +gboolean stacking_occluded(struct _ObClient *client,struct _ObClient *sibling); + #endif