Skip to content
This repository has been archived by the owner on Nov 1, 2021. It is now read-only.

Add relative outputs to wlr_output_layout #1002

Closed
wants to merge 12 commits into from

Conversation

VincentVanlaer
Copy link
Contributor

@VincentVanlaer VincentVanlaer commented May 26, 2018

Introduces a way for compositors to create layouts with outputs that are relative to other outputs.
These relative outputs will update when the position or size of the reference ouput changes.
A rootston implementation is provided which

  • allows relative outputs to be configured,
  • makes sure that gaps do not occur when not all configured outputs are connected,
  • makes sure that overlaps do not occur when outputs without configuration are connected.

See #550.

API changes:

  • removed wlr_output_layout_move
  • wlr_output_layout_add_auto: changed behavior, ouputs added through this function do not adjust dynamically anymore.

cc @Ongy @Timidger

@Ongy
Copy link

Ongy commented May 26, 2018

wlr_output_layout_add_auto: changed behavior, ouputs added through this function do not adjust dynamically anymore.

I'm really not a fan of this.
This may lead to unconnected outputs at some point, and with the current cursor logic, there's no way to move it from one such output to the other.

BUT I started to implement something in this general fashion for waymonad before, and dropped it because I couldn't find a statisfactory solution. There's a lot of things that can go wrong, and some tradeoffs have to be made in the end.

I'd prefer if the _auto added outputs didn't get a fixed x/y like this, IMO it's better to default their internal relative location, so they are right of the output that currently provides the right most edge.
This also requires a bit of defaulting/collision detection if it's supposed to work at all times, but won't split the desktop.

@VincentVanlaer
Copy link
Contributor Author

@Ongy The idea was that using add_auto is fine for examples and just getting things on screen, but that it isn't really suited for compositors in general. See the discussion in #550 for the discussion on this. With that in mind I opted for the implementation that would require the least work. I prefer to see wlr_output_layout as something that helps the compositor with the calculations and integrates well with the rest of the library. Any "policies" surrounding what to do when outputs disappear, when gaps occur, ... should in my opinion live in the compositor.

@Ongy
Copy link

Ongy commented May 26, 2018

The latest response you got there is more on my line than yours here.
At the very least there should be code to warp the cursor if it reaches the edge (rootston/cursor.c I guess) if you want to follow acrisci's advice there.

But the _auto was good as it was for me, add and forget.
While I generally like the idea of having relative output layouts, but your changes make the wlr_output_layout automation useless for my needs.
It's not add-and-forget for _auto. I still need to manage my outputs by hand (as library consumer, not necessarily enduser). This is a regression which (IMO) outweighs the current benefit.

As is, I'm against these changes.

@VincentVanlaer
Copy link
Contributor Author

I'm not necessarily against having add_auto attach to right of the rightmost output. The only reason I implemented it in this way was because I saw not much use for it in a full-fledged compositor, due to its shortcomings (order dependent hard to deal with if one wants to attach outputs to those auto outputs). But I can understand that it can have its use, and I will try to create a decent implementation for this.

Regarding deadzones, while I do think it is useful to deal with them, I don't think cursor warping is something that should be in this PR, especially with the behavior of the previous paragraph.

@Ongy
Copy link

Ongy commented May 26, 2018

I always considerd the _auto limited. And I do agree, that it won't be the end-all be-all for most compositors.
But I think that the add-and-forget property of it is its strong suite.

And yes, if they layout prevents detached outputs, the warping over deadzones won't be necessary at all.

@VincentVanlaer
Copy link
Contributor Author

Reverted add_auto behavior.
While improving on the rootston behavior in order to prevent gaps, I found that there were a few things missing in wlr_output_layoutin order to make it reasonably easy and efficient to extend.

  • All functions for interfacing with the wlr_output_layout use wlr_output and do a lookup in the list of wlr_output_layout_output.
  • The implementation in rootston in this PR requires an explicit ordering of wlr_output_layout_output. Make this part of the API.
  • User data in wlr_output_layout_output, like in wlr_output_layout.
  • Instead of re-configuring the layout every time an output is added/removed/changed, which can be inefficient (see roots_layout_reflow), we could require the user to explicitly reconfigure the layout., throug something like wlr_output_layout_reconfigure. This also makes sure that intermediate states do not get transmitted to xdg-outputclients.
  • If the layout fires a signal when wlr_output_layout_reconfigure is called, it makes custom reflows after mode changes or transform easier to deal with. In the case of rootston, reflowing after modeset currently only works because the handler in wlr_output_layout_output gets called before the handler in roots_output.

Unless there's heavy opposition to this, I will make this into a separate issue.

@acrisci
Copy link

acrisci commented May 28, 2018

I'd like to keep the auto layout function as it is as I said in that issue because it's really useful for creating default layouts.

@VincentVanlaer
Copy link
Contributor Author

It should behave the same in the last version of this PR

@@ -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) \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather write a little less efficient but more readable code

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather, I'm not quite sure why you define this here. It's only used once until the #undef, so it can just be inline.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a remnant from when I used it in two functions

@@ -270,20 +348,6 @@ struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout,
return NULL;
}

void wlr_output_layout_move(struct wlr_output_layout *layout,
struct wlr_output *output, int lx, int ly) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't remove this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wlr_output_layout_add can do the same thing (and does so in master), so what would be the reason for keeping this around?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might make sense. Make an issue for it. It's an unrelated breaking public API change so it needs some discussion and visibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this might also depend on what is decided in #1013.

