Files
river/riverctl/main.zig
T

158 lines
5.5 KiB
Zig
Raw Normal View History

2020-05-02 19:21:10 +02:00
// This file is part of river, a dynamic tiling wayland compositor.
//
2021-01-17 16:30:47 +01:00
// Copyright 2020-2021 The River Developers
2020-05-02 19:21:10 +02:00
//
// 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
2022-01-31 19:33:22 +01:00
// the Free Software Foundation, version 3.
2020-05-02 19:21:10 +02:00
//
// 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/>.
2020-03-19 16:29:22 +01:00
2020-05-02 19:21:10 +02:00
const std = @import("std");
2021-01-17 16:30:47 +01:00
const mem = std.mem;
2025-07-11 18:08:50 +02:00
const fs = std.fs;
2026-04-29 13:00:56 +02:00
const Io = std.Io;
2024-03-07 16:19:22 +01:00
const posix = std.posix;
const assert = std.debug.assert;
2026-04-29 13:00:56 +02:00
const process = std.process;
const fatal = process.fatal;
2021-10-11 12:44:46 +02:00
const builtin = @import("builtin");
2020-05-02 19:21:10 +02:00
2020-11-02 13:59:59 +01:00
const wayland = @import("wayland");
const wl = wayland.client.wl;
const zriver = wayland.client.zriver;
2020-05-02 19:21:10 +02:00
2021-07-24 19:31:04 +02:00
const flags = @import("flags");
const usage =
\\usage: riverctl [options] <command>
\\
2021-11-01 00:34:15 +01:00
\\ -h Print this help message and exit.
2021-07-24 19:31:04 +02:00
\\ -version Print the version number and exit.
\\
\\Complete documentation of the recognized commands may be found in
\\the riverctl(1) man page.
\\
;
2026-04-29 13:00:56 +02:00
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;
2021-01-17 16:30:47 +01:00
const gpa = std.heap.c_allocator;
pub const Globals = struct {
control: ?*zriver.ControlV1 = null,
2020-11-02 13:59:59 +01:00
seat: ?*wl.Seat = null,
2020-05-19 18:22:22 +02:00
};
2026-04-29 13:00:56 +02:00
pub fn main(init: std.process.Init.Minimal) !void {
_main(init) catch |err| {
switch (err) {
2021-07-24 19:31:04 +02:00
error.RiverControlNotAdvertised => fatal(
\\The Wayland server does not support river-control-unstable-v1.
\\Do your versions of river and riverctl match?
, .{}),
2021-07-24 19:31:04 +02:00
error.SeatNotAdverstised => fatal(
\\The Wayland server did not advertise any seat.
, .{}),
error.ConnectFailed => {
std.log.err("Unable to connect to the Wayland server.", .{});
2026-04-29 13:00:56 +02:00
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?", .{});
}
},
else => return err,
}
};
}
2026-04-29 13:00:56 +02:00
fn _main(init: std.process.Init.Minimal) !void {
const args = try init.args.toSlice(gpa);
defer gpa.free(args);
const result = flags.parser(&.{
2022-12-28 22:11:14 +01:00
.{ .name = "h", .kind = .boolean },
.{ .name = "version", .kind = .boolean },
2026-04-29 13:00:56 +02:00
}).parse(args[1..]) catch {
try stderr.writeAll(usage);
process.exit(1);
2021-07-24 19:31:04 +02:00
};
2022-12-28 22:11:14 +01:00
if (result.flags.h) {
2026-04-29 13:00:56 +02:00
try stdout.writeAll(usage);
process.exit(0);
2021-07-24 19:31:04 +02:00
}
2022-12-28 22:11:14 +01:00
if (result.flags.version) {
2026-04-29 13:00:56 +02:00
try stdout.writeAll(@import("build_options").version ++ "\n");
process.exit(0);
2021-07-24 19:31:04 +02:00
}
2020-11-02 13:59:59 +01:00
const display = try wl.Display.connect(null);
const registry = try display.getRegistry();
2020-05-19 18:22:22 +02:00
2021-01-17 16:30:47 +01:00
var globals = Globals{};
2020-05-19 18:22:22 +02:00
2021-04-27 12:24:30 +02:00
registry.setListener(*Globals, registryListener, &globals);
2022-05-09 21:56:28 +02:00
if (display.roundtrip() != .SUCCESS) fatal("initial roundtrip failed", .{});
2020-11-02 13:59:59 +01:00
2021-04-26 21:03:04 +02:00
const control = globals.control orelse return error.RiverControlNotAdvertised;
const seat = globals.seat orelse return error.SeatNotAdverstised;
2020-05-19 18:22:22 +02:00
2021-07-24 19:31:04 +02:00
for (result.args) |arg| control.addArgument(arg);
2020-05-19 18:22:22 +02:00
2021-04-26 21:03:04 +02:00
const callback = try control.runCommand(seat);
2021-10-11 12:44:46 +02:00
callback.setListener(?*anyopaque, callbackListener, null);
2021-04-26 21:03:04 +02:00
// Loop until our callback is called and we exit.
2022-05-09 21:56:28 +02:00
while (true) {
if (display.dispatch() != .SUCCESS) fatal("failed to dispatch wayland events", .{});
}
2020-03-19 16:29:22 +01:00
}
2020-05-19 18:22:22 +02:00
2021-01-17 16:30:47 +01:00
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *Globals) void {
2020-11-02 13:59:59 +01:00
switch (event) {
.global => |global| {
2025-03-07 12:37:11 +01:00
if (mem.orderZ(u8, global.interface, wl.Seat.interface.name) == .eq) {
assert(globals.seat == null); // TODO: support multiple seats
2021-01-17 16:30:47 +01:00
globals.seat = registry.bind(global.name, wl.Seat, 1) catch @panic("out of memory");
2025-03-07 12:37:11 +01:00
} else if (mem.orderZ(u8, global.interface, zriver.ControlV1.interface.name) == .eq) {
2021-01-17 16:30:47 +01:00
globals.control = registry.bind(global.name, zriver.ControlV1, 1) catch @panic("out of memory");
2020-11-02 13:59:59 +01:00
}
},
.global_remove => {},
2020-05-19 18:22:22 +02:00
}
}
2021-10-11 12:44:46 +02:00
fn callbackListener(_: *zriver.CommandCallbackV1, event: zriver.CommandCallbackV1.Event, _: ?*anyopaque) void {
2020-11-02 13:59:59 +01:00
switch (event) {
.success => |success| {
2021-01-17 16:30:47 +01:00
if (mem.len(success.output) > 0) {
2026-04-29 13:00:56 +02:00
stdout.print("{s}\n", .{success.output}) catch @panic("failed to write to stdout");
2020-11-02 13:59:59 +01:00
}
2026-04-29 13:00:56 +02:00
process.exit(0);
2020-11-02 13:59:59 +01:00
},
2021-07-24 19:31:04 +02:00
.failure => |failure| {
// A small hack to provide usage text when river reports an unknown command.
2023-10-16 16:18:36 +02:00
if (mem.orderZ(u8, failure.failure_message, "unknown command") == .eq) {
2021-07-24 19:31:04 +02:00
std.log.err("unknown command", .{});
2026-04-29 13:00:56 +02:00
stderr.writeAll(usage) catch {};
process.exit(1);
2021-07-24 19:31:04 +02:00
}
fatal("{s}", .{failure.failure_message});
},
2020-06-15 22:24:54 +02:00
}
}