From 8fd14fefa175ed9cd518c17d822e39a1bd273ff7 Mon Sep 17 00:00:00 2001 From: Din Tort Date: Mon, 27 Oct 2025 00:25:10 +0100 Subject: [PATCH] Add scripts to create macOS app bundle and LaunchAgent #1244 This change introduces two new helper scripts for macOS users to improve integration with the operating system. The `create-macos-app.sh` script builds a minimal `.app` wrapper for Solaar. This allows Solaar to be treated as a standard macOS application, enabling it to request necessary permissions, such as input monitoring, and to be discoverable by the OS. The script generates the required directory structure, a wrapper executable to launch Solaar, a standard `Info.plist` file, and an application icon from the source PNG. The `create-macos-launchagent.sh` script sets up a LaunchAgent to automatically start Solaar at login and keep it running in the background. This ensures that the Solaar process is always available for device management without requiring manual intervention from the user. The script also configures log file locations for standard output and error streams. Together, these scripts provide a more native and user-friendly experience for Solaar on macOS. https://github.com/pwr-Solaar/Solaar/issues/1244 --- tools/create-macos-app.sh | 95 +++++++++++++++++++++++++++++++ tools/create-macos-launchagent.sh | 66 +++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100755 tools/create-macos-app.sh create mode 100755 tools/create-macos-launchagent.sh diff --git a/tools/create-macos-app.sh b/tools/create-macos-app.sh new file mode 100755 index 00000000..8f4a70ef --- /dev/null +++ b/tools/create-macos-app.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# Helper to build a minimal macOS .app wrapper for Solaar. +set -euo pipefail + +APP_ROOT=${1:-/Applications/Solaar.app} +SOLAR_PATH=${SOLAR_PATH:-/opt/homebrew/bin/solaar} +ICON_SOURCE=${ICON_SOURCE:-share/solaar/icons/solaar-light_100.png} + +if [[ ! -x "${SOLAR_PATH}" ]]; then + echo "Error: Unable to execute ${SOLAR_PATH}. Set SOLAR_PATH to the solaar binary." >&2 + exit 1 +fi + +case "${APP_ROOT}" in + ""|"/"|".") + echo "Error: Refusing to create app bundle at unsafe location: \"${APP_ROOT}\"" >&2 + exit 1 + ;; +esac + +echo "Creating Solaar app bundle at ${APP_ROOT}" +rm -rf "${APP_ROOT}" + +APP_CONTENTS="${APP_ROOT}/Contents" +MACOS_DIR="${APP_CONTENTS}/MacOS" +RESOURCES_DIR="${APP_CONTENTS}/Resources" + +mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}" + +WRAPPER="${MACOS_DIR}/solaar-wrapper" +cat > "${WRAPPER}" </dev/null 2>&1 && command -v iconutil >/dev/null 2>&1 && [[ -f "${ICON_SOURCE}" ]]; then + TMP_ICONSET=$(mktemp -d /tmp/solaar-iconset.XXXXXX) + trap 'rm -rf "${TMP_ICONSET}"' EXIT + for SIZE in 16 32 64 128 256 512; do + sips -s format png -z "${SIZE}" "${SIZE}" "${ICON_SOURCE}" --out "${TMP_ICONSET}/icon_${SIZE}x${SIZE}.png" >/dev/null + DOUBLE=$((SIZE * 2)) + sips -s format png -z "${DOUBLE}" "${DOUBLE}" "${ICON_SOURCE}" --out "${TMP_ICONSET}/icon_${SIZE}x${SIZE}@2x.png" >/dev/null + done + if iconutil -c icns "${TMP_ICONSET}" -o "${RESOURCES_DIR}/solaar.icns" >/dev/null 2>&1; then + HAVE_ICON=1 + echo "Added icon from ${ICON_SOURCE}" + else + echo "Warning: Failed to create solaar.icns – continuing without custom icon" >&2 + fi + rm -rf "${TMP_ICONSET}" + trap - EXIT +else + echo "Skipping icon generation (requires sips, iconutil, and ${ICON_SOURCE})" +fi + +{ +cat < + + + + CFBundleExecutable + solaar-wrapper + CFBundleIdentifier + io.github.pwr-solaar.solaar + CFBundleName + Solaar + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + LSMinimumSystemVersion + 11.0 + NSInputMonitoringUsageDescription + Solaar needs to access input devices to configure and monitor your Logitech keyboards, mice, and other peripherals. +EOF +if [[ ${HAVE_ICON} -eq 1 ]]; then +cat <<'EOF' + CFBundleIconFile + solaar.icns +EOF +fi +cat <<'EOF' + + +EOF +} > "${APP_CONTENTS}/Info.plist" + +echo "Solaar app bundle created at ${APP_ROOT}" +echo "" +echo "To install the LaunchAgent for automatic startup and keep-alive execute:" +echo " bash tools/create-macos-launchagent.sh" diff --git a/tools/create-macos-launchagent.sh b/tools/create-macos-launchagent.sh new file mode 100755 index 00000000..4af93d89 --- /dev/null +++ b/tools/create-macos-launchagent.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Helper to install a LaunchAgent for Solaar to keep it running in the background. +set -euo pipefail + +SOLAR_PATH=${SOLAR_PATH:-/opt/homebrew/bin/solaar} + +if [[ ! -x "${SOLAR_PATH}" ]]; then + echo "Error: Unable to execute ${SOLAR_PATH}. Set SOLAR_PATH to the solaar binary." >&2 + exit 1 +fi + +LAUNCH_AGENT_DIR="${HOME}/Library/LaunchAgents" +LAUNCH_AGENT_PLIST="${LAUNCH_AGENT_DIR}/io.github.pwr-solaar.solaar.plist" + +mkdir -p "${LAUNCH_AGENT_DIR}" + +echo "Creating LaunchAgent to keep Solaar running..." + +# Unload if already loaded (suppress errors) +launchctl unload "${LAUNCH_AGENT_PLIST}" 2>/dev/null || true + +cat > "${LAUNCH_AGENT_PLIST}" < + + + + Label + io.github.pwr-solaar.solaar + ProgramArguments + + ${SOLAR_PATH} + --window=hide + + RunAtLoad + + KeepAlive + + StandardOutPath + ${HOME}/Library/Logs/solaar.log + StandardErrorPath + ${HOME}/Library/Logs/solaar.error.log + ProcessType + Background + + +EOF + +launchctl load "${LAUNCH_AGENT_PLIST}" + +echo "LaunchAgent created at ${LAUNCH_AGENT_PLIST}" +echo "" +echo "To disable automatic startup:" +echo " launchctl unload \"${LAUNCH_AGENT_PLIST}\"" +echo "" +echo "To re-enable automatic startup:" +echo " launchctl load \"${LAUNCH_AGENT_PLIST}\"" +echo "" +echo "To start Solaar:" +echo " launchctl start io.github.pwr-solaar.solaar" +echo "" +echo "To stop Solaar:" +echo " launchctl stop io.github.pwr-solaar.solaar" +echo "" +echo "Logs will be written to:" +echo " ${HOME}/Library/Logs/solaar.log" +echo " ${HOME}/Library/Logs/solaar.error.log"