Implemented wayland initialization code

This commit is contained in:
Lorenzo Torres 2025-08-03 23:23:03 +02:00
parent f43e03d6f3
commit f894fb317d
10 changed files with 244 additions and 155 deletions

View file

@ -1 +0,0 @@
# Sideros

View file

@ -7,5 +7,6 @@ layout (binding = 0) uniform Uniform {
} ubo; } ubo;
void main() { void main() {
gl_Position = ubo.proj * vec4(vertPos, 1.0); vec4 out_vec = ubo.proj * vec4(vertPos, 1.0);
gl_Position = vec4(out_vec.x, out_vec.y, 0.5, out_vec.w);
} }

View file

@ -6,58 +6,6 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const glfw_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
});
const glfw = b.addLibrary(.{
.name = "glfw",
.root_module = glfw_module,
});
glfw_module.addCSourceFiles(.{ .files = &[_][]const u8{
"ext/glfw/src/cocoa_init.m",
"ext/glfw/src/cocoa_joystick.m",
"ext/glfw/src/cocoa_monitor.m",
"ext/glfw/src/cocoa_time.c",
"ext/glfw/src/cocoa_window.m",
"ext/glfw/src/context.c",
"ext/glfw/src/egl_context.c",
"ext/glfw/src/glx_context.c",
"ext/glfw/src/init.c",
"ext/glfw/src/input.c",
"ext/glfw/src/linux_joystick.c",
"ext/glfw/src/monitor.c",
"ext/glfw/src/nsgl_context.m",
"ext/glfw/src/null_init.c",
"ext/glfw/src/null_joystick.c",
"ext/glfw/src/null_monitor.c",
"ext/glfw/src/null_window.c",
"ext/glfw/src/osmesa_context.c",
"ext/glfw/src/platform.c",
"ext/glfw/src/posix_module.c",
"ext/glfw/src/posix_poll.c",
"ext/glfw/src/posix_thread.c",
"ext/glfw/src/posix_time.c",
"ext/glfw/src/vulkan.c",
"ext/glfw/src/wgl_context.c",
"ext/glfw/src/win32_init.c",
"ext/glfw/src/win32_joystick.c",
"ext/glfw/src/win32_module.c",
"ext/glfw/src/win32_monitor.c",
"ext/glfw/src/win32_thread.c",
"ext/glfw/src/win32_time.c",
"ext/glfw/src/win32_window.c",
"ext/glfw/src/window.c",
"ext/glfw/src/wl_init.c",
"ext/glfw/src/wl_monitor.c",
"ext/glfw/src/wl_window.c",
"ext/glfw/src/x11_init.c",
"ext/glfw/src/x11_monitor.c",
"ext/glfw/src/x11_window.c",
"ext/glfw/src/xkb_unicode.c",
}, .flags = &[_][]const u8{ "-D_GLFW_X11", "-Wall", "-Wextra" } });
const sideros = b.createModule(.{ const sideros = b.createModule(.{
.root_source_file = b.path("src/sideros.zig"), .root_source_file = b.path("src/sideros.zig"),
.target = target, .target = target,
@ -88,7 +36,6 @@ pub fn build(b: *std.Build) void {
renderer.addImport("ecs", ecs); renderer.addImport("ecs", ecs);
ecs.addImport("renderer", renderer); ecs.addImport("renderer", renderer);
renderer.addIncludePath(b.path("ext/glfw/include"));
compileAllShaders(b, renderer); compileAllShaders(b, renderer);
sideros.addImport("mods", mods); sideros.addImport("mods", mods);
@ -104,10 +51,11 @@ pub fn build(b: *std.Build) void {
}), }),
}); });
exe.root_module.addImport("sideros", sideros); exe.root_module.addImport("sideros", sideros);
exe.root_module.addCSourceFile(.{ .file = b.path("ext/xdg-shell.c") });
exe.root_module.addIncludePath(b.path("ext"));
exe.linkSystemLibrary("vulkan"); exe.linkSystemLibrary("vulkan");
exe.linkSystemLibrary("wayland-client"); exe.linkSystemLibrary("wayland-client");
exe.linkLibrary(glfw);
exe.linkLibC(); exe.linkLibC();
b.installArtifact(exe); b.installArtifact(exe);

