From ab143504abf17c6eb3fcba3cdbf2615d29cf16c1 Mon Sep 17 00:00:00 2001 From: Joshua Holmes Date: Tue, 7 Jan 2025 03:47:44 +0000 Subject: [PATCH] linux: improve logging when both backends fail --- src/core/Linux.zig | 31 +++++- src/core/linux/Wayland.zig | 96 ++++++++--------- src/core/linux/X11.zig | 204 +++++++++++++++++++------------------ 3 files changed, 186 insertions(+), 145 deletions(-) diff --git a/src/core/Linux.zig b/src/core/Linux.zig index cf78908e..74895ae7 100644 --- a/src/core/Linux.zig +++ b/src/core/Linux.zig @@ -115,7 +115,22 @@ pub fn initWindow( else => "An unknown error occured while trying to connect to X11", }; log.err("{s}\n\nFalling back to Wayland\n", .{err_msg}); - try Wayland.initWindow(core, window_id); + Wayland.initWindow(core, window_id) catch |e| { + log.err("Failed to connect to fallback display server, Wayland.\n", .{}); + var libs = std.ArrayList(u8).init(core.allocator); + defer libs.deinit(); + if (X11.libx11 == null) { + try libs.appendSlice("\t* " ++ X11.LibX11.lib_name ++ "\n"); + } + if (X11.libxkbcommon == null) { + try libs.appendSlice("\t* " ++ X11.LibXkbCommon.lib_name ++ "\n"); + } + if (X11.libgl == null) { + try libs.appendSlice("\t* " ++ X11.LibGL.lib_name ++ "\n"); + } + log.err("The following X11 libraries were not available:\n{s}", .{libs.items}); + return e; + }; }; }, .wayland => { @@ -127,7 +142,19 @@ pub fn initWindow( else => "An unknown error occured while trying to connect to Wayland", }; log.err("{s}\n\nFalling back to X11\n", .{err_msg}); - try X11.initWindow(core, window_id); + X11.initWindow(core, window_id) catch |e| { + log.err("Failed to connect to fallback display server, X11.\n", .{}); + var libs = std.ArrayList(u8).init(core.allocator); + defer libs.deinit(); + if (Wayland.libwaylandclient == null) { + try libs.appendSlice("\t* " ++ Wayland.LibWaylandClient.lib_name ++ "\n"); + } + if (Wayland.libxkbcommon == null) { + try libs.appendSlice("\t* " ++ Wayland.LibXkbCommon.lib_name ++ "\n"); + } + log.err("The following Wayland libraries were not available:\n{s}", .{libs.items}); + return e; + }; }; }, } diff --git a/src/core/linux/Wayland.zig b/src/core/linux/Wayland.zig index 9844c1b9..acca423f 100644 --- a/src/core/linux/Wayland.zig +++ b/src/core/linux/Wayland.zig @@ -25,11 +25,11 @@ pub const c = @cImport({ // This needs to be declared here so it can be used in the exported functions below, // but doesn't need to be defined until run time (and can't be defined until run time). -var libwaylandclient: LibWaylandClient = undefined; +pub var libwaylandclient: ?LibWaylandClient = null; // This does not need to be declared here, but we are declaring it here to be consistent // with `libwaylandclient`. -var libxkbcommon: LibXkbCommon = undefined; +pub var libxkbcommon: ?LibXkbCommon = null; var core_ptr: *Core = undefined; @@ -38,19 +38,19 @@ var core_ptr: *Core = undefined; // compile time, but since they are not run until run time, after `libwaylandclient` is // defined, an error never occurs. 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 }); + 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}); + 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 }); + 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}); + return @call(.always_tail, libwaylandclient.?.wl_proxy_destroy, .{proxy}); } pub const Native = struct { @@ -87,7 +87,7 @@ pub fn initWindow( core_window.native = .{ .wayland = .{ .interfaces = Interfaces{}, - .display = libwaylandclient.wl_display_connect(null) orelse return error.FailedToConnectToDisplay, + .display = libwaylandclient.?.wl_display_connect(null) orelse return error.FailedToConnectToDisplay, .modifiers = .{ .alt = false, .caps_lock = false, @@ -108,7 +108,7 @@ pub fn initWindow( .surface_descriptor = undefined, .surface = undefined, .toplevel = undefined, - .xkb_context = libxkbcommon.xkb_context_new(0) orelse return error.FailedToGetXkbContext, + .xkb_context = libxkbcommon.?.xkb_context_new(0) orelse return error.FailedToGetXkbContext, }, }; @@ -124,10 +124,10 @@ pub fn initWindow( // TODO: Look at replacing these 2 calls to wl_display_roundtrip with wl_display::sync // Round trip to get all the registry objects - _ = libwaylandclient.wl_display_roundtrip(wl.display); + _ = libwaylandclient.?.wl_display_roundtrip(wl.display); // Round trip to get all initial output events - _ = libwaylandclient.wl_display_roundtrip(wl.display); + _ = libwaylandclient.?.wl_display_roundtrip(wl.display); // Update `core_window` since registry listener and seat listener changed values in it core_window = core.windows.getValue(window_id); @@ -174,7 +174,7 @@ pub fn initWindow( } // Wait for events to get pushed - _ = libwaylandclient.wl_display_roundtrip(wl.display); + _ = libwaylandclient.?.wl_display_roundtrip(wl.display); core_window = core.windows.getValue(window_id); wl = &core_window.native.?.wayland; @@ -183,7 +183,7 @@ pub fn initWindow( c.wl_surface_commit(wl.surface); while (true) { - const result = libwaylandclient.wl_display_dispatch(wl.display); + const result = libwaylandclient.?.wl_display_dispatch(wl.display); core_window = core.windows.getValue(window_id); wl = &core_window.native.?.wayland; @@ -203,7 +203,7 @@ pub fn initWindow( // Commit changes to surface c.wl_surface_commit(wl.surface); - _ = libwaylandclient.wl_display_roundtrip(wl.display); + _ = libwaylandclient.?.wl_display_roundtrip(wl.display); core.windows.setValue(window_id, core_window); try core.initWindow(window_id); @@ -212,14 +212,14 @@ pub fn initWindow( pub fn tick(window_id: mach.ObjectID) !void { const wl = &core_ptr.windows.getValue(window_id).native.?.wayland; - while (libwaylandclient.wl_display_flush(wl.display) == -1) { + while (libwaylandclient.?.wl_display_flush(wl.display) == -1) { if (std.posix.errno(-1) == std.posix.E.AGAIN) { log.err("flush error", .{}); return error.FlushError; } var pollfd = [_]std.posix.pollfd{ std.posix.pollfd{ - .fd = libwaylandclient.wl_display_get_fd(wl.display), + .fd = libwaylandclient.?.wl_display_get_fd(wl.display), .events = std.posix.POLL.OUT, .revents = 0, }, @@ -233,7 +233,7 @@ pub fn tick(window_id: mach.ObjectID) !void { } } - _ = libwaylandclient.wl_display_roundtrip(wl.display); + _ = libwaylandclient.?.wl_display_roundtrip(wl.display); } pub fn setTitle(wl: *const Native, title: [:0]const u8) void { @@ -245,7 +245,7 @@ pub fn setDisplayMode(wl: *Wayland, display_mode: DisplayMode) void { _ = display_mode; } -const LibXkbCommon = struct { +pub const LibXkbCommon = struct { handle: std.DynLib, xkb_context_new: *const @TypeOf(c.xkb_context_new), @@ -265,9 +265,11 @@ const LibXkbCommon = struct { xkb_compose_state_get_one_sym: *const @TypeOf(c.xkb_compose_state_get_one_sym), xkb_keysym_to_utf32: *const @TypeOf(c.xkb_keysym_to_utf32), + pub const lib_name = "libxkbcommon.so.0"; + pub fn load() !LibXkbCommon { var lib: LibXkbCommon = undefined; - lib.handle = try mach.dynLibOpen("libxkbcommon.so.0"); + lib.handle = std.DynLib.open(lib_name) 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]); @@ -280,7 +282,7 @@ const LibXkbCommon = struct { } }; -const LibWaylandClient = struct { +pub const LibWaylandClient = struct { handle: std.DynLib, wl_display_connect: *const @TypeOf(c.wl_display_connect), @@ -318,9 +320,11 @@ const LibWaylandClient = struct { wl_surface_interface: *@TypeOf(c.wl_surface_interface), wl_touch_interface: *@TypeOf(c.wl_touch_interface), + pub const lib_name = "libwayland-client.so.0"; + pub fn load() !LibWaylandClient { var lib: LibWaylandClient = undefined; - lib.handle = try mach.dynLibOpen("libwayland-client.so.0"); + lib.handle = std.DynLib.open(lib_name) 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]); @@ -363,7 +367,7 @@ const registry_listener = struct { wl.interfaces.wl_compositor = @ptrCast(c.wl_registry_bind( registry, name, - libwaylandclient.wl_compositor_interface, + libwaylandclient.?.wl_compositor_interface, @min(3, version), ) orelse @panic("uh idk how to proceed")); } else if (std.mem.eql(u8, "wl_subcompositor", interface)) { @@ -371,7 +375,7 @@ const registry_listener = struct { wl.interfaces.wl_subcompositor = @ptrCast(c.wl_registry_bind( registry, name, - libwaylandclient.wl_subcompositor_interface, + libwaylandclient.?.wl_subcompositor_interface, @min(3, version), ) orelse @panic("uh idk how to proceed")); } else if (std.mem.eql(u8, "wl_shm", interface)) { @@ -379,7 +383,7 @@ const registry_listener = struct { wl.interfaces.wl_shm = @ptrCast(c.wl_registry_bind( registry, name, - libwaylandclient.wl_shm_interface, + libwaylandclient.?.wl_shm_interface, @min(3, version), ) orelse @panic("uh idk how to proceed")); } else if (std.mem.eql(u8, "wl_output", interface)) { @@ -387,14 +391,14 @@ const registry_listener = struct { wl.interfaces.wl_output = @ptrCast(c.wl_registry_bind( registry, name, - libwaylandclient.wl_output_interface, + 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(c.wl_registry_bind( // registry, // name, - // libwaylandclient.wl_data_device_manager_interface, + // 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)) { @@ -418,7 +422,7 @@ const registry_listener = struct { wl.interfaces.wl_seat = @ptrCast(c.wl_registry_bind( registry, name, - libwaylandclient.wl_seat_interface, + libwaylandclient.?.wl_seat_interface, @min(3, version), ) orelse @panic("uh idk how to proceed")); @@ -457,7 +461,7 @@ const keyboard_listener = struct { const map_str = std.posix.mmap(null, keymap_size, std.posix.PROT.READ, .{ .TYPE = .SHARED }, fd, 0) catch unreachable; - const keymap = libxkbcommon.xkb_keymap_new_from_string( + const keymap = 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, @@ -470,13 +474,13 @@ const keyboard_listener = struct { std.posix.close(fd); //Release reference to old state and create new state - libxkbcommon.xkb_state_unref(wl.xkb_state); - const state = libxkbcommon.xkb_state_new(keymap).?; + libxkbcommon.?.xkb_state_unref(wl.xkb_state); + const state = libxkbcommon.?.xkb_state_new(keymap).?; //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 = libxkbcommon.xkb_compose_table_new_from_locale( + var compose_table = libxkbcommon.?.xkb_compose_table_new_from_locale( wl.xkb_context, locale, c.XKB_COMPOSE_COMPILE_NO_FLAGS, @@ -484,24 +488,24 @@ const keyboard_listener = struct { //If creation failed, lets try the C locale if (compose_table == null) - compose_table = libxkbcommon.xkb_compose_table_new_from_locale( + compose_table = libxkbcommon.?.xkb_compose_table_new_from_locale( wl.xkb_context, "C", c.XKB_COMPOSE_COMPILE_NO_FLAGS, ).?; - defer libxkbcommon.xkb_compose_table_unref(compose_table); + defer libxkbcommon.?.xkb_compose_table_unref(compose_table); wl.keymap = keymap; wl.xkb_state = state; - wl.compose_state = libxkbcommon.xkb_compose_state_new(compose_table, c.XKB_COMPOSE_STATE_NO_FLAGS).?; + wl.compose_state = libxkbcommon.?.xkb_compose_state_new(compose_table, c.XKB_COMPOSE_STATE_NO_FLAGS).?; - wl.modifier_indices.control_index = libxkbcommon.xkb_keymap_mod_get_index(keymap, "Control"); - wl.modifier_indices.alt_index = libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod1"); - wl.modifier_indices.shift_index = libxkbcommon.xkb_keymap_mod_get_index(keymap, "Shift"); - wl.modifier_indices.super_index = libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod4"); - wl.modifier_indices.caps_lock_index = libxkbcommon.xkb_keymap_mod_get_index(keymap, "Lock"); - wl.modifier_indices.num_lock_index = libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod2"); + wl.modifier_indices.control_index = libxkbcommon.?.xkb_keymap_mod_get_index(keymap, "Control"); + wl.modifier_indices.alt_index = libxkbcommon.?.xkb_keymap_mod_get_index(keymap, "Mod1"); + wl.modifier_indices.shift_index = libxkbcommon.?.xkb_keymap_mod_get_index(keymap, "Shift"); + wl.modifier_indices.super_index = libxkbcommon.?.xkb_keymap_mod_get_index(keymap, "Mod4"); + wl.modifier_indices.caps_lock_index = libxkbcommon.?.xkb_keymap_mod_get_index(keymap, "Lock"); + wl.modifier_indices.num_lock_index = libxkbcommon.?.xkb_keymap_mod_get_index(keymap, "Mod2"); core_ptr.windows.setValue(window_id, core_window); } @@ -538,12 +542,12 @@ const keyboard_listener = struct { var keysyms: ?[*]const c.xkb_keysym_t = undefined; //Get the keysym from the keycode (scancode + 8) - if (libxkbcommon.xkb_state_key_get_syms(wl.xkb_state, scancode + 8, &keysyms) == 1) { + if (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 - const codepoint = libxkbcommon.xkb_keysym_to_utf32(keysym); + const codepoint = libxkbcommon.?.xkb_keysym_to_utf32(keysym); if (codepoint != 0) { core_ptr.pushEvent(.{ .char_input = .{ .codepoint = @truncate(codepoint), .window_id = window_id } }); } @@ -563,7 +567,7 @@ const keyboard_listener = struct { return; // TODO: handle this return value - _ = libxkbcommon.xkb_state_update_mask( + _ = libxkbcommon.?.xkb_state_update_mask( wl.xkb_state.?, mods_depressed, mods_latched, @@ -582,7 +586,7 @@ const keyboard_listener = struct { .{ wl.modifier_indices.num_lock_index, "num_lock" }, .{ wl.modifier_indices.caps_lock_index, "caps_lock" }, }) |key| { - @field(wl.modifiers, key[1]) = libxkbcommon.xkb_state_mod_index_is_active( + @field(wl.modifiers, key[1]) = libxkbcommon.?.xkb_state_mod_index_is_active( wl.xkb_state, key[0], c.XKB_STATE_MODS_EFFECTIVE, @@ -844,11 +848,11 @@ fn composeSymbol(wl: *const Native, sym: c.xkb_keysym_t) c.xkb_keysym_t { if (sym == c.XKB_KEY_NoSymbol or wl.compose_state == null) return sym; - if (libxkbcommon.xkb_compose_state_feed(wl.compose_state, sym) != c.XKB_COMPOSE_FEED_ACCEPTED) + if (libxkbcommon.?.xkb_compose_state_feed(wl.compose_state, sym) != c.XKB_COMPOSE_FEED_ACCEPTED) return sym; - return switch (libxkbcommon.xkb_compose_state_get_status(wl.compose_state)) { - c.XKB_COMPOSE_COMPOSED => libxkbcommon.xkb_compose_state_get_one_sym(wl.compose_state), + return switch (libxkbcommon.?.xkb_compose_state_get_status(wl.compose_state)) { + c.XKB_COMPOSE_COMPOSED => libxkbcommon.?.xkb_compose_state_get_one_sym(wl.compose_state), c.XKB_COMPOSE_COMPOSING, c.XKB_COMPOSE_CANCELLED => c.XKB_KEY_NoSymbol, else => sym, }; diff --git a/src/core/linux/X11.zig b/src/core/linux/X11.zig index 45264768..a0d72992 100644 --- a/src/core/linux/X11.zig +++ b/src/core/linux/X11.zig @@ -33,6 +33,10 @@ pub const defaultPanic = std.debug.panicImpl; // TODO: determine if it's really needed to store global pointer var core_ptr: *Core = undefined; +pub var libx11: ?LibX11 = null; +pub var libxkbcommon: ?LibXkbCommon = null; +pub var libgl: ?LibGL = null; + pub const Native = struct { backend_type: gpu.BackendType, cursors: [@typeInfo(CursorShape).@"enum".fields.len]?c.Cursor, @@ -40,11 +44,7 @@ pub const Native = struct { empty_event_pipe: [2]std.c.fd_t, gl_ctx: ?*LibGL.Context, hidden_cursor: c.Cursor, - libgl: ?LibGL, - libx11: LibX11, libxcursor: ?LibXCursor, - libxkbcommon: LibXkbCommon, - libxrr: ?LibXRR, motif_wm_hints: c.Atom, net_wm_bypass_compositor: c.Atom, net_wm_ping: c.Atom, @@ -68,35 +68,45 @@ pub fn initWindow( core_ptr = core; var core_window = core.windows.getValue(window_id); // TODO(core): return errors.NotSupported if not supported - const libx11 = try LibX11.load(); + // Try to load both libs so either or both missing libs can be communicated, if necessary + libx11 = LibX11.load() catch |err| switch (err) { + error.LibraryNotFound => null, + else => return err, + }; + libxkbcommon = LibXkbCommon.load() catch |err| switch (err) { + error.LibraryNotFound => null, + else => return err, + }; + libgl = LibGL.load() catch |err| switch (err) { + error.LibraryNotFound => null, + else => return err, + }; + if (libx11 == null or libxkbcommon == null or libgl == null) return error.LibraryNotFound; // Note: X11 (at least, older versions of it definitely) have a race condition with frame submission // when the Vulkan presentation mode != .none; XInitThreads() resolves this. We use XInitThreads // /solely/ to ensure we can use .double and .triple presentation modes, we do not use it for // anything else and otherwise treat all X11 API calls as if they are not thread-safe as with all // other native GUI APIs. - _ = libx11.XInitThreads(); - const libgl: ?LibGL = LibGL.load() catch |err| switch (err) { + _ = libx11.?.XInitThreads(); + + const libxcursor = LibXCursor.load() catch |err| switch (err) { error.LibraryNotFound => null, else => return err, }; - const libxcursor: ?LibXCursor = LibXCursor.load() catch |err| switch (err) { + const libxrr = LibXRR.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 display = libx11.XOpenDisplay(null) orelse { + const display = libx11.?.XOpenDisplay(null) orelse { return error.FailedToConnectToDisplay; }; const screen = c.DefaultScreen(display); const visual = c.DefaultVisual(display, screen); const root_window = c.RootWindow(display, screen); - const colormap = libx11.XCreateColormap(display, root_window, visual, c.AllocNone); - defer _ = libx11.XFreeColormap(display, colormap); + const colormap = libx11.?.XCreateColormap(display, root_window, visual, c.AllocNone); + defer _ = libx11.?.XFreeColormap(display, colormap); var set_window_attrs = c.XSetWindowAttributes{ .colormap = colormap, @@ -108,11 +118,11 @@ pub fn initWindow( }; // TODO: read error after function call and handle - const x_window_id = libx11.XCreateWindow( + const x_window_id = 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? + @divFloor(libx11.?.XDisplayWidth(display, screen), 2), // TODO: add window width? + @divFloor(libx11.?.XDisplayHeight(display, screen), 2), // TODO: add window height? core_window.width, core_window.height, 0, @@ -123,12 +133,12 @@ pub fn initWindow( &set_window_attrs, ); - const blank_pixmap = libx11.XCreatePixmap(display, x_window_id, 1, 1, 1); + const blank_pixmap = libx11.?.XCreatePixmap(display, x_window_id, 1, 1, 1); var color = c.XColor{}; core_window.refresh_rate = blk: { - if (libxrr != null) { - const conf = libxrr.?.XRRGetScreenInfo(display, root_window); - break :blk @intCast(libxrr.?.XRRConfigCurrentRate(conf)); + if (libxrr) |_libxrr| { + const conf = _libxrr.XRRGetScreenInfo(display, root_window); + break :blk @intCast(_libxrr.XRRConfigCurrentRate(conf)); } break :blk 60; }; @@ -144,22 +154,18 @@ pub fn initWindow( .display = display, .empty_event_pipe = try std.posix.pipe(), .gl_ctx = null, - .hidden_cursor = libx11.XCreatePixmapCursor(display, blank_pixmap, blank_pixmap, &color, &color, 0, 0), - .libgl = libgl, - .libx11 = libx11, + .hidden_cursor = libx11.?.XCreatePixmapCursor(display, blank_pixmap, blank_pixmap, &color, &color, 0, 0), .libxcursor = libxcursor, - .libxkbcommon = try LibXkbCommon.load(), - .libxrr = libxrr, - .motif_wm_hints = libx11.XInternAtom(display, "_MOTIF_WM_HINTS", c.False), - .net_wm_bypass_compositor = libx11.XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", c.False), - .net_wm_ping = libx11.XInternAtom(display, "NET_WM_PING", c.False), - .net_wm_window_type = libx11.XInternAtom(display, "_NET_WM_WINDOW_TYPE", c.False), - .net_wm_window_type_dock = libx11.XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", c.False), + .motif_wm_hints = libx11.?.XInternAtom(display, "_MOTIF_WM_HINTS", c.False), + .net_wm_bypass_compositor = libx11.?.XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", c.False), + .net_wm_ping = libx11.?.XInternAtom(display, "NET_WM_PING", c.False), + .net_wm_window_type = libx11.?.XInternAtom(display, "_NET_WM_WINDOW_TYPE", c.False), + .net_wm_window_type_dock = libx11.?.XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", c.False), .root_window = root_window, .surface_descriptor = surface_descriptor, .window = x_window_id, - .wm_delete_window = libx11.XInternAtom(display, "WM_DELETE_WINDOW", c.False), - .wm_protocols = libx11.XInternAtom(display, "WM_PROTOCOLS", c.False), + .wm_delete_window = libx11.?.XInternAtom(display, "WM_DELETE_WINDOW", c.False), + .wm_protocols = libx11.?.XInternAtom(display, "WM_PROTOCOLS", c.False), } }; var x11 = &core_window.native.?.x11; @@ -170,46 +176,42 @@ pub fn initWindow( _ = try std.posix.fcntl(x11.empty_event_pipe[i], std.posix.F.SETFD, df | std.posix.FD_CLOEXEC); } var protocols = [_]c.Atom{ x11.wm_delete_window, x11.net_wm_ping }; - _ = libx11.XSetWMProtocols(x11.display, x11.window, &protocols, protocols.len); - _ = libx11.XStoreName(x11.display, x11.window, core_window.title); - _ = libx11.XSelectInput(x11.display, x11.window, set_window_attrs.event_mask); - _ = libx11.XMapWindow(x11.display, x11.window); + _ = libx11.?.XSetWMProtocols(x11.display, x11.window, &protocols, protocols.len); + _ = libx11.?.XStoreName(x11.display, x11.window, core_window.title); + _ = libx11.?.XSelectInput(x11.display, x11.window, set_window_attrs.event_mask); + _ = libx11.?.XMapWindow(x11.display, x11.window); // TODO: see if this can be removed const backend_type = try Core.detectBackendType(core.allocator); 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(x11.display, screen, attrs.ptr); - defer _ = libx11.XFree(visual_info); - x11.gl_ctx = libgl.?.glXCreateContext(x11.display, visual_info, null, true); - _ = libgl.?.glXMakeCurrent(x11.display, x11.window, x11.gl_ctx); - } else { - return error.LibGLNotFound; - } + // 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(x11.display, screen, attrs.ptr); + defer _ = libx11.?.XFree(visual_info); + x11.gl_ctx = libgl.?.glXCreateContext(x11.display, visual_info, null, true); + _ = libgl.?.glXMakeCurrent(x11.display, x11.window, x11.gl_ctx); }, else => {}, } // Create hidden cursor - const gc = libx11.XCreateGC(x11.display, blank_pixmap, 0, null); + const gc = libx11.?.XCreateGC(x11.display, blank_pixmap, 0, null); if (gc != null) { - _ = libx11.XDrawPoint(x11.display, blank_pixmap, gc, 0, 0); - _ = libx11.XFreeGC(x11.display, gc); + _ = libx11.?.XDrawPoint(x11.display, blank_pixmap, gc, 0, 0); + _ = libx11.?.XFreeGC(x11.display, gc); } x11.cursors[@intFromEnum(CursorShape.arrow)] = try createStandardCursor(x11, .arrow); @@ -223,26 +225,26 @@ pub fn tick(window_id: mach.ObjectID) !void { var x11 = &core_window.native.?.x11; while (c.QLength(x11.display) != 0) { var event: c.XEvent = undefined; - _ = x11.libx11.XNextEvent(x11.display, &event); + _ = libx11.?.XNextEvent(x11.display, &event); processEvent(window_id, &event); // update in case core_window was changed core_window = core_ptr.windows.getValue(window_id); x11 = &core_window.native.?.x11; } - _ = x11.libx11.XFlush(x11.display); + _ = 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); } pub fn setTitle(x11: *const Native, title: [:0]const u8) void { - _ = x11.libx11.XStoreName(x11.display, x11.window, title); + _ = libx11.?.XStoreName(x11.display, x11.window, title); } pub fn setDisplayMode(x11: *const Native, display_mode: DisplayMode, border: bool) void { - const wm_state = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE", c.False); - const wm_fullscreen = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False); + const wm_state = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE", c.False); + const wm_fullscreen = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False); switch (display_mode) { .windowed => { var atoms = std.BoundedArray(c.Atom, 5){}; @@ -254,7 +256,7 @@ pub fn setDisplayMode(x11: *const Native, display_mode: DisplayMode, border: boo // if (x11.floating) { // atoms.append(x11.net_wm_state_above) catch unreachable; // } - _ = x11.libx11.XChangeProperty( + _ = libx11.?.XChangeProperty( x11.display, x11.window, wm_state, @@ -267,37 +269,37 @@ pub fn setDisplayMode(x11: *const Native, display_mode: DisplayMode, border: boo x11.setFullscreen(false); x11.setDecorated(border); x11.setFloating(false); - _ = x11.libx11.XMapWindow(x11.display, x11.window); - _ = x11.libx11.XFlush(x11.display); + _ = libx11.?.XMapWindow(x11.display, x11.window); + _ = libx11.?.XFlush(x11.display); }, .fullscreen => { x11.setFullscreen(true); - _ = x11.libx11.XFlush(x11.display); + _ = libx11.?.XFlush(x11.display); }, .fullscreen_borderless => { x11.setDecorated(false); x11.setFloating(true); x11.setFullscreen(false); - _ = x11.libx11.XResizeWindow( + _ = 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); + _ = libx11.?.XFlush(x11.display); }, } } fn setFullscreen(x11: *const Native, enabled: bool) void { - const wm_state = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE", c.False); - const wm_fullscreen = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False); + const wm_state = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE", c.False); + const wm_fullscreen = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False); x11.sendEventToWM(wm_state, &.{ @intFromBool(enabled), @intCast(wm_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); + const bypass_compositor = libx11.?.XInternAtom(x11.display, "_NET_WM_BYPASS_COMPOSITOR", c.False); if (bypass_compositor != c.None) { - _ = x11.libx11.XChangeProperty( + _ = libx11.?.XChangeProperty( x11.display, x11.window, bypass_compositor, @@ -311,8 +313,8 @@ fn setFullscreen(x11: *const Native, enabled: bool) void { } fn setFloating(x11: *const Native, enabled: bool) void { - const wm_state = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE", c.False); - const wm_above = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE_ABOVE", c.False); + const wm_state = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE", c.False); + const wm_above = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE_ABOVE", c.False); 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; @@ -326,14 +328,14 @@ fn sendEventToWM(x11: *const Native, message_type: c.Atom, data: []const c_long) ev.xclient.message_type = message_type; ev.xclient.format = 32; @memcpy(ev.xclient.data.l[0..data.len], data); - _ = x11.libx11.XSendEvent( + _ = libx11.?.XSendEvent( x11.display, x11.root_window, c.False, c.SubstructureNotifyMask | c.SubstructureRedirectMask, &ev, ); - _ = x11.libx11.XFlush(x11.display); + _ = libx11.?.XFlush(x11.display); } fn setDecorated(x11: *const Native, enabled: bool) void { @@ -351,7 +353,7 @@ fn setDecorated(x11: *const Native, enabled: bool) void { .input_mode = 0, .status = 0, }; - _ = x11.libx11.XChangeProperty( + _ = libx11.?.XChangeProperty( x11.display, x11.window, x11.motif_wm_hints, @@ -363,7 +365,7 @@ fn setDecorated(x11: *const Native, enabled: bool) void { ); } -const LibX11 = struct { +pub const LibX11 = struct { handle: std.DynLib, XInitThreads: *const @TypeOf(c.XInitThreads), XrmInitialize: *const @TypeOf(c.XrmInitialize), @@ -407,9 +409,12 @@ const LibX11 = struct { XAllocSizeHints: *const @TypeOf(c.XAllocSizeHints), XSetWMNormalHints: *const @TypeOf(c.XSetWMNormalHints), XFree: *const @TypeOf(c.XFree), + + pub const lib_name = "libX11.so.6"; + pub fn load() !LibX11 { var lib: LibX11 = undefined; - lib.handle = try mach.dynLibOpen("libX11.so.6"); + lib.handle = std.DynLib.open(lib_name) 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]); @@ -429,7 +434,7 @@ const LibXCursor = struct { XcursorLibraryLoadImage: *const @TypeOf(c.XcursorLibraryLoadImage), pub fn load() !LibXCursor { var lib: LibXCursor = undefined; - lib.handle = try mach.dynLibOpen("libXcursor.so.1"); + 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]); @@ -445,7 +450,7 @@ const LibXRR = struct { XRRConfigCurrentRate: *const @TypeOf(c.XRRConfigCurrentRate), pub fn load() !LibXRR { var lib: LibXRR = undefined; - lib.handle = try mach.dynLibOpen("libXrandr.so.1"); + 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]); @@ -455,7 +460,7 @@ const LibXRR = struct { } }; -const LibGL = struct { +pub const LibGL = struct { const Drawable = c.XID; const Context = opaque {}; const FBConfig = opaque {}; @@ -474,9 +479,12 @@ const LibGL = struct { 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 const lib_name = "libGL.so.1"; + pub fn load() !LibGL { var lib: LibGL = undefined; - lib.handle = try mach.dynLibOpen("libGL.so.1"); + lib.handle = std.DynLib.open(lib_name) 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]); @@ -486,7 +494,7 @@ const LibGL = struct { } }; -const LibXkbCommon = struct { +pub const LibXkbCommon = struct { handle: std.DynLib, // xkb_context_new: *const @TypeOf(c.xkb_context_new), @@ -506,9 +514,11 @@ const LibXkbCommon = struct { // xkb_compose_state_get_one_sym: *const @TypeOf(c.xkb_compose_state_get_one_sym), xkb_keysym_to_utf32: *const @TypeOf(c.xkb_keysym_to_utf32), + pub const lib_name = "libxkbcommon.so.0"; + pub fn load() !LibXkbCommon { var lib: LibXkbCommon = undefined; - lib.handle = try mach.dynLibOpen("libxkbcommon.so.0"); + lib.handle = std.DynLib.open(lib_name) 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]); @@ -557,7 +567,7 @@ fn createStandardCursor(x11: *const Native, shape: CursorShape) !c.Cursor { .resize_all => c.XC_fleur, .not_allowed => c.XC_X_cursor, }; - const cursor = x11.libx11.XCreateFontCursor(x11.display, xc); + const cursor = libx11.?.XCreateFontCursor(x11.display, xc); if (cursor == 0) return error.FailedToCreateCursor; return cursor; } @@ -570,7 +580,7 @@ fn getCursorPos(x11: *const Native) Position { var cursor_x: c_int = 0; var cursor_y: c_int = 0; var mask: c_uint = 0; - _ = x11.libx11.XQueryPointer( + _ = libx11.?.XQueryPointer( x11.display, x11.window, &root_window, @@ -594,7 +604,7 @@ fn processEvent(window_id: mach.ObjectID, event: *c.XEvent) void { // TODO: key repeat event var keysym: c.KeySym = undefined; - _ = x11.libx11.XLookupString(&event.xkey, null, 0, &keysym, null); + _ = libx11.?.XLookupString(&event.xkey, null, 0, &keysym, null); const key_event = KeyEvent{ .key = toMachKey(keysym), @@ -606,7 +616,7 @@ fn processEvent(window_id: mach.ObjectID, event: *c.XEvent) void { c.KeyPress => { core_ptr.pushEvent(.{ .key_press = key_event }); - const codepoint = x11.libxkbcommon.xkb_keysym_to_utf32(@truncate(keysym)); + const codepoint = libxkbcommon.?.xkb_keysym_to_utf32(@truncate(keysym)); if (codepoint != 0) { core_ptr.pushEvent(.{ .char_input = .{ .codepoint = @truncate(codepoint), .window_id = window_id } }); } @@ -666,7 +676,7 @@ fn processEvent(window_id: mach.ObjectID, event: *c.XEvent) void { // it's still responding to events var reply = event.*; reply.xclient.window = x11.root_window; - _ = x11.libx11.XSendEvent( + _ = libx11.?.XSendEvent( x11.display, x11.root_window, c.False, @@ -731,7 +741,7 @@ fn processEvent(window_id: mach.ObjectID, event: *c.XEvent) void { core_ptr.pushEvent(.{ .focus_lost = .{ .window_id = window_id } }); }, c.ResizeRequest => { - _ = x11.libx11.XResizeWindow( + _ = libx11.?.XResizeWindow( x11.display, x11.window, @intCast(c.DisplayWidth(x11.display, c.DefaultScreen(x11.display))),