reformatted with zig fmt

This commit is contained in:
Squibid 2026-03-01 22:23:31 -05:00
parent e6a45f91b6
commit 6be66b6a71
Signed by: squibid
GPG key ID: BECE5684D3C4005D
32 changed files with 2108 additions and 2237 deletions

View file

@ -6,11 +6,6 @@ root = true
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true
[*.zig]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.lua] [*.lua]
indent_style = space indent_style = space
indent_size = 2 indent_size = 2

142
build.zig
View file

@ -4,89 +4,89 @@ const builtin = @import("builtin");
const Scanner = @import("wayland").Scanner; const Scanner = @import("wayland").Scanner;
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
// TODO: this will probably change based on the install paths, make this a var // TODO: this will probably change based on the install paths, make this a var
// that can be passed at comptime? // that can be passed at comptime?
const runtime_path_prefix = switch (builtin.mode) { const runtime_path_prefix = switch (builtin.mode) {
.Debug => "runtime/", .Debug => "runtime/",
else => "/usr/share", else => "/usr/share",
}; };
// Add protocol xml files // Add protocol xml files
const scanner = Scanner.create(b, .{}); const scanner = Scanner.create(b, .{});
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml"); scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
scanner.addSystemProtocol("stable/tablet/tablet-v2.xml"); scanner.addSystemProtocol("stable/tablet/tablet-v2.xml");
scanner.addSystemProtocol("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"); scanner.addSystemProtocol("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml");
scanner.addCustomProtocol(b.path("protocols/wlr-layer-shell-unstable-v1.xml")); scanner.addCustomProtocol(b.path("protocols/wlr-layer-shell-unstable-v1.xml"));
scanner.addCustomProtocol(b.path("protocols/mez-remote-lua-unstable-v1.xml")); scanner.addCustomProtocol(b.path("protocols/mez-remote-lua-unstable-v1.xml"));
// Generate protocol code // Generate protocol code
scanner.generate("zmez_remote_lua_manager_v1", 1); scanner.generate("zmez_remote_lua_manager_v1", 1);
scanner.generate("wl_compositor", 6); scanner.generate("wl_compositor", 6);
scanner.generate("wl_subcompositor", 1); scanner.generate("wl_subcompositor", 1);
scanner.generate("wl_shm", 2); scanner.generate("wl_shm", 2);
scanner.generate("wl_output", 4); scanner.generate("wl_output", 4);
scanner.generate("wl_seat", 9); scanner.generate("wl_seat", 9);
scanner.generate("wl_data_device_manager", 3); scanner.generate("wl_data_device_manager", 3);
scanner.generate("zxdg_decoration_manager_v1", 1); scanner.generate("zxdg_decoration_manager_v1", 1);
scanner.generate("xdg_wm_base", 7); scanner.generate("xdg_wm_base", 7);
scanner.generate("zwp_tablet_manager_v2", 2); scanner.generate("zwp_tablet_manager_v2", 2);
scanner.generate("zwlr_layer_shell_v1", 5); scanner.generate("zwlr_layer_shell_v1", 5);
const wayland = b.createModule(.{ .root_source_file = scanner.result }); const wayland = b.createModule(.{ .root_source_file = scanner.result });
const xkbcommon = b.dependency("xkbcommon", .{}).module("xkbcommon"); const xkbcommon = b.dependency("xkbcommon", .{}).module("xkbcommon");
const pixman = b.dependency("pixman", .{}).module("pixman"); const pixman = b.dependency("pixman", .{}).module("pixman");
const wlroots = b.dependency("wlroots", .{}).module("wlroots"); const wlroots = b.dependency("wlroots", .{}).module("wlroots");
const zlua = b.dependency("zlua", .{ .optimize = optimize, .target = target, .lang = .lua51 }).module("zlua"); const zlua = b.dependency("zlua", .{ .optimize = optimize, .target = target, .lang = .lua51 }).module("zlua");
const clap = b.dependency("clap", .{}).module("clap"); const clap = b.dependency("clap", .{}).module("clap");
wlroots.addImport("wayland", wayland); wlroots.addImport("wayland", wayland);
wlroots.addImport("xkbcommon", xkbcommon); wlroots.addImport("xkbcommon", xkbcommon);
wlroots.addImport("pixman", pixman); wlroots.addImport("pixman", pixman);
wlroots.resolved_target = target; wlroots.resolved_target = target;
wlroots.linkSystemLibrary("wlroots-0.19", .{}); wlroots.linkSystemLibrary("wlroots-0.19", .{});
const mez = b.addExecutable(.{ const mez = b.addExecutable(.{
.name = "mez", .name = "mez",
.root_module = b.createModule(.{ .root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"), .root_source_file = b.path("src/main.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}), }),
}); });
mez.root_module.link_libc = true; mez.root_module.link_libc = true;
mez.root_module.addImport("wayland", wayland); mez.root_module.addImport("wayland", wayland);
mez.root_module.addImport("xkbcommon", xkbcommon); mez.root_module.addImport("xkbcommon", xkbcommon);
mez.root_module.addImport("wlroots", wlroots); mez.root_module.addImport("wlroots", wlroots);
mez.root_module.addImport("zlua", zlua); mez.root_module.addImport("zlua", zlua);
mez.root_module.addImport("clap", clap); mez.root_module.addImport("clap", clap);
mez.root_module.linkSystemLibrary("wayland-server", .{}); mez.root_module.linkSystemLibrary("wayland-server", .{});
mez.root_module.linkSystemLibrary("xkbcommon", .{}); mez.root_module.linkSystemLibrary("xkbcommon", .{});
mez.root_module.linkSystemLibrary("pixman-1", .{}); mez.root_module.linkSystemLibrary("pixman-1", .{});
mez.root_module.linkSystemLibrary("libevdev", .{}); mez.root_module.linkSystemLibrary("libevdev", .{});
var ret: u8 = undefined; var ret: u8 = undefined;
const version = b.runAllowFail( const version = b.runAllowFail(
&.{"git", "-C", b.build_root.path orelse ".", "describe", "--tags", "--dirty"}, &.{ "git", "-C", b.build_root.path orelse ".", "describe", "--tags", "--dirty" },
&ret, &ret,
.Inherit, .Inherit,
) catch "dev\n"; ) catch "dev\n";
const options = b.addOptions(); const options = b.addOptions();
options.addOption([]const u8, "runtime_path_prefix", runtime_path_prefix); options.addOption([]const u8, "runtime_path_prefix", runtime_path_prefix);
options.addOption([]const u8, "version", version); options.addOption([]const u8, "version", version);
mez.root_module.addOptions("config", options); mez.root_module.addOptions("config", options);
b.installArtifact(mez); b.installArtifact(mez);
const run_step = b.step("run", "Run the compositor"); const run_step = b.step("run", "Run the compositor");
const run_cmd = b.addRunArtifact(mez); const run_cmd = b.addRunArtifact(mez);
run_step.dependOn(&run_cmd.step); run_step.dependOn(&run_cmd.step);
run_cmd.step.dependOn(b.getInstallStep()); run_cmd.step.dependOn(b.getInstallStep());
} }

View file

@ -30,245 +30,198 @@ mode: enum { normal, drag } = .normal,
// Drag information // Drag information
drag: ?struct { drag: ?struct {
event_code: u32, event_code: u32,
start: struct { start: struct { x: c_int, y: c_int },
x: c_int, view: ?struct { view: *View, dims: struct { width: c_int, height: c_int }, offset: struct {
y: c_int x: c_int,
}, y: c_int,
view: ?struct { } },
view: *View,
dims: struct { width: c_int, height: c_int },
offset: struct { x: c_int, y: c_int, }
},
}, },
pub fn init(self: *Cursor) void { pub fn init(self: *Cursor) void {
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
self.* = .{ self.* = .{
.wlr_cursor = try wlr.Cursor.create(), .wlr_cursor = try wlr.Cursor.create(),
.x_cursor_manager = try wlr.XcursorManager.create(null, 24), .x_cursor_manager = try wlr.XcursorManager.create(null, 24),
.drag = null .drag = null,
}; };
try self.x_cursor_manager.load(1); try self.x_cursor_manager.load(1);
self.wlr_cursor.attachOutputLayout(server.root.output_layout); self.wlr_cursor.attachOutputLayout(server.root.output_layout);
self.wlr_cursor.events.motion.add(&self.motion); self.wlr_cursor.events.motion.add(&self.motion);
self.wlr_cursor.events.motion_absolute.add(&self.motion_absolute); self.wlr_cursor.events.motion_absolute.add(&self.motion_absolute);
self.wlr_cursor.events.button.add(&self.button); self.wlr_cursor.events.button.add(&self.button);
self.wlr_cursor.events.axis.add(&self.axis); self.wlr_cursor.events.axis.add(&self.axis);
self.wlr_cursor.events.frame.add(&self.frame); self.wlr_cursor.events.frame.add(&self.frame);
self.wlr_cursor.events.hold_begin.add(&self.hold_begin); self.wlr_cursor.events.hold_begin.add(&self.hold_begin);
self.wlr_cursor.events.hold_end.add(&self.hold_end); self.wlr_cursor.events.hold_end.add(&self.hold_end);
} }
pub fn deinit(self: *Cursor) void { pub fn deinit(self: *Cursor) void {
self.motion.link.remove(); self.motion.link.remove();
self.motion_absolute.link.remove(); self.motion_absolute.link.remove();
self.button.link.remove(); self.button.link.remove();
self.axis.link.remove(); self.axis.link.remove();
self.frame.link.remove(); self.frame.link.remove();
self.hold_begin.link.remove(); self.hold_begin.link.remove();
self.hold_end.link.remove(); self.hold_end.link.remove();
self.wlr_cursor.destroy(); self.wlr_cursor.destroy();
self.x_cursor_manager.destroy(); self.x_cursor_manager.destroy();
} }
pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { pub fn processCursorMotion(self: *Cursor, time_msec: u32) void {
var passthrough = true; var passthrough = true;
if (self.mode == .drag) { if (self.mode == .drag) {
const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers(); const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers();
std.debug.assert(self.drag != null); std.debug.assert(self.drag != null);
// Proceed if mousemap for current mouse and modifier state's exist // Proceed if mousemap for current mouse and modifier state's exist
if (server.mousemaps.get(Mousemap.hash(modifiers, @bitCast(self.drag.?.event_code)))) |map| { if (server.mousemaps.get(Mousemap.hash(modifiers, @bitCast(self.drag.?.event_code)))) |map| {
if(map.options.lua_drag_ref_idx > 0) { if (map.options.lua_drag_ref_idx > 0) {
passthrough = map.callback(.drag, .{ passthrough = map.callback(.drag, .{ .{ .x = @as(c_int, @intFromFloat(self.wlr_cursor.x)), .y = @as(c_int, @intFromFloat(self.wlr_cursor.y)) }, .{ .start = self.drag.?.start, .view = if (self.drag.?.view != null) .{ .id = self.drag.?.view.?.view.id, .dims = self.drag.?.view.?.dims, .offset = self.drag.?.view.?.offset } else null } });
.{ }
.x = @as(c_int, @intFromFloat(self.wlr_cursor.x)), }
.y = @as(c_int, @intFromFloat(self.wlr_cursor.y))
},
.{
.start = self.drag.?.start,
.view = if (self.drag.?.view != null) .{
.id = self.drag.?.view.?.view.id,
.dims = self.drag.?.view.?.dims,
.offset = self.drag.?.view.?.offset
} else null
}
});
}
} }
}
if(passthrough) { if (passthrough) {
const output = server.seat.focused_output; const output = server.seat.focused_output;
// Exit the switch if no focused output exists // Exit the switch if no focused output exists
std.debug.assert(output != null); std.debug.assert(output != null);
const surfaceAtResult = output.?.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y); const surfaceAtResult = output.?.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y);
if (surfaceAtResult) |surface| { if (surfaceAtResult) |surface| {
if(surface.scene_node_data.* == .view) { if (surface.scene_node_data.* == .view) {
server.events.exec("ViewPointerMotion", .{ server.events.exec("ViewPointerMotion", .{ surface.scene_node_data.view.id, @as(c_int, @intFromFloat(self.wlr_cursor.x)), @as(c_int, @intFromFloat(self.wlr_cursor.y)) });
surface.scene_node_data.view.id, }
@as(c_int, @intFromFloat(self.wlr_cursor.x)),
@as(c_int, @intFromFloat(self.wlr_cursor.y))
});
}
server.seat.wlr_seat.pointerNotifyEnter(surfaceAtResult.?.surface, surfaceAtResult.?.sx, surfaceAtResult.?.sy); server.seat.wlr_seat.pointerNotifyEnter(surfaceAtResult.?.surface, surfaceAtResult.?.sx, surfaceAtResult.?.sy);
server.seat.wlr_seat.pointerNotifyMotion(time_msec, surfaceAtResult.?.sx, surfaceAtResult.?.sy); server.seat.wlr_seat.pointerNotifyMotion(time_msec, surfaceAtResult.?.sx, surfaceAtResult.?.sy);
} else { } else {
// This may not be necessary, remove if no bugs // This may not be necessary, remove if no bugs
server.seat.wlr_seat.pointerClearFocus(); server.seat.wlr_seat.pointerClearFocus();
self.wlr_cursor.setXcursor(self.x_cursor_manager, "default"); self.wlr_cursor.setXcursor(self.x_cursor_manager, "default");
}
} }
}
} }
// --------- WLR Cursor event handlers --------- // --------- WLR Cursor event handlers ---------
fn handleMotion( fn handleMotion(
_: *wl.Listener(*wlr.Pointer.event.Motion), _: *wl.Listener(*wlr.Pointer.event.Motion),
event: *wlr.Pointer.event.Motion, event: *wlr.Pointer.event.Motion,
) void { ) void {
server.cursor.wlr_cursor.move(event.device, event.delta_x, event.delta_y); server.cursor.wlr_cursor.move(event.device, event.delta_x, event.delta_y);
server.cursor.processCursorMotion(event.time_msec); server.cursor.processCursorMotion(event.time_msec);
} }
fn handleMotionAbsolute( fn handleMotionAbsolute(
_: *wl.Listener(*wlr.Pointer.event.MotionAbsolute), _: *wl.Listener(*wlr.Pointer.event.MotionAbsolute),
event: *wlr.Pointer.event.MotionAbsolute, event: *wlr.Pointer.event.MotionAbsolute,
) void { ) void {
server.cursor.wlr_cursor.warpAbsolute(event.device, event.x, event.y); server.cursor.wlr_cursor.warpAbsolute(event.device, event.x, event.y);
server.cursor.processCursorMotion(event.time_msec); server.cursor.processCursorMotion(event.time_msec);
} }
fn handleButton( fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.Pointer.event.Button) void {
listener: *wl.Listener(*wlr.Pointer.event.Button), const cursor: *Cursor = @fieldParentPtr("button", listener);
event: *wlr.Pointer.event.Button
) void {
const cursor: *Cursor = @fieldParentPtr("button", listener);
switch (event.state) {
.pressed => {
cursor.mode = .drag;
cursor.drag = .{
.event_code = event.button,
.start = .{
.x = @as(c_int, @intFromFloat(cursor.wlr_cursor.x)),
.y = @as(c_int, @intFromFloat(cursor.wlr_cursor.y))
},
.view = null
};
// Keep track of where the drag started
if(server.seat.focused_surface) |fs| {
if(fs == .view) {
cursor.drag.?.view = .{
.view = fs.view,
.dims = .{
.width = fs.view.xdg_toplevel.base.geometry.width,
.height = fs.view.xdg_toplevel.base.geometry.height
},
.offset = .{
.x = cursor.drag.?.start.x - fs.view.scene_tree.node.x,
.y = cursor.drag.?.start.y - fs.view.scene_tree.node.y
},
};
}
}
},
.released => {
cursor.mode = .normal;
// How do we do this on the lua side
// if(cursor.drag.view) |view| {
// _ = view.xdg_toplevel.setResizing(false);
// }
cursor.drag.?.view = null;
},
else => {
std.log.err("Invalid/Unimplemented pointer button event type", .{});
}
}
var passthrough = true;
const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers();
// Proceed if mousemap for current mouse and modifier state's exist
if (server.mousemaps.get(Mousemap.hash(modifiers, @bitCast(event.button)))) |map| {
switch (event.state) { switch (event.state) {
.pressed => { .pressed => {
// Only make callback if a callback function exists cursor.mode = .drag;
if(map.options.lua_press_ref_idx > 0) {
passthrough = map.callback(.press, .{ cursor.drag = .{ .event_code = event.button, .start = .{ .x = @as(c_int, @intFromFloat(cursor.wlr_cursor.x)), .y = @as(c_int, @intFromFloat(cursor.wlr_cursor.y)) }, .view = null };
.{
.x = @as(c_int, @intFromFloat(cursor.wlr_cursor.x)), // Keep track of where the drag started
.y = @as(c_int, @intFromFloat(cursor.wlr_cursor.y)) if (server.seat.focused_surface) |fs| {
}, if (fs == .view) {
}); cursor.drag.?.view = .{
} .view = fs.view,
}, .dims = .{ .width = fs.view.xdg_toplevel.base.geometry.width, .height = fs.view.xdg_toplevel.base.geometry.height },
.released => { .offset = .{ .x = cursor.drag.?.start.x - fs.view.scene_tree.node.x, .y = cursor.drag.?.start.y - fs.view.scene_tree.node.y },
if(map.options.lua_press_ref_idx > 0) { };
passthrough = map.callback(.release, .{ }
.{ }
.x = @as(c_int, @intFromFloat(cursor.wlr_cursor.x)), },
.y = @as(c_int, @intFromFloat(cursor.wlr_cursor.y)) .released => {
}, cursor.mode = .normal;
});
} // How do we do this on the lua side
}, // if(cursor.drag.view) |view| {
else => { unreachable; } // _ = view.xdg_toplevel.setResizing(false);
// }
cursor.drag.?.view = null;
},
else => {
std.log.err("Invalid/Unimplemented pointer button event type", .{});
},
} }
}
// If no keymap exists for button event, forward it to a surface var passthrough = true;
// TODO: Allow for transparent mousemaps that pass mouse button events anyways const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers();
if(passthrough) {
_ = server.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state); // Proceed if mousemap for current mouse and modifier state's exist
} if (server.mousemaps.get(Mousemap.hash(modifiers, @bitCast(event.button)))) |map| {
switch (event.state) {
.pressed => {
// Only make callback if a callback function exists
if (map.options.lua_press_ref_idx > 0) {
passthrough = map.callback(.press, .{
.{ .x = @as(c_int, @intFromFloat(cursor.wlr_cursor.x)), .y = @as(c_int, @intFromFloat(cursor.wlr_cursor.y)) },
});
}
},
.released => {
if (map.options.lua_press_ref_idx > 0) {
passthrough = map.callback(.release, .{
.{ .x = @as(c_int, @intFromFloat(cursor.wlr_cursor.x)), .y = @as(c_int, @intFromFloat(cursor.wlr_cursor.y)) },
});
}
},
else => {
unreachable;
},
}
}
// If no keymap exists for button event, forward it to a surface
// TODO: Allow for transparent mousemaps that pass mouse button events anyways
if (passthrough) {
_ = server.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
}
} }
fn handleHoldBegin( fn handleHoldBegin(listener: *wl.Listener(*wlr.Pointer.event.HoldBegin), event: *wlr.Pointer.event.HoldBegin) void {
listener: *wl.Listener(*wlr.Pointer.event.HoldBegin), _ = listener;
event: *wlr.Pointer.event.HoldBegin _ = event;
) void { std.log.err("Unimplemented cursor start hold", .{});
_ = listener;
_ = event;
std.log.err("Unimplemented cursor start hold", .{});
} }
fn handleHoldEnd( fn handleHoldEnd(listener: *wl.Listener(*wlr.Pointer.event.HoldEnd), event: *wlr.Pointer.event.HoldEnd) void {
listener: *wl.Listener(*wlr.Pointer.event.HoldEnd), _ = listener;
event: *wlr.Pointer.event.HoldEnd _ = event;
) void { std.log.err("Unimplemented cursor end hold", .{});
_ = listener;
_ = event;
std.log.err("Unimplemented cursor end hold", .{});
} }
fn handleAxis( fn handleAxis(
_: *wl.Listener(*wlr.Pointer.event.Axis), _: *wl.Listener(*wlr.Pointer.event.Axis),
event: *wlr.Pointer.event.Axis, event: *wlr.Pointer.event.Axis,
) void { ) void {
server.seat.wlr_seat.pointerNotifyAxis( server.seat.wlr_seat.pointerNotifyAxis(
event.time_msec, event.time_msec,
event.orientation, event.orientation,
event.delta, event.delta,
event.delta_discrete, event.delta_discrete,
event.source, event.source,
event.relative_direction, event.relative_direction,
); );
} }
fn handleFrame(_: *wl.Listener(*wlr.Cursor), _: *wlr.Cursor) void { fn handleFrame(_: *wl.Listener(*wlr.Cursor), _: *wlr.Cursor) void {
server.seat.wlr_seat.pointerNotifyFrame(); server.seat.wlr_seat.pointerNotifyFrame();
} }

View file

@ -31,15 +31,10 @@ fn printNode(node: *wlr.SceneNode, depth: usize) void {
const type_name = switch (node.type) { const type_name = switch (node.type) {
.tree => "TREE", .tree => "TREE",
.rect => "RECT", .rect => "RECT",
.buffer => "BUFFER" .buffer => "BUFFER",
}; };
writer.print("{s} @ ({d}, {d}) enabled={}", .{ writer.print("{s} @ ({d}, {d}) enabled={}", .{ type_name, node.x, node.y, node.enabled }) catch unreachable;
type_name,
node.x,
node.y,
node.enabled
}) catch unreachable;
// Add associated data if present // Add associated data if present
if (node.data) |data| { if (node.data) |data| {
@ -56,7 +51,7 @@ fn printNode(node: *wlr.SceneNode, depth: usize) void {
}) catch unreachable; }) catch unreachable;
}, },
.output_layer => { .output_layer => {
writer.print(" → Output Layer", .{}) catch unreachable; writer.print(" → Output Layer", .{}) catch unreachable;
}, },
.view => |view| { .view => |view| {
writer.print(" → View: id={} mapped={} focused={}", .{ writer.print(" → View: id={} mapped={} focused={}", .{
@ -84,7 +79,7 @@ fn printNode(node: *wlr.SceneNode, depth: usize) void {
if (namespace.len > 0) { if (namespace.len > 0) {
writer.print(" namespace=\"{s}\"", .{namespace}) catch unreachable; writer.print(" namespace=\"{s}\"", .{namespace}) catch unreachable;
} }
} },
} }
} }

View file

@ -26,120 +26,114 @@ modifiers: wl.Listener(*wlr.Keyboard) = .init(handleModifiers),
destroy: wl.Listener(*wlr.InputDevice) = .init(handleDestroy), destroy: wl.Listener(*wlr.InputDevice) = .init(handleDestroy),
pub fn init(device: *wlr.InputDevice) *Keyboard { pub fn init(device: *wlr.InputDevice) *Keyboard {
const self = gpa.create(Keyboard) catch Utils.oomPanic(); const self = gpa.create(Keyboard) catch Utils.oomPanic();
errdefer { errdefer {
std.log.err("Unable to initialize new keyboard, exiting", .{}); std.log.err("Unable to initialize new keyboard, exiting", .{});
std.process.exit(6); std.process.exit(6);
} }
self.* = .{ self.* = .{
.context = xkb.Context.new(.no_flags) orelse return error.ContextFailed, .context = xkb.Context.new(.no_flags) orelse return error.ContextFailed,
.wlr_keyboard = device.toKeyboard(), .wlr_keyboard = device.toKeyboard(),
.device = device, .device = device,
}; };
// TODO: configure this via lua later // TODO: configure this via lua later
// Should handle this error here // Should handle this error here
if (!self.wlr_keyboard.setKeymap(server.seat.keymap)) return error.SetKeymapFailed; if (!self.wlr_keyboard.setKeymap(server.seat.keymap)) return error.SetKeymapFailed;
self.wlr_keyboard.setRepeatInfo(25, 600); self.wlr_keyboard.setRepeatInfo(25, 600);
self.wlr_keyboard.events.modifiers.add(&self.modifiers); self.wlr_keyboard.events.modifiers.add(&self.modifiers);
self.wlr_keyboard.events.key.add(&self.key); self.wlr_keyboard.events.key.add(&self.key);
self.wlr_keyboard.events.keymap.add(&self.key_map); self.wlr_keyboard.events.keymap.add(&self.key_map);
device.events.destroy.add(&self.destroy); device.events.destroy.add(&self.destroy);
self.wlr_keyboard.data = self; self.wlr_keyboard.data = self;
std.log.err("Adding new keyboard {s}", .{device.name orelse "(unnamed)"}); std.log.err("Adding new keyboard {s}", .{device.name orelse "(unnamed)"});
if(!server.seat.keyboard_group.wlr_group.addKeyboard(self.wlr_keyboard)) { if (!server.seat.keyboard_group.wlr_group.addKeyboard(self.wlr_keyboard)) {
std.log.err("Adding new keyboard {s} failed", .{device.name orelse "(unnamed)"}); std.log.err("Adding new keyboard {s} failed", .{device.name orelse "(unnamed)"});
} }
return self; return self;
} }
pub fn deinit (self: *Keyboard) void { pub fn deinit(self: *Keyboard) void {
self.key.link.remove(); self.key.link.remove();
self.key_map.link.remove(); self.key_map.link.remove();
self.modifiers.link.remove(); self.modifiers.link.remove();
} }
fn handleModifiers(_: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) void { fn handleModifiers(_: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) void {
server.seat.wlr_seat.setKeyboard(wlr_keyboard); server.seat.wlr_seat.setKeyboard(wlr_keyboard);
server.seat.wlr_seat.keyboardNotifyModifiers(&wlr_keyboard.modifiers); server.seat.wlr_seat.keyboardNotifyModifiers(&wlr_keyboard.modifiers);
} }
fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void { fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void {
const keyboard: *Keyboard = @fieldParentPtr("key", listener); const keyboard: *Keyboard = @fieldParentPtr("key", listener);
// Translate libinput keycode -> xkbcommon // Translate libinput keycode -> xkbcommon
const keycode = event.keycode + 8; const keycode = event.keycode + 8;
var handled: bool = false; var handled: bool = false;
const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers(); const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers();
if (server.seat.keyboard_group.wlr_group.keyboard.xkb_state) |xkb_state| { if (server.seat.keyboard_group.wlr_group.keyboard.xkb_state) |xkb_state| {
const keysyms = xkb_state.keyGetSyms(keycode); const keysyms = xkb_state.keyGetSyms(keycode);
for (keysyms) |sym| { for (keysyms) |sym| {
handled = keypress(modifiers, sym, event.state); handled = keypress(modifiers, sym, event.state);
}
// give the keyboard group information about what to repeat and update it
if (handled and keyboard.wlr_keyboard.repeat_info.delay > 0) {
server.seat.keyboard_group.modifiers = modifiers;
server.seat.keyboard_group.keysyms = keysyms;
server.seat.keyboard_group.repeat_source.?.timerUpdate(
keyboard.wlr_keyboard.repeat_info.delay,
) catch {
std.log.warn("failed to update keyboard repeat timer", .{});
};
} else {
server.seat.keyboard_group.modifiers = null;
server.seat.keyboard_group.keysyms = null;
}
} }
// give the keyboard group information about what to repeat and update it if (handled and keyboard.wlr_keyboard.repeat_info.delay > 0) {}
if (handled and keyboard.wlr_keyboard.repeat_info.delay > 0) {
server.seat.keyboard_group.modifiers = modifiers; if (!handled) {
server.seat.keyboard_group.keysyms = keysyms; server.seat.wlr_seat.setKeyboard(&server.seat.keyboard_group.wlr_group.keyboard);
server.seat.keyboard_group.repeat_source.?.timerUpdate( server.seat.wlr_seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state);
keyboard.wlr_keyboard.repeat_info.delay,
) catch {
std.log.warn("failed to update keyboard repeat timer", .{});
};
} else {
server.seat.keyboard_group.modifiers = null;
server.seat.keyboard_group.keysyms = null;
} }
}
if (handled and keyboard.wlr_keyboard.repeat_info.delay > 0) {
}
if (!handled) {
server.seat.wlr_seat.setKeyboard(&server.seat.keyboard_group.wlr_group.keyboard);
server.seat.wlr_seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state);
}
} }
pub fn keypress( pub fn keypress(modifiers: wlr.Keyboard.ModifierMask, sym: xkb.Keysym, state: wl.Keyboard.KeyState) bool {
modifiers: wlr.Keyboard.ModifierMask, if (server.keymaps.get(Keymap.hash(modifiers, sym))) |map| {
sym: xkb.Keysym, if (state == .pressed and map.options.lua_press_ref_idx > 0) {
state: wl.Keyboard.KeyState map.callback(false);
) bool { return true;
if (server.keymaps.get(Keymap.hash(modifiers, sym))) |map| { } else if (state == .released and map.options.lua_release_ref_idx > 0) {
if (state == .pressed and map.options.lua_press_ref_idx > 0) { map.callback(true);
map.callback(false); return true;
return true; }
} else if (state == .released and map.options.lua_release_ref_idx > 0) {
map.callback(true);
return true;
} }
}
return false; return false;
} }
fn handleKeyMap(_: *wl.Listener(*wlr.Keyboard), _: *wlr.Keyboard) void { fn handleKeyMap(_: *wl.Listener(*wlr.Keyboard), _: *wlr.Keyboard) void {
std.log.err("Unimplemented handle keyboard keymap", .{}); std.log.err("Unimplemented handle keyboard keymap", .{});
} }
pub fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) void { pub fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) void {
const keyboard: *Keyboard = @fieldParentPtr("destroy", listener); const keyboard: *Keyboard = @fieldParentPtr("destroy", listener);
std.log.debug("removing keyboard: {s}", .{keyboard.device.name orelse "(null)"}); std.log.debug("removing keyboard: {s}", .{keyboard.device.name orelse "(null)"});
keyboard.modifiers.link.remove(); keyboard.modifiers.link.remove();
keyboard.key.link.remove(); keyboard.key.link.remove();
keyboard.key_map.link.remove(); keyboard.key_map.link.remove();
keyboard.destroy.link.remove(); keyboard.destroy.link.remove();
gpa.destroy(keyboard); gpa.destroy(keyboard);
} }

View file

@ -6,7 +6,7 @@ const wlr = @import("wlroots");
const xkb = @import("xkbcommon"); const xkb = @import("xkbcommon");
const Keyboard = @import("Keyboard.zig"); const Keyboard = @import("Keyboard.zig");
const Utils = @import("Utils.zig"); const Utils = @import("Utils.zig");
const server = &@import("main.zig").server; const server = &@import("main.zig").server;
const gpa = std.heap.c_allocator; const gpa = std.heap.c_allocator;
@ -17,47 +17,48 @@ modifiers: ?wlr.Keyboard.ModifierMask,
keysyms: ?[]const xkb.Keysym, keysyms: ?[]const xkb.Keysym,
pub fn init() *KeyboardGroup { pub fn init() *KeyboardGroup {
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
const self = try gpa.create(KeyboardGroup); const self = try gpa.create(KeyboardGroup);
self.* = .{ self.* = .{
.wlr_group = wlr.KeyboardGroup.create() catch Utils.oomPanic(), .wlr_group = wlr.KeyboardGroup.create() catch Utils.oomPanic(),
.repeat_source = blk: { .repeat_source = blk: {
break :blk server.event_loop.addTimer(?*KeyboardGroup, handleRepeat, self) catch { break :blk server.event_loop.addTimer(?*KeyboardGroup, handleRepeat, self) catch {
std.log.err("Failed to create event loop timer, keyboard repeating will not work!", .{}); std.log.err("Failed to create event loop timer, keyboard repeating will not work!", .{});
break :blk null; break :blk null;
}; };
}, },
.modifiers = null, .modifiers = null,
.keysyms = null, .keysyms = null,
}; };
return self; return self;
} }
pub fn deinit(self: *KeyboardGroup) void { pub fn deinit(self: *KeyboardGroup) void {
self.wlr_group.destroy(); self.wlr_group.destroy();
gpa.destroy(self); gpa.destroy(self);
} }
fn handleRepeat(data: ?*KeyboardGroup) c_int { fn handleRepeat(data: ?*KeyboardGroup) c_int {
// we failed to create the event loop timer, which means we can't repeat keys // we failed to create the event loop timer, which means we can't repeat keys
if (data.?.repeat_source == null) return 0; if (data.?.repeat_source == null) return 0;
if (data == null or data.?.keysyms == null or data.?.modifiers == null or
data.?.wlr_group.keyboard.repeat_info.rate <= 0)
{
return 0;
}
data.?.repeat_source.?.timerUpdate(
@divTrunc(1000, data.?.wlr_group.keyboard.repeat_info.rate),
) catch {
// not sure how big of a deal it is if we miss a timer update
std.log.warn("failed to update keyboard repeat timer", .{});
};
for (data.?.keysyms.?) |sym| {
_ = Keyboard.keypress(data.?.modifiers.?, sym, .pressed);
}
if (data == null or data.?.keysyms == null or data.?.modifiers == null or
data.?.wlr_group.keyboard.repeat_info.rate <= 0) {
return 0; return 0;
}
data.?.repeat_source.?.timerUpdate(
@divTrunc(1000, data.?.wlr_group.keyboard.repeat_info.rate),
) catch {
// not sure how big of a deal it is if we miss a timer update
std.log.warn("failed to update keyboard repeat timer", .{});
};
for (data.?.keysyms.?) |sym| {
_ = Keyboard.keypress(data.?.modifiers.?, sym, .pressed);
}
return 0;
} }

View file

@ -23,109 +23,97 @@ commit: wl.Listener(*wlr.Surface) = .init(handleCommit),
// new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup), // new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface { pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface {
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
const self = try gpa.create(LayerSurface); const self = try gpa.create(LayerSurface);
self.* = .{ self.* = .{
.output = undefined, .output = undefined,
.wlr_layer_surface = wlr_layer_surface, .wlr_layer_surface = wlr_layer_surface,
.scene_layer_surface = undefined, .scene_layer_surface = undefined,
.scene_node_data = .{ .layer_surface = self } .scene_node_data = .{ .layer_surface = self },
};
if(wlr_layer_surface.output.?.data == null) {
std.log.err("Wlr_output arbitrary data not assigned", .{});
unreachable;
}
self.output = @ptrCast(@alignCast(wlr_layer_surface.output.?.data));
if(server.seat.focused_output) |output| {
self.scene_layer_surface = switch (wlr_layer_surface.current.layer) {
.background => try output.layers.background.createSceneLayerSurfaceV1(wlr_layer_surface),
.bottom => try output.layers.bottom.createSceneLayerSurfaceV1(wlr_layer_surface),
.top => try output.layers.top.createSceneLayerSurfaceV1(wlr_layer_surface),
.overlay => try output.layers.overlay.createSceneLayerSurfaceV1(wlr_layer_surface),
else => {
std.log.err("New layer surface of unidentified type", .{});
unreachable;
}
}; };
}
self.wlr_layer_surface.surface.data = &self.scene_node_data; if (wlr_layer_surface.output.?.data == null) {
self.scene_layer_surface.tree.node.data = &self.scene_node_data; std.log.err("Wlr_output arbitrary data not assigned", .{});
unreachable;
}
self.output = @ptrCast(@alignCast(wlr_layer_surface.output.?.data));
self.wlr_layer_surface.events.destroy.add(&self.destroy); if (server.seat.focused_output) |output| {
self.wlr_layer_surface.surface.events.map.add(&self.map); self.scene_layer_surface = switch (wlr_layer_surface.current.layer) {
self.wlr_layer_surface.surface.events.unmap.add(&self.unmap); .background => try output.layers.background.createSceneLayerSurfaceV1(wlr_layer_surface),
self.wlr_layer_surface.surface.events.commit.add(&self.commit); .bottom => try output.layers.bottom.createSceneLayerSurfaceV1(wlr_layer_surface),
.top => try output.layers.top.createSceneLayerSurfaceV1(wlr_layer_surface),
.overlay => try output.layers.overlay.createSceneLayerSurfaceV1(wlr_layer_surface),
else => {
std.log.err("New layer surface of unidentified type", .{});
unreachable;
},
};
}
return self; self.wlr_layer_surface.surface.data = &self.scene_node_data;
self.scene_layer_surface.tree.node.data = &self.scene_node_data;
self.wlr_layer_surface.events.destroy.add(&self.destroy);
self.wlr_layer_surface.surface.events.map.add(&self.map);
self.wlr_layer_surface.surface.events.unmap.add(&self.unmap);
self.wlr_layer_surface.surface.events.commit.add(&self.commit);
return self;
} }
pub fn deinit(self: *LayerSurface) void { pub fn deinit(self: *LayerSurface) void {
self.destroy.link.remove(); self.destroy.link.remove();
self.map.link.remove(); self.map.link.remove();
self.unmap.link.remove(); self.unmap.link.remove();
self.commit.link.remove(); self.commit.link.remove();
self.wlr_layer_surface.surface.data = null; self.wlr_layer_surface.surface.data = null;
gpa.destroy(self); gpa.destroy(self);
} }
pub fn allowKeyboard(self: *LayerSurface) void { pub fn allowKeyboard(self: *LayerSurface) void {
const keyboard_interactive = self.wlr_layer_surface.current.keyboard_interactive; const keyboard_interactive = self.wlr_layer_surface.current.keyboard_interactive;
if(keyboard_interactive == .exclusive or keyboard_interactive == .on_demand) { if (keyboard_interactive == .exclusive or keyboard_interactive == .on_demand) {
server.seat.wlr_seat.keyboardNotifyEnter( server.seat.wlr_seat.keyboardNotifyEnter(self.wlr_layer_surface.surface, &server.seat.keyboard_group.wlr_group.keyboard.keycodes, &server.seat.keyboard_group.wlr_group.keyboard.modifiers);
self.wlr_layer_surface.surface, }
&server.seat.keyboard_group.wlr_group.keyboard.keycodes,
&server.seat.keyboard_group.wlr_group.keyboard.modifiers
);
}
} }
// --------- LayerSurface event handlers --------- // --------- LayerSurface event handlers ---------
fn handleDestroy( fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), _: *wlr.LayerSurfaceV1) void {
listener: *wl.Listener(*wlr.LayerSurfaceV1), const layer: *LayerSurface = @fieldParentPtr("destroy", listener);
_: *wlr.LayerSurfaceV1 layer.deinit();
) void {
const layer: *LayerSurface = @fieldParentPtr("destroy", listener);
layer.deinit();
} }
fn handleMap( fn handleMap(listener: *wl.Listener(void)) void {
listener: *wl.Listener(void) const layer_suraface: *LayerSurface = @fieldParentPtr("map", listener);
) void { std.log.debug("layer surface mapped", .{});
const layer_suraface: *LayerSurface = @fieldParentPtr("map", listener); layer_suraface.output.arrangeLayers();
std.log.debug("layer surface mapped", .{}); layer_suraface.allowKeyboard();
layer_suraface.output.arrangeLayers();
layer_suraface.allowKeyboard();
} }
fn handleUnmap(listener: *wl.Listener(void)) void { fn handleUnmap(listener: *wl.Listener(void)) void {
const layer_surface: *LayerSurface = @fieldParentPtr("unmap", listener); const layer_surface: *LayerSurface = @fieldParentPtr("unmap", listener);
if (server.seat.focused_surface) |fs| { if (server.seat.focused_surface) |fs| {
if (fs == .layer_surface and fs.layer_surface == layer_surface) { if (fs == .layer_surface and fs.layer_surface == layer_surface) {
server.seat.focusSurface(null); server.seat.focusSurface(null);
}
} }
}
// FIXME: this crashes mez when killing mez // FIXME: this crashes mez when killing mez
layer_surface.output.arrangeLayers(); layer_surface.output.arrangeLayers();
// TODO: Idk if this should be deiniting the layer surface entirely // TODO: Idk if this should be deiniting the layer surface entirely
layer_surface.deinit(); layer_surface.deinit();
} }
fn handleCommit( fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
listener: *wl.Listener(*wlr.Surface), const layer_surface: *LayerSurface = @fieldParentPtr("commit", listener);
_: *wlr.Surface
) void {
const layer_surface: *LayerSurface = @fieldParentPtr("commit", listener);
if (!layer_surface.wlr_layer_surface.initial_commit) return; if (!layer_surface.wlr_layer_surface.initial_commit) return;
layer_surface.output.arrangeLayers(); layer_surface.output.arrangeLayers();
} }

View file

@ -5,10 +5,10 @@ const zwlr = @import("wayland").server.zwlr;
const wlr = @import("wlroots"); const wlr = @import("wlroots");
const std = @import("std"); const std = @import("std");
const Utils = @import("Utils.zig"); const Utils = @import("Utils.zig");
const Server = @import("Server.zig"); const Server = @import("Server.zig");
const View = @import("View.zig"); const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig"); const LayerSurface = @import("LayerSurface.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData; const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
@ -28,168 +28,167 @@ scene_node_data: SceneNodeData,
scene_output: *wlr.SceneOutput, scene_output: *wlr.SceneOutput,
layers: struct { layers: struct {
background: *wlr.SceneTree, background: *wlr.SceneTree,
bottom: *wlr.SceneTree, bottom: *wlr.SceneTree,
content: *wlr.SceneTree, content: *wlr.SceneTree,
top: *wlr.SceneTree, top: *wlr.SceneTree,
fullscreen: *wlr.SceneTree, fullscreen: *wlr.SceneTree,
overlay: *wlr.SceneTree overlay: *wlr.SceneTree,
}, },
layer_scene_node_data: struct { layer_scene_node_data: struct {
background: SceneNodeData, background: SceneNodeData,
bottom: SceneNodeData, bottom: SceneNodeData,
content: SceneNodeData, content: SceneNodeData,
top: SceneNodeData, top: SceneNodeData,
fullscreen: SceneNodeData, fullscreen: SceneNodeData,
overlay: SceneNodeData overlay: SceneNodeData,
}, },
frame: wl.Listener(*wlr.Output) = .init(handleFrame), frame: wl.Listener(*wlr.Output) = .init(handleFrame),
request_state: wl.Listener(*wlr.Output.event.RequestState) = .init(handleRequestState), request_state: wl.Listener(*wlr.Output.event.RequestState) = .init(handleRequestState),
destroy: wl.Listener(*wlr.Output) = .init(handleDestroy), destroy: wl.Listener(*wlr.Output) = .init(handleDestroy),
// The wlr.Output should be destroyed by the caller on failure to trigger cleanup. // The wlr.Output should be destroyed by the caller on failure to trigger cleanup.
pub fn init(wlr_output: *wlr.Output) ?*Output { pub fn init(wlr_output: *wlr.Output) ?*Output {
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
const self = try gpa.create(Output); const self = try gpa.create(Output);
self.* = .{ self.* = .{
.focused = false, .focused = false,
.id = @intFromPtr(wlr_output), .id = @intFromPtr(wlr_output),
.wlr_output = wlr_output, .wlr_output = wlr_output,
.tree = try server.root.scene.tree.createSceneTree(), .tree = try server.root.scene.tree.createSceneTree(),
.fullscreen = null, .fullscreen = null,
.layers = .{
.background = try self.tree.createSceneTree(),
.bottom = try self.tree.createSceneTree(),
.content = try self.tree.createSceneTree(),
.top = try self.tree.createSceneTree(),
.fullscreen = try self.tree.createSceneTree(),
.overlay = try self.tree.createSceneTree(),
},
.layer_scene_node_data = .{
.background = .{ .output_layer = self.layers.background },
.bottom = .{ .output_layer = self.layers.bottom },
.content = .{ .output_layer = self.layers.content },
.top = .{ .output_layer = self.layers.top },
.fullscreen = .{ .output_layer = self.layers.fullscreen },
.overlay = .{ .output_layer = self.layers.overlay }
},
.scene_output = try server.root.scene.createSceneOutput(wlr_output),
.scene_node_data = SceneNodeData{ .output = self },
.state = wlr.Output.State.init()
};
wlr_output.events.frame.add(&self.frame); .layers = .{
wlr_output.events.request_state.add(&self.request_state); .background = try self.tree.createSceneTree(),
wlr_output.events.destroy.add(&self.destroy); .bottom = try self.tree.createSceneTree(),
.content = try self.tree.createSceneTree(),
.top = try self.tree.createSceneTree(),
.fullscreen = try self.tree.createSceneTree(),
.overlay = try self.tree.createSceneTree(),
},
errdefer deinit(self); .layer_scene_node_data = .{
.background = .{ .output_layer = self.layers.background },
.bottom = .{ .output_layer = self.layers.bottom },
.content = .{ .output_layer = self.layers.content },
.top = .{ .output_layer = self.layers.top },
.fullscreen = .{ .output_layer = self.layers.fullscreen },
.overlay = .{ .output_layer = self.layers.overlay },
},
if(!wlr_output.initRender(server.allocator, server.renderer)) { .scene_output = try server.root.scene.createSceneOutput(wlr_output),
std.log.err("Unable to start output {s}", .{wlr_output.name}); .scene_node_data = SceneNodeData{ .output = self },
return null; .state = wlr.Output.State.init()
} };
self.state.setEnabled(true); wlr_output.events.frame.add(&self.frame);
wlr_output.events.request_state.add(&self.request_state);
wlr_output.events.destroy.add(&self.destroy);
if (wlr_output.preferredMode()) |mode| { errdefer deinit(self);
self.state.setMode(mode);
}
if(!wlr_output.commitState(&self.state)) { if (!wlr_output.initRender(server.allocator, server.renderer)) {
std.log.err("Unable to commit state to output {s}", .{wlr_output.name}); std.log.err("Unable to start output {s}", .{wlr_output.name});
return null; return null;
} }
// TODO: Allow user to define output positions self.state.setEnabled(true);
const layout_output = try server.root.output_layout.addAuto(self.wlr_output);
server.root.scene_output_layout.addOutput(layout_output, self.scene_output);
self.setFocused();
self.wlr_output.data = self; if (wlr_output.preferredMode()) |mode| {
self.tree.node.data = &self.scene_node_data; self.state.setMode(mode);
}
self.layers.background.node.data = &self.layer_scene_node_data.background; if (!wlr_output.commitState(&self.state)) {
self.layers.bottom.node.data = &self.layer_scene_node_data.bottom; std.log.err("Unable to commit state to output {s}", .{wlr_output.name});
self.layers.content.node.data = &self.layer_scene_node_data.content; return null;
self.layers.top.node.data = &self.layer_scene_node_data.top; }
self.layers.fullscreen.node.data = &self.layer_scene_node_data.fullscreen;
self.layers.overlay.node.data = &self.layer_scene_node_data.overlay;
server.events.exec("OutputInitPost", .{self.id}); // TODO: Allow user to define output positions
const layout_output = try server.root.output_layout.addAuto(self.wlr_output);
server.root.scene_output_layout.addOutput(layout_output, self.scene_output);
self.setFocused();
return self; self.wlr_output.data = self;
self.tree.node.data = &self.scene_node_data;
self.layers.background.node.data = &self.layer_scene_node_data.background;
self.layers.bottom.node.data = &self.layer_scene_node_data.bottom;
self.layers.content.node.data = &self.layer_scene_node_data.content;
self.layers.top.node.data = &self.layer_scene_node_data.top;
self.layers.fullscreen.node.data = &self.layer_scene_node_data.fullscreen;
self.layers.overlay.node.data = &self.layer_scene_node_data.overlay;
server.events.exec("OutputInitPost", .{self.id});
return self;
} }
pub fn deinit(self: *Output) void { pub fn deinit(self: *Output) void {
server.events.exec("OutputDeinitPre", .{self.id}); server.events.exec("OutputDeinitPre", .{self.id});
self.frame.link.remove(); self.frame.link.remove();
self.request_state.link.remove(); self.request_state.link.remove();
self.destroy.link.remove(); self.destroy.link.remove();
self.state.finish(); self.state.finish();
self.wlr_output.destroy(); self.wlr_output.destroy();
server.events.exec("OutputDeinitPost", .{}); server.events.exec("OutputDeinitPost", .{});
gpa.destroy(self); gpa.destroy(self);
} }
pub fn setFocused(self: *Output) void { pub fn setFocused(self: *Output) void {
if(server.seat.focused_output) |prev_output| { if (server.seat.focused_output) |prev_output| {
prev_output.focused = false; prev_output.focused = false;
} }
server.seat.focused_output = self; server.seat.focused_output = self;
self.focused = true; self.focused = true;
} }
pub fn configureLayers(self: *Output) void { pub fn configureLayers(self: *Output) void {
var output_box: wlr.Box = .{ var output_box: wlr.Box = .{
.x = 0, .x = 0,
.y = 0, .y = 0,
.width = undefined, .width = undefined,
.height = undefined, .height = undefined,
};
self.wlr_output.effectiveResolution(&output_box.width, &output_box.height);
// Should calculate usable area here for LUA view positioning
for ([_]zwlr.LayerShellV1.Layer{ .background, .bottom, .top, .overlay }) |layer| {
const tree = blk: {
const trees = [_]*wlr.SceneTree{
self.layers.background,
self.layers.bottom,
self.layers.top,
self.layers.overlay,
};
break :blk trees[@intCast(@intFromEnum(layer))];
}; };
self.wlr_output.effectiveResolution(&output_box.width, &output_box.height);
var it = tree.children.iterator(.forward); // Should calculate usable area here for LUA view positioning
while(it.next()) |node| { for ([_]zwlr.LayerShellV1.Layer{ .background, .bottom, .top, .overlay }) |layer| {
if(node.data == null) continue; const tree = blk: {
const trees = [_]*wlr.SceneTree{
self.layers.background,
self.layers.bottom,
self.layers.top,
self.layers.overlay,
};
break :blk trees[@intCast(@intFromEnum(layer))];
};
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); var it = tree.children.iterator(.forward);
switch (scene_node_data.*) { while (it.next()) |node| {
.layer_surface => { if (node.data == null) continue;
_ = scene_node_data.layer_surface.wlr_layer_surface.configured(
@intCast(output_box.width), const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
@intCast(output_box.height) switch (scene_node_data.*) {
); .layer_surface => {
}, _ = scene_node_data.layer_surface.wlr_layer_surface.configured(@intCast(output_box.width), @intCast(output_box.height));
else => { },
std.log.err("Something other than a layer surface found in layer surface scene trees", .{}); else => {
unreachable; std.log.err("Something other than a layer surface found in layer surface scene trees", .{});
unreachable;
},
}
} }
}
} }
}
} }
const SurfaceAtResult = struct { const SurfaceAtResult = struct {
@ -200,165 +199,152 @@ const SurfaceAtResult = struct {
}; };
pub fn surfaceAt(self: *Output, lx: f64, ly: f64) ?SurfaceAtResult { pub fn surfaceAt(self: *Output, lx: f64, ly: f64) ?SurfaceAtResult {
var sx: f64 = undefined; var sx: f64 = undefined;
var sy: f64 = undefined; var sy: f64 = undefined;
const layers = [_]*wlr.SceneTree{ const layers = [_]*wlr.SceneTree{ self.layers.top, self.layers.overlay, self.layers.fullscreen, self.layers.content, self.layers.bottom, self.layers.background };
self.layers.top,
self.layers.overlay,
self.layers.fullscreen,
self.layers.content,
self.layers.bottom,
self.layers.background
};
for(layers) |layer| { for (layers) |layer| {
const node = layer.node.at(lx, ly, &sx, &sy); const node = layer.node.at(lx, ly, &sx, &sy);
if(node == null) continue; if (node == null) continue;
const surface: ?*wlr.Surface = blk: { const surface: ?*wlr.Surface = blk: {
if (node.?.type == .buffer) { if (node.?.type == .buffer) {
const scene_buffer = wlr.SceneBuffer.fromNode(node.?); const scene_buffer = wlr.SceneBuffer.fromNode(node.?);
if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| { if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| {
break :blk scene_surface.surface; break :blk scene_surface.surface;
} }
} }
break :blk null; break :blk null;
};
if(surface == null) continue;
const scene_node_data: *SceneNodeData = blk: {
var n = node.?;
while (true) {
if (@as(?*SceneNodeData, @ptrCast(@alignCast(n.data)))) |snd| {
break :blk snd;
}
if (n.parent) |parent_tree| {
n = &parent_tree.node;
} else {
continue;
}
}
};
switch (scene_node_data.*) {
.layer_surface, .view => {
return SurfaceAtResult{
.scene_node_data = scene_node_data,
.surface = surface.?,
.sx = sx,
.sy = sy,
}; };
}, if (surface == null) continue;
else => continue
}
}
return null; const scene_node_data: *SceneNodeData = blk: {
var n = node.?;
while (true) {
if (@as(?*SceneNodeData, @ptrCast(@alignCast(n.data)))) |snd| {
break :blk snd;
}
if (n.parent) |parent_tree| {
n = &parent_tree.node;
} else {
continue;
}
}
};
switch (scene_node_data.*) {
.layer_surface, .view => {
return SurfaceAtResult{
.scene_node_data = scene_node_data,
.surface = surface.?,
.sx = sx,
.sy = sy,
};
},
else => continue,
}
}
return null;
} }
pub fn getFullscreenedView(self: *Output) ?*View { pub fn getFullscreenedView(self: *Output) ?*View {
if(self.layers.fullscreen.children.length() != 1) { if (self.layers.fullscreen.children.length() != 1) {
return null; return null;
}
var it = self.layers.fullscreen.children.iterator(.forward);
if(it.next().?.data) |data| {
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(data));
if(scene_node_data.* == .view) {
return scene_node_data.view;
} }
}
return null; var it = self.layers.fullscreen.children.iterator(.forward);
if (it.next().?.data) |data| {
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(data));
if (scene_node_data.* == .view) {
return scene_node_data.view;
}
}
return null;
} }
// --------- WlrOutput Event Handlers --------- // --------- WlrOutput Event Handlers ---------
fn handleRequestState( fn handleRequestState(
listener: *wl.Listener(*wlr.Output.event.RequestState), listener: *wl.Listener(*wlr.Output.event.RequestState),
event: *wlr.Output.event.RequestState, event: *wlr.Output.event.RequestState,
) void { ) void {
const output: *Output = @fieldParentPtr("request_state", listener); const output: *Output = @fieldParentPtr("request_state", listener);
if (!output.wlr_output.commitState(event.state)) { if (!output.wlr_output.commitState(event.state)) {
std.log.warn("failed to set output state {}", .{event.state}); std.log.warn("failed to set output state {}", .{event.state});
} }
server.events.exec("OutputStateChange", .{}); server.events.exec("OutputStateChange", .{});
} }
fn handleFrame( fn handleFrame(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
_: *wl.Listener(*wlr.Output), const scene_output = server.root.scene.getSceneOutput(wlr_output);
wlr_output: *wlr.Output
) void {
const scene_output = server.root.scene.getSceneOutput(wlr_output);
if(scene_output == null) { if (scene_output == null) {
std.log.err("Unable to get scene output to render", .{}); std.log.err("Unable to get scene output to render", .{});
return; return;
} }
// std.log.info("Rendering commited scene output\n", .{}); // std.log.info("Rendering commited scene output\n", .{});
_ = scene_output.?.commit(null); _ = scene_output.?.commit(null);
var now = posix.clock_gettime(posix.CLOCK.MONOTONIC) catch @panic("CLOCK_MONOTONIC not supported"); var now = posix.clock_gettime(posix.CLOCK.MONOTONIC) catch @panic("CLOCK_MONOTONIC not supported");
scene_output.?.sendFrameDone(&now); scene_output.?.sendFrameDone(&now);
} }
fn handleDestroy( fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
listener: *wl.Listener(*wlr.Output), std.log.debug("Handling destroy", .{});
_: *wlr.Output const output: *Output = @fieldParentPtr("destroy", listener);
) void {
std.log.debug("Handling destroy", .{});
const output: *Output = @fieldParentPtr("destroy", listener);
std.log.debug("removing output: {s}", .{output.wlr_output.name}); std.log.debug("removing output: {s}", .{output.wlr_output.name});
output.frame.link.remove(); output.frame.link.remove();
output.request_state.link.remove(); output.request_state.link.remove();
output.destroy.link.remove(); output.destroy.link.remove();
server.root.output_layout.remove(output.wlr_output); server.root.output_layout.remove(output.wlr_output);
gpa.destroy(output); gpa.destroy(output);
} }
pub fn arrangeLayers(self: *Output) void { pub fn arrangeLayers(self: *Output) void {
var full_box: wlr.Box = .{ var full_box: wlr.Box = .{
.x = 0, .x = 0,
.y = 0, .y = 0,
.width = undefined, .width = undefined,
.height = undefined, .height = undefined,
}; };
self.wlr_output.effectiveResolution(&full_box.width, &full_box.height); self.wlr_output.effectiveResolution(&full_box.width, &full_box.height);
inline for (@typeInfo(zwlr.LayerShellV1.Layer).@"enum".fields) |comptime_layer| { inline for (@typeInfo(zwlr.LayerShellV1.Layer).@"enum".fields) |comptime_layer| {
const layer: *wlr.SceneTree = @field(self.layers, comptime_layer.name); const layer: *wlr.SceneTree = @field(self.layers, comptime_layer.name);
var it = layer.children.safeIterator(.forward); var it = layer.children.safeIterator(.forward);
while (it.next()) |node| { while (it.next()) |node| {
if(node.data == null) continue; if (node.data == null) continue;
// if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| { // if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| {
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
const layer_surface: *LayerSurface = switch(scene_node_data.*) { const layer_surface: *LayerSurface = switch (scene_node_data.*) {
.layer_surface => @fieldParentPtr("scene_node_data", scene_node_data), .layer_surface => @fieldParentPtr("scene_node_data", scene_node_data),
else => continue else => continue,
}; };
if (!layer_surface.wlr_layer_surface.initialized) continue; if (!layer_surface.wlr_layer_surface.initialized) continue;
// TEST: river seems to try and prevent clients from taking an // TEST: river seems to try and prevent clients from taking an
// exclusive size greater than half the screen by killing them. Do we // exclusive size greater than half the screen by killing them. Do we
// need to? Clients can do quite a bit of nasty stuff and taking // need to? Clients can do quite a bit of nasty stuff and taking
// exclusive focus isn't even that bad. // exclusive focus isn't even that bad.
layer_surface.scene_layer_surface.configure(&full_box, &full_box); layer_surface.scene_layer_surface.configure(&full_box, &full_box);
// TEST: are these calls useless? // TEST: are these calls useless?
// const x = layer_surface.scene_layer_surface.tree.node.x; // const x = layer_surface.scene_layer_surface.tree.node.x;
// const y = layer_surface.scene_layer_surface.tree.node.y; // const y = layer_surface.scene_layer_surface.tree.node.y;
// layer_surface.scene_layer_surface.tree.node.setPosition(x, y); // layer_surface.scene_layer_surface.tree.node.setPosition(x, y);
}
} }
}
} }

View file

@ -23,75 +23,75 @@ new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNe
reposition: wl.Listener(void) = wl.Listener(void).init(handleReposition), reposition: wl.Listener(void) = wl.Listener(void).init(handleReposition),
pub fn init( pub fn init(
xdg_popup: *wlr.XdgPopup, xdg_popup: *wlr.XdgPopup,
parent: *wlr.SceneTree, parent: *wlr.SceneTree,
) *Popup { ) *Popup {
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
const self = try gpa.create(Popup); const self = try gpa.create(Popup);
errdefer gpa.destroy(self); errdefer gpa.destroy(self);
self.* = .{ self.* = .{
.id = @intFromPtr(xdg_popup), .id = @intFromPtr(xdg_popup),
.xdg_popup = xdg_popup, .xdg_popup = xdg_popup,
.tree = try parent.createSceneXdgSurface(xdg_popup.base), .tree = try parent.createSceneXdgSurface(xdg_popup.base),
}; };
xdg_popup.events.destroy.add(&self.destroy); xdg_popup.events.destroy.add(&self.destroy);
xdg_popup.base.surface.events.commit.add(&self.commit); xdg_popup.base.surface.events.commit.add(&self.commit);
xdg_popup.base.events.new_popup.add(&self.new_popup); xdg_popup.base.events.new_popup.add(&self.new_popup);
xdg_popup.events.reposition.add(&self.reposition); xdg_popup.events.reposition.add(&self.reposition);
return self; return self;
} }
fn handleDestroy(listener: *wl.Listener(void)) void { fn handleDestroy(listener: *wl.Listener(void)) void {
const popup: *Popup = @fieldParentPtr("destroy", listener); const popup: *Popup = @fieldParentPtr("destroy", listener);
popup.destroy.link.remove(); popup.destroy.link.remove();
popup.commit.link.remove(); popup.commit.link.remove();
popup.new_popup.link.remove(); popup.new_popup.link.remove();
popup.reposition.link.remove(); popup.reposition.link.remove();
gpa.destroy(popup); gpa.destroy(popup);
} }
fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const popup: *Popup = @fieldParentPtr("commit", listener); const popup: *Popup = @fieldParentPtr("commit", listener);
if (popup.xdg_popup.base.initial_commit) { if (popup.xdg_popup.base.initial_commit) {
handleReposition(&popup.reposition); handleReposition(&popup.reposition);
} }
} }
fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void { fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void {
const popup: *Popup = @fieldParentPtr("new_popup", listener); const popup: *Popup = @fieldParentPtr("new_popup", listener);
_ = Popup.init(xdg_popup, popup.tree); _ = Popup.init(xdg_popup, popup.tree);
} }
fn handleReposition(listener: *wl.Listener(void)) void { fn handleReposition(listener: *wl.Listener(void)) void {
const popup: *Popup = @fieldParentPtr("reposition", listener); const popup: *Popup = @fieldParentPtr("reposition", listener);
var box: wlr.Box = undefined; var box: wlr.Box = undefined;
// TODO: figure this out to prevent popups from rendering outside of the // TODO: figure this out to prevent popups from rendering outside of the
// current monitor // current monitor
// //
// if (SceneNodeData.getFromNode(&popup.tree.node)) |node| { // if (SceneNodeData.getFromNode(&popup.tree.node)) |node| {
// const output = switch (node.data) { // const output = switch (node.data) {
// .view => |view| view.output orelse return, // .view => |view| view.output orelse return,
// .layer_surface => |layer_surface| layer_surface.output, // .layer_surface => |layer_surface| layer_surface.output,
// }; // };
// //
// server.root.output_layout.getBox(output.wlr_output, &box); // server.root.output_layout.getBox(output.wlr_output, &box);
// } // }
var root_lx: c_int = undefined; var root_lx: c_int = undefined;
var root_ly: c_int = undefined; var root_ly: c_int = undefined;
_ = popup.tree.node.coords(&root_lx, &root_ly); _ = popup.tree.node.coords(&root_lx, &root_ly);
box.x -= root_lx; box.x -= root_lx;
box.y -= root_ly; box.y -= root_ly;
popup.xdg_popup.unconstrainFromBox(&box); popup.xdg_popup.unconstrainFromBox(&box);
} }

View file

@ -17,90 +17,92 @@ remote_lua_v1: *mez.RemoteLuaV1,
L: *zlua.Lua, L: *zlua.Lua,
pub fn sendNewLogEntry(str: [*:0]const u8) void { pub fn sendNewLogEntry(str: [*:0]const u8) void {
var node = server.remote_lua_clients.first; var node = server.remote_lua_clients.first;
while (node) |n| { while (node) |n| {
const data: ?*RemoteLua = @fieldParentPtr("node", n); const data: ?*RemoteLua = @fieldParentPtr("node", n);
if (data) |d| d.remote_lua_v1.sendNewLogEntry(str); if (data) |d| d.remote_lua_v1.sendNewLogEntry(str);
node = n.next; node = n.next;
} }
} }
pub fn create(client: *wl.Client, version: u32, id: u32) !void { pub fn create(client: *wl.Client, version: u32, id: u32) !void {
const remote_lua_v1 = try mez.RemoteLuaV1.create(client, version, id); const remote_lua_v1 = try mez.RemoteLuaV1.create(client, version, id);
const node = try gpa.create(RemoteLua); const node = try gpa.create(RemoteLua);
errdefer gpa.destroy(node); errdefer gpa.destroy(node);
node.* = .{ node.* = .{
.remote_lua_v1 = remote_lua_v1, .remote_lua_v1 = remote_lua_v1,
.node = .{}, .node = .{},
.L = try zlua.Lua.init(gpa), .L = try zlua.Lua.init(gpa),
}; };
errdefer node.L.deinit(); errdefer node.L.deinit();
node.L.openLibs(); node.L.openLibs();
Lua.openLibs(node.L); Lua.openLibs(node.L);
Lua.loadRuntimeDir(node.L) catch |err| if (err == error.LuaRuntime) { Lua.loadRuntimeDir(node.L) catch |err| if (err == error.LuaRuntime) {
std.log.warn("{s}", .{try node.L.toString(-1)}); std.log.warn("{s}", .{try node.L.toString(-1)});
}; };
// TODO: replace stdout and stderr with buffers we can send to the clients // TODO: replace stdout and stderr with buffers we can send to the clients
server.remote_lua_clients.prepend(&node.node); server.remote_lua_clients.prepend(&node.node);
remote_lua_v1.setHandler(*RemoteLua, handleRequest, handleDestroy, node); remote_lua_v1.setHandler(*RemoteLua, handleRequest, handleDestroy, node);
} }
fn handleRequest( fn handleRequest(
remote_lua_v1: *mez.RemoteLuaV1, remote_lua_v1: *mez.RemoteLuaV1,
request: mez.RemoteLuaV1.Request, request: mez.RemoteLuaV1.Request,
remote: *RemoteLua, remote: *RemoteLua,
) void { ) void {
const L = remote.L; const L = remote.L;
switch (request) { switch (request) {
.destroy => remote_lua_v1.destroy(), .destroy => remote_lua_v1.destroy(),
.push_lua => |req| { .push_lua => |req| {
const chunk: [:0]const u8 = std.mem.sliceTo(req.lua_chunk, 0); const chunk: [:0]const u8 = std.mem.sliceTo(req.lua_chunk, 0);
const str = std.mem.concatWithSentinel(gpa, u8, &[_][]const u8{ const str = std.mem.concatWithSentinel(gpa, u8, &[_][]const u8{
"return ", "return ",
chunk, chunk,
";", ";",
}, 0) catch return catchLuaFail(remote); }, 0) catch return catchLuaFail(remote);
defer gpa.free(str); defer gpa.free(str);
zlua.Lua.loadBuffer(L, str, "=repl") catch { zlua.Lua.loadBuffer(L, str, "=repl") catch {
L.pop(L.getTop()); L.pop(L.getTop());
L.loadString(chunk) catch { L.loadString(chunk) catch {
catchLuaFail(remote); catchLuaFail(remote);
L.pop(-1); L.pop(-1);
}; };
return; return;
}; };
L.protectedCall(.{ .results = zlua.mult_return, }) catch { L.protectedCall(.{
catchLuaFail(remote); .results = zlua.mult_return,
L.pop(1); }) catch {
}; catchLuaFail(remote);
L.pop(1);
};
var i: i32 = 1; var i: i32 = 1;
const nresults = L.getTop(); const nresults = L.getTop();
while (i <= nresults) : (i += 1) { while (i <= nresults) : (i += 1) {
sendNewLogEntry(LuaUtils.toStringEx(L)); sendNewLogEntry(LuaUtils.toStringEx(L));
L.pop(-1); L.pop(-1);
} }
}, },
} }
} }
fn handleDestroy(_: *mez.RemoteLuaV1, remote_lua: *RemoteLua) void { fn handleDestroy(_: *mez.RemoteLuaV1, remote_lua: *RemoteLua) void {
if (remote_lua.node.prev) |p| { if (remote_lua.node.prev) |p| {
if (remote_lua.node.next) |n| n.prev.? = p; if (remote_lua.node.next) |n| n.prev.? = p;
p.next = remote_lua.node.next; p.next = remote_lua.node.next;
} else server.remote_lua_clients.first = remote_lua.node.next; } else server.remote_lua_clients.first = remote_lua.node.next;
remote_lua.L.deinit(); remote_lua.L.deinit();
gpa.destroy(remote_lua); gpa.destroy(remote_lua);
} }
fn catchLuaFail(remote: *RemoteLua) void { fn catchLuaFail(remote: *RemoteLua) void {
const err: [:0]const u8 = LuaUtils.toStringEx(remote.L); const err: [:0]const u8 = LuaUtils.toStringEx(remote.L);
sendNewLogEntry(std.mem.sliceTo(err, 0)); sendNewLogEntry(std.mem.sliceTo(err, 0));
} }

View file

@ -13,37 +13,37 @@ const server = &@import("main.zig").server;
global: *wl.Global, global: *wl.Global,
pub fn init() !?*RemoteLuaManager { pub fn init() !?*RemoteLuaManager {
const self = try gpa.create(RemoteLuaManager); const self = try gpa.create(RemoteLuaManager);
self.global = try wl.Global.create(server.wl_server, mez.RemoteLuaManagerV1, 1, ?*anyopaque, null, bind); self.global = try wl.Global.create(server.wl_server, mez.RemoteLuaManagerV1, 1, ?*anyopaque, null, bind);
return self; return self;
} }
fn bind(client: *wl.Client, _: ?*anyopaque, version: u32, id: u32) void { fn bind(client: *wl.Client, _: ?*anyopaque, version: u32, id: u32) void {
const remote_lua_manager_v1 = mez.RemoteLuaManagerV1.create(client, version, id) catch { const remote_lua_manager_v1 = mez.RemoteLuaManagerV1.create(client, version, id) catch {
client.postNoMemory(); client.postNoMemory();
Utils.oomPanic(); Utils.oomPanic();
}; };
remote_lua_manager_v1.setHandler(?*anyopaque, handleRequest, null, null); remote_lua_manager_v1.setHandler(?*anyopaque, handleRequest, null, null);
} }
fn handleRequest( fn handleRequest(
remote_lua_manager_v1: *mez.RemoteLuaManagerV1, remote_lua_manager_v1: *mez.RemoteLuaManagerV1,
request: mez.RemoteLuaManagerV1.Request, request: mez.RemoteLuaManagerV1.Request,
_: ?*anyopaque, _: ?*anyopaque,
) void { ) void {
switch (request) { switch (request) {
.destroy => remote_lua_manager_v1.destroy(), .destroy => remote_lua_manager_v1.destroy(),
.get_remote => |req| { .get_remote => |req| {
RemoteLua.create( RemoteLua.create(
remote_lua_manager_v1.getClient(), remote_lua_manager_v1.getClient(),
remote_lua_manager_v1.getVersion(), remote_lua_manager_v1.getVersion(),
req.id, req.id,
) catch { ) catch {
remote_lua_manager_v1.getClient().postNoMemory(); remote_lua_manager_v1.getClient().postNoMemory();
Utils.oomPanic(); Utils.oomPanic();
}; };
}, },
} }
} }

View file

@ -1,5 +1,4 @@
/// The root of Mezzaluna is, you guessed it, the root of many of the systems mez needs: /// The root of Mezzaluna is, you guessed it, the root of many of the systems mez needs:
const Root = @This(); const Root = @This();
const std = @import("std"); const std = @import("std");
@ -26,92 +25,92 @@ scene_output_layout: *wlr.SceneOutputLayout,
output_layout: *wlr.OutputLayout, output_layout: *wlr.OutputLayout,
pub fn init(self: *Root) void { pub fn init(self: *Root) void {
std.log.info("Creating root of mezzaluna\n", .{}); std.log.info("Creating root of mezzaluna\n", .{});
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
const output_layout = try wlr.OutputLayout.create(server.wl_server); const output_layout = try wlr.OutputLayout.create(server.wl_server);
errdefer output_layout.destroy(); errdefer output_layout.destroy();
const scene = try wlr.Scene.create(); const scene = try wlr.Scene.create();
errdefer scene.tree.node.destroy(); errdefer scene.tree.node.destroy();
self.* = .{ self.* = .{
.scene = scene, .scene = scene,
.scene_node_data = .{ .root = self }, .scene_node_data = .{ .root = self },
.output_layout = output_layout, .output_layout = output_layout,
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server), .xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server),
.scene_output_layout = try scene.attachOutputLayout(output_layout), .scene_output_layout = try scene.attachOutputLayout(output_layout),
}; };
self.scene.tree.node.data = &self.scene_node_data; self.scene.tree.node.data = &self.scene_node_data;
} }
pub fn deinit(self: *Root) void { pub fn deinit(self: *Root) void {
var it = self.scene.tree.children.iterator(.forward); var it = self.scene.tree.children.iterator(.forward);
while(it.next()) |node| { while (it.next()) |node| {
if(node.data == null) continue; if (node.data == null) continue;
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
switch(scene_node_data.*) { switch (scene_node_data.*) {
.output => { .output => {
scene_node_data.output.deinit(); scene_node_data.output.deinit();
}, },
else => { else => {
std.log.debug("The root has a child that is not an output", .{}); std.log.debug("The root has a child that is not an output", .{});
unreachable; unreachable;
} },
}
} }
}
self.output_layout.destroy(); self.output_layout.destroy();
self.scene.tree.node.destroy(); self.scene.tree.node.destroy();
} }
// Search output_layout's ouputs, and each outputs views // Search output_layout's ouputs, and each outputs views
pub fn viewById(self: *Root, id: u64) ?*View { pub fn viewById(self: *Root, id: u64) ?*View {
var output_it = self.output_layout.outputs.iterator(.forward); var output_it = self.output_layout.outputs.iterator(.forward);
while(output_it.next()) |o| { while (output_it.next()) |o| {
if(o.output.data == null) { if (o.output.data == null) {
std.log.err("Wlr_output arbitrary data not assigned", .{}); std.log.err("Wlr_output arbitrary data not assigned", .{});
unreachable; unreachable;
}
const output: *Output = @ptrCast(@alignCast(o.output.data.?));
var node_it = output.layers.content.children.iterator(.forward);
while (node_it.next()) |node| {
if (node.data == null) continue;
const view_snd: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
// TODO: Should we assert that we want only views to be here
// -- Basically should we use switch statements for snd interactions
// -- Or if statements, for simplicity
if (view_snd.* == .view and view_snd.view.id == id) {
return view_snd.view;
}
}
if (output.fullscreen) |fullscreen| {
if (fullscreen.id == id) return fullscreen;
}
} }
const output: *Output = @ptrCast(@alignCast(o.output.data.?)); return null;
var node_it = output.layers.content.children.iterator(.forward);
while(node_it.next()) |node| {
if(node.data == null) continue;
const view_snd: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
// TODO: Should we assert that we want only views to be here
// -- Basically should we use switch statements for snd interactions
// -- Or if statements, for simplicity
if(view_snd.* == .view and view_snd.view.id == id) {
return view_snd.view;
}
}
if(output.fullscreen) |fullscreen| {
if(fullscreen.id == id) return fullscreen;
}
}
return null;
} }
pub fn outputById(self: *Root, id: u64) ?*Output { pub fn outputById(self: *Root, id: u64) ?*Output {
var it = self.scene.outputs.iterator(.forward); var it = self.scene.outputs.iterator(.forward);
while(it.next()) |scene_output| { while (it.next()) |scene_output| {
if(scene_output.output.data == null) continue; if (scene_output.output.data == null) continue;
const output: *Output = @as(*Output, @ptrCast(@alignCast(scene_output.output.data.?))); const output: *Output = @as(*Output, @ptrCast(@alignCast(scene_output.output.data.?)));
if(output.id == id) return output; if (output.id == id) return output;
} }
return null; return null;
} }

View file

@ -5,10 +5,4 @@ const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig"); const Output = @import("Output.zig");
const Root = @import("Root.zig"); const Root = @import("Root.zig");
pub const SceneNodeData = union(enum) { pub const SceneNodeData = union(enum) { view: *View, layer_surface: *LayerSurface, output: *Output, output_layer: *wlr.SceneTree, root: *Root };
view: *View,
layer_surface: *LayerSurface,
output: *Output,
output_layer: *wlr.SceneTree,
root: *Root
};

View file

@ -6,24 +6,24 @@ const wl = @import("wayland").server.wl;
const xkb = @import("xkbcommon"); const xkb = @import("xkbcommon");
const KeyboardGroup = @import("KeyboardGroup.zig"); const KeyboardGroup = @import("KeyboardGroup.zig");
const Utils = @import("Utils.zig"); const Utils = @import("Utils.zig");
const Popup = @import("Popup.zig"); const Popup = @import("Popup.zig");
const View = @import("View.zig"); const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig"); const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig"); const Output = @import("Output.zig");
const server = &@import("main.zig").server; const server = &@import("main.zig").server;
pub const FocusData = union(enum) { pub const FocusData = union(enum) {
view: *View, view: *View,
layer_surface: *LayerSurface, layer_surface: *LayerSurface,
pub fn getSurface(self: FocusData) *wlr.Surface { pub fn getSurface(self: FocusData) *wlr.Surface {
return switch (self) { return switch (self) {
.view => |*v| v.*.xdg_toplevel.base.surface, .view => |*v| v.*.xdg_toplevel.base.surface,
.layer_surface => |*ls| ls.*.wlr_layer_surface.surface, .layer_surface => |*ls| ls.*.wlr_layer_surface.surface,
}; };
} }
}; };
wlr_seat: *wlr.Seat, wlr_seat: *wlr.Seat,
@ -40,144 +40,139 @@ request_set_primary_selection: wl.Listener(*wlr.Seat.event.RequestSetPrimarySele
// request_start_drage // request_start_drage
pub fn init(self: *Seat) void { pub fn init(self: *Seat) void {
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
const xkb_context = xkb.Context.new(.no_flags) orelse { const xkb_context = xkb.Context.new(.no_flags) orelse {
std.log.err("Unable to create a xkb context, exiting", .{}); std.log.err("Unable to create a xkb context, exiting", .{});
std.process.exit(7); std.process.exit(7);
}; };
defer xkb_context.unref(); defer xkb_context.unref();
const keymap = xkb.Keymap.newFromNames(xkb_context, null, .no_flags) orelse { const keymap = xkb.Keymap.newFromNames(xkb_context, null, .no_flags) orelse {
std.log.err("Unable to create a xkb keymap, exiting", .{}); std.log.err("Unable to create a xkb keymap, exiting", .{});
std.process.exit(8); std.process.exit(8);
}; };
defer keymap.unref(); defer keymap.unref();
self.* = .{ self.* = .{
.wlr_seat = try wlr.Seat.create(server.wl_server, "default"), .wlr_seat = try wlr.Seat.create(server.wl_server, "default"),
.focused_surface = null, .focused_surface = null,
.focused_output = null, .focused_output = null,
.keyboard_group = .init(), .keyboard_group = .init(),
.keymap = keymap.ref(), .keymap = keymap.ref(),
}; };
errdefer { errdefer {
self.keyboard_group.deinit(); self.keyboard_group.deinit();
self.wlr_seat.destroy(); self.wlr_seat.destroy();
} }
_ = self.keyboard_group.wlr_group.keyboard.setKeymap(self.keymap); _ = self.keyboard_group.wlr_group.keyboard.setKeymap(self.keymap);
self.wlr_seat.setKeyboard(&self.keyboard_group.wlr_group.keyboard); self.wlr_seat.setKeyboard(&self.keyboard_group.wlr_group.keyboard);
self.wlr_seat.events.request_set_cursor.add(&self.request_set_cursor); self.wlr_seat.events.request_set_cursor.add(&self.request_set_cursor);
self.wlr_seat.events.request_set_selection.add(&self.request_set_selection); self.wlr_seat.events.request_set_selection.add(&self.request_set_selection);
self.wlr_seat.events.request_set_primary_selection.add(&self.request_set_primary_selection); self.wlr_seat.events.request_set_primary_selection.add(&self.request_set_primary_selection);
} }
pub fn deinit(self: *Seat) void { pub fn deinit(self: *Seat) void {
self.request_set_cursor.link.remove(); self.request_set_cursor.link.remove();
self.request_set_selection.link.remove(); self.request_set_selection.link.remove();
self.request_set_primary_selection.link.remove(); self.request_set_primary_selection.link.remove();
self.keyboard_group.deinit(); self.keyboard_group.deinit();
self.wlr_seat.destroy(); self.wlr_seat.destroy();
} }
pub fn focusSurface(self: *Seat, to_focus: ?FocusData) void { pub fn focusSurface(self: *Seat, to_focus: ?FocusData) void {
const surface: ?*wlr.Surface = blk: { const surface: ?*wlr.Surface = blk: {
if (to_focus != null) { if (to_focus != null) {
break :blk to_focus.?.getSurface(); break :blk to_focus.?.getSurface();
} else { } else {
break :blk null; break :blk null;
} }
}; };
// Remove focus from the current surface unless: // Remove focus from the current surface unless:
// - current and to focus are the same surface // - current and to focus are the same surface
// - current layer has exclusive keyboard interactivity // - current layer has exclusive keyboard interactivity
// - current is fullscreen and to focus is content // - current is fullscreen and to focus is content
// - current is fullscreen and layer is bottom or background // - current is fullscreen and layer is bottom or background
if (self.focused_surface) |current_focus| { if (self.focused_surface) |current_focus| {
const current_surface = current_focus.getSurface(); const current_surface = current_focus.getSurface();
if (current_surface == surface) return; // Same surface if (current_surface == surface) return; // Same surface
if(to_focus != null) { if (to_focus != null) {
switch (current_focus) { switch (current_focus) {
.layer_surface => |*current_layer_surface| { .layer_surface => |*current_layer_surface| {
if(current_layer_surface.*.wlr_layer_surface.current.keyboard_interactive == .exclusive) return; if (current_layer_surface.*.wlr_layer_surface.current.keyboard_interactive == .exclusive) return;
}, },
.view => |*current_view| { .view => |*current_view| {
if(current_view.*.fullscreen) { if (current_view.*.fullscreen) {
switch (to_focus.?) { switch (to_focus.?) {
.layer_surface => |*layer_surface| { .layer_surface => |*layer_surface| {
const layer = layer_surface.*.wlr_layer_surface.current.layer; const layer = layer_surface.*.wlr_layer_surface.current.layer;
if(layer == .background or layer == .bottom) return; if (layer == .background or layer == .bottom) return;
}, },
.view => |*view| { .view => |*view| {
if(!view.*.fullscreen) return; if (!view.*.fullscreen) return;
} },
}
}
},
} }
} } else if (current_focus == .view and current_focus.view.fullscreen) {
return;
} }
}
} else if(current_focus == .view and current_focus.view.fullscreen){
return;
}
// Clear the focus if applicable // Clear the focus if applicable
switch (current_focus) { switch (current_focus) {
.layer_surface => {}, // IDK if we actually have to clear any focus here .layer_surface => {}, // IDK if we actually have to clear any focus here
else => { else => {
if(wlr.XdgSurface.tryFromWlrSurface(current_surface)) |xdg_surface| { if (wlr.XdgSurface.tryFromWlrSurface(current_surface)) |xdg_surface| {
_ = xdg_surface.role_data.toplevel.?.setActivated(false); _ = xdg_surface.role_data.toplevel.?.setActivated(false);
}
},
} }
}
} }
}
if (to_focus != null) {
if(to_focus != null) { server.seat.wlr_seat.keyboardNotifyEnter(surface.?, &server.seat.keyboard_group.wlr_group.keyboard.keycodes, &server.seat.keyboard_group.wlr_group.keyboard.modifiers);
server.seat.wlr_seat.keyboardNotifyEnter( if (to_focus.? != .layer_surface) {
surface.?, if (to_focus.? == .view) to_focus.?.view.focused = true;
&server.seat.keyboard_group.wlr_group.keyboard.keycodes, if (wlr.XdgSurface.tryFromWlrSurface(surface.?)) |xdg_surface| {
&server.seat.keyboard_group.wlr_group.keyboard.modifiers _ = xdg_surface.role_data.toplevel.?.setActivated(true);
); }
if(to_focus.? != .layer_surface) { }
if(to_focus.? == .view) to_focus.?.view.focused = true;
if(wlr.XdgSurface.tryFromWlrSurface(surface.?)) |xdg_surface| {
_ = xdg_surface.role_data.toplevel.?.setActivated(true);
}
} }
} self.focused_surface = to_focus;
self.focused_surface = to_focus;
} }
pub fn focusOutput(self: *Seat, output: *Output) void { pub fn focusOutput(self: *Seat, output: *Output) void {
if(server.seat.focused_output) |prev_output| { if (server.seat.focused_output) |prev_output| {
prev_output.focused = false; prev_output.focused = false;
} }
self.focused_output = output; self.focused_output = output;
} }
fn handleRequestSetCursor( fn handleRequestSetCursor(
_: *wl.Listener(*wlr.Seat.event.RequestSetCursor), _: *wl.Listener(*wlr.Seat.event.RequestSetCursor),
event: *wlr.Seat.event.RequestSetCursor, event: *wlr.Seat.event.RequestSetCursor,
) void { ) void {
if (event.seat_client == server.seat.wlr_seat.pointer_state.focused_client) if (event.seat_client == server.seat.wlr_seat.pointer_state.focused_client)
server.cursor.wlr_cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y); server.cursor.wlr_cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y);
} }
fn handleRequestSetSelection( fn handleRequestSetSelection(
_: *wl.Listener(*wlr.Seat.event.RequestSetSelection), _: *wl.Listener(*wlr.Seat.event.RequestSetSelection),
event: *wlr.Seat.event.RequestSetSelection, event: *wlr.Seat.event.RequestSetSelection,
) void { ) void {
server.seat.wlr_seat.setSelection(event.source, event.serial); server.seat.wlr_seat.setSelection(event.source, event.serial);
} }
fn handleRequestSetPrimarySelection( fn handleRequestSetPrimarySelection(
_: *wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection), _: *wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection),
event: *wlr.Seat.event.RequestSetPrimarySelection, event: *wlr.Seat.event.RequestSetPrimarySelection,
) void { ) void {
server.seat.wlr_seat.setPrimarySelection(event.source, event.serial); server.seat.wlr_seat.setPrimarySelection(event.source, event.serial);
} }

View file

@ -4,22 +4,22 @@ const std = @import("std");
const wl = @import("wayland").server.wl; const wl = @import("wayland").server.wl;
const wlr = @import("wlroots"); const wlr = @import("wlroots");
const Root = @import("Root.zig"); const Root = @import("Root.zig");
const Seat = @import("Seat.zig"); const Seat = @import("Seat.zig");
const Cursor = @import("Cursor.zig"); const Cursor = @import("Cursor.zig");
const Keyboard = @import("Keyboard.zig"); const Keyboard = @import("Keyboard.zig");
const LayerSurface = @import("LayerSurface.zig"); const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig"); const Output = @import("Output.zig");
const View = @import("View.zig"); const View = @import("View.zig");
const Keymap = @import("types/Keymap.zig"); const Keymap = @import("types/Keymap.zig");
const Mousemap = @import("types/Mousemap.zig"); const Mousemap = @import("types/Mousemap.zig");
const Hook = @import("types/Hook.zig"); const Hook = @import("types/Hook.zig");
const Events = @import("types/Events.zig"); const Events = @import("types/Events.zig");
const Popup = @import("Popup.zig"); const Popup = @import("Popup.zig");
const RemoteLua = @import("RemoteLua.zig"); const RemoteLua = @import("RemoteLua.zig");
const RemoteLuaManager = @import("RemoteLuaManager.zig"); const RemoteLuaManager = @import("RemoteLuaManager.zig");
const Utils = @import("Utils.zig"); const Utils = @import("Utils.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData; const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
const gpa = std.heap.c_allocator; const gpa = std.heap.c_allocator;
const server = &@import("main.zig").server; const server = &@import("main.zig").server;
@ -62,195 +62,177 @@ new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) = .init(handleNewLayerSurfac
request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) = .init(handleRequestActivate), request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) = .init(handleRequestActivate),
pub fn init(self: *Server) void { pub fn init(self: *Server) void {
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
const wl_server = wl.Server.create() catch { const wl_server = wl.Server.create() catch {
std.log.err("Server create failed, exiting with 2", .{}); std.log.err("Server create failed, exiting with 2", .{});
std.process.exit(2); std.process.exit(2);
}; };
const event_loop = wl_server.getEventLoop(); const event_loop = wl_server.getEventLoop();
var session: ?*wlr.Session = undefined; var session: ?*wlr.Session = undefined;
const backend = wlr.Backend.autocreate(event_loop, &session) catch { const backend = wlr.Backend.autocreate(event_loop, &session) catch {
std.log.err("Backend create failed, exiting with 3", .{}); std.log.err("Backend create failed, exiting with 3", .{});
std.process.exit(3); std.process.exit(3);
}; };
const renderer = wlr.Renderer.autocreate(backend) catch { const renderer = wlr.Renderer.autocreate(backend) catch {
std.log.err("Renderer create failed, exiting with 4", .{}); std.log.err("Renderer create failed, exiting with 4", .{});
std.process.exit(4); std.process.exit(4);
}; };
self.* = .{ self.* = .{
.wl_server = wl_server, .wl_server = wl_server,
.backend = backend, .backend = backend,
.renderer = renderer, .renderer = renderer,
.allocator = wlr.Allocator.autocreate(backend, renderer) catch { .allocator = wlr.Allocator.autocreate(backend, renderer) catch {
std.log.err("Allocator create failed, exiting with 5", .{}); std.log.err("Allocator create failed, exiting with 5", .{});
std.process.exit(5); std.process.exit(5);
}, },
.xdg_shell = try wlr.XdgShell.create(wl_server, 2), .xdg_shell = try wlr.XdgShell.create(wl_server, 2),
.layer_shell = try wlr.LayerShellV1.create(wl_server, 4), .layer_shell = try wlr.LayerShellV1.create(wl_server, 4),
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(self.wl_server), .xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(self.wl_server),
.xdg_activation = try wlr.XdgActivationV1.create(self.wl_server), .xdg_activation = try wlr.XdgActivationV1.create(self.wl_server),
.event_loop = event_loop, .event_loop = event_loop,
.session = session, .session = session,
.compositor = try wlr.Compositor.create(wl_server, 6, renderer), .compositor = try wlr.Compositor.create(wl_server, 6, renderer),
.shm = try wlr.Shm.createWithRenderer(wl_server, 1, renderer), .shm = try wlr.Shm.createWithRenderer(wl_server, 1, renderer),
// TODO: let the user configure a cursor theme and side lua // TODO: let the user configure a cursor theme and side lua
.root = undefined, .root = undefined,
.seat = undefined, .seat = undefined,
.cursor = undefined, .cursor = undefined,
.remote_lua_manager = RemoteLuaManager.init() catch Utils.oomPanic(), .remote_lua_manager = RemoteLuaManager.init() catch Utils.oomPanic(),
.keymaps = .init(gpa), .keymaps = .init(gpa),
.mousemaps = .init(gpa), .mousemaps = .init(gpa),
.hooks = .init(gpa), .hooks = .init(gpa),
.events = try .init(gpa), .events = try .init(gpa),
.remote_lua_clients = .{}, .remote_lua_clients = .{},
}; };
self.renderer.initServer(wl_server) catch { self.renderer.initServer(wl_server) catch {
std.log.err("Renderer init failed, exiting with 6", .{}); std.log.err("Renderer init failed, exiting with 6", .{});
std.process.exit(6); std.process.exit(6);
}; };
self.root.init(); self.root.init();
self.seat.init(); self.seat.init();
self.cursor.init(); self.cursor.init();
_ = try wlr.Subcompositor.create(self.wl_server); _ = try wlr.Subcompositor.create(self.wl_server);
_ = try wlr.DataDeviceManager.create(self.wl_server); _ = try wlr.DataDeviceManager.create(self.wl_server);
_ = try wlr.ExportDmabufManagerV1.create(self.wl_server); _ = try wlr.ExportDmabufManagerV1.create(self.wl_server);
_ = try wlr.Viewporter.create(self.wl_server); _ = try wlr.Viewporter.create(self.wl_server);
_ = try wlr.Presentation.create(self.wl_server, self.backend, 2); _ = try wlr.Presentation.create(self.wl_server, self.backend, 2);
_ = try wlr.ScreencopyManagerV1.create(self.wl_server); _ = try wlr.ScreencopyManagerV1.create(self.wl_server);
_ = try wlr.AlphaModifierV1.create(self.wl_server); _ = try wlr.AlphaModifierV1.create(self.wl_server);
_ = try wlr.DataControlManagerV1.create(self.wl_server); _ = try wlr.DataControlManagerV1.create(self.wl_server);
_ = try wlr.PrimarySelectionDeviceManagerV1.create(self.wl_server); _ = try wlr.PrimarySelectionDeviceManagerV1.create(self.wl_server);
_ = try wlr.SinglePixelBufferManagerV1.create(self.wl_server); _ = try wlr.SinglePixelBufferManagerV1.create(self.wl_server);
_ = try wlr.FractionalScaleManagerV1.create(self.wl_server, 1); _ = try wlr.FractionalScaleManagerV1.create(self.wl_server, 1);
_ = try wlr.XdgOutputManagerV1.create(self.wl_server, self.root.output_layout); _ = try wlr.XdgOutputManagerV1.create(self.wl_server, self.root.output_layout);
self.root.scene.setGammaControlManagerV1(try wlr.GammaControlManagerV1.create(self.wl_server)); self.root.scene.setGammaControlManagerV1(try wlr.GammaControlManagerV1.create(self.wl_server));
// Add event listeners to events // Add event listeners to events
self.backend.events.new_input.add(&self.new_input); self.backend.events.new_input.add(&self.new_input);
self.backend.events.new_output.add(&self.new_output); self.backend.events.new_output.add(&self.new_output);
self.xdg_shell.events.new_toplevel.add(&self.new_xdg_toplevel); self.xdg_shell.events.new_toplevel.add(&self.new_xdg_toplevel);
self.xdg_shell.events.new_popup.add(&self.new_xdg_popup); self.xdg_shell.events.new_popup.add(&self.new_xdg_popup);
self.xdg_toplevel_decoration_manager.events.new_toplevel_decoration.add(&self.new_xdg_toplevel_decoration); self.xdg_toplevel_decoration_manager.events.new_toplevel_decoration.add(&self.new_xdg_toplevel_decoration);
self.layer_shell.events.new_surface.add(&self.new_layer_surface); self.layer_shell.events.new_surface.add(&self.new_layer_surface);
self.xdg_activation.events.request_activate.add(&self.request_activate); self.xdg_activation.events.request_activate.add(&self.request_activate);
self.events.exec("ServerStartPost", .{}); self.events.exec("ServerStartPost", .{});
} }
pub fn deinit(self: *Server) noreturn { pub fn deinit(self: *Server) noreturn {
self.new_input.link.remove(); self.new_input.link.remove();
self.new_output.link.remove(); self.new_output.link.remove();
self.new_xdg_toplevel.link.remove(); self.new_xdg_toplevel.link.remove();
self.new_xdg_popup.link.remove(); self.new_xdg_popup.link.remove();
self.new_xdg_toplevel_decoration.link.remove(); self.new_xdg_toplevel_decoration.link.remove();
self.new_layer_surface.link.remove(); self.new_layer_surface.link.remove();
self.seat.deinit(); self.seat.deinit();
self.root.deinit(); self.root.deinit();
self.cursor.deinit(); self.cursor.deinit();
self.backend.destroy(); self.backend.destroy();
self.wl_server.destroyClients(); self.wl_server.destroyClients();
self.wl_server.destroy(); self.wl_server.destroy();
std.log.debug("Exiting mez succesfully", .{}); std.log.debug("Exiting mez succesfully", .{});
std.process.exit(0); std.process.exit(0);
} }
// --------- Backend event handlers --------- // --------- Backend event handlers ---------
fn handleNewInput( fn handleNewInput(_: *wl.Listener(*wlr.InputDevice), device: *wlr.InputDevice) void {
_: *wl.Listener(*wlr.InputDevice), switch (device.type) {
device: *wlr.InputDevice .keyboard => _ = Keyboard.init(device),
) void { .pointer => server.cursor.wlr_cursor.attachInputDevice(device),
switch (device.type) { else => {
.keyboard => _ = Keyboard.init(device), std.log.err("New input request for input that is not a keyboard or pointer: {s}", .{device.name orelse "(null)"});
.pointer => server.cursor.wlr_cursor.attachInputDevice(device), },
else => { }
std.log.err(
"New input request for input that is not a keyboard or pointer: {s}",
.{device.name orelse "(null)"}
);
},
}
// We should really only set true capabilities // We should really only set true capabilities
server.seat.wlr_seat.setCapabilities(.{ server.seat.wlr_seat.setCapabilities(.{
.pointer = true, .pointer = true,
.keyboard = true, .keyboard = true,
}); });
} }
fn handleNewOutput( fn handleNewOutput(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
_: *wl.Listener(*wlr.Output), _ = Output.init(wlr_output);
wlr_output: *wlr.Output
) void {
_ = Output.init(wlr_output);
} }
fn handleNewXdgToplevel( fn handleNewXdgToplevel(_: *wl.Listener(*wlr.XdgToplevel), xdg_toplevel: *wlr.XdgToplevel) void {
_: *wl.Listener(*wlr.XdgToplevel), _ = View.init(xdg_toplevel);
xdg_toplevel: *wlr.XdgToplevel
) void {
_ = View.init(xdg_toplevel);
} }
fn handleNewXdgToplevelDecoration( fn handleNewXdgToplevelDecoration(_: *wl.Listener(*wlr.XdgToplevelDecorationV1), decoration: *wlr.XdgToplevelDecorationV1) void {
_: *wl.Listener(*wlr.XdgToplevelDecorationV1), if (server.root.viewById(@intFromPtr(decoration.toplevel))) |view| {
decoration: *wlr.XdgToplevelDecorationV1 view.xdg_toplevel_decoration = decoration;
) void { }
if(server.root.viewById(@intFromPtr(decoration.toplevel))) |view| {
view.xdg_toplevel_decoration = decoration;
}
} }
fn handleNewXdgPopup(_: *wl.Listener(*wlr.XdgPopup), _: *wlr.XdgPopup) void { fn handleNewXdgPopup(_: *wl.Listener(*wlr.XdgPopup), _: *wlr.XdgPopup) void {
std.log.debug("Unimplemented Server.handleNewXdgPopup\n", .{}); std.log.debug("Unimplemented Server.handleNewXdgPopup\n", .{});
} }
fn handleNewLayerSurface( fn handleNewLayerSurface(_: *wl.Listener(*wlr.LayerSurfaceV1), layer_surface: *wlr.LayerSurfaceV1) void {
_: *wl.Listener(*wlr.LayerSurfaceV1), std.log.debug("requested layer shell\n", .{});
layer_surface: *wlr.LayerSurfaceV1 if (layer_surface.output == null) {
) void { if (server.seat.focused_output == null) {
std.log.debug("requested layer shell\n", .{}); std.log.err("No output available for new layer surface", .{});
if (layer_surface.output == null) { layer_surface.destroy();
if (server.seat.focused_output == null) { return;
std.log.err("No output available for new layer surface", .{}); }
layer_surface.destroy();
return; layer_surface.output = server.seat.focused_output.?.wlr_output;
} }
layer_surface.output = server.seat.focused_output.?.wlr_output; _ = LayerSurface.init(layer_surface);
}
_ = LayerSurface.init(layer_surface);
} }
fn handleRequestActivate( fn handleRequestActivate(
_: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate), _: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
event: *wlr.XdgActivationV1.event.RequestActivate, event: *wlr.XdgActivationV1.event.RequestActivate,
) void { ) void {
if(event.surface.data == null) return; if (event.surface.data == null) return;
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(event.surface.data.?)); const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(event.surface.data.?));
if(scene_node_data.* == .view) { if (scene_node_data.* == .view) {
if(server.seat.focused_output) |output| { if (server.seat.focused_output) |output| {
if(output.fullscreen) |fullscreen| { if (output.fullscreen) |fullscreen| {
server.seat.focusSurface(.{ .view = fullscreen }); server.seat.focusSurface(.{ .view = fullscreen });
return; return;
} }
}
server.seat.focusSurface(Seat.FocusData{ .view = scene_node_data.view });
} else {
std.log.warn("Ignoring request to activate non-view", .{});
} }
server.seat.focusSurface(Seat.FocusData{ .view = scene_node_data.view });
} else {
std.log.warn("Ignoring request to activate non-view", .{});
}
} }

View file

@ -3,6 +3,6 @@ const Utils = @This();
const std = @import("std"); const std = @import("std");
pub fn oomPanic() noreturn { pub fn oomPanic() noreturn {
std.log.err("Out of memory error, exiting with 1", .{}); std.log.err("Out of memory error, exiting with 1", .{});
std.process.exit(1); std.process.exit(1);
} }

View file

@ -56,287 +56,273 @@ set_title: wl.Listener(void) = .init(handleSetTitle),
// set_parent: wl.Listener(void) = .init(handleSetParent), // set_parent: wl.Listener(void) = .init(handleSetParent),
pub fn init(xdg_toplevel: *wlr.XdgToplevel) *View { pub fn init(xdg_toplevel: *wlr.XdgToplevel) *View {
errdefer Utils.oomPanic(); errdefer Utils.oomPanic();
const self = try gpa.create(View); const self = try gpa.create(View);
errdefer gpa.destroy(self); errdefer gpa.destroy(self);
self.* = .{ self.* = .{
.focused = false, .focused = false,
.mapped = false, .mapped = false,
.fullscreen = false, .fullscreen = false,
.id = @intFromPtr(xdg_toplevel), .id = @intFromPtr(xdg_toplevel),
.output = null, .output = null,
.geometry = .{ .width = 0, .height = 0, .x = 0, .y = 0 }, .geometry = .{ .width = 0, .height = 0, .x = 0, .y = 0 },
.xdg_toplevel = xdg_toplevel,
.scene_tree = undefined,
.surface_tree = undefined,
.xdg_toplevel_decoration = null,
.borders = undefined,
.border_width = 0,
.scene_node_data = .{ .view = self },
};
.xdg_toplevel = xdg_toplevel, // Add new Toplevel to root of the tree
.scene_tree = undefined, if (server.seat.focused_output) |output| {
.surface_tree = undefined, self.scene_tree = try output.layers.content.createSceneTree();
.xdg_toplevel_decoration = null, self.surface_tree = try self.scene_tree.createSceneXdgSurface(xdg_toplevel.base);
.borders = undefined, self.output = output;
.border_width = 0, }
.scene_node_data = .{ .view = self } self.scene_tree.node.data = &self.scene_node_data;
}; self.xdg_toplevel.base.data = &self.scene_node_data;
// Add new Toplevel to root of the tree self.xdg_toplevel.events.destroy.add(&self.destroy);
if(server.seat.focused_output) |output| { self.xdg_toplevel.base.surface.events.map.add(&self.map);
self.scene_tree = try output.layers.content.createSceneTree(); self.xdg_toplevel.base.surface.events.unmap.add(&self.unmap);
self.surface_tree = try self.scene_tree.createSceneXdgSurface(xdg_toplevel.base); self.xdg_toplevel.base.surface.events.commit.add(&self.commit);
self.output = output; self.xdg_toplevel.base.events.new_popup.add(&self.new_popup);
} self.xdg_toplevel.base.events.ack_configure.add(&self.ack_configure);
self.scene_tree.node.data = &self.scene_node_data; for (self.borders, 0..) |_, i| {
self.xdg_toplevel.base.data = &self.scene_node_data; const color: [4]f32 = .{ 0, 0, 0, 1 };
self.borders[i] = try wlr.SceneTree.createSceneRect(self.scene_tree, 0, 0, &color);
self.borders[i].node.data = self;
}
self.xdg_toplevel.events.destroy.add(&self.destroy); return self;
self.xdg_toplevel.base.surface.events.map.add(&self.map);
self.xdg_toplevel.base.surface.events.unmap.add(&self.unmap);
self.xdg_toplevel.base.surface.events.commit.add(&self.commit);
self.xdg_toplevel.base.events.new_popup.add(&self.new_popup);
self.xdg_toplevel.base.events.ack_configure.add(&self.ack_configure);
for (self.borders, 0..) |_, i| {
const color: [4]f32 = .{ 0, 0, 0, 1 };
self.borders[i] = try wlr.SceneTree.createSceneRect(self.scene_tree, 0, 0, &color);
self.borders[i].node.data = self;
}
return self;
} }
// Tell the client to close // Tell the client to close
// It better behave! // It better behave!
pub fn close(self: *View) void { pub fn close(self: *View) void {
self.xdg_toplevel.sendClose(); self.xdg_toplevel.sendClose();
} }
pub fn setBorderColor(self: *View, color: *const [4]f32) void { pub fn setBorderColor(self: *View, color: *const [4]f32) void {
for (self.borders) |border| border.setColor(color); for (self.borders) |border| border.setColor(color);
} }
pub fn toggleFullscreen(self: *View) void { pub fn toggleFullscreen(self: *View) void {
self.fullscreen = !self.fullscreen; self.fullscreen = !self.fullscreen;
if(self.output) |output| { if (self.output) |output| {
if(self.fullscreen and output.fullscreen != self) { if (self.fullscreen and output.fullscreen != self) {
// Check to see if another fullscreened view exists, if so replace it // Check to see if another fullscreened view exists, if so replace it
if(output.getFullscreenedView()) |view| { if (output.getFullscreenedView()) |view| {
view.toggleFullscreen(); view.toggleFullscreen();
} }
self.scene_tree.node.reparent(output.layers.fullscreen); self.scene_tree.node.reparent(output.layers.fullscreen);
self.setPosition(0, 0); self.setPosition(0, 0);
self.setSize(output.wlr_output.width, output.wlr_output.height); self.setSize(output.wlr_output.width, output.wlr_output.height);
output.fullscreen = self; output.fullscreen = self;
} else { } else {
self.scene_tree.node.reparent(output.layers.content); self.scene_tree.node.reparent(output.layers.content);
output.fullscreen = null; output.fullscreen = null;
}
} }
} _ = self.xdg_toplevel.setFullscreen(self.fullscreen);
_ = self.xdg_toplevel.setFullscreen(self.fullscreen);
} }
pub fn setPosition(self: *View, x: i32, y: i32) void { pub fn setPosition(self: *View, x: i32, y: i32) void {
if (self.output == null or !self.xdg_toplevel.base.surface.mapped) return; if (self.output == null or !self.xdg_toplevel.base.surface.mapped) return;
self.geometry.x = x; self.geometry.x = x;
self.geometry.y = y; self.geometry.y = y;
self.scene_tree.node.setPosition(self.geometry.x, self.geometry.y); self.scene_tree.node.setPosition(self.geometry.x, self.geometry.y);
self.resizeBorders(); self.resizeBorders();
} }
pub fn setSize(self: *View, width: i32, height: i32) void { pub fn setSize(self: *View, width: i32, height: i32) void {
if (self.output == null or !self.xdg_toplevel.base.surface.mapped) return; if (self.output == null or !self.xdg_toplevel.base.surface.mapped) return;
// at the very least the client must be big enough to have borders // at the very least the client must be big enough to have borders
self.geometry.width = @max(1 + 2 * self.border_width, width); self.geometry.width = @max(1 + 2 * self.border_width, width);
self.geometry.height = @max(1 + 2 * self.border_width, height); self.geometry.height = @max(1 + 2 * self.border_width, height);
// This returns a configure serial for verifying the configure // This returns a configure serial for verifying the configure
_ = self.xdg_toplevel.setSize( _ = self.xdg_toplevel.setSize(
self.geometry.width - 2 * self.border_width, self.geometry.width - 2 * self.border_width,
self.geometry.height - 2 * self.border_width, self.geometry.height - 2 * self.border_width,
); );
// clip the surface tree to the size of the view // clip the surface tree to the size of the view
self.surface_tree.node.subsurfaceTreeSetClip(&wlr.Box{ self.surface_tree.node.subsurfaceTreeSetClip(&wlr.Box{
.x = 0, .x = 0,
.y = 0, .y = 0,
.width = self.geometry.width, .width = self.geometry.width,
.height = self.geometry.height, .height = self.geometry.height,
}); });
self.resizeBorders(); self.resizeBorders();
} }
/// this function handles all things related to sizing and positioning and /// this function handles all things related to sizing and positioning and
/// should be called after something in the size or position is changed /// should be called after something in the size or position is changed
pub fn resizeBorders(self: *View) void { pub fn resizeBorders(self: *View) void {
// set the position of the surface to not clip with the borders // set the position of the surface to not clip with the borders
self.surface_tree.node.setPosition(self.border_width, self.border_width); self.surface_tree.node.setPosition(self.border_width, self.border_width);
self.borders[0].setSize(self.geometry.width, self.border_width); self.borders[0].setSize(self.geometry.width, self.border_width);
self.borders[1].setSize(self.geometry.width, self.border_width); self.borders[1].setSize(self.geometry.width, self.border_width);
self.borders[2].setSize(self.border_width, self.geometry.height); self.borders[2].setSize(self.border_width, self.geometry.height);
self.borders[3].setSize(self.border_width, self.geometry.height); self.borders[3].setSize(self.border_width, self.geometry.height);
self.borders[1].node.setPosition(0, self.geometry.height - self.border_width); self.borders[1].node.setPosition(0, self.geometry.height - self.border_width);
self.borders[2].node.setPosition(self.geometry.width - self.border_width, 0); self.borders[2].node.setPosition(self.geometry.width - self.border_width, 0);
} }
// --------- XdgTopLevel event handlers --------- // --------- XdgTopLevel event handlers ---------
fn handleMap(listener: *wl.Listener(void)) void { fn handleMap(listener: *wl.Listener(void)) void {
const view: *View = @fieldParentPtr("map", listener); const view: *View = @fieldParentPtr("map", listener);
server.events.exec("ViewMapPre", .{view.id}); server.events.exec("ViewMapPre", .{view.id});
// we're gonna tell the client that it's tiled so it doesn't try anything // we're gonna tell the client that it's tiled so it doesn't try anything
// stupid // stupid
_ = view.xdg_toplevel.setTiled(.{ _ = view.xdg_toplevel.setTiled(.{
.top = true, .top = true,
.bottom = true, .bottom = true,
.left = true, .left = true,
.right = true, .right = true,
}); });
view.xdg_toplevel.events.request_fullscreen.add(&view.request_fullscreen); view.xdg_toplevel.events.request_fullscreen.add(&view.request_fullscreen);
view.xdg_toplevel.events.request_move.add(&view.request_move); view.xdg_toplevel.events.request_move.add(&view.request_move);
view.xdg_toplevel.events.request_resize.add(&view.request_resize); view.xdg_toplevel.events.request_resize.add(&view.request_resize);
view.xdg_toplevel.events.set_app_id.add(&view.set_app_id); view.xdg_toplevel.events.set_app_id.add(&view.set_app_id);
view.xdg_toplevel.events.set_title.add(&view.set_title); view.xdg_toplevel.events.set_title.add(&view.set_title);
// view.xdg_toplevel.events.set_parent.add(&view.set_parent); // view.xdg_toplevel.events.set_parent.add(&view.set_parent);
view.mapped = true; view.mapped = true;
server.events.exec("ViewMapPost", .{view.id}); server.events.exec("ViewMapPost", .{view.id});
} }
fn handleUnmap(listener: *wl.Listener(void)) void { fn handleUnmap(listener: *wl.Listener(void)) void {
const view: *View = @fieldParentPtr("unmap", listener); const view: *View = @fieldParentPtr("unmap", listener);
std.log.debug("Unmapping view '{s}'", .{view.xdg_toplevel.title orelse "(unnamed)"}); std.log.debug("Unmapping view '{s}'", .{view.xdg_toplevel.title orelse "(unnamed)"});
server.events.exec("ViewUnmapPre", .{view.id}); server.events.exec("ViewUnmapPre", .{view.id});
view.mapped = false; // we do this before any work is done so that nobody tries view.mapped = false; // we do this before any work is done so that nobody tries
// any funny business // any funny business
if (server.seat.focused_surface) |fs| { if (server.seat.focused_surface) |fs| {
if (fs == .view and fs.view == view) { if (fs == .view and fs.view == view) {
server.seat.focusSurface(null); server.seat.focusSurface(null);
}
} }
}
view.request_fullscreen.link.remove(); view.request_fullscreen.link.remove();
view.request_move.link.remove(); view.request_move.link.remove();
view.request_resize.link.remove(); view.request_resize.link.remove();
view.set_title.link.remove(); view.set_title.link.remove();
view.set_app_id.link.remove(); view.set_app_id.link.remove();
view.ack_configure.link.remove(); view.ack_configure.link.remove();
server.events.exec("ViewUnmapPost", .{view.id}); server.events.exec("ViewUnmapPost", .{view.id});
} }
fn handleDestroy(listener: *wl.Listener(void)) void { fn handleDestroy(listener: *wl.Listener(void)) void {
const view: *View = @fieldParentPtr("destroy", listener); const view: *View = @fieldParentPtr("destroy", listener);
// Remove decorations // Remove decorations
view.map.link.remove(); view.map.link.remove();
view.unmap.link.remove(); view.unmap.link.remove();
view.commit.link.remove(); view.commit.link.remove();
view.destroy.link.remove(); view.destroy.link.remove();
view.new_popup.link.remove(); view.new_popup.link.remove();
view.xdg_toplevel.base.surface.data = null; view.xdg_toplevel.base.surface.data = null;
view.scene_tree.node.destroy(); view.scene_tree.node.destroy();
// Destroy popups // Destroy popups
gpa.destroy(view); gpa.destroy(view);
} }
fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const view: *View = @fieldParentPtr("commit", listener); const view: *View = @fieldParentPtr("commit", listener);
// On the first commit, send a configure to tell the client it can proceed // On the first commit, send a configure to tell the client it can proceed
if (view.xdg_toplevel.base.initial_commit) { if (view.xdg_toplevel.base.initial_commit) {
// 5 is the XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION, I'm just not sure where it is in the bindings // 5 is the XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION, I'm just not sure where it is in the bindings
if (view.xdg_toplevel.base.client.shell.version >= 5) { if (view.xdg_toplevel.base.client.shell.version >= 5) {
// the client should know that it can only fullscreen, nothing else // the client should know that it can only fullscreen, nothing else
_ = view.xdg_toplevel.setWmCapabilities(.{ .fullscreen = true, }); _ = view.xdg_toplevel.setWmCapabilities(.{
.fullscreen = true,
});
}
// before committing we tell the client that we'll handle the decorations
if (view.xdg_toplevel_decoration) |deco| _ = deco.setMode(.server_side);
// this tells the client that it can start doing things we don't use our
// wrapper here cause we don't want to enforce any of our rules
_ = view.xdg_toplevel.setSize(0, 0);
return;
} }
// before committing we tell the client that we'll handle the decorations // resize on every commit
if (view.xdg_toplevel_decoration) |deco| _ = deco.setMode(.server_side); view.resizeBorders();
// this tells the client that it can start doing things we don't use our
// wrapper here cause we don't want to enforce any of our rules
_ = view.xdg_toplevel.setSize(0, 0);
return;
}
// resize on every commit
view.resizeBorders();
} }
// --------- XdgToplevel Event Handlers --------- // --------- XdgToplevel Event Handlers ---------
fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void { fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void {
const view: *View = @fieldParentPtr("new_popup", listener); const view: *View = @fieldParentPtr("new_popup", listener);
_ = Popup.init(xdg_popup, view.scene_tree); _ = Popup.init(xdg_popup, view.scene_tree);
} }
fn handleRequestMove( fn handleRequestMove(listener: *wl.Listener(*wlr.XdgToplevel.event.Move), _: *wlr.XdgToplevel.event.Move) void {
listener: *wl.Listener(*wlr.XdgToplevel.event.Move), const view: *View = @fieldParentPtr("request_move", listener);
_: *wlr.XdgToplevel.event.Move server.events.exec("ViewRequestMove", .{view.id});
) void {
const view: *View = @fieldParentPtr("request_move", listener);
server.events.exec("ViewRequestMove", .{view.id});
} }
fn handleRequestResize( fn handleRequestResize(listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), _: *wlr.XdgToplevel.event.Resize) void {
listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), const view: *View = @fieldParentPtr("request_resize", listener);
_: *wlr.XdgToplevel.event.Resize server.events.exec("ViewRequestResize", .{view.id});
) void {
const view: *View = @fieldParentPtr("request_resize", listener);
server.events.exec("ViewRequestResize", .{view.id});
} }
fn handleAckConfigure( fn handleAckConfigure(
listener: *wl.Listener(*wlr.XdgSurface.Configure), listener: *wl.Listener(*wlr.XdgSurface.Configure),
_: *wlr.XdgSurface.Configure, _: *wlr.XdgSurface.Configure,
) void { ) void {
const view: *View = @fieldParentPtr("ack_configure", listener); const view: *View = @fieldParentPtr("ack_configure", listener);
_ = view; _ = view;
std.log.err("Unimplemented ack configure", .{}); std.log.err("Unimplemented ack configure", .{});
} }
fn handleRequestFullscreen( fn handleRequestFullscreen(listener: *wl.Listener(void)) void {
listener: *wl.Listener(void) const view: *View = @fieldParentPtr("request_fullscreen", listener);
) void { server.events.exec("ViewRequestFullscreen", .{view.id});
const view: *View = @fieldParentPtr("request_fullscreen", listener);
server.events.exec("ViewRequestFullscreen", .{view.id});
} }
fn handleRequestMinimize( fn handleRequestMinimize(listener: *wl.Listener(void)) void {
listener: *wl.Listener(void) const view: *View = @fieldParentPtr("request_minimize", listener);
) void { server.events.exec("ViewRequestMinimize", .{view.id});
const view: *View = @fieldParentPtr("request_minimize", listener); std.log.debug("request_minimize unimplemented", .{});
server.events.exec("ViewRequestMinimize", .{view.id});
std.log.debug("request_minimize unimplemented", .{});
} }
fn handleSetAppId( fn handleSetAppId(listener: *wl.Listener(void)) void {
listener: *wl.Listener(void) const view: *View = @fieldParentPtr("set_app_id", listener);
) void { server.events.exec("ViewAppIdUpdate", .{view.id});
const view: *View = @fieldParentPtr("set_app_id", listener); std.log.debug("request_set_app_id unimplemented", .{});
server.events.exec("ViewAppIdUpdate", .{view.id});
std.log.debug("request_set_app_id unimplemented", .{});
} }
fn handleSetTitle( fn handleSetTitle(listener: *wl.Listener(void)) void {
listener: *wl.Listener(void) const view: *View = @fieldParentPtr("set_title", listener);
) void { server.events.exec("ViewTitleUpdate", .{view.id});
const view: *View = @fieldParentPtr("set_title", listener); std.log.debug("request_set_title unimplemented", .{});
server.events.exec("ViewTitleUpdate", .{view.id});
std.log.debug("request_set_title unimplemented", .{});
} }

View file

@ -2,7 +2,7 @@ const std = @import("std");
const zlua = @import("zlua"); const zlua = @import("zlua");
const wlr = @import("wlroots"); const wlr = @import("wlroots");
const Utils = @import("../Utils.zig"); const Utils = @import("../Utils.zig");
const LuaUtils = @import("LuaUtils.zig"); const LuaUtils = @import("LuaUtils.zig");
const gpa = std.heap.c_allocator; const gpa = std.heap.c_allocator;
@ -13,53 +13,53 @@ const server = &@import("../main.zig").server;
/// ---Spawn new application via the shell command /// ---Spawn new application via the shell command
/// ---@param cmd string Command to be run by a shell /// ---@param cmd string Command to be run by a shell
pub fn spawn(L: *zlua.Lua) i32 { pub fn spawn(L: *zlua.Lua) i32 {
const cmd = L.checkString(1); const cmd = L.checkString(1);
var child = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, gpa); var child = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, gpa);
child.env_map = env_map; child.env_map = env_map;
child.spawn() catch |err| switch (err) { child.spawn() catch |err| switch (err) {
error.OutOfMemory => Utils.oomPanic(), error.OutOfMemory => Utils.oomPanic(),
else => L.raiseErrorStr("Unable to spawn process child process", .{}), else => L.raiseErrorStr("Unable to spawn process child process", .{}),
}; };
L.pushNil(); L.pushNil();
return 1; return 1;
} }
/// ---Exit mezzaluna /// ---Exit mezzaluna
pub fn exit(L: *zlua.Lua) i32 { pub fn exit(L: *zlua.Lua) i32 {
server.wl_server.terminate(); server.wl_server.terminate();
L.pushNil(); L.pushNil();
return 1; return 1;
} }
/// ---Change to a different virtual terminal /// ---Change to a different virtual terminal
/// ---@param vt_num integer virtual terminal number to switch to /// ---@param vt_num integer virtual terminal number to switch to
pub fn change_vt(L: *zlua.Lua) i32 { pub fn change_vt(L: *zlua.Lua) i32 {
const vt_num = num: { const vt_num = num: {
const res = LuaUtils.coerceInteger(c_uint, L.checkInteger(1)) catch |err| break :num err; const res = LuaUtils.coerceInteger(c_uint, L.checkInteger(1)) catch |err| break :num err;
if (res < 1) break :num error.InvalidInteger; if (res < 1) break :num error.InvalidInteger;
break :num res; break :num res;
} catch L.raiseErrorStr("The vt number must be >= 1 and < inf", .{}); } catch L.raiseErrorStr("The vt number must be >= 1 and < inf", .{});
if (server.session) |session| { if (server.session) |session| {
std.log.debug("Changing virtual terminal to {d}", .{vt_num}); std.log.debug("Changing virtual terminal to {d}", .{vt_num});
wlr.Session.changeVt(session, vt_num) catch { wlr.Session.changeVt(session, vt_num) catch {
L.raiseErrorStr("Failed to switch vt", .{}); L.raiseErrorStr("Failed to switch vt", .{});
}; };
} else { } else {
L.raiseErrorStr("Mez has not been initialized yet", .{}); L.raiseErrorStr("Mez has not been initialized yet", .{});
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
/// --- Print the scene tree for debugging /// --- Print the scene tree for debugging
pub fn print_scene(L: *zlua.Lua) i32 { pub fn print_scene(L: *zlua.Lua) i32 {
@import("../Debug.zig").debugPrintSceneTree(); @import("../Debug.zig").debugPrintSceneTree();
L.pushNil(); L.pushNil();
return 1; return 1;
} }

View file

@ -6,23 +6,23 @@ const zlua = @import("zlua");
const gpa = std.heap.c_allocator; const gpa = std.heap.c_allocator;
pub fn getNestedField(L: *zlua.Lua, path: []u8) bool { pub fn getNestedField(L: *zlua.Lua, path: []u8) bool {
var tokens = std.mem.tokenizeScalar(u8, path, '.'); var tokens = std.mem.tokenizeScalar(u8, path, '.');
var first = true; var first = true;
while (tokens.next()) |token| { while (tokens.next()) |token| {
const tok = gpa.dupeZ(u8, token) catch return false; const tok = gpa.dupeZ(u8, token) catch return false;
if (first) { if (first) {
_ = L.getGlobal(tok) catch return false; _ = L.getGlobal(tok) catch return false;
first = false; first = false;
} else { } else {
_ = L.getField(-1, tok); _ = L.getField(-1, tok);
L.remove(-2); L.remove(-2);
}
if (L.isNil(-1)) {
return false;
}
} }
if (L.isNil(-1)) { return true;
return false;
}
}
return true;
} }

View file

@ -4,7 +4,7 @@ const std = @import("std");
const zlua = @import("zlua"); const zlua = @import("zlua");
const Lua = @import("Lua.zig"); const Lua = @import("Lua.zig");
const Utils = @import("../Utils.zig"); const Utils = @import("../Utils.zig");
const gpa = std.heap.c_allocator; const gpa = std.heap.c_allocator;
@ -12,29 +12,29 @@ const gpa = std.heap.c_allocator;
/// ---@vararg string paths to join /// ---@vararg string paths to join
/// ---@return string? /// ---@return string?
pub fn joinpath(L: *zlua.Lua) i32 { pub fn joinpath(L: *zlua.Lua) i32 {
const nargs: i32 = L.getTop(); const nargs: i32 = L.getTop();
if (nargs < 2) { if (nargs < 2) {
L.raiseErrorStr("Expected at least two paths to join", .{}); L.raiseErrorStr("Expected at least two paths to join", .{});
return 0; return 0;
}
var paths = std.ArrayList([:0]const u8).initCapacity(gpa, @intCast(nargs)) catch Utils.oomPanic();
defer paths.deinit(gpa);
var i: u8 = 1;
while (i <= nargs) : (i += 1) {
if (!L.isString(i)) {
L.raiseErrorStr("Expected string at argument %d", .{i});
return 0;
} }
const partial_path = L.toString(i) catch unreachable; var paths = std.ArrayList([:0]const u8).initCapacity(gpa, @intCast(nargs)) catch Utils.oomPanic();
paths.append(gpa, partial_path) catch Utils.oomPanic(); defer paths.deinit(gpa);
}
const final_path: []const u8 = std.fs.path.join(gpa, paths.items) catch Utils.oomPanic(); var i: u8 = 1;
defer gpa.free(final_path); while (i <= nargs) : (i += 1) {
if (!L.isString(i)) {
L.raiseErrorStr("Expected string at argument %d", .{i});
return 0;
}
_ = L.pushString(final_path); const partial_path = L.toString(i) catch unreachable;
return 1; paths.append(gpa, partial_path) catch Utils.oomPanic();
}
const final_path: []const u8 = std.fs.path.join(gpa, paths.items) catch Utils.oomPanic();
defer gpa.free(final_path);
_ = L.pushString(final_path);
return 1;
} }

View file

@ -15,60 +15,60 @@ const server = &@import("../main.zig").server;
/// ---@param options table /// ---@param options table
/// ---@return number id /// ---@return number id
pub fn add(L: *zlua.Lua) i32 { pub fn add(L: *zlua.Lua) i32 {
L.checkType(2, .table); L.checkType(2, .table);
var hook: *THook = gpa.create(THook) catch Utils.oomPanic(); var hook: *THook = gpa.create(THook) catch Utils.oomPanic();
// We support both a string and a table of strings as the first value of // We support both a string and a table of strings as the first value of
// add. Regardless of which type is passed in we create an arraylist of // add. Regardless of which type is passed in we create an arraylist of
// []const u8's // []const u8's
if (L.isTable(1)) { if (L.isTable(1)) {
hook.events = gpa.alloc(comptime []const u8, L.objectLen(1)) catch Utils.oomPanic(); hook.events = gpa.alloc(comptime []const u8, L.objectLen(1)) catch Utils.oomPanic();
var i: u32 = 0; var i: u32 = 0;
L.pushNil(); L.pushNil();
while (L.next(1)) { while (L.next(1)) {
if (L.isString(-1)) { if (L.isString(-1)) {
const s = L.checkString(-1); const s = L.checkString(-1);
hook.events[i] = gpa.dupe(u8, s) catch Utils.oomPanic(); hook.events[i] = gpa.dupe(u8, s) catch Utils.oomPanic();
i += 1; i += 1;
} }
L.pop(1); L.pop(1);
}
} else if (L.isString(1)) {
hook.events = gpa.alloc(comptime []const u8, 1) catch Utils.oomPanic();
const s = L.checkString(1);
hook.events[0] = gpa.dupe(u8, s) catch Utils.oomPanic();
} }
} else if (L.isString(1)) {
hook.events = gpa.alloc(comptime []const u8, 1) catch Utils.oomPanic();
const s = L.checkString(1);
hook.events[0] = gpa.dupe(u8, s) catch Utils.oomPanic();
}
_ = L.pushString("callback"); _ = L.pushString("callback");
_ = L.getTable(2); _ = L.getTable(2);
if (L.isFunction(-1)) { if (L.isFunction(-1)) {
hook.options.lua_cb_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic(); hook.options.lua_cb_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
} }
// TEST: this should be safe as the lua_cb_ref_idx's should never be the same // TEST: this should be safe as the lua_cb_ref_idx's should never be the same
// but that all really depends on the implementation of the hashmap // but that all really depends on the implementation of the hashmap
server.hooks.put(hook.options.lua_cb_ref_idx, hook) catch Utils.oomPanic(); server.hooks.put(hook.options.lua_cb_ref_idx, hook) catch Utils.oomPanic();
for (hook.events) |value| { for (hook.events) |value| {
server.events.put(value, hook) catch Utils.oomPanic(); server.events.put(value, hook) catch Utils.oomPanic();
} }
L.pushInteger(hook.options.lua_cb_ref_idx); L.pushInteger(hook.options.lua_cb_ref_idx);
return 1; return 1;
} }
/// ---Create an existing hook /// ---Create an existing hook
/// ---@param id number /// ---@param id number
/// ---@return boolean has it been deleted /// ---@return boolean has it been deleted
pub fn del(L: *zlua.Lua) i32 { pub fn del(L: *zlua.Lua) i32 {
const hook_id = LuaUtils.coerceInteger(i32, L.checkInteger(1)) catch L.raiseErrorStr("hook id must be a valid number", .{}); const hook_id = LuaUtils.coerceInteger(i32, L.checkInteger(1)) catch L.raiseErrorStr("hook id must be a valid number", .{});
const hook = server.hooks.get(hook_id); const hook = server.hooks.get(hook_id);
if (hook == null) L.raiseErrorStr("hook {} does not exist", .{hook_id}); if (hook == null) L.raiseErrorStr("hook {} does not exist", .{hook_id});
for (hook.?.events) |value| { for (hook.?.events) |value| {
server.events.del(value, hook.?); server.events.del(value, hook.?);
} }
L.pushBoolean(server.hooks.remove(hook_id)); L.pushBoolean(server.hooks.remove(hook_id));
return 1; return 1;
} }

View file

@ -14,17 +14,17 @@ const c = @import("../C.zig").c;
const server = &@import("../main.zig").server; const server = &@import("../main.zig").server;
fn parse_modkeys(modStr: []const u8) wlr.Keyboard.ModifierMask { fn parse_modkeys(modStr: []const u8) wlr.Keyboard.ModifierMask {
var it = std.mem.splitScalar(u8, modStr, '|'); var it = std.mem.splitScalar(u8, modStr, '|');
var modifiers = wlr.Keyboard.ModifierMask{}; var modifiers = wlr.Keyboard.ModifierMask{};
while (it.next()) |m| { while (it.next()) |m| {
inline for (std.meta.fields(@TypeOf(modifiers))) |f| { inline for (std.meta.fields(@TypeOf(modifiers))) |f| {
if (f.type == bool and std.mem.eql(u8, m, f.name)) { if (f.type == bool and std.mem.eql(u8, m, f.name)) {
@field(modifiers, f.name) = true; @field(modifiers, f.name) = true;
} }
}
} }
}
return modifiers; return modifiers;
} }
/// ---Create a new keymap /// ---Create a new keymap
@ -32,36 +32,36 @@ fn parse_modkeys(modStr: []const u8) wlr.Keyboard.ModifierMask {
/// ---@param string keys /// ---@param string keys
/// ---@param table options /// ---@param table options
pub fn add_keymap(L: *zlua.Lua) i32 { pub fn add_keymap(L: *zlua.Lua) i32 {
var keymap: Keymap = undefined; var keymap: Keymap = undefined;
keymap.options.repeat = true; keymap.options.repeat = true;
const mod = L.checkString(1); const mod = L.checkString(1);
keymap.modifier = parse_modkeys(mod); keymap.modifier = parse_modkeys(mod);
const key = L.checkString(2); const key = L.checkString(2);
keymap.keycode = xkb.Keysym.fromName(key, .no_flags); keymap.keycode = xkb.Keysym.fromName(key, .no_flags);
_ = L.pushString("press"); _ = L.pushString("press");
_ = L.getTable(3); _ = L.getTable(3);
if (L.isFunction(-1)) { if (L.isFunction(-1)) {
keymap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic(); keymap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
} }
_ = L.pushString("release"); _ = L.pushString("release");
_ = L.getTable(3); _ = L.getTable(3);
if (L.isFunction(-1)) { if (L.isFunction(-1)) {
keymap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic(); keymap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
} }
_ = L.pushString("repeat"); _ = L.pushString("repeat");
_ = L.getTable(3); _ = L.getTable(3);
keymap.options.repeat = L.isNil(-1) or L.toBoolean(-1); keymap.options.repeat = L.isNil(-1) or L.toBoolean(-1);
const hash = Keymap.hash(keymap.modifier, keymap.keycode); const hash = Keymap.hash(keymap.modifier, keymap.keycode);
server.keymaps.put(hash, keymap) catch Utils.oomPanic(); server.keymaps.put(hash, keymap) catch Utils.oomPanic();
L.pushNil(); L.pushNil();
return 1; return 1;
} }
/// ---Create a new mousemap /// ---Create a new mousemap
@ -69,113 +69,113 @@ pub fn add_keymap(L: *zlua.Lua) i32 {
/// ---@param string libevdev button name (ex. "BTN_LEFT", "BTN_RIGHT") /// ---@param string libevdev button name (ex. "BTN_LEFT", "BTN_RIGHT")
/// ---@param table options /// ---@param table options
pub fn add_mousemap(L: *zlua.Lua) i32 { pub fn add_mousemap(L: *zlua.Lua) i32 {
var mousemap: Mousemap = undefined; var mousemap: Mousemap = undefined;
const mod = L.checkString(1); const mod = L.checkString(1);
mousemap.modifier = parse_modkeys(mod); mousemap.modifier = parse_modkeys(mod);
const button = L.checkString(2); const button = L.checkString(2);
mousemap.event_code = c.libevdev_event_code_from_name(c.EV_KEY, button); mousemap.event_code = c.libevdev_event_code_from_name(c.EV_KEY, button);
_ = L.pushString("press"); _ = L.pushString("press");
_ = L.getTable(3); _ = L.getTable(3);
if (L.isFunction(-1)) { if (L.isFunction(-1)) {
mousemap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic(); mousemap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
} }
_ = L.pushString("release"); _ = L.pushString("release");
_ = L.getTable(3); _ = L.getTable(3);
if (L.isFunction(-1)) { if (L.isFunction(-1)) {
mousemap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic(); mousemap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
} }
_ = L.pushString("drag"); _ = L.pushString("drag");
_ = L.getTable(3); _ = L.getTable(3);
if (L.isFunction(-1)) { if (L.isFunction(-1)) {
mousemap.options.lua_drag_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic(); mousemap.options.lua_drag_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
} }
const hash = Mousemap.hash(mousemap.modifier, mousemap.event_code); const hash = Mousemap.hash(mousemap.modifier, mousemap.event_code);
server.mousemaps.put(hash, mousemap) catch Utils.oomPanic(); server.mousemaps.put(hash, mousemap) catch Utils.oomPanic();
L.pushNil(); L.pushNil();
return 1; return 1;
} }
/// ---Remove an existing keymap /// ---Remove an existing keymap
/// ---@param string modifiers /// ---@param string modifiers
/// ---@param string keys /// ---@param string keys
pub fn del_keymap(L: *zlua.Lua) i32 { pub fn del_keymap(L: *zlua.Lua) i32 {
L.checkType(1, .string); L.checkType(1, .string);
L.checkType(2, .string); L.checkType(2, .string);
var keymap: Keymap = undefined; var keymap: Keymap = undefined;
const mod = L.checkString(1); const mod = L.checkString(1);
keymap.modifier = parse_modkeys(mod); keymap.modifier = parse_modkeys(mod);
const key = L.checkString(2); const key = L.checkString(2);
keymap.keycode = xkb.Keysym.fromName(key, .no_flags); keymap.keycode = xkb.Keysym.fromName(key, .no_flags);
_ = server.keymaps.remove(Keymap.hash(keymap.modifier, keymap.keycode)); _ = server.keymaps.remove(Keymap.hash(keymap.modifier, keymap.keycode));
L.pushNil(); L.pushNil();
return 1; return 1;
} }
/// ---Remove an existing mousemap /// ---Remove an existing mousemap
/// ---@param string modifiers /// ---@param string modifiers
/// ---@param string button /// ---@param string button
pub fn del_mousemap(L: *zlua.Lua) i32 { pub fn del_mousemap(L: *zlua.Lua) i32 {
L.checkType(1, .string); L.checkType(1, .string);
L.checkType(2, .string); L.checkType(2, .string);
var mousemap: Mousemap = undefined; var mousemap: Mousemap = undefined;
const mod = L.checkString(1); const mod = L.checkString(1);
mousemap.modifier = parse_modkeys(mod); mousemap.modifier = parse_modkeys(mod);
const button = L.checkString(2); const button = L.checkString(2);
mousemap.event_code = c.libevdev_event_code_from_name(c.EV_KEY, button); mousemap.event_code = c.libevdev_event_code_from_name(c.EV_KEY, button);
_ = server.mousemaps.remove(Mousemap.hash(mousemap.modifier, mousemap.event_code)); _ = server.mousemaps.remove(Mousemap.hash(mousemap.modifier, mousemap.event_code));
L.pushNil(); L.pushNil();
return 1; return 1;
} }
/// ---Get the repeat information /// ---Get the repeat information
/// ---@return integer[2] /// ---@return integer[2]
pub fn get_repeat_info(L: *zlua.Lua) i32 { pub fn get_repeat_info(L: *zlua.Lua) i32 {
L.newTable(); L.newTable();
L.pushInteger(server.seat.keyboard_group.wlr_group.keyboard.repeat_info.rate); L.pushInteger(server.seat.keyboard_group.wlr_group.keyboard.repeat_info.rate);
L.setField(-2, "rate"); L.setField(-2, "rate");
L.pushInteger(server.seat.keyboard_group.wlr_group.keyboard.repeat_info.delay); L.pushInteger(server.seat.keyboard_group.wlr_group.keyboard.repeat_info.delay);
L.setField(-2, "delay"); L.setField(-2, "delay");
return 1; return 1;
} }
/// ---Set the repeat information /// ---Set the repeat information
/// ---@param integer rate /// ---@param integer rate
/// ---@param integer delay /// ---@param integer delay
pub fn set_repeat_info(L: *zlua.Lua) i32 { pub fn set_repeat_info(L: *zlua.Lua) i32 {
const rate = LuaUtils.coerceInteger(i32, L.checkInteger(1)) catch { const rate = LuaUtils.coerceInteger(i32, L.checkInteger(1)) catch {
L.raiseErrorStr("The rate must be a valid number", .{}); L.raiseErrorStr("The rate must be a valid number", .{});
}; };
const delay = LuaUtils.coerceInteger(i32, L.checkInteger(2)) catch { const delay = LuaUtils.coerceInteger(i32, L.checkInteger(2)) catch {
L.raiseErrorStr("The delay must be a valid number", .{}); L.raiseErrorStr("The delay must be a valid number", .{});
}; };
server.seat.keyboard_group.wlr_group.keyboard.setRepeatInfo(rate, delay); server.seat.keyboard_group.wlr_group.keyboard.setRepeatInfo(rate, delay);
return 0; return 0;
} }
/// ---Set the cursor type /// ---Set the cursor type
/// ---@param string cursor name /// ---@param string cursor name
pub fn set_cursor_type(L: *zlua.Lua) i32 { pub fn set_cursor_type(L: *zlua.Lua) i32 {
const name = L.checkString(1); const name = L.checkString(1);
server.cursor.wlr_cursor.setXcursor(server.cursor.x_cursor_manager, name); server.cursor.wlr_cursor.setXcursor(server.cursor.x_cursor_manager, name);
return 0; return 0;
} }

View file

@ -6,11 +6,11 @@ const zlua = @import("zlua");
const LuaUtils = @import("LuaUtils.zig"); const LuaUtils = @import("LuaUtils.zig");
const Bridge = @import("Bridge.zig"); const Bridge = @import("Bridge.zig");
const Fs = @import("Fs.zig"); const Fs = @import("Fs.zig");
const Input = @import("Input.zig"); const Input = @import("Input.zig");
const Api = @import("Api.zig"); const Api = @import("Api.zig");
const Hook = @import("Hook.zig"); const Hook = @import("Hook.zig");
const View = @import("View.zig"); const View = @import("View.zig");
const Output = @import("Output.zig"); const Output = @import("Output.zig");
const Remote = @import("Remote.zig"); const Remote = @import("Remote.zig");
@ -19,165 +19,168 @@ const gpa = std.heap.c_allocator;
state: *zlua.Lua, state: *zlua.Lua,
pub fn loadRuntimeDir(self: *zlua.Lua) !void { pub fn loadRuntimeDir(self: *zlua.Lua) !void {
const path_dir = try std.fs.path.joinZ(gpa, &[_][]const u8{ const path_dir = try std.fs.path.joinZ(gpa, &[_][]const u8{
config.runtime_path_prefix, config.runtime_path_prefix,
"share", "share",
"mezzaluna", "mezzaluna",
}); });
defer gpa.free(path_dir); defer gpa.free(path_dir);
{ {
_ = try self.getGlobal("mez"); _ = try self.getGlobal("mez");
_ = self.getField(-1, "path"); _ = self.getField(-1, "path");
defer self.pop(2); defer self.pop(2);
_ = self.pushString(path_dir); _ = self.pushString(path_dir);
self.setField(-2, "runtime"); self.setField(-2, "runtime");
} }
const path_full = try std.fs.path.joinZ(gpa, &[_][]const u8{ const path_full = try std.fs.path.joinZ(gpa, &[_][]const u8{
path_dir, path_dir,
"init.lua", "init.lua",
}); });
defer gpa.free(path_full); defer gpa.free(path_full);
self.doFile(path_full) catch { self.doFile(path_full) catch {
const err = try self.toString(-1); const err = try self.toString(-1);
std.log.debug("Failed to run lua file: {s}", .{err}); std.log.debug("Failed to run lua file: {s}", .{err});
}; };
} }
pub fn setBaseConfig(self: *zlua.Lua, path: []const u8) !void { pub fn setBaseConfig(self: *zlua.Lua, path: []const u8) !void {
{ {
_ = try self.getGlobal("mez"); _ = try self.getGlobal("mez");
_ = self.getField(-1, "path"); _ = self.getField(-1, "path");
defer self.pop(2); defer self.pop(2);
const new_path = try std.fs.path.join(gpa, &[_][]const u8{path, "init.lua"}); const new_path = try std.fs.path.join(gpa, &[_][]const u8{ path, "init.lua" });
defer gpa.free(new_path); defer gpa.free(new_path);
_ = self.pushString(new_path); _ = self.pushString(new_path);
self.setField(-2, "config"); self.setField(-2, "config");
} }
{ {
_ = try self.getGlobal("mez"); _ = try self.getGlobal("mez");
_ = self.getField(-1, "path"); _ = self.getField(-1, "path");
defer self.pop(2); defer self.pop(2);
const cur_path = self.toString(-1) catch ""; const cur_path = self.toString(-1) catch "";
const unsentinel: []const u8 = std.mem.span(cur_path.ptr); const unsentinel: []const u8 = std.mem.span(cur_path.ptr);
const new_path = try std.mem.concat(gpa, u8, &[_][]const u8{ const new_path = try std.mem.concat(gpa, u8, &[_][]const u8{
unsentinel, unsentinel,
";", ";",
path, path,
}); });
defer gpa.free(new_path); defer gpa.free(new_path);
_ = self.pushString(new_path); _ = self.pushString(new_path);
_ = self.setField(-2, "path"); _ = self.setField(-2, "path");
} }
} }
fn loadBaseConfig(self: *zlua.Lua) !void { fn loadBaseConfig(self: *zlua.Lua) !void {
const lua_path = "mez.path.base_config"; const lua_path = "mez.path.base_config";
if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) { if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) {
std.log.err("Base config path not found. is your runtime dir setup?", .{}); std.log.err("Base config path not found. is your runtime dir setup?", .{});
return; return;
} }
const path = self.toString(-1) catch |err| { const path = self.toString(-1) catch |err| {
std.log.err("Failed to pop the base config path from the lua stack. {}", .{err}); std.log.err("Failed to pop the base config path from the lua stack. {}", .{err});
return; return;
}; };
self.pop(-1); self.pop(-1);
try self.doFile(path); try self.doFile(path);
} }
fn loadConfigDir(self: *zlua.Lua) !void { fn loadConfigDir(self: *zlua.Lua) !void {
const lua_path = "mez.path.config"; const lua_path = "mez.path.config";
if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) { if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) {
std.log.err("Config path not found. is your runtime dir setup?", .{}); std.log.err("Config path not found. is your runtime dir setup?", .{});
return; return;
} }
const path = self.toString(-1) catch |err| { const path = self.toString(-1) catch |err| {
std.log.err("Failed to pop the config path from the lua stack. {}", .{err}); std.log.err("Failed to pop the config path from the lua stack. {}", .{err});
return; return;
}; };
self.pop(-1); self.pop(-1);
try self.doFile(path); try self.doFile(path);
} }
pub fn openLibs(self: *zlua.Lua) void { pub fn openLibs(self: *zlua.Lua) void {
{
self.newTable();
defer _ = self.setGlobal("mez");
{ {
self.newTable(); self.newTable();
defer _ = self.setField(-2, "path"); defer _ = self.setGlobal("mez");
{
self.newTable();
defer _ = self.setField(-2, "path");
}
{
const fs_funcs = zlua.fnRegsFromType(Fs);
LuaUtils.newLib(self, fs_funcs);
self.setField(-2, "fs");
}
{
const input_funcs = zlua.fnRegsFromType(Input);
LuaUtils.newLib(self, input_funcs);
self.setField(-2, "input");
}
{
const hook_funcs = zlua.fnRegsFromType(Hook);
LuaUtils.newLib(self, hook_funcs);
self.setField(-2, "hook");
}
{
const api_funcs = zlua.fnRegsFromType(Api);
LuaUtils.newLib(self, api_funcs);
self.setField(-2, "api");
}
{
const view_funcs = zlua.fnRegsFromType(View);
LuaUtils.newLib(self, view_funcs);
self.setField(-2, "view");
}
{
const output_funcs = zlua.fnRegsFromType(Output);
LuaUtils.newLib(self, output_funcs);
self.setField(-2, "output");
}
{
const remote_funcs = zlua.fnRegsFromType(Remote);
LuaUtils.newLib(self, remote_funcs);
self.setField(-2, "remote");
}
} }
{
const fs_funcs = zlua.fnRegsFromType(Fs);
LuaUtils.newLib(self, fs_funcs);
self.setField(-2, "fs");
}
{
const input_funcs = zlua.fnRegsFromType(Input);
LuaUtils.newLib(self, input_funcs);
self.setField(-2, "input");
}
{
const hook_funcs = zlua.fnRegsFromType(Hook);
LuaUtils.newLib(self, hook_funcs);
self.setField(-2, "hook");
}
{
const api_funcs = zlua.fnRegsFromType(Api);
LuaUtils.newLib(self, api_funcs);
self.setField(-2, "api");
}
{
const view_funcs = zlua.fnRegsFromType(View);
LuaUtils.newLib(self, view_funcs);
self.setField(-2, "view");
}
{
const output_funcs = zlua.fnRegsFromType(Output);
LuaUtils.newLib(self, output_funcs);
self.setField(-2, "output");
}
{
const remote_funcs = zlua.fnRegsFromType(Remote);
LuaUtils.newLib(self, remote_funcs);
self.setField(-2, "remote");
}
}
} }
pub const Config = struct { str: ?[]const u8, enabled: bool, }; pub const Config = struct {
str: ?[]const u8,
enabled: bool,
};
pub fn init(self: *Lua, cfg: Config) !void { pub fn init(self: *Lua, cfg: Config) !void {
self.state = try zlua.Lua.init(gpa); self.state = try zlua.Lua.init(gpa);
errdefer self.state.deinit(); errdefer self.state.deinit();
self.state.openLibs(); self.state.openLibs();
openLibs(self.state); openLibs(self.state);
if (!cfg.enabled) try setBaseConfig(self.state, ""); if (!cfg.enabled) try setBaseConfig(self.state, "");
loadRuntimeDir(self.state) catch |err| if (err == error.LuaRuntime) { loadRuntimeDir(self.state) catch |err| if (err == error.LuaRuntime) {
std.log.warn("{s}", .{try self.state.toString(-1)}); std.log.warn("{s}", .{try self.state.toString(-1)});
};
loadBaseConfig(self.state) catch |err| if (err == error.LuaRuntime) {
std.log.warn("{s}", .{try self.state.toString(-1)});
};
if (cfg.str) |path| {
defer gpa.free(path);
try setBaseConfig(self.state, path);
}
if (cfg.enabled) {
loadConfigDir(self.state) catch |err| if (err == error.LuaRuntime) {
std.log.warn("{s}", .{try self.state.toString(-1)});
}; };
}
std.log.debug("Loaded lua", .{}); loadBaseConfig(self.state) catch |err| if (err == error.LuaRuntime) {
std.log.warn("{s}", .{try self.state.toString(-1)});
};
if (cfg.str) |path| {
defer gpa.free(path);
try setBaseConfig(self.state, path);
}
if (cfg.enabled) {
loadConfigDir(self.state) catch |err| if (err == error.LuaRuntime) {
std.log.warn("{s}", .{try self.state.toString(-1)});
};
}
std.log.debug("Loaded lua", .{});
} }
pub fn deinit(self: *Lua) void { pub fn deinit(self: *Lua) void {
self.state.deinit(); self.state.deinit();
} }

View file

@ -8,53 +8,53 @@ const View = @import("../View.zig");
const server = &@import("../main.zig").server; const server = &@import("../main.zig").server;
pub fn coerceNumber(comptime x: type, number: zlua.Number) error{InvalidNumber}!x { pub fn coerceNumber(comptime x: type, number: zlua.Number) error{InvalidNumber}!x {
if (number < std.math.minInt(x) or number > std.math.maxInt(x) or std.math.isNan(number)) { if (number < std.math.minInt(x) or number > std.math.maxInt(x) or std.math.isNan(number)) {
return error.InvalidNumber; return error.InvalidNumber;
} }
switch (@typeInfo(x)) { switch (@typeInfo(x)) {
.int => return @as(x, @intFromFloat(number)), .int => return @as(x, @intFromFloat(number)),
.float => return @floatCast(number), .float => return @floatCast(number),
else => @compileError("unsupported type"), else => @compileError("unsupported type"),
} }
} }
pub fn coerceInteger(comptime x: type, number: zlua.Integer) error{InvalidInteger}!x { pub fn coerceInteger(comptime x: type, number: zlua.Integer) error{InvalidInteger}!x {
if (number < std.math.minInt(x) or number > std.math.maxInt(x) or std.math.isNan(number)) { if (number < std.math.minInt(x) or number > std.math.maxInt(x) or std.math.isNan(number)) {
return error.InvalidInteger; return error.InvalidInteger;
} }
switch (@typeInfo(x)) { switch (@typeInfo(x)) {
.int => return @intCast(number), .int => return @intCast(number),
.float => return @as(x, @floatFromInt(number)), .float => return @as(x, @floatFromInt(number)),
else => @compileError("unsupported type"), else => @compileError("unsupported type"),
} }
} }
pub fn newLib(L: *zlua.Lua, f: []const zlua.FnReg) void { pub fn newLib(L: *zlua.Lua, f: []const zlua.FnReg) void {
L.newLibTable(f); // documented as being unavailable, but it is. L.newLibTable(f); // documented as being unavailable, but it is.
for (f) |value| { for (f) |value| {
if (value.func == null) continue; if (value.func == null) continue;
L.pushClosure(value.func.?, 0); L.pushClosure(value.func.?, 0);
L.setField(-2, value.name); L.setField(-2, value.name);
} }
} }
/// makes a best effort to convert the value at the top of the stack to a string /// makes a best effort to convert the value at the top of the stack to a string
/// if we're unable to do so return "nil" /// if we're unable to do so return "nil"
pub fn toStringEx(L: *zlua.Lua) [:0]const u8 { pub fn toStringEx(L: *zlua.Lua) [:0]const u8 {
const errstr = "nil"; const errstr = "nil";
_ = L.getGlobal("tostring") catch return errstr; _ = L.getGlobal("tostring") catch return errstr;
L.insert(1); L.insert(1);
L.protectedCall(.{ .args = 1, .results = 1 }) catch return errstr; L.protectedCall(.{ .args = 1, .results = 1 }) catch return errstr;
return L.toString(-1) catch errstr; return L.toString(-1) catch errstr;
} }
pub fn viewById(view_id: u64) ?*View { pub fn viewById(view_id: u64) ?*View {
if (view_id == 0) { if (view_id == 0) {
if(server.seat.focused_surface) |fs| { if (server.seat.focused_surface) |fs| {
if(fs == .view) return fs.view; if (fs == .view) return fs.view;
}
} else {
return server.root.viewById(view_id);
} }
} else { return null;
return server.root.viewById(view_id);
}
return null;
} }

View file

@ -7,181 +7,180 @@ const LuaUtils = @import("LuaUtils.zig");
const server = &@import("../main.zig").server; const server = &@import("../main.zig").server;
fn output_id_err(L: *zlua.Lua) noreturn { fn output_id_err(L: *zlua.Lua) noreturn {
L.raiseErrorStr("The output id must be >= 0 and < inf", .{}); L.raiseErrorStr("The output id must be >= 0 and < inf", .{});
} }
/// ---@alias output_id integer /// ---@alias output_id integer
/// ---Get the ids for all available outputs /// ---Get the ids for all available outputs
/// ---@return output_id[]? /// ---@return output_id[]?
pub fn get_all_ids(L: *zlua.Lua) i32 { pub fn get_all_ids(L: *zlua.Lua) i32 {
var it = server.root.scene.outputs.iterator(.forward); var it = server.root.scene.outputs.iterator(.forward);
var index: usize = 1; var index: usize = 1;
L.newTable(); L.newTable();
while(it.next()) |scene_output| : (index += 1) { while (it.next()) |scene_output| : (index += 1) {
if(scene_output.output.data == null) continue; if (scene_output.output.data == null) continue;
const output = @as(*Output, @ptrCast(@alignCast(scene_output.output.data.?))); const output = @as(*Output, @ptrCast(@alignCast(scene_output.output.data.?)));
L.pushInteger(@intCast(index)); L.pushInteger(@intCast(index));
L.pushInteger(@intCast(output.id)); L.pushInteger(@intCast(output.id));
L.setTable(-3); L.setTable(-3);
} }
return 1; return 1;
} }
/// ---Get the id for the focused output /// ---Get the id for the focused output
/// ---@return output_id? /// ---@return output_id?
pub fn get_focused_id(L: *zlua.Lua) i32 { pub fn get_focused_id(L: *zlua.Lua) i32 {
if(server.seat.focused_output) |output| { if (server.seat.focused_output) |output| {
L.pushInteger(@intCast(output.id)); L.pushInteger(@intCast(output.id));
return 1; return 1;
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
/// ---Get refresh rate for the output /// ---Get refresh rate for the output
/// ---@param output_id output_id 0 maps to focused output /// ---@param output_id output_id 0 maps to focused output
/// ---@return integer? /// ---@return integer?
pub fn get_rate(L: *zlua.Lua) i32 { pub fn get_rate(L: *zlua.Lua) i32 {
const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L); const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L);
const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id); const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id);
if(output) |o| { if (output) |o| {
L.pushInteger(@intCast(o.wlr_output.refresh)); L.pushInteger(@intCast(o.wlr_output.refresh));
return 1;
}
L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
/// ---Get resolution in pixels of the output /// ---Get resolution in pixels of the output
/// ---@param output_id output_id 0 maps to focused output /// ---@param output_id output_id 0 maps to focused output
/// ---@return { width: integer, height: integer }? /// ---@return { width: integer, height: integer }?
pub fn get_resolution(L: *zlua.Lua) i32 { pub fn get_resolution(L: *zlua.Lua) i32 {
const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L); const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L);
const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id); const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id);
if(output) |o| { if (output) |o| {
L.newTable(); L.newTable();
_ = L.pushString("width"); _ = L.pushString("width");
L.pushInteger(@intCast(o.wlr_output.width)); L.pushInteger(@intCast(o.wlr_output.width));
L.setTable(-3); L.setTable(-3);
_ = L.pushString("height"); _ = L.pushString("height");
L.pushInteger(@intCast(o.wlr_output.height)); L.pushInteger(@intCast(o.wlr_output.height));
L.setTable(-3); L.setTable(-3);
return 1;
}
L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
/// ---Get the serial for the output /// ---Get the serial for the output
/// ---@param output_id output_id 0 maps to focused output /// ---@param output_id output_id 0 maps to focused output
/// ---@return string? /// ---@return string?
pub fn get_serial(L: *zlua.Lua) i32 { pub fn get_serial(L: *zlua.Lua) i32 {
const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L); const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L);
const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id); const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id);
if(output) |o| { if (output) |o| {
if(o.wlr_output.serial == null) { if (o.wlr_output.serial == null) {
L.pushNil(); L.pushNil();
return 1; return 1;
}
_ = L.pushString(std.mem.span(o.wlr_output.serial.?));
return 1;
} }
_ = L.pushString(std.mem.span(o.wlr_output.serial.?)); L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
/// ---Get the make for the output /// ---Get the make for the output
/// ---@param output_id output_id 0 maps to focused output /// ---@param output_id output_id 0 maps to focused output
/// ---@return string? /// ---@return string?
pub fn get_make(L: *zlua.Lua) i32 { pub fn get_make(L: *zlua.Lua) i32 {
const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L); const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L);
const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id); const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id);
if(output) |o| { if (output) |o| {
if(o.wlr_output.make == null) { if (o.wlr_output.make == null) {
L.pushNil(); L.pushNil();
return 1; return 1;
}
_ = L.pushString(std.mem.span(o.wlr_output.make.?));
return 1;
} }
_ = L.pushString(std.mem.span(o.wlr_output.make.?)); L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
/// ---Get the model for the output /// ---Get the model for the output
/// ---@param output_id output_id 0 maps to focused output /// ---@param output_id output_id 0 maps to focused output
/// ---@return stirng? /// ---@return stirng?
pub fn get_model(L: *zlua.Lua) i32 { pub fn get_model(L: *zlua.Lua) i32 {
const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L); const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L);
const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id); const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id);
if(output) |o| { if (output) |o| {
if(o.wlr_output.model == null) { if (o.wlr_output.model == null) {
L.pushNil(); L.pushNil();
return 1; return 1;
}
_ = L.pushString(std.mem.span(o.wlr_output.model.?));
return 1;
} }
_ = L.pushString(std.mem.span(o.wlr_output.model.?)); L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
/// ---Get the description for the output /// ---Get the description for the output
/// ---@param output_id output_id 0 maps to focused output /// ---@param output_id output_id 0 maps to focused output
/// ---@return stirng? /// ---@return stirng?
pub fn get_description(L: *zlua.Lua) i32 { pub fn get_description(L: *zlua.Lua) i32 {
const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L); const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L);
const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id); const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id);
if(output) |o| { if (output) |o| {
if(o.wlr_output.description == null) { if (o.wlr_output.description == null) {
L.pushNil(); L.pushNil();
return 1; return 1;
}
_ = L.pushString(std.mem.span(o.wlr_output.description.?));
return 1;
} }
_ = L.pushString(std.mem.span(o.wlr_output.description.?)); L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
/// ---Get the name of the output /// ---Get the name of the output
/// ---@param output_id output_id 0 maps to focused output /// ---@param output_id output_id 0 maps to focused output
/// ---@return stirng /// ---@return stirng
pub fn get_name(L: *zlua.Lua) i32 { pub fn get_name(L: *zlua.Lua) i32 {
const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L); const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L);
const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id); const output: ?*Output = if (output_id == 0) server.seat.focused_output else server.root.outputById(output_id);
if(output) |o| { if (output) |o| {
_ = L.pushString(std.mem.span(o.wlr_output.name)); _ = L.pushString(std.mem.span(o.wlr_output.name));
return 1;
}
L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }

View file

@ -7,6 +7,6 @@ const LuaUtils = @import("LuaUtils.zig");
const RemoteLua = @import("../RemoteLua.zig"); const RemoteLua = @import("../RemoteLua.zig");
pub fn print(L: *zlua.Lua) i32 { pub fn print(L: *zlua.Lua) i32 {
RemoteLua.sendNewLogEntry(L.checkString(1)); RemoteLua.sendNewLogEntry(L.checkString(1));
return 0; return 0;
} }

View file

@ -11,7 +11,7 @@ const LuaUtils = @import("LuaUtils.zig");
const server = &@import("../main.zig").server; const server = &@import("../main.zig").server;
fn view_id_err(L: *zlua.Lua) noreturn { fn view_id_err(L: *zlua.Lua) noreturn {
L.raiseErrorStr("The view id must be >= 0 and < inf", .{}); L.raiseErrorStr("The view id must be >= 0 and < inf", .{});
} }
// This allows us to differentiate random numbers from ids // This allows us to differentiate random numbers from ids
@ -20,84 +20,83 @@ fn view_id_err(L: *zlua.Lua) noreturn {
// ---Get the ids for all available views // ---Get the ids for all available views
// ---@return view_id[]? // ---@return view_id[]?
pub fn get_all_ids(L: *zlua.Lua) i32 { pub fn get_all_ids(L: *zlua.Lua) i32 {
var output_it = server.root.output_layout.outputs.iterator(.forward); var output_it = server.root.output_layout.outputs.iterator(.forward);
var index: i32 = 1; var index: i32 = 1;
L.newTable(); L.newTable();
while(output_it.next()) |o| { while (output_it.next()) |o| {
if(o.output.data == null) { if (o.output.data == null) {
std.log.err("Output arbitrary data not assigned", .{}); std.log.err("Output arbitrary data not assigned", .{});
unreachable; unreachable;
}
const output: *Output = @ptrCast(@alignCast(o.output.data.?));
if (!output.state.enabled) continue;
// Only search the content and fullscreen layers for views
const layers = [_]*wlr.SceneTree{
output.layers.content,
output.layers.fullscreen,
};
for(layers) |layer| {
if(layer.children.length() == 0) continue; // No children
if(@intFromPtr(layer) == 0) {
std.log.err("ts is literally a null ptr", .{});
unreachable;
}
var view_it = layer.children.iterator(.forward);
while(view_it.next()) |v| {
if(v.data == null) {
std.log.err("Unassigned arbitrary data in scene graph", .{});
unreachable;
} }
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(v.data.?)); const output: *Output = @ptrCast(@alignCast(o.output.data.?));
if (!output.state.enabled) continue;
if(scene_node_data.* == .view) { // Only search the content and fullscreen layers for views
L.pushInteger(@intCast(index)); const layers = [_]*wlr.SceneTree{
L.pushInteger(@intCast(scene_node_data.view.id)); output.layers.content,
L.setTable(-3); output.layers.fullscreen,
};
index += 1; for (layers) |layer| {
if (layer.children.length() == 0) continue; // No children
if (@intFromPtr(layer) == 0) {
std.log.err("ts is literally a null ptr", .{});
unreachable;
}
var view_it = layer.children.iterator(.forward);
while (view_it.next()) |v| {
if (v.data == null) {
std.log.err("Unassigned arbitrary data in scene graph", .{});
unreachable;
}
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(v.data.?));
if (scene_node_data.* == .view) {
L.pushInteger(@intCast(index));
L.pushInteger(@intCast(scene_node_data.view.id));
L.setTable(-3);
index += 1;
}
}
} }
}
} }
}
return 1; return 1;
} }
// ---Get the id for the focused view // ---Get the id for the focused view
// ---@return view_id? // ---@return view_id?
pub fn get_focused_id(L: *zlua.Lua) i32 { pub fn get_focused_id(L: *zlua.Lua) i32 {
if(server.seat.focused_surface) |fs| { if (server.seat.focused_surface) |fs| {
if(fs == .view) { if (fs == .view) {
L.pushInteger(@intCast(fs.view.id)); L.pushInteger(@intCast(fs.view.id));
return 1; return 1;
}
} }
}
L.pushNil(); L.pushNil();
return 1; return 1;
} }
// ---Close the view with view_id // ---Close the view with view_id
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
pub fn close(L: *zlua.Lua) i32 { pub fn close(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if(LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
v.close(); v.close();
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
// ---position the view by it's top left corner // ---position the view by it's top left corner
@ -105,39 +104,39 @@ pub fn close(L: *zlua.Lua) i32 {
// ---@param x number x position for view // ---@param x number x position for view
// ---@param y number y position for view // ---@param y number y position for view
pub fn set_position(L: *zlua.Lua) i32 { pub fn set_position(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
const x = LuaUtils.coerceNumber(i32, L.checkNumber(2)) catch L.raiseErrorStr("The x must be > -inf and < inf", .{}); const x = LuaUtils.coerceNumber(i32, L.checkNumber(2)) catch L.raiseErrorStr("The x must be > -inf and < inf", .{});
const y = LuaUtils.coerceNumber(i32, L.checkNumber(3)) catch L.raiseErrorStr("The y must be > -inf and < inf", .{}); const y = LuaUtils.coerceNumber(i32, L.checkNumber(3)) catch L.raiseErrorStr("The y must be > -inf and < inf", .{});
if(LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
v.setPosition(x, y); v.setPosition(x, y);
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
// ---Get the position of the view // ---Get the position of the view
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@return { x: integer, y: integer }? Position of the view // ---@return { x: integer, y: integer }? Position of the view
pub fn get_position(L: *zlua.Lua) i32 { pub fn get_position(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
L.newTable(); L.newTable();
_ = L.pushString("x"); _ = L.pushString("x");
L.pushInteger(@intCast(v.scene_tree.node.x)); L.pushInteger(@intCast(v.scene_tree.node.x));
L.setTable(-3); L.setTable(-3);
_ = L.pushString("y"); _ = L.pushString("y");
L.pushInteger(@intCast(v.scene_tree.node.y)); L.pushInteger(@intCast(v.scene_tree.node.y));
L.setTable(-3); L.setTable(-3);
return 1;
}
L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
// ---Set the size of the spesified view. Will be resized relative to // ---Set the size of the spesified view. Will be resized relative to
@ -145,234 +144,234 @@ pub fn get_position(L: *zlua.Lua) i32 {
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@return // ---@return
pub fn set_size(L: *zlua.Lua) i32 { pub fn set_size(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
// We use u32s here to enforce a minimum size of zero. The call to resize a // We use u32s here to enforce a minimum size of zero. The call to resize a
// toplevel requires a i32, which doesn't make too much sense as there's an // toplevel requires a i32, which doesn't make too much sense as there's an
// assertion in the code enforcing that both the width and height are greater // assertion in the code enforcing that both the width and height are greater
// than or equal to zero. // than or equal to zero.
const width = LuaUtils.coerceNumber(u32, L.checkNumber(2)) catch L.raiseErrorStr("The width must be >= 0 and < inf", .{}); const width = LuaUtils.coerceNumber(u32, L.checkNumber(2)) catch L.raiseErrorStr("The width must be >= 0 and < inf", .{});
const height = LuaUtils.coerceNumber(u32, L.checkNumber(3)) catch L.raiseErrorStr("The height must be >= 0 and < inf", .{}); const height = LuaUtils.coerceNumber(u32, L.checkNumber(3)) catch L.raiseErrorStr("The height must be >= 0 and < inf", .{});
if(LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
v.setSize(@intCast(width), @intCast(height)); v.setSize(@intCast(width), @intCast(height));
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
// ---Get the size of the view // ---Get the size of the view
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@return { width: integer, height: integer }? Size of the view // ---@return { width: integer, height: integer }? Size of the view
pub fn get_size(L: *zlua.Lua) i32 { pub fn get_size(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
L.newTable(); L.newTable();
_ = L.pushString("width"); _ = L.pushString("width");
L.pushInteger(@intCast(v.xdg_toplevel.current.width)); L.pushInteger(@intCast(v.xdg_toplevel.current.width));
L.setTable(-3); L.setTable(-3);
_ = L.pushString("height"); _ = L.pushString("height");
L.pushInteger(@intCast(v.xdg_toplevel.current.height)); L.pushInteger(@intCast(v.xdg_toplevel.current.height));
L.setTable(-3); L.setTable(-3);
return 1;
}
L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
// ---Remove focus from current view, and set to given id // ---Remove focus from current view, and set to given id
// ---@param view_id view_id Id of the view to be focused, or nil to remove focus // ---@param view_id view_id Id of the view to be focused, or nil to remove focus
pub fn set_focused(L: *zlua.Lua) i32 { pub fn set_focused(L: *zlua.Lua) i32 {
const view_id: ?c_longlong = L.optInteger(1); const view_id: ?c_longlong = L.optInteger(1);
if(view_id == null) { if (view_id == null) {
server.seat.focusSurface(null); server.seat.focusSurface(null);
} else if(server.root.viewById(@intCast(view_id.?))) |view| { } else if (server.root.viewById(@intCast(view_id.?))) |view| {
server.seat.focusSurface(.{ .view = view }); server.seat.focusSurface(.{ .view = view });
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
// ---Toggle the view to enter fullscreen. Will enter the fullsreen // ---Toggle the view to enter fullscreen. Will enter the fullsreen
// layer. // layer.
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
pub fn toggle_fullscreen(L: *zlua.Lua) i32 { pub fn toggle_fullscreen(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
std.log.debug("fullscreen view {d}", .{view_id}); std.log.debug("fullscreen view {d}", .{view_id});
if(LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
std.log.debug("toggling fullscreen", .{}); std.log.debug("toggling fullscreen", .{});
v.toggleFullscreen(); v.toggleFullscreen();
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
// ---Get the title of the view // ---Get the title of the view
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@return string? // ---@return string?
pub fn get_title(L: *zlua.Lua) i32 { pub fn get_title(L: *zlua.Lua) i32 {
const view_id: u64 = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id: u64 = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if(LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
if(v.xdg_toplevel.title == null) { if (v.xdg_toplevel.title == null) {
L.pushNil(); L.pushNil();
return 1; return 1;
}
_ = L.pushString(std.mem.span(v.xdg_toplevel.title.?));
return 1;
} }
_ = L.pushString(std.mem.span(v.xdg_toplevel.title.?)); L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
// ---Get the app_id of the view // ---Get the app_id of the view
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@return string? // ---@return string?
pub fn get_app_id(L: *zlua.Lua) i32 { pub fn get_app_id(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if(LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
if(v.xdg_toplevel.app_id == null) { if (v.xdg_toplevel.app_id == null) {
L.pushNil(); L.pushNil();
return 1; return 1;
}
_ = L.pushString(std.mem.span(v.xdg_toplevel.app_id.?));
return 1;
} }
_ = L.pushString(std.mem.span(v.xdg_toplevel.app_id.?)); L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
// ---Enable or disable a view // ---Enable or disable a view
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@param enabled boolean // ---@param enabled boolean
pub fn set_enabled(L: *zlua.Lua) i32 { pub fn set_enabled(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (!L.isBoolean(2)) { if (!L.isBoolean(2)) {
L.raiseErrorStr("argument 2 must be a boolean", .{}); L.raiseErrorStr("argument 2 must be a boolean", .{});
} }
const activate = L.toBoolean(2); const activate = L.toBoolean(2);
if (LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
_ = v.xdg_toplevel.setActivated(activate); _ = v.xdg_toplevel.setActivated(activate);
return 0; return 0;
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
// ---Check if a view is enabled // ---Check if a view is enabled
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@return boolean? // ---@return boolean?
pub fn get_enabled(L: *zlua.Lua) i32 { pub fn get_enabled(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if(LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
_ = L.pushBoolean(v.xdg_toplevel.current.activated); _ = L.pushBoolean(v.xdg_toplevel.current.activated);
return 1;
}
L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
// ---Set a view you intend to resize // ---Set a view you intend to resize
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@param enable boolean // ---@param enable boolean
pub fn set_resizing(L: *zlua.Lua) i32 { pub fn set_resizing(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (!L.isBoolean(2)) { if (!L.isBoolean(2)) {
L.raiseErrorStr("argument 2 must be a boolean", .{}); L.raiseErrorStr("argument 2 must be a boolean", .{});
} }
const resizing = L.toBoolean(2); const resizing = L.toBoolean(2);
if (LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
_ = v.xdg_toplevel.setResizing(resizing); _ = v.xdg_toplevel.setResizing(resizing);
return 0; return 0;
} }
L.pushNil(); L.pushNil();
return 1; return 1;
} }
// ---Check if a view is resizing // ---Check if a view is resizing
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
// ---@return boolean? nil if view cannot be found // ---@return boolean? nil if view cannot be found
pub fn get_resizing(L: *zlua.Lua) i32 { pub fn get_resizing(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if(LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
_ = L.pushBoolean(v.xdg_toplevel.current.resizing); _ = L.pushBoolean(v.xdg_toplevel.current.resizing);
return 1;
}
L.pushNil();
return 1; return 1;
}
L.pushNil();
return 1;
} }
// ---Set the borders of a view // ---Set the borders of a view
// ---@param view_id view_id 0 maps to focused view // ---@param view_id view_id 0 maps to focused view
/// ---@param options table options for the view's borders /// ---@param options table options for the view's borders
pub fn set_border(L: *zlua.Lua) i32 { pub fn set_border(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L); const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
_ = L.pushString("color"); _ = L.pushString("color");
_ = L.getTable(2); _ = L.getTable(2);
if (!L.isString(-1)) L.raiseErrorStr("The color must be a string", .{}); if (!L.isString(-1)) L.raiseErrorStr("The color must be a string", .{});
const color = L.checkString(-1); const color = L.checkString(-1);
const border_color: [4]f32 = color: { const border_color: [4]f32 = color: {
errdefer L.raiseErrorStr("The color must be a valid hex string", .{}); errdefer L.raiseErrorStr("The color must be a valid hex string", .{});
var start_color_idx: u8 = 0; var start_color_idx: u8 = 0;
if (color[0] == '#') start_color_idx = 1; if (color[0] == '#') start_color_idx = 1;
const color_fields = (color.len - start_color_idx) / 2; const color_fields = (color.len - start_color_idx) / 2;
var alpha: ?f32 = null; var alpha: ?f32 = null;
if (color_fields != 4) { if (color_fields != 4) {
if (color_fields != 3) L.raiseErrorStr("The color must be at least 6 characters long", .{}); if (color_fields != 3) L.raiseErrorStr("The color must be at least 6 characters long", .{});
alpha = 1; alpha = 1;
} }
const r = ((@as(f32, @floatFromInt(try std.fmt.parseInt(u8, color[start_color_idx..start_color_idx + 2], 16))) / 255.0) * 1000.0) / 1000.0; const r = ((@as(f32, @floatFromInt(try std.fmt.parseInt(u8, color[start_color_idx .. start_color_idx + 2], 16))) / 255.0) * 1000.0) / 1000.0;
const g = ((@as(f32, @floatFromInt(try std.fmt.parseInt(u8, color[start_color_idx + 2..start_color_idx + 4], 16))) / 255.0) * 1000.0) / 1000.0; const g = ((@as(f32, @floatFromInt(try std.fmt.parseInt(u8, color[start_color_idx + 2 .. start_color_idx + 4], 16))) / 255.0) * 1000.0) / 1000.0;
const b = ((@as(f32, @floatFromInt(try std.fmt.parseInt(u8, color[start_color_idx + 4..start_color_idx + 6], 16))) / 255.0) * 1000.0) / 1000.0; const b = ((@as(f32, @floatFromInt(try std.fmt.parseInt(u8, color[start_color_idx + 4 .. start_color_idx + 6], 16))) / 255.0) * 1000.0) / 1000.0;
const a = alpha orelse ((@as(f32, @floatFromInt(try std.fmt.parseInt(u8, color[start_color_idx + 6..start_color_idx + 8], 16))) / 255.0) * 1000.0) / 1000.0; const a = alpha orelse ((@as(f32, @floatFromInt(try std.fmt.parseInt(u8, color[start_color_idx + 6 .. start_color_idx + 8], 16))) / 255.0) * 1000.0) / 1000.0;
break :color .{ r, g, b, a }; break :color .{ r, g, b, a };
}; };
_ = L.pushString("width"); _ = L.pushString("width");
_ = L.getTable(2); _ = L.getTable(2);
const width = LuaUtils.coerceInteger(u32, L.checkInteger(-1)) catch { const width = LuaUtils.coerceInteger(u32, L.checkInteger(-1)) catch {
L.raiseErrorStr("The border width must be >= 0 and < inf", .{}); L.raiseErrorStr("The border width must be >= 0 and < inf", .{});
}; };
if (LuaUtils.viewById(view_id)) |v| { if (LuaUtils.viewById(view_id)) |v| {
v.setBorderColor(&border_color); v.setBorderColor(&border_color);
if (width != v.border_width) { if (width != v.border_width) {
v.border_width = @intCast(width); v.border_width = @intCast(width);
// the size has changed which means we need to update the borders // the size has changed which means we need to update the borders
v.resizeBorders(); v.resizeBorders();
}
return 0;
} }
return 0; return 0;
}
return 0;
} }
/// TODO: impl /// TODO: impl
@ -383,6 +382,6 @@ pub fn set_border(L: *zlua.Lua) i32 {
/// NOTE(squibid): this should be handled by the layout_manager to reduce the /// NOTE(squibid): this should be handled by the layout_manager to reduce the
/// work required by the user /// work required by the user
pub fn setWmCapabilities(L: *zlua.Lua) i32 { pub fn setWmCapabilities(L: *zlua.Lua) i32 {
_ = L; _ = L;
return 0; return 0;
} }

View file

@ -27,66 +27,66 @@ const args =
; ;
pub fn main() !void { pub fn main() !void {
const params = comptime clap.parseParamsComptime(args); const params = comptime clap.parseParamsComptime(args);
var diag = clap.Diagnostic{}; var diag = clap.Diagnostic{};
const parsers = comptime .{ const parsers = comptime .{
.path = clap.parsers.string, .path = clap.parsers.string,
.command = clap.parsers.string, .command = clap.parsers.string,
}; };
var res = clap.parse(clap.Help, &params, parsers, .{ var res = clap.parse(clap.Help, &params, parsers, .{
.diagnostic = &diag, .diagnostic = &diag,
.allocator = gpa, .allocator = gpa,
}) catch |err| { }) catch |err| {
try diag.reportToFile(.stderr(), err); try diag.reportToFile(.stderr(), err);
return err; return err;
}; };
defer res.deinit(); defer res.deinit();
if (res.args.help == 1) { if (res.args.help == 1) {
try @constCast(&std.fs.File.stdout().writer(&[_]u8{}).interface).writeAll(usage); try @constCast(&std.fs.File.stdout().writer(&[_]u8{}).interface).writeAll(usage);
std.process.exit(0); std.process.exit(0);
} }
if (res.args.version == 1) { if (res.args.version == 1) {
try @constCast(&std.fs.File.stdout().writer(&[_]u8{}).interface).writeAll(config.version); try @constCast(&std.fs.File.stdout().writer(&[_]u8{}).interface).writeAll(config.version);
std.process.exit(0); std.process.exit(0);
} }
var lua_config: Lua.Config = .{ .enabled = true, .str = null }; var lua_config: Lua.Config = .{ .enabled = true, .str = null };
if (res.args.u != null and res.args.clean == 1) { if (res.args.u != null and res.args.clean == 1) {
std.debug.panic("You cannot set both -u and --clean", .{}); std.debug.panic("You cannot set both -u and --clean", .{});
} else if (res.args.u != null) { } else if (res.args.u != null) {
// this is freed in lua/lua.zig // this is freed in lua/lua.zig
const path = try std.fs.cwd().realpathAlloc(gpa, res.args.u.?); const path = try std.fs.cwd().realpathAlloc(gpa, res.args.u.?);
lua_config.str = path; lua_config.str = path;
} else if (res.args.clean == 1) { } else if (res.args.clean == 1) {
lua_config.enabled = false; lua_config.enabled = false;
} }
wlr.log.init(.err, null); wlr.log.init(.err, null);
std.log.info("Starting mezzaluna", .{}); std.log.info("Starting mezzaluna", .{});
server.init(); server.init();
defer server.deinit(); defer server.deinit();
try lua.init(lua_config); try lua.init(lua_config);
var buf: [11]u8 = undefined; var buf: [11]u8 = undefined;
const socket = try server.wl_server.addSocketAuto(&buf); const socket = try server.wl_server.addSocketAuto(&buf);
env_map = try std.process.getEnvMap(gpa); env_map = try std.process.getEnvMap(gpa);
try env_map.put("WAYLAND_DISPLAY", socket); try env_map.put("WAYLAND_DISPLAY", socket);
if (res.args.c) |cmd| { if (res.args.c) |cmd| {
var child = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, gpa); var child = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, gpa);
child.env_map = &env_map; child.env_map = &env_map;
try child.spawn(); try child.spawn();
} }
defer env_map.deinit(); defer env_map.deinit();
std.log.info("Starting backend", .{}); std.log.info("Starting backend", .{});
server.backend.start() catch |err| { server.backend.start() catch |err| {
std.debug.panic("Failed to start backend: {}", .{err}); std.debug.panic("Failed to start backend: {}", .{err});
}; };
std.log.info("Starting server", .{}); std.log.info("Starting server", .{});
server.wl_server.run(); server.wl_server.run();
} }

View file

@ -7,53 +7,53 @@ const Hook = @import("Hook.zig");
const server = &@import("../main.zig").server; const server = &@import("../main.zig").server;
const Node = struct { const Node = struct {
hook: *const Hook, hook: *const Hook,
node: std.SinglyLinkedList.Node, node: std.SinglyLinkedList.Node,
}; };
events: std.StringHashMap(*std.SinglyLinkedList), events: std.StringHashMap(*std.SinglyLinkedList),
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
pub fn init(allocator: std.mem.Allocator) !Events { pub fn init(allocator: std.mem.Allocator) !Events {
return Events{ return Events{
.allocator = allocator, .allocator = allocator,
.events = .init(allocator), .events = .init(allocator),
}; };
} }
pub fn put(self: *Events, key: []const u8, hook: *const Hook) !void { pub fn put(self: *Events, key: []const u8, hook: *const Hook) !void {
var ll: *std.SinglyLinkedList = undefined; var ll: *std.SinglyLinkedList = undefined;
if (self.events.get(key)) |sll| { if (self.events.get(key)) |sll| {
ll = sll; ll = sll;
} else { } else {
ll = try self.allocator.create(std.SinglyLinkedList); ll = try self.allocator.create(std.SinglyLinkedList);
ll.* = .{}; ll.* = .{};
try self.events.put(key, ll); try self.events.put(key, ll);
} }
const data = try self.allocator.create(Node); const data = try self.allocator.create(Node);
data.* = .{ data.* = .{
.hook = hook, .hook = hook,
.node = .{}, .node = .{},
}; };
ll.prepend(&data.node); ll.prepend(&data.node);
} }
pub fn del(self: *Events, key: []const u8, hook: *const Hook) void { pub fn del(self: *Events, key: []const u8, hook: *const Hook) void {
if (self.events.get(key)) |e| { if (self.events.get(key)) |e| {
var node = e.first; var node = e.first;
while (node) |n| : (node = n.next) { while (node) |n| : (node = n.next) {
const data: *Node = @fieldParentPtr("node", n); const data: *Node = @fieldParentPtr("node", n);
if (data.hook.options.lua_cb_ref_idx == hook.options.lua_cb_ref_idx) e.remove(n); if (data.hook.options.lua_cb_ref_idx == hook.options.lua_cb_ref_idx) e.remove(n);
}
} }
}
} }
pub fn exec(self: *Events, event: []const u8, args: anytype) void { pub fn exec(self: *Events, event: []const u8, args: anytype) void {
if (self.events.get(event)) |e| { if (self.events.get(event)) |e| {
var node = e.first; var node = e.first;
while (node) |n| : (node = n.next) { while (node) |n| : (node = n.next) {
const data: *Node = @fieldParentPtr("node", n); const data: *Node = @fieldParentPtr("node", n);
data.hook.callback(args); data.hook.callback(args);
}
} }
}
} }

View file

@ -13,34 +13,34 @@ const Lua = &@import("../main.zig").lua;
events: [][]const u8, // a list of events events: [][]const u8, // a list of events
options: struct { options: struct {
// group: []const u8, // TODO: do we need groups? // group: []const u8, // TODO: do we need groups?
/// This is the location of the callback lua function in the lua registry /// This is the location of the callback lua function in the lua registry
lua_cb_ref_idx: i32, lua_cb_ref_idx: i32,
}, },
pub fn callback(self: *const Hook, args: anytype) void { pub fn callback(self: *const Hook, args: anytype) void {
const ArgsType = @TypeOf(args); const ArgsType = @TypeOf(args);
const args_type_info = @typeInfo(ArgsType); const args_type_info = @typeInfo(ArgsType);
if (args_type_info != .@"struct") { if (args_type_info != .@"struct") {
@compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
} }
const t = Lua.state.rawGetIndex(zlua.registry_index, self.options.lua_cb_ref_idx); const t = Lua.state.rawGetIndex(zlua.registry_index, self.options.lua_cb_ref_idx);
if (t != zlua.LuaType.function) { if (t != zlua.LuaType.function) {
RemoteLua.sendNewLogEntry("Failed to call hook, it doesn't have a callback."); RemoteLua.sendNewLogEntry("Failed to call hook, it doesn't have a callback.");
Lua.state.pop(1); Lua.state.pop(1);
return; return;
} }
// allow passing any arguments to the lua hook // allow passing any arguments to the lua hook
var i: u8 = 0; var i: u8 = 0;
inline for (args, 1..) |field, k| { inline for (args, 1..) |field, k| {
try Lua.state.pushAny(field); try Lua.state.pushAny(field);
i = k; i = k;
} }
Lua.state.protectedCall(.{ .args = i }) catch { Lua.state.protectedCall(.{ .args = i }) catch {
RemoteLua.sendNewLogEntry(Lua.state.toString(-1) catch unreachable); RemoteLua.sendNewLogEntry(Lua.state.toString(-1) catch unreachable);
}; };
Lua.state.pop(-1); Lua.state.pop(-1);
} }

View file

@ -14,31 +14,31 @@ const Lua = &@import("../main.zig").lua;
modifier: wlr.Keyboard.ModifierMask, modifier: wlr.Keyboard.ModifierMask,
keycode: xkb.Keysym, keycode: xkb.Keysym,
options: struct { options: struct {
repeat: bool, repeat: bool,
/// This is the location of the on press lua function in the lua registry /// This is the location of the on press lua function in the lua registry
lua_press_ref_idx: i32, lua_press_ref_idx: i32,
/// This is the location of the on release lua function in the lua registry /// This is the location of the on release lua function in the lua registry
lua_release_ref_idx: i32, lua_release_ref_idx: i32,
}, },
pub fn callback(self: *const Keymap, release: bool) void { pub fn callback(self: *const Keymap, release: bool) void {
const lua_ref_idx = if (release) self.options.lua_release_ref_idx else self.options.lua_press_ref_idx; const lua_ref_idx = if (release) self.options.lua_release_ref_idx else self.options.lua_press_ref_idx;
const t = Lua.state.rawGetIndex(zlua.registry_index, lua_ref_idx); const t = Lua.state.rawGetIndex(zlua.registry_index, lua_ref_idx);
if (t != zlua.LuaType.function) { if (t != zlua.LuaType.function) {
RemoteLua.sendNewLogEntry("Failed to call keybind, it doesn't have a callback."); RemoteLua.sendNewLogEntry("Failed to call keybind, it doesn't have a callback.");
Lua.state.pop(1); Lua.state.pop(1);
return; return;
} }
Lua.state.protectedCall(.{ .args = 0, .results = 0 }) catch { Lua.state.protectedCall(.{ .args = 0, .results = 0 }) catch {
RemoteLua.sendNewLogEntry(Lua.state.toString(-1) catch unreachable); RemoteLua.sendNewLogEntry(Lua.state.toString(-1) catch unreachable);
}; };
Lua.state.pop(-1); Lua.state.pop(-1);
} }
pub fn hash(modifier: wlr.Keyboard.ModifierMask, keycode: xkb.Keysym) u64 { pub fn hash(modifier: wlr.Keyboard.ModifierMask, keycode: xkb.Keysym) u64 {
const mod_val: u32 = @bitCast(modifier); const mod_val: u32 = @bitCast(modifier);
const key_val: u32 = @intFromEnum(keycode); const key_val: u32 = @intFromEnum(keycode);
return (@as(u64, mod_val) << 32) | @as(u64, key_val); return (@as(u64, mod_val) << 32) | @as(u64, key_val);
} }

View file

@ -14,55 +14,55 @@ const Lua = &@import("../main.zig").lua;
modifier: wlr.Keyboard.ModifierMask, modifier: wlr.Keyboard.ModifierMask,
event_code: i32, event_code: i32,
options: struct { options: struct {
/// This is the location of the on press lua function in the lua registry /// This is the location of the on press lua function in the lua registry
lua_press_ref_idx: i32, lua_press_ref_idx: i32,
/// This is the location of the on release lua function in the lua registry /// This is the location of the on release lua function in the lua registry
lua_release_ref_idx: i32, lua_release_ref_idx: i32,
/// This is the location of the on drag lua function in the lua registry /// This is the location of the on drag lua function in the lua registry
lua_drag_ref_idx: i32, lua_drag_ref_idx: i32,
}, },
pub const MousemapState = enum { press, drag, release }; pub const MousemapState = enum { press, drag, release };
// Returns true if mouse input should be passed through // Returns true if mouse input should be passed through
pub fn callback(self: *const Mousemap, state: MousemapState, args: anytype) bool { pub fn callback(self: *const Mousemap, state: MousemapState, args: anytype) bool {
const ArgsType = @TypeOf(args); const ArgsType = @TypeOf(args);
const args_type_info = @typeInfo(ArgsType); const args_type_info = @typeInfo(ArgsType);
if (args_type_info != .@"struct") { if (args_type_info != .@"struct") {
@compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
} }
const lua_ref_idx = switch(state) { const lua_ref_idx = switch (state) {
.press => self.options.lua_press_ref_idx, .press => self.options.lua_press_ref_idx,
.release => self.options.lua_release_ref_idx, .release => self.options.lua_release_ref_idx,
.drag => self.options.lua_drag_ref_idx .drag => self.options.lua_drag_ref_idx,
}; };
const t = Lua.state.rawGetIndex(zlua.registry_index, lua_ref_idx); const t = Lua.state.rawGetIndex(zlua.registry_index, lua_ref_idx);
if (t != zlua.LuaType.function) { if (t != zlua.LuaType.function) {
RemoteLua.sendNewLogEntry("Failed to call mousemap, it doesn't have a callback."); RemoteLua.sendNewLogEntry("Failed to call mousemap, it doesn't have a callback.");
Lua.state.pop(1); Lua.state.pop(1);
return false; return false;
} }
// allow passing any arguments to the lua hook // allow passing any arguments to the lua hook
var i: u8 = 0; var i: u8 = 0;
inline for (args, 1..) |field, k| { inline for (args, 1..) |field, k| {
try Lua.state.pushAny(field); try Lua.state.pushAny(field);
i = k; i = k;
} }
Lua.state.protectedCall(.{ .args = i, .results = 1 }) catch { Lua.state.protectedCall(.{ .args = i, .results = 1 }) catch {
RemoteLua.sendNewLogEntry(Lua.state.toString(-1) catch unreachable); RemoteLua.sendNewLogEntry(Lua.state.toString(-1) catch unreachable);
}; };
const ret = if (Lua.state.isBoolean(-1)) Lua.state.toBoolean(-1) else false; const ret = if (Lua.state.isBoolean(-1)) Lua.state.toBoolean(-1) else false;
Lua.state.pop(-1); Lua.state.pop(-1);
return ret; return ret;
} }
pub fn hash(modifier: wlr.Keyboard.ModifierMask, event_code: i32) u64 { pub fn hash(modifier: wlr.Keyboard.ModifierMask, event_code: i32) u64 {
const mod_val: u32 = @bitCast(modifier); const mod_val: u32 = @bitCast(modifier);
const button_val: u32 = @bitCast(event_code); const button_val: u32 = @bitCast(event_code);
return (@as(u64, mod_val) << 32) | @as(u64, button_val); return (@as(u64, mod_val) << 32) | @as(u64, button_val);
} }