From b8419806efc0f0adfdd1a5aaec037aebc032bbd3 Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Sun, 19 Oct 2025 16:39:43 -0400 Subject: [PATCH] reorganized into seat file and cursor file --- src/cursor.zig | 124 ++++++++++++++++++++ src/keyboard.zig | 72 +++++++----- src/main.zig | 1 - src/output.zig | 36 ++++-- src/root.zig | 77 +++++++------ src/seat.zig | 45 ++++++++ src/server.zig | 292 +++++++++++++++++------------------------------ src/toplevel.zig | 35 ------ src/view.zig | 65 +++++++++++ 9 files changed, 449 insertions(+), 298 deletions(-) create mode 100644 src/cursor.zig create mode 100644 src/seat.zig delete mode 100644 src/toplevel.zig create mode 100644 src/view.zig diff --git a/src/cursor.zig b/src/cursor.zig new file mode 100644 index 0000000..04d2d84 --- /dev/null +++ b/src/cursor.zig @@ -0,0 +1,124 @@ +pub const Cursor = @This(); + +const std = @import("std"); +const wl = @import("wayland").server.wl; +const wlr = @import("wlroots"); + +const server = &@import("main.zig").server; + +wlr_cursor: *wlr.Cursor, +x_cursor_manager: *wlr.XcursorManager, + +motion: wl.Listener(*wlr.Pointer.event.Motion) = .init(handleMotion), +motion_absolute: wl.Listener(*wlr.Pointer.event.MotionAbsolute) = .init(handleMotionAbsolute), +button: wl.Listener(*wlr.Pointer.event.Button) = .init(handleButton), +axis: wl.Listener(*wlr.Pointer.event.Axis) = .init(handleAxis), +frame: wl.Listener(*wlr.Cursor) = .init(handleFrame), +hold_begin: wl.Listener(*wlr.Pointer.event.HoldBegin) = .init(handleHoldBegin), +hold_end: wl.Listener(*wlr.Pointer.event.HoldEnd) = .init(handleHoldEnd), + + +pub fn init(self: *Cursor) !void { + self.* = .{ + .wlr_cursor = try wlr.Cursor.create(), + .x_cursor_manager = try wlr.XcursorManager.create(null, 24), + }; + + try self.x_cursor_manager.load(1); + + 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.wlr_cursor.destroy(); + self.x_cursor_manager.destroy(); + + self.motion.link.remove(); + self.motion_absolute.link.remove(); + self.button.link.remove(); + self.axis.link.remove(); + self.frame.link.remove(); +} + +pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { + if (server.root.viewAt(self.wlr_cursor.x, self.wlr_cursor.y)) |res| { + server.seat.wlr_seat.pointerNotifyEnter(res.surface, res.sx, res.sy); + server.seat.wlr_seat.pointerNotifyMotion(time_msec, res.sx, res.sy); + } else { + self.wlr_cursor.setXcursor(self.x_cursor_manager, "default"); + server.seat.wlr_seat.pointerClearFocus(); + } +} + +// --------- WLR Cursor event handlers --------- +fn handleMotion( + _: *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); +} + +fn handleMotionAbsolute( + _: *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); +} + +fn handleButton( + _: *wl.Listener(*wlr.Pointer.event.Button), + event: *wlr.Pointer.event.Button, +) void { + _ = server.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state); + // TODO: figure out what this listener is supposed to do + + // if (event.state == .released) { + // server.cursor_mode = .passthrough; + // } else if (server.viewAt(server.cursor.x, server.cursor.y)) |res| { + // server.focusView(res.toplevel, res.surface); + // } +} + +fn handleHoldBegin( + listener: *wl.Listener(*wlr.Pointer.event.HoldBegin), + event: *wlr.Pointer.event.HoldBegin +) void { + _ = listener; + _ = event; + std.log.err("Unimplemented cursor being 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, +) void { + 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(); +} diff --git a/src/keyboard.zig b/src/keyboard.zig index 49cd989..734f09d 100644 --- a/src/keyboard.zig +++ b/src/keyboard.zig @@ -2,66 +2,74 @@ const Keyboard = @This(); const std = @import("std"); const gpa = std.heap.c_allocator; +const server = &@import("main.zig").server; const wl = @import("wayland").server.wl; const wlr = @import("wlroots"); const xkb = @import("xkbcommon"); -const Server = @import("server.zig"); - -server: *Server, -link: wl.list.Link = undefined, +wlr_keyboard: *wlr.Keyboard, device: *wlr.InputDevice, -modifiers: wl.Listener(*wlr.Keyboard) = .init(handleModifiers), +keyboards: wl.list.Head(Keyboard, .link) = undefined, + +link: wl.list.Link = undefined, + +// Keyboard listeners key: wl.Listener(*wlr.Keyboard.event.Key) = .init(handleKey), +key_map: wl.Listener(*wlr.Keyboard) = .init(handleKeyMap), +modifiers: wl.Listener(*wlr.Keyboard) = .init(handleModifiers), + +// Device listeners destroy: wl.Listener(*wlr.InputDevice) = .init(handleDestroy), -pub fn create(server: *Server, device: *wlr.InputDevice) !void { - const keyboard = try gpa.create(Keyboard); - errdefer gpa.destroy(keyboard); - keyboard.* = .{ - .server = server, +pub fn init(self: *Keyboard, device: *wlr.InputDevice) !void { + self.* = .{ + .wlr_keyboard = device.toKeyboard(), .device = device, }; const context = xkb.Context.new(.no_flags) orelse return error.ContextFailed; defer context.unref(); + const keymap = xkb.Keymap.newFromNames(context, null, .no_flags) orelse return error.KeymapFailed; defer keymap.unref(); - const wlr_keyboard = device.toKeyboard(); // TODO: configure this via lua later - if (!wlr_keyboard.setKeymap(keymap)) return error.SetKeymapFailed; - wlr_keyboard.setRepeatInfo(25, 600); + if (!self.wlr_keyboard.setKeymap(keymap)) return error.SetKeymapFailed; + self.wlr_keyboard.setRepeatInfo(25, 600); - wlr_keyboard.events.modifiers.add(&keyboard.modifiers); - wlr_keyboard.events.key.add(&keyboard.key); - device.events.destroy.add(&keyboard.destroy); + 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); - std.log.debug("adding keyboard: {s}", .{keyboard.*.device.*.name orelse "(null)"}); + device.events.destroy.add(&self.destroy); - server.seat.setKeyboard(wlr_keyboard); - server.keyboards.append(keyboard); + std.log.debug("adding keyboard: {s}", .{self.wlr_keyboard.base.name orelse "(null)"}); + + server.seat.wlr_seat.setKeyboard(self.wlr_keyboard); + + self.keyboards.init(); + self.keyboards.append(self); } -pub fn handleModifiers(listener: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) void { - const keyboard: *Keyboard = @fieldParentPtr("modifiers", listener); - keyboard.server.seat.setKeyboard(wlr_keyboard); - keyboard.server.seat.keyboardNotifyModifiers(&wlr_keyboard.modifiers); +// pub fn destroy(self: *Keyboard) { +// +// } + +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); } -pub fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void { - const keyboard: *Keyboard = @fieldParentPtr("key", listener); - const wlr_keyboard = keyboard.device.toKeyboard(); - +fn handleKey(_: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void { // Translate libinput keycode -> xkbcommon // const keycode = event.keycode + 8; // TODO: lua handle keybinds here const handled = false; - if (wlr_keyboard.getModifiers().alt and event.state == .pressed) { + if (server.keyboard.wlr_keyboard.getModifiers().alt and event.state == .pressed) { // for (wlr_keyboard.xkb_state.?.keyGetSyms(keycode)) |sym| { // if (keyboard.server.handleKeybind(sym)) { // handled = true; @@ -71,11 +79,15 @@ pub fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Ke } if (!handled) { - keyboard.server.seat.setKeyboard(wlr_keyboard); - keyboard.server.seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state); + server.seat.wlr_seat.setKeyboard(server.keyboard.wlr_keyboard); + server.seat.wlr_seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state); } } +fn handleKeyMap(_: *wl.Listener(*wlr.Keyboard), _: *wlr.Keyboard) void { + std.log.err("Unimplemented handle keyboard keymap", .{}); +} + pub fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) void { const keyboard: *Keyboard = @fieldParentPtr("destroy", listener); diff --git a/src/main.zig b/src/main.zig index 86cbc12..6f6bcc6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,7 +5,6 @@ const Server = @import("server.zig"); const gpa = std.heap.c_allocator; - pub var server: Server = undefined; pub fn main() !void { diff --git a/src/output.zig b/src/output.zig index 77a48cb..d2e0f35 100644 --- a/src/output.zig +++ b/src/output.zig @@ -11,6 +11,7 @@ const wlr = @import("wlroots"); const Server = @import("server.zig"); wlr_output: *wlr.Output, +scene_output: *wlr.SceneOutput, frame: wl.Listener(*wlr.Output) = .init(handleFrame), request_state: wl.Listener(*wlr.Output.event.RequestState) = .init(handleRequestState), @@ -22,25 +23,34 @@ pub fn create(wlr_output: *wlr.Output) !*Output { output.* = .{ .wlr_output = wlr_output, + .scene_output = try server.root.scene.createSceneOutput(wlr_output) }; + wlr_output.events.frame.add(&output.frame); wlr_output.events.request_state.add(&output.request_state); wlr_output.events.destroy.add(&output.destroy); std.log.debug("adding output: {s}", .{output.*.wlr_output.*.name}); - const layout_output = try server.output_layout.addAuto(wlr_output); + // I don't think we need the result of this + _ = try server.root.output_layout.addAuto(wlr_output); - const scene_output = try server.scene.createSceneOutput(wlr_output); - server.scene_output_layout.addOutput(layout_output, scene_output); + // I beive this has the output render the entire scene which is finefor now return output; } -pub fn handleRequestState( -listener: *wl.Listener(*wlr.Output.event.RequestState), -event: *wlr.Output.event.RequestState, - ) void { +// Conflicting name with destroy listener +// Should probably add _listner as a postfix to listeners +// +// pub fn destroy(output: *Output) void { +// gpa.free(output); +// } + +fn handleRequestState( + listener: *wl.Listener(*wlr.Output.event.RequestState), + event: *wlr.Output.event.RequestState, +) void { std.log.debug("Handling request state", .{}); const output: *Output = @fieldParentPtr("request_state", listener); @@ -49,10 +59,13 @@ event: *wlr.Output.event.RequestState, } } -pub fn handleFrame(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void { +fn handleFrame( + _: *wl.Listener(*wlr.Output), + wlr_output: *wlr.Output +) void { std.log.debug("Handling frame for {s}", .{wlr_output.name}); - const scene_output = server.scene.*.getSceneOutput(wlr_output); + const scene_output = server.root.scene.getSceneOutput(wlr_output); if(scene_output) |so| { std.log.info("Rendering commited scene output\n", .{}); @@ -64,7 +77,10 @@ pub fn handleFrame(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void { } -pub fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void { +fn handleDestroy( + listener: *wl.Listener(*wlr.Output), + _: *wlr.Output +) void { std.log.debug("Handling destroy", .{}); const output: *Output = @fieldParentPtr("destroy", listener); diff --git a/src/root.zig b/src/root.zig index 0b460a3..18a5fb8 100644 --- a/src/root.zig +++ b/src/root.zig @@ -5,20 +5,19 @@ const wl = @import("wayland").server.wl; const wlr = @import("wlroots"); const Output = @import("output.zig"); -const TopLevel = @import("toplevel.zig"); +const View = @import("view.zig"); const server = &@import("main.zig").server; const gpa = std.heap.c_allocator; scene: *wlr.Scene, +scene_output_layout: *wlr.SceneOutputLayout, output_layout: *wlr.OutputLayout, -new_output: wl.Listener(*wlr.Output), +all_views: std.ArrayList(*View), -all_top_levels: std.ArrayList(*TopLevel), - -pub fn init(root: *Root) !void { +pub fn init(self: *Root) !void { std.log.info("Creating root of mezzaluna\n", .{}); const output_layout = try wlr.OutputLayout.create(server.wl_server); @@ -27,16 +26,19 @@ pub fn init(root: *Root) !void { const scene = try wlr.Scene.create(); errdefer scene.tree.node.destroy(); - root.* = .{ + self.* = .{ .scene = scene, .output_layout = output_layout, - .new_output = .init(handleNewOutput), + .scene_output_layout = try scene.attachOutputLayout(output_layout), - .all_top_levels = try .initCapacity(gpa, 10), + .all_views = try .initCapacity(gpa, 10), }; +} - server.backend.events.new_output.add(&root.new_output); +pub fn deinit(self: *Root) void { + self.output_layout.destroy(); + self.scene.tree.node.destroy(); } pub fn addOutput(self: *Root, new_output: *Output) void { @@ -46,35 +48,44 @@ pub fn addOutput(self: *Root, new_output: *Output) void { }; } -pub fn addTopLevel(self: *Root, top_level: *TopLevel) void { - self.all_top_levels.append(gpa, top_level) catch { - std.log.err("Out of memory to append top level", .{}); +pub fn addView(self: *Root, view: *View) void { + self.all_views.append(gpa, view) catch { + std.log.err("Out of memory to append view", .{}); }; - // self.scene.tree.children.append(wlr.SceneNode) + _ = self.scene.tree.createSceneXdgSurface(view.xdg_toplevel.base) catch { + std.log.err("Unable to create scene node for new view", .{}); + }; } -fn handleNewOutput(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void { - std.log.info("Handling a new output - {s}", .{wlr_output.name}); +const ViewAtResult = struct { + // TODO: uncomment when we have toplevels + // toplevel: *Toplevel, + surface: *wlr.Surface, + sx: f64, + sy: f64, +}; - if (!wlr_output.initRender(server.allocator, server.renderer)) return; +pub fn viewAt(self: *Root, lx: f64, ly: f64) ?ViewAtResult { + var sx: f64 = undefined; + var sy: f64 = undefined; + if (self.scene.tree.node.at(lx, ly, &sx, &sy)) |node| { + if (node.type != .buffer) return null; + // TODO: uncomment when we have toplevels + // const scene_buffer = wlr.SceneBuffer.fromNode(node); + // const scene_surface = wlr.SceneSurface.tryFromBuffer(scene_buffer) orelse return null; - var state = wlr.Output.State.init(); - defer state.finish(); - - state.setEnabled(true); - - if (wlr_output.preferredMode()) |mode| { - state.setMode(mode); + var it: ?*wlr.SceneTree = node.parent; + while (it) |n| : (it = n.node.parent) { + // if (@as(?*Toplevel, @ptrCast(@alignCast(n.node.data)))) |toplevel| { + // return ViewAtResult{ + // .toplevel = toplevel, + // .surface = scene_surface.surface, + // .sx = sx, + // .sy = sy, + // }; + // } + } } - if (!wlr_output.commitState(&state)) return; - - const new_output = Output.create(wlr_output) catch { - std.log.err("failed to allocate new output", .{}); - wlr_output.destroy(); - return; - }; - - server.root.addOutput(new_output); + return null; } - diff --git a/src/seat.zig b/src/seat.zig new file mode 100644 index 0000000..0b151b8 --- /dev/null +++ b/src/seat.zig @@ -0,0 +1,45 @@ +const Seat = @This(); + +const std = @import("std"); +const server = &@import("main.zig").server; + +const wlr = @import("wlroots"); +const wl = @import("wayland").server.wl; + +wlr_seat: *wlr.Seat, + +request_set_cursor: wl.Listener(*wlr.Seat.event.RequestSetCursor) = .init(handleRequestSetCursor), +request_set_selection: wl.Listener(*wlr.Seat.event.RequestSetSelection) = .init(handleRequestSetSelection), +// request_set_primary_selection +// request_start_drage + +pub fn init(self: *Seat) !void { + self.* = .{ + .wlr_seat = try wlr.Seat.create(server.wl_server, "default"), + }; + + self.wlr_seat.events.request_set_cursor.add(&self.request_set_cursor); + self.wlr_seat.events.request_set_selection.add(&self.request_set_selection); +} + +pub fn deinit(self: *Seat) void { + self.wlr_seat.destroy(); + + self.request_set_cursor.link.remove(); + self.request_set_selection.link.remove(); +} + +fn handleRequestSetCursor( + _: *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); +} + +fn handleRequestSetSelection ( + _: *wl.Listener(*wlr.Seat.event.RequestSetSelection), + event: *wlr.Seat.event.RequestSetSelection, +) void { + server.seat.wlr_seat.setSelection(event.source, event.serial); +} diff --git a/src/server.zig b/src/server.zig index 2a70767..8c35a72 100644 --- a/src/server.zig +++ b/src/server.zig @@ -1,263 +1,177 @@ const Server = @This(); const std = @import("std"); -const gpa = std.heap.c_allocator; - const wl = @import("wayland").server.wl; const wlr = @import("wlroots"); -const Output = @import("output.zig"); -const Keyboard = @import("keyboard.zig"); -const TopLevel = @import("toplevel.zig"); 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 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, + +// Input allocator: *wlr.Allocator, -backend: *wlr.Backend, -compositor: *wlr.Compositor, -event_loop: *wl.EventLoop, -output_layout: *wlr.OutputLayout, -renderer: *wlr.Renderer, -scene: *wlr.Scene, -scene_output_layout: *wlr.SceneOutputLayout, -session: ?*wlr.Session, -shm: *wlr.Shm, -wl_server: *wl.Server, -xdg_shell: *wlr.XdgShell, + root: Root, +seat: Seat, +cursor: Cursor, +keyboard: Keyboard, -// Input things -seat: *wlr.Seat, -keyboards: wl.list.Head(Keyboard, .link) = undefined, -cursor: *wlr.Cursor, -cursor_mgr: *wlr.XcursorManager, - -// Listeners -new_input: wl.Listener(*wlr.InputDevice) = .init(newInput), +// 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_surface: wl.Listener(*wlr.XdgSurface) = .init(handleNewXdgSurface), +// new_xdg_popup +// new_xdg_toplevel -cursor_motion: wl.Listener(*wlr.Pointer.event.Motion) = .init(cursorMotion), -cursor_motion_absolute: wl.Listener(*wlr.Pointer.event.MotionAbsolute) = .init(cursorMotionAbsolute), -cursor_button: wl.Listener(*wlr.Pointer.event.Button) = .init(cursorButton), -cursor_axis: wl.Listener(*wlr.Pointer.event.Axis) = .init(cursorAxis), -cursor_frame: wl.Listener(*wlr.Cursor) = .init(cursorFrame), -request_set_cursor: wl.Listener(*wlr.Seat.event.RequestSetCursor) = .init(requestSetCursor), -request_set_selection: wl.Listener(*wlr.Seat.event.RequestSetSelection) = .init(requestSetSelection), +// Seat listeners -pub fn init(server: *Server) !void { +pub fn init(self: *Server) !void { const wl_server = try wl.Server.create(); const event_loop = wl_server.getEventLoop(); var session: ?*wlr.Session = undefined; const backend = try wlr.Backend.autocreate(event_loop, &session); const renderer = try wlr.Renderer.autocreate(backend); - const output_layout = try wlr.OutputLayout.create(wl_server); - const scene = try wlr.Scene.create(); - // Do we need to fail if session is NULL - - server.* = .{ + self.* = .{ .wl_server = wl_server, .backend = backend, .renderer = renderer, .allocator = try wlr.Allocator.autocreate(backend, renderer), - .scene = scene, - .output_layout = output_layout, - .scene_output_layout = try scene.attachOutputLayout(output_layout), .xdg_shell = try wlr.XdgShell.create(wl_server, 2), .event_loop = event_loop, .session = session, .compositor = try wlr.Compositor.create(wl_server, 6, renderer), .shm = try wlr.Shm.createWithRenderer(wl_server, 1, renderer), - .seat = try wlr.Seat.create(wl_server, "default"), - .cursor = try wlr.Cursor.create(), // TODO: let the user configure a cursor theme and side lua - .cursor_mgr = try wlr.XcursorManager.create(null, 24), .root = undefined, + .seat = undefined, + .cursor = undefined, + .keyboard = undefined, }; - try server.renderer.initServer(wl_server); - try Root.init(&server.root); + try self.renderer.initServer(wl_server); - server.xdg_shell.events.new_surface.add(&server.new_xdg_surface); + try self.root.init(); + try self.seat.init(); + try self.cursor.init(); - _ = try wlr.Subcompositor.create(server.wl_server); - _ = try wlr.DataDeviceManager.create(server.wl_server); + _ = try wlr.Subcompositor.create(self.wl_server); + _ = try wlr.DataDeviceManager.create(self.wl_server); - server.backend.events.new_input.add(&server.new_input); + // 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); - server.seat.events.request_set_cursor.add(&server.request_set_cursor); - server.seat.events.request_set_selection.add(&server.request_set_selection); + // XdgShell events + self.xdg_shell.events.new_surface.add(&self.new_xdg_surface); - server.keyboards.init(); - - server.cursor.attachOutputLayout(server.output_layout); - try server.cursor_mgr.load(1); - server.cursor.events.motion.add(&server.cursor_motion); - server.cursor.events.motion_absolute.add(&server.cursor_motion_absolute); - server.cursor.events.button.add(&server.cursor_button); - server.cursor.events.axis.add(&server.cursor_axis); - server.cursor.events.frame.add(&server.cursor_frame); } -pub fn deinit(server: *Server) void { - server.wl_server.destroyClients(); +pub fn deinit(self: *Server) void { + self.seat.deinit(); + self.root.deinit(); + self.cursor.deinit(); - server.cursor.destroy(); - server.cursor_mgr.destroy(); + self.new_input.link.remove(); + self.new_output.link.remove(); - server.new_input.link.remove(); - server.cursor_motion.link.remove(); - server.cursor_motion_absolute.link.remove(); - server.cursor_button.link.remove(); - server.cursor_axis.link.remove(); - server.cursor_frame.link.remove(); - server.request_set_cursor.link.remove(); - server.request_set_selection.link.remove(); + self.wl_server.destroyClients(); - server.backend.destroy(); - server.seat.destroy(); - server.wl_server.destroy(); + self.backend.destroy(); + self.wl_server.destroy(); } -fn newInput(listener: *wl.Listener(*wlr.InputDevice), device: *wlr.InputDevice) void { - const server: *Server = @fieldParentPtr("new_input", listener); +// --------- Backend event handlers --------- +fn handleNewInput( + _: *wl.Listener(*wlr.InputDevice), + device: *wlr.InputDevice +) void { switch (device.type) { - .keyboard => Keyboard.create(server, device) catch |err| { - std.log.err("failed to create keyboard: {}", .{err}); - return; + .keyboard => server.keyboard.init(device) catch { + std.log.err("Unable to create keyboard from device {s}", .{device.name orelse "(null)"}); + }, + .pointer => server.cursor.wlr_cursor.attachInputDevice(device), + else => { + std.log.err( + "New input request for input that is not a keyboard or pointer: {s}", + .{device.name orelse "(null)"} + ); }, - .pointer => server.cursor.attachInputDevice(device), - else => {}, } - server.seat.setCapabilities(.{ + server.seat.wlr_seat.setCapabilities(.{ .pointer = true, - .keyboard = server.keyboards.length() > 0, + .keyboard = server.keyboard.keyboards.length() > 0, }); } -fn cursorMotion( - listener: *wl.Listener(*wlr.Pointer.event.Motion), - event: *wlr.Pointer.event.Motion, +fn handleNewOutput( + _: *wl.Listener(*wlr.Output), + wlr_output: *wlr.Output ) void { - const server: *Server = @fieldParentPtr("cursor_motion", listener); - server.cursor.move(event.device, event.delta_x, event.delta_y); - server.processCursorMotion(event.time_msec); -} + std.log.info("Handling a new output - {s}", .{wlr_output.name}); -fn cursorMotionAbsolute( - listener: *wl.Listener(*wlr.Pointer.event.MotionAbsolute), - event: *wlr.Pointer.event.MotionAbsolute, -) void { - const server: *Server = @fieldParentPtr("cursor_motion_absolute", listener); - server.cursor.warpAbsolute(event.device, event.x, event.y); - server.processCursorMotion(event.time_msec); -} + if (!wlr_output.initRender(server.allocator, server.renderer)) return; -fn processCursorMotion(server: *Server, time_msec: u32) void { - if (server.viewAt(server.cursor.x, server.cursor.y)) |res| { - server.seat.pointerNotifyEnter(res.surface, res.sx, res.sy); - server.seat.pointerNotifyMotion(time_msec, res.sx, res.sy); - } else { - server.cursor.setXcursor(server.cursor_mgr, "default"); - server.seat.pointerClearFocus(); + 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) catch { + std.log.err("failed to allocate new output", .{}); + wlr_output.destroy(); + return; + }; + + server.root.addOutput(new_output); } -const ViewAtResult = struct { - // TODO: uncomment when we have toplevels - // toplevel: *Toplevel, - surface: *wlr.Surface, - sx: f64, - sy: f64, -}; -fn viewAt(server: *Server, lx: f64, ly: f64) ?ViewAtResult { - var sx: f64 = undefined; - var sy: f64 = undefined; - if (server.scene.tree.node.at(lx, ly, &sx, &sy)) |node| { - if (node.type != .buffer) return null; - // TODO: uncomment when we have toplevels - // const scene_buffer = wlr.SceneBuffer.fromNode(node); - // const scene_surface = wlr.SceneSurface.tryFromBuffer(scene_buffer) orelse return null; - - var it: ?*wlr.SceneTree = node.parent; - while (it) |n| : (it = n.node.parent) { - // if (@as(?*Toplevel, @ptrCast(@alignCast(n.node.data)))) |toplevel| { - // return ViewAtResult{ - // .toplevel = toplevel, - // .surface = scene_surface.surface, - // .sx = sx, - // .sy = sy, - // }; - // } - } - } - return null; -} - -fn cursorButton( - listener: *wl.Listener(*wlr.Pointer.event.Button), - event: *wlr.Pointer.event.Button, -) void { - const server: *Server = @fieldParentPtr("cursor_button", listener); - _ = server.seat.pointerNotifyButton(event.time_msec, event.button, event.state); - // TODO: figure out what this listener is supposed to do - - // if (event.state == .released) { - // server.cursor_mode = .passthrough; - // } else if (server.viewAt(server.cursor.x, server.cursor.y)) |res| { - // server.focusView(res.toplevel, res.surface); - // } -} - -fn cursorAxis( - listener: *wl.Listener(*wlr.Pointer.event.Axis), - event: *wlr.Pointer.event.Axis, -) void { - const server: *Server = @fieldParentPtr("cursor_axis", listener); - server.seat.pointerNotifyAxis( - event.time_msec, - event.orientation, - event.delta, - event.delta_discrete, - event.source, - event.relative_direction, - ); -} - -fn cursorFrame(listener: *wl.Listener(*wlr.Cursor), _: *wlr.Cursor) void { - const server: *Server = @fieldParentPtr("cursor_frame", listener); - server.seat.pointerNotifyFrame(); -} - -fn requestSetCursor( - listener: *wl.Listener(*wlr.Seat.event.RequestSetCursor), - event: *wlr.Seat.event.RequestSetCursor, -) void { - const server: *Server = @fieldParentPtr("request_set_cursor", listener); - if (event.seat_client == server.seat.pointer_state.focused_client) - server.cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y); -} - -fn requestSetSelection( - listener: *wl.Listener(*wlr.Seat.event.RequestSetSelection), +fn handleRequestSetSelection( + _: *wl.Listener(*wlr.Seat.event.RequestSetSelection), event: *wlr.Seat.event.RequestSetSelection, ) void { - const server: *Server = @fieldParentPtr("request_set_selection", listener); server.seat.setSelection(event.source, event.serial); } -fn handleNewXdgSurface(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void { - const server: *Server = @fieldParentPtr("new_xdg_surface", listener); - +fn handleNewXdgSurface( + _: *wl.Listener(*wlr.XdgSurface), + xdg_surface: *wlr.XdgSurface +) void { std.log.info("New xdg_toplevel added", .{}); - const top_level = TopLevel.init(xdg_surface) catch { + const view = View.init(xdg_surface) catch { std.log.err("Unable to allocate a top level", .{}); return; }; - server.root.addTopLevel(top_level); + server.root.addView(view); } diff --git a/src/toplevel.zig b/src/toplevel.zig deleted file mode 100644 index 8de9bf6..0000000 --- a/src/toplevel.zig +++ /dev/null @@ -1,35 +0,0 @@ -const TopLevel = @This(); - -const std = @import("std"); -const wl = @import("wayland").server.wl; -const wlr = @import("wlroots"); - -const gpa = std.heap.c_allocator; -const server = &@import("main.zig").server; - -xdg_toplevel: *wlr.XdgToplevel, -box: *wlr.Box, - -pub fn init(xdg_surface: *wlr.XdgSurface) !*TopLevel { - const top_level = gpa.create(TopLevel) catch |err| { - std.log.err("Unable to allocate memory for new XdgTopLevel", .{}); - return err; - }; - - if(xdg_surface.role_data.toplevel) |xgd_toplevel| { - top_level.* = .{ - .xdg_toplevel = xgd_toplevel, - .box = undefined, - }; - - top_level.box.x = 0; - top_level.box.y = 0; - top_level.box.width = 640; - top_level.box.height = 360; - } - - // Listen to important toplevel events - // top_level.xdg_toplevel.events.set_title.add(listener: *Listener(void)) - - return top_level; -} diff --git a/src/view.zig b/src/view.zig new file mode 100644 index 0000000..6f30df6 --- /dev/null +++ b/src/view.zig @@ -0,0 +1,65 @@ +const View = @This(); + +const std = @import("std"); +const wl = @import("wayland").server.wl; +const wlr = @import("wlroots"); + +const gpa = std.heap.c_allocator; +const server = &@import("main.zig").server; + +xdg_toplevel: *wlr.XdgToplevel, +box: *wlr.Box, + +// Listeners +destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy), +map: wl.Listener(void) = wl.Listener(void).init(handleMap), +unmap: wl.Listener(void) = wl.Listener(void).init(handleUnmap), +commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit), +new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup), + +pub fn init(xdg_surface: *wlr.XdgSurface) !*View { + const view = gpa.create(View) catch |err| { + std.log.err("Unable to allocate memory for new XdgTopLevel", .{}); + return err; + }; + + if(xdg_surface.role_data.toplevel) |xgd_toplevel| { + view.* = .{ + .xdg_toplevel = xgd_toplevel, + .box = undefined, + }; + + view.box.x = 0; + view.box.y = 0; + } + + return view; +} + +// --------- XdgTopLevel event handlers --------- +fn handleMap(listener: *wl.Listener(void)) void { + _ = listener; + std.log.err("Unimplemented view handle map", .{}); +} + +fn handleUnmap(listener: *wl.Listener(void)) void { + _ = listener; + std.log.err("Unimplemented view handle unamp", .{}); +} + +fn handleDestroy(listener: *wl.Listener(void)) void { + _ = listener; + std.log.err("Unimplemented view handle destroy", .{}); +} + +fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void { + _ = listener; + _ = surface; + std.log.err("Unimplemented view handle commit", .{}); +} + +fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), popup: *wlr.XdgPopup) void { + _ = listener; + _ = popup; + std.log.err("Unimplemented view handle new popup", .{}); +}