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