This source file includes following definitions.
- GetRemappedKey
- IsRight
- HasDiamondKey
- IsISOLevel5ShiftUsedByCurrentInputMethod
- pref_service_for_testing_
- DeviceAddedForTesting
- GetDeviceType
- RewriteForTesting
- DeviceKeyPressedOrReleased
- WillProcessEvent
- DidProcessEvent
- DeviceHierarchyChanged
- DeviceAdded
- DeviceRemoved
- RefreshKeycodes
- NativeKeySymToNativeKeycode
- TopRowKeysAreFunctionKeys
- RewriteWithKeyboardRemappingsByKeySym
- RewriteWithKeyboardRemappingsByKeyCode
- GetPrefService
- IsAppleKeyboard
- GetRemappedModifierMasks
- RewriteModifiers
- RewriteNumPadKeys
- RewriteExtendedKeys
- RewriteFunctionKeys
- RewriteLocatedEvent
- OverwriteEvent
- DeviceAddedInternal
#include "chrome/browser/chromeos/events/event_rewriter.h"
#include <X11/extensions/XInput2.h>
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
#include <X11/Xlib.h>
#undef RootWindow
#undef Status
#include <vector>
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "chrome/browser/chromeos/events/keyboard_driven_event_rewriter.h"
#include "chrome/browser/chromeos/events/xinput_hierarchy_changed_event_listener.h"
#include "chrome/browser/chromeos/login/login_display_host_impl.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/pref_names.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/ime/input_method_manager.h"
#include "chromeos/ime/xkeyboard.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
#include "ui/wm/core/window_util.h"
namespace {
const int kBadDeviceId = -1;
const struct ModifierRemapping {
int remap_to;
int flag;
unsigned int native_modifier;
ui::KeyboardCode keycode;
KeySym native_keysyms[4];
} kModifierRemappings[] = {
{ chromeos::input_method::kSearchKey, 0, Mod4Mask, ui::VKEY_LWIN,
{ XK_Super_L, XK_Super_L, XK_Super_L, XK_Super_L }},
{ chromeos::input_method::kControlKey, ui::EF_CONTROL_DOWN, ControlMask,
ui::VKEY_CONTROL,
{ XK_Control_L, XK_Control_R, XK_Control_L, XK_Control_R }},
{ chromeos::input_method::kAltKey, ui::EF_ALT_DOWN, Mod1Mask,
ui::VKEY_MENU, { XK_Alt_L, XK_Alt_R, XK_Meta_L, XK_Meta_R }},
{ chromeos::input_method::kVoidKey, 0, 0U, ui::VKEY_UNKNOWN,
{ XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol }},
{ chromeos::input_method::kCapsLockKey, 0, 0U, ui::VKEY_CAPITAL,
{ XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock }},
{ chromeos::input_method::kEscapeKey, 0, 0U, ui::VKEY_ESCAPE,
{ XK_Escape, XK_Escape, XK_Escape, XK_Escape }},
};
const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1];
const struct ModifierFlagToPrefName {
unsigned int native_modifier;
int flag;
const char* pref_name;
} kModifierFlagToPrefName[] = {
{ Mod3Mask, 0, prefs::kLanguageRemapCapsLockKeyTo },
{ Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo },
{ ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo },
{ Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo },
{ Mod2Mask, 0, prefs::kLanguageRemapDiamondKeyTo },
};
const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
const PrefService& pref_service) {
if (!pref_service.FindPreference(pref_name.c_str()))
return NULL;
const int value = pref_service.GetInteger(pref_name.c_str());
for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) {
if (value == kModifierRemappings[i].remap_to)
return &kModifierRemappings[i];
}
return NULL;
}
bool IsRight(KeySym native_keysym) {
switch (native_keysym) {
case XK_Alt_R:
case XK_Control_R:
case XK_Hyper_R:
case XK_Meta_R:
case XK_Shift_R:
case XK_Super_R:
return true;
default:
break;
}
return false;
}
bool HasDiamondKey() {
return CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kHasChromeOSDiamondKey);
}
bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
chromeos::input_method::InputMethodManager* manager =
chromeos::input_method::InputMethodManager::Get();
return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
}
}
namespace chromeos {
EventRewriter::EventRewriter()
: last_device_id_(kBadDeviceId),
xkeyboard_for_testing_(NULL),
keyboard_driven_event_rewriter_(new KeyboardDrivenEventRewriter),
pref_service_for_testing_(NULL) {
base::MessageLoopForUI::current()->AddObserver(this);
if (base::SysInfo::IsRunningOnChromeOS()) {
XInputHierarchyChangedEventListener::GetInstance()->AddObserver(this);
}
RefreshKeycodes();
}
EventRewriter::~EventRewriter() {
base::MessageLoopForUI::current()->RemoveObserver(this);
if (base::SysInfo::IsRunningOnChromeOS()) {
XInputHierarchyChangedEventListener::GetInstance()->RemoveObserver(this);
}
}
EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting(
int device_id,
const std::string& device_name) {
return DeviceAddedInternal(device_id, device_name);
}
EventRewriter::DeviceType EventRewriter::GetDeviceType(
const std::string& device_name) {
std::vector<std::string> tokens;
Tokenize(device_name, " .", &tokens);
bool found_apple = false;
bool found_keyboard = false;
for (size_t i = 0; i < tokens.size(); ++i) {
if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple"))
found_apple = true;
if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard"))
found_keyboard = true;
if (found_apple && found_keyboard)
return kDeviceAppleKeyboard;
}
return kDeviceUnknown;
}
void EventRewriter::RewriteForTesting(XEvent* event) {
Rewrite(event);
}
void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
std::map<int, DeviceType>::const_iterator iter =
device_id_to_type_.find(device_id);
if (iter == device_id_to_type_.end()) {
DeviceAdded(device_id);
}
last_device_id_ = device_id;
}
base::EventStatus EventRewriter::WillProcessEvent(
const base::NativeEvent& event) {
XEvent* xevent = event;
if (xevent->type == KeyPress || xevent->type == KeyRelease) {
Rewrite(xevent);
} else if (xevent->type == GenericEvent) {
XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
if (xievent->evtype == XI_KeyPress || xievent->evtype == XI_KeyRelease) {
if (xievent->deviceid == xievent->sourceid)
DeviceKeyPressedOrReleased(xievent->deviceid);
} else {
RewriteLocatedEvent(xevent);
}
} else if (xevent->type == MappingNotify) {
if (xevent->xmapping.request == MappingModifier ||
xevent->xmapping.request == MappingKeyboard) {
RefreshKeycodes();
}
}
return base::EVENT_CONTINUE;
}
void EventRewriter::DidProcessEvent(const base::NativeEvent& event) {
}
void EventRewriter::DeviceHierarchyChanged() {}
void EventRewriter::DeviceAdded(int device_id) {
DCHECK_NE(XIAllDevices, device_id);
DCHECK_NE(XIAllMasterDevices, device_id);
if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
LOG(ERROR) << "Unexpected device_id passed: " << device_id;
return;
}
int ndevices_return = 0;
XIDeviceInfo* device_info = XIQueryDevice(gfx::GetXDisplay(),
device_id,
&ndevices_return);
if (!device_info) {
LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown.";
return;
}
DCHECK_EQ(1, ndevices_return);
for (int i = 0; i < ndevices_return; ++i) {
DCHECK_EQ(device_id, device_info[i].deviceid);
DCHECK(device_info[i].name);
DeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
}
XIFreeDeviceInfo(device_info);
}
void EventRewriter::DeviceRemoved(int device_id) {
device_id_to_type_.erase(device_id);
}
void EventRewriter::RefreshKeycodes() {
keysym_to_keycode_map_.clear();
}
KeyCode EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym) {
if (keysym_to_keycode_map_.count(keysym))
return keysym_to_keycode_map_[keysym];
XDisplay* display = gfx::GetXDisplay();
KeyCode keycode = XKeysymToKeycode(display, keysym);
keysym_to_keycode_map_[keysym] = keycode;
return keycode;
}
bool EventRewriter::TopRowKeysAreFunctionKeys(XEvent* event) const {
const PrefService* prefs = GetPrefService();
if (prefs &&
prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
return true;
ash::wm::WindowState* state = ash::wm::GetActiveWindowState();
return state ? state->top_row_keys_are_function_keys() : false;
}
bool EventRewriter::RewriteWithKeyboardRemappingsByKeySym(
const KeyboardRemapping* remappings,
size_t num_remappings,
KeySym keysym,
unsigned int native_mods,
KeySym* remapped_native_keysym,
unsigned int* remapped_native_mods) {
for (size_t i = 0; i < num_remappings; ++i) {
const KeyboardRemapping& map = remappings[i];
if (keysym != map.input_keysym)
continue;
unsigned int matched_mods = native_mods & map.input_native_mods;
if (matched_mods != map.input_native_mods)
continue;
*remapped_native_keysym = map.output_keysym;
*remapped_native_mods = (native_mods & ~map.input_native_mods) |
map.output_native_mods;
return true;
}
return false;
}
bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
const KeyboardRemapping* remappings,
size_t num_remappings,
KeyCode keycode,
unsigned int native_mods,
KeySym* remapped_native_keysym,
unsigned int* remapped_native_mods) {
for (size_t i = 0; i < num_remappings; ++i) {
const KeyboardRemapping& map = remappings[i];
KeyCode input_keycode = NativeKeySymToNativeKeycode(map.input_keysym);
if (keycode != input_keycode)
continue;
unsigned int matched_mods = native_mods & map.input_native_mods;
if (matched_mods != map.input_native_mods)
continue;
*remapped_native_keysym = map.output_keysym;
*remapped_native_mods = (native_mods & ~map.input_native_mods) |
map.output_native_mods;
return true;
}
return false;
}
const PrefService* EventRewriter::GetPrefService() const {
if (pref_service_for_testing_)
return pref_service_for_testing_;
Profile* profile = ProfileManager::GetActiveUserProfile();
return profile ? profile->GetPrefs() : NULL;
}
void EventRewriter::Rewrite(XEvent* event) {
if (event->xkey.send_event)
return;
if (keyboard_driven_event_rewriter_->RewriteIfKeyboardDrivenOnLoginScreen(
event)) {
return;
}
RewriteModifiers(event);
RewriteNumPadKeys(event);
RewriteExtendedKeys(event);
RewriteFunctionKeys(event);
}
bool EventRewriter::IsAppleKeyboard() const {
if (last_device_id_ == kBadDeviceId)
return false;
std::map<int, DeviceType>::const_iterator iter =
device_id_to_type_.find(last_device_id_);
if (iter == device_id_to_type_.end()) {
LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown.";
return false;
}
const DeviceType type = iter->second;
return type == kDeviceAppleKeyboard;
}
void EventRewriter::GetRemappedModifierMasks(
unsigned int original_native_modifiers,
unsigned int* remapped_native_modifiers) const {
if (UserManager::Get()->IsLoggedInAsGuest() &&
LoginDisplayHostImpl::default_host()) {
return;
}
const PrefService* pref_service = GetPrefService();
if (!pref_service)
return;
const bool skip_mod2 = !HasDiamondKey();
const bool skip_mod3 = IsISOLevel5ShiftUsedByCurrentInputMethod();
for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) {
if ((skip_mod2 && kModifierFlagToPrefName[i].native_modifier == Mod2Mask) ||
(skip_mod3 && kModifierFlagToPrefName[i].native_modifier == Mod3Mask)) {
continue;
}
if (original_native_modifiers &
kModifierFlagToPrefName[i].native_modifier) {
const ModifierRemapping* remapped_key =
GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service);
if (IsAppleKeyboard() &&
(kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) {
remapped_key = kModifierRemappingCtrl;
}
if (remapped_key) {
*remapped_native_modifiers |= remapped_key->native_modifier;
} else {
*remapped_native_modifiers |=
kModifierFlagToPrefName[i].native_modifier;
}
}
}
unsigned int native_mask = Mod4Mask | ControlMask | Mod1Mask;
if (!skip_mod2)
native_mask |= Mod2Mask;
if (!skip_mod3)
native_mask |= Mod3Mask;
*remapped_native_modifiers =
(original_native_modifiers & ~native_mask) |
*remapped_native_modifiers;
}
bool EventRewriter::RewriteModifiers(XEvent* event) {
DCHECK(event->type == KeyPress || event->type == KeyRelease);
if (UserManager::Get()->IsLoggedInAsGuest() &&
LoginDisplayHostImpl::default_host())
return false;
const PrefService* pref_service = GetPrefService();
if (!pref_service)
return false;
DCHECK_EQ(input_method::kControlKey, kModifierRemappingCtrl->remap_to);
XKeyEvent* xkey = &event->xkey;
KeySym keysym = XLookupKeysym(xkey, 0);
ui::KeyboardCode original_keycode = ui::KeyboardCodeFromNative(event);
ui::KeyboardCode remapped_keycode = original_keycode;
KeyCode remapped_native_keycode = xkey->keycode;
const ModifierRemapping* remapped_key = NULL;
switch (keysym) {
case XF86XK_Launch6:
if (HasDiamondKey())
remapped_key =
GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
if (!remapped_key)
remapped_key = kModifierRemappingCtrl;
break;
case XF86XK_Launch7:
remapped_key =
GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
break;
case XK_Super_L:
case XK_Super_R:
if (IsAppleKeyboard())
remapped_key = kModifierRemappingCtrl;
else
remapped_key =
GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
break;
case XK_Control_L:
case XK_Control_R:
remapped_key =
GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
break;
case XK_Alt_L:
case XK_Alt_R:
case XK_Meta_L:
case XK_Meta_R:
remapped_key =
GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
break;
default:
break;
}
if (remapped_key) {
int flags = ui::EventFlagsFromNative(event);
remapped_keycode = remapped_key->keycode;
const size_t level = ((flags & ui::EF_SHIFT_DOWN) ? (1 << 1) : 0) +
(IsRight(keysym) ? (1 << 0) : 0);
const KeySym native_keysym = remapped_key->native_keysyms[level];
remapped_native_keycode = NativeKeySymToNativeKeycode(native_keysym);
}
unsigned int remapped_native_modifiers = 0U;
GetRemappedModifierMasks(xkey->state, &remapped_native_modifiers);
if (event->type == KeyPress &&
original_keycode != ui::VKEY_CAPITAL &&
remapped_keycode == ui::VKEY_CAPITAL) {
input_method::XKeyboard* xkeyboard = xkeyboard_for_testing_ ?
xkeyboard_for_testing_ :
input_method::InputMethodManager::Get()->GetXKeyboard();
xkeyboard->SetCapsLockEnabled(!xkeyboard->CapsLockIsEnabled());
}
OverwriteEvent(event, remapped_native_keycode, remapped_native_modifiers);
return true;
}
bool EventRewriter::RewriteNumPadKeys(XEvent* event) {
DCHECK(event->type == KeyPress || event->type == KeyRelease);
bool rewritten = false;
XKeyEvent* xkey = &event->xkey;
const KeySym keysym = XLookupKeysym(xkey, 0);
switch (keysym) {
case XK_KP_Insert:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_0),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Delete:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_Decimal),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_End:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_1),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Down:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_2),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Next:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_3),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Left:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_4),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Begin:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_5),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Right:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_6),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Home:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_7),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Up:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_8),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Prior:
OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_9),
xkey->state | Mod2Mask);
rewritten = true;
break;
case XK_KP_Divide:
case XK_KP_Multiply:
case XK_KP_Subtract:
case XK_KP_Add:
case XK_KP_Enter:
OverwriteEvent(event, xkey->keycode, xkey->state | Mod2Mask);
rewritten = true;
break;
default:
break;
}
return rewritten;
}
bool EventRewriter::RewriteExtendedKeys(XEvent* event) {
DCHECK(event->type == KeyPress || event->type == KeyRelease);
XKeyEvent* xkey = &event->xkey;
const KeySym keysym = XLookupKeysym(xkey, 0);
KeySym remapped_native_keysym = 0;
unsigned int remapped_native_mods = 0;
bool rewritten = false;
if (xkey->state & Mod4Mask) {
static const KeyboardRemapping kAvoidRemappings[] = {
{
XK_BackSpace, Mod1Mask | Mod4Mask,
XK_BackSpace, Mod1Mask,
},
{
XK_Up, Mod1Mask | ControlMask | Mod4Mask,
XK_Up, Mod1Mask | ControlMask,
},
{
XK_Up, Mod1Mask | Mod4Mask,
XK_Up, Mod1Mask,
},
{
XK_Down, Mod1Mask | ControlMask | Mod4Mask,
XK_Down, Mod1Mask | ControlMask,
},
{
XK_Down, Mod1Mask | Mod4Mask,
XK_Down, Mod1Mask,
}
};
rewritten = RewriteWithKeyboardRemappingsByKeySym(
kAvoidRemappings,
arraysize(kAvoidRemappings),
keysym,
xkey->state,
&remapped_native_keysym,
&remapped_native_mods);
}
if (!rewritten) {
static const KeyboardRemapping kSearchRemappings[] = {
{
XK_BackSpace, Mod4Mask,
XK_Delete, 0
},
{
XK_Left, Mod4Mask,
XK_Home, 0
},
{
XK_Up, Mod4Mask,
XK_Prior, 0
},
{
XK_Right, Mod4Mask,
XK_End, 0
},
{
XK_Down, Mod4Mask,
XK_Next, 0
},
{
XK_period, Mod4Mask,
XK_Insert, 0
}
};
rewritten = RewriteWithKeyboardRemappingsByKeySym(
kSearchRemappings,
arraysize(kSearchRemappings),
keysym,
xkey->state,
&remapped_native_keysym,
&remapped_native_mods);
}
if (!rewritten) {
static const KeyboardRemapping kNonSearchRemappings[] = {
{
XK_BackSpace, Mod1Mask,
XK_Delete, 0
},
{
XK_Up, Mod1Mask | ControlMask,
XK_Home, 0
},
{
XK_Up, Mod1Mask,
XK_Prior, 0
},
{
XK_Down, Mod1Mask | ControlMask,
XK_End, 0
},
{
XK_Down, Mod1Mask,
XK_Next, 0
}
};
rewritten = RewriteWithKeyboardRemappingsByKeySym(
kNonSearchRemappings,
arraysize(kNonSearchRemappings),
keysym,
xkey->state,
&remapped_native_keysym,
&remapped_native_mods);
}
if (!rewritten)
return false;
OverwriteEvent(event,
NativeKeySymToNativeKeycode(remapped_native_keysym),
remapped_native_mods);
return true;
}
bool EventRewriter::RewriteFunctionKeys(XEvent* event) {
DCHECK(event->type == KeyPress || event->type == KeyRelease);
XKeyEvent* xkey = &event->xkey;
const KeySym keysym = XLookupKeysym(xkey, 0);
KeySym remapped_native_keysym = 0;
unsigned int remapped_native_mods = 0;
bool rewritten = false;
bool top_row_keys_are_special_keys = !TopRowKeysAreFunctionKeys(event);
if ((xkey->state & Mod4Mask) && top_row_keys_are_special_keys) {
static const KeyboardRemapping kFkeysToFkeys[] = {
{ XK_F1, Mod4Mask, XK_F1, },
{ XK_F2, Mod4Mask, XK_F2, },
{ XK_F3, Mod4Mask, XK_F3, },
{ XK_F4, Mod4Mask, XK_F4, },
{ XK_F5, Mod4Mask, XK_F5, },
{ XK_F6, Mod4Mask, XK_F6, },
{ XK_F7, Mod4Mask, XK_F7, },
{ XK_F8, Mod4Mask, XK_F8, },
{ XK_F9, Mod4Mask, XK_F9, },
{ XK_F10, Mod4Mask, XK_F10, },
{ XK_F11, Mod4Mask, XK_F11, },
{ XK_F12, Mod4Mask, XK_F12, },
};
rewritten = RewriteWithKeyboardRemappingsByKeySym(
kFkeysToFkeys,
arraysize(kFkeysToFkeys),
keysym,
xkey->state,
&remapped_native_keysym,
&remapped_native_mods);
}
if (!rewritten) {
static const KeyboardRemapping kFkeysToSpecialKeys[] = {
{ XK_F1, 0, XF86XK_Back, 0 },
{ XK_F2, 0, XF86XK_Forward, 0 },
{ XK_F3, 0, XF86XK_Reload, 0 },
{ XK_F4, 0, XF86XK_LaunchB, 0 },
{ XK_F5, 0, XF86XK_LaunchA, 0 },
{ XK_F6, 0, XF86XK_MonBrightnessDown, 0 },
{ XK_F7, 0, XF86XK_MonBrightnessUp, 0 },
{ XK_F8, 0, XF86XK_AudioMute, 0 },
{ XK_F9, 0, XF86XK_AudioLowerVolume, 0 },
{ XK_F10, 0, XF86XK_AudioRaiseVolume, 0 },
};
if (top_row_keys_are_special_keys) {
rewritten = RewriteWithKeyboardRemappingsByKeySym(
kFkeysToSpecialKeys,
arraysize(kFkeysToSpecialKeys),
keysym,
xkey->state,
&remapped_native_keysym,
&remapped_native_mods);
} else if (xkey->state & Mod4Mask) {
rewritten = RewriteWithKeyboardRemappingsByKeySym(
kFkeysToSpecialKeys,
arraysize(kFkeysToSpecialKeys),
keysym,
xkey->state & ~Mod4Mask,
&remapped_native_keysym,
&remapped_native_mods);
}
}
if (!rewritten && (xkey->state & Mod4Mask)) {
static const KeyboardRemapping kNumberKeysToFkeys[] = {
{ XK_1, Mod4Mask, XK_F1, 0 },
{ XK_2, Mod4Mask, XK_F2, 0 },
{ XK_3, Mod4Mask, XK_F3, 0 },
{ XK_4, Mod4Mask, XK_F4, 0 },
{ XK_5, Mod4Mask, XK_F5, 0 },
{ XK_6, Mod4Mask, XK_F6, 0 },
{ XK_7, Mod4Mask, XK_F7, 0 },
{ XK_8, Mod4Mask, XK_F8, 0 },
{ XK_9, Mod4Mask, XK_F9, 0 },
{ XK_0, Mod4Mask, XK_F10, 0 },
{ XK_minus, Mod4Mask, XK_F11, 0 },
{ XK_equal, Mod4Mask, XK_F12, 0 }
};
rewritten = RewriteWithKeyboardRemappingsByKeyCode(
kNumberKeysToFkeys,
arraysize(kNumberKeysToFkeys),
xkey->keycode,
xkey->state,
&remapped_native_keysym,
&remapped_native_mods);
}
if (!rewritten)
return false;
OverwriteEvent(event,
NativeKeySymToNativeKeycode(remapped_native_keysym),
remapped_native_mods);
return true;
}
void EventRewriter::RewriteLocatedEvent(XEvent* event) {
DCHECK_EQ(GenericEvent, event->type);
XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data);
if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease)
return;
unsigned int remapped_native_modifiers = 0U;
GetRemappedModifierMasks(xievent->mods.effective, &remapped_native_modifiers);
xievent->mods.effective = remapped_native_modifiers;
if ((xievent->evtype == XI_ButtonPress ||
pressed_device_ids_.count(xievent->sourceid)) &&
(xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) {
xievent->mods.effective &= ~Mod1Mask;
xievent->detail = Button3;
if (xievent->evtype == XI_ButtonRelease) {
XISetMask(xievent->buttons.mask, Button3);
XIClearMask(xievent->buttons.mask, Button1);
xievent->mods.effective &= ~Button1Mask;
pressed_device_ids_.erase(xievent->sourceid);
} else {
pressed_device_ids_.insert(xievent->sourceid);
}
}
}
void EventRewriter::OverwriteEvent(XEvent* event,
unsigned int new_native_keycode,
unsigned int new_native_state) {
DCHECK(event->type == KeyPress || event->type == KeyRelease);
XKeyEvent* xkey = &event->xkey;
xkey->keycode = new_native_keycode;
xkey->state = new_native_state;
}
EventRewriter::DeviceType EventRewriter::DeviceAddedInternal(
int device_id,
const std::string& device_name) {
const DeviceType type = EventRewriter::GetDeviceType(device_name);
if (type == kDeviceAppleKeyboard) {
VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
<< "id=" << device_id;
}
device_id_to_type_[device_id] = type;
return type;
}
}