diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 1618c4a..c3fa9a2 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -42,3 +42,13 @@ jobs: - name: Build AppImage run: nix develop -c zig build -Dappimage -Doptimize=ReleaseSafe + + - name: Upload Build Artifacts + uses: actions/upload-artifact@v4 + with: + name: spacecap-linux-${{ github.event.pull_request.number }} + path: | + zig-out/linux/spacecap + zig-out/linux/spacecap-linux-x86_64.AppImage + retention-days: 7 + if-no-files-found: error diff --git a/build.zig.zon b/build.zig.zon index 820486a..2551280 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .spacecap, - .version = "0.4.0", + .version = "0.5.0", .fingerprint = 0xdc2511c12abe6e80, .minimum_zig_version = "0.16.0", .dependencies = .{ diff --git a/build/build_app_image.sh b/build/build_app_image.sh index 68d0567..90bdf4f 100755 --- a/build/build_app_image.sh +++ b/build/build_app_image.sh @@ -12,7 +12,7 @@ rm -f zig-out/linux/spacecap-linux-x86_64.AppImage LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}" linuxdeploy \ --appdir AppDir \ --executable zig-out/linux/spacecap \ - --desktop-file packaging/linux/spacecap.desktop \ + --desktop-file packaging/linux/org.spacecap.spacecap.desktop \ --icon-file packaging/spacecap.svg \ --exclude-library libvulkan.so.1 \ --exclude-library libxkbcommon.so.0 \ diff --git a/flake.nix b/flake.nix index f839882..16a6d50 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,15 @@ system: let pkgs = import nixpkgs { inherit system; - overlays = []; + overlays = [ + (_final: prev: { + # NOTE: This can be removed with: https://github.com/mgerb/spacecap/issues/144 + # GTK's optional file-search integration pulls TinySPARQL into the + # AppImage, which then tries to link sqlite3. This can be avoided + # by disabling it here. + gtk3 = prev.gtk3.override {trackerSupport = false;}; + }) + ]; }; zigpkgs = zig.packages.${system}; in { diff --git a/install.sh b/install.sh index 8167aac..fd4d60d 100755 --- a/install.sh +++ b/install.sh @@ -3,11 +3,12 @@ set -eu APP_NAME="spacecap" +APP_ID="org.spacecap.spacecap" ARTIFACT_NAME="spacecap-linux-x86_64.AppImage" CHECKSUMS_NAME="SHA256SUMS.txt" RELEASES_URL="https://github.com/mgerb/spacecap/releases" RAW_URL="https://raw.githubusercontent.com/mgerb/spacecap/main" -DESKTOP_URL="$RAW_URL/packaging/linux/spacecap.desktop" +DESKTOP_URL="$RAW_URL/packaging/linux/$APP_ID.desktop" ICON_URL="$RAW_URL/packaging/spacecap.svg" CHANNEL="nightly" MODE="install" @@ -72,7 +73,9 @@ INSTALL_DIR="$HOME/.local/bin" INSTALL_PATH="$INSTALL_DIR/$APP_NAME" DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}" DESKTOP_DIR="$DATA_DIR/applications" -DESKTOP_PATH="$DESKTOP_DIR/$APP_NAME.desktop" +DESKTOP_PATH="$DESKTOP_DIR/$APP_ID.desktop" +# NOTE: Remove this eventually. +LEGACY_DESKTOP_PATH="$DESKTOP_DIR/$APP_NAME.desktop" ICON_DIR="$DATA_DIR/icons/hicolor/scalable/apps" ICON_PATH="$ICON_DIR/$APP_NAME.svg" WAS_INSTALLED=0 @@ -82,7 +85,7 @@ fi if [ "$MODE" = "uninstall" ]; then REMOVED=0 - for path in "$INSTALL_PATH" "$DESKTOP_PATH" "$ICON_PATH"; do + for path in "$INSTALL_PATH" "$DESKTOP_PATH" "$LEGACY_DESKTOP_PATH" "$ICON_PATH"; do if [ -e "$path" ] || [ -L "$path" ]; then rm -f "$path" echo "Removed $path." @@ -121,7 +124,7 @@ TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT INT TERM APPIMAGE_TMP="$TMP_DIR/$ARTIFACT_NAME" CHECKSUMS_TMP="$TMP_DIR/$CHECKSUMS_NAME" -DESKTOP_TMP="$TMP_DIR/$APP_NAME.desktop" +DESKTOP_TMP="$TMP_DIR/$APP_ID.desktop" ICON_TMP="$TMP_DIR/$APP_NAME.svg" echo "Downloading $APP_NAME ($CHANNEL) from $DOWNLOAD_URL." @@ -143,6 +146,9 @@ mkdir -p "$INSTALL_DIR" "$DESKTOP_DIR" "$ICON_DIR" mv "$APPIMAGE_TMP" "$INSTALL_PATH" mv "$DESKTOP_TMP" "$DESKTOP_PATH" mv "$ICON_TMP" "$ICON_PATH" +if [ "$LEGACY_DESKTOP_PATH" != "$DESKTOP_PATH" ]; then + rm -f "$LEGACY_DESKTOP_PATH" +fi VERSION="$("$INSTALL_PATH" --version)" if [ "$WAS_INSTALLED" -eq 1 ]; then diff --git a/packaging/linux/spacecap.desktop b/packaging/linux/org.spacecap.spacecap.desktop similarity index 100% rename from packaging/linux/spacecap.desktop rename to packaging/linux/org.spacecap.spacecap.desktop diff --git a/src/app_registration/linux/xdg_desktop_portal_app_registration.zig b/src/app_registration/linux/xdg_desktop_portal_app_registration.zig new file mode 100644 index 0000000..83a6ddb --- /dev/null +++ b/src/app_registration/linux/xdg_desktop_portal_app_registration.zig @@ -0,0 +1,88 @@ +const std = @import("std"); +const app_identity = @import("../../common/linux/app_identity.zig"); +const XdgDesktopPortal = @import("../../common/linux/xdg_desktop_portal.zig"); +const c = @import("libportal"); + +const log = std.log.scoped(.xdg_desktop_portal_app_registration); + +fn variant_type(comptime signature: [:0]const u8) *const c.GVariantType { + return @ptrCast(signature.ptr); +} + +fn is_registry_unavailable(err: *c.GError) bool { + if (err.domain != c.g_dbus_error_quark()) return false; + + return err.code == c.G_DBUS_ERROR_UNKNOWN_METHOD or + err.code == c.G_DBUS_ERROR_UNKNOWN_INTERFACE or + err.code == c.G_DBUS_ERROR_UNKNOWN_OBJECT or + err.code == c.G_DBUS_ERROR_SERVICE_UNKNOWN or + err.code == c.G_DBUS_ERROR_NAME_HAS_NO_OWNER; +} + +/// Register the app ID before any other dbus connections start up. +/// The dbus connection remains open until the app closes. +pub const XdgDesktopPortalAppRegistration = struct { + const Self = @This(); + + dbus: *c.GDBusConnection, + + pub fn init() !Self { + var g_error: ?*c.GError = null; + defer if (g_error) |err| c.g_error_free(err); + + const dbus = c.g_bus_get_sync(c.G_BUS_TYPE_SESSION, null, &g_error) orelse { + if (g_error) |err| { + log.err("[init] failed to get session bus: {s}", .{err.message.?}); + } + return error.Dbus; + }; + errdefer c.g_object_unref(dbus); + + register(dbus); + + return .{ .dbus = dbus }; + } + + pub fn deinit(self: *Self) void { + c.g_object_unref(self.dbus); + } + + fn register(dbus: *c.GDBusConnection) void { + var options: c.GVariantBuilder = undefined; + c.g_variant_builder_init(&options, variant_type("a{sv}")); + const payload = c.g_variant_new( + "(s@a{sv})", + app_identity.APP_ID.ptr, + c.g_variant_builder_end(&options), + ).?; + + var g_error: ?*c.GError = null; + defer if (g_error) |err| c.g_error_free(err); + + const result = c.g_dbus_connection_call_sync( + dbus, + XdgDesktopPortal.DBUS_DESTINATION.ptr, + XdgDesktopPortal.DBUS_OBJECT_PATH.ptr, + XdgDesktopPortal.REGISTRY_INTERFACE.ptr, + XdgDesktopPortal.REGISTER_METHOD.ptr, + payload, + variant_type("()"), + c.G_DBUS_CALL_FLAGS_NONE, + -1, + null, + &g_error, + ); + defer if (result) |value| c.g_variant_unref(value); + + if (g_error) |err| { + if (is_registry_unavailable(err)) { + log.info("[register] host portal registry is unavailable: {s}", .{err.message.?}); + } else { + log.err("[register] failed to register app id '{s}': {s}", .{ app_identity.APP_ID, err.message.? }); + } + return; + } + + log.info("[register] registered app id '{s}'", .{app_identity.APP_ID}); + } +}; diff --git a/src/app_registration/platform_app_registration.zig b/src/app_registration/platform_app_registration.zig new file mode 100644 index 0000000..371f7da --- /dev/null +++ b/src/app_registration/platform_app_registration.zig @@ -0,0 +1,14 @@ +const Util = @import("../util.zig"); + +/// Any type of platform specific app init logic should go in here. +/// e.g. On Linux, register the app name over dbus. +pub const PlatformAppRegistration = if (Util.is_linux()) + @import("./linux/xdg_desktop_portal_app_registration.zig").XdgDesktopPortalAppRegistration +else + struct { + pub fn init() !@This() { + return .{}; + } + + pub fn deinit(_: *@This()) void {} + }; diff --git a/src/common/linux/app_identity.zig b/src/common/linux/app_identity.zig new file mode 100644 index 0000000..909787e --- /dev/null +++ b/src/common/linux/app_identity.zig @@ -0,0 +1,2 @@ +pub const APP_NAME: [:0]const u8 = "Spacecap"; +pub const APP_ID: [:0]const u8 = "org.spacecap.spacecap"; diff --git a/src/common/linux/token_manager.zig b/src/common/linux/token_manager.zig index e1fdc0a..2f98735 100644 --- a/src/common/linux/token_manager.zig +++ b/src/common/linux/token_manager.zig @@ -1,11 +1,12 @@ const std = @import("std"); +const XdgDesktopPortal = @import("./xdg_desktop_portal.zig"); /// Caller owns the memory pub fn get_request_path(allocator: std.mem.Allocator, unique_name: [:0]const u8, token: [:0]const u8) std.mem.Allocator.Error![:0]const u8 { // Generate the path const path: [:0]u8 = try std.fmt.allocPrintSentinel( allocator, - "/org/freedesktop/portal/desktop/request/{s}/{s}", + XdgDesktopPortal.DBUS_OBJECT_PATH ++ "/request/{s}/{s}", .{ unique_name, token }, 0, ); diff --git a/src/common/linux/xdg_desktop_portal.zig b/src/common/linux/xdg_desktop_portal.zig new file mode 100644 index 0000000..2362ec0 --- /dev/null +++ b/src/common/linux/xdg_desktop_portal.zig @@ -0,0 +1,23 @@ +//! dbus constants + +pub const DBUS_DESTINATION: [:0]const u8 = "org.freedesktop.portal.Desktop"; +pub const DBUS_OBJECT_PATH: [:0]const u8 = "/org/freedesktop/portal/desktop"; + +// Interface +pub const FILE_CHOOSER_INTERFACE: [:0]const u8 = "org.freedesktop.portal.FileChooser"; +pub const GLOBAL_SHORTCUTS_INTERFACE: [:0]const u8 = "org.freedesktop.portal.GlobalShortcuts"; +pub const REGISTRY_INTERFACE: [:0]const u8 = "org.freedesktop.host.portal.Registry"; +pub const REQUEST_INTERFACE: [:0]const u8 = "org.freedesktop.portal.Request"; +pub const SESSION_INTERFACE: [:0]const u8 = "org.freedesktop.portal.Session"; + +// Method +pub const BIND_SHORTCUTS_METHOD: [:0]const u8 = "BindShortcuts"; +pub const CLOSE_METHOD: [:0]const u8 = "Close"; +pub const CONFIGURE_SHORTCUTS_METHOD: [:0]const u8 = "ConfigureShortcuts"; +pub const CREATE_SESSION_METHOD: [:0]const u8 = "CreateSession"; +pub const OPEN_FILE_METHOD: [:0]const u8 = "OpenFile"; +pub const REGISTER_METHOD: [:0]const u8 = "Register"; + +// Signal +pub const ACTIVATED_SIGNAL: [:0]const u8 = "Activated"; +pub const RESPONSE_SIGNAL: [:0]const u8 = "Response"; diff --git a/src/file_picker/linux/xdg_desktop_portal_file_picker.zig b/src/file_picker/linux/xdg_desktop_portal_file_picker.zig index f6a19b9..23927ed 100644 --- a/src/file_picker/linux/xdg_desktop_portal_file_picker.zig +++ b/src/file_picker/linux/xdg_desktop_portal_file_picker.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const TokenManager = @import("../../common/linux/token_manager.zig"); +const XdgDesktopPortal = @import("../../common/linux/xdg_desktop_portal.zig"); const FilePicker = @import("../file_picker.zig").FilePicker; const FilePickerError = @import("../file_picker.zig").FilePickerError; @@ -8,13 +9,6 @@ const c = @import("libportal"); const log = std.log.scoped(.xdg_desktop_portal_file_picker); -const DBUS_DESTINATION: [:0]const u8 = "org.freedesktop.portal.Desktop"; -const DBUS_OBJECT_PATH: [:0]const u8 = "/org/freedesktop/portal/desktop"; -const FILE_CHOOSER_INTERFACE: [:0]const u8 = "org.freedesktop.portal.FileChooser"; -const REQUEST_INTERFACE: [:0]const u8 = "org.freedesktop.portal.Request"; -const OPEN_FILE_METHOD: [:0]const u8 = "OpenFile"; -const RESPONSE_SIGNAL: [:0]const u8 = "Response"; - fn variant_type(comptime signature: [:0]const u8) *const c.GVariantType { return @ptrCast(signature.ptr); } @@ -145,8 +139,8 @@ pub const XdgDesktopPortalFilePicker = struct { var subscription_id = c.g_dbus_connection_signal_subscribe( self.dbus, null, - REQUEST_INTERFACE.ptr, - RESPONSE_SIGNAL.ptr, + XdgDesktopPortal.REQUEST_INTERFACE.ptr, + XdgDesktopPortal.RESPONSE_SIGNAL.ptr, request_path.ptr, null, c.G_DBUS_SIGNAL_FLAGS_NONE, @@ -164,10 +158,10 @@ pub const XdgDesktopPortalFilePicker = struct { var err: ?*c.GError = null; const request_handle = c.g_dbus_connection_call_sync( self.dbus, - DBUS_DESTINATION.ptr, - DBUS_OBJECT_PATH.ptr, - FILE_CHOOSER_INTERFACE.ptr, - OPEN_FILE_METHOD.ptr, + XdgDesktopPortal.DBUS_DESTINATION.ptr, + XdgDesktopPortal.DBUS_OBJECT_PATH.ptr, + XdgDesktopPortal.FILE_CHOOSER_INTERFACE.ptr, + XdgDesktopPortal.OPEN_FILE_METHOD.ptr, payload, null, c.G_DBUS_CALL_FLAGS_NONE, @@ -205,8 +199,8 @@ pub const XdgDesktopPortalFilePicker = struct { subscription_id = c.g_dbus_connection_signal_subscribe( self.dbus, null, - REQUEST_INTERFACE.ptr, - RESPONSE_SIGNAL.ptr, + XdgDesktopPortal.REQUEST_INTERFACE.ptr, + XdgDesktopPortal.RESPONSE_SIGNAL.ptr, actual_request_path, null, c.G_DBUS_SIGNAL_FLAGS_NONE, diff --git a/src/global_shortcuts/linux/xdg_desktop_portal_global_shortcuts.zig b/src/global_shortcuts/linux/xdg_desktop_portal_global_shortcuts.zig index 353462a..1d3ade3 100644 --- a/src/global_shortcuts/linux/xdg_desktop_portal_global_shortcuts.zig +++ b/src/global_shortcuts/linux/xdg_desktop_portal_global_shortcuts.zig @@ -2,7 +2,7 @@ const std = @import("std"); const TokenManager = @import("../../common/linux/token_manager.zig"); -const TokenStorage = @import("../../common/linux/token_storage.zig"); +const XdgDesktopPortal = @import("../../common/linux/xdg_desktop_portal.zig"); const GlobalShortcuts = @import("../global_shortcuts.zig").GlobalShortcuts; const assert = std.debug.assert; @@ -18,7 +18,6 @@ const Actions = std.StringArrayHashMapUnmanaged(Action); pub const XdgDesktopPortalGlobalShortcuts = struct { const Self = @This(); - const Token = [16]u8; allocator: std.mem.Allocator, io: std.Io, @@ -26,9 +25,7 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { main_loop: ?*c.GMainLoop = null, run_thread: ?std.Thread = null, ctx: ?*c.GMainContext = null, - // actions: *Actions, actions: Actions, - session_token: ?[:0]u8 = null, registeredShortcutHandler: ?GlobalShortcuts.ShortcutHandler = null, /// The handle of the current global shortcuts portal session, @@ -96,10 +93,6 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { run_thread.join(); } - if (self.session_token) |session_token| { - self.allocator.free(session_token); - } - self.actions.deinit(self.allocator); } @@ -118,10 +111,10 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { // Close existing session c.g_dbus_connection_call( self.dbus, - "org.freedesktop.portal.Desktop", + XdgDesktopPortal.DBUS_DESTINATION.ptr, handle.ptr, - "org.freedesktop.portal.Session", - "Close", + XdgDesktopPortal.SESSION_INTERFACE.ptr, + XdgDesktopPortal.CLOSE_METHOD.ptr, null, null, c.G_DBUS_CALL_FLAGS_NONE, @@ -158,7 +151,7 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { c.g_main_context_push_thread_default(_self.ctx.?); defer c.g_main_context_pop_thread_default(_self.ctx.?); - _self.request(.{ .create_session = .{ .restore_session = true } }) catch |err| { + _self.request(.create_session) catch |err| { log.err("create session error: {}\n", .{err}); }; @@ -221,10 +214,10 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { const result = c.g_dbus_connection_call_sync( self.dbus, - "org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.GlobalShortcuts", - "ConfigureShortcuts", + XdgDesktopPortal.DBUS_DESTINATION.ptr, + XdgDesktopPortal.DBUS_OBJECT_PATH.ptr, + XdgDesktopPortal.GLOBAL_SHORTCUTS_INTERFACE.ptr, + XdgDesktopPortal.CONFIGURE_SHORTCUTS_METHOD.ptr, payload, null, c.G_DBUS_CALL_FLAGS_NONE, @@ -269,18 +262,14 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { } } - const Method = union(enum) { - create_session: struct { - /// Whether or not to use the existing session token. We probably always want to do this. - /// Keeping this functionality around for future changes. - restore_session: bool, - }, + const Method = enum { + create_session, bind_shortcuts, fn name(self: Method) [:0]const u8 { return switch (self) { - .create_session => "CreateSession", - .bind_shortcuts => "BindShortcuts", + .create_session => XdgDesktopPortal.CREATE_SESSION_METHOD, + .bind_shortcuts => XdgDesktopPortal.BIND_SHORTCUTS_METHOD, }; } @@ -293,30 +282,16 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { switch (self) { // See https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.GlobalShortcuts.html#org-freedesktop-portal-globalshortcuts-createsession .create_session => { - if (self.create_session.restore_session) { - shortcuts.session_token = TokenStorage.load_token_z(shortcuts.allocator, shortcuts.io, "session_token") catch |err| { - log.err("[make_payload] TokenStorage.load_token_z error: {}", .{err}); - return null; - }; - } - - if (shortcuts.session_token == null) { - shortcuts.session_token = @constCast(TokenManager.generate_token(shortcuts.allocator, shortcuts.io) catch |err| { - log.err("[make_payload] TokenStorage.generate_token error: {}", .{err}); - return null; - }); - TokenStorage.save_token(shortcuts.allocator, shortcuts.io, "session_token", shortcuts.session_token.?) catch |err| { - log.err("[make_payload] TokenStorage.save_token error: {}", .{err}); - return null; - }; - } - - assert(shortcuts.session_token != null); + const session_handle_token = TokenManager.generate_token(shortcuts.allocator, shortcuts.io) catch |err| { + log.err("[make_payload] TokenManager.generate_token error: {}", .{err}); + return null; + }; + defer shortcuts.allocator.free(session_handle_token); return c.g_variant_new_parsed( "({'handle_token': <%s>, 'session_handle_token': <%s>},)", request_token.ptr, - shortcuts.session_token.?.ptr, + session_handle_token.ptr, ); }, // See https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.GlobalShortcuts.html#org-freedesktop-portal-globalshortcuts-bindshortcuts @@ -386,9 +361,9 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { shortcuts.activate_subscription = c.g_dbus_connection_signal_subscribe( shortcuts.dbus, null, - "org.freedesktop.portal.GlobalShortcuts", - "Activated", - "/org/freedesktop/portal/desktop", + XdgDesktopPortal.GLOBAL_SHORTCUTS_INTERFACE.ptr, + XdgDesktopPortal.ACTIVATED_SIGNAL.ptr, + XdgDesktopPortal.DBUS_OBJECT_PATH.ptr, handle, c.G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, shortcut_activated, @@ -539,8 +514,8 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { self.response_subscription = c.g_dbus_connection_signal_subscribe( self.dbus, null, - "org.freedesktop.portal.Request", - "Response", + XdgDesktopPortal.REQUEST_INTERFACE.ptr, + XdgDesktopPortal.RESPONSE_SIGNAL.ptr, request_path, null, c.G_DBUS_SIGNAL_FLAGS_NONE, @@ -551,9 +526,9 @@ pub const XdgDesktopPortalGlobalShortcuts = struct { c.g_dbus_connection_call( self.dbus, - "org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.GlobalShortcuts", + XdgDesktopPortal.DBUS_DESTINATION.ptr, + XdgDesktopPortal.DBUS_OBJECT_PATH.ptr, + XdgDesktopPortal.GLOBAL_SHORTCUTS_INTERFACE.ptr, method.name(), payload, null, diff --git a/src/main.zig b/src/main.zig index 4463c66..769a472 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,6 +15,7 @@ const ipc_module = @import("./ipc/ipc.zig"); const IpcCommand = ipc_module.IpcCommand; const Env = @import("./env.zig"); const Logger = @import("./logger.zig"); +const PlatformAppRegistration = @import("./app_registration/platform_app_registration.zig").PlatformAppRegistration; const log = std.log.scoped(.main); @@ -88,6 +89,9 @@ fn cli_app(allocator: std.mem.Allocator, io: std.Io, parsed_args: ?args.Args) !b /// and launch the UI/event loop. fn gui_app(allocator: std.mem.Allocator, io: std.Io, parsed_args: ?args.Args) !void { _ = parsed_args; + var app_registration = try PlatformAppRegistration.init(); + defer app_registration.deinit(); + PlatformCaptureSetup.init(); defer PlatformCaptureSetup.deinit(); diff --git a/src/ui/sdl.zig b/src/ui/sdl.zig index 56669ce..dca1c01 100644 --- a/src/ui/sdl.zig +++ b/src/ui/sdl.zig @@ -1,4 +1,6 @@ const std = @import("std"); +const build_options = @import("build_options"); +const app_identity = @import("../common/linux/app_identity.zig"); const util = @import("../util.zig"); const imguiz = @import("imguiz").imguiz; @@ -46,8 +48,14 @@ pub fn get_sdl_vulkan_extensions(allocator: std.mem.Allocator) !SDLVulkanExtensi /// If Linux, try Wayland, fallback to x11, which causes a panic because it's not supported. pub fn init() !void { - _ = imguiz.SDL_SetHint(imguiz.SDL_HINT_APP_NAME, "Spacecap"); - _ = imguiz.SDL_SetHint(imguiz.SDL_HINT_APP_ID, "spacecap"); + if (!imguiz.SDL_SetAppMetadata( + app_identity.APP_NAME.ptr, + build_options.version.ptr, + app_identity.APP_ID.ptr, + )) { + log.err("[init] failed to set app metadata: {s}", .{imguiz.SDL_GetError()}); + return error.SDLSetAppMetadataFailure; + } if (util.is_linux()) { if (try try_sdl_init_with_hint("wayland")) { diff --git a/src/ui/tray.zig b/src/ui/tray.zig index 5e6d5ec..cb98793 100644 --- a/src/ui/tray.zig +++ b/src/ui/tray.zig @@ -30,7 +30,10 @@ pub const Tray = struct { }, pub fn init(store: *Store, app_icon: *AppIcon) !Self { - const tray = imguiz.SDL_CreateTray(app_icon.app_icon_surface_blue, "Spacecap") orelse return error.TrayInitCreateTray; + const tray = imguiz.SDL_CreateTray(app_icon.app_icon_surface_blue, "Spacecap") orelse { + log.err("[init] SDL_CreateTray failed: {s}", .{imguiz.SDL_GetError()}); + return error.TrayInitCreateTray; + }; errdefer { imguiz.SDL_DestroyTray(tray); }