4
src/c.zig Normal file
View file

@ -0,0 +1,4 @@
pub const c = @cImport({
@cInclude("wayland-client.h");
@cInclude("xdg-shell.h");
});

View file

@ -4,73 +4,67 @@ const math = @import("sideros").math;
const Input = @import("sideros").Input; const Input = @import("sideros").Input;
const mods = @import("sideros").mods; const mods = @import("sideros").mods;
const ecs = @import("sideros").ecs; const ecs = @import("sideros").ecs;
const Renderer = @import("sideros").Renderer; //const Renderer = @import("sideros").Renderer;
const wayland = @import("wayland.zig");
fn testSystem2(pool: *ecs.Pool) void { //fn testSystem2(pool: *ecs.Pool) void {
std.debug.print("{any}\n", .{pool.resources.input.isKeyDown(.a)}); // std.debug.print("{any}\n", .{pool.resources.input.isKeyDown(.a)});
} //}
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator(); const allocator = gpa.allocator();
defer if (gpa.deinit() != .ok) @panic("Leaked memory"); defer if (gpa.deinit() != .ok) @panic("Leaked memory");
var global_runtime = mods.GlobalRuntime.init(allocator); //var global_runtime = mods.GlobalRuntime.init(allocator);
defer global_runtime.deinit(); //defer global_runtime.deinit();
try global_runtime.addFunction("debug", mods.Wasm.debug); //try global_runtime.addFunction("debug", mods.Wasm.debug);
const file = try std.fs.cwd().openFile("assets/core.wasm", .{}); //const file = try std.fs.cwd().openFile("assets/core.wasm", .{});
const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB //const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB
defer allocator.free(all); //defer allocator.free(all);
var parser = try mods.Parser.init(allocator, all); //var parser = try mods.Parser.init(allocator, all);
defer parser.deinit(); //defer parser.deinit();
parser.parseModule() catch |err| { //parser.parseModule() catch |err| {
std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] }); // std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] });
return err; // return err;
}; //};
const module = parser.module(); //const module = parser.module();
// defer module.deinit(allocator); //// defer module.deinit(allocator);
var runtime = try mods.Runtime.init(allocator, module, &global_runtime); //var runtime = try mods.Runtime.init(allocator, module, &global_runtime);
defer runtime.deinit(allocator); //defer runtime.deinit(allocator);
var parameters = [_]mods.VM.Value{.{ .i32 = 17 }}; //var parameters = [_]mods.VM.Value{.{ .i32 = 17 }};
try runtime.callExternal(allocator, .preinit, &parameters); //try runtime.callExternal(allocator, .preinit, &parameters);
const result = runtime.stack.pop().?; //const result = runtime.stack.pop().?;
std.debug.print("Result of preinit: {any}\n", .{result}); //std.debug.print("Result of preinit: {any}\n", .{result});
var w = try Renderer.Window.create(800, 600, "sideros"); //var w = try Renderer.Window.create(800, 600, "sideros");
defer w.destroy(); //defer w.destroy();
var r = try Renderer.init(allocator, w); //var r = try Renderer.init(allocator, w);
defer r.deinit(); //defer r.deinit();
const resources = ecs.Resources{ //const resources = ecs.Resources{
.window = w, // .window = w,
.renderer = r, // .renderer = r,
.input = .{ .key_pressed = .{false} ** @intFromEnum(Input.KeyCode.menu) }, // .input = .{ .key_pressed = .{false} ** @intFromEnum(Input.KeyCode.menu) },
}; //};
var pool = try ecs.Pool.init(allocator, resources); //var pool = try ecs.Pool.init(allocator, resources);
defer pool.deinit(); //defer pool.deinit();
w.setResources(&pool.resources); //w.setResources(&pool.resources);
try pool.addSystemGroup(&[_]ecs.System{ //try pool.addSystemGroup(&[_]ecs.System{
Renderer.render, // Renderer.render,
}, true); //}, true);
// try pool.addSystemGroup(&[_]ecs.System{ //try pool.addSystemGroup(&[_]ecs.System{
// testSystem2, // testSystem2,
// }); //});
// for (0..1000) |_| { // for (0..1000) |_| {
// const entity = try pool.createEntity(); // const entity = try pool.createEntity();
// try pool.addComponent(entity, ecs.components.Position{ .x = 1.0, .y = 0.5, .z = 3.0 }); // try pool.addComponent(entity, ecs.components.Position{ .x = 1.0, .y = 0.5, .z = 3.0 });
// try pool.addComponent(entity, ecs.components.Speed{ .speed = 5.0 }); // try pool.addComponent(entity, ecs.components.Speed{ .speed = 5.0 });
// } // }
var last_time: f64 = 0.0; try wayland.init(allocator);
while (!w.shouldClose()) {
const current_time = Renderer.Window.getTime();
pool.resources.delta_time = current_time - last_time;
last_time = current_time;
Renderer.Window.pollEvents();
pool.tick();
}
} }

