diff --git a/BGMApp/BGMApp.xcodeproj/project.pbxproj b/BGMApp/BGMApp.xcodeproj/project.pbxproj index 41df7ef9..49dcfae5 100644 --- a/BGMApp/BGMApp.xcodeproj/project.pbxproj +++ b/BGMApp/BGMApp.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 1C533C7A1EED28B700270802 /* uninstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 1C533C791EED28B700270802 /* uninstall.sh */; }; 1C533C7B1EED2F6200270802 /* safe_install_dir.sh in Resources */ = {isa = PBXBuildFile; fileRef = 276972901CB16008007A2F7C /* safe_install_dir.sh */; }; 1C533C7C1EED2F8A00270802 /* com.bearisdriving.BGM.XPCHelper.plist.template in Resources */ = {isa = PBXBuildFile; fileRef = 2769728D1CAFCEFD007A2F7C /* com.bearisdriving.BGM.XPCHelper.plist.template */; }; + 1C533C801EF532CA00270802 /* _uninstall-non-interactive.sh in Resources */ = {isa = PBXBuildFile; fileRef = 1C533C7F1EF532CA00270802 /* _uninstall-non-interactive.sh */; }; 1CB8B33D1BBA75EF000E2DD1 /* BGMAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33C1BBA75EF000E2DD1 /* BGMAppDelegate.mm */; }; 1CB8B33F1BBA75EF000E2DD1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33E1BBA75EF000E2DD1 /* main.m */; }; 1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7D1BE5068A00FB8FE4 /* CACFArray.cpp */; }; @@ -216,6 +217,7 @@ 1C46994D1BD7694C00F78043 /* BGMDeviceControlSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlSync.h; sourceTree = ""; }; 1C50FF641EC9F4500031A6EA /* libPublicUtility.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libPublicUtility.a; path = "../../../Library/Developer/Xcode/DerivedData/BGM-cgeucfvbrkmtbnhewbqjwrqspirp/Build/Products/Debug/libPublicUtility.a"; sourceTree = ""; }; 1C533C791EED28B700270802 /* uninstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = uninstall.sh; path = ../../uninstall.sh; sourceTree = ""; }; + 1C533C7F1EF532CA00270802 /* _uninstall-non-interactive.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "_uninstall-non-interactive.sh"; sourceTree = ""; }; 1C8034C21BDAFD5700668E00 /* CAPThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAPThread.cpp; path = PublicUtility/CAPThread.cpp; sourceTree = ""; }; 1C8034C31BDAFD5700668E00 /* CAPThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAPThread.h; path = PublicUtility/CAPThread.h; sourceTree = ""; }; 1CB8B3361BBA75EF000E2DD1 /* Background Music.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Background Music.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -520,6 +522,7 @@ 1CED61681C3081C2002CAFCF /* LICENSE */, 1CC1DF951BE8607700FB8FE4 /* Images.xcassets */, 1CB8B33A1BBA75EF000E2DD1 /* Info.plist */, + 1C533C7F1EF532CA00270802 /* _uninstall-non-interactive.sh */, 1C533C791EED28B700270802 /* uninstall.sh */, 1CB8B33E1BBA75EF000E2DD1 /* main.m */, ); @@ -799,6 +802,7 @@ files = ( 274827951E11052500B31D8D /* MainMenu.xib in Resources */, 1C533C7A1EED28B700270802 /* uninstall.sh in Resources */, + 1C533C801EF532CA00270802 /* _uninstall-non-interactive.sh in Resources */, 1CED61691C3081C2002CAFCF /* LICENSE in Resources */, 1C2FC3041EB4D6E700A76592 /* BGMApp.sdef in Resources */, 1CC1DF961BE8607700FB8FE4 /* Images.xcassets in Resources */, diff --git a/BGMApp/BGMApp/Music Players/BGMVLC.m b/BGMApp/BGMApp/Music Players/BGMVLC.m index e6029e14..d585f833 100644 --- a/BGMApp/BGMApp/Music Players/BGMVLC.m +++ b/BGMApp/BGMApp/Music Players/BGMVLC.m @@ -101,7 +101,7 @@ - (BOOL) unpause { // // VLC's Scripting Bridge interface doesn't seem to have a cleaner way to do this. + (void) togglePlay { - NSString* src = @"tell application \"VLC\" to play"; + NSString* src = @"tell application id \"org.videolan.vlc\" to play"; NSAppleScript* script = [[NSAppleScript alloc] initWithSource:src]; [script executeAndReturnError:nil]; } diff --git a/BGMApp/BGMApp/_uninstall-non-interactive.sh b/BGMApp/BGMApp/_uninstall-non-interactive.sh new file mode 100755 index 00000000..ead51154 --- /dev/null +++ b/BGMApp/BGMApp/_uninstall-non-interactive.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# vim: tw=120: + +# This file is part of Background Music. +# +# Background Music is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 2 of the +# License, or (at your option) any later version. +# +# Background Music is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Background Music. If not, see . + +# +# _uninstall-non-interactive.sh +# +# Copyright © 2016 Nick Jacques +# Copyright © 2016, 2017 Kyle Neideck +# +# Removes BGMApp, BGMDriver and BGMXPCHelper from the system immediately. Run by uninstall.sh and the Homebrew formula. +# + +# TODO: Log commands and their output to uninstall.log, like build_and_install.sh does, rather than just sending +# everything to /dev/null. + +# TODO: Show a custom error message if the script fails, like build_and_install.sh. + +# Halt on errors. +set -e + +PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH + +bold=$(tput bold) +normal=$(tput sgr0) + +app_path="/Applications/Background Music.app" +driver_path="/Library/Audio/Plug-Ins/HAL/Background Music Device.driver" +xpc_path1="/usr/local/libexec/BGMXPCHelper.xpc" +xpc_path2="/Library/Application Support/Background Music/BGMXPCHelper.xpc" + +# Check that files/directories are at most this big before we delete them, just to be safe. +max_size_mb_for_rm=15 + +file_paths=("${app_path}" "${driver_path}" "${xpc_path1}" "${xpc_path2}") + +bgmapp_process_name="Background Music" + +launchd_plist_label="com.bearisdriving.BGM.XPCHelper" +launchd_plist="/Library/LaunchDaemons/${launchd_plist_label}.plist" + +coreaudiod_plist="/System/Library/LaunchDaemons/com.apple.audio.coreaudiod.plist" + +user_group_name="_BGMXPCHelper" + +# We move files to this temp directory and then move the directory to the user's trash at the end of the script. +# Unfortunately, this means that if the user tries to use the "put back" feature the files will just go back to the +# temp directory. +trash_dir="$(mktemp -d -t UninstalledBackgroundMusicFiles)/" + +# Takes a path to a file or directory and returns false if the file/directory is larger than $max_size_mb_for_rm. +function size_check { + local size="$(du -sm "$1" 2>/dev/null | awk '{ print $1 }')" + [[ "${size}" =~ ^[0-9]+$ ]] && [[ "${size}" -le ${max_size_mb_for_rm} ]] +} + +# Ensure that the user can use sudo. (But not if this is a Travis CI build, because then it would fail.) +if ([[ -z ${TRAVIS:-} ]] || [[ "${TRAVIS}" != true ]]) && ! sudo -v; then + echo "ERROR: This script must be run by a user with administrator (sudo) privileges." >&2 + exit 1 +fi + +# Try to kill Background Music.app, in case it's running. +killall "${bgmapp_process_name}" &>/dev/null || true + +# TODO: Use +# mdfind kMDItemCFBundleIdentifier = "com.bearisdriving.BGM.App" +# to offer alternatives if Background Music.app isn't installed to /Applications. Or we could open it with +# open -b "com.bearisdriving.BGM.App" -- delete-yourself +# and have Background Music.app delete itself and close when it gets the "delete-yourself" argument. Though +# that wouldn't be backwards compatible. + +# Remove the files defined in file_paths +for path in "${file_paths[@]}"; do + if [ -e "${path}" ]; then + if size_check "${path}"; then + echo "Moving \"${path}\" to the trash." + sudo mv -f "${path}" "${trash_dir}" &>/dev/null + else + echo "Error: Refusing to delete \"${path}\" because it was much larger than expected." >&2 + fi + fi +done + +echo "Removing Background Music launchd service." +sudo launchctl list | grep "${launchd_plist_label}" >/dev/null && \ + (sudo launchctl bootout system "${launchd_plist}" &>/dev/null || \ + # Try older versions of the command in case the user has an old version of launchctl. + sudo launchctl unbootstrap system "${launchd_plist}" &>/dev/null || \ + sudo launchctl unload "${launchd_plist}" >/dev/null) || \ + echo " Service does not exist." + +echo "Removing Background Music launchd service configuration file." +if [ -e "${launchd_plist}" ]; then + sudo mv -f "${launchd_plist}" "${trash_dir}" +fi + +# Be paranoid about user_group_name because we really don't want to delete every user account. +if ! [[ -z ${user_group_name} ]] && [[ "${user_group_name}" != "" ]]; then + echo "Removing Background Music user." + dscl . -read /Users/"${user_group_name}" &>/dev/null && \ + sudo dscl . -delete /Users/"${user_group_name}" 1>/dev/null || \ + echo " User does not exist." + + echo "Removing Background Music group." + dscl . -read /Groups/"${user_group_name}" &>/dev/null && \ + sudo dscl . -delete /Groups/"${user_group_name}" 1>/dev/null || \ + echo " Group does not exist." +else + echo "Warning: could not delete the Background Music user/group due to an internal error in $0." >&2 +fi + +# We're done removing files, so now actually move trash_dir into the trash. And if that fails, just delete it normally. +osascript -e 'tell application id "com.apple.finder" + move the POSIX file "'"${trash_dir}"'" to trash + end tell' >/dev/null 2>&1 \ + || rm -rf "${trash_dir}" \ + || true + +echo "Restarting Core Audio." +# Wait a little because moving files to the trash plays a short sound. +sleep 2 +# The extra or-clauses are fallback versions of the command that restarts coreaudiod. Apparently some of these commands +# don't work with older versions of launchctl, so I figure there's no harm in trying a bunch of different ways until +# one works. +(sudo launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \ + sudo launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \ + sudo launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \ + sudo launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \ + (sudo launchctl unload "${coreaudiod_plist}" &>/dev/null && \ + sudo launchctl load "${coreaudiod_plist}" &>/dev/null) || \ + sudo killall coreaudiod &>/dev/null) + +echo "..." +sleep 3 + +# TODO: What if they only have one audio device? +echo "" +echo "${bold}Done! Toggle your audio output device in the Sound section of System Preferences to finish" \ + "uninstalling. (Or just restart your computer.)${normal}" + + diff --git a/package.sh b/package.sh index cea12b38..8f63a9d8 100755 --- a/package.sh +++ b/package.sh @@ -90,6 +90,8 @@ cp -R "BGMApp/build/Release/BGMXPCHelper.xpc" "$scripts_dir" set_permissions "pkgroot" chmod 755 "pkgroot/Applications/Background Music.app/Contents/MacOS/Background Music" +chmod 755 "pkgroot/Applications/Background Music.app/Contents/Resources/uninstall.sh" +chmod 755 "pkgroot/Applications/Background Music.app/Contents/Resources/_uninstall-non-interactive.sh" chmod 755 "pkgroot/Library/Audio/Plug-Ins/HAL/Background Music Device.driver/Contents/MacOS/Background Music Device" set_permissions "$scripts_dir" diff --git a/uninstall.sh b/uninstall.sh index 8ad23b44..26b8a8df 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -20,16 +20,11 @@ # uninstall.sh # # Copyright © 2016 Nick Jacques -# Copyright © 2016 Kyle Neideck +# Copyright © 2016, 2017 Kyle Neideck # # Removes BGMApp, BGMDriver and BGMXPCHelper from the system. # -# TODO: Log commands and their output to uninstall.log, like build_and_install.sh does, rather than just sending -# everything to /dev/null. - -# TODO: Show a custom error message if the script fails, like build_and_install.sh. - # Halt on errors. set -e @@ -38,38 +33,6 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH bold=$(tput bold) normal=$(tput sgr0) -app_path="/Applications/Background Music.app" -driver_path="/Library/Audio/Plug-Ins/HAL/Background Music Device.driver" -xpc_path1="/usr/local/libexec/BGMXPCHelper.xpc" -xpc_path2="/Library/Application Support/Background Music/BGMXPCHelper.xpc" - -# Check that files/directories are at most this big before we delete them, just to be safe. -max_size_mb_for_rm=15 - -file_paths=("${app_path}" "${driver_path}" "${xpc_path1}" "${xpc_path2}") - -bgmapp_process_name="Background Music" - -launchd_plist_label="com.bearisdriving.BGM.XPCHelper" -launchd_plist="/Library/LaunchDaemons/${launchd_plist_label}.plist" - -coreaudiod_plist="/System/Library/LaunchDaemons/com.apple.audio.coreaudiod.plist" - -user_group_name="_BGMXPCHelper" - -# We move files to this temp directory and then move the directory to the user's trash at the end of the script. -# Unfortunately, this means that if the user tries to use the "put back" feature the files will just go back to the -# temp directory. -trash_dir="$(mktemp -d -t UninstalledBackgroundMusicFiles)/" - -# Takes a path to a file or directory and returns false if the file/directory is larger than $max_size_mb_for_rm. -function size_check { - local size="$(du -sm "$1" 2>/dev/null | awk '{ print $1 }')" - [[ "${size}" =~ ^[0-9]+$ ]] && [[ "${size}" -le ${max_size_mb_for_rm} ]] -} - -clear - # Warn if running as root. if [[ $(id -u) -eq 0 ]]; then echo "$(tput setaf 11)WARNING$(tput sgr0): This script is not intended to be run as root. Run" \ @@ -77,104 +40,34 @@ if [[ $(id -u) -eq 0 ]]; then echo "" fi -echo "${bold}You are about to uninstall Background Music and its components!${normal}" +echo "${bold}You are about to uninstall Background Music.${normal}" echo "Please pause all audio before continuing." -echo "You must be able to run 'sudo' commands to continue. (But don't worry if you don't know what that means.)" echo "" read -p "Continue (y/N)? " user_prompt if [ "$user_prompt" == "y" ] || [ "$user_prompt" == "Y" ]; then - - # Ensure that the user can use sudo. (But not if this is a Travis CI build, because then it would fail.) - if ([[ -z ${TRAVIS:-} ]] || [[ "${TRAVIS}" != true ]]) && ! sudo -v; then - echo "ERROR: This script must be run by a user with administrator (sudo) privileges." >&2 - exit 1 - fi - - echo "" - - # Try to kill Background Music.app, in case it's running. - killall "${bgmapp_process_name}" &>/dev/null || true - - # TODO: Use - # mdfind kMDItemCFBundleIdentifier = "com.bearisdriving.BGM.App" - # to offer alternatives if Background Music.app isn't installed to /Applications. Or we could open it with - # open -b "com.bearisdriving.BGM.App" -- delete-yourself - # and have Background Music.app delete itself and close when it gets the "delete-yourself" argument. Though - # that wouldn't be backwards compatible. - - # Remove the files defined in file_paths - for path in "${file_paths[@]}"; do - if [ -e "${path}" ]; then - if size_check "${path}"; then - echo "Moving \"${path}\" to the trash." - sudo mv -f "${path}" "${trash_dir}" &>/dev/null - else - echo "Error: Refusing to delete \"${path}\" because it was much larger than expected." - fi - fi - done - - echo "Removing Background Music launchd service." - sudo launchctl list | grep "${launchd_plist_label}" >/dev/null && \ - (sudo launchctl bootout system "${launchd_plist}" &>/dev/null || \ - # Try older versions of the command in case the user has an old version of launchctl. - sudo launchctl unbootstrap system "${launchd_plist}" &>/dev/null || \ - sudo launchctl unload "${launchd_plist}" >/dev/null) || \ - echo " Service does not exist." - - echo "Removing Background Music launchd service configuration file." - if [ -e "${launchd_plist}" ]; then - sudo mv -f "${launchd_plist}" "${trash_dir}" - fi - - # Be paranoid about user_group_name because we really don't want to delete every user account. - if ! [[ -z ${user_group_name} ]] && [[ "${user_group_name}" != "" ]]; then - echo "Removing Background Music user." - dscl . -read /Users/"${user_group_name}" &>/dev/null && \ - sudo dscl . -delete /Users/"${user_group_name}" 1>/dev/null || \ - echo " User does not exist." - - echo "Removing Background Music group." - dscl . -read /Groups/"${user_group_name}" &>/dev/null && \ - sudo dscl . -delete /Groups/"${user_group_name}" 1>/dev/null || \ - echo " Group does not exist." + # Run from the dir containing this script. + cd "$( dirname "${BASH_SOURCE[0]}" )" + + if [ -f "BGMApp/BGMApp/_uninstall-non-interactive.sh" ]; then + # Running from the source directory. + bash "BGMApp/BGMApp/_uninstall-non-interactive.sh" + elif [ -f "_uninstall-non-interactive.sh" ]; then + # Probably running from Background Music.app/Contents/Resources. + bash "_uninstall-non-interactive.sh" else - echo "Warning: could not delete the Background Music user/group due to an internal error in $0." + echo "${bold}ERROR: Could not find _uninstall-non-interactive.sh${normal}" >&2 + exit 1 fi - # We're done removing files, so now actually move trash_dir into the trash. And if that fails, just delete it normally. - osascript -e 'tell application "Finder" to move the POSIX file "'"${trash_dir}"'" to trash' >/dev/null 2>&1 \ - || rm -rf "${trash_dir}" \ - || true - - echo "Restarting Core Audio." - # Wait a little because moving files to the trash plays a short sound. - sleep 2 - # The extra or-clauses are fallback versions of the command that restarts coreaudiod. Apparently some of these commands - # don't work with older versions of launchctl, so I figure there's no harm in trying a bunch of different ways until - # one works. - (sudo launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \ - sudo launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \ - sudo launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \ - sudo launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \ - (sudo launchctl unload "${coreaudiod_plist}" &>/dev/null && \ - sudo launchctl load "${coreaudiod_plist}" &>/dev/null) || \ - sudo killall coreaudiod &>/dev/null) && \ - echo "..." && \ - sleep 3 - # Invalidate sudo ticket sudo -k - # TODO: What if they only have one audio device? - echo -e "\n${bold}Done! Toggle your sound output device in the Sound control panel to complete the uninstall.${normal}" - # Open System Preferences and go to Sound > Output. - osascript -e 'tell application "System Preferences" - activate - reveal anchor "output" of pane id "com.apple.preference.sound" - end tell' >/dev/null || true + osascript -e 'tell application id "com.apple.systempreferences" + activate + reveal anchor "output" of pane id "com.apple.preference.sound" + end tell' >/dev/null || true echo "" else