macos: run as LSUIElement menubar app with NSStatusItem

Ship Lan Mouse on macOS as an accessory app (no Dock icon, no main
window on launch) with a status-bar item for show/quit. Closing the
window hides it instead of quitting so the menu bar stays the primary
surface.

- build-aux/macos-lsui-element.plist: LSUIElement=true plus
  NSInputMonitoringUsageDescription and NSAppleEventsUsageDescription
  (merged into Info.plist via cargo-bundle's osx_info_plist_exts).
- lan-mouse-gtk/src/macos_status_item.rs: NSStatusItem setup via raw
  objc_msgSend FFI. Loads a bundled 22pt PNG as a template image so
  it auto-tints for light/dark menu bars.
- scripts/makeicns.sh: emit Contents/Resources/menubar-template.png
  from the existing SVG.
- scripts/copy-macos-dylib.sh: flatten cargo-bundle's preserved
  target/ subdir under Resources so NSBundle pathForResource: finds
  the template image.
- lan-mouse-gtk/src/lib.rs: register the new modules, set up a
  Cmd+Q-wired quit action, configure bundle env vars (schemas,
  XDG_DATA_DIRS, GTK_DATA_PREFIX) when running from inside the .app,
  and filter the known upstream Gtk theme-parser warning spam.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jon Kinney
2026-04-24 02:09:12 -05:00
committed by Ferdinand Schober
parent c40e10505b
commit 903b0504e0
8 changed files with 468 additions and 4 deletions

View File

@@ -43,6 +43,9 @@ bundle_path=$(dirname "$(dirname "$(dirname "$exec_path")")")
# Path to the Frameworks directory
fwks_path="$bundle_path/Contents/Frameworks"
mkdir -p "$fwks_path"
# Path to bundled GTK/GSettings data
resources_path="$bundle_path/Contents/Resources"
share_path="$resources_path/share"
# Copy and fix references for a binary (executable or dylib)
#
@@ -58,6 +61,10 @@ fix_references() {
libs=$(otool -L "$bin" | awk -v homebrew="$homebrew_path" '$0 ~ homebrew {print $1}')
echo "$libs" | while IFS= read -r old_path; do
if [ -z "$old_path" ]; then
continue
fi
local base_name="$(basename "$old_path")"
local dest="$fwks_path/$base_name"
@@ -81,6 +88,42 @@ fix_references() {
fix_references "$exec_path"
copy_runtime_data() {
mkdir -p "$share_path"
if [ -d "$homebrew_path/share/glib-2.0/schemas" ]; then
mkdir -p "$share_path/glib-2.0"
rm -rf "$share_path/glib-2.0/schemas"
cp -RL "$homebrew_path/share/glib-2.0/schemas" "$share_path/glib-2.0/schemas"
if command -v glib-compile-schemas >/dev/null 2>&1; then
glib-compile-schemas "$share_path/glib-2.0/schemas"
elif [ -x "$homebrew_path/bin/glib-compile-schemas" ]; then
"$homebrew_path/bin/glib-compile-schemas" "$share_path/glib-2.0/schemas"
fi
fi
if [ -d "$homebrew_path/share/gtk-4.0" ]; then
rm -rf "$share_path/gtk-4.0"
cp -RL "$homebrew_path/share/gtk-4.0" "$share_path/gtk-4.0"
fi
if [ -d "$homebrew_path/share/icons/Adwaita" ]; then
mkdir -p "$share_path/icons"
rm -rf "$share_path/icons/Adwaita"
cp -RL "$homebrew_path/share/icons/Adwaita" "$share_path/icons/Adwaita"
fi
}
copy_runtime_data
# cargo-bundle preserves the source path under Contents/Resources (so
# `target/menubar-template.png` lands at `Resources/target/...`). Flatten it
# so NSBundle pathForResource: finds the file at the Resources root.
if [ -f "$resources_path/target/menubar-template.png" ]; then
mv "$resources_path/target/menubar-template.png" "$resources_path/menubar-template.png"
rmdir "$resources_path/target" 2>/dev/null || true
fi
# Ensure the main executable has our Frameworks path in its RPATH
if ! otool -l "$exec_path" | grep -q "@executable_path/../Frameworks"; then
echo "Adding RPATH to $exec_path"