Cleaner approach to decorations
xi opened this issue · comments
I believe the current approach to decorations is a bit messy and hard to extend. I tried to propose extensions in #1724 and #1725 with mixed success. If we want to avoid breaking changes in the future, I think it is better to think hard about the design first before merging any changes.
I am probably overthinking this. But now that the overthinking has already happened, I might as well share it.
Why is this complicated?
We cannot directly port concepts from openbox because Wayland distinguishes between client side decorations (CSD) and server side decorations (SSD).
The current situation
As of
- There are 3 modes of decoration: 'none', 'borders', and 'full'.
- The
ToggleDecoration
action is implemented,Decorate
andUndecorate
are not. - CSD negotiation is implemented for both xdg-shell and xwayland.
- The results can be overwritten using
<windowRule serverDecoration="">
- A default can be provided using
<core><decoration>
- CSD windows get initialized with 'none' decoration, SSD windows get initialized with 'full' decoration
- The results can be overwritten using
<theme><keepBorder>
is implement, but behaves differently from openbox (and also from its documentation) (see #876)- If it is disabled,
ToggleDecorations
toggles between 'none' and 'full' - If it is enabled,
Toggledecorations
cycles between 'border', 'none', and 'full' - It has no effect on the initial decoration of CSD windows (as claimed by the documentation)
- If it is disabled,
- The implementation doesn't use the 3 modes. Instead it uses the following:
view->ssd_enabled
andview->ssd_titlebar_hidden
to track whether ssd and titlebar are enabledview->ssd
andview->ssd->titlebar.tree->node->enabled
to track whether ssd and titlebar actually exist. For example, full screen windows do not have decoration, even if it is enabled.has_ssd(view)
stores the result of the CSD/SSD negotiation
What do we want?
As a start for discussion:
- We want to keep backwards compatibility as much as possible. Among other things, this means that we have to stick with the the 3-state
ToggleDecorations
andkeepBorder
. - For CSD windows, undecorated means 'none'
- For SSD windows, the meaning of undecorated depends on
keepBorder
- if
keepBorder=yes
, undecorated means 'borders' - if
keepBorder=yes
, undecorated means 'none'
- if
- Decorated always means 'full'
- I am not 100% sure about this. Another approach would be to allow users to disable titlebars globally. In that case, decorated would mean 'border' and undecorated would mean 'none'. On the other hand, this could also be achieved by using something like
<windowRule serverDecoration="server"> <action name="Undecorate"/>
(rule) orToggleDecorations
(interactive). - You could also argue that for CSD windows, decorated should mean 'none' because they have their own decoration.
- I am not 100% sure about this. Another approach would be to allow users to disable titlebars globally. In that case, decorated would mean 'border' and undecorated would mean 'none'. On the other hand, this could also be achieved by using something like
- Users should be able to switch to any of the 3 decoration modes manually, but not necessarily in a single step (this is what
ToggleDecorations
provides).
Settings / Rules / Actions
Actions are more flexible than rules (because they can be used both in <windowRole>
and <keybind>
/<mousebind>
) and rules are more flexible than settings (because different rules can apply to different windows). So if we add anything, I think we should prefer actions over rules and settings.
Use cases
Which of these do we want to support?
disable CSD
<windowRule identifier="">
<windowRule serverDecoration="server">
</windowRule>
manually toggle decorations
<keybind key="">
<action name="ToggleDecorations"/>
</keybind>
manually toggle between 'full' and 'none'
Only possible if keepBorder
is disabled globally.
manually toggle between 'full' and 'border'
Not currently possible
manually toggle between 'border' and 'none'
Not currently possible
manually switch to 'full'
Not currently possible
manually switch to 'border'
Not currently possible
manually switch to 'none'
Not currently possible
toggle between 'decorated' and 'undecorated' (whatever that means based on CSD/SSD negotation and keepBorder
)
Not currently possible
manually switch to 'decorated' (whatever that means based on CSD/SSD negotation and keepBorder
)
Not currently possible
manually switch to 'undecorated' (whatever that means based on CSD/SSD negotation and keepBorder
)
Not currently possible
disable titlebars
Not currently possible. It could look something like this:
<windowRule identifier="">
<action name="Undecorate"/>
</windowRule>
hide titlebars on maximize
Not currently possible (except that borders are removed automatically). It could look something like this:
<keybind key="W-Up">
<action name="Undecorate"/>
<action name="Maximize" direction="both"/>
</keybind>
<keybind key="W-Down">
<action name="Decorate"/>
<action name="Unmaximize"/>
</keybind>
<keybind key="W-Left">
<action name="Undecorate"/>
<action name="SnapToEdge" direction="left"/>
</keybind>
<keybind key="W-Right">
<action name="Undecorate"/>
<action name="SnapToEdge" direction="right"/>
</keybind>
<mousebind button="W-Left" action="Drag">
<action name="Undecorate"/>
<action name="Move"/>
</mousebind>
Proposals
- Initially,
ToggleDecorations
toggled between 'none' and 'full'. This does not cover any usecases involving 'border', but it is straight forward andDecorate
/Undecorate
could easily be added. - The current version was implemented in #876. It adds basic support for 'border', but does not cover all use cases. it is also no longer clear what
Decorate
/Undecorate
should do. - The initial version of #876 did not have the 3-state
ToggleDecorations
. Instead, it would toggle between 'full' and 'none' ifkeepBorder=no
and between 'full' and 'border' otherwise. This was changed because it would add non-removable borders to CSD windows. - In #1724 I proposed to mirror the structure of the code:
ToggleDecorations
/Decorate
/Undecorate
would manipulateview->ssd_enabled
, andToggleTitlebar
/ShowTitlebar
/HideTitlebar
would manipulateview->ssd_titlebar_hidden
. This would cover most use cases, but breaks backwards compatibility and was considered too low-level for end users. - #1725 is based on a proposal by @ahesford. The main idea was to add
keepBorder
attributes to some actions to overwrite the global setting. It covers many use cases without breaking backwards compatibility, but has some weird edge cases. Decorate
/Undecorate
could use rules based onhas_ssd(view)
andkeepBorder
to decide what is the right thing to do. That would be an opinionated approach that intentionally does not cover all use cases.
Some additional ideas that only cover specific use cases:
- There was also the proposal to add
<action name="SetServerSideDecorationMode" mode="none|border|all"/>
.- We could add value ranges, e.g. 'border-or-more' and 'border-or-less'. This would allow for example to remove titlebars from SSD windows without affecting CSD windows.
- The original use case why I got into all of this was disabling titlebars. That could be done with a configurable default value for
view->ssd_titlebar_hidden
. However, I would prefer to find a more generic solution. <windowRule>
could gain andecoration=server|client
attribute so that rules can be restricted to SSD or CSD windows.
Nice write-up of the current situation, thanks for that.
I think one use-case missing in the above list is people that want to *always* have full SSD decorations around the windows, even if they additionally use client side ones.
Just throwing another random idea around: We do have the If action, we could add a query argument for the current SSD state, e.g. CSD|borders|full|none
. That in combination with a SetDecorations
action which directly addresses the different states should allow users to use it for any scenario they want.
Something like:
<!-- Use borders by default for non-csd views -->
<windowRule identifier="*">
<action name="If">
<query decorations="csd" />
<else>
<action name="SetDecorations" decorations="borders" />
</else>
</action>
</windowRule>
or
<!-- Use full decorations by default, even for CSD views -->
<windowRule identifier="*">
<action name="SetDecorations" decorations="full" />
</windowRule>
Please add an option to preserve window size for SetDecorations
and ToggleDecorations
.
The window size (the underlying client generated surface) should be kept (unless the window is snapped to some edge / region or maximized), so I assume you are referring to modifying the window size when changing the SSD decoration state?
Yes. Preserve window's external dimensions.