From 5f06b32574d2ef1e7727aea8e572381593778de5 Mon Sep 17 00:00:00 2001 From: Vincent Liu Date: Tue, 3 Aug 2021 17:42:41 +1000 Subject: [PATCH] update SwordRPC (fix issue #2), clean up code --- README.md | 10 +- RPFX.xcodeproj/project.pbxproj | 40 ++-- .../xcshareddata/swiftpm/Package.resolved | 15 +- .../xcshareddata/xcschemes/RPFX.xcscheme | 2 +- RPFX/AppDelegate.swift | 179 ++++++++---------- RPFX/Applescript.swift | 11 +- RPFX/Constants.swift | 24 ++- RPFX/HelperFunctions.swift | 2 +- 8 files changed, 135 insertions(+), 148 deletions(-) diff --git a/README.md b/README.md index c6f8065..0a60903 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ # RPFX -**RPFX** is a Discord **R**ich **P**resence client **f**or **X**code. +**RPFX** is a Discord **R**ich **P**resence client **f**or **X**code that lets you share what you're programming on your Discord status. rpfx -You can show all your friends what you're coding up right now! - RPFX will display the current file you're working on, as well as your workspace. In addition, it will also show file icons for the following file types: @@ -17,14 +15,14 @@ In addition, it will also show file icons for the following file types: - `.cpp` - `.c` -RPFX is designed to be fairly modular, so you can tweak its code to use your own Discord application if you want to add more file icons (or any other functionality). +If you wish, you can tweak RPFX to use your own Discord application if you want to add custom functionality, such as more file icons. ## Dependencies RPFX uses [my fork](https://github.com/PKBeam/SwordRPC) of [Azoy's SwordRPC](https://github.com/Azoy/SwordRPC). ## System Requirements -- macOS (the latest release is built for macOS 11, but you may be able to run this on older versions) -- Xcode installed (otherwise this wouldn't be very useful) +- macOS Big Sur (11.0) +- Xcode and Discord installed (otherwise this program isn't very useful) ## Usage When you first start up RPFX, it will prompt you for permission to control Xcode. We don't actually need to *control* Xcode, diff --git a/RPFX.xcodeproj/project.pbxproj b/RPFX.xcodeproj/project.pbxproj index e7eb62c..fada251 100644 --- a/RPFX.xcodeproj/project.pbxproj +++ b/RPFX.xcodeproj/project.pbxproj @@ -8,8 +8,8 @@ /* Begin PBXBuildFile section */ 5028EA45244B181D003A1830 /* HelperFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5028EA44244B181D003A1830 /* HelperFunctions.swift */; }; + 506555DE26B90E0100A4416F /* SwordRPC in Frameworks */ = {isa = PBXBuildFile; productRef = 506555DD26B90E0100A4416F /* SwordRPC */; }; 5068E2DF244AA3C90012CCF7 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5068E2DE244AA3C90012CCF7 /* Constants.swift */; }; - 5068E2E2244AD9170012CCF7 /* SwordRPC in Frameworks */ = {isa = PBXBuildFile; productRef = 5068E2E1244AD9170012CCF7 /* SwordRPC */; }; 50C66C1B244B3A5800A4BECD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50C66C1A244B3A5800A4BECD /* Main.storyboard */; }; 50C6CBB3244974A400F10C5D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C6CBB2244974A400F10C5D /* AppDelegate.swift */; }; 50C6CBB7244974A700F10C5D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50C6CBB6244974A700F10C5D /* Assets.xcassets */; }; @@ -19,6 +19,7 @@ /* Begin PBXFileReference section */ 5028EA44244B181D003A1830 /* HelperFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperFunctions.swift; sourceTree = ""; }; + 506555DF26B9210700A4416F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; 5068E2DE244AA3C90012CCF7 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 5068E2E3244ADAFE0012CCF7 /* SwordRPC */ = {isa = PBXFileReference; lastKnownFileType = folder; name = SwordRPC; path = "../SwordRPC-fork/SwordRPC"; sourceTree = ""; }; 50C66C1A244B3A5800A4BECD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; @@ -36,7 +37,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5068E2E2244AD9170012CCF7 /* SwordRPC in Frameworks */, + 506555DE26B90E0100A4416F /* SwordRPC in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -63,6 +64,7 @@ 50C6CBB1244974A400F10C5D /* RPFX */ = { isa = PBXGroup; children = ( + 506555DF26B9210700A4416F /* README.md */, 50C6CBB2244974A400F10C5D /* AppDelegate.swift */, 5028EA44244B181D003A1830 /* HelperFunctions.swift */, 5068E2DE244AA3C90012CCF7 /* Constants.swift */, @@ -101,7 +103,7 @@ ); name = RPFX; packageProductDependencies = ( - 5068E2E1244AD9170012CCF7 /* SwordRPC */, + 506555DD26B90E0100A4416F /* SwordRPC */, ); productName = RPFX; productReference = 50C6CBAF244974A400F10C5D /* RPFX.app */; @@ -114,7 +116,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1110; - LastUpgradeCheck = 1200; + LastUpgradeCheck = 1250; ORGANIZATIONNAME = "Vincent Liu"; TargetAttributes = { 50C6CBAE244974A400F10C5D = { @@ -133,7 +135,7 @@ ); mainGroup = 50C6CBA6244974A400F10C5D; packageReferences = ( - 5068E2E0244AD9170012CCF7 /* XCRemoteSwiftPackageReference "SwordRPC" */, + 506555DC26B90E0100A4416F /* XCRemoteSwiftPackageReference "SwordRPC" */, ); productRefGroup = 50C6CBB0244974A400F10C5D /* Products */; projectDirPath = ""; @@ -212,7 +214,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = s; + GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -223,13 +225,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -278,12 +280,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -304,10 +306,9 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 11.1; + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.PKBeam.RPFX; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -328,10 +329,9 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 11.1; + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.PKBeam.RPFX; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; }; name = Release; }; @@ -359,20 +359,20 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 5068E2E0244AD9170012CCF7 /* XCRemoteSwiftPackageReference "SwordRPC" */ = { + 506555DC26B90E0100A4416F /* XCRemoteSwiftPackageReference "SwordRPC" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/PKBeam/SwordRPC"; + repositoryURL = "https://github.com/PKBeam/SwordRPC.git"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.2.2; + branch = master; + kind = branch; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 5068E2E1244AD9170012CCF7 /* SwordRPC */ = { + 506555DD26B90E0100A4416F /* SwordRPC */ = { isa = XCSwiftPackageProductDependency; - package = 5068E2E0244AD9170012CCF7 /* XCRemoteSwiftPackageReference "SwordRPC" */; + package = 506555DC26B90E0100A4416F /* XCRemoteSwiftPackageReference "SwordRPC" */; productName = SwordRPC; }; /* End XCSwiftPackageProductDependency section */ diff --git a/RPFX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RPFX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1f92589..17996a8 100644 --- a/RPFX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RPFX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -3,11 +3,20 @@ "pins": [ { "package": "Socket", - "repositoryURL": "https://github.com/IBM-Swift/BlueSocket.git", + "repositoryURL": "https://github.com/Kitura/BlueSocket.git", "state": { "branch": null, - "revision": "26c8f4e4e3000421a0c97aaf0c7b5e9aa5475222", - "version": "0.12.94" + "revision": "68ec325aa8649253a71d637099942f3298a70324", + "version": "2.0.1" + } + }, + { + "package": "swift-argument-parser", + "repositoryURL": "https://github.com/apple/swift-argument-parser", + "state": { + "branch": null, + "revision": "83b23d940471b313427da226196661856f6ba3e0", + "version": "0.4.4" } } ] diff --git a/RPFX.xcodeproj/xcshareddata/xcschemes/RPFX.xcscheme b/RPFX.xcodeproj/xcshareddata/xcschemes/RPFX.xcscheme index 7b4757d..591f52d 100644 --- a/RPFX.xcodeproj/xcshareddata/xcschemes/RPFX.xcscheme +++ b/RPFX.xcodeproj/xcshareddata/xcschemes/RPFX.xcscheme @@ -1,6 +1,6 @@ 0 - var discordOpen = NSWorkspace.shared.runningApplications.filter({$0.bundleIdentifier == discordBundleId}).count > 0 + func scanRunningApplications() { + let runningApps = NSWorkspace.shared.runningApplications + let xcodeOpen = runningApps.contains(where: {$0.bundleIdentifier == xcodeBundleId}) + let discordOpen = runningApps.contains(where: {$0.bundleIdentifier == discordBundleId}) - // one time check on startup if xcodeOpen && discordOpen { initRPC() + } else if rpc != nil { + deinitRPC() } + } - // run on application launch - NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.didLaunchApplicationNotification, object: nil, queue: nil, using: { notif in - if let app = notif.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication { - let appName = app.bundleIdentifier - if appName == xcodeBundleId { - debugPrint("xcode open") - xcodeOpen = true - } - if appName == discordBundleId { - debugPrint("discord open") - discordOpen = true - } - - if xcodeOpen && discordOpen { - self.initRPC() - } - } - }) - - // run on application close - NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.didTerminateApplicationNotification, object: nil, queue: nil, using: { notif in - if let app = notif.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication { - let appName = app.bundleIdentifier - if appName == xcodeBundleId { - debugPrint("xcode closed") - xcodeOpen = false - self.deinitRPC() - } - if appName == discordBundleId { - debugPrint("discord closed") - discordOpen = false - self.deinitRPC() + func applicationDidFinishLaunching(_ aNotification: Notification) { + debugPrint("RPFX launched") + scanRunningApplications() + + // closure that updates RPC connection status if Xcode/Discord were involved in the notification + let onNotif: (Notification) -> Void = { notification in + if let app = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication { + if [xcodeBundleId, discordBundleId].contains(app.bundleIdentifier) { + self.scanRunningApplications() } } - }) - + } + let notifCenter = NSWorkspace.shared.notificationCenter + notifCenter.addObserver(forName: NSWorkspace.didLaunchApplicationNotification, object: nil, queue: nil, using: onNotif) + notifCenter.addObserver(forName: NSWorkspace.didTerminateApplicationNotification, object: nil, queue: nil, using: onNotif) } func applicationWillTerminate(_ aNotification: Notification) { // Insert code here to tear down your application debugPrint("RPFX shutting down...") - deinitRPC() - statusUpdateTimer?.invalidate() - discordConnectTimer?.invalidate() + if rpc != nil { + deinitRPC() + } } - - } extension AppDelegate: SwordRPCDelegate { func swordRPCDidConnect(_ rpc: SwordRPC) { debugPrint("SwordRPC connected") - startDate = Date() - beginTimer() - } - func swordRPCDidDisconnect(_ rpc: SwordRPC, code: Int?, message msg: String?) { - debugPrint("disconnected") - statusUpdateTimer?.invalidate() + // record the time the connection was initiated + connectionStartDate = Date() + + // create status update timer + statusUpdateTimer = Timer.init( + timeInterval: statusRefreshInterval, + repeats: true, + block: { _ in + self.updateStatus() + } + ) + // for some reason, a scheduledTimer here won't refire + // (workaround: manually add it to the common RunLoop) + RunLoop.main.add(statusUpdateTimer, forMode: .common) + statusUpdateTimer.fire() } - func swordRPCDidReceiveError(_ rpc: SwordRPC, code: Int, message msg: String) { - + func swordRPCDidDisconnect(_ rpc: SwordRPC, code: Int?, message msg: String?) { + debugPrint("SwordRPC disconnected") + // stop status update timer + statusUpdateTimer.invalidate() } } diff --git a/RPFX/Applescript.swift b/RPFX/Applescript.swift index 4b6f73f..08dc9cb 100644 --- a/RPFX/Applescript.swift +++ b/RPFX/Applescript.swift @@ -59,13 +59,10 @@ func getActiveFilename() -> String? { } // find the first window title that matches a filename - // (the first window name is the one in focus) - for window in windowNames { - // make sure the focused window refers to a file - for file in fileNames { - if file == window { - return file - } + for window in windowNames { // iterate in order: the first window name is the one in focus + // check the focused window refers to a file + if fileNames.contains(window) { + return window } } diff --git a/RPFX/Constants.swift b/RPFX/Constants.swift index 663f16a..246bc84 100644 --- a/RPFX/Constants.swift +++ b/RPFX/Constants.swift @@ -8,20 +8,22 @@ import Foundation -let debug = false +let debug = true -// used to register for notifs when Xcode opens/closes let xcodeBundleId = "com.apple.dt.Xcode" let discordBundleId = "com.hnc.Discord" // how often we check Xcode for a status update -let refreshInterval = 5.0 // seconds +let statusRefreshInterval = 5.0 // seconds + // how long to wait between attempts to connect to discord RPC -let discordRPCconnectInterval = 5.0 // seconds +let discordConnectInterval = 5.0 // seconds + +// name of an anonymous workspace in Xcode +let xcodeUntitledWorkspace = "Untitled" // some other window names of Xcode // Can we tell if the user is browsing Developer Docs? - //let xcodeWindowNames = [ // "Devices", // Devices and Simulators // "Organiser", // Organiser @@ -31,12 +33,14 @@ let discordRPCconnectInterval = 5.0 // seconds // // prefix "Add Package to " //] -// The following constants are for use with the Discord App -// if you're using your own Discord App, update this as needed +/* + The following constants are for use with the Discord App. + If you're using your own Discord App, update this as needed. +*/ let discordClientId = "700358131481444403" -// discord image keys of supported file types +// image keys of supported file types let discordRPImageKeys = [ "swift", "playground", @@ -48,9 +52,9 @@ let discordRPImageKeys = [ "c", ] -// fallback for unsupported file types +// fallback image for unsupported file types let discordRPImageKeyDefault = "file" -// Xcode application icon +// image for Xcode icon let discordRPImageKeyXcode = "xcode" diff --git a/RPFX/HelperFunctions.swift b/RPFX/HelperFunctions.swift index 4c5d70b..b5029f0 100644 --- a/RPFX/HelperFunctions.swift +++ b/RPFX/HelperFunctions.swift @@ -10,7 +10,7 @@ import Foundation func debugPrint(_ msg: String) { if debug { - print(msg) + print("[RPFX] " + msg) } }