diff --git a/KeyType.xcodeproj/project.pbxproj b/KeyType.xcodeproj/project.pbxproj index 7f1af40..caffaa7 100644 --- a/KeyType.xcodeproj/project.pbxproj +++ b/KeyType.xcodeproj/project.pbxproj @@ -10,7 +10,7 @@ 870105941DE9646100486507 /* KeyEventController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870105931DE9646100486507 /* KeyEventController.swift */; }; 870105961DE968CF00486507 /* Remap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870105951DE968CF00486507 /* Remap.swift */; }; 870105981DE979DC00486507 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870105971DE979DC00486507 /* Key.swift */; }; - 8701059A1DE97ADB00486507 /* EventConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870105991DE97ADB00486507 /* EventConverter.swift */; }; + 8701059A1DE97ADB00486507 /* RemapFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870105991DE97ADB00486507 /* RemapFinder.swift */; }; 870105A01DEA07E700486507 /* KeyCombination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8701059F1DEA07E700486507 /* KeyCombination.swift */; }; 870105A21DEA08AA00486507 /* CGEventExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870105A11DEA08AA00486507 /* CGEventExtension.swift */; }; 8768503124F198E20021D517 /* Custom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8768503024F198E20021D517 /* Custom.swift */; }; @@ -19,6 +19,7 @@ 87DADDD824F1866100D79B36 /* KanaEisu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87DADDD724F1866100D79B36 /* KanaEisu.swift */; }; 87DADDDA24F1874600D79B36 /* LRDvorak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87DADDD924F1874600D79B36 /* LRDvorak.swift */; }; 87DADDDC24F189A100D79B36 /* Emacs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87DADDDB24F189A100D79B36 /* Emacs.swift */; }; + 87DADDDE24F3D1C600D79B36 /* EventConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87DADDDD24F3D1C600D79B36 /* EventConverter.swift */; }; 87EC00B31DE88D3D007C1F66 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87EC00B21DE88D3D007C1F66 /* AppDelegate.swift */; }; 87EC00B51DE88D3D007C1F66 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87EC00B41DE88D3D007C1F66 /* ViewController.swift */; }; 87EC00B71DE88D3D007C1F66 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 87EC00B61DE88D3D007C1F66 /* Assets.xcassets */; }; @@ -49,7 +50,7 @@ 870105931DE9646100486507 /* KeyEventController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyEventController.swift; sourceTree = ""; }; 870105951DE968CF00486507 /* Remap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Remap.swift; sourceTree = ""; }; 870105971DE979DC00486507 /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = ""; }; - 870105991DE97ADB00486507 /* EventConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventConverter.swift; sourceTree = ""; }; + 870105991DE97ADB00486507 /* RemapFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemapFinder.swift; sourceTree = ""; }; 8701059F1DEA07E700486507 /* KeyCombination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyCombination.swift; sourceTree = ""; }; 870105A11DEA08AA00486507 /* CGEventExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGEventExtension.swift; sourceTree = ""; }; 8768503024F198E20021D517 /* Custom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Custom.swift; sourceTree = ""; }; @@ -58,6 +59,7 @@ 87DADDD724F1866100D79B36 /* KanaEisu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KanaEisu.swift; sourceTree = ""; }; 87DADDD924F1874600D79B36 /* LRDvorak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LRDvorak.swift; sourceTree = ""; }; 87DADDDB24F189A100D79B36 /* Emacs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emacs.swift; sourceTree = ""; }; + 87DADDDD24F3D1C600D79B36 /* EventConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventConverter.swift; sourceTree = ""; }; 87EC00AF1DE88D3D007C1F66 /* KeyType.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KeyType.app; sourceTree = BUILT_PRODUCTS_DIR; }; 87EC00B21DE88D3D007C1F66 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 87EC00B41DE88D3D007C1F66 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -149,11 +151,12 @@ 87EC00B41DE88D3D007C1F66 /* ViewController.swift */, 87EC00DD1DE89151007C1F66 /* KeyEventWatcher.swift */, 870105931DE9646100486507 /* KeyEventController.swift */, - 870105991DE97ADB00486507 /* EventConverter.swift */, + 870105991DE97ADB00486507 /* RemapFinder.swift */, 870105A11DEA08AA00486507 /* CGEventExtension.swift */, 87EC00B61DE88D3D007C1F66 /* Assets.xcassets */, 87EC00B81DE88D3D007C1F66 /* Main.storyboard */, 87EC00BB1DE88D3D007C1F66 /* Info.plist */, + 87DADDDD24F3D1C600D79B36 /* EventConverter.swift */, ); path = KeyType; sourceTree = ""; @@ -336,7 +339,8 @@ files = ( 8768503124F198E20021D517 /* Custom.swift in Sources */, 87EC00B51DE88D3D007C1F66 /* ViewController.swift in Sources */, - 8701059A1DE97ADB00486507 /* EventConverter.swift in Sources */, + 8701059A1DE97ADB00486507 /* RemapFinder.swift in Sources */, + 87DADDDE24F3D1C600D79B36 /* EventConverter.swift in Sources */, 87DADDDA24F1874600D79B36 /* LRDvorak.swift in Sources */, 870105961DE968CF00486507 /* Remap.swift in Sources */, 87EC00B31DE88D3D007C1F66 /* AppDelegate.swift in Sources */, diff --git a/KeyType/EventConverter.swift b/KeyType/EventConverter.swift index bf631b8..a9491d9 100644 --- a/KeyType/EventConverter.swift +++ b/KeyType/EventConverter.swift @@ -1,47 +1,63 @@ // -// ModifiedProgrammerDvorak.swift +// EventConverter.swift // KeyType // -// MIT License -// Copyright (c) 2016 potsbo +// Created by Shimpei Otsubo on 2020/08/24. +// Copyright © 2020 Shimpei Otsubo. All rights reserved. // import Foundation class EventConverter { - private var remapMap: [CGKeyCode: [Remap]] + private let finder: RemapFinder + private var keyCode: CGKeyCode? - init(_ keyMappingList: KeyMapCollection) { - var remapList: [CGKeyCode: [Remap]] = [:] - - for val in keyMappingList { - let key = val.input.keyCode + init(finder: RemapFinder) { + self.finder = finder + } - if remapList[key] == nil { - remapList[key] = [] + func convert(type: CGEventType, event: CGEvent) -> CGEvent { + switch type { + case .flagsChanged: + if event.isModiferKeyEvent() { + return event.isModiferKeyDownEvent() ? modifierKeyDown(event) : modifierKeyUp(event) + } else { + return event } - - remapList[key]?.append(val) + case .keyDown: + return keyDown(event) + case .keyUp: + return keyUp(event) + default: + keyCode = nil + return event } + } - remapMap = remapList + private func keyDown(_ event: CGEvent) -> CGEvent { + keyCode = nil + let event = finder.find(event) ?? event + return event } - func getConvertedEvent(_ event: CGEvent) -> CGEvent? { - let eventKeyCombination = KeyCombination(fromEvent: event) + private func keyUp(_ event: CGEvent) -> CGEvent { + keyCode = nil + let event = finder.find(event) ?? event + return event + } - guard let candidateRemaps = remapMap[eventKeyCombination.keyCode] else { - return nil - } + private func modifierKeyDown(_ event: CGEvent) -> CGEvent { + keyCode = event.keyCode + return event + } - for remap in candidateRemaps { - if eventKeyCombination.isCompatible(with: remap) { - event.keyCode = remap.outputKeyCode - event.flags = remap.renderEventFlag(for: event) - return event + private func modifierKeyUp(_ event: CGEvent) -> CGEvent { + if keyCode == event.keyCode { + if let convertedEvent = finder.find(event) { + KeyCombination(fromEvent: convertedEvent).postEvent() } } - - return nil + keyCode = nil + return event } } diff --git a/KeyType/KeyEventWatcher.swift b/KeyType/KeyEventWatcher.swift index 726815e..67d22c9 100644 --- a/KeyType/KeyEventWatcher.swift +++ b/KeyType/KeyEventWatcher.swift @@ -9,9 +9,7 @@ import Cocoa class KeyEventWatcher { - private var keyCode: CGKeyCode? - private let bundleId = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String - private let config = EventConverter(LRDvorak + KanaEisu + Emacs + Custom) + private let config = EventConverter(finder: RemapFinder(LRDvorak + KanaEisu + Emacs + Custom)) private var eventMaskToWatch: CGEventMask { let eventTypeList: [CGEventType] = [ @@ -47,10 +45,10 @@ class KeyEventWatcher { private func eventTap() -> CFMachPort? { let observer = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque()) - func callback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged? { + func callback(proxy _: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged? { guard let observer = refcon else { return Unmanaged.passRetained(event) } let mySelf = Unmanaged.fromOpaque(observer).takeUnretainedValue() - return mySelf.eventCallback(proxy: proxy, type: type, event: event) + return Unmanaged.passRetained(mySelf.config.convert(type: type, event: event)) } let tap = CGEvent.tapCreate( @@ -64,49 +62,4 @@ class KeyEventWatcher { return tap } - - private func eventCallback(proxy _: CGEventTapProxy, type: CGEventType, event: CGEvent) -> Unmanaged? { - switch type { - case .flagsChanged: - if event.isModiferKeyEvent() { - return event.isModiferKeyDownEvent() ? modifierKeyDown(event) : modifierKeyUp(event) - } else { - return Unmanaged.passRetained(event) - } - case .keyDown: - return keyDown(event) - case .keyUp: - return keyUp(event) - default: - keyCode = nil - return Unmanaged.passRetained(event) - } - } - - private func keyDown(_ event: CGEvent) -> Unmanaged? { - keyCode = nil - let event = config.getConvertedEvent(event) ?? event - return Unmanaged.passRetained(event) - } - - private func keyUp(_ event: CGEvent) -> Unmanaged? { - keyCode = nil - let event = config.getConvertedEvent(event) ?? event - return Unmanaged.passRetained(event) - } - - private func modifierKeyDown(_ event: CGEvent) -> Unmanaged? { - keyCode = event.keyCode - return Unmanaged.passRetained(event) - } - - private func modifierKeyUp(_ event: CGEvent) -> Unmanaged? { - if keyCode == event.keyCode { - if let convertedEvent = config.getConvertedEvent(event) { - KeyCombination(fromEvent: convertedEvent).postEvent() - } - } - keyCode = nil - return Unmanaged.passRetained(event) - } } diff --git a/KeyType/RemapFinder.swift b/KeyType/RemapFinder.swift new file mode 100644 index 0000000..80ca416 --- /dev/null +++ b/KeyType/RemapFinder.swift @@ -0,0 +1,48 @@ +// +// ModifiedProgrammerDvorak.swift +// KeyType +// +// MIT License +// Copyright (c) 2016 potsbo +// + +import Foundation + +class RemapFinder { + private var remapMap: [CGKeyCode: [Remap]] + + init(_ keyMappingList: KeyMapCollection) { + var remapList: [CGKeyCode: [Remap]] = [:] + + for val in keyMappingList { + let key = val.input.keyCode + + if remapList[key] == nil { + remapList[key] = [] + } + + remapList[key]?.append(val) + } + + remapMap = remapList + } + + // TODO: return remap instead of CGEvent + func find(_ event: CGEvent) -> CGEvent? { + let eventKeyCombination = KeyCombination(fromEvent: event) + + guard let candidateRemaps = remapMap[eventKeyCombination.keyCode] else { + return nil + } + + for remap in candidateRemaps { + if eventKeyCombination.isCompatible(with: remap) { + event.keyCode = remap.outputKeyCode + event.flags = remap.renderEventFlag(for: event) + return event + } + } + + return nil + } +} diff --git a/KeyTypeTests/EventConverterTests.swift b/KeyTypeTests/EventConverterTests.swift index 17659dc..9594f4c 100644 --- a/KeyTypeTests/EventConverterTests.swift +++ b/KeyTypeTests/EventConverterTests.swift @@ -11,18 +11,17 @@ import XCTest class EventConverterTests: XCTestCase { func testLeftCommandToEisu() { - XCTAssert(convert(KanaEisu, key: Key.commandL)?.keyCode == Key.EISU.rawValue) + XCTAssert(convert(KanaEisu, key: Key.commandL).keyCode == Key.EISU.rawValue) } func testRightCommandToKana() { - XCTAssert(convert(KanaEisu, key: Key.commandR)?.keyCode == Key.KANA.rawValue) - XCTAssertNil(convert(KanaEisu, key: Key.A)) + XCTAssert(convert(KanaEisu, key: Key.commandR).keyCode == Key.KANA.rawValue) + XCTAssert(convert(KanaEisu, key: Key.A).keyCode == Key.A.rawValue) } func testNoTouch() { - XCTAssertNil(convert(KanaEisu, key: Key.A)) - XCTAssertNil(convert(KanaEisu, key: Key.A, flags: .maskCommand)) - XCTAssertNil(convert(KanaEisu, key: Key.commandR, flags: .maskCommand)) + XCTAssertTrue(convert(KanaEisu, key: Key.A, flags: .maskCommand).keyCode == Key.A.rawValue) + XCTAssertTrue(convert(KanaEisu, key: Key.commandR, flags: .maskCommand).keyCode == Key.commandR.rawValue) } func testWithoutSimple() { @@ -30,7 +29,7 @@ class EventConverterTests: XCTestCase { convert( [Remap(Key.A.without.option, to: Key.B.alone)], key: Key.A - )?.keyCode == Key.B.rawValue + ).keyCode == Key.B.rawValue ) } @@ -41,7 +40,7 @@ class EventConverterTests: XCTestCase { [Remap(Key.A.without.option, to: Key.B.alone)], key: Key.A, flags: CGEventFlags.maskShift - )?.keyCode == Key.B.rawValue + ).keyCode == Key.B.rawValue ) // with `shift` and `command` @@ -50,21 +49,22 @@ class EventConverterTests: XCTestCase { [Remap(Key.A.without.option, to: Key.B.alone)], key: Key.A, flags: CGEventFlags.maskShift.union(.maskCommand) - )?.keyCode == Key.B.rawValue + ).keyCode == Key.B.rawValue ) // key with `option` can never trigger the Remap - XCTAssertNil( + XCTAssertTrue( convert( [Remap(Key.A.with.shift.without.option, to: Key.B.alone)], key: Key.A, flags: CGEventFlags.maskShift.union(.maskAlternate) - ) + ).keyCode == Key.A.rawValue ) } - private func convert(_ collection: KeyMapCollection, key: Key, flags: CGEventFlags? = nil) -> CGEvent? { - let converter = EventConverter(collection) + private func convert(_ collection: KeyMapCollection, key: Key, flags: CGEventFlags? = nil) -> CGEvent { + let finder = RemapFinder(collection) + let converter = EventConverter(finder: finder) let event = CGEvent( keyboardEventSource: nil, virtualKey: key.rawValue, @@ -73,6 +73,6 @@ class EventConverterTests: XCTestCase { if let flags = flags { event.flags.insert(flags) } - return converter.getConvertedEvent(event) + return converter.convert(type: .keyDown, event: event) } }