From e229588c8d2c51e6f571062e48957283c1b10e7f Mon Sep 17 00:00:00 2001 From: Joshua Holmes Date: Tue, 3 Dec 2024 00:27:30 -0800 Subject: [PATCH 1/6] wayland: setup with new object system --- src/core/Linux.zig | 95 ++++----- src/core/linux/Wayland.zig | 406 +++++++++++++++++++++---------------- 2 files changed, 275 insertions(+), 226 deletions(-) diff --git a/src/core/Linux.zig b/src/core/Linux.zig index a09a541495..89151f2288 100644 --- a/src/core/Linux.zig +++ b/src/core/Linux.zig @@ -59,32 +59,27 @@ const MISSING_FEATURES_X11 = [_][]const u8{ "Resizing window", "Changing display const MISSING_FEATURES_WAYLAND = [_][]const u8{ "Changing display mode", "VSync", "Setting window border/cursor" }; pub fn tick(core: *Core) !void { - _ = core; + var windows = core.windows.slice(); + while (windows.next()) |window_id| { + const native_opt: ?Native = core.windows.get(window_id, .native); + if (native_opt) |native| { + switch (native) { + .x11 => {}, // X11.tick(window_id), + .wayland => try Wayland.tick(window_id), + } + } else { + try initWindow(core, window_id); + } + } } -pub fn init( - linux: *Linux, +pub fn initWindow( core: *Core, - options: InitOptions, + window_id: mach.ObjectID, ) !void { - linux.allocator = options.allocator; - - if (!options.is_app and try wantGamemode(linux.allocator)) linux.gamemode = initLinuxGamemode(); - linux.headless = options.headless; - linux.refresh_rate = 60; // TODO: set to something meaningful - linux.vsync_mode = .triple; - linux.size = options.size; - linux.title = try options.allocator.dupeZ(u8, options.title); - linux.border = options.border; - linux.cursor_mode = .normal; - linux.cursor_shape = .arrow; - if (!options.headless) { - linux.display_mode = options.display_mode; - } - const desired_backend: BackendEnum = blk: { const backend = std.process.getEnvVarOwned( - linux.allocator, + core.allocator, "MACH_BACKEND", ) catch |err| switch (err) { error.EnvironmentVariableNotFound => { @@ -92,7 +87,7 @@ pub fn init( }, else => return err, }; - defer linux.allocator.free(backend); + defer core.allocator.free(backend); if (std.ascii.eqlIgnoreCase(backend, "x11")) break :blk .x11; if (std.ascii.eqlIgnoreCase(backend, "wayland")) break :blk .wayland; @@ -102,18 +97,19 @@ pub fn init( // Try to initialize the desired backend, falling back to the other if that one is not supported switch (desired_backend) { .x11 => { - X11.init(linux, core, options) catch |err| { - const err_msg = switch (err) { - error.LibraryNotFound => "Missing X11 library", - error.FailedToConnectToDisplay => "Failed to connect to X11 display", - else => "An unknown error occured while trying to connect to X11", - }; - log.err("{s}\n\nFalling back to Wayland\n", .{err_msg}); - try Wayland.init(linux, core, options); - }; + return; + // X11.initWindow(core, window_id) catch |err| { + // const err_msg = switch (err) { + // error.LibraryNotFound => "Missing X11 library", + // error.FailedToConnectToDisplay => "Failed to connect to X11 display", + // 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 => { - Wayland.init(linux, core, options) catch |err| { + Wayland.initWindow(core, window_id) catch |err| { const err_msg = switch (err) { error.NoServerSideDecorationSupport => "Server Side Decorations aren't supported", error.LibraryNotFound => "Missing Wayland library", @@ -121,35 +117,26 @@ pub fn init( else => "An unknown error occured while trying to connect to Wayland", }; log.err("{s}\n\nFalling back to X11\n", .{err_msg}); - try X11.init(linux, core, options); + // try X11.initWindow(core, window_id); }; }, } - switch (linux.backend) { - .wayland => |be| { - linux.surface_descriptor = .{ .next_in_chain = .{ .from_wayland_surface = be.surface_descriptor } }; - }, - .x11 => |be| { - linux.surface_descriptor = .{ .next_in_chain = .{ .from_xlib_window = be.surface_descriptor } }; - }, - } - // warn about incomplete features // TODO: remove this when linux is not missing major features - try warnAboutIncompleteFeatures(linux.backend, &MISSING_FEATURES_X11, &MISSING_FEATURES_WAYLAND, options.allocator); -} - -pub fn deinit(linux: *Linux) void { - if (linux.gamemode != null and linux.gamemode.?) deinitLinuxGamemode(); - - linux.allocator.free(linux.title); - - switch (linux.backend) { - .wayland => linux.backend.wayland.deinit(linux), - .x11 => linux.backend.x11.deinit(linux), - } -} + try warnAboutIncompleteFeatures(desired_backend, &MISSING_FEATURES_X11, &MISSING_FEATURES_WAYLAND, core.allocator); +} + +// pub fn deinit(linux: *Linux) void { +// if (linux.gamemode != null and linux.gamemode.?) deinitLinuxGamemode(); +// +// linux.allocator.free(linux.title); +// +// switch (linux.backend) { +// .wayland => linux.backend.wayland.deinit(linux), +// .x11 => linux.backend.x11.deinit(linux), +// } +// } pub fn update(linux: *Linux) !void { switch (linux.backend) { diff --git a/src/core/linux/Wayland.zig b/src/core/linux/Wayland.zig index a75533ea02..42972207b3 100644 --- a/src/core/linux/Wayland.zig +++ b/src/core/linux/Wayland.zig @@ -25,66 +25,69 @@ 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_global: LibWaylandClient = undefined; +var libwaylandclient: LibWaylandClient = undefined; + +// This does not need to be declared here, but we are declaring it here to be consistent +// with `libwaylandclient`. +var libxkbcommon: LibXkbCommon = undefined; + +var core_ptr: *Core = undefined; // These exported functions are defined because the wayland headers don't define them, -// and then the linker gets confused. They reference undefined `libwaylandclient_global` at -// compile time, but since they are not run until run time, after `libwaylandclient_global` is +// and then the linker gets confused. They reference undefined `libwaylandclient` at +// 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_global.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_global.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_global.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_global.wl_proxy_destroy, .{proxy}); + return @call(.always_tail, libwaylandclient.wl_proxy_destroy, .{proxy}); } -core: *Core, -surface_descriptor: *gpu.Surface.DescriptorFromWaylandSurface, -configured: bool = false, - -display: *c.wl_display, -surface: *c.wl_surface, -toplevel: *c.xdg_toplevel, -interfaces: Interfaces, -libwaylandclient: LibWaylandClient, - -// input stuff -keyboard: ?*c.wl_keyboard = null, -pointer: ?*c.wl_pointer = null, - -// keyboard stuff -xkb_context: ?*c.xkb_context = null, -xkb_state: ?*c.xkb_state = null, -compose_state: ?*c.xkb_compose_state = null, -keymap: ?*c.xkb_keymap = null, -libxkbcommon: LibXkbCommon, -modifiers: Core.KeyMods, -modifier_indices: KeyModInd, - -pub const Native = struct {}; - -pub fn init( - linux: *Linux, +pub const Native = struct { + surface_descriptor: gpu.Surface.DescriptorFromWaylandSurface, + configured: bool = false, + + display: *c.wl_display, + surface: *c.wl_surface, + toplevel: *c.xdg_toplevel, + interfaces: Interfaces, + + // input stuff + keyboard: ?*c.wl_keyboard = null, + pointer: ?*c.wl_pointer = null, + + // keyboard stuff + xkb_context: *c.xkb_context, + xkb_state: ?*c.xkb_state = null, + compose_state: ?*c.xkb_compose_state = null, + keymap: ?*c.xkb_keymap = null, + modifiers: Core.KeyMods, + modifier_indices: KeyModInd, +}; + +pub fn initWindow( core: *Core, - options: InitOptions, + window_id: mach.ObjectID, ) !void { - libwaylandclient_global = try LibWaylandClient.load(); - linux.backend = .{ - .wayland = Wayland{ - .core = core, - .libxkbcommon = try LibXkbCommon.load(), - .libwaylandclient = libwaylandclient_global, + core_ptr = core; + var core_window = core.windows.getValue(window_id); + libwaylandclient = try LibWaylandClient.load(); + libxkbcommon = try LibXkbCommon.load(); + + core_window.native = .{ + .wayland = .{ .interfaces = Interfaces{}, - .display = libwaylandclient_global.wl_display_connect(null) orelse return error.FailedToConnectToDisplay, + .display = libwaylandclient.wl_display_connect(null) orelse return error.FailedToConnectToDisplay, .modifiers = .{ .alt = false, .caps_lock = false, @@ -101,33 +104,44 @@ pub fn init( .caps_lock_index = undefined, .num_lock_index = undefined, }, + // These undefined values require the initialization of `.interfaces` which happens in the registry listener .surface_descriptor = undefined, .surface = undefined, .toplevel = undefined, + .xkb_context = libxkbcommon.xkb_context_new(0) orelse return error.FailedToGetXkbContext, }, }; - var wl = &linux.backend.wayland; - wl.xkb_context = wl.libxkbcommon.xkb_context_new(0) orelse return error.FailedToGetXkbContext; + + // Save so that the new `.native` value is accessible from the registry listener + core.windows.setValue(window_id, core_window); + var wl = &core_window.native.?.wayland; + const registry = c.wl_display_get_registry(wl.display) orelse return error.FailedToGetDisplayRegistry; - // TODO: handle error return value here - _ = c.wl_registry_add_listener(registry, ®istry_listener.listener, linux); + if (c.wl_registry_add_listener(registry, ®istry_listener.listener, @ptrFromInt(window_id)) != 0) { + return error.ListenerHasAlreadyBeenSet; + } + + // 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); - //Round trip to get all the registry objects - _ = wl.libwaylandclient.wl_display_roundtrip(wl.display); + // Round trip to get all initial output events + _ = libwaylandclient.wl_display_roundtrip(wl.display); - //Round trip to get all initial output events - _ = wl.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); + wl = &core_window.native.?.wayland; if (wl.interfaces.zxdg_decoration_manager_v1 == null) { return error.NoServerSideDecorationSupport; } - //Setup surface + // Setup surface wl.surface = c.wl_compositor_create_surface(wl.interfaces.wl_compositor) orelse return error.UnableToCreateSurface; - wl.surface_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromWaylandSurface); - wl.surface_descriptor.* = .{ .display = wl.display, .surface = wl.surface }; + wl.surface_descriptor = .{ .display = wl.display, .surface = wl.surface }; + // Setup opaque region { const region = c.wl_compositor_create_region(wl.interfaces.wl_compositor) orelse return error.CouldntCreateWaylandRegtion; @@ -135,8 +149,8 @@ pub fn init( region, 0, 0, - @intCast(linux.size.width), - @intCast(linux.size.height), + @intCast(core_window.width), + @intCast(core_window.height), ); c.wl_surface_set_opaque_region(wl.surface, region); c.wl_region_destroy(region); @@ -145,20 +159,36 @@ pub fn init( const xdg_surface = c.xdg_wm_base_get_xdg_surface(wl.interfaces.xdg_wm_base, wl.surface) orelse return error.UnableToCreateXdgSurface; wl.toplevel = c.xdg_surface_get_toplevel(xdg_surface) orelse return error.UnableToGetXdgTopLevel; - // TODO: handle this return value - _ = c.xdg_surface_add_listener(xdg_surface, &xdg_surface_listener.listener, linux); + // Save so that xdg listeners can use the `wl.surface` + core_ptr.windows.setValue(window_id, core_window); + + if (c.xdg_surface_add_listener(xdg_surface, &xdg_surface_listener.listener, @ptrFromInt(window_id)) != 0) { + return error.ListenerHasAlreadyBeenSet; + } + + if (c.xdg_toplevel_add_listener(wl.toplevel, &xdg_toplevel_listener.listener, @ptrFromInt(window_id)) != 0) { + return error.ListenerHasAlreadyBeenSet; + } + + // Wait for events to get pushed + _ = libwaylandclient.wl_display_roundtrip(wl.display); - // TODO: handle this return value - _ = c.xdg_toplevel_add_listener(wl.toplevel, &xdg_toplevel_listener.listener, linux); + core_window = core.windows.getValue(window_id); + wl = &core_window.native.?.wayland; // Commit changes to surface c.wl_surface_commit(wl.surface); - while (wl.libwaylandclient.wl_display_dispatch(wl.display) != -1 and !wl.configured) { - // This space intentionally left blank + while (true) { + const result = libwaylandclient.wl_display_dispatch(wl.display); + + core_window = core.windows.getValue(window_id); + wl = &core_window.native.?.wayland; + + if (result != -1 and wl.configured) break; } - c.xdg_toplevel_set_title(wl.toplevel, options.title); + c.xdg_toplevel_set_title(wl.toplevel, @ptrCast(core_window.title)); const decoration = c.zxdg_decoration_manager_v1_get_toplevel_decoration( wl.interfaces.zxdg_decoration_manager_v1, @@ -169,28 +199,30 @@ pub fn init( // Commit changes to surface c.wl_surface_commit(wl.surface); - // TODO: handle return value - _ = wl.libwaylandclient.wl_display_roundtrip(wl.display); -} -pub fn deinit( - wl: *Wayland, - linux: *Linux, -) void { - linux.allocator.destroy(wl.surface_descriptor); + _ = libwaylandclient.wl_display_roundtrip(wl.display); + + core.windows.setValue(window_id, core_window); } -pub fn update(wl: *Wayland, linux: *Linux) !void { - _ = linux; +// pub fn deinit( +// wl: *Wayland, +// linux: *Linux, +// ) void { +// linux.allocator.destroy(wl.surface_descriptor); +// } + +pub fn tick(window_id: mach.ObjectID) !void { + const wl = &core_ptr.windows.getValue(window_id).native.?.wayland; - while (wl.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 = wl.libwaylandclient.wl_display_get_fd(wl.display), + .fd = libwaylandclient.wl_display_get_fd(wl.display), .events = std.posix.POLL.OUT, .revents = 0, }, @@ -204,9 +236,7 @@ pub fn update(wl: *Wayland, linux: *Linux) !void { } } - _ = wl.libwaylandclient.wl_display_roundtrip(wl.display); - - wl.core.input.tick(); + _ = libwaylandclient.wl_display_roundtrip(wl.display); } pub fn setTitle(wl: *Wayland, title: [:0]const u8) void { @@ -259,6 +289,7 @@ const LibWaylandClient = struct { wl_display_connect: *const @TypeOf(c.wl_display_connect), wl_display_roundtrip: *const @TypeOf(c.wl_display_roundtrip), wl_display_dispatch: *const @TypeOf(c.wl_display_dispatch), + wl_display_dispatch_pending: *const @TypeOf(c.wl_display_dispatch_pending), wl_display_flush: *const @TypeOf(c.wl_display_flush), wl_display_get_fd: *const @TypeOf(c.wl_display_get_fd), wl_proxy_add_listener: *const @TypeOf(c.wl_proxy_add_listener), @@ -326,43 +357,47 @@ const KeyModInd = struct { }; const registry_listener = struct { - fn registryHandleGlobal(linux: *Linux, registry: ?*c.struct_wl_registry, name: u32, interface_ptr: [*:0]const u8, version: u32) callconv(.C) void { + fn registryHandleGlobal(window_id: mach.ObjectID, registry: ?*c.struct_wl_registry, name: u32, interface_ptr: [*:0]const u8, version: u32) callconv(.C) void { const interface = std.mem.span(interface_ptr); - const wl = &linux.backend.wayland; + var core_window = core_ptr.windows.getValue(window_id); + const wl = &core_window.native.?.wayland; if (std.mem.eql(u8, "wl_compositor", interface)) { wl.interfaces.wl_compositor = @ptrCast(c.wl_registry_bind( registry, name, - wl.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)) { + // TODO: Remove this binding because we aren't using wl_shm wl.interfaces.wl_subcompositor = @ptrCast(c.wl_registry_bind( registry, name, - wl.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)) { + // TODO: Remove this binding because we aren't using wl_shm wl.interfaces.wl_shm = @ptrCast(c.wl_registry_bind( registry, name, - wl.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)) { + // TODO: Determine if this is being used. If it is, make a comment here wl.interfaces.wl_output = @ptrCast(c.wl_registry_bind( registry, name, - wl.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, - // wl.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)) { @@ -374,7 +409,7 @@ const registry_listener = struct { ) orelse @panic("uh idk how to proceed")); // TODO: handle return value - _ = c.xdg_wm_base_add_listener(wl.interfaces.xdg_wm_base, &xdg_wm_base_listener.listener, linux); + _ = c.xdg_wm_base_add_listener(wl.interfaces.xdg_wm_base, &xdg_wm_base_listener.listener, @ptrFromInt(window_id)); } else if (std.mem.eql(u8, "zxdg_decoration_manager_v1", interface)) { wl.interfaces.zxdg_decoration_manager_v1 = @ptrCast(c.wl_registry_bind( registry, @@ -386,17 +421,21 @@ const registry_listener = struct { wl.interfaces.wl_seat = @ptrCast(c.wl_registry_bind( registry, name, - wl.libwaylandclient.wl_seat_interface, + libwaylandclient.wl_seat_interface, @min(3, version), ) orelse @panic("uh idk how to proceed")); // TODO: handle return value - _ = c.wl_seat_add_listener(wl.interfaces.wl_seat, &seat_listener.listener, linux); + _ = c.wl_seat_add_listener(wl.interfaces.wl_seat, &seat_listener.listener, @ptrFromInt(window_id)); + } else { + // No changes made to `wl`, so exit function + return; } + core_ptr.windows.setValue(window_id, core_window); } - fn registryHandleGlobalRemove(linux: *Linux, registry: ?*c.struct_wl_registry, name: u32) callconv(.C) void { - _ = linux; + fn registryHandleGlobalRemove(window_id: mach.ObjectID, registry: ?*c.struct_wl_registry, name: u32) callconv(.C) void { + _ = window_id; _ = registry; _ = name; } @@ -410,9 +449,10 @@ const registry_listener = struct { }; const keyboard_listener = struct { - fn keyboardHandleKeymap(linux: *Linux, keyboard: ?*c.struct_wl_keyboard, format: u32, fd: i32, keymap_size: u32) callconv(.C) void { + fn keyboardHandleKeymap(window_id: mach.ObjectID, keyboard: ?*c.struct_wl_keyboard, format: u32, fd: i32, keymap_size: u32) callconv(.C) void { _ = keyboard; - const wl = &linux.backend.wayland; + var core_window = core_ptr.windows.getValue(window_id); + const wl = &core_window.native.?.wayland; if (format != c.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { @panic("TODO"); @@ -420,7 +460,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 = wl.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, @@ -433,13 +473,13 @@ const keyboard_listener = struct { std.posix.close(fd); //Release reference to old state and create new state - wl.libxkbcommon.xkb_state_unref(wl.xkb_state); - const state = wl.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 = wl.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, @@ -447,85 +487,86 @@ const keyboard_listener = struct { //If creation failed, lets try the C locale if (compose_table == null) - compose_table = wl.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 wl.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 = wl.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 = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Control"); - wl.modifier_indices.alt_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod1"); - wl.modifier_indices.shift_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Shift"); - wl.modifier_indices.super_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod4"); - wl.modifier_indices.caps_lock_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Lock"); - wl.modifier_indices.num_lock_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod2"); + core_ptr.windows.setValue(window_id, core_window); } - fn keyboardHandleEnter(linux: *Linux, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface, keys: [*c]c.struct_wl_array) callconv(.C) void { + fn keyboardHandleEnter(window_id: mach.ObjectID, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface, keys: [*c]c.struct_wl_array) callconv(.C) void { _ = keyboard; _ = serial; _ = surface; _ = keys; - const wl = &linux.backend.wayland; - wl.core.pushEvent(.focus_gained); + core_ptr.pushEvent(.{ .focus_gained = .{ .window_id = window_id } }); } - fn keyboardHandleLeave(linux: *Linux, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void { + fn keyboardHandleLeave(window_id: mach.ObjectID, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void { _ = keyboard; _ = serial; _ = surface; - const wl = &linux.backend.wayland; - wl.core.pushEvent(.focus_lost); + core_ptr.pushEvent(.{ .focus_lost = .{ .window_id = window_id } }); } - fn keyboardHandleKey(linux: *Linux, keyboard: ?*c.struct_wl_keyboard, serial: u32, time: u32, scancode: u32, state: u32) callconv(.C) void { + fn keyboardHandleKey(window_id: mach.ObjectID, keyboard: ?*c.struct_wl_keyboard, serial: u32, time: u32, scancode: u32, state: u32) callconv(.C) void { _ = keyboard; _ = serial; _ = time; - var wl = &linux.backend.wayland; + const wl = &core_ptr.windows.getValue(window_id).native.?.wayland; const pressed = state == 1; - const key_event = KeyEvent{ .key = toMachKey(scancode), .mods = wl.modifiers }; + const key_event = KeyEvent{ .key = toMachKey(scancode), .mods = wl.modifiers, .window_id = window_id }; if (pressed) { - wl.core.pushEvent(.{ .key_press = key_event }); + core_ptr.pushEvent(.{ .key_press = key_event }); var keysyms: ?[*]c.xkb_keysym_t = undefined; //Get the keysym from the keycode (scancode + 8) - if (wl.libxkbcommon.xkb_state_key_get_syms(wl.xkb_state, scancode + 8, &keysyms) == 1) { + 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 = wl.libxkbcommon.xkb_keysym_to_utf32(keysym); + const codepoint = libxkbcommon.xkb_keysym_to_utf32(keysym); if (codepoint != 0) { - wl.core.pushEvent(Core.Event{ .char_input = .{ .codepoint = @truncate(codepoint) } }); + core_ptr.pushEvent(.{ .char_input = .{ .codepoint = @truncate(codepoint), .window_id = window_id } }); } } } else { - wl.core.pushEvent(.{ .key_release = key_event }); + core_ptr.pushEvent(.{ .key_release = key_event }); } } - fn keyboardHandleModifiers(linux: *Linux, keyboard: ?*c.struct_wl_keyboard, serial: u32, mods_depressed: u32, mods_latched: u32, mods_locked: u32, group: u32) callconv(.C) void { + fn keyboardHandleModifiers(window_id: mach.ObjectID, keyboard: ?*c.struct_wl_keyboard, serial: u32, mods_depressed: u32, mods_latched: u32, mods_locked: u32, group: u32) callconv(.C) void { _ = keyboard; _ = serial; - var wl = &linux.backend.wayland; + var core_window = core_ptr.windows.getValue(window_id); + const wl = &core_window.native.?.wayland; if (wl.keymap == null) return; // TODO: handle this return value - _ = wl.libxkbcommon.xkb_state_update_mask( + _ = libxkbcommon.xkb_state_update_mask( wl.xkb_state.?, mods_depressed, mods_latched, @@ -544,16 +585,18 @@ 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]) = wl.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, ) == 1; } + + core_ptr.windows.setValue(window_id, core_window); } - fn keyboardHandleRepeatInfo(linux: *Linux, keyboard: ?*c.struct_wl_keyboard, rate: i32, delay: i32) callconv(.C) void { - _ = linux; + fn keyboardHandleRepeatInfo(window_id: mach.ObjectID, keyboard: ?*c.struct_wl_keyboard, rate: i32, delay: i32) callconv(.C) void { + _ = window_id; _ = keyboard; _ = rate; _ = delay; @@ -570,102 +613,103 @@ const keyboard_listener = struct { }; const pointer_listener = struct { - fn handlePointerAxis(linux: *Linux, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32, value: c.wl_fixed_t) callconv(.C) void { - _ = linux; + fn handlePointerAxis(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32, value: c.wl_fixed_t) callconv(.C) void { + _ = window_id; _ = pointer; _ = time; _ = axis; _ = value; } - fn handlePointerFrame(linux: *Linux, pointer: ?*c.struct_wl_pointer) callconv(.C) void { - _ = linux; + fn handlePointerFrame(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer) callconv(.C) void { + _ = window_id; _ = pointer; } - fn handlePointerAxisSource(linux: *Linux, pointer: ?*c.struct_wl_pointer, axis_source: u32) callconv(.C) void { - _ = linux; + fn handlePointerAxisSource(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, axis_source: u32) callconv(.C) void { + _ = window_id; _ = pointer; _ = axis_source; } - fn handlePointerAxisStop(linux: *Linux, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32) callconv(.C) void { - _ = linux; + fn handlePointerAxisStop(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32) callconv(.C) void { + _ = window_id; _ = pointer; _ = time; _ = axis; } - fn handlePointerAxisDiscrete(linux: *Linux, pointer: ?*c.struct_wl_pointer, axis: u32, discrete: i32) callconv(.C) void { - _ = linux; + fn handlePointerAxisDiscrete(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, axis: u32, discrete: i32) callconv(.C) void { + _ = window_id; _ = pointer; _ = axis; _ = discrete; } - fn handlePointerAxisValue120(linux: *Linux, pointer: ?*c.struct_wl_pointer, axis: u32, value_120: i32) callconv(.C) void { - _ = linux; + fn handlePointerAxisValue120(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, axis: u32, value_120: i32) callconv(.C) void { + _ = window_id; _ = pointer; _ = axis; _ = value_120; } - fn handlePointerAxisRelativeDirection(linux: *Linux, pointer: ?*c.struct_wl_pointer, axis: u32, direction: u32) callconv(.C) void { - _ = linux; + fn handlePointerAxisRelativeDirection(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, axis: u32, direction: u32) callconv(.C) void { + _ = window_id; _ = pointer; _ = axis; _ = direction; } - fn handlePointerEnter(linux: *Linux, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void { + fn handlePointerEnter(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void { _ = fixed_x; _ = fixed_y; - _ = linux; + _ = window_id; _ = pointer; _ = serial; _ = surface; } - fn handlePointerLeave(linux: *Linux, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void { - _ = linux; + fn handlePointerLeave(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void { + _ = window_id; _ = pointer; _ = serial; _ = surface; } - fn handlePointerMotion(linux: *Linux, pointer: ?*c.struct_wl_pointer, serial: u32, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void { + fn handlePointerMotion(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, serial: u32, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void { _ = pointer; _ = serial; - var wl = &linux.backend.wayland; const x = c.wl_fixed_to_double(fixed_x); const y = c.wl_fixed_to_double(fixed_y); - wl.core.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y } } }); + core_ptr.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y }, .window_id = window_id } }); } - fn handlePointerButton(linux: *Linux, pointer: ?*c.struct_wl_pointer, serial: u32, time: u32, button: u32, state: u32) callconv(.C) void { + fn handlePointerButton(window_id: mach.ObjectID, pointer: ?*c.struct_wl_pointer, serial: u32, time: u32, button: u32, state: u32) callconv(.C) void { _ = pointer; _ = serial; _ = time; - var wl = &linux.backend.wayland; + const wl = &core_ptr.windows.getValue(window_id).native.?.wayland; const mouse_button: Core.MouseButton = @enumFromInt(button - c.BTN_LEFT); const pressed = state == c.WL_POINTER_BUTTON_STATE_PRESSED; - const x = wl.core.input_state.mouse_position.x; - const y = wl.core.input_state.mouse_position.y; + const x = core_ptr.input_state.mouse_position.x; + const y = core_ptr.input_state.mouse_position.y; if (pressed) { - wl.core.pushEvent(Core.Event{ .mouse_press = .{ + core_ptr.pushEvent(Core.Event{ .mouse_press = .{ .button = mouse_button, .mods = wl.modifiers, .pos = .{ .x = x, .y = y }, + .window_id = window_id, } }); } else { - wl.core.pushEvent(Core.Event{ .mouse_release = .{ + core_ptr.pushEvent(Core.Event{ .mouse_release = .{ .button = mouse_button, .mods = wl.modifiers, .pos = .{ .x = x, .y = y }, + .window_id = window_id, } }); } } @@ -686,20 +730,23 @@ const pointer_listener = struct { }; const seat_listener = struct { - fn seatHandleName(linux: *Linux, seat: ?*c.struct_wl_seat, name_ptr: [*:0]const u8) callconv(.C) void { - _ = linux; + fn seatHandleName(window_id: mach.ObjectID, seat: ?*c.struct_wl_seat, name_ptr: [*:0]const u8) callconv(.C) void { + _ = window_id; _ = seat; _ = name_ptr; } - fn seatHandleCapabilities(linux: *Linux, seat: ?*c.struct_wl_seat, caps: c.wl_seat_capability) callconv(.C) void { - const wl = &linux.backend.wayland; + fn seatHandleCapabilities(window_id: mach.ObjectID, seat: ?*c.struct_wl_seat, caps: c.wl_seat_capability) callconv(.C) void { + var core_window = core_ptr.windows.getValue(window_id); + const wl = &core_window.native.?.wayland; + var changed = false; if ((caps & c.WL_SEAT_CAPABILITY_KEYBOARD) != 0) { + changed = true; wl.keyboard = c.wl_seat_get_keyboard(seat); // TODO: handle return value - _ = c.wl_keyboard_add_listener(wl.keyboard, &keyboard_listener.listener, linux); + _ = c.wl_keyboard_add_listener(wl.keyboard, &keyboard_listener.listener, @ptrFromInt(window_id)); } if ((caps & c.WL_SEAT_CAPABILITY_TOUCH) != 0) { @@ -707,15 +754,17 @@ const seat_listener = struct { } if ((caps & c.WL_SEAT_CAPABILITY_POINTER) != 0) { + changed = true; wl.pointer = c.wl_seat_get_pointer(seat); // TODO: handle return value - _ = c.wl_pointer_add_listener(wl.pointer, &pointer_listener.listener, linux); + _ = c.wl_pointer_add_listener(wl.pointer, &pointer_listener.listener, @ptrFromInt(window_id)); } // Delete keyboard if its no longer in the seat if (wl.keyboard) |keyboard| { if ((caps & c.WL_SEAT_CAPABILITY_KEYBOARD) == 0) { + changed = true; c.wl_keyboard_destroy(keyboard); wl.keyboard = null; } @@ -723,10 +772,15 @@ const seat_listener = struct { if (wl.pointer) |pointer| { if ((caps & c.WL_SEAT_CAPABILITY_POINTER) == 0) { + changed = true; c.wl_pointer_destroy(pointer); wl.pointer = null; } } + + if (changed) { + core_ptr.windows.setValue(window_id, core_window); + } } const listener = c.wl_seat_listener{ @@ -736,8 +790,8 @@ const seat_listener = struct { }; const xdg_wm_base_listener = struct { - fn wmBaseHandlePing(linux: *Linux, wm_base: ?*c.struct_xdg_wm_base, serial: u32) callconv(.C) void { - _ = linux; + fn wmBaseHandlePing(window_id: mach.ObjectID, wm_base: ?*c.struct_xdg_wm_base, serial: u32) callconv(.C) void { + _ = window_id; c.xdg_wm_base_pong(wm_base, serial); } @@ -745,34 +799,41 @@ const xdg_wm_base_listener = struct { }; const xdg_surface_listener = struct { - fn xdgSurfaceHandleConfigure(linux: *Linux, xdg_surface: ?*c.struct_xdg_surface, serial: u32) callconv(.C) void { + fn xdgSurfaceHandleConfigure(window_id: mach.ObjectID, xdg_surface: ?*c.struct_xdg_surface, serial: u32) callconv(.C) void { c.xdg_surface_ack_configure(xdg_surface, serial); - var wl = &linux.backend.wayland; + var core_window = core_ptr.windows.getValue(window_id); + const wl = &core_window.native.?.wayland; if (wl.configured) { c.wl_surface_commit(wl.surface); } else { wl.configured = true; + core_ptr.windows.setValue(window_id, core_window); + core_window = core_ptr.windows.getValue(window_id); } - setContentAreaOpaque(wl, linux.size); + setContentAreaOpaque(wl, Core.Size{ .width = core_window.width, .height = core_window.height }); } const listener = c.xdg_surface_listener{ .configure = @ptrCast(&xdgSurfaceHandleConfigure) }; }; const xdg_toplevel_listener = struct { - fn xdgToplevelHandleClose(linux: *Linux, toplevel: ?*c.struct_xdg_toplevel) callconv(.C) void { - _ = linux; + fn xdgToplevelHandleClose(window_id: mach.ObjectID, toplevel: ?*c.struct_xdg_toplevel) callconv(.C) void { + // TODO: implement this + _ = window_id; _ = toplevel; } - fn xdgToplevelHandleConfigure(linux: *Linux, toplevel: ?*c.struct_xdg_toplevel, width: i32, height: i32, states: [*c]c.struct_wl_array) callconv(.C) void { + fn xdgToplevelHandleConfigure(window_id: mach.ObjectID, toplevel: ?*c.struct_xdg_toplevel, width: i32, height: i32, states: [*c]c.struct_wl_array) callconv(.C) void { + var core_window = core_ptr.windows.getValue(window_id); _ = toplevel; _ = states; if (width > 0 and height > 0) { - linux.size = .{ .width = @intCast(width), .height = @intCast(height) }; + core_window.width = @intCast(width); + core_window.height = @intCast(height); + core_ptr.windows.setValue(window_id, core_window); } } @@ -782,15 +843,15 @@ const xdg_toplevel_listener = struct { }; }; -fn composeSymbol(wl: *Wayland, sym: c.xkb_keysym_t) c.xkb_keysym_t { +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 (wl.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 (wl.libxkbcommon.xkb_compose_state_get_status(wl.compose_state)) { - c.XKB_COMPOSE_COMPOSED => wl.libxkbcommon.xkb_compose_state_get_one_sym(wl.compose_state), + 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, }; @@ -918,12 +979,13 @@ fn toMachKey(key: u32) Core.Key { }; } -fn setContentAreaOpaque(wl: *Wayland, new_size: Core.Size) void { +fn setContentAreaOpaque(wl: *const Native, new_size: Core.Size) void { const region = c.wl_compositor_create_region(wl.interfaces.wl_compositor) orelse return; c.wl_region_add(region, 0, 0, @intCast(new_size.width), @intCast(new_size.height)); c.wl_surface_set_opaque_region(wl.surface, region); c.wl_region_destroy(region); - wl.core.swap_chain_update.set(); + // FIX: What is the Mach Object System way of doing this? + // core_ptr.swap_chain_update.set(); } From 33370aabe8348b90dd411f5d30bcba64c24fe5e3 Mon Sep 17 00:00:00 2001 From: Joshua Holmes Date: Sat, 7 Dec 2024 18:57:36 +0000 Subject: [PATCH 2/6] linux: implement better logging for when wayland fails or x11 is desired --- src/core/Linux.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/Linux.zig b/src/core/Linux.zig index 89151f2288..706c95454c 100644 --- a/src/core/Linux.zig +++ b/src/core/Linux.zig @@ -97,7 +97,8 @@ pub fn initWindow( // Try to initialize the desired backend, falling back to the other if that one is not supported switch (desired_backend) { .x11 => { - return; + log.err("\nX11 needs to be setup to work with the new object system, so it is not working at the moment. Using Wayland.\n", .{}); + try Wayland.initWindow(core, window_id); // X11.initWindow(core, window_id) catch |err| { // const err_msg = switch (err) { // error.LibraryNotFound => "Missing X11 library", @@ -116,7 +117,9 @@ pub fn initWindow( error.FailedToConnectToDisplay => "Failed to connect to Wayland display", 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\nCannot connect to Wayland. X11 is unavailable as a fallback while it is being reconfigured to work with the new object system. Failing...\n", .{err_msg}); + return error.X11NotImplemented; + // log.err("{s}\n\nFalling back to X11\n", .{err_msg}); // try X11.initWindow(core, window_id); }; }, From c643f140340382f20b21a8dc8eea982120eb2652 Mon Sep 17 00:00:00 2001 From: Joshua Holmes Date: Sat, 14 Dec 2024 22:06:07 +0000 Subject: [PATCH 3/6] wayland: fix window initialization --- src/core/linux/Wayland.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/linux/Wayland.zig b/src/core/linux/Wayland.zig index 42972207b3..8da93b8d09 100644 --- a/src/core/linux/Wayland.zig +++ b/src/core/linux/Wayland.zig @@ -140,6 +140,9 @@ pub fn initWindow( // Setup surface wl.surface = c.wl_compositor_create_surface(wl.interfaces.wl_compositor) orelse return error.UnableToCreateSurface; wl.surface_descriptor = .{ .display = wl.display, .surface = wl.surface }; + core_window.surface_descriptor = .{ .next_in_chain = .{ + .from_wayland_surface = &wl.surface_descriptor, + } }; // Setup opaque region { @@ -203,6 +206,7 @@ pub fn initWindow( _ = libwaylandclient.wl_display_roundtrip(wl.display); core.windows.setValue(window_id, core_window); + try core.initWindow(window_id); } // pub fn deinit( From 5d4f0d6746fb2a2cbb33bb8246b70056118f612c Mon Sep 17 00:00:00 2001 From: Joshua Holmes Date: Fri, 20 Dec 2024 06:30:48 +0000 Subject: [PATCH 4/6] x11: get x11 to build and run --- src/core/Linux.zig | 30 ++-- src/core/linux/X11.zig | 301 ++++++++++++++++++++++------------------- 2 files changed, 179 insertions(+), 152 deletions(-) diff --git a/src/core/Linux.zig b/src/core/Linux.zig index 706c95454c..f9901cb8ff 100644 --- a/src/core/Linux.zig +++ b/src/core/Linux.zig @@ -63,8 +63,9 @@ pub fn tick(core: *Core) !void { while (windows.next()) |window_id| { const native_opt: ?Native = core.windows.get(window_id, .native); if (native_opt) |native| { + // check for display server events switch (native) { - .x11 => {}, // X11.tick(window_id), + .x11 => try X11.tick(window_id), .wayland => try Wayland.tick(window_id), } } else { @@ -83,6 +84,7 @@ pub fn initWindow( "MACH_BACKEND", ) catch |err| switch (err) { error.EnvironmentVariableNotFound => { + // default backend break :blk .wayland; }, else => return err, @@ -97,17 +99,15 @@ pub fn initWindow( // Try to initialize the desired backend, falling back to the other if that one is not supported switch (desired_backend) { .x11 => { - log.err("\nX11 needs to be setup to work with the new object system, so it is not working at the moment. Using Wayland.\n", .{}); - try Wayland.initWindow(core, window_id); - // X11.initWindow(core, window_id) catch |err| { - // const err_msg = switch (err) { - // error.LibraryNotFound => "Missing X11 library", - // error.FailedToConnectToDisplay => "Failed to connect to X11 display", - // 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); - // }; + X11.initWindow(core, window_id) catch |err| { + const err_msg = switch (err) { + error.LibraryNotFound => "Missing X11 library", + error.FailedToConnectToDisplay => "Failed to connect to X11 display", + 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 => { Wayland.initWindow(core, window_id) catch |err| { @@ -117,10 +117,8 @@ pub fn initWindow( error.FailedToConnectToDisplay => "Failed to connect to Wayland display", else => "An unknown error occured while trying to connect to Wayland", }; - log.err("{s}\n\nCannot connect to Wayland. X11 is unavailable as a fallback while it is being reconfigured to work with the new object system. Failing...\n", .{err_msg}); - return error.X11NotImplemented; - // log.err("{s}\n\nFalling back to X11\n", .{err_msg}); - // try X11.initWindow(core, window_id); + log.err("{s}\n\nFalling back to X11\n", .{err_msg}); + try X11.initWindow(core, window_id); }; }, } diff --git a/src/core/linux/X11.zig b/src/core/linux/X11.zig index b19510f5e9..57f1d6a55b 100644 --- a/src/core/linux/X11.zig +++ b/src/core/linux/X11.zig @@ -30,44 +30,43 @@ const log = std.log.scoped(.mach); pub const defaultLog = std.log.defaultLog; pub const defaultPanic = std.debug.panicImpl; -pub const X11 = @This(); - -allocator: std.mem.Allocator, -core: *Core, - -libx11: LibX11, -libxrr: ?LibXRR, -libgl: ?LibGL, -libxcursor: ?LibXCursor, -libxkbcommon: LibXkbCommon, -gl_ctx: ?*LibGL.Context, -display: *c.Display, -empty_event_pipe: [2]std.c.fd_t, -wm_protocols: c.Atom, -wm_delete_window: c.Atom, -net_wm_ping: c.Atom, -net_wm_bypass_compositor: c.Atom, -motif_wm_hints: c.Atom, -net_wm_window_type: c.Atom, -net_wm_window_type_dock: c.Atom, -root_window: c.Window, -window: c.Window, -backend_type: gpu.BackendType, -hidden_cursor: c.Cursor, +// TODO: determine if it's really needed to store global pointer +var core_ptr: *Core = undefined; + +pub const Native = struct { + backend_type: gpu.BackendType, + cursors: [@typeInfo(CursorShape).@"enum".fields.len]?c.Cursor, + display: *c.Display, + 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, + net_wm_window_type: c.Atom, + net_wm_window_type_dock: c.Atom, + root_window: c.Window, + surface_descriptor: gpu.Surface.DescriptorFromXlibWindow, + window: c.Window, + wm_delete_window: c.Atom, + wm_protocols: c.Atom, +}; // Mutable fields only used by main thread -cursors: [@typeInfo(CursorShape).@"enum".fields.len]?c.Cursor, // Mutable state fields; read/write by any thread -surface_descriptor: *gpu.Surface.DescriptorFromXlibWindow, - -pub const Native = struct {}; -pub fn init( - linux: *Linux, +pub fn initWindow( core: *Core, - options: InitOptions, + window_id: mach.ObjectID, ) !void { + core_ptr = core; + var core_window = core.windows.getValue(window_id); // TODO(core): return errors.NotSupported if not supported const libx11 = try LibX11.load(); @@ -95,7 +94,10 @@ pub fn init( 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); + var set_window_attrs = c.XSetWindowAttributes{ .colormap = colormap, // TODO: reduce @@ -104,13 +106,15 @@ pub fn init( c.ExposureMask | c.FocusChangeMask | c.VisibilityChangeMask | c.EnterWindowMask | c.LeaveWindowMask | c.PropertyChangeMask, }; - const window = libx11.XCreateWindow( + + // TODO: read error after function call and handle + 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? - linux.size.width, - linux.size.height, + core_window.width, + core_window.height, 0, c.DefaultDepth(display, screen), c.InputOutput, @@ -118,54 +122,47 @@ pub fn init( c.CWColormap | c.CWEventMask, &set_window_attrs, ); - var window_attrs: c.XWindowAttributes = undefined; - _ = libx11.XGetWindowAttributes(display, window, &window_attrs); - linux.size = Core.Size{ - .width = @intCast(window_attrs.width), - .height = @intCast(window_attrs.height), - }; - const blank_pixmap = libx11.XCreatePixmap(display, window, 1, 1, 1); + + const blank_pixmap = libx11.XCreatePixmap(display, x_window_id, 1, 1, 1); var color = c.XColor{}; - linux.refresh_rate = blk: { + core_window.refresh_rate = blk: { if (libxrr != null) { const conf = libxrr.?.XRRGetScreenInfo(display, root_window); break :blk @intCast(libxrr.?.XRRConfigCurrentRate(conf)); } break :blk 60; }; - const surface_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromXlibWindow); - surface_descriptor.* = .{ - .display = display, - .window = @intCast(window), - }; - linux.backend = .{ .x11 = X11{ - .core = core, - .allocator = options.allocator, + + const surface_descriptor = gpu.Surface.DescriptorFromXlibWindow{ .display = display, .window = @intCast(x_window_id) }; + core_window.surface_descriptor = .{ .next_in_chain = .{ + .from_xlib_window = &surface_descriptor, + } }; + + core_window.native = .{ .x11 = .{ + .backend_type = try Core.detectBackendType(core.allocator), + .cursors = std.mem.zeroes([@typeInfo(CursorShape).@"enum".fields.len]?c.Cursor), .display = display, - .libx11 = libx11, + .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, .libxcursor = libxcursor, + .libxkbcommon = try LibXkbCommon.load(), .libxrr = libxrr, - .empty_event_pipe = try std.posix.pipe(), - .gl_ctx = null, - .wm_protocols = libx11.XInternAtom(display, "WM_PROTOCOLS", c.False), - .wm_delete_window = libx11.XInternAtom(display, "WM_DELETE_WINDOW", 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), - .net_wm_bypass_compositor = libx11.XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", c.False), - .motif_wm_hints = libx11.XInternAtom(display, "_MOTIF_WM_HINTS", c.False), .root_window = root_window, - .window = window, - .hidden_cursor = libx11.XCreatePixmapCursor(display, blank_pixmap, blank_pixmap, &color, &color, 0, 0), - .backend_type = try Core.detectBackendType(options.allocator), - .cursors = std.mem.zeroes([@typeInfo(CursorShape).@"enum".fields.len]?c.Cursor), .surface_descriptor = surface_descriptor, - .libxkbcommon = try LibXkbCommon.load(), + .window = x_window_id, + .wm_delete_window = libx11.XInternAtom(display, "WM_DELETE_WINDOW", c.False), + .wm_protocols = libx11.XInternAtom(display, "WM_PROTOCOLS", c.False), } }; - var x11 = &linux.backend.x11; - _ = libx11.XrmInitialize(); - defer _ = libx11.XFreeColormap(display, colormap); + var x11 = &core_window.native.?.x11; + for (0..2) |i| { const sf = try std.posix.fcntl(x11.empty_event_pipe[i], std.posix.F.GETFL, 0); const df = try std.posix.fcntl(x11.empty_event_pipe[i], std.posix.F.GETFD, 0); @@ -174,11 +171,12 @@ pub fn init( } 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, options.title.ptr); + _ = 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.XGetWindowAttributes(x11.display, x11.window, &window_attrs); - const backend_type = try Core.detectBackendType(options.allocator); + + // TODO: see if this can be removed + const backend_type = try Core.detectBackendType(core.allocator); switch (backend_type) { .opengl, .opengles => { if (libgl != null) { @@ -206,66 +204,73 @@ pub fn init( }, else => {}, } + // Create hidden cursor 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); } - // TODO: remove allocation - x11.cursors[@intFromEnum(CursorShape.arrow)] = try x11.createStandardCursor(.arrow); -} + x11.cursors[@intFromEnum(CursorShape.arrow)] = try createStandardCursor(x11, .arrow); -pub fn deinit( - x11: *X11, - linux: *Linux, -) void { - linux.allocator.destroy(x11.surface_descriptor); - for (x11.cursors) |cur| { - if (cur) |_| { - // _ = x11.libx11.XFreeCursor(x11.display, cur.?); - } - } - if (x11.libxcursor) |*libxcursor| { - libxcursor.handle.close(); - } - if (x11.libxrr) |*libxrr| { - libxrr.handle.close(); - } - if (x11.libgl) |*libgl| { - if (x11.gl_ctx) |gl_ctx| { - libgl.glXDestroyContext(x11.display, gl_ctx); - } - libgl.handle.close(); - } - _ = x11.libx11.XUnmapWindow(x11.display, x11.window); - _ = x11.libx11.XDestroyWindow(x11.display, x11.window); - _ = x11.libx11.XCloseDisplay(x11.display); - x11.libx11.handle.close(); - std.posix.close(x11.empty_event_pipe[0]); - std.posix.close(x11.empty_event_pipe[1]); + core.windows.setValue(window_id, core_window); + try core.initWindow(window_id); } +// pub fn deinit( +// x11: *X11, +// linux: *Linux, +// ) void { +// linux.allocator.destroy(x11.surface_descriptor); +// for (x11.cursors) |cur| { +// if (cur) |_| { +// // _ = x11.libx11.XFreeCursor(x11.display, cur.?); +// } +// } +// if (x11.libxcursor) |*libxcursor| { +// libxcursor.handle.close(); +// } +// if (x11.libxrr) |*libxrr| { +// libxrr.handle.close(); +// } +// if (x11.libgl) |*libgl| { +// if (x11.gl_ctx) |gl_ctx| { +// libgl.glXDestroyContext(x11.display, gl_ctx); +// } +// libgl.handle.close(); +// } +// _ = x11.libx11.XUnmapWindow(x11.display, x11.window); +// _ = x11.libx11.XDestroyWindow(x11.display, x11.window); +// _ = x11.libx11.XCloseDisplay(x11.display); +// x11.libx11.handle.close(); +// std.posix.close(x11.empty_event_pipe[0]); +// std.posix.close(x11.empty_event_pipe[1]); +// } + // Called on the main thread -pub fn update(x11: *X11, linux: *Linux) !void { +pub fn tick(window_id: mach.ObjectID) !void { + var core_window = core_ptr.windows.getValue(window_id); + var x11 = &core_window.native.?.x11; while (c.QLength(x11.display) != 0) { var event: c.XEvent = undefined; _ = x11.libx11.XNextEvent(x11.display, &event); - x11.processEvent(linux, &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); // const frequency_delay = @as(f32, @floatFromInt(x11.input.delay_ns)) / @as(f32, @floatFromInt(std.time.ns_per_s)); // TODO: glfw.waitEventsTimeout(frequency_delay); - - x11.core.input.tick(); } -pub fn setTitle(x11: *X11, title: [:0]const u8) void { +pub fn setTitle(x11: *const Native, title: [:0]const u8) void { _ = x11.libx11.XStoreName(x11.display, x11.window, title); } -pub fn setDisplayMode(x11: *X11, linux: *Linux, display_mode: DisplayMode) 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_fullscreen = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False); switch (display_mode) { @@ -290,7 +295,7 @@ pub fn setDisplayMode(x11: *X11, linux: *Linux, display_mode: DisplayMode) void @intCast(atoms.len), ); x11.setFullscreen(false); - x11.setDecorated(linux.border); + x11.setDecorated(border); x11.setFloating(false); _ = x11.libx11.XMapWindow(x11.display, x11.window); _ = x11.libx11.XFlush(x11.display); @@ -314,7 +319,7 @@ pub fn setDisplayMode(x11: *X11, linux: *Linux, display_mode: DisplayMode) void } } -fn setFullscreen(x11: *X11, 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_fullscreen = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False); x11.sendEventToWM(wm_state, &.{ @intFromBool(enabled), @intCast(wm_fullscreen), 0, 1 }); @@ -335,7 +340,7 @@ fn setFullscreen(x11: *X11, enabled: bool) void { } } -fn setFloating(x11: *X11, 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 net_wm_state_remove = 0; @@ -344,7 +349,7 @@ fn setFloating(x11: *X11, enabled: bool) void { x11.sendEventToWM(wm_state, &.{ action, @intCast(wm_above), 0, 1 }); } -fn sendEventToWM(x11: *X11, message_type: c.Atom, data: []const c_long) void { +fn sendEventToWM(x11: *const Native, message_type: c.Atom, data: []const c_long) void { var ev = std.mem.zeroes(c.XEvent); ev.type = c.ClientMessage; ev.xclient.window = x11.window; @@ -361,7 +366,7 @@ fn sendEventToWM(x11: *X11, message_type: c.Atom, data: []const c_long) void { _ = x11.libx11.XFlush(x11.display); } -fn setDecorated(x11: *X11, enabled: bool) void { +fn setDecorated(x11: *const Native, enabled: bool) void { const MWMHints = struct { flags: u32, functions: u32, @@ -546,7 +551,7 @@ const LibXkbCommon = struct { } }; -fn createStandardCursor(x11: *X11, shape: CursorShape) !c.Cursor { +fn createStandardCursor(x11: *const Native, shape: CursorShape) !c.Cursor { if (x11.libxcursor) |libxcursor| { const theme = libxcursor.XcursorGetTheme(x11.display); if (theme != null) { @@ -587,7 +592,7 @@ fn createStandardCursor(x11: *X11, shape: CursorShape) !c.Cursor { return cursor; } -fn getCursorPos(x11: *X11) Position { +fn getCursorPos(x11: *const Native) Position { var root_window: c.Window = undefined; var child_window: c.Window = undefined; var root_cursor_x: c_int = 0; @@ -610,7 +615,10 @@ fn getCursorPos(x11: *X11) Position { return .{ .x = @floatFromInt(cursor_x), .y = @floatFromInt(cursor_y) }; } -fn processEvent(x11: *X11, linux: *Linux, event: *c.XEvent) void { +/// Handle XEvents. Window object can be modified. +fn processEvent(window_id: mach.ObjectID, event: *c.XEvent) void { + var core_window = core_ptr.windows.getValue(window_id); + const x11 = &core_window.native.?.x11; switch (event.type) { c.KeyPress, c.KeyRelease => { // TODO: key repeat event @@ -618,19 +626,23 @@ fn processEvent(x11: *X11, linux: *Linux, event: *c.XEvent) void { var keysym: c.KeySym = undefined; _ = x11.libx11.XLookupString(&event.xkey, null, 0, &keysym, null); - const key_event = KeyEvent{ .key = toMachKey(keysym), .mods = toMachMods(event.xkey.state) }; + const key_event = KeyEvent{ + .key = toMachKey(keysym), + .mods = toMachMods(event.xkey.state), + .window_id = window_id, + }; switch (event.type) { c.KeyPress => { - x11.core.pushEvent(.{ .key_press = key_event }); + core_ptr.pushEvent(.{ .key_press = key_event }); const codepoint = x11.libxkbcommon.xkb_keysym_to_utf32(@truncate(keysym)); if (codepoint != 0) { - x11.core.pushEvent(.{ .char_input = .{ .codepoint = @truncate(codepoint) } }); + core_ptr.pushEvent(.{ .char_input = .{ .codepoint = @truncate(codepoint), .window_id = window_id } }); } }, c.KeyRelease => { - x11.core.pushEvent(.{ .key_release = key_event }); + core_ptr.pushEvent(.{ .key_release = key_event }); }, else => unreachable, } @@ -645,28 +657,30 @@ fn processEvent(x11: *X11, linux: *Linux, event: *c.XEvent) void { 7 => .{ -1.0, 0.0 }, else => unreachable, }; - x11.core.pushEvent(.{ .mouse_scroll = .{ .xoffset = scroll[0], .yoffset = scroll[1] } }); + core_ptr.pushEvent(.{ .mouse_scroll = .{ .xoffset = scroll[0], .yoffset = scroll[1], .window_id = window_id } }); return; }; - const cursor_pos = x11.getCursorPos(); + const cursor_pos = getCursorPos(x11); const mouse_button = MouseButtonEvent{ .button = button, .pos = cursor_pos, .mods = toMachMods(event.xbutton.state), + .window_id = window_id, }; - x11.core.pushEvent(.{ .mouse_press = mouse_button }); + core_ptr.pushEvent(.{ .mouse_press = mouse_button }); }, c.ButtonRelease => { const button = toMachButton(event.xbutton.button) orelse return; - const cursor_pos = x11.getCursorPos(); + const cursor_pos = getCursorPos(x11); const mouse_button = MouseButtonEvent{ .button = button, .pos = cursor_pos, .mods = toMachMods(event.xbutton.state), + .window_id = window_id, }; - x11.core.pushEvent(.{ .mouse_release = mouse_button }); + core_ptr.pushEvent(.{ .mouse_release = mouse_button }); }, c.ClientMessage => { if (event.xclient.message_type == c.None) return; @@ -676,7 +690,7 @@ fn processEvent(x11: *X11, linux: *Linux, event: *c.XEvent) void { if (protocol == c.None) return; if (protocol == x11.wm_delete_window) { - x11.core.pushEvent(.close); + core_ptr.pushEvent(.{ .close = .{ .window_id = window_id } }); } else if (protocol == x11.net_wm_ping) { // The window manager is pinging the application to ensure // it's still responding to events @@ -695,26 +709,33 @@ fn processEvent(x11: *X11, linux: *Linux, event: *c.XEvent) void { c.EnterNotify => { const x: f32 = @floatFromInt(event.xcrossing.x); const y: f32 = @floatFromInt(event.xcrossing.y); - x11.core.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y } } }); + core_ptr.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y }, .window_id = window_id } }); }, c.MotionNotify => { const x: f32 = @floatFromInt(event.xmotion.x); const y: f32 = @floatFromInt(event.xmotion.y); - x11.core.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y } } }); + core_ptr.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y }, .window_id = window_id } }); }, c.ConfigureNotify => { - if (event.xconfigure.width != linux.size.width or - event.xconfigure.height != linux.size.height) + if (event.xconfigure.width != core_window.width or + event.xconfigure.height != core_window.height) { - linux.size.width = @intCast(event.xconfigure.width); - linux.size.height = @intCast(event.xconfigure.height); - x11.core.swap_chain_update.set(); - x11.core.pushEvent(.{ - .framebuffer_resize = .{ - .width = linux.size.width, - .height = linux.size.height, + core_window.width = @intCast(event.xconfigure.width); + core_window.height = @intCast(event.xconfigure.height); + + // FIX: What is the Mach Object System way of doing this? + // core_ptr.swap_chain_update.set(); + + core_ptr.pushEvent(.{ + .window_resize = .{ + .size = Core.Size{ + .width = core_window.width, + .height = core_window.height, + }, + .window_id = window_id, }, }); + core_ptr.windows.setValue(window_id, core_window); } }, c.FocusIn => { @@ -726,7 +747,7 @@ fn processEvent(x11: *X11, linux: *Linux, event: *c.XEvent) void { return; } - x11.core.pushEvent(.focus_gained); + core_ptr.pushEvent(.{ .focus_gained = .{ .window_id = window_id } }); }, c.FocusOut => { if (event.xfocus.mode == c.NotifyGrab or @@ -737,7 +758,15 @@ fn processEvent(x11: *X11, linux: *Linux, event: *c.XEvent) void { return; } - x11.core.pushEvent(.focus_lost); + core_ptr.pushEvent(.{ .focus_lost = .{ .window_id = window_id } }); + }, + c.ResizeRequest => { + _ = x11.libx11.XResizeWindow( + x11.display, + x11.window, + @intCast(c.DisplayWidth(x11.display, c.DefaultScreen(x11.display))), + @intCast(c.DisplayHeight(x11.display, c.DefaultScreen(x11.display))), + ); }, else => {}, } From 46cfd121cfa77d2d2945150f4ffeab7899acb979 Mon Sep 17 00:00:00 2001 From: Joshua Holmes Date: Fri, 20 Dec 2024 07:16:39 +0000 Subject: [PATCH 5/6] linux: allow title to be changed again --- src/core/Linux.zig | 26 ++++++++++++++++---------- src/core/linux/Wayland.zig | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/core/Linux.zig b/src/core/Linux.zig index f9901cb8ff..21bb2f226a 100644 --- a/src/core/Linux.zig +++ b/src/core/Linux.zig @@ -63,6 +63,7 @@ pub fn tick(core: *Core) !void { while (windows.next()) |window_id| { const native_opt: ?Native = core.windows.get(window_id, .native); if (native_opt) |native| { + check_for_mach_updates(core, window_id); // check for display server events switch (native) { .x11 => try X11.tick(window_id), @@ -146,16 +147,10 @@ pub fn update(linux: *Linux) !void { } } -pub fn setTitle(linux: *Linux, title: [:0]const u8) void { - const new_title = linux.allocator.dupeZ(u8, title) catch { - log.err("Failed to reallocate memory for new window title", .{}); - return; - }; - linux.allocator.free(linux.title); - linux.title = new_title; - switch (linux.backend) { - .wayland => linux.backend.wayland.setTitle(linux.title), - .x11 => linux.backend.x11.setTitle(linux.title), +fn setTitle(native: *const Native, title: [:0]const u8) void { + switch (native.*) { + .wayland => |wl| Wayland.setTitle(&wl, title), + .x11 => |x| X11.setTitle(&x, title), } } @@ -192,6 +187,17 @@ pub fn setCursorShape(_: *Linux, _: CursorShape) void { return; } +/// Checks for updates in mach object fields. Does nothing if window is not initialized. +fn check_for_mach_updates(core: *Core, window_id: mach.ObjectID) void { + const core_window = core.windows.getValue(window_id); + const native = &core_window.native; + if (native.*) |n| { + if (core.windows.updated(window_id, .title)) { + setTitle(&n, core_window.title); + } + } +} + /// Check if gamemode should be activated pub fn wantGamemode(allocator: std.mem.Allocator) error{ OutOfMemory, InvalidWtf8 }!bool { const use_gamemode = std.process.getEnvVarOwned( diff --git a/src/core/linux/Wayland.zig b/src/core/linux/Wayland.zig index 8da93b8d09..87c8cb8f51 100644 --- a/src/core/linux/Wayland.zig +++ b/src/core/linux/Wayland.zig @@ -243,7 +243,7 @@ pub fn tick(window_id: mach.ObjectID) !void { _ = libwaylandclient.wl_display_roundtrip(wl.display); } -pub fn setTitle(wl: *Wayland, title: [:0]const u8) void { +pub fn setTitle(wl: *const Native, title: [:0]const u8) void { c.xdg_toplevel_set_title(wl.toplevel, title); } From d0707b7b13a8e2b8f2c61162adb0e6f93b2bfd00 Mon Sep 17 00:00:00 2001 From: Joshua Holmes Date: Fri, 20 Dec 2024 07:18:03 +0000 Subject: [PATCH 6/6] linux: note that wayland cannot be resized anymore This is because something is not working with the new Mach Object system. It should be able to be fixed after messing around with it. --- src/core/Linux.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Linux.zig b/src/core/Linux.zig index 21bb2f226a..7351d8d4f8 100644 --- a/src/core/Linux.zig +++ b/src/core/Linux.zig @@ -56,7 +56,7 @@ backend: Backend, // these arrays are used as info messages to the user that some features are missing // please keep these up to date until we can remove them const MISSING_FEATURES_X11 = [_][]const u8{ "Resizing window", "Changing display mode", "VSync", "Setting window border/cursor" }; -const MISSING_FEATURES_WAYLAND = [_][]const u8{ "Changing display mode", "VSync", "Setting window border/cursor" }; +const MISSING_FEATURES_WAYLAND = [_][]const u8{ "Resizing window", "Changing display mode", "VSync", "Setting window border/cursor" }; pub fn tick(core: *Core) !void { var windows = core.windows.slice();