diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m index aa2e914371..b938279197 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m @@ -944,6 +944,7 @@ - (void)registerConfigurationWindow:(NSWindowController *)window { } - (void)showOSK { + [self.oskWindow prepareToShowOsk]; [[self.oskWindow window] makeKeyAndOrderFront:nil]; [[self.oskWindow window] setLevel:NSStatusWindowLevel]; [[self.oskWindow window] setTitle:self.oskWindowTitle]; @@ -1242,13 +1243,32 @@ - (NSEventModifierFlags) determineModifiers { NSEventModifierFlags modifierFlags = 0; if (self.receivedKeyDownFromOsk) { + /** + * the event was generated from the OSK, so use the oskEventModifiers in effect at time of event generation + */ modifierFlags = self.oskEventModifiers; os_log_debug([KMLogs eventsLog], "--- use modifiers from OSK, oskEventModifiers: 0x%lX", (unsigned long)modifierFlags); self.oskEventModifiers = 0; - } else { + } else { + /** + * the event originated from the physical keyboard, use the modifiers on kCGEventFlagsChanged events + */ NSEventModifierFlags originalModifiers = self.currentModifiers; modifierFlags = [self.modifierMapping adjustModifiers:originalModifiers]; os_log_debug([KMLogs eventsLog], "--- use adjusted modifiers from current state: 0x%lX", (unsigned long)modifierFlags); + + /** + * If the OSK is open, then we also need to apply its modifiers, if any, to this event originating from the physical keyboard. + * If the OSK is closed, its modifiers will be zero. + */ + NSEventModifierFlags oskModifiers = [self.oskWindow getOskEventModifierFlags]; + if (oskModifiers != 0) { + os_log_debug([KMLogs eventsLog], "--- modifiers from OSK to apply to physical keyboard event: 0x%lX", (unsigned long)oskModifiers); + + // combine osk modifiers with adjusted modifiers + modifierFlags = oskModifiers | modifierFlags; + os_log_debug([KMLogs eventsLog], "--- combined modifiers to apply to physical keyboard event: 0x%lX", (unsigned long)modifierFlags); + } } return modifierFlags; diff --git a/mac/Keyman4MacIM/Keyman4MacIM/OnScreenKeyboard/OSKWindowController.h b/mac/Keyman4MacIM/Keyman4MacIM/OnScreenKeyboard/OSKWindowController.h index fd3a953a85..ecb2f2566e 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/OnScreenKeyboard/OSKWindowController.h +++ b/mac/Keyman4MacIM/Keyman4MacIM/OnScreenKeyboard/OSKWindowController.h @@ -13,6 +13,8 @@ @property (nonatomic, weak) IBOutlet OSKView *oskView; +- (void)prepareToShowOsk; - (void)resetOSK; +- (NSEventModifierFlags)getOskEventModifierFlags; @end diff --git a/mac/Keyman4MacIM/Keyman4MacIM/OnScreenKeyboard/OSKWindowController.m b/mac/Keyman4MacIM/Keyman4MacIM/OnScreenKeyboard/OSKWindowController.m index ca4181d11c..b3824fa897 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/OnScreenKeyboard/OSKWindowController.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/OnScreenKeyboard/OSKWindowController.m @@ -66,9 +66,16 @@ - (void)windowDidResize:(NSNotification *)notification { [self.oskView resizeOSKLayout]; } +- (void)prepareToShowOsk { + os_log_info([KMLogs oskLog], "OSKWindowController prepareToShowOsk"); +} + - (void)windowWillClose:(NSNotification *)notification { + os_log_debug([KMLogs oskLog], "OSKWindowController windowWillClose"); [KMSettingsRepository.shared writeShowOskOnActivate:NO]; - os_log_debug([KMLogs oskLog], "OSKWindowController windowWillClose, updating settings writeShowOsk to NO"); + + // whenever the OSK is closing clear all of its modifier keys + [self.oskView clearOskModifiers]; } - (void)helpAction:(id)sender { @@ -97,6 +104,16 @@ - (void)resetOSK { } } +/** + * returns current event flags representing the state of the modifiers on the OSK or zero if OSK is closed + */ +- (NSEventModifierFlags)getOskEventModifierFlags { + if (self.window.isVisible) { + return [self.oskView getOskEventModifierFlags]; + } else { + return 0; + } +} - (void)startTimerWithTimeInterval:(NSTimeInterval)interval { if (_modFlagsTimer == nil) { TimerTarget *timerTarget = [[TimerTarget alloc] init]; diff --git a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/OnScreenKeyboard/OSKView.h b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/OnScreenKeyboard/OSKView.h index 082604871e..19c3263baa 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/OnScreenKeyboard/OSKView.h +++ b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/OnScreenKeyboard/OSKView.h @@ -25,6 +25,8 @@ - (void)setOskOptionState:(BOOL)oskAltState; - (void)setPhysicalControlState:(BOOL)ctrlState; - (void)setOskControlState:(BOOL)oskCtrlState; +- (void)clearOskModifiers; +- (NSEventModifierFlags)getOskEventModifierFlags; - (void)resetOSK; - (void)resizeOSKLayout; - (int64_t)createOskEventUserData; diff --git a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/OnScreenKeyboard/OSKView.m b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/OnScreenKeyboard/OSKView.m index 4bf0f6a0e8..e98fe60663 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/OnScreenKeyboard/OSKView.m +++ b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/OnScreenKeyboard/OSKView.m @@ -140,7 +140,7 @@ - (void)setKvk:(KVKFile *)kvk { _oskLayout = nil; _oskDefaultNKeys = nil; - [self displayKeyLabelsForLayer]; + [self updateKeyLabelsForCurrentLayer]; } - (void)initOSKKeys { @@ -174,7 +174,7 @@ - (void)initOSKKeys { py += keyHeight; } - [self displayKeyLabelsForLayer]; + [self updateKeyLabelsForCurrentLayer]; } - (NSArray *)oskLayout { @@ -427,6 +427,18 @@ - (NSArray *)oskDefaultNKeys { return _oskDefaultNKeys; } +/** + * called when closing the OSK + * when the OSK is not visible then modifier state is determined solely by the physical keyboard + */ +- (void)clearOskModifiers { + _oskShiftState = NO; + _oskOptionState = NO; + _oskControlState = NO; + [self updateModifierKeysForState]; + [self updateKeyLabelsForCurrentLayer]; +} + - (void)resetOSK { [self setOskShiftState:NO]; [self setOskOptionState:NO]; @@ -475,6 +487,47 @@ - (void)keyAction:(id)sender { } } +/** + * get the event modifier flags representing the current state of the osk modifiers + */ +- (NSEventModifierFlags)getOskEventModifierFlags { + return [self getEventModifierFlags:self.oskShiftState optionFlag:self.oskOptionState controlFlag:self.oskControlState]; +} + +/** + * get the event modifier flags that represent the combined state of the physical and osk modifiers + */ +- (NSEventModifierFlags) getCombinedEventModifierFlags { + BOOL shift = self.physicalShiftState | self.oskShiftState; + BOOL option = self.physicalOptionState | self.oskOptionState; + BOOL control = self.physicalControlState | self.oskControlState; + + return [self getEventModifierFlags:shift optionFlag:option controlFlag:control]; +} + +/** + * get the NSEventModifierFlags value that corresponds to the specified modifier states + */ +- (NSEventModifierFlags)getEventModifierFlags:(BOOL)shift optionFlag:(BOOL)option controlFlag:(BOOL)control { + NSEventModifierFlags modifierFlags = 0; + + if (shift) { + modifierFlags = modifierFlags | NSEventModifierFlagShift; + } + /** + * Both left and right option keys on the OSK cause oskOptionState to be true without distinction of left or right. + * However, the generated event is a right alt so that it will trigger the right alt rules in the Keyman keyboard. + */ + if (option) { + modifierFlags = modifierFlags | MK_RIGHT_ALT_MASK; + } + if (control) { + modifierFlags = modifierFlags | NSEventModifierFlagControl; + } + + return modifierFlags; +} + /** * Create the 64-bit value to pass in the kCGEventSourceUserData field of the generated CGEvent * The upper-most bit is set to distinguish it from the default value of zero, and the lower 32 bits @@ -488,22 +541,9 @@ - (void)keyAction:(id)sender { - (int64_t) createOskEventUserData { // set bit to identify this user data as originating from the OSK int64_t oskEventData = OSK_EVENT_FLAG; - - if (self.oskShiftState || self.physicalShiftState) { - oskEventData = oskEventData | kCGEventFlagMaskShift; - } - /** - * Both left and right option keys on the OSK cause oskOptionState to be true without distinction of left or right. - * However, the generated event is a right alt so that it will trigger the right alt rules in the Keyman keyboard. - */ - if (self.oskOptionState || self.physicalOptionState) { - oskEventData = oskEventData | MK_RIGHT_ALT_MASK; - } - if (self.oskControlState || self.oskControlState) { - oskEventData = oskEventData | kCGEventFlagMaskControl; - } - - return oskEventData; + NSEventModifierFlags eventModifierFlags = [self getCombinedEventModifierFlags]; + + return oskEventData | eventModifierFlags; } - (void)handleKeyEvent:(NSEvent *)event { @@ -524,22 +564,20 @@ - (void)setKeyPressedOff:(KeyView *)keyView { } /** - * Display the key labels that correspond to the current state of the modifier keys. - * The layer shown depends on both the physical modifier state and the osk modifier state. + * get the Keyman modifier flags that represent the combined state of the physical and osk modifiers */ -- (void)displayKeyLabelsForLayer { - os_log_debug([KMELogs oskLog], "OSKView displayKeyLabelsForLayer, phsyical modifiers [shift: %d, option: %d, control: %d]\nosk modifiers [shift: %d, option: %d, control: %d]", self.physicalShiftState, self.physicalOptionState, self.physicalControlState, self.oskShiftState, self.oskOptionState, self.oskControlState); - +- (WORD) getKeymanModifierFlagsForCurrentLayer { BOOL shift = self.physicalShiftState | self.oskShiftState; BOOL option = self.physicalOptionState | self.oskOptionState; BOOL control = self.physicalControlState | self.oskControlState; - [self resetKeyLabels]; - NSMutableArray *mKeys = [[self keyTags] mutableCopy]; - NSArray *nkeys = [self.kvk keys]; - if (nkeys == nil) - nkeys = self.oskDefaultNKeys; - + return [self getKeymanModifierFlags:shift optionFlag:option controlFlag:control]; +} + +/** + * get Keyman modifier flags for the specified modifier states + */ +- (WORD)getKeymanModifierFlags:(BOOL)shift optionFlag:(BOOL)option controlFlag:(BOOL)control { WORD flags = 0; if (shift) { flags |= KVKS_SHIFT; @@ -555,11 +593,29 @@ - (void)displayKeyLabelsForLayer { flags |= KVKS_RCTRL; } + return flags; +} + +/** + * Display the key labels that correspond to the current state of the modifier keys. + * The layer shown depends on both the physical modifier state and the osk modifier state. + */ +- (void)updateKeyLabelsForCurrentLayer { + os_log_debug([KMELogs oskLog], "OSKView updateKeyLabelsForState, phsyical modifiers [shift: %d, option: %d, control: %d]\nosk modifiers [shift: %d, option: %d, control: %d]", self.physicalShiftState, self.physicalOptionState, self.physicalControlState, self.oskShiftState, self.oskOptionState, self.oskControlState); + + [self resetKeyLabels]; + NSMutableArray *mKeys = [[self keyTags] mutableCopy]; + NSArray *nkeys = [self.kvk keys]; + if (nkeys == nil) + nkeys = self.oskDefaultNKeys; + + WORD keymanFlagsForCurrentLayer = [self getKeymanModifierFlagsForCurrentLayer]; + NSString *ansiFont = [self ansiFont]; NSString *unicodeFont = [self unicodeFont]; unsigned short keyCode; for (NKey *nkey in nkeys) { - if (nkey.modifierFlags == flags) { + if (nkey.modifierFlags == keymanFlagsForCurrentLayer) { keyCode = [self MacKeyCode:nkey.keyCode]; os_log_debug([KMELogs keyLog], "keyCode: %d, %x for key: %{public}@", keyCode, keyCode, nkey); if (keyCode < USHRT_MAX) { @@ -632,32 +688,17 @@ - (void)setPhysicalShiftState:(BOOL)state { */ _oskShiftState = NO; - if (state) { - os_log_debug([KMELogs oskLog], "hardware shift key pressed"); - [self displayKeyLabelsForLayer]; - [self displaysShiftKeysAsPressed:YES]; - } - else { - os_log_debug([KMELogs oskLog], "hardware shift key released"); - [self displayKeyLabelsForLayer]; - [self displaysShiftKeysAsPressed:NO]; - } + os_log_debug([KMELogs oskLog], "hardware shift key released"); + [self updateKeyLabelsForCurrentLayer]; + [self updateShiftKeysForState]; } } - (void)setOskShiftState:(BOOL)state { if (_oskShiftState != state && !self.physicalShiftState) { _oskShiftState = state; - if (state) { - os_log_debug([KMELogs oskLog], "OSK shift key selected"); - [self displayKeyLabelsForLayer]; - [self displaysShiftKeysAsPressed:YES]; - } - else if (!self.physicalShiftState) { - os_log_debug([KMELogs oskLog], "OSK shift key de-selected"); - [self displayKeyLabelsForLayer]; - [self displaysShiftKeysAsPressed:NO]; - } + [self updateKeyLabelsForCurrentLayer]; + [self updateShiftKeysForState]; } } @@ -670,29 +711,16 @@ - (void)setPhysicalOptionState:(BOOL)state { * The state of the physical key overrides the modifier key clicked in the OSK. */ _oskOptionState = NO; - - if (state) { - [self displayKeyLabelsForLayer]; - [self displaysOptionKeysAsPressed:YES]; - } - else { - [self displayKeyLabelsForLayer]; - [self displaysOptionKeysAsPressed:NO]; - } + [self updateKeyLabelsForCurrentLayer]; + [self updateOptionKeysForState]; } } - (void)setOskOptionState:(BOOL)state { if (_oskOptionState != state && !self.physicalOptionState) { _oskOptionState = state; - if (state) { - [self displayKeyLabelsForLayer]; - [self displaysOptionKeysAsPressed:YES]; - } - else if (!self.physicalOptionState) { - [self displayKeyLabelsForLayer]; - [self displaysOptionKeysAsPressed:NO]; - } + [self updateKeyLabelsForCurrentLayer]; + [self updateOptionKeysForState]; } } @@ -705,47 +733,43 @@ - (void)setPhysicalControlState:(BOOL)state { * The state of the physical key overrides the modifier key clicked in the OSK. */ _oskControlState = NO; - - if (state) { - [self displayKeyLabelsForLayer]; - [self displaysControlKeysAsPressed:YES]; - } - else { - [self displayKeyLabelsForLayer]; - [self displaysControlKeysAsPressed:NO]; - } + [self updateKeyLabelsForCurrentLayer]; + [self updateControlKeysForState]; } } - (void)setOskControlState:(BOOL)state { if (_oskControlState != state && !self.physicalControlState) { _oskControlState = state; - if (state) { - [self displayKeyLabelsForLayer]; - [self displaysControlKeysAsPressed:YES]; - } - else if (!self.physicalControlState) { - [self displayKeyLabelsForLayer]; - [self displaysControlKeysAsPressed:NO]; - } + [self updateKeyLabelsForCurrentLayer]; + [self updateControlKeysForState]; } } -- (void)displaysShiftKeysAsPressed:(BOOL)pressed { +- (void)updateModifierKeysForState { + [self updateShiftKeysForState]; + [self updateOptionKeysForState]; + [self updateControlKeysForState]; +} + +- (void)updateShiftKeysForState { + BOOL pressed = self.oskShiftState || self.physicalShiftState; KeyView *leftShiftKey = (KeyView *)[self viewWithTag:MVK_LEFT_SHIFT|0x1000]; KeyView *rightShiftKey = (KeyView *)[self viewWithTag:MVK_RIGHT_SHIFT|0x1000]; [leftShiftKey setKeyPressed:pressed]; [rightShiftKey setKeyPressed:pressed]; } -- (void)displaysOptionKeysAsPressed:(BOOL)pressed { +- (void)updateOptionKeysForState{ + BOOL pressed = self.oskOptionState || self.physicalOptionState; KeyView *leftOptionKey = (KeyView *)[self viewWithTag:MVK_LEFT_ALT|0x1000]; KeyView *rightOptionKey = (KeyView *)[self viewWithTag:MVK_RIGHT_ALT|0x1000]; [leftOptionKey setKeyPressed:pressed]; [rightOptionKey setKeyPressed:pressed]; } -- (void)displaysControlKeysAsPressed:(BOOL)pressed { +- (void)updateControlKeysForState { + BOOL pressed = self.oskControlState || self.physicalControlState; KeyView *leftControlKey = (KeyView *)[self viewWithTag:MVK_LEFT_CTRL|0x1000]; KeyView *rightControlKey = (KeyView *)[self viewWithTag:MVK_RIGHT_CTRL|0x1000]; [leftControlKey setKeyPressed:pressed];