commit - 0d587e8bfd989d8c425430d9832ce55186f26831
commit + 9ca3db0820a063e58fc45a2bcbdffa061ea311ec
blob - ae040fcb2af5620698ec753e51a7106c713b9fad
blob + 3b72907378186aea4b7cef3e8272441751a3001f
--- .builds/alpine.yml
+++ .builds/alpine.yml
cd ..
# Eat Github's resources rather than the Zig Software Foundation's resources!
- wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-linux-0.15.1.tar.xz
- tar xf zig-x86_64-linux-0.15.1.tar.xz
- sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/
- sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig
+ wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz
+ tar xf zig-x86_64-linux-0.16.0.tar.xz
+ sudo mv zig-x86_64-linux-0.16.0/zig /usr/bin/
+ sudo mv zig-x86_64-linux-0.16.0/lib /usr/lib/zig
- build: |
cd river-classic
zig build --summary all
blob - be19c56893a7a4681624b3c9e0559b71f1460b5e
blob + 28bc349e3041befb38f09e19f10bb5999d0366d0
--- .builds/archlinux.yml
+++ .builds/archlinux.yml
cd ..
# Eat Github's resources rather than the Zig Software Foundation's resources!
- wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-linux-0.15.1.tar.xz
- tar xf zig-x86_64-linux-0.15.1.tar.xz
- sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/
- sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig
+ wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz
+ tar xf zig-x86_64-linux-0.16.0.tar.xz
+ sudo mv zig-x86_64-linux-0.16.0/zig /usr/bin/
+ sudo mv zig-x86_64-linux-0.16.0/lib /usr/lib/zig
- build: |
cd river-classic
- zig build --summary all
+ # Arch's glibc version has SFrame sections in crt objects, force llvm/lld usage as a workaround
+ # https://codeberg.org/ziglang/zig/issues/31272
+ zig build -Dllvm --summary all
- build_xwayland: |
cd river-classic
- zig build --summary all -Dxwayland
+ zig build -Dllvm --summary all -Dxwayland
- fmt: |
cd river-classic
zig fmt --check river/
blob - 319157185845a3fb650c1d681f6ee74f1e3a6d02
blob + 220978579418c077b13a7e3f74f1adc6b528d53f
--- .builds/freebsd.yml
+++ .builds/freebsd.yml
cd ..
# Eat Github's resources rather than the Zig Software Foundation's resources!
- wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-freebsd-0.15.1.tar.xz
- tar xf zig-x86_64-freebsd-0.15.1.tar.xz
- sudo mv zig-x86_64-freebsd-0.15.1/zig /usr/bin/
- sudo mv zig-x86_64-freebsd-0.15.1/lib /usr/lib/zig
+ wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.16.0/zig-x86_64-freebsd-0.16.0.tar.xz
+ tar xf zig-x86_64-freebsd-0.16.0.tar.xz
+ sudo mv zig-x86_64-freebsd-0.16.0/zig /usr/bin/
+ sudo mv zig-x86_64-freebsd-0.16.0/lib /usr/lib/zig
- build: |
cd river-classic
zig build --summary all
blob - 3389c86c99467f36e872462555659b4a6bcb399a
blob + 03cb27d540eacf456abb8d4e94196d3456ea6ea2
--- .gitignore
+++ .gitignore
.zig-cache/
zig-out/
+zig-pkg/
blob - 0f84f01e6712159f4dcb605775b5ebc1e1e831aa
blob + 90e06fcb683f84198268e36cc2bfa5dd21612749
--- README.md
+++ README.md
installed. The "development" versions are required if applicable to your
distribution.
-- [zig](https://ziglang.org/download/) 0.15
+- [zig](https://ziglang.org/download/) 0.16
- wayland
- wayland-protocols
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.20
blob - 81128dc1e29081f8fc72c6aeb477aa151054cd71
blob + 0ba03fb1ec9c74142ad64a7eff226c71bc80160b
--- build.zig
+++ build.zig
const fs = std.fs;
const mem = std.mem;
+const manifest = @import("build.zig.zon");
+const version = manifest.version;
+
const Scanner = @import("wayland").Scanner;
pub fn build(b: *Build) !void {
const strip = b.option(bool, "strip", "Omit debug information") orelse false;
const pie = b.option(bool, "pie", "Build a Position Independent Executable") orelse false;
+ const use_llvm = b.option(bool, "llvm", "Force use of Zig's LLVM backend and the lld linker") orelse false;
const omit_frame_pointer = switch (optimize) {
.Debug, .ReleaseSafe => false,
) orelse scdoc_found: {
_ = b.findProgram(&.{"scdoc"}, &.{}) catch |err| switch (err) {
error.FileNotFound => break :scdoc_found false,
- else => return err,
};
break :scdoc_found true;
};
const git_describe_long = b.runAllowFail(
&.{ "git", "-C", b.build_root.path orelse ".", "describe", "--long" },
&ret,
- .Inherit,
+ .inherit,
) catch break :blk version;
var it = mem.splitScalar(u8, mem.trim(u8, git_describe_long, &std.ascii.whitespace), '-');
.target = target,
.optimize = optimize,
.strip = strip,
+ .link_libc = true,
}),
+ .use_llvm = use_llvm,
+ .use_lld = use_llvm,
});
river.root_module.addOptions("build_options", options);
- river.linkLibC();
- river.linkSystemLibrary("libevdev");
- river.linkSystemLibrary("libinput");
- river.linkSystemLibrary("wayland-server");
- river.linkSystemLibrary("wlroots-0.20");
- river.linkSystemLibrary("xkbcommon");
- river.linkSystemLibrary("pixman-1");
+ river.root_module.linkSystemLibrary("libevdev", .{});
+ river.root_module.linkSystemLibrary("libinput", .{});
+ river.root_module.linkSystemLibrary("wayland-server", .{});
+ river.root_module.linkSystemLibrary("wlroots-0.20", .{});
+ river.root_module.linkSystemLibrary("xkbcommon", .{});
+ river.root_module.linkSystemLibrary("pixman-1", .{});
river.root_module.addImport("wayland", wayland);
river.root_module.addImport("xkbcommon", xkbcommon);
river.root_module.addImport("flags", flags);
river.root_module.addImport("globber", globber);
- river.addCSourceFile(.{
+ river.root_module.addCSourceFile(.{
.file = b.path("river/wlroots_log_wrapper.c"),
.flags = &.{ "-std=c99", "-O2" },
});
.target = target,
.optimize = optimize,
.strip = strip,
+ .link_libc = true,
}),
+ .use_llvm = use_llvm,
+ .use_lld = use_llvm,
});
riverctl.root_module.addOptions("build_options", options);
riverctl.root_module.addImport("flags", flags);
riverctl.root_module.addImport("wayland", wayland);
- riverctl.linkLibC();
- riverctl.linkSystemLibrary("wayland-client");
+ riverctl.root_module.linkSystemLibrary("wayland-client", .{});
riverctl.pie = pie;
riverctl.root_module.omit_frame_pointer = omit_frame_pointer;
.target = target,
.optimize = optimize,
.strip = strip,
+ .link_libc = true,
}),
+ .use_llvm = use_llvm,
+ .use_lld = use_llvm,
});
rivertile.root_module.addOptions("build_options", options);
rivertile.root_module.addImport("flags", flags);
rivertile.root_module.addImport("wayland", wayland);
- rivertile.linkLibC();
- rivertile.linkSystemLibrary("wayland-client");
+ rivertile.root_module.linkSystemLibrary("wayland-client", .{});
rivertile.pie = pie;
rivertile.root_module.omit_frame_pointer = omit_frame_pointer;
// This makes the caching work for the Workaround, and the extra argument is ignored by /bin/sh.
scdoc.addFileArg(b.path("doc/" ++ page ++ ".1.scd"));
- const stdout = scdoc.captureStdOut();
+ const stdout = scdoc.captureStdOut(.{});
b.getInstallStep().dependOn(&b.addInstallFile(stdout, "share/man/man1/" ++ page ++ ".1").step);
}
}
test_step.dependOn(&run_globber_test.step);
}
}
-
-const version = manifest.version;
-/// Getting rid of this wart requires upstream zig improvements.
-/// See: https://github.com/ziglang/zig/issues/22775
-const manifest: struct {
- name: @Type(.enum_literal),
- version: []const u8,
- paths: []const []const u8,
- dependencies: struct {
- pixman: struct {
- url: []const u8,
- hash: []const u8,
- },
- wayland: struct {
- url: []const u8,
- hash: []const u8,
- },
- wlroots: struct {
- url: []const u8,
- hash: []const u8,
- },
- xkbcommon: struct {
- url: []const u8,
- hash: []const u8,
- },
- },
- fingerprint: u64,
-} = @import("build.zig.zon");
blob - ba347f5cfa48e9a57ded29b50e31dd230ec7ef4f
blob + 6c5f5ece7ff92614a359343967c7006f19bcd07d
--- build.zig.zon
+++ build.zig.zon
.hash = "pixman-0.3.0-LClMnz2VAAAs7QSCGwLimV5VUYx0JFnX5xWU6HwtMuDX",
},
.wayland = .{
- .url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.4.0.tar.gz",
- .hash = "wayland-0.4.0-lQa1khbMAQAsLS2eBR7M5lofyEGPIbu2iFDmoz8lPC27",
+ .url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.6.0.tar.gz",
+ .hash = "wayland-0.6.0-lQa1kqz8AQADQmdNJsNhLoNHcnEGEUjrOaPV-dtEnEmX",
},
.wlroots = .{
- .url = "https://codeberg.org/ifreund/zig-wlroots/archive/v0.20.0.tar.gz",
- .hash = "wlroots-0.20.0-jmOlcmtCBADS6eoJ6mkeiSNZkibrhD-c5Qwn-LiM86r1",
+ .url = "https://codeberg.org/ifreund/zig-wlroots/archive/v0.20.1.tar.gz",
+ .hash = "wlroots-0.20.1-jmOlcqNVBAB3uB5oqBTzpRlwu-FmMyyZMVAWCe5kmcSt",
},
.xkbcommon = .{
.url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.3.0.tar.gz",
blob - 79adde984759ab0b66ef8430baea4b164987fca5
blob + 72f197fba7b43369df6355d671948c9b071d40d9
--- common/flags.zig
+++ common/flags.zig
-// Zero allocation argument parsing for unix-like systems.
-// Released under the Zero Clause BSD (0BSD) license:
-//
-// Copyright 2023 Isaac Freund
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted.
-//
-// 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.
+// SPDX-FileCopyrightText: © 2023 Isaac Freund
+// SPDX-License-Identifier: 0BSD
const std = @import("std");
const mem = std.mem;
pub const Flag = struct {
- name: [:0]const u8,
+ name: []const u8,
kind: enum { boolean, arg },
};
-pub fn parser(comptime Arg: type, comptime flags: []const Flag) type {
- switch (Arg) {
- // TODO consider allowing []const u8
- [:0]const u8, [*:0]const u8 => {}, // ok
- else => @compileError("invalid argument type: " ++ @typeName(Arg)),
- }
+pub fn parser(comptime flags: []const Flag) type {
return struct {
pub const Result = struct {
/// Remaining args after the recognized flags
- args: []const Arg,
+ args: []const [:0]const u8,
/// Data obtained from parsed flags
flags: Flags,
pub const Flags = flags_type: {
- var fields: []const std.builtin.Type.StructField = &.{};
- for (flags) |flag| {
- const field: std.builtin.Type.StructField = switch (flag.kind) {
- .boolean => .{
- .name = flag.name,
- .type = bool,
- .default_value_ptr = &false,
- .is_comptime = false,
- .alignment = @alignOf(bool),
+ const Attributes = std.builtin.Type.StructField.Attributes;
+ var names: [flags.len][]const u8 = undefined;
+ var types: [flags.len]type = undefined;
+ var attrs: [flags.len]Attributes = undefined;
+ for (flags, &names, &types, &attrs) |flag, *name, *ty, *attr| {
+ name.* = flag.name;
+ switch (flag.kind) {
+ .boolean => {
+ ty.* = bool;
+ attr.* = .{ .default_value_ptr = &false };
},
- .arg => .{
- .name = flag.name,
- .type = ?[:0]const u8,
- .default_value_ptr = &@as(?[:0]const u8, null),
- .is_comptime = false,
- .alignment = @alignOf(?[:0]const u8),
+ .arg => {
+ ty.* = ?[:0]const u8;
+ attr.* = .{ .default_value_ptr = &@as(ty.*, null) };
},
- };
- fields = fields ++ [_]std.builtin.Type.StructField{field};
+ }
}
- break :flags_type @Type(.{ .@"struct" = .{
- .layout = .auto,
- .fields = fields,
- .decls = &.{},
- .is_tuple = false,
- } });
+ break :flags_type @Struct(.auto, null, &names, &types, &attrs);
};
};
- pub fn parse(args: []const Arg) !Result {
+ pub fn parse(args: []const [:0]const u8) error{MissingFlagArgument}!Result {
var result_flags: Result.Flags = .{};
var i: usize = 0;
outer: while (i < args.len) : (i += 1) {
- const arg = switch (Arg) {
- [*:0]const u8 => mem.sliceTo(args[i], 0),
- [:0]const u8 => args[i],
- else => unreachable,
- };
inline for (flags) |flag| {
- if (mem.eql(u8, "-" ++ flag.name, arg)) {
+ if (mem.eql(u8, "-" ++ flag.name, args[i])) {
switch (flag.kind) {
.boolean => @field(result_flags, flag.name) = true,
.arg => {
"' requires an argument but none was provided!", .{});
return error.MissingFlagArgument;
}
- @field(result_flags, flag.name) = switch (Arg) {
- [*:0]const u8 => mem.sliceTo(args[i], 0),
- [:0]const u8 => args[i],
- else => unreachable,
- };
+ @field(result_flags, flag.name) = args[i];
},
}
continue :outer;
blob - 06e30d8291349bf132dce025603df0e674102174
blob + f55ea14f8c29d082be69fe50325d1f45761c2e76
--- river/Control.zig
+++ river/Control.zig
client.postNoMemory();
return;
};
- control.args_map.putNoClobber(.{ .client = client, .id = id }, .{}) catch {
+ control.args_map.putNoClobber(.{ .client = client, .id = id }, .empty) catch {
control_v1.destroy();
client.postNoMemory();
return;
blob - 0d5c47246b1963a09b28e8e08ac6a81f7319f817
blob + feb19d2b2f7fcd9fc1fd1ed4bf781a640b474dd1
--- river/Cursor.zig
+++ river/Cursor.zig
/// Keeps track of the last known location of all touch points in layout coordinates.
/// This information is necessary for proper touch dnd support if there are multiple touch points.
-touch_points: std.AutoHashMapUnmanaged(i32, LayoutPoint) = .{},
+touch_points: std.AutoHashMapUnmanaged(i32, LayoutPoint) = .empty,
axis: wl.Listener(*wlr.Pointer.event.Axis) = wl.Listener(*wlr.Pointer.event.Axis).init(handleAxis),
frame: wl.Listener(*wlr.Cursor) = wl.Listener(*wlr.Cursor).init(handleFrame),
.passthrough => {
cursor.updateFocusFollowsCursorTarget();
if (!cursor.hidden) {
- const now = posix.clock_gettime(.MONOTONIC) catch @panic("CLOCK_MONOTONIC not supported");
- // 2^32-1 milliseconds is ~50 days, which is a realistic uptime.
- // This means that we must wrap if the monotonic time is greater than
- // 2^32-1 milliseconds and hope that clients don't get too confused.
- const msec: u32 = @intCast(@rem(
- now.sec *% std.time.ms_per_s +% @divTrunc(now.nsec, std.time.ns_per_ms),
- math.maxInt(u32),
- ));
- cursor.passthrough(msec);
+ cursor.passthrough(util.msecTimestamp());
}
},
// TODO: Leave down mode if the target surface is no longer visible.
blob - 96df75296813758781f771c062383f0dd082a191
blob + b5232766665dd99c3c883256953c1fa258da24d7
--- river/InputManager.zig
+++ river/InputManager.zig
/// List of input device configurations. Ordered by glob generality, with
/// the most general towards the start and the most specific towards the end.
-configs: std.ArrayList(InputConfig) = .{},
+configs: std.ArrayList(InputConfig) = .empty,
devices: wl.list.Head(InputDevice, .link),
seats: wl.list.Head(Seat, .link),
blob - d0f70d2e4489650491157466d44d9ae88c34eaed
blob + 1cd4f98f8700edf79a2ed2afa6bf4f82b024fa86
--- river/Mode.zig
+++ river/Mode.zig
const SwitchMapping = @import("SwitchMapping.zig");
name: [:0]const u8,
-mappings: std.ArrayListUnmanaged(Mapping) = .{},
-pointer_mappings: std.ArrayListUnmanaged(PointerMapping) = .{},
-switch_mappings: std.ArrayListUnmanaged(SwitchMapping) = .{},
+mappings: std.ArrayList(Mapping) = .empty,
+pointer_mappings: std.ArrayList(PointerMapping) = .empty,
+switch_mappings: std.ArrayList(SwitchMapping) = .empty,
pub fn deinit(mode: *Mode) void {
util.gpa.free(mode.name);
blob - 7a8228c9ed651c4ec2dd447134eca1905409e6ea
blob + 5b20a4104891e8b6e16b16ec97c807a98004055b
--- river/Output.zig
+++ river/Output.zig
error.CommitFailed => log.err("output commit failed for {s}", .{output.wlr_output.name}),
};
- var now = posix.clock_gettime(.MONOTONIC) catch @panic("CLOCK_MONOTONIC not supported");
+ var now = util.timestamp();
scene_output.sendFrameDone(&now);
}
blob - 7e05cf8750e58e9b74ddd33c05aa091b8f732eb9
blob + a6a41573961f71590d7f226e753d514d514b6893
--- river/OutputStatus.zig
+++ river/OutputStatus.zig
const log = std.log.scoped(.river_status);
resources: wl.list.Head(zriver.OutputStatusV1, null),
-view_tags: std.ArrayListUnmanaged(u32) = .{},
+view_tags: std.ArrayList(u32) = .empty,
focused_tags: u32 = 0,
urgent_tags: u32 = 0,
blob - ae7e298cdeac2c5e09ae5c836c990eb78286cb68
blob + 20ab1f7a34e8b22a6b2ac5945a06fc1444fc3607
--- river/Seat.zig
+++ river/Seat.zig
return;
};
if (out) |s| {
- var stdout = std.fs.File.stdout().writer(&.{});
- stdout.interface.print("{s}", .{s}) catch |err| {
- std.log.scoped(.command).err("{s}: write to stdout failed {}", .{ args[0], err });
- };
+ std.log.scoped(.command).info("mapped command output: {s}", .{s});
}
}
blob - c7b566323698f5ea60c0343b1de05ad3ef6ac9ba
blob + dc277c538fa6659f438712612f53a3ce1013c454
--- river/Server.zig
+++ river/Server.zig
server.* = .{
.wl_server = wl_server,
- .sigint_source = try loop.addSignal(*wl.Server, posix.SIG.INT, terminate, wl_server),
- .sigterm_source = try loop.addSignal(*wl.Server, posix.SIG.TERM, terminate, wl_server),
+ .sigint_source = try loop.addSignal(*wl.Server, @intFromEnum(posix.SIG.INT), terminate, wl_server),
+ .sigterm_source = try loop.addSignal(*wl.Server, @intFromEnum(posix.SIG.TERM), terminate, wl_server),
.fixes = try wlr.Fixes.create(wl_server, 1),
blob - 8cba7d0e9ab5371a213a477a38d87eb33754ae7b
blob + 5e4bf7410b50bfaccd1dfa3cdf1e49029afaa645
--- river/View.zig
+++ river/View.zig
pub fn sendFrameDone(view: View) void {
assert(view.mapped and !view.destroying);
- const now = posix.clock_gettime(.MONOTONIC) catch @panic("CLOCK_MONOTONIC not supported");
+ const now = util.timestamp();
view.rootSurface().?.sendFrameDone(&now);
}
blob - 59dd8a004a0a4f5a67c2f955455094f50f1a6e34
blob + 9ef5b9796ba04f9e7610714da98d1b34c8fc8736
--- river/c.zig
+++ river/c.zig
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pub const c = @cImport({
- @cDefine("_POSIX_C_SOURCE", "200809L");
-
- @cInclude("stdlib.h");
- @cInclude("unistd.h");
-
@cInclude("linux/input-event-codes.h");
@cInclude("libevdev/libevdev.h");
blob - c09db1fa660ebf98cdf47dcc86c50d2082eb3180
blob + 52e0a16616649c7dedb6ed5c320712060ac81491
--- river/command/keyboard.zig
+++ river/command/keyboard.zig
args: []const [:0]const u8,
_: *?[]const u8,
) Error!void {
- const result = flags.parser([:0]const u8, &.{
+ const result = flags.parser(&.{
.{ .name = "rules", .kind = .arg },
.{ .name = "model", .kind = .arg },
.{ .name = "variant", .kind = .arg },
if (args.len < 2) return Error.NotEnoughArguments;
if (args.len > 2) return Error.TooManyArguments;
- const file = std.fs.cwd().openFile(args[1], .{}) catch return error.CannotReadFile;
- defer file.close();
+ const io = std.Io.Threaded.global_single_threaded.io();
+ const file = std.Io.Dir.cwd().openFile(io, args[1], .{}) catch return error.CannotReadFile;
+ defer file.close(io);
+ var reader = file.reader(io, &.{});
// 1 GiB is arbitrarily chosen as an exceedingly large but not infinite upper bound.
- const file_bytes = file.readToEndAlloc(util.gpa, 1024 * 1024 * 1024) catch |err| {
+ const file_bytes = reader.interface.allocRemaining(util.gpa, .limited(1024 * 1024 * 1024)) catch |err| {
switch (err) {
- error.FileTooBig, error.OutOfMemory => return error.OutOfMemory,
+ error.OutOfMemory => return error.OutOfMemory,
else => return error.CannotReadFile,
}
};
blob - 2778fa64a4787f9d1188cddb6f854ec35d96aae4
blob + cd966f4b984ee1e8a53308b9c8663e7fdf5cba5a
--- river/command/map.zig
+++ river/command/map.zig
args: []const [:0]const u8,
out: *?[]const u8,
) Error!void {
- const result = flags.parser([:0]const u8, &.{
+ const result = flags.parser(&.{
.{ .name = "release", .kind = .boolean },
.{ .name = "repeat", .kind = .boolean },
.{ .name = "layout", .kind = .arg },
/// Example:
/// unmap normal Mod4+Shift Return
pub fn unmap(seat: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void {
- const result = flags.parser([:0]const u8, &.{
+ const result = flags.parser(&.{
.{ .name = "release", .kind = .boolean },
}).parse(args[1..]) catch {
return error.InvalidValue;
blob - 6dccf8002c894899281acc9db34444b1efa21c47
blob + d9bf1b39a0d02de1cc3fa61aab875a7ba9557ac8
--- river/command/output.zig
+++ river/command/output.zig
_: *?[]const u8,
) Error!void {
if (args.len < 2) return Error.NotEnoughArguments;
- const result = flags.parser([:0]const u8, &.{
+ const result = flags.parser(&.{
.{ .name = "current-tags", .kind = .boolean },
}).parse(args[1..]) catch {
- return error.InvalidOption;
+ return error.InvalidValue;
};
if (result.args.len < 1) return Error.NotEnoughArguments;
if (result.args.len > 1) return Error.TooManyArguments;
blob - 082783a2bb4e178dfaf467eb6380d6a2a3f76b6e
blob + ff6b0a47d5b0c15e2a590ac05e898ebd6910a14e
--- river/command/rule.zig
+++ river/command/rule.zig
};
pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
- const result = flags.parser([:0]const u8, &.{
+ const result = flags.parser(&.{
.{ .name = "app-id", .kind = .arg },
.{ .name = "title", .kind = .arg },
}).parse(args[1..]) catch {
}
pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
- const result = flags.parser([:0]const u8, &.{
+ const result = flags.parser(&.{
.{ .name = "app-id", .kind = .arg },
.{ .name = "title", .kind = .arg },
}).parse(args[1..]) catch {
}
}
-fn alignLeft(buf: []const u8, width: usize, writer: *std.io.Writer) Error!void {
+fn alignLeft(buf: []const u8, width: usize, writer: *std.Io.Writer) Error!void {
assert(buf.len <= width);
try writer.writeAll(buf);
try writer.splatByteAll(' ', width - buf.len);
const app_id_column_max = 2 + @max("app-id".len, max_glob_len.app_id);
const title_column_max = 2 + @max("title".len, max_glob_len.title);
- var buffer = std.io.Writer.Allocating.init(util.gpa);
+ var buffer = std.Io.Writer.Allocating.init(util.gpa);
defer buffer.deinit();
const writer = &buffer.writer;
blob - 0462abea2f09bb5069b0398f5b32c0c1b34fdf8b
blob + ba63731a1a7b67c29ba6c8276baaa22df902962d
--- river/command/spawn.zig
+++ river/command/spawn.zig
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
-const posix = std.posix;
+const c = std.c;
-const c = @import("../c.zig").c;
const util = @import("../util.zig");
const process = @import("../process.zig");
const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", args[1], null };
- const pid = posix.fork() catch {
- out.* = try std.fmt.allocPrint(util.gpa, "fork/execve failed", .{});
- return Error.Other;
+ const pid: c.pid_t = blk: {
+ const rc = c.fork();
+ if (c.errno(rc) != .SUCCESS) {
+ out.* = try std.fmt.allocPrint(util.gpa, "fork/execve failed", .{});
+ return Error.Other;
+ }
+ break :blk @intCast(rc);
};
if (pid == 0) {
process.cleanupChild();
- const pid2 = posix.fork() catch c._exit(1);
+ const pid2: c.pid_t = blk: {
+ const rc = c.fork();
+ if (c.errno(rc) != .SUCCESS) {
+ c._exit(1);
+ }
+ break :blk @intCast(rc);
+ };
+
if (pid2 == 0) {
- posix.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
+ _ = c.execve("/bin/sh", &child_args, c.environ);
+ c._exit(1); // only reachable if execve fails
}
c._exit(0);
}
// Wait the intermediate child.
- const ret = posix.waitpid(pid, 0);
- if (!posix.W.IFEXITED(ret.status) or
- (posix.W.IFEXITED(ret.status) and posix.W.EXITSTATUS(ret.status) != 0))
+ const status: u32 = while (true) {
+ var status: c_int = 0;
+ switch (c.errno(c.waitpid(pid, &status, 0))) {
+ .SUCCESS => break @bitCast(status),
+ .INTR => continue,
+ else => return Error.Unexpected, // should never happen, but don't trust the kernel
+ }
+ };
+
+ if (!c.W.IFEXITED(status) or
+ (c.W.IFEXITED(status) and c.W.EXITSTATUS(status) != 0))
{
out.* = try std.fmt.allocPrint(util.gpa, "fork/execve failed", .{});
return Error.Other;
blob - 3f8a2143bf2fcdd4d78fe66c80654f294aaef50a
blob + eb8020b6e28355bcc84729cdca534525aebdaeb5
--- river/command/view_operations.zig
+++ river/command/view_operations.zig
args: []const [:0]const u8,
_: *?[]const u8,
) Error!void {
- const result = flags.parser([:0]const u8, &.{
+ const result = flags.parser(&.{
.{ .name = "skip-floating", .kind = .boolean },
}).parse(args[1..]) catch {
return error.InvalidValue;
blob - c21c0b312f3bcebc931647bc9b0a88b4e25a5084
blob + ad4b194ffff8fdf80a1167d80abbd7bf39aa8eef
--- river/command.zig
+++ river/command.zig
ConflictingOptions,
WriteFailed,
OutOfMemory,
+ Unexpected,
Other,
};
Error.CannotReadFile => "cannot read file",
Error.CannotParseFile => "cannot parse file",
Error.WriteFailed, Error.OutOfMemory => "out of memory",
+ Error.Unexpected => "unexpected error",
Error.Other => unreachable,
};
}
blob - d2f948155f02aee12a289875533c1b2a0623c696
blob + 3346b50bdd7554240b9d5aa1f2565f86b703b945
--- river/main.zig
+++ river/main.zig
const build_options = @import("build_options");
const std = @import("std");
-const mem = std.mem;
+const Io = std.Io;
const fs = std.fs;
const log = std.log;
+const mem = std.mem;
const posix = std.posix;
+const exit = std.process.exit;
+const fatal = std.process.fatal;
+const c = std.c;
+
const builtin = @import("builtin");
const wlr = @import("wlroots");
const flags = @import("flags");
-const c = @import("c.zig").c;
const util = @import("util.zig");
const process = @import("process.zig");
pub var server: Server = undefined;
-pub fn main() anyerror!void {
- const result = flags.parser([*:0]const u8, &.{
+pub fn main(init: std.process.Init.Minimal) anyerror!void {
+ const io = std.Io.Threaded.global_single_threaded.io();
+
+ var stdout_buffer: [64]u8 = undefined;
+ var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer);
+ const stdout = &stdout_writer.interface;
+
+ var stderr_buffer: [64]u8 = undefined;
+ var stderr_writer = Io.File.stderr().writer(io, &stderr_buffer);
+ const stderr = &stderr_writer.interface;
+
+ const args = try init.args.toSlice(util.gpa);
+ defer util.gpa.free(args);
+
+ const result = flags.parser(&.{
.{ .name = "h", .kind = .boolean },
.{ .name = "version", .kind = .boolean },
.{ .name = "c", .kind = .arg },
.{ .name = "log-level", .kind = .arg },
.{ .name = "no-xwayland", .kind = .boolean },
- }).parse(std.os.argv[1..]) catch {
- try fs.File.stderr().writeAll(usage);
- posix.exit(1);
+ }).parse(args[1..]) catch {
+ try stderr.writeAll(usage);
+ exit(1);
};
if (result.flags.h) {
- try fs.File.stdout().writeAll(usage);
- posix.exit(0);
+ try stdout.writeAll(usage);
+ exit(0);
}
if (result.args.len != 0) {
log.err("unknown option '{s}'", .{result.args[0]});
- try fs.File.stderr().writeAll(usage);
- posix.exit(1);
+ try stderr.writeAll(usage);
+ exit(1);
}
if (result.flags.version) {
- try fs.File.stdout().writeAll(build_options.version ++ "\n");
- posix.exit(0);
+ try stdout.writeAll(build_options.version ++ "\n");
+ exit(0);
}
if (result.flags.@"log-level") |level| {
if (mem.eql(u8, level, "error")) {
runtime_log_level = .debug;
} else {
log.err("invalid log level '{s}'", .{level});
- try fs.File.stderr().writeAll(usage);
- posix.exit(1);
+ try stderr.writeAll(usage);
+ exit(1);
}
}
const runtime_xwayland = !result.flags.@"no-xwayland";
if (result.flags.c) |command| {
break :blk try util.gpa.dupeZ(u8, command);
} else {
- break :blk try defaultInitPath();
+ break :blk try defaultInitPath(io, init.environ);
}
};
const child_pgid = if (startup_command) |cmd| blk: {
log.info("running init executable '{s}'", .{cmd});
const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null };
- const pid = try posix.fork();
+
+ const pid: c.pid_t = pid: {
+ const rc = c.fork();
+ switch (c.errno(rc)) {
+ .SUCCESS => {},
+ else => |err| fatal("failed to start init process: {}", .{err}),
+ }
+ break :pid @intCast(rc);
+ };
+
if (pid == 0) {
process.cleanupChild();
- posix.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
+ _ = c.execve("/bin/sh", &child_args, c.environ);
+ c._exit(1); // only reachable if execve fails
}
util.gpa.free(cmd);
// Since the child has called setsid, the pid is the pgid
log.info("shutting down", .{});
}
-fn defaultInitPath() !?[:0]const u8 {
+fn defaultInitPath(io: Io, environ: std.process.Environ) !?[:0]const u8 {
const path = blk: {
- if (posix.getenv("XDG_CONFIG_HOME")) |xdg_config_home| {
+ if (environ.getPosix("XDG_CONFIG_HOME")) |xdg_config_home| {
break :blk try fs.path.joinZ(util.gpa, &[_][]const u8{ xdg_config_home, "river/init" });
- } else if (posix.getenv("HOME")) |home| {
+ } else if (environ.getPosix("HOME")) |home| {
break :blk try fs.path.joinZ(util.gpa, &[_][]const u8{ home, ".config/river/init" });
} else {
return null;
}
};
- posix.accessZ(path, posix.X_OK) catch |err| {
+ Io.Dir.cwd().access(io, path, .{ .execute = true }) catch |err| {
if (err == error.PermissionDenied) {
- if (posix.accessZ(path, posix.R_OK)) {
- log.err("failed to run init executable {s}: the file is not executable", .{path});
- posix.exit(1);
+ if (Io.Dir.cwd().access(io, path, .{})) {
+ fatal("failed to run init executable {s}: the file is not executable", .{path});
} else |_| {}
}
log.err("failed to run init executable {s}: {s}", .{ path, @errorName(err) });
) void {
if (@intFromEnum(level) > @intFromEnum(runtime_log_level)) return;
- const scope_prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
-
- var buffer: [256]u8 = undefined;
- const stderr = std.debug.lockStderrWriter(&buffer);
- defer std.debug.unlockStderrWriter();
-
- stderr.print(level.asText() ++ scope_prefix ++ format ++ "\n", args) catch {};
+ std.log.defaultLog(level, scope, format, args);
}
/// See wlroots_log_wrapper.c
blob - 7709a7dc73180d5b77d54e7946037c1e8f489cbb
blob + 2189f6aecf3c3415b57734742d36c3eb5aa81108
--- river/process.zig
+++ river/process.zig
const std = @import("std");
const posix = std.posix;
-const c = @import("c.zig").c;
-
var original_rlimit: ?posix.rlimit = null;
pub fn setup() void {
}
pub fn cleanupChild() void {
- if (c.setsid() < 0) unreachable;
+ if (std.c.setsid() < 0) unreachable;
if (posix.system.sigprocmask(posix.SIG.SETMASK, &posix.sigemptyset(), null) < 0) unreachable;
const sig_dfl = posix.Sigaction{
blob - 8c5edd3079717725851dfd07f0ddcb543c1e512a
blob + f4b133ffab3b449ca9e68aae85c61cdf9d69e692
--- river/rule_list.zig
+++ river/rule_list.zig
/// Ordered from most specific to most general.
/// Ordered first by app-id generality then by title generality.
- rules: std.ArrayListUnmanaged(Rule) = .{},
+ rules: std.ArrayList(Rule) = .empty,
pub fn deinit(list: *List) void {
for (list.rules.items) |rule| {
blob - efbaf34f40016cfbf95f424beefbae4dcd297af1
blob + d8c404253820abc6e905cc5fbb0eecbf76df139a
--- river/util.zig
+++ river/util.zig
/// The global general-purpose allocator used throughout river's code
pub const gpa = std.heap.c_allocator;
+
+pub fn timestamp() std.c.timespec {
+ var timespec: std.c.timespec = undefined;
+ switch (std.c.errno(std.c.clock_gettime(std.c.CLOCK.MONOTONIC, ×pec))) {
+ .SUCCESS => return timespec,
+ else => @panic("CLOCK_MONOTONIC not supported"),
+ }
+}
+
+pub fn msecTimestamp() u32 {
+ const now = timestamp();
+ // 2^32-1 milliseconds is ~50 days, which is a realistic uptime.
+ // This means that we must wrap if the monotonic time is greater than
+ // 2^32-1 milliseconds and hope that clients don't get too confused.
+ return @intCast(@rem(
+ now.sec *% std.time.ms_per_s +% @divTrunc(now.nsec, std.time.ns_per_ms),
+ std.math.maxInt(u32),
+ ));
+}
blob - 39abacf2ca5045c3ecc9697f44f6d74467613425
blob + 840fa9f48821f70a4dad1ce95cce38b345720957
--- riverctl/main.zig
+++ riverctl/main.zig
const std = @import("std");
const mem = std.mem;
const fs = std.fs;
+const Io = std.Io;
const posix = std.posix;
const assert = std.debug.assert;
+const process = std.process;
+const fatal = process.fatal;
const builtin = @import("builtin");
const wayland = @import("wayland");
\\
;
+const io = Io.Threaded.global_single_threaded.io();
+var stdout_buffer: [64]u8 = undefined;
+var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer);
+const stdout = &stdout_writer.interface;
+
+var stderr_buffer: [64]u8 = undefined;
+var stderr_writer = Io.File.stderr().writer(io, &stderr_buffer);
+const stderr = &stderr_writer.interface;
+
const gpa = std.heap.c_allocator;
pub const Globals = struct {
seat: ?*wl.Seat = null,
};
-pub fn main() !void {
- _main() catch |err| {
+pub fn main(init: std.process.Init.Minimal) !void {
+ _main(init) catch |err| {
switch (err) {
error.RiverControlNotAdvertised => fatal(
\\The Wayland server does not support river-control-unstable-v1.
, .{}),
error.ConnectFailed => {
std.log.err("Unable to connect to the Wayland server.", .{});
- if (posix.getenvZ("WAYLAND_DISPLAY") == null) {
+ if (init.environ.getPosix("WAYLAND_DISPLAY") == null) {
fatal("WAYLAND_DISPLAY is not set.", .{});
} else {
fatal("Does WAYLAND_DISPLAY contain the socket name of a running server?", .{});
};
}
-fn _main() !void {
- const result = flags.parser([*:0]const u8, &.{
+fn _main(init: std.process.Init.Minimal) !void {
+ const args = try init.args.toSlice(gpa);
+ defer gpa.free(args);
+
+ const result = flags.parser(&.{
.{ .name = "h", .kind = .boolean },
.{ .name = "version", .kind = .boolean },
- }).parse(std.os.argv[1..]) catch {
- try fs.File.stderr().writeAll(usage);
- posix.exit(1);
+ }).parse(args[1..]) catch {
+ try stderr.writeAll(usage);
+ process.exit(1);
};
if (result.flags.h) {
- try fs.File.stdout().writeAll(usage);
- posix.exit(0);
+ try stdout.writeAll(usage);
+ process.exit(0);
}
if (result.flags.version) {
- try fs.File.stdout().writeAll(@import("build_options").version ++ "\n");
- posix.exit(0);
+ try stdout.writeAll(@import("build_options").version ++ "\n");
+ process.exit(0);
}
const display = try wl.Display.connect(null);
switch (event) {
.success => |success| {
if (mem.len(success.output) > 0) {
- var stdout = fs.File.stdout().writer(&.{});
- stdout.interface.print("{s}\n", .{success.output}) catch @panic("failed to write to stdout");
+ stdout.print("{s}\n", .{success.output}) catch @panic("failed to write to stdout");
}
- posix.exit(0);
+ process.exit(0);
},
.failure => |failure| {
// A small hack to provide usage text when river reports an unknown command.
if (mem.orderZ(u8, failure.failure_message, "unknown command") == .eq) {
std.log.err("unknown command", .{});
- fs.File.stderr().writeAll(usage) catch {};
- posix.exit(1);
+ stderr.writeAll(usage) catch {};
+ process.exit(1);
}
fatal("{s}", .{failure.failure_message});
},
}
}
-
-fn fatal(comptime format: []const u8, args: anytype) noreturn {
- std.log.err(format, args);
- posix.exit(1);
-}
blob - 972641e0a59eea6267b06283e65e7c5467c16447
blob + 31b994a0b2c180f5438d7959c83e9a8565a64420
--- rivertile/main.zig
+++ rivertile/main.zig
const mem = std.mem;
const math = std.math;
const posix = std.posix;
+const process = std.process;
+const fatal = std.process.fatal;
const assert = std.debug.assert;
const wayland = @import("wayland");
}
};
-pub fn main() !void {
- const result = flags.parser([*:0]const u8, &[_]flags.Flag{
+pub fn main(init: std.process.Init.Minimal) !void {
+ const args = try init.args.toSlice(gpa);
+ defer gpa.free(args);
+
+ const io = std.Io.Threaded.global_single_threaded.io();
+ var stdout_buffer: [64]u8 = undefined;
+ var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buffer);
+ const stdout = &stdout_writer.interface;
+
+ var stderr_buffer: [64]u8 = undefined;
+ var stderr_writer = std.Io.File.stderr().writer(io, &stderr_buffer);
+ const stderr = &stderr_writer.interface;
+
+ const result = flags.parser(&.{
.{ .name = "h", .kind = .boolean },
.{ .name = "version", .kind = .boolean },
.{ .name = "view-padding", .kind = .arg },
.{ .name = "main-location", .kind = .arg },
.{ .name = "main-count", .kind = .arg },
.{ .name = "main-ratio", .kind = .arg },
- }).parse(std.os.argv[1..]) catch {
- try std.fs.File.stderr().writeAll(usage);
- posix.exit(1);
+ }).parse(args[1..]) catch {
+ try stderr.writeAll(usage);
+ process.exit(1);
};
if (result.flags.h) {
- try std.fs.File.stdout().writeAll(usage);
- posix.exit(0);
+ try stdout.writeAll(usage);
+ process.exit(0);
}
if (result.args.len != 0) fatalPrintUsage("unknown option '{s}'", .{result.args[0]});
if (result.flags.version) {
- try std.fs.File.stdout().writeAll(@import("build_options").version ++ "\n");
- posix.exit(0);
+ try stdout.writeAll(@import("build_options").version ++ "\n");
+ process.exit(0);
}
if (result.flags.@"view-padding") |raw| {
view_padding = fmt.parseUnsigned(u31, raw, 10) catch
}
const display = wl.Display.connect(null) catch {
- std.debug.print("Unable to connect to Wayland server.\n", .{});
- posix.exit(1);
+ fatal("Unable to connect to Wayland server.\n", .{});
};
defer display.disconnect();
}
}
-fn fatal(comptime format: []const u8, args: anytype) noreturn {
- std.log.err(format, args);
- posix.exit(1);
-}
-
fn fatalPrintUsage(comptime format: []const u8, args: anytype) noreturn {
std.log.err(format, args);
- std.fs.File.stderr().writeAll(usage) catch {};
- posix.exit(1);
+ std.debug.print(usage, .{});
+ process.exit(1);
}
fn saturatingCast(comptime T: type, x: anytype) T {