Index: ui/events/ozone/evdev/keyboard_evdev.cc |
diff --git a/ui/events/ozone/evdev/keyboard_evdev.cc b/ui/events/ozone/evdev/keyboard_evdev.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..93f5b3320b7f867e337ed1926b9d62ddcef283e2 |
--- /dev/null |
+++ b/ui/events/ozone/evdev/keyboard_evdev.cc |
@@ -0,0 +1,239 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/events/ozone/evdev/keyboard_evdev.h" |
+ |
+#include "base/single_thread_task_runner.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "ui/events/event.h" |
+#include "ui/events/event_constants.h" |
+#include "ui/events/event_utils.h" |
+#include "ui/events/keycodes/dom/keycode_converter.h" |
+#include "ui/events/ozone/evdev/event_modifiers_evdev.h" |
+#include "ui/events/ozone/evdev/keyboard_util_evdev.h" |
+#include "ui/events/ozone/layout/keyboard_layout_engine.h" |
+#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" |
+#include "ui/events/ozone/layout/layout_util.h" |
+ |
+namespace ui { |
+ |
+// We can't include ui/events/keycodes/dom/dom_code.h here because of |
+// conflicts with preprocessor macros in <linux/input.h>, so we use the |
+// same underlying data with an additional prefix. |
+#define USB_KEYMAP(usb, xkb, win, mac, code, id) DOM_CODE_ ## id = usb |
+#define USB_KEYMAP_DECLARATION enum class DomCode |
+#include "ui/events/keycodes/dom/keycode_converter_data.inc" |
+#undef USB_KEYMAP |
+#undef USB_KEYMAP_DECLARATION |
+ |
+namespace { |
+ |
+const int kRepeatDelayMs = 500; |
+const int kRepeatIntervalMs = 50; |
+ |
+int EventFlagToEvdevModifier(int flag) { |
+ switch (flag) { |
+ case EF_CAPS_LOCK_DOWN: |
+ return EVDEV_MODIFIER_CAPS_LOCK; |
+ case EF_SHIFT_DOWN: |
+ return EVDEV_MODIFIER_SHIFT; |
+ case EF_CONTROL_DOWN: |
+ return EVDEV_MODIFIER_CONTROL; |
+ case EF_ALT_DOWN: |
+ return EVDEV_MODIFIER_ALT; |
+ case EF_ALTGR_DOWN: |
+ return EVDEV_MODIFIER_ALTGR; |
+ case EF_MOD3_DOWN: |
+ return EVDEV_MODIFIER_MOD3; |
+ case EF_LEFT_MOUSE_BUTTON: |
+ return EVDEV_MODIFIER_LEFT_MOUSE_BUTTON; |
+ case EF_MIDDLE_MOUSE_BUTTON: |
+ return EVDEV_MODIFIER_MIDDLE_MOUSE_BUTTON; |
+ case EF_RIGHT_MOUSE_BUTTON: |
+ return EVDEV_MODIFIER_RIGHT_MOUSE_BUTTON; |
+ case EF_BACK_MOUSE_BUTTON: |
+ return EVDEV_MODIFIER_BACK_MOUSE_BUTTON; |
+ case EF_FORWARD_MOUSE_BUTTON: |
+ return EVDEV_MODIFIER_FORWARD_MOUSE_BUTTON; |
+ case EF_COMMAND_DOWN: |
+ return EVDEV_MODIFIER_COMMAND; |
+ default: |
+ return EVDEV_MODIFIER_NONE; |
+ } |
+} |
+ |
+} // namespace |
+ |
+KeyboardEvdev::KeyboardEvdev(EventModifiersEvdev* modifiers, |
+ KeyboardLayoutEngine* keyboard_layout_engine, |
+ const EventDispatchCallback& callback) |
+ : callback_(callback), |
+ modifiers_(modifiers), |
+ keyboard_layout_engine_(keyboard_layout_engine), |
+ weak_ptr_factory_(this) { |
+ repeat_delay_ = base::TimeDelta::FromMilliseconds(kRepeatDelayMs); |
+ repeat_interval_ = base::TimeDelta::FromMilliseconds(kRepeatIntervalMs); |
+} |
+ |
+KeyboardEvdev::~KeyboardEvdev() { |
+} |
+ |
+void KeyboardEvdev::OnKeyChange(unsigned int key, |
+ bool down, |
+ bool suppress_auto_repeat, |
+ base::TimeDelta timestamp, |
+ int device_id) { |
+ if (key > KEY_MAX) |
+ return; |
+ |
+ bool was_down = key_state_.test(key); |
+ bool is_repeat = down && was_down; |
+ if (!down && !was_down) |
+ return; // Key already released. |
+ |
+ key_state_.set(key, down); |
+ UpdateKeyRepeat(key, down, suppress_auto_repeat, device_id); |
+ DispatchKey(key, down, is_repeat, timestamp, device_id); |
+} |
+ |
+void KeyboardEvdev::SetCapsLockEnabled(bool enabled) { |
+ modifiers_->SetModifierLock(EVDEV_MODIFIER_CAPS_LOCK, enabled); |
+} |
+ |
+bool KeyboardEvdev::IsCapsLockEnabled() { |
+ return (modifiers_->GetModifierFlags() & EF_CAPS_LOCK_DOWN) != 0; |
+} |
+ |
+bool KeyboardEvdev::IsAutoRepeatEnabled() { |
+ return auto_repeat_enabled_; |
+} |
+ |
+void KeyboardEvdev::SetAutoRepeatEnabled(bool enabled) { |
+ auto_repeat_enabled_ = enabled; |
+} |
+ |
+void KeyboardEvdev::SetAutoRepeatRate(const base::TimeDelta& delay, |
+ const base::TimeDelta& interval) { |
+ repeat_delay_ = delay; |
+ repeat_interval_ = interval; |
+} |
+ |
+void KeyboardEvdev::GetAutoRepeatRate(base::TimeDelta* delay, |
+ base::TimeDelta* interval) { |
+ *delay = repeat_delay_; |
+ *interval = repeat_interval_; |
+} |
+ |
+void KeyboardEvdev::UpdateModifier(int modifier_flag, bool down) { |
+ if (modifier_flag == EF_NONE) |
+ return; |
+ |
+ int modifier = EventFlagToEvdevModifier(modifier_flag); |
+ if (modifier == EVDEV_MODIFIER_NONE) |
+ return; |
+ |
+ // TODO post-X11: Revise remapping to not use EF_MOD3_DOWN. |
+ // Currently EF_MOD3_DOWN means that the CapsLock key is currently down, |
+ // and EF_CAPS_LOCK_DOWN means the caps lock state is enabled (and the |
+ // key may or may not be down, but usually isn't). There does need to |
+ // to be two different flags, since the physical CapsLock key is subject |
+ // to remapping, but the caps lock state (which can be triggered in a |
+ // variety of ways) is not. |
+ if (modifier == EVDEV_MODIFIER_CAPS_LOCK) |
+ modifiers_->UpdateModifier(EVDEV_MODIFIER_MOD3, down); |
+ else |
+ modifiers_->UpdateModifier(modifier, down); |
+} |
+ |
+void KeyboardEvdev::UpdateKeyRepeat(unsigned int key, |
+ bool down, |
+ bool suppress_auto_repeat, |
+ int device_id) { |
+ if (!auto_repeat_enabled_ || suppress_auto_repeat) |
+ StopKeyRepeat(); |
+ else if (key != repeat_key_ && down) |
+ StartKeyRepeat(key, device_id); |
+ else if (key == repeat_key_ && !down) |
+ StopKeyRepeat(); |
+} |
+ |
+void KeyboardEvdev::StartKeyRepeat(unsigned int key, int device_id) { |
+ repeat_key_ = key; |
+ repeat_device_id_ = device_id; |
+ repeat_sequence_++; |
+ |
+ ScheduleKeyRepeat(repeat_delay_); |
+} |
+ |
+void KeyboardEvdev::StopKeyRepeat() { |
+ repeat_key_ = KEY_RESERVED; |
+ repeat_sequence_++; |
+} |
+ |
+void KeyboardEvdev::ScheduleKeyRepeat(const base::TimeDelta& delay) { |
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
+ FROM_HERE, base::Bind(&KeyboardEvdev::OnRepeatTimeout, |
+ weak_ptr_factory_.GetWeakPtr(), repeat_sequence_), |
+ delay); |
+} |
+ |
+void KeyboardEvdev::OnRepeatTimeout(unsigned int sequence) { |
+ if (repeat_sequence_ != sequence) |
+ return; |
+ |
+ // Post a task behind any pending key releases in the message loop |
+ // FIFO. This ensures there's no spurious repeats during periods of UI |
+ // thread jank. |
+ base::ThreadTaskRunnerHandle::Get()->PostTask( |
+ FROM_HERE, base::Bind(&KeyboardEvdev::OnRepeatCommit, |
+ weak_ptr_factory_.GetWeakPtr(), repeat_sequence_)); |
+} |
+ |
+void KeyboardEvdev::OnRepeatCommit(unsigned int sequence) { |
+ if (repeat_sequence_ != sequence) |
+ return; |
+ |
+ DispatchKey(repeat_key_, true /* down */, true /* repeat */, |
+ EventTimeForNow(), repeat_device_id_); |
+ |
+ ScheduleKeyRepeat(repeat_interval_); |
+} |
+ |
+void KeyboardEvdev::DispatchKey(unsigned int key, |
+ bool down, |
+ bool repeat, |
+ base::TimeDelta timestamp, |
+ int device_id) { |
+ DomCode dom_code = |
+ KeycodeConverter::NativeKeycodeToDomCode(EvdevCodeToNativeCode(key)); |
+ if (dom_code == DomCode::DOM_CODE_NONE) |
+ return; |
+ int flags = modifiers_->GetModifierFlags(); |
+ DomKey dom_key; |
+ KeyboardCode key_code; |
+ uint16 character; |
+ uint32 platform_keycode = 0; |
+ if (!keyboard_layout_engine_->Lookup(dom_code, flags, &dom_key, &character, |
+ &key_code, &platform_keycode)) { |
+ return; |
+ } |
+ if (!repeat) { |
+ int flag = ModifierDomKeyToEventFlag(dom_key); |
+ UpdateModifier(flag, down); |
+ // X11 XKB, using the configuration as modified for ChromeOS, always sets |
+ // EF_MOD3_DOWN for the physical CapsLock key, even if the layout maps |
+ // it to something else, so we imitate this to make certain layouts (e.g. |
+ // German Neo2) work. crbug.com/495277 |
+ if (dom_code == DomCode::DOM_CODE_CAPS_LOCK) |
+ UpdateModifier(EF_MOD3_DOWN, down); |
+ } |
+ |
+ KeyEvent event(down ? ET_KEY_PRESSED : ET_KEY_RELEASED, key_code, dom_code, |
+ modifiers_->GetModifierFlags(), dom_key, character, timestamp); |
+ event.set_source_device_id(device_id); |
+ if (platform_keycode) |
+ event.set_platform_keycode(platform_keycode); |
+ callback_.Run(&event); |
+} |
+} // namespace ui |