Skip to content

Commit

Permalink
Add relative outputs to wlr_output_layout
Browse files Browse the repository at this point in the history
An output that is made relative to another output will appear on one of
the borders of the parent output. Which border the outputs attaches to
can be configured. An exception to this is the SAME_AS configuration.
In this case child output is positioned at the same location as the
parent output. When the parent output changes position, the child
oututs will follow in order to maintain teh same relative position.

Relative outputs may overlap depending on the configuration. While the
original issue had detection of overlapping outputs as a feature, this
better fits in the compositor, since the library would have to make
assumptions about the wanted behavior. An implementation for rootston
can be found in later commits.

When an ouput is removed and is a parent of another output, the child
output is fixed at its current position. If the compositor requires
different behaviour it can do this in response to the destroy signal.

Fixes swaywm#550
  • Loading branch information
VincentVanlaer committed Jun 2, 2018
1 parent d3670df commit 3c62f4b
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 37 deletions.
40 changes: 33 additions & 7 deletions include/wlr/types/wlr_output_layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,24 @@ struct wlr_output_layout {

struct wlr_output_layout_output_state;

enum wlr_output_layout_output_configuration {
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED,
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_AUTO,
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_LEFT_OF,
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_RIGHT_OF,
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_BELOW,
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_ABOVE,
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_SAME_AS,
};

struct wlr_output_layout_output {
struct wlr_output *output;
int x, y;
enum wlr_output_layout_output_configuration configuration;
struct wlr_output_layout_output *reference;

struct wl_list link;

struct wlr_output_layout_output_state *state;

struct {
Expand Down Expand Up @@ -88,16 +102,28 @@ struct wlr_box *wlr_output_layout_get_box(
struct wlr_output_layout *layout, struct wlr_output *reference);

/**
* Add an auto configured output to the layout. This will place the output in a
* sensible location in the layout. The coordinates of the output in the layout
* may adjust dynamically when the layout changes. If the output is already in
* the layout, it will become auto configured. If the position of the output is
* set such as with `wlr_output_layout_move()`, the output will become manually
* configured.
*/
* Add an auto configured output to the layout. This will place the output in a
* sensible location in the layout. The coordinates of the output in the layout
* may adjust dynamically when the layout changes. If the output is already in
* the layout, it will become auto configured. Relative layouts are not supported
* with this kind of output.
*/
void wlr_output_layout_add_auto(struct wlr_output_layout *layout,
struct wlr_output *output);


/**
* Add an auto configured output to the layout relative to another output.
* This will place the relative output next to the border of the reference
* output. The output layout will adjust dynamically to keep the relative
* output in this position when the absolute output changes coordinates.
* If the position of the relative output is set to absolute coordinates,
* it will become manually configured.
*/
void wlr_output_layout_add_relative(struct wlr_output_layout *layout,
struct wlr_output *output, struct wlr_output *reference,
enum wlr_output_layout_output_configuration configuration);

/**
* Get the output closest to the center of the layout extents.
*/
Expand Down
205 changes: 175 additions & 30 deletions types/wlr_output_layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ struct wlr_output_layout_output_state {
struct wlr_output_layout_output *l_output;

struct wlr_box _box; // should never be read directly, use the getter
bool auto_configured;

struct wl_listener mode;
struct wl_listener scale;
Expand Down Expand Up @@ -48,6 +47,17 @@ struct wlr_output_layout *wlr_output_layout_create(void) {
static void output_layout_output_destroy(
struct wlr_output_layout_output *l_output) {
wlr_signal_emit_safe(&l_output->events.destroy, l_output);

struct wlr_output_layout_output *l_output_rel;

wl_list_for_each(l_output_rel, &l_output->state->layout->outputs, link) {
if (l_output_rel->reference == l_output) {
l_output_rel->reference = NULL;
l_output_rel->configuration =
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED;
}
}

wlr_output_destroy_global(l_output->output);
wl_list_remove(&l_output->state->mode.link);
wl_list_remove(&l_output->state->scale.link);
Expand Down Expand Up @@ -93,38 +103,68 @@ static struct wlr_box *output_layout_output_get_box(
* the rightmost output in the layout in a horizontal line.
*/
static void output_layout_reconfigure(struct wlr_output_layout *layout) {
int max_x = INT_MIN;
int max_x_y = INT_MIN; // y value for the max_x output

// find the rightmost x coordinate occupied by a manually configured output
// in the layout
struct wlr_output_layout_output *l_output;

wl_list_for_each(l_output, &layout->outputs, link) {
if (l_output->state->auto_configured) {
continue;
}
int max_x = INT_MIN;
int max_x_y = INT_MIN;

struct wlr_box *box = output_layout_output_get_box(l_output);
if (box->x + box->width > max_x) {
max_x = box->x + box->width;
max_x_y = box->y;
}
}

if (max_x == INT_MIN) {
// there are no manually configured outputs
max_x = 0;
max_x_y = 0;
}
switch(l_output->configuration) {
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED:
break;
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_LEFT_OF: {
struct wlr_box *ref_box =
output_layout_output_get_box(l_output->reference);
l_output->x = ref_box->x - box->width;
l_output->y = ref_box->y;
break;
}
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_RIGHT_OF: {
struct wlr_box *ref_box =
output_layout_output_get_box(l_output->reference);
l_output->x = ref_box->x + ref_box->width;
l_output->y = ref_box->y;
break;
}
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_BELOW: {
struct wlr_box *ref_box =
output_layout_output_get_box(l_output->reference);
l_output->x = ref_box->x;
l_output->y = ref_box->y + box->height;
break;
}
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_ABOVE: {
struct wlr_box *ref_box =
output_layout_output_get_box(l_output->reference);
l_output->x = ref_box->x;
l_output->y = ref_box->y - ref_box->height;
break;
}
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_SAME_AS: {
struct wlr_box *ref_box =
output_layout_output_get_box(l_output->reference);
l_output->x = ref_box->x;
l_output->y = ref_box->y;
break;
}
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_AUTO: {
l_output->x = max_x;
l_output->y = max_x_y;
break;
}
}

wl_list_for_each(l_output, &layout->outputs, link) {
if (!l_output->state->auto_configured) {
continue;
if (max_x < l_output->x + box->width) {
max_x = l_output->x + box->width;
max_x_y = l_output->y;
}
struct wlr_box *box = output_layout_output_get_box(l_output);
l_output->x = max_x;
l_output->y = max_x_y;
max_x += box->width;

struct wlr_output *output = l_output->output;

wlr_log(L_DEBUG, "%s - %dx%d+%d+%d", output->name, output->width, output->height, l_output->x, l_output->y);
}

wl_list_for_each(l_output, &layout->outputs, link) {
Expand Down Expand Up @@ -176,7 +216,6 @@ static struct wlr_output_layout_output *output_layout_output_create(
l_output->state->layout = layout;
l_output->output = output;
wl_signal_init(&l_output->events.destroy);
wl_list_insert(&layout->outputs, &l_output->link);

wl_signal_add(&output->events.mode, &l_output->state->mode);
l_output->state->mode.notify = handle_output_mode;
Expand All @@ -190,6 +229,45 @@ static struct wlr_output_layout_output *output_layout_output_create(
return l_output;
}

// Adapted from wl_list_for_each
// Starts with wlr_ to avoid name conflicts
#define wlr_wl_list_for_each_offset(pos, head, member, offset) \
for (pos = wl_container_of((offset)->member.next, pos, member); \
&pos->member != (head); \
pos = wl_container_of(pos->member.next, pos, member))

void relocate_output_with_children(struct wlr_output_layout *layout,
struct wlr_output_layout_output *output,
struct wl_list *ref) {

struct wlr_output_layout_output *child_output;
struct wlr_output_layout_output *parent_output =
output;

if (output->link.prev == ref) {
return;
}

wlr_wl_list_for_each_offset(child_output, &layout->outputs, link, output) {
while (child_output->reference != parent_output) {
if (parent_output == output) {
goto move_outputs;
}
parent_output = parent_output->reference;
}
}

move_outputs:
ref->next->prev = child_output->link.prev; // A->prev = B
child_output->link.prev->next = ref->next; // B->next = A
child_output->link.prev = output->link.prev;
output->link.prev->next = &child_output->link;
output->link.prev = ref;
ref->next = &output->link;
}

#undef wlr_wl_list_for_each_offset

void wlr_output_layout_add(struct wlr_output_layout *layout,
struct wlr_output *output, int lx, int ly) {
struct wlr_output_layout_output *l_output =
Expand All @@ -200,10 +278,15 @@ void wlr_output_layout_add(struct wlr_output_layout *layout,
wlr_log(L_ERROR, "Failed to create wlr_output_layout_output");
return;
}
wl_list_insert(&layout->outputs, &l_output->link);
} else if (l_output->configuration !=
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED) {
relocate_output_with_children(layout, l_output, &layout->outputs);
}
l_output->x = lx;
l_output->y = ly;
l_output->state->auto_configured = false;
l_output->configuration = WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED;

output_layout_reconfigure(layout);
wlr_output_create_global(output);
wlr_signal_emit_safe(&layout->events.add, l_output);
Expand Down Expand Up @@ -277,7 +360,7 @@ void wlr_output_layout_move(struct wlr_output_layout *layout,
if (l_output) {
l_output->x = lx;
l_output->y = ly;
l_output->state->auto_configured = false;
l_output->configuration = WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED;
output_layout_reconfigure(layout);
} else {
wlr_log(L_ERROR, "output not found in this layout: %s", output->name);
Expand Down Expand Up @@ -405,9 +488,72 @@ void wlr_output_layout_add_auto(struct wlr_output_layout *layout,
wlr_log(L_ERROR, "Failed to create wlr_output_layout_output");
return;
}
} else {
struct wlr_output_layout_output *l_output_rel;

wl_list_for_each(l_output_rel, &layout->outputs, link) {
if (l_output_rel->reference == l_output) {
l_output_rel->reference = NULL;
l_output_rel->configuration =
WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED;
}
}
wl_list_remove(&l_output->link);
}

l_output->state->auto_configured = true;
wl_list_insert(layout->outputs.prev, &l_output->link); // Insert at end
output_layout_reconfigure(layout);
wlr_output_create_global(output);
wlr_signal_emit_safe(&layout->events.add, l_output);
}

void wlr_output_layout_add_relative(struct wlr_output_layout *layout,
struct wlr_output *output, struct wlr_output *reference,
enum wlr_output_layout_output_configuration configuration) {
assert(layout && reference);

if (output == reference) {
wlr_log(L_ERROR, "Cannot self reference outputs");
return;
}

struct wlr_output_layout_output *l_output_reference =
wlr_output_layout_get(layout, reference);

if (!l_output_reference) {
wlr_output_layout_add_auto(layout, reference);
l_output_reference = wlr_output_layout_get(layout, reference);
}
struct wlr_output_layout_output *l_output =
wlr_output_layout_get(layout, output);
if (!l_output) {
l_output = output_layout_output_create(layout, output);
if (!l_output) {
wlr_log(L_ERROR, "Failed to create wlr_output_layout_output");
return;
}
wl_list_insert(&l_output_reference->link, &l_output->link);
} else {
// Prevent loops
struct wlr_output_layout_output *parent = l_output_reference;

while (parent) {
if (parent->reference == l_output) {
parent->configuration = WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED;
parent->reference = NULL;
relocate_output_with_children(layout, parent, &layout->outputs);
break;
}

parent = parent->reference;
}

relocate_output_with_children(layout, l_output, &l_output_reference->link);
}

l_output->configuration = configuration;
l_output->reference = l_output_reference;

output_layout_reconfigure(layout);
wlr_output_create_global(output);
wlr_signal_emit_safe(&layout->events.add, l_output);
Expand All @@ -430,7 +576,6 @@ struct wlr_output *wlr_output_layout_get_center_output(
return wlr_output_layout_output_at(layout, dest_x, dest_y);
}


struct wlr_output *wlr_output_layout_adjacent_output(
struct wlr_output_layout *layout, enum wlr_direction direction,
struct wlr_output *reference, double ref_lx, double ref_ly) {
Expand Down

0 comments on commit 3c62f4b

Please sign in to comment.