stacking has been made more reliable with groups and group transients.
this was a pretty invasive change in client.c though, so it may break things? it did expose some bugginess in client_calc_layer, which is now better than ever, hopefully there isn't more to be found.
This commit is contained in:
parent
1ff3c1dd9d
commit
62a39c4c70
4 changed files with 129 additions and 82 deletions
|
@ -301,6 +301,8 @@ void client_manage(Window window)
|
||||||
client_get_all(self);
|
client_get_all(self);
|
||||||
client_restore_session_state(self);
|
client_restore_session_state(self);
|
||||||
|
|
||||||
|
client_calc_layer(self);
|
||||||
|
|
||||||
{
|
{
|
||||||
Time t = sn_app_started(self->startup_id, self->class);
|
Time t = sn_app_started(self->startup_id, self->class);
|
||||||
if (t) self->user_time = t;
|
if (t) self->user_time = t;
|
||||||
|
@ -461,7 +463,8 @@ void client_manage(Window window)
|
||||||
/* This is focus stealing prevention, if a user_time has been set */
|
/* This is focus stealing prevention, if a user_time has been set */
|
||||||
ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
|
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);
|
self->window, self->user_time, client_last_user_time);
|
||||||
if (!self->user_time || self->user_time >= client_last_user_time)
|
if (!self->user_time || self->user_time >= client_last_user_time ||
|
||||||
|
client_search_focus_parent(self) != NULL)
|
||||||
{
|
{
|
||||||
/* since focus can change the stacking orders, if we focus the
|
/* since focus can change the stacking orders, if we focus the
|
||||||
window then the standard raise it gets is not enough, we need
|
window then the standard raise it gets is not enough, we need
|
||||||
|
@ -1901,8 +1904,6 @@ static void client_change_state(ObClient *self)
|
||||||
netstate[num++] = prop_atoms.ob_wm_state_undecorated;
|
netstate[num++] = prop_atoms.ob_wm_state_undecorated;
|
||||||
PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
|
PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
|
||||||
|
|
||||||
client_calc_layer(self);
|
|
||||||
|
|
||||||
if (self->frame)
|
if (self->frame)
|
||||||
frame_adjust_state(self->frame);
|
frame_adjust_state(self->frame);
|
||||||
}
|
}
|
||||||
|
@ -1968,20 +1969,23 @@ static ObStackingLayer calc_layer(ObClient *self)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
|
static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
|
||||||
ObStackingLayer l, gboolean raised)
|
ObStackingLayer min, gboolean raised)
|
||||||
{
|
{
|
||||||
ObStackingLayer old, own;
|
ObStackingLayer old, own;
|
||||||
GSList *it;
|
GSList *it;
|
||||||
|
|
||||||
old = self->layer;
|
old = self->layer;
|
||||||
own = calc_layer(self);
|
own = calc_layer(self);
|
||||||
self->layer = l > own ? l : own;
|
self->layer = MAX(own, min);
|
||||||
|
|
||||||
|
ob_debug("layer for %s: %d\n", self->title, self->layer);
|
||||||
|
|
||||||
for (it = self->transients; it; it = g_slist_next(it))
|
for (it = self->transients; it; it = g_slist_next(it))
|
||||||
client_calc_layer_recursive(it->data, orig,
|
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 */
|
if (orig->frame) { /* only restack if the original window is managed */
|
||||||
stacking_remove(CLIENT_AS_WINDOW(self));
|
stacking_remove(CLIENT_AS_WINDOW(self));
|
||||||
stacking_add(CLIENT_AS_WINDOW(self));
|
stacking_add(CLIENT_AS_WINDOW(self));
|
||||||
|
@ -1990,17 +1994,16 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
|
||||||
|
|
||||||
void client_calc_layer(ObClient *self)
|
void client_calc_layer(ObClient *self)
|
||||||
{
|
{
|
||||||
ObStackingLayer l;
|
|
||||||
ObClient *orig;
|
ObClient *orig;
|
||||||
|
GSList *it;
|
||||||
|
|
||||||
orig = self;
|
orig = self;
|
||||||
|
|
||||||
/* transients take on the layer of their parents */
|
/* transients take on the layer of their parents */
|
||||||
self = client_search_top_transient(self);
|
it = client_search_top_transients(self);
|
||||||
|
|
||||||
l = calc_layer(self);
|
for (; it; it = g_slist_next(it))
|
||||||
|
client_calc_layer_recursive(it->data, orig, 0, FALSE);
|
||||||
client_calc_layer_recursive(self, orig, l, FALSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean client_should_show(ObClient *self)
|
gboolean client_should_show(ObClient *self)
|
||||||
|
@ -2323,8 +2326,8 @@ void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
|
||||||
self->fullscreen == fs) return; /* already done */
|
self->fullscreen == fs) return; /* already done */
|
||||||
|
|
||||||
self->fullscreen = fs;
|
self->fullscreen = fs;
|
||||||
client_change_state(self); /* change the state hints on the client,
|
client_change_state(self); /* change the state hints on the client */
|
||||||
and adjust out layer/stacking */
|
client_calc_layer(self); /* and adjust out layer/stacking */
|
||||||
|
|
||||||
if (fs) {
|
if (fs) {
|
||||||
if (savearea)
|
if (savearea)
|
||||||
|
@ -2427,11 +2430,13 @@ static void client_iconify_recursive(ObClient *self,
|
||||||
|
|
||||||
void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
|
void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
|
||||||
{
|
{
|
||||||
/* move up the transient chain as far as possible first */
|
GSList *it;
|
||||||
self = client_search_top_transient(self);
|
|
||||||
|
|
||||||
client_iconify_recursive(client_search_top_transient(self),
|
/* move up the transient chain as far as possible first */
|
||||||
iconic, curdesk);
|
it = client_search_top_transients(self);
|
||||||
|
|
||||||
|
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)
|
void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
|
||||||
|
@ -2631,8 +2636,12 @@ void client_set_desktop_recursive(ObClient *self,
|
||||||
|
|
||||||
void client_set_desktop(ObClient *self, guint target, gboolean donthide)
|
void client_set_desktop(ObClient *self, guint target, gboolean donthide)
|
||||||
{
|
{
|
||||||
client_set_desktop_recursive(client_search_top_transient(self),
|
GSList *it;
|
||||||
target, donthide);
|
|
||||||
|
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)
|
ObClient *client_search_modal_child(ObClient *self)
|
||||||
|
@ -2839,16 +2848,14 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
|
||||||
if (demands_attention != self->demands_attention)
|
if (demands_attention != self->demands_attention)
|
||||||
client_hilite(self, demands_attention);
|
client_hilite(self, demands_attention);
|
||||||
|
|
||||||
client_calc_layer(self);
|
|
||||||
client_change_state(self); /* change the hint to reflect these changes */
|
client_change_state(self); /* change the hint to reflect these changes */
|
||||||
}
|
}
|
||||||
|
|
||||||
ObClient *client_focus_target(ObClient *self)
|
ObClient *client_focus_target(ObClient *self)
|
||||||
{
|
{
|
||||||
ObClient *child;
|
ObClient *child = NULL;
|
||||||
|
|
||||||
/* if we have a modal child, then focus it, not us */
|
child = client_search_modal_child(self);
|
||||||
child = client_search_modal_child(client_search_top_transient(self));
|
|
||||||
if (child) return child;
|
if (child) return child;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -3224,13 +3231,17 @@ guint client_monitor(ObClient *self)
|
||||||
return most;
|
return most;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObClient *client_search_top_transient(ObClient *self)
|
GSList *client_search_top_transients(ObClient *self)
|
||||||
{
|
{
|
||||||
/* move up the transient chain as far as possible */
|
GSList *ret = NULL;
|
||||||
if (self->transient_for) {
|
|
||||||
if (self->transient_for != OB_TRAN_GROUP) {
|
/* move up the direct transient chain as far as possible */
|
||||||
return client_search_top_transient(self->transient_for);
|
while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
|
||||||
} else {
|
self = self->transient_for;
|
||||||
|
|
||||||
|
if (!self->transient_for)
|
||||||
|
ret = g_slist_prepend(ret, self);
|
||||||
|
else {
|
||||||
GSList *it;
|
GSList *it;
|
||||||
|
|
||||||
g_assert(self->group);
|
g_assert(self->group);
|
||||||
|
@ -3238,16 +3249,15 @@ ObClient *client_search_top_transient(ObClient *self)
|
||||||
for (it = self->group->members; it; it = g_slist_next(it)) {
|
for (it = self->group->members; it; it = g_slist_next(it)) {
|
||||||
ObClient *c = it->data;
|
ObClient *c = it->data;
|
||||||
|
|
||||||
/* checking transient_for prevents infinate loops! */
|
if (!c->transient_for)
|
||||||
if (c != self && !c->transient_for)
|
ret = g_slist_prepend(ret, c);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
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)
|
ObClient *client_search_focus_parent(ObClient *self)
|
||||||
|
|
|
@ -571,7 +571,11 @@ ObClient *client_search_focus_tree_full(ObClient *self);
|
||||||
*/
|
*/
|
||||||
ObClient *client_search_modal_child(ObClient *self);
|
ObClient *client_search_modal_child(ObClient *self);
|
||||||
|
|
||||||
ObClient *client_search_top_transient(ObClient *self);
|
/*! Returns a list of top-level windows which this is a transient for.
|
||||||
|
It will only contain more than 1 element if the client is transient for its
|
||||||
|
group.
|
||||||
|
*/
|
||||||
|
GSList *client_search_top_transients(ObClient *self);
|
||||||
|
|
||||||
/*! Search for a parent of a client. This only searches up *ONE LEVEL*, and
|
/*! Search for a parent of a client. This only searches up *ONE LEVEL*, and
|
||||||
returns the searched for parent if it is a parent, or NULL if not. */
|
returns the searched for parent if it is a parent, or NULL if not. */
|
||||||
|
|
|
@ -260,11 +260,15 @@ ObClient* focus_fallback_target(ObFocusFallbackType type)
|
||||||
if (!config_focus_follow || config_focus_last)
|
if (!config_focus_follow || config_focus_last)
|
||||||
trans = TRUE;
|
trans = TRUE;
|
||||||
else {
|
else {
|
||||||
if ((target = client_under_pointer()) &&
|
if ((target = client_under_pointer())) {
|
||||||
client_search_transient
|
GSList *sit;
|
||||||
(client_search_top_transient(target), old))
|
|
||||||
{
|
sit = client_search_top_transients(target);
|
||||||
trans = TRUE;
|
for (; sit; sit = g_slist_next(sit))
|
||||||
|
if (client_search_transient(sit->data, old)) {
|
||||||
|
trans = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,8 @@ static void do_lower(GList *wins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GList *pick_windows(ObClient *top, ObClient *selected, gboolean raise)
|
static GList *pick_windows_recur(ObClient *top, ObClient *selected,
|
||||||
|
gboolean raise)
|
||||||
{
|
{
|
||||||
GList *ret = NULL;
|
GList *ret = NULL;
|
||||||
GList *it, *next, *prev;
|
GList *it, *next, *prev;
|
||||||
|
@ -194,21 +195,19 @@ static GList *pick_windows(ObClient *top, ObClient *selected, gboolean raise)
|
||||||
|
|
||||||
if (!c->modal) {
|
if (!c->modal) {
|
||||||
if (!sel_child) {
|
if (!sel_child) {
|
||||||
trans = g_list_concat(trans,
|
trans = g_list_concat
|
||||||
pick_windows(c, selected, raise));
|
(trans, pick_windows_recur(c, selected, raise));
|
||||||
} else {
|
} else {
|
||||||
trans_sel = g_list_concat(trans_sel,
|
trans_sel = g_list_concat
|
||||||
pick_windows(c, selected,
|
(trans_sel, pick_windows_recur(c, selected, raise));
|
||||||
raise));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!sel_child) {
|
if (!sel_child) {
|
||||||
modals = g_list_concat(modals,
|
modals = g_list_concat
|
||||||
pick_windows(c, selected, raise));
|
(modals, pick_windows_recur(c, selected, raise));
|
||||||
} else {
|
} else {
|
||||||
modal_sel = g_list_concat(modal_sel,
|
modal_sel = g_list_concat
|
||||||
pick_windows(c, selected,
|
(modal_sel, pick_windows_recur(c, selected, raise));
|
||||||
raise));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* if we dont have a prev then start back at the beginning,
|
/* if we dont have a prev then start back at the beginning,
|
||||||
|
@ -230,8 +229,8 @@ static GList *pick_windows(ObClient *top, ObClient *selected, gboolean raise)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GList *pick_group_windows(ObClient *top, ObClient *selected,
|
static GList *pick_group_windows_recur(ObClient *top, ObClient *selected,
|
||||||
gboolean raise, gboolean normal)
|
gboolean raise, gboolean normal)
|
||||||
{
|
{
|
||||||
GList *ret = NULL;
|
GList *ret = NULL;
|
||||||
GList *it, *next, *prev;
|
GList *it, *next, *prev;
|
||||||
|
@ -262,8 +261,8 @@ static GList *pick_group_windows(ObClient *top, ObClient *selected,
|
||||||
(normal && t == OB_CLIENT_TYPE_NORMAL)))
|
(normal && t == OB_CLIENT_TYPE_NORMAL)))
|
||||||
{
|
{
|
||||||
ret = g_list_concat(ret,
|
ret = g_list_concat(ret,
|
||||||
pick_windows(sit->data,
|
pick_windows_recur(sit->data,
|
||||||
selected, raise));
|
selected, raise));
|
||||||
/* if we dont have a prev then start back at the beginning,
|
/* if we dont have a prev then start back at the beginning,
|
||||||
otherwise skip back to the prev's next */
|
otherwise skip back to the prev's next */
|
||||||
next = prev ? g_list_next(prev) : stacking_list;
|
next = prev ? g_list_next(prev) : stacking_list;
|
||||||
|
@ -274,18 +273,43 @@ static GList *pick_group_windows(ObClient *top, ObClient *selected,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GList *pick_windows(ObClient *selected, gboolean raise, gboolean group)
|
||||||
|
{
|
||||||
|
GList *it;
|
||||||
|
GSList *top, *top_it;
|
||||||
|
GSList *top_reorder = NULL;
|
||||||
|
GList *ret = NULL;
|
||||||
|
|
||||||
|
top = client_search_top_transients(selected);
|
||||||
|
|
||||||
|
/* go thru stacking list backwords so we can use g_slist_prepend */
|
||||||
|
for (it = g_list_last(stacking_list); it && top;
|
||||||
|
it = g_list_previous(it))
|
||||||
|
if ((top_it = g_slist_find(top, it->data))) {
|
||||||
|
top_reorder = g_slist_prepend(top_reorder, top_it->data);
|
||||||
|
top = g_slist_delete_link(top, top_it);
|
||||||
|
}
|
||||||
|
g_assert(top == NULL);
|
||||||
|
|
||||||
|
for (top_it = top_reorder; top_it; top_it = g_slist_next(top_it))
|
||||||
|
ret = g_list_concat(ret,
|
||||||
|
pick_windows_recur(top_it->data, selected, raise));
|
||||||
|
|
||||||
|
for (top_it = top_reorder; top_it; top_it = g_slist_next(top_it))
|
||||||
|
ret = g_list_concat(ret,
|
||||||
|
pick_group_windows_recur(top_it->data,
|
||||||
|
selected, raise, group));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void stacking_raise(ObWindow *window, gboolean group)
|
void stacking_raise(ObWindow *window, gboolean group)
|
||||||
{
|
{
|
||||||
GList *wins;
|
GList *wins;
|
||||||
|
|
||||||
if (WINDOW_IS_CLIENT(window)) {
|
if (WINDOW_IS_CLIENT(window)) {
|
||||||
ObClient *c;
|
|
||||||
ObClient *selected;
|
ObClient *selected;
|
||||||
selected = WINDOW_AS_CLIENT(window);
|
selected = WINDOW_AS_CLIENT(window);
|
||||||
c = client_search_top_transient(selected);
|
wins = pick_windows(selected, TRUE, group);
|
||||||
wins = pick_windows(c, selected, TRUE);
|
|
||||||
wins = g_list_concat(wins,
|
|
||||||
pick_group_windows(c, selected, TRUE, group));
|
|
||||||
} else {
|
} else {
|
||||||
wins = g_list_append(NULL, window);
|
wins = g_list_append(NULL, window);
|
||||||
stacking_list = g_list_remove(stacking_list, window);
|
stacking_list = g_list_remove(stacking_list, window);
|
||||||
|
@ -299,13 +323,9 @@ void stacking_lower(ObWindow *window, gboolean group)
|
||||||
GList *wins;
|
GList *wins;
|
||||||
|
|
||||||
if (WINDOW_IS_CLIENT(window)) {
|
if (WINDOW_IS_CLIENT(window)) {
|
||||||
ObClient *c;
|
|
||||||
ObClient *selected;
|
ObClient *selected;
|
||||||
selected = WINDOW_AS_CLIENT(window);
|
selected = WINDOW_AS_CLIENT(window);
|
||||||
c = client_search_top_transient(selected);
|
wins = pick_windows(selected, FALSE, group);
|
||||||
wins = pick_windows(c, selected, FALSE);
|
|
||||||
wins = g_list_concat(pick_group_windows(c, selected, FALSE, group),
|
|
||||||
wins);
|
|
||||||
} else {
|
} else {
|
||||||
wins = g_list_append(NULL, window);
|
wins = g_list_append(NULL, window);
|
||||||
stacking_list = g_list_remove(stacking_list, window);
|
stacking_list = g_list_remove(stacking_list, window);
|
||||||
|
@ -341,7 +361,7 @@ void stacking_add_nonintrusive(ObWindow *win)
|
||||||
{
|
{
|
||||||
ObClient *client;
|
ObClient *client;
|
||||||
ObClient *parent = NULL;
|
ObClient *parent = NULL;
|
||||||
GList *it_before = NULL;
|
GList *it_below = NULL;
|
||||||
|
|
||||||
if (!WINDOW_IS_CLIENT(win)) {
|
if (!WINDOW_IS_CLIENT(win)) {
|
||||||
stacking_add(win); /* no special rules for others */
|
stacking_add(win); /* no special rules for others */
|
||||||
|
@ -373,29 +393,38 @@ void stacking_add_nonintrusive(ObWindow *win)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(it_before = g_list_find(stacking_list, parent))) {
|
if (!(it_below = g_list_find(stacking_list, parent))) {
|
||||||
/* no parent to put above, try find the focused client to go
|
/* no parent to put above, try find the focused client to go
|
||||||
under */
|
under */
|
||||||
if (focus_client && focus_client->layer == client->layer) {
|
if (focus_client && focus_client->layer == client->layer) {
|
||||||
if ((it_before = g_list_find(stacking_list, focus_client)))
|
if ((it_below = g_list_find(stacking_list, focus_client)))
|
||||||
it_before = it_before->next;
|
it_below = it_below->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!it_before) {
|
if (!it_below) {
|
||||||
/* out of ideas, just add it normally... */
|
/* out of ideas, just add it normally... */
|
||||||
stacking_add(win);
|
stacking_add(win);
|
||||||
} else {
|
} else {
|
||||||
GList *it;
|
|
||||||
|
|
||||||
/* make sure it's not in the wrong layer though ! */
|
/* make sure it's not in the wrong layer though ! */
|
||||||
while (it_before && client->layer < ((ObClient*)it_before->data)->layer)
|
for (; it_below; it_below = g_list_next(it_below))
|
||||||
it_before = g_list_next(it_before);
|
{
|
||||||
while (it_before != stacking_list &&
|
/* stop when the window is not in a higher layer than the window
|
||||||
client->layer > ((ObClient*)g_list_previous(it_before)->data)->layer)
|
it is going above (it_below) */
|
||||||
it_before = g_list_previous(it_before);
|
if (client->layer >= window_layer(it_below->data))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (; it_below != stacking_list;
|
||||||
|
it_below = g_list_previous(it_below))
|
||||||
|
{
|
||||||
|
/* stop when the window is not in a lower layer than the
|
||||||
|
window it is going under (it_above) */
|
||||||
|
GList *it_above = g_list_previous(it_below);
|
||||||
|
if (client->layer <= window_layer(it_above->data))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
GList *wins = g_list_append(NULL, win);
|
GList *wins = g_list_append(NULL, win);
|
||||||
do_restack(wins, it_before);
|
do_restack(wins, it_below);
|
||||||
g_list_free(wins);
|
g_list_free(wins);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue