mirror of
https://github.com/brl/mutter.git
synced 2024-12-25 12:32:05 +00:00
replace the old apply_constraints with wacky new approach involving
2002-10-18 Havoc Pennington <hp@pobox.com> * src/stack.c (constrain_stacking): replace the old apply_constraints with wacky new approach involving graphing all the constraints then walking the graph. Fixes #94876 and probably other stacking bugs as well, thanks to Arvind for tracking down the issue. (compute_layer): add FIXME and reference to bug #96140
This commit is contained in:
parent
5c5de1c6b3
commit
370982b812
10
ChangeLog
10
ChangeLog
@ -1,3 +1,13 @@
|
||||
2002-10-18 Havoc Pennington <hp@pobox.com>
|
||||
|
||||
* src/stack.c (constrain_stacking): replace the old
|
||||
apply_constraints with wacky new approach involving graphing all
|
||||
the constraints then walking the graph. Fixes #94876 and probably
|
||||
other stacking bugs as well, thanks to Arvind for tracking down
|
||||
the issue.
|
||||
|
||||
(compute_layer): add FIXME and reference to bug #96140
|
||||
|
||||
2002-10-17 Havoc Pennington <hp@redhat.com>
|
||||
|
||||
* src/stack.c (apply_constraints): don't place
|
||||
|
336
src/stack.c
336
src/stack.c
@ -358,6 +358,16 @@ compute_layer (MetaWindow *window)
|
||||
* and a dialog transient for the normal window; you don't want the dialog
|
||||
* above the dock if it wouldn't normally be.
|
||||
*/
|
||||
|
||||
/* FIXME when promoting a window here,
|
||||
* it's necessary to promote its transient children
|
||||
* (or other windows constrained to be above it)
|
||||
* as well, but we aren't handling that, and it's
|
||||
* somewhat hard to fix.
|
||||
*
|
||||
* http://bugzilla.gnome.org/show_bug.cgi?id=96140
|
||||
*/
|
||||
|
||||
MetaStackLayer group_max;
|
||||
|
||||
group_max = get_maximum_layer_in_group (window);
|
||||
@ -399,40 +409,125 @@ compare_window_position (void *a,
|
||||
else
|
||||
return 0; /* not reached */
|
||||
}
|
||||
|
||||
/*
|
||||
* Stacking constraints
|
||||
*
|
||||
* Assume constraints of the form "AB" meaning "window A must be
|
||||
* below window B"
|
||||
*
|
||||
* If we have windows stacked from bottom to top
|
||||
* "ABC" then raise A we get "BCA". Say C is
|
||||
* transient for B is transient for A. So
|
||||
* we have constraints AB and BC.
|
||||
*
|
||||
* After raising A, we need to reapply the constraints.
|
||||
* If we do this by raising one window at a time -
|
||||
*
|
||||
* start: BCA
|
||||
* apply AB: CAB
|
||||
* apply BC: ABC
|
||||
*
|
||||
* but apply constraints in the wrong order and it breaks:
|
||||
*
|
||||
* start: BCA
|
||||
* apply BC: BCA
|
||||
* apply AB: CAB
|
||||
*
|
||||
* We make a directed graph of the constraints by linking
|
||||
* from "above windows" to "below windows as follows:
|
||||
*
|
||||
* AB -> BC -> CD
|
||||
* \
|
||||
* CE
|
||||
*
|
||||
* If we then walk that graph and apply the constraints in the order
|
||||
* that they appear, we will apply them correctly. Note that the
|
||||
* graph MAY have cycles, so we have to guard against that.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct Constraint Constraint;
|
||||
|
||||
struct Constraint
|
||||
{
|
||||
MetaWindow *above;
|
||||
MetaWindow *below;
|
||||
|
||||
/* used to keep the constraint in the
|
||||
* list of constraints for window "below"
|
||||
*/
|
||||
Constraint *next;
|
||||
|
||||
/* used to create the graph. */
|
||||
GSList *next_nodes;
|
||||
|
||||
/* constraint has been applied, used
|
||||
* to detect cycles.
|
||||
*/
|
||||
unsigned int applied : 1;
|
||||
|
||||
/* constraint has a previous node in the graph,
|
||||
* used to find places to start in the graph.
|
||||
* (I think this also has the side effect
|
||||
* of preventing cycles, since cycles will
|
||||
* have no starting point - so maybe
|
||||
* the "applied" flag isn't needed.)
|
||||
*/
|
||||
unsigned int has_prev : 1;
|
||||
};
|
||||
|
||||
/* We index the array of constraints by window
|
||||
* stack positions, just because the stack
|
||||
* positions are a convenient index.
|
||||
*/
|
||||
static void
|
||||
ensure_above (MetaWindow *above,
|
||||
MetaWindow *below)
|
||||
{
|
||||
if (above->stack_position < below->stack_position)
|
||||
add_constraint (Constraint **constraints,
|
||||
MetaWindow *above,
|
||||
MetaWindow *below)
|
||||
{
|
||||
Constraint *c;
|
||||
|
||||
/* check if constraint is a duplicate */
|
||||
c = constraints[below->stack_position];
|
||||
while (c != NULL)
|
||||
{
|
||||
/* move above to below->stack_position bumping below down the stack */
|
||||
meta_window_set_stack_position (above, below->stack_position);
|
||||
g_assert (below->stack_position + 1 == above->stack_position);
|
||||
if (c->above == above)
|
||||
return;
|
||||
c = c->next;
|
||||
}
|
||||
|
||||
meta_topic (META_DEBUG_STACK, "Above pos %d > below pos %d\n",
|
||||
above->stack_position, below->stack_position);
|
||||
/* if not, add the constraint */
|
||||
c = g_new (Constraint, 1);
|
||||
c->above = above;
|
||||
c->below = below;
|
||||
c->next = constraints[below->stack_position];
|
||||
c->next_nodes = NULL;
|
||||
c->applied = FALSE;
|
||||
c->has_prev = FALSE;
|
||||
|
||||
constraints[below->stack_position] = c;
|
||||
}
|
||||
|
||||
#define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w) \
|
||||
((w->xtransient_for == None || \
|
||||
w->transient_parent_is_root_window) && \
|
||||
#define WINDOW_HAS_TRANSIENT_TYPE(w) \
|
||||
(w->type == META_WINDOW_DIALOG || \
|
||||
w->type == META_WINDOW_MODAL_DIALOG || \
|
||||
w->type == META_WINDOW_TOOLBAR || \
|
||||
w->type == META_WINDOW_MENU || \
|
||||
w->type == META_WINDOW_UTILITY))
|
||||
w->type == META_WINDOW_UTILITY)
|
||||
|
||||
#define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w) \
|
||||
((w->xtransient_for == None || \
|
||||
w->transient_parent_is_root_window) && \
|
||||
WINDOW_HAS_TRANSIENT_TYPE (w))
|
||||
|
||||
static void
|
||||
apply_constraints (GList *list)
|
||||
create_constraints (Constraint **constraints,
|
||||
GList *windows)
|
||||
{
|
||||
GList *tmp;
|
||||
|
||||
/* This algorithm could stand to be a bit less
|
||||
* quadratic
|
||||
*/
|
||||
tmp = list;
|
||||
tmp = windows;
|
||||
while (tmp != NULL)
|
||||
{
|
||||
MetaWindow *w = tmp->data;
|
||||
@ -455,13 +550,21 @@ apply_constraints (GList *list)
|
||||
while (tmp2 != NULL)
|
||||
{
|
||||
MetaWindow *group_window = tmp2->data;
|
||||
|
||||
|
||||
#if 0
|
||||
/* old way of doing it */
|
||||
if (!(meta_window_is_ancestor_of_transient (w, group_window)) &&
|
||||
!WINDOW_TRANSIENT_FOR_WHOLE_GROUP (group_window))
|
||||
!WINDOW_TRANSIENT_FOR_WHOLE_GROUP (group_window)) /* note */;/*note*/
|
||||
#else
|
||||
/* better way I think, so transient-for-group are constrained
|
||||
* only above non-transient-type windows in their group
|
||||
*/
|
||||
if (!WINDOW_HAS_TRANSIENT_TYPE (group_window))
|
||||
#endif
|
||||
{
|
||||
meta_topic (META_DEBUG_STACK, "Stacking %s above %s as it's a dialog transient for its group\n",
|
||||
meta_topic (META_DEBUG_STACK, "Constraining %s above %s as it's transient for its group\n",
|
||||
w->desc, group_window->desc);
|
||||
ensure_above (w, group_window);
|
||||
add_constraint (constraints, w, group_window);
|
||||
}
|
||||
|
||||
tmp2 = tmp2->next;
|
||||
@ -479,9 +582,9 @@ apply_constraints (GList *list)
|
||||
|
||||
if (parent)
|
||||
{
|
||||
meta_topic (META_DEBUG_STACK, "Stacking %s above %s due to transiency\n",
|
||||
meta_topic (META_DEBUG_STACK, "Constraining %s above %s due to transiency\n",
|
||||
w->desc, parent->desc);
|
||||
ensure_above (w, parent);
|
||||
add_constraint (constraints, w, parent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -489,6 +592,181 @@ apply_constraints (GList *list)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
graph_constraints (Constraint **constraints,
|
||||
int n_constraints)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while (i < n_constraints)
|
||||
{
|
||||
Constraint *c;
|
||||
|
||||
/* If we have "A below B" and "B below C" then AB -> BC so we
|
||||
* add BC to next_nodes in AB.
|
||||
*/
|
||||
|
||||
c = constraints[i];
|
||||
while (c != NULL)
|
||||
{
|
||||
Constraint *n;
|
||||
|
||||
g_assert (c->below->stack_position == i);
|
||||
|
||||
/* Constraints where ->above is below are our
|
||||
* next_nodes and we are their previous
|
||||
*/
|
||||
n = constraints[c->above->stack_position];
|
||||
while (n != NULL)
|
||||
{
|
||||
c->next_nodes = g_slist_prepend (c->next_nodes,
|
||||
n);
|
||||
/* c is a previous node of n */
|
||||
n->has_prev = TRUE;
|
||||
|
||||
n = n->next;
|
||||
}
|
||||
|
||||
c = c->next;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_constraints (Constraint **constraints,
|
||||
int n_constraints)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while (i < n_constraints)
|
||||
{
|
||||
Constraint *c;
|
||||
|
||||
c = constraints[i];
|
||||
while (c != NULL)
|
||||
{
|
||||
Constraint *next = c->next;
|
||||
|
||||
g_slist_free (c->next_nodes);
|
||||
|
||||
g_free (c);
|
||||
|
||||
c = next;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_above (MetaWindow *above,
|
||||
MetaWindow *below)
|
||||
{
|
||||
if (above->stack_position < below->stack_position)
|
||||
{
|
||||
/* move above to below->stack_position bumping below down the stack */
|
||||
meta_window_set_stack_position (above, below->stack_position);
|
||||
g_assert (below->stack_position + 1 == above->stack_position);
|
||||
}
|
||||
|
||||
meta_topic (META_DEBUG_STACK, "%s above at %d > %s below at %d\n",
|
||||
above->desc, above->stack_position,
|
||||
below->desc, below->stack_position);
|
||||
}
|
||||
|
||||
static void
|
||||
traverse_constraint (Constraint *c)
|
||||
{
|
||||
GSList *tmp;
|
||||
|
||||
if (c->applied)
|
||||
return;
|
||||
|
||||
ensure_above (c->above, c->below);
|
||||
c->applied = TRUE;
|
||||
|
||||
tmp = c->next_nodes;
|
||||
while (tmp != NULL)
|
||||
{
|
||||
traverse_constraint (tmp->data);
|
||||
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
apply_constraints (Constraint **constraints,
|
||||
int n_constraints)
|
||||
{
|
||||
GSList *heads;
|
||||
GSList *tmp;
|
||||
int i;
|
||||
|
||||
/* List all heads in an ordered constraint chain */
|
||||
heads = NULL;
|
||||
i = 0;
|
||||
while (i < n_constraints)
|
||||
{
|
||||
Constraint *c;
|
||||
|
||||
c = constraints[i];
|
||||
while (c != NULL)
|
||||
{
|
||||
if (!c->has_prev)
|
||||
heads = g_slist_prepend (heads, c);
|
||||
|
||||
c = c->next;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
/* Now traverse the chain and apply constraints */
|
||||
tmp = heads;
|
||||
while (tmp != NULL)
|
||||
{
|
||||
Constraint *c = tmp->data;
|
||||
|
||||
traverse_constraint (c);
|
||||
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
g_slist_free (heads);
|
||||
}
|
||||
|
||||
static void
|
||||
constrain_stacking (MetaStack *stack)
|
||||
{
|
||||
Constraint **constraints;
|
||||
|
||||
/* It'd be nice if this were all faster, probably */
|
||||
|
||||
if (!stack->need_constrain)
|
||||
return;
|
||||
|
||||
meta_topic (META_DEBUG_STACK,
|
||||
"Reapplying constraints\n");
|
||||
|
||||
constraints = g_new0 (Constraint*,
|
||||
stack->n_positions);
|
||||
|
||||
create_constraints (constraints, stack->sorted);
|
||||
|
||||
graph_constraints (constraints, stack->n_positions);
|
||||
|
||||
apply_constraints (constraints, stack->n_positions);
|
||||
|
||||
free_constraints (constraints, stack->n_positions);
|
||||
g_free (constraints);
|
||||
|
||||
stack->need_constrain = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_stack_ensure_sorted (MetaStack *stack)
|
||||
{
|
||||
@ -621,15 +899,7 @@ meta_stack_ensure_sorted (MetaStack *stack)
|
||||
}
|
||||
|
||||
/* Update stack_position to reflect transiency constraints */
|
||||
if (stack->need_constrain)
|
||||
{
|
||||
meta_topic (META_DEBUG_STACK,
|
||||
"Reapplying constraints\n");
|
||||
|
||||
apply_constraints (stack->sorted);
|
||||
|
||||
stack->need_constrain = FALSE;
|
||||
}
|
||||
constrain_stacking (stack);
|
||||
|
||||
/* Sort stack->sorted with layers having priority over stack_position
|
||||
*/
|
||||
|
@ -227,11 +227,12 @@ make_dialog (GtkWidget *parent,
|
||||
GtkWidget *dialog;
|
||||
char *str;
|
||||
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
|
||||
dialog = gtk_message_dialog_new (parent ? GTK_WINDOW (parent) : NULL,
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_INFO,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Here is a dialog %d",
|
||||
parent ? "Here is a dialog %d" :
|
||||
"Here is a dialog %d with no transient parent",
|
||||
depth);
|
||||
|
||||
str = g_strdup_printf ("%d dialog", depth);
|
||||
@ -306,21 +307,7 @@ no_parent_dialog_cb (gpointer callback_data,
|
||||
guint callback_action,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new (NULL,
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_INFO,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Here is a dialog with no transient parent");
|
||||
|
||||
/* Close dialog on user response */
|
||||
g_signal_connect (G_OBJECT (dialog),
|
||||
"response",
|
||||
G_CALLBACK (gtk_widget_destroy),
|
||||
NULL);
|
||||
|
||||
gtk_widget_show (dialog);
|
||||
make_dialog (NULL, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user