Mezzaluna/src/server.zig
2025-10-23 23:29:38 -04:00

201 lines
5.4 KiB
Zig

const Server = @This();
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 Output = @import("output.zig");
const View = @import("view.zig");
const Utils = @import("utils.zig");
const gpa = std.heap.c_allocator;
const server = &@import("main.zig").server;
wl_server: *wl.Server,
compositor: *wlr.Compositor,
renderer: *wlr.Renderer,
backend: *wlr.Backend,
event_loop: *wl.EventLoop,
session: ?*wlr.Session,
shm: *wlr.Shm,
xdg_shell: *wlr.XdgShell,
xdg_toplevel_decoration_manager: *wlr.XdgDecorationManagerV1,
// Input
allocator: *wlr.Allocator,
root: Root,
seat: Seat,
cursor: Cursor,
keyboard: Keyboard,
// Backend listeners
new_input: wl.Listener(*wlr.InputDevice) = .init(handleNewInput),
new_output: wl.Listener(*wlr.Output) = .init(handleNewOutput),
// backend.events.destroy
// XdgShell listeners
new_xdg_toplevel: wl.Listener(*wlr.XdgToplevel) = .init(handleNewXdgToplevel),
new_xdg_popup: wl.Listener(*wlr.XdgPopup) = .init(handleNewXdgPopup),
new_xdg_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) = .init(handleNewXdgToplevelDecoration),
// new_xdg_popup
// new_xdg_toplevel
// Seat listeners
pub fn init(self: *Server) void {
errdefer Utils.oomPanic();
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();
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);
};
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),
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.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,
.keyboard = undefined,
};
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();
_ = try wlr.Subcompositor.create(self.wl_server);
_ = try wlr.DataDeviceManager.create(self.wl_server);
// Add event listeners to events
// Backedn events
self.backend.events.new_input.add(&self.new_input);
self.backend.events.new_output.add(&self.new_output);
// XdgShell events
self.xdg_shell.events.new_toplevel.add(&self.new_xdg_toplevel);
self.xdg_shell.events.new_popup.add(&self.new_xdg_popup);
// XdgDecorationManagerV1 events
// self.xdg_toplevel_decoration_manager.events.new_toplevel_decoration.add(&self.new_toplevel_decoration);
}
pub fn deinit(self: *Server) void {
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.link.remove();
self.seat.deinit();
self.root.deinit();
self.cursor.deinit();
self.backend.destroy();
self.wl_server.destroyClients();
self.wl_server.destroy();
}
// --------- Backend event handlers ---------
fn handleNewInput(
_: *wl.Listener(*wlr.InputDevice),
device: *wlr.InputDevice
) void {
switch (device.type) {
.keyboard => server.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)"}
);
},
}
server.seat.wlr_seat.setCapabilities(.{
.pointer = true,
.keyboard = server.keyboard.keyboards.length() > 0,
});
}
fn handleNewOutput(
_: *wl.Listener(*wlr.Output),
wlr_output: *wlr.Output
) void {
std.log.info("Handling a new output - {s}", .{wlr_output.name});
if (!wlr_output.initRender(server.allocator, server.renderer)) return;
var state = wlr.Output.State.init();
defer state.finish();
state.setEnabled(true);
if (wlr_output.preferredMode()) |mode| {
state.setMode(mode);
}
if (!wlr_output.commitState(&state)) return;
const new_output = Output.create(wlr_output);
server.root.addOutput(new_output);
}
fn handleNewXdgToplevel(
_: *wl.Listener(*wlr.XdgToplevel),
xdg_toplevel: *wlr.XdgToplevel
) void {
_ = View.initFromTopLevel(xdg_toplevel);
}
fn handleNewXdgPopup(
_: *wl.Listener(*wlr.XdgPopup),
_: *wlr.XdgPopup
) void {
std.log.err("Unimplemented handle new xdg popup", .{});
}
fn handleNewXdgToplevelDecoration(
_: *wl.Listener(*wlr.XdgToplevelDecorationV1),
decoration: *wlr.XdgToplevelDecorationV1
) void {
// TODO: Configured with lua perhaps
decoration.current.mode = .server_side;
}