aboutsummaryrefslogtreecommitdiff
path: root/rivertile
diff options
context:
space:
mode:
Diffstat (limited to 'rivertile')
-rw-r--r--rivertile/args.zig126
-rw-r--r--rivertile/main.zig94
2 files changed, 195 insertions, 25 deletions
diff --git a/rivertile/args.zig b/rivertile/args.zig
new file mode 100644
index 0000000..31b57bc
--- /dev/null
+++ b/rivertile/args.zig
@@ -0,0 +1,126 @@
+// This file is part of river, a dynamic tiling wayland compositor.
+//
+// Copyright 2021 The River Developers
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+const std = @import("std");
+const mem = std.mem;
+const cstr = std.cstr;
+
+const root = @import("root");
+
+pub const FlagDef = struct {
+ name: [*:0]const u8,
+ kind: enum { boolean, arg },
+};
+
+pub fn Args(comptime num_positionals: comptime_int, comptime flag_defs: []const FlagDef) type {
+ return struct {
+ const Self = @This();
+
+ positionals: [num_positionals][*:0]const u8,
+ flags: [flag_defs.len]struct {
+ name: [*:0]const u8,
+ value: union {
+ boolean: bool,
+ arg: ?[*:0]const u8,
+ },
+ },
+
+ pub fn parse(argv: [][*:0]const u8) Self {
+ var ret: Self = undefined;
+
+ // Init all flags in the flags array to false/null
+ inline for (flag_defs) |flag_def, flag_idx| {
+ switch (flag_def.kind) {
+ .boolean => ret.flags[flag_idx] = .{
+ .name = flag_def.name,
+ .value = .{ .boolean = false },
+ },
+ .arg => ret.flags[flag_idx] = .{
+ .name = flag_def.name,
+ .value = .{ .arg = null },
+ },
+ }
+ }
+
+ // Parse the argv in to the positionals and flags arrays
+ var arg_idx: usize = 0;
+ var positional_idx: usize = 0;
+ outer: while (arg_idx < argv.len) : (arg_idx += 1) {
+ var should_continue = false;
+ inline for (flag_defs) |flag_def, flag_idx| {
+ if (cstr.cmp(flag_def.name, argv[arg_idx]) == 0) {
+ switch (flag_def.kind) {
+ .boolean => ret.flags[flag_idx].value.boolean = true,
+ .arg => {
+ arg_idx += 1;
+ ret.flags[flag_idx].value.arg = if (arg_idx < argv.len)
+ argv[arg_idx]
+ else
+ root.fatal("flag '" ++ flag_def.name ++
+ "' requires an argument but none was provided!", .{});
+ },
+ }
+ // TODO: this variable exists as a workaround for the fact that
+ // using continue :outer here crashes the stage1 compiler.
+ should_continue = true;
+ }
+ }
+ if (should_continue) continue;
+
+ if (positional_idx == num_positionals) {
+ root.fatal(
+ "{} positional arguments expected but more were provided!",
+ .{num_positionals},
+ );
+ }
+
+ // This check should not be needed as this code is unreachable
+ // if num_positionals is 0. Howevere the stage1 zig compiler does
+ // not seem to be smart enough to realize this.
+ if (num_positionals > 0) {
+ ret.positionals[positional_idx] = argv[arg_idx];
+ } else {
+ unreachable;
+ }
+ positional_idx += 1;
+ }
+
+ if (positional_idx < num_positionals) {
+ root.fatal(
+ "{} positional arguments expected but only {} were provided!",
+ .{ num_positionals, positional_idx },
+ );
+ }
+
+ return ret;
+ }
+
+ pub fn boolFlag(self: Self, flag_name: [*:0]const u8) bool {
+ for (self.flags) |flag| {
+ if (cstr.cmp(flag.name, flag_name) == 0) return flag.value.boolean;
+ }
+ unreachable;
+ }
+
+ pub fn argFlag(self: Self, flag_name: [*:0]const u8) ?[*:0]const u8 {
+ for (self.flags) |flag| {
+ if (cstr.cmp(flag.name, flag_name) == 0) return flag.value.arg;
+ }
+ unreachable;
+ }
+ };
+}
diff --git a/rivertile/main.zig b/rivertile/main.zig
index 881abc3..29d9665 100644
--- a/rivertile/main.zig
+++ b/rivertile/main.zig
@@ -45,6 +45,9 @@ const wayland = @import("wayland");
const wl = wayland.client.wl;
const river = wayland.client.river;
+const Args = @import("args.zig").Args;
+const FlagDef = @import("args.zig").FlagDef;
+
const Location = enum {
top,
right,
@@ -52,9 +55,12 @@ const Location = enum {
left,
};
-// TODO: expose these as command line options
-const default_view_padding = 6;
-const default_outer_padding = 6;
+// Configured through command line options
+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;
/// We don't free resources on exit, only when output globals are removed.
const gpa = std.heap.c_allocator;
@@ -78,14 +84,20 @@ const Output = struct {
wl_output: *wl.Output,
name: u32,
- main_location: Location = .left,
- main_count: u32 = 1,
- main_factor: f64 = 0.6,
+ main_location: Location,
+ main_count: u32,
+ main_factor: f64,
layout: *river.LayoutV2 = undefined,
fn init(output: *Output, context: *Context, wl_output: *wl.Output, name: u32) !void {
- output.* = .{ .wl_output = wl_output, .name = name };
+ output.* = .{
+ .wl_output = wl_output,
+ .name = name,
+ .main_location = default_main_location,
+ .main_count = default_main_count,
+ .main_factor = default_main_factor,
+ };
if (context.initialized) try output.getLayout(context);
}
@@ -143,12 +155,12 @@ const Output = struct {
0;
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,
+ .left, .right => ev.usable_width - 2 * outer_padding,
+ .top, .bottom => ev.usable_height - 2 * outer_padding,
};
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,
+ .left, .right => ev.usable_height - 2 * outer_padding,
+ .top, .bottom => ev.usable_width - 2 * outer_padding,
};
// to make things pixel-perfect, we make the first main and first secondary
@@ -200,37 +212,37 @@ const Output = struct {
height = secondary_height + if (i == output.main_count) secondary_height_rem else 0;
}
- x += @intCast(i32, default_view_padding);
- y += @intCast(i32, default_view_padding);
- width -= 2 * default_view_padding;
- height -= 2 * default_view_padding;
+ x += @intCast(i32, view_padding);
+ y += @intCast(i32, view_padding);
+ width -= 2 * view_padding;
+ height -= 2 * view_padding;
switch (output.main_location) {
.left => layout.pushViewDimensions(
ev.serial,
- x + @intCast(i32, default_outer_padding),
- y + @intCast(i32, default_outer_padding),
+ x + @intCast(i32, outer_padding),
+ y + @intCast(i32, outer_padding),
width,
height,
),
.right => layout.pushViewDimensions(
ev.serial,
- @intCast(i32, usable_width - width) - x + @intCast(i32, default_outer_padding),
- y + @intCast(i32, default_outer_padding),
+ @intCast(i32, usable_width - width) - x + @intCast(i32, outer_padding),
+ y + @intCast(i32, outer_padding),
width,
height,
),
.top => layout.pushViewDimensions(
ev.serial,
- y + @intCast(i32, default_outer_padding),
- x + @intCast(i32, default_outer_padding),
+ y + @intCast(i32, outer_padding),
+ x + @intCast(i32, outer_padding),
height,
width,
),
.bottom => layout.pushViewDimensions(
ev.serial,
- y + @intCast(i32, default_outer_padding),
- @intCast(i32, usable_width - width) - x + @intCast(i32, default_outer_padding),
+ y + @intCast(i32, outer_padding),
+ @intCast(i32, usable_width - width) - x + @intCast(i32, outer_padding),
height,
width,
),
@@ -247,6 +259,37 @@ const Output = struct {
};
pub fn main() !void {
+ // https://github.com/ziglang/zig/issues/7807
+ const argv: [][*:0]const u8 = std.os.argv;
+ const args = Args(0, &[_]FlagDef{
+ .{ .name = "-view-padding", .kind = .arg },
+ .{ .name = "-outer-padding", .kind = .arg },
+ .{ .name = "-main-location", .kind = .arg },
+ .{ .name = "-main-count", .kind = .arg },
+ .{ .name = "-main-factor", .kind = .arg },
+ }).parse(argv[1..]);
+
+ if (args.argFlag("-view-padding")) |raw| {
+ view_padding = std.fmt.parseUnsigned(u32, mem.span(raw), 10) catch
+ fatal("invalid value '{s}' provided to -view-padding", .{raw});
+ }
+ if (args.argFlag("-outer-padding")) |raw| {
+ outer_padding = std.fmt.parseUnsigned(u32, mem.span(raw), 10) catch
+ fatal("invalid value '{s}' provided to -outer-padding", .{raw});
+ }
+ if (args.argFlag("-main-location")) |raw| {
+ default_main_location = std.meta.stringToEnum(Location, mem.span(raw)) orelse
+ fatal("invalid value '{s}' provided to -main-location", .{raw});
+ }
+ if (args.argFlag("-main-count")) |raw| {
+ 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});
+ }
+
const display = wl.Display.connect(null) catch {
std.debug.warn("Unable to connect to Wayland server.\n", .{});
std.os.exit(1);
@@ -298,7 +341,8 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *
}
}
-fn fatal(comptime format: []const u8, args: anytype) noreturn {
- std.log.err(format, args);
+pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
+ const stderr = std.io.getStdErr().writer();
+ stderr.print("err: " ++ format ++ "\n", args) catch {};
std.os.exit(1);
}