Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions KeyType.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand All @@ -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 */; };
Expand Down Expand Up @@ -49,7 +50,7 @@
870105931DE9646100486507 /* KeyEventController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyEventController.swift; sourceTree = "<group>"; };
870105951DE968CF00486507 /* Remap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Remap.swift; sourceTree = "<group>"; };
870105971DE979DC00486507 /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = "<group>"; };
870105991DE97ADB00486507 /* EventConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventConverter.swift; sourceTree = "<group>"; };
870105991DE97ADB00486507 /* RemapFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemapFinder.swift; sourceTree = "<group>"; };
8701059F1DEA07E700486507 /* KeyCombination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyCombination.swift; sourceTree = "<group>"; };
870105A11DEA08AA00486507 /* CGEventExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGEventExtension.swift; sourceTree = "<group>"; };
8768503024F198E20021D517 /* Custom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Custom.swift; sourceTree = "<group>"; };
Expand All @@ -58,6 +59,7 @@
87DADDD724F1866100D79B36 /* KanaEisu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KanaEisu.swift; sourceTree = "<group>"; };
87DADDD924F1874600D79B36 /* LRDvorak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LRDvorak.swift; sourceTree = "<group>"; };
87DADDDB24F189A100D79B36 /* Emacs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emacs.swift; sourceTree = "<group>"; };
87DADDDD24F3D1C600D79B36 /* EventConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventConverter.swift; sourceTree = "<group>"; };
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 = "<group>"; };
87EC00B41DE88D3D007C1F66 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -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 = "<group>";
Expand Down Expand Up @@ -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 */,
Expand Down
68 changes: 42 additions & 26 deletions KeyType/EventConverter.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
53 changes: 3 additions & 50 deletions KeyType/KeyEventWatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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] = [
Expand Down Expand Up @@ -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<CGEvent>? {
func callback(proxy _: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {
guard let observer = refcon else { return Unmanaged.passRetained(event) }
let mySelf = Unmanaged<KeyEventWatcher>.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(
Expand All @@ -64,49 +62,4 @@ class KeyEventWatcher {

return tap
}

private func eventCallback(proxy _: CGEventTapProxy, type: CGEventType, event: CGEvent) -> Unmanaged<CGEvent>? {
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<CGEvent>? {
keyCode = nil
let event = config.getConvertedEvent(event) ?? event
return Unmanaged.passRetained(event)
}

private func keyUp(_ event: CGEvent) -> Unmanaged<CGEvent>? {
keyCode = nil
let event = config.getConvertedEvent(event) ?? event
return Unmanaged.passRetained(event)
}

private func modifierKeyDown(_ event: CGEvent) -> Unmanaged<CGEvent>? {
keyCode = event.keyCode
return Unmanaged.passRetained(event)
}

private func modifierKeyUp(_ event: CGEvent) -> Unmanaged<CGEvent>? {
if keyCode == event.keyCode {
if let convertedEvent = config.getConvertedEvent(event) {
KeyCombination(fromEvent: convertedEvent).postEvent()
}
}
keyCode = nil
return Unmanaged.passRetained(event)
}
}
48 changes: 48 additions & 0 deletions KeyType/RemapFinder.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
28 changes: 14 additions & 14 deletions KeyTypeTests/EventConverterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,25 @@ 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() {
XCTAssertTrue(
convert(
[Remap(Key.A.without.option, to: Key.B.alone)],
key: Key.A
)?.keyCode == Key.B.rawValue
).keyCode == Key.B.rawValue
)
}

Expand All @@ -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`
Expand All @@ -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,
Expand All @@ -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)
}
}