From 3fa889b13665f230594fe74525287c53e3d2de57 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Sat, 13 Jul 2024 19:01:22 +0330 Subject: [PATCH] core: revive wayland --- src/Core.zig | 8 +- src/core/Wayland.zig | 1577 ++++++++++++-------------------- src/core/X11.zig | 53 +- src/core/{x11 => }/unicode.zig | 9 +- src/sysgpu/vulkan.zig | 22 +- src/sysgpu/vulkan/proc.zig | 3 +- 6 files changed, 650 insertions(+), 1022 deletions(-) rename src/core/{x11 => }/unicode.zig (98%) diff --git a/src/Core.zig b/src/Core.zig index a1880e5a..d25c9cdf 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -14,7 +14,7 @@ const Frequency = @import("core/Frequency.zig"); const Platform = switch (build_options.core_platform) { .x11 => @import("core/X11.zig"), - .wayland => @panic("TODO: revive wayland backend"), + .wayland => @import("core/Wayland.zig"), .web => @panic("TODO: revive wasm backend"), }; @@ -148,7 +148,7 @@ fn init(core: *Mod, entities: *mach.Entities.Mod, options: InitOptions) !void { state.title[options.title.len] = 0; } - try Platform.init(&state.platform, options.allocator, options); + try Platform.init(&state.platform, options); state.instance = gpu.createInstance(null) orelse { log.err("failed to create GPU instance", .{}); @@ -951,11 +951,11 @@ comptime { } fn assertHasDecl(comptime T: anytype, comptime decl_name: []const u8) void { - if (!@hasDecl(T, decl_name)) @compileError("Core missing declaration: " ++ decl_name); + if (!@hasDecl(T, decl_name)) @compileError(@typeName(T) ++ " missing declaration: " ++ decl_name); } fn assertHasField(comptime T: anytype, comptime field_name: []const u8) void { - if (!@hasField(T, field_name)) @compileError("Core missing field: " ++ field_name); + if (!@hasField(T, field_name)) @compileError(@typeName(T) ++ " missing field: " ++ field_name); } test { diff --git a/src/core/Wayland.zig b/src/core/Wayland.zig index a55e6412..7eedcb18 100644 --- a/src/core/Wayland.zig +++ b/src/core/Wayland.zig @@ -1,31 +1,25 @@ const std = @import("std"); -const mach = @import("../../../main.zig"); +const mach = @import("../main.zig"); +const Core = @import("../Core.zig"); +const InputState = @import("InputState.zig"); +const Frequency = @import("Frequency.zig"); +const unicode = @import("unicode.zig"); +const detectBackendType = @import("common.zig").detectBackendType; const gpu = mach.gpu; -const InitOptions = @import("../../../Core.zig").InitOptions; -const Event = @import("../../../Core.zig").Event; -const KeyEvent = @import("../../../Core.zig").KeyEvent; -const MouseButtonEvent = @import("../../../Core.zig").MouseButtonEvent; -const MouseButton = @import("../../../Core.zig").MouseButton; -const Size = @import("../../../Core.zig").Size; -const DisplayMode = @import("../../../Core.zig").DisplayMode; -const SizeLimit = @import("../../../Core.zig").SizeLimit; -const CursorShape = @import("../../../Core.zig").CursorShape; -const VSyncMode = @import("../../../Core.zig").VSyncMode; -const CursorMode = @import("../../../Core.zig").CursorMode; -const Position = @import("../../../Core.zig").Position; -const Key = @import("../../../Core.zig").Key; -const KeyMods = @import("../../../Core.zig").KeyMods; -const Joystick = @import("../../../Core.zig").Joystick; -const InputState = @import("../../InputState.zig"); -const Frequency = @import("../../Frequency.zig"); -const RequestAdapterResponse = @import("../common.zig").RequestAdapterResponse; -const printUnhandledErrorCallback = @import("../common.zig").printUnhandledErrorCallback; -const detectBackendType = @import("../common.zig").detectBackendType; -const wantGamemode = @import("../common.zig").wantGamemode; -const initLinuxGamemode = @import("../common.zig").initLinuxGamemode; -const deinitLinuxGamemode = @import("../common.zig").deinitLinuxGamemode; -const requestAdapterCallback = @import("../common.zig").requestAdapterCallback; -const Unicode = @import("../x11/unicode.zig"); +const InitOptions = Core.InitOptions; +const Event = Core.Event; +const KeyEvent = Core.KeyEvent; +const MouseButtonEvent = Core.MouseButtonEvent; +const MouseButton = Core.MouseButton; +const Size = Core.Size; +const DisplayMode = Core.DisplayMode; +const CursorShape = Core.CursorShape; +const VSyncMode = Core.VSyncMode; +const CursorMode = Core.CursorMode; +const Position = Core.Position; +const Key = Core.Key; +const KeyMods = Core.KeyMods; +const Joystick = Core.Joystick; const log = std.log.scoped(.mach); @@ -153,14 +147,10 @@ const LibWaylandClient = struct { }; const EventQueue = std.fifo.LinearFifo(Event, .Dynamic); - pub const EventIterator = struct { - events_mu: *std.Thread.RwLock, queue: *EventQueue, pub inline fn next(self: *EventIterator) ?Event { - self.events_mu.lockShared(); - defer self.events_mu.unlockShared(); return self.queue.readItem(); } }; @@ -181,138 +171,23 @@ const Interfaces = struct { // xdg_activation_v1: *c.xdg_activation_v1, }; -fn Changable(comptime T: type, comptime uses_allocator: bool) type { - return struct { - current: T, - last: if (uses_allocator) ?T else void, - allocator: if (uses_allocator) std.mem.Allocator else void, - changed: bool = false, +pub const Wayland = @This(); - const Self = @This(); +allocator: std.mem.Allocator, +core: *Core, - ///Initialize with a default value - pub fn init(value: T, allocator: if (uses_allocator) std.mem.Allocator else void) !Self { - if (uses_allocator) { - return .{ - .allocator = allocator, - .last = null, - .current = try allocator.dupeZ(std.meta.Child(T), value), - }; - } else { - return .{ - .allocator = {}, - .last = {}, - .current = value, - }; - } - } - - /// Set a new value for the changable - pub fn set(self: *Self, value: T) !void { - if (uses_allocator) { - //If we have a last value, free it - if (self.last) |last_value| { - self.allocator.free(last_value); - - self.last = null; - } - - self.last = self.current; - - self.current = try self.allocator.dupeZ(std.meta.Child(T), value); - } else { - self.current = value; - } - self.changed = true; - } - - /// Read the current value out, resetting the changed flag - pub fn read(self: *Self) ?T { - if (!self.changed) - return null; - - self.changed = false; - return self.current; - } - - /// Free's the last allocation and resets the `last` value - pub fn freeLast(self: *Self) void { - if (uses_allocator) { - if (self.last) |last_value| { - self.allocator.free(last_value); - } - - self.last = null; - } - } - - pub fn deinit(self: *Self) void { - if (uses_allocator) { - if (self.last) |last_value| { - self.allocator.free(last_value); - } - - self.allocator.free(self.current); - } - - self.* = undefined; - } - }; -} - -/// Global state passed to things as the user data parameter, anything that needs to be accessed by callbacks should be in here. -const GlobalState = struct { - //xkb - libxkbcommon: LibXkbCommon, - xkb_context: ?*c.xkb_context, - keymap: ?*c.xkb_keymap, - xkb_state: ?*c.xkb_state, - compose_state: ?*c.xkb_compose_state, - - control_index: c.xkb_mod_index_t, - alt_index: c.xkb_mod_index_t, - shift_index: c.xkb_mod_index_t, - super_index: c.xkb_mod_index_t, - caps_lock_index: c.xkb_mod_index_t, - num_lock_index: c.xkb_mod_index_t, - - // Wayland objects/state - configured: bool, - interfaces: Interfaces, - surface: ?*c.struct_wl_surface, - - // Input/Event stuff - keyboard: ?*c.wl_keyboard = null, - pointer: ?*c.wl_pointer = null, - events_mu: std.Thread.RwLock = .{}, - events: EventQueue, - - input_state: InputState, - modifiers: KeyMods, - - //changables - state_mu: std.Thread.RwLock = .{}, - window_size_mu: std.Thread.RwLock = .{}, - window_size: Changable(Size, false), - swap_chain_update: std.Thread.ResetEvent = .{}, - - // Mutable fields; written by the App.update thread, read from any - swap_chain_mu: std.Thread.RwLock = .{}, - - fn pushEvent(self: *GlobalState, event: Event) void { - self.events_mu.lock(); - defer self.events_mu.unlock(); - - self.events.writeItem(event) catch @panic("TODO"); - } -}; - -pub const Core = @This(); - -gpu_device: *gpu.Device, -surface: *gpu.Surface, -swap_chain: *gpu.SwapChain, -swap_chain_desc: gpu.SwapChain.Descriptor, +// Xkb +libxkbcommon: LibXkbCommon, +xkb_context: ?*c.xkb_context, +keymap: ?*c.xkb_keymap, +xkb_state: ?*c.xkb_state, +compose_state: ?*c.xkb_compose_state, +control_index: c.xkb_mod_index_t, +alt_index: c.xkb_mod_index_t, +shift_index: c.xkb_mod_index_t, +super_index: c.xkb_mod_index_t, +caps_lock_index: c.xkb_mod_index_t, +num_lock_index: c.xkb_mod_index_t, // Wayland objects/state display: *c.struct_wl_display, @@ -321,542 +196,72 @@ xdg_surface: *c.xdg_surface, toplevel: *c.xdg_toplevel, tag: [*]c_char, decoration: *c.zxdg_toplevel_decoration_v1, -global_state: GlobalState, - -// internal tracking state -app_update_thread_started: bool = false, -done: std.Thread.ResetEvent = .{}, - -//timings -frame: *Frequency, -input: *Frequency, - -// changables -title: Changable([:0]const u8, true), -min_size: Changable(Size, false), -max_size: Changable(Size, false), - -fn registryHandleGlobal(user_data: *GlobalState, registry: ?*c.struct_wl_registry, name: u32, interface_ptr: [*:0]const u8, version: u32) callconv(.C) void { - const interface = std.mem.span(interface_ptr); - - log.debug("Got interface: {s}", .{interface}); - - if (std.mem.eql(u8, "wl_compositor", interface)) { - user_data.interfaces.wl_compositor = @ptrCast(c.wl_registry_bind( - registry, - name, - libwaylandclient.wl_compositor_interface, - @min(3, version), - ) orelse @panic("uh idk how to proceed")); - log.debug("Bound wl_compositor :)", .{}); - } else if (std.mem.eql(u8, "wl_subcompositor", interface)) { - user_data.interfaces.wl_subcompositor = @ptrCast(c.wl_registry_bind( - registry, - name, - libwaylandclient.wl_subcompositor_interface, - @min(3, version), - ) orelse @panic("uh idk how to proceed")); - log.debug("Bound wl_subcompositor :)", .{}); - } else if (std.mem.eql(u8, "wl_shm", interface)) { - user_data.interfaces.wl_shm = @ptrCast(c.wl_registry_bind( - registry, - name, - libwaylandclient.wl_shm_interface, - @min(3, version), - ) orelse @panic("uh idk how to proceed")); - log.debug("Bound wl_shm :)", .{}); - } else if (std.mem.eql(u8, "wl_output", interface)) { - user_data.interfaces.wl_output = @ptrCast(c.wl_registry_bind( - registry, - name, - libwaylandclient.wl_output_interface, - @min(3, version), - ) orelse @panic("uh idk how to proceed")); - log.debug("Bound wl_output :)", .{}); - // } else if (std.mem.eql(u8, "wl_data_device_manager", interface)) { - // user_data.interfaces.wl_data_device_manager = @ptrCast(user_data.libwaylandclient.wl_registry_bind( - // registry, - // name, - // user_data.libwaylandclient.wl_data_device_manager_interface, - // @min(3, version), - // ) orelse @panic("uh idk how to proceed")); - // log.debug("Bound wl_data_device_manager :)", .{}); - } else if (std.mem.eql(u8, "xdg_wm_base", interface)) { - user_data.interfaces.xdg_wm_base = @ptrCast(c.wl_registry_bind( - registry, - name, - &LibWaylandClient.xdg_wm_base_interface, - // &LibWaylandClient._glfw_xdg_wm_base_interface, - @min(3, version), - ) orelse @panic("uh idk how to proceed")); - log.debug("Bound xdg_wm_base :)", .{}); - - //TODO: handle return value - _ = c.xdg_wm_base_add_listener(user_data.interfaces.xdg_wm_base, &.{ .ping = @ptrCast(&wmBaseHandlePing) }, user_data); - } else if (std.mem.eql(u8, "zxdg_decoration_manager_v1", interface)) { - user_data.interfaces.zxdg_decoration_manager_v1 = @ptrCast(c.wl_registry_bind( - registry, - name, - &LibWaylandClient.zxdg_decoration_manager_v1_interface, - @min(3, version), - ) orelse @panic("uh idk how to proceed")); - log.debug("Bound zxdg_decoration_manager_v1 :)", .{}); - } else if (std.mem.eql(u8, "wl_seat", interface)) { - user_data.interfaces.wl_seat = @ptrCast(c.wl_registry_bind( - registry, - name, - libwaylandclient.wl_seat_interface, - @min(3, version), - ) orelse @panic("uh idk how to proceed")); - log.debug("Bound wl_seat :)", .{}); - - //TODO: handle return value - _ = c.wl_seat_add_listener(user_data.interfaces.wl_seat, &.{ - .capabilities = @ptrCast(&seatHandleCapabilities), - .name = @ptrCast(&seatHandleName), //ptrCast for the `[*:0]const u8` - }, user_data); - } -} - -fn seatHandleName(user_data: *GlobalState, seat: ?*c.struct_wl_seat, name_ptr: [*:0]const u8) callconv(.C) void { - _ = user_data; - const name = std.mem.span(name_ptr); - - log.info("seat {*} has name {s}", .{ seat, name }); -} - -fn seatHandleCapabilities(user_data: *GlobalState, seat: ?*c.struct_wl_seat, caps: c.wl_seat_capability) callconv(.C) void { - log.info("seat {*} has caps {d}", .{ seat, caps }); - - if ((caps & c.WL_SEAT_CAPABILITY_KEYBOARD) != 0) { - user_data.keyboard = c.wl_seat_get_keyboard(seat); - - //TODO: handle return value - _ = c.wl_keyboard_add_listener(user_data.keyboard, &.{ - .keymap = @ptrCast(&keyboardHandleKeymap), - .enter = @ptrCast(&keyboardHandleEnter), - .leave = @ptrCast(&keyboardHandleLeave), - .key = @ptrCast(&keyboardHandleKey), - .modifiers = @ptrCast(&keyboardHandleModifiers), - .repeat_info = @ptrCast(&keyboardHandleRepeatInfo), - }, user_data); - } - - if ((caps & c.WL_SEAT_CAPABILITY_TOUCH) != 0) { - //TODO - } - - if ((caps & c.WL_SEAT_CAPABILITY_POINTER) != 0) { - user_data.pointer = c.wl_seat_get_pointer(seat); - - //TODO: handle return value - _ = c.wl_pointer_add_listener(user_data.pointer, &.{ - .axis = @ptrCast(&handlePointerAxis), - .axis_discrete = @ptrCast(&handlePointerAxisDiscrete), - .axis_relative_direction = @ptrCast(&handlePointerAxisRelativeDirection), - .axis_source = @ptrCast(&handlePointerAxisSource), - .axis_stop = @ptrCast(&handlePointerAxisStop), - .axis_value120 = @ptrCast(&handlePointerAxisValue120), - .button = @ptrCast(&handlePointerButton), - .enter = @ptrCast(&handlePointerEnter), - .frame = @ptrCast(&handlePointerFrame), - .leave = @ptrCast(&handlePointerLeave), - .motion = @ptrCast(&handlePointerMotion), - }, user_data); - } - - // Delete keyboard if its no longer in the seat - if (user_data.keyboard) |keyboard| { - if ((caps & c.WL_SEAT_CAPABILITY_KEYBOARD) == 0) { - c.wl_keyboard_destroy(keyboard); - user_data.keyboard = null; - } - } - - if (user_data.pointer) |pointer| { - if ((caps & c.WL_SEAT_CAPABILITY_POINTER) == 0) { - c.wl_pointer_destroy(pointer); - user_data.pointer = null; - } - } -} - -fn handlePointerEnter(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void { - _ = fixed_x; // autofix - _ = fixed_y; // autofix - _ = user_data; // autofix - _ = pointer; // autofix - _ = serial; // autofix - _ = surface; // autofix -} -fn handlePointerLeave(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void { - _ = user_data; // autofix - _ = pointer; // autofix - _ = serial; // autofix - _ = surface; // autofix -} -fn handlePointerMotion(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, serial: u32, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void { - _ = pointer; // autofix - _ = serial; // autofix - - const x = c.wl_fixed_to_double(fixed_x); - const y = c.wl_fixed_to_double(fixed_y); - - user_data.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y } } }); - user_data.input_state.mouse_position = .{ .x = x, .y = y }; -} -fn handlePointerButton(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, serial: u32, time: u32, button: u32, state: u32) callconv(.C) void { - _ = pointer; // autofix - _ = serial; // autofix - _ = time; // autofix - - const mouse_button: MouseButton = @enumFromInt(button - c.BTN_LEFT); - const pressed = state == c.WL_POINTER_BUTTON_STATE_PRESSED; - - user_data.input_state.mouse_buttons.setValue(@intFromEnum(mouse_button), pressed); - - if (pressed) { - user_data.pushEvent(Event{ .mouse_press = .{ - .button = mouse_button, - .mods = user_data.modifiers, - .pos = user_data.input_state.mouse_position, - } }); - } else { - user_data.pushEvent(Event{ .mouse_release = .{ - .button = mouse_button, - .mods = user_data.modifiers, - .pos = user_data.input_state.mouse_position, - } }); - } -} -fn handlePointerAxis(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32, value: c.wl_fixed_t) callconv(.C) void { - _ = user_data; // autofix - _ = pointer; // autofix - _ = time; // autofix - _ = axis; // autofix - _ = value; // autofix -} -fn handlePointerFrame(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer) callconv(.C) void { - _ = user_data; // autofix - _ = pointer; // autofix -} -fn handlePointerAxisSource(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, axis_source: u32) callconv(.C) void { - _ = user_data; // autofix - _ = pointer; // autofix - _ = axis_source; // autofix -} -fn handlePointerAxisStop(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32) callconv(.C) void { - _ = user_data; // autofix - _ = pointer; // autofix - _ = time; // autofix - _ = axis; // autofix -} -fn handlePointerAxisDiscrete(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, axis: u32, discrete: i32) callconv(.C) void { - _ = user_data; // autofix - _ = pointer; // autofix - _ = axis; // autofix - _ = discrete; // autofix -} -fn handlePointerAxisValue120(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, axis: u32, value_120: i32) callconv(.C) void { - _ = user_data; // autofix - _ = pointer; // autofix - _ = axis; // autofix - _ = value_120; // autofix -} -fn handlePointerAxisRelativeDirection(user_data: *GlobalState, pointer: ?*c.struct_wl_pointer, axis: u32, direction: u32) callconv(.C) void { - _ = user_data; // autofix - _ = pointer; // autofix - _ = axis; // autofix - _ = direction; // autofix -} - -fn keyboardHandleKeymap(user_data: *GlobalState, keyboard: ?*c.struct_wl_keyboard, format: u32, fd: i32, keymap_size: u32) callconv(.C) void { - _ = keyboard; - - if (format != c.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - @panic("TODO"); - } - - const map_str = std.os.mmap(null, keymap_size, std.os.PROT.READ, std.os.MAP.SHARED, fd, 0) catch unreachable; - - const keymap = user_data.libxkbcommon.xkb_keymap_new_from_string( - user_data.xkb_context, - @alignCast(map_str), //align cast happening here, im sure its fine? TODO: figure out if this okay - c.XKB_KEYMAP_FORMAT_TEXT_V1, - 0, - ).?; - log.debug("got keymap {*}", .{keymap}); - - //Unmap the keymap - std.os.munmap(map_str); - //Close the fd - std.posix.close(fd); - - const state = user_data.libxkbcommon.xkb_state_new(keymap).?; - // defer user_data.libxkbcommon.xkb_state_unref(state); - - //this chain hurts me. why must C be this way. - const locale = std.os.getenv("LC_ALL") orelse std.os.getenv("LC_CTYPE") orelse std.os.getenv("LANG") orelse "C"; - - var compose_table = user_data.libxkbcommon.xkb_compose_table_new_from_locale( - user_data.xkb_context, - locale, - c.XKB_COMPOSE_COMPILE_NO_FLAGS, - ); - - //If creation failed, lets try the C locale - if (compose_table == null) - compose_table = user_data.libxkbcommon.xkb_compose_table_new_from_locale( - user_data.xkb_context, - "C", - c.XKB_COMPOSE_COMPILE_NO_FLAGS, - ).?; - - defer user_data.libxkbcommon.xkb_compose_table_unref(compose_table); - - user_data.keymap = keymap; - user_data.xkb_state = state; - user_data.compose_state = user_data.libxkbcommon.xkb_compose_state_new(compose_table, c.XKB_COMPOSE_STATE_NO_FLAGS).?; - - user_data.control_index = user_data.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Control"); - user_data.alt_index = user_data.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod1"); - user_data.shift_index = user_data.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Shift"); - user_data.super_index = user_data.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod4"); - user_data.caps_lock_index = user_data.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Lock"); - user_data.num_lock_index = user_data.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod2"); -} -fn keyboardHandleEnter(user_data: *GlobalState, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface, keys: [*c]c.struct_wl_array) callconv(.C) void { - _ = keyboard; - _ = serial; - _ = surface; - _ = keys; - - user_data.pushEvent(.focus_gained); -} -fn keyboardHandleLeave(user_data: *GlobalState, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void { - _ = keyboard; - _ = serial; - _ = surface; - - user_data.pushEvent(.focus_lost); -} -fn keyboardHandleKey(user_data: *GlobalState, keyboard: ?*c.struct_wl_keyboard, serial: u32, time: u32, scancode: u32, state: u32) callconv(.C) void { - _ = keyboard; - _ = serial; - _ = time; - - const key = toMachKey(scancode); - const pressed = state == 1; - - user_data.input_state.keys.setValue(@intFromEnum(key), pressed); - - if (pressed) { - user_data.pushEvent(Event{ .key_press = .{ - .key = key, - .mods = user_data.modifiers, - } }); - - var keysyms: ?[*]c.xkb_keysym_t = undefined; - //Get the keysym from the keycode (scancode + 8) - if (user_data.libxkbcommon.xkb_state_key_get_syms(user_data.xkb_state, scancode + 8, &keysyms) == 1) { - //Compose the keysym - const keysym: c.xkb_keysym_t = composeSymbol(user_data, keysyms.?[0]); - - //Try to convert that keysym to a unicode codepoint - if (Unicode.unicodeFromKeySym(keysym)) |codepoint| { - user_data.pushEvent(Event{ .char_input = .{ .codepoint = codepoint } }); - } - } - } else { - user_data.pushEvent(Event{ .key_release = .{ - .key = key, - .mods = user_data.modifiers, - } }); - } -} -fn keyboardHandleModifiers(user_data: *GlobalState, keyboard: ?*c.struct_wl_keyboard, serial: u32, mods_depressed: u32, mods_latched: u32, mods_locked: u32, group: u32) callconv(.C) void { - _ = keyboard; - _ = serial; - - if (user_data.keymap == null) - return; - - //TODO: handle this return value - _ = user_data.libxkbcommon.xkb_state_update_mask( - user_data.xkb_state.?, - mods_depressed, - mods_latched, - mods_locked, - 0, - 0, - group, - ); - - //Iterate over all the modifiers - inline for (.{ - .{ user_data.alt_index, "alt" }, - .{ user_data.shift_index, "shift" }, - .{ user_data.super_index, "super" }, - .{ user_data.control_index, "control" }, - .{ user_data.num_lock_index, "num_lock" }, - .{ user_data.caps_lock_index, "caps_lock" }, - }) |key| { - @field(user_data.modifiers, key[1]) = user_data.libxkbcommon.xkb_state_mod_index_is_active( - user_data.xkb_state, - key[0], - c.XKB_STATE_MODS_EFFECTIVE, - ) == 1; - } -} -fn keyboardHandleRepeatInfo(user_data: *GlobalState, keyboard: ?*c.struct_wl_keyboard, rate: i32, delay: i32) callconv(.C) void { - _ = user_data; - _ = keyboard; - _ = rate; - _ = delay; -} - -fn composeSymbol(user_data: *GlobalState, sym: c.xkb_keysym_t) c.xkb_keysym_t { - if (sym == c.XKB_KEY_NoSymbol or user_data.compose_state == null) - return sym; - - if (user_data.libxkbcommon.xkb_compose_state_feed(user_data.compose_state, sym) != c.XKB_COMPOSE_FEED_ACCEPTED) - return sym; - - return switch (user_data.libxkbcommon.xkb_compose_state_get_status(user_data.compose_state)) { - c.XKB_COMPOSE_COMPOSED => user_data.libxkbcommon.xkb_compose_state_get_one_sym(user_data.compose_state), - c.XKB_COMPOSE_COMPOSING, c.XKB_COMPOSE_CANCELLED => c.XKB_KEY_NoSymbol, - else => sym, - }; -} - -fn wmBaseHandlePing(user_data: *GlobalState, wm_base: ?*c.struct_xdg_wm_base, serial: u32) callconv(.C) void { - _ = user_data; - - log.debug("Got wm base ping {*} with serial {d}", .{ wm_base, serial }); - - c.xdg_wm_base_pong(wm_base, serial); -} - -fn registryHandleGlobalRemove(user_data: *GlobalState, registry: ?*c.struct_wl_registry, name: u32) callconv(.C) void { - _ = user_data; - _ = registry; - _ = name; -} - -const registry_listener = c.wl_registry_listener{ - // ptrcast is for the [*:0] -> [*c] conversion, silly yes - .global = @ptrCast(®istryHandleGlobal), - // ptrcast is for the user_data param, which is guarenteed to be our type (and if its not, it should be caught by safety checks) - .global_remove = @ptrCast(®istryHandleGlobalRemove), -}; - -fn xdgSurfaceHandleConfigure(user_data: *GlobalState, xdg_surface: ?*c.struct_xdg_surface, serial: u32) callconv(.C) void { - c.xdg_surface_ack_configure(xdg_surface, serial); - - if (user_data.configured) { - c.wl_surface_commit(user_data.surface); - } else { - log.debug("xdg surface configured", .{}); - user_data.configured = true; - } - - user_data.state_mu.lock(); - defer user_data.state_mu.unlock(); - - if (user_data.window_size.read()) |new_window_size| { - setContentAreaOpaque(user_data, new_window_size); - } -} - -fn xdgToplevelHandleClose(user_data: *GlobalState, toplevel: ?*c.struct_xdg_toplevel) callconv(.C) void { - _ = user_data; - _ = toplevel; -} - -fn xdgToplevelHandleConfigure(user_data: *GlobalState, toplevel: ?*c.struct_xdg_toplevel, width: i32, height: i32, states: [*c]c.struct_wl_array) callconv(.C) void { - _ = toplevel; - _ = states; - - log.debug("{d}/{d}", .{ width, height }); - - if (width > 0 and height > 0) { - user_data.state_mu.lock(); - defer user_data.state_mu.unlock(); - - try user_data.window_size.set(.{ .width = @intCast(width), .height = @intCast(height) }); - } -} - -fn setContentAreaOpaque(state: *GlobalState, new_size: Size) void { - const region = c.wl_compositor_create_region(state.interfaces.wl_compositor) orelse return; - - c.wl_region_add(region, 0, 0, @intCast(new_size.width), @intCast(new_size.height)); - c.wl_surface_set_opaque_region(state.surface, region); - c.wl_region_destroy(region); - - state.swap_chain_update.set(); +configured: bool, +interfaces: Interfaces, +surface: ?*c.struct_wl_surface, + +// Input/Event stuff +keyboard: ?*c.wl_keyboard, +pointer: ?*c.wl_pointer, +events: EventQueue, +input_state: InputState, +modifiers: KeyMods, + +title: [:0]u8, +display_mode: DisplayMode, +vsync_mode: VSyncMode, +cursor_mode: CursorMode, +cursor_shape: CursorShape, +border: bool, +headless: bool, +refresh_rate: u32, +size: Size, +surface_descriptor: gpu.Surface.Descriptor, + +fn pushEvent(wl: *Wayland, event: Event) void { + wl.events.writeItem(event) catch @panic("TODO"); } // Called on the main thread -pub fn init( - core: *Core, - allocator: std.mem.Allocator, - frame: *Frequency, - input: *Frequency, - options: InitOptions, -) !void { - core.global_state = .{ - .interfaces = .{}, - .configured = false, - .surface = null, - .events = EventQueue.init(allocator), - .libxkbcommon = try LibXkbCommon.load(), - .xkb_context = null, - .keymap = null, - .xkb_state = null, - .compose_state = null, - .alt_index = undefined, - .shift_index = undefined, - .super_index = undefined, - .control_index = undefined, - .caps_lock_index = undefined, - .num_lock_index = undefined, - .input_state = .{}, - .modifiers = .{ - .alt = false, - .caps_lock = false, - .control = false, - .num_lock = false, - .shift = false, - .super = false, - }, - .window_size = try @TypeOf(core.global_state.window_size).init(options.size, {}), - }; - +pub fn init(wl: *Wayland, options: InitOptions) !void { libwaylandclient = try LibWaylandClient.load(); + wl.allocator = options.allocator; + wl.core = @fieldParentPtr("platform", wl); + wl.libxkbcommon = try LibXkbCommon.load(); + wl.keymap = null; + wl.xkb_state = null; + wl.compose_state = null; + wl.configured = false; + wl.interfaces = .{}; + wl.input_state = .{}; + wl.modifiers = .{ + .alt = false, + .caps_lock = false, + .control = false, + .num_lock = false, + .shift = false, + .super = false, + }; + wl.events = EventQueue.init(options.allocator); + wl.display = libwaylandclient.wl_display_connect(null) orelse return error.FailedToConnectToWaylandDisplay; + wl.xkb_context = wl.libxkbcommon.xkb_context_new(0).?; + wl.registry = c.wl_display_get_registry(wl.display) orelse return error.FailedToGetDisplayRegistry; - core.global_state.xkb_context = core.global_state.libxkbcommon.xkb_context_new(0).?; - - core.display = libwaylandclient.wl_display_connect(null) orelse return error.FailedToConnectToWaylandDisplay; - - const registry = c.wl_display_get_registry(core.display) orelse return error.FailedToGetDisplayRegistry; // TODO: handle error return value here - _ = c.wl_registry_add_listener(registry, ®istry_listener, &core.global_state); + _ = c.wl_registry_add_listener(wl.registry, ®istry_listener, wl); //Round trip to get all the registry objects - _ = libwaylandclient.wl_display_roundtrip(core.display); + _ = libwaylandclient.wl_display_roundtrip(wl.display); //Round trip to get all initial output events - _ = libwaylandclient.wl_display_roundtrip(core.display); + _ = libwaylandclient.wl_display_roundtrip(wl.display); - core.global_state.surface = c.wl_compositor_create_surface(core.global_state.interfaces.wl_compositor) orelse return error.UnableToCreateSurface; - log.debug("Got surface {*}", .{core.global_state.surface}); + wl.surface = c.wl_compositor_create_surface(wl.interfaces.wl_compositor) orelse return error.UnableToCreateSurface; - var tag: [*:0]c_char = undefined; - libwaylandclient.wl_proxy_set_tag(@ptrCast(core.global_state.surface), @ptrCast(&tag)); + libwaylandclient.wl_proxy_set_tag(@ptrCast(wl.surface), @ptrCast(&wl.tag)); { - const region = c.wl_compositor_create_region(core.global_state.interfaces.wl_compositor) orelse return error.CouldntCreateWaylandRegtion; + const region = c.wl_compositor_create_region(wl.interfaces.wl_compositor) orelse return error.CouldntCreateWaylandRegtion; c.wl_region_add( region, @@ -865,401 +270,187 @@ pub fn init( @intCast(options.size.width), @intCast(options.size.height), ); - c.wl_surface_set_opaque_region(core.global_state.surface, region); + c.wl_surface_set_opaque_region(wl.surface, region); c.wl_region_destroy(region); } - const xdg_surface = c.xdg_wm_base_get_xdg_surface(core.global_state.interfaces.xdg_wm_base, core.global_state.surface) orelse return error.UnableToCreateXdgSurface; - log.debug("Got xdg surface {*}", .{xdg_surface}); + wl.xdg_surface = c.xdg_wm_base_get_xdg_surface(wl.interfaces.xdg_wm_base, wl.surface) orelse return error.UnableToCreateXdgSurface; + wl.toplevel = c.xdg_surface_get_toplevel(wl.xdg_surface) orelse return error.UnableToGetXdgTopLevel; + wl.title = try options.allocator.dupeZ(u8, options.title); + wl.size = options.size; - const toplevel = c.xdg_surface_get_toplevel(xdg_surface) orelse return error.UnableToGetXdgTopLevel; - log.debug("Got xdg toplevel {*}", .{toplevel}); + // TODO: handle this return value + _ = c.xdg_surface_add_listener(wl.xdg_surface, &.{ .configure = @ptrCast(&xdgSurfaceHandleConfigure) }, wl); - core.min_size = try @TypeOf(core.min_size).init(.{ .width = 0, .height = 0 }, {}); - core.max_size = try @TypeOf(core.max_size).init(.{ .width = 0, .height = 0 }, {}); - - //TODO: handle this return value - _ = c.xdg_surface_add_listener(xdg_surface, &.{ .configure = @ptrCast(&xdgSurfaceHandleConfigure) }, &core.global_state); - - //TODO: handle this return value - _ = c.xdg_toplevel_add_listener(toplevel, &.{ + // TODO: handle this return value + _ = c.xdg_toplevel_add_listener(wl.toplevel, &.{ .configure = @ptrCast(&xdgToplevelHandleConfigure), .close = @ptrCast(&xdgToplevelHandleClose), - }, &core.global_state); + }, wl); - //Commit changes to surface - c.wl_surface_commit(core.global_state.surface); + // Commit changes to surface + c.wl_surface_commit(wl.surface); - while (libwaylandclient.wl_display_dispatch(core.display) != -1 and !core.global_state.configured) { + while (libwaylandclient.wl_display_dispatch(wl.display) != -1 and !wl.configured) { // This space intentionally left blank } - core.title = try @TypeOf(core.title).init(options.title, allocator); + c.xdg_toplevel_set_title(wl.toplevel, wl.title); - c.xdg_toplevel_set_title(toplevel, core.title.current); - - const decoration = c.zxdg_decoration_manager_v1_get_toplevel_decoration( - core.global_state.interfaces.zxdg_decoration_manager_v1, - toplevel, + wl.decoration = c.zxdg_decoration_manager_v1_get_toplevel_decoration( + wl.interfaces.zxdg_decoration_manager_v1, + wl.toplevel, ) orelse return error.UnableToGetToplevelDecoration; - log.debug("Got xdg toplevel decoration {*}", .{decoration}); - c.zxdg_toplevel_decoration_v1_set_mode( - decoration, - c.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE, - ); + c.zxdg_toplevel_decoration_v1_set_mode(wl.decoration, c.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - //Commit changes to surface - c.wl_surface_commit(core.global_state.surface); - //TODO: handle return value - _ = libwaylandclient.wl_display_roundtrip(core.display); + // Commit changes to surface + c.wl_surface_commit(wl.surface); + // TODO: handle return value + _ = libwaylandclient.wl_display_roundtrip(wl.display); - const instance = gpu.createInstance(null) orelse { - log.err("failed to create GPU instance", .{}); - std.process.exit(1); - }; - const surface = instance.createSurface(&gpu.Surface.Descriptor{ - .next_in_chain = .{ - .from_wayland_surface = &.{ - .display = core.display, - .surface = core.global_state.surface.?, - }, - }, - }); - - var response: RequestAdapterResponse = undefined; - instance.requestAdapter(&gpu.RequestAdapterOptions{ - .compatible_surface = surface, - .power_preference = options.power_preference, - .force_fallback_adapter = .false, - }, &response, requestAdapterCallback); - if (response.status != .success) { - log.err("failed to create GPU adapter: {?s}", .{response.message}); - log.info("-> maybe try MACH_GPU_BACKEND=opengl ?", .{}); - std.process.exit(1); - } - - // Print which adapter we are going to use. - var props = std.mem.zeroes(gpu.Adapter.Properties); - response.adapter.?.getProperties(&props); - if (props.backend_type == .null) { - log.err("no backend found for {s} adapter", .{props.adapter_type.name()}); - std.process.exit(1); - } - log.info("found {s} backend on {s} adapter: {s}, {s}\n", .{ - props.backend_type.name(), - props.adapter_type.name(), - props.name, - props.driver_description, - }); - - // Create a device with default limits/features. - const gpu_device = response.adapter.?.createDevice(&.{ - .required_features_count = if (options.required_features) |v| @as(u32, @intCast(v.len)) else 0, - .required_features = if (options.required_features) |v| @as(?[*]const gpu.FeatureName, v.ptr) else null, - .required_limits = if (options.required_limits) |limits| @as(?*const gpu.RequiredLimits, &gpu.RequiredLimits{ - .limits = limits, - }) else null, - .device_lost_callback = &deviceLostCallback, - .device_lost_userdata = null, - }) orelse { - log.err("failed to create GPU device\n", .{}); - std.process.exit(1); - }; - gpu_device.setUncapturedErrorCallback({}, printUnhandledErrorCallback); - - const swap_chain_desc = gpu.SwapChain.Descriptor{ - .label = "main swap chain", - .usage = options.swap_chain_usage, - .format = .bgra8_unorm, - .width = options.size.width, - .height = options.size.height, - .present_mode = .mailbox, - }; - const swap_chain = gpu_device.createSwapChain(surface, &swap_chain_desc); - - log.debug("DONE", .{}); - - core.* = .{ - .display = core.display, - .registry = registry, - .tag = tag, - .xdg_surface = xdg_surface, - .toplevel = toplevel, - .decoration = decoration, - .gpu_device = gpu_device, - .title = core.title, - .min_size = core.min_size, - .max_size = core.max_size, - .frame = frame, - .input = input, - .global_state = core.global_state, - .swap_chain = swap_chain, - .swap_chain_desc = swap_chain_desc, - .surface = surface, - }; + // TODO: remove allocation + const wayland_surface_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromWaylandSurface); + wayland_surface_descriptor.* = .{ .display = wl.display, .surface = wl.surface.? }; + wl.surface_descriptor = .{ .next_in_chain = .{ .from_wayland_surface = wayland_surface_descriptor } }; + wl.border = options.border; + wl.headless = options.headless; + wl.refresh_rate = 60; // TODO } -pub fn deinit(self: *Core) void { - self.title.deinit(); +pub fn deinit(wl: *Wayland) void { + wl.allocator.free(wl.title); + wl.allocator.destroy(wl.surface_descriptor.next_in_chain.from_wayland_surface); } // Called on the main thread -pub fn update(self: *Core, app: anytype) !bool { - if (self.done.isSet()) return true; - - //State updates - { - self.global_state.state_mu.lock(); - defer self.global_state.state_mu.unlock(); - - var need_surface_commit: bool = false; - - // Check if we have a new title - if (self.title.read()) |new_title| { - defer self.title.freeLast(); - - c.xdg_toplevel_set_title(self.toplevel, new_title); - } - - // Check if we have a new min size - if (self.min_size.read()) |new_min_size| { - c.xdg_toplevel_set_min_size(self.toplevel, @intCast(new_min_size.width), @intCast(new_min_size.height)); - - need_surface_commit = true; - } - - // Check if we have a new max size - if (self.max_size.read()) |new_max_size| { - c.xdg_toplevel_set_max_size(self.toplevel, @intCast(new_max_size.width), @intCast(new_max_size.height)); - - need_surface_commit = true; - } - - if (need_surface_commit) - c.wl_surface_commit(self.global_state.surface); - } - - // while (libwaylandclient.wl_display_flush(self.display) == -1) { - // // if (std.os.errno() == std.posix.E.AGAIN) { +pub fn update(wl: *Wayland) !void { + // while (libwaylandclient.wl_display_flush(wl.display) == -1) { + // // if (std.posix.errno() == std.posix.E.AGAIN) { // // log.err("flush error", .{}); // // return true; // // } // var pollfd = [_]std.posix.pollfd{ // std.posix.pollfd{ - // .fd = libwaylandclient.wl_display_get_fd(self.display), + // .fd = libwaylandclient.wl_display_get_fd(wl.display), // .events = std.posix.POLL.OUT, // .revents = 0, // }, // }; // while (try std.posix.poll(&pollfd, -1) != 0) { - // // if (std.os.errno() == std.posix.E.INTR or std.os.errno() == std.posix.E.AGAIN) { + // // if (std.posix.errno() == std.posix.E.INTR or std.posix.errno() == std.posix.E.AGAIN) { // // log.err("poll error", .{}); // // return true; // // } // } // } - if (@hasDecl(std.meta.Child(@TypeOf(app)), "updateMainThread")) { - if (app.updateMainThread() catch unreachable) { - self.done.set(); - return true; - } - } + _ = libwaylandclient.wl_display_roundtrip(wl.display); - _ = libwaylandclient.wl_display_roundtrip(self.display); - - self.input.tick(); - return false; + wl.core.input.tick(); } // May be called from any thread. -pub inline fn pollEvents(self: *Core) EventIterator { - return EventIterator{ .events_mu = &self.global_state.events_mu, .queue = &self.global_state.events }; +pub inline fn pollEvents(wl: *Wayland) EventIterator { + return EventIterator{ .queue = &wl.events }; } // May be called from any thread. -pub fn setTitle(self: *Core, title: [:0]const u8) void { - self.global_state.state_mu.lock(); - defer self.global_state.state_mu.unlock(); - - self.title.set(title) catch unreachable; +pub fn setTitle(wl: *Wayland, title: [:0]const u8) void { + wl.title = @ptrCast(wl.allocator.realloc(wl.title, title.len + 1) catch @panic("TODO")); + @memcpy(wl.title, title); + c.xdg_toplevel_set_title(wl.toplevel, title); } // May be called from any thread. -pub fn setDisplayMode(_: *Core, _: DisplayMode) void { +pub fn setDisplayMode(_: *Wayland, _: DisplayMode) void { @panic("TODO: implement setDisplayMode for Wayland"); } // May be called from any thread. -pub fn displayMode(_: *Core) DisplayMode { - @panic("TODO: implement displayMode for Wayland"); -} - -// May be called from any thread. -pub fn setBorder(_: *Core, _: bool) void { +pub fn setBorder(_: *Wayland, _: bool) void { @panic("TODO: implement setBorder for Wayland"); } // May be called from any thread. -pub fn border(_: *Core) bool { - @panic("TODO: implement border for Wayland"); -} - -// May be called from any thread. -pub fn setHeadless(_: *Core, _: bool) void { +pub fn setHeadless(_: *Wayland, _: bool) void { @panic("TODO: implement setHeadless for Wayland"); } // May be called from any thread. -pub fn headless(_: *Core) bool { - @panic("TODO: implement headless for Wayland"); -} - -// May be called from any thread. -pub fn setVSync(_: *Core, _: VSyncMode) void { +pub fn setVSync(_: *Wayland, _: VSyncMode) void { @panic("TODO: implement setVSync for Wayland"); } // May be called from any thread. -pub fn vsync(_: *Core) VSyncMode { - @panic("TODO: implement vsync for Wayland"); +pub fn setSize(wl: *Wayland, new_size: Size) void { + wl.size.lock(); + defer wl.size.unlock(); + + setContentAreaOpaque(&wl, new_size); + wl.size.set(new_size) catch unreachable; } // May be called from any thread. -pub fn setSize(self: *Core, new_size: Size) void { - self.global_state.window_size_mu.lock(); - defer self.global_state.window_size_mu.unlock(); - - setContentAreaOpaque(&self.global_state, new_size); - self.global_state.window_size.set(new_size) catch unreachable; +pub fn size(wl: *Wayland) Size { + return wl.size; } // May be called from any thread. -pub fn size(self: *Core) Size { - self.state_mu.lock(); - defer self.state_mu.unlock(); - - return self.window_size.current; -} - -// May be called from any thread. -pub fn setSizeLimit(self: *Core, limits: SizeLimit) void { - self.state_mu.lock(); - defer self.state_mu.unlock(); - - if (limits.max.width) |width| if (width == 0) @panic("todo: what do we do here?"); - if (limits.max.height) |height| if (height == 0) @panic("todo: what do we do here?"); - if (limits.min.width) |width| if (width == 0) @panic("todo: what do we do here?"); - if (limits.min.height) |height| if (height == 0) @panic("todo: what do we do here?"); - - //TODO: only set the changed one, not both! - self.min_size.set(.{ - .width = limits.min.width orelse 0, - .height = limits.min.height orelse 0, - }); - self.max_size.set(.{ - .width = limits.max.width orelse 0, - .height = limits.max.height orelse 0, - }); -} - -// May be called from any thread. -pub fn sizeLimit(self: *Core) SizeLimit { - self.state_mu.lock(); - defer self.state_mu.unlock(); - - return SizeLimit{ - .max = .{ - .width = if (self.max_size.current.width == 0) null else self.max_size.current.width, - .height = if (self.max_size.current.height == 0) null else self.max_size.current.height, - }, - .min = .{ - .width = if (self.min_size.current.width == 0) null else self.min_size.current.width, - .height = if (self.min_size.current.height == 0) null else self.min_size.current.height, - }, - }; -} - -// May be called from any thread. -pub fn setCursorMode(_: *Core, _: CursorMode) void { +pub fn setCursorMode(_: *Wayland, _: CursorMode) void { @panic("TODO: implement setCursorMode for Wayland"); } // May be called from any thread. -pub fn cursorMode(_: *Core) CursorMode { - @panic("TODO: implement cursorMode for Wayland"); -} - -// May be called from any thread. -pub fn setCursorShape(_: *Core, _: CursorShape) void { +pub fn setCursorShape(_: *Wayland, _: CursorShape) void { @panic("TODO: implement setCursorShape for Wayland"); } // May be called from any thread. -pub fn cursorShape(_: *Core) CursorShape { - @panic("TODO: implement cursorShape for Wayland"); -} - -// May be called from any thread. -pub fn joystickPresent(_: *Core, _: Joystick) bool { +pub fn joystickPresent(_: *Wayland, _: Joystick) bool { @panic("TODO: implement joystickPresent for Wayland"); } // May be called from any thread. -pub fn joystickName(_: *Core, _: Joystick) ?[:0]const u8 { +pub fn joystickName(_: *Wayland, _: Joystick) ?[:0]const u8 { @panic("TODO: implement joystickName for Wayland"); } // May be called from any thread. -pub fn joystickButtons(_: *Core, _: Joystick) ?[]const bool { +pub fn joystickButtons(_: *Wayland, _: Joystick) ?[]const bool { @panic("TODO: implement joystickButtons for Wayland"); } // May be called from any thread. -pub fn joystickAxes(_: *Core, _: Joystick) ?[]const f32 { +pub fn joystickAxes(_: *Wayland, _: Joystick) ?[]const f32 { @panic("TODO: implement joystickAxes for Wayland"); } // May be called from any thread. -pub fn keyPressed(self: *Core, key: Key) bool { - self.input_state.isKeyPressed(key); +pub fn keyPressed(wl: *Wayland, key: Key) bool { + wl.input_state.isKeyPressed(key); } // May be called from any thread. -pub fn keyReleased(self: *Core, key: Key) bool { - self.input_state.isKeyReleased(key); +pub fn keyReleased(wl: *Wayland, key: Key) bool { + wl.input_state.isKeyReleased(key); } // May be called from any thread. -pub fn mousePressed(self: *Core, button: MouseButton) bool { - return self.input_state.isMouseButtonPressed(button); +pub fn mousePressed(wl: *Wayland, button: MouseButton) bool { + return wl.input_state.isMouseButtonPressed(button); } // May be called from any thread. -pub fn mouseReleased(self: *Core, button: MouseButton) bool { - return self.input_state.isMouseButtonReleased(button); +pub fn mouseReleased(wl: *Wayland, button: MouseButton) bool { + return wl.input_state.isMouseButtonReleased(button); } // May be called from any thread. -pub fn mousePosition(self: *Core) Position { - return self.mouse_pos; -} - -// May be called from any thread. -pub inline fn outOfMemory(_: *Core) bool { - @panic("TODO: implement outOfMemory for Wayland"); -} - -// TODO(important): expose device loss to users, this can happen especially in the web and on mobile -// devices. Users will need to re-upload all assets to the GPU in this event. -fn deviceLostCallback(reason: gpu.Device.LostReason, msg: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void { - _ = userdata; - _ = reason; - log.err("mach: device lost: {s}", .{msg}); - @panic("mach: device lost"); +pub fn mousePosition(wl: *Wayland) Position { + return wl.mouse_pos; } fn toMachKey(key: u32) Key { @@ -1383,3 +574,451 @@ fn toMachKey(key: u32) Key { else => .unknown, }; } + +fn registryHandleGlobal(wl: *Wayland, registry: ?*c.struct_wl_registry, name: u32, interface_ptr: [*:0]const u8, version: u32) callconv(.C) void { + const interface = std.mem.span(interface_ptr); + + if (std.mem.eql(u8, "wl_compositor", interface)) { + wl.interfaces.wl_compositor = @ptrCast(c.wl_registry_bind( + registry, + name, + libwaylandclient.wl_compositor_interface, + @min(3, version), + ) orelse @panic("uh idk how to proceed")); + } else if (std.mem.eql(u8, "wl_subcompositor", interface)) { + wl.interfaces.wl_subcompositor = @ptrCast(c.wl_registry_bind( + registry, + name, + libwaylandclient.wl_subcompositor_interface, + @min(3, version), + ) orelse @panic("uh idk how to proceed")); + } else if (std.mem.eql(u8, "wl_shm", interface)) { + wl.interfaces.wl_shm = @ptrCast(c.wl_registry_bind( + registry, + name, + libwaylandclient.wl_shm_interface, + @min(3, version), + ) orelse @panic("uh idk how to proceed")); + } else if (std.mem.eql(u8, "wl_output", interface)) { + wl.interfaces.wl_output = @ptrCast(c.wl_registry_bind( + registry, + name, + libwaylandclient.wl_output_interface, + @min(3, version), + ) orelse @panic("uh idk how to proceed")); + // } else if (std.mem.eql(u8, "wl_data_device_manager", interface)) { + // wl.interfaces.wl_data_device_manager = @ptrCast(wl.libwaylandclient.wl_registry_bind( + // registry, + // name, + // wl.libwaylandclient.wl_data_device_manager_interface, + // @min(3, version), + // ) orelse @panic("uh idk how to proceed")); + } else if (std.mem.eql(u8, "xdg_wm_base", interface)) { + wl.interfaces.xdg_wm_base = @ptrCast(c.wl_registry_bind( + registry, + name, + &LibWaylandClient.xdg_wm_base_interface, + // &LibWaylandClient._glfw_xdg_wm_base_interface, + @min(3, version), + ) orelse @panic("uh idk how to proceed")); + + // TODO: handle return value + _ = c.xdg_wm_base_add_listener(wl.interfaces.xdg_wm_base, &.{ .ping = @ptrCast(&wmBaseHandlePing) }, wl); + } else if (std.mem.eql(u8, "zxdg_decoration_manager_v1", interface)) { + wl.interfaces.zxdg_decoration_manager_v1 = @ptrCast(c.wl_registry_bind( + registry, + name, + &LibWaylandClient.zxdg_decoration_manager_v1_interface, + @min(3, version), + ) orelse @panic("uh idk how to proceed")); + } else if (std.mem.eql(u8, "wl_seat", interface)) { + wl.interfaces.wl_seat = @ptrCast(c.wl_registry_bind( + registry, + name, + libwaylandclient.wl_seat_interface, + @min(3, version), + ) orelse @panic("uh idk how to proceed")); + + // TODO: handle return value + _ = c.wl_seat_add_listener(wl.interfaces.wl_seat, &.{ + .capabilities = @ptrCast(&seatHandleCapabilities), + .name = @ptrCast(&seatHandleName), //ptrCast for the `[*:0]const u8` + }, wl); + } +} + +fn seatHandleName(wl: *Wayland, seat: ?*c.struct_wl_seat, name_ptr: [*:0]const u8) callconv(.C) void { + _ = wl; + _ = seat; + _ = name_ptr; +} + +fn seatHandleCapabilities(wl: *Wayland, seat: ?*c.struct_wl_seat, caps: c.wl_seat_capability) callconv(.C) void { + if ((caps & c.WL_SEAT_CAPABILITY_KEYBOARD) != 0) { + wl.keyboard = c.wl_seat_get_keyboard(seat); + + // TODO: handle return value + _ = c.wl_keyboard_add_listener(wl.keyboard, &.{ + .keymap = @ptrCast(&keyboardHandleKeymap), + .enter = @ptrCast(&keyboardHandleEnter), + .leave = @ptrCast(&keyboardHandleLeave), + .key = @ptrCast(&keyboardHandleKey), + .modifiers = @ptrCast(&keyboardHandleModifiers), + .repeat_info = @ptrCast(&keyboardHandleRepeatInfo), + }, wl); + } + + if ((caps & c.WL_SEAT_CAPABILITY_TOUCH) != 0) { + // TODO + } + + if ((caps & c.WL_SEAT_CAPABILITY_POINTER) != 0) { + wl.pointer = c.wl_seat_get_pointer(seat); + + // TODO: handle return value + _ = c.wl_pointer_add_listener(wl.pointer, &.{ + .axis = @ptrCast(&handlePointerAxis), + .axis_discrete = @ptrCast(&handlePointerAxisDiscrete), + .axis_relative_direction = @ptrCast(&handlePointerAxisRelativeDirection), + .axis_source = @ptrCast(&handlePointerAxisSource), + .axis_stop = @ptrCast(&handlePointerAxisStop), + .axis_value120 = @ptrCast(&handlePointerAxisValue120), + .button = @ptrCast(&handlePointerButton), + .enter = @ptrCast(&handlePointerEnter), + .frame = @ptrCast(&handlePointerFrame), + .leave = @ptrCast(&handlePointerLeave), + .motion = @ptrCast(&handlePointerMotion), + }, wl); + } + + // Delete keyboard if its no longer in the seat + if (wl.keyboard) |keyboard| { + if ((caps & c.WL_SEAT_CAPABILITY_KEYBOARD) == 0) { + c.wl_keyboard_destroy(keyboard); + wl.keyboard = null; + } + } + + if (wl.pointer) |pointer| { + if ((caps & c.WL_SEAT_CAPABILITY_POINTER) == 0) { + c.wl_pointer_destroy(pointer); + wl.pointer = null; + } + } +} + +fn handlePointerEnter(wl: *Wayland, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void { + _ = fixed_x; + _ = fixed_y; + _ = wl; + _ = pointer; + _ = serial; + _ = surface; +} + +fn handlePointerLeave(wl: *Wayland, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void { + _ = wl; + _ = pointer; + _ = serial; + _ = surface; +} + +fn handlePointerMotion(wl: *Wayland, pointer: ?*c.struct_wl_pointer, serial: u32, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void { + _ = pointer; + _ = serial; + + const x = c.wl_fixed_to_double(fixed_x); + const y = c.wl_fixed_to_double(fixed_y); + + wl.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y } } }); + wl.input_state.mouse_position = .{ .x = x, .y = y }; +} + +fn handlePointerButton(wl: *Wayland, pointer: ?*c.struct_wl_pointer, serial: u32, time: u32, button: u32, state: u32) callconv(.C) void { + _ = pointer; + _ = serial; + _ = time; + + const mouse_button: MouseButton = @enumFromInt(button - c.BTN_LEFT); + const pressed = state == c.WL_POINTER_BUTTON_STATE_PRESSED; + + wl.input_state.mouse_buttons.setValue(@intFromEnum(mouse_button), pressed); + + if (pressed) { + wl.pushEvent(Event{ .mouse_press = .{ + .button = mouse_button, + .mods = wl.modifiers, + .pos = wl.input_state.mouse_position, + } }); + } else { + wl.pushEvent(Event{ .mouse_release = .{ + .button = mouse_button, + .mods = wl.modifiers, + .pos = wl.input_state.mouse_position, + } }); + } +} + +fn handlePointerAxis(wl: *Wayland, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32, value: c.wl_fixed_t) callconv(.C) void { + _ = wl; + _ = pointer; + _ = time; + _ = axis; + _ = value; +} + +fn handlePointerFrame(wl: *Wayland, pointer: ?*c.struct_wl_pointer) callconv(.C) void { + _ = wl; + _ = pointer; +} + +fn handlePointerAxisSource(wl: *Wayland, pointer: ?*c.struct_wl_pointer, axis_source: u32) callconv(.C) void { + _ = wl; + _ = pointer; + _ = axis_source; +} + +fn handlePointerAxisStop(wl: *Wayland, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32) callconv(.C) void { + _ = wl; + _ = pointer; + _ = time; + _ = axis; +} + +fn handlePointerAxisDiscrete(wl: *Wayland, pointer: ?*c.struct_wl_pointer, axis: u32, discrete: i32) callconv(.C) void { + _ = wl; + _ = pointer; + _ = axis; + _ = discrete; +} + +fn handlePointerAxisValue120(wl: *Wayland, pointer: ?*c.struct_wl_pointer, axis: u32, value_120: i32) callconv(.C) void { + _ = wl; + _ = pointer; + _ = axis; + _ = value_120; +} + +fn handlePointerAxisRelativeDirection(wl: *Wayland, pointer: ?*c.struct_wl_pointer, axis: u32, direction: u32) callconv(.C) void { + _ = wl; + _ = pointer; + _ = axis; + _ = direction; +} + +fn keyboardHandleKeymap(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, format: u32, fd: i32, keymap_size: u32) callconv(.C) void { + _ = keyboard; + + if (format != c.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + @panic("TODO"); + } + + const map_str = std.posix.mmap(null, keymap_size, std.posix.PROT.READ, .{ .TYPE = .SHARED }, fd, 0) catch unreachable; + + const keymap = wl.libxkbcommon.xkb_keymap_new_from_string( + wl.xkb_context, + @alignCast(map_str), //align cast happening here, im sure its fine? TODO: figure out if this okay + c.XKB_KEYMAP_FORMAT_TEXT_V1, + 0, + ).?; + + //Unmap the keymap + std.posix.munmap(map_str); + //Close the fd + std.posix.close(fd); + + const state = wl.libxkbcommon.xkb_state_new(keymap).?; + // defer wl.libxkbcommon.xkb_state_unref(state); + + //this chain hurts me. why must C be this way. + const locale = std.posix.getenv("LC_ALL") orelse std.posix.getenv("LC_CTYPE") orelse std.posix.getenv("LANG") orelse "C"; + + var compose_table = wl.libxkbcommon.xkb_compose_table_new_from_locale( + wl.xkb_context, + locale, + c.XKB_COMPOSE_COMPILE_NO_FLAGS, + ); + + //If creation failed, lets try the C locale + if (compose_table == null) + compose_table = wl.libxkbcommon.xkb_compose_table_new_from_locale( + wl.xkb_context, + "C", + c.XKB_COMPOSE_COMPILE_NO_FLAGS, + ).?; + + defer wl.libxkbcommon.xkb_compose_table_unref(compose_table); + + wl.keymap = keymap; + wl.xkb_state = state; + wl.compose_state = wl.libxkbcommon.xkb_compose_state_new(compose_table, c.XKB_COMPOSE_STATE_NO_FLAGS).?; + + wl.control_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Control"); + wl.alt_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod1"); + wl.shift_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Shift"); + wl.super_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod4"); + wl.caps_lock_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Lock"); + wl.num_lock_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod2"); +} + +fn keyboardHandleEnter(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface, keys: [*c]c.struct_wl_array) callconv(.C) void { + _ = keyboard; + _ = serial; + _ = surface; + _ = keys; + + wl.pushEvent(.focus_gained); +} + +fn keyboardHandleLeave(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void { + _ = keyboard; + _ = serial; + _ = surface; + + wl.pushEvent(.focus_lost); +} + +fn keyboardHandleKey(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, serial: u32, time: u32, scancode: u32, state: u32) callconv(.C) void { + _ = keyboard; + _ = serial; + _ = time; + + const key = toMachKey(scancode); + const pressed = state == 1; + + wl.input_state.keys.setValue(@intFromEnum(key), pressed); + + if (pressed) { + wl.pushEvent(Event{ .key_press = .{ + .key = key, + .mods = wl.modifiers, + } }); + + var keysyms: ?[*]c.xkb_keysym_t = undefined; + //Get the keysym from the keycode (scancode + 8) + if (wl.libxkbcommon.xkb_state_key_get_syms(wl.xkb_state, scancode + 8, &keysyms) == 1) { + //Compose the keysym + const keysym: c.xkb_keysym_t = composeSymbol(wl, keysyms.?[0]); + + //Try to convert that keysym to a unicode codepoint + if (unicode.unicodeFromKeySym(keysym)) |codepoint| { + wl.pushEvent(Event{ .char_input = .{ .codepoint = codepoint } }); + } + } + } else { + wl.pushEvent(Event{ .key_release = .{ + .key = key, + .mods = wl.modifiers, + } }); + } +} + +fn keyboardHandleModifiers(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, serial: u32, mods_depressed: u32, mods_latched: u32, mods_locked: u32, group: u32) callconv(.C) void { + _ = keyboard; + _ = serial; + + if (wl.keymap == null) + return; + + // TODO: handle this return value + _ = wl.libxkbcommon.xkb_state_update_mask( + wl.xkb_state.?, + mods_depressed, + mods_latched, + mods_locked, + 0, + 0, + group, + ); + + //Iterate over all the modifiers + inline for (.{ + .{ wl.alt_index, "alt" }, + .{ wl.shift_index, "shift" }, + .{ wl.super_index, "super" }, + .{ wl.control_index, "control" }, + .{ wl.num_lock_index, "num_lock" }, + .{ wl.caps_lock_index, "caps_lock" }, + }) |key| { + @field(wl.modifiers, key[1]) = wl.libxkbcommon.xkb_state_mod_index_is_active( + wl.xkb_state, + key[0], + c.XKB_STATE_MODS_EFFECTIVE, + ) == 1; + } +} + +fn keyboardHandleRepeatInfo(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, rate: i32, delay: i32) callconv(.C) void { + _ = wl; + _ = keyboard; + _ = rate; + _ = delay; +} + +fn composeSymbol(wl: *Wayland, sym: c.xkb_keysym_t) c.xkb_keysym_t { + if (sym == c.XKB_KEY_NoSymbol or wl.compose_state == null) + return sym; + + if (wl.libxkbcommon.xkb_compose_state_feed(wl.compose_state, sym) != c.XKB_COMPOSE_FEED_ACCEPTED) + return sym; + + return switch (wl.libxkbcommon.xkb_compose_state_get_status(wl.compose_state)) { + c.XKB_COMPOSE_COMPOSED => wl.libxkbcommon.xkb_compose_state_get_one_sym(wl.compose_state), + c.XKB_COMPOSE_COMPOSING, c.XKB_COMPOSE_CANCELLED => c.XKB_KEY_NoSymbol, + else => sym, + }; +} + +fn wmBaseHandlePing(wl: *Wayland, wm_base: ?*c.struct_xdg_wm_base, serial: u32) callconv(.C) void { + _ = wl; + c.xdg_wm_base_pong(wm_base, serial); +} + +fn registryHandleGlobalRemove(wl: *Wayland, registry: ?*c.struct_wl_registry, name: u32) callconv(.C) void { + _ = wl; + _ = registry; + _ = name; +} + +const registry_listener = c.wl_registry_listener{ + // ptrcast is for the [*:0] -> [*c] conversion, silly yes + .global = @ptrCast(®istryHandleGlobal), + // ptrcast is for the wl param, which is guarenteed to be our type (and if its not, it should be caught by safety checks) + .global_remove = @ptrCast(®istryHandleGlobalRemove), +}; + +fn xdgSurfaceHandleConfigure(wl: *Wayland, xdg_surface: ?*c.struct_xdg_surface, serial: u32) callconv(.C) void { + c.xdg_surface_ack_configure(xdg_surface, serial); + + if (wl.configured) { + c.wl_surface_commit(wl.surface); + } else { + wl.configured = true; + } + + setContentAreaOpaque(wl, wl.size); +} + +fn xdgToplevelHandleClose(wl: *Wayland, toplevel: ?*c.struct_xdg_toplevel) callconv(.C) void { + _ = wl; + _ = toplevel; +} + +fn xdgToplevelHandleConfigure(wl: *Wayland, toplevel: ?*c.struct_xdg_toplevel, width: i32, height: i32, states: [*c]c.struct_wl_array) callconv(.C) void { + _ = toplevel; + _ = states; + + if (width > 0 and height > 0) { + wl.size = .{ .width = @intCast(width), .height = @intCast(height) }; + } +} + +fn setContentAreaOpaque(wl: *Wayland, new_size: Size) void { + const region = c.wl_compositor_create_region(wl.interfaces.wl_compositor) orelse return; + + c.wl_region_add(region, 0, 0, @intCast(new_size.width), @intCast(new_size.height)); + c.wl_surface_set_opaque_region(wl.surface, region); + c.wl_region_destroy(region); + + wl.core.swap_chain_update.set(); +} diff --git a/src/core/X11.zig b/src/core/X11.zig index 5a18ed0f..c42deffe 100644 --- a/src/core/X11.zig +++ b/src/core/X11.zig @@ -1,9 +1,19 @@ const builtin = @import("builtin"); const std = @import("std"); +const c = @cImport({ + @cInclude("X11/Xlib.h"); + @cInclude("X11/Xatom.h"); + @cInclude("X11/cursorfont.h"); + @cInclude("X11/Xcursor/Xcursor.h"); + @cInclude("X11/extensions/Xrandr.h"); +}); const mach = @import("../main.zig"); -const gpu = mach.gpu; -const unicode = @import("x11/unicode.zig"); const Core = @import("../Core.zig"); +const InputState = @import("InputState.zig"); +const Frequency = @import("Frequency.zig"); +const unicode = @import("unicode.zig"); +const detectBackendType = @import("common.zig").detectBackendType; +const gpu = mach.gpu; const InitOptions = Core.InitOptions; const Event = Core.Event; const KeyEvent = Core.KeyEvent; @@ -11,7 +21,6 @@ const MouseButtonEvent = Core.MouseButtonEvent; const MouseButton = Core.MouseButton; const Size = Core.Size; const DisplayMode = Core.DisplayMode; -const SizeLimit = Core.SizeLimit; const CursorShape = Core.CursorShape; const VSyncMode = Core.VSyncMode; const CursorMode = Core.CursorMode; @@ -19,17 +28,6 @@ const Key = Core.Key; const KeyMods = Core.KeyMods; const Joystick = Core.Joystick; const Position = Core.Position; -const InputState = @import("InputState.zig"); -const Frequency = @import("Frequency.zig"); -const detectBackendType = @import("common.zig").detectBackendType; - -pub const c = @cImport({ - @cInclude("X11/Xlib.h"); - @cInclude("X11/Xatom.h"); - @cInclude("X11/cursorfont.h"); - @cInclude("X11/Xcursor/Xcursor.h"); - @cInclude("X11/extensions/Xrandr.h"); -}); const log = std.log.scoped(.mach); @@ -171,18 +169,18 @@ const LibGL = struct { pub const X11 = @This(); -// Read-only fields -core: *Core, allocator: std.mem.Allocator, -display: *c.Display, +core: *Core, + libx11: LibX11, libxrr: ?LibXRR, libgl: ?LibGL, libxcursor: ?LibXCursor, +gl_ctx: ?*LibGL.Context, +display: *c.Display, width: c_int, height: c_int, empty_event_pipe: [2]std.c.fd_t, -gl_ctx: ?*LibGL.Context, wm_protocols: c.Atom, wm_delete_window: c.Atom, net_wm_ping: c.Atom, @@ -211,8 +209,7 @@ input_mu: std.Thread.RwLock = .{}, input_state: InputState = .{}, // Mutable state fields; read/write by any thread -current_title: [:0]const u8, -current_title_changed: bool = false, +title: [:0]const u8, display_mode: DisplayMode = .windowed, vsync_mode: VSyncMode = .triple, border: bool, @@ -233,9 +230,7 @@ pub const EventIterator = struct { }; // Called on the main thread -pub fn init(x11: *X11, allocator: std.mem.Allocator, options: InitOptions) !void { - if (!@import("builtin").is_test) _ = mach.sysgpu.sysgpu.Export(mach.sysgpu.Impl); - +pub fn init(x11: *X11, options: InitOptions) !void { const libx11 = try LibX11.load(); const libxcursor: ?LibXCursor = LibXCursor.load() catch |err| switch (err) { error.LibraryNotFound => null, @@ -317,7 +312,7 @@ pub fn init(x11: *X11, allocator: std.mem.Allocator, options: InitOptions) !void var window_attrs: c.XWindowAttributes = undefined; _ = libx11.XGetWindowAttributes(display, window, &window_attrs); - const backend_type = try detectBackendType(allocator); + const backend_type = try detectBackendType(options.allocator); const refresh_rate: u16 = blk: { if (libxrr != null) { @@ -362,7 +357,7 @@ pub fn init(x11: *X11, allocator: std.mem.Allocator, options: InitOptions) !void // so we anticipate 2x that. If the event rate is higher than this per frame, it will grow to // that maximum (we never shrink the event queue capacity in order to avoid allocations causing // any stutter.) - var events = EventQueue.init(allocator); + var events = EventQueue.init(options.allocator); try events.ensureTotalCapacity(2048); const window_size = Size{ @@ -389,7 +384,7 @@ pub fn init(x11: *X11, allocator: std.mem.Allocator, options: InitOptions) !void x11.* = .{ .core = @fieldParentPtr("platform", x11), - .allocator = allocator, + .allocator = options.allocator, .display = display, .libx11 = libx11, .libgl = libgl, @@ -415,7 +410,7 @@ pub fn init(x11: *X11, allocator: std.mem.Allocator, options: InitOptions) !void .backend_type = backend_type, .refresh_rate = refresh_rate, .events = events, - .current_title = options.title, + .title = options.title, .display_mode = .windowed, .border = options.border, .headless = options.headless, @@ -489,8 +484,8 @@ pub inline fn pollEvents(x11: *X11) EventIterator { // May be called from any thread. pub fn setTitle(x11: *X11, title: [:0]const u8) void { - x11.current_title = title; - _ = x11.libx11.XStoreName(x11.display, x11.window, x11.current_title.ptr); + x11.title = title; + _ = x11.libx11.XStoreName(x11.display, x11.window, x11.title.ptr); } // May be called from any thread. diff --git a/src/core/x11/unicode.zig b/src/core/unicode.zig similarity index 98% rename from src/core/x11/unicode.zig rename to src/core/unicode.zig index 109e7ca1..a352d9c1 100644 --- a/src/core/x11/unicode.zig +++ b/src/core/unicode.zig @@ -1,7 +1,6 @@ -///! This code is taken from https://github.com/glfw/glfw/blob/master/src/xkb_unicode.c -const c = @import("../X11.zig").c; - -const keysym_table = &[_]struct { c.KeySym, u21 }{ +///! Taken from https://github.com/glfw/glfw/blob/master/src/xkb_unicode.c +const KeySym = c_ulong; +const keysym_table = &[_]struct { KeySym, u21 }{ .{ 0x01a1, 0x0104 }, .{ 0x01a2, 0x02d8 }, .{ 0x01a3, 0x0141 }, @@ -832,7 +831,7 @@ const keysym_table = &[_]struct { c.KeySym, u21 }{ .{ 0xffbd, '=' }, // XKB_KEY_KP_Equal }; -pub fn unicodeFromKeySym(keysym: c.KeySym) ?u21 { +pub fn unicodeFromKeySym(keysym: KeySym) ?u21 { var min: usize = 0; var mid: usize = 0; var max = keysym_table.len - 1; diff --git a/src/sysgpu/vulkan.zig b/src/sysgpu/vulkan.zig index bcdb8ec8..be4e3cec 100644 --- a/src/sysgpu/vulkan.zig +++ b/src/sysgpu/vulkan.zig @@ -135,8 +135,7 @@ pub const Instance = struct { vk.extensions.khr_surface.name, vk.extensions.khr_xlib_surface.name, vk.extensions.khr_xcb_surface.name, - // TODO: renderdoc will not work with this extension - // vk.extensions.khr_wayland_surface.name, + vk.extensions.khr_wayland_surface.name, }, .windows => &.{ vk.extensions.khr_surface.name, @@ -400,17 +399,14 @@ pub const Surface = struct { null, ); } else if (utils.findChained(sysgpu.Surface.DescriptorFromWaylandSurface, desc.next_in_chain.generic)) |wayland_desc| { - _ = wayland_desc; - @panic("unimplemented"); - // TODO: renderdoc will not work with wayland - // break :blk try vki.createWaylandSurfaceKHR( - // vk_instance, - // &vk.WaylandSurfaceCreateInfoKHR{ - // .display = @ptrCast(wayland_desc.display), - // .surface = @ptrCast(wayland_desc.surface), - // }, - // null, - // ); + break :blk try vki.createWaylandSurfaceKHR( + vk_instance, + &vk.WaylandSurfaceCreateInfoKHR{ + .display = @ptrCast(wayland_desc.display), + .surface = @ptrCast(wayland_desc.surface), + }, + null, + ); } return error.InvalidDescriptor; diff --git a/src/sysgpu/vulkan/proc.zig b/src/sysgpu/vulkan/proc.zig index 306db6bd..4ddfc41b 100644 --- a/src/sysgpu/vulkan/proc.zig +++ b/src/sysgpu/vulkan/proc.zig @@ -17,8 +17,7 @@ pub const InstanceFunctions = vk.InstanceWrapper(&.{ .{ .instance_commands = .{ .createDevice = true, - // TODO: renderdoc will not work with wayland - // .createWaylandSurfaceKHR = builtin.target.os.tag == .linux, + .createWaylandSurfaceKHR = builtin.target.os.tag == .linux, .createWin32SurfaceKHR = builtin.target.os.tag == .windows, .createXlibSurfaceKHR = builtin.target.os.tag == .linux, .destroyInstance = true,