@acrisci
Copy link

acrisci commented May 28, 2018

User data in wlr_output_layout_output, like in wlr_output_layout.

I'd rather just make the wlr_output_layout_output private. #1013


/**
* Add an auto configured output to the layout relative to another output.
* This will place the relative output next to the border of the absolute
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/absolute/reference/

* This will place the relative output next to the border of the absolute
* output. The output layout will adjust dynamically to keep the relative
* output in this position when the absolute output changes coordinates.
* If either output is not in the output layout, it will be added. If the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will be added

Hmm?

free(lrc);
}
free(lc->name);
free(kc);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kc?

desktop->layout = wlr_output_layout_create();
wlr_xdg_output_manager_create(server->wl_display, desktop->layout);
struct roots_layout_config *default_layout;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could add a little assert(!wl_list_empty(&config->layouts)) here

struct roots_layout_config *config =
wl_container_of(next, desktop->layout->current_config, link);

wlr_log(L_DEBUG, "Switching to layout %s", config->name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of different output layouts for rootston.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more of a development feature. It makes it easy to test multiple layouts without having to restart rootston and helps to test situations where the layout is changing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of it but also not opposed to it, but either way it belongs in a separate PR imo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just this specific part of the rootston implementation or the whole rootston implementation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The multiple layouts and switching at runtime part

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
In order to properly manage relative layouts in rootston, the
'wlr_output_layout' has been encapsulated in 'roots_layout'. This is
primarily to deal with outputs not being available to reference. This
lives in rootston since compositors might want to deal differently with
missing outputs.

The behavior of rootston in the case of missing outputs, is to mark
rules whose relative output is not available as unconfigured. When an
output is unconfigured it will be placed to the right of any other outputs.
This way, no gaps will occur in the layout, unless the user
specifically configures them with fixed output.
@VincentVanlaer
Copy link
Contributor Author

Removed multiple layouts in rootston.

@acrisci seems like you have missed my comment: #1002 (comment)

# Output position configuration
[layout]
# Put the output with name VGA-1 at (0,0)
VGA-1 = fixed 0 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible to change this to 0,0 (one argument)?

case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED:
break;
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_LEFT_OF: {
struct wlr_box *ref_box =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation is a bit weird here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's because of brackets? If you prefer, use case THING:;. But indenting with two levels is just weird.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of that indentation either, but I'll just move the ref_box declaration above the switch statement.

break;
case WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_LEFT_OF: {
struct wlr_box *ref_box =
output_layout_output_get_box(l_output->reference);

This comment was marked as resolved.

wl_container_of((child_output)->link.next, output, link)) {
while (child_output->reference != parent_output) {
if (parent_output == output) {
goto move_outputs;

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to break out of two loops, hence the goto

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right

return;
}

for (child_output = wl_container_of((output)->link.next, output, link);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of this complicated loop

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how to do it with wl_list_for_each, I need to skip to the output that's being moved. It could be possible, but I don't feel it is all that cleaner.

}

move_outputs:
ref->next->prev = child_output->link.prev; // A->prev = B
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so that's the code ordering outputs in topological ordering. I bet you can rewrite those manual pointer manipulations with wl_list_remove and wl_list_insert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I can, I was just being too perfectionistic w.r.t. the number of required operations.

} else {
struct wlr_output_layout_output *l_output_rel;

wl_list_for_each(l_output_rel, &layout->outputs, link) {

This comment was marked as resolved.

Copy link
Member

@emersion emersion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks pretty good.

@VincentVanlaer
Copy link
Contributor Author

All issues are addressed. I added error handling to the config parsing of the layout to prevent rootston from segfaulting when a user created an invalid configuration.

}
}

void roots_layout_reflow(struct roots_layout *layout) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this is obvious, but why is this required? I don't even understand what this functions does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It finds a position for the outputs that rootston can't configure (no configuration is available or the reference output is not connected) such that nog gaps accur in the layout. The first loop obtains the extents of the outputs that rootston was able to configure, the second loop moves the outputs to a proper position.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably have already discussed about this, but why can't this happen in wlr_output_layout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was to make the auto configuration not too complex and have it as a set it and forget type of configuration for examples and prototypes. Having a very specific behavior for these kind of outputs in wlr_output_layout might be to prescriptive for how compositors should handle outputs when they cannot be fully configured. Moving this into the wlr_output_layout would cater this specific case, but might not be sufficient for other compositors. As a minor point, it also shows how one can extend the wlr_output_layout and allowed me to see where it could be improved. #1002 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment explaining what this function does, since it's not obvious from its name?

VGA-1 = fixed 0,0
# Put the output with name VGA-2 to the left of the output with name VGA-1
VGA-2 = left-of VGA-1
# Other possibilities include : right-of, above, below, mirror
Copy link
Member

@emersion emersion Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: no space between "include" and the colon (:fr:)

@@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is duplicated in two places. Maybe it can be split in a function?


if (max_x == INT_MIN) {

This comment was marked as outdated.

void roots_layout_reflow(struct roots_layout *layout) {
int max_x = INT_MIN;
int max_x_y = 0;
bool configured;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be accessed when uninitialized. Also, this variable doesn't seem to be used outside the loop, so it can be moved inside.

Copy link
Member

@emersion emersion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

@emersion emersion requested a review from ddevault June 9, 2018 20:43
@VincentVanlaer
Copy link
Contributor Author

Closed because it is too complex for wlroots. This should live completely in the compositor or in a separate client.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants