linux: improve logging when both backends fail

This commit is contained in:
Joshua Holmes 2025-01-07 03:47:44 +00:00 committed by Emi Gutekanst
parent 377842aef8
commit ab143504ab
3 changed files with 186 additions and 145 deletions

View file

@ -115,7 +115,22 @@ pub fn initWindow(
else => "An unknown error occured while trying to connect to X11", else => "An unknown error occured while trying to connect to X11",
}; };
log.err("{s}\n\nFalling back to Wayland\n", .{err_msg}); 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 => { .wayland => {
@ -127,7 +142,19 @@ pub fn initWindow(
else => "An unknown error occured while trying to connect to Wayland", else => "An unknown error occured while trying to connect to Wayland",
}; };
log.err("{s}\n\nFalling back to X11\n", .{err_msg}); 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;
};
}; };
}, },
} }

View file

@ -25,11 +25,11 @@ pub const c = @cImport({
// This needs to be declared here so it can be used in the exported functions below, // 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). // 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 // This does not need to be declared here, but we are declaring it here to be consistent
// with `libwaylandclient`. // with `libwaylandclient`.
var libxkbcommon: LibXkbCommon = undefined; pub var libxkbcommon: ?LibXkbCommon = null;
var core_ptr: *Core = undefined; 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 // compile time, but since they are not run until run time, after `libwaylandclient` is
// defined, an error never occurs. // 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 { 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 { 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 { 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(); var arg_list: std.builtin.VaList = @cVaStart();
defer @cVaEnd(&arg_list); 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 { 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 { pub const Native = struct {
@ -87,7 +87,7 @@ pub fn initWindow(
core_window.native = .{ core_window.native = .{
.wayland = .{ .wayland = .{
.interfaces = Interfaces{}, .interfaces = Interfaces{},
.display = libwaylandclient.wl_display_connect(null) orelse return error.FailedToConnectToDisplay, .display = libwaylandclient.?.wl_display_connect(null) orelse return error.FailedToConnectToDisplay,
.modifiers = .{ .modifiers = .{
.alt = false, .alt = false,
.caps_lock = false, .caps_lock = false,
@ -108,7 +108,7 @@ pub fn initWindow(
.surface_descriptor = undefined, .surface_descriptor = undefined,
.surface = undefined, .surface = undefined,
.toplevel = 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 // TODO: Look at replacing these 2 calls to wl_display_roundtrip with wl_display::sync
// Round trip to get all the registry objects // 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 // 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 // Update `core_window` since registry listener and seat listener changed values in it
core_window = core.windows.getValue(window_id); core_window = core.windows.getValue(window_id);
@ -174,7 +174,7 @@ pub fn initWindow(
} }
// Wait for events to get pushed // 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); core_window = core.windows.getValue(window_id);
wl = &core_window.native.?.wayland; wl = &core_window.native.?.wayland;
@ -183,7 +183,7 @@ pub fn initWindow(
c.wl_surface_commit(wl.surface); c.wl_surface_commit(wl.surface);
while (true) { 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); core_window = core.windows.getValue(window_id);
wl = &core_window.native.?.wayland; wl = &core_window.native.?.wayland;
@ -203,7 +203,7 @@ pub fn initWindow(
// Commit changes to surface // Commit changes to surface
c.wl_surface_commit(wl.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); core.windows.setValue(window_id, core_window);
try core.initWindow(window_id); try core.initWindow(window_id);
@ -212,14 +212,14 @@ pub fn initWindow(
pub fn tick(window_id: mach.ObjectID) !void { pub fn tick(window_id: mach.ObjectID) !void {
const wl = &core_ptr.windows.getValue(window_id).native.?.wayland; 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) { if (std.posix.errno(-1) == std.posix.E.AGAIN) {
log.err("flush error", .{}); log.err("flush error", .{});
return error.FlushError; return error.FlushError;
} }
var pollfd = [_]std.posix.pollfd{ var pollfd = [_]std.posix.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, .events = std.posix.POLL.OUT,
.revents = 0, .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 { 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; _ = display_mode;
} }
const LibXkbCommon = struct { pub const LibXkbCommon = struct {
handle: std.DynLib, handle: std.DynLib,
xkb_context_new: *const @TypeOf(c.xkb_context_new), 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_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), xkb_keysym_to_utf32: *const @TypeOf(c.xkb_keysym_to_utf32),
pub const lib_name = "libxkbcommon.so.0";
pub fn load() !LibXkbCommon { pub fn load() !LibXkbCommon {
var lib: LibXkbCommon = undefined; 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| { inline for (@typeInfo(LibXkbCommon).@"struct".fields[1..]) |field| {
const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); 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, handle: std.DynLib,
wl_display_connect: *const @TypeOf(c.wl_display_connect), 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_surface_interface: *@TypeOf(c.wl_surface_interface),
wl_touch_interface: *@TypeOf(c.wl_touch_interface), wl_touch_interface: *@TypeOf(c.wl_touch_interface),
pub const lib_name = "libwayland-client.so.0";
pub fn load() !LibWaylandClient { pub fn load() !LibWaylandClient {
var lib: LibWaylandClient = undefined; 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| { inline for (@typeInfo(LibWaylandClient).@"struct".fields[1..]) |field| {
const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); 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( wl.interfaces.wl_compositor = @ptrCast(c.wl_registry_bind(
registry, registry,
name, name,
libwaylandclient.wl_compositor_interface, libwaylandclient.?.wl_compositor_interface,
@min(3, version), @min(3, version),
) orelse @panic("uh idk how to proceed")); ) orelse @panic("uh idk how to proceed"));
} else if (std.mem.eql(u8, "wl_subcompositor", interface)) { } 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( wl.interfaces.wl_subcompositor = @ptrCast(c.wl_registry_bind(
registry, registry,
name, name,
libwaylandclient.wl_subcompositor_interface, libwaylandclient.?.wl_subcompositor_interface,
@min(3, version), @min(3, version),
) orelse @panic("uh idk how to proceed")); ) orelse @panic("uh idk how to proceed"));
} else if (std.mem.eql(u8, "wl_shm", interface)) { } 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( wl.interfaces.wl_shm = @ptrCast(c.wl_registry_bind(
registry, registry,
name, name,
libwaylandclient.wl_shm_interface, libwaylandclient.?.wl_shm_interface,
@min(3, version), @min(3, version),
) orelse @panic("uh idk how to proceed")); ) orelse @panic("uh idk how to proceed"));
} else if (std.mem.eql(u8, "wl_output", interface)) { } 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( wl.interfaces.wl_output = @ptrCast(c.wl_registry_bind(
registry, registry,
name, name,
libwaylandclient.wl_output_interface, libwaylandclient.?.wl_output_interface,
@min(3, version), @min(3, version),
) orelse @panic("uh idk how to proceed")); ) orelse @panic("uh idk how to proceed"));
// } else if (std.mem.eql(u8, "wl_data_device_manager", interface)) { // } else if (std.mem.eql(u8, "wl_data_device_manager", interface)) {
// wl.interfaces.wl_data_device_manager = @ptrCast(c.wl_registry_bind( // wl.interfaces.wl_data_device_manager = @ptrCast(c.wl_registry_bind(
// registry, // registry,
// name, // name,
// libwaylandclient.wl_data_device_manager_interface, // libwaylandclient.?.wl_data_device_manager_interface,
// @min(3, version), // @min(3, version),
// ) orelse @panic("uh idk how to proceed")); // ) orelse @panic("uh idk how to proceed"));
} else if (std.mem.eql(u8, "xdg_wm_base", interface)) { } 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( wl.interfaces.wl_seat = @ptrCast(c.wl_registry_bind(
registry, registry,
name, name,
libwaylandclient.wl_seat_interface, libwaylandclient.?.wl_seat_interface,
@min(3, version), @min(3, version),
) orelse @panic("uh idk how to proceed")); ) 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 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, wl.xkb_context,
@alignCast(map_str), //align cast happening here, im sure its fine? TODO: figure out if this okay @alignCast(map_str), //align cast happening here, im sure its fine? TODO: figure out if this okay
c.XKB_KEYMAP_FORMAT_TEXT_V1, c.XKB_KEYMAP_FORMAT_TEXT_V1,
@ -470,13 +474,13 @@ const keyboard_listener = struct {
std.posix.close(fd); std.posix.close(fd);
//Release reference to old state and create new state //Release reference to old state and create new state
libxkbcommon.xkb_state_unref(wl.xkb_state); libxkbcommon.?.xkb_state_unref(wl.xkb_state);
const state = libxkbcommon.xkb_state_new(keymap).?; const state = libxkbcommon.?.xkb_state_new(keymap).?;
//this chain hurts me. why must C be this way. //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"; 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, wl.xkb_context,
locale, locale,
c.XKB_COMPOSE_COMPILE_NO_FLAGS, c.XKB_COMPOSE_COMPILE_NO_FLAGS,
@ -484,24 +488,24 @@ const keyboard_listener = struct {
//If creation failed, lets try the C locale //If creation failed, lets try the C locale
if (compose_table == null) 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, wl.xkb_context,
"C", "C",
c.XKB_COMPOSE_COMPILE_NO_FLAGS, 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.keymap = keymap;
wl.xkb_state = state; 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.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.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.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.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.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.num_lock_index = libxkbcommon.?.xkb_keymap_mod_get_index(keymap, "Mod2");
core_ptr.windows.setValue(window_id, core_window); core_ptr.windows.setValue(window_id, core_window);
} }
@ -538,12 +542,12 @@ const keyboard_listener = struct {
var keysyms: ?[*]const c.xkb_keysym_t = undefined; var keysyms: ?[*]const c.xkb_keysym_t = undefined;
//Get the keysym from the keycode (scancode + 8) //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 //Compose the keysym
const keysym: c.xkb_keysym_t = composeSymbol(wl, keysyms.?[0]); const keysym: c.xkb_keysym_t = composeSymbol(wl, keysyms.?[0]);
//Try to convert that keysym to a unicode codepoint //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) { if (codepoint != 0) {
core_ptr.pushEvent(.{ .char_input = .{ .codepoint = @truncate(codepoint), .window_id = window_id } }); core_ptr.pushEvent(.{ .char_input = .{ .codepoint = @truncate(codepoint), .window_id = window_id } });
} }
@ -563,7 +567,7 @@ const keyboard_listener = struct {
return; return;
// TODO: handle this return value // TODO: handle this return value
_ = libxkbcommon.xkb_state_update_mask( _ = libxkbcommon.?.xkb_state_update_mask(
wl.xkb_state.?, wl.xkb_state.?,
mods_depressed, mods_depressed,
mods_latched, mods_latched,
@ -582,7 +586,7 @@ const keyboard_listener = struct {
.{ wl.modifier_indices.num_lock_index, "num_lock" }, .{ wl.modifier_indices.num_lock_index, "num_lock" },
.{ wl.modifier_indices.caps_lock_index, "caps_lock" }, .{ wl.modifier_indices.caps_lock_index, "caps_lock" },
}) |key| { }) |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, wl.xkb_state,
key[0], key[0],
c.XKB_STATE_MODS_EFFECTIVE, 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) if (sym == c.XKB_KEY_NoSymbol or wl.compose_state == null)
return sym; 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 sym;
return switch (libxkbcommon.xkb_compose_state_get_status(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_COMPOSED => libxkbcommon.?.xkb_compose_state_get_one_sym(wl.compose_state),
c.XKB_COMPOSE_COMPOSING, c.XKB_COMPOSE_CANCELLED => c.XKB_KEY_NoSymbol, c.XKB_COMPOSE_COMPOSING, c.XKB_COMPOSE_CANCELLED => c.XKB_KEY_NoSymbol,
else => sym, else => sym,
}; };

View file

@ -33,6 +33,10 @@ pub const defaultPanic = std.debug.panicImpl;
// TODO: determine if it's really needed to store global pointer // TODO: determine if it's really needed to store global pointer
var core_ptr: *Core = undefined; var core_ptr: *Core = undefined;
pub var libx11: ?LibX11 = null;
pub var libxkbcommon: ?LibXkbCommon = null;
pub var libgl: ?LibGL = null;
pub const Native = struct { pub const Native = struct {
backend_type: gpu.BackendType, backend_type: gpu.BackendType,
cursors: [@typeInfo(CursorShape).@"enum".fields.len]?c.Cursor, cursors: [@typeInfo(CursorShape).@"enum".fields.len]?c.Cursor,
@ -40,11 +44,7 @@ pub const Native = struct {
empty_event_pipe: [2]std.c.fd_t, empty_event_pipe: [2]std.c.fd_t,
gl_ctx: ?*LibGL.Context, gl_ctx: ?*LibGL.Context,
hidden_cursor: c.Cursor, hidden_cursor: c.Cursor,
libgl: ?LibGL,
libx11: LibX11,
libxcursor: ?LibXCursor, libxcursor: ?LibXCursor,
libxkbcommon: LibXkbCommon,
libxrr: ?LibXRR,
motif_wm_hints: c.Atom, motif_wm_hints: c.Atom,
net_wm_bypass_compositor: c.Atom, net_wm_bypass_compositor: c.Atom,
net_wm_ping: c.Atom, net_wm_ping: c.Atom,
@ -68,35 +68,45 @@ pub fn initWindow(
core_ptr = core; core_ptr = core;
var core_window = core.windows.getValue(window_id); var core_window = core.windows.getValue(window_id);
// TODO(core): return errors.NotSupported if not supported // 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 // 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 // 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 // /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 // anything else and otherwise treat all X11 API calls as if they are not thread-safe as with all
// other native GUI APIs. // other native GUI APIs.
_ = libx11.XInitThreads(); _ = libx11.?.XInitThreads();
const libgl: ?LibGL = LibGL.load() catch |err| switch (err) {
const libxcursor = LibXCursor.load() catch |err| switch (err) {
error.LibraryNotFound => null, error.LibraryNotFound => null,
else => return err, else => return err,
}; };
const libxcursor: ?LibXCursor = LibXCursor.load() catch |err| switch (err) { const libxrr = LibXRR.load() catch |err| switch (err) {
error.LibraryNotFound => null, error.LibraryNotFound => null,
else => return err, else => return err,
}; };
const libxrr: ?LibXRR = LibXRR.load() catch |err| switch (err) { const display = libx11.?.XOpenDisplay(null) orelse {
error.LibraryNotFound => null,
else => return err,
};
const display = libx11.XOpenDisplay(null) orelse {
return error.FailedToConnectToDisplay; return error.FailedToConnectToDisplay;
}; };
const screen = c.DefaultScreen(display); const screen = c.DefaultScreen(display);
const visual = c.DefaultVisual(display, screen); const visual = c.DefaultVisual(display, screen);
const root_window = c.RootWindow(display, screen); const root_window = c.RootWindow(display, screen);
const colormap = libx11.XCreateColormap(display, root_window, visual, c.AllocNone); const colormap = libx11.?.XCreateColormap(display, root_window, visual, c.AllocNone);
defer _ = libx11.XFreeColormap(display, colormap); defer _ = libx11.?.XFreeColormap(display, colormap);
var set_window_attrs = c.XSetWindowAttributes{ var set_window_attrs = c.XSetWindowAttributes{
.colormap = colormap, .colormap = colormap,
@ -108,11 +118,11 @@ pub fn initWindow(
}; };
// TODO: read error after function call and handle // TODO: read error after function call and handle
const x_window_id = libx11.XCreateWindow( const x_window_id = libx11.?.XCreateWindow(
display, display,
root_window, root_window,
@divFloor(libx11.XDisplayWidth(display, screen), 2), // TODO: add window width? @divFloor(libx11.?.XDisplayWidth(display, screen), 2), // TODO: add window width?
@divFloor(libx11.XDisplayHeight(display, screen), 2), // TODO: add window height? @divFloor(libx11.?.XDisplayHeight(display, screen), 2), // TODO: add window height?
core_window.width, core_window.width,
core_window.height, core_window.height,
0, 0,
@ -123,12 +133,12 @@ pub fn initWindow(
&set_window_attrs, &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{}; var color = c.XColor{};
core_window.refresh_rate = blk: { core_window.refresh_rate = blk: {
if (libxrr != null) { if (libxrr) |_libxrr| {
const conf = libxrr.?.XRRGetScreenInfo(display, root_window); const conf = _libxrr.XRRGetScreenInfo(display, root_window);
break :blk @intCast(libxrr.?.XRRConfigCurrentRate(conf)); break :blk @intCast(_libxrr.XRRConfigCurrentRate(conf));
} }
break :blk 60; break :blk 60;
}; };
@ -144,22 +154,18 @@ pub fn initWindow(
.display = display, .display = display,
.empty_event_pipe = try std.posix.pipe(), .empty_event_pipe = try std.posix.pipe(),
.gl_ctx = null, .gl_ctx = null,
.hidden_cursor = libx11.XCreatePixmapCursor(display, blank_pixmap, blank_pixmap, &color, &color, 0, 0), .hidden_cursor = libx11.?.XCreatePixmapCursor(display, blank_pixmap, blank_pixmap, &color, &color, 0, 0),
.libgl = libgl,
.libx11 = libx11,
.libxcursor = libxcursor, .libxcursor = libxcursor,
.libxkbcommon = try LibXkbCommon.load(), .motif_wm_hints = libx11.?.XInternAtom(display, "_MOTIF_WM_HINTS", c.False),
.libxrr = libxrr, .net_wm_bypass_compositor = libx11.?.XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", c.False),
.motif_wm_hints = libx11.XInternAtom(display, "_MOTIF_WM_HINTS", c.False), .net_wm_ping = libx11.?.XInternAtom(display, "NET_WM_PING", c.False),
.net_wm_bypass_compositor = libx11.XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", c.False), .net_wm_window_type = libx11.?.XInternAtom(display, "_NET_WM_WINDOW_TYPE", c.False),
.net_wm_ping = libx11.XInternAtom(display, "NET_WM_PING", c.False), .net_wm_window_type_dock = libx11.?.XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", 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, .root_window = root_window,
.surface_descriptor = surface_descriptor, .surface_descriptor = surface_descriptor,
.window = x_window_id, .window = x_window_id,
.wm_delete_window = libx11.XInternAtom(display, "WM_DELETE_WINDOW", c.False), .wm_delete_window = libx11.?.XInternAtom(display, "WM_DELETE_WINDOW", c.False),
.wm_protocols = libx11.XInternAtom(display, "WM_PROTOCOLS", c.False), .wm_protocols = libx11.?.XInternAtom(display, "WM_PROTOCOLS", c.False),
} }; } };
var x11 = &core_window.native.?.x11; 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); _ = 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 }; var protocols = [_]c.Atom{ x11.wm_delete_window, x11.net_wm_ping };
_ = libx11.XSetWMProtocols(x11.display, x11.window, &protocols, protocols.len); _ = libx11.?.XSetWMProtocols(x11.display, x11.window, &protocols, protocols.len);
_ = libx11.XStoreName(x11.display, x11.window, core_window.title); _ = libx11.?.XStoreName(x11.display, x11.window, core_window.title);
_ = libx11.XSelectInput(x11.display, x11.window, set_window_attrs.event_mask); _ = libx11.?.XSelectInput(x11.display, x11.window, set_window_attrs.event_mask);
_ = libx11.XMapWindow(x11.display, x11.window); _ = libx11.?.XMapWindow(x11.display, x11.window);
// TODO: see if this can be removed // TODO: see if this can be removed
const backend_type = try Core.detectBackendType(core.allocator); const backend_type = try Core.detectBackendType(core.allocator);
switch (backend_type) { switch (backend_type) {
.opengl, .opengles => { .opengl, .opengles => {
if (libgl != null) { // zig fmt: off
// zig fmt: off const attrs = &[_]c_int{
const attrs = &[_]c_int{ LibGL.rgba,
LibGL.rgba, LibGL.doublebuffer,
LibGL.doublebuffer, LibGL.depth_size, 24,
LibGL.depth_size, 24, LibGL.stencil_size, 8,
LibGL.stencil_size, 8, LibGL.red_size, 8,
LibGL.red_size, 8, LibGL.green_size, 8,
LibGL.green_size, 8, LibGL.blue_size, 8,
LibGL.blue_size, 8, LibGL.sample_buffers, 0,
LibGL.sample_buffers, 0, LibGL.samples, 0,
LibGL.samples, 0, c.None,
c.None, };
}; // zig fmt: on
// zig fmt: on const visual_info = libgl.?.glXChooseVisual(x11.display, screen, attrs.ptr);
const visual_info = libgl.?.glXChooseVisual(x11.display, screen, attrs.ptr); defer _ = libx11.?.XFree(visual_info);
defer _ = libx11.XFree(visual_info); x11.gl_ctx = libgl.?.glXCreateContext(x11.display, visual_info, null, true);
x11.gl_ctx = libgl.?.glXCreateContext(x11.display, visual_info, null, true); _ = libgl.?.glXMakeCurrent(x11.display, x11.window, x11.gl_ctx);
_ = libgl.?.glXMakeCurrent(x11.display, x11.window, x11.gl_ctx);
} else {
return error.LibGLNotFound;
}
}, },
else => {}, else => {},
} }
// Create hidden cursor // 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) { if (gc != null) {
_ = libx11.XDrawPoint(x11.display, blank_pixmap, gc, 0, 0); _ = libx11.?.XDrawPoint(x11.display, blank_pixmap, gc, 0, 0);
_ = libx11.XFreeGC(x11.display, gc); _ = libx11.?.XFreeGC(x11.display, gc);
} }
x11.cursors[@intFromEnum(CursorShape.arrow)] = try createStandardCursor(x11, .arrow); 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; var x11 = &core_window.native.?.x11;
while (c.QLength(x11.display) != 0) { while (c.QLength(x11.display) != 0) {
var event: c.XEvent = undefined; var event: c.XEvent = undefined;
_ = x11.libx11.XNextEvent(x11.display, &event); _ = libx11.?.XNextEvent(x11.display, &event);
processEvent(window_id, &event); processEvent(window_id, &event);
// update in case core_window was changed // update in case core_window was changed
core_window = core_ptr.windows.getValue(window_id); core_window = core_ptr.windows.getValue(window_id);
x11 = &core_window.native.?.x11; 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)); // const frequency_delay = @as(f32, @floatFromInt(x11.input.delay_ns)) / @as(f32, @floatFromInt(std.time.ns_per_s));
// TODO: glfw.waitEventsTimeout(frequency_delay); // TODO: glfw.waitEventsTimeout(frequency_delay);
} }
pub fn setTitle(x11: *const Native, title: [:0]const u8) void { 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 { 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_state = 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_fullscreen = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False);
switch (display_mode) { switch (display_mode) {
.windowed => { .windowed => {
var atoms = std.BoundedArray(c.Atom, 5){}; 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) { // if (x11.floating) {
// atoms.append(x11.net_wm_state_above) catch unreachable; // atoms.append(x11.net_wm_state_above) catch unreachable;
// } // }
_ = x11.libx11.XChangeProperty( _ = libx11.?.XChangeProperty(
x11.display, x11.display,
x11.window, x11.window,
wm_state, wm_state,
@ -267,37 +269,37 @@ pub fn setDisplayMode(x11: *const Native, display_mode: DisplayMode, border: boo
x11.setFullscreen(false); x11.setFullscreen(false);
x11.setDecorated(border); x11.setDecorated(border);
x11.setFloating(false); x11.setFloating(false);
_ = x11.libx11.XMapWindow(x11.display, x11.window); _ = libx11.?.XMapWindow(x11.display, x11.window);
_ = x11.libx11.XFlush(x11.display); _ = libx11.?.XFlush(x11.display);
}, },
.fullscreen => { .fullscreen => {
x11.setFullscreen(true); x11.setFullscreen(true);
_ = x11.libx11.XFlush(x11.display); _ = libx11.?.XFlush(x11.display);
}, },
.fullscreen_borderless => { .fullscreen_borderless => {
x11.setDecorated(false); x11.setDecorated(false);
x11.setFloating(true); x11.setFloating(true);
x11.setFullscreen(false); x11.setFullscreen(false);
_ = x11.libx11.XResizeWindow( _ = libx11.?.XResizeWindow(
x11.display, x11.display,
x11.window, x11.window,
@intCast(c.DisplayWidth(x11.display, c.DefaultScreen(x11.display))), @intCast(c.DisplayWidth(x11.display, c.DefaultScreen(x11.display))),
@intCast(c.DisplayHeight(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 { fn setFullscreen(x11: *const Native, enabled: bool) void {
const wm_state = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE", c.False); const wm_state = 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_fullscreen = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False);
x11.sendEventToWM(wm_state, &.{ @intFromBool(enabled), @intCast(wm_fullscreen), 0, 1 }); x11.sendEventToWM(wm_state, &.{ @intFromBool(enabled), @intCast(wm_fullscreen), 0, 1 });
// Force composition OFF to reduce overhead // Force composition OFF to reduce overhead
const compositing_disable_on: c_long = @intFromBool(enabled); 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) { if (bypass_compositor != c.None) {
_ = x11.libx11.XChangeProperty( _ = libx11.?.XChangeProperty(
x11.display, x11.display,
x11.window, x11.window,
bypass_compositor, bypass_compositor,
@ -311,8 +313,8 @@ fn setFullscreen(x11: *const Native, enabled: bool) void {
} }
fn setFloating(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_state = 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_above = libx11.?.XInternAtom(x11.display, "_NET_WM_STATE_ABOVE", c.False);
const net_wm_state_remove = 0; const net_wm_state_remove = 0;
const net_wm_state_add = 1; const net_wm_state_add = 1;
const action: c_long = if (enabled) net_wm_state_add else net_wm_state_remove; 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.message_type = message_type;
ev.xclient.format = 32; ev.xclient.format = 32;
@memcpy(ev.xclient.data.l[0..data.len], data); @memcpy(ev.xclient.data.l[0..data.len], data);
_ = x11.libx11.XSendEvent( _ = libx11.?.XSendEvent(
x11.display, x11.display,
x11.root_window, x11.root_window,
c.False, c.False,
c.SubstructureNotifyMask | c.SubstructureRedirectMask, c.SubstructureNotifyMask | c.SubstructureRedirectMask,
&ev, &ev,
); );
_ = x11.libx11.XFlush(x11.display); _ = libx11.?.XFlush(x11.display);
} }
fn setDecorated(x11: *const Native, enabled: bool) void { fn setDecorated(x11: *const Native, enabled: bool) void {
@ -351,7 +353,7 @@ fn setDecorated(x11: *const Native, enabled: bool) void {
.input_mode = 0, .input_mode = 0,
.status = 0, .status = 0,
}; };
_ = x11.libx11.XChangeProperty( _ = libx11.?.XChangeProperty(
x11.display, x11.display,
x11.window, x11.window,
x11.motif_wm_hints, 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, handle: std.DynLib,
XInitThreads: *const @TypeOf(c.XInitThreads), XInitThreads: *const @TypeOf(c.XInitThreads),
XrmInitialize: *const @TypeOf(c.XrmInitialize), XrmInitialize: *const @TypeOf(c.XrmInitialize),
@ -407,9 +409,12 @@ const LibX11 = struct {
XAllocSizeHints: *const @TypeOf(c.XAllocSizeHints), XAllocSizeHints: *const @TypeOf(c.XAllocSizeHints),
XSetWMNormalHints: *const @TypeOf(c.XSetWMNormalHints), XSetWMNormalHints: *const @TypeOf(c.XSetWMNormalHints),
XFree: *const @TypeOf(c.XFree), XFree: *const @TypeOf(c.XFree),
pub const lib_name = "libX11.so.6";
pub fn load() !LibX11 { pub fn load() !LibX11 {
var lib: LibX11 = undefined; 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| { inline for (@typeInfo(LibX11).@"struct".fields[1..]) |field| {
const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]);
@ -429,7 +434,7 @@ const LibXCursor = struct {
XcursorLibraryLoadImage: *const @TypeOf(c.XcursorLibraryLoadImage), XcursorLibraryLoadImage: *const @TypeOf(c.XcursorLibraryLoadImage),
pub fn load() !LibXCursor { pub fn load() !LibXCursor {
var lib: LibXCursor = undefined; 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| { inline for (@typeInfo(LibXCursor).@"struct".fields[1..]) |field| {
const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]);
@ -445,7 +450,7 @@ const LibXRR = struct {
XRRConfigCurrentRate: *const @TypeOf(c.XRRConfigCurrentRate), XRRConfigCurrentRate: *const @TypeOf(c.XRRConfigCurrentRate),
pub fn load() !LibXRR { pub fn load() !LibXRR {
var lib: LibXRR = undefined; 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| { inline for (@typeInfo(LibXRR).@"struct".fields[1..]) |field| {
const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); 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 Drawable = c.XID;
const Context = opaque {}; const Context = opaque {};
const FBConfig = opaque {}; const FBConfig = opaque {};
@ -474,9 +479,12 @@ const LibGL = struct {
glXMakeCurrent: *const fn (*c.Display, Drawable, ?*Context) callconv(.C) bool, glXMakeCurrent: *const fn (*c.Display, Drawable, ?*Context) callconv(.C) bool,
glXChooseVisual: *const fn (*c.Display, c_int, [*]const c_int) callconv(.C) *c.XVisualInfo, glXChooseVisual: *const fn (*c.Display, c_int, [*]const c_int) callconv(.C) *c.XVisualInfo,
glXSwapBuffers: *const fn (*c.Display, Drawable) callconv(.C) bool, glXSwapBuffers: *const fn (*c.Display, Drawable) callconv(.C) bool,
pub const lib_name = "libGL.so.1";
pub fn load() !LibGL { pub fn load() !LibGL {
var lib: LibGL = undefined; 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| { inline for (@typeInfo(LibGL).@"struct".fields[1..]) |field| {
const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); 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, handle: std.DynLib,
// xkb_context_new: *const @TypeOf(c.xkb_context_new), // 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_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), xkb_keysym_to_utf32: *const @TypeOf(c.xkb_keysym_to_utf32),
pub const lib_name = "libxkbcommon.so.0";
pub fn load() !LibXkbCommon { pub fn load() !LibXkbCommon {
var lib: LibXkbCommon = undefined; 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| { inline for (@typeInfo(LibXkbCommon).@"struct".fields[1..]) |field| {
const name = std.fmt.comptimePrint("{s}\x00", .{field.name}); const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]); 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, .resize_all => c.XC_fleur,
.not_allowed => c.XC_X_cursor, .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; if (cursor == 0) return error.FailedToCreateCursor;
return cursor; return cursor;
} }
@ -570,7 +580,7 @@ fn getCursorPos(x11: *const Native) Position {
var cursor_x: c_int = 0; var cursor_x: c_int = 0;
var cursor_y: c_int = 0; var cursor_y: c_int = 0;
var mask: c_uint = 0; var mask: c_uint = 0;
_ = x11.libx11.XQueryPointer( _ = libx11.?.XQueryPointer(
x11.display, x11.display,
x11.window, x11.window,
&root_window, &root_window,
@ -594,7 +604,7 @@ fn processEvent(window_id: mach.ObjectID, event: *c.XEvent) void {
// TODO: key repeat event // TODO: key repeat event
var keysym: c.KeySym = undefined; 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{ const key_event = KeyEvent{
.key = toMachKey(keysym), .key = toMachKey(keysym),
@ -606,7 +616,7 @@ fn processEvent(window_id: mach.ObjectID, event: *c.XEvent) void {
c.KeyPress => { c.KeyPress => {
core_ptr.pushEvent(.{ .key_press = key_event }); 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) { if (codepoint != 0) {
core_ptr.pushEvent(.{ .char_input = .{ .codepoint = @truncate(codepoint), .window_id = window_id } }); 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 // it's still responding to events
var reply = event.*; var reply = event.*;
reply.xclient.window = x11.root_window; reply.xclient.window = x11.root_window;
_ = x11.libx11.XSendEvent( _ = libx11.?.XSendEvent(
x11.display, x11.display,
x11.root_window, x11.root_window,
c.False, 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 } }); core_ptr.pushEvent(.{ .focus_lost = .{ .window_id = window_id } });
}, },
c.ResizeRequest => { c.ResizeRequest => {
_ = x11.libx11.XResizeWindow( _ = libx11.?.XResizeWindow(
x11.display, x11.display,
x11.window, x11.window,
@intCast(c.DisplayWidth(x11.display, c.DefaultScreen(x11.display))), @intCast(c.DisplayWidth(x11.display, c.DefaultScreen(x11.display))),