From e80b883a47d0a2fe3d42f42d1eb56969728ea162 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 26 Apr 2021 21:03:28 +0200 Subject: [PATCH] river-layout: update to v2 This implements the changes to the river-layout protocol proposed in the previous commit removing river-options. --- build.zig | 2 +- completions/bash/riverctl | 2 + completions/fish/riverctl.fish | 4 +- completions/zsh/_riverctl | 2 + doc/riverctl.1.scd | 10 ++ doc/rivertile.1.scd | 11 +++ ...iver-layout-v1.xml => river-layout-v2.xml} | 97 +++++++++++++++---- river/Layout.zig | 14 +-- river/LayoutDemand.zig | 1 - river/LayoutManager.zig | 6 +- river/Root.zig | 3 +- river/command.zig | 4 + river/command/layout.zig | 97 +++++++++++++++++++ rivertile/main.zig | 83 +++++++++++----- 14 files changed, 275 insertions(+), 61 deletions(-) rename protocol/{river-layout-v1.xml => river-layout-v2.xml} (72%) diff --git a/build.zig b/build.zig index 04ab4e9..a913616 100644 --- a/build.zig +++ b/build.zig @@ -65,7 +65,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-v1.xml"); + scanner.addProtocolPath("protocol/river-layout-v2.xml"); scanner.addProtocolPath("protocol/wlr-layer-shell-unstable-v1.xml"); scanner.addProtocolPath("protocol/wlr-output-power-management-unstable-v1.xml"); diff --git a/completions/bash/riverctl b/completions/bash/riverctl index 576ca75..e0000b7 100644 --- a/completions/bash/riverctl +++ b/completions/bash/riverctl @@ -19,6 +19,8 @@ function __riverctl_completion () zoom \ default-layout \ output-layout \ + set-layout-value \ + mod-layout-value \ set-focused-tags \ set-view-tags \ toggle-focused-tags \ diff --git a/completions/fish/riverctl.fish b/completions/fish/riverctl.fish index 9b9d09b..c3139dc 100644 --- a/completions/fish/riverctl.fish +++ b/completions/fish/riverctl.fish @@ -1,6 +1,6 @@ function __fish_riverctl_complete_no_subcommand for i in (commandline -opc) - if contains -- $i close csd-filter-add exit float-filter-add focus-output focus-view move resize snap send-to-output spawn swap toggle-float toggle-fullscreen zoom default-layout output-layout set-focused-tags set-view-tags toggle-focused-tags toggle-view-tags spawn-tagmask declare-mode enter-mode map map-pointer unmap unmap-pointer attach-mode background-color border-color-focused border-color-unfocused border-width focus-follows-cursor opacity set-repeat xcursor-theme + if contains -- $i close csd-filter-add exit float-filter-add focus-output focus-view move resize snap send-to-output spawn swap toggle-float toggle-fullscreen zoom default-layout output-layout set-layout-value mod-layout-value set-focused-tags set-view-tags toggle-focused-tags toggle-view-tags spawn-tagmask declare-mode enter-mode map map-pointer unmap unmap-pointer attach-mode background-color border-color-focused border-color-unfocused border-width focus-follows-cursor opacity set-repeat xcursor-theme return 1 end end @@ -25,6 +25,8 @@ complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a toggle-fu complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a zoom -d 'Bump the focused view to the top of the layout stack' complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a default-layout -d 'Set the layout namespace to be used by all outputs by default.' complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a output-layout -d 'Set the layout namespace of currently focused output.' +complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-layout-value -d 'Set the value with name _name_ of the layout on the focused output with matching namespace.' +complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a mod-layout-value -d 'Modify the value with name _name_ of the layout on the focused output with matching namespace.' # Tag managements complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-focused-tags -d 'Show views with tags corresponding to the set bits of tags' complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-view-tags -d 'Assign the currently focused view the tags corresponding to the set bits of tags' diff --git a/completions/zsh/_riverctl b/completions/zsh/_riverctl index feac372..1e0f60c 100644 --- a/completions/zsh/_riverctl +++ b/completions/zsh/_riverctl @@ -31,6 +31,8 @@ _riverctl() { 'zoom:Bump the focused view to the top of the layout stack' 'default-layout:Set the layout namespace to be used by all outputs by default.' 'output-layout:Set the layout namespace of currently focused output.' + 'set-layout-value:Set the value with name _name_ of the layout on the focused output with matching namespace.' + 'mod-layout-value:Modify the value with name _name_ of the layout on the focused output with matching namespace.' # Tag management 'set-focused-tags:Show views with tags corresponding to the set bits of tags' 'set-view-tags:Assign the currently focused view the tags corresponding to the set bits of tags' diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index 7c9c3d5..96589d5 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -79,6 +79,16 @@ 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. + ## TAG MANAGEMENT Tags are similar to workspaces but more flexible. You can assign views multiple diff --git a/doc/rivertile.1.scd b/doc/rivertile.1.scd index fdab055..e94b39c 100644 --- a/doc/rivertile.1.scd +++ b/doc/rivertile.1.scd @@ -13,6 +13,17 @@ rivertile - Tiled layout generator for river *rivertile* is a layout client for river. It provides a simple tiled layout split main/secondary stacks. +# VALUES + +_main_location_ (string: top, bottom, left, or right) + The location of the main area in the layout. + +_main_count_ (int) + 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. + # AUTHORS Maintained by Isaac Freund who is assisted by open diff --git a/protocol/river-layout-v1.xml b/protocol/river-layout-v2.xml similarity index 72% rename from protocol/river-layout-v1.xml rename to protocol/river-layout-v2.xml index 2ddcea3..f0c82e0 100644 --- a/protocol/river-layout-v1.xml +++ b/protocol/river-layout-v2.xml @@ -1,5 +1,5 @@ - + Copyright 2020-2021 The River Developers @@ -19,7 +19,7 @@ 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_v1 object. + 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 @@ -32,7 +32,7 @@ intentional limitation. Note that the client may need to handle multiple layout demands per - river_layout_v1 object simultaneously. + 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 @@ -40,9 +40,9 @@ only be done by creating a new major version of the extension. - + - A global factory for river_layout_v1 objects. + A global factory for river_layout_v2 objects. @@ -54,26 +54,26 @@ - - This creates a new river_layout_v1 object for the given wl_output. + + 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_v1 + 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_v1 object. + 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 @@ -88,8 +88,8 @@ - - This request indicates that the client will not use the river_layout_v1 + + This request indicates that the client will not use the river_layout_v2 object any more. @@ -98,7 +98,7 @@ 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_v1 object. + a different namespace they must create a new river_layout_v2 object. @@ -188,14 +188,69 @@ - - - The client may use this request to inform the compositor that one or - muliple of the parameters it uses to generate layouts have changed. + + + This event indicates that the value of this river_layout_v2 object + with the given name has been set to the given value. - If the client is responsible for the current view layout, the compositor - may decide to send a new layout demand to update the layout. + 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/river/Layout.zig b/river/Layout.zig index 9456b4b..c6af635 100644 --- a/river/Layout.zig +++ b/river/Layout.zig @@ -35,12 +35,12 @@ const LayoutDemand = @import("LayoutDemand.zig"); const log = std.log.scoped(.layout); -layout: *river.LayoutV1, +layout: *river.LayoutV2, 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.LayoutV1.create(client, version, id); + const layout = try river.LayoutV2.create(client, version, id); if (namespaceInUse(namespace, output, client)) { layout.sendNamespaceInUse(); @@ -91,7 +91,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.LayoutV1, request: river.LayoutV1.Request, _: ?*c_void) void { +fn handleRequestInert(layout: *river.LayoutV2, request: river.LayoutV2.Request, _: ?*c_void) void { if (request == .destroy) layout.destroy(); } @@ -128,14 +128,10 @@ pub fn startLayoutDemand(self: *Self, views: u32) void { self.output.root.trackLayoutDemands(); } -fn handleRequest(layout: *river.LayoutV1, request: river.LayoutV1.Request, self: *Self) void { +fn handleRequest(layout: *river.LayoutV2, request: river.LayoutV2.Request, self: *Self) void { switch (request) { .destroy => layout.destroy(), - // Parameters of the layout changed. We only care about this, if the - // layout is currently in use, in which case we rearrange the output. - .parameters_changed => if (self == self.output.pending.layout) self.output.arrangeViews(), - // We receive this event when the client wants to push a view dimension proposal // to the layout demand matching the serial. .push_view_dimensions => |req| { @@ -171,7 +167,7 @@ fn handleRequest(layout: *river.LayoutV1, request: river.LayoutV1.Request, self: } } -fn handleDestroy(layout: *river.LayoutV1, self: *Self) void { +fn handleDestroy(layout: *river.LayoutV2, self: *Self) void { log.debug( "destroying layout '{}' on output '{}'", .{ self.namespace, self.output.wlr_output.name }, diff --git a/river/LayoutDemand.zig b/river/LayoutDemand.zig index 4bc9426..388a2c4 100644 --- a/river/LayoutDemand.zig +++ b/river/LayoutDemand.zig @@ -21,7 +21,6 @@ const std = @import("std"); const wlr = @import("wlroots"); const wayland = @import("wayland"); const wl = wayland.server.wl; -const zriver = wayland.server.zriver; const util = @import("util.zig"); diff --git a/river/LayoutManager.zig b/river/LayoutManager.zig index 905954d..618f2ad 100644 --- a/river/LayoutManager.zig +++ b/river/LayoutManager.zig @@ -37,7 +37,7 @@ server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleSer pub fn init(self: *Self, server: *Server) !void { self.* = .{ - .global = try wl.Global.create(server.wl_server, river.LayoutManagerV1, 1, *Self, self, bind), + .global = try wl.Global.create(server.wl_server, river.LayoutManagerV2, 1, *Self, self, bind), }; server.wl_server.addDestroyListener(&self.server_destroy); @@ -49,7 +49,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.LayoutManagerV1.create(client, 1, id) catch { + const layout_manager = river.LayoutManagerV2.create(client, 1, id) catch { client.postNoMemory(); log.crit("out of memory", .{}); return; @@ -57,7 +57,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.LayoutManagerV1, request: river.LayoutManagerV1.Request, self: *Self) void { +fn handleRequest(layout_manager: *river.LayoutManagerV2, request: river.LayoutManagerV2.Request, self: *Self) void { switch (request) { .destroy => layout_manager.destroy(), diff --git a/river/Root.zig b/river/Root.zig index ae5b5b4..68994c0 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -75,7 +75,8 @@ drag_icons: std.SinglyLinkedList(DragIcon) = .{}, xwayland_unmanaged_views: if (build_options.xwayland) std.TailQueue(XwaylandUnmanaged) else - void = if (build_options.xwayland) .{}, + void = if (build_options.xwayland) + .{}, /// Number of layout demands pending before the transaction may be started. pending_layout_demands: u32 = 0, diff --git a/river/command.zig b/river/command.zig index 03668df..a6aff9c 100644 --- a/river/command.zig +++ b/river/command.zig @@ -59,12 +59,14 @@ const str_to_impl_fn = [_]struct { .{ .name = "focus-view", .impl = @import("command/focus_view.zig").focusView }, .{ .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 = "opacity", .impl = @import("command/opacity.zig").opacity }, .{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout }, .{ .name = "resize", .impl = @import("command/move.zig").resize }, .{ .name = "send-to-output", .impl = @import("command/send_to_output.zig").sendToOutput }, .{ .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 }, @@ -92,6 +94,7 @@ pub const Error = error{ InvalidDirection, InvalidPhysicalDirection, InvalidOrientation, + InvalidType, InvalidRgba, InvalidValue, UnknownOption, @@ -135,6 +138,7 @@ pub fn errToMsg(err: Error) [:0]const u8 { Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'", Error.InvalidPhysicalDirection => "invalid direction. Must be 'up', 'down', 'left' or 'right'", Error.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'", + Error.InvalidType => "invalid type", Error.InvalidRgba => "invalid color format, must be #RRGGBB or #RRGGBBAA", Error.InvalidValue => "invalid value", Error.OutOfMemory => "out of memory", diff --git a/river/command/layout.zig b/river/command/layout.zig index 12df4d2..dcef1da 100644 --- a/river/command/layout.zig +++ b/river/command/layout.zig @@ -16,6 +16,8 @@ // along with this program. If not, see . const std = @import("std"); +const mem = std.mem; +const wl = @import("wayland").server.wl; const util = @import("../util.zig"); const Error = @import("../command.zig").Error; @@ -52,3 +54,98 @@ pub fn defaultLayout( if (output.layout_namespace == null) output.handleLayoutNamespaceChange(); } } + +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( + allocator: *std.mem.Allocator, + seat: *Seat, + args: []const []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; + + 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.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 []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(); +} diff --git a/rivertile/main.zig b/rivertile/main.zig index 4133a75..881abc3 100644 --- a/rivertile/main.zig +++ b/rivertile/main.zig @@ -38,6 +38,7 @@ const std = @import("std"); const mem = std.mem; +const math = std.math; const assert = std.debug.assert; const wayland = @import("wayland"); @@ -51,9 +52,7 @@ const Location = enum { left, }; -const default_main_location: Location = .left; -const default_main_count = 1; -const default_main_factor = 0.6; +// TODO: expose these as command line options const default_view_padding = 6; const default_outer_padding = 6; @@ -62,7 +61,7 @@ const gpa = std.heap.c_allocator; const Context = struct { initialized: bool = false, - layout_manager: ?*river.LayoutManagerV1 = null, + layout_manager: ?*river.LayoutManagerV2 = null, outputs: std.TailQueue(Output) = .{}, fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void { @@ -79,7 +78,11 @@ const Output = struct { wl_output: *wl.Output, name: u32, - layout: *river.LayoutV1 = undefined, + main_location: Location = .left, + main_count: u32 = 1, + main_factor: f64 = 0.6, + + layout: *river.LayoutV2 = undefined, fn init(output: *Output, context: *Context, wl_output: *wl.Output, name: u32) !void { output.* = .{ .wl_output = wl_output, .name = name }; @@ -97,21 +100,53 @@ const Output = struct { output.layout.destroy(); } - fn layoutListener(layout: *river.LayoutV1, event: river.LayoutV1.Event, output: *Output) void { + fn layoutListener(layout: *river.LayoutV2, event: river.LayoutV2.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); + } + }, + .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; + } + } + }, + .layout_demand => |ev| { - const secondary_count = if (ev.view_count > default_main_count) - ev.view_count - default_main_count + const secondary_count = if (ev.view_count > output.main_count) + ev.view_count - output.main_count else 0; - const usable_width = switch (default_main_location) { + const usable_width = switch (output.main_location) { .left, .right => ev.usable_width - 2 * default_outer_padding, .top, .bottom => ev.usable_height - 2 * default_outer_padding, }; - const usable_height = switch (default_main_location) { + const usable_height = switch (output.main_location) { .left, .right => ev.usable_height - 2 * default_outer_padding, .top, .bottom => ev.usable_width - 2 * default_outer_padding, }; @@ -126,18 +161,18 @@ const Output = struct { var secondary_height: u32 = undefined; var secondary_height_rem: u32 = undefined; - if (default_main_count > 0 and secondary_count > 0) { - main_width = @floatToInt(u32, default_main_factor * @intToFloat(f64, usable_width)); - main_height = usable_height / default_main_count; - main_height_rem = usable_height % default_main_count; + if (output.main_count > 0 and secondary_count > 0) { + main_width = @floatToInt(u32, output.main_factor * @intToFloat(f64, usable_width)); + main_height = usable_height / output.main_count; + main_height_rem = usable_height % output.main_count; secondary_width = usable_width - main_width; secondary_height = usable_height / secondary_count; secondary_height_rem = usable_height % secondary_count; - } else if (default_main_count > 0) { + } else if (output.main_count > 0) { main_width = usable_width; - main_height = usable_height / default_main_count; - main_height_rem = usable_height % default_main_count; + main_height = usable_height / output.main_count; + main_height_rem = usable_height % output.main_count; } else if (secondary_width > 0) { main_width = 0; secondary_width = usable_width; @@ -152,17 +187,17 @@ const Output = struct { var width: u32 = undefined; var height: u32 = undefined; - if (i < default_main_count) { + if (i < output.main_count) { x = 0; y = @intCast(i32, (i * main_height) + if (i > 0) main_height_rem else 0); width = main_width; height = main_height + if (i == 0) main_height_rem else 0; } else { x = @intCast(i32, main_width); - y = @intCast(i32, (i - default_main_count) * secondary_height + - if (i > default_main_count) secondary_height_rem else 0); + y = @intCast(i32, (i - output.main_count) * secondary_height + + if (i > output.main_count) secondary_height_rem else 0); width = secondary_width; - height = secondary_height + if (i == default_main_count) secondary_height_rem else 0; + height = secondary_height + if (i == output.main_count) secondary_height_rem else 0; } x += @intCast(i32, default_view_padding); @@ -170,7 +205,7 @@ const Output = struct { width -= 2 * default_view_padding; height -= 2 * default_view_padding; - switch (default_main_location) { + switch (output.main_location) { .left => layout.pushViewDimensions( ev.serial, x + @intCast(i32, default_outer_padding), @@ -242,8 +277,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.LayoutManagerV1.getInterface().name) == 0) { - context.layout_manager = registry.bind(global.name, river.LayoutManagerV1, 1) catch return; + if (std.cstr.cmp(global.interface, river.LayoutManagerV2.getInterface().name) == 0) { + context.layout_manager = registry.bind(global.name, river.LayoutManagerV2, 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}); }