From cb6cc266c64644100ac72fed8e50d217a677ed2e Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Wed, 18 Feb 2026 21:05:50 -0600 Subject: [PATCH] Add BetterDisplay as a macOS FMA (#40087) **Related issue:** Resolves #40043 --- .../inputs/homebrew/betterdisplay.json | 8 +++++++ ee/maintained-apps/outputs/apps.json | 7 ++++++ .../outputs/betterdisplay/darwin.json | 21 ++++++++++++++++++ .../components/icons/BetterDisplay.tsx | 14 ++++++++++++ .../SoftwarePage/components/icons/index.ts | 2 ++ .../app-icon-betterdisplay-60x60@2x.png | Bin 0 -> 12615 bytes 6 files changed, 52 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/betterdisplay.json create mode 100644 ee/maintained-apps/outputs/betterdisplay/darwin.json create mode 100644 frontend/pages/SoftwarePage/components/icons/BetterDisplay.tsx create mode 100644 website/assets/images/app-icon-betterdisplay-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/betterdisplay.json b/ee/maintained-apps/inputs/homebrew/betterdisplay.json new file mode 100644 index 0000000000..a6e98ace0a --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/betterdisplay.json @@ -0,0 +1,8 @@ +{ + "name": "BetterDisplay", + "unique_identifier": "pro.betterdisplay.BetterDisplay", + "token": "betterdisplay", + "installer_format": "dmg", + "slug": "betterdisplay/darwin", + "default_categories": ["Productivity"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 9e743cf9d8..66cec25ded 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -232,6 +232,13 @@ "unique_identifier": "com.barebones.bbedit", "description": "BBEdit is a text, code, and markup editor." }, + { + "name": "BetterDisplay", + "slug": "betterdisplay/darwin", + "platform": "darwin", + "unique_identifier": "pro.betterdisplay.BetterDisplay", + "description": "BetterDisplay is a display management tool." + }, { "name": "Beyond Compare", "slug": "beyond-compare/darwin", diff --git a/ee/maintained-apps/outputs/betterdisplay/darwin.json b/ee/maintained-apps/outputs/betterdisplay/darwin.json new file mode 100644 index 0000000000..1d036676cb --- /dev/null +++ b/ee/maintained-apps/outputs/betterdisplay/darwin.json @@ -0,0 +1,21 @@ +{ + "versions": [ + { + "version": "4.1.5", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'pro.betterdisplay.BetterDisplay';" + }, + "installer_url": "https://github.com/waydabber/BetterDisplay/releases/download/v4.1.5/BetterDisplay-v4.1.5.dmg", + "install_script_ref": "940c2611", + "uninstall_script_ref": "1ed32f46", + "sha256": "f847dcd0677463288e0e7942da3013eb44df94e5ec5985db8ac1bdb438b4068d", + "default_categories": [ + "Productivity" + ] + } + ], + "refs": { + "1ed32f46": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 && \"$console_user\" == \"root\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nquit_application 'pro.betterdisplay.BetterDisplay'\nsudo rm -rf \"$APPDIR/BetterDisplay.app\"\nsudo rm -rf 'betterdisplaycli'\ntrash $LOGGED_IN_USER '~/Library/Application Support/BetterDisplay'\ntrash $LOGGED_IN_USER '~/Library/Application Support/BetterDummy'\ntrash $LOGGED_IN_USER '~/Library/Caches/pro.betterdisplay.BetterDisplay'\ntrash $LOGGED_IN_USER '~/Library/Caches/SentryCrash/BetterDisplay'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/pro.betterdisplay.BetterDisplay'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/pro.betterdisplay.BetterDisplay.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/pro.betterdisplay.BetterDisplay.plist'\n", + "940c2611": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 && \"$console_user\" == \"root\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 && \"$console_user\" == \"root\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Try to launch the application\n if osascript -e \"tell application id \\\"$bundle_id\\\" to activate\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_and_track_application 'pro.betterdisplay.BetterDisplay'\nif [ -d \"$APPDIR/BetterDisplay.app\" ]; then\n\tsudo mv \"$APPDIR/BetterDisplay.app\" \"$TMPDIR/BetterDisplay.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/BetterDisplay.app\" \"$APPDIR\"\nrelaunch_application 'pro.betterdisplay.BetterDisplay'\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/BetterDisplay.tsx b/frontend/pages/SoftwarePage/components/icons/BetterDisplay.tsx new file mode 100644 index 0000000000..00ec2a9cf3 --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/BetterDisplay.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const BetterDisplay = (props: SVGProps) => ( + + + +); +export default BetterDisplay; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index 1ab46f931a..3931e954e1 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -4,6 +4,7 @@ import { HOST_LINUX_PLATFORMS } from "interfaces/platform"; import { ISoftware } from "interfaces/software"; +import BetterDisplay from "./BetterDisplay"; import ConnectFonts from "./ConnectFonts"; import CrashPlan from "./CrashPlan"; import Nextcloud from "./Nextcloud"; @@ -266,6 +267,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { "aws client vpn": AwsVpnClient, balenaetcher: BalenaEtcher, bbedit: BBEdit, + betterdisplay: BetterDisplay, "beyond compare": BeyondCompare, bitwarden: Bitwarden, blender: Blender, diff --git a/website/assets/images/app-icon-betterdisplay-60x60@2x.png b/website/assets/images/app-icon-betterdisplay-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bae840db2886c5ad621ce184dd16d9dd1736336a GIT binary patch literal 12615 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