diff --git a/scripts/macosx/pkg_arm64.sh b/scripts/macosx/pkg_arm64.sh index 379ee35..870584d 100644 --- a/scripts/macosx/pkg_arm64.sh +++ b/scripts/macosx/pkg_arm64.sh @@ -78,6 +78,14 @@ EOF echo ".app created successfully." +SIGN_IDENTITY="${CROSSDESK_SIGN_IDENTITY:--}" + +if command -v codesign &> /dev/null; then + codesign --force --deep --sign "$SIGN_IDENTITY" "${APP_BUNDLE}" 2>/dev/null || { + echo "Warning: Code signing failed. The app may not work properly." + } +fi + echo "building pkg..." pkgbuild \ --identifier "${IDENTIFIER}" \ @@ -90,13 +98,38 @@ mkdir -p scripts cat > scripts/postinstall <<'EOF' #!/bin/bash + +IDENTIFIER="cn.crossdesk.app" +APP_NAME="CrossDesk" +APP_PATH="/Applications/${APP_NAME}.app" + USER_HOME=$( /usr/bin/stat -f "%Su" /dev/console ) HOME_DIR=$( /usr/bin/dscl . -read /Users/$USER_HOME NFSHomeDirectory | awk '{print $2}' ) DEST="$HOME_DIR/Library/Application Support/CrossDesk/certs" - mkdir -p "$DEST" -cp -R "/Library/Application Support/CrossDesk/certs/"* "$DEST/" +if [ -d "/Library/Application Support/CrossDesk/certs" ]; then + cp -R "/Library/Application Support/CrossDesk/certs/"* "$DEST/" 2>/dev/null || true +fi + +if [ -d "$APP_PATH" ]; then + touch "$APP_PATH" + /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f "$APP_PATH" 2>/dev/null || true + + USER_ID=$(dscl . -read /Users/$USER_HOME UniqueID 2>/dev/null | awk '{print $2}') + + if [ -n "$USER_ID" ]; then + sleep 1 + launchctl asuser $USER_ID open "$APP_PATH" 2>/dev/null & + APP_PID=$! + sleep 3 + kill "$APP_PID" 2>/dev/null || pkill -f "${APP_NAME}" 2>/dev/null || true + fi + + open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture" 2>/dev/null & + sleep 1 + open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" 2>/dev/null & +fi exit 0 EOF diff --git a/scripts/macosx/pkg_x64.sh b/scripts/macosx/pkg_x64.sh index eccd731..6d76b26 100644 --- a/scripts/macosx/pkg_x64.sh +++ b/scripts/macosx/pkg_x64.sh @@ -78,6 +78,14 @@ EOF echo ".app created successfully." +SIGN_IDENTITY="${CROSSDESK_SIGN_IDENTITY:--}" + +if command -v codesign &> /dev/null; then + codesign --force --deep --sign "$SIGN_IDENTITY" "${APP_BUNDLE}" 2>/dev/null || { + echo "Warning: Code signing failed. The app may not work properly." + } +fi + echo "building pkg..." pkgbuild \ --identifier "${IDENTIFIER}" \ @@ -90,13 +98,38 @@ mkdir -p scripts cat > scripts/postinstall <<'EOF' #!/bin/bash + +IDENTIFIER="cn.crossdesk.app" +APP_NAME="CrossDesk" +APP_PATH="/Applications/${APP_NAME}.app" + USER_HOME=$( /usr/bin/stat -f "%Su" /dev/console ) HOME_DIR=$( /usr/bin/dscl . -read /Users/$USER_HOME NFSHomeDirectory | awk '{print $2}' ) DEST="$HOME_DIR/Library/Application Support/CrossDesk/certs" - mkdir -p "$DEST" -cp -R "/Library/Application Support/CrossDesk/certs/"* "$DEST/" +if [ -d "/Library/Application Support/CrossDesk/certs" ]; then + cp -R "/Library/Application Support/CrossDesk/certs/"* "$DEST/" 2>/dev/null || true +fi + +if [ -d "$APP_PATH" ]; then + touch "$APP_PATH" + /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f "$APP_PATH" 2>/dev/null || true + + USER_ID=$(dscl . -read /Users/$USER_HOME UniqueID 2>/dev/null | awk '{print $2}') + + if [ -n "$USER_ID" ]; then + sleep 1 + launchctl asuser $USER_ID open "$APP_PATH" 2>/dev/null & + APP_PID=$! + sleep 3 + kill "$APP_PID" 2>/dev/null || pkill -f "${APP_NAME}" 2>/dev/null || true + fi + + open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture" 2>/dev/null & + sleep 1 + open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" 2>/dev/null & +fi exit 0 EOF diff --git a/scripts/macosx/sign_app.sh b/scripts/macosx/sign_app.sh new file mode 100644 index 0000000..6a551da --- /dev/null +++ b/scripts/macosx/sign_app.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Helper script to sign macOS app with different certificate options +# Usage: ./sign_app.sh [certificate_name] + +APP_PATH="$1" +CERT_NAME="$2" + +if [ -z "$APP_PATH" ]; then + echo "Usage: $0 [certificate_name]" + echo "" + echo "Examples:" + echo " $0 CrossDesk.app # Ad-hoc signing" + echo " $0 CrossDesk.app \"CrossDesk Dev\" # Sign with certificate name" + echo " $0 CrossDesk.app \"-\" # Explicit ad-hoc signing" + exit 1 +fi + +if [ ! -d "$APP_PATH" ]; then + echo "Error: App bundle not found: $APP_PATH" + exit 1 +fi + +if [ -z "$CERT_NAME" ]; then + # Default to ad-hoc signing + CERT_NAME="-" + echo "Using ad-hoc signing (no certificate specified)" +else + echo "Using certificate: $CERT_NAME" +fi + +# Check if codesign is available +if ! command -v codesign &> /dev/null; then + echo "Error: codesign not found. Please install Xcode Command Line Tools." + exit 1 +fi + +# Sign the app +echo "Signing $APP_PATH..." +codesign --force --deep --sign "$CERT_NAME" "$APP_PATH" + +if [ $? -eq 0 ]; then + echo "Signing successful!" + + # Verify signature + echo "" + echo "Verifying signature..." + codesign -dv --verbose=4 "$APP_PATH" 2>&1 | head -20 + + echo "" + echo "Checking entitlements..." + codesign -d --entitlements - "$APP_PATH" 2>&1 || echo "No entitlements found" +else + echo "Error: Signing failed" + exit 1 +fi + diff --git a/scripts/macosx/uninstall.sh b/scripts/macosx/uninstall.sh new file mode 100644 index 0000000..8e2ad75 --- /dev/null +++ b/scripts/macosx/uninstall.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# macOS uninstall script for CrossDesk +# This script removes the application and cleans up permissions + +APP_NAME="CrossDesk" +IDENTIFIER="cn.crossdesk.app" + +echo "Uninstalling ${APP_NAME}..." + +# Remove application +if [ -d "/Applications/${APP_NAME}.app" ]; then + echo "Removing application..." + rm -rf "/Applications/${APP_NAME}.app" +fi + +# Remove user data +USER_HOME=$(/usr/bin/stat -f "%Su" /dev/console 2>/dev/null) +if [ -n "$USER_HOME" ]; then + HOME_DIR=$(/usr/bin/dscl . -read "/Users/$USER_HOME" NFSHomeDirectory 2>/dev/null | awk '{print $2}') + if [ -n "$HOME_DIR" ]; then + echo "Removing user data..." + rm -rf "$HOME_DIR/Library/Application Support/${APP_NAME}" + rm -rf "$HOME_DIR/Library/Logs/${APP_NAME}" + rm -rf "$HOME_DIR/Library/Caches/${APP_NAME}" + rm -rf "$HOME_DIR/Library/Preferences/${IDENTIFIER}.plist" + rm -rf "$HOME_DIR/Library/LaunchAgents/${APP_NAME}.plist" + fi +fi + +# Remove system-wide certificates +if [ -d "/Library/Application Support/${APP_NAME}/certs" ]; then + echo "Removing system certificates..." + rm -rf "/Library/Application Support/${APP_NAME}" +fi + +# Clean up permissions using tccutil (macOS 10.14+) +# Note: This requires user interaction and may not work in all cases +if command -v tccutil &> /dev/null; then + echo "Cleaning up permissions..." + # Reset screen recording permission + tccutil reset ScreenCapture "$IDENTIFIER" 2>/dev/null || true + # Reset accessibility permission + tccutil reset Accessibility "$IDENTIFIER" 2>/dev/null || true + # Reset camera permission + tccutil reset Camera "$IDENTIFIER" 2>/dev/null || true + # Reset microphone permission + tccutil reset Microphone "$IDENTIFIER" 2>/dev/null || true + echo "Permissions cleaned up. You may need to manually remove them from System Settings > Privacy & Security if they still appear." +else + echo "tccutil not available. Please manually remove permissions from System Settings > Privacy & Security" +fi + +echo "Uninstallation complete." +echo "" +echo "Note: If permissions still appear in System Settings, please remove them manually:" +echo " 1. Open System Settings > Privacy & Security" +echo " 2. Remove ${APP_NAME} from Screen Recording and Accessibility" + +exit 0 + diff --git a/src/screen_capturer/macosx/screen_capturer_sck_impl.mm b/src/screen_capturer/macosx/screen_capturer_sck_impl.mm index 0e59c3d..f668be3 100644 --- a/src/screen_capturer/macosx/screen_capturer_sck_impl.mm +++ b/src/screen_capturer/macosx/screen_capturer_sck_impl.mm @@ -199,10 +199,12 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) { dispatch_semaphore_t sema = dispatch_semaphore_create(0); __block SCShareableContent *content = nil; + __block NSError *capture_error = nil; [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent *result, NSError *error) { if (error) { + capture_error = error; NSLog(@"Failed to get shareable content: %@", error); } else { content = result; @@ -211,8 +213,15 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) { }]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + if (capture_error) { + NSString *error_desc = capture_error.localizedDescription ?: @"Unknown error"; + LOG_ERROR("Failed to get shareable content: {}", + std::string([error_desc UTF8String])); + return 0; + } + if (!content || content.displays.count == 0) { - LOG_ERROR("Failed to get display info"); + LOG_ERROR("Failed to get display info: content is nil or no displays available"); return 0; }