mirror of
https://github.com/keymanapp/keyman.git
synced 2026-11-05 10:34:54 +00:00
Merge pull request #13006 from keymanapp/fix/mac/12997-restore-notifications
fix(mac): properly manage Input Method lifecycle
This commit is contained in:
commit
0da0fb0d37
3 changed files with 98 additions and 42 deletions
|
|
@ -14,5 +14,6 @@
|
|||
|
||||
- (void)menuAction:(id)sender;
|
||||
- (void)handleBackspace:(NSEvent *)event;
|
||||
|
||||
- (void)changeClients:(NSString *)clientAppId;
|
||||
- (void)deactivateClient;
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -25,22 +25,13 @@ KMInputMethodEventHandler* _eventHandler;
|
|||
|
||||
- (id)initWithServer:(IMKServer *)server delegate:(id)delegate client:(id)inputClient
|
||||
{
|
||||
os_log_debug([KMLogs lifecycleLog], "initWithServer, active app: '%{public}@'", [KMInputMethodLifecycle getRunningApplicationId]);
|
||||
os_log_debug([KMLogs lifecycleLog], "+++KMInputController initWithServer, active app: '%{public}@'", [KMInputMethodLifecycle getRunningApplicationId]);
|
||||
|
||||
self = [super initWithServer:server delegate:delegate client:inputClient];
|
||||
if (self) {
|
||||
self.appDelegate.inputController = self;
|
||||
os_log_debug([KMLogs lifecycleLog], " +++initWithServer, self: %p", self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register to receive the Deactivated and ChangedClient notification generated from KMInputMethodLifecycle so
|
||||
* that the eventHandler can be changed. There is no need to receive the Activated notification because
|
||||
* the InputController does it all it needs to when it receives the ChangedClient.
|
||||
*/
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputMethodDeactivated:) name:kInputMethodDeactivatedNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputMethodChangedClient:) name:kInputMethodClientChangeNotification object:nil];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
|
@ -67,35 +58,81 @@ KMInputMethodEventHandler* _eventHandler;
|
|||
}
|
||||
|
||||
/**
|
||||
* The Keyman input method is deactivating because the user chose a different input method: notification from KMInputMethodLifecycle
|
||||
* Called by the app delegate when KMInputMethodLifecycle determines the user has switched clients
|
||||
*/
|
||||
- (void)inputMethodDeactivated:(NSNotification *)notification {
|
||||
os_log_debug([KMLogs lifecycleLog], "***KMInputController inputMethodDeactivated, deactivating eventHandler");
|
||||
if (_eventHandler != nil) {
|
||||
[_eventHandler deactivate];
|
||||
}
|
||||
- (void)changeClients:(NSString *)clientAppId {
|
||||
os_log_debug([KMLogs lifecycleLog], "***KMInputController changeClients, deactivate old eventHandler and activate new one");
|
||||
|
||||
[self deactivateEventHandler];
|
||||
[self activateEventHandler:clientAppId];
|
||||
}
|
||||
|
||||
/**
|
||||
* The user has switched to a different text input client: notification from KMInputMethodLifecycle
|
||||
* Called by the app delegate when KMInputMethodLifecycle determines the user has deactivated Keyman
|
||||
*/
|
||||
- (void)inputMethodChangedClient:(NSNotification *)notification {
|
||||
os_log_debug([KMLogs lifecycleLog], "***KMInputController inputMethodChangedClient, deactivating old eventHandler and activating new one");
|
||||
if (_eventHandler != nil) {
|
||||
[_eventHandler deactivate];
|
||||
}
|
||||
_eventHandler = [[KMInputMethodEventHandler alloc] initWithClient:[KMInputMethodLifecycle getRunningApplicationId] client:self.client];
|
||||
|
||||
- (void)deactivateClient {
|
||||
os_log_debug([KMLogs lifecycleLog], "***KMInputController deactivateClient, deactivate old eventHandler");
|
||||
|
||||
[self deactivateEventHandler];
|
||||
}
|
||||
|
||||
- (void)deactivateEventHandler {
|
||||
if (_eventHandler != nil) {
|
||||
os_log_debug([KMLogs lifecycleLog], " * KMInputController deactivate old eventHandler");
|
||||
[_eventHandler deactivate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)activateEventHandler:(NSString *)clientAppId {
|
||||
os_log_debug([KMLogs lifecycleLog], " * KMInputController activate new eventHandler");
|
||||
_eventHandler = [[KMInputMethodEventHandler alloc] initWithClient:clientAppId client:self.client];
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the OS to activate the input method, but some calls are not useful and are followed milliseconds later
|
||||
* by a deactivateServer. These short-lived activations may be generated by clicking and releasing menu items without changing applications.
|
||||
* Rather than treat every message received as a true activation, we send a message to KMInputMethodLifeCycle to evaluate.
|
||||
*/
|
||||
- (void)activateServer:(id)sender {
|
||||
os_log_info([KMLogs lifecycleLog], " +++KMInputController, activateServer, self=%p", self);
|
||||
[sender overrideKeyboardWithKeyboardNamed:@"com.apple.keylayout.US"];
|
||||
|
||||
/*
|
||||
When this KMInputController becomes the active server for the input method,
|
||||
then immediately update the AppDelegate. The duration that this controller is the
|
||||
active server may be extremely short, but, if so, we will receive another
|
||||
call to activateServer moments later and can update the AppDelegate again.
|
||||
*/
|
||||
[self attachToAppDelegate];
|
||||
|
||||
/*
|
||||
Call the shared lifecycle object so it can evaluate the current state
|
||||
and determine whether this is
|
||||
1) a real activation of the input method or
|
||||
2) a change in clients or
|
||||
3) a false alarm
|
||||
*/
|
||||
[KMInputMethodLifecycle.shared activateClient:sender];
|
||||
}
|
||||
|
||||
- (void)attachToAppDelegate {
|
||||
self.appDelegate.inputController = self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the OS to deactivate the input method
|
||||
*/
|
||||
- (void)deactivateServer:(id)sender {
|
||||
os_log_info([KMLogs lifecycleLog], " +++KMInputController, deactivateServer, self=%p", self);
|
||||
|
||||
/*
|
||||
Call the shared lifecycle object so it can evaluate the current state
|
||||
and determine whether this is
|
||||
1) a real deactivation of the input method or
|
||||
2) a change in clients or
|
||||
3) a false alarm
|
||||
*/
|
||||
[KMInputMethodLifecycle.shared deactivateClient:sender];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (NSMenu *)menu {
|
||||
|
|
|
|||
|
|
@ -123,15 +123,39 @@ id _lastServerWithOSKShowing = nil;
|
|||
// register to receive notifications generated from KMInputMethodLifecycle
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputMethodActivated:) name:kInputMethodActivatedNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputMethodDeactivated:) name:kInputMethodDeactivatedNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputMethodChangedClient:) name:kInputMethodClientChangeNotification object:nil];
|
||||
|
||||
// start Input Method lifecycle
|
||||
[KMInputMethodLifecycle.shared startLifecycle];
|
||||
}
|
||||
|
||||
/**
|
||||
* When the input method is deactivated, hide the OSK and disable the low-level event tap
|
||||
* When the input method is activated -- notification from KMInputMethodLifecycle
|
||||
*/
|
||||
- (void)inputMethodActivated:(NSNotification *)notification {
|
||||
|
||||
// enable event tap
|
||||
if (self.lowLevelEventTap && !CGEventTapIsEnabled(self.lowLevelEventTap)) {
|
||||
os_log_debug([KMLogs lifecycleLog], "***KMInputMethodAppDelegate inputMethodActivated, re-enabling event tap...");
|
||||
CGEventTapEnable(self.lowLevelEventTap, YES);
|
||||
}
|
||||
|
||||
// show OSK if necessary
|
||||
os_log_debug([KMLogs lifecycleLog], "--- inputMethodActivated, kvk is non-nil: %{public}@ showOskOnActivate: %{public}@", (_kvk!=nil)?@"true":@"false", [KMInputMethodLifecycle.shared shouldShowOskOnActivate]?@"true":@"false");
|
||||
|
||||
if ([KMInputMethodLifecycle.shared shouldShowOskOnActivate]) {
|
||||
os_log_debug([KMLogs oskLog], "***KMInputMethodAppDelegate inputMethodActivated, showing OSK");
|
||||
[KMSentryHelper addDebugBreadCrumb:@"lifecycle" message:@"opening OSK on input method activation"];
|
||||
[self showOSK];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the input method is deactivated -- notification from KMInputMethodLifecycle
|
||||
*/
|
||||
- (void)inputMethodDeactivated:(NSNotification *)notification {
|
||||
|
||||
// if the OSK is visible, hide it
|
||||
if ([self.oskWindow.window isVisible]) {
|
||||
os_log_debug([KMLogs oskLog], "***KMInputMethodAppDelegate inputMethodDeactivated, hiding OSK");
|
||||
[KMSentryHelper addInfoBreadCrumb:@"lifecycle" message:@"hiding OSK on input method deactivation"];
|
||||
|
|
@ -141,28 +165,22 @@ id _lastServerWithOSKShowing = nil;
|
|||
}
|
||||
[KMSentryHelper addOskVisibleTag:[self.oskWindow.window isVisible]];
|
||||
|
||||
// disable the event tap
|
||||
if (self.lowLevelEventTap) {
|
||||
os_log_debug([KMLogs lifecycleLog], "***inputMethodDeactivated, disabling event tap");
|
||||
CGEventTapEnable(self.lowLevelEventTap, NO);
|
||||
}
|
||||
|
||||
// deactive the text input client
|
||||
[self.inputController deactivateClient];
|
||||
}
|
||||
|
||||
/**
|
||||
* When the input method is activated, enable the low-level event tap and show the OSK
|
||||
* When the user switches to a different text input client -- notification from KMInputMethodLifecycle
|
||||
*/
|
||||
- (void)inputMethodActivated:(NSNotification *)notification {
|
||||
if (self.lowLevelEventTap && !CGEventTapIsEnabled(self.lowLevelEventTap)) {
|
||||
os_log_debug([KMLogs lifecycleLog], "***KMInputMethodAppDelegate inputMethodActivated, re-enabling event tap...");
|
||||
CGEventTapEnable(self.lowLevelEventTap, YES);
|
||||
}
|
||||
|
||||
os_log_debug([KMLogs lifecycleLog], "--- inputMethodActivated, kvk is non-nil: %{public}@ showOskOnActivate: %{public}@", (_kvk!=nil)?@"true":@"false", [KMInputMethodLifecycle.shared shouldShowOskOnActivate]?@"true":@"false");
|
||||
|
||||
if ([KMInputMethodLifecycle.shared shouldShowOskOnActivate]) {
|
||||
os_log_debug([KMLogs oskLog], "***KMInputMethodAppDelegate inputMethodActivated, showing OSK");
|
||||
[KMSentryHelper addInfoBreadCrumb:@"lifecycle" message:@"opening OSK on input method activation"];
|
||||
[self showOSK];
|
||||
}
|
||||
- (void)inputMethodChangedClient:(NSNotification *)notification {
|
||||
os_log_debug([KMLogs lifecycleLog], "***KMInputMethodAppDelegate inputMethodChangedClient");
|
||||
[self.inputController changeClients:[KMInputMethodLifecycle getRunningApplicationId]];
|
||||
}
|
||||
|
||||
- (KeymanVersionInfo)versionInfo {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue