diff --git a/build.zig b/build.zig
index d2f43ee..389dff5 100644
--- a/build.zig
+++ b/build.zig
@@ -55,7 +55,7 @@ pub fn build(b: *zbs.Builder) !void {
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
scanner.addProtocolPath("protocol/river-control-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-output-power-management-unstable-v1.xml");
diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index a485a73..994c78d 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -90,15 +90,11 @@ over the Wayland protocol.
Set the layout namespace of currently focused output, overriding
the value set with *default-layout* if any.
-*set-layout-value* _namespace_ _type_ _name_ _value_
- Set 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.
-
-*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.
+*send-layout-cmd* _namespace_ _command_
+ Send _command_ to the layout client on the currently focused output
+ with the given _namespace_, if any. What commands a layout client
+ understands depends on the layout client. For rivertile, see the
+ documentation in the *rivertile*(1) man page.
## TAG MANAGEMENT
diff --git a/doc/rivertile.1.scd b/doc/rivertile.1.scd
index c0658c1..d12a4ce 100644
--- a/doc/rivertile.1.scd
+++ b/doc/rivertile.1.scd
@@ -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
layout. (Default: 1)
-*-main-factor* _ratio_
- Set the initial ratio of main area to total layout area. (Default: 0.6)
+*-main-ratio* _ratio_
+ 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).
-_main_location_ (string: top, bottom, left, or right)
- The location of the main area in the layout.
+*set-main-location* [*top*|*bottom*|*left*|*right*]
+ Set the location of the main area in the layout.
-_main_count_ (int)
- The number of views in the main area of the layout.
+*set-main-count* _count_
+ Set the number of views in the main area of the layout.
-_main_factor_ (fixed: [0.1, 0.9])
- The ratio of main area to total layout area.
+*mod-main-count* _delta_
+ 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
@@ -55,9 +66,9 @@ Start *rivertile* with 4 pixels outer padding and 2 main views:
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
diff --git a/example/init b/example/init
index 07e0347..886beae 100755
--- a/example/init
+++ b/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
riverctl map normal $mod Return zoom
-# Mod+H and Mod+L to decrease/increase the main_factor value of rivertile by 0.05
-riverctl map normal $mod H mod-layout-value rivertile fixed main_factor -0.05
-riverctl map normal $mod L mod-layout-value rivertile fixed main_factor +0.05
+# Mod+H and Mod+L to decrease/increase the main ratio of rivertile(1)
+riverctl map normal $mod H send-layout-cmd rivertile "mod-main-ratio -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.
-riverctl map normal $mod+Shift H mod-layout-value rivertile int main_count +1
-riverctl map normal $mod+Shift L mod-layout-value rivertile int main_count -1
+# Mod+Shift+H and Mod+Shift+L to increment/decrement the main count of rivertile(1)
+riverctl map normal $mod+Shift H send-layout-cmd rivertile "mod-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
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
# 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 Right set-layout-value rivertile string main_location right
-riverctl map normal $mod Down set-layout-value rivertile string main_location bottom
-riverctl map normal $mod Left set-layout-value rivertile string main_location left
+riverctl map normal $mod Up send-layout-cmd rivertile "set-main-location top"
+riverctl map normal $mod Right send-layout-cmd rivertile "set-main-location right"
+riverctl map normal $mod Down send-layout-cmd rivertile "set-main-location bottom"
+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
# normal mode. This makes it useful for testing a nested wayland compositor
diff --git a/protocol/river-layout-v2.xml b/protocol/river-layout-v2.xml
deleted file mode 100644
index f0c82e0..0000000
--- a/protocol/river-layout-v2.xml
+++ /dev/null
@@ -1,256 +0,0 @@
-
-
-
- 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.
-
-
-
- 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.
-
-
-
-
- A global factory for river_layout_v2 objects.
-
-
-
-
- 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.
-
-
-
-
-
- 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.
-
-
-
-
-
-
-
-
-
- This interface allows clients to receive layout demands from the
- compositor for a specific output and subsequently propose positions and
- dimensions of individual views.
-
-
-
-
-
-
-
-
-
- This request indicates that the client will not use the river_layout_v2
- object any more.
-
-
-
-
-
- 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.
-
-
-
-
-
- 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.
-
-
-
-
-
-
-
-
-
-
- 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.
-
-
-
-
-
-
-
-
- This event is sent by the server as the last event of the layout
- demand with matching serial, after all advertise_view events.
-
-
-
-
-
-
- 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.
-
-
-
-
-
-
-
-
-
-
- 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.
-
-
-
-
-
-
- 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)
-
-
-
-
-
-
-
- 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)
-
-
-
-
-
-
-
- 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)
-
-
-
-
-
-
-
- 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)
-
-
-
-
-
-
-
- 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)
-
-
-
-
-
-
diff --git a/protocol/river-layout-v3.xml b/protocol/river-layout-v3.xml
new file mode 100644
index 0000000..3363355
--- /dev/null
+++ b/protocol/river-layout-v3.xml
@@ -0,0 +1,181 @@
+
+
+
+ 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.
+
+
+
+ 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.
+
+
+
+
+ A global factory for river_layout_v3 objects.
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+ This interface allows clients to receive layout demands from the
+ compositor for a specific output and subsequently propose positions and
+ dimensions of individual views.
+
+
+
+
+
+
+
+
+
+ This request indicates that the client will not use the river_layout_v3
+ object any more.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
diff --git a/river/Layout.zig b/river/Layout.zig
index eca3d7b..9fe6109 100644
--- a/river/Layout.zig
+++ b/river/Layout.zig
@@ -36,12 +36,12 @@ const LayoutDemand = @import("LayoutDemand.zig");
const log = std.log.scoped(.layout);
-layout: *river.LayoutV2,
+layout: *river.LayoutV3,
namespace: []const u8,
output: *Output,
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)) {
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
/// 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();
}
@@ -108,28 +108,19 @@ pub fn startLayoutDemand(self: *Self, views: u32) void {
log.err("failed starting layout demand", .{});
return;
};
- const serial = self.output.layout_demand.?.serial;
- // Then we let the client know that we require a layout
self.layout.sendLayoutDemand(
views,
self.output.usable_box.width,
self.output.usable_box.height,
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();
}
-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) {
.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();
}
diff --git a/river/LayoutManager.zig b/river/LayoutManager.zig
index 98df1c7..168a0e5 100644
--- a/river/LayoutManager.zig
+++ b/river/LayoutManager.zig
@@ -38,7 +38,7 @@ server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleSer
pub fn init(self: *Self) !void {
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);
@@ -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 {
- const layout_manager = river.LayoutManagerV2.create(client, 1, id) catch {
+ const layout_manager = river.LayoutManagerV3.create(client, 1, id) catch {
client.postNoMemory();
log.crit("out of memory", .{});
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);
}
-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) {
.destroy => layout_manager.destroy(),
diff --git a/river/command.zig b/river/command.zig
index b3fc953..47413ee 100644
--- a/river/command.zig
+++ b/river/command.zig
@@ -64,14 +64,13 @@ const str_to_impl_fn = [_]struct {
.{ .name = "list-inputs", .impl = @import("command/input.zig").listInputs },
.{ .name = "map", .impl = @import("command/map.zig").map },
.{ .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 = "output-layout", .impl = @import("command/layout.zig").outputLayout },
.{ .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 = "set-cursor-warp", .impl = @import("command/config.zig").setCursorWarp },
.{ .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-view-tags", .impl = @import("command/tags.zig").setViewTags },
.{ .name = "snap", .impl = @import("command/move.zig").snap },
@@ -99,7 +98,6 @@ pub const Error = error{
InvalidButton,
InvalidCharacter,
InvalidDirection,
- InvalidType,
InvalidPhysicalDirection,
InvalidOrientation,
InvalidRgba,
@@ -144,7 +142,6 @@ pub fn errToMsg(err: Error) [:0]const u8 {
Error.InvalidButton => "invalid button",
Error.InvalidCharacter => "invalid character in argument",
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.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'",
Error.InvalidRgba => "invalid color format, must be #RRGGBB or #RRGGBBAA",
diff --git a/river/command/layout.zig b/river/command/layout.zig
index 5fcf432..da67e82 100644
--- a/river/command/layout.zig
+++ b/river/command/layout.zig
@@ -56,28 +56,20 @@ pub fn defaultLayout(
}
}
-const SetType = enum {
- int,
- fixed,
- string,
-};
-
-/// 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(
+/// riverctl send-layout-cmd rivertile "mod-main-count 1"
+/// riverctl send-layout-cmd rivertile "mod-main-factor -0.1"
+/// riverctl send-layout-cmd rivertile "main-location top"
+pub fn sendLayoutCmd(
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(SetType, args[2]) orelse return Error.InvalidType;
+ if (args.len < 3) return Error.NotEnoughArguments;
+ if (args.len > 3) return Error.TooManyArguments;
const output = seat.focused_output;
+ const target_namespace = args[1];
var it = output.layouts.first;
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;
} 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.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));
- },
- }
+ layout.layout.sendUserCommand(args[2]);
output.arrangeViews();
}
diff --git a/rivertile/main.zig b/rivertile/main.zig
index d2c88ac..6600563 100644
--- a/rivertile/main.zig
+++ b/rivertile/main.zig
@@ -59,11 +59,19 @@ const usage =
\\ layout. (Default left)
\\ -main-count Set the initial number of views in the main area of the
\\ 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)
\\
;
+const Command = enum {
+ @"set-main-location",
+ @"set-main-count",
+ @"mod-main-count",
+ @"set-main-ratio",
+ @"mod-main-ratio",
+};
+
const Location = enum {
top,
right,
@@ -76,14 +84,14 @@ var view_padding: u32 = 6;
var outer_padding: u32 = 6;
var default_main_location: Location = .left;
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.
const gpa = std.heap.c_allocator;
const Context = struct {
initialized: bool = false,
- layout_manager: ?*river.LayoutManagerV2 = null,
+ layout_manager: ?*river.LayoutManagerV3 = null,
outputs: std.TailQueue(Output) = .{},
fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void {
@@ -102,9 +110,9 @@ const Output = struct {
main_location: Location,
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 {
output.* = .{
@@ -112,7 +120,7 @@ const Output = struct {
.name = name,
.main_location = default_main_location,
.main_count = default_main_count,
- .main_factor = default_main_factor,
+ .main_ratio = default_main_ratio,
};
if (context.initialized) try output.getLayout(context);
}
@@ -128,39 +136,63 @@ const Output = struct {
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) {
.namespace_in_use => fatal("namespace 'rivertile' already in use.", .{}),
- .set_int_value => |ev| {
- if (mem.eql(u8, mem.span(ev.name), "main_count")) {
- if (ev.value > 0) output.main_count = @intCast(u32, ev.value);
+ .user_command => |ev| {
+ var it = mem.tokenize(mem.span(ev.command), " ");
+ 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;
}
- },
- .mod_int_value => |ev| {
- if (mem.eql(u8, mem.span(ev.name), "main_count")) {
- const result = @as(i33, output.main_count) + ev.delta;
- if (result > 0) output.main_count = @intCast(u32, result);
- }
- },
-
- .set_fixed_value => |ev| {
- if (mem.eql(u8, mem.span(ev.name), "main_factor")) {
- output.main_factor = math.clamp(ev.value.toDouble(), 0.1, 0.9);
- }
- },
- .mod_fixed_value => |ev| {
- if (mem.eql(u8, mem.span(ev.name), "main_factor")) {
- const new_value = ev.delta.toDouble() + output.main_factor;
- output.main_factor = math.clamp(new_value, 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;
- }
+ 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;
+ };
+ },
+ .@"set-main-count" => {
+ output.main_count = std.fmt.parseInt(u32, raw_arg, 10) catch |err| {
+ 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);
+ },
+ .@"set-main-ratio" => {
+ const arg = std.fmt.parseFloat(f64, raw_arg) catch |err| {
+ std.log.err("failed to parse argument: {}", .{err});
+ return;
+ };
+ output.main_ratio = math.clamp(arg, 0.1, 0.9);
+ },
+ .@"mod-main-ratio" => {
+ const arg = std.fmt.parseFloat(f64, raw_arg) catch |err| {
+ std.log.err("failed to parse argument: {}", .{err});
+ return;
+ };
+ output.main_ratio = math.clamp(output.main_ratio + arg, 0.1, 0.9);
+ },
}
},
@@ -191,7 +223,7 @@ const Output = struct {
var secondary_height_rem: u32 = undefined;
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_rem = usable_height % main_count;
@@ -236,41 +268,43 @@ const Output = struct {
switch (output.main_location) {
.left => layout.pushViewDimensions(
- ev.serial,
x + @intCast(i32, outer_padding),
y + @intCast(i32, outer_padding),
width,
height,
+ ev.serial,
),
.right => layout.pushViewDimensions(
- ev.serial,
@intCast(i32, usable_width - width) - x + @intCast(i32, outer_padding),
y + @intCast(i32, outer_padding),
width,
height,
+ ev.serial,
),
.top => layout.pushViewDimensions(
- ev.serial,
y + @intCast(i32, outer_padding),
x + @intCast(i32, outer_padding),
height,
width,
+ ev.serial,
),
.bottom => layout.pushViewDimensions(
- ev.serial,
y + @intCast(i32, outer_padding),
@intCast(i32, usable_width - width) - x + @intCast(i32, outer_padding),
height,
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 = "-main-location", .kind = .arg },
.{ .name = "-main-count", .kind = .arg },
- .{ .name = "-main-factor", .kind = .arg },
+ .{ .name = "-main-ratio", .kind = .arg },
}).parse(argv[1..]);
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
fatal("invalid value '{s}' provided to -main-count", .{raw});
}
- if (args.argFlag("-main-factor")) |raw| {
- default_main_factor = std.fmt.parseFloat(f64, mem.span(raw)) catch
- fatal("invalid value '{s}' provided to -main-factor", .{raw});
+ if (args.argFlag("-main-ratio")) |raw| {
+ default_main_ratio = std.fmt.parseFloat(f64, mem.span(raw)) catch
+ fatal("invalid value '{s}' provided to -main-ratio", .{raw});
}
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 {
switch (event) {
.global => |global| {
- if (std.cstr.cmp(global.interface, river.LayoutManagerV2.getInterface().name) == 0) {
- context.layout_manager = registry.bind(global.name, river.LayoutManagerV2, 1) catch return;
+ if (std.cstr.cmp(global.interface, river.LayoutManagerV3.getInterface().name) == 0) {
+ context.layout_manager = registry.bind(global.name, river.LayoutManagerV3, 1) catch return;
} 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});
}