Merge pull request 'clan-app: Now displays runtime icon correctly in process overview' (#5019) from Qubasa/clan-core:fix_runtime_icon into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5019
This commit is contained in:
@@ -109,6 +109,7 @@ def app_run(app_opts: ClanAppOptions) -> int:
|
||||
title="Clan App",
|
||||
size=Size(1280, 1024, SizeHint.NONE),
|
||||
shared_threads=shared_threads,
|
||||
app_id="org.clan.app",
|
||||
)
|
||||
|
||||
API.overwrite_fn(get_system_file)
|
||||
|
||||
@@ -5,6 +5,11 @@ import platform
|
||||
from ctypes import CFUNCTYPE, c_char_p, c_int, c_void_p
|
||||
from pathlib import Path
|
||||
|
||||
# Native handle kinds
|
||||
WEBVIEW_NATIVE_HANDLE_KIND_UI_WINDOW = 0
|
||||
WEBVIEW_NATIVE_HANDLE_KIND_UI_WIDGET = 1
|
||||
WEBVIEW_NATIVE_HANDLE_KIND_BROWSER_CONTROLLER = 2
|
||||
|
||||
|
||||
def _encode_c_string(s: str) -> bytes:
|
||||
return s.encode("utf-8")
|
||||
@@ -72,6 +77,10 @@ class _WebviewLibrary:
|
||||
self.webview_create.argtypes = [c_int, c_void_p]
|
||||
self.webview_create.restype = c_void_p
|
||||
|
||||
self.webview_create_with_app_id = self.lib.webview_create_with_app_id
|
||||
self.webview_create_with_app_id.argtypes = [c_int, c_void_p, c_char_p]
|
||||
self.webview_create_with_app_id.restype = c_void_p
|
||||
|
||||
self.webview_destroy = self.lib.webview_destroy
|
||||
self.webview_destroy.argtypes = [c_void_p]
|
||||
|
||||
@@ -105,6 +114,10 @@ class _WebviewLibrary:
|
||||
self.webview_return = self.lib.webview_return
|
||||
self.webview_return.argtypes = [c_void_p, c_char_p, c_int, c_char_p]
|
||||
|
||||
self.webview_get_native_handle = self.lib.webview_get_native_handle
|
||||
self.webview_get_native_handle.argtypes = [c_void_p, c_int]
|
||||
self.webview_get_native_handle.restype = c_void_p
|
||||
|
||||
self.binding_callback_t = CFUNCTYPE(None, c_char_p, c_char_p, c_void_p)
|
||||
|
||||
self.CFUNCTYPE = CFUNCTYPE
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import functools
|
||||
import json
|
||||
import logging
|
||||
import platform
|
||||
import threading
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
@@ -11,7 +12,10 @@ from typing import TYPE_CHECKING, Any
|
||||
from clan_lib.api import MethodRegistry, message_queue
|
||||
from clan_lib.api.tasks import WebThread
|
||||
|
||||
from ._webview_ffi import _encode_c_string, _webview_lib
|
||||
from ._webview_ffi import (
|
||||
_encode_c_string,
|
||||
_webview_lib,
|
||||
)
|
||||
from .webview_bridge import WebviewBridge
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -32,6 +36,21 @@ class FuncStatus(IntEnum):
|
||||
FAILURE = 1
|
||||
|
||||
|
||||
class NativeHandleKind(IntEnum):
|
||||
# Top-level window. @c GtkWindow pointer (GTK), @c NSWindow pointer (Cocoa)
|
||||
# or @c HWND (Win32)
|
||||
UI_WINDOW = 0
|
||||
|
||||
# Browser widget. @c GtkWidget pointer (GTK), @c NSView pointer (Cocoa) or
|
||||
# @c HWND (Win32).
|
||||
UI_WIDGET = 1
|
||||
|
||||
# Browser controller. @c WebKitWebView pointer (WebKitGTK), @c WKWebView
|
||||
# pointer (Cocoa/WebKit) or @c ICoreWebView2Controller pointer
|
||||
# (Win32/WebView2).
|
||||
BROWSER_CONTROLLER = 2
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Size:
|
||||
width: int
|
||||
@@ -46,6 +65,7 @@ class Webview:
|
||||
size: Size | None = None
|
||||
window: int | None = None
|
||||
shared_threads: dict[str, WebThread] | None = None
|
||||
app_id: str | None = None
|
||||
|
||||
# initialized later
|
||||
_bridge: WebviewBridge | None = None
|
||||
@@ -56,7 +76,14 @@ class Webview:
|
||||
def _create_handle(self) -> None:
|
||||
# Initialize the webview handle
|
||||
with_debugger = True
|
||||
handle = _webview_lib.webview_create(int(with_debugger), self.window)
|
||||
|
||||
# Use webview_create_with_app_id only on Linux if app_id is provided
|
||||
if self.app_id and platform.system() == "Linux":
|
||||
handle = _webview_lib.webview_create_with_app_id(
|
||||
int(with_debugger), self.window, _encode_c_string(self.app_id)
|
||||
)
|
||||
else:
|
||||
handle = _webview_lib.webview_create(int(with_debugger), self.window)
|
||||
callbacks: dict[str, Callable[..., Any]] = {}
|
||||
|
||||
# Since we can't use object.__setattr__, we'll initialize differently
|
||||
@@ -217,6 +244,21 @@ class Webview:
|
||||
self._callbacks[name] = c_callback
|
||||
_webview_lib.webview_bind(self.handle, _encode_c_string(name), c_callback, None)
|
||||
|
||||
def get_native_handle(
|
||||
self, kind: NativeHandleKind = NativeHandleKind.UI_WINDOW
|
||||
) -> int | None:
|
||||
"""Get the native handle (platform-dependent).
|
||||
|
||||
Args:
|
||||
kind: Handle kind - NativeHandleKind enum value
|
||||
|
||||
Returns:
|
||||
Native handle as integer, or None if failed
|
||||
|
||||
"""
|
||||
handle = _webview_lib.webview_get_native_handle(self.handle, kind.value)
|
||||
return handle if handle else None
|
||||
|
||||
def unbind(self, name: str) -> None:
|
||||
if name in self._callbacks:
|
||||
_webview_lib.webview_unbind(self.handle, _encode_c_string(name))
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
gobject-introspection,
|
||||
gtk4,
|
||||
lib,
|
||||
stdenv,
|
||||
# macOS-specific dependencies
|
||||
imagemagick,
|
||||
makeWrapper,
|
||||
libicns,
|
||||
}:
|
||||
let
|
||||
source =
|
||||
@@ -91,7 +96,12 @@ pythonRuntime.pkgs.buildPythonApplication {
|
||||
# gtk4 deps
|
||||
wrapGAppsHook4
|
||||
]
|
||||
++ runtimeDependencies;
|
||||
++ runtimeDependencies
|
||||
++ lib.optionals stdenv.hostPlatform.isDarwin [
|
||||
imagemagick
|
||||
makeWrapper
|
||||
libicns
|
||||
];
|
||||
|
||||
# The necessity of setting buildInputs and propagatedBuildInputs to the
|
||||
# same values for your Python package within Nix largely stems from ensuring
|
||||
@@ -148,16 +158,113 @@ pythonRuntime.pkgs.buildPythonApplication {
|
||||
postInstall = ''
|
||||
mkdir -p $out/${pythonRuntime.sitePackages}/clan_app/.webui
|
||||
cp -r ${clan-app-ui}/lib/node_modules/@clan/ui/dist/* $out/${pythonRuntime.sitePackages}/clan_app/.webui
|
||||
mkdir -p $out/share/icons/hicolor
|
||||
cp -r ./clan_app/assets/white-favicons/* $out/share/icons/hicolor
|
||||
|
||||
${lib.optionalString (!stdenv.hostPlatform.isDarwin) ''
|
||||
mkdir -p $out/share/icons/hicolor
|
||||
cp -r ./clan_app/assets/white-favicons/* $out/share/icons/hicolor
|
||||
''}
|
||||
|
||||
${lib.optionalString stdenv.hostPlatform.isDarwin ''
|
||||
set -eu pipefail
|
||||
# Create macOS app bundle structure
|
||||
mkdir -p "$out/Applications/Clan App.app/Contents/"{MacOS,Resources}
|
||||
|
||||
# Create Info.plist
|
||||
cat > "$out/Applications/Clan App.app/Contents/Info.plist" << 'EOF'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Clan App</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Clan App</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>clan-app.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.clan.app</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Clan App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Clan Protocol</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>clan</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
# Create app icon (convert PNG to ICNS using minimal approach to avoid duplicates)
|
||||
# Create a temporary iconset directory structure
|
||||
mkdir clan-app.iconset
|
||||
|
||||
# Create a minimal iconset with only essential, non-duplicate sizes
|
||||
# Each PNG file should map to a unique ICNS type
|
||||
cp ./clan_app/assets/white-favicons/16x16/apps/clan-app.png clan-app.iconset/icon_16x16.png
|
||||
cp ./clan_app/assets/white-favicons/128x128/apps/clan-app.png clan-app.iconset/icon_128x128.png
|
||||
|
||||
# Use libicns png2icns tool to create proper ICNS file with minimal set
|
||||
png2icns "$out/Applications/Clan App.app/Contents/Resources/clan-app.icns" \
|
||||
clan-app.iconset/icon_16x16.png \
|
||||
clan-app.iconset/icon_128x128.png
|
||||
|
||||
# Create PkgInfo file (standard requirement for macOS apps)
|
||||
echo -n "APPL????" > "$out/Applications/Clan App.app/Contents/PkgInfo"
|
||||
|
||||
# Create the main executable script with proper process name
|
||||
cat > "$out/Applications/Clan App.app/Contents/MacOS/Clan App" << EOF
|
||||
#!/bin/bash
|
||||
# Execute with the correct process name for app icon to appear
|
||||
exec -a "\$0" "$out/bin/.clan-app-orig" "\$@"
|
||||
EOF
|
||||
|
||||
chmod +x "$out/Applications/Clan App.app/Contents/MacOS/Clan App"
|
||||
set +eu pipefail
|
||||
''}
|
||||
'';
|
||||
|
||||
# TODO: If we start clan-app over the cli the process name is "python" and icons don't show up correctly on macOS
|
||||
# I looked in how blender does it, but couldn't figure it out yet.
|
||||
# They do an exec -a in their wrapper script, but that doesn't seem to work here.
|
||||
|
||||
# Don't leak python packages into a devshell.
|
||||
# It can be very confusing if you `nix run` than load the cli from the devshell instead.
|
||||
postFixup = ''
|
||||
rm $out/nix-support/propagated-build-inputs
|
||||
''
|
||||
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
|
||||
set -eu pipefail
|
||||
mv $out/bin/clan-app $out/bin/.clan-app-orig
|
||||
|
||||
|
||||
# Create command line wrapper that executes the app bundle
|
||||
cat > $out/bin/clan-app << EOF
|
||||
#!/bin/bash
|
||||
exec "$out/Applications/Clan App.app/Contents/MacOS/Clan App" "\$@"
|
||||
EOF
|
||||
chmod +x $out/bin/clan-app
|
||||
set +eu pipefail
|
||||
'';
|
||||
checkPhase = ''
|
||||
set -eu pipefail
|
||||
export FONTCONFIG_FILE=${fontconfig.out}/etc/fonts/fonts.conf
|
||||
export FONTCONFIG_PATH=${fontconfig.out}/etc/fonts
|
||||
|
||||
@@ -171,6 +278,7 @@ pythonRuntime.pkgs.buildPythonApplication {
|
||||
fc-list
|
||||
|
||||
PYTHONPATH= $out/bin/clan-app --help
|
||||
set +eu pipefail
|
||||
'';
|
||||
desktopItems = [ desktop-file ];
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
if ! command -v xdg-mime &> /dev/null; then
|
||||
echo "Warning: 'xdg-mime' is not available. The desktop file cannot be installed."
|
||||
fi
|
||||
|
||||
ALREADY_INSTALLED=$(nix profile list --json | jq 'has("elements") and (.elements | has("clan-app"))')
|
||||
|
||||
if [ "$ALREADY_INSTALLED" = "true" ]; then
|
||||
@@ -14,9 +9,23 @@ else
|
||||
nix profile install .#clan-app
|
||||
fi
|
||||
|
||||
# Check OS type
|
||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
|
||||
# install desktop file
|
||||
set -eou pipefail
|
||||
DESKTOP_FILE_NAME=org.clan.app.desktop
|
||||
if ! command -v xdg-mime &> /dev/null; then
|
||||
echo "Warning: 'xdg-mime' is not available. The desktop file cannot be installed."
|
||||
fi
|
||||
|
||||
xdg-mime default "$DESKTOP_FILE_NAME" x-scheme-handler/clan
|
||||
# install desktop file on Linux
|
||||
set -eou pipefail
|
||||
DESKTOP_FILE_NAME=org.clan.app.desktop
|
||||
xdg-mime default "$DESKTOP_FILE_NAME" x-scheme-handler/clan
|
||||
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
echo "macOS detected."
|
||||
mkdir -p ~/Applications
|
||||
ln -sf ~/.nix-profile/Applications/Clan\ App.app ~/Applications
|
||||
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f ~/Applications/Clan\ App.app
|
||||
else
|
||||
echo "Unsupported OS: $OSTYPE"
|
||||
fi
|
||||
|
||||
9
pkgs/clan-app/macos-remote.sh
Executable file
9
pkgs/clan-app/macos-remote.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eou pipefail
|
||||
|
||||
rsync --exclude result --exclude .direnv --exclude node_modules --delete -r ~/Projects/clan-core/pkgs/clan-app mac-mini-dev:~/clan-core/pkgs
|
||||
|
||||
ssh mac-mini-dev "cd \$HOME/clan-core/pkgs/clan-app && nix build .#clan-app -Lv --show-trace"
|
||||
ssh mac-mini-dev "cd \$HOME/clan-core/pkgs/clan-app && ./install-desktop.sh"
|
||||
|
||||
@@ -24,19 +24,10 @@ clangStdenv.mkDerivation {
|
||||
domain = "git.clan.lol";
|
||||
owner = "clan";
|
||||
repo = "webview";
|
||||
rev = "ef481aca8e531f6677258ca911c61aaaf71d2214";
|
||||
hash = "sha256-KF9ESpo40z6VXyYsZCLWJAIh0RFe1Zy/Qw4k7cTpoYU=";
|
||||
rev = "c27041cb50f79c197080a3f4fa2bad4557ef3234";
|
||||
hash = "sha256-xNkX7O+GFMbv3YnXPrtO6vw+BUqCbVeFd8FjgPKfEG0=";
|
||||
};
|
||||
|
||||
# @Mic92: Where is this revision coming from? I can't see it in any of the branches.
|
||||
# I removed the icon python code for now
|
||||
# src = pkgs.fetchFromGitHub {
|
||||
# owner = "clan-lol";
|
||||
# repo = "webview";
|
||||
# rev = "7d24f0192765b7e08f2d712fae90c046d08f318e";
|
||||
# hash = "sha256-yokVI9tFiEEU5M/S2xAeJOghqqiCvTelLo8WLKQZsSY=";
|
||||
# };
|
||||
|
||||
outputs = [
|
||||
"out"
|
||||
"dev"
|
||||
|
||||
Reference in New Issue
Block a user