From e50d43c97c90202d6069c036fbdc68ed08870b79 Mon Sep 17 00:00:00 2001 From: Seungyun Lee Date: Sun, 28 Aug 2022 13:45:35 +0900 Subject: [PATCH 01/27] refactor: Revamp to support new app setting plist --- PlayTools/Controls/PlayInput.swift | 4 +- PlayTools/Keymap/EditorController.swift | 4 +- PlayTools/PlayCover.swift | 2 - PlayTools/PlayLoader.m | 4 +- PlayTools/PlaySettings.swift | 217 +++++++----------------- 5 files changed, 72 insertions(+), 159 deletions(-) diff --git a/PlayTools/Controls/PlayInput.swift b/PlayTools/Controls/PlayInput.swift index e3f2d8dc..917757ad 100644 --- a/PlayTools/Controls/PlayInput.swift +++ b/PlayTools/Controls/PlayInput.swift @@ -20,7 +20,7 @@ final class PlayInput: NSObject { func setup() { actions = [] var counter = 2 - for key in settings.layout { + for key in settings.keymap { if key.count == 4 { actions.append(ButtonAction(id: counter, keyid: Int(key[0]), @@ -107,7 +107,7 @@ final class PlayInput: NSObject { } func initialize() { - if PlaySettings.shared.keymapping == false { + if !PlaySettings.shared.keymapping { return } diff --git a/PlayTools/Keymap/EditorController.swift b/PlayTools/Keymap/EditorController.swift index a882f0e5..0185beb9 100644 --- a/PlayTools/Keymap/EditorController.swift +++ b/PlayTools/Keymap/EditorController.swift @@ -75,7 +75,7 @@ final class EditorController: NSObject { } func showButtons() { - for btn in settings.layout { + for btn in settings.keymap { if let ctrl = ControlModel.createControlFromData(data: btn) { addControlToView(control: ctrl) } @@ -87,7 +87,7 @@ final class EditorController: NSObject { for model in controls { updatedLayout.append(model.save()) } - settings.layout = updatedLayout + settings.keymap = updatedLayout controls = [] view.subviews.forEach { $0.removeFromSuperview() } } diff --git a/PlayTools/PlayCover.swift b/PlayTools/PlayCover.swift index cdd4bfe3..97c0ba8b 100644 --- a/PlayTools/PlayCover.swift +++ b/PlayTools/PlayCover.swift @@ -21,9 +21,7 @@ final public class PlayCover: NSObject { @objc static public func launch() { quitWhenClose() - PlaySettings.shared.setupLayout() PlayInput.shared.initialize() - PlaySettings.shared.clearLegacy() } @objc static public func quitWhenClose() { diff --git a/PlayTools/PlayLoader.m b/PlayTools/PlayLoader.m index 1e1c6cce..34fc3a7f 100644 --- a/PlayTools/PlayLoader.m +++ b/PlayTools/PlayLoader.m @@ -39,8 +39,8 @@ // #define OEM_ID ("J320xAP") // get device model from playcover .plist -#define DEVICE_MODEL ([[[PlaySettings shared] GET_IPAD_MODEL] UTF8String]) -#define OEM_ID ([[[PlaySettings shared] GET_OEM_ID] UTF8String]) +#define DEVICE_MODEL ([[[PlaySettings shared] deviceModel] UTF8String]) +#define OEM_ID ([[[PlaySettings shared] oemID] UTF8String]) static int my_uname(struct utsname *uts) { int result = 0; diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index 620328f2..79c75565 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -1,188 +1,103 @@ import Foundation import UIKit +import os.log let settings = PlaySettings.shared -extension Dictionary { - - func store(_ toURL: URL) throws { - let data = try PropertyListSerialization.data(fromPropertyList: self, format: .xml, options: 0) - try data.write(to: toURL, options: .atomic) - } - - static func read( _ from: URL) throws -> Dictionary? { - var format = PropertyListSerialization.PropertyListFormat.xml - if let data = FileManager.default.contents(atPath: from.path) { - return try PropertyListSerialization - .propertyList(from: data, - options: .mutableContainersAndLeaves, - format: &format) as? Dictionary - } - return nil - } - -} - @objc public final class PlaySettings: NSObject { - - private static let fileExtension = "plist" - @objc public static let shared = PlaySettings() - private static let enableWindowAutoSize = "pc.enableWindowAutoSize" + private static let keywords = ["game", "unity", "metal", "netflix", "opengl", "minecraft", "mihoyo", "disney"] - private static let gamingmodeKey = "pc.gamingMode" + let bundleIdentifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? "" + let settingsUrl: URL + var settingsData: AppSettingsData - lazy var gamingMode: Bool = { - if let key = settings[PlaySettings.gamingmodeKey] as? Bool { - return key + override init() { + settingsUrl = URL(fileURLWithPath: "/Users/\(NSUserName())/Library/Containers/io.playcover.PlayCover") + .appendingPathComponent("App Settings") + .appendingPathComponent("\(bundleIdentifier).plist") + do { + let data = try Data(contentsOf: settingsUrl) + settingsData = try PropertyListDecoder().decode(AppSettingsData.self, from: data) + } catch { + settingsData = AppSettingsData() + os_log("[PlayTools] PlaySettings decode failed.\n%@", type: .error, error as CVarArg) } - return PlaySettings.isGame - }() - - private static let notchKey = "pc.hasNotch" + } - lazy var notch: Bool = { - if let key = settings[PlaySettings.notchKey] as? Bool { - return key + var isGame: Bool = { + if let info = Bundle.main.infoDictionary?.description { + for keyword in PlaySettings.keywords { + if info.contains(keyword) && !info.contains("xbox") { + return true + } + } } return false }() - private static let layoutKey = "pc.layout" + lazy var keymapping = settingsData.keymapping - lazy var layout: [[CGFloat]] = [] { + lazy var keymap = settingsData.keymap { didSet { + settingsData.keymap = keymap + let encoder = PropertyListEncoder() + encoder.outputFormat = .xml do { - settings[PlaySettings.layoutKey] = layout - allPrefs[Bundle.main.bundleIdentifier!] = settings - try allPrefs.store(PlaySettings.settingsUrl) + let data = try encoder.encode(settingsData) + try data.write(to: settingsUrl) } catch { - print("failed to save settings: \(error)") + os_log("[PlayTools] PlaySettings encode failed.\n%@", type: .error, error as CVarArg) } } } - public func setupLayout() { - layout = settings[PlaySettings.layoutKey] as? [[CGFloat]] ?? [] - } - - private static let adaptiveDisplayKey = "pc.adaptiveDisplay" - @objc public var adaptiveDisplay: Bool { - if let key = settings[PlaySettings.adaptiveDisplayKey] as? Bool { - return key - } - return PlaySettings.isGame - } - - private static let keymappingKey = "pc.keymapping" - @objc public var keymapping: Bool { - if let key = settings[PlaySettings.keymappingKey] as? Bool { - return key - } - return PlaySettings.isGame - } - - private static let refreshRateKey = "pc.refreshRate" - @objc lazy public var refreshRate: Int = { - if let key = settings[PlaySettings.refreshRateKey] as? Int { - return key - } - return 60 - }() - - private static let sensivityKey = "pc.sensivity" + lazy var gamingMode: Bool = isGame - @objc lazy public var sensivity: Float = { - if let key = settings[PlaySettings.sensivityKey] as? Float { - return key / 100 - } - return 0.5 - }() + lazy var notch = settingsData.notch - private static let gameWindowSizeHeight = "pc.gameWindowSizeHeight" - @objc lazy public var windowSizeHeight: CGFloat = { - if let key = settings[PlaySettings.gameWindowSizeHeight] as? CGFloat { - return key - } - return 1080.0 - }() + lazy var sensivity = settingsData.sensitivity - private static let gameWindowSizeWidth = "pc.gameWindowSizeWidth" - @objc lazy public var windowSizeWidth: CGFloat = { - if let key = settings[PlaySettings.gameWindowSizeWidth] as? CGFloat { - return key - } - return 1920.0 - }() + lazy var windowSizeHeight = CGFloat(settingsData.windowHeight) - private static let ipadModelKey = "pc.ipadModel" - @objc lazy public var GET_IPAD_MODEL: NSString = { - if let key = settings[PlaySettings.ipadModelKey] as? NSString { - return key - } - return "iPad8,6" - }() + lazy var windowSizeWidth = CGFloat(settingsData.windowWidth) - @objc lazy public var GET_OEM_ID: NSString = { - if let key = settings[PlaySettings.ipadModelKey] as? NSString { - switch key { - case "iPad6,7": - return "J98aAP" - case "iPad8,6": - return "J320xAP" - case "iPad13,8": - return "J522AP" - default: - return "J320xAP" - } - } - return "J320xAP" - }() - - static var isGame: Bool { - if let info = Bundle.main.infoDictionary?.description { - for keyword in PlaySettings.keywords { - if info.contains(keyword) && !info.contains("xbox") { - return true - } - } - } - return false - } + @objc lazy var adaptiveDisplay = isGame - lazy var settings: [String: Any] = { - if let prefs = allPrefs[Bundle.main.bundleIdentifier!] as? [String: Any] { - return prefs - } - return [PlaySettings.adaptiveDisplayKey: PlaySettings.isGame, PlaySettings.keymappingKey: PlaySettings.isGame] - }() + @objc lazy var deviceModel = settingsData.iosDeviceModel as NSString - lazy var allPrefs: [String: Any] = { - do { - if let all = try [String: Any].read(PlaySettings.settingsUrl) { - return all - } - } catch { - print("failed to load settings: \(error)") + @objc lazy var oemID: NSString = { + switch settingsData.iosDeviceModel { + case "iPad6,7": + return "J98aAP" + case "iPad8,6": + return "J320xAP" + case "iPad13,8": + return "J522AP" + default: + return "J320xAP" } - return [:] }() - public func clearLegacy() { - UserDefaults.standard.removeObject(forKey: "layout") - UserDefaults.standard.removeObject(forKey: "pclayout") - UserDefaults.standard.removeObject(forKey: "playcover.macro") - UserDefaults.standard.removeObject(forKey: PlaySettings.sensivityKey) - UserDefaults.standard.removeObject(forKey: PlaySettings.refreshRateKey) - UserDefaults.standard.removeObject(forKey: PlaySettings.keymappingKey) - UserDefaults.standard.removeObject(forKey: PlaySettings.adaptiveDisplayKey) - UserDefaults.standard.removeObject(forKey: PlaySettings.gameWindowSizeWidth) - UserDefaults.standard.removeObject(forKey: PlaySettings.gameWindowSizeHeight) - UserDefaults.standard.removeObject(forKey: PlaySettings.enableWindowAutoSize) - } + @objc lazy var refreshRate = settingsData.refreshRate - public static let settingsUrl = URL(fileURLWithPath: "/Users/\(NSUserName())/Library/Preferences/playcover.plist") +} - private static var keywords = ["game", "unity", "metal", "netflix", "opengl", "minecraft", "mihoyo", "disney"] +struct AppSettingsData: Codable { + var keymapping = true + var mouseMapping = true + var keymap: [[CGFloat]] = [] + var sensitivity: Float = 50 + + var disableTimeout = false + var iosDeviceModel = "iPad8,6" + var refreshRate = 60 + var windowWidth = 1920 + var windowHeight = 1080 + var resolution = 2 + var aspectRatio = 1 + var notch = false + var bypass = false + var version = "2.0.0" } From 76358671d57be850f6ec1374b9288d79f6ef72b6 Mon Sep 17 00:00:00 2001 From: Seungyun Lee Date: Mon, 29 Aug 2022 03:22:43 +0900 Subject: [PATCH 02/27] refactor: Revamp to work with new keymapping plist --- PlayTools.xcodeproj/project.pbxproj | 4 ++ PlayTools/Controls/PlayAction.swift | 25 +++++++ PlayTools/Controls/PlayInput.swift | 32 ++++----- PlayTools/Controls/PlayMice.swift | 6 +- PlayTools/Keymap/ControlModel.swift | 47 ++++--------- PlayTools/Keymap/EditorController.swift | 45 ++++++++++--- PlayTools/Keymap/Keymapping.swift | 87 +++++++++++++++++++++++++ PlayTools/PlaySettings.swift | 15 ----- 8 files changed, 183 insertions(+), 78 deletions(-) create mode 100644 PlayTools/Keymap/Keymapping.swift diff --git a/PlayTools.xcodeproj/project.pbxproj b/PlayTools.xcodeproj/project.pbxproj index 18cdb1a5..e7d4fbd5 100644 --- a/PlayTools.xcodeproj/project.pbxproj +++ b/PlayTools.xcodeproj/project.pbxproj @@ -80,6 +80,7 @@ AA719872287A81A000623C15 /* UITouch+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AA719844287A81A000623C15 /* UITouch+Private.h */; }; AA818CB9287ABFB1000BEE9D /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA818CB8287ABFB1000BEE9D /* IOKit.framework */; }; AA818CBB287ABFD5000BEE9D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA818CBA287ABFD5000BEE9D /* UIKit.framework */; }; + B1E8CF8A28BBE2AB004340D3 /* Keymapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E8CF8928BBE2AB004340D3 /* Keymapping.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -160,6 +161,7 @@ AA719844287A81A000623C15 /* UITouch+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITouch+Private.h"; sourceTree = ""; }; AA818CB8287ABFB1000BEE9D /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; }; AA818CBA287ABFD5000BEE9D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + B1E8CF8928BBE2AB004340D3 /* Keymapping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keymapping.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -264,6 +266,7 @@ AA71979C287A481500623C15 /* EditorController.swift */, AA71979D287A481500623C15 /* DragElementsView.swift */, AA71979E287A481500623C15 /* KeyCodeNames.swift */, + B1E8CF8928BBE2AB004340D3 /* Keymapping.swift */, ); path = Keymap; sourceTree = ""; @@ -517,6 +520,7 @@ AA719850287A81A000623C15 /* UITouch-KIFAdditions.m in Sources */, AA71985F287A81A000623C15 /* IOHIDEvent+KIF.m in Sources */, AA719861287A81A000623C15 /* NSFileManager-KIFAdditions.m in Sources */, + B1E8CF8A28BBE2AB004340D3 /* Keymapping.swift in Sources */, AA71985A287A81A000623C15 /* UIApplication-KIFAdditions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/PlayTools/Controls/PlayAction.swift b/PlayTools/Controls/PlayAction.swift index 2c25062f..de57a5fb 100644 --- a/PlayTools/Controls/PlayAction.swift +++ b/PlayTools/Controls/PlayAction.swift @@ -42,6 +42,16 @@ class ButtonAction: Action { } } + convenience init(id: Int, data: Button) { + self.init( + id: id, + keyid: data.keyCode, + key: GCKeyCode(rawValue: CFIndex(data.keyCode)), + point: CGPoint( + x: data.transform.xCoord.absoluteX, + y: data.transform.yCoord.absoluteY)) + } + func update(pressed: Bool) { if pressed { Toucher.touchcam(point: point, phase: UITouch.Phase.began, tid: id) @@ -79,6 +89,21 @@ class JoystickAction: Action { } } + convenience init(id: Int, data: Joystick) { + self.init( + id: id, + keys: [ + GCKeyCode(rawValue: CFIndex(data.upKeyCode)), + GCKeyCode(rawValue: CFIndex(data.downKeyCode)), + GCKeyCode(rawValue: CFIndex(data.leftKeyCode)), + GCKeyCode(rawValue: CFIndex(data.rightKeyCode)) + ], + center: CGPoint( + x: data.transform.xCoord.absoluteX, + y: data.transform.yCoord.absoluteY), + shift: data.transform.size.absoluteSize) + } + func invalidate() { Toucher.touchcam(point: center, phase: UITouch.Phase.ended, tid: id) self.moving = false diff --git a/PlayTools/Controls/PlayInput.swift b/PlayTools/Controls/PlayInput.swift index 917757ad..d77a1f28 100644 --- a/PlayTools/Controls/PlayInput.swift +++ b/PlayTools/Controls/PlayInput.swift @@ -20,27 +20,21 @@ final class PlayInput: NSObject { func setup() { actions = [] var counter = 2 - for key in settings.keymap { - if key.count == 4 { - actions.append(ButtonAction(id: counter, - keyid: Int(key[0]), - key: GCKeyCode.init(rawValue: CFIndex(key[0])), - point: CGPoint(x: key[1].absoluteX, - y: key[2].absoluteY))) - } else if key.count == 8 { - actions.append(JoystickAction(id: counter, - keys: [GCKeyCode.init(rawValue: CFIndex(key[0])), - GCKeyCode.init(rawValue: CFIndex(key[1])), - GCKeyCode.init(rawValue: CFIndex(key[2])), - GCKeyCode.init(rawValue: CFIndex(key[3]))], - center: CGPoint(x: key[4].absoluteX, - y: key[5].absoluteY), - shift: key[6].absoluteSize)) - } else if key.count == 2 && PlaySettings.shared.gamingMode { - PlayMice.shared.setup(key) - } + for button in keymap.keymapData.buttonModels { + actions.append(ButtonAction(id: counter, data: button)) + counter += 1 + } + + for joystick in keymap.keymapData.joystickModel { + actions.append(JoystickAction(id: counter, data: joystick)) counter += 1 } + if settings.gamingMode { + for mouse in keymap.keymapData.mouseAreaModel { + PlayMice.shared.setup(mouse) + counter += 1 + } + } if let keyboard = GCKeyboard.coalesced?.keyboardInput { keyboard.keyChangedHandler = { _, _, keyCode, _ in if editor.editorMode diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 794d01ca..279b67a4 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -44,8 +44,10 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool return point } - public func setup(_ key: [CGFloat]) { - camera = CameraControl(centerX: key[0].absoluteX, centerY: key[1].absoluteY) + func setup(_ data: MouseArea) { + camera = CameraControl( + centerX: data.transform.xCoord.absoluteX, + centerY: data.transform.yCoord.absoluteY) for mouse in GCMouse.mice() { mouse.mouseInput?.mouseMovedHandler = { _, deltaX, deltaY in if !mode.visible { diff --git a/PlayTools/Keymap/ControlModel.swift b/PlayTools/Keymap/ControlModel.swift index d6d563f4..a098234f 100644 --- a/PlayTools/Keymap/ControlModel.swift +++ b/PlayTools/Keymap/ControlModel.swift @@ -52,33 +52,10 @@ class Element: UIButton { } class ControlModel { - static func createControlFromData(data: [CGFloat]) -> ControlModel? { - if data.count == 4 { - return ButtonModel(data: ControlData(keyCodes: [Int(data[0])], - size: data[3], - xCoord: data[1], - yCoord: data[2], - parent: nil)) - } else if data.count == 8 { - return JoystickModel(data: ControlData(keyCodes: [Int(data[0]), - Int(data[1]), Int(data[2]), - Int(data[3])], - size: data[6], - xCoord: data[4], - yCoord: data[5])) - } else if data.count == 2 { - return MouseAreaModel(data: ControlData(size: 25, xCoord: data[0], yCoord: data[1])) - } - return nil - } var data: ControlData var button: Element - func save() -> [CGFloat] { - return [] - } - func update() {} func focus(_ focus: Bool) {} @@ -128,8 +105,10 @@ class ButtonModel: ControlModel { update() } - override func save() -> [CGFloat] { - return [CGFloat(data.keyCodes[0]), data.xCoord, data.yCoord, data.size] + func save() -> Button { + Button( + keyCode: data.keyCodes[0], + transform: KeyModelTransform(size: data.size, xCoord: data.xCoord, yCoord: data.yCoord)) } override func update() { @@ -235,13 +214,13 @@ class JoystickButtonModel: ControlModel { class JoystickModel: ControlModel { var joystickButtons = [JoystickButtonModel]() - override func save() -> [CGFloat] { - var data = [CGFloat]() - for joystickButton in joystickButtons { - data.append(CGFloat(joystickButton.data.keyCodes[0])) - } - data.append(contentsOf: [self.data.xCoord, self.data.yCoord, self.data.size, self.data.size]) - return data + func save() -> Joystick { + Joystick( + upKeyCode: joystickButtons[0].data.keyCodes[0], + rightKeyCode: joystickButtons[3].data.keyCodes[0], + downKeyCode: joystickButtons[1].data.keyCodes[0], + leftKeyCode: joystickButtons[2].data.keyCodes[0], + transform: KeyModelTransform(size: data.size, xCoord: data.xCoord, yCoord: data.yCoord)) } override init(data: ControlData) { @@ -309,8 +288,8 @@ class JoystickModel: ControlModel { } class MouseAreaModel: ControlModel { - override func save() -> [CGFloat] { - return [data.xCoord, data.yCoord] + func save() -> MouseArea { + MouseArea(transform: KeyModelTransform(size: data.size, xCoord: data.xCoord, yCoord: data.yCoord)) } override func focus(_ focus: Bool) { diff --git a/PlayTools/Keymap/EditorController.swift b/PlayTools/Keymap/EditorController.swift index 0185beb9..c0740a87 100644 --- a/PlayTools/Keymap/EditorController.swift +++ b/PlayTools/Keymap/EditorController.swift @@ -75,19 +75,48 @@ final class EditorController: NSObject { } func showButtons() { - for btn in settings.keymap { - if let ctrl = ControlModel.createControlFromData(data: btn) { - addControlToView(control: ctrl) - } + for button in keymap.keymapData.buttonModels { + let ctrl = ButtonModel(data: ControlData( + keyCodes: [button.keyCode], + size: button.transform.size, + xCoord: button.transform.xCoord, + yCoord: button.transform.yCoord, + parent: nil)) + addControlToView(control: ctrl) + } + for joystick in keymap.keymapData.joystickModel { + let ctrl = JoystickModel(data: ControlData( + keyCodes: [joystick.upKeyCode, joystick.downKeyCode, joystick.leftKeyCode, joystick.rightKeyCode], + size: joystick.transform.size, + xCoord: joystick.transform.xCoord, + yCoord: joystick.transform.yCoord)) + addControlToView(control: ctrl) + } + for mouse in keymap.keymapData.mouseAreaModel { + let ctrl = + MouseAreaModel(data: ControlData( + size: mouse.transform.size, + xCoord: mouse.transform.xCoord, + yCoord: mouse.transform.yCoord)) + addControlToView(control: ctrl) } } func saveButtons() { - var updatedLayout = [[CGFloat]]() - for model in controls { - updatedLayout.append(model.save()) + var keymapData = KeymappingData(bundleIdentifier: keymap.bundleIdentifier) + controls.forEach { + switch $0 { + case let model as JoystickModel: + keymapData.joystickModel.append(model.save()) + case let model as MouseAreaModel: + keymapData.mouseAreaModel.append(model.save()) + case let model as ButtonModel: + keymapData.buttonModels.append(model.save()) + default: + break + } } - settings.keymap = updatedLayout + keymap.keymapData = keymapData controls = [] view.subviews.forEach { $0.removeFromSuperview() } } diff --git a/PlayTools/Keymap/Keymapping.swift b/PlayTools/Keymap/Keymapping.swift new file mode 100644 index 00000000..01830f1d --- /dev/null +++ b/PlayTools/Keymap/Keymapping.swift @@ -0,0 +1,87 @@ +// +// Keymapping.swift +// PlayTools +// +// Created by 이승윤 on 2022/08/29. +// + +import Foundation +import os.log + +let keymap = Keymapping.shared + +class Keymapping { + static let shared = Keymapping() + + let bundleIdentifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? "" + let keymapUrl: URL + var keymapData: KeymappingData { + didSet { + encode() + } + } + + init() { + keymapUrl = URL(fileURLWithPath: "/Users/\(NSUserName())/Library/Containers/io.playcover.PlayCover") + .appendingPathComponent("Keymapping") + .appendingPathComponent("\(bundleIdentifier).plist") + keymapData = KeymappingData(bundleIdentifier: bundleIdentifier) + if !decode() { + encode() + } + } + + func encode() { + let encoder = PropertyListEncoder() + encoder.outputFormat = .xml + do { + let data = try encoder.encode(keymapData) + try data.write(to: keymapUrl) + } catch { + os_log("[PlayTools] Keymapping encode failed.\n%@", type: .error, error as CVarArg) + } + } + + func decode() -> Bool { + do { + let data = try Data(contentsOf: keymapUrl) + keymapData = try PropertyListDecoder().decode(KeymappingData.self, from: data) + return true + } catch { + keymapData = KeymappingData(bundleIdentifier: bundleIdentifier) + os_log("[PlayTools] Keymapping decode failed.\n%@", type: .error, error as CVarArg) + return false + } + } +} + +struct KeyModelTransform: Codable { + var size: CGFloat + var xCoord: CGFloat + var yCoord: CGFloat +} + +struct Button: Codable { + var keyCode: Int + var transform: KeyModelTransform +} + +struct Joystick: Codable { + var upKeyCode: Int + var rightKeyCode: Int + var downKeyCode: Int + var leftKeyCode: Int + var transform: KeyModelTransform +} + +struct MouseArea: Codable { + var transform: KeyModelTransform +} + +struct KeymappingData: Codable { + var buttonModels: [Button] = [] + var joystickModel: [Joystick] = [] + var mouseAreaModel: [MouseArea] = [] + var bundleIdentifier: String + var version = "2.0.0" +} diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index 79c75565..b63005ee 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -39,20 +39,6 @@ let settings = PlaySettings.shared lazy var keymapping = settingsData.keymapping - lazy var keymap = settingsData.keymap { - didSet { - settingsData.keymap = keymap - let encoder = PropertyListEncoder() - encoder.outputFormat = .xml - do { - let data = try encoder.encode(settingsData) - try data.write(to: settingsUrl) - } catch { - os_log("[PlayTools] PlaySettings encode failed.\n%@", type: .error, error as CVarArg) - } - } - } - lazy var gamingMode: Bool = isGame lazy var notch = settingsData.notch @@ -87,7 +73,6 @@ let settings = PlaySettings.shared struct AppSettingsData: Codable { var keymapping = true var mouseMapping = true - var keymap: [[CGFloat]] = [] var sensitivity: Float = 50 var disableTimeout = false From 64e0127f5c4619041bc10505e527763860d542c6 Mon Sep 17 00:00:00 2001 From: Seungyun Lee Date: Mon, 29 Aug 2022 03:36:20 +0900 Subject: [PATCH 03/27] fix: Create keymapping directory when does not exist --- PlayTools/Keymap/Keymapping.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/PlayTools/Keymap/Keymapping.swift b/PlayTools/Keymap/Keymapping.swift index 01830f1d..09948626 100644 --- a/PlayTools/Keymap/Keymapping.swift +++ b/PlayTools/Keymap/Keymapping.swift @@ -14,7 +14,7 @@ class Keymapping { static let shared = Keymapping() let bundleIdentifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? "" - let keymapUrl: URL + var keymapUrl: URL var keymapData: KeymappingData { didSet { encode() @@ -24,7 +24,17 @@ class Keymapping { init() { keymapUrl = URL(fileURLWithPath: "/Users/\(NSUserName())/Library/Containers/io.playcover.PlayCover") .appendingPathComponent("Keymapping") - .appendingPathComponent("\(bundleIdentifier).plist") + if !FileManager.default.fileExists(atPath: keymapUrl.path) { + do { + try FileManager.default.createDirectory( + atPath: keymapUrl.path, + withIntermediateDirectories: true, + attributes: [:]) + } catch { + os_log("[PlayTools] Failed to create Keymapping directory.\n%@", type: .error, error as CVarArg) + } + } + keymapUrl.appendPathComponent("\(bundleIdentifier).plist") keymapData = KeymappingData(bundleIdentifier: bundleIdentifier) if !decode() { encode() From 6845e2906ed6ca4b9f81aa71872b6538f993e905 Mon Sep 17 00:00:00 2001 From: Seungyun Lee Date: Mon, 29 Aug 2022 04:32:20 +0900 Subject: [PATCH 04/27] feat: Implement Discord Activity(Rich Presence) --- PlayTools.xcodeproj/project.pbxproj | 43 ++++++++++ .../xcshareddata/swiftpm/Package.resolved | 23 ++++++ .../DiscordActivity/DiscordActivity.swift | 16 ++++ PlayTools/DiscordActivity/DiscordIPC.swift | 80 +++++++++++++++++++ PlayTools/PlayCover.swift | 1 + PlayTools/PlaySettings.swift | 3 + 6 files changed, 166 insertions(+) create mode 100644 PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 PlayTools/DiscordActivity/DiscordActivity.swift create mode 100644 PlayTools/DiscordActivity/DiscordIPC.swift diff --git a/PlayTools.xcodeproj/project.pbxproj b/PlayTools.xcodeproj/project.pbxproj index e7d4fbd5..fbae46bf 100644 --- a/PlayTools.xcodeproj/project.pbxproj +++ b/PlayTools.xcodeproj/project.pbxproj @@ -81,6 +81,9 @@ AA818CB9287ABFB1000BEE9D /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA818CB8287ABFB1000BEE9D /* IOKit.framework */; }; AA818CBB287ABFD5000BEE9D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA818CBA287ABFD5000BEE9D /* UIKit.framework */; }; B1E8CF8A28BBE2AB004340D3 /* Keymapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E8CF8928BBE2AB004340D3 /* Keymapping.swift */; }; + B127172228817AB90025112B /* SwordRPC in Frameworks */ = {isa = PBXBuildFile; productRef = B127172128817AB90025112B /* SwordRPC */; }; + B127172528817C040025112B /* DiscordIPC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B127172428817C040025112B /* DiscordIPC.swift */; }; + B1271729288284BE0025112B /* DiscordActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1271728288284BE0025112B /* DiscordActivity.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -162,6 +165,8 @@ AA818CB8287ABFB1000BEE9D /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; }; AA818CBA287ABFD5000BEE9D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; B1E8CF8928BBE2AB004340D3 /* Keymapping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keymapping.swift; sourceTree = ""; }; + B127172428817C040025112B /* DiscordIPC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscordIPC.swift; sourceTree = ""; }; + B1271728288284BE0025112B /* DiscordActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscordActivity.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -169,6 +174,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B127172228817AB90025112B /* SwordRPC in Frameworks */, AA818CB9287ABFB1000BEE9D /* IOKit.framework in Frameworks */, AA818CBB287ABFD5000BEE9D /* UIKit.framework in Frameworks */, ); @@ -198,6 +204,7 @@ AA7196DA287A447700623C15 /* PlayTools */ = { isa = PBXGroup; children = ( + B127172328817AC70025112B /* DiscordActivity */, AA719721287A480C00623C15 /* Controls */, AA719799287A481500623C15 /* Keymap */, AA71978C287A481500623C15 /* Menu */, @@ -349,6 +356,15 @@ name = Frameworks; sourceTree = ""; }; + B127172328817AC70025112B /* DiscordActivity */ = { + isa = PBXGroup; + children = ( + B127172428817C040025112B /* DiscordIPC.swift */, + B1271728288284BE0025112B /* DiscordActivity.swift */, + ); + path = DiscordActivity; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -404,6 +420,9 @@ dependencies = ( ); name = PlayTools; + packageProductDependencies = ( + B127172128817AB90025112B /* SwordRPC */, + ); productName = PlayTools; productReference = AA7196D8287A447700623C15 /* PlayTools.framework */; productType = "com.apple.product-type.framework"; @@ -432,6 +451,9 @@ Base, ); mainGroup = AA7196CE287A447700623C15; + packageReferences = ( + B127172028817AB90025112B /* XCRemoteSwiftPackageReference "swordRPC" */, + ); productRefGroup = AA7196D9287A447700623C15 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -483,6 +505,7 @@ AA71975A287A480D00623C15 /* Toucher.swift in Sources */, AA71984A287A81A000623C15 /* NSString+KIFAdditions.m in Sources */, AA7197A1287A481500623C15 /* CircleMenuLoader.swift in Sources */, + B1271729288284BE0025112B /* DiscordActivity.swift in Sources */, AA71984E287A81A000623C15 /* UIScrollView-KIFAdditions.m in Sources */, AA719849287A81A000623C15 /* NSException-KIFAdditions.m in Sources */, AA7197AB287A481500623C15 /* ControlModel.swift in Sources */, @@ -517,6 +540,7 @@ AA71970C287A44D200623C15 /* PlayScreen.swift in Sources */, AA71986C287A81A000623C15 /* NSObject+Swizzle.m in Sources */, AA71970F287A44D200623C15 /* PlayCover.swift in Sources */, + B127172528817C040025112B /* DiscordIPC.swift in Sources */, AA719850287A81A000623C15 /* UITouch-KIFAdditions.m in Sources */, AA71985F287A81A000623C15 /* IOHIDEvent+KIF.m in Sources */, AA719861287A81A000623C15 /* NSFileManager-KIFAdditions.m in Sources */, @@ -758,6 +782,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + B127172028817AB90025112B /* XCRemoteSwiftPackageReference "swordRPC" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/khoralee/swordRPC"; + requirement = { + branch = main; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + B127172128817AB90025112B /* SwordRPC */ = { + isa = XCSwiftPackageProductDependency; + package = B127172028817AB90025112B /* XCRemoteSwiftPackageReference "swordRPC" */; + productName = SwordRPC; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = AA7196CF287A447700623C15 /* Project object */; } diff --git a/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..156442b9 --- /dev/null +++ b/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,23 @@ +{ + "pins" : [ + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio", + "state" : { + "revision" : "124119f0bb12384cef35aa041d7c3a686108722d", + "version" : "2.40.0" + } + }, + { + "identity" : "swordrpc", + "kind" : "remoteSourceControl", + "location" : "https://github.com/khoralee/swordRPC", + "state" : { + "branch" : "main", + "revision" : "68e86ed7ffcf6e39bb5f52e692cab3c96f9d9f7c" + } + } + ], + "version" : 2 +} diff --git a/PlayTools/DiscordActivity/DiscordActivity.swift b/PlayTools/DiscordActivity/DiscordActivity.swift new file mode 100644 index 00000000..4a26d88a --- /dev/null +++ b/PlayTools/DiscordActivity/DiscordActivity.swift @@ -0,0 +1,16 @@ +// +// DiscordActivity.swift +// PlayTools +// +// Created by 이승윤 on 2022/07/16. +// + +import Foundation + +class DiscordActivity: Codable { + var enable = true + var applicationID = "" + var details = "" + var state = "" + var image = "" +} diff --git a/PlayTools/DiscordActivity/DiscordIPC.swift b/PlayTools/DiscordActivity/DiscordIPC.swift new file mode 100644 index 00000000..7cd63285 --- /dev/null +++ b/PlayTools/DiscordActivity/DiscordIPC.swift @@ -0,0 +1,80 @@ +// +// DiscordIPC.swift +// PlayTools +// +// Created by 이승윤 on 2022/07/15. +// + +import Foundation +import SwordRPC + +class DiscordIPC { + public static let shared = DiscordIPC() + + func initailize() { + if PlaySettings.shared.discordActivity.enable { + let ipc: SwordRPC + let custom = PlaySettings.shared.discordActivity + if custom.applicationID.isEmpty { + ipc = SwordRPC(appId: "996108521680678993") + } else { + ipc = SwordRPC(appId: custom.applicationID) + } + let activity = createActivity(from: custom) + ipc.connect() + ipc.setPresence(activity) + } + } + + func createActivity(from custom: DiscordActivity) -> RichPresence { + var activity = RichPresence() + + if custom.details.isEmpty { + let name = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "" + let displayName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? name + activity.details = "Playing \(displayName)" + } else { + if custom.details.count == 1 { custom.details += " " } + activity.details = custom.details + } + + let poweredStr = "Powered by PlayCover" + if custom.state.isEmpty { + activity.state = poweredStr + } else { + if custom.state.count == 1 { custom.state += " " } + activity.state = custom.state + activity.assets.smallText = poweredStr + activity.assets.largeText = poweredStr + } + + let logo = "https://github.com/PlayCover/playcover-website/raw/master/src/assets/images/play-cover.png" + if custom.image.isEmpty { + let bundleID = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? "" + if let appImage = loadImage(bundleID: bundleID) { + activity.assets.largeImage = appImage + activity.assets.largeText = nil + activity.assets.smallImage = logo + } else { + activity.assets.largeImage = logo + } + } else { + activity.assets.largeImage = custom.image + activity.assets.largeText = nil + activity.assets.smallImage = logo + } + + activity.timestamps.start = Date() + + activity.buttons[0].label = "Download PlayCover" + activity.buttons[0].url = "https://github.com/PlayCover/PlayCover/releases" + activity.buttons.removeLast() + + return activity + } + + // TODO : load App icon image from appstore + func loadImage(bundleID: String) -> String? { + return nil + } +} diff --git a/PlayTools/PlayCover.swift b/PlayTools/PlayCover.swift index 97c0ba8b..4e0f4756 100644 --- a/PlayTools/PlayCover.swift +++ b/PlayTools/PlayCover.swift @@ -22,6 +22,7 @@ final public class PlayCover: NSObject { @objc static public func launch() { quitWhenClose() PlayInput.shared.initialize() + DiscordIPC.shared.initailize() } @objc static public func quitWhenClose() { diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index b63005ee..828e102f 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -37,6 +37,8 @@ let settings = PlaySettings.shared return false }() + lazy var discordActivity = settingsData.discordActivity + lazy var keymapping = settingsData.keymapping lazy var gamingMode: Bool = isGame @@ -84,5 +86,6 @@ struct AppSettingsData: Codable { var aspectRatio = 1 var notch = false var bypass = false + var discordActivity = DiscordActivity() var version = "2.0.0" } From 05475f2ebcfbdf3b3258c95c4267bda9a8f2b0a7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 29 Aug 2022 16:39:22 +0100 Subject: [PATCH 05/27] Mouse sensitivity fix --- PlayTools/Controls/PlayMice.swift | 4 ++-- PlayTools/Keymap/ControlModel.swift | 2 +- PlayTools/PlaySettings.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 279b67a4..ded2af75 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -165,8 +165,8 @@ final class CameraControl { } movingFast = true } - self.location.x += deltaX * CGFloat(PlaySettings.shared.sensivity) - self.location.y -= deltaY * CGFloat(PlaySettings.shared.sensivity) + self.location.x += deltaX * CGFloat(PlaySettings.shared.sensitivity) + self.location.y -= deltaY * CGFloat(PlaySettings.shared.sensitivity) Toucher.touchcam(point: self.location, phase: UITouch.Phase.moved, tid: 1) let previous = sequence diff --git a/PlayTools/Keymap/ControlModel.swift b/PlayTools/Keymap/ControlModel.swift index a098234f..5f2b5a66 100644 --- a/PlayTools/Keymap/ControlModel.swift +++ b/PlayTools/Keymap/ControlModel.swift @@ -15,7 +15,7 @@ import GameController self.parent = parent } - init(keyCodes: [Int], size: CGFloat, xCoord: CGFloat, yCoord: CGFloat, sensivity: CGFloat) { + init(keyCodes: [Int], size: CGFloat, xCoord: CGFloat, yCoord: CGFloat, sensitivity: CGFloat) { self.keyCodes = keyCodes self.size = size self.xCoord = xCoord diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index 828e102f..ad3b811a 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -45,7 +45,7 @@ let settings = PlaySettings.shared lazy var notch = settingsData.notch - lazy var sensivity = settingsData.sensitivity + lazy var sensitivity = settingsData.sensitivity / 100 lazy var windowSizeHeight = CGFloat(settingsData.windowHeight) From dc6f3f3a6007a7196ee32a4b626661bb4c91904a Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Sat, 3 Sep 2022 16:46:42 +0800 Subject: [PATCH 06/27] release window object when editor is off --- PlayTools/Keymap/EditorController.swift | 28 ++++++++++++------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/PlayTools/Keymap/EditorController.swift b/PlayTools/Keymap/EditorController.swift index 7a1280de..4c2c1419 100644 --- a/PlayTools/Keymap/EditorController.swift +++ b/PlayTools/Keymap/EditorController.swift @@ -17,15 +17,14 @@ final class EditorController: NSObject { var focusedControl: ControlModel? - lazy var editorWindow: UIWindow = initWindow() - var previousWindow: UIWindow? + var editorWindow: UIWindow? + weak var previousWindow: UIWindow? var controls: [ControlModel] = [] - lazy var viewController = EditorViewController(nibName: nil, bundle: nil) - lazy var view: EditorView! = viewController.view as? EditorView + var view: EditorView! {editorWindow?.rootViewController?.view as? EditorView} private func initWindow() -> UIWindow { let window = UIWindow(windowScene: screen.windowScene!) - window.rootViewController = viewController + window.rootViewController = EditorViewController(nibName: nil, bundle: nil) return window } @@ -50,12 +49,14 @@ final class EditorController: NSObject { public func switchMode() { lock.lock() - if editorMode { KeymapHolder.shared.hide() saveButtons() - editorMode = false -// editorWindow.windowScene = nil + editorWindow?.isHidden = true + editorWindow?.windowScene = nil + editorWindow?.rootViewController = nil + // menu still holds this object until next responder hit test + editorWindow = nil previousWindow?.makeKeyAndVisible() mode.show(false) focusedControl = nil @@ -63,19 +64,16 @@ final class EditorController: NSObject { } else { mode.show(true) previousWindow = screen.keyWindow -// editorWindow.windowScene = screen.windowScene - editorMode = true - editorWindow.makeKeyAndVisible() + editorWindow = initWindow() + editorWindow?.makeKeyAndVisible() showButtons() Toast.showOver(msg: "Click to start keymmaping edit") } +// Toast.showOver(msg: "\(UIApplication.shared.windows.count)") lock.unlock() } - var editorMode: Bool { - get { !editorWindow.isHidden } - set { editorWindow.isHidden = !newValue} - } + var editorMode: Bool { !(editorWindow?.isHidden ?? true)} public func setKeyCode(_ key: Int) { if editorMode { From 398446ee7d844c592511c5746dd6642bcbe10fea Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Mon, 5 Sep 2022 00:13:52 +0800 Subject: [PATCH 07/27] add manually rotating window --- PlayTools/Controls/MenuController.swift | 47 +++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/PlayTools/Controls/MenuController.swift b/PlayTools/Controls/MenuController.swift index d71be7b9..a9ef34ea 100644 --- a/PlayTools/Controls/MenuController.swift +++ b/PlayTools/Controls/MenuController.swift @@ -7,7 +7,24 @@ import UIKit -extension UIViewController { +class RotateViewController: UIViewController { + static let orientationList: [UIInterfaceOrientation] = [ + .landscapeLeft, .portrait, .landscapeRight, .portraitUpsideDown] + static var orientationTraverser = 0 + + static func rotate() { + orientationTraverser += 1 + } + + override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { + RotateViewController.orientationList[ + RotateViewController.orientationTraverser % RotateViewController.orientationList.count] + } + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { .all } + override var modalPresentationStyle: UIModalPresentationStyle { get {.fullScreen} set {} } +} + +extension UIApplication { @objc func switchEditorMode(_ sender: AnyObject) { EditorController.shared.switchMode() @@ -27,6 +44,20 @@ extension UIViewController { func downscaleElement(_ sender: AnyObject) { EditorController.shared.focusedControl?.resize(down: true) } + + @objc + func rotateView(_ sender: AnyObject) { + RotateViewController.rotate() + let viewController = RotateViewController(nibName: nil, bundle: nil) + var parent = screen.keyWindow?.rootViewController + while parent?.presentedViewController != nil { + parent = parent?.presentedViewController + } + parent?.present(viewController, animated: true) + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1, execute: { + viewController.dismiss(animated: true) + }) + } } struct CommandsList { @@ -36,11 +67,13 @@ struct CommandsList { var keymapping = ["Open/Close Keymapping Editor", "Delete selected element", "Upsize selected element", - "Downsize selected element"] -var keymappingSelectors = [#selector(UIViewController.switchEditorMode(_:)), - #selector(UIViewController.removeElement(_:)), - #selector(UIViewController.upscaleElement(_:)), - #selector(UIViewController.downscaleElement(_:))] + "Downsize selected element", + "Rotate display area"] +var keymappingSelectors = [#selector(UIApplication.switchEditorMode(_:)), + #selector(UIApplication.removeElement(_:)), + #selector(UIApplication.upscaleElement(_:)), + #selector(UIApplication.downscaleElement(_:)), + #selector(UIApplication.rotateView(_:))] class MenuController { init(with builder: UIMenuBuilder) { @@ -51,7 +84,7 @@ class MenuController { @available(iOS 15.0, *) class func keymappingMenu() -> UIMenu { - let keyCommands = [ "K", UIKeyCommand.inputDelete, UIKeyCommand.inputUpArrow, UIKeyCommand.inputDownArrow ] + let keyCommands = [ "K", UIKeyCommand.inputDelete, UIKeyCommand.inputUpArrow, UIKeyCommand.inputDownArrow, "R" ] let arrowKeyChildrenCommands = zip(keyCommands, keymapping).map { (command, btn) in UIKeyCommand(title: btn, From 646de9efbc6e9ca6dc8a71c8d0122aa2a5b4305c Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Sun, 4 Sep 2022 16:42:06 -0400 Subject: [PATCH 08/27] Delete Package.resolved --- .../xcshareddata/swiftpm/Package.resolved | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 156442b9..00000000 --- a/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,23 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swift-nio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio", - "state" : { - "revision" : "124119f0bb12384cef35aa041d7c3a686108722d", - "version" : "2.40.0" - } - }, - { - "identity" : "swordrpc", - "kind" : "remoteSourceControl", - "location" : "https://github.com/khoralee/swordRPC", - "state" : { - "branch" : "main", - "revision" : "68e86ed7ffcf6e39bb5f52e692cab3c96f9d9f7c" - } - } - ], - "version" : 2 -} From 9870e28a71566c5959bc0711f6ebfb5ca8babe04 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Mon, 5 Sep 2022 21:06:36 +0800 Subject: [PATCH 09/27] revert development team --- .idea/.gitignore | 3 --- .idea/PlayTools.iml | 8 -------- .idea/inspectionProfiles/profiles_settings.xml | 6 ------ .idea/misc.xml | 4 ---- .idea/modules.xml | 8 -------- .idea/vcs.xml | 6 ------ PlayTools.xcodeproj/project.pbxproj | 4 ++-- 7 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/PlayTools.iml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d33521..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/PlayTools.iml b/.idea/PlayTools.iml deleted file mode 100644 index d0876a78..00000000 --- a/.idea/PlayTools.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2da..00000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index d1e22ecb..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 11a0ea94..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/PlayTools.xcodeproj/project.pbxproj b/PlayTools.xcodeproj/project.pbxproj index 8168ad5f..7bb05989 100644 --- a/PlayTools.xcodeproj/project.pbxproj +++ b/PlayTools.xcodeproj/project.pbxproj @@ -682,7 +682,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = FKQLLJTGYY; + DEVELOPMENT_TEAM = 792V9HMJW3; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -726,7 +726,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = FKQLLJTGYY; + DEVELOPMENT_TEAM = 792V9HMJW3; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; From b8a173c123a51809df2541379631272a1e008492 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Tue, 6 Sep 2022 01:20:15 +0800 Subject: [PATCH 10/27] fix draggable button save, MB mapping edit, default focused mapping, circle menu unfocus --- PlayTools/Controls/PlayAction.swift | 9 -------- PlayTools/Controls/PlayMice.swift | 6 ++++++ PlayTools/Keymap/ControlModel.swift | 21 ------------------- PlayTools/Keymap/EditorController.swift | 28 +++++++++++++------------ 4 files changed, 21 insertions(+), 43 deletions(-) diff --git a/PlayTools/Controls/PlayAction.swift b/PlayTools/Controls/PlayAction.swift index 8153b4e0..6b2198f7 100644 --- a/PlayTools/Controls/PlayAction.swift +++ b/PlayTools/Controls/PlayAction.swift @@ -116,15 +116,6 @@ class JoystickAction: Action { Toucher.touchQueue.async(execute: self.update) } } -// keyboard.button(forKeyCode: keys[1])?.pressedChangedHandler = { _, _, _ in -// self.update() -// } -// keyboard.button(forKeyCode: keys[2])?.pressedChangedHandler = { _, _, _ in -// self.update() -// } -// keyboard.button(forKeyCode: keys[3])?.pressedChangedHandler = { _, _, _ in -// self.update() -// } } } diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index dca2cad1..bb3cb570 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -94,6 +94,12 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool return event } return nil + } else if EditorController.shared.editorMode { + if _up == 8 { + EditorController.shared.setKeyCode(-2) + } else if _up == 33554432 { + EditorController.shared.setKeyCode(-3) + } } return event } as ResponseBlock) diff --git a/PlayTools/Keymap/ControlModel.swift b/PlayTools/Keymap/ControlModel.swift index d5934056..1385242a 100644 --- a/PlayTools/Keymap/ControlModel.swift +++ b/PlayTools/Keymap/ControlModel.swift @@ -147,27 +147,6 @@ class ButtonModel: ControlModel { } } -class RMBModel: ButtonModel { - override func setKeyCodes(keys: [Int]) { - data.keyCodes = [-2] - button.setTitle("RMB", for: UIControl.State.normal) - } -} - -class LMBModel: ButtonModel { - override func setKeyCodes(keys: [Int]) { - data.keyCodes = [-1] - button.setTitle("LMB", for: UIControl.State.normal) - } -} - -class MMBModel: ButtonModel { - override func setKeyCodes(keys: [Int]) { - data.keyCodes = [-3] - button.setTitle("MMB", for: UIControl.State.normal) - } -} - class JoystickButtonModel: ControlModel { override init(data: ControlData) { super.init(data: data) diff --git a/PlayTools/Keymap/EditorController.swift b/PlayTools/Keymap/EditorController.swift index eb43814a..1933951c 100644 --- a/PlayTools/Keymap/EditorController.swift +++ b/PlayTools/Keymap/EditorController.swift @@ -105,14 +105,6 @@ final class EditorController: NSObject { parent: nil)) addControlToView(control: ctrl) } - for joystick in keymap.keymapData.joystickModel { - let ctrl = JoystickModel(data: ControlData( - keyCodes: [joystick.upKeyCode, joystick.downKeyCode, joystick.leftKeyCode, joystick.rightKeyCode], - size: joystick.transform.size, - xCoord: joystick.transform.xCoord, - yCoord: joystick.transform.yCoord)) - addControlToView(control: ctrl) - } for mouse in keymap.keymapData.mouseAreaModel { let ctrl = MouseAreaModel(data: ControlData( @@ -121,6 +113,14 @@ final class EditorController: NSObject { yCoord: mouse.transform.yCoord)) addControlToView(control: ctrl) } + for joystick in keymap.keymapData.joystickModel { + let ctrl = JoystickModel(data: ControlData( + keyCodes: [joystick.upKeyCode, joystick.downKeyCode, joystick.leftKeyCode, joystick.rightKeyCode], + size: joystick.transform.size, + xCoord: joystick.transform.xCoord, + yCoord: joystick.transform.yCoord)) + addControlToView(control: ctrl) + } } func saveButtons() { @@ -129,12 +129,13 @@ final class EditorController: NSObject { switch $0 { case let model as JoystickModel: keymapData.joystickModel.append(model.save()) + // subclasses must be checked first + case let model as DraggableButtonModel: + keymapData.draggableButtonModels.append(model.save()) case let model as MouseAreaModel: keymapData.mouseAreaModel.append(model.save()) case let model as ButtonModel: keymapData.buttonModels.append(model.save()) - case let model as DraggableButtonModel: - keymapData.draggableButtonModels.append(model.save()) default: break } @@ -168,7 +169,7 @@ final class EditorController: NSObject { @objc public func addRMB(_ toPoint: CGPoint) { if editorMode { - addControlToView(control: RMBModel(data: ControlData(keyCodes: [-2], + addControlToView(control: ButtonModel(data: ControlData(keyCodes: [-2], size: 5, xCoord: toPoint.x.relativeX, yCoord: toPoint.y.relativeY, @@ -178,7 +179,7 @@ final class EditorController: NSObject { @objc public func addLMB(_ toPoint: CGPoint) { if editorMode { - addControlToView(control: LMBModel(data: ControlData(keyCodes: [-1], + addControlToView(control: ButtonModel(data: ControlData(keyCodes: [-1], size: 5, xCoord: toPoint.x.relativeX, yCoord: toPoint.y.relativeY, @@ -188,7 +189,7 @@ final class EditorController: NSObject { @objc public func addMMB(_ toPoint: CGPoint) { if editorMode { - addControlToView(control: MMBModel(data: ControlData(keyCodes: [-3], + addControlToView(control: ButtonModel(data: ControlData(keyCodes: [-3], size: 5, xCoord: toPoint.x.relativeX, yCoord: toPoint.y.relativeY, @@ -253,6 +254,7 @@ class EditorView: UIView { for cntrl in editor.controls { cntrl.focus(false) } + editor.focusedControl = nil KeymapHolder.shared.add(sender.location(in: self)) } From c0f8ba68c8826bae03375cd9989f054a45ac9bac Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Tue, 6 Sep 2022 03:25:53 +0800 Subject: [PATCH 11/27] fix rotate content unrecoverable if pressed in less than 1 second --- PlayTools/Controls/MenuController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayTools/Controls/MenuController.swift b/PlayTools/Controls/MenuController.swift index a0403711..10d34a23 100644 --- a/PlayTools/Controls/MenuController.swift +++ b/PlayTools/Controls/MenuController.swift @@ -53,7 +53,7 @@ extension UIViewController { let viewController = RotateViewController(nibName: nil, bundle: nil) self.present(viewController, animated: true) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1, execute: { - viewController.dismiss(animated: true) + self.dismiss(animated: true) }) } } From dea9685e773c142123769d1c14b08f7aaa1ba673 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Tue, 6 Sep 2022 04:19:30 +0800 Subject: [PATCH 12/27] Delete Package.resolved again --- .../xcshareddata/swiftpm/Package.resolved | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index d6f8a3b4..00000000 --- a/PlayTools.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,32 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swift-atomics", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-atomics.git", - "state" : { - "revision" : "919eb1d83e02121cdb434c7bfc1f0c66ef17febe", - "version" : "1.0.2" - } - }, - { - "identity" : "swift-nio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio", - "state" : { - "revision" : "b4e0a274f7f34210e97e2f2c50ab02a10b549250", - "version" : "2.41.1" - } - }, - { - "identity" : "swordrpc", - "kind" : "remoteSourceControl", - "location" : "https://github.com/khoralee/swordRPC", - "state" : { - "branch" : "main", - "revision" : "68e86ed7ffcf6e39bb5f52e692cab3c96f9d9f7c" - } - } - ], - "version" : 2 -} From 2426f6a68baefd3b03b65bc3b50101d001f71e71 Mon Sep 17 00:00:00 2001 From: Seungyun Lee Date: Tue, 30 Aug 2022 01:54:13 +0900 Subject: [PATCH 13/27] fix: Add missing setting data to fix wrong behavior --- PlayTools/Controls/ControlMode.swift | 6 +++--- PlayTools/Controls/PlayInput.swift | 4 ++-- PlayTools/Controls/PlayMice.swift | 2 +- PlayTools/PlaySettings.swift | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PlayTools/Controls/ControlMode.swift b/PlayTools/Controls/ControlMode.swift index 1ffb9ee4..33a7f4d0 100644 --- a/PlayTools/Controls/ControlMode.swift +++ b/PlayTools/Controls/ControlMode.swift @@ -10,7 +10,7 @@ let mode = ControlMode.mode @objc final public class ControlMode: NSObject { @objc static public let mode = ControlMode() - @objc public var visible: Bool = PlaySettings.shared.gamingMode + @objc public var visible: Bool = PlaySettings.shared.mouseMapping @objc static public func isMouseClick(_ event: Any) -> Bool { return [1, 2].contains(Dynamic(event).type.asInt) @@ -23,7 +23,7 @@ let mode = ControlMode.mode if screen.fullscreen { screen.switchDock(true) } - if PlaySettings.shared.gamingMode { + if PlaySettings.shared.mouseMapping { Dynamic.NSCursor.unhide() disableCursor(1) } @@ -31,7 +31,7 @@ let mode = ControlMode.mode } } else { if visible { - if PlaySettings.shared.gamingMode { + if PlaySettings.shared.mouseMapping { Dynamic.NSCursor.hide() disableCursor(0) } diff --git a/PlayTools/Controls/PlayInput.swift b/PlayTools/Controls/PlayInput.swift index 33ac532b..2be2cd1a 100644 --- a/PlayTools/Controls/PlayInput.swift +++ b/PlayTools/Controls/PlayInput.swift @@ -31,7 +31,7 @@ final class PlayInput: NSObject { counter += 1 } - if settings.gamingMode { + if settings.isGame && settings.mouseMapping { for mouse in keymap.keymapData.mouseAreaModel { PlayMice.shared.setup(mouse) counter += 1 @@ -93,7 +93,7 @@ final class PlayInput: NSObject { ] private func swapMode(_ pressed: Bool) { - if !PlaySettings.shared.gamingMode { + if !(settings.isGame && settings.mouseMapping) { return } if pressed { diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index bb3cb570..2e719b12 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -20,7 +20,7 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool private static var isInit = false - private var acceptMouseEvents = !PlaySettings.shared.gamingMode + private var acceptMouseEvents = !PlaySettings.shared.mouseMapping public override init() { super.init() diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index ad3b811a..0a33078b 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -41,7 +41,7 @@ let settings = PlaySettings.shared lazy var keymapping = settingsData.keymapping - lazy var gamingMode: Bool = isGame + lazy var mouseMapping = settingsData.mouseMapping lazy var notch = settingsData.notch @@ -51,7 +51,7 @@ let settings = PlaySettings.shared lazy var windowSizeWidth = CGFloat(settingsData.windowWidth) - @objc lazy var adaptiveDisplay = isGame + @objc lazy var adaptiveDisplay = settingsData.resolution == 0 ? false : true @objc lazy var deviceModel = settingsData.iosDeviceModel as NSString @@ -82,7 +82,7 @@ struct AppSettingsData: Codable { var refreshRate = 60 var windowWidth = 1920 var windowHeight = 1080 - var resolution = 2 + var resolution = 0 var aspectRatio = 1 var notch = false var bypass = false From 1ac8c1ed92db3e01813ebd22fd3a3ca88722cd0f Mon Sep 17 00:00:00 2001 From: Seungyun Lee Date: Wed, 7 Sep 2022 12:25:19 +0900 Subject: [PATCH 14/27] fix: Remove all isGame related things --- PlayTools/Controls/PlayAction.swift | 2 +- PlayTools/Controls/PlayInput.swift | 4 ++-- PlayTools/PlaySettings.swift | 15 +-------------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/PlayTools/Controls/PlayAction.swift b/PlayTools/Controls/PlayAction.swift index 6b2198f7..ef804849 100644 --- a/PlayTools/Controls/PlayAction.swift +++ b/PlayTools/Controls/PlayAction.swift @@ -69,7 +69,7 @@ class DraggableButtonAction: ButtonAction { override init(id: Int, keyid: Int, key: GCKeyCode, point: CGPoint) { self.releasePoint = point super.init(id: id, keyid: keyid, key: key, point: point) - if settings.gamingMode { + if settings.mouseMapping { PlayMice.shared.setupMouseMovedHandler() } } diff --git a/PlayTools/Controls/PlayInput.swift b/PlayTools/Controls/PlayInput.swift index 2be2cd1a..fd93736c 100644 --- a/PlayTools/Controls/PlayInput.swift +++ b/PlayTools/Controls/PlayInput.swift @@ -31,7 +31,7 @@ final class PlayInput: NSObject { counter += 1 } - if settings.isGame && settings.mouseMapping { + if settings.mouseMapping { for mouse in keymap.keymapData.mouseAreaModel { PlayMice.shared.setup(mouse) counter += 1 @@ -93,7 +93,7 @@ final class PlayInput: NSObject { ] private func swapMode(_ pressed: Bool) { - if !(settings.isGame && settings.mouseMapping) { + if !settings.mouseMapping { return } if pressed { diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index 0a33078b..07970792 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -7,8 +7,6 @@ let settings = PlaySettings.shared @objc public final class PlaySettings: NSObject { @objc public static let shared = PlaySettings() - private static let keywords = ["game", "unity", "metal", "netflix", "opengl", "minecraft", "mihoyo", "disney"] - let bundleIdentifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? "" let settingsUrl: URL var settingsData: AppSettingsData @@ -26,17 +24,6 @@ let settings = PlaySettings.shared } } - var isGame: Bool = { - if let info = Bundle.main.infoDictionary?.description { - for keyword in PlaySettings.keywords { - if info.contains(keyword) && !info.contains("xbox") { - return true - } - } - } - return false - }() - lazy var discordActivity = settingsData.discordActivity lazy var keymapping = settingsData.keymapping @@ -82,7 +69,7 @@ struct AppSettingsData: Codable { var refreshRate = 60 var windowWidth = 1920 var windowHeight = 1080 - var resolution = 0 + var resolution = 2 var aspectRatio = 1 var notch = false var bypass = false From dbbec8e93a601e81bb5481cb1baaac9bbb7e3ff5 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Tue, 6 Sep 2022 23:50:46 -0400 Subject: [PATCH 15/27] Remove refresh rate, set default identifer to M1, fix typo --- .../Controls/PTFakeTouch/NSObject+Swizzle.m | 9 -------- PlayTools/PlayLoader.m | 12 +++++------ PlayTools/PlayScreen.swift | 21 ------------------- PlayTools/PlaySettings.swift | 6 +----- 4 files changed, 7 insertions(+), 41 deletions(-) diff --git a/PlayTools/Controls/PTFakeTouch/NSObject+Swizzle.m b/PlayTools/Controls/PTFakeTouch/NSObject+Swizzle.m index c8bddcc7..d8cb36e0 100644 --- a/PlayTools/Controls/PTFakeTouch/NSObject+Swizzle.m +++ b/PlayTools/Controls/PTFakeTouch/NSObject+Swizzle.m @@ -89,10 +89,6 @@ +(void)load [objc_getClass("FBSDisplayMode") swizzleInstanceMethod:@selector(size) withMethod:@selector(hook_size)]; } - if ([[PlaySettings shared] refreshRate] == 120){ - [objc_getClass("UnityAppController") swizzleInstanceMethod:@selector(callbackFramerateChange:) withMethod:@selector(hook_callbackFramerateChange:)]; - } - [objc_getClass("NSMenuItem") swizzleClassMethod:@selector(enabled) withMethod:@selector(hook_enabled)]; [objc_getClass("IOSViewController") swizzleInstanceMethod:@selector(prefersPointerLocked) withMethod:@selector(hook_prefersPointerLocked)]; @@ -111,11 +107,6 @@ -(BOOL) hook_prefersPointerLocked { return false; } -- (void) hook_callbackFramerateChange:(int)targetFPS { - printf("FPS %d", targetFPS); - [self hook_callbackFramerateChange:120]; -} - - (MTLPixelFormat) hook_stencilAttachmentPixelFormat { return MTLPixelFormatDepth32Float; } diff --git a/PlayTools/PlayLoader.m b/PlayTools/PlayLoader.m index bcfc72c0..f2f0ddde 100644 --- a/PlayTools/PlayLoader.m +++ b/PlayTools/PlayLoader.m @@ -88,9 +88,9 @@ static int my_sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void * (strcmp(name, "hw.model") == 0)) { if (oldp != NULL) { int ret = sysctlbyname(name, oldp, oldlenp, newp, newlen); - const char *mechine = DEVICE_MODEL; - strncpy((char *)oldp, mechine, strlen(mechine)); - *oldlenp = strlen(mechine); + const char *machine = DEVICE_MODEL; + strncpy((char *)oldp, machine, strlen(machine)); + *oldlenp = strlen(machine); return ret; } else { int ret = sysctlbyname(name, oldp, oldlenp, newp, newlen); @@ -99,9 +99,9 @@ static int my_sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void * } else if ((strcmp(name, "hw.target") == 0)) { if (oldp != NULL) { int ret = sysctlbyname(name, oldp, oldlenp, newp, newlen); - const char *mechine = OEM_ID; - strncpy((char *)oldp, mechine, strlen(mechine)); - *oldlenp = strlen(mechine); + const char *machine = OEM_ID; + strncpy((char *)oldp, machine, strlen(machine)); + *oldlenp = strlen(machine); return ret; } else { int ret = sysctlbyname(name, oldp, oldlenp, newp, newlen); diff --git a/PlayTools/PlayScreen.swift b/PlayTools/PlayScreen.swift index 7a5e67df..a8186702 100644 --- a/PlayTools/PlayScreen.swift +++ b/PlayTools/PlayScreen.swift @@ -173,27 +173,6 @@ extension UIView { } } -extension UIScreen { - - @objc open var maximumFramesPerSecond: Int { - return PlaySettings.shared.refreshRate - } - - @available(iOS 15.0, *) - @objc open var preferredFrameRateRange: CAFrameRateRange { - return CAFrameRateRange(minimum: 60, - maximum: Float(PlaySettings.shared.refreshRate), - __preferred: Float(PlaySettings.shared.refreshRate)) - } - -} - -extension CADisplayLink { - @objc open var preferredFramesPerSecond: Int { - return PlaySettings.shared.refreshRate - } -} - extension UIWindow { var nsWindow: NSObject? { diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index 07970792..6949eef2 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -54,9 +54,6 @@ let settings = PlaySettings.shared return "J320xAP" } }() - - @objc lazy var refreshRate = settingsData.refreshRate - } struct AppSettingsData: Codable { @@ -65,8 +62,7 @@ struct AppSettingsData: Codable { var sensitivity: Float = 50 var disableTimeout = false - var iosDeviceModel = "iPad8,6" - var refreshRate = 60 + var iosDeviceModel = "iPad13,8" var windowWidth = 1920 var windowHeight = 1080 var resolution = 2 From 17e66a885a98dc5b99153606d135b1d640134b4c Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Fri, 9 Sep 2022 03:40:53 +0800 Subject: [PATCH 16/27] proxy mouse input when mouse mapping is off --- PlayTools/Controls/PlayMice.swift | 52 ++++++++++++++++++++++++++----- PlayTools/PlayScreen.swift | 2 +- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index bb3cb570..94c04a08 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -29,17 +29,36 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool setupMouseButton(_up: 8, _down: 16) setupMouseButton(_up: 33554432, _down: 67108864) PlayMice.isInit = true + if acceptMouseEvents { + setupMouseMovedHandler() + } } } + var fakedMousePressed = false + public var cursorPos: CGPoint { var point = CGPoint(x: 0, y: 0) if #available(macOS 11, *) { point = Dynamic(screen.nsWindow).mouseLocationOutsideOfEventStream.asCGPoint! } if let rect = (Dynamic(screen.nsWindow).frame.asCGRect) { - point.x = (point.x / rect.width) * screen.screenRect.width - point.y = screen.screenRect.height - ((point.y / rect.height) * screen.screenRect.height) + let viewRect: CGRect = screen.screenRect + let widthRate = viewRect.width / rect.width + var rate = viewRect.height / rect.height + if widthRate > rate { + // keep aspect ratio + rate = widthRate + } + // horizontally in center + point.x -= (rect.width - viewRect.width / rate)/2 + point.x *= rate + if screen.fullscreen { + // vertically in center + point.y -= (rect.height - viewRect.height / rate)/2 + } + point.y *= rate + point.y = viewRect.height - point.y } return point } @@ -62,15 +81,19 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool self.camera?.updated(CGFloat(deltaX), CGFloat(deltaY)) } } + if self.acceptMouseEvents && self.fakedMousePressed { + Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.moved, tid: 1) + } } +// Toast.showOver(msg: "\(self.cursorPos)") } } } public func stop() { - for mouse in GCMouse.mice() { - mouse.mouseInput?.mouseMovedHandler = nil - } +// for mouse in GCMouse.mice() { +// mouse.mouseInput?.mouseMovedHandler = nil +// } camera?.stop() camera = nil mouseActions = [:] @@ -87,11 +110,21 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool var mouseActions: [Int: ButtonAction] = [:] private func setupMouseButton(_up: Int, _down: Int) { + // no this is not up, this is down. And the later down is up. Dynamic.NSEvent.addLocalMonitorForEventsMatchingMask(_up, handler: { event in if !mode.visible || self.acceptMouseEvents { self.mouseActions[_up]?.update(pressed: true) if self.acceptMouseEvents { - return event + let window = Dynamic(event, memberName: "window").asObject + if !self.fakedMousePressed + // for traffic light buttons when not fullscreen + && self.cursorPos.y > 0 + // for traffic light buttons when fullscreen + && window == screen.nsWindow { + Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.began, tid: 1) + self.fakedMousePressed = true + return nil + } } return nil } else if EditorController.shared.editorMode { @@ -100,6 +133,7 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool } else if _up == 33554432 { EditorController.shared.setKeyCode(-3) } + return event } return event } as ResponseBlock) @@ -107,7 +141,11 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool if !mode.visible || self.acceptMouseEvents { self.mouseActions[_up]?.update(pressed: false) if self.acceptMouseEvents { - return event + if self.fakedMousePressed { + self.fakedMousePressed = false + Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.ended, tid: 1) + return nil + } } return nil } diff --git a/PlayTools/PlayScreen.swift b/PlayTools/PlayScreen.swift index 7a5e67df..52ffa953 100644 --- a/PlayTools/PlayScreen.swift +++ b/PlayTools/PlayScreen.swift @@ -88,7 +88,7 @@ public final class PlayScreen: NSObject { return size.toAspectRatio() } var fullscreen: Bool { - return Dynamic(nsWindow).styleMask.contains(16384).asBool ?? false + return (Dynamic(nsWindow).styleMask.asInt ?? 0) & 16384 != 0 } @objc public var screenRect: CGRect { From eda054a3ebae0fce2067996740061036a42b4d4c Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Fri, 9 Sep 2022 04:08:48 +0800 Subject: [PATCH 17/27] improve experience when mouse mapping is off --- PlayTools/Controls/PlayMice.swift | 43 ++++++++++++++++--------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index f4626215..232cad7a 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -112,20 +112,21 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool private func setupMouseButton(_up: Int, _down: Int) { // no this is not up, this is down. And the later down is up. Dynamic.NSEvent.addLocalMonitorForEventsMatchingMask(_up, handler: { event in - if !mode.visible || self.acceptMouseEvents { - self.mouseActions[_up]?.update(pressed: true) - if self.acceptMouseEvents { - let window = Dynamic(event, memberName: "window").asObject - if !self.fakedMousePressed - // for traffic light buttons when not fullscreen - && self.cursorPos.y > 0 - // for traffic light buttons when fullscreen - && window == screen.nsWindow { - Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.began, tid: 1) - self.fakedMousePressed = true - return nil - } + if self.acceptMouseEvents { + let window = Dynamic(event, memberName: "window").asObject + if !self.fakedMousePressed + // for traffic light buttons when not fullscreen + && self.cursorPos.y > 0 + // for traffic light buttons when fullscreen + && window == screen.nsWindow { + Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.began, tid: 1) + self.fakedMousePressed = true + return nil } + return event + } + if !mode.visible { + self.mouseActions[_up]?.update(pressed: true) return nil } else if EditorController.shared.editorMode { if _up == 8 { @@ -138,15 +139,15 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool return event } as ResponseBlock) Dynamic.NSEvent.addLocalMonitorForEventsMatchingMask(_down, handler: { event in - if !mode.visible || self.acceptMouseEvents { - self.mouseActions[_up]?.update(pressed: false) - if self.acceptMouseEvents { - if self.fakedMousePressed { - self.fakedMousePressed = false - Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.ended, tid: 1) - return nil - } + if self.acceptMouseEvents { + if self.fakedMousePressed { + self.fakedMousePressed = false + Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.ended, tid: 1) + return nil } + } + if !mode.visible { + self.mouseActions[_up]?.update(pressed: false) return nil } return event From 80117d8d1ad68ca571c1860d434476d920d69ebf Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Sun, 11 Sep 2022 00:46:21 +0800 Subject: [PATCH 18/27] send fake touch events via runloop source0 --- .../Controls/PTFakeTouch/PTFakeMetaTouch.h | 4 +- .../Controls/PTFakeTouch/PTFakeMetaTouch.m | 101 +++++++++++++----- .../addition/UITouch-KIFAdditions.h | 1 + .../addition/UITouch-KIFAdditions.m | 8 +- PlayTools/Controls/Toucher.swift | 6 +- 5 files changed, 87 insertions(+), 33 deletions(-) diff --git a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.h b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.h index 46f047b8..3cf3f7be 100644 --- a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.h +++ b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.h @@ -31,10 +31,10 @@ double ColourDistance(RGB e1, RGB e2); * @param phase 操作的类别 type of the operation * @param window key window in which touch event is to happen * - * @return realid returns the allocated ID for this touch point, or -1 if no such point + * @return deleted whether the system had deleted a previous touch */ -+ (NSInteger)fakeTouchId:(NSInteger)pointId AtPoint:(CGPoint)point withTouchPhase:(UITouchPhase)phase inWindow:(UIWindow*)window; ++ (NSInteger)fakeTouchId:(NSInteger)pointId AtPoint:(CGPoint)point withTouchPhase:(UITouchPhase)phase inWindow:(UIWindow*)window onView:(UIView*)view; /** * Get a not used pointId 获取一个没有使用过的触屏序列号 obtain a never used touch screen sequence number * diff --git a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m index c71ee87a..94e66312 100644 --- a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m +++ b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m @@ -10,10 +10,17 @@ #import "UITouch-KIFAdditions.h" #import "UIApplication-KIFAdditions.h" #import "UIEvent+KIFAdditions.h" +#import "CoreFoundation/CFRunLoop.h" #include +#include static NSMutableArray *touchAry; static NSMutableArray *livingTouchAry; +static CFRunLoopSourceRef source; + +static UITouch* toRemove = NULL, *toStationarify = NULL; + +NSArray *safeTouchAry; void disableCursor(boolean_t disable){ void *handle; @@ -59,6 +66,29 @@ void moveCursorTo(CGPoint point){ } } +void eventSendCallback(void* info) { + UIEvent *event = [[UIApplication sharedApplication] _touchesEvent]; + // to retain objects from being released + [event _clearTouches]; + NSArray *myAry = safeTouchAry; + for (UITouch *aTouch in myAry) { + switch (aTouch.phase) { + case UITouchPhaseEnded: + case UITouchPhaseCancelled: + toRemove = aTouch; + break; + case UITouchPhaseBegan: +// case UITouchPhaseMoved: + toStationarify = aTouch; + break; + default: + break; + } + [event _addTouch:aTouch forDelayedDelivery:NO]; + } + [[UIApplication sharedApplication] sendEvent:event]; +} + @implementation PTFakeMetaTouch + (void)load{ @@ -71,6 +101,15 @@ + (void)load{ [touch setPhaseAndUpdateTimestamp:UITouchPhaseEnded]; [touchAry addObject:touch]; } + CFRunLoopSourceContext context; + memset(&context, 0, sizeof(CFRunLoopSourceContext)); + context.perform = eventSendCallback; + // content of context is copied + source = CFRunLoopSourceCreate(NULL, -1, &context); + CFRunLoopRef loop = CFRunLoopGetMain(); + CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes); +// CFRunLoopMode mode = (CFRunLoopMode)UITrackingRunLoopMode; +// CFRunLoopAddSource(loop, source, GSEventReceiveRunLoopMode); } + (UITouch* ) touch: (NSInteger) pointId { @@ -80,43 +119,47 @@ + (UITouch* ) touch: (NSInteger) pointId { return nil; } -+ (NSInteger)fakeTouchId:(NSInteger)pointId AtPoint:(CGPoint)point withTouchPhase:(UITouchPhase)phase inWindow:(UIWindow*)window{ ++ (NSInteger)fakeTouchId:(NSInteger)pointId AtPoint:(CGPoint)point withTouchPhase:(UITouchPhase)phase inWindow:(UIWindow*)window onView:(UIView*)view{ + bool deleted = false; + UITouch* touch = NULL; + if(toRemove != NULL) { + touch = toRemove; + toRemove = NULL; + [livingTouchAry removeObjectIdenticalTo:touch]; + deleted = true; + } + if(toStationarify != NULL) { + // in case this is changed during the operations + touch = toStationarify; + toStationarify = NULL; + if(touch.phase == UITouchPhaseBegan) { + [touch setPhaseAndUpdateTimestamp:UITouchPhaseStationary]; + } + } pointId -= 1; // ideally should be phase began when this hit // but if by any means other phases come... well lets be forgiving - NSUInteger livingIndex = [livingTouchAry indexOfObjectIdenticalTo:touchAry[pointId]]; - if(livingIndex == NSNotFound) { - if(phase == UITouchPhaseEnded) return -1; - UITouch *touch = [[UITouch alloc] initAtPoint:point inWindow:window]; - livingIndex = livingTouchAry.count; + touch = touchAry[pointId]; + bool old = [livingTouchAry containsObject:touch]; + bool new = !old; + if(new) { + if(phase == UITouchPhaseEnded) return deleted; + touch = [[UITouch alloc] initAtPoint:point inWindow:window onView:view]; [livingTouchAry addObject:touch]; [touchAry setObject:touch atIndexedSubscript:pointId ]; - } - UITouch *touch = [livingTouchAry objectAtIndex:livingIndex]; - - [touch setLocationInWindow:point]; - if(touch.phase!=UITouchPhaseBegan){ + } else { [touch setPhaseAndUpdateTimestamp:phase]; + [touch setLocationInWindow:point]; } - +// CFRunLoopSourceContext context; +// CFRunLoopSourceGetContext(source, &context); + + CFTypeRef delayRelease = CFBridgingRetain(safeTouchAry); + safeTouchAry = [[NSArray alloc] initWithArray:livingTouchAry copyItems:NO]; + CFRunLoopSourceSignal(source); + CFBridgingRelease(delayRelease); // UIEvent *event = [self eventWithTouches:livingTouchAry]; - UIEvent *event = [[UIApplication sharedApplication] _touchesEvent]; - dispatch_sync(dispatch_get_main_queue(), ^{ - [event _clearTouches]; - for (UITouch *aTouch in livingTouchAry) { - [event _addTouch:aTouch forDelayedDelivery:NO]; - } - [[UIApplication sharedApplication] sendEvent:event]; - }); - if ((touch.phase==UITouchPhaseBegan)||touch.phase==UITouchPhaseMoved) { - [touch setPhaseAndUpdateTimestamp:UITouchPhaseStationary]; - } - - if (phase == UITouchPhaseEnded) { - [livingTouchAry removeObjectAtIndex:livingIndex]; -// [touchAry removeObjectAtIndex:pointId]; - } - return livingIndex; + return deleted; } diff --git a/PlayTools/Controls/PTFakeTouch/addition/UITouch-KIFAdditions.h b/PlayTools/Controls/PTFakeTouch/addition/UITouch-KIFAdditions.h index d2d63d6f..be81e6dd 100644 --- a/PlayTools/Controls/PTFakeTouch/addition/UITouch-KIFAdditions.h +++ b/PlayTools/Controls/PTFakeTouch/addition/UITouch-KIFAdditions.h @@ -36,6 +36,7 @@ KW_FIX_CATEGORY_BUG_H(UITouch_KIFAdditions) - (id)initInView:(UIView *)view; - (id)initAtPoint:(CGPoint)point inView:(UIView *)view; - (id)initAtPoint:(CGPoint)point inWindow:(UIWindow *)window; +- (id)initAtPoint:(CGPoint)point inWindow:(UIWindow *)window onView:(UIView*)view; - (id)initTouch; - (void)resetTouch; diff --git a/PlayTools/Controls/PTFakeTouch/addition/UITouch-KIFAdditions.m b/PlayTools/Controls/PTFakeTouch/addition/UITouch-KIFAdditions.m index 399239d3..bdcff737 100644 --- a/PlayTools/Controls/PTFakeTouch/addition/UITouch-KIFAdditions.m +++ b/PlayTools/Controls/PTFakeTouch/addition/UITouch-KIFAdditions.m @@ -34,6 +34,12 @@ - (id)initInView:(UIView *)view; } - (id)initAtPoint:(CGPoint)point inWindow:(UIWindow *)window; +{ + UIView *view = [window hitTest:point withEvent:nil]; + return [self initAtPoint:point inWindow:window onView:view]; +} + +- (id)initAtPoint:(CGPoint)point inWindow:(UIWindow *)window onView:(UIView *)view; { self = [super init]; if (self == nil) { @@ -46,7 +52,7 @@ - (id)initAtPoint:(CGPoint)point inWindow:(UIWindow *)window; //[self setTapCount:1]; [self _setLocationInWindow:point resetPrevious:YES]; - UIView *hitTestView = [window hitTest:point withEvent:nil]; + UIView *hitTestView = view; [self setView:hitTestView]; [self setPhase:UITouchPhaseBegan]; diff --git a/PlayTools/Controls/Toucher.swift b/PlayTools/Controls/Toucher.swift index 1e14095d..b86b64d4 100644 --- a/PlayTools/Controls/Toucher.swift +++ b/PlayTools/Controls/Toucher.swift @@ -9,14 +9,18 @@ import UIKit class Toucher { static var keyWindow: UIWindow? + static var keyView: UIView? static var touchQueue = DispatchQueue.init(label: "playcover.toucher", qos: .userInteractive) static func touchcam(point: CGPoint, phase: UITouch.Phase, tid: Int) { touchQueue.async { if keyWindow == nil { keyWindow = screen.keyWindow + DispatchQueue.main.sync { + keyView = keyWindow?.hitTest(point, with: nil) + } } - PTFakeMetaTouch.fakeTouchId(tid, at: point, with: phase, in: keyWindow) + PTFakeMetaTouch.fakeTouchId(tid, at: point, with: phase, in: keyWindow, on: keyView) } } } From 57f9eeeea379ba9a336229304eace917d1c96171 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Sun, 11 Sep 2022 06:43:54 +0800 Subject: [PATCH 19/27] fix camera logic when frame rate is low --- .../Controls/PTFakeTouch/PTFakeMetaTouch.m | 43 +++++++++++-------- PlayTools/Controls/PlayMice.swift | 9 ++-- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m index 94e66312..95278522 100644 --- a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m +++ b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m @@ -19,7 +19,7 @@ static CFRunLoopSourceRef source; static UITouch* toRemove = NULL, *toStationarify = NULL; - +bool needsCopy = false; NSArray *safeTouchAry; void disableCursor(boolean_t disable){ @@ -69,22 +69,25 @@ void moveCursorTo(CGPoint point){ void eventSendCallback(void* info) { UIEvent *event = [[UIApplication sharedApplication] _touchesEvent]; // to retain objects from being released - [event _clearTouches]; - NSArray *myAry = safeTouchAry; - for (UITouch *aTouch in myAry) { - switch (aTouch.phase) { - case UITouchPhaseEnded: - case UITouchPhaseCancelled: - toRemove = aTouch; - break; - case UITouchPhaseBegan: -// case UITouchPhaseMoved: - toStationarify = aTouch; - break; - default: - break; + if(needsCopy){ + needsCopy = false; + [event _clearTouches]; + NSArray *myAry = safeTouchAry; + for (UITouch *aTouch in myAry) { + switch (aTouch.phase) { + case UITouchPhaseEnded: + case UITouchPhaseCancelled: + toRemove = aTouch; + break; + case UITouchPhaseBegan: + // case UITouchPhaseMoved: + toStationarify = aTouch; + break; + default: + break; + } + [event _addTouch:aTouch forDelayedDelivery:NO]; } - [event _addTouch:aTouch forDelayedDelivery:NO]; } [[UIApplication sharedApplication] sendEvent:event]; } @@ -105,7 +108,7 @@ + (void)load{ memset(&context, 0, sizeof(CFRunLoopSourceContext)); context.perform = eventSendCallback; // content of context is copied - source = CFRunLoopSourceCreate(NULL, -1, &context); + source = CFRunLoopSourceCreate(NULL, -2, &context); CFRunLoopRef loop = CFRunLoopGetMain(); CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes); // CFRunLoopMode mode = (CFRunLoopMode)UITrackingRunLoopMode; @@ -148,9 +151,15 @@ + (NSInteger)fakeTouchId:(NSInteger)pointId AtPoint:(CGPoint)point withTouchPhas [livingTouchAry addObject:touch]; [touchAry setObject:touch atIndexedSubscript:pointId ]; } else { + if(touch.phase == UITouchPhaseBegan && phase == UITouchPhaseMoved) { + return deleted; + } [touch setPhaseAndUpdateTimestamp:phase]; [touch setLocationInWindow:point]; } + if(phase != UITouchPhaseMoved) { + needsCopy = true; + } // CFRunLoopSourceContext context; // CFRunLoopSourceGetContext(source, &context); diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 2e719b12..6de8ab53 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -54,7 +54,7 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool public func setupMouseMovedHandler() { for mouse in GCMouse.mice() { mouse.mouseInput?.mouseMovedHandler = { _, deltaX, deltaY in - Toucher.touchQueue.async { +// Toucher.touchQueue.async { if !mode.visible { if let draggableButton = DraggableButtonAction.activeButton { draggableButton.onMouseMoved(deltaX: CGFloat(deltaX), deltaY: CGFloat(deltaY)) @@ -62,7 +62,7 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool self.camera?.updated(CGFloat(deltaX), CGFloat(deltaY)) } } - } +// } } } } @@ -186,8 +186,9 @@ final class CameraControl { Toucher.touchcam(point: self.location, phase: UITouch.Phase.moved, tid: 1) let previous = sequence - delay(0.016) { - // if no other touch events in the past 0.016 sec + // if mouse not moving during this time, then an ended event is sent. + delay(0.2) { + // if no other touch events in this period if previous != self.sequence { return } From ef38cf649cc33ae27ba0d37d638dc34fc3afcf70 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Mon, 12 Sep 2022 02:19:15 +0800 Subject: [PATCH 20/27] reduce camera dispatch queue pressure --- .../Controls/PTFakeTouch/PTFakeMetaTouch.m | 51 +++++++------ PlayTools/Controls/PlayMice.swift | 72 ++++++++++--------- PlayTools/Controls/Toucher.swift | 6 +- 3 files changed, 64 insertions(+), 65 deletions(-) diff --git a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m index 95278522..4b899e0a 100644 --- a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m +++ b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m @@ -19,7 +19,6 @@ static CFRunLoopSourceRef source; static UITouch* toRemove = NULL, *toStationarify = NULL; -bool needsCopy = false; NSArray *safeTouchAry; void disableCursor(boolean_t disable){ @@ -69,25 +68,22 @@ void moveCursorTo(CGPoint point){ void eventSendCallback(void* info) { UIEvent *event = [[UIApplication sharedApplication] _touchesEvent]; // to retain objects from being released - if(needsCopy){ - needsCopy = false; - [event _clearTouches]; - NSArray *myAry = safeTouchAry; - for (UITouch *aTouch in myAry) { - switch (aTouch.phase) { - case UITouchPhaseEnded: - case UITouchPhaseCancelled: - toRemove = aTouch; - break; - case UITouchPhaseBegan: - // case UITouchPhaseMoved: - toStationarify = aTouch; - break; - default: - break; - } - [event _addTouch:aTouch forDelayedDelivery:NO]; + [event _clearTouches]; + NSArray *myAry = safeTouchAry; + for (UITouch *aTouch in myAry) { + switch (aTouch.phase) { + case UITouchPhaseEnded: + case UITouchPhaseCancelled: + toRemove = aTouch; + break; + case UITouchPhaseBegan: +// case UITouchPhaseMoved: + toStationarify = aTouch; + break; + default: + break; } + [event _addTouch:aTouch forDelayedDelivery:NO]; } [[UIApplication sharedApplication] sendEvent:event]; } @@ -125,11 +121,13 @@ + (UITouch* ) touch: (NSInteger) pointId { + (NSInteger)fakeTouchId:(NSInteger)pointId AtPoint:(CGPoint)point withTouchPhase:(UITouchPhase)phase inWindow:(UIWindow*)window onView:(UIView*)view{ bool deleted = false; UITouch* touch = NULL; + bool needsCopy = false; if(toRemove != NULL) { touch = toRemove; toRemove = NULL; [livingTouchAry removeObjectIdenticalTo:touch]; deleted = true; + needsCopy = true; } if(toStationarify != NULL) { // in case this is changed during the operations @@ -150,23 +148,22 @@ + (NSInteger)fakeTouchId:(NSInteger)pointId AtPoint:(CGPoint)point withTouchPhas touch = [[UITouch alloc] initAtPoint:point inWindow:window onView:view]; [livingTouchAry addObject:touch]; [touchAry setObject:touch atIndexedSubscript:pointId ]; + needsCopy = true; } else { if(touch.phase == UITouchPhaseBegan && phase == UITouchPhaseMoved) { return deleted; } - [touch setPhaseAndUpdateTimestamp:phase]; [touch setLocationInWindow:point]; } - if(phase != UITouchPhaseMoved) { - needsCopy = true; - } + [touch setPhaseAndUpdateTimestamp:phase]; // CFRunLoopSourceContext context; // CFRunLoopSourceGetContext(source, &context); - - CFTypeRef delayRelease = CFBridgingRetain(safeTouchAry); - safeTouchAry = [[NSArray alloc] initWithArray:livingTouchAry copyItems:NO]; + if(needsCopy) { + CFTypeRef delayRelease = CFBridgingRetain(safeTouchAry); + safeTouchAry = [[NSArray alloc] initWithArray:livingTouchAry copyItems:NO]; + CFBridgingRelease(delayRelease); + } CFRunLoopSourceSignal(source); - CFBridgingRelease(delayRelease); // UIEvent *event = [self eventWithTouches:livingTouchAry]; return deleted; } diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 6de8ab53..88041624 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -54,15 +54,13 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool public func setupMouseMovedHandler() { for mouse in GCMouse.mice() { mouse.mouseInput?.mouseMovedHandler = { _, deltaX, deltaY in -// Toucher.touchQueue.async { - if !mode.visible { - if let draggableButton = DraggableButtonAction.activeButton { - draggableButton.onMouseMoved(deltaX: CGFloat(deltaX), deltaY: CGFloat(deltaY)) - } else { - self.camera?.updated(CGFloat(deltaX), CGFloat(deltaY)) - } + if !mode.visible { + if let draggableButton = DraggableButtonAction.activeButton { + draggableButton.onMouseMoved(deltaX: CGFloat(deltaX), deltaY: CGFloat(deltaY)) + } else { + self.camera?.updated(CGFloat(deltaX), CGFloat(deltaY)) } -// } + } } } } @@ -146,20 +144,44 @@ final class CameraControl { // if max speed of this touch is high var movingFast = false - // seq number for each move event. Used in closure to determine if this move is the last - var sequence = 0 // like sequence but resets when touch begins. Used to calc touch duration var counter = 0 // if should wait before beginning next touch var cooldown = false // if the touch point had been prevented from lifting off because of moving slow var idled = false + // in how many tests has this been identified as stationary + var stationaryCount = 0 + let stationaryThreshold = 3 + + @objc func checkEnded() { + // if been stationary for enough time + if self.stationaryCount < self.stationaryThreshold { + self.stationaryCount += 1 + self.delay(0.1, closure: checkEnded) + return + } + // and slow touch lasts for sufficient time + if self.movingFast || self.counter > 64 { + self.doLiftOff() + } else { + self.idled = true + // idle for at most 4 seconds + self.delay(4) { + if self.stationaryCount < self.stationaryThreshold { + self.stationaryCount += 1 + self.delay(0.1, closure: self.checkEnded) + return + } + self.doLiftOff() + } + } + } @objc func updated(_ deltaX: CGFloat, _ deltaY: CGFloat) { if mode.visible || cooldown { return } - sequence += 1 // count touch duration counter += 1 if !isMoving { @@ -168,7 +190,10 @@ final class CameraControl { idled = false location = center counter = 0 + stationaryCount = 0 Toucher.touchcam(point: self.center, phase: UITouch.Phase.began, tid: 1) + + delay(0.5, closure: checkEnded) } // if not moving fast, regard the user fine-tuning the camera(e.g. aiming) // so hold the touch for longer to avoid cold startup @@ -184,29 +209,7 @@ final class CameraControl { self.location.x += deltaX * CGFloat(PlaySettings.shared.sensitivity) self.location.y -= deltaY * CGFloat(PlaySettings.shared.sensitivity) Toucher.touchcam(point: self.location, phase: UITouch.Phase.moved, tid: 1) - let previous = sequence - - // if mouse not moving during this time, then an ended event is sent. - delay(0.2) { - // if no other touch events in this period - if previous != self.sequence { - return - } - // and slow touch lasts for sufficient time - if self.movingFast || self.counter > 64 { - self.doLiftOff() - } else { - self.idled = true - // idle for at most 4 seconds - self.delay(4) { - if previous != self.sequence { - return - } - self.doLiftOff() - } - } - } - + stationaryCount = 0 } public func doLiftOff() { @@ -225,7 +228,6 @@ final class CameraControl { } func stop() { - sequence = 0 self.doLiftOff() } } diff --git a/PlayTools/Controls/Toucher.swift b/PlayTools/Controls/Toucher.swift index b86b64d4..edcbf553 100644 --- a/PlayTools/Controls/Toucher.swift +++ b/PlayTools/Controls/Toucher.swift @@ -8,13 +8,13 @@ import UIKit class Toucher { - static var keyWindow: UIWindow? - static var keyView: UIView? + static weak var keyWindow: UIWindow? + static weak var keyView: UIView? static var touchQueue = DispatchQueue.init(label: "playcover.toucher", qos: .userInteractive) static func touchcam(point: CGPoint, phase: UITouch.Phase, tid: Int) { touchQueue.async { - if keyWindow == nil { + if keyWindow == nil || keyView == nil { keyWindow = screen.keyWindow DispatchQueue.main.sync { keyView = keyWindow?.hitTest(point, with: nil) From 30ca797a0e0f7e9b89095ff25185c16c67ce28b3 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Mon, 12 Sep 2022 05:02:06 +0800 Subject: [PATCH 21/27] tune camera control parameters --- PlayTools/Controls/PlayMice.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 88041624..47e19bb9 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -152,7 +152,7 @@ final class CameraControl { var idled = false // in how many tests has this been identified as stationary var stationaryCount = 0 - let stationaryThreshold = 3 + let stationaryThreshold = 2 @objc func checkEnded() { // if been stationary for enough time @@ -197,9 +197,10 @@ final class CameraControl { } // if not moving fast, regard the user fine-tuning the camera(e.g. aiming) // so hold the touch for longer to avoid cold startup - if deltaX.magnitude + deltaY.magnitude > 4 { + if deltaX.magnitude + deltaY.magnitude > 10 { // if we had mistaken this as player aiming if self.idled { +// Toast.showOver(msg: "idled") // since not aiming, re-touch to re-gain control self.doLiftOff() return @@ -220,8 +221,8 @@ final class CameraControl { self.isMoving = false // ending and beginning too frequently leads to the beginning event not recognized // so let the beginning event wait some time - // 0.016 here is safe as long as the 0.016 above works - delay(0.016) { + // pause for one frame or two + delay(0.02) { self.cooldown = false } cooldown = true From 8dd3f5f96c445fd25b61bde3fc83447caf1158fc Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Mon, 12 Sep 2022 23:36:37 +0800 Subject: [PATCH 22/27] one key map to many buttons --- PlayTools/Controls/PlayAction.swift | 20 ++++++++++++++++++-- PlayTools/Controls/PlayMice.swift | 22 ++++++++++++++-------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/PlayTools/Controls/PlayAction.swift b/PlayTools/Controls/PlayAction.swift index ef804849..eb0b8a25 100644 --- a/PlayTools/Controls/PlayAction.swift +++ b/PlayTools/Controls/PlayAction.swift @@ -19,6 +19,9 @@ extension GCKeyboard { class ButtonAction: Action { func invalidate() { Toucher.touchcam(point: point, phase: UITouch.Phase.ended, tid: id) + if let keyboard = GCKeyboard.coalesced?.keyboardInput { + keyboard.button(forKeyCode: key)?.pressedChangedHandler = nil + } } let key: GCKeyCode @@ -33,10 +36,14 @@ class ButtonAction: Action { self.id = id if let keyboard = GCKeyboard.coalesced?.keyboardInput { if !PlayMice.shared.setMiceButtons(keyid, action: self) { - keyboard.button(forKeyCode: key)?.pressedChangedHandler = { _, _, pressed in + let handler = keyboard.button(forKeyCode: key)?.pressedChangedHandler + keyboard.button(forKeyCode: key)?.pressedChangedHandler = { button, value, pressed in if !mode.visible && !PlayInput.cmdPressed() { self.update(pressed: pressed) } + if let previous = handler { + previous(button, value, pressed) + } } } } @@ -112,8 +119,12 @@ class JoystickAction: Action { self.id = id if let keyboard = GCKeyboard.coalesced?.keyboardInput { for key in keys { - keyboard.button(forKeyCode: key)?.pressedChangedHandler = { _, _, _ in + let handler = keyboard.button(forKeyCode: key)?.pressedChangedHandler + keyboard.button(forKeyCode: key)?.pressedChangedHandler = { button, value, pressed in Toucher.touchQueue.async(execute: self.update) + if let previous = handler { + previous(button, value, pressed) + } } } } @@ -137,6 +148,11 @@ class JoystickAction: Action { func invalidate() { Toucher.touchcam(point: center, phase: UITouch.Phase.ended, tid: id) self.moving = false + if let keyboard = GCKeyboard.coalesced?.keyboardInput { + for key in keys { + keyboard.button(forKeyCode: key)?.pressedChangedHandler = nil + } + } } func update() { diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 2e719b12..4065db42 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -73,7 +73,9 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool } camera?.stop() camera = nil - mouseActions = [:] + mouseActions.keys.forEach { key in + mouseActions[key] = [] + } } func setMiceButtons(_ keyId: Int, action: ButtonAction) -> Bool { @@ -84,12 +86,14 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool return false } - var mouseActions: [Int: ButtonAction] = [:] + var mouseActions: [Int: [ButtonAction]] = [2: [], 8: [], 33554432: []] private func setupMouseButton(_up: Int, _down: Int) { Dynamic.NSEvent.addLocalMonitorForEventsMatchingMask(_up, handler: { event in if !mode.visible || self.acceptMouseEvents { - self.mouseActions[_up]?.update(pressed: true) + self.mouseActions[_up]!.forEach({ buttonAction in + buttonAction.update(pressed: true) + }) if self.acceptMouseEvents { return event } @@ -105,7 +109,9 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool } as ResponseBlock) Dynamic.NSEvent.addLocalMonitorForEventsMatchingMask(_down, handler: { event in if !mode.visible || self.acceptMouseEvents { - self.mouseActions[_up]?.update(pressed: false) + self.mouseActions[_up]!.forEach({ buttonAction in + buttonAction.update(pressed: false) + }) if self.acceptMouseEvents { return event } @@ -117,11 +123,11 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool private func setMiceButton(_ keyId: Int, action: ButtonAction) { switch keyId { - case -1: mouseActions[2] = action - case -2: mouseActions[8] = action - case -3: mouseActions[33554432] = action + case -1: mouseActions[2]!.append(action) + case -2: mouseActions[8]!.append(action) + case -3: mouseActions[33554432]!.append(action) default: - mouseActions[2] = action + mouseActions[2]!.append(action) } } } From ad1b05e6b5e0e5b554c5431a4d05be61cb514d06 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Wed, 14 Sep 2022 00:57:24 +0800 Subject: [PATCH 23/27] tune camera parameter & replace incorrect header file --- PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m | 2 +- PlayTools/Controls/PlayMice.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m index 4b899e0a..ac04c75c 100644 --- a/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m +++ b/PlayTools/Controls/PTFakeTouch/PTFakeMetaTouch.m @@ -12,7 +12,7 @@ #import "UIEvent+KIFAdditions.h" #import "CoreFoundation/CFRunLoop.h" #include -#include +#include static NSMutableArray *touchAry; static NSMutableArray *livingTouchAry; diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 47e19bb9..06ce91dc 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -193,11 +193,11 @@ final class CameraControl { stationaryCount = 0 Toucher.touchcam(point: self.center, phase: UITouch.Phase.began, tid: 1) - delay(0.5, closure: checkEnded) + delay(0.1, closure: checkEnded) } // if not moving fast, regard the user fine-tuning the camera(e.g. aiming) // so hold the touch for longer to avoid cold startup - if deltaX.magnitude + deltaY.magnitude > 10 { + if deltaX.magnitude + deltaY.magnitude > 12 { // if we had mistaken this as player aiming if self.idled { // Toast.showOver(msg: "idled") From 8e13fa797baadcce9ee01196f85037030be9116d Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Thu, 15 Sep 2022 05:49:56 +0100 Subject: [PATCH 24/27] Switch to macOS Plugin instead of Dynamic (#38) Switch to macOS Plugin instead of Dynamic (#38) * Add Plugin * AKPluginLoader * AKPluginLoader * Remove majority of Dynamic * Remove Dynamic * Init AKInterface earlier * Fix redundant key press removal * Try to eliminate beep in UIKit * Finish reimplementing remaining dynamic logic * Comment out sound fix for now * Add handler for redundant key press removal * Hopefully? functional monstrosity * Cleanup 1 * Cleanup 2 * Fix Cleanup 2 * Resolve conflicts * Update event handlers * SwiftLint * Fix conflicts * Fix conflicts * Debug check for fullscreen * Revert "Debug check for fullscreen" This reverts commit 3e5d705402b45695f0fd0bff66d8e80587d0f185. --- AKInterface-Bridging-Header.h | 4 + AKPlugin.swift | 83 +++++ PlayTools.xcodeproj/project.pbxproj | 202 +++++++++-- .../xcshareddata/xcschemes/PlayTools.xcscheme | 14 + PlayTools/Controls/ControlMode.swift | 14 +- PlayTools/Controls/PlayAction.swift | 13 +- PlayTools/Controls/PlayInput.swift | 29 +- PlayTools/Controls/PlayMice.swift | 124 +++---- PlayTools/DiscordActivity/DiscordIPC.swift | 2 +- PlayTools/Keymap/ControlModel.swift | 2 +- PlayTools/Keymap/EditorController.swift | 16 +- PlayTools/Keymap/Keymapping.swift | 7 +- PlayTools/PlayCover.swift | 9 +- PlayTools/PlayScreen.swift | 57 +--- PlayTools/PlaySettings.swift | 3 +- PlayTools/PlayUI.swift | 9 +- PlayTools/Utils/AKPluginLoader.swift | 32 ++ PlayTools/Utils/Dynamic/Dynamic.swift | 317 ------------------ PlayTools/Utils/Dynamic/Invocation.swift | 238 ------------- PlayTools/Utils/Dynamic/Logger.swift | 106 ------ PlayTools/Utils/Dynamic/TypeMapping.swift | 125 ------- PlayTools/Utils/PlayInfo.swift | 5 +- Plugin.swift | 28 ++ 23 files changed, 436 insertions(+), 1003 deletions(-) create mode 100644 AKInterface-Bridging-Header.h create mode 100644 AKPlugin.swift create mode 100644 PlayTools/Utils/AKPluginLoader.swift delete mode 100644 PlayTools/Utils/Dynamic/Dynamic.swift delete mode 100644 PlayTools/Utils/Dynamic/Invocation.swift delete mode 100644 PlayTools/Utils/Dynamic/Logger.swift delete mode 100644 PlayTools/Utils/Dynamic/TypeMapping.swift create mode 100644 Plugin.swift diff --git a/AKInterface-Bridging-Header.h b/AKInterface-Bridging-Header.h new file mode 100644 index 00000000..1b2cb5d6 --- /dev/null +++ b/AKInterface-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/AKPlugin.swift b/AKPlugin.swift new file mode 100644 index 00000000..2f1ce05b --- /dev/null +++ b/AKPlugin.swift @@ -0,0 +1,83 @@ +// +// MacPlugin.swift +// AKInterface +// +// Created by Isaac Marovitz on 13/09/2022. +// + +import AppKit +import Foundation + +class AKPlugin: NSObject, Plugin { + required override init() { + } + + var screenCount: Int { + NSScreen.screens.count + } + + var mousePoint: CGPoint { + NSApplication.shared.windows.first!.mouseLocationOutsideOfEventStream as CGPoint + } + + var windowFrame: CGRect { + NSApplication.shared.windows.first!.frame as CGRect + } + + var isMainScreenEqualToFirst: Bool { + return NSScreen.main == NSScreen.screens.first + } + + var mainScreenFrame: CGRect { + return NSScreen.main!.frame as CGRect + } + + var isFullscreen: Bool { + NSApplication.shared.windows.first!.styleMask.contains(.fullScreen) + } + + func hideCursor() { + NSCursor.hide() + } + + func unhideCursor() { + NSCursor.unhide() + } + + func terminateApplication() { + NSApplication.shared.terminate(self) + } + + func eliminateRedundantKeyPressEvents(_ dontIgnore: @escaping() -> Bool) { + NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: { event in + if dontIgnore() { + return event + } + return nil + }) + } + + func setupMouseButton(_ _up: Int, _ _down: Int, _ dontIgnore: @escaping(Int, Bool, Bool) -> Bool) { + NSEvent.addLocalMonitorForEvents(matching: NSEvent.EventTypeMask(rawValue: UInt64(_up)), handler: { event in + let isEventWindow = event.window == NSApplication.shared.windows.first! + if dontIgnore(_up, true, isEventWindow) { + return event + } + return nil + }) + NSEvent.addLocalMonitorForEvents(matching: NSEvent.EventTypeMask(rawValue: UInt64(_down)), handler: { event in + if dontIgnore(_up, false, true) { + return event + } + return nil + }) + } + + func urlForApplicationWithBundleIdentifier(_ value: String) -> URL? { + NSWorkspace.shared.urlForApplication(withBundleIdentifier: value) + } + + func setMenuBarVisible(_ visible: Bool) { + NSMenu.setMenuBarVisible(visible) + } +} diff --git a/PlayTools.xcodeproj/project.pbxproj b/PlayTools.xcodeproj/project.pbxproj index 7bb05989..d3b169e3 100644 --- a/PlayTools.xcodeproj/project.pbxproj +++ b/PlayTools.xcodeproj/project.pbxproj @@ -7,6 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 6E76639B28D0FAE700DE4AF9 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E76639A28D0FAE700DE4AF9 /* Plugin.swift */; }; + 6E76639C28D0FAE700DE4AF9 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E76639A28D0FAE700DE4AF9 /* Plugin.swift */; }; + 6E7663A128D0FB5300DE4AF9 /* AKPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E7663A028D0FB5300DE4AF9 /* AKPlugin.swift */; }; + 6E7663A528D0FEBE00DE4AF9 /* AKPluginLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E7663A428D0FEBE00DE4AF9 /* AKPluginLoader.swift */; }; + 6E84A14528D0F94E00BF7495 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA818CBA287ABFD5000BEE9D /* UIKit.framework */; }; + 6E84A15028D0F97500BF7495 /* AKInterface.bundle in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6E84A14C28D0F96D00BF7495 /* AKInterface.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; AA71970B287A44D200623C15 /* PlayLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = AA719702287A44D200623C15 /* PlayLoader.m */; }; AA71970C287A44D200623C15 /* PlayScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA719703287A44D200623C15 /* PlayScreen.swift */; }; AA71970D287A44D200623C15 /* PlaySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA719704287A44D200623C15 /* PlaySettings.swift */; }; @@ -22,10 +28,6 @@ AA7197A1287A481500623C15 /* CircleMenuLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA71978D287A481500623C15 /* CircleMenuLoader.swift */; }; AA7197A2287A481500623C15 /* CircleMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA71978E287A481500623C15 /* CircleMenu.swift */; }; AA7197A3287A481500623C15 /* CircleMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA71978F287A481500623C15 /* CircleMenuButton.swift */; }; - AA7197A5287A481500623C15 /* Dynamic.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA719793287A481500623C15 /* Dynamic.swift */; }; - AA7197A6287A481500623C15 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA719794287A481500623C15 /* Logger.swift */; }; - AA7197A7287A481500623C15 /* Invocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA719795287A481500623C15 /* Invocation.swift */; }; - AA7197A8287A481500623C15 /* TypeMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA719796287A481500623C15 /* TypeMapping.swift */; }; AA7197A9287A481500623C15 /* PlayInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA719797287A481500623C15 /* PlayInfo.swift */; }; AA7197AA287A481500623C15 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA719798287A481500623C15 /* Toast.swift */; }; AA7197AB287A481500623C15 /* ControlModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA71979A287A481500623C15 /* ControlModel.swift */; }; @@ -79,14 +81,32 @@ AA719871287A81A000623C15 /* FixCategoryBug.h in Headers */ = {isa = PBXBuildFile; fileRef = AA719843287A81A000623C15 /* FixCategoryBug.h */; }; AA719872287A81A000623C15 /* UITouch+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AA719844287A81A000623C15 /* UITouch+Private.h */; }; AA818CB9287ABFB1000BEE9D /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA818CB8287ABFB1000BEE9D /* IOKit.framework */; }; - AA818CBB287ABFD5000BEE9D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA818CBA287ABFD5000BEE9D /* UIKit.framework */; }; B127172228817AB90025112B /* SwordRPC in Frameworks */ = {isa = PBXBuildFile; productRef = B127172128817AB90025112B /* SwordRPC */; }; B127172528817C040025112B /* DiscordIPC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B127172428817C040025112B /* DiscordIPC.swift */; }; B1271729288284BE0025112B /* DiscordActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1271728288284BE0025112B /* DiscordActivity.swift */; }; B1E8CF8A28BBE2AB004340D3 /* Keymapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E8CF8928BBE2AB004340D3 /* Keymapping.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXCopyFilesBuildPhase section */ + 6E84A14728D0F94E00BF7495 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 6E84A15028D0F97500BF7495 /* AKInterface.bundle in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 6E76639628D0FA6200DE4AF9 /* AKInterface-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AKInterface-Bridging-Header.h"; sourceTree = ""; }; + 6E76639A28D0FAE700DE4AF9 /* Plugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Plugin.swift; sourceTree = ""; }; + 6E7663A028D0FB5300DE4AF9 /* AKPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AKPlugin.swift; sourceTree = ""; }; + 6E7663A428D0FEBE00DE4AF9 /* AKPluginLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AKPluginLoader.swift; sourceTree = ""; }; + 6E84A14C28D0F96D00BF7495 /* AKInterface.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AKInterface.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; AA3430B0287AB7F2004E208A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; AA7196D8287A447700623C15 /* PlayTools.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PlayTools.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AA719702287A44D200623C15 /* PlayLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayLoader.m; sourceTree = ""; }; @@ -106,10 +126,6 @@ AA71978D287A481500623C15 /* CircleMenuLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMenuLoader.swift; sourceTree = ""; }; AA71978E287A481500623C15 /* CircleMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMenu.swift; sourceTree = ""; }; AA71978F287A481500623C15 /* CircleMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMenuButton.swift; sourceTree = ""; }; - AA719793287A481500623C15 /* Dynamic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dynamic.swift; sourceTree = ""; }; - AA719794287A481500623C15 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; - AA719795287A481500623C15 /* Invocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Invocation.swift; sourceTree = ""; }; - AA719796287A481500623C15 /* TypeMapping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeMapping.swift; sourceTree = ""; }; AA719797287A481500623C15 /* PlayInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayInfo.swift; sourceTree = ""; }; AA719798287A481500623C15 /* Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; AA71979A287A481500623C15 /* ControlModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlModel.swift; sourceTree = ""; }; @@ -170,23 +186,41 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 6E84A14928D0F96D00BF7495 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; AA7196D5287A447700623C15 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( B127172228817AB90025112B /* SwordRPC in Frameworks */, + 6E84A14528D0F94E00BF7495 /* UIKit.framework in Frameworks */, AA818CB9287ABFB1000BEE9D /* IOKit.framework in Frameworks */, - AA818CBB287ABFD5000BEE9D /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 6E76639928D0FA6F00DE4AF9 /* AKInterface */ = { + isa = PBXGroup; + children = ( + 6E76639A28D0FAE700DE4AF9 /* Plugin.swift */, + 6E7663A028D0FB5300DE4AF9 /* AKPlugin.swift */, + 6E76639628D0FA6200DE4AF9 /* AKInterface-Bridging-Header.h */, + ); + name = AKInterface; + sourceTree = ""; + }; AA7196CE287A447700623C15 = { isa = PBXGroup; children = ( AA7196DA287A447700623C15 /* PlayTools */, + 6E76639928D0FA6F00DE4AF9 /* AKInterface */, AA7196D9287A447700623C15 /* Products */, AA818CB6287ABFA7000BEE9D /* Recovered References */, AA818CB7287ABFB1000BEE9D /* Frameworks */, @@ -197,6 +231,7 @@ isa = PBXGroup; children = ( AA7196D8287A447700623C15 /* PlayTools.framework */, + 6E84A14C28D0F96D00BF7495 /* AKInterface.bundle */, ); name = Products; sourceTree = ""; @@ -211,12 +246,12 @@ AA719791287A481500623C15 /* Utils */, AA71970A287A44D200623C15 /* Info.plist */, AA719706287A44D200623C15 /* PlayCover.swift */, - AA719705287A44D200623C15 /* PlayLoader.h */, - AA719702287A44D200623C15 /* PlayLoader.m */, AA719703287A44D200623C15 /* PlayScreen.swift */, AA719704287A44D200623C15 /* PlaySettings.swift */, - AA719708287A44D200623C15 /* PlayTools.h */, AA719709287A44D200623C15 /* PlayUI.swift */, + AA719705287A44D200623C15 /* PlayLoader.h */, + AA719702287A44D200623C15 /* PlayLoader.m */, + AA719708287A44D200623C15 /* PlayTools.h */, ); path = PlayTools; sourceTree = ""; @@ -248,24 +283,13 @@ AA719791287A481500623C15 /* Utils */ = { isa = PBXGroup; children = ( - AA719792287A481500623C15 /* Dynamic */, AA719797287A481500623C15 /* PlayInfo.swift */, AA719798287A481500623C15 /* Toast.swift */, + 6E7663A428D0FEBE00DE4AF9 /* AKPluginLoader.swift */, ); path = Utils; sourceTree = ""; }; - AA719792287A481500623C15 /* Dynamic */ = { - isa = PBXGroup; - children = ( - AA719793287A481500623C15 /* Dynamic.swift */, - AA719794287A481500623C15 /* Logger.swift */, - AA719795287A481500623C15 /* Invocation.swift */, - AA719796287A481500623C15 /* TypeMapping.swift */, - ); - path = Dynamic; - sourceTree = ""; - }; AA719799287A481500623C15 /* Keymap */ = { isa = PBXGroup; children = ( @@ -405,6 +429,23 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 6E84A14B28D0F96D00BF7495 /* AKInterface */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6E84A14D28D0F96D00BF7495 /* Build configuration list for PBXNativeTarget "AKInterface" */; + buildPhases = ( + 6E84A14828D0F96D00BF7495 /* Sources */, + 6E84A14928D0F96D00BF7495 /* Frameworks */, + 6E84A14A28D0F96D00BF7495 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AKInterface; + productName = AKInterface; + productReference = 6E84A14C28D0F96D00BF7495 /* AKInterface.bundle */; + productType = "com.apple.product-type.bundle"; + }; AA7196D7287A447700623C15 /* PlayTools */ = { isa = PBXNativeTarget; buildConfigurationList = AA7196DF287A447700623C15 /* Build configuration list for PBXNativeTarget "PlayTools" */; @@ -414,6 +455,7 @@ AA7196D5287A447700623C15 /* Frameworks */, AA7196D6287A447700623C15 /* Resources */, 6ED6ACAB28AAB30F0040D234 /* SwiftLint */, + 6E84A14728D0F94E00BF7495 /* Embed Frameworks */, ); buildRules = ( ); @@ -436,6 +478,10 @@ BuildIndependentTargetsInParallel = NO; LastUpgradeCheck = 1340; TargetAttributes = { + 6E84A14B28D0F96D00BF7495 = { + CreatedOnToolsVersion = 14.0; + LastSwiftMigration = 1400; + }; AA7196D7287A447700623C15 = { CreatedOnToolsVersion = 13.4.1; LastSwiftMigration = 1340; @@ -459,11 +505,19 @@ projectRoot = ""; targets = ( AA7196D7287A447700623C15 /* PlayTools */, + 6E84A14B28D0F96D00BF7495 /* AKInterface */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 6E84A14A28D0F96D00BF7495 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; AA7196D6287A447700623C15 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -495,6 +549,15 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 6E84A14828D0F96D00BF7495 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6E7663A128D0FB5300DE4AF9 /* AKPlugin.swift in Sources */, + 6E76639C28D0FAE700DE4AF9 /* Plugin.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; AA7196D4287A447700623C15 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -505,18 +568,18 @@ AA71975A287A480D00623C15 /* Toucher.swift in Sources */, AA71984A287A81A000623C15 /* NSString+KIFAdditions.m in Sources */, AA7197A1287A481500623C15 /* CircleMenuLoader.swift in Sources */, + 6E76639B28D0FAE700DE4AF9 /* Plugin.swift in Sources */, B1271729288284BE0025112B /* DiscordActivity.swift in Sources */, AA71984E287A81A000623C15 /* UIScrollView-KIFAdditions.m in Sources */, AA719849287A81A000623C15 /* NSException-KIFAdditions.m in Sources */, AA7197AB287A481500623C15 /* ControlModel.swift in Sources */, - AA7197A8287A481500623C15 /* TypeMapping.swift in Sources */, AA7197AE287A481500623C15 /* DragElementsView.swift in Sources */, - AA7197A7287A481500623C15 /* Invocation.swift in Sources */, AA71970D287A44D200623C15 /* PlaySettings.swift in Sources */, AA71984F287A81A000623C15 /* UIView-Debugging.m in Sources */, AA719759287A480D00623C15 /* PlayAction.swift in Sources */, AA719847287A81A000623C15 /* UIAccessibilityElement-KIFAdditions.m in Sources */, AA71984D287A81A000623C15 /* UIWindow-KIFAdditions.m in Sources */, + 6E7663A528D0FEBE00DE4AF9 /* AKPluginLoader.swift in Sources */, AA719867287A81A000623C15 /* NSPredicate+KIFAdditions.m in Sources */, AA719845287A81A000623C15 /* NSBundle+Swizzle.m in Sources */, AA7197A2287A481500623C15 /* CircleMenu.swift in Sources */, @@ -526,10 +589,8 @@ AA719789287A480D00623C15 /* PlayInput.swift in Sources */, AA719865287A81A000623C15 /* UIEvent+KIFAdditions.m in Sources */, AA71970B287A44D200623C15 /* PlayLoader.m in Sources */, - AA7197A5287A481500623C15 /* Dynamic.swift in Sources */, AA719712287A44D200623C15 /* PlayUI.swift in Sources */, AA719860287A81A000623C15 /* UIView-KIFAdditions.m in Sources */, - AA7197A6287A481500623C15 /* Logger.swift in Sources */, AA7197AF287A481500623C15 /* KeyCodeNames.swift in Sources */, AA71978A287A480D00623C15 /* ControlMode.swift in Sources */, AA719758287A480D00623C15 /* PlayMice.swift in Sources */, @@ -552,6 +613,70 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + 6E84A14E28D0F96D00BF7495 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPrincipalClass = "$(PRODUCT_BUNDLE_NAME).AKPlugin"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 12.3; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.playcover.AKInterface; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "AKInterface-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 6E84A14F28D0F96D00BF7495 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPrincipalClass = "$(PRODUCT_BUNDLE_NAME).AKPlugin"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 12.3; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.playcover.AKInterface; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "AKInterface-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; AA7196DD287A447700623C15 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -706,14 +831,15 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SCAN_ALL_SOURCE_FILES_FOR_INCLUDES = YES; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "$(SWIFT_MODULE_NAME)/$(SWIFT_MODULE_NAME).h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME)-Swift.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 2; }; name = Debug; }; @@ -750,19 +876,29 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SCAN_ALL_SOURCE_FILES_FOR_INCLUDES = YES; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "$(SWIFT_MODULE_NAME)/$(SWIFT_MODULE_NAME).h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME)-Swift.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 2; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 6E84A14D28D0F96D00BF7495 /* Build configuration list for PBXNativeTarget "AKInterface" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6E84A14E28D0F96D00BF7495 /* Debug */, + 6E84A14F28D0F96D00BF7495 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; AA7196D2287A447700623C15 /* Build configuration list for PBXProject "PlayTools" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/PlayTools.xcodeproj/xcshareddata/xcschemes/PlayTools.xcscheme b/PlayTools.xcodeproj/xcshareddata/xcschemes/PlayTools.xcscheme index 87f1d7e8..fca9673d 100644 --- a/PlayTools.xcodeproj/xcshareddata/xcschemes/PlayTools.xcscheme +++ b/PlayTools.xcodeproj/xcshareddata/xcschemes/PlayTools.xcscheme @@ -6,6 +6,20 @@ parallelizeBuildables = "YES" buildImplicitDependencies = "YES"> + + + + Bool { - return [1, 2].contains(Dynamic(event).type.asInt) - } + static public let mode = ControlMode() + public var visible: Bool = PlaySettings.shared.mouseMapping func show(_ show: Bool) { if !editor.editorMode { @@ -24,7 +20,7 @@ let mode = ControlMode.mode screen.switchDock(true) } if PlaySettings.shared.mouseMapping { - Dynamic.NSCursor.unhide() + AKInterface.shared!.unhideCursor() disableCursor(1) } PlayInput.shared.invalidate() @@ -32,7 +28,7 @@ let mode = ControlMode.mode } else { if visible { if PlaySettings.shared.mouseMapping { - Dynamic.NSCursor.hide() + AKInterface.shared!.hideCursor() disableCursor(0) } if screen.fullscreen { diff --git a/PlayTools/Controls/PlayAction.swift b/PlayTools/Controls/PlayAction.swift index eb0b8a25..65d2ecda 100644 --- a/PlayTools/Controls/PlayAction.swift +++ b/PlayTools/Controls/PlayAction.swift @@ -25,17 +25,15 @@ class ButtonAction: Action { } let key: GCKeyCode - let keyid: Int let point: CGPoint var id: Int - init(id: Int, keyid: Int, key: GCKeyCode, point: CGPoint) { - self.keyid = keyid + init(id: Int, key: GCKeyCode, point: CGPoint) { self.key = key self.point = point self.id = id if let keyboard = GCKeyboard.coalesced?.keyboardInput { - if !PlayMice.shared.setMiceButtons(keyid, action: self) { + if !PlayMice.shared.setMiceButtons(key.rawValue, action: self) { let handler = keyboard.button(forKeyCode: key)?.pressedChangedHandler keyboard.button(forKeyCode: key)?.pressedChangedHandler = { button, value, pressed in if !mode.visible && !PlayInput.cmdPressed() { @@ -52,8 +50,7 @@ class ButtonAction: Action { convenience init(id: Int, data: Button) { self.init( id: id, - keyid: data.keyCode, - key: GCKeyCode(rawValue: CFIndex(data.keyCode)), + key: GCKeyCode(rawValue: data.keyCode), point: CGPoint( x: data.transform.xCoord.absoluteX, y: data.transform.yCoord.absoluteY)) @@ -73,9 +70,9 @@ class DraggableButtonAction: ButtonAction { var releasePoint: CGPoint - override init(id: Int, keyid: Int, key: GCKeyCode, point: CGPoint) { + override init(id: Int, key: GCKeyCode, point: CGPoint) { self.releasePoint = point - super.init(id: id, keyid: keyid, key: key, point: point) + super.init(id: id, key: key, point: point) if settings.mouseMapping { PlayMice.shared.setupMouseMovedHandler() } diff --git a/PlayTools/Controls/PlayInput.swift b/PlayTools/Controls/PlayInput.swift index fd93736c..a3bb0d64 100644 --- a/PlayTools/Controls/PlayInput.swift +++ b/PlayTools/Controls/PlayInput.swift @@ -2,7 +2,7 @@ import Foundation import GameController import UIKit -final class PlayInput: NSObject { +class PlayInput { static let shared = PlayInput() var actions = [Action]() var timeoutForBind = true @@ -52,10 +52,10 @@ final class PlayInput: NSObject { EditorController.shared.setKeyCode(keyCode.rawValue) } } - keyboard.button(forKeyCode: GCKeyCode(rawValue: 227))?.pressedChangedHandler = { _, _, pressed in + keyboard.button(forKeyCode: .leftGUI)?.pressedChangedHandler = { _, _, pressed in PlayInput.lCmdPressed = pressed } - keyboard.button(forKeyCode: GCKeyCode(rawValue: 231))?.pressedChangedHandler = { _, _, pressed in + keyboard.button(forKeyCode: .rightGUI)?.pressedChangedHandler = { _, _, pressed in PlayInput.rCmdPressed = pressed } keyboard.button(forKeyCode: .leftAlt)?.pressedChangedHandler = { _, _, pressed in @@ -68,8 +68,6 @@ final class PlayInput: NSObject { } static public func cmdPressed() -> Bool { - // return keyboard.button(forKeyCode: GCKeyCode(rawValue: 227))!.isPressed - // || keyboard.button(forKeyCode: GCKeyCode(rawValue: 231))!.isPressed return lCmdPressed || rCmdPressed } @@ -85,8 +83,8 @@ final class PlayInput: NSObject { } private static let FORBIDDEN: [GCKeyCode] = [ - GCKeyCode.init(rawValue: 227), // LCmd - GCKeyCode.init(rawValue: 231), // RCmd + .leftGUI, + .rightGUI, .leftAlt, .rightAlt, .printScreen @@ -125,18 +123,13 @@ final class PlayInput: NSObject { } setup() - // fix beep sound - eliminateRedundantKeyPressEvents() + + // Fix beep sound + AKInterface.shared! + .eliminateRedundantKeyPressEvents({ self.dontIgnore() }) } - private func eliminateRedundantKeyPressEvents() { - // TODO later: should not be hard-coded - let NSEventMaskKeyDown: UInt64 = 1024 - Dynamic.NSEvent.addLocalMonitorForEventsMatchingMask( NSEventMaskKeyDown, handler: { event in - if (mode.visible && !EditorController.shared.editorMode) || PlayInput.cmdPressed() { - return event - } - return nil - } as ResponseBlock) + func dontIgnore() -> Bool { + (mode.visible && !EditorController.shared.editorMode) || PlayInput.cmdPressed() } } diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index fbfc9241..2664f5f8 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -4,26 +4,17 @@ // import Foundation - import GameController -import CoreGraphics - -typealias ResponseBlock = @convention(block) (_ event: Any) -> Any? - -typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool - -@objc final public class PlayMice: NSObject { - - @objc public static let shared = PlayMice() - private var camera: CameraControl? +public class PlayMice { + public static let shared = PlayMice() private static var isInit = false + private var camera: CameraControl? private var acceptMouseEvents = !PlaySettings.shared.mouseMapping - public override init() { - super.init() + public init() { if !PlayMice.isInit { setupMouseButton(_up: 2, _down: 4) setupMouseButton(_up: 8, _down: 16) @@ -40,26 +31,26 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool public var cursorPos: CGPoint { var point = CGPoint(x: 0, y: 0) if #available(macOS 11, *) { - point = Dynamic(screen.nsWindow).mouseLocationOutsideOfEventStream.asCGPoint! + point = AKInterface.shared!.mousePoint } - if let rect = (Dynamic(screen.nsWindow).frame.asCGRect) { - let viewRect: CGRect = screen.screenRect - let widthRate = viewRect.width / rect.width - var rate = viewRect.height / rect.height - if widthRate > rate { - // keep aspect ratio - rate = widthRate - } - // horizontally in center - point.x -= (rect.width - viewRect.width / rate)/2 - point.x *= rate - if screen.fullscreen { - // vertically in center - point.y -= (rect.height - viewRect.height / rate)/2 - } - point.y *= rate - point.y = viewRect.height - point.y + let rect = AKInterface.shared!.windowFrame + let viewRect: CGRect = screen.screenRect + let widthRate = viewRect.width / rect.width + var rate = viewRect.height / rect.height + if widthRate > rate { + // Keep aspect ratio + rate = widthRate + } + // Horizontally in center + point.x -= (rect.width - viewRect.width / rate)/2 + point.x *= rate + if screen.fullscreen { + // Vertically in center + point.y -= (rect.height - viewRect.height / rate)/2 } + point.y *= rate + point.y = viewRect.height - point.y + return point } @@ -110,57 +101,52 @@ typealias ResponseBlockBool = @convention(block) (_ event: Any) -> Bool var mouseActions: [Int: [ButtonAction]] = [2: [], 8: [], 33554432: []] private func setupMouseButton(_up: Int, _down: Int) { - // no this is not up, this is down. And the later down is up. - Dynamic.NSEvent.addLocalMonitorForEventsMatchingMask(_up, handler: { event in - if EditorController.shared.editorMode { - if _up == 8 { + AKInterface.shared!.setupMouseButton(_up, _down, dontIgnore(_:_:_:)) + } + + private func dontIgnore(_ actionIndex: Int, _ state: Bool, _ isEventWindow: Bool) -> Bool { + if EditorController.shared.editorMode { + if state { + if actionIndex == 8 { EditorController.shared.setKeyCode(-2) - } else if _up == 33554432 { + } else if actionIndex == 33554432 { EditorController.shared.setKeyCode(-3) } - return event + return true + } else { + return true } - if self.acceptMouseEvents { - let window = Dynamic(event, memberName: "window").asObject + } + if self.acceptMouseEvents { + if state { if !self.fakedMousePressed - // for traffic light buttons when not fullscreen + // For traffic light buttons when not fullscreen && self.cursorPos.y > 0 - // for traffic light buttons when fullscreen - && window == screen.nsWindow { - Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.began, tid: 1) + // For traffic light buttons when fullscreen + && isEventWindow { + Toucher.touchcam(point: self.cursorPos, + phase: UITouch.Phase.began, + tid: 1) self.fakedMousePressed = true - return nil + return false } - return event - } - if !mode.visible { - self.mouseActions[_up]!.forEach({ buttonAction in - buttonAction.update(pressed: true) - }) - return nil - } - return event - } as ResponseBlock) - Dynamic.NSEvent.addLocalMonitorForEventsMatchingMask(_down, handler: { event in - if EditorController.shared.editorMode { - return event - } - if self.acceptMouseEvents { + return true + } else { if self.fakedMousePressed { self.fakedMousePressed = false Toucher.touchcam(point: self.cursorPos, phase: UITouch.Phase.ended, tid: 1) - return nil + return false } - return event - } - if !mode.visible { - self.mouseActions[_up]!.forEach({ buttonAction in - buttonAction.update(pressed: false) - }) - return nil + return true } - return event - } as ResponseBlock) + } + if !mode.visible { + self.mouseActions[actionIndex]!.forEach({ buttonAction in + buttonAction.update(pressed: state) + }) + return false + } + return true } private func setMiceButton(_ keyId: Int, action: ButtonAction) { diff --git a/PlayTools/DiscordActivity/DiscordIPC.swift b/PlayTools/DiscordActivity/DiscordIPC.swift index 7cd63285..a50b43c8 100644 --- a/PlayTools/DiscordActivity/DiscordIPC.swift +++ b/PlayTools/DiscordActivity/DiscordIPC.swift @@ -11,7 +11,7 @@ import SwordRPC class DiscordIPC { public static let shared = DiscordIPC() - func initailize() { + func initialize() { if PlaySettings.shared.discordActivity.enable { let ipc: SwordRPC let custom = PlaySettings.shared.discordActivity diff --git a/PlayTools/Keymap/ControlModel.swift b/PlayTools/Keymap/ControlModel.swift index 1385242a..4a21569c 100644 --- a/PlayTools/Keymap/ControlModel.swift +++ b/PlayTools/Keymap/ControlModel.swift @@ -1,6 +1,6 @@ import GameController -@objc class ControlData: NSObject { +class ControlData { var keyCodes: [Int] var size: CGFloat var xCoord: CGFloat diff --git a/PlayTools/Keymap/EditorController.swift b/PlayTools/Keymap/EditorController.swift index 1933951c..19fd736a 100644 --- a/PlayTools/Keymap/EditorController.swift +++ b/PlayTools/Keymap/EditorController.swift @@ -9,7 +9,7 @@ class EditorViewController: UIViewController { } } -final class EditorController: NSObject { +class EditorController { static let shared = EditorController() @@ -145,7 +145,7 @@ final class EditorController: NSObject { view.subviews.forEach { $0.removeFromSuperview() } } - @objc public func addJoystick(_ center: CGPoint) { + public func addJoystick(_ center: CGPoint) { if editorMode { addControlToView(control: JoystickModel(data: ControlData(keyCodes: [GCKeyCode.keyW.rawValue, GCKeyCode.keyS.rawValue, @@ -157,7 +157,7 @@ final class EditorController: NSObject { } } - @objc public func addButton(_ toPoint: CGPoint) { + public func addButton(_ toPoint: CGPoint) { if editorMode { addControlToView(control: ButtonModel(data: ControlData(keyCodes: [-1], size: 5, @@ -167,7 +167,7 @@ final class EditorController: NSObject { } } - @objc public func addRMB(_ toPoint: CGPoint) { + public func addRMB(_ toPoint: CGPoint) { if editorMode { addControlToView(control: ButtonModel(data: ControlData(keyCodes: [-2], size: 5, @@ -177,7 +177,7 @@ final class EditorController: NSObject { } } - @objc public func addLMB(_ toPoint: CGPoint) { + public func addLMB(_ toPoint: CGPoint) { if editorMode { addControlToView(control: ButtonModel(data: ControlData(keyCodes: [-1], size: 5, @@ -187,7 +187,7 @@ final class EditorController: NSObject { } } - @objc public func addMMB(_ toPoint: CGPoint) { + public func addMMB(_ toPoint: CGPoint) { if editorMode { addControlToView(control: ButtonModel(data: ControlData(keyCodes: [-3], size: 5, @@ -197,7 +197,7 @@ final class EditorController: NSObject { } } - @objc public func addMouseArea(_ center: CGPoint) { + public func addMouseArea(_ center: CGPoint) { if editorMode { addControlToView(control: MouseAreaModel(data: ControlData(size: 25, xCoord: center.x.relativeX, @@ -205,7 +205,7 @@ final class EditorController: NSObject { } } - @objc public func addDraggableButton(_ center: CGPoint, _ keyCode: Int) { + public func addDraggableButton(_ center: CGPoint, _ keyCode: Int) { if editorMode { addControlToView(control: DraggableButtonModel(data: ControlData(keyCodes: [keyCode], size: 15, diff --git a/PlayTools/Keymap/Keymapping.swift b/PlayTools/Keymap/Keymapping.swift index 5aaa4486..24d8af68 100644 --- a/PlayTools/Keymap/Keymapping.swift +++ b/PlayTools/Keymap/Keymapping.swift @@ -6,7 +6,6 @@ // import Foundation -import os.log let keymap = Keymapping.shared @@ -31,7 +30,7 @@ class Keymapping { withIntermediateDirectories: true, attributes: [:]) } catch { - os_log("[PlayTools] Failed to create Keymapping directory.\n%@", type: .error, error as CVarArg) + print("[PlayTools] Failed to create Keymapping directory.\n%@") } } keymapUrl.appendPathComponent("\(bundleIdentifier).plist") @@ -48,7 +47,7 @@ class Keymapping { let data = try encoder.encode(keymapData) try data.write(to: keymapUrl) } catch { - os_log("[PlayTools] Keymapping encode failed.\n%@", type: .error, error as CVarArg) + print("[PlayTools] Keymapping encode failed.\n%@") } } @@ -59,7 +58,7 @@ class Keymapping { return true } catch { keymapData = KeymappingData(bundleIdentifier: bundleIdentifier) - os_log("[PlayTools] Keymapping decode failed.\n%@", type: .error, error as CVarArg) + print("[PlayTools] Keymapping decode failed.\n%@") return false } } diff --git a/PlayTools/PlayCover.swift b/PlayTools/PlayCover.swift index 4e0f4756..15092916 100644 --- a/PlayTools/PlayCover.swift +++ b/PlayTools/PlayCover.swift @@ -5,24 +5,21 @@ import Foundation import UIKit -import Security -import MetalKit -import WebKit final public class PlayCover: NSObject { @objc static let shared = PlayCover() var menuController: MenuController? - var firstTime = true private override init() {} @objc static public func launch() { quitWhenClose() + AKInterface.initialize() PlayInput.shared.initialize() - DiscordIPC.shared.initailize() + DiscordIPC.shared.initialize() } @objc static public func quitWhenClose() { @@ -32,7 +29,7 @@ final public class PlayCover: NSObject { queue: OperationQueue.main ) { noti in if PlayScreen.shared.nsWindow?.isEqual(noti.object) ?? false { - Dynamic.NSApplication.sharedApplication.terminate(self) + AKInterface.shared!.terminateApplication() } } } diff --git a/PlayTools/PlayScreen.swift b/PlayTools/PlayScreen.swift index 0187984a..4f124384 100644 --- a/PlayTools/PlayScreen.swift +++ b/PlayTools/PlayScreen.swift @@ -2,10 +2,9 @@ // ScreenController.swift // PlayTools // + import Foundation import UIKit -import SwiftUI -import AVFoundation let screen = PlayScreen.shared let mainScreenWidth = PlaySettings.shared.windowSizeWidth @@ -26,7 +25,6 @@ extension CGSize { } extension CGRect { - func aspectRatio() -> CGFloat { if mainScreenWidth > mainScreenHeight { return mainScreenWidth / mainScreenHeight @@ -45,27 +43,25 @@ extension CGRect { } extension UIScreen { - static var aspectRatio: CGFloat { - let count = Dynamic.NSScreen.screens.count.asInt ?? 0 + let count = AKInterface.shared!.screenCount if PlaySettings.shared.notch { if count == 1 { return mainScreenWidth / mainScreenHeight // 1.6 or 1.77777778 } else { - if Dynamic.NSScreen.mainScreen.asObject == Dynamic.NSScreen.screens.first { + if AKInterface.shared!.isMainScreenEqualToFirst { return mainScreenWidth / mainScreenHeight } } } - if let frame = Dynamic(Dynamic.NSScreen.mainScreen.asObject).frame.asCGRect { - return frame.aspectRatio() - } - return mainScreenWidth / mainScreenHeight + + let frame = AKInterface.shared!.mainScreenFrame + return frame.aspectRatio() } } -public final class PlayScreen: NSObject { +public class PlayScreen: NSObject { @objc public static let shared = PlayScreen() @objc public static func frame(_ rect: CGRect) -> CGRect { @@ -88,7 +84,7 @@ public final class PlayScreen: NSObject { return size.toAspectRatio() } var fullscreen: Bool { - return (Dynamic(nsWindow).styleMask.asInt ?? 0) & 16384 != 0 + return AKInterface.shared!.isFullscreen } @objc public var screenRect: CGRect { @@ -131,12 +127,8 @@ public final class PlayScreen: NSObject { window?.nsWindow } - var nsScreen: NSObject? { - Dynamic(nsWindow).nsScreen.asObject - } - func switchDock(_ visible: Bool) { - Dynamic.NSMenu.setMenuBarVisible(visible) + AKInterface.shared!.setMenuBarVisible(visible) } } @@ -162,19 +154,7 @@ extension CGFloat { } } -extension UIView { - - class func allSubviews(from parenView: UIView) -> [T] { - return parenView.subviews.flatMap { subView -> [T] in - var result = allSubviews(from: subView) as [T] - if let view = subView as? T { result.append(view) } - return result - } - } -} - extension UIWindow { - var nsWindow: NSObject? { guard let nsWindows = NSClassFromString("NSApplication")? .value(forKeyPath: "sharedApplication.windows") as? [AnyObject] else { return nil } @@ -187,22 +167,3 @@ extension UIWindow { return nil } } - -extension NSObject { - func call(_ method: String, object: CGSize) -> Bool { - if self.responds(to: Selector(method)) { - self.perform(Selector(method), with: object) - return true - } else { - return false - } - } - func call(_ method: String) -> Bool { - if self.responds(to: Selector(method)) { - self.perform(Selector(method)) - return true - } else { - return false - } - } -} diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index 6949eef2..1526a46f 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -1,6 +1,5 @@ import Foundation import UIKit -import os.log let settings = PlaySettings.shared @@ -20,7 +19,7 @@ let settings = PlaySettings.shared settingsData = try PropertyListDecoder().decode(AppSettingsData.self, from: data) } catch { settingsData = AppSettingsData() - os_log("[PlayTools] PlaySettings decode failed.\n%@", type: .error, error as CVarArg) + print("[PlayTools] PlaySettings decode failed.\n%@") } } diff --git a/PlayTools/PlayUI.swift b/PlayTools/PlayUI.swift index 52e60a00..702d1c88 100644 --- a/PlayTools/PlayUI.swift +++ b/PlayTools/PlayUI.swift @@ -6,14 +6,9 @@ import Foundation import UIKit -let shared = PlayUI.shared - -final class PlayUI { - +class PlayUI { static let shared = PlayUI() - private init() {} - func showAlert(_ title: String, _ content: String) { let alertController = UIAlertController(title: title, message: content, preferredStyle: .alert) PlayInput.shared.root?.present(alertController, animated: true, completion: nil) @@ -24,7 +19,7 @@ final class PlayUI { message: "Please, install it from playcover.io site to use this app.", preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "OK", style: .default) { _ in - Dynamic.NSApplication.sharedApplication.terminate(self) + AKInterface.shared!.terminateApplication() }) PlayInput.shared.root?.present(alertController, animated: true, completion: nil) } diff --git a/PlayTools/Utils/AKPluginLoader.swift b/PlayTools/Utils/AKPluginLoader.swift new file mode 100644 index 00000000..97700e46 --- /dev/null +++ b/PlayTools/Utils/AKPluginLoader.swift @@ -0,0 +1,32 @@ +// +// AKPluginLoader.swift +// PlayTools +// +// Created by Isaac Marovitz on 13/09/2022. +// + +import Foundation + +class AKInterface { + public static var shared: Plugin? + + public static func initialize() { + shared = loadPlugin() + } + + private static func loadPlugin() -> Plugin? { + // 1. Form the plugin's bundle URL + guard let bundleURL = Bundle.main.builtInPlugInsURL? + .appendingPathComponent("AKInterface") + .appendingPathExtension("bundle") else { return nil } + + // 2. Create a bundle instance with the plugin URL + guard let bundle = Bundle(url: bundleURL) else { return nil } + + // 3. Load the bundle and our plugin class + guard let pluginClass = bundle.principalClass as? Plugin.Type else { return nil } + + // 4. Create an instance of the plugin class + return pluginClass.init() + } +} diff --git a/PlayTools/Utils/Dynamic/Dynamic.swift b/PlayTools/Utils/Dynamic/Dynamic.swift deleted file mode 100644 index b14fa385..00000000 --- a/PlayTools/Utils/Dynamic/Dynamic.swift +++ /dev/null @@ -1,317 +0,0 @@ -// -// Dynamic -// Created by Mhd Hejazi on 4/15/20. -// Copyright © 2020 Samabox. All rights reserved. -// - -import Foundation - -public typealias ObjC = Dynamic - -@dynamicCallable -@dynamicMemberLookup -public class Dynamic: CustomDebugStringConvertible, Loggable { - public static var loggingEnabled: Bool = false { - didSet { - Invocation.loggingEnabled = loggingEnabled - } - } - var loggingEnabled: Bool { Self.loggingEnabled } - - public static let `nil` = Dynamic(nil) - - private let object: AnyObject? - private let memberName: String? - private var invocation: Invocation? - private var error: Error? - - public var isError: Bool { error != nil || object is Error } - public var debugDescription: String { object?.debugDescription ?? "" } - - public init(_ object: Any?, memberName: String? = nil) { - self.object = object as AnyObject? - self.memberName = memberName - - log(.end).log(.start) - log("# Dynamic") - log("Object:", object ?? "").log("Member:", memberName ?? "") - } - - public init(className: String) { - self.object = NSClassFromString(className) - self.memberName = nil - - log(.end).log(.start) - log("# Dynamic") - log("Class:", className) - } - - public static subscript(dynamicMember className: String) -> Dynamic { - Dynamic(className: className) - } - - public subscript(dynamicMember member: String) -> Dynamic { - get { - getProperty(member) - } - set { - self[dynamicMember: member] = newValue.resolve() - } - } - - public subscript(dynamicMember member: String) -> T? { - get { - self[dynamicMember: member].unwrap() - } - set { - setProperty(member, value: newValue) - } - } - - @discardableResult - public func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs) -> Dynamic { - // Constructors - if object is AnyClass, memberName == nil { - if pairs.isEmpty { - return self.`init`.dynamicallyCall(withKeywordArguments: pairs) - } else { - return self.`initWith`.dynamicallyCall(withKeywordArguments: pairs) - } - } - - guard let name = memberName else { return self } - - let selector = name + pairs.reduce("") { result, pair in - if result.isEmpty { - return (pair.key.first?.uppercased() ?? "") + pair.key.dropFirst() + ":" - } else { - return result + (pair.key + ":") - } - } - callMethod(selector, with: pairs.map { $0.value }) - return self - } - - @discardableResult - public func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs) -> T? { - let result: Dynamic = dynamicallyCall(withKeywordArguments: pairs) - return result.unwrap() - } - - private func getProperty(_ name: String) -> Dynamic { - log("Get:", "\(object?.debugDescription ?? "").\(name)") - - let resolved = resolve() - if resolved is Error { - return self - } - - if resolved == nil { - return Self.nil - } - - log(.end) - - return Dynamic(resolved, memberName: name) - } - - private func setProperty(_ name: String, value: T?) { - log("Set:", "\(object?.debugDescription ?? "").\(name)") - - let resolved = resolve() - log(.end) - - var setterName: String - if name.count > 2, name.hasPrefix("is"), name[name.index(name.startIndex, offsetBy: 2)].isUppercase { - setterName = "set" + name.dropFirst(2) - } else { - setterName = "set" + (name.first?.uppercased() ?? "") + name.dropFirst() - } - - Dynamic(resolved, memberName: setterName)(value) - } - - private func callMethod(_ selector: String, with arguments: [Any?] = []) { - guard var target = object as? NSObject, !isError else { return } - log("Call: [\(type(of: target)) \(selector)]") - - var invocation: Invocation - - // Call `alloc()` before `init()` - if target is AnyClass, selector.hasPrefix("init") { - guard let allocated = allocate(type: target) else { return } - target = allocated - } - - do { - invocation = try Invocation(target: target, selector: NSSelectorFromString(selector)) - } catch { - print("WARNING: Trying to access an unrecognized member: \(type(of: target)).\(selector)") - self.error = error - return - } - - self.invocation = invocation - - for index in 0.. NSObject? { - do { - let invocation = try Invocation(target: type, selector: NSSelectorFromString("alloc")) - invocation.invoke() - return invocation.returnedObject as? NSObject - } catch { - self.error = error - return nil - } - } - - private func resolve() -> AnyObject? { - // This is a class. Return it. - if object is AnyClass && memberName == nil { - return object - } - - guard let object = object else { - return nil - } - - // This is a method we have called before. Return the result. - if let result = invocation?.returnedObject { - return result - } - - // This is an error caused by a previous call. Just pass it. - if object is Error { - return object - } - if error != nil { - return error as AnyObject? - } - - // This is a wrapped object. Return it. - guard let name = memberName else { - return object - } - - // This is a wrapped object with a member name. Return the member. - if invocation?.isInvoked != true { - callMethod(name) - } - - return invocation?.returnedObject ?? error as AnyObject? - } - - @available(*, unavailable, message: "Call init() directly from the class name.") - public func alloc() {} -} - -extension Dynamic { - public var asAnyObject: AnyObject? { - let result = resolve() - log(.end) - return result - } - - public var asValue: NSValue? { - if let object = resolve() { - log(.end) - return NSValue(nonretainedObject: object) - } - - log(.end) - - guard let invocation = invocation, - let returnType = invocation.returnType, - invocation.returnsAny else { return nil } - - let buffer = UnsafeMutablePointer.allocate(capacity: invocation.returnLength) - defer { buffer.deallocate() } - buffer.initialize(repeating: 0, count: invocation.returnLength) - - invocation.getReturnValue(result: &buffer.pointee) - - let value = NSValue(bytes: buffer, objCType: UnsafePointer(returnType)) - return value - } - - public var asObject: NSObject? { asAnyObject as? NSObject } - public var asArray: NSArray? { asAnyObject as? NSArray } - public var asDictionary: NSDictionary? { asAnyObject as? NSDictionary } - public var asString: String? { asAnyObject?.description } - public var asInt8: Int8? { unwrap() } - public var asUInt8: UInt8? { unwrap() } - public var asInt16: Int16? { unwrap() } - public var asUInt16: UInt16? { unwrap() } - public var asInt32: Int32? { unwrap() } - public var asUInt32: UInt32? { unwrap() } - public var asInt64: Int64? { unwrap() } - public var asUInt64: UInt64? { unwrap() } - public var asFloat: Float? { unwrap() } - public var asDouble: Double? { unwrap() } - public var asBool: Bool? { unwrap() } - public var asInt: Int? { unwrap() } - public var asUInt: UInt? { unwrap() } - public var asSelector: Selector? { unwrap() } - - public func asInferred() -> T? { unwrap() } - - private func unwrap() -> T? { - guard let value = asValue else { return nil } - guard let invocation = invocation else { - if let result = object as? T { - return result - } - return nil - } - - let encoding = invocation.returnTypeString - if encoding == "^v" || encoding == "@" { - return value.nonretainedObjectValue as? T - } - - var storedSize = 0 - var storedAlignment = 0 - NSGetSizeAndAlignment(invocation.returnType!, &storedSize, &storedAlignment) - guard MemoryLayout.size == storedSize && MemoryLayout.alignment == storedAlignment else { - return nil - } - - let buffer = UnsafeMutablePointer.allocate(capacity: 1) - defer { buffer.deallocate() } - value.getValue(buffer) - - return buffer.pointee - } -} - -#if canImport(UIKit) -import UIKit - -extension Dynamic { - public var asCGPoint: CGPoint? { unwrap() } - public var asCGVector: CGVector? { unwrap() } - public var asCGFloat: CGFloat? { unwrap() } - public var asCGSize: CGSize? { unwrap() } - public var asCGRect: CGRect? { unwrap() } - public var asCGAffineTransform: CGAffineTransform? { unwrap() } - public var asUIEdgeInsets: UIEdgeInsets? { unwrap() } - public var asUIOffset: UIOffset? { unwrap() } - - #if !os(watchOS) - public var asCATransform3D: CATransform3D? { unwrap() } - #endif -} -#endif diff --git a/PlayTools/Utils/Dynamic/Invocation.swift b/PlayTools/Utils/Dynamic/Invocation.swift deleted file mode 100644 index a08a2f44..00000000 --- a/PlayTools/Utils/Dynamic/Invocation.swift +++ /dev/null @@ -1,238 +0,0 @@ -// -// Dynamic -// Created by Mhd Hejazi on 4/15/20. -// Copyright © 2020 Samabox. All rights reserved. -// - -import Foundation - -class Invocation: Loggable { - public static var loggingEnabled: Bool = false - var loggingEnabled: Bool { Self.loggingEnabled } - - private let target: NSObject - private let selector: Selector - - var invocation: NSObject? - - var numberOfArguments: Int = 0 - var returnLength: Int = 0 - var returnType: UnsafePointer? - var returnTypeString: String? { - guard let returnType = returnType else { return nil } - return String(cString: returnType) - } - var returnsObject: Bool { - /// `@` is the type encoding for an object - returnTypeString == "@" - } - var returnsAny: Bool { - /// `v` is the type encoding for Void - returnTypeString != "v" - } - lazy var returnedObject: AnyObject? = { - returnedObjectValue() - }() - private(set) var isInvoked: Bool = false - - init(target: NSObject, selector: Selector) throws { - self.target = target - self.selector = selector - - log(.start) - log("# Invocation") - log("[\(type(of: target)) \(selector)]") - log("Selector:", selector) - - try initialize() - } - - // swiftlint:disable function_body_length - private func initialize() throws { - /// `NSMethodSignature *methodSignature = [target methodSignatureForSelector: selector]` - let methodSignature: NSObject - do { - let selector = NSSelectorFromString("methodSignatureForSelector:") - let signature = (@convention(c)(NSObject, Selector, Selector) -> Any).self - let method = unsafeBitCast(target.method(for: selector), to: signature) - guard let result = method(target, selector, self.selector) as? NSObject else { - let error = InvocationError.unrecognizedSelector(type(of: target), self.selector) - log("ERROR:", error) - throw error - } - methodSignature = result - } - - /// `numberOfArguments = methodSignature.numberOfArguments` - self.numberOfArguments = methodSignature.value(forKeyPath: "numberOfArguments") as? Int ?? 0 - log("NumberOfArguments:", numberOfArguments) - - /// `methodReturnLength = methodSignature.methodReturnLength` - self.returnLength = methodSignature.value(forKeyPath: "methodReturnLength") as? Int ?? 0 - log("ReturnLength:", returnLength) - - /// `methodReturnType = methodSignature.methodReturnType` - let methodReturnType: UnsafePointer - do { - let selector = NSSelectorFromString("methodReturnType") - let signature = (@convention(c)(NSObject, Selector) -> UnsafePointer).self - let method = unsafeBitCast(methodSignature.method(for: selector), to: signature) - methodReturnType = method(methodSignature, selector) - } - self.returnType = methodReturnType - log("ReturnType:", self.returnTypeString ?? "?") - - /// `NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: methodSignature]` - let invocation: NSObject - do { - let NSInvocation = NSClassFromString("NSInvocation") as AnyObject - let selector = NSSelectorFromString("invocationWithMethodSignature:") - let signature = (@convention(c)(AnyObject, Selector, AnyObject) -> AnyObject).self - let method = unsafeBitCast(NSInvocation.method(for: selector), to: signature) - guard let result = method(NSInvocation, selector, methodSignature) as? NSObject else { - let error = InvocationError.unrecognizedSelector(type(of: target), self.selector) - log("ERROR:", error) - throw error - } - invocation = result - } - self.invocation = invocation - - /// `invocation.selector = selector` - do { - let selector = NSSelectorFromString("setSelector:") - let signature = (@convention(c)(NSObject, Selector, Selector) -> Void).self - let method = unsafeBitCast(invocation.method(for: selector), to: signature) - method(invocation, selector, self.selector) - } - - /// `[invocation retainArguments]` - do { - let selector = NSSelectorFromString("retainArguments") - let signature = (@convention(c)(NSObject, Selector) -> Void).self - let method = unsafeBitCast(invocation.method(for: selector), to: signature) - method(invocation, selector) - } - } - - func setArgument(_ argument: Any?, at index: NSInteger) { - guard let invocation = invocation else { return } - - log("Argument #\(index - 1):", argument ?? "") - - /// `[invocation setArgument:&argument atIndex:i + 2]` - let selector = NSSelectorFromString("setArgument:atIndex:") - let signature = (@convention(c)(NSObject, Selector, UnsafeRawPointer, Int) -> Void).self - let method = unsafeBitCast(invocation.method(for: selector), to: signature) - - if let valueArgument = argument as? NSValue { - /// Get the type byte size - let typeSize = UnsafeMutablePointer.allocate(capacity: 1) - defer { typeSize.deallocate() } - NSGetSizeAndAlignment(valueArgument.objCType, typeSize, nil) - - /// Get the actual value - let buffer = UnsafeMutablePointer.allocate(capacity: typeSize.pointee) - defer { buffer.deallocate() } - valueArgument.getValue(buffer) - - method(invocation, selector, buffer, index) - } else { - withUnsafePointer(to: argument) { pointer in - method(invocation, selector, pointer, index) - } - } - } - - func invoke() { - guard let invocation = invocation, !isInvoked else { return } - - log("Invoking...") - - isInvoked = true - - /// `[invocation invokeWithTarget: target]` - do { - let selector = NSSelectorFromString("invokeWithTarget:") - let signature = (@convention(c)(NSObject, Selector, AnyObject) -> Void).self - let method = unsafeBitCast(invocation.method(for: selector), to: signature) - method(invocation, selector, target) - } - - log(.end) - } - - func getReturnValue(result: inout T) { - guard let invocation = invocation else { return } - - /// `[invocation getReturnValue: returnValue]` - do { - let selector = NSSelectorFromString("getReturnValue:") - let signature = (@convention(c)(NSObject, Selector, UnsafeMutableRawPointer) -> Void).self - let method = unsafeBitCast(invocation.method(for: selector), to: signature) - withUnsafeMutablePointer(to: &result) { pointer in - method(invocation, selector, pointer) - } - } - - if NSStringFromSelector(self.selector) == "alloc" { - log("getReturnValue() -> ") - } else { - log("getReturnValue() ->", result) - } - } - - private func returnedObjectValue() -> AnyObject? { - guard returnsObject, returnLength > 0 else { - return nil - } - - var result: AnyObject? - - getReturnValue(result: &result) - - guard let object = result else { - return nil - } - - /// Take the ownership of the initialized objects to ensure they're deallocated properly. - if isRetainingMethod() { - return Unmanaged.passRetained(object).takeRetainedValue() - } - - /// `NSInvocation.getReturnValue()` doesn't give us the ownership of the returned object, but the compiler - /// tries to release this object anyway. So, we are retaining it to balance with the compiler's release. - return Unmanaged.passRetained(object).takeUnretainedValue() - } - - private func isRetainingMethod() -> Bool { - /// Refer to: https://bit.ly/308okXm - let selector = NSStringFromSelector(self.selector) - return selector == "alloc" || - selector.hasPrefix("new") || - selector.hasPrefix("copy") || - selector.hasPrefix("mutableCopy") - } -} - -public enum InvocationError: CustomNSError { - case unrecognizedSelector(_ classType: AnyClass, _ selector: Selector) - - public static var errorDomain: String { String(describing: Invocation.self) } - - public var errorCode: Int { - switch self { - case .unrecognizedSelector: - return 404 - } - } - - public var errorUserInfo: [String: Any] { - var message: String - switch self { - case .unrecognizedSelector(let classType, let selector): - message = "'\(String(describing: classType))' doesn't recognize selector '\(selector)'" - } - return [NSLocalizedDescriptionKey: message] - } -} diff --git a/PlayTools/Utils/Dynamic/Logger.swift b/PlayTools/Utils/Dynamic/Logger.swift deleted file mode 100644 index 52077cc6..00000000 --- a/PlayTools/Utils/Dynamic/Logger.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// Dynamic -// Created by Mhd Hejazi on 4/15/20. -// Copyright © 2020 Samabox. All rights reserved. -// - -import Foundation - -protocol Loggable: AnyObject { - var loggingEnabled: Bool { get } -} - -extension Loggable { - var loggingEnabled: Bool { false } - var logUsingPrint: Bool { true } - - @discardableResult - func log(_ items: Any...) -> Logger { - guard loggingEnabled else { return Logger.dummy } - return Logger.logger(for: self).log(items) - } - - @discardableResult - func log(_ group: Logger.Group) -> Logger { - guard loggingEnabled else { return Logger.dummy } - return Logger.logger(for: self).log(group) - } -} - -class Logger { - enum Group { - case start, end - } - - static let dummy = DummyLogger() - static var enabled = true - - private static var loggers: [ObjectIdentifier: Logger] = [:] - private static var level: Int = 0 - - static func logger(for object: AnyObject) -> Logger { - let id = ObjectIdentifier(object) - if let logger = Self.loggers[id] { - return logger - } - - let logger = Logger() - Self.loggers[id] = logger - - return logger - } - - @discardableResult - func log(_ items: Any..., withBullet: Bool = true) -> Logger { - log(items, withBullet: withBullet) - } - - @discardableResult - func log(_ items: [Any], withBullet: Bool = true) -> Logger { - guard Self.enabled else { return self } - - let message = items.lazy.map { String(describing: $0) }.joined(separator: " ") - var indent = String(repeating: " ╷ ", count: Self.level) - if !indent.isEmpty, withBullet { - indent = indent.dropLast(2) + "‣ " - } - print(indent + message) - return self - } - - @discardableResult - func log(_ group: Group) -> Logger { - switch group { - case .start: logGroupStart() - case .end: logGroupEnd() - } - return self - } - - private func logGroupStart() { - guard Self.enabled else { return } - - log([" ╭╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴"], withBullet: false) - Self.level += 1 - } - - private func logGroupEnd() { - guard Self.enabled else { return } - - guard Self.level > 0 else { return } - Self.level -= 1 - log([" ╰╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴"], withBullet: false) - } -} - -class DummyLogger: Logger { - @discardableResult - override func log(_ items: [Any], withBullet: Bool = true) -> Logger { - self - } - - @discardableResult - override func log(_ group: Group) -> Logger { - self - } -} diff --git a/PlayTools/Utils/Dynamic/TypeMapping.swift b/PlayTools/Utils/Dynamic/TypeMapping.swift deleted file mode 100644 index 5cab4528..00000000 --- a/PlayTools/Utils/Dynamic/TypeMapping.swift +++ /dev/null @@ -1,125 +0,0 @@ -// -// Dynamic -// Created by Mhd Hejazi on 4/18/20. -// Copyright © 2020 Samabox. All rights reserved. -// - -// swiftlint:disable cyclomatic_complexity syntactic_sugar - -import Foundation - -/// The type mapping table can be found here: -/// https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/working_with_foundation_types -class TypeMapping { - private static let typePairs: [(swiftType: Any.Type, objCType: AnyObject.Type)] = [ - (Array.self, NSArray.self), - (Calendar.self, NSCalendar.self), - (CharacterSet.self, NSCharacterSet.self), - (Data.self, NSData.self), - (DateComponents.self, NSDateComponents.self), - (DateInterval.self, NSDateInterval.self), - (Date.self, NSDate.self), - (Decimal.self, NSDecimalNumber.self), - (Dictionary.self, NSDictionary.self), - (IndexPath.self, NSIndexPath.self), - (IndexSet.self, NSIndexSet.self), - (Locale.self, NSLocale.self), - (Notification.self, NSNotification.self), - (PersonNameComponents.self, NSPersonNameComponents.self), - (Set.self, NSSet.self), - (String.self, NSString.self), - (TimeZone.self, NSTimeZone.self), - (URL.self, NSURL.self), - (URLComponents.self, NSURLComponents.self), - (URLQueryItem.self, NSURLQueryItem.self), - (URLRequest.self, NSURLRequest.self), - (UUID.self, NSUUID.self) - ] - - private static let swiftToObjCTypes: [ObjectIdentifier: AnyObject.Type] = { - let pairs = typePairs.map { - (ObjectIdentifier($0.swiftType), $0.objCType) - } - return [ObjectIdentifier: AnyObject.Type](uniqueKeysWithValues: pairs) - }() - - private static let objCToSwiftTypes: [ObjectIdentifier: Any.Type] = { - let pairs = typePairs.map { - (ObjectIdentifier($0.objCType), $0.swiftType) - } - return [ObjectIdentifier: Any.Type](uniqueKeysWithValues: pairs) - }() - - static func swiftType(for type: Any.Type) -> Any.Type? { - objCToSwiftTypes[ObjectIdentifier(type)] - } - - static func objCType(for type: Any.Type) -> Any.Type? { - swiftToObjCTypes[ObjectIdentifier(type)] - } - - static func mappedType(for type: Any.Type) -> Any.Type? { - swiftType(for: type) ?? objCType(for: type) - } - - static func convertToObjCType(_ object: Any?) -> Any? { - switch object { - case is Array: return object as? NSArray - case is Calendar: return object as? NSCalendar - case is CharacterSet: return object as? NSCharacterSet - case is Data: return object as? NSData - case is DateComponents: return object as? NSDateComponents - case is DateInterval: return object as? NSDateInterval - case is Date: return object as? NSDate - case is Decimal: return object as? NSDecimalNumber - case is Dictionary: return object as? NSDictionary - case is IndexPath: return object as? NSIndexPath - case is IndexSet: return object as? NSIndexSet - case is Locale: return object as? NSLocale - case is Notification: return object as? NSNotification - case is PersonNameComponents: return object as? NSPersonNameComponents - case is Set: return object as? NSSet - case is String: return object as? NSString - case is TimeZone: return object as? NSTimeZone - case is URL: return object as? NSURL - case is URLComponents: return object as? NSURLComponents - case is URLQueryItem: return object as? NSURLQueryItem - case is URLRequest: return object as? NSURLRequest - case is UUID: return object as? NSUUID - default: return nil - } - } - - static func convertToSwiftType(_ object: Any?) -> Any? { - switch object { - case is NSArray: return object as? Array - case is NSCalendar: return object as? Calendar - case is NSCharacterSet: return object as? CharacterSet - case is NSData: return object as? Data - case is NSDateComponents: return object as? DateComponents - case is NSDateInterval: return object as? DateInterval - case is NSDate: return object as? Date - case is NSDecimalNumber: return object as? Decimal - case is NSDictionary: return object as? Dictionary - case is NSIndexPath: return object as? IndexPath - case is NSIndexSet: return object as? IndexSet - case is NSLocale: return object as? Locale - case is NSMeasurement: return object as? Measurement - case is NSNotification: return object as? Notification - case is NSPersonNameComponents: return object as? PersonNameComponents - case is NSSet: return object as? Set - case is NSString: return object as? String - case is NSTimeZone: return object as? TimeZone - case is NSURL: return object as? URL - case is NSURLComponents: return object as? URLComponents - case is NSURLQueryItem: return object as? URLQueryItem - case is NSURLRequest: return object as? URLRequest - case is NSUUID: return object as? UUID - default: return nil - } - } - - static func convertType(of object: Any?) -> Any? { - convertToObjCType(object) ?? convertToSwiftType(object) - } -} diff --git a/PlayTools/Utils/PlayInfo.swift b/PlayTools/Utils/PlayInfo.swift index 30cd9b64..830dbbb9 100644 --- a/PlayTools/Utils/PlayInfo.swift +++ b/PlayTools/Utils/PlayInfo.swift @@ -4,12 +4,11 @@ // import Foundation -import GameController class PlayInfo { static var isLauncherInstalled: Bool { - return Dynamic.NSWorkspace.sharedWorkspace - .URLForApplicationWithBundleIdentifier("io.playcover.PlayCover").asAnyObject != nil + return AKInterface.shared! + .urlForApplicationWithBundleIdentifier("io.playcover.PlayCover") != nil } } diff --git a/Plugin.swift b/Plugin.swift new file mode 100644 index 00000000..07962480 --- /dev/null +++ b/Plugin.swift @@ -0,0 +1,28 @@ +// +// Plugin.swift +// PlayTools +// +// Created by Isaac Marovitz on 13/09/2022. +// + +import Foundation + +@objc(Plugin) +public protocol Plugin: NSObjectProtocol { + init() + + var screenCount: Int { get } + var mousePoint: CGPoint { get } + var windowFrame: CGRect { get } + var mainScreenFrame: CGRect { get } + var isMainScreenEqualToFirst: Bool { get } + var isFullscreen: Bool { get } + + func hideCursor() + func unhideCursor() + func terminateApplication() + func eliminateRedundantKeyPressEvents(_ dontIgnore: @escaping() -> Bool) + func setupMouseButton(_ _up: Int, _ _down: Int, _ dontIgnore: @escaping(Int, Bool, Bool) -> Bool) + func urlForApplicationWithBundleIdentifier(_ value: String) -> URL? + func setMenuBarVisible(_ value: Bool) +} From b64762709a68f372bf70ab422f73f896fbaed023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Moreno?= <47700212+JoseMoreville@users.noreply.github.com> Date: Sat, 24 Sep 2022 22:49:10 -0600 Subject: [PATCH 25/27] =?UTF-8?q?Feat:=20=E2=9C=A8=20Added=20iPhone=2014?= =?UTF-8?q?=20Pro=20Max=20and=20iPhone=2013=20Pro=20Max?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PlayTools/PlaySettings.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index 1526a46f..609b8465 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -49,6 +49,10 @@ let settings = PlaySettings.shared return "J320xAP" case "iPad13,8": return "J522AP" + case "iPhone15,3": + return "A2896" + case "iPhone14,3": + return "A2645" default: return "J320xAP" } From a5f9dbc021330674817ed6ba21087e2f55f36657 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Wed, 28 Sep 2022 10:14:03 +0800 Subject: [PATCH 26/27] clean up aiming optimisation --- PlayTools/Controls/PlayMice.swift | 42 ++++++------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 2664f5f8..288a4a38 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -178,40 +178,22 @@ final class CameraControl { Toucher.touchQueue.asyncAfter(deadline: when, execute: closure) } - // if max speed of this touch is high - var movingFast = false // like sequence but resets when touch begins. Used to calc touch duration var counter = 0 // if should wait before beginning next touch var cooldown = false - // if the touch point had been prevented from lifting off because of moving slow - var idled = false // in how many tests has this been identified as stationary var stationaryCount = 0 let stationaryThreshold = 2 @objc func checkEnded() { // if been stationary for enough time - if self.stationaryCount < self.stationaryThreshold { + if self.stationaryCount < self.stationaryThreshold || (self.stationaryCount < 20 - self.counter) { self.stationaryCount += 1 self.delay(0.1, closure: checkEnded) return } - // and slow touch lasts for sufficient time - if self.movingFast || self.counter > 64 { - self.doLiftOff() - } else { - self.idled = true - // idle for at most 4 seconds - self.delay(4) { - if self.stationaryCount < self.stationaryThreshold { - self.stationaryCount += 1 - self.delay(0.1, closure: self.checkEnded) - return - } - self.doLiftOff() - } - } + self.doLiftOff() } @objc func updated(_ deltaX: CGFloat, _ deltaY: CGFloat) { @@ -222,8 +204,6 @@ final class CameraControl { counter += 1 if !isMoving { isMoving = true - movingFast = false - idled = false location = center counter = 0 stationaryCount = 0 @@ -231,21 +211,12 @@ final class CameraControl { delay(0.1, closure: checkEnded) } - // if not moving fast, regard the user fine-tuning the camera(e.g. aiming) - // so hold the touch for longer to avoid cold startup - if deltaX.magnitude + deltaY.magnitude > 12 { - // if we had mistaken this as player aiming - if self.idled { -// Toast.showOver(msg: "idled") - // since not aiming, re-touch to re-gain control - self.doLiftOff() - return - } - movingFast = true - } self.location.x += deltaX * CGFloat(PlaySettings.shared.sensitivity) self.location.y -= deltaY * CGFloat(PlaySettings.shared.sensitivity) Toucher.touchcam(point: self.location, phase: UITouch.Phase.moved, tid: 1) + if stationaryCount >= self.stationaryThreshold { + self.counter = 0 + } stationaryCount = 0 } @@ -254,6 +225,9 @@ final class CameraControl { return } Toucher.touchcam(point: self.location, phase: UITouch.Phase.ended, tid: 1) +// DispatchQueue.main.async { +// Toast.showOver(msg: "mouse released") +// } self.isMoving = false // ending and beginning too frequently leads to the beginning event not recognized // so let the beginning event wait some time From 95072f4c22682c1d1c98474ad9f4ca0da2d12e81 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Sun, 2 Oct 2022 23:27:07 +0800 Subject: [PATCH 27/27] increase lift off frequency --- PlayTools/Controls/PlayMice.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/PlayTools/Controls/PlayMice.swift b/PlayTools/Controls/PlayMice.swift index 288a4a38..fc5d5c71 100644 --- a/PlayTools/Controls/PlayMice.swift +++ b/PlayTools/Controls/PlayMice.swift @@ -190,7 +190,7 @@ final class CameraControl { // if been stationary for enough time if self.stationaryCount < self.stationaryThreshold || (self.stationaryCount < 20 - self.counter) { self.stationaryCount += 1 - self.delay(0.1, closure: checkEnded) + self.delay(0.04, closure: checkEnded) return } self.doLiftOff() @@ -209,12 +209,16 @@ final class CameraControl { stationaryCount = 0 Toucher.touchcam(point: self.center, phase: UITouch.Phase.began, tid: 1) - delay(0.1, closure: checkEnded) + delay(0.01, closure: checkEnded) + } + if self.counter == 120 { + self.doLiftOff() + return } self.location.x += deltaX * CGFloat(PlaySettings.shared.sensitivity) self.location.y -= deltaY * CGFloat(PlaySettings.shared.sensitivity) Toucher.touchcam(point: self.location, phase: UITouch.Phase.moved, tid: 1) - if stationaryCount >= self.stationaryThreshold { + if stationaryCount > self.stationaryThreshold { self.counter = 0 } stationaryCount = 0