View file

@ -19,10 +19,10 @@ current_frame: u32,
vertex_buffer: vk.Buffer, vertex_buffer: vk.Buffer,
index_buffer: vk.Buffer, index_buffer: vk.Buffer,
pub fn init(allocator: Allocator, w: Window) !Renderer { pub fn init(allocator: Allocator, display: ?*anyopaque, s: ?*anyopaque) !Renderer {
const instance = try vk.Instance.create(allocator); const instance = try vk.Instance.create(allocator);
const surface = try vk.Surface.create(instance, w); const surface = try vk.Surface.create(instance, display, s);
var physical_device = try vk.PhysicalDevice.pick(allocator, instance); var physical_device = try vk.PhysicalDevice.pick(allocator, instance);
const device = try physical_device.create_device(surface, allocator, 2); const device = try physical_device.create_device(surface, allocator, 2);
@ -34,7 +34,7 @@ pub fn init(allocator: Allocator, w: Window) !Renderer {
const render_pass = try vk.RenderPass(2).create(allocator, device, surface, physical_device); const render_pass = try vk.RenderPass(2).create(allocator, device, surface, physical_device);
const swapchain = try vk.Swapchain(2).create(allocator, surface, device, physical_device, w, render_pass); const swapchain = try vk.Swapchain(2).create(allocator, surface, device, physical_device, render_pass);
const graphics_pipeline = try vk.GraphicsPipeline(2).create(device, swapchain, render_pass, vertex_shader, fragment_shader); const graphics_pipeline = try vk.GraphicsPipeline(2).create(device, swapchain, render_pass, vertex_shader, fragment_shader);
@ -75,8 +75,9 @@ pub fn deinit(self: Renderer) void {
} }
// TODO: render is maybe a bad name? something like present() or submit() is better? // TODO: render is maybe a bad name? something like present() or submit() is better?
pub fn render(pool: *ecs.Pool) anyerror!void { //pub fn render(pool: *ecs.Pool) anyerror!void {
var renderer = pool.resources.renderer; pub fn render(renderer: *Renderer) anyerror!void {
//var renderer = pool.resources.renderer;
try renderer.device.waitFence(renderer.current_frame); try renderer.device.waitFence(renderer.current_frame);
const image = try renderer.swapchain.nextImage(renderer.device, renderer.current_frame); const image = try renderer.swapchain.nextImage(renderer.device, renderer.current_frame);
@ -94,4 +95,6 @@ pub fn render(pool: *ecs.Pool) anyerror!void {
try renderer.device.submit(renderer.swapchain, image, renderer.current_frame); try renderer.device.submit(renderer.swapchain, image, renderer.current_frame);
renderer.current_frame = (renderer.current_frame + 1) % 2; renderer.current_frame = (renderer.current_frame + 1) % 2;
renderer.device.waitIdle();
} }

View file

@ -10,9 +10,8 @@ pub const Error = error{
}; };
pub fn getExtensions() [][*c]const u8 { pub fn getExtensions() [][*c]const u8 {
var extension_count: u32 = undefined; const raw: [*c][*c]const u8 = .{"VK_KHR_wayland_surface", "VK_KHR_surface"};
const raw: [*c][*c]const u8 = c.glfwGetRequiredInstanceExtensions(&extension_count); const extensions = raw[0..2];
const extensions = raw[0..extension_count];
return extensions; return extensions;
} }
@ -20,25 +19,10 @@ pub fn getExtensions() [][*c]const u8 {
title: []const u8, title: []const u8,
width: usize, width: usize,
height: usize, height: usize,
raw: *c.GLFWwindow, raw: *c.wl_display,
pub fn create(width: usize, height: usize, title: []const u8) !Window { pub fn create(width: usize, height: usize, title: []const u8) !Window {
if (c.glfwInit() != c.GLFW_TRUE) { const raw = c.wl_display_connect(null);
const status = c.glfwGetError(null);
return switch (status) {
c.GLFW_PLATFORM_UNAVAILABLE => Error.platform_unavailable,
c.GLFW_PLATFORM_ERROR => Error.platform_error,
else => unreachable,
};
}
c.glfwWindowHint(c.GLFW_RESIZABLE, c.GLFW_FALSE);
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
const raw = c.glfwCreateWindow(@intCast(width), @intCast(height), title.ptr, null, null);
c.glfwShowWindow(raw);
_ = c.glfwSetKeyCallback(raw, keyCallback);
_ = c.glfwSetCursorPosCallback(raw, cursorCallback);
return Window{ return Window{
.title = title, .title = title,
@ -52,12 +36,8 @@ pub fn setResources(self: *Window, resources: *ecs.Resources) void {
c.glfwSetWindowUserPointer(self.raw, resources); c.glfwSetWindowUserPointer(self.raw, resources);
} }
pub fn pollEvents() void {
c.glfwPollEvents();
}
pub fn shouldClose(self: Window) bool { pub fn shouldClose(self: Window) bool {
return c.glfwWindowShouldClose(self.raw) == c.GLFW_TRUE; return c.wl_display_dispatch(self.raw) != -1;
} }
pub fn size(self: Window) struct { usize, usize } { pub fn size(self: Window) struct { usize, usize } {
@ -70,8 +50,7 @@ pub fn size(self: Window) struct { usize, usize } {
} }
pub fn destroy(self: Window) void { pub fn destroy(self: Window) void {
c.glfwDestroyWindow(self.raw); c.wl_display_disconnect(self.raw);
c.glfwTerminate();
} }
pub fn getTime() f64 { pub fn getTime() f64 {

View file

@ -1,5 +1,5 @@
pub const c = @cImport({ pub const c = @cImport({
@cDefine("GLFW_INCLUDE_NONE", {});
@cInclude("vulkan/vulkan.h"); @cInclude("vulkan/vulkan.h");
@cInclude("GLFW/glfw3.h"); @cInclude("vulkan/vulkan_wayland.h");
@cInclude("wayland-client.h");
}); });

View file

@ -73,7 +73,8 @@ pub const Instance = struct {
handle: c.VkInstance, handle: c.VkInstance,
pub fn create(allocator: Allocator) !Instance { pub fn create(allocator: Allocator) !Instance {
const extensions = Window.getExtensions(); const extensions = [_][*c]const u8 {c.VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, c.VK_KHR_SURFACE_EXTENSION_NAME};
//const extensions = [_][:0]const u8 {"VK_KHR_wayland_surface\0", "VK_KHR_surface\0"};
// Querry avaliable extensions size // Querry avaliable extensions size
var avaliableExtensionsCount: u32 = 0; var avaliableExtensionsCount: u32 = 0;
@ -140,7 +141,7 @@ pub const Instance = struct {
.sType = c.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .sType = c.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &app_info, .pApplicationInfo = &app_info,
.enabledExtensionCount = @intCast(extensions.len), .enabledExtensionCount = @intCast(extensions.len),
.ppEnabledExtensionNames = extensions.ptr, .ppEnabledExtensionNames = @ptrCast(extensions[0..]),
.enabledLayerCount = @intCast(newLayers.items.len), .enabledLayerCount = @intCast(newLayers.items.len),
.ppEnabledLayerNames = newLayers.items.ptr, .ppEnabledLayerNames = newLayers.items.ptr,
}; };
@ -267,7 +268,7 @@ pub fn RenderPass(comptime n: usize) type {
pub fn begin(self: Self, swapchain: Swapchain(n), device: Device(n), image: usize, frame: usize) void { pub fn begin(self: Self, swapchain: Swapchain(n), device: Device(n), image: usize, frame: usize) void {
std.debug.assert(frame < n); std.debug.assert(frame < n);
const clear_color: c.VkClearValue = .{ .color = .{ .float32 = .{ 0.0, 0.0, 0.0, 1.0 } } }; const clear_color: c.VkClearValue = .{ .color = .{ .float32 = .{ 1.0, 0.0, 0.0, 1.0 } } };
const begin_info: c.VkRenderPassBeginInfo = .{ const begin_info: c.VkRenderPassBeginInfo = .{
.sType = c.VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .sType = c.VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
@ -604,7 +605,7 @@ pub fn Swapchain(comptime n: usize) type {
} }
// TODO: Allow to recreate so Window can be resized // TODO: Allow to recreate so Window can be resized
pub fn create(allocator: Allocator, surface: Surface, device: Device(n), physical_device: PhysicalDevice, w: Window, render_pass: RenderPass(n)) !Self { pub fn create(allocator: Allocator, surface: Surface, device: Device(n), physical_device: PhysicalDevice, render_pass: RenderPass(n)) !Self {
const present_modes = try surface.presentModes(allocator, physical_device); const present_modes = try surface.presentModes(allocator, physical_device);
defer allocator.free(present_modes); defer allocator.free(present_modes);
const capabilities = try surface.capabilities(physical_device); const capabilities = try surface.capabilities(physical_device);
@ -625,7 +626,7 @@ pub fn Swapchain(comptime n: usize) type {
if (capabilities.currentExtent.width != std.math.maxInt(u32)) { if (capabilities.currentExtent.width != std.math.maxInt(u32)) {
extent = capabilities.currentExtent; extent = capabilities.currentExtent;
} else { } else {
const width, const height = w.size(); const width: u32, const height: u32 = .{800, 600};
extent = .{ extent = .{
.width = @intCast(width), .width = @intCast(width),
@ -756,10 +757,16 @@ pub fn Swapchain(comptime n: usize) type {
pub const Surface = struct { pub const Surface = struct {
handle: c.VkSurfaceKHR, handle: c.VkSurfaceKHR,
pub fn create(instance: Instance, w: Window) !Surface { pub fn create(instance: Instance, display: ?*anyopaque, surface: ?*anyopaque) !Surface {
var handle: c.VkSurfaceKHR = undefined; var handle: c.VkSurfaceKHR = undefined;
try mapError(c.glfwCreateWindowSurface(instance.handle, w.raw, null, &handle)); const create_info: c.VkWaylandSurfaceCreateInfoKHR = .{
return Surface{ .sType = c.VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
.display = @ptrCast(display),
.surface = @ptrCast(surface),
};
try mapError(c.vkCreateWaylandSurfaceKHR(instance.handle, &create_info, null, &handle));
return .{
.handle = handle, .handle = handle,
}; };
} }
@ -834,7 +841,7 @@ pub fn Device(comptime n: usize) type {
} }
pub fn waitFence(self: Self, frame: usize) !void { pub fn waitFence(self: Self, frame: usize) !void {
std.debug.assert(frame < n); //std.debug.assert(frame < n);
try mapError(c.vkWaitForFences(self.handle, 1, &self.in_flight_fence[frame], c.VK_TRUE, std.math.maxInt(u64))); try mapError(c.vkWaitForFences(self.handle, 1, &self.in_flight_fence[frame], c.VK_TRUE, std.math.maxInt(u64)));
try mapError(c.vkResetFences(self.handle, 1, &self.in_flight_fence[frame])); try mapError(c.vkResetFences(self.handle, 1, &self.in_flight_fence[frame]));
} }
@ -917,28 +924,30 @@ pub fn Device(comptime n: usize) type {
pub fn submit(self: Self, swapchain: Swapchain(n), image: usize, frame: usize) !void { pub fn submit(self: Self, swapchain: Swapchain(n), image: usize, frame: usize) !void {
std.debug.assert(frame < n); std.debug.assert(frame < n);
//const wait_semaphores: [1]c.VkSemaphore = .{self.image_available[frame]}; const wait_semaphores: [1]c.VkSemaphore = .{self.image_available[frame]};
//const signal_semaphores: [1]c.VkSemaphore = .{self.render_finished[frame]}; const signal_semaphores: [1]c.VkSemaphore = .{self.render_finished[frame]};
//const swapchains: [1]c.VkSwapchainKHR = .{swapchain.handle}; const swapchains: [1]c.VkSwapchainKHR = .{swapchain.handle};
_ = swapchains;
const stages: []const u32 = &[_]u32{c.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; const stages: []const u32 = &[_]u32{c.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
const submit_info: c.VkSubmitInfo = .{ const submit_info: c.VkSubmitInfo = .{
.sType = c.VK_STRUCTURE_TYPE_SUBMIT_INFO, .sType = c.VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1, .waitSemaphoreCount = 1,
.pWaitSemaphores = &self.image_available[frame], .pWaitSemaphores = wait_semaphores[0..].ptr,
.pWaitDstStageMask = stages.ptr, .pWaitDstStageMask = stages.ptr,
.commandBufferCount = 1, .commandBufferCount = 1,
.pCommandBuffers = &self.command_buffers[frame], .pCommandBuffers = &self.command_buffers[frame],
.signalSemaphoreCount = 1, .signalSemaphoreCount = 1,
.pSignalSemaphores = &self.render_finished[frame], .pSignalSemaphores = signal_semaphores[0..].ptr,
}; };
_ = c.vkResetFences(self.handle, 1, &self.in_flight_fence[frame]);
try mapError(c.vkQueueSubmit(self.graphics_queue, 1, &submit_info, self.in_flight_fence[frame])); try mapError(c.vkQueueSubmit(self.graphics_queue, 1, &submit_info, self.in_flight_fence[frame]));
const present_info: c.VkPresentInfoKHR = .{ const present_info: c.VkPresentInfoKHR = .{
.sType = c.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .sType = c.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1, .waitSemaphoreCount = 1,
.pWaitSemaphores = &self.render_finished[frame], .pWaitSemaphores = signal_semaphores[0..].ptr,
.swapchainCount = 1, .swapchainCount = 1,
.pSwapchains = &swapchain.handle, .pSwapchains = &swapchain.handle,
.pImageIndices = @ptrCast(&image), .pImageIndices = @ptrCast(&image),

152
src/wayland.zig Normal file
View file

@ -0,0 +1,152 @@
const c = @import("c.zig").c;
const std = @import("std");
const Renderer = @import("sideros").Renderer;
var resize = false;
var quit = false;
var new_width: u32 = 0;
var new_height: u32 = 0;
const State = struct {
compositor: ?*c.wl_compositor = null,
shell: ?*c.xdg_wm_base = null,
surface: ?*c.wl_surface = null,
renderer: *Renderer = undefined,
configured: bool = false,
};
fn registryHandleGlobal(data: ?*anyopaque, registry: ?*c.wl_registry, name: u32, interface: [*c]const u8, version: u32) callconv(.c) void {
_ = version;
const state: *State = @alignCast(@ptrCast(data));
if (std.mem.eql(u8, std.mem.span(interface), std.mem.span(c.wl_compositor_interface.name))) {
state.compositor = @ptrCast(c.wl_registry_bind(registry.?, name, &c.wl_compositor_interface, 4));
} else if (std.mem.eql(u8, @as([:0]const u8, std.mem.span(interface)), std.mem.span(c.xdg_wm_base_interface.name))) {
state.shell = @ptrCast(c.wl_registry_bind(registry.?, name, &c.xdg_wm_base_interface, 4));
_ = c.xdg_wm_base_add_listener(state.shell, &shell_listener, null);
}
}
fn registryHandleGlobalRemove(data: ?*anyopaque, registry: ?*c.wl_registry, name: u32) callconv(.c) void {
_ = data;
_ = registry;
_ = name;
}
fn shellHandlePing(data: ?*anyopaque, shell: ?*c.xdg_wm_base, serial: u32) callconv(.c) void {
_ = data;
c.xdg_wm_base_pong(shell, serial);
}
fn shellHandleSurfaceConfigure(data: ?*anyopaque, surface: ?*c.xdg_surface, serial: u32) callconv(.c) void {
const state: *State = @alignCast(@ptrCast(data));
c.xdg_surface_ack_configure(surface, serial);
state.configured = true;
}
fn toplevelHandleConfigure(data: ?*anyopaque, toplevel: ?*c.xdg_toplevel, width: i32, height: i32, states: ?*c.wl_array) callconv(.c) void {
_ = data;
_ = toplevel;
_ = states;
if (width != 0 and height != 0) {
resize = true;
new_width = @intCast(width);
new_height = @intCast(height);
}
}
fn toplevelHandleClose(data: ?*anyopaque, toplevel: ?*c.xdg_toplevel) callconv(.c) void {
_ = data;
_ = toplevel;
quit = true;
}
fn toplevelHandleConfigureBounds(data: ?*anyopaque, toplevel: ?*c.xdg_toplevel, width: i32, height: i32) callconv(.c) void {
_ = data;
_ = toplevel;
_ = width;
_ = height;
}
fn frameHandleDone(data: ?*anyopaque, callback: ?*c.wl_callback, time: u32) callconv(.c) void {
_ = time;
const state: *State = @alignCast(@ptrCast(data));
_ = c.wl_callback_destroy(callback);
const cb = c.wl_surface_frame(state.surface);
_ = c.wl_callback_add_listener(cb, &frame_listener, state);
state.renderer.render() catch @panic("can't render");
_ = c.wl_surface_commit(state.surface);
}
const frame_listener: c.wl_callback_listener = .{
.done = frameHandleDone,
};
const shell_listener: c.xdg_wm_base_listener = .{
.ping = shellHandlePing,
};
const surface_listener: c.xdg_surface_listener = .{
.configure = shellHandleSurfaceConfigure,
};
const toplevel_listener: c.xdg_toplevel_listener = .{
.configure = toplevelHandleConfigure,
.configure_bounds = toplevelHandleConfigureBounds,
.close = toplevelHandleClose,
};
const registry_listener: c.wl_registry_listener = .{
.global = registryHandleGlobal,
.global_remove = registryHandleGlobalRemove,
};
pub fn init(allocator: std.mem.Allocator) !void {
var state: State = .{};
const display = c.wl_display_connect(null);
defer c.wl_display_disconnect(display);
if (display == null) {
return error.ConnectionFailed;
}
const registry = c.wl_display_get_registry(display);
_ = c.wl_registry_add_listener(registry, &registry_listener, @ptrCast(&state));
_ = c.wl_display_roundtrip(display);
const surface = c.wl_compositor_create_surface(state.compositor);
const xdg_surface = c.xdg_wm_base_get_xdg_surface(state.shell, surface);
_ = c.xdg_surface_add_listener(xdg_surface, &surface_listener, @ptrCast(&state));
state.surface = surface;
const toplevel = c.xdg_surface_get_toplevel(xdg_surface);
_ = c.xdg_toplevel_add_listener(toplevel, &toplevel_listener, null);
const title = [_]u8 {'s', 'i', 'd', 'e', 'r', 'o', 's', 0};
c.xdg_toplevel_set_title(toplevel, @ptrCast(&title[0]));
c.xdg_toplevel_set_app_id(toplevel, @ptrCast(&title[0]));
c.xdg_toplevel_set_min_size(toplevel, 800, 600);
c.xdg_toplevel_set_max_size(toplevel, 800, 600);
_ = c.wl_surface_commit(surface);
while (!state.configured) {
_ = c.wl_display_dispatch(display);
}
var renderer = try Renderer.init(allocator, @ptrCast(display), @ptrCast(surface));
defer renderer.deinit();
try renderer.render();
state.renderer = &renderer;
const cb = c.wl_surface_frame(surface);
_ = c.wl_callback_add_listener(cb, &frame_listener, @ptrCast(&state));
_ = c.wl_surface_commit(surface);
while (!quit) {
_ = c.wl_display_dispatch(display);
}
}