From 21c59b568f4189991caad07d67c2c6be59a807c4 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Sat, 24 Aug 2024 23:27:15 -0700 Subject: [PATCH] core: remove linux support The `mach.Core` API needs major design changes, and every backend that it supports today must be updated to account for those API design changes. Unless someone is actively maintaining and improving the state of a given backend, it slows down our ability to make those critical changes. Unfortunately, the backends for X11 and Wayland today are half-baked, nobody is actively maintaining on or contributing to them, and the Linux CI tests have been broken for over a month as a result which harms overall stability of Mach. As a result, this PR removes Linux support from `mach.Core` Signed-off-by: Stephen Gutekanst --- build.zig | 45 +- src/Core.zig | 4 - src/core/Wayland.zig | 1024 ------------------------------- src/core/X11.zig | 1161 ------------------------------------ src/core/wayland/wayland.c | 7 - 5 files changed, 4 insertions(+), 2237 deletions(-) delete mode 100644 src/core/Wayland.zig delete mode 100644 src/core/X11.zig delete mode 100644 src/core/wayland/wayland.c diff --git a/build.zig b/build.zig index d22c7cd058..f65c6ff6f6 100644 --- a/build.zig +++ b/build.zig @@ -102,46 +102,11 @@ pub fn build(b: *std.Build) !void { if (want_examples) try buildExamples(b, optimize, target, module); } if (want_core) { - if (target.result.cpu.arch != .wasm32) { - // TODO: for some reason this is not functional, a Zig bug (only when using this Zig package - // externally): - // - // module.addCSourceFile(.{ .file = b.path("src/core/platform/wayland/wayland.c" }); - // - // error: unable to check cache: stat file '/Volumes/data/hexops/mach-core-starter-project/zig-cache//Volumes/data/hexops/mach-core-starter-project/src/core/platform/wayland/wayland.c' failed: FileNotFound - // - // So instead we do this: - const lib = b.addStaticLibrary(.{ - .name = "core-wayland", - .target = target, - .optimize = optimize, - }); - lib.addCSourceFile(.{ - .file = b.path("src/core/wayland/wayland.c"), - }); - lib.linkLibC(); - module.linkLibrary(lib); - - if (b.lazyDependency("x11_headers", .{ - .target = target, - .optimize = optimize, - })) |dep| { - module.linkLibrary(dep.artifact("x11-headers")); - lib.linkLibrary(dep.artifact("x11-headers")); - } - if (b.lazyDependency("wayland_headers", .{ + if (target.result.isDarwin()) { + if (b.lazyDependency("mach_objc", .{ .target = target, .optimize = optimize, - })) |dep| { - module.linkLibrary(dep.artifact("wayland-headers")); - lib.linkLibrary(dep.artifact("wayland-headers")); - } - if (target.result.isDarwin()) { - if (b.lazyDependency("mach_objc", .{ - .target = target, - .optimize = optimize, - })) |dep| module.addImport("objc", dep.module("mach-objc")); - } + })) |dep| module.addImport("objc", dep.module("mach-objc")); } } if (want_sysaudio) { @@ -284,8 +249,6 @@ pub fn build(b: *std.Build) !void { } pub const Platform = enum { - x11, - wayland, wasm, win32, darwin, @@ -295,7 +258,7 @@ pub const Platform = enum { if (target.cpu.arch == .wasm32) return .wasm; if (target.os.tag.isDarwin()) return .darwin; if (target.os.tag == .windows) return .win32; - return .x11; + return .null; } }; diff --git a/src/Core.zig b/src/Core.zig index 5e48156a5d..8cfb2613d4 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -13,8 +13,6 @@ pub const Timer = @import("core/Timer.zig"); const Frequency = @import("core/Frequency.zig"); pub const Platform = switch (build_options.core_platform) { - .x11 => @import("core/X11.zig"), - .wayland => @import("core/Wayland.zig"), .wasm => @panic("TODO: support mach.Core WASM platform"), .win32 => @import("core/win32.zig"), .darwin => @import("core/Darwin.zig"), @@ -25,8 +23,6 @@ pub const Platform = switch (build_options.core_platform) { // platform must take control and drive the main loop itself. pub const supports_non_blocking = switch (build_options.core_platform) { .win32 => true, - .x11 => true, - .wayland => true, .wasm => false, .darwin => false, .null => false, diff --git a/src/core/Wayland.zig b/src/core/Wayland.zig deleted file mode 100644 index 7eedcb1868..0000000000 --- a/src/core/Wayland.zig +++ /dev/null @@ -1,1024 +0,0 @@ -const std = @import("std"); -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 = 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); - -pub const c = @cImport({ - @cInclude("wayland-client-protocol.h"); - @cInclude("wayland-xdg-shell-client-protocol.h"); - @cInclude("wayland-xdg-decoration-client-protocol.h"); - @cInclude("wayland-viewporter-client-protocol.h"); - @cInclude("wayland-relative-pointer-unstable-v1-client-protocol.h"); - @cInclude("wayland-pointer-constraints-unstable-v1-client-protocol.h"); - @cInclude("wayland-idle-inhibit-unstable-v1-client-protocol.h"); - @cInclude("xkbcommon/xkbcommon.h"); - @cInclude("xkbcommon/xkbcommon-compose.h"); - @cInclude("linux/input-event-codes.h"); -}); - -var libwaylandclient: LibWaylandClient = undefined; - -export fn wl_proxy_add_listener(proxy: ?*c.struct_wl_proxy, implementation: [*c]?*const fn () callconv(.C) void, data: ?*anyopaque) c_int { - return @call(.always_tail, libwaylandclient.wl_proxy_add_listener, .{ proxy, implementation, data }); -} - -export fn wl_proxy_get_version(proxy: ?*c.struct_wl_proxy) u32 { - return @call(.always_tail, libwaylandclient.wl_proxy_get_version, .{proxy}); -} - -export fn wl_proxy_marshal_flags(proxy: ?*c.struct_wl_proxy, opcode: u32, interface: [*c]const c.struct_wl_interface, version: u32, flags: u32, ...) ?*c.struct_wl_proxy { - var arg_list: std.builtin.VaList = @cVaStart(); - defer @cVaEnd(&arg_list); - - return @call(.always_tail, libwaylandclient.wl_proxy_marshal_flags, .{ proxy, opcode, interface, version, flags, arg_list }); -} - -export fn wl_proxy_destroy(proxy: ?*c.struct_wl_proxy) void { - return @call(.always_tail, libwaylandclient.wl_proxy_destroy, .{proxy}); -} - -const LibXkbCommon = struct { - handle: std.DynLib, - - xkb_context_new: *const @TypeOf(c.xkb_context_new), - xkb_keymap_new_from_string: *const @TypeOf(c.xkb_keymap_new_from_string), - xkb_state_new: *const @TypeOf(c.xkb_state_new), - xkb_keymap_unref: *const @TypeOf(c.xkb_keymap_unref), - xkb_state_unref: *const @TypeOf(c.xkb_state_unref), - xkb_compose_table_new_from_locale: *const @TypeOf(c.xkb_compose_table_new_from_locale), - xkb_compose_state_new: *const @TypeOf(c.xkb_compose_state_new), - xkb_compose_table_unref: *const @TypeOf(c.xkb_compose_table_unref), - xkb_keymap_mod_get_index: *const @TypeOf(c.xkb_keymap_mod_get_index), - xkb_state_update_mask: *const @TypeOf(c.xkb_state_update_mask), - xkb_state_mod_index_is_active: *const @TypeOf(c.xkb_state_mod_index_is_active), - xkb_state_key_get_syms: *const @TypeOf(c.xkb_state_key_get_syms), - xkb_compose_state_feed: *const @TypeOf(c.xkb_compose_state_feed), - xkb_compose_state_get_status: *const @TypeOf(c.xkb_compose_state_get_status), - xkb_compose_state_get_one_sym: *const @TypeOf(c.xkb_compose_state_get_one_sym), - - pub fn load() !LibXkbCommon { - var lib: LibXkbCommon = undefined; - lib.handle = std.DynLib.open("libxkbcommon.so.0") catch return error.LibraryNotFound; - inline for (@typeInfo(LibXkbCommon).Struct.fields[1..]) |field| { - const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); - const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); - @field(lib, field.name) = lib.handle.lookup(field.type, name_z) orelse { - log.err("Symbol lookup failed for {s}", .{name}); - return error.SymbolLookup; - }; - } - return lib; - } -}; - -const LibWaylandClient = struct { - handle: std.DynLib, - - wl_display_connect: *const @TypeOf(c.wl_display_connect), - wl_proxy_add_listener: *const @TypeOf(c.wl_proxy_add_listener), - wl_proxy_get_version: *const @TypeOf(c.wl_proxy_get_version), - wl_proxy_marshal_flags: *const @TypeOf(c.wl_proxy_marshal_flags), - wl_proxy_set_tag: *const @TypeOf(c.wl_proxy_set_tag), - wl_proxy_destroy: *const @TypeOf(c.wl_proxy_destroy), - wl_display_roundtrip: *const @TypeOf(c.wl_display_roundtrip), - wl_display_dispatch: *const @TypeOf(c.wl_display_dispatch), - wl_display_flush: *const @TypeOf(c.wl_display_flush), - wl_display_get_fd: *const @TypeOf(c.wl_display_get_fd), - - //Interfaces - wl_compositor_interface: *@TypeOf(c.wl_compositor_interface), - wl_subcompositor_interface: *@TypeOf(c.wl_subcompositor_interface), - wl_shm_interface: *@TypeOf(c.wl_subcompositor_interface), - wl_data_device_manager_interface: *@TypeOf(c.wl_data_device_manager_interface), - - wl_buffer_interface: *@TypeOf(c.wl_buffer_interface), - wl_callback_interface: *@TypeOf(c.wl_callback_interface), - wl_data_device_interface: *@TypeOf(c.wl_data_device_interface), - wl_data_offer_interface: *@TypeOf(c.wl_data_offer_interface), - wl_data_source_interface: *@TypeOf(c.wl_data_source_interface), - wl_keyboard_interface: *@TypeOf(c.wl_keyboard_interface), - wl_output_interface: *@TypeOf(c.wl_output_interface), - wl_pointer_interface: *@TypeOf(c.wl_pointer_interface), - wl_region_interface: *@TypeOf(c.wl_region_interface), - wl_registry_interface: *@TypeOf(c.wl_registry_interface), - wl_seat_interface: *@TypeOf(c.wl_seat_interface), - wl_shell_surface_interface: *@TypeOf(c.wl_shell_surface_interface), - wl_shm_pool_interface: *@TypeOf(c.wl_shm_pool_interface), - wl_subsurface_interface: *@TypeOf(c.wl_subsurface_interface), - wl_surface_interface: *@TypeOf(c.wl_surface_interface), - wl_touch_interface: *@TypeOf(c.wl_touch_interface), - - pub extern const xdg_wm_base_interface: @TypeOf(c.xdg_wm_base_interface); - pub extern const zxdg_decoration_manager_v1_interface: @TypeOf(c.zxdg_decoration_manager_v1_interface); - - pub fn load() !LibWaylandClient { - var lib: LibWaylandClient = undefined; - lib.handle = std.DynLib.open("libwayland-client.so.0") catch return error.LibraryNotFound; - inline for (@typeInfo(LibWaylandClient).Struct.fields[1..]) |field| { - const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); - const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); - @field(lib, field.name) = lib.handle.lookup(field.type, name_z) orelse { - log.err("Symbol lookup failed for {s}", .{name}); - return error.SymbolLookup; - }; - } - return lib; - } -}; - -const EventQueue = std.fifo.LinearFifo(Event, .Dynamic); -pub const EventIterator = struct { - queue: *EventQueue, - - pub inline fn next(self: *EventIterator) ?Event { - return self.queue.readItem(); - } -}; - -const Interfaces = struct { - wl_compositor: ?*c.wl_compositor = null, - wl_subcompositor: ?*c.wl_subcompositor = null, - wl_shm: ?*c.wl_shm = null, - wl_output: ?*c.wl_output = null, - wl_seat: ?*c.wl_seat = null, - wl_data_device_manager: ?*c.wl_data_device_manager = null, - xdg_wm_base: ?*c.xdg_wm_base = null, - zxdg_decoration_manager_v1: ?*c.zxdg_decoration_manager_v1 = null, - // wp_viewporter: *c.wp_viewporter, - // zwp_relative_pointer_manager_v1: *c.zwp_relative_pointer_manager_v1, - // zwp_pointer_constraints_v1: *c.zwp_pointer_constraints_v1, - // zwp_idle_inhibit_manager_v1: *c.zwp_idle_inhibit_manager_v1, - // xdg_activation_v1: *c.xdg_activation_v1, -}; - -pub const Wayland = @This(); - -allocator: std.mem.Allocator, -core: *Core, - -// 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, -registry: *c.struct_wl_registry, -xdg_surface: *c.xdg_surface, -toplevel: *c.xdg_toplevel, -tag: [*]c_char, -decoration: *c.zxdg_toplevel_decoration_v1, -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(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; - - // TODO: handle error return value here - _ = c.wl_registry_add_listener(wl.registry, ®istry_listener, wl); - - //Round trip to get all the registry objects - _ = libwaylandclient.wl_display_roundtrip(wl.display); - - //Round trip to get all initial output events - _ = libwaylandclient.wl_display_roundtrip(wl.display); - - wl.surface = c.wl_compositor_create_surface(wl.interfaces.wl_compositor) orelse return error.UnableToCreateSurface; - - libwaylandclient.wl_proxy_set_tag(@ptrCast(wl.surface), @ptrCast(&wl.tag)); - - { - const region = c.wl_compositor_create_region(wl.interfaces.wl_compositor) orelse return error.CouldntCreateWaylandRegtion; - - c.wl_region_add( - region, - 0, - 0, - @intCast(options.size.width), - @intCast(options.size.height), - ); - c.wl_surface_set_opaque_region(wl.surface, region); - c.wl_region_destroy(region); - } - - 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; - - // TODO: handle this return value - _ = c.xdg_surface_add_listener(wl.xdg_surface, &.{ .configure = @ptrCast(&xdgSurfaceHandleConfigure) }, wl); - - // TODO: handle this return value - _ = c.xdg_toplevel_add_listener(wl.toplevel, &.{ - .configure = @ptrCast(&xdgToplevelHandleConfigure), - .close = @ptrCast(&xdgToplevelHandleClose), - }, wl); - - // Commit changes to surface - c.wl_surface_commit(wl.surface); - - while (libwaylandclient.wl_display_dispatch(wl.display) != -1 and !wl.configured) { - // This space intentionally left blank - } - - c.xdg_toplevel_set_title(wl.toplevel, wl.title); - - wl.decoration = c.zxdg_decoration_manager_v1_get_toplevel_decoration( - wl.interfaces.zxdg_decoration_manager_v1, - wl.toplevel, - ) orelse return error.UnableToGetToplevelDecoration; - - 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(wl.surface); - // TODO: handle return value - _ = libwaylandclient.wl_display_roundtrip(wl.display); - - // 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(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(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(wl.display), - // .events = std.posix.POLL.OUT, - // .revents = 0, - // }, - // }; - - // while (try std.posix.poll(&pollfd, -1) != 0) { - // // if (std.posix.errno() == std.posix.E.INTR or std.posix.errno() == std.posix.E.AGAIN) { - // // log.err("poll error", .{}); - // // return true; - // // } - // } - // } - - _ = libwaylandclient.wl_display_roundtrip(wl.display); - - wl.core.input.tick(); -} - -// May be called from any thread. -pub inline fn pollEvents(wl: *Wayland) EventIterator { - return EventIterator{ .queue = &wl.events }; -} - -// May be called from any thread. -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(_: *Wayland, _: DisplayMode) void { - @panic("TODO: implement setDisplayMode for Wayland"); -} - -// May be called from any thread. -pub fn setBorder(_: *Wayland, _: bool) void { - @panic("TODO: implement setBorder for Wayland"); -} - -// May be called from any thread. -pub fn setHeadless(_: *Wayland, _: bool) void { - @panic("TODO: implement setHeadless for Wayland"); -} - -// May be called from any thread. -pub fn setVSync(_: *Wayland, _: VSyncMode) void { - @panic("TODO: implement setVSync for Wayland"); -} - -// May be called from any thread. -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 size(wl: *Wayland) Size { - return wl.size; -} - -// May be called from any thread. -pub fn setCursorMode(_: *Wayland, _: CursorMode) void { - @panic("TODO: implement setCursorMode for Wayland"); -} - -// May be called from any thread. -pub fn setCursorShape(_: *Wayland, _: CursorShape) void { - @panic("TODO: implement setCursorShape for Wayland"); -} - -// May be called from any thread. -pub fn joystickPresent(_: *Wayland, _: Joystick) bool { - @panic("TODO: implement joystickPresent for Wayland"); -} - -// May be called from any thread. -pub fn joystickName(_: *Wayland, _: Joystick) ?[:0]const u8 { - @panic("TODO: implement joystickName for Wayland"); -} - -// May be called from any thread. -pub fn joystickButtons(_: *Wayland, _: Joystick) ?[]const bool { - @panic("TODO: implement joystickButtons for Wayland"); -} - -// May be called from any thread. -pub fn joystickAxes(_: *Wayland, _: Joystick) ?[]const f32 { - @panic("TODO: implement joystickAxes for Wayland"); -} - -// May be called from any thread. -pub fn keyPressed(wl: *Wayland, key: Key) bool { - wl.input_state.isKeyPressed(key); -} - -// May be called from any thread. -pub fn keyReleased(wl: *Wayland, key: Key) bool { - wl.input_state.isKeyReleased(key); -} - -// May be called from any thread. -pub fn mousePressed(wl: *Wayland, button: MouseButton) bool { - return wl.input_state.isMouseButtonPressed(button); -} - -// May be called from any thread. -pub fn mouseReleased(wl: *Wayland, button: MouseButton) bool { - return wl.input_state.isMouseButtonReleased(button); -} - -// May be called from any thread. -pub fn mousePosition(wl: *Wayland) Position { - return wl.mouse_pos; -} - -fn toMachKey(key: u32) Key { - return switch (key) { - c.KEY_GRAVE => .grave, - c.KEY_1 => .one, - c.KEY_2 => .two, - c.KEY_3 => .three, - c.KEY_4 => .four, - c.KEY_5 => .five, - c.KEY_6 => .six, - c.KEY_7 => .seven, - c.KEY_8 => .eight, - c.KEY_9 => .nine, - c.KEY_0 => .zero, - c.KEY_SPACE => .space, - c.KEY_MINUS => .minus, - c.KEY_EQUAL => .equal, - c.KEY_Q => .q, - c.KEY_W => .w, - c.KEY_E => .e, - c.KEY_R => .r, - c.KEY_T => .t, - c.KEY_Y => .y, - c.KEY_U => .u, - c.KEY_I => .i, - c.KEY_O => .o, - c.KEY_P => .p, - c.KEY_LEFTBRACE => .left_bracket, - c.KEY_RIGHTBRACE => .right_bracket, - c.KEY_A => .a, - c.KEY_S => .s, - c.KEY_D => .d, - c.KEY_F => .f, - c.KEY_G => .g, - c.KEY_H => .h, - c.KEY_J => .j, - c.KEY_K => .k, - c.KEY_L => .l, - c.KEY_SEMICOLON => .semicolon, - c.KEY_APOSTROPHE => .apostrophe, - c.KEY_Z => .z, - c.KEY_X => .x, - c.KEY_C => .c, - c.KEY_V => .v, - c.KEY_B => .b, - c.KEY_N => .n, - c.KEY_M => .m, - c.KEY_COMMA => .comma, - c.KEY_DOT => .period, - c.KEY_SLASH => .slash, - c.KEY_BACKSLASH => .backslash, - c.KEY_ESC => .escape, - c.KEY_TAB => .tab, - c.KEY_LEFTSHIFT => .left_shift, - c.KEY_RIGHTSHIFT => .right_shift, - c.KEY_LEFTCTRL => .left_control, - c.KEY_RIGHTCTRL => .right_control, - c.KEY_LEFTALT => .left_alt, - c.KEY_RIGHTALT => .right_alt, - c.KEY_LEFTMETA => .left_super, - c.KEY_RIGHTMETA => .right_super, - c.KEY_NUMLOCK => .num_lock, - c.KEY_CAPSLOCK => .caps_lock, - c.KEY_PRINT => .print, - c.KEY_SCROLLLOCK => .scroll_lock, - c.KEY_PAUSE => .pause, - c.KEY_DELETE => .delete, - c.KEY_BACKSPACE => .backspace, - c.KEY_ENTER => .enter, - c.KEY_HOME => .home, - c.KEY_END => .end, - c.KEY_PAGEUP => .page_up, - c.KEY_PAGEDOWN => .page_down, - c.KEY_INSERT => .insert, - c.KEY_LEFT => .left, - c.KEY_RIGHT => .right, - c.KEY_DOWN => .down, - c.KEY_UP => .up, - c.KEY_F1 => .f1, - c.KEY_F2 => .f2, - c.KEY_F3 => .f3, - c.KEY_F4 => .f4, - c.KEY_F5 => .f5, - c.KEY_F6 => .f6, - c.KEY_F7 => .f7, - c.KEY_F8 => .f8, - c.KEY_F9 => .f9, - c.KEY_F10 => .f10, - c.KEY_F11 => .f11, - c.KEY_F12 => .f12, - c.KEY_F13 => .f13, - c.KEY_F14 => .f14, - c.KEY_F15 => .f15, - c.KEY_F16 => .f16, - c.KEY_F17 => .f17, - c.KEY_F18 => .f18, - c.KEY_F19 => .f19, - c.KEY_F20 => .f20, - c.KEY_F21 => .f21, - c.KEY_F22 => .f22, - c.KEY_F23 => .f23, - c.KEY_F24 => .f24, - c.KEY_KPSLASH => .kp_divide, - c.KEY_KPASTERISK => .kp_multiply, - c.KEY_KPMINUS => .kp_subtract, - c.KEY_KPPLUS => .kp_add, - c.KEY_KP0 => .kp_0, - c.KEY_KP1 => .kp_1, - c.KEY_KP2 => .kp_2, - c.KEY_KP3 => .kp_3, - c.KEY_KP4 => .kp_4, - c.KEY_KP5 => .kp_5, - c.KEY_KP6 => .kp_6, - c.KEY_KP7 => .kp_7, - c.KEY_KP8 => .kp_8, - c.KEY_KP9 => .kp_9, - c.KEY_KPDOT => .kp_decimal, - c.KEY_KPEQUAL => .kp_equal, - c.KEY_KPENTER => .kp_enter, - 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 deleted file mode 100644 index c42deffef6..0000000000 --- a/src/core/X11.zig +++ /dev/null @@ -1,1161 +0,0 @@ -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 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; -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 Key = Core.Key; -const KeyMods = Core.KeyMods; -const Joystick = Core.Joystick; -const Position = Core.Position; - -const log = std.log.scoped(.mach); - -pub const defaultLog = std.log.defaultLog; -pub const defaultPanic = std.debug.panicImpl; - -const LibX11 = struct { - handle: std.DynLib, - - XInitThreads: *const @TypeOf(c.XInitThreads), - XrmInitialize: *const @TypeOf(c.XrmInitialize), - XOpenDisplay: *const @TypeOf(c.XOpenDisplay), - XCloseDisplay: *const @TypeOf(c.XCloseDisplay), - XCreateWindow: *const @TypeOf(c.XCreateWindow), - XSelectInput: *const @TypeOf(c.XSelectInput), - XMapWindow: *const @TypeOf(c.XMapWindow), - XNextEvent: *const @TypeOf(c.XNextEvent), - XDisplayWidth: *const @TypeOf(c.XDisplayWidth), - XDisplayHeight: *const @TypeOf(c.XDisplayHeight), - XCreateColormap: *const @TypeOf(c.XCreateColormap), - XSetErrorHandler: *const @TypeOf(c.XSetErrorHandler), - XGetWindowAttributes: *const @TypeOf(c.XGetWindowAttributes), - XStoreName: *const @TypeOf(c.XStoreName), - XFreeColormap: *const @TypeOf(c.XFreeColormap), - XUnmapWindow: *const @TypeOf(c.XUnmapWindow), - XDestroyWindow: *const @TypeOf(c.XDestroyWindow), - XFlush: *const @TypeOf(c.XFlush), - XLookupString: *const @TypeOf(c.XLookupString), - XQueryPointer: *const @TypeOf(c.XQueryPointer), - XInternAtom: *const @TypeOf(c.XInternAtom), - XSendEvent: *const @TypeOf(c.XSendEvent), - XSetWMProtocols: *const @TypeOf(c.XSetWMProtocols), - XDefineCursor: *const @TypeOf(c.XDefineCursor), - XUndefineCursor: *const @TypeOf(c.XUndefineCursor), - XCreatePixmap: *const @TypeOf(c.XCreatePixmap), - XCreateGC: *const @TypeOf(c.XCreateGC), - XDrawPoint: *const @TypeOf(c.XDrawPoint), - XFreeGC: *const @TypeOf(c.XFreeGC), - XCreatePixmapCursor: *const @TypeOf(c.XCreatePixmapCursor), - XGrabPointer: *const @TypeOf(c.XGrabPointer), - XUngrabPointer: *const @TypeOf(c.XUngrabPointer), - XCreateFontCursor: *const @TypeOf(c.XCreateFontCursor), - XFreeCursor: *const @TypeOf(c.XFreeCursor), - XChangeProperty: *const @TypeOf(c.XChangeProperty), - XResizeWindow: *const @TypeOf(c.XResizeWindow), - XConfigureWindow: *const @TypeOf(c.XConfigureWindow), - XSetWMHints: *const @TypeOf(c.XSetWMHints), - XDeleteProperty: *const @TypeOf(c.XDeleteProperty), - XAllocSizeHints: *const @TypeOf(c.XAllocSizeHints), - XSetWMNormalHints: *const @TypeOf(c.XSetWMNormalHints), - XFree: *const @TypeOf(c.XFree), - - pub fn load() !LibX11 { - var lib: LibX11 = undefined; - lib.handle = std.DynLib.open("libX11.so.6") catch return error.LibraryNotFound; - inline for (@typeInfo(LibX11).Struct.fields[1..]) |field| { - const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); - const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); - @field(lib, field.name) = lib.handle.lookup(field.type, name_z) orelse return error.SymbolLookup; - } - return lib; - } -}; - -const LibXCursor = struct { - handle: std.DynLib, - - XcursorImageCreate: *const @TypeOf(c.XcursorImageCreate), - XcursorImageDestroy: *const @TypeOf(c.XcursorImageDestroy), - XcursorImageLoadCursor: *const @TypeOf(c.XcursorImageLoadCursor), - XcursorGetTheme: *const @TypeOf(c.XcursorGetTheme), - XcursorGetDefaultSize: *const @TypeOf(c.XcursorGetDefaultSize), - XcursorLibraryLoadImage: *const @TypeOf(c.XcursorLibraryLoadImage), - - pub fn load() !LibXCursor { - var lib: LibXCursor = undefined; - lib.handle = std.DynLib.open("libXcursor.so.1") catch return error.LibraryNotFound; - inline for (@typeInfo(LibXCursor).Struct.fields[1..]) |field| { - const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); - const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); - @field(lib, field.name) = lib.handle.lookup(field.type, name_z) orelse return error.SymbolLookup; - } - return lib; - } -}; - -const LibXRR = struct { - handle: std.DynLib, - - XRRGetScreenInfo: *const @TypeOf(c.XRRGetScreenInfo), - XRRConfigCurrentRate: *const @TypeOf(c.XRRConfigCurrentRate), - - pub fn load() !LibXRR { - var lib: LibXRR = undefined; - lib.handle = std.DynLib.open("libXrandr.so.1") catch return error.LibraryNotFound; - inline for (@typeInfo(LibXRR).Struct.fields[1..]) |field| { - const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); - const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); - @field(lib, field.name) = lib.handle.lookup(field.type, name_z) orelse return error.SymbolLookup; - } - return lib; - } -}; - -const LibGL = struct { - const Drawable = c.XID; - const Context = opaque {}; - const FBConfig = opaque {}; - - const rgba = 4; - const doublebuffer = 5; - const red_size = 8; - const green_size = 9; - const blue_size = 10; - const depth_size = 12; - const stencil_size = 13; - const sample_buffers = 0x186a0; - const samples = 0x186a1; - - handle: std.DynLib, - - glXCreateContext: *const fn (*c.Display, *c.XVisualInfo, ?*Context, bool) callconv(.C) ?*Context, - glXDestroyContext: *const fn (*c.Display, ?*Context) callconv(.C) void, - glXMakeCurrent: *const fn (*c.Display, Drawable, ?*Context) callconv(.C) bool, - glXChooseVisual: *const fn (*c.Display, c_int, [*]const c_int) callconv(.C) *c.XVisualInfo, - glXSwapBuffers: *const fn (*c.Display, Drawable) callconv(.C) bool, - - pub fn load() !LibGL { - var lib: LibGL = undefined; - lib.handle = std.DynLib.open("libGL.so.1") catch return error.LibraryNotFound; - inline for (@typeInfo(LibGL).Struct.fields[1..]) |field| { - const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); - const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); - @field(lib, field.name) = lib.handle.lookup(field.type, name_z) orelse return error.SymbolLookup; - } - return lib; - } -}; - -pub const X11 = @This(); - -allocator: std.mem.Allocator, -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, -wm_protocols: c.Atom, -wm_delete_window: c.Atom, -net_wm_ping: c.Atom, -net_wm_state_fullscreen: c.Atom, -net_wm_state: c.Atom, -net_wm_state_above: c.Atom, -net_wm_bypass_compositor: c.Atom, -motif_wm_hints: c.Atom, -net_wm_window_type: c.Atom, -net_wm_window_type_dock: c.Atom, -root_window: c.Window, -window: c.Window, -backend_type: gpu.BackendType, -refresh_rate: u32, -hidden_cursor: c.Cursor, - -// Mutable fields only used by main thread -cursors: [@typeInfo(CursorShape).Enum.fields.len]?c.Cursor, - -// Event queue; written from main thread; read from any -events_mu: std.Thread.RwLock = .{}, -events: EventQueue, - -// Input state; written from main thread; read from any -input_mu: std.Thread.RwLock = .{}, -input_state: InputState = .{}, - -// Mutable state fields; read/write by any thread -title: [:0]const u8, -display_mode: DisplayMode = .windowed, -vsync_mode: VSyncMode = .triple, -border: bool, -headless: bool, -size: Size, -cursor_mode: CursorMode = .normal, -cursor_shape: CursorShape = .arrow, -surface_descriptor: gpu.Surface.Descriptor, - -const EventQueue = std.fifo.LinearFifo(Event, .Dynamic); - -pub const EventIterator = struct { - queue: *EventQueue, - - pub inline fn next(iterator: *EventIterator) ?Event { - return iterator.queue.readItem(); - } -}; - -// Called on the main thread -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, - else => return err, - }; - const libxrr: ?LibXRR = LibXRR.load() catch |err| switch (err) { - error.LibraryNotFound => null, - else => return err, - }; - const libgl: ?LibGL = LibGL.load() catch |err| switch (err) { - error.LibraryNotFound => null, - else => return err, - }; - - _ = libx11.XSetErrorHandler(errorHandler); - _ = libx11.XInitThreads(); - _ = libx11.XrmInitialize(); - - const display = libx11.XOpenDisplay(null) orelse { - std.log.err("X11: Cannot open display", .{}); - return error.CannotOpenDisplay; - }; - - const screen = c.DefaultScreen(display); - const root_window = c.RootWindow(display, screen); - const visual = c.DefaultVisual(display, screen); - const colormap = libx11.XCreateColormap(display, root_window, visual, c.AllocNone); - var set_window_attrs = c.XSetWindowAttributes{ - .colormap = colormap, - // TODO: reduce - .event_mask = c.StructureNotifyMask | c.KeyPressMask | c.KeyReleaseMask | - c.PointerMotionMask | c.ButtonPressMask | c.ButtonReleaseMask | - c.ExposureMask | c.FocusChangeMask | c.VisibilityChangeMask | - c.EnterWindowMask | c.LeaveWindowMask | c.PropertyChangeMask, - }; - defer _ = libx11.XFreeColormap(display, colormap); - - const empty_event_pipe = try std.posix.pipe(); - for (0..2) |i| { - const sf = try std.posix.fcntl(empty_event_pipe[i], std.posix.F.GETFL, 0); - const df = try std.posix.fcntl(empty_event_pipe[i], std.posix.F.GETFD, 0); - _ = try std.posix.fcntl(empty_event_pipe[i], std.posix.F.SETFL, sf | @as(u32, @bitCast(std.posix.O{ .NONBLOCK = true }))); - _ = try std.posix.fcntl(empty_event_pipe[i], std.posix.F.SETFD, df | std.posix.FD_CLOEXEC); - } - - const window = libx11.XCreateWindow( - display, - root_window, - @divFloor(libx11.XDisplayWidth(display, screen), 2), // TODO: add window width? - @divFloor(libx11.XDisplayHeight(display, screen), 2), // TODO: add window height? - options.size.width, - options.size.height, - 0, - c.DefaultDepth(display, screen), - c.InputOutput, - visual, - c.CWColormap | c.CWEventMask, - &set_window_attrs, - ); - - const wm_protocols = libx11.XInternAtom(display, "WM_PROTOCOLS", c.False); - const wm_delete_window = libx11.XInternAtom(display, "WM_DELETE_WINDOW", c.False); - const net_wm_ping = libx11.XInternAtom(display, "NET_WM_PING", c.False); - const net_wm_state_fullscreen = libx11.XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", c.False); - const net_wm_state = libx11.XInternAtom(display, "_NET_WM_STATE", c.False); - const net_wm_state_above = libx11.XInternAtom(display, "_NET_WM_STATE_ABOVE", c.False); - const motif_wm_hints = libx11.XInternAtom(display, "_MOTIF_WM_HINTS", c.False); - const net_wm_window_type = libx11.XInternAtom(display, "_NET_WM_WINDOW_TYPE", c.False); - const net_wm_window_type_dock = libx11.XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", c.False); - const net_wm_bypass_compositor = libx11.XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", c.False); - - var protocols = [_]c.Atom{ wm_delete_window, net_wm_ping }; - _ = libx11.XSetWMProtocols(display, window, &protocols, protocols.len); - - _ = libx11.XStoreName(display, window, options.title.ptr); - _ = libx11.XSelectInput(display, window, set_window_attrs.event_mask); - _ = libx11.XMapWindow(display, window); - - var window_attrs: c.XWindowAttributes = undefined; - _ = libx11.XGetWindowAttributes(display, window, &window_attrs); - - const backend_type = try detectBackendType(options.allocator); - - const refresh_rate: u16 = blk: { - if (libxrr != null) { - const conf = libxrr.?.XRRGetScreenInfo(display, root_window); - break :blk @intCast(libxrr.?.XRRConfigCurrentRate(conf)); - } - break :blk 60; - }; - - var gl_ctx: ?*LibGL.Context = null; - switch (backend_type) { - .opengl, .opengles => { - if (libgl != null) { - // zig fmt: off - const attrs = &[_]c_int{ - LibGL.rgba, - LibGL.doublebuffer, - LibGL.depth_size, 24, - LibGL.stencil_size, 8, - LibGL.red_size, 8, - LibGL.green_size, 8, - LibGL.blue_size, 8, - LibGL.sample_buffers, 0, - LibGL.samples, 0, - c.None, - }; - // zig fmt: on - - const visual_info = libgl.?.glXChooseVisual(display, screen, attrs.ptr); - defer _ = libx11.XFree(visual_info); - gl_ctx = libgl.?.glXCreateContext(display, visual_info, null, true); - _ = libgl.?.glXMakeCurrent(display, window, gl_ctx); - } else { - return error.LibGLNotFound; - } - }, - else => {}, - } - - // The initial capacity we choose for the event queue is 2x our maximum expected event rate per - // frame. Specifically, 1000hz mouse updates are likely the maximum event rate we will encounter - // 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(options.allocator); - try events.ensureTotalCapacity(2048); - - const window_size = Size{ - .width = @intCast(window_attrs.width), - .height = @intCast(window_attrs.height), - }; - - // Create hidden cursor - const blank_pixmap = libx11.XCreatePixmap(display, window, 1, 1, 1); - const gc = libx11.XCreateGC(display, blank_pixmap, 0, null); - if (gc != null) { - _ = libx11.XDrawPoint(display, blank_pixmap, gc, 0, 0); - _ = libx11.XFreeGC(display, gc); - } - var color = c.XColor{}; - const hidden_cursor = libx11.XCreatePixmapCursor(display, blank_pixmap, blank_pixmap, &color, &color, 0, 0); - - // TODO: remove allocation - const surface_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromXlibWindow); - surface_descriptor.* = .{ - .display = display, - .window = @intCast(window), - }; - - x11.* = .{ - .core = @fieldParentPtr("platform", x11), - .allocator = options.allocator, - .display = display, - .libx11 = libx11, - .libgl = libgl, - .libxcursor = libxcursor, - .libxrr = libxrr, - .empty_event_pipe = empty_event_pipe, - .gl_ctx = gl_ctx, - .width = window_attrs.width, - .height = window_attrs.height, - .wm_protocols = wm_protocols, - .wm_delete_window = wm_delete_window, - .net_wm_ping = net_wm_ping, - .net_wm_state_fullscreen = net_wm_state_fullscreen, - .net_wm_state = net_wm_state, - .net_wm_state_above = net_wm_state_above, - .net_wm_window_type = net_wm_window_type, - .net_wm_window_type_dock = net_wm_window_type_dock, - .net_wm_bypass_compositor = net_wm_bypass_compositor, - .motif_wm_hints = motif_wm_hints, - .root_window = root_window, - .window = window, - .hidden_cursor = hidden_cursor, - .backend_type = backend_type, - .refresh_rate = refresh_rate, - .events = events, - .title = options.title, - .display_mode = .windowed, - .border = options.border, - .headless = options.headless, - .size = window_size, - .cursors = std.mem.zeroes([@typeInfo(CursorShape).Enum.fields.len]?c.Cursor), - .surface_descriptor = .{ .next_in_chain = .{ .from_xlib_window = surface_descriptor } }, - }; - x11.cursors[@intFromEnum(CursorShape.arrow)] = try x11.createStandardCursor(.arrow); -} - -fn pushEvent(x11: *X11, event: Event) !void { - x11.events_mu.lock(); - defer x11.events_mu.unlock(); - try x11.events.writeItem(event); -} - -// Called on the main thread -pub fn deinit(x11: *X11) void { - x11.allocator.destroy(x11.surface_descriptor.next_in_chain.from_xlib_window); - - for (x11.cursors) |cur| { - if (cur) |_| { - // _ = x11.libx11.XFreeCursor(x11.display, cur.?); - } - } - x11.events.deinit(); - - if (x11.libxcursor) |*libxcursor| { - libxcursor.handle.close(); - } - - if (x11.libxrr) |*libxrr| { - libxrr.handle.close(); - } - - if (x11.libgl) |*libgl| { - if (x11.gl_ctx) |gl_ctx| { - libgl.glXDestroyContext(x11.display, gl_ctx); - } - libgl.handle.close(); - } - - _ = x11.libx11.XUnmapWindow(x11.display, x11.window); - _ = x11.libx11.XDestroyWindow(x11.display, x11.window); - _ = x11.libx11.XCloseDisplay(x11.display); - x11.libx11.handle.close(); - - std.posix.close(x11.empty_event_pipe[0]); - std.posix.close(x11.empty_event_pipe[1]); -} - -// Called on the main thread -pub fn update(x11: *X11) !void { - while (c.QLength(x11.display) != 0) { - var event: c.XEvent = undefined; - _ = x11.libx11.XNextEvent(x11.display, &event); - try x11.processEvent(&event); - } - _ = x11.libx11.XFlush(x11.display); - - // const frequency_delay = @as(f32, @floatFromInt(x11.input.delay_ns)) / @as(f32, @floatFromInt(std.time.ns_per_s)); - // TODO: glfw.waitEventsTimeout(frequency_delay); - - x11.core.input.tick(); -} - -// May be called from any thread. -pub inline fn pollEvents(x11: *X11) EventIterator { - return EventIterator{ .queue = &x11.events }; -} - -// May be called from any thread. -pub fn setTitle(x11: *X11, title: [:0]const u8) void { - x11.title = title; - _ = x11.libx11.XStoreName(x11.display, x11.window, x11.title.ptr); -} - -// May be called from any thread. -pub fn setDisplayMode(x11: *X11, mode: DisplayMode) void { - switch (mode) { - .windowed => { - var atoms = std.BoundedArray(c.Atom, 5){}; - - if (x11.display_mode == .fullscreen) { - atoms.append(x11.net_wm_state_fullscreen) catch unreachable; - } - - atoms.append(x11.motif_wm_hints) catch unreachable; - - // TODO - // if (x11.floating) { - // atoms.append(x11.net_wm_state_above) catch unreachable; - // } - _ = x11.libx11.XChangeProperty( - x11.display, - x11.window, - x11.net_wm_state, - c.XA_ATOM, - 32, - c.PropModeReplace, - @ptrCast(atoms.slice()), - atoms.len, - ); - - x11.setFullscreen(false); - x11.setDecorated(x11.border); - x11.setFloating(false); - _ = x11.libx11.XMapWindow(x11.display, x11.window); - _ = x11.libx11.XFlush(x11.display); - }, - .fullscreen => { - x11.setFullscreen(true); - _ = x11.libx11.XFlush(x11.display); - }, - .borderless => { - x11.setDecorated(false); - x11.setFloating(true); - x11.setFullscreen(false); - - _ = x11.libx11.XResizeWindow( - x11.display, - x11.window, - @intCast(c.DisplayWidth(x11.display, c.DefaultScreen(x11.display))), - @intCast(c.DisplayHeight(x11.display, c.DefaultScreen(x11.display))), - ); - _ = x11.libx11.XFlush(x11.display); - }, - } -} - -// May be called from any thread. -pub fn setBorder(x11: *X11, value: bool) void { - _ = x11; - _ = value; - // TODO - // if (x11.display_mode != .borderless) x11.window.setAttrib(.decorated, x11.border); -} - -// May be called from any thread. -pub fn setHeadless(x11: *X11, value: bool) void { - _ = x11; - _ = value; - // TODO - // if (x11.headless) x11.window.hide() else x11.window.show(); -} - -// May be called from any thread. -pub fn setVSync(x11: *X11, mode: VSyncMode) void { - x11.swap_chain_desc.present_mode = switch (mode) { - .none => .immediate, - .double => .fifo, - .triple => .mailbox, - }; - x11.vsync_mode = mode; - x11.swap_chain_update.set(); -} - -// May be called from any thread. -pub fn setSize(x11: *X11, value: Size) void { - _ = x11.libx11.XResizeWindow(x11.display, x11.window, value.width, value.height); - _ = x11.libx11.XFlush(x11.display); - x11.size = value; -} - -// May be called from any thread. -pub fn setCursorMode(x11: *X11, mode: CursorMode) void { - x11.updateCursor(mode, x11.cursor_shape); -} - -// May be called from any thread. -pub fn setCursorShape(x11: *X11, shape: CursorShape) void { - const cursor = x11.createStandardCursor(shape) catch |err| blk: { - log.warn( - "mach: setCursorShape: {}: {s} not yet supported\n", - .{ err, @tagName(shape) }, - ); - break :blk null; - }; - x11.cursors[@intFromEnum(shape)] = cursor; - x11.updateCursor(x11.cursor_mode, shape); -} - -// May be called from any thread. -pub fn joystickPresent(_: *X11, _: Joystick) bool { - @panic("TODO: implement joystickPresent for X11"); -} - -// May be called from any thread. -pub fn joystickName(_: *X11, _: Joystick) ?[:0]const u8 { - @panic("TODO: implement joystickName for X11"); -} - -// May be called from any thread. -pub fn joystickButtons(_: *X11, _: Joystick) ?[]const bool { - @panic("TODO: implement joystickButtons for X11"); -} - -// May be called from any thread. -pub fn joystickAxes(_: *X11, _: Joystick) ?[]const f32 { - @panic("TODO: implement joystickAxes for X11"); -} - -// May be called from any thread. -pub fn keyPressed(x11: *X11, key: Key) bool { - x11.input_mu.lockShared(); - defer x11.input_mu.unlockShared(); - return x11.input_state.isKeyPressed(key); -} - -// May be called from any thread. -pub fn keyReleased(x11: *X11, key: Key) bool { - x11.input_mu.lockShared(); - defer x11.input_mu.unlockShared(); - return x11.input_state.isKeyReleased(key); -} - -// May be called from any thread. -pub fn mousePressed(x11: *X11, button: MouseButton) bool { - x11.input_mu.lockShared(); - defer x11.input_mu.unlockShared(); - return x11.input_state.isMouseButtonPressed(button); -} - -// May be called from any thread. -pub fn mouseReleased(x11: *X11, button: MouseButton) bool { - x11.input_mu.lockShared(); - defer x11.input_mu.unlockShared(); - return x11.input_state.isMouseButtonReleased(button); -} - -// May be called from any thread. -pub fn mousePosition(x11: *X11) Position { - x11.input_mu.lockShared(); - defer x11.input_mu.unlockShared(); - return x11.input_state.mouse_position; -} - -fn processEvent(x11: *X11, event: *c.XEvent) !void { - switch (event.type) { - c.KeyPress, c.KeyRelease => { - // TODO: key repeat event - - var keysym: c.KeySym = undefined; - _ = x11.libx11.XLookupString(&event.xkey, null, 0, &keysym, null); - - const key_event = KeyEvent{ .key = toMachKey(keysym), .mods = toMachMods(event.xkey.state) }; - - switch (event.type) { - c.KeyPress => { - x11.input_mu.lock(); - x11.input_state.keys.set(@intFromEnum(key_event.key)); - x11.input_mu.unlock(); - try x11.pushEvent(.{ .key_press = key_event }); - - if (unicode.unicodeFromKeySym(keysym)) |codepoint| { - try x11.pushEvent(.{ .char_input = .{ .codepoint = codepoint } }); - } - }, - c.KeyRelease => { - x11.input_mu.lock(); - x11.input_state.keys.unset(@intFromEnum(key_event.key)); - x11.input_mu.unlock(); - try x11.pushEvent(.{ .key_release = key_event }); - }, - else => unreachable, - } - }, - c.ButtonPress => { - const button = toMachButton(event.xbutton.button) orelse { - // Modern X provides scroll events as mouse button presses - const scroll: struct { f32, f32 } = switch (event.xbutton.button) { - c.Button4 => .{ 0.0, 1.0 }, - c.Button5 => .{ 0.0, -1.0 }, - 6 => .{ 1.0, 0.0 }, - 7 => .{ -1.0, 0.0 }, - else => unreachable, - }; - try x11.pushEvent(.{ .mouse_scroll = .{ .xoffset = scroll[0], .yoffset = scroll[1] } }); - return; - }; - const cursor_pos = x11.getCursorPos(); - const mouse_button = MouseButtonEvent{ - .button = button, - .pos = cursor_pos, - .mods = toMachMods(event.xbutton.state), - }; - - x11.input_mu.lock(); - x11.input_state.mouse_buttons.set(@intFromEnum(mouse_button.button)); - x11.input_mu.unlock(); - try x11.pushEvent(.{ .mouse_press = mouse_button }); - }, - c.ButtonRelease => { - const button = toMachButton(event.xbutton.button) orelse return; - const cursor_pos = x11.getCursorPos(); - const mouse_button = MouseButtonEvent{ - .button = button, - .pos = cursor_pos, - .mods = toMachMods(event.xbutton.state), - }; - - x11.input_mu.lock(); - x11.input_state.mouse_buttons.unset(@intFromEnum(mouse_button.button)); - x11.input_mu.unlock(); - try x11.pushEvent(.{ .mouse_release = mouse_button }); - }, - c.ClientMessage => { - if (event.xclient.message_type == c.None) return; - - if (event.xclient.message_type == x11.wm_protocols) { - const protocol = event.xclient.data.l[0]; - if (protocol == c.None) return; - - if (protocol == x11.wm_delete_window) { - try x11.pushEvent(.close); - } else if (protocol == x11.net_wm_ping) { - // The window manager is pinging the application to ensure - // it's still responding to events - var reply = event.*; - reply.xclient.window = x11.root_window; - _ = x11.libx11.XSendEvent( - x11.display, - x11.root_window, - c.False, - c.SubstructureNotifyMask | c.SubstructureRedirectMask, - &reply, - ); - } - } - }, - c.EnterNotify => { - const x: f32 = @floatFromInt(event.xcrossing.x); - const y: f32 = @floatFromInt(event.xcrossing.y); - x11.input_mu.lock(); - x11.input_state.mouse_position = .{ .x = x, .y = y }; - x11.input_mu.unlock(); - try x11.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y } } }); - }, - c.MotionNotify => { - const x: f32 = @floatFromInt(event.xmotion.x); - const y: f32 = @floatFromInt(event.xmotion.y); - x11.input_mu.lock(); - x11.input_state.mouse_position = .{ .x = x, .y = y }; - x11.input_mu.unlock(); - try x11.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y } } }); - }, - c.ConfigureNotify => { - if (event.xconfigure.width != x11.size.width or - event.xconfigure.height != x11.size.height) - { - x11.size.width = @intCast(event.xconfigure.width); - x11.size.height = @intCast(event.xconfigure.height); - x11.core.swap_chain_update.set(); - try x11.pushEvent(.{ - .framebuffer_resize = .{ - .width = x11.size.width, - .height = x11.size.height, - }, - }); - } - }, - c.FocusIn => { - if (event.xfocus.mode == c.NotifyGrab or - event.xfocus.mode == c.NotifyUngrab) - { - // Ignore focus events from popup indicator windows, window menu - // key chords and window dragging - return; - } - - try x11.pushEvent(.focus_gained); - }, - c.FocusOut => { - if (event.xfocus.mode == c.NotifyGrab or - event.xfocus.mode == c.NotifyUngrab) - { - // Ignore focus events from popup indicator windows, window menu - // key chords and window dragging - return; - } - - try x11.pushEvent(.focus_lost); - }, - else => {}, - } -} - -fn setDecorated(x11: *X11, enabled: bool) void { - const MWMHints = struct { - flags: u32, - functions: u32, - decorations: u32, - input_mode: i32, - status: u32, - }; - - const hints = MWMHints{ - .functions = 0, - .flags = 2, - .decorations = if (enabled) 1 else 0, - .input_mode = 0, - .status = 0, - }; - - _ = x11.libx11.XChangeProperty( - x11.display, - x11.window, - x11.motif_wm_hints, - x11.motif_wm_hints, - 32, - c.PropModeReplace, - @ptrCast(&hints), - 5, - ); -} - -fn setFullscreen(x11: *X11, enabled: bool) void { - x11.sendEventToWM(x11.net_wm_state, &.{ @intFromBool(enabled), @intCast(x11.net_wm_state_fullscreen), 0, 1 }); - - // Force composition OFF to reduce overhead - const compositing_disable_on: c_long = @intFromBool(enabled); - const bypass_compositor = x11.libx11.XInternAtom(x11.display, "_NET_WM_BYPASS_COMPOSITOR", c.False); - - if (bypass_compositor != c.None) { - _ = x11.libx11.XChangeProperty( - x11.display, - x11.window, - bypass_compositor, - c.XA_CARDINAL, - 32, - c.PropModeReplace, - @ptrCast(&compositing_disable_on), - 1, - ); - } -} - -fn setFloating(x11: *X11, enabled: bool) void { - const net_wm_state_remove = 0; - const net_wm_state_add = 1; - const action: c_long = if (enabled) net_wm_state_add else net_wm_state_remove; - x11.sendEventToWM(x11.net_wm_state, &.{ action, @intCast(x11.net_wm_state_above), 0, 1 }); -} - -fn sendEventToWM(x11: *X11, message_type: c.Atom, data: []const c_long) void { - var ev = std.mem.zeroes(c.XEvent); - ev.type = c.ClientMessage; - ev.xclient.window = x11.window; - ev.xclient.message_type = message_type; - ev.xclient.format = 32; - @memcpy(ev.xclient.data.l[0..data.len], data); - _ = x11.libx11.XSendEvent( - x11.display, - x11.root_window, - c.False, - c.SubstructureNotifyMask | c.SubstructureRedirectMask, - &ev, - ); - _ = x11.libx11.XFlush(x11.display); -} - -fn getCursorPos(x11: *X11) Position { - var root_window: c.Window = undefined; - var child_window: c.Window = undefined; - var root_cursor_x: c_int = 0; - var root_cursor_y: c_int = 0; - var cursor_x: c_int = 0; - var cursor_y: c_int = 0; - var mask: c_uint = 0; - _ = x11.libx11.XQueryPointer( - x11.display, - x11.window, - &root_window, - &child_window, - &root_cursor_x, - &root_cursor_y, - &cursor_x, - &cursor_y, - &mask, - ); - - return .{ .x = @floatFromInt(cursor_x), .y = @floatFromInt(cursor_y) }; -} - -// fn createImageCursor(display: *c.Display, pixels: []const u8, width: u32, height: u32) c.Cursor { -// const image = libxcursor.XcursorImageCreate(@intCast(width), @intCast(height)) orelse return c.None; -// defer libxcursor.XcursorImageDestroy(image); - -// for (image.*.pixels[0 .. width * height], 0..) |*target, i| { -// const r = pixels[i * 4 + 0]; -// const g = pixels[i * 4 + 1]; -// const b = pixels[i * 4 + 2]; -// const a: u32 = pixels[i * 4 + 3]; -// target.* = (a << 24) | -// ((r * a / 255) << 16) | -// ((g * a / 255) << 8) | -// ((b * a / 255) << 0); -// } - -// return libxcursor.XcursorImageLoadCursor(display, image); -// } - -fn updateCursor(x11: *X11, mode: CursorMode, shape: CursorShape) void { - switch (mode) { - .normal => { - if (x11.cursors[@intFromEnum(shape)]) |current_cursor| { - _ = x11.libx11.XDefineCursor(x11.display, x11.window, current_cursor); - } else { - // TODO: what's the correct behavior here? reset to parent cursor? - _ = x11.libx11.XUndefineCursor(x11.display, x11.window); - } - - if (x11.last_cursor_mode == .disabled) { - _ = x11.libx11.XUngrabPointer(x11.display, c.CurrentTime); - } - }, - .hidden => { - _ = x11.libx11.XDefineCursor(x11.display, x11.window, x11.hidden_cursor); - if (x11.last_cursor_mode == .disabled) { - _ = x11.libx11.XUngrabPointer(x11.display, c.CurrentTime); - } - }, - .disabled => { - _ = x11.libx11.XDefineCursor(x11.display, x11.window, x11.hidden_cursor); - _ = x11.libx11.XGrabPointer( - x11.display, - x11.window, - c.True, - c.ButtonPressMask | c.ButtonReleaseMask | c.PointerMotionMask, - c.GrabModeAsync, - c.GrabModeAsync, - x11.window, - c.None, - c.CurrentTime, - ); - }, - } -} - -fn createStandardCursor(x11: *X11, shape: CursorShape) !c.Cursor { - if (x11.libxcursor) |libxcursor| { - const theme = libxcursor.XcursorGetTheme(x11.display); - if (theme != null) { - const name = switch (shape) { - .arrow => "default", - .ibeam => "text", - .crosshair => "crosshair", - .pointing_hand => "pointer", - .resize_ew => "ew-resize", - .resize_ns => "ns-resize", - .resize_nwse => "nwse-resize", - .resize_nesw => "nesw-resize", - .resize_all => "all-scroll", - .not_allowed => "not-allowed", - }; - - const cursor_size = libxcursor.XcursorGetDefaultSize(x11.display); - const image = libxcursor.XcursorLibraryLoadImage(name, theme, cursor_size); - defer libxcursor.XcursorImageDestroy(image); - - if (image != null) { - return libxcursor.XcursorImageLoadCursor(x11.display, image); - } - } - } - - const xc: c_uint = switch (shape) { - .arrow => c.XC_left_ptr, - .ibeam => c.XC_xterm, - .crosshair => c.XC_crosshair, - .pointing_hand => c.XC_hand2, - .resize_ew => c.XC_sb_h_double_arrow, - .resize_ns => c.XC_sb_v_double_arrow, - .resize_nwse => c.XC_sb_h_double_arrow, - .resize_nesw => c.XC_sb_h_double_arrow, - .resize_all => c.XC_fleur, - .not_allowed => c.XC_X_cursor, - }; - - const cursor = x11.libx11.XCreateFontCursor(x11.display, xc); - if (cursor == 0) return error.FailedToCreateCursor; - - return cursor; -} - -fn toMachButton(button: c_uint) ?MouseButton { - return switch (button) { - c.Button1 => .left, - c.Button2 => .middle, - c.Button3 => .right, - // Scroll events are handled by caller - c.Button4, c.Button5, 6, 7 => null, - // Additional buttons after 7 are treated as regular buttons - 8 => .four, - 9 => .five, - 10 => .six, - 11 => .seven, - 12 => .eight, - // Unknown button - else => null, - }; -} - -fn toMachKey(key: c.KeySym) Key { - return switch (key) { - c.XK_a, c.XK_A => .a, - c.XK_b, c.XK_B => .b, - c.XK_c, c.XK_C => .c, - c.XK_d, c.XK_D => .d, - c.XK_e, c.XK_E => .e, - c.XK_f, c.XK_F => .f, - c.XK_g, c.XK_G => .g, - c.XK_h, c.XK_H => .h, - c.XK_i, c.XK_I => .i, - c.XK_j, c.XK_J => .j, - c.XK_k, c.XK_K => .k, - c.XK_l, c.XK_L => .l, - c.XK_m, c.XK_M => .m, - c.XK_n, c.XK_N => .n, - c.XK_o, c.XK_O => .o, - c.XK_p, c.XK_P => .p, - c.XK_q, c.XK_Q => .q, - c.XK_r, c.XK_R => .r, - c.XK_s, c.XK_S => .s, - c.XK_t, c.XK_T => .t, - c.XK_u, c.XK_U => .u, - c.XK_v, c.XK_V => .v, - c.XK_w, c.XK_W => .w, - c.XK_x, c.XK_X => .x, - c.XK_y, c.XK_Y => .y, - c.XK_z, c.XK_Z => .z, - - c.XK_0 => .zero, - c.XK_1 => .one, - c.XK_2 => .two, - c.XK_3 => .three, - c.XK_4 => .four, - c.XK_5 => .five, - c.XK_6 => .six, - c.XK_7 => .seven, - c.XK_8 => .eight, - c.XK_9 => .nine, - - c.XK_F1 => .f1, - c.XK_F2 => .f2, - c.XK_F3 => .f3, - c.XK_F4 => .f4, - c.XK_F5 => .f5, - c.XK_F6 => .f6, - c.XK_F7 => .f7, - c.XK_F8 => .f8, - c.XK_F9 => .f9, - c.XK_F10 => .f10, - c.XK_F11 => .f11, - c.XK_F12 => .f12, - c.XK_F13 => .f13, - c.XK_F14 => .f14, - c.XK_F15 => .f15, - c.XK_F16 => .f16, - c.XK_F17 => .f17, - c.XK_F18 => .f18, - c.XK_F19 => .f19, - c.XK_F20 => .f20, - c.XK_F21 => .f21, - c.XK_F22 => .f22, - c.XK_F23 => .f23, - c.XK_F24 => .f24, - c.XK_F25 => .f25, - - c.XK_KP_Divide => .kp_divide, - c.XK_KP_Multiply => .kp_multiply, - c.XK_KP_Subtract => .kp_subtract, - c.XK_KP_Add => .kp_add, - c.XK_KP_0 => .kp_0, - c.XK_KP_1 => .kp_1, - c.XK_KP_2 => .kp_2, - c.XK_KP_3 => .kp_3, - c.XK_KP_4 => .kp_4, - c.XK_KP_5 => .kp_5, - c.XK_KP_6 => .kp_6, - c.XK_KP_7 => .kp_7, - c.XK_KP_8 => .kp_8, - c.XK_KP_9 => .kp_9, - c.XK_KP_Decimal => .kp_decimal, - c.XK_KP_Equal => .kp_equal, - c.XK_KP_Enter => .kp_enter, - - c.XK_Return => .enter, - c.XK_Escape => .escape, - c.XK_Tab => .tab, - c.XK_Shift_L => .left_shift, - c.XK_Shift_R => .right_shift, - c.XK_Control_L => .left_control, - c.XK_Control_R => .right_control, - c.XK_Alt_L => .left_alt, - c.XK_Alt_R => .right_alt, - c.XK_Super_L => .left_super, - c.XK_Super_R => .right_super, - c.XK_Menu => .menu, - c.XK_Num_Lock => .num_lock, - c.XK_Caps_Lock => .caps_lock, - c.XK_Print => .print, - c.XK_Scroll_Lock => .scroll_lock, - c.XK_Pause => .pause, - c.XK_Delete => .delete, - c.XK_Home => .home, - c.XK_End => .end, - c.XK_Page_Up => .page_up, - c.XK_Page_Down => .page_down, - c.XK_Insert => .insert, - c.XK_Left => .left, - c.XK_Right => .right, - c.XK_Up => .up, - c.XK_Down => .down, - c.XK_BackSpace => .backspace, - c.XK_space => .space, - c.XK_minus => .minus, - c.XK_equal => .equal, - c.XK_braceleft => .left_bracket, - c.XK_braceright => .right_bracket, - c.XK_backslash => .backslash, - c.XK_semicolon => .semicolon, - c.XK_apostrophe => .apostrophe, - c.XK_comma => .comma, - c.XK_period => .period, - c.XK_slash => .slash, - c.XK_grave => .grave, - - else => .unknown, - }; -} - -fn toMachMods(mods: c_uint) KeyMods { - return .{ - .shift = mods & c.ShiftMask != 0, - .control = mods & c.ControlMask != 0, - .alt = mods & c.Mod1Mask != 0, - .super = mods & c.Mod4Mask != 0, - .caps_lock = mods & c.LockMask != 0, - .num_lock = mods & c.Mod2Mask != 0, - }; -} - -fn errorHandler(display: ?*c.Display, event: [*c]c.XErrorEvent) callconv(.C) c_int { - _ = display; - log.err("X11: error code {d}\n", .{event.*.error_code}); - return 0; -} diff --git a/src/core/wayland/wayland.c b/src/core/wayland/wayland.c deleted file mode 100644 index 9d76e0059e..0000000000 --- a/src/core/wayland/wayland.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "wayland-client-protocol-code.h" -#include "wayland-xdg-shell-client-protocol-code.h" -#include "wayland-xdg-decoration-client-protocol-code.h" -#include "wayland-viewporter-client-protocol-code.h" -#include "wayland-relative-pointer-unstable-v1-client-protocol-code.h" -#include "wayland-pointer-constraints-unstable-v1-client-protocol-code.h" -#include "wayland-idle-inhibit-unstable-v1-client-protocol-code.h" \ No newline at end of file