diff --git a/build.zig b/build.zig index b1b9a5f..a08fbde 100644 --- a/build.zig +++ b/build.zig @@ -15,6 +15,7 @@ pub fn build(b: *std.Build) void { const wlroots = b.dependency("wlroots", .{}).module("wlroots"); const zlua = b.dependency("zlua", .{}).module("zlua"); const zargs = b.dependency("args", .{ .target = target, .optimize = optimize }).module("args"); + const linenoize = b.dependency("linenoize", .{}).module("linenoise"); wlroots.addImport("wayland", wayland); wlroots.resolved_target = target; @@ -35,6 +36,7 @@ pub fn build(b: *std.Build) void { whet.root_module.addImport("wlroots", wlroots); whet.root_module.addImport("zlua", zlua); whet.root_module.addImport("args", zargs); + whet.root_module.addImport("linenoize", linenoize); whet.root_module.linkSystemLibrary("wayland-client", .{}); diff --git a/build.zig.zon b/build.zig.zon index 6976916..5f9812f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -20,6 +20,10 @@ .url = "git+https://github.com/ikskuh/zig-args?ref=master#e060ac80c244e9675471b6d213b22ddc83cc8f98", .hash = "args-0.0.0-CiLiqo_RAADz2TiHUzG5-0Mk7IZHR-h1SZgUrb_k4c7d", }, + .linenoize = .{ + .url = "git+https://github.com/Squibid/linenoize#b873710c9107d176e1a8d7b3b36df57f77447ce6", + .hash = "linenoize-0.1.1-J7HK8NTiAABSqJQoK5IQ-EfVdX4nples5yHeP7bCji7D", + }, }, .paths = .{ "build.zig", diff --git a/src/main.zig b/src/main.zig index 7e225ae..c75b56a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,5 +1,6 @@ const std = @import("std"); const argsParser = @import("args"); +const Linenoize = @import("linenoize").Linenoise; const util = @import("util.zig"); const Remote = @import("remote.zig"); @@ -20,60 +21,28 @@ const usage = \\ ; -fn loop(input: bool) !void { - var pollfds: [2]std.posix.pollfd = undefined; +fn inputThreadRun() !void { + const ln: *Linenoize = @constCast(&Linenoize.init(gpa)); + defer ln.deinit(); - pollfds[0] = .{ // wayland fd - .fd = remote.display.getFd(), - .events = std.posix.POLL.IN, - .revents = 0, - }; - if (input) { - pollfds[1] = .{ // stdin - .fd = std.posix.STDIN_FILENO, - .events = std.posix.POLL.IN, - .revents = 0, - }; - } - - var buf: [2]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&buf); - const stdout = &stdout_writer.interface; while (true) { - if (input) { - try stdout.print("> ", .{}); - try stdout.flush(); + const in = ln.linenoise("> ") catch |err| switch (err) { + error.CtrlC => std.process.exit(130), + else => return err, + } orelse continue; + defer gpa.free(in); + + if (remote.remote_lua) |rl| { + const w_sentinel = gpa.allocSentinel(u8, in.len, 0) catch util.oom(); + defer gpa.free(w_sentinel); + std.mem.copyForwards(u8, w_sentinel, in[0..in.len]); + + rl.pushLua(w_sentinel); + + const err = remote.display.flush(); + if (err != .SUCCESS) util.fatal("lost connection to the wayland socket", .{}); } - - _ = std.posix.poll(&pollfds, -1) catch |err| { - util.fatal("poll() failed: {s}", .{@errorName(err)}); - }; - - for (pollfds) |fd| { - if (fd.revents & std.posix.POLL.IN == 1) { - if (fd.fd == std.posix.STDIN_FILENO) { - var in_buf: [1024]u8 = undefined; - const len = std.posix.read(fd.fd, &in_buf) catch 0; - - if (len == 0) { - try stdout.print("\n", .{}); - continue; - } - - if (in_buf[len - 1] == '\n') in_buf[len - 1] = 0 else in_buf[len] = 0; - if (remote.remote_lua) |rl| rl.pushLua(@ptrCast(in_buf[0..len].ptr)); - } - - // FIXME: we really shouldn't be reading from the socket - if (fd.fd == remote.display.getFd()) { - var in_buf: [1024]u8 = undefined; - const len = std.posix.read(fd.fd, &in_buf) catch 0; - std.debug.print("\n{s}", .{in_buf[0..len]}); - } - } - } - - try remote.flush(); + try ln.history.add(in); } } @@ -101,13 +70,16 @@ pub fn main() !void { // connect to the compositor remote = Remote.init(); defer remote.deinit(); + var input_thread: std.Thread = undefined; // handle options if (options.options.code) |c| { remote.remote_lua.?.pushLua(@ptrCast(c[0..].ptr)); - } else if (options.options.@"follow-log") { - try loop(false); - } else { - try loop(true); + } else if (!options.options.@"follow-log") { + input_thread = try .spawn(.{}, inputThreadRun, .{}); } + + while (remote.display.dispatch() == .SUCCESS) {} + + input_thread.join(); } diff --git a/src/remote.zig b/src/remote.zig index 97afd25..d227206 100644 --- a/src/remote.zig +++ b/src/remote.zig @@ -39,7 +39,7 @@ pub fn init() Remote { self.remote_lua = self.remote_lua_manager.?.getRemote() catch util.oom(); if (self.remote_lua) |rl| { - rl.setListener(?*anyopaque, handleRemote, null); + rl.setListener(*Remote, handleRemote, @constCast(&self)); } else { util.fatal("failed to setup the remote listener", .{}); } @@ -47,35 +47,12 @@ pub fn init() Remote { return self; } -pub fn flush(self: *Remote) !void { - while (true) { - while (!self.display.prepareRead()) { - const errno = self.display.dispatchPending(); - if (errno != .SUCCESS) { - util.fatal("failed to dispatch pending wayland events: E{s}", .{@tagName(errno)}); - } - } - - const errno = self.display.flush(); - switch (errno) { - .SUCCESS => return, - .PIPE => { - // libwayland uses this error to indicate that the wayland server - // closed its side of the wayland socket. We want to continue to - // read any buffered messages from the server though as there is - // likely a protocol error message we'd like libwayland to log. - _ = self.display.readEvents(); - util.fatal("connection to wayland server unexpectedly terminated", .{}); - }, - else => { - util.fatal("failed to flush wayland requests: E{s}", .{@tagName(errno)}); - }, - } - } -} - pub fn deinit(self: *Remote) void { self.registry.destroy(); + if (self.remote_lua_manager) |rl_manager| rl_manager.destroy(); + if (self.remote_lua) |rl| rl.destroy(); + if (self.compositor) |comp| comp.destroy(); + self.display.disconnect(); } fn registry_listener( @@ -93,7 +70,7 @@ fn registry_event(registry: *wl.Registry, event: wl.Registry.Event, remote: *Rem .global => |ev| { if (std.mem.orderZ(u8, ev.interface, wl.Compositor.interface.name) == .eq) { const ver = 1; - if (ev.version < 1) { + if (ev.version < ver) { util.fatal("advertised wl_compositor version too old, version {} required", .{ver}); } remote.compositor = try registry.bind(ev.name, wl.Compositor, ver); @@ -109,12 +86,10 @@ fn registry_event(registry: *wl.Registry, event: wl.Registry.Event, remote: *Rem } } -// FIXME: this doesn't actually handle events for some reason and we currently -// just read from the socket directly -fn handleRemote(_: *mez.RemoteLuaV1, event: mez.RemoteLuaV1.Event, _: ?*anyopaque) void { +fn handleRemote(_: *mez.RemoteLuaV1, event: mez.RemoteLuaV1.Event, _: *Remote) void { switch (event) { .new_log_entry => |e| { - std.log.info("{s}", .{e.text}); + std.debug.print("\r{s}\r\n", .{e.text}); }, } }