apprt/gtk: use some comptime to load X11 functions

This commit is contained in:
Mitchell Hashimoto
2023-12-31 08:00:43 -08:00
parent 3b7e83150f
commit a3ce446fee

View File

@ -5,13 +5,6 @@ const input = @import("../../input.zig");
const log = std.log.scoped(.gtk_x11); const log = std.log.scoped(.gtk_x11);
// X11 Function types. We load these dynamically at runtime to avoid having to
// link against X11.
const XkbQueryExtensionType = *const fn (?*c.struct__XDisplay, [*c]c_int, [*c]c_int, [*c]c_int, [*c]c_int, [*c]c_int) callconv(.C) c_int;
const XkbSelectEventDetailsType = *const fn (?*c.struct__XDisplay, c_uint, c_uint, c_ulong, c_ulong) callconv(.C) c_int;
const XEventsQueuedType = *const fn (?*c.struct__XDisplay, c_int) callconv(.C) c_int;
const XPeekEventType = *const fn (?*c.struct__XDisplay, [*c]c.union__XEvent) callconv(.C) c_int;
/// Returns true if the passed in display is an X11 display. /// Returns true if the passed in display is an X11 display.
pub fn x11_is_display(display: ?*c.GdkDisplay) bool { pub fn x11_is_display(display: ?*c.GdkDisplay) bool {
return c.g_type_check_instance_is_a( return c.g_type_check_instance_is_a(
@ -24,12 +17,7 @@ pub const X11Xkb = struct {
opcode: c_int, opcode: c_int,
base_event_code: c_int, base_event_code: c_int,
base_error_code: c_int, base_error_code: c_int,
funcs: Funcs,
// Dynamic functions
XkbQueryExtension: XkbQueryExtensionType = undefined,
XkbSelectEventDetails: XkbSelectEventDetailsType = undefined,
XEventsQueued: XEventsQueuedType = undefined,
XPeekEvent: XPeekEventType = undefined,
/// Initialize an X11Xkb struct, for the given GDK display. If the display /// Initialize an X11Xkb struct, for the given GDK display. If the display
/// isn't backed by X then this will return null. /// isn't backed by X then this will return null.
@ -43,48 +31,17 @@ pub const X11Xkb = struct {
log.debug("X11Xkb.init: initializing Xkb", .{}); log.debug("X11Xkb.init: initializing Xkb", .{});
const xdisplay = c.gdk_x11_display_get_xdisplay(display); const xdisplay = c.gdk_x11_display_get_xdisplay(display);
var result: X11Xkb = .{ .opcode = 0, .base_event_code = 0, .base_error_code = 0 }; var result: X11Xkb = .{
.opcode = 0,
// Load in the X11 calls we need. .base_event_code = 0,
log.debug(" X11Xkb.init: loading libX11.so dynamically", .{}); .base_error_code = 0,
var libX11 = try std.DynLib.open("libX11.so"); .funcs = try Funcs.init(),
defer libX11.close();
result.XkbQueryExtension = libX11.lookup(
XkbQueryExtensionType,
"XkbQueryExtension",
) orelse {
log.err("Fatal: error dynamic loading libX11: missing symbol XkbQueryExtension", .{});
return error.XkbInitializationError;
};
result.XkbSelectEventDetails = libX11.lookup(
XkbSelectEventDetailsType,
"XkbSelectEventDetails",
) orelse {
log.err("Fatal: error dynamic loading libX11: missing symbol XkbSelectEventDetails", .{});
return error.XkbInitializationError;
};
result.XEventsQueued = libX11.lookup(
XEventsQueuedType,
"XEventsQueued",
) orelse {
log.err("Fatal: error dynamic loading libX11: missing symbol XEventsQueued", .{});
return error.XkbInitializationError;
};
result.XPeekEvent = libX11.lookup(
XPeekEventType,
"XPeekEvent",
) orelse {
log.err("Fatal: error dynamic loading libX11: missing symbol XPeekEvent", .{});
return error.XkbInitializationError;
}; };
log.debug("X11Xkb.init: running XkbQueryExtension", .{}); log.debug("X11Xkb.init: running XkbQueryExtension", .{});
var major = c.XkbMajorVersion; var major = c.XkbMajorVersion;
var minor = c.XkbMinorVersion; var minor = c.XkbMinorVersion;
if (result.XkbQueryExtension( if (result.funcs.XkbQueryExtension(
xdisplay, xdisplay,
&result.opcode, &result.opcode,
&result.base_event_code, &result.base_event_code,
@ -97,7 +54,7 @@ pub const X11Xkb = struct {
} }
log.debug("X11Xkb.init: running XkbSelectEventDetails", .{}); log.debug("X11Xkb.init: running XkbSelectEventDetails", .{});
if (result.XkbSelectEventDetails( if (result.funcs.XkbSelectEventDetails(
xdisplay, xdisplay,
c.XkbUseCoreKbd, c.XkbUseCoreKbd,
c.XkbStateNotify, c.XkbStateNotify,
@ -127,10 +84,10 @@ pub const X11Xkb = struct {
// Shoutout to Mozilla for figuring out a clean way to do this, this is // Shoutout to Mozilla for figuring out a clean way to do this, this is
// paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp. // paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp.
const xdisplay = c.gdk_x11_display_get_xdisplay(display); const xdisplay = c.gdk_x11_display_get_xdisplay(display);
if (self.XEventsQueued(xdisplay, c.QueuedAfterReading) == 0) return null; if (self.funcs.XEventsQueued(xdisplay, c.QueuedAfterReading) == 0) return null;
var nextEvent: c.XEvent = undefined; var nextEvent: c.XEvent = undefined;
_ = self.XPeekEvent(xdisplay, &nextEvent); _ = self.funcs.XPeekEvent(xdisplay, &nextEvent);
if (nextEvent.type != self.base_event_code) return null; if (nextEvent.type != self.base_event_code) return null;
const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent); const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent);
@ -151,3 +108,41 @@ pub const X11Xkb = struct {
return mods; return mods;
} }
}; };
/// The functions that we load dynamically from libX11.so.
const Funcs = struct {
XkbQueryExtension: XkbQueryExtensionType,
XkbSelectEventDetails: XkbSelectEventDetailsType,
XEventsQueued: XEventsQueuedType,
XPeekEvent: XPeekEventType,
// X11 Function types. We load these dynamically at runtime to avoid having to
// link against X11.
const XkbQueryExtensionType = *const fn (?*c.struct__XDisplay, [*c]c_int, [*c]c_int, [*c]c_int, [*c]c_int, [*c]c_int) callconv(.C) c_int;
const XkbSelectEventDetailsType = *const fn (?*c.struct__XDisplay, c_uint, c_uint, c_ulong, c_ulong) callconv(.C) c_int;
const XEventsQueuedType = *const fn (?*c.struct__XDisplay, c_int) callconv(.C) c_int;
const XPeekEventType = *const fn (?*c.struct__XDisplay, [*c]c.union__XEvent) callconv(.C) c_int;
pub fn init() !Funcs {
var libX11 = try std.DynLib.open("libX11.so");
defer libX11.close();
var result: Funcs = undefined;
inline for (@typeInfo(Funcs).Struct.fields) |field| {
const name = comptime name: {
const null_term = field.name ++ .{0};
break :name null_term[0..field.name.len :0];
};
@field(result, field.name) = libX11.lookup(
field.type,
name,
) orelse {
log.err(" error dynamic loading libX11: missing symbol {s}", .{field.name});
return error.XkbInitializationError;
};
}
return result;
}
};