mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-24 01:07:41 +00:00
187 lines
6.6 KiB
Objective-C
187 lines
6.6 KiB
Objective-C
/*
|
|
* Keyman is copyright (C) SIL International. MIT License.
|
|
*
|
|
* Created by Serkan Kurt on 2015-01-29.
|
|
*
|
|
*/
|
|
|
|
#import "KMInputController.h"
|
|
#import "KMInputMethodEventHandler.h"
|
|
#import "KMOSVersion.h"
|
|
#include <Carbon/Carbon.h> /* For kVK_ constants. */
|
|
#import "KMSettingsRepository.h"
|
|
#import "KMLogs.h"
|
|
#import "InputMethodKit/InputMethodKit.h"
|
|
#import "KMInputMethodLifecycle.h"
|
|
#import "KMSentryHelper.h"
|
|
|
|
@implementation KMInputController
|
|
|
|
KMInputMethodEventHandler* _eventHandler;
|
|
|
|
- (KMInputMethodAppDelegate *)appDelegate {
|
|
return (KMInputMethodAppDelegate *)[NSApp delegate];
|
|
}
|
|
|
|
- (id)initWithServer:(IMKServer *)server delegate:(id)delegate client:(id)inputClient
|
|
{
|
|
os_log_debug([KMLogs lifecycleLog], "+++KMInputController initWithServer, active app: '%{public}@'", [KMInputMethodLifecycle getRunningApplicationId]);
|
|
|
|
self = [super initWithServer:server delegate:delegate client:inputClient];
|
|
if (self) {
|
|
os_log_debug([KMLogs lifecycleLog], " +++initWithServer, self: %p", self);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (NSUInteger)recognizedEvents:(id)sender {
|
|
return NSEventMaskKeyDown | NSEventMaskFlagsChanged;
|
|
}
|
|
|
|
- (BOOL)handleEvent:(NSEvent *)event client:(id)sender {
|
|
if (event == nil || sender == nil || self.kmx == nil || _eventHandler == nil) {
|
|
os_log_error([KMLogs eventsLog], "IMInputController handleEvent: not prepared to handle event = %{public}@", event);
|
|
return NO; // Not sure this can ever happen.
|
|
}
|
|
|
|
return [_eventHandler handleEvent:event client:sender];
|
|
}
|
|
|
|
// Passthrough from the app delegate low level event hook
|
|
// to the input method event handler for handleBackspace.
|
|
- (void)handleBackspace:(NSEvent *)event {
|
|
os_log_debug([KMLogs keyLog], "KMInputController handleBackspace, event = %{public}@", event);
|
|
if(_eventHandler != nil) {
|
|
[_eventHandler handleBackspace:event];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called by the app delegate when KMInputMethodLifecycle determines the user has switched clients
|
|
*/
|
|
- (void)changeClients:(NSString *)clientAppId {
|
|
os_log_debug([KMLogs lifecycleLog], "***KMInputController changeClients, deactivate old eventHandler and activate new one");
|
|
|
|
[self deactivateEventHandler];
|
|
[self activateEventHandler:clientAppId];
|
|
}
|
|
|
|
/**
|
|
* Called by the app delegate when KMInputMethodLifecycle determines the user has deactivated Keyman
|
|
*/
|
|
- (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];
|
|
}
|
|
|
|
- (NSMenu *)menu {
|
|
return self.appDelegate.menu;
|
|
}
|
|
|
|
- (KMXFile *)kmx {
|
|
return self.appDelegate.kmx;
|
|
}
|
|
|
|
- (void)menuAction:(id)sender {
|
|
NSMenuItem *mItem = [sender objectForKey:kIMKCommandMenuItemName];
|
|
NSInteger itag = mItem.tag;
|
|
os_log_debug([KMLogs uiLog], "Keyman menu clicked - tag: %lu", itag);
|
|
if (itag == CONFIG_MENUITEM_TAG) {
|
|
[KMSentryHelper addUserBreadCrumb:@"menu" message:@"Configuration..."];
|
|
[self showConfigurationWindow:sender];
|
|
}
|
|
else if (itag == OSK_MENUITEM_TAG) {
|
|
[KMSentryHelper addUserBreadCrumb:@"menu" message:@"On-screen Keyboard"];
|
|
[KMSettingsRepository.shared writeShowOskOnActivate:YES];
|
|
os_log_debug([KMLogs oskLog], "menuAction OSK_MENUITEM_TAG, updating settings writeShowOsk to YES");
|
|
[self.appDelegate showOSK];
|
|
}
|
|
else if (itag == ABOUT_MENUITEM_TAG) {
|
|
[KMSentryHelper addUserBreadCrumb:@"menu" message:@"About"];
|
|
[self.appDelegate showAboutWindow];
|
|
}
|
|
else if (itag >= KEYMAN_FIRST_KEYBOARD_MENUITEM_TAG) {
|
|
[KMSentryHelper addUserBreadCrumb:@"menu" message:@"Selected Keyboard"];
|
|
[self.appDelegate selectKeyboardFromMenu:itag];
|
|
}
|
|
}
|
|
|
|
- (void)showConfigurationWindow:(id)sender {
|
|
// Using `showConfigurationWindow` instead of `showPreferences:` because `showPreferences:` is missing in
|
|
// High Sierra (10.13.1 - 10.13.3). See: https://bugreport.apple.com/web/?problemID=35422518
|
|
// rrb: where Apple's API is broken (10.13.1-10.13.3) call our workaround, otherwise, call showPreferences
|
|
u_int16_t systemVersion = [KMOSVersion SystemVersion];
|
|
if ([KMOSVersion Version_10_13_1] <= systemVersion && systemVersion <= [KMOSVersion Version_10_13_3]) // between 10.13.1 and 10.13.3 inclusive
|
|
{
|
|
os_log_info([KMLogs uiLog], "Input Menu: calling workaround instead of showPreferences (sys ver %x)", systemVersion);
|
|
[self.appDelegate showConfigurationWindow]; // call our workaround
|
|
}
|
|
else
|
|
{
|
|
os_log_info([KMLogs uiLog], "Input Menu: calling Apple's showPreferences (sys ver %x)", systemVersion);
|
|
[self showPreferences:sender]; // call Apple API
|
|
}
|
|
}
|
|
|
|
@end
|