blob: d4ff72f416e6a0e8302dd5dac339338f3d382c86 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import "mac/KeyCodeMap.h"
#include <map>
#import <Carbon/Carbon.h>
#include "base/logging.h"
#include "base/mutex.h"
#include "session/commands.pb.h"
using mozc::commands::KeyEvent;
using mozc::once_t;
using mozc::CallOnce;
#include "mac/init_kanamap.h"
#include "mac/init_specialcharmap.h"
#include "mac/init_specialkeymap.h"
static const unichar kYenMark = 0xA5;
@interface KeyCodeMap ()
// Extract Mac modifier flag bits from |flags| and set them into
// |keyEvent|.
- (void)addModifierFlags:(NSUInteger)flags
toMozcKeyEvent:(KeyEvent *)keyEvent;
// Extract key event information from |event| of NSFlagsChanged.
// Returns YES only if the flag change should create an Mozc key
// events.
- (BOOL)handleModifierFlagsChange:(NSEvent *)event
toMozcKeyEvent:(KeyEvent *)keyEvent;
@end
@implementation KeyCodeMap
@synthesize inputMode = inputMode_;
- (id)init {
CallOnce(&kOnceForKanaMap, InitKanaMap);
CallOnce(&kOnceForSpecialKeyMap, InitSpecialKeyMap);
CallOnce(&kOnceForSpecialCharMap, InitSpecialCharMap);
if (kSpecialKeyMap == NULL) {
self = nil;
return self;
}
self = [super init];
if (!self) {
inputMode_ = ASCII;
}
return self;
}
- (void)addModifierFlags:(NSUInteger)flags
toMozcKeyEvent:(KeyEvent *)keyEvent {
if ((flags & NSShiftKeyMask)) {
keyEvent->add_modifier_keys(KeyEvent::SHIFT);
}
if (flags & NSControlKeyMask) {
keyEvent->add_modifier_keys(KeyEvent::CTRL);
}
if (flags & NSAlternateKeyMask) {
keyEvent->add_modifier_keys(KeyEvent::ALT);
}
}
- (BOOL)handleModifierFlagsChange:(NSEvent *)event
toMozcKeyEvent:(KeyEvent *)keyEvent {
NSUInteger newFlags = [event modifierFlags];
// Clear unused modifier flag bits.
newFlags = newFlags & (NSShiftKeyMask |
NSControlKeyMask | NSAlternateKeyMask);
if (~modifierFlags_ & newFlags) {
// New modifier key is pressed: overwrite |modifierFlags_|.
// Consider the case as follows:
// 1. shift and control is pressed
// 2. only control is released
// 3. alt is newly pressed
// At that time, modifierFlags_ should be shift+alt.
modifierFlags_ = newFlags;
}
// If new modifier key is not pressed (i.e. some keys are released),
// we do not update |modifierFlags_| at that time. |modifierFlags_|
// will become 0 when all modifier keys are released.
if (newFlags != 0) {
// The user is still pressing some modifier keys.
return NO;
}
if (newFlags == 0 && modifierFlags_ == 0) {
// The user released modifier keys but modifierFlags_ are already cleared.
// Ex: Shift -> a (send Shift-a) -> release a -> release Shift (here)
return NO;
}
[self addModifierFlags:modifierFlags_ toMozcKeyEvent:keyEvent];
modifierFlags_ = 0;
return YES;
}
- (BOOL)isModeSwitchingKey:(NSEvent *)event {
NSUInteger keyCode = [event keyCode];
return keyCode == kVK_JIS_Eisu || keyCode == kVK_JIS_Kana;
}
- (BOOL)getMozcKeyCodeFromKeyEvent:(NSEvent *)event
toMozcKeyEvent:(KeyEvent *)keyEvent {
// This method uses |charactersIgnoringModifiers| basically but uses
// |characters| if its modifier is only Shift.
// For example:
// \C-a => CTRL + a where characters = "\x01"
// \S-a => A where characters = "\x41"
// \C-S-a => CTRL + SHIFT + a where characters = "\x61"
// \C-[ => Ctrl + [ where characters = "\x1b"
// \S-[ => { where characters = "\x7b"
// \C-S-[ => CTRL + SHIFT + [ where characters = "\x5b"
//
// The main reason to do this is to avoid the confusion between \C-[
// and ESC.
//
// There should be discussion like \C-S-a => CTRL + A instead of
// CTRL + SHIFT + a, but we're not doing this currently because
// Mozc must have keyboard layout information like "\S-[ => {" for
// the case of input is symbol.
if ([event type] == NSFlagsChanged) {
return [self handleModifierFlagsChange:event toMozcKeyEvent:keyEvent];
} else {
modifierFlags_ = 0;
}
NSString *inputString = [event characters];
NSString *inputStringRaw = [event charactersIgnoringModifiers];
if ([inputString length] == 0) {
return NO;
}
NSUInteger nsModifiers = [event modifierFlags];
// strip caps lock effects.
nsModifiers &= (~NSAlphaShiftKeyMask & NSDeviceIndependentModifierFlagsMask);
unsigned short keyCode = [event keyCode];
unichar inputChar = [((nsModifiers == NSShiftKeyMask) ?
inputString : inputStringRaw) characterAtIndex:0];
map<unsigned short, KeyEvent::SpecialKey>::const_iterator sp_iter =
kSpecialKeyMap->find(keyCode);
if (sp_iter != kSpecialKeyMap->end()) {
keyEvent->set_special_key(sp_iter->second);
} else {
map<unichar, KeyEvent::SpecialKey>::const_iterator spc_iter =
kSpecialCharMap->find(inputChar);
if (spc_iter != kSpecialCharMap->end()) {
keyEvent->set_special_key(spc_iter->second);
} else if (isgraph(inputChar)) {
keyEvent->set_key_code(inputChar);
} else if (inputChar == kYenMark) {
keyEvent->set_key_code('\\');
}
// fill kana "key_string" if mode is kana.
if (inputMode_ == KANA && !keyEvent->has_special_key()) {
map<unsigned short, const char *>::const_iterator kana_iter;
if (nsModifiers == NSShiftKeyMask && kKanaMapShift &&
(kana_iter = kKanaMapShift->find(keyCode)) != kKanaMapShift->end()) {
keyEvent->set_key_string(kana_iter->second);
} else if (kKanaMap &&
(kana_iter = kKanaMap->find(keyCode)) != kKanaMap->end()) {
keyEvent->set_key_string(kana_iter->second);
}
}
}
if (nsModifiers & NSCommandKeyMask) {
// When command key is pressed, it is not a mozc key command.
// Just ignore.
return NO;
}
#ifdef DEBUG
// We enclose this logging with DEBUG explicitly because we found
// that stringWithFormat is sometimes not trustworthy and DLOG(INFO)
// will compute the string-to-be-logged even in production.
LOG(INFO) << [[NSString stringWithFormat:@"%@", event] UTF8String]
<< " -> " << keyEvent->DebugString();
#endif
if (nsModifiers == NSShiftKeyMask && !keyEvent->has_special_key()) {
// If only the modifier is Shift and |keyEvent| is not normal key,
// don't put any modifiers.
return YES;
}
[self addModifierFlags:nsModifiers toMozcKeyEvent:keyEvent];
return YES;
}
@end