river-layout: update to v3
- Remove advertise_view and advertise_done events. Using the information provided by these for any purpose would make the layout far less predictable. Futhermore, in the months this has been available for use, to my knowledge nobody has actually used it for anything useful. - Replace the set/mod layout value events with a single user_command event. This simplifies the protocol and is more flexible for clients. - Add a layout_name argument to the commit request. This name is an arbitrary, user-facing string that might, for example, be displayed by a status bar. This was present in early drafts of the protocol, but was removed in favor of river-options. Since river-options itself has since been removed and this feature is nice to have, re-add it. - Rename main factor to main ratio in rivertile. The "factor" name was just legacy from dwm, "ratio" is much more accurate.
This commit is contained in:
parent
96e1082156
commit
2635f3299a
11 changed files with 322 additions and 437 deletions
|
@ -55,7 +55,7 @@ pub fn build(b: *zbs.Builder) !void {
|
||||||
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
|
scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/river-status-unstable-v1.xml");
|
scanner.addProtocolPath("protocol/river-status-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/river-layout-v2.xml");
|
scanner.addProtocolPath("protocol/river-layout-v3.xml");
|
||||||
scanner.addProtocolPath("protocol/wlr-layer-shell-unstable-v1.xml");
|
scanner.addProtocolPath("protocol/wlr-layer-shell-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/wlr-output-power-management-unstable-v1.xml");
|
scanner.addProtocolPath("protocol/wlr-output-power-management-unstable-v1.xml");
|
||||||
|
|
||||||
|
|
|
@ -90,15 +90,11 @@ over the Wayland protocol.
|
||||||
Set the layout namespace of currently focused output, overriding
|
Set the layout namespace of currently focused output, overriding
|
||||||
the value set with *default-layout* if any.
|
the value set with *default-layout* if any.
|
||||||
|
|
||||||
*set-layout-value* _namespace_ _type_ _name_ _value_
|
*send-layout-cmd* _namespace_ _command_
|
||||||
Set the value with name _name_ of the layout on the focused output
|
Send _command_ to the layout client on the currently focused output
|
||||||
with matching namespace. If there is no matching layout, this command
|
with the given _namespace_, if any. What commands a layout client
|
||||||
does nothing.
|
understands depends on the layout client. For rivertile, see the
|
||||||
|
documentation in the *rivertile*(1) man page.
|
||||||
*mod-layout-value* _namespace_ _type_ _name_ _value_
|
|
||||||
Modify the value with name _name_ of the layout on the focused
|
|
||||||
output with matching namespace. If there is no matching layout,
|
|
||||||
this command does nothing.
|
|
||||||
|
|
||||||
## TAG MANAGEMENT
|
## TAG MANAGEMENT
|
||||||
|
|
||||||
|
|
|
@ -32,22 +32,33 @@ modified while rivertile is running with the help of *riverctl*(1).
|
||||||
Set the initial number of views in the main area of the
|
Set the initial number of views in the main area of the
|
||||||
layout. (Default: 1)
|
layout. (Default: 1)
|
||||||
|
|
||||||
*-main-factor* _ratio_
|
*-main-ratio* _ratio_
|
||||||
Set the initial ratio of main area to total layout area. (Default: 0.6)
|
Set the initial ratio of main area to total layout area. The _ratio_
|
||||||
|
must be between 0.1 and 0.9, inclusive. (Default: 0.6)
|
||||||
|
|
||||||
# VALUES
|
# COMMANDS
|
||||||
|
|
||||||
These values may be modified while rivertile is running with the help of
|
These commands may be sent to rivertile at runtime with the help of
|
||||||
*riverctl*(1).
|
*riverctl*(1).
|
||||||
|
|
||||||
_main_location_ (string: top, bottom, left, or right)
|
*set-main-location* [*top*|*bottom*|*left*|*right*]
|
||||||
The location of the main area in the layout.
|
Set the location of the main area in the layout.
|
||||||
|
|
||||||
_main_count_ (int)
|
*set-main-count* _count_
|
||||||
The number of views in the main area of the layout.
|
Set the number of views in the main area of the layout.
|
||||||
|
|
||||||
_main_factor_ (fixed: [0.1, 0.9])
|
*mod-main-count* _delta_
|
||||||
The ratio of main area to total layout area.
|
Modify the number of views in the main area of the layout by a
|
||||||
|
positive or negative _delta_.
|
||||||
|
|
||||||
|
*set-main-ratio* _ratio_
|
||||||
|
Set the ratio of main area to total layout area. The _ratio_ must
|
||||||
|
be between 0.1 and 0.9, inclusive.
|
||||||
|
|
||||||
|
*mod-main-ratio* _delta_
|
||||||
|
Modify the ratio of main area to total layout area by a positive or
|
||||||
|
negative _delta_. The resulting ratio will be clamped to be between
|
||||||
|
0.1 and 0.9, inclusive.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
|
@ -55,9 +66,9 @@ Start *rivertile* with 4 pixels outer padding and 2 main views:
|
||||||
|
|
||||||
rivertile -outer-padding 4 -main-count 2
|
rivertile -outer-padding 4 -main-count 2
|
||||||
|
|
||||||
Set the main location of rivertile to top at runtime:
|
Set the main location of rivertile to *top* at runtime:
|
||||||
|
|
||||||
riverctl set-layout-value rivertile string main_location top
|
riverctl send-layout-cmd rivertile "set-main-location top"
|
||||||
|
|
||||||
# AUTHORS
|
# AUTHORS
|
||||||
|
|
||||||
|
|
20
example/init
20
example/init
|
@ -40,13 +40,13 @@ riverctl map normal $mod+Shift Comma send-to-output previous
|
||||||
# Mod+Return to bump the focused view to the top of the layout stack
|
# Mod+Return to bump the focused view to the top of the layout stack
|
||||||
riverctl map normal $mod Return zoom
|
riverctl map normal $mod Return zoom
|
||||||
|
|
||||||
# Mod+H and Mod+L to decrease/increase the main_factor value of rivertile by 0.05
|
# Mod+H and Mod+L to decrease/increase the main ratio of rivertile(1)
|
||||||
riverctl map normal $mod H mod-layout-value rivertile fixed main_factor -0.05
|
riverctl map normal $mod H send-layout-cmd rivertile "mod-main-ratio -0.05"
|
||||||
riverctl map normal $mod L mod-layout-value rivertile fixed main_factor +0.05
|
riverctl map normal $mod L send-layout-cmd rivertile "mod-main-ratio +0.05"
|
||||||
|
|
||||||
# Mod+Shift+H and Mod+Shift+L to increment/decrement the main_count value of rivertile.
|
# Mod+Shift+H and Mod+Shift+L to increment/decrement the main count of rivertile(1)
|
||||||
riverctl map normal $mod+Shift H mod-layout-value rivertile int main_count +1
|
riverctl map normal $mod+Shift H send-layout-cmd rivertile "mod-main-count +1"
|
||||||
riverctl map normal $mod+Shift L mod-layout-value rivertile int main_count -1
|
riverctl map normal $mod+Shift L send-layout-cmd rivertile "mod-main-count -1"
|
||||||
|
|
||||||
# Mod+Alt+{H,J,K,L} to move views
|
# Mod+Alt+{H,J,K,L} to move views
|
||||||
riverctl map normal $mod+Mod1 H move left 100
|
riverctl map normal $mod+Mod1 H move left 100
|
||||||
|
@ -102,10 +102,10 @@ riverctl map normal $mod Space toggle-float
|
||||||
riverctl map normal $mod F toggle-fullscreen
|
riverctl map normal $mod F toggle-fullscreen
|
||||||
|
|
||||||
# Mod+{Up,Right,Down,Left} to change layout orientation
|
# Mod+{Up,Right,Down,Left} to change layout orientation
|
||||||
riverctl map normal $mod Up set-layout-value rivertile string main_location top
|
riverctl map normal $mod Up send-layout-cmd rivertile "set-main-location top"
|
||||||
riverctl map normal $mod Right set-layout-value rivertile string main_location right
|
riverctl map normal $mod Right send-layout-cmd rivertile "set-main-location right"
|
||||||
riverctl map normal $mod Down set-layout-value rivertile string main_location bottom
|
riverctl map normal $mod Down send-layout-cmd rivertile "set-main-location bottom"
|
||||||
riverctl map normal $mod Left set-layout-value rivertile string main_location left
|
riverctl map normal $mod Left send-layout-cmd rivertile "set-main-location left"
|
||||||
|
|
||||||
# Declare a passthrough mode. This mode has only a single mapping to return to
|
# Declare a passthrough mode. This mode has only a single mapping to return to
|
||||||
# normal mode. This makes it useful for testing a nested wayland compositor
|
# normal mode. This makes it useful for testing a nested wayland compositor
|
||||||
|
|
|
@ -1,256 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<protocol name="river_layout_v2">
|
|
||||||
<copyright>
|
|
||||||
Copyright 2020-2021 The River Developers
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
</copyright>
|
|
||||||
|
|
||||||
<description summary="let clients propose view positions and dimensions">
|
|
||||||
This protocol specifies a way for clients to propose arbitrary positions and
|
|
||||||
dimensions for a set of views on a specific output of a compositor through
|
|
||||||
the river_layout_v2 object.
|
|
||||||
|
|
||||||
This set of views is logically structured as a simple list. Views
|
|
||||||
in this list cannot be individually addressed, instead the order of
|
|
||||||
requests/events is significant.
|
|
||||||
|
|
||||||
The entire set of proposed positions and dimensions for the views in the
|
|
||||||
list are called a layout. Due to their list heritage, layouts are also
|
|
||||||
logically strictly linear; Any complex underlying data structure a client
|
|
||||||
may use when generating the layout is lost in transmission. This is an
|
|
||||||
intentional limitation.
|
|
||||||
|
|
||||||
Note that the client may need to handle multiple layout demands per
|
|
||||||
river_layout_v2 object simultaneously.
|
|
||||||
|
|
||||||
Warning! The protocol described in this file is currently in the testing
|
|
||||||
phase. Backward compatible changes may be added together with the
|
|
||||||
corresponding interface version bump. Backward incompatible changes can
|
|
||||||
only be done by creating a new major version of the extension.
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<interface name="river_layout_manager_v2" version="1">
|
|
||||||
<description summary="manage river layout objects">
|
|
||||||
A global factory for river_layout_v2 objects.
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<request name="destroy" type="destructor">
|
|
||||||
<description summary="destroy the river_layout_manager object">
|
|
||||||
This request indicates that the client will not use the
|
|
||||||
river_layout_manager object any more. Objects that have been created
|
|
||||||
through this instance are not affected.
|
|
||||||
</description>
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<request name="get_layout">
|
|
||||||
<description summary="create a river_layout_v2 object">
|
|
||||||
This creates a new river_layout_v2 object for the given wl_output.
|
|
||||||
|
|
||||||
All layout related communication is done through this interface.
|
|
||||||
|
|
||||||
The namespace is used by the compositor to decide which river_layout_v2
|
|
||||||
object will receive layout demands for the output.
|
|
||||||
|
|
||||||
The namespace is required to be be unique per-output. Furthermore,
|
|
||||||
two separate clients may not share a namespace on separate outputs. If
|
|
||||||
these conditions are not upheld, the the namespace_in_use event will
|
|
||||||
be sent directly after creation of the river_layout_v2 object.
|
|
||||||
</description>
|
|
||||||
<arg name="id" type="new_id" interface="river_layout_v2"/>
|
|
||||||
<arg name="output" type="object" interface="wl_output"/>
|
|
||||||
<arg name="namespace" type="string" summary="namespace of the layout object"/>
|
|
||||||
</request>
|
|
||||||
</interface>
|
|
||||||
|
|
||||||
<interface name="river_layout_v2" version="1">
|
|
||||||
<description summary="receive and respond to layout demands">
|
|
||||||
This interface allows clients to receive layout demands from the
|
|
||||||
compositor for a specific output and subsequently propose positions and
|
|
||||||
dimensions of individual views.
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<enum name="error">
|
|
||||||
<entry name="count_mismatch" value="0" summary="number of
|
|
||||||
proposed dimensions does not match number of views in layout"/>
|
|
||||||
<entry name="already_committed" value="1" summary="the layout demand with
|
|
||||||
the provided serial was already committed"/>
|
|
||||||
</enum>
|
|
||||||
|
|
||||||
<request name="destroy" type="destructor">
|
|
||||||
<description summary="destroy the river_layout_v2 object">
|
|
||||||
This request indicates that the client will not use the river_layout_v2
|
|
||||||
object any more.
|
|
||||||
</description>
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<event name="namespace_in_use">
|
|
||||||
<description summary="the requested namespace is already in use">
|
|
||||||
After this event is sent, all requests aside from the destroy event
|
|
||||||
will be ignored by the server. If the client wishes to try again with
|
|
||||||
a different namespace they must create a new river_layout_v2 object.
|
|
||||||
</description>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<event name="layout_demand">
|
|
||||||
<description summary="the compositor requires a layout">
|
|
||||||
The compositor sends this event to inform the client that it requires a
|
|
||||||
layout for a set of views.
|
|
||||||
|
|
||||||
The usable width and height height indicate the space in which the
|
|
||||||
client can safely position views without interfering with desktop
|
|
||||||
widgets such as panels.
|
|
||||||
|
|
||||||
The serial of this event is used to identify subsequent events and
|
|
||||||
request as belonging to this layout demand. Beware that the client
|
|
||||||
might need to handle multiple layout demands at the same time.
|
|
||||||
|
|
||||||
The server will ignore responses to all but the most recent
|
|
||||||
layout demand. Thus, clients are only required to respond to the most
|
|
||||||
recent layout_demand received. If a newer layout_demand is received
|
|
||||||
before the client has finished responding to an old demand, the client
|
|
||||||
may abort work on the old demand as any further work would be wasted.
|
|
||||||
</description>
|
|
||||||
<arg name="view_count" type="uint" summary="number of views in the layout"/>
|
|
||||||
<arg name="usable_width" type="uint" summary="width of the usable area"/>
|
|
||||||
<arg name="usable_height" type="uint" summary="height of the usable area"/>
|
|
||||||
<arg name="tags" type="uint" summary="tags of the output, 32-bit bitfield"/>
|
|
||||||
<arg name="serial" type="uint" summary="serial of the layout demand"/>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<event name="advertise_view">
|
|
||||||
<description summary="make layout client aware of view">
|
|
||||||
This event is sent by the server as part of the layout demand with
|
|
||||||
matching serial. It provides additional information about one of
|
|
||||||
the views to be arranged.
|
|
||||||
|
|
||||||
Every view part of the layout demand is advertised exactly once,
|
|
||||||
in the order of the view list.
|
|
||||||
</description>
|
|
||||||
<arg name="tags" type="uint" summary="tags of the view, 32-bit bitfield"/>
|
|
||||||
<arg name="app_id" type="string" summary="view app-id" allow-null="true"/>
|
|
||||||
<arg name="serial" type="uint" summary="serial of the layout demand"/>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<event name="advertise_done">
|
|
||||||
<description summary="all views have been advertised">
|
|
||||||
This event is sent by the server as the last event of the layout
|
|
||||||
demand with matching serial, after all advertise_view events.
|
|
||||||
</description>
|
|
||||||
<arg name="serial" type="uint" summary="serial of the layout demand"/>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<request name="push_view_dimensions">
|
|
||||||
<description summary="propose dimensions of the next view">
|
|
||||||
This request proposes a size and position of a view in the layout demand
|
|
||||||
with matching serial.
|
|
||||||
|
|
||||||
Pushed view dimensions apply to the views in the same order they were
|
|
||||||
advertised. That is, the first push_view_dimensions request applies
|
|
||||||
to the first view advertised, the second to the second, and so on.
|
|
||||||
|
|
||||||
A client must propose position and dimensions for the entire set of
|
|
||||||
views. Proposing too many or too few view dimensions is a protocol error.
|
|
||||||
|
|
||||||
This request may be sent before the corresponding view has been
|
|
||||||
advertised.
|
|
||||||
|
|
||||||
The x and y coordinates are relative to the usable area of the output,
|
|
||||||
with (0,0) as the top left corner.
|
|
||||||
</description>
|
|
||||||
<arg name="serial" type="uint" summary="serial of layout demand"/>
|
|
||||||
<arg name="x" type="int" summary="x coordinate of view"/>
|
|
||||||
<arg name="y" type="int" summary="y coordinate of view"/>
|
|
||||||
<arg name="width" type="uint" summary="width of view"/>
|
|
||||||
<arg name="height" type="uint" summary="height of view"/>
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<request name="commit">
|
|
||||||
<description summary="commit a layout">
|
|
||||||
This request indicates that the client is done pushing dimensions
|
|
||||||
and the compositor may apply the layout. This completes the layout
|
|
||||||
demand with matching serial, any other requests sent with the serial
|
|
||||||
are a protocol error.
|
|
||||||
|
|
||||||
The compositor is free to use this proposed layout however it chooses,
|
|
||||||
including ignoring it.
|
|
||||||
</description>
|
|
||||||
<arg name="serial" type="uint" summary="serial of layout demand"/>
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<event name="set_int_value">
|
|
||||||
<description summary="an int value has been set">
|
|
||||||
This event indicates that the value of this river_layout_v2 object
|
|
||||||
with the given name has been set to the given value.
|
|
||||||
|
|
||||||
This event will be followed by a layout_demand if necessary (i.e. if
|
|
||||||
this layout object is currently being used by the compositor to
|
|
||||||
layout an output)
|
|
||||||
</description>
|
|
||||||
<arg name="name" type="string"/>
|
|
||||||
<arg name="value" type="int"/>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<event name="mod_int_value">
|
|
||||||
<description summary="an int value has been modified">
|
|
||||||
This event indicates that the value of this river_layout_v2 object
|
|
||||||
with the given name has been modifed by the given delta.
|
|
||||||
|
|
||||||
This event will be followed by a layout_demand if necessary (i.e. if
|
|
||||||
this layout object is currently being used by the compositor to
|
|
||||||
layout an output)
|
|
||||||
</description>
|
|
||||||
<arg name="name" type="string"/>
|
|
||||||
<arg name="delta" type="int"/>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<event name="set_fixed_value">
|
|
||||||
<description summary="a fixed value has been set">
|
|
||||||
This event indicates that the value of this river_layout_v2 object
|
|
||||||
with the given name has been set to the given value.
|
|
||||||
|
|
||||||
This event will be followed by a layout_demand if necessary (i.e. if
|
|
||||||
this layout object is currently being used by the compositor to
|
|
||||||
layout an output)
|
|
||||||
</description>
|
|
||||||
<arg name="name" type="string"/>
|
|
||||||
<arg name="value" type="fixed"/>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<event name="mod_fixed_value">
|
|
||||||
<description summary="a fixed value has been modified">
|
|
||||||
This event indicates that the value of this river_layout_v2 object
|
|
||||||
with the given name has been modifed by the given delta.
|
|
||||||
|
|
||||||
This event will be followed by a layout_demand if necessary (i.e. if
|
|
||||||
this layout object is currently being used by the compositor to
|
|
||||||
layout an output)
|
|
||||||
</description>
|
|
||||||
<arg name="name" type="string"/>
|
|
||||||
<arg name="delta" type="fixed"/>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<event name="set_string_value">
|
|
||||||
<description summary="a string value has been set">
|
|
||||||
This event indicates that the value of this river_layout_v2 object
|
|
||||||
with the given name has been set to the given value.
|
|
||||||
|
|
||||||
This event will be followed by a layout_demand if necessary (i.e. if
|
|
||||||
this layout object is currently being used by the compositor to
|
|
||||||
layout an output)
|
|
||||||
</description>
|
|
||||||
<arg name="name" type="string"/>
|
|
||||||
<arg name="value" type="string"/>
|
|
||||||
</event>
|
|
||||||
</interface>
|
|
||||||
</protocol>
|
|
181
protocol/river-layout-v3.xml
Normal file
181
protocol/river-layout-v3.xml
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="river_layout_v3">
|
||||||
|
<copyright>
|
||||||
|
Copyright 2020-2021 The River Developers
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<description summary="let clients propose view positions and dimensions">
|
||||||
|
This protocol specifies a way for clients to propose arbitrary positions
|
||||||
|
and dimensions for a set of views on a specific output of a compositor
|
||||||
|
through the river_layout_v3 object.
|
||||||
|
|
||||||
|
Layouts are a strictly linear list of views, the position and dimensions
|
||||||
|
of which are supplied by the client. Any complex underlying data structure
|
||||||
|
a client may use when generating the layout is lost in transmission. This
|
||||||
|
is an intentional limitation.
|
||||||
|
|
||||||
|
Additonally, this protocol allows the compositor to deliver arbitrary
|
||||||
|
user-provided commands associated with a layout to clients. A client
|
||||||
|
may use these commands to implement runtime configuration/control, or
|
||||||
|
may ignore them entirely. How the user provides these commands to the
|
||||||
|
compositor is not specified by this protocol and left to compositor policy.
|
||||||
|
|
||||||
|
Warning! The protocol described in this file is currently in the
|
||||||
|
testing phase. Backward compatible changes may be added together with
|
||||||
|
the corresponding interface version bump. Backward incompatible changes
|
||||||
|
can only be done by creating a new major version of the extension.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<interface name="river_layout_manager_v3" version="1">
|
||||||
|
<description summary="manage river layout objects">
|
||||||
|
A global factory for river_layout_v3 objects.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the river_layout_manager object">
|
||||||
|
This request indicates that the client will not use the
|
||||||
|
river_layout_manager object any more. Objects that have been created
|
||||||
|
through this instance are not affected.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_layout">
|
||||||
|
<description summary="create a river_layout_v3 object">
|
||||||
|
This creates a new river_layout_v3 object for the given wl_output.
|
||||||
|
|
||||||
|
All layout related communication is done through this interface.
|
||||||
|
|
||||||
|
The namespace is used by the compositor to decide which river_layout_v3
|
||||||
|
object will receive layout demands for the output.
|
||||||
|
|
||||||
|
The namespace is required to be be unique per-output. Furthermore,
|
||||||
|
two separate clients may not share a namespace on separate outputs. If
|
||||||
|
these conditions are not upheld, the the namespace_in_use event will
|
||||||
|
be sent directly after creation of the river_layout_v3 object.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="river_layout_v3"/>
|
||||||
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
|
<arg name="namespace" type="string" summary="namespace of the layout object"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="river_layout_v3" version="1">
|
||||||
|
<description summary="receive and respond to layout demands">
|
||||||
|
This interface allows clients to receive layout demands from the
|
||||||
|
compositor for a specific output and subsequently propose positions and
|
||||||
|
dimensions of individual views.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="count_mismatch" value="0" summary="number of
|
||||||
|
proposed dimensions does not match number of views in layout"/>
|
||||||
|
<entry name="already_committed" value="1" summary="the layout demand with
|
||||||
|
the provided serial was already committed"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the river_layout_v3 object">
|
||||||
|
This request indicates that the client will not use the river_layout_v3
|
||||||
|
object any more.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="namespace_in_use">
|
||||||
|
<description summary="the requested namespace is already in use">
|
||||||
|
After this event is sent, all requests aside from the destroy event
|
||||||
|
will be ignored by the server. If the client wishes to try again with
|
||||||
|
a different namespace they must create a new river_layout_v3 object.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="layout_demand">
|
||||||
|
<description summary="the compositor requires a layout">
|
||||||
|
The compositor sends this event to inform the client that it requires a
|
||||||
|
layout for a set of views.
|
||||||
|
|
||||||
|
The usable width and height height indicate the space in which the
|
||||||
|
client can safely position views without interfering with desktop
|
||||||
|
widgets such as panels.
|
||||||
|
|
||||||
|
The serial of this event is used to identify subsequent requests as
|
||||||
|
belonging to this layout demand. Beware that the client might need
|
||||||
|
to handle multiple layout demands at the same time.
|
||||||
|
|
||||||
|
The server will ignore responses to all but the most recent layout
|
||||||
|
demand. Thus, clients are only required to respond to the most recent
|
||||||
|
layout_demand received. If a newer layout_demand is received before
|
||||||
|
the client has finished responding to an old demand, the client should
|
||||||
|
abort work on the old demand as any further work would be wasted.
|
||||||
|
</description>
|
||||||
|
<arg name="view_count" type="uint" summary="number of views in the layout"/>
|
||||||
|
<arg name="usable_width" type="uint" summary="width of the usable area"/>
|
||||||
|
<arg name="usable_height" type="uint" summary="height of the usable area"/>
|
||||||
|
<arg name="tags" type="uint" summary="tags of the output, 32-bit bitfield"/>
|
||||||
|
<arg name="serial" type="uint" summary="serial of the layout demand"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="push_view_dimensions">
|
||||||
|
<description summary="propose dimensions of the next view">
|
||||||
|
This request proposes a size and position for a view in the layout demand
|
||||||
|
with matching serial.
|
||||||
|
|
||||||
|
A client must send this request for every view that is part of the
|
||||||
|
layout demand. The number of views in the layout is given by the
|
||||||
|
view_count argument of the layout_demand event. Pushing too many or
|
||||||
|
too few view dimensions is a protocol error.
|
||||||
|
|
||||||
|
The x and y coordinates are relative to the usable area of the output,
|
||||||
|
with (0,0) as the top left corner.
|
||||||
|
</description>
|
||||||
|
<arg name="x" type="int" summary="x coordinate of view"/>
|
||||||
|
<arg name="y" type="int" summary="y coordinate of view"/>
|
||||||
|
<arg name="width" type="uint" summary="width of view"/>
|
||||||
|
<arg name="height" type="uint" summary="height of view"/>
|
||||||
|
<arg name="serial" type="uint" summary="serial of layout demand"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="commit">
|
||||||
|
<description summary="commit a layout">
|
||||||
|
This request indicates that the client is done pushing dimensions
|
||||||
|
and the compositor may apply the layout. This completes the layout
|
||||||
|
demand with matching serial, any other requests sent with the serial
|
||||||
|
are a protocol error.
|
||||||
|
|
||||||
|
The layout_name argument is a user-facing name or short description
|
||||||
|
of the layout that is being committed. The compositor may for example
|
||||||
|
display this on a status bar, though what exactly is done with it is
|
||||||
|
left to the compositor's discretion.
|
||||||
|
|
||||||
|
The compositor is free to use this proposed layout however it chooses,
|
||||||
|
including ignoring it.
|
||||||
|
</description>
|
||||||
|
<arg name="layout_name" type="string" summary="name of committed layout"/>
|
||||||
|
<arg name="serial" type="uint" summary="serial of layout demand"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="user_command">
|
||||||
|
<description summary="an argument of a user command">
|
||||||
|
This event informs the client of a command sent to it by the user.
|
||||||
|
|
||||||
|
The semantic meaning of the command is left for the client to
|
||||||
|
decide. It is also free to ignore it entirely if it so chooses.
|
||||||
|
|
||||||
|
A layout_demand will be sent after this event if the compositor is
|
||||||
|
currently using this layout object to arrange the output.
|
||||||
|
</description>
|
||||||
|
<arg name="command" type="string"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
|
@ -36,12 +36,12 @@ const LayoutDemand = @import("LayoutDemand.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.layout);
|
const log = std.log.scoped(.layout);
|
||||||
|
|
||||||
layout: *river.LayoutV2,
|
layout: *river.LayoutV3,
|
||||||
namespace: []const u8,
|
namespace: []const u8,
|
||||||
output: *Output,
|
output: *Output,
|
||||||
|
|
||||||
pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namespace: []const u8) !void {
|
pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namespace: []const u8) !void {
|
||||||
const layout = try river.LayoutV2.create(client, version, id);
|
const layout = try river.LayoutV3.create(client, version, id);
|
||||||
|
|
||||||
if (namespaceInUse(namespace, output, client)) {
|
if (namespaceInUse(namespace, output, client)) {
|
||||||
layout.sendNamespaceInUse();
|
layout.sendNamespaceInUse();
|
||||||
|
@ -92,7 +92,7 @@ fn namespaceInUse(namespace: []const u8, output: *Output, client: *wl.Client) bo
|
||||||
|
|
||||||
/// This exists to handle layouts that have been rendered inert (due to the
|
/// This exists to handle layouts that have been rendered inert (due to the
|
||||||
/// namespace already being in use) until the client destroys them.
|
/// namespace already being in use) until the client destroys them.
|
||||||
fn handleRequestInert(layout: *river.LayoutV2, request: river.LayoutV2.Request, _: ?*c_void) void {
|
fn handleRequestInert(layout: *river.LayoutV3, request: river.LayoutV3.Request, _: ?*c_void) void {
|
||||||
if (request == .destroy) layout.destroy();
|
if (request == .destroy) layout.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,28 +108,19 @@ pub fn startLayoutDemand(self: *Self, views: u32) void {
|
||||||
log.err("failed starting layout demand", .{});
|
log.err("failed starting layout demand", .{});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const serial = self.output.layout_demand.?.serial;
|
|
||||||
|
|
||||||
// Then we let the client know that we require a layout
|
|
||||||
self.layout.sendLayoutDemand(
|
self.layout.sendLayoutDemand(
|
||||||
views,
|
views,
|
||||||
self.output.usable_box.width,
|
self.output.usable_box.width,
|
||||||
self.output.usable_box.height,
|
self.output.usable_box.height,
|
||||||
self.output.pending.tags,
|
self.output.pending.tags,
|
||||||
serial,
|
self.output.layout_demand.?.serial,
|
||||||
);
|
);
|
||||||
|
|
||||||
// And finally we advertise all visible views
|
|
||||||
var it = ViewStack(View).iter(self.output.views.first, .forward, self.output.pending.tags, Output.arrangeFilter);
|
|
||||||
while (it.next()) |view| {
|
|
||||||
self.layout.sendAdvertiseView(view.pending.tags, view.getAppId(), serial);
|
|
||||||
}
|
|
||||||
self.layout.sendAdvertiseDone(serial);
|
|
||||||
|
|
||||||
server.root.trackLayoutDemands();
|
server.root.trackLayoutDemands();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleRequest(layout: *river.LayoutV2, request: river.LayoutV2.Request, self: *Self) void {
|
fn handleRequest(layout: *river.LayoutV3, request: river.LayoutV3.Request, self: *Self) void {
|
||||||
switch (request) {
|
switch (request) {
|
||||||
.destroy => layout.destroy(),
|
.destroy => layout.destroy(),
|
||||||
|
|
||||||
|
@ -168,7 +159,7 @@ fn handleRequest(layout: *river.LayoutV2, request: river.LayoutV2.Request, self:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleDestroy(layout: *river.LayoutV2, self: *Self) void {
|
fn handleDestroy(layout: *river.LayoutV3, self: *Self) void {
|
||||||
self.destroy();
|
self.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleSer
|
||||||
|
|
||||||
pub fn init(self: *Self) !void {
|
pub fn init(self: *Self) !void {
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.global = try wl.Global.create(server.wl_server, river.LayoutManagerV2, 1, *Self, self, bind),
|
.global = try wl.Global.create(server.wl_server, river.LayoutManagerV3, 1, *Self, self, bind),
|
||||||
};
|
};
|
||||||
|
|
||||||
server.wl_server.addDestroyListener(&self.server_destroy);
|
server.wl_server.addDestroyListener(&self.server_destroy);
|
||||||
|
@ -50,7 +50,7 @@ fn handleServerDestroy(listener: *wl.Listener(*wl.Server), wl_server: *wl.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
|
fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
|
||||||
const layout_manager = river.LayoutManagerV2.create(client, 1, id) catch {
|
const layout_manager = river.LayoutManagerV3.create(client, 1, id) catch {
|
||||||
client.postNoMemory();
|
client.postNoMemory();
|
||||||
log.crit("out of memory", .{});
|
log.crit("out of memory", .{});
|
||||||
return;
|
return;
|
||||||
|
@ -58,7 +58,7 @@ fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) voi
|
||||||
layout_manager.setHandler(*Self, handleRequest, null, self);
|
layout_manager.setHandler(*Self, handleRequest, null, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleRequest(layout_manager: *river.LayoutManagerV2, request: river.LayoutManagerV2.Request, self: *Self) void {
|
fn handleRequest(layout_manager: *river.LayoutManagerV3, request: river.LayoutManagerV3.Request, self: *Self) void {
|
||||||
switch (request) {
|
switch (request) {
|
||||||
.destroy => layout_manager.destroy(),
|
.destroy => layout_manager.destroy(),
|
||||||
|
|
||||||
|
|
|
@ -64,14 +64,13 @@ const str_to_impl_fn = [_]struct {
|
||||||
.{ .name = "list-inputs", .impl = @import("command/input.zig").listInputs },
|
.{ .name = "list-inputs", .impl = @import("command/input.zig").listInputs },
|
||||||
.{ .name = "map", .impl = @import("command/map.zig").map },
|
.{ .name = "map", .impl = @import("command/map.zig").map },
|
||||||
.{ .name = "map-pointer", .impl = @import("command/map.zig").mapPointer },
|
.{ .name = "map-pointer", .impl = @import("command/map.zig").mapPointer },
|
||||||
.{ .name = "mod-layout-value", .impl = @import("command/layout.zig").modLayoutValue },
|
|
||||||
.{ .name = "move", .impl = @import("command/move.zig").move },
|
.{ .name = "move", .impl = @import("command/move.zig").move },
|
||||||
.{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout },
|
.{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout },
|
||||||
.{ .name = "resize", .impl = @import("command/move.zig").resize },
|
.{ .name = "resize", .impl = @import("command/move.zig").resize },
|
||||||
|
.{ .name = "send-layout-cmd", .impl = @import("command/layout.zig").sendLayoutCmd },
|
||||||
.{ .name = "send-to-output", .impl = @import("command/output.zig").sendToOutput },
|
.{ .name = "send-to-output", .impl = @import("command/output.zig").sendToOutput },
|
||||||
.{ .name = "set-cursor-warp", .impl = @import("command/config.zig").setCursorWarp },
|
.{ .name = "set-cursor-warp", .impl = @import("command/config.zig").setCursorWarp },
|
||||||
.{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags },
|
.{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags },
|
||||||
.{ .name = "set-layout-value", .impl = @import("command/layout.zig").setLayoutValue },
|
|
||||||
.{ .name = "set-repeat", .impl = @import("command/set_repeat.zig").setRepeat },
|
.{ .name = "set-repeat", .impl = @import("command/set_repeat.zig").setRepeat },
|
||||||
.{ .name = "set-view-tags", .impl = @import("command/tags.zig").setViewTags },
|
.{ .name = "set-view-tags", .impl = @import("command/tags.zig").setViewTags },
|
||||||
.{ .name = "snap", .impl = @import("command/move.zig").snap },
|
.{ .name = "snap", .impl = @import("command/move.zig").snap },
|
||||||
|
@ -99,7 +98,6 @@ pub const Error = error{
|
||||||
InvalidButton,
|
InvalidButton,
|
||||||
InvalidCharacter,
|
InvalidCharacter,
|
||||||
InvalidDirection,
|
InvalidDirection,
|
||||||
InvalidType,
|
|
||||||
InvalidPhysicalDirection,
|
InvalidPhysicalDirection,
|
||||||
InvalidOrientation,
|
InvalidOrientation,
|
||||||
InvalidRgba,
|
InvalidRgba,
|
||||||
|
@ -144,7 +142,6 @@ pub fn errToMsg(err: Error) [:0]const u8 {
|
||||||
Error.InvalidButton => "invalid button",
|
Error.InvalidButton => "invalid button",
|
||||||
Error.InvalidCharacter => "invalid character in argument",
|
Error.InvalidCharacter => "invalid character in argument",
|
||||||
Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'",
|
Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'",
|
||||||
Error.InvalidType => "invalid type",
|
|
||||||
Error.InvalidPhysicalDirection => "invalid direction. Must be 'up', 'down', 'left' or 'right'",
|
Error.InvalidPhysicalDirection => "invalid direction. Must be 'up', 'down', 'left' or 'right'",
|
||||||
Error.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'",
|
Error.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'",
|
||||||
Error.InvalidRgba => "invalid color format, must be #RRGGBB or #RRGGBBAA",
|
Error.InvalidRgba => "invalid color format, must be #RRGGBB or #RRGGBBAA",
|
||||||
|
|
|
@ -56,28 +56,20 @@ pub fn defaultLayout(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SetType = enum {
|
/// riverctl send-layout-cmd rivertile "mod-main-count 1"
|
||||||
int,
|
/// riverctl send-layout-cmd rivertile "mod-main-factor -0.1"
|
||||||
fixed,
|
/// riverctl send-layout-cmd rivertile "main-location top"
|
||||||
string,
|
pub fn sendLayoutCmd(
|
||||||
};
|
|
||||||
|
|
||||||
/// riverctl set-layout-value rivertile int main_count 42
|
|
||||||
/// riverctl set-layout-value rivertile fixed main_factor 42.0
|
|
||||||
/// riverctl set-layout-value rivertile string main_location top
|
|
||||||
pub fn setLayoutValue(
|
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
seat: *Seat,
|
seat: *Seat,
|
||||||
args: []const [:0]const u8,
|
args: []const [:0]const u8,
|
||||||
out: *?[]const u8,
|
out: *?[]const u8,
|
||||||
) Error!void {
|
) Error!void {
|
||||||
if (args.len < 5) return Error.NotEnoughArguments;
|
if (args.len < 3) return Error.NotEnoughArguments;
|
||||||
if (args.len > 5) return Error.TooManyArguments;
|
if (args.len > 3) return Error.TooManyArguments;
|
||||||
|
|
||||||
const target_namespace = args[1];
|
|
||||||
const kind = std.meta.stringToEnum(SetType, args[2]) orelse return Error.InvalidType;
|
|
||||||
|
|
||||||
const output = seat.focused_output;
|
const output = seat.focused_output;
|
||||||
|
const target_namespace = args[1];
|
||||||
|
|
||||||
var it = output.layouts.first;
|
var it = output.layouts.first;
|
||||||
const layout = while (it) |node| : (it = node.next) {
|
const layout = while (it) |node| : (it = node.next) {
|
||||||
|
@ -85,68 +77,7 @@ pub fn setLayoutValue(
|
||||||
if (mem.eql(u8, layout.namespace, target_namespace)) break layout;
|
if (mem.eql(u8, layout.namespace, target_namespace)) break layout;
|
||||||
} else return;
|
} else return;
|
||||||
|
|
||||||
const null_terminated_name = try util.gpa.dupeZ(u8, args[3]);
|
layout.layout.sendUserCommand(args[2]);
|
||||||
defer util.gpa.free(null_terminated_name);
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
.int => {
|
|
||||||
const value = try std.fmt.parseInt(i32, args[4], 10);
|
|
||||||
layout.layout.sendSetIntValue(null_terminated_name, value);
|
|
||||||
},
|
|
||||||
.fixed => {
|
|
||||||
const value = try std.fmt.parseFloat(f64, args[4]);
|
|
||||||
layout.layout.sendSetFixedValue(null_terminated_name, wl.Fixed.fromDouble(value));
|
|
||||||
},
|
|
||||||
.string => {
|
|
||||||
const null_terminated_value = try util.gpa.dupeZ(u8, args[4]);
|
|
||||||
defer util.gpa.free(null_terminated_value);
|
|
||||||
layout.layout.sendSetStringValue(null_terminated_name, null_terminated_value);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
output.arrangeViews();
|
|
||||||
}
|
|
||||||
|
|
||||||
const ModType = enum {
|
|
||||||
int,
|
|
||||||
fixed,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// riverctl mode-layout-value rivertile int main_count 42
|
|
||||||
/// riverctl set-layout-value rivertile fixed main_factor 42.0
|
|
||||||
pub fn modLayoutValue(
|
|
||||||
allocator: *std.mem.Allocator,
|
|
||||||
seat: *Seat,
|
|
||||||
args: []const [:0]const u8,
|
|
||||||
out: *?[]const u8,
|
|
||||||
) Error!void {
|
|
||||||
if (args.len < 5) return Error.NotEnoughArguments;
|
|
||||||
if (args.len > 5) return Error.TooManyArguments;
|
|
||||||
|
|
||||||
const target_namespace = args[1];
|
|
||||||
const kind = std.meta.stringToEnum(ModType, args[2]) orelse return Error.InvalidType;
|
|
||||||
|
|
||||||
const output = seat.focused_output;
|
|
||||||
|
|
||||||
var it = output.layouts.first;
|
|
||||||
const layout = while (it) |node| : (it = node.next) {
|
|
||||||
const layout = &node.data;
|
|
||||||
if (mem.eql(u8, layout.namespace, target_namespace)) break layout;
|
|
||||||
} else return;
|
|
||||||
|
|
||||||
const null_terminated_name = try util.gpa.dupeZ(u8, args[3]);
|
|
||||||
defer util.gpa.free(null_terminated_name);
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
.int => {
|
|
||||||
const value = try std.fmt.parseInt(i32, args[4], 10);
|
|
||||||
layout.layout.sendModIntValue(null_terminated_name, value);
|
|
||||||
},
|
|
||||||
.fixed => {
|
|
||||||
const value = try std.fmt.parseFloat(f64, args[4]);
|
|
||||||
layout.layout.sendModFixedValue(null_terminated_name, wl.Fixed.fromDouble(value));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
output.arrangeViews();
|
output.arrangeViews();
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,11 +59,19 @@ const usage =
|
||||||
\\ layout. (Default left)
|
\\ layout. (Default left)
|
||||||
\\ -main-count Set the initial number of views in the main area of the
|
\\ -main-count Set the initial number of views in the main area of the
|
||||||
\\ layout. (Default 1)
|
\\ layout. (Default 1)
|
||||||
\\ -main-factor Set the initial ratio of main area to total layout
|
\\ -main-ratio Set the initial ratio of main area to total layout
|
||||||
\\ area. (Default: 0.6)
|
\\ area. (Default: 0.6)
|
||||||
\\
|
\\
|
||||||
;
|
;
|
||||||
|
|
||||||
|
const Command = enum {
|
||||||
|
@"set-main-location",
|
||||||
|
@"set-main-count",
|
||||||
|
@"mod-main-count",
|
||||||
|
@"set-main-ratio",
|
||||||
|
@"mod-main-ratio",
|
||||||
|
};
|
||||||
|
|
||||||
const Location = enum {
|
const Location = enum {
|
||||||
top,
|
top,
|
||||||
right,
|
right,
|
||||||
|
@ -76,14 +84,14 @@ var view_padding: u32 = 6;
|
||||||
var outer_padding: u32 = 6;
|
var outer_padding: u32 = 6;
|
||||||
var default_main_location: Location = .left;
|
var default_main_location: Location = .left;
|
||||||
var default_main_count: u32 = 1;
|
var default_main_count: u32 = 1;
|
||||||
var default_main_factor: f64 = 0.6;
|
var default_main_ratio: f64 = 0.6;
|
||||||
|
|
||||||
/// We don't free resources on exit, only when output globals are removed.
|
/// We don't free resources on exit, only when output globals are removed.
|
||||||
const gpa = std.heap.c_allocator;
|
const gpa = std.heap.c_allocator;
|
||||||
|
|
||||||
const Context = struct {
|
const Context = struct {
|
||||||
initialized: bool = false,
|
initialized: bool = false,
|
||||||
layout_manager: ?*river.LayoutManagerV2 = null,
|
layout_manager: ?*river.LayoutManagerV3 = null,
|
||||||
outputs: std.TailQueue(Output) = .{},
|
outputs: std.TailQueue(Output) = .{},
|
||||||
|
|
||||||
fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void {
|
fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void {
|
||||||
|
@ -102,9 +110,9 @@ const Output = struct {
|
||||||
|
|
||||||
main_location: Location,
|
main_location: Location,
|
||||||
main_count: u32,
|
main_count: u32,
|
||||||
main_factor: f64,
|
main_ratio: f64,
|
||||||
|
|
||||||
layout: *river.LayoutV2 = undefined,
|
layout: *river.LayoutV3 = undefined,
|
||||||
|
|
||||||
fn init(output: *Output, context: *Context, wl_output: *wl.Output, name: u32) !void {
|
fn init(output: *Output, context: *Context, wl_output: *wl.Output, name: u32) !void {
|
||||||
output.* = .{
|
output.* = .{
|
||||||
|
@ -112,7 +120,7 @@ const Output = struct {
|
||||||
.name = name,
|
.name = name,
|
||||||
.main_location = default_main_location,
|
.main_location = default_main_location,
|
||||||
.main_count = default_main_count,
|
.main_count = default_main_count,
|
||||||
.main_factor = default_main_factor,
|
.main_ratio = default_main_ratio,
|
||||||
};
|
};
|
||||||
if (context.initialized) try output.getLayout(context);
|
if (context.initialized) try output.getLayout(context);
|
||||||
}
|
}
|
||||||
|
@ -128,39 +136,63 @@ const Output = struct {
|
||||||
output.layout.destroy();
|
output.layout.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layoutListener(layout: *river.LayoutV2, event: river.LayoutV2.Event, output: *Output) void {
|
fn layoutListener(layout: *river.LayoutV3, event: river.LayoutV3.Event, output: *Output) void {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.namespace_in_use => fatal("namespace 'rivertile' already in use.", .{}),
|
.namespace_in_use => fatal("namespace 'rivertile' already in use.", .{}),
|
||||||
|
|
||||||
.set_int_value => |ev| {
|
.user_command => |ev| {
|
||||||
if (mem.eql(u8, mem.span(ev.name), "main_count")) {
|
var it = mem.tokenize(mem.span(ev.command), " ");
|
||||||
if (ev.value > 0) output.main_count = @intCast(u32, ev.value);
|
const raw_cmd = it.next() orelse {
|
||||||
|
std.log.err("not enough arguments", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const raw_arg = it.next() orelse {
|
||||||
|
std.log.err("not enough arguments", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if (it.next() != null) {
|
||||||
|
std.log.err("too many arguments", .{});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
const cmd = std.meta.stringToEnum(Command, raw_cmd) orelse {
|
||||||
|
std.log.err("unknown command: {s}", .{raw_cmd});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
switch (cmd) {
|
||||||
|
.@"set-main-location" => {
|
||||||
|
output.main_location = std.meta.stringToEnum(Location, raw_arg) orelse {
|
||||||
|
std.log.err("unknown location: {s}", .{raw_arg});
|
||||||
|
return;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
.mod_int_value => |ev| {
|
.@"set-main-count" => {
|
||||||
if (mem.eql(u8, mem.span(ev.name), "main_count")) {
|
output.main_count = std.fmt.parseInt(u32, raw_arg, 10) catch |err| {
|
||||||
const result = @as(i33, output.main_count) + ev.delta;
|
std.log.err("failed to parse argument: {}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.@"mod-main-count" => {
|
||||||
|
const arg = std.fmt.parseInt(i32, raw_arg, 10) catch |err| {
|
||||||
|
std.log.err("failed to parse argument: {}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const result = math.add(i33, output.main_count, arg) catch math.maxInt(u32);
|
||||||
if (result > 0) output.main_count = @intCast(u32, result);
|
if (result > 0) output.main_count = @intCast(u32, result);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
.@"set-main-ratio" => {
|
||||||
.set_fixed_value => |ev| {
|
const arg = std.fmt.parseFloat(f64, raw_arg) catch |err| {
|
||||||
if (mem.eql(u8, mem.span(ev.name), "main_factor")) {
|
std.log.err("failed to parse argument: {}", .{err});
|
||||||
output.main_factor = math.clamp(ev.value.toDouble(), 0.1, 0.9);
|
return;
|
||||||
}
|
};
|
||||||
|
output.main_ratio = math.clamp(arg, 0.1, 0.9);
|
||||||
},
|
},
|
||||||
.mod_fixed_value => |ev| {
|
.@"mod-main-ratio" => {
|
||||||
if (mem.eql(u8, mem.span(ev.name), "main_factor")) {
|
const arg = std.fmt.parseFloat(f64, raw_arg) catch |err| {
|
||||||
const new_value = ev.delta.toDouble() + output.main_factor;
|
std.log.err("failed to parse argument: {}", .{err});
|
||||||
output.main_factor = math.clamp(new_value, 0.1, 0.9);
|
return;
|
||||||
}
|
};
|
||||||
|
output.main_ratio = math.clamp(output.main_ratio + arg, 0.1, 0.9);
|
||||||
},
|
},
|
||||||
|
|
||||||
.set_string_value => |ev| {
|
|
||||||
if (mem.eql(u8, mem.span(ev.name), "main_location")) {
|
|
||||||
if (std.meta.stringToEnum(Location, mem.span(ev.value))) |new_location| {
|
|
||||||
output.main_location = new_location;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -191,7 +223,7 @@ const Output = struct {
|
||||||
var secondary_height_rem: u32 = undefined;
|
var secondary_height_rem: u32 = undefined;
|
||||||
|
|
||||||
if (main_count > 0 and secondary_count > 0) {
|
if (main_count > 0 and secondary_count > 0) {
|
||||||
main_width = @floatToInt(u32, output.main_factor * @intToFloat(f64, usable_width));
|
main_width = @floatToInt(u32, output.main_ratio * @intToFloat(f64, usable_width));
|
||||||
main_height = usable_height / main_count;
|
main_height = usable_height / main_count;
|
||||||
main_height_rem = usable_height % main_count;
|
main_height_rem = usable_height % main_count;
|
||||||
|
|
||||||
|
@ -236,41 +268,43 @@ const Output = struct {
|
||||||
|
|
||||||
switch (output.main_location) {
|
switch (output.main_location) {
|
||||||
.left => layout.pushViewDimensions(
|
.left => layout.pushViewDimensions(
|
||||||
ev.serial,
|
|
||||||
x + @intCast(i32, outer_padding),
|
x + @intCast(i32, outer_padding),
|
||||||
y + @intCast(i32, outer_padding),
|
y + @intCast(i32, outer_padding),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
ev.serial,
|
||||||
),
|
),
|
||||||
.right => layout.pushViewDimensions(
|
.right => layout.pushViewDimensions(
|
||||||
ev.serial,
|
|
||||||
@intCast(i32, usable_width - width) - x + @intCast(i32, outer_padding),
|
@intCast(i32, usable_width - width) - x + @intCast(i32, outer_padding),
|
||||||
y + @intCast(i32, outer_padding),
|
y + @intCast(i32, outer_padding),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
ev.serial,
|
||||||
),
|
),
|
||||||
.top => layout.pushViewDimensions(
|
.top => layout.pushViewDimensions(
|
||||||
ev.serial,
|
|
||||||
y + @intCast(i32, outer_padding),
|
y + @intCast(i32, outer_padding),
|
||||||
x + @intCast(i32, outer_padding),
|
x + @intCast(i32, outer_padding),
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
|
ev.serial,
|
||||||
),
|
),
|
||||||
.bottom => layout.pushViewDimensions(
|
.bottom => layout.pushViewDimensions(
|
||||||
ev.serial,
|
|
||||||
y + @intCast(i32, outer_padding),
|
y + @intCast(i32, outer_padding),
|
||||||
@intCast(i32, usable_width - width) - x + @intCast(i32, outer_padding),
|
@intCast(i32, usable_width - width) - x + @intCast(i32, outer_padding),
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
|
ev.serial,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.commit(ev.serial);
|
switch (output.main_location) {
|
||||||
|
.left => layout.commit("rivertile - left", ev.serial),
|
||||||
|
.right => layout.commit("rivertile - right", ev.serial),
|
||||||
|
.top => layout.commit("rivertile - top", ev.serial),
|
||||||
|
.bottom => layout.commit("rivertile - bottom", ev.serial),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.advertise_view => {},
|
|
||||||
.advertise_done => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -285,7 +319,7 @@ pub fn main() !void {
|
||||||
.{ .name = "-outer-padding", .kind = .arg },
|
.{ .name = "-outer-padding", .kind = .arg },
|
||||||
.{ .name = "-main-location", .kind = .arg },
|
.{ .name = "-main-location", .kind = .arg },
|
||||||
.{ .name = "-main-count", .kind = .arg },
|
.{ .name = "-main-count", .kind = .arg },
|
||||||
.{ .name = "-main-factor", .kind = .arg },
|
.{ .name = "-main-ratio", .kind = .arg },
|
||||||
}).parse(argv[1..]);
|
}).parse(argv[1..]);
|
||||||
|
|
||||||
if (args.boolFlag("-h") or args.boolFlag("--help")) {
|
if (args.boolFlag("-h") or args.boolFlag("--help")) {
|
||||||
|
@ -308,9 +342,9 @@ pub fn main() !void {
|
||||||
default_main_count = std.fmt.parseUnsigned(u32, mem.span(raw), 10) catch
|
default_main_count = std.fmt.parseUnsigned(u32, mem.span(raw), 10) catch
|
||||||
fatal("invalid value '{s}' provided to -main-count", .{raw});
|
fatal("invalid value '{s}' provided to -main-count", .{raw});
|
||||||
}
|
}
|
||||||
if (args.argFlag("-main-factor")) |raw| {
|
if (args.argFlag("-main-ratio")) |raw| {
|
||||||
default_main_factor = std.fmt.parseFloat(f64, mem.span(raw)) catch
|
default_main_ratio = std.fmt.parseFloat(f64, mem.span(raw)) catch
|
||||||
fatal("invalid value '{s}' provided to -main-factor", .{raw});
|
fatal("invalid value '{s}' provided to -main-ratio", .{raw});
|
||||||
}
|
}
|
||||||
|
|
||||||
const display = wl.Display.connect(null) catch {
|
const display = wl.Display.connect(null) catch {
|
||||||
|
@ -343,8 +377,8 @@ pub fn main() !void {
|
||||||
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *Context) void {
|
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *Context) void {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.global => |global| {
|
.global => |global| {
|
||||||
if (std.cstr.cmp(global.interface, river.LayoutManagerV2.getInterface().name) == 0) {
|
if (std.cstr.cmp(global.interface, river.LayoutManagerV3.getInterface().name) == 0) {
|
||||||
context.layout_manager = registry.bind(global.name, river.LayoutManagerV2, 1) catch return;
|
context.layout_manager = registry.bind(global.name, river.LayoutManagerV3, 1) catch return;
|
||||||
} else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {
|
} else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {
|
||||||
context.addOutput(registry, global.name) catch |err| fatal("failed to bind output: {}", .{err});
|
context.addOutput(registry, global.name) catch |err| fatal("failed to bind output: {}", .{err});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue