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
insert_final_newline = true
[*.zig]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.lua]
indent_style = space
indent_size = 2

142
build.zig
View file

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

View file

@ -30,245 +30,198 @@ mode: enum { normal, drag } = .normal,
// Drag information
drag: ?struct {
event_code: u32,
start: struct {
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, }
},
event_code: u32,
start: struct { 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 {
errdefer Utils.oomPanic();
errdefer Utils.oomPanic();
self.* = .{
.wlr_cursor = try wlr.Cursor.create(),
.x_cursor_manager = try wlr.XcursorManager.create(null, 24),
.drag = null
};
self.* = .{
.wlr_cursor = try wlr.Cursor.create(),
.x_cursor_manager = try wlr.XcursorManager.create(null, 24),
.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_absolute.add(&self.motion_absolute);
self.wlr_cursor.events.button.add(&self.button);
self.wlr_cursor.events.axis.add(&self.axis);
self.wlr_cursor.events.frame.add(&self.frame);
self.wlr_cursor.events.hold_begin.add(&self.hold_begin);
self.wlr_cursor.events.hold_end.add(&self.hold_end);
self.wlr_cursor.events.motion.add(&self.motion);
self.wlr_cursor.events.motion_absolute.add(&self.motion_absolute);
self.wlr_cursor.events.button.add(&self.button);
self.wlr_cursor.events.axis.add(&self.axis);
self.wlr_cursor.events.frame.add(&self.frame);
self.wlr_cursor.events.hold_begin.add(&self.hold_begin);
self.wlr_cursor.events.hold_end.add(&self.hold_end);
}
pub fn deinit(self: *Cursor) void {
self.motion.link.remove();
self.motion_absolute.link.remove();
self.button.link.remove();
self.axis.link.remove();
self.frame.link.remove();
self.hold_begin.link.remove();
self.hold_end.link.remove();
self.motion.link.remove();
self.motion_absolute.link.remove();
self.button.link.remove();
self.axis.link.remove();
self.frame.link.remove();
self.hold_begin.link.remove();
self.hold_end.link.remove();
self.wlr_cursor.destroy();
self.x_cursor_manager.destroy();
self.wlr_cursor.destroy();
self.x_cursor_manager.destroy();
}
pub fn processCursorMotion(self: *Cursor, time_msec: u32) void {
var passthrough = true;
var passthrough = true;
if (self.mode == .drag) {
const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers();
if (self.mode == .drag) {
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
if (server.mousemaps.get(Mousemap.hash(modifiers, @bitCast(self.drag.?.event_code)))) |map| {
if(map.options.lua_drag_ref_idx > 0) {
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
}
});
}
// 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 (map.options.lua_drag_ref_idx > 0) {
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 } });
}
}
}
}
if(passthrough) {
const output = server.seat.focused_output;
// Exit the switch if no focused output exists
std.debug.assert(output != null);
if (passthrough) {
const output = server.seat.focused_output;
// Exit the switch if no focused output exists
std.debug.assert(output != null);
const surfaceAtResult = output.?.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y);
if (surfaceAtResult) |surface| {
if(surface.scene_node_data.* == .view) {
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))
});
}
const surfaceAtResult = output.?.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y);
if (surfaceAtResult) |surface| {
if (surface.scene_node_data.* == .view) {
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)) });
}
server.seat.wlr_seat.pointerNotifyEnter(surfaceAtResult.?.surface, surfaceAtResult.?.sx, surfaceAtResult.?.sy);
server.seat.wlr_seat.pointerNotifyMotion(time_msec, surfaceAtResult.?.sx, surfaceAtResult.?.sy);
} else {
// This may not be necessary, remove if no bugs
server.seat.wlr_seat.pointerClearFocus();
self.wlr_cursor.setXcursor(self.x_cursor_manager, "default");
server.seat.wlr_seat.pointerNotifyEnter(surfaceAtResult.?.surface, surfaceAtResult.?.sx, surfaceAtResult.?.sy);
server.seat.wlr_seat.pointerNotifyMotion(time_msec, surfaceAtResult.?.sx, surfaceAtResult.?.sy);
} else {
// This may not be necessary, remove if no bugs
server.seat.wlr_seat.pointerClearFocus();
self.wlr_cursor.setXcursor(self.x_cursor_manager, "default");
}
}
}
}
// --------- WLR Cursor event handlers ---------
fn handleMotion(
_: *wl.Listener(*wlr.Pointer.event.Motion),
event: *wlr.Pointer.event.Motion,
_: *wl.Listener(*wlr.Pointer.event.Motion),
event: *wlr.Pointer.event.Motion,
) void {
server.cursor.wlr_cursor.move(event.device, event.delta_x, event.delta_y);
server.cursor.processCursorMotion(event.time_msec);
server.cursor.wlr_cursor.move(event.device, event.delta_x, event.delta_y);
server.cursor.processCursorMotion(event.time_msec);
}
fn handleMotionAbsolute(
_: *wl.Listener(*wlr.Pointer.event.MotionAbsolute),
event: *wlr.Pointer.event.MotionAbsolute,
_: *wl.Listener(*wlr.Pointer.event.MotionAbsolute),
event: *wlr.Pointer.event.MotionAbsolute,
) void {
server.cursor.wlr_cursor.warpAbsolute(event.device, event.x, event.y);
server.cursor.processCursorMotion(event.time_msec);
server.cursor.wlr_cursor.warpAbsolute(event.device, event.x, event.y);
server.cursor.processCursorMotion(event.time_msec);
}
fn handleButton(
listener: *wl.Listener(*wlr.Pointer.event.Button),
event: *wlr.Pointer.event.Button
) void {
const cursor: *Cursor = @fieldParentPtr("button", listener);
fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), 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) {
.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; }
.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", .{});
},
}
}
// 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);
}
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) {
.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(
listener: *wl.Listener(*wlr.Pointer.event.HoldBegin),
event: *wlr.Pointer.event.HoldBegin
) void {
_ = listener;
_ = event;
std.log.err("Unimplemented cursor start hold", .{});
fn handleHoldBegin(listener: *wl.Listener(*wlr.Pointer.event.HoldBegin), event: *wlr.Pointer.event.HoldBegin) void {
_ = listener;
_ = event;
std.log.err("Unimplemented cursor start hold", .{});
}
fn handleHoldEnd(
listener: *wl.Listener(*wlr.Pointer.event.HoldEnd),
event: *wlr.Pointer.event.HoldEnd
) void {
_ = listener;
_ = event;
std.log.err("Unimplemented cursor end hold", .{});
fn handleHoldEnd(listener: *wl.Listener(*wlr.Pointer.event.HoldEnd), event: *wlr.Pointer.event.HoldEnd) void {
_ = listener;
_ = event;
std.log.err("Unimplemented cursor end hold", .{});
}
fn handleAxis(
_: *wl.Listener(*wlr.Pointer.event.Axis),
event: *wlr.Pointer.event.Axis,
_: *wl.Listener(*wlr.Pointer.event.Axis),
event: *wlr.Pointer.event.Axis,
) void {
server.seat.wlr_seat.pointerNotifyAxis(
event.time_msec,
event.orientation,
event.delta,
event.delta_discrete,
event.source,
event.relative_direction,
);
server.seat.wlr_seat.pointerNotifyAxis(
event.time_msec,
event.orientation,
event.delta,
event.delta_discrete,
event.source,
event.relative_direction,
);
}
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) {
.tree => "TREE",
.rect => "RECT",
.buffer => "BUFFER"
.buffer => "BUFFER",
};
writer.print("{s} @ ({d}, {d}) enabled={}", .{
type_name,
node.x,
node.y,
node.enabled
}) catch unreachable;
writer.print("{s} @ ({d}, {d}) enabled={}", .{ type_name, node.x, node.y, node.enabled }) catch unreachable;
// Add associated data if present
if (node.data) |data| {
@ -56,7 +51,7 @@ fn printNode(node: *wlr.SceneNode, depth: usize) void {
}) catch unreachable;
},
.output_layer => {
writer.print(" → Output Layer", .{}) catch unreachable;
writer.print(" → Output Layer", .{}) catch unreachable;
},
.view => |view| {
writer.print(" → View: id={} mapped={} focused={}", .{
@ -84,7 +79,7 @@ fn printNode(node: *wlr.SceneNode, depth: usize) void {
if (namespace.len > 0) {
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),
pub fn init(device: *wlr.InputDevice) *Keyboard {
const self = gpa.create(Keyboard) catch Utils.oomPanic();
const self = gpa.create(Keyboard) catch Utils.oomPanic();
errdefer {
std.log.err("Unable to initialize new keyboard, exiting", .{});
std.process.exit(6);
}
errdefer {
std.log.err("Unable to initialize new keyboard, exiting", .{});
std.process.exit(6);
}
self.* = .{
.context = xkb.Context.new(.no_flags) orelse return error.ContextFailed,
.wlr_keyboard = device.toKeyboard(),
.device = device,
};
self.* = .{
.context = xkb.Context.new(.no_flags) orelse return error.ContextFailed,
.wlr_keyboard = device.toKeyboard(),
.device = device,
};
// TODO: configure this via lua later
// Should handle this error here
if (!self.wlr_keyboard.setKeymap(server.seat.keymap)) return error.SetKeymapFailed;
self.wlr_keyboard.setRepeatInfo(25, 600);
// TODO: configure this via lua later
// Should handle this error here
if (!self.wlr_keyboard.setKeymap(server.seat.keymap)) return error.SetKeymapFailed;
self.wlr_keyboard.setRepeatInfo(25, 600);
self.wlr_keyboard.events.modifiers.add(&self.modifiers);
self.wlr_keyboard.events.key.add(&self.key);
self.wlr_keyboard.events.keymap.add(&self.key_map);
self.wlr_keyboard.events.modifiers.add(&self.modifiers);
self.wlr_keyboard.events.key.add(&self.key);
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)"});
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}", .{device.name orelse "(unnamed)"});
if (!server.seat.keyboard_group.wlr_group.addKeyboard(self.wlr_keyboard)) {
std.log.err("Adding new keyboard {s} failed", .{device.name orelse "(unnamed)"});
}
return self;
return self;
}
pub fn deinit (self: *Keyboard) void {
self.key.link.remove();
self.key_map.link.remove();
self.modifiers.link.remove();
pub fn deinit(self: *Keyboard) void {
self.key.link.remove();
self.key_map.link.remove();
self.modifiers.link.remove();
}
fn handleModifiers(_: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) void {
server.seat.wlr_seat.setKeyboard(wlr_keyboard);
server.seat.wlr_seat.keyboardNotifyModifiers(&wlr_keyboard.modifiers);
server.seat.wlr_seat.setKeyboard(wlr_keyboard);
server.seat.wlr_seat.keyboardNotifyModifiers(&wlr_keyboard.modifiers);
}
fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void {
const keyboard: *Keyboard = @fieldParentPtr("key", listener);
// Translate libinput keycode -> xkbcommon
const keycode = event.keycode + 8;
const keyboard: *Keyboard = @fieldParentPtr("key", listener);
// Translate libinput keycode -> xkbcommon
const keycode = event.keycode + 8;
var handled: bool = false;
const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers();
if (server.seat.keyboard_group.wlr_group.keyboard.xkb_state) |xkb_state| {
const keysyms = xkb_state.keyGetSyms(keycode);
for (keysyms) |sym| {
handled = keypress(modifiers, sym, event.state);
var handled: bool = false;
const modifiers = server.seat.keyboard_group.wlr_group.keyboard.getModifiers();
if (server.seat.keyboard_group.wlr_group.keyboard.xkb_state) |xkb_state| {
const keysyms = xkb_state.keyGetSyms(keycode);
for (keysyms) |sym| {
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) {
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;
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);
}
}
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(
modifiers: wlr.Keyboard.ModifierMask,
sym: xkb.Keysym,
state: wl.Keyboard.KeyState
) bool {
if (server.keymaps.get(Keymap.hash(modifiers, sym))) |map| {
if (state == .pressed and map.options.lua_press_ref_idx > 0) {
map.callback(false);
return true;
} else if (state == .released and map.options.lua_release_ref_idx > 0) {
map.callback(true);
return true;
pub fn keypress(modifiers: wlr.Keyboard.ModifierMask, sym: xkb.Keysym, state: wl.Keyboard.KeyState) bool {
if (server.keymaps.get(Keymap.hash(modifiers, sym))) |map| {
if (state == .pressed and map.options.lua_press_ref_idx > 0) {
map.callback(false);
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 {
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 {
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.key.link.remove();
keyboard.key_map.link.remove();
keyboard.destroy.link.remove();
keyboard.modifiers.link.remove();
keyboard.key.link.remove();
keyboard.key_map.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 Keyboard = @import("Keyboard.zig");
const Utils = @import("Utils.zig");
const Utils = @import("Utils.zig");
const server = &@import("main.zig").server;
const gpa = std.heap.c_allocator;
@ -17,47 +17,48 @@ modifiers: ?wlr.Keyboard.ModifierMask,
keysyms: ?[]const xkb.Keysym,
pub fn init() *KeyboardGroup {
errdefer Utils.oomPanic();
errdefer Utils.oomPanic();
const self = try gpa.create(KeyboardGroup);
self.* = .{
.wlr_group = wlr.KeyboardGroup.create() catch Utils.oomPanic(),
.repeat_source = blk: {
break :blk server.event_loop.addTimer(?*KeyboardGroup, handleRepeat, self) catch {
std.log.err("Failed to create event loop timer, keyboard repeating will not work!", .{});
break :blk null;
};
},
.modifiers = null,
.keysyms = null,
};
const self = try gpa.create(KeyboardGroup);
self.* = .{
.wlr_group = wlr.KeyboardGroup.create() catch Utils.oomPanic(),
.repeat_source = blk: {
break :blk server.event_loop.addTimer(?*KeyboardGroup, handleRepeat, self) catch {
std.log.err("Failed to create event loop timer, keyboard repeating will not work!", .{});
break :blk null;
};
},
.modifiers = null,
.keysyms = null,
};
return self;
return self;
}
pub fn deinit(self: *KeyboardGroup) void {
self.wlr_group.destroy();
gpa.destroy(self);
self.wlr_group.destroy();
gpa.destroy(self);
}
fn handleRepeat(data: ?*KeyboardGroup) c_int {
// we failed to create the event loop timer, which means we can't repeat keys
if (data.?.repeat_source == null) return 0;
// we failed to create the event loop timer, which means we can't repeat keys
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;
}
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),
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.* = .{
.output = undefined,
.wlr_layer_surface = wlr_layer_surface,
.scene_layer_surface = undefined,
.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.* = .{
.output = undefined,
.wlr_layer_surface = wlr_layer_surface,
.scene_layer_surface = undefined,
.scene_node_data = .{ .layer_surface = self },
};
}
self.wlr_layer_surface.surface.data = &self.scene_node_data;
self.scene_layer_surface.tree.node.data = &self.scene_node_data;
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));
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);
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;
},
};
}
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 {
self.destroy.link.remove();
self.map.link.remove();
self.unmap.link.remove();
self.commit.link.remove();
self.destroy.link.remove();
self.map.link.remove();
self.unmap.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 {
const keyboard_interactive = self.wlr_layer_surface.current.keyboard_interactive;
if(keyboard_interactive == .exclusive or keyboard_interactive == .on_demand) {
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
);
}
const keyboard_interactive = self.wlr_layer_surface.current.keyboard_interactive;
if (keyboard_interactive == .exclusive or keyboard_interactive == .on_demand) {
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);
}
}
// --------- LayerSurface event handlers ---------
fn handleDestroy(
listener: *wl.Listener(*wlr.LayerSurfaceV1),
_: *wlr.LayerSurfaceV1
) void {
const layer: *LayerSurface = @fieldParentPtr("destroy", listener);
layer.deinit();
fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), _: *wlr.LayerSurfaceV1) void {
const layer: *LayerSurface = @fieldParentPtr("destroy", listener);
layer.deinit();
}
fn handleMap(
listener: *wl.Listener(void)
) void {
const layer_suraface: *LayerSurface = @fieldParentPtr("map", listener);
std.log.debug("layer surface mapped", .{});
layer_suraface.output.arrangeLayers();
layer_suraface.allowKeyboard();
fn handleMap(listener: *wl.Listener(void)) void {
const layer_suraface: *LayerSurface = @fieldParentPtr("map", listener);
std.log.debug("layer surface mapped", .{});
layer_suraface.output.arrangeLayers();
layer_suraface.allowKeyboard();
}
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 (fs == .layer_surface and fs.layer_surface == layer_surface) {
server.seat.focusSurface(null);
if (server.seat.focused_surface) |fs| {
if (fs == .layer_surface and fs.layer_surface == layer_surface) {
server.seat.focusSurface(null);
}
}
}
// FIXME: this crashes mez when killing mez
layer_surface.output.arrangeLayers();
// FIXME: this crashes mez when killing mez
layer_surface.output.arrangeLayers();
// TODO: Idk if this should be deiniting the layer surface entirely
layer_surface.deinit();
// TODO: Idk if this should be deiniting the layer surface entirely
layer_surface.deinit();
}
fn handleCommit(
listener: *wl.Listener(*wlr.Surface),
_: *wlr.Surface
) void {
const layer_surface: *LayerSurface = @fieldParentPtr("commit", listener);
fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const layer_surface: *LayerSurface = @fieldParentPtr("commit", listener);
if (!layer_surface.wlr_layer_surface.initial_commit) return;
layer_surface.output.arrangeLayers();
if (!layer_surface.wlr_layer_surface.initial_commit) return;
layer_surface.output.arrangeLayers();
}

View file

@ -5,10 +5,10 @@ const zwlr = @import("wayland").server.zwlr;
const wlr = @import("wlroots");
const std = @import("std");
const Utils = @import("Utils.zig");
const Utils = @import("Utils.zig");
const Server = @import("Server.zig");
const View = @import("View.zig");
const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
@ -28,168 +28,167 @@ scene_node_data: SceneNodeData,
scene_output: *wlr.SceneOutput,
layers: struct {
background: *wlr.SceneTree,
bottom: *wlr.SceneTree,
content: *wlr.SceneTree,
top: *wlr.SceneTree,
fullscreen: *wlr.SceneTree,
overlay: *wlr.SceneTree
background: *wlr.SceneTree,
bottom: *wlr.SceneTree,
content: *wlr.SceneTree,
top: *wlr.SceneTree,
fullscreen: *wlr.SceneTree,
overlay: *wlr.SceneTree,
},
layer_scene_node_data: struct {
background: SceneNodeData,
bottom: SceneNodeData,
content: SceneNodeData,
top: SceneNodeData,
fullscreen: SceneNodeData,
overlay: SceneNodeData
background: SceneNodeData,
bottom: SceneNodeData,
content: SceneNodeData,
top: SceneNodeData,
fullscreen: SceneNodeData,
overlay: SceneNodeData,
},
frame: wl.Listener(*wlr.Output) = .init(handleFrame),
request_state: wl.Listener(*wlr.Output.event.RequestState) = .init(handleRequestState),
destroy: wl.Listener(*wlr.Output) = .init(handleDestroy),
// The wlr.Output should be destroyed by the caller on failure to trigger cleanup.
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.* = .{
.focused = false,
.id = @intFromPtr(wlr_output),
.wlr_output = wlr_output,
.tree = try server.root.scene.tree.createSceneTree(),
.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()
};
self.* = .{
.focused = false,
.id = @intFromPtr(wlr_output),
.wlr_output = wlr_output,
.tree = try server.root.scene.tree.createSceneTree(),
.fullscreen = null,
wlr_output.events.frame.add(&self.frame);
wlr_output.events.request_state.add(&self.request_state);
wlr_output.events.destroy.add(&self.destroy);
.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(),
},
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)) {
std.log.err("Unable to start output {s}", .{wlr_output.name});
return null;
}
.scene_output = try server.root.scene.createSceneOutput(wlr_output),
.scene_node_data = SceneNodeData{ .output = self },
.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| {
self.state.setMode(mode);
}
errdefer deinit(self);
if(!wlr_output.commitState(&self.state)) {
std.log.err("Unable to commit state to output {s}", .{wlr_output.name});
return null;
}
if (!wlr_output.initRender(server.allocator, server.renderer)) {
std.log.err("Unable to start output {s}", .{wlr_output.name});
return null;
}
// 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();
self.state.setEnabled(true);
self.wlr_output.data = self;
self.tree.node.data = &self.scene_node_data;
if (wlr_output.preferredMode()) |mode| {
self.state.setMode(mode);
}
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;
if (!wlr_output.commitState(&self.state)) {
std.log.err("Unable to commit state to output {s}", .{wlr_output.name});
return null;
}
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 {
server.events.exec("OutputDeinitPre", .{self.id});
server.events.exec("OutputDeinitPre", .{self.id});
self.frame.link.remove();
self.request_state.link.remove();
self.destroy.link.remove();
self.frame.link.remove();
self.request_state.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 {
if(server.seat.focused_output) |prev_output| {
prev_output.focused = false;
}
if (server.seat.focused_output) |prev_output| {
prev_output.focused = false;
}
server.seat.focused_output = self;
self.focused = true;
server.seat.focused_output = self;
self.focused = true;
}
pub fn configureLayers(self: *Output) void {
var output_box: wlr.Box = .{
.x = 0,
.y = 0,
.width = 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))];
var output_box: wlr.Box = .{
.x = 0,
.y = 0,
.width = undefined,
.height = undefined,
};
self.wlr_output.effectiveResolution(&output_box.width, &output_box.height);
var it = tree.children.iterator(.forward);
while(it.next()) |node| {
if(node.data == null) continue;
// 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))];
};
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
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", .{});
unreachable;
var it = tree.children.iterator(.forward);
while (it.next()) |node| {
if (node.data == null) continue;
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
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", .{});
unreachable;
},
}
}
}
}
}
}
const SurfaceAtResult = struct {
@ -200,165 +199,152 @@ const SurfaceAtResult = struct {
};
pub fn surfaceAt(self: *Output, lx: f64, ly: f64) ?SurfaceAtResult {
var sx: f64 = undefined;
var sy: f64 = undefined;
var sx: f64 = undefined;
var sy: f64 = undefined;
const layers = [_]*wlr.SceneTree{
self.layers.top,
self.layers.overlay,
self.layers.fullscreen,
self.layers.content,
self.layers.bottom,
self.layers.background
};
const layers = [_]*wlr.SceneTree{ self.layers.top, self.layers.overlay, self.layers.fullscreen, self.layers.content, self.layers.bottom, self.layers.background };
for(layers) |layer| {
const node = layer.node.at(lx, ly, &sx, &sy);
if(node == null) continue;
for (layers) |layer| {
const node = layer.node.at(lx, ly, &sx, &sy);
if (node == null) continue;
const surface: ?*wlr.Surface = blk: {
if (node.?.type == .buffer) {
const scene_buffer = wlr.SceneBuffer.fromNode(node.?);
if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| {
break :blk scene_surface.surface;
}
}
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,
const surface: ?*wlr.Surface = blk: {
if (node.?.type == .buffer) {
const scene_buffer = wlr.SceneBuffer.fromNode(node.?);
if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| {
break :blk scene_surface.surface;
}
}
break :blk null;
};
},
else => continue
}
}
if (surface == null) 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 {
if(self.layers.fullscreen.children.length() != 1) {
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;
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;
}
// --------- WlrOutput Event Handlers ---------
fn handleRequestState(
listener: *wl.Listener(*wlr.Output.event.RequestState),
event: *wlr.Output.event.RequestState,
listener: *wl.Listener(*wlr.Output.event.RequestState),
event: *wlr.Output.event.RequestState,
) void {
const output: *Output = @fieldParentPtr("request_state", listener);
const output: *Output = @fieldParentPtr("request_state", listener);
if (!output.wlr_output.commitState(event.state)) {
std.log.warn("failed to set output state {}", .{event.state});
}
if (!output.wlr_output.commitState(event.state)) {
std.log.warn("failed to set output state {}", .{event.state});
}
server.events.exec("OutputStateChange", .{});
server.events.exec("OutputStateChange", .{});
}
fn handleFrame(
_: *wl.Listener(*wlr.Output),
wlr_output: *wlr.Output
) void {
const scene_output = server.root.scene.getSceneOutput(wlr_output);
fn handleFrame(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
const scene_output = server.root.scene.getSceneOutput(wlr_output);
if(scene_output == null) {
std.log.err("Unable to get scene output to render", .{});
return;
}
if (scene_output == null) {
std.log.err("Unable to get scene output to render", .{});
return;
}
// std.log.info("Rendering commited scene output\n", .{});
_ = scene_output.?.commit(null);
// std.log.info("Rendering commited scene output\n", .{});
_ = scene_output.?.commit(null);
var now = posix.clock_gettime(posix.CLOCK.MONOTONIC) catch @panic("CLOCK_MONOTONIC not supported");
scene_output.?.sendFrameDone(&now);
var now = posix.clock_gettime(posix.CLOCK.MONOTONIC) catch @panic("CLOCK_MONOTONIC not supported");
scene_output.?.sendFrameDone(&now);
}
fn handleDestroy(
listener: *wl.Listener(*wlr.Output),
_: *wlr.Output
) void {
std.log.debug("Handling destroy", .{});
const output: *Output = @fieldParentPtr("destroy", listener);
fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) 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.request_state.link.remove();
output.destroy.link.remove();
output.frame.link.remove();
output.request_state.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 {
var full_box: wlr.Box = .{
.x = 0,
.y = 0,
.width = undefined,
.height = undefined,
};
self.wlr_output.effectiveResolution(&full_box.width, &full_box.height);
var full_box: wlr.Box = .{
.x = 0,
.y = 0,
.width = undefined,
.height = undefined,
};
self.wlr_output.effectiveResolution(&full_box.width, &full_box.height);
inline for (@typeInfo(zwlr.LayerShellV1.Layer).@"enum".fields) |comptime_layer| {
const layer: *wlr.SceneTree = @field(self.layers, comptime_layer.name);
var it = layer.children.safeIterator(.forward);
inline for (@typeInfo(zwlr.LayerShellV1.Layer).@"enum".fields) |comptime_layer| {
const layer: *wlr.SceneTree = @field(self.layers, comptime_layer.name);
var it = layer.children.safeIterator(.forward);
while (it.next()) |node| {
if(node.data == null) continue;
while (it.next()) |node| {
if (node.data == null) continue;
// if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| {
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
// if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| {
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
const layer_surface: *LayerSurface = switch(scene_node_data.*) {
.layer_surface => @fieldParentPtr("scene_node_data", scene_node_data),
else => continue
};
const layer_surface: *LayerSurface = switch (scene_node_data.*) {
.layer_surface => @fieldParentPtr("scene_node_data", scene_node_data),
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
// 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
// exclusive focus isn't even that bad.
// TEST: river seems to try and prevent clients from taking an
// 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
// 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?
// const x = layer_surface.scene_layer_surface.tree.node.x;
// const y = layer_surface.scene_layer_surface.tree.node.y;
// layer_surface.scene_layer_surface.tree.node.setPosition(x, y);
// TEST: are these calls useless?
// const x = layer_surface.scene_layer_surface.tree.node.x;
// const y = layer_surface.scene_layer_surface.tree.node.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),
pub fn init(
xdg_popup: *wlr.XdgPopup,
parent: *wlr.SceneTree,
xdg_popup: *wlr.XdgPopup,
parent: *wlr.SceneTree,
) *Popup {
errdefer Utils.oomPanic();
errdefer Utils.oomPanic();
const self = try gpa.create(Popup);
errdefer gpa.destroy(self);
const self = try gpa.create(Popup);
errdefer gpa.destroy(self);
self.* = .{
.id = @intFromPtr(xdg_popup),
.xdg_popup = xdg_popup,
.tree = try parent.createSceneXdgSurface(xdg_popup.base),
};
self.* = .{
.id = @intFromPtr(xdg_popup),
.xdg_popup = xdg_popup,
.tree = try parent.createSceneXdgSurface(xdg_popup.base),
};
xdg_popup.events.destroy.add(&self.destroy);
xdg_popup.base.surface.events.commit.add(&self.commit);
xdg_popup.base.events.new_popup.add(&self.new_popup);
xdg_popup.events.reposition.add(&self.reposition);
xdg_popup.events.destroy.add(&self.destroy);
xdg_popup.base.surface.events.commit.add(&self.commit);
xdg_popup.base.events.new_popup.add(&self.new_popup);
xdg_popup.events.reposition.add(&self.reposition);
return self;
return self;
}
fn handleDestroy(listener: *wl.Listener(void)) void {
const popup: *Popup = @fieldParentPtr("destroy", listener);
const popup: *Popup = @fieldParentPtr("destroy", listener);
popup.destroy.link.remove();
popup.commit.link.remove();
popup.new_popup.link.remove();
popup.reposition.link.remove();
popup.destroy.link.remove();
popup.commit.link.remove();
popup.new_popup.link.remove();
popup.reposition.link.remove();
gpa.destroy(popup);
gpa.destroy(popup);
}
fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const popup: *Popup = @fieldParentPtr("commit", listener);
if (popup.xdg_popup.base.initial_commit) {
handleReposition(&popup.reposition);
}
const popup: *Popup = @fieldParentPtr("commit", listener);
if (popup.xdg_popup.base.initial_commit) {
handleReposition(&popup.reposition);
}
}
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 {
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
// current monitor
//
// if (SceneNodeData.getFromNode(&popup.tree.node)) |node| {
// const output = switch (node.data) {
// .view => |view| view.output orelse return,
// .layer_surface => |layer_surface| layer_surface.output,
// };
//
// server.root.output_layout.getBox(output.wlr_output, &box);
// }
// TODO: figure this out to prevent popups from rendering outside of the
// current monitor
//
// if (SceneNodeData.getFromNode(&popup.tree.node)) |node| {
// const output = switch (node.data) {
// .view => |view| view.output orelse return,
// .layer_surface => |layer_surface| layer_surface.output,
// };
//
// server.root.output_layout.getBox(output.wlr_output, &box);
// }
var root_lx: c_int = undefined;
var root_ly: c_int = undefined;
_ = popup.tree.node.coords(&root_lx, &root_ly);
var root_lx: c_int = undefined;
var root_ly: c_int = undefined;
_ = popup.tree.node.coords(&root_lx, &root_ly);
box.x -= root_lx;
box.y -= root_ly;
box.x -= root_lx;
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,
pub fn sendNewLogEntry(str: [*:0]const u8) void {
var node = server.remote_lua_clients.first;
while (node) |n| {
const data: ?*RemoteLua = @fieldParentPtr("node", n);
if (data) |d| d.remote_lua_v1.sendNewLogEntry(str);
node = n.next;
}
var node = server.remote_lua_clients.first;
while (node) |n| {
const data: ?*RemoteLua = @fieldParentPtr("node", n);
if (data) |d| d.remote_lua_v1.sendNewLogEntry(str);
node = n.next;
}
}
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);
errdefer gpa.destroy(node);
node.* = .{
.remote_lua_v1 = remote_lua_v1,
.node = .{},
.L = try zlua.Lua.init(gpa),
};
errdefer node.L.deinit();
node.L.openLibs();
Lua.openLibs(node.L);
Lua.loadRuntimeDir(node.L) catch |err| if (err == error.LuaRuntime) {
std.log.warn("{s}", .{try node.L.toString(-1)});
};
// TODO: replace stdout and stderr with buffers we can send to the clients
const node = try gpa.create(RemoteLua);
errdefer gpa.destroy(node);
node.* = .{
.remote_lua_v1 = remote_lua_v1,
.node = .{},
.L = try zlua.Lua.init(gpa),
};
errdefer node.L.deinit();
node.L.openLibs();
Lua.openLibs(node.L);
Lua.loadRuntimeDir(node.L) catch |err| if (err == error.LuaRuntime) {
std.log.warn("{s}", .{try node.L.toString(-1)});
};
// 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(
remote_lua_v1: *mez.RemoteLuaV1,
request: mez.RemoteLuaV1.Request,
remote: *RemoteLua,
remote_lua_v1: *mez.RemoteLuaV1,
request: mez.RemoteLuaV1.Request,
remote: *RemoteLua,
) void {
const L = remote.L;
switch (request) {
.destroy => remote_lua_v1.destroy(),
.push_lua => |req| {
const chunk: [:0]const u8 = std.mem.sliceTo(req.lua_chunk, 0);
const L = remote.L;
switch (request) {
.destroy => remote_lua_v1.destroy(),
.push_lua => |req| {
const chunk: [:0]const u8 = std.mem.sliceTo(req.lua_chunk, 0);
const str = std.mem.concatWithSentinel(gpa, u8, &[_][]const u8{
"return ",
chunk,
";",
}, 0) catch return catchLuaFail(remote);
defer gpa.free(str);
const str = std.mem.concatWithSentinel(gpa, u8, &[_][]const u8{
"return ",
chunk,
";",
}, 0) catch return catchLuaFail(remote);
defer gpa.free(str);
zlua.Lua.loadBuffer(L, str, "=repl") catch {
L.pop(L.getTop());
L.loadString(chunk) catch {
catchLuaFail(remote);
L.pop(-1);
};
return;
};
zlua.Lua.loadBuffer(L, str, "=repl") catch {
L.pop(L.getTop());
L.loadString(chunk) catch {
catchLuaFail(remote);
L.pop(-1);
};
return;
};
L.protectedCall(.{ .results = zlua.mult_return, }) catch {
catchLuaFail(remote);
L.pop(1);
};
L.protectedCall(.{
.results = zlua.mult_return,
}) catch {
catchLuaFail(remote);
L.pop(1);
};
var i: i32 = 1;
const nresults = L.getTop();
while (i <= nresults) : (i += 1) {
sendNewLogEntry(LuaUtils.toStringEx(L));
L.pop(-1);
}
},
}
var i: i32 = 1;
const nresults = L.getTop();
while (i <= nresults) : (i += 1) {
sendNewLogEntry(LuaUtils.toStringEx(L));
L.pop(-1);
}
},
}
}
fn handleDestroy(_: *mez.RemoteLuaV1, remote_lua: *RemoteLua) void {
if (remote_lua.node.prev) |p| {
if (remote_lua.node.next) |n| n.prev.? = p;
p.next = remote_lua.node.next;
} else server.remote_lua_clients.first = remote_lua.node.next;
if (remote_lua.node.prev) |p| {
if (remote_lua.node.next) |n| n.prev.? = p;
p.next = remote_lua.node.next;
} else server.remote_lua_clients.first = remote_lua.node.next;
remote_lua.L.deinit();
gpa.destroy(remote_lua);
remote_lua.L.deinit();
gpa.destroy(remote_lua);
}
fn catchLuaFail(remote: *RemoteLua) void {
const err: [:0]const u8 = LuaUtils.toStringEx(remote.L);
sendNewLogEntry(std.mem.sliceTo(err, 0));
const err: [:0]const u8 = LuaUtils.toStringEx(remote.L);
sendNewLogEntry(std.mem.sliceTo(err, 0));
}

View file

@ -13,37 +13,37 @@ const server = &@import("main.zig").server;
global: *wl.Global,
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 {
const remote_lua_manager_v1 = mez.RemoteLuaManagerV1.create(client, version, id) catch {
client.postNoMemory();
Utils.oomPanic();
};
remote_lua_manager_v1.setHandler(?*anyopaque, handleRequest, null, null);
const remote_lua_manager_v1 = mez.RemoteLuaManagerV1.create(client, version, id) catch {
client.postNoMemory();
Utils.oomPanic();
};
remote_lua_manager_v1.setHandler(?*anyopaque, handleRequest, null, null);
}
fn handleRequest(
remote_lua_manager_v1: *mez.RemoteLuaManagerV1,
request: mez.RemoteLuaManagerV1.Request,
_: ?*anyopaque,
remote_lua_manager_v1: *mez.RemoteLuaManagerV1,
request: mez.RemoteLuaManagerV1.Request,
_: ?*anyopaque,
) void {
switch (request) {
.destroy => remote_lua_manager_v1.destroy(),
.get_remote => |req| {
RemoteLua.create(
remote_lua_manager_v1.getClient(),
remote_lua_manager_v1.getVersion(),
req.id,
) catch {
remote_lua_manager_v1.getClient().postNoMemory();
Utils.oomPanic();
};
},
}
switch (request) {
.destroy => remote_lua_manager_v1.destroy(),
.get_remote => |req| {
RemoteLua.create(
remote_lua_manager_v1.getClient(),
remote_lua_manager_v1.getVersion(),
req.id,
) catch {
remote_lua_manager_v1.getClient().postNoMemory();
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:
const Root = @This();
const std = @import("std");
@ -26,92 +25,92 @@ scene_output_layout: *wlr.SceneOutputLayout,
output_layout: *wlr.OutputLayout,
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);
errdefer output_layout.destroy();
const output_layout = try wlr.OutputLayout.create(server.wl_server);
errdefer output_layout.destroy();
const scene = try wlr.Scene.create();
errdefer scene.tree.node.destroy();
const scene = try wlr.Scene.create();
errdefer scene.tree.node.destroy();
self.* = .{
.scene = scene,
.scene_node_data = .{ .root = self },
.output_layout = output_layout,
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server),
.scene_output_layout = try scene.attachOutputLayout(output_layout),
};
self.* = .{
.scene = scene,
.scene_node_data = .{ .root = self },
.output_layout = output_layout,
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server),
.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 {
var it = self.scene.tree.children.iterator(.forward);
var it = self.scene.tree.children.iterator(.forward);
while(it.next()) |node| {
if(node.data == null) continue;
while (it.next()) |node| {
if (node.data == null) continue;
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
switch(scene_node_data.*) {
.output => {
scene_node_data.output.deinit();
},
else => {
std.log.debug("The root has a child that is not an output", .{});
unreachable;
}
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
switch (scene_node_data.*) {
.output => {
scene_node_data.output.deinit();
},
else => {
std.log.debug("The root has a child that is not an output", .{});
unreachable;
},
}
}
}
self.output_layout.destroy();
self.scene.tree.node.destroy();
self.output_layout.destroy();
self.scene.tree.node.destroy();
}
// Search output_layout's ouputs, and each outputs views
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| {
if(o.output.data == null) {
std.log.err("Wlr_output arbitrary data not assigned", .{});
unreachable;
while (output_it.next()) |o| {
if (o.output.data == null) {
std.log.err("Wlr_output arbitrary data not assigned", .{});
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.?));
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;
return null;
}
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| {
if(scene_output.output.data == null) continue;
while (it.next()) |scene_output| {
if (scene_output.output.data == null) continue;
const output: *Output = @as(*Output, @ptrCast(@alignCast(scene_output.output.data.?)));
if(output.id == id) return output;
}
const output: *Output = @as(*Output, @ptrCast(@alignCast(scene_output.output.data.?)));
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 Root = @import("Root.zig");
pub const SceneNodeData = union(enum) {
view: *View,
layer_surface: *LayerSurface,
output: *Output,
output_layer: *wlr.SceneTree,
root: *Root
};
pub const SceneNodeData = union(enum) { 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 KeyboardGroup = @import("KeyboardGroup.zig");
const Utils = @import("Utils.zig");
const Utils = @import("Utils.zig");
const Popup = @import("Popup.zig");
const View = @import("View.zig");
const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
const server = &@import("main.zig").server;
pub const FocusData = union(enum) {
view: *View,
layer_surface: *LayerSurface,
view: *View,
layer_surface: *LayerSurface,
pub fn getSurface(self: FocusData) *wlr.Surface {
return switch (self) {
.view => |*v| v.*.xdg_toplevel.base.surface,
.layer_surface => |*ls| ls.*.wlr_layer_surface.surface,
};
}
pub fn getSurface(self: FocusData) *wlr.Surface {
return switch (self) {
.view => |*v| v.*.xdg_toplevel.base.surface,
.layer_surface => |*ls| ls.*.wlr_layer_surface.surface,
};
}
};
wlr_seat: *wlr.Seat,
@ -40,144 +40,139 @@ request_set_primary_selection: wl.Listener(*wlr.Seat.event.RequestSetPrimarySele
// request_start_drage
pub fn init(self: *Seat) void {
errdefer Utils.oomPanic();
errdefer Utils.oomPanic();
const xkb_context = xkb.Context.new(.no_flags) orelse {
std.log.err("Unable to create a xkb context, exiting", .{});
std.process.exit(7);
};
defer xkb_context.unref();
const xkb_context = xkb.Context.new(.no_flags) orelse {
std.log.err("Unable to create a xkb context, exiting", .{});
std.process.exit(7);
};
defer xkb_context.unref();
const keymap = xkb.Keymap.newFromNames(xkb_context, null, .no_flags) orelse {
std.log.err("Unable to create a xkb keymap, exiting", .{});
std.process.exit(8);
};
defer keymap.unref();
const keymap = xkb.Keymap.newFromNames(xkb_context, null, .no_flags) orelse {
std.log.err("Unable to create a xkb keymap, exiting", .{});
std.process.exit(8);
};
defer keymap.unref();
self.* = .{
.wlr_seat = try wlr.Seat.create(server.wl_server, "default"),
.focused_surface = null,
.focused_output = null,
.keyboard_group = .init(),
.keymap = keymap.ref(),
};
errdefer {
self.keyboard_group.deinit();
self.wlr_seat.destroy();
}
self.* = .{
.wlr_seat = try wlr.Seat.create(server.wl_server, "default"),
.focused_surface = null,
.focused_output = null,
.keyboard_group = .init(),
.keymap = keymap.ref(),
};
errdefer {
self.keyboard_group.deinit();
self.wlr_seat.destroy();
}
_ = self.keyboard_group.wlr_group.keyboard.setKeymap(self.keymap);
self.wlr_seat.setKeyboard(&self.keyboard_group.wlr_group.keyboard);
_ = self.keyboard_group.wlr_group.keyboard.setKeymap(self.keymap);
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_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_cursor.add(&self.request_set_cursor);
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);
}
pub fn deinit(self: *Seat) void {
self.request_set_cursor.link.remove();
self.request_set_selection.link.remove();
self.request_set_primary_selection.link.remove();
self.request_set_cursor.link.remove();
self.request_set_selection.link.remove();
self.request_set_primary_selection.link.remove();
self.keyboard_group.deinit();
self.wlr_seat.destroy();
self.keyboard_group.deinit();
self.wlr_seat.destroy();
}
pub fn focusSurface(self: *Seat, to_focus: ?FocusData) void {
const surface: ?*wlr.Surface = blk: {
if (to_focus != null) {
break :blk to_focus.?.getSurface();
} else {
break :blk null;
}
};
const surface: ?*wlr.Surface = blk: {
if (to_focus != null) {
break :blk to_focus.?.getSurface();
} else {
break :blk null;
}
};
// Remove focus from the current surface unless:
// - current and to focus are the same surface
// - current layer has exclusive keyboard interactivity
// - current is fullscreen and to focus is content
// - current is fullscreen and layer is bottom or background
if (self.focused_surface) |current_focus| {
const current_surface = current_focus.getSurface();
if (current_surface == surface) return; // Same surface
// Remove focus from the current surface unless:
// - current and to focus are the same surface
// - current layer has exclusive keyboard interactivity
// - current is fullscreen and to focus is content
// - current is fullscreen and layer is bottom or background
if (self.focused_surface) |current_focus| {
const current_surface = current_focus.getSurface();
if (current_surface == surface) return; // Same surface
if(to_focus != null) {
switch (current_focus) {
.layer_surface => |*current_layer_surface| {
if(current_layer_surface.*.wlr_layer_surface.current.keyboard_interactive == .exclusive) return;
},
.view => |*current_view| {
if(current_view.*.fullscreen) {
switch (to_focus.?) {
.layer_surface => |*layer_surface| {
const layer = layer_surface.*.wlr_layer_surface.current.layer;
if(layer == .background or layer == .bottom) return;
},
.view => |*view| {
if(!view.*.fullscreen) return;
}
if (to_focus != null) {
switch (current_focus) {
.layer_surface => |*current_layer_surface| {
if (current_layer_surface.*.wlr_layer_surface.current.keyboard_interactive == .exclusive) return;
},
.view => |*current_view| {
if (current_view.*.fullscreen) {
switch (to_focus.?) {
.layer_surface => |*layer_surface| {
const layer = layer_surface.*.wlr_layer_surface.current.layer;
if (layer == .background or layer == .bottom) return;
},
.view => |*view| {
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
switch (current_focus) {
.layer_surface => {}, // IDK if we actually have to clear any focus here
else => {
if(wlr.XdgSurface.tryFromWlrSurface(current_surface)) |xdg_surface| {
_ = xdg_surface.role_data.toplevel.?.setActivated(false);
// Clear the focus if applicable
switch (current_focus) {
.layer_surface => {}, // IDK if we actually have to clear any focus here
else => {
if (wlr.XdgSurface.tryFromWlrSurface(current_surface)) |xdg_surface| {
_ = xdg_surface.role_data.toplevel.?.setActivated(false);
}
},
}
}
}
}
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
);
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);
}
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);
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 {
if(server.seat.focused_output) |prev_output| {
prev_output.focused = false;
}
if (server.seat.focused_output) |prev_output| {
prev_output.focused = false;
}
self.focused_output = output;
self.focused_output = output;
}
fn handleRequestSetCursor(
_: *wl.Listener(*wlr.Seat.event.RequestSetCursor),
event: *wlr.Seat.event.RequestSetCursor,
_: *wl.Listener(*wlr.Seat.event.RequestSetCursor),
event: *wlr.Seat.event.RequestSetCursor,
) void {
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);
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);
}
fn handleRequestSetSelection(
_: *wl.Listener(*wlr.Seat.event.RequestSetSelection),
event: *wlr.Seat.event.RequestSetSelection,
_: *wl.Listener(*wlr.Seat.event.RequestSetSelection),
event: *wlr.Seat.event.RequestSetSelection,
) void {
server.seat.wlr_seat.setSelection(event.source, event.serial);
server.seat.wlr_seat.setSelection(event.source, event.serial);
}
fn handleRequestSetPrimarySelection(
_: *wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection),
event: *wlr.Seat.event.RequestSetPrimarySelection,
_: *wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection),
event: *wlr.Seat.event.RequestSetPrimarySelection,
) 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 wlr = @import("wlroots");
const Root = @import("Root.zig");
const Seat = @import("Seat.zig");
const Cursor = @import("Cursor.zig");
const Keyboard = @import("Keyboard.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
const View = @import("View.zig");
const Keymap = @import("types/Keymap.zig");
const Mousemap = @import("types/Mousemap.zig");
const Hook = @import("types/Hook.zig");
const Events = @import("types/Events.zig");
const Popup = @import("Popup.zig");
const RemoteLua = @import("RemoteLua.zig");
const Root = @import("Root.zig");
const Seat = @import("Seat.zig");
const Cursor = @import("Cursor.zig");
const Keyboard = @import("Keyboard.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
const View = @import("View.zig");
const Keymap = @import("types/Keymap.zig");
const Mousemap = @import("types/Mousemap.zig");
const Hook = @import("types/Hook.zig");
const Events = @import("types/Events.zig");
const Popup = @import("Popup.zig");
const RemoteLua = @import("RemoteLua.zig");
const RemoteLuaManager = @import("RemoteLuaManager.zig");
const Utils = @import("Utils.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
const Utils = @import("Utils.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
const gpa = std.heap.c_allocator;
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),
pub fn init(self: *Server) void {
errdefer Utils.oomPanic();
errdefer Utils.oomPanic();
const wl_server = wl.Server.create() catch {
std.log.err("Server create failed, exiting with 2", .{});
std.process.exit(2);
};
const wl_server = wl.Server.create() catch {
std.log.err("Server create failed, exiting with 2", .{});
std.process.exit(2);
};
const event_loop = wl_server.getEventLoop();
const event_loop = wl_server.getEventLoop();
var session: ?*wlr.Session = undefined;
const backend = wlr.Backend.autocreate(event_loop, &session) catch {
std.log.err("Backend create failed, exiting with 3", .{});
std.process.exit(3);
};
var session: ?*wlr.Session = undefined;
const backend = wlr.Backend.autocreate(event_loop, &session) catch {
std.log.err("Backend create failed, exiting with 3", .{});
std.process.exit(3);
};
const renderer = wlr.Renderer.autocreate(backend) catch {
std.log.err("Renderer create failed, exiting with 4", .{});
std.process.exit(4);
};
const renderer = wlr.Renderer.autocreate(backend) catch {
std.log.err("Renderer create failed, exiting with 4", .{});
std.process.exit(4);
};
self.* = .{
.wl_server = wl_server,
.backend = backend,
.renderer = renderer,
.allocator = wlr.Allocator.autocreate(backend, renderer) catch {
std.log.err("Allocator create failed, exiting with 5", .{});
std.process.exit(5);
},
.xdg_shell = try wlr.XdgShell.create(wl_server, 2),
.layer_shell = try wlr.LayerShellV1.create(wl_server, 4),
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(self.wl_server),
.xdg_activation = try wlr.XdgActivationV1.create(self.wl_server),
.event_loop = event_loop,
.session = session,
.compositor = try wlr.Compositor.create(wl_server, 6, renderer),
.shm = try wlr.Shm.createWithRenderer(wl_server, 1, renderer),
// TODO: let the user configure a cursor theme and side lua
.root = undefined,
.seat = undefined,
.cursor = undefined,
.remote_lua_manager = RemoteLuaManager.init() catch Utils.oomPanic(),
.keymaps = .init(gpa),
.mousemaps = .init(gpa),
.hooks = .init(gpa),
.events = try .init(gpa),
.remote_lua_clients = .{},
};
self.* = .{
.wl_server = wl_server,
.backend = backend,
.renderer = renderer,
.allocator = wlr.Allocator.autocreate(backend, renderer) catch {
std.log.err("Allocator create failed, exiting with 5", .{});
std.process.exit(5);
},
.xdg_shell = try wlr.XdgShell.create(wl_server, 2),
.layer_shell = try wlr.LayerShellV1.create(wl_server, 4),
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(self.wl_server),
.xdg_activation = try wlr.XdgActivationV1.create(self.wl_server),
.event_loop = event_loop,
.session = session,
.compositor = try wlr.Compositor.create(wl_server, 6, renderer),
.shm = try wlr.Shm.createWithRenderer(wl_server, 1, renderer),
// TODO: let the user configure a cursor theme and side lua
.root = undefined,
.seat = undefined,
.cursor = undefined,
.remote_lua_manager = RemoteLuaManager.init() catch Utils.oomPanic(),
.keymaps = .init(gpa),
.mousemaps = .init(gpa),
.hooks = .init(gpa),
.events = try .init(gpa),
.remote_lua_clients = .{},
};
self.renderer.initServer(wl_server) catch {
std.log.err("Renderer init failed, exiting with 6", .{});
std.process.exit(6);
};
self.renderer.initServer(wl_server) catch {
std.log.err("Renderer init failed, exiting with 6", .{});
std.process.exit(6);
};
self.root.init();
self.seat.init();
self.cursor.init();
self.root.init();
self.seat.init();
self.cursor.init();
_ = try wlr.Subcompositor.create(self.wl_server);
_ = try wlr.DataDeviceManager.create(self.wl_server);
_ = try wlr.ExportDmabufManagerV1.create(self.wl_server);
_ = try wlr.Viewporter.create(self.wl_server);
_ = try wlr.Presentation.create(self.wl_server, self.backend, 2);
_ = try wlr.ScreencopyManagerV1.create(self.wl_server);
_ = try wlr.AlphaModifierV1.create(self.wl_server);
_ = try wlr.DataControlManagerV1.create(self.wl_server);
_ = try wlr.PrimarySelectionDeviceManagerV1.create(self.wl_server);
_ = try wlr.SinglePixelBufferManagerV1.create(self.wl_server);
_ = try wlr.FractionalScaleManagerV1.create(self.wl_server, 1);
_ = try wlr.XdgOutputManagerV1.create(self.wl_server, self.root.output_layout);
self.root.scene.setGammaControlManagerV1(try wlr.GammaControlManagerV1.create(self.wl_server));
_ = try wlr.Subcompositor.create(self.wl_server);
_ = try wlr.DataDeviceManager.create(self.wl_server);
_ = try wlr.ExportDmabufManagerV1.create(self.wl_server);
_ = try wlr.Viewporter.create(self.wl_server);
_ = try wlr.Presentation.create(self.wl_server, self.backend, 2);
_ = try wlr.ScreencopyManagerV1.create(self.wl_server);
_ = try wlr.AlphaModifierV1.create(self.wl_server);
_ = try wlr.DataControlManagerV1.create(self.wl_server);
_ = try wlr.PrimarySelectionDeviceManagerV1.create(self.wl_server);
_ = try wlr.SinglePixelBufferManagerV1.create(self.wl_server);
_ = try wlr.FractionalScaleManagerV1.create(self.wl_server, 1);
_ = try wlr.XdgOutputManagerV1.create(self.wl_server, self.root.output_layout);
self.root.scene.setGammaControlManagerV1(try wlr.GammaControlManagerV1.create(self.wl_server));
// Add event listeners to events
self.backend.events.new_input.add(&self.new_input);
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_popup.add(&self.new_xdg_popup);
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.xdg_activation.events.request_activate.add(&self.request_activate);
// Add event listeners to events
self.backend.events.new_input.add(&self.new_input);
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_popup.add(&self.new_xdg_popup);
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.xdg_activation.events.request_activate.add(&self.request_activate);
self.events.exec("ServerStartPost", .{});
self.events.exec("ServerStartPost", .{});
}
pub fn deinit(self: *Server) noreturn {
self.new_input.link.remove();
self.new_output.link.remove();
self.new_xdg_toplevel.link.remove();
self.new_xdg_popup.link.remove();
self.new_xdg_toplevel_decoration.link.remove();
self.new_layer_surface.link.remove();
self.new_input.link.remove();
self.new_output.link.remove();
self.new_xdg_toplevel.link.remove();
self.new_xdg_popup.link.remove();
self.new_xdg_toplevel_decoration.link.remove();
self.new_layer_surface.link.remove();
self.seat.deinit();
self.root.deinit();
self.cursor.deinit();
self.seat.deinit();
self.root.deinit();
self.cursor.deinit();
self.backend.destroy();
self.backend.destroy();
self.wl_server.destroyClients();
self.wl_server.destroy();
self.wl_server.destroyClients();
self.wl_server.destroy();
std.log.debug("Exiting mez succesfully", .{});
std.process.exit(0);
std.log.debug("Exiting mez succesfully", .{});
std.process.exit(0);
}
// --------- Backend event handlers ---------
fn handleNewInput(
_: *wl.Listener(*wlr.InputDevice),
device: *wlr.InputDevice
) void {
switch (device.type) {
.keyboard => _ = Keyboard.init(device),
.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)"}
);
},
}
fn handleNewInput(_: *wl.Listener(*wlr.InputDevice), device: *wlr.InputDevice) void {
switch (device.type) {
.keyboard => _ = Keyboard.init(device),
.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
server.seat.wlr_seat.setCapabilities(.{
.pointer = true,
.keyboard = true,
});
// We should really only set true capabilities
server.seat.wlr_seat.setCapabilities(.{
.pointer = true,
.keyboard = true,
});
}
fn handleNewOutput(
_: *wl.Listener(*wlr.Output),
wlr_output: *wlr.Output
) void {
_ = Output.init(wlr_output);
fn handleNewOutput(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
_ = Output.init(wlr_output);
}
fn handleNewXdgToplevel(
_: *wl.Listener(*wlr.XdgToplevel),
xdg_toplevel: *wlr.XdgToplevel
) void {
_ = View.init(xdg_toplevel);
fn handleNewXdgToplevel(_: *wl.Listener(*wlr.XdgToplevel), xdg_toplevel: *wlr.XdgToplevel) void {
_ = View.init(xdg_toplevel);
}
fn handleNewXdgToplevelDecoration(
_: *wl.Listener(*wlr.XdgToplevelDecorationV1),
decoration: *wlr.XdgToplevelDecorationV1
) void {
if(server.root.viewById(@intFromPtr(decoration.toplevel))) |view| {
view.xdg_toplevel_decoration = decoration;
}
fn handleNewXdgToplevelDecoration(_: *wl.Listener(*wlr.XdgToplevelDecorationV1), decoration: *wlr.XdgToplevelDecorationV1) void {
if (server.root.viewById(@intFromPtr(decoration.toplevel))) |view| {
view.xdg_toplevel_decoration = decoration;
}
}
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(
_: *wl.Listener(*wlr.LayerSurfaceV1),
layer_surface: *wlr.LayerSurfaceV1
) void {
std.log.debug("requested layer shell\n", .{});
if (layer_surface.output == null) {
if (server.seat.focused_output == null) {
std.log.err("No output available for new layer surface", .{});
layer_surface.destroy();
return;
fn handleNewLayerSurface(_: *wl.Listener(*wlr.LayerSurfaceV1), layer_surface: *wlr.LayerSurfaceV1) void {
std.log.debug("requested layer shell\n", .{});
if (layer_surface.output == null) {
if (server.seat.focused_output == null) {
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(
_: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
event: *wlr.XdgActivationV1.event.RequestActivate,
) 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(server.seat.focused_output) |output| {
if(output.fullscreen) |fullscreen| {
server.seat.focusSurface(.{ .view = fullscreen });
return;
}
if (scene_node_data.* == .view) {
if (server.seat.focused_output) |output| {
if (output.fullscreen) |fullscreen| {
server.seat.focusSurface(.{ .view = fullscreen });
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");
pub fn oomPanic() noreturn {
std.log.err("Out of memory error, exiting with 1", .{});
std.process.exit(1);
std.log.err("Out of memory error, exiting with 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),
pub fn init(xdg_toplevel: *wlr.XdgToplevel) *View {
errdefer Utils.oomPanic();
errdefer Utils.oomPanic();
const self = try gpa.create(View);
errdefer gpa.destroy(self);
const self = try gpa.create(View);
errdefer gpa.destroy(self);
self.* = .{
.focused = false,
.mapped = false,
.fullscreen = false,
.id = @intFromPtr(xdg_toplevel),
.output = null,
.geometry = .{ .width = 0, .height = 0, .x = 0, .y = 0 },
self.* = .{
.focused = false,
.mapped = false,
.fullscreen = false,
.id = @intFromPtr(xdg_toplevel),
.output = null,
.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,
.scene_tree = undefined,
.surface_tree = undefined,
.xdg_toplevel_decoration = null,
.borders = undefined,
.border_width = 0,
// Add new Toplevel to root of the tree
if (server.seat.focused_output) |output| {
self.scene_tree = try output.layers.content.createSceneTree();
self.surface_tree = try self.scene_tree.createSceneXdgSurface(xdg_toplevel.base);
self.output = output;
}
.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
if(server.seat.focused_output) |output| {
self.scene_tree = try output.layers.content.createSceneTree();
self.surface_tree = try self.scene_tree.createSceneXdgSurface(xdg_toplevel.base);
self.output = output;
}
self.xdg_toplevel.events.destroy.add(&self.destroy);
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);
self.scene_tree.node.data = &self.scene_node_data;
self.xdg_toplevel.base.data = &self.scene_node_data;
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;
}
self.xdg_toplevel.events.destroy.add(&self.destroy);
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;
return self;
}
// Tell the client to close
// It better behave!
pub fn close(self: *View) void {
self.xdg_toplevel.sendClose();
self.xdg_toplevel.sendClose();
}
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 {
self.fullscreen = !self.fullscreen;
if(self.output) |output| {
if(self.fullscreen and output.fullscreen != self) {
// Check to see if another fullscreened view exists, if so replace it
if(output.getFullscreenedView()) |view| {
view.toggleFullscreen();
}
self.fullscreen = !self.fullscreen;
if (self.output) |output| {
if (self.fullscreen and output.fullscreen != self) {
// Check to see if another fullscreened view exists, if so replace it
if (output.getFullscreenedView()) |view| {
view.toggleFullscreen();
}
self.scene_tree.node.reparent(output.layers.fullscreen);
self.setPosition(0, 0);
self.setSize(output.wlr_output.width, output.wlr_output.height);
output.fullscreen = self;
} else {
self.scene_tree.node.reparent(output.layers.content);
output.fullscreen = null;
self.scene_tree.node.reparent(output.layers.fullscreen);
self.setPosition(0, 0);
self.setSize(output.wlr_output.width, output.wlr_output.height);
output.fullscreen = self;
} else {
self.scene_tree.node.reparent(output.layers.content);
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 {
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.y = y;
self.geometry.x = x;
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 {
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
self.geometry.width = @max(1 + 2 * self.border_width, width);
self.geometry.height = @max(1 + 2 * self.border_width, height);
// 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.height = @max(1 + 2 * self.border_width, height);
// This returns a configure serial for verifying the configure
_ = self.xdg_toplevel.setSize(
self.geometry.width - 2 * self.border_width,
self.geometry.height - 2 * self.border_width,
);
// This returns a configure serial for verifying the configure
_ = self.xdg_toplevel.setSize(
self.geometry.width - 2 * self.border_width,
self.geometry.height - 2 * self.border_width,
);
// clip the surface tree to the size of the view
self.surface_tree.node.subsurfaceTreeSetClip(&wlr.Box{
.x = 0,
.y = 0,
.width = self.geometry.width,
.height = self.geometry.height,
});
// clip the surface tree to the size of the view
self.surface_tree.node.subsurfaceTreeSetClip(&wlr.Box{
.x = 0,
.y = 0,
.width = self.geometry.width,
.height = self.geometry.height,
});
self.resizeBorders();
self.resizeBorders();
}
/// this function handles all things related to sizing and positioning and
/// should be called after something in the size or position is changed
pub fn resizeBorders(self: *View) void {
// set the position of the surface to not clip with the borders
self.surface_tree.node.setPosition(self.border_width, self.border_width);
// set the position of the surface to not clip with the borders
self.surface_tree.node.setPosition(self.border_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[2].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[2].node.setPosition(self.geometry.width - self.border_width, 0);
self.borders[0].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[3].setSize(self.border_width, self.geometry.height);
self.borders[1].node.setPosition(0, self.geometry.height - self.border_width);
self.borders[2].node.setPosition(self.geometry.width - self.border_width, 0);
}
// --------- XdgTopLevel event handlers ---------
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
// stupid
_ = view.xdg_toplevel.setTiled(.{
.top = true,
.bottom = true,
.left = true,
.right = true,
});
// we're gonna tell the client that it's tiled so it doesn't try anything
// stupid
_ = view.xdg_toplevel.setTiled(.{
.top = true,
.bottom = true,
.left = true,
.right = true,
});
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_resize.add(&view.request_resize);
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_parent.add(&view.set_parent);
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_resize.add(&view.request_resize);
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_parent.add(&view.set_parent);
view.mapped = true;
server.events.exec("ViewMapPost", .{view.id});
view.mapped = true;
server.events.exec("ViewMapPost", .{view.id});
}
fn handleUnmap(listener: *wl.Listener(void)) void {
const view: *View = @fieldParentPtr("unmap", listener);
std.log.debug("Unmapping view '{s}'", .{view.xdg_toplevel.title orelse "(unnamed)"});
const view: *View = @fieldParentPtr("unmap", listener);
std.log.debug("Unmapping view '{s}'", .{view.xdg_toplevel.title orelse "(unnamed)"});
server.events.exec("ViewUnmapPre", .{view.id});
view.mapped = false; // we do this before any work is done so that nobody tries
// any funny business
server.events.exec("ViewUnmapPre", .{view.id});
view.mapped = false; // we do this before any work is done so that nobody tries
// any funny business
if (server.seat.focused_surface) |fs| {
if (fs == .view and fs.view == view) {
server.seat.focusSurface(null);
if (server.seat.focused_surface) |fs| {
if (fs == .view and fs.view == view) {
server.seat.focusSurface(null);
}
}
}
view.request_fullscreen.link.remove();
view.request_move.link.remove();
view.request_resize.link.remove();
view.set_title.link.remove();
view.set_app_id.link.remove();
view.ack_configure.link.remove();
view.request_fullscreen.link.remove();
view.request_move.link.remove();
view.request_resize.link.remove();
view.set_title.link.remove();
view.set_app_id.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 {
const view: *View = @fieldParentPtr("destroy", listener);
const view: *View = @fieldParentPtr("destroy", listener);
// Remove decorations
// Remove decorations
view.map.link.remove();
view.unmap.link.remove();
view.commit.link.remove();
view.destroy.link.remove();
view.new_popup.link.remove();
view.map.link.remove();
view.unmap.link.remove();
view.commit.link.remove();
view.destroy.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();
// Destroy popups
view.scene_tree.node.destroy();
// Destroy popups
gpa.destroy(view);
gpa.destroy(view);
}
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
if (view.xdg_toplevel.base.initial_commit) {
// On the first commit, send a configure to tell the client it can proceed
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
if (view.xdg_toplevel.base.client.shell.version >= 5) {
// the client should know that it can only fullscreen, nothing else
_ = view.xdg_toplevel.setWmCapabilities(.{ .fullscreen = true, });
// 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) {
// the client should know that it can only fullscreen, nothing else
_ = 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
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;
}
// resize on every commit
view.resizeBorders();
// resize on every commit
view.resizeBorders();
}
// --------- XdgToplevel Event Handlers ---------
fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void {
const view: *View = @fieldParentPtr("new_popup", listener);
_ = Popup.init(xdg_popup, view.scene_tree);
const view: *View = @fieldParentPtr("new_popup", listener);
_ = Popup.init(xdg_popup, view.scene_tree);
}
fn handleRequestMove(
listener: *wl.Listener(*wlr.XdgToplevel.event.Move),
_: *wlr.XdgToplevel.event.Move
) void {
const view: *View = @fieldParentPtr("request_move", listener);
server.events.exec("ViewRequestMove", .{view.id});
fn handleRequestMove(listener: *wl.Listener(*wlr.XdgToplevel.event.Move), _: *wlr.XdgToplevel.event.Move) void {
const view: *View = @fieldParentPtr("request_move", listener);
server.events.exec("ViewRequestMove", .{view.id});
}
fn handleRequestResize(
listener: *wl.Listener(*wlr.XdgToplevel.event.Resize),
_: *wlr.XdgToplevel.event.Resize
) void {
const view: *View = @fieldParentPtr("request_resize", listener);
server.events.exec("ViewRequestResize", .{view.id});
fn handleRequestResize(listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), _: *wlr.XdgToplevel.event.Resize) void {
const view: *View = @fieldParentPtr("request_resize", listener);
server.events.exec("ViewRequestResize", .{view.id});
}
fn handleAckConfigure(
listener: *wl.Listener(*wlr.XdgSurface.Configure),
_: *wlr.XdgSurface.Configure,
listener: *wl.Listener(*wlr.XdgSurface.Configure),
_: *wlr.XdgSurface.Configure,
) void {
const view: *View = @fieldParentPtr("ack_configure", listener);
_ = view;
std.log.err("Unimplemented ack configure", .{});
const view: *View = @fieldParentPtr("ack_configure", listener);
_ = view;
std.log.err("Unimplemented ack configure", .{});
}
fn handleRequestFullscreen(
listener: *wl.Listener(void)
) void {
const view: *View = @fieldParentPtr("request_fullscreen", listener);
server.events.exec("ViewRequestFullscreen", .{view.id});
fn handleRequestFullscreen(listener: *wl.Listener(void)) void {
const view: *View = @fieldParentPtr("request_fullscreen", listener);
server.events.exec("ViewRequestFullscreen", .{view.id});
}
fn handleRequestMinimize(
listener: *wl.Listener(void)
) void {
const view: *View = @fieldParentPtr("request_minimize", listener);
server.events.exec("ViewRequestMinimize", .{view.id});
std.log.debug("request_minimize unimplemented", .{});
fn handleRequestMinimize(listener: *wl.Listener(void)) void {
const view: *View = @fieldParentPtr("request_minimize", listener);
server.events.exec("ViewRequestMinimize", .{view.id});
std.log.debug("request_minimize unimplemented", .{});
}
fn handleSetAppId(
listener: *wl.Listener(void)
) void {
const view: *View = @fieldParentPtr("set_app_id", listener);
server.events.exec("ViewAppIdUpdate", .{view.id});
std.log.debug("request_set_app_id unimplemented", .{});
fn handleSetAppId(listener: *wl.Listener(void)) void {
const view: *View = @fieldParentPtr("set_app_id", listener);
server.events.exec("ViewAppIdUpdate", .{view.id});
std.log.debug("request_set_app_id unimplemented", .{});
}
fn handleSetTitle(
listener: *wl.Listener(void)
) void {
const view: *View = @fieldParentPtr("set_title", listener);
server.events.exec("ViewTitleUpdate", .{view.id});
std.log.debug("request_set_title unimplemented", .{});
fn handleSetTitle(listener: *wl.Listener(void)) void {
const view: *View = @fieldParentPtr("set_title", listener);
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 wlr = @import("wlroots");
const Utils = @import("../Utils.zig");
const Utils = @import("../Utils.zig");
const LuaUtils = @import("LuaUtils.zig");
const gpa = std.heap.c_allocator;
@ -13,53 +13,53 @@ const server = &@import("../main.zig").server;
/// ---Spawn new application via the shell command
/// ---@param cmd string Command to be run by a shell
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);
child.env_map = env_map;
child.spawn() catch |err| switch (err) {
error.OutOfMemory => Utils.oomPanic(),
else => L.raiseErrorStr("Unable to spawn process child process", .{}),
};
var child = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, gpa);
child.env_map = env_map;
child.spawn() catch |err| switch (err) {
error.OutOfMemory => Utils.oomPanic(),
else => L.raiseErrorStr("Unable to spawn process child process", .{}),
};
L.pushNil();
return 1;
L.pushNil();
return 1;
}
/// ---Exit mezzaluna
pub fn exit(L: *zlua.Lua) i32 {
server.wl_server.terminate();
server.wl_server.terminate();
L.pushNil();
return 1;
L.pushNil();
return 1;
}
/// ---Change to a different virtual terminal
/// ---@param vt_num integer virtual terminal number to switch to
pub fn change_vt(L: *zlua.Lua) i32 {
const vt_num = num: {
const res = LuaUtils.coerceInteger(c_uint, L.checkInteger(1)) catch |err| break :num err;
if (res < 1) break :num error.InvalidInteger;
break :num res;
} catch L.raiseErrorStr("The vt number must be >= 1 and < inf", .{});
const vt_num = num: {
const res = LuaUtils.coerceInteger(c_uint, L.checkInteger(1)) catch |err| break :num err;
if (res < 1) break :num error.InvalidInteger;
break :num res;
} catch L.raiseErrorStr("The vt number must be >= 1 and < inf", .{});
if (server.session) |session| {
std.log.debug("Changing virtual terminal to {d}", .{vt_num});
wlr.Session.changeVt(session, vt_num) catch {
L.raiseErrorStr("Failed to switch vt", .{});
};
} else {
L.raiseErrorStr("Mez has not been initialized yet", .{});
}
if (server.session) |session| {
std.log.debug("Changing virtual terminal to {d}", .{vt_num});
wlr.Session.changeVt(session, vt_num) catch {
L.raiseErrorStr("Failed to switch vt", .{});
};
} else {
L.raiseErrorStr("Mez has not been initialized yet", .{});
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
/// --- Print the scene tree for debugging
pub fn print_scene(L: *zlua.Lua) i32 {
@import("../Debug.zig").debugPrintSceneTree();
@import("../Debug.zig").debugPrintSceneTree();
L.pushNil();
return 1;
L.pushNil();
return 1;
}

View file

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

View file

@ -4,7 +4,7 @@ const std = @import("std");
const zlua = @import("zlua");
const Lua = @import("Lua.zig");
const Utils = @import("../Utils.zig");
const Utils = @import("../Utils.zig");
const gpa = std.heap.c_allocator;
@ -12,29 +12,29 @@ const gpa = std.heap.c_allocator;
/// ---@vararg string paths to join
/// ---@return string?
pub fn joinpath(L: *zlua.Lua) i32 {
const nargs: i32 = L.getTop();
if (nargs < 2) {
L.raiseErrorStr("Expected at least two paths to join", .{});
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 nargs: i32 = L.getTop();
if (nargs < 2) {
L.raiseErrorStr("Expected at least two paths to join", .{});
return 0;
}
const partial_path = L.toString(i) catch unreachable;
paths.append(gpa, partial_path) catch Utils.oomPanic();
}
var paths = std.ArrayList([:0]const u8).initCapacity(gpa, @intCast(nargs)) catch Utils.oomPanic();
defer paths.deinit(gpa);
const final_path: []const u8 = std.fs.path.join(gpa, paths.items) catch Utils.oomPanic();
defer gpa.free(final_path);
var i: u8 = 1;
while (i <= nargs) : (i += 1) {
if (!L.isString(i)) {
L.raiseErrorStr("Expected string at argument %d", .{i});
return 0;
}
_ = L.pushString(final_path);
return 1;
const partial_path = L.toString(i) catch unreachable;
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
/// ---@return number id
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
// add. Regardless of which type is passed in we create an arraylist of
// []const u8's
if (L.isTable(1)) {
hook.events = gpa.alloc(comptime []const u8, L.objectLen(1)) catch Utils.oomPanic();
var i: u32 = 0;
L.pushNil();
while (L.next(1)) {
if (L.isString(-1)) {
const s = L.checkString(-1);
hook.events[i] = gpa.dupe(u8, s) catch Utils.oomPanic();
i += 1;
}
L.pop(1);
// 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
// []const u8's
if (L.isTable(1)) {
hook.events = gpa.alloc(comptime []const u8, L.objectLen(1)) catch Utils.oomPanic();
var i: u32 = 0;
L.pushNil();
while (L.next(1)) {
if (L.isString(-1)) {
const s = L.checkString(-1);
hook.events[i] = gpa.dupe(u8, s) catch Utils.oomPanic();
i += 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.getTable(2);
if (L.isFunction(-1)) {
hook.options.lua_cb_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("callback");
_ = L.getTable(2);
if (L.isFunction(-1)) {
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
// but that all really depends on the implementation of the hashmap
server.hooks.put(hook.options.lua_cb_ref_idx, hook) catch Utils.oomPanic();
// 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
server.hooks.put(hook.options.lua_cb_ref_idx, hook) catch Utils.oomPanic();
for (hook.events) |value| {
server.events.put(value, hook) catch Utils.oomPanic();
}
for (hook.events) |value| {
server.events.put(value, hook) catch Utils.oomPanic();
}
L.pushInteger(hook.options.lua_cb_ref_idx);
return 1;
L.pushInteger(hook.options.lua_cb_ref_idx);
return 1;
}
/// ---Create an existing hook
/// ---@param id number
/// ---@return boolean has it been deleted
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 = server.hooks.get(hook_id);
if (hook == null) L.raiseErrorStr("hook {} does not exist", .{hook_id});
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);
if (hook == null) L.raiseErrorStr("hook {} does not exist", .{hook_id});
for (hook.?.events) |value| {
server.events.del(value, hook.?);
}
L.pushBoolean(server.hooks.remove(hook_id));
return 1;
for (hook.?.events) |value| {
server.events.del(value, hook.?);
}
L.pushBoolean(server.hooks.remove(hook_id));
return 1;
}

View file

@ -14,17 +14,17 @@ const c = @import("../C.zig").c;
const server = &@import("../main.zig").server;
fn parse_modkeys(modStr: []const u8) wlr.Keyboard.ModifierMask {
var it = std.mem.splitScalar(u8, modStr, '|');
var modifiers = wlr.Keyboard.ModifierMask{};
while (it.next()) |m| {
inline for (std.meta.fields(@TypeOf(modifiers))) |f| {
if (f.type == bool and std.mem.eql(u8, m, f.name)) {
@field(modifiers, f.name) = true;
}
var it = std.mem.splitScalar(u8, modStr, '|');
var modifiers = wlr.Keyboard.ModifierMask{};
while (it.next()) |m| {
inline for (std.meta.fields(@TypeOf(modifiers))) |f| {
if (f.type == bool and std.mem.eql(u8, m, f.name)) {
@field(modifiers, f.name) = true;
}
}
}
}
return modifiers;
return modifiers;
}
/// ---Create a new keymap
@ -32,36 +32,36 @@ fn parse_modkeys(modStr: []const u8) wlr.Keyboard.ModifierMask {
/// ---@param string keys
/// ---@param table options
pub fn add_keymap(L: *zlua.Lua) i32 {
var keymap: Keymap = undefined;
keymap.options.repeat = true;
var keymap: Keymap = undefined;
keymap.options.repeat = true;
const mod = L.checkString(1);
keymap.modifier = parse_modkeys(mod);
const mod = L.checkString(1);
keymap.modifier = parse_modkeys(mod);
const key = L.checkString(2);
keymap.keycode = xkb.Keysym.fromName(key, .no_flags);
const key = L.checkString(2);
keymap.keycode = xkb.Keysym.fromName(key, .no_flags);
_ = L.pushString("press");
_ = L.getTable(3);
if (L.isFunction(-1)) {
keymap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("press");
_ = L.getTable(3);
if (L.isFunction(-1)) {
keymap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("release");
_ = L.getTable(3);
if (L.isFunction(-1)) {
keymap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("release");
_ = L.getTable(3);
if (L.isFunction(-1)) {
keymap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("repeat");
_ = L.getTable(3);
keymap.options.repeat = L.isNil(-1) or L.toBoolean(-1);
_ = L.pushString("repeat");
_ = L.getTable(3);
keymap.options.repeat = L.isNil(-1) or L.toBoolean(-1);
const hash = Keymap.hash(keymap.modifier, keymap.keycode);
server.keymaps.put(hash, keymap) catch Utils.oomPanic();
const hash = Keymap.hash(keymap.modifier, keymap.keycode);
server.keymaps.put(hash, keymap) catch Utils.oomPanic();
L.pushNil();
return 1;
L.pushNil();
return 1;
}
/// ---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 table options
pub fn add_mousemap(L: *zlua.Lua) i32 {
var mousemap: Mousemap = undefined;
var mousemap: Mousemap = undefined;
const mod = L.checkString(1);
mousemap.modifier = parse_modkeys(mod);
const mod = L.checkString(1);
mousemap.modifier = parse_modkeys(mod);
const button = L.checkString(2);
mousemap.event_code = c.libevdev_event_code_from_name(c.EV_KEY, button);
const button = L.checkString(2);
mousemap.event_code = c.libevdev_event_code_from_name(c.EV_KEY, button);
_ = L.pushString("press");
_ = L.getTable(3);
if (L.isFunction(-1)) {
mousemap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("press");
_ = L.getTable(3);
if (L.isFunction(-1)) {
mousemap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("release");
_ = L.getTable(3);
if (L.isFunction(-1)) {
mousemap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("release");
_ = L.getTable(3);
if (L.isFunction(-1)) {
mousemap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("drag");
_ = L.getTable(3);
if (L.isFunction(-1)) {
mousemap.options.lua_drag_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
_ = L.pushString("drag");
_ = L.getTable(3);
if (L.isFunction(-1)) {
mousemap.options.lua_drag_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic();
}
const hash = Mousemap.hash(mousemap.modifier, mousemap.event_code);
server.mousemaps.put(hash, mousemap) catch Utils.oomPanic();
const hash = Mousemap.hash(mousemap.modifier, mousemap.event_code);
server.mousemaps.put(hash, mousemap) catch Utils.oomPanic();
L.pushNil();
return 1;
L.pushNil();
return 1;
}
/// ---Remove an existing keymap
/// ---@param string modifiers
/// ---@param string keys
pub fn del_keymap(L: *zlua.Lua) i32 {
L.checkType(1, .string);
L.checkType(2, .string);
L.checkType(1, .string);
L.checkType(2, .string);
var keymap: Keymap = undefined;
const mod = L.checkString(1);
var keymap: Keymap = undefined;
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);
_ = server.keymaps.remove(Keymap.hash(keymap.modifier, keymap.keycode));
keymap.keycode = xkb.Keysym.fromName(key, .no_flags);
_ = server.keymaps.remove(Keymap.hash(keymap.modifier, keymap.keycode));
L.pushNil();
return 1;
L.pushNil();
return 1;
}
/// ---Remove an existing mousemap
/// ---@param string modifiers
/// ---@param string button
pub fn del_mousemap(L: *zlua.Lua) i32 {
L.checkType(1, .string);
L.checkType(2, .string);
L.checkType(1, .string);
L.checkType(2, .string);
var mousemap: Mousemap = undefined;
const mod = L.checkString(1);
mousemap.modifier = parse_modkeys(mod);
var mousemap: Mousemap = undefined;
const mod = L.checkString(1);
mousemap.modifier = parse_modkeys(mod);
const button = L.checkString(2);
mousemap.event_code = c.libevdev_event_code_from_name(c.EV_KEY, button);
const button = L.checkString(2);
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();
return 1;
L.pushNil();
return 1;
}
/// ---Get the repeat information
/// ---@return integer[2]
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.setField(-2, "rate");
L.pushInteger(server.seat.keyboard_group.wlr_group.keyboard.repeat_info.delay);
L.setField(-2, "delay");
L.pushInteger(server.seat.keyboard_group.wlr_group.keyboard.repeat_info.rate);
L.setField(-2, "rate");
L.pushInteger(server.seat.keyboard_group.wlr_group.keyboard.repeat_info.delay);
L.setField(-2, "delay");
return 1;
return 1;
}
/// ---Set the repeat information
/// ---@param integer rate
/// ---@param integer delay
pub fn set_repeat_info(L: *zlua.Lua) i32 {
const rate = LuaUtils.coerceInteger(i32, L.checkInteger(1)) catch {
L.raiseErrorStr("The rate must be a valid number", .{});
};
const delay = LuaUtils.coerceInteger(i32, L.checkInteger(2)) catch {
L.raiseErrorStr("The delay must be a valid number", .{});
};
const rate = LuaUtils.coerceInteger(i32, L.checkInteger(1)) catch {
L.raiseErrorStr("The rate must be a valid number", .{});
};
const delay = LuaUtils.coerceInteger(i32, L.checkInteger(2)) catch {
L.raiseErrorStr("The delay must be a valid number", .{});
};
server.seat.keyboard_group.wlr_group.keyboard.setRepeatInfo(rate, delay);
return 0;
server.seat.keyboard_group.wlr_group.keyboard.setRepeatInfo(rate, delay);
return 0;
}
/// ---Set the cursor type
/// ---@param string cursor name
pub fn set_cursor_type(L: *zlua.Lua) i32 {
const name = L.checkString(1);
server.cursor.wlr_cursor.setXcursor(server.cursor.x_cursor_manager, name);
const name = L.checkString(1);
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 Bridge = @import("Bridge.zig");
const Fs = @import("Fs.zig");
const Input = @import("Input.zig");
const Api = @import("Api.zig");
const Hook = @import("Hook.zig");
const View = @import("View.zig");
const Fs = @import("Fs.zig");
const Input = @import("Input.zig");
const Api = @import("Api.zig");
const Hook = @import("Hook.zig");
const View = @import("View.zig");
const Output = @import("Output.zig");
const Remote = @import("Remote.zig");
@ -19,165 +19,168 @@ const gpa = std.heap.c_allocator;
state: *zlua.Lua,
pub fn loadRuntimeDir(self: *zlua.Lua) !void {
const path_dir = try std.fs.path.joinZ(gpa, &[_][]const u8{
config.runtime_path_prefix,
"share",
"mezzaluna",
});
defer gpa.free(path_dir);
const path_dir = try std.fs.path.joinZ(gpa, &[_][]const u8{
config.runtime_path_prefix,
"share",
"mezzaluna",
});
defer gpa.free(path_dir);
{
_ = try self.getGlobal("mez");
_ = self.getField(-1, "path");
defer self.pop(2);
_ = self.pushString(path_dir);
self.setField(-2, "runtime");
}
{
_ = try self.getGlobal("mez");
_ = self.getField(-1, "path");
defer self.pop(2);
_ = self.pushString(path_dir);
self.setField(-2, "runtime");
}
const path_full = try std.fs.path.joinZ(gpa, &[_][]const u8{
path_dir,
"init.lua",
});
defer gpa.free(path_full);
const path_full = try std.fs.path.joinZ(gpa, &[_][]const u8{
path_dir,
"init.lua",
});
defer gpa.free(path_full);
self.doFile(path_full) catch {
const err = try self.toString(-1);
std.log.debug("Failed to run lua file: {s}", .{err});
};
self.doFile(path_full) catch {
const err = try self.toString(-1);
std.log.debug("Failed to run lua file: {s}", .{err});
};
}
pub fn setBaseConfig(self: *zlua.Lua, path: []const u8) !void {
{
_ = try self.getGlobal("mez");
_ = self.getField(-1, "path");
defer self.pop(2);
const new_path = try std.fs.path.join(gpa, &[_][]const u8{path, "init.lua"});
defer gpa.free(new_path);
_ = self.pushString(new_path);
self.setField(-2, "config");
}
{
_ = try self.getGlobal("mez");
_ = self.getField(-1, "path");
defer self.pop(2);
const cur_path = self.toString(-1) catch "";
{
_ = try self.getGlobal("mez");
_ = self.getField(-1, "path");
defer self.pop(2);
const new_path = try std.fs.path.join(gpa, &[_][]const u8{ path, "init.lua" });
defer gpa.free(new_path);
_ = self.pushString(new_path);
self.setField(-2, "config");
}
{
_ = try self.getGlobal("mez");
_ = self.getField(-1, "path");
defer self.pop(2);
const cur_path = self.toString(-1) catch "";
const unsentinel: []const u8 = std.mem.span(cur_path.ptr);
const new_path = try std.mem.concat(gpa, u8, &[_][]const u8{
unsentinel,
";",
path,
});
defer gpa.free(new_path);
_ = self.pushString(new_path);
_ = self.setField(-2, "path");
}
const unsentinel: []const u8 = std.mem.span(cur_path.ptr);
const new_path = try std.mem.concat(gpa, u8, &[_][]const u8{
unsentinel,
";",
path,
});
defer gpa.free(new_path);
_ = self.pushString(new_path);
_ = self.setField(-2, "path");
}
}
fn loadBaseConfig(self: *zlua.Lua) !void {
const lua_path = "mez.path.base_config";
if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) {
std.log.err("Base config path not found. is your runtime dir setup?", .{});
return;
}
const path = self.toString(-1) catch |err| {
std.log.err("Failed to pop the base config path from the lua stack. {}", .{err});
return;
};
self.pop(-1);
try self.doFile(path);
const lua_path = "mez.path.base_config";
if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) {
std.log.err("Base config path not found. is your runtime dir setup?", .{});
return;
}
const path = self.toString(-1) catch |err| {
std.log.err("Failed to pop the base config path from the lua stack. {}", .{err});
return;
};
self.pop(-1);
try self.doFile(path);
}
fn loadConfigDir(self: *zlua.Lua) !void {
const lua_path = "mez.path.config";
if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) {
std.log.err("Config path not found. is your runtime dir setup?", .{});
return;
}
const path = self.toString(-1) catch |err| {
std.log.err("Failed to pop the config path from the lua stack. {}", .{err});
return;
};
self.pop(-1);
try self.doFile(path);
const lua_path = "mez.path.config";
if (!Bridge.getNestedField(self, @constCast(lua_path[0..]))) {
std.log.err("Config path not found. is your runtime dir setup?", .{});
return;
}
const path = self.toString(-1) catch |err| {
std.log.err("Failed to pop the config path from the lua stack. {}", .{err});
return;
};
self.pop(-1);
try self.doFile(path);
}
pub fn openLibs(self: *zlua.Lua) void {
{
self.newTable();
defer _ = self.setGlobal("mez");
{
self.newTable();
defer _ = self.setField(-2, "path");
self.newTable();
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 {
self.state = try zlua.Lua.init(gpa);
errdefer self.state.deinit();
self.state.openLibs();
self.state = try zlua.Lua.init(gpa);
errdefer self.state.deinit();
self.state.openLibs();
openLibs(self.state);
openLibs(self.state);
if (!cfg.enabled) try setBaseConfig(self.state, "");
loadRuntimeDir(self.state) catch |err| if (err == error.LuaRuntime) {
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)});
if (!cfg.enabled) try setBaseConfig(self.state, "");
loadRuntimeDir(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 {
self.state.deinit();
self.state.deinit();
}

View file

@ -8,53 +8,53 @@ const View = @import("../View.zig");
const server = &@import("../main.zig").server;
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)) {
return error.InvalidNumber;
}
switch (@typeInfo(x)) {
.int => return @as(x, @intFromFloat(number)),
.float => return @floatCast(number),
else => @compileError("unsupported type"),
}
if (number < std.math.minInt(x) or number > std.math.maxInt(x) or std.math.isNan(number)) {
return error.InvalidNumber;
}
switch (@typeInfo(x)) {
.int => return @as(x, @intFromFloat(number)),
.float => return @floatCast(number),
else => @compileError("unsupported type"),
}
}
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)) {
return error.InvalidInteger;
}
switch (@typeInfo(x)) {
.int => return @intCast(number),
.float => return @as(x, @floatFromInt(number)),
else => @compileError("unsupported type"),
}
if (number < std.math.minInt(x) or number > std.math.maxInt(x) or std.math.isNan(number)) {
return error.InvalidInteger;
}
switch (@typeInfo(x)) {
.int => return @intCast(number),
.float => return @as(x, @floatFromInt(number)),
else => @compileError("unsupported type"),
}
}
pub fn newLib(L: *zlua.Lua, f: []const zlua.FnReg) void {
L.newLibTable(f); // documented as being unavailable, but it is.
for (f) |value| {
if (value.func == null) continue;
L.pushClosure(value.func.?, 0);
L.setField(-2, value.name);
}
L.newLibTable(f); // documented as being unavailable, but it is.
for (f) |value| {
if (value.func == null) continue;
L.pushClosure(value.func.?, 0);
L.setField(-2, value.name);
}
}
/// 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"
pub fn toStringEx(L: *zlua.Lua) [:0]const u8 {
const errstr = "nil";
_ = L.getGlobal("tostring") catch return errstr;
L.insert(1);
L.protectedCall(.{ .args = 1, .results = 1 }) catch return errstr;
return L.toString(-1) catch errstr;
const errstr = "nil";
_ = L.getGlobal("tostring") catch return errstr;
L.insert(1);
L.protectedCall(.{ .args = 1, .results = 1 }) catch return errstr;
return L.toString(-1) catch errstr;
}
pub fn viewById(view_id: u64) ?*View {
if (view_id == 0) {
if(server.seat.focused_surface) |fs| {
if(fs == .view) return fs.view;
if (view_id == 0) {
if (server.seat.focused_surface) |fs| {
if (fs == .view) return fs.view;
}
} else {
return server.root.viewById(view_id);
}
} else {
return server.root.viewById(view_id);
}
return null;
return null;
}

View file

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

View file

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

View file

@ -11,7 +11,7 @@ const LuaUtils = @import("LuaUtils.zig");
const server = &@import("../main.zig").server;
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
@ -20,84 +20,83 @@ fn view_id_err(L: *zlua.Lua) noreturn {
// ---Get the ids for all available views
// ---@return view_id[]?
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;
L.newTable();
var index: i32 = 1;
L.newTable();
while(output_it.next()) |o| {
if(o.output.data == null) {
std.log.err("Output arbitrary data not assigned", .{});
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;
while (output_it.next()) |o| {
if (o.output.data == null) {
std.log.err("Output arbitrary data not assigned", .{});
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) {
L.pushInteger(@intCast(index));
L.pushInteger(@intCast(scene_node_data.view.id));
L.setTable(-3);
// Only search the content and fullscreen layers for views
const layers = [_]*wlr.SceneTree{
output.layers.content,
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
// ---@return view_id?
pub fn get_focused_id(L: *zlua.Lua) i32 {
if(server.seat.focused_surface) |fs| {
if(fs == .view) {
L.pushInteger(@intCast(fs.view.id));
return 1;
if (server.seat.focused_surface) |fs| {
if (fs == .view) {
L.pushInteger(@intCast(fs.view.id));
return 1;
}
}
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
// ---Close the view with view_id
// ---@param view_id view_id 0 maps to focused view
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| {
v.close();
}
if (LuaUtils.viewById(view_id)) |v| {
v.close();
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
// ---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 y number y position for view
pub fn set_position(L: *zlua.Lua) i32 {
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 y = LuaUtils.coerceNumber(i32, L.checkNumber(3)) catch L.raiseErrorStr("The y must be > -inf and < inf", .{});
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 y = LuaUtils.coerceNumber(i32, L.checkNumber(3)) catch L.raiseErrorStr("The y must be > -inf and < inf", .{});
if(LuaUtils.viewById(view_id)) |v| {
v.setPosition(x, y);
}
if (LuaUtils.viewById(view_id)) |v| {
v.setPosition(x, y);
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
// ---Get the position of the view
// ---@param view_id view_id 0 maps to focused view
// ---@return { x: integer, y: integer }? Position of the view
pub fn get_position(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (LuaUtils.viewById(view_id)) |v| {
L.newTable();
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (LuaUtils.viewById(view_id)) |v| {
L.newTable();
_ = L.pushString("x");
L.pushInteger(@intCast(v.scene_tree.node.x));
L.setTable(-3);
_ = L.pushString("x");
L.pushInteger(@intCast(v.scene_tree.node.x));
L.setTable(-3);
_ = L.pushString("y");
L.pushInteger(@intCast(v.scene_tree.node.y));
L.setTable(-3);
_ = L.pushString("y");
L.pushInteger(@intCast(v.scene_tree.node.y));
L.setTable(-3);
return 1;
}
L.pushNil();
return 1;
}
L.pushNil();
return 1;
}
// ---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
// ---@return
pub fn set_size(L: *zlua.Lua) i32 {
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
// 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
// than or equal to zero.
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 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
// 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
// than or equal to zero.
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", .{});
if(LuaUtils.viewById(view_id)) |v| {
v.setSize(@intCast(width), @intCast(height));
}
if (LuaUtils.viewById(view_id)) |v| {
v.setSize(@intCast(width), @intCast(height));
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
// ---Get the size of the view
// ---@param view_id view_id 0 maps to focused view
// ---@return { width: integer, height: integer }? Size of the view
pub fn get_size(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (LuaUtils.viewById(view_id)) |v| {
L.newTable();
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (LuaUtils.viewById(view_id)) |v| {
L.newTable();
_ = L.pushString("width");
L.pushInteger(@intCast(v.xdg_toplevel.current.width));
L.setTable(-3);
_ = L.pushString("width");
L.pushInteger(@intCast(v.xdg_toplevel.current.width));
L.setTable(-3);
_ = L.pushString("height");
L.pushInteger(@intCast(v.xdg_toplevel.current.height));
L.setTable(-3);
_ = L.pushString("height");
L.pushInteger(@intCast(v.xdg_toplevel.current.height));
L.setTable(-3);
return 1;
}
L.pushNil();
return 1;
}
L.pushNil();
return 1;
}
// ---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
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) {
server.seat.focusSurface(null);
} else if(server.root.viewById(@intCast(view_id.?))) |view| {
server.seat.focusSurface(.{ .view = view });
}
if (view_id == null) {
server.seat.focusSurface(null);
} else if (server.root.viewById(@intCast(view_id.?))) |view| {
server.seat.focusSurface(.{ .view = view });
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
// ---Toggle the view to enter fullscreen. Will enter the fullsreen
// layer.
// ---@param view_id view_id 0 maps to focused view
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});
if(LuaUtils.viewById(view_id)) |v| {
std.log.debug("toggling fullscreen", .{});
v.toggleFullscreen();
}
std.log.debug("fullscreen view {d}", .{view_id});
if (LuaUtils.viewById(view_id)) |v| {
std.log.debug("toggling fullscreen", .{});
v.toggleFullscreen();
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
// ---Get the title of the view
// ---@param view_id view_id 0 maps to focused view
// ---@return string?
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(v.xdg_toplevel.title == null) {
L.pushNil();
return 1;
if (LuaUtils.viewById(view_id)) |v| {
if (v.xdg_toplevel.title == null) {
L.pushNil();
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;
}
L.pushNil();
return 1;
}
// ---Get the app_id of the view
// ---@param view_id view_id 0 maps to focused view
// ---@return string?
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(v.xdg_toplevel.app_id == null) {
L.pushNil();
return 1;
if (LuaUtils.viewById(view_id)) |v| {
if (v.xdg_toplevel.app_id == null) {
L.pushNil();
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;
}
L.pushNil();
return 1;
}
// ---Enable or disable a view
// ---@param view_id view_id 0 maps to focused view
// ---@param enabled boolean
pub fn set_enabled(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (!L.isBoolean(2)) {
L.raiseErrorStr("argument 2 must be a boolean", .{});
}
const activate = L.toBoolean(2);
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (!L.isBoolean(2)) {
L.raiseErrorStr("argument 2 must be a boolean", .{});
}
const activate = L.toBoolean(2);
if (LuaUtils.viewById(view_id)) |v| {
_ = v.xdg_toplevel.setActivated(activate);
return 0;
}
if (LuaUtils.viewById(view_id)) |v| {
_ = v.xdg_toplevel.setActivated(activate);
return 0;
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
// ---Check if a view is enabled
// ---@param view_id view_id 0 maps to focused view
// ---@return boolean?
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| {
_ = L.pushBoolean(v.xdg_toplevel.current.activated);
if (LuaUtils.viewById(view_id)) |v| {
_ = L.pushBoolean(v.xdg_toplevel.current.activated);
return 1;
}
L.pushNil();
return 1;
}
L.pushNil();
return 1;
}
// ---Set a view you intend to resize
// ---@param view_id view_id 0 maps to focused view
// ---@param enable boolean
pub fn set_resizing(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (!L.isBoolean(2)) {
L.raiseErrorStr("argument 2 must be a boolean", .{});
}
const resizing = L.toBoolean(2);
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
if (!L.isBoolean(2)) {
L.raiseErrorStr("argument 2 must be a boolean", .{});
}
const resizing = L.toBoolean(2);
if (LuaUtils.viewById(view_id)) |v| {
_ = v.xdg_toplevel.setResizing(resizing);
return 0;
}
if (LuaUtils.viewById(view_id)) |v| {
_ = v.xdg_toplevel.setResizing(resizing);
return 0;
}
L.pushNil();
return 1;
L.pushNil();
return 1;
}
// ---Check if a view is resizing
// ---@param view_id view_id 0 maps to focused view
// ---@return boolean? nil if view cannot be found
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| {
_ = L.pushBoolean(v.xdg_toplevel.current.resizing);
if (LuaUtils.viewById(view_id)) |v| {
_ = L.pushBoolean(v.xdg_toplevel.current.resizing);
return 1;
}
L.pushNil();
return 1;
}
L.pushNil();
return 1;
}
// ---Set the borders of a view
// ---@param view_id view_id 0 maps to focused view
/// ---@param options table options for the view's borders
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.getTable(2);
if (!L.isString(-1)) L.raiseErrorStr("The color must be a string", .{});
const color = L.checkString(-1);
const border_color: [4]f32 = color: {
errdefer L.raiseErrorStr("The color must be a valid hex string", .{});
_ = L.pushString("color");
_ = L.getTable(2);
if (!L.isString(-1)) L.raiseErrorStr("The color must be a string", .{});
const color = L.checkString(-1);
const border_color: [4]f32 = color: {
errdefer L.raiseErrorStr("The color must be a valid hex string", .{});
var start_color_idx: u8 = 0;
if (color[0] == '#') start_color_idx = 1;
const color_fields = (color.len - start_color_idx) / 2;
var start_color_idx: u8 = 0;
if (color[0] == '#') start_color_idx = 1;
const color_fields = (color.len - start_color_idx) / 2;
var alpha: ?f32 = null;
if (color_fields != 4) {
if (color_fields != 3) L.raiseErrorStr("The color must be at least 6 characters long", .{});
alpha = 1;
}
var alpha: ?f32 = null;
if (color_fields != 4) {
if (color_fields != 3) L.raiseErrorStr("The color must be at least 6 characters long", .{});
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 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 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 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 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;
break :color .{ r, g, b, a };
};
break :color .{ r, g, b, a };
};
_ = L.pushString("width");
_ = L.getTable(2);
const width = LuaUtils.coerceInteger(u32, L.checkInteger(-1)) catch {
L.raiseErrorStr("The border width must be >= 0 and < inf", .{});
};
_ = L.pushString("width");
_ = L.getTable(2);
const width = LuaUtils.coerceInteger(u32, L.checkInteger(-1)) catch {
L.raiseErrorStr("The border width must be >= 0 and < inf", .{});
};
if (LuaUtils.viewById(view_id)) |v| {
v.setBorderColor(&border_color);
if (LuaUtils.viewById(view_id)) |v| {
v.setBorderColor(&border_color);
if (width != v.border_width) {
v.border_width = @intCast(width);
// the size has changed which means we need to update the borders
v.resizeBorders();
if (width != v.border_width) {
v.border_width = @intCast(width);
// the size has changed which means we need to update the borders
v.resizeBorders();
}
return 0;
}
return 0;
}
return 0;
}
/// 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
/// work required by the user
pub fn setWmCapabilities(L: *zlua.Lua) i32 {
_ = L;
return 0;
_ = L;
return 0;
}

View file

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

View file

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

View file

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

View file

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