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",
};
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;
};
};
},
}

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,
// 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,
};

View file

@ -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))),