srctree

Gregory Mullen parent 52674667 87439336
connect to sway ipc & print workspace events

src/root.zig added: 178, removed: 19, total 159
@@ -1,4 +1,6 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
 
const sway_ipc = @import("sway-ipc.zig");
const gui = @import("gui.zig");
 
@@ -37,14 +39,60 @@ export fn obs_module_description() [*:0]const u8 {
return "it does stuff";
}
 
fn logFmt(comptime text: []const u8, vars: anytype) void {
var buf: [0xffff:0]u8 = undefined;
 
const txt = std.fmt.bufPrintZ(&buf, text, vars) catch unreachable;
log(txt);
}
 
fn log(text: [*:0]const u8) void {
obs.blog(obs.LOG_INFO, text);
}
 
var arena: std.heap.ArenaAllocator = undefined;
var alloc: Allocator = undefined;
 
fn thread(_: ?*anyopaque) void {
obs.blog(obs.LOG_INFO, "sway thread running");
var sway = sway_ipc.Connection.init(alloc) catch |err| {
logFmt("connection error {}", .{err});
return;
};
sway.subscribe() catch {
log("crash trying to subscribe");
unreachable;
};
std.time.sleep(10_000_000_000);
for (0..10) |_| {
const msg = sway.loop() catch {
log("unexpected read error");
unreachable;
};
const text = alloc.dupeZ(u8, msg.data) catch unreachable;
defer alloc.free(text);
log(text);
std.time.sleep(10_000_000);
}
}
 
var threads: [1]std.Thread = undefined;
 
export fn obs_module_load() bool {
obs.blog(obs.LOG_INFO, "plugin loaded successfully (version %s)", PLUGIN_VERSION);
//gui.init();
arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
alloc = arena.allocator();
threads[0] = std.Thread.spawn(.{}, thread, .{null}) catch unreachable;
 
enumScene();
logFmt("sway-focus plugin loaded successfully {s}", .{PLUGIN_VERSION});
return true;
}
 
export fn obs_module_unload() void {
std.Thread.join(threads[0]);
arena.deinit();
}
 
fn enumScene() void {
obs.obs_enum_scenes(enumSceneCb, null);
}
@@ -59,12 +107,7 @@ fn enumSceneCb(_: ?*anyopaque, _: ?*obs.obs_source_t) callconv(.C) bool {
fn enumSceneItemCb(_: ?*obs.obs_scene_t, _: ?*obs.obs_sceneitem_t, _: ?*void) callconv(.C) void {}
 
// TODO
// thread
// socket
// send message
// get messages
// get windows
// poll socket
// trigger scene on event
// stall on remove for min seconds
 
 
src/sway-ipc.zig added: 178, removed: 19, total 159
@@ -1,4 +1,7 @@
pub const Payload = enum(i32) {
const std = @import("std");
const Allocator = std.mem.Allocator;
 
pub const Payload = enum(u32) {
RUN_COMMAND = 0, // Runs the payload as sway commands
GET_WORKSPACES = 1, // Get the list of current workspaces
SUBSCRIBE = 2, // Subscribe the IPC connection to the events listed in the payload
@@ -14,22 +17,135 @@ pub const Payload = enum(i32) {
GET_BINDING_STATE = 12, // Request the current binding state, e.g. the currently active binding mode name.
GET_INPUTS = 100, // Get the list of input devices
GET_SEATS = 101, // Get the list of seats
//
pub fn fromInt(i: u32) Payload {
_ = i;
return .RUN_COMMAND;
}
};
 
pub const Message = packed struct {
magic: [6]u8 = "i3-ipc",
length: i32,
payload_type: Payload,
const Subscribe = Message{
.header = .{
.length = 13,
.payload_type = .SUBSCRIBE,
},
.data = "[\"workspace\"]",
};
 
pub const Message = struct {
pub const Header = struct {
magic: []const u8 = "i3-ipc",
length: u32 = 0,
payload_type: Payload = undefined,
};
header: Header = .{},
data: []const u8,
 
pub fn raze(m: Message, a: Allocator) void {
a.free(m.data);
}
 
pub fn read(a: Allocator, r: *std.net.Stream.Reader) !Message {
var m = Message{ .data = undefined };
try r.skipBytes(6, .{});
m.header = .{
.length = try r.readInt(u32, .little),
.payload_type = Payload.fromInt(try r.readInt(u32, .little)),
};
// TODO specify a better max size
if (m.header.length == 0 or m.header.length > 0x2ffff) unreachable;
m.data = try a.alloc(u8, m.header.length);
const rlen = try r.read(@constCast(m.data));
std.debug.assert(rlen == m.data.len);
return m;
}
 
pub fn send(m: Message, w: *std.net.Stream.Writer) !void {
std.debug.assert(m.header.length == m.data.len);
try w.writeAll(m.header.magic);
try w.writeInt(u32, m.header.length, .little);
try w.writeInt(u32, @intFromEnum(m.header.payload_type), .little);
try w.writeAll(m.data);
}
};
 
pub fn getSockPath(a: Allocator) ![]const u8 {
var child = std.ChildProcess.init(&[_][]const u8{ "sway", "--get-socketpath" }, a);
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
 
var stdout = std.ArrayList(u8).init(a);
var stderr = std.ArrayList(u8).init(a);
defer stdout.clearAndFree();
 
try child.spawn();
try child.collectOutput(&stdout, &stderr, 0xff);
_ = try child.wait();
 
_ = stdout.pop();
return try stdout.toOwnedSlice();
}
 
pub const Connection = struct {
socket: []const u8,
alloc: Allocator,
socket_path: ?[]const u8 = null,
sock: ?std.net.Stream = null,
 
pub fn connect(c: *Connection) void {
_ = c;
pub fn init(a: Allocator) !Connection {
var c = Connection{ .alloc = a };
c.socket_path = try getSockPath(c.alloc);
c.sock = try std.net.connectUnixSocket(c.socket_path.?);
return c;
}
 
pub fn loop(c: *Connection) void {
_ = c;
pub fn raze(c: *Connection) void {
if (c.socket_path) |path| c.alloc.free(path);
c.socket_path = null;
if (c.sock) |sock| sock.close();
}
 
pub fn subscribe(c: *Connection) !void {
var sock = c.sock orelse unreachable;
var w = sock.writer();
try Subscribe.send(&w);
}
 
pub fn loop(c: *Connection) !Message {
var sock = c.sock orelse unreachable;
 
var r = sock.reader();
return try Message.read(c.alloc, &r);
}
};
 
test "sway get socketpath" {
const a = std.testing.allocator;
var child = std.ChildProcess.init(&[_][]const u8{ "sway", "--get-socketpath" }, a);
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
 
var stdout = std.ArrayList(u8).init(a);
defer stdout.clearAndFree();
var stderr = std.ArrayList(u8).init(a);
defer stdout.clearAndFree();
 
try child.spawn();
try child.collectOutput(&stdout, &stderr, 0xff);
const out = try child.wait();
_ = out;
std.debug.print("sway socket path {s}\n", .{stdout.items});
}
 
test "sending" {
var buf: [0xff]u8 = undefined;
 
var fbs = std.io.fixedBufferStream(&buf);
var w = fbs.writer();
 
const m = Subscribe;
 
try w.writeStruct(m.header);
try w.writeAll(m.data);
 
std.debug.print("struct {any}\n", .{buf});
}