diff --git a/Apps/Keyty/Sources/Keyty/Domain/Keyboard/KeyboardKeyCode.swift b/Apps/Keyty/Sources/Keyty/Domain/Keyboard/KeyboardKeyCode.swift index 51a5bd4..a2afeaa 100644 --- a/Apps/Keyty/Sources/Keyty/Domain/Keyboard/KeyboardKeyCode.swift +++ b/Apps/Keyty/Sources/Keyty/Domain/Keyboard/KeyboardKeyCode.swift @@ -17,8 +17,12 @@ enum KeyboardKeyCode: UInt16 { // MARK: - Letters & punctuation case a = 0x00 - case k = 0x28 + case e = 0x0E + case digit7 = 0x1A case minus = 0x1B + case u = 0x20 + case k = 0x28 + case grave = 0x32 // MARK: - Whitespace & editing diff --git a/Apps/Keyty/Sources/Keyty/Platform/Capture/EventTap/EventTap.swift b/Apps/Keyty/Sources/Keyty/Platform/Capture/EventTap/EventTap.swift index 68056ad..a38cbe5 100644 --- a/Apps/Keyty/Sources/Keyty/Platform/Capture/EventTap/EventTap.swift +++ b/Apps/Keyty/Sources/Keyty/Platform/Capture/EventTap/EventTap.swift @@ -108,7 +108,7 @@ final class EventTap { } fileprivate func handleFlagsChanged(_ cgEvent: CGEvent) { - let flags = NSEvent.ModifierFlags(rawValue: UInt(cgEvent.flags.rawValue)) + let flags = NSEvent.ModifierFlags(cgEventFlags: cgEvent.flags) self.delegate?.eventTap(self, noteFlagsChanged: flags) } diff --git a/Apps/Keyty/Sources/Keyty/Support/Extensions/AppKit/NSEventModifierFlags+RawMasks.swift b/Apps/Keyty/Sources/Keyty/Support/Extensions/AppKit/NSEventModifierFlags+RawMasks.swift new file mode 100644 index 0000000..e49ede4 --- /dev/null +++ b/Apps/Keyty/Sources/Keyty/Support/Extensions/AppKit/NSEventModifierFlags+RawMasks.swift @@ -0,0 +1,23 @@ +// +// NSEventModifierFlags+RawMasks.swift +// Keyty +// +// SPDX-FileCopyrightText: 2026 Serhii Bykov +// SPDX-License-Identifier: BSD-3-Clause +// + +import AppKit + +extension NSEvent.ModifierFlags { + init(cgEventFlags: CGEventFlags) { + self.init(rawValue: UInt(cgEventFlags.rawValue)) + } + + func addingRawMasks(_ masks: UInt...) -> Self { + self.addingRawMasks(masks) + } + + func addingRawMasks(_ masks: [UInt]) -> Self { + Self(rawValue: masks.reduce(self.rawValue) { $0 | $1 }) + } +} diff --git a/Apps/Keyty/Tests/KeytyTests/Domain/Events/MouseEventTests.swift b/Apps/Keyty/Tests/KeytyTests/Domain/Events/MouseEventTests.swift index a3b3c1a..c7c65f7 100644 --- a/Apps/Keyty/Tests/KeytyTests/Domain/Events/MouseEventTests.swift +++ b/Apps/Keyty/Tests/KeytyTests/Domain/Events/MouseEventTests.swift @@ -11,10 +11,10 @@ import XCTest final class MouseEventTests: XCTestCase { func testKindClassifiesButtonsAndScrollDirections() { - XCTAssertEqual(makeMouseEvent(type: .leftMouseDown, buttonNumber: 0).kind, .leftButton) - XCTAssertEqual(makeMouseEvent(type: .rightMouseDown, buttonNumber: 1).kind, .rightButton) - XCTAssertEqual(makeMouseEvent(type: .otherMouseDown, buttonNumber: 2).kind, .middleButton) - XCTAssertEqual(makeMouseEvent(type: .otherMouseDown, buttonNumber: 3).kind, .otherButton(4)) + XCTAssertEqual(TestMouseEvents.make(type: .leftMouseDown, buttonNumber: 0).kind, .leftButton) + XCTAssertEqual(TestMouseEvents.make(type: .rightMouseDown, buttonNumber: 1).kind, .rightButton) + XCTAssertEqual(TestMouseEvents.make(type: .otherMouseDown, buttonNumber: 2).kind, .middleButton) + XCTAssertEqual(TestMouseEvents.make(type: .otherMouseDown, buttonNumber: 3).kind, .otherButton(4)) XCTAssertEqual(makeScrollEvent(deltaX: 0, deltaY: 1).kind, .wheelUp) XCTAssertEqual(makeScrollEvent(deltaX: 0, deltaY: -1).kind, .wheelDown) XCTAssertEqual(makeScrollEvent(deltaX: -1, deltaY: 0).kind, .wheelLeft) @@ -68,19 +68,6 @@ final class MouseEventTests: XCTestCase { XCTAssertEqual(location.y, 980) } - private func makeMouseEvent(type: NSEvent.EventType, buttonNumber: Int) -> MouseEvent { - let cgType: CGEventType = { - switch type { - case .leftMouseDown: return .leftMouseDown - case .rightMouseDown: return .rightMouseDown - default: return .otherMouseDown - } - }() - let cgEvent = CGEvent(mouseEventSource: nil, mouseType: cgType, mouseCursorPosition: .zero, mouseButton: .left)! - cgEvent.setIntegerValueField(.mouseEventButtonNumber, value: Int64(buttonNumber)) - return MouseEvent(nsEvent: NSEvent(cgEvent: cgEvent)!) - } - private func makeScrollEvent(deltaX: CGFloat, deltaY: CGFloat) -> MouseEvent { let cgEvent = CGEvent(scrollWheelEvent2Source: nil, units: .pixel, wheelCount: 2, wheel1: Int32(deltaY), wheel2: Int32(deltaX), wheel3: 0)! return MouseEvent(nsEvent: NSEvent(cgEvent: cgEvent)!) diff --git a/Apps/Keyty/Tests/KeytyTests/Features/Visualizers/Keyboard/Keycaps/KeyboardVisualizerSpecialKeyFilteringTests.swift b/Apps/Keyty/Tests/KeytyTests/Features/Visualizers/Keyboard/Keycaps/KeyboardVisualizerSpecialKeyFilteringTests.swift index 920a064..0de10f6 100644 --- a/Apps/Keyty/Tests/KeytyTests/Features/Visualizers/Keyboard/Keycaps/KeyboardVisualizerSpecialKeyFilteringTests.swift +++ b/Apps/Keyty/Tests/KeytyTests/Features/Visualizers/Keyboard/Keycaps/KeyboardVisualizerSpecialKeyFilteringTests.swift @@ -33,8 +33,8 @@ final class KeyboardVisualizerSpecialKeyFilteringTests: XCTestCase { } func testResolverClassifiesInsertFunctionKeyAsSpecial() { - let ch = Self.appKitFunctionKey(NSInsertFunctionKey) - let event = Self.makeKeystroke( + let ch = TestKeyboardCharacters.functionKeyCharacter(NSInsertFunctionKey) + let event = TestKeystrokes.make( keyCode: KeyboardKeyCode.help.rawValue, characters: ch, charactersIgnoringModifiers: ch @@ -44,7 +44,7 @@ final class KeyboardVisualizerSpecialKeyFilteringTests: XCTestCase { } func testResolverClassifiesPrintableLetterAsNonSpecial() { - let event = Self.makeKeystroke( + let event = TestKeystrokes.make( keyCode: KeyboardKeyCode.a.rawValue, characters: "a", charactersIgnoringModifiers: "a" @@ -53,27 +53,4 @@ final class KeyboardVisualizerSpecialKeyFilteringTests: XCTestCase { XCTAssertFalse(KeyboardSpecialKeyResolver.isSpecial(event)) } - private static func makeKeystroke( - keyCode: UInt16, - characters: String, - charactersIgnoringModifiers: String - ) -> StandardKeyEvent { - let event = NSEvent.keyEvent( - with: .keyDown, - location: .zero, - modifierFlags: [], - timestamp: NSDate.timeIntervalSinceReferenceDate, - windowNumber: 0, - context: nil, - characters: characters, - charactersIgnoringModifiers: charactersIgnoringModifiers, - isARepeat: false, - keyCode: keyCode - )! - return StandardKeyEvent(nsEvent: event) - } - - private static func appKitFunctionKey(_ key: Int) -> String { - String(UnicodeScalar(key)!) - } } diff --git a/Apps/Keyty/Tests/KeytyTests/Features/Visualizers/Keyboard/Keycaps/KeycapItemFactoryTests.swift b/Apps/Keyty/Tests/KeytyTests/Features/Visualizers/Keyboard/Keycaps/KeycapItemFactoryTests.swift index 89ac9dd..f1b1e42 100644 --- a/Apps/Keyty/Tests/KeytyTests/Features/Visualizers/Keyboard/Keycaps/KeycapItemFactoryTests.swift +++ b/Apps/Keyty/Tests/KeytyTests/Features/Visualizers/Keyboard/Keycaps/KeycapItemFactoryTests.swift @@ -12,9 +12,8 @@ import XCTest final class KeycapItemFactoryTests: XCTestCase { func testKeyboardModifierKeysDecodeLocationSpecificFlags() { - let flags = NSEvent.ModifierFlags( - .command, - deviceMasks: UInt(NX_DEVICELCMDKEYMASK), + let flags = NSEvent.ModifierFlags.command.addingRawMasks( + UInt(NX_DEVICELCMDKEYMASK), UInt(NX_DEVICERCMDKEYMASK) ) @@ -37,9 +36,8 @@ final class KeycapItemFactoryTests: XCTestCase { func testModifierItemsUseLocationSpecificModifierKeys() { let items = KeycapItemFactory.modifierItems( - currentFlags: NSEvent.ModifierFlags( - .command, - deviceMasks: UInt(NX_DEVICELCMDKEYMASK), + currentFlags: NSEvent.ModifierFlags.command.addingRawMasks( + UInt(NX_DEVICELCMDKEYMASK), UInt(NX_DEVICERCMDKEYMASK) ), releasedFlags: [], @@ -55,9 +53,8 @@ final class KeycapItemFactoryTests: XCTestCase { func testModifierItemsUseInwardAlignmentForNonCommandModifierKeys() { let items = KeycapItemFactory.modifierItems( - currentFlags: NSEvent.ModifierFlags( - .shift, - deviceMasks: UInt(NX_DEVICELSHIFTKEYMASK), + currentFlags: NSEvent.ModifierFlags.shift.addingRawMasks( + UInt(NX_DEVICELSHIFTKEYMASK), UInt(NX_DEVICERSHIFTKEYMASK) ), releasedFlags: [], @@ -137,11 +134,11 @@ final class KeycapItemFactoryTests: XCTestCase { let palette = Self.makePalette() let downItem = KeycapItemFactory.mouseItem( - for: Self.makeMouseEvent(type: .leftMouseDown), + for: TestMouseEvents.make(type: .leftMouseDown), palette: palette ) let upItem = KeycapItemFactory.mouseItem( - for: Self.makeMouseEvent(type: .leftMouseUp), + for: TestMouseEvents.make(type: .leftMouseUp), palette: palette ) @@ -167,9 +164,4 @@ final class KeycapItemFactoryTests: XCTestCase { ) } - private static func makeMouseEvent(type: NSEvent.EventType) -> MouseEvent { - let cgType: CGEventType = type == .leftMouseDown ? .leftMouseDown : .leftMouseUp - let cgEvent = CGEvent(mouseEventSource: nil, mouseType: cgType, mouseCursorPosition: .zero, mouseButton: .left)! - return MouseEvent(nsEvent: NSEvent(cgEvent: cgEvent)!) - } } diff --git a/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventProcessorTests.swift b/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventProcessorTests.swift index fb8c74c..e2f4de8 100644 --- a/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventProcessorTests.swift +++ b/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventProcessorTests.swift @@ -15,7 +15,7 @@ final class EventProcessorTests: XCTestCase { var items: [DisplayItem] = [] processor.onItemProduced = { items.append($0) } - processor.noteMouseEvent(Self.makeMouseEvent(type: .leftMouseUp, buttonNumber: 0, modifiers: [.command])) + processor.noteMouseEvent(TestMouseEvents.make(type: .leftMouseUp, buttonNumber: 0, modifiers: [.command])) XCTAssertEqual(items.count, 2) XCTAssertEqual(items[0].kind, .content) @@ -28,7 +28,7 @@ final class EventProcessorTests: XCTestCase { var items: [DisplayItem] = [] processor.onItemProduced = { items.append($0) } - processor.noteMouseEvent(Self.makeMouseEvent(type: .leftMouseUp, buttonNumber: 0, modifiers: [])) + processor.noteMouseEvent(TestMouseEvents.make(type: .leftMouseUp, buttonNumber: 0, modifiers: [])) XCTAssertEqual(items.count, 2) XCTAssertEqual(items[0].kind, .content) @@ -36,29 +36,4 @@ final class EventProcessorTests: XCTestCase { XCTAssertEqual(items[1].kind, .groupBreak) } - private static func makeMouseEvent( - type: NSEvent.EventType, - buttonNumber: Int, - modifiers: NSEvent.ModifierFlags - ) -> MouseEvent { - let cgType: CGEventType = { - switch type { - case .leftMouseDown, .leftMouseUp: return type == .leftMouseDown ? .leftMouseDown : .leftMouseUp - case .rightMouseDown, .rightMouseUp: return type == .rightMouseDown ? .rightMouseDown : .rightMouseUp - default: return type == .otherMouseDown ? .otherMouseDown : .otherMouseUp - } - }() - - var cgFlags: CGEventFlags = [] - if modifiers.contains(.shift) { cgFlags.insert(.maskShift) } - if modifiers.contains(.command) { cgFlags.insert(.maskCommand) } - if modifiers.contains(.control) { cgFlags.insert(.maskControl) } - if modifiers.contains(.option) { cgFlags.insert(.maskAlternate) } - if modifiers.contains(.function) { cgFlags.insert(.maskSecondaryFn) } - - let cgEvent = CGEvent(mouseEventSource: nil, mouseType: cgType, mouseCursorPosition: .zero, mouseButton: .left)! - cgEvent.setIntegerValueField(.mouseEventButtonNumber, value: Int64(buttonNumber)) - cgEvent.flags = cgFlags - return MouseEvent(nsEvent: NSEvent(cgEvent: cgEvent)!) - } } diff --git a/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventTransformerKeystrokeTests.swift b/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventTransformerKeystrokeTests.swift index 86b809f..40902a0 100644 --- a/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventTransformerKeystrokeTests.swift +++ b/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventTransformerKeystrokeTests.swift @@ -16,277 +16,262 @@ import XCTest final class EventTransformerKeystrokeTests: XCTestCase { var keystroke: StandardKeyEvent! var keyboardLayout: TISInputSource! - var keyboardLayouts: [TISInputSource] = [] func transform(_ event: StandardKeyEvent) -> String { - EventTransformer(keyboardLayout: keyboardLayout).transform(.keystroke(event)) + EventTransformer(keyboardLayout: self.keyboardLayout).transform(.keystroke(event)) } - func usEnglishKeyboardLayout() -> TISInputSource { - let properties: [String: Any] = [ - kTISPropertyInputSourceID as String: "com.apple.keylayout.US", - kTISPropertyInputSourceType as String: kTISTypeKeyboardLayout as String - ] - keyboardLayouts = TISCreateInputSourceList(properties as CFDictionary, true)! - .takeRetainedValue() as! [TISInputSource] - XCTAssertGreaterThan(keyboardLayouts.count, 0) - return keyboardLayouts[0] - } - - func makeKeystroke(keyCode: UInt16, modifiers: NSEvent.ModifierFlags, - characters: String, charactersIgnoringModifiers: String) -> StandardKeyEvent { - let event = NSEvent.keyEvent( - with: .keyDown, - location: .zero, - modifierFlags: modifiers, - timestamp: NSDate.timeIntervalSinceReferenceDate, - windowNumber: 0, - context: nil, - characters: characters, - charactersIgnoringModifiers: charactersIgnoringModifiers, - isARepeat: false, - keyCode: keyCode - )! - return StandardKeyEvent(nsEvent: event) - } - - override func setUp() { - super.setUp() - keyboardLayout = usEnglishKeyboardLayout() + override func setUpWithError() throws { + try super.setUpWithError() + self.keyboardLayout = try TestKeyboardLayouts.requireUSEnglish() } +} - // MARK: - Numbers +// MARK: - Numbers +extension EventTransformerKeystrokeTests { func test_KCKeystroke_convertsCtrlNumberToNumber() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 262401), characters: "7", charactersIgnoringModifiers: "7") - XCTAssertEqual(transform(keystroke), "⌃7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.control, characters: "7", charactersIgnoringModifiers: "7") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.control + "7") } func test_KCKeystroke_convertsShiftNumberToShiftNumber() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 131330), characters: "&", charactersIgnoringModifiers: "&") - XCTAssertEqual(transform(keystroke), "⇧7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.shift, characters: "&", charactersIgnoringModifiers: "&") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.shift + "7") } func test_KCKeystroke_convertsCtrlShiftNumberToNumber() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 393475), characters: "7", charactersIgnoringModifiers: "&") - XCTAssertEqual(transform(keystroke), "⌃⇧7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.controlShift, characters: "7", charactersIgnoringModifiers: "&") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.control + KeyboardGlyphCatalog.shift + "7") } func test_KCKeystroke_convertsCmdNumberToNumber() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 1048840), characters: "7", charactersIgnoringModifiers: "7") - XCTAssertEqual(transform(keystroke), "⌘7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.command, characters: "7", charactersIgnoringModifiers: "7") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.command + "7") } func test_KCKeystroke_convertsCmdShiftNumberToNumber() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 1179914), characters: "7", charactersIgnoringModifiers: "&") - XCTAssertEqual(transform(keystroke), "⇧⌘7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.commandShift, characters: "7", charactersIgnoringModifiers: "&") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.shift + KeyboardGlyphCatalog.command + "7") } func test_KCKeystroke_convertsCmdOptNumberToNumber() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 1573160), characters: "¶", charactersIgnoringModifiers: "7") - XCTAssertEqual(transform(keystroke), "⌥⌘7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.commandOption, characters: "¶", charactersIgnoringModifiers: "7") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.option + KeyboardGlyphCatalog.command + "7") } func test_KCKeystroke_convertsShiftOptionNumberToNumber() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 655650), characters: "»", charactersIgnoringModifiers: "7") - XCTAssertEqual(transform(keystroke), "⌥⇧7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.optionShift, characters: "»", charactersIgnoringModifiers: "7") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.option + KeyboardGlyphCatalog.shift + "7") } func test_KCKeystroke_convertsCmdOptShiftNumberToShiftedNumber() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 1704234), characters: "‡", charactersIgnoringModifiers: "&") - XCTAssertEqual(transform(keystroke), "⌥⇧⌘7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.commandOptionShift, characters: "‡", charactersIgnoringModifiers: "&") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.option + KeyboardGlyphCatalog.shift + KeyboardGlyphCatalog.command + "7") } +} - // MARK: - Letters +// MARK: - Letters +extension EventTransformerKeystrokeTests { func test_KCKeystroke_convertsCtrlLetterToUppercaseLetter() { - keystroke = makeKeystroke(keyCode: 0, modifiers: NSEvent.ModifierFlags(rawValue: 262401), characters: "^A", charactersIgnoringModifiers: "a") - XCTAssertEqual(transform(keystroke), "⌃A") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.a.rawValue, modifiers: TestModifierFlags.control, characters: "^A", charactersIgnoringModifiers: "a") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.control + "A") } func test_KCKeystroke_convertsCtrlShiftLetterToLetter() { - keystroke = makeKeystroke(keyCode: 0, modifiers: NSEvent.ModifierFlags(rawValue: 393475), characters: "^A", charactersIgnoringModifiers: "a") - XCTAssertEqual(transform(keystroke), "⌃⇧A") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.a.rawValue, modifiers: TestModifierFlags.controlShift, characters: "^A", charactersIgnoringModifiers: "a") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.control + KeyboardGlyphCatalog.shift + "A") } func test_KCKeystroke_convertsCtrlShiftCmdLetterToLetter() { - keystroke = makeKeystroke(keyCode: 0, modifiers: NSEvent.ModifierFlags(rawValue: 1442059), characters: "^A", charactersIgnoringModifiers: "A") - XCTAssertEqual(transform(keystroke), "⌃⇧⌘A") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.a.rawValue, modifiers: TestModifierFlags.controlCommandShift, characters: "^A", charactersIgnoringModifiers: "A") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.control + KeyboardGlyphCatalog.shift + KeyboardGlyphCatalog.command + "A") } func test_KCKeystroke_convertsCtrlOptLetterToUppercaseLetter() { - keystroke = makeKeystroke(keyCode: 0, modifiers: NSEvent.ModifierFlags(rawValue: 786721), characters: "^A", charactersIgnoringModifiers: "a") - XCTAssertEqual(transform(keystroke), "⌃⌥A") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.a.rawValue, modifiers: TestModifierFlags.controlOption, characters: "^A", charactersIgnoringModifiers: "a") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.control + KeyboardGlyphCatalog.option + "A") } func test_KCKeystroke_convertsCtrlOptShiftLetterToLetter() { - keystroke = makeKeystroke(keyCode: 0, modifiers: NSEvent.ModifierFlags(rawValue: 917795), characters: "^A", charactersIgnoringModifiers: "A") - XCTAssertEqual(transform(keystroke), "⌃⌥⇧A") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.a.rawValue, modifiers: TestModifierFlags.controlOptionShift, characters: "^A", charactersIgnoringModifiers: "A") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.control + KeyboardGlyphCatalog.option + KeyboardGlyphCatalog.shift + "A") } func test_KCKeystroke_displaysOptLetterByDefault() { - keystroke = makeKeystroke(keyCode: 32, modifiers: NSEvent.ModifierFlags(rawValue: 524576), characters: "", charactersIgnoringModifiers: "u") - XCTAssertEqual(transform(keystroke), "⌥U") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.u.rawValue, modifiers: TestModifierFlags.option, characters: "", charactersIgnoringModifiers: "u") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.option + "U") } +} - // MARK: - Function Row - +// MARK: - Function Row +extension EventTransformerKeystrokeTests { func test_KCKeystroke_convertsFnF1ToBrightnessDecrease() { - keystroke = makeKeystroke(keyCode: 145, modifiers: NSEvent.ModifierFlags(rawValue: 8388864), characters: "", charactersIgnoringModifiers: "") - XCTAssertEqual(transform(keystroke), "🔅") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.brightnessDown.rawValue, modifiers: TestModifierFlags.function, characters: "", charactersIgnoringModifiers: "") + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.brightnessDown.string) } func test_KCKeystroke_convertsFnF2ToBrightnessIncrease() { - keystroke = makeKeystroke(keyCode: 144, modifiers: NSEvent.ModifierFlags(rawValue: 8388864), characters: "", charactersIgnoringModifiers: "") - XCTAssertEqual(transform(keystroke), "🔆") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.brightnessUp.rawValue, modifiers: TestModifierFlags.function, characters: "", charactersIgnoringModifiers: "") + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.brightnessUp.string) } func test_KCKeystroke_convertsFnF3ToMissionControl() { - keystroke = makeKeystroke(keyCode: 160, modifiers: NSEvent.ModifierFlags(rawValue: 8388864), characters: "", charactersIgnoringModifiers: "") - XCTAssertEqual(transform(keystroke), "🖥") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.missionControl.rawValue, modifiers: TestModifierFlags.function, characters: "", charactersIgnoringModifiers: "") + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.missionControl.string) } func test_KCKeystroke_convertsFnF4ToLauncher() { - keystroke = makeKeystroke(keyCode: 131, modifiers: NSEvent.ModifierFlags(rawValue: 8388864), characters: "", charactersIgnoringModifiers: "") - XCTAssertEqual(transform(keystroke), "🚀") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.launchpad.rawValue, modifiers: TestModifierFlags.function, characters: "", charactersIgnoringModifiers: "") + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.launchpad.string) } +} - // MARK: - JIS layout - +// MARK: - JIS layout +extension EventTransformerKeystrokeTests { func test_KCKeystroke_convertsEisuKey() { - keystroke = makeKeystroke(keyCode: 102, modifiers: [], characters: "", charactersIgnoringModifiers: "") - XCTAssertEqual(transform(keystroke), "英数") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.eisu.rawValue, modifiers: [], characters: "", charactersIgnoringModifiers: "") + XCTAssertEqual(self.transform(self.keystroke), KeyboardSpecialKey.eisu.displayText) } func test_KCKeystroke_convertsKanaKey() { - keystroke = makeKeystroke(keyCode: 104, modifiers: [], characters: "", charactersIgnoringModifiers: "") - XCTAssertEqual(transform(keystroke), "かな") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.kana.rawValue, modifiers: [], characters: "", charactersIgnoringModifiers: "") + XCTAssertEqual(self.transform(self.keystroke), KeyboardSpecialKey.kana.displayText) } +} - // MARK: - Option-modified characters +// MARK: - Option-modified characters +extension EventTransformerKeystrokeTests { func test_optionShiftNumberDisplaysExplicitModifiers() { - keystroke = makeKeystroke(keyCode: 26, modifiers: NSEvent.ModifierFlags(rawValue: 655650), characters: "»", charactersIgnoringModifiers: "7") - XCTAssertEqual(transform(keystroke), "⌥⇧7") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.digit7.rawValue, modifiers: TestModifierFlags.optionShift, characters: "»", charactersIgnoringModifiers: "7") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.option + KeyboardGlyphCatalog.shift + "7") } +} - // MARK: - Special Cases +// MARK: - Special Cases +extension EventTransformerKeystrokeTests { func test_tabKey() { - keystroke = makeKeystroke(keyCode: 48, modifiers: NSEvent.ModifierFlags(rawValue: 256), characters: "\t", charactersIgnoringModifiers: "\t") - XCTAssertEqual(transform(keystroke), "⇥") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.tab.rawValue, modifiers: TestModifierFlags.none, characters: "\t", charactersIgnoringModifiers: "\t") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.tab) } func test_returnAndKeypadEnterUseDifferentSymbols() { - keystroke = makeKeystroke( + self.keystroke = TestKeystrokes.make( keyCode: KeyboardKeyCode.returnKey.rawValue, - modifiers: NSEvent.ModifierFlags(rawValue: 256), + modifiers: TestModifierFlags.none, characters: "\r", charactersIgnoringModifiers: "\r" ) - XCTAssertEqual(transform(keystroke), UnicodeToken.returnKey.string) + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.returnKey.string) - keystroke = makeKeystroke( + self.keystroke = TestKeystrokes.make( keyCode: KeyboardKeyCode.keypadEnter.rawValue, - modifiers: NSEvent.ModifierFlags(rawValue: 256), + modifiers: TestModifierFlags.none, characters: "\r", charactersIgnoringModifiers: "\r" ) - XCTAssertEqual(transform(keystroke), UnicodeToken.keypadEnter.string) + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.keypadEnter.string) } func test_deleteAndForwardDeleteUseDifferentSymbols() { - keystroke = makeKeystroke( + self.keystroke = TestKeystrokes.make( keyCode: KeyboardKeyCode.delete.rawValue, - modifiers: NSEvent.ModifierFlags(rawValue: 256), + modifiers: TestModifierFlags.none, characters: UnicodeToken.delete.string, charactersIgnoringModifiers: UnicodeToken.delete.string ) - XCTAssertEqual(transform(keystroke), UnicodeToken.delete.string) + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.delete.string) - keystroke = makeKeystroke( + self.keystroke = TestKeystrokes.make( keyCode: KeyboardKeyCode.forwardDelete.rawValue, - modifiers: NSEvent.ModifierFlags(rawValue: 256), + modifiers: TestModifierFlags.none, characters: UnicodeToken.forwardDelete.string, charactersIgnoringModifiers: UnicodeToken.forwardDelete.string ) - XCTAssertEqual(transform(keystroke), UnicodeToken.forwardDelete.string) + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.forwardDelete.string) } func test_shiftTab() { - let ch = String(UnicodeScalar(0x19)!) - keystroke = makeKeystroke(keyCode: 48, modifiers: NSEvent.ModifierFlags(rawValue: 131330), characters: ch, charactersIgnoringModifiers: ch) - XCTAssertEqual(transform(keystroke), "⇤") + let ch = TestKeyboardCharacters.backTab + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.tab.rawValue, modifiers: TestModifierFlags.shift, characters: ch, charactersIgnoringModifiers: ch) + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.backTab) } func test_arrowKeysUseFilledTriangleSymbols() { let cases: [(KeyboardKeyCode, String)] = [ - (.leftArrow, "◀"), - (.upArrow, "▲"), - (.downArrow, "▼"), - (.rightArrow, "▶") + (.leftArrow, UnicodeToken.leftArrow.string), + (.upArrow, UnicodeToken.upArrow.string), + (.downArrow, UnicodeToken.downArrow.string), + (.rightArrow, UnicodeToken.rightArrow.string) ] for (keyCode, expected) in cases { - keystroke = makeKeystroke( + self.keystroke = TestKeystrokes.make( keyCode: keyCode.rawValue, - modifiers: NSEvent.ModifierFlags(rawValue: 256), + modifiers: TestModifierFlags.none, characters: expected, charactersIgnoringModifiers: expected ) - XCTAssertEqual(transform(keystroke), expected) + XCTAssertEqual(self.transform(self.keystroke), expected) } } func test_insertFunctionKeyDisplaysInsertForHelpKeyCode() { - let ch = Self.appKitFunctionKey(NSInsertFunctionKey) - keystroke = makeKeystroke(keyCode: KeyboardKeyCode.help.rawValue, modifiers: [], characters: ch, charactersIgnoringModifiers: ch) - XCTAssertEqual(transform(keystroke), "ins") + let ch = TestKeyboardCharacters.functionKeyCharacter(NSInsertFunctionKey) + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.help.rawValue, modifiers: [], characters: ch, charactersIgnoringModifiers: ch) + XCTAssertEqual(self.transform(self.keystroke), KeyboardSpecialKey.insert.displayText) } func test_helpFunctionKeyDisplaysHelpForHelpKeyCode() { - let ch = Self.appKitFunctionKey(NSHelpFunctionKey) - keystroke = makeKeystroke(keyCode: KeyboardKeyCode.help.rawValue, modifiers: [], characters: ch, charactersIgnoringModifiers: ch) - XCTAssertEqual(transform(keystroke), UnicodeToken.questionMark.string + UnicodeToken.enclosingCircle.string) + let ch = TestKeyboardCharacters.functionKeyCharacter(NSHelpFunctionKey) + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.help.rawValue, modifiers: [], characters: ch, charactersIgnoringModifiers: ch) + XCTAssertEqual(self.transform(self.keystroke), UnicodeToken.questionMark.string + UnicodeToken.enclosingCircle.string) } func test_helpKeyCodeWithoutSemanticCharactersDefaultsToInsert() { - keystroke = makeKeystroke(keyCode: KeyboardKeyCode.help.rawValue, modifiers: [], characters: "", charactersIgnoringModifiers: "") - XCTAssertEqual(transform(keystroke), "ins") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.help.rawValue, modifiers: [], characters: "", charactersIgnoringModifiers: "") + XCTAssertEqual(self.transform(self.keystroke), KeyboardSpecialKey.insert.displayText) } +} - // MARK: - US English - Special Cases with Modifiers +// MARK: - US English - Special Cases with Modifiers +extension EventTransformerKeystrokeTests { func test_optionShiftUp() { - let ch = String(format: "%lu", UInt64(0x00006000002f5c00)) - keystroke = makeKeystroke(keyCode: 126, modifiers: NSEvent.ModifierFlags(rawValue: 11141410), characters: ch, charactersIgnoringModifiers: ch) + let ch = TestKeyboardCharacters.functionKeyCharacter(NSUpArrowFunctionKey) + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.upArrow.rawValue, modifiers: TestModifierFlags.functionOptionShiftNumericPad, characters: ch, charactersIgnoringModifiers: ch) - XCTAssertEqual(transform(keystroke), "⌥⇧▲") + XCTAssertEqual( + self.transform(self.keystroke), + KeyboardGlyphCatalog.option + KeyboardGlyphCatalog.shift + UnicodeToken.upArrow.string + ) } func test_optionUSpecialCase() { - keystroke = makeKeystroke(keyCode: 32, modifiers: NSEvent.ModifierFlags(rawValue: 524576), characters: "", charactersIgnoringModifiers: "u") - XCTAssertEqual(transform(keystroke), "⌥U") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.u.rawValue, modifiers: TestModifierFlags.option, characters: "", charactersIgnoringModifiers: "u") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.option + "U") } func test_optionESpecialCase() { - keystroke = makeKeystroke(keyCode: 14, modifiers: NSEvent.ModifierFlags(rawValue: 524576), characters: "", charactersIgnoringModifiers: "e") - XCTAssertEqual(transform(keystroke), "⌥E") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.e.rawValue, modifiers: TestModifierFlags.option, characters: "", charactersIgnoringModifiers: "e") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.option + "E") } func test_optionBacktickSpecialCase() { - keystroke = makeKeystroke(keyCode: 50, modifiers: NSEvent.ModifierFlags(rawValue: 524576), characters: "", charactersIgnoringModifiers: "`") - XCTAssertEqual(transform(keystroke), "⌥`") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.grave.rawValue, modifiers: TestModifierFlags.option, characters: "", charactersIgnoringModifiers: "`") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.option + "`") } +} - // MARK: - German - Special Case +// MARK: - German - Special Case +extension EventTransformerKeystrokeTests { func test_commandßDisplaysCommandß() { - keystroke = makeKeystroke(keyCode: 27, modifiers: NSEvent.ModifierFlags(rawValue: 1048840), characters: "ß", charactersIgnoringModifiers: "ß") - XCTAssertEqual(transform(keystroke), "⌘ß") + self.keystroke = TestKeystrokes.make(keyCode: KeyboardKeyCode.minus.rawValue, modifiers: TestModifierFlags.command, characters: "ß", charactersIgnoringModifiers: "ß") + XCTAssertEqual(self.transform(self.keystroke), KeyboardGlyphCatalog.command + "ß") } - private static func appKitFunctionKey(_ key: Int) -> String { - String(UnicodeScalar(key)!) - } } diff --git a/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventTransformerMouseTests.swift b/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventTransformerMouseTests.swift index e839179..730421f 100644 --- a/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventTransformerMouseTests.swift +++ b/Apps/Keyty/Tests/KeytyTests/Services/EventPipeline/EventTransformerMouseTests.swift @@ -12,83 +12,50 @@ import XCTest final class EventTransformerMouseTests: XCTestCase { var keyboardLayout: TISInputSource! - var keyboardLayouts: [TISInputSource] = [] func transform(_ event: MouseEvent) -> String { EventTransformer(keyboardLayout: keyboardLayout).transform(.mouse(event)) } - func usEnglishKeyboardLayout() -> TISInputSource { - let properties: [String: Any] = [ - kTISPropertyInputSourceID as String: "com.apple.keylayout.US", - kTISPropertyInputSourceType as String: kTISTypeKeyboardLayout as String - ] - keyboardLayouts = TISCreateInputSourceList(properties as CFDictionary, true)! - .takeRetainedValue() as! [TISInputSource] - XCTAssertGreaterThan(keyboardLayouts.count, 0) - return keyboardLayouts[0] - } - - func makeMouseEvent(type: NSEvent.EventType, buttonNumber: Int, - modifiers: NSEvent.ModifierFlags) -> MouseEvent { - let cgType: CGEventType = { - switch type { - case .leftMouseDown: return .leftMouseDown - case .rightMouseDown: return .rightMouseDown - default: return .otherMouseDown - } - }() - var cgFlags: CGEventFlags = [] - if modifiers.contains(.shift) { cgFlags.insert(.maskShift) } - if modifiers.contains(.command) { cgFlags.insert(.maskCommand) } - if modifiers.contains(.control) { cgFlags.insert(.maskControl) } - if modifiers.contains(.option) { cgFlags.insert(.maskAlternate) } - let cgEvent = CGEvent(mouseEventSource: nil, mouseType: cgType, - mouseCursorPosition: .zero, mouseButton: .left)! - cgEvent.setIntegerValueField(.mouseEventButtonNumber, value: Int64(buttonNumber)) - cgEvent.flags = cgFlags - return MouseEvent(nsEvent: NSEvent(cgEvent: cgEvent)!) - } - - override func setUp() { - super.setUp() - keyboardLayout = usEnglishKeyboardLayout() + override func setUpWithError() throws { + try super.setUpWithError() + self.keyboardLayout = try TestKeyboardLayouts.requireUSEnglish() } // MARK: - Mouse buttons func test_MouseEvent_leftMouseDownIsLMB() { - let event = makeMouseEvent(type: .leftMouseDown, buttonNumber: 0, modifiers: []) + let event = TestMouseEvents.make(type: .leftMouseDown, buttonNumber: 0, modifiers: []) XCTAssertEqual(transform(event), "LMB") } func test_MouseEvent_rightMouseDownIsRMB() { - let event = makeMouseEvent(type: .rightMouseDown, buttonNumber: 1, modifiers: []) + let event = TestMouseEvents.make(type: .rightMouseDown, buttonNumber: 1, modifiers: []) XCTAssertEqual(transform(event), "RMB") } func test_MouseEvent_middleMouseDownIsMMB() { - let event = makeMouseEvent(type: .otherMouseDown, buttonNumber: 2, modifiers: []) + let event = TestMouseEvents.make(type: .otherMouseDown, buttonNumber: 2, modifiers: []) XCTAssertEqual(transform(event), "MMB") } func test_MouseEvent_fourthButtonIsMB4() { - let event = makeMouseEvent(type: .otherMouseDown, buttonNumber: 3, modifiers: []) + let event = TestMouseEvents.make(type: .otherMouseDown, buttonNumber: 3, modifiers: []) XCTAssertEqual(transform(event), "MB4") } func test_MouseEvent_fifthButtonIsMB5() { - let event = makeMouseEvent(type: .otherMouseDown, buttonNumber: 4, modifiers: []) + let event = TestMouseEvents.make(type: .otherMouseDown, buttonNumber: 4, modifiers: []) XCTAssertEqual(transform(event), "MB5") } func test_MouseEvent_commandLeftClickShowsCommandLMB() { - let event = makeMouseEvent(type: .leftMouseDown, buttonNumber: 0, modifiers: .command) - XCTAssertEqual(transform(event), "⌘LMB") + let event = TestMouseEvents.make(type: .leftMouseDown, buttonNumber: 0, modifiers: .command) + XCTAssertEqual(transform(event), KeyboardGlyphCatalog.command + "LMB") } func test_MouseEvent_optionShiftRightClickShowsModifiersWithRMB() { - let event = makeMouseEvent(type: .rightMouseDown, buttonNumber: 1, modifiers: [.option, .shift]) - XCTAssertEqual(transform(event), "⌥⇧RMB") + let event = TestMouseEvents.make(type: .rightMouseDown, buttonNumber: 1, modifiers: [.option, .shift]) + XCTAssertEqual(transform(event), KeyboardGlyphCatalog.option + KeyboardGlyphCatalog.shift + "RMB") } } diff --git a/Apps/Keyty/Tests/KeytyTests/Support/TestKeyboardCharacters.swift b/Apps/Keyty/Tests/KeytyTests/Support/TestKeyboardCharacters.swift new file mode 100644 index 0000000..333e832 --- /dev/null +++ b/Apps/Keyty/Tests/KeytyTests/Support/TestKeyboardCharacters.swift @@ -0,0 +1,17 @@ +// +// TestKeyboardCharacters.swift +// KeytyTests +// +// SPDX-FileCopyrightText: 2026 Serhii Bykov +// SPDX-License-Identifier: BSD-3-Clause +// + +import AppKit + +enum TestKeyboardCharacters { + static func functionKeyCharacter(_ key: Int) -> String { + String(UnicodeScalar(key)!) + } + + static let backTab = String(UnicodeScalar(NSBackTabCharacter)!) +} diff --git a/Apps/Keyty/Tests/KeytyTests/Support/TestKeyboardLayouts.swift b/Apps/Keyty/Tests/KeytyTests/Support/TestKeyboardLayouts.swift new file mode 100644 index 0000000..7549318 --- /dev/null +++ b/Apps/Keyty/Tests/KeytyTests/Support/TestKeyboardLayouts.swift @@ -0,0 +1,33 @@ +// +// TestKeyboardLayouts.swift +// KeytyTests +// +// SPDX-FileCopyrightText: 2026 Serhii Bykov +// SPDX-License-Identifier: BSD-3-Clause +// + +import Carbon +import XCTest + +enum TestKeyboardLayouts { + static func requireUSEnglish(file: StaticString = #filePath, + line: UInt = #line) throws -> TISInputSource { + let properties: [String: Any] = [ + kTISPropertyInputSourceID as String: "com.apple.keylayout.US", + kTISPropertyInputSourceType as String: kTISTypeKeyboardLayout as String + ] + let inputSources = try XCTUnwrap( + TISCreateInputSourceList(properties as CFDictionary, true)? + .takeRetainedValue() as? [TISInputSource], + "Expected the US English keyboard layout to be available", + file: file, + line: line + ) + return try XCTUnwrap( + inputSources.first, + "Expected the US English keyboard layout to be available", + file: file, + line: line + ) + } +} diff --git a/Apps/Keyty/Tests/KeytyTests/Support/TestKeystrokes.swift b/Apps/Keyty/Tests/KeytyTests/Support/TestKeystrokes.swift new file mode 100644 index 0000000..f52a3f3 --- /dev/null +++ b/Apps/Keyty/Tests/KeytyTests/Support/TestKeystrokes.swift @@ -0,0 +1,33 @@ +// +// TestKeystrokes.swift +// KeytyTests +// +// SPDX-FileCopyrightText: 2026 Serhii Bykov +// SPDX-License-Identifier: BSD-3-Clause +// + +import AppKit +@testable import Keyty + +enum TestKeystrokes { + static func make( + keyCode: UInt16, + modifiers: NSEvent.ModifierFlags = [], + characters: String, + charactersIgnoringModifiers: String + ) -> StandardKeyEvent { + let event = NSEvent.keyEvent( + with: .keyDown, + location: .zero, + modifierFlags: modifiers, + timestamp: NSDate.timeIntervalSinceReferenceDate, + windowNumber: 0, + context: nil, + characters: characters, + charactersIgnoringModifiers: charactersIgnoringModifiers, + isARepeat: false, + keyCode: keyCode + )! + return StandardKeyEvent(nsEvent: event) + } +} diff --git a/Apps/Keyty/Tests/KeytyTests/Support/TestModifierFlags.swift b/Apps/Keyty/Tests/KeytyTests/Support/TestModifierFlags.swift new file mode 100644 index 0000000..94da86a --- /dev/null +++ b/Apps/Keyty/Tests/KeytyTests/Support/TestModifierFlags.swift @@ -0,0 +1,63 @@ +// +// TestModifierFlags.swift +// KeytyTests +// +// SPDX-FileCopyrightText: 2026 Serhii Bykov +// SPDX-License-Identifier: BSD-3-Clause +// + +import AppKit +import IOKit.hidsystem +@testable import Keyty + +enum TestModifierFlags { + private static let recordedEventStateMask: UInt = 0x100 + + static let none = Self.recorded([]) + static let control = Self.recorded(.control, deviceMasks: UInt(NX_DEVICELCTLKEYMASK)) + static let shift = Self.recorded(.shift, deviceMasks: UInt(NX_DEVICELSHIFTKEYMASK)) + static let controlShift = Self.recorded( + [.control, .shift], + deviceMasks: UInt(NX_DEVICELCTLKEYMASK), UInt(NX_DEVICELSHIFTKEYMASK) + ) + static let command = Self.recorded(.command, deviceMasks: UInt(NX_DEVICELCMDKEYMASK)) + static let commandShift = Self.recorded( + [.command, .shift], + deviceMasks: UInt(NX_DEVICELCMDKEYMASK), UInt(NX_DEVICELSHIFTKEYMASK) + ) + static let commandOption = Self.recorded( + [.command, .option], + deviceMasks: UInt(NX_DEVICELCMDKEYMASK), UInt(NX_DEVICELALTKEYMASK) + ) + static let commandOptionShift = Self.recorded( + [.command, .option, .shift], + deviceMasks: UInt(NX_DEVICELCMDKEYMASK), UInt(NX_DEVICELALTKEYMASK), UInt(NX_DEVICELSHIFTKEYMASK) + ) + static let controlCommandShift = Self.recorded( + [.control, .command, .shift], + deviceMasks: UInt(NX_DEVICELCTLKEYMASK), UInt(NX_DEVICELCMDKEYMASK), UInt(NX_DEVICELSHIFTKEYMASK) + ) + static let controlOption = Self.recorded( + [.control, .option], + deviceMasks: UInt(NX_DEVICELCTLKEYMASK), UInt(NX_DEVICELALTKEYMASK) + ) + static let controlOptionShift = Self.recorded( + [.control, .option, .shift], + deviceMasks: UInt(NX_DEVICELCTLKEYMASK), UInt(NX_DEVICELALTKEYMASK), UInt(NX_DEVICELSHIFTKEYMASK) + ) + static let option = Self.recorded(.option, deviceMasks: UInt(NX_DEVICELALTKEYMASK)) + static let optionShift = Self.recorded( + [.option, .shift], + deviceMasks: UInt(NX_DEVICELALTKEYMASK), UInt(NX_DEVICELSHIFTKEYMASK) + ) + static let function = Self.recorded(.function) + static let functionOptionShiftNumericPad = Self.recorded( + [.function, .option, .shift, .numericPad], + deviceMasks: UInt(NX_DEVICELALTKEYMASK), UInt(NX_DEVICELSHIFTKEYMASK) + ) + + private static func recorded(_ flags: NSEvent.ModifierFlags, + deviceMasks: UInt...) -> NSEvent.ModifierFlags { + flags.addingRawMasks([recordedEventStateMask] + deviceMasks) + } +} diff --git a/Apps/Keyty/Tests/KeytyTests/Support/TestMouseEvents.swift b/Apps/Keyty/Tests/KeytyTests/Support/TestMouseEvents.swift new file mode 100644 index 0000000..5323f11 --- /dev/null +++ b/Apps/Keyty/Tests/KeytyTests/Support/TestMouseEvents.swift @@ -0,0 +1,57 @@ +// +// TestMouseEvents.swift +// KeytyTests +// +// SPDX-FileCopyrightText: 2026 Serhii Bykov +// SPDX-License-Identifier: BSD-3-Clause +// + +import AppKit +@testable import Keyty + +enum TestMouseEvents { + static func make( + type: NSEvent.EventType, + buttonNumber: Int = 0, + modifiers: NSEvent.ModifierFlags = [] + ) -> MouseEvent { + let cgEvent = CGEvent( + mouseEventSource: nil, + mouseType: Self.cgMouseEventType(for: type), + mouseCursorPosition: .zero, + mouseButton: Self.cgMouseButton(for: type) + )! + cgEvent.setIntegerValueField(.mouseEventButtonNumber, value: Int64(buttonNumber)) + cgEvent.flags = Self.cgEventFlags(for: modifiers) + return MouseEvent(nsEvent: NSEvent(cgEvent: cgEvent)!) + } + + private static func cgMouseEventType(for nsEventType: NSEvent.EventType) -> CGEventType { + switch nsEventType { + case .leftMouseDown: return .leftMouseDown + case .leftMouseUp: return .leftMouseUp + case .rightMouseDown: return .rightMouseDown + case .rightMouseUp: return .rightMouseUp + case .otherMouseDown: return .otherMouseDown + case .otherMouseUp: return .otherMouseUp + default: return .otherMouseDown + } + } + + private static func cgMouseButton(for nsEventType: NSEvent.EventType) -> CGMouseButton { + switch nsEventType { + case .rightMouseDown, .rightMouseUp: return .right + default: return .left + } + } + + private static func cgEventFlags(for modifierFlags: NSEvent.ModifierFlags) -> CGEventFlags { + var flags: CGEventFlags = [] + if modifierFlags.contains(.shift) { flags.insert(.maskShift) } + if modifierFlags.contains(.command) { flags.insert(.maskCommand) } + if modifierFlags.contains(.control) { flags.insert(.maskControl) } + if modifierFlags.contains(.option) { flags.insert(.maskAlternate) } + if modifierFlags.contains(.function) { flags.insert(.maskSecondaryFn) } + return flags + } +}