diff options
author | reveman <reveman@chromium.org> | 2015-12-09 14:50:02 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-12-09 22:50:58 +0000 |
commit | 5cacf70cc85a4c73d001845c9d50be56e8799c5e (patch) | |
tree | e219f945a8a6f8b125c536b65e7aa20804a4aab6 | |
parent | 22cecce9f8fba6f5e369901196b33bcc8dc604bd (diff) | |
download | chromium_src-5cacf70cc85a4c73d001845c9d50be56e8799c5e.zip chromium_src-5cacf70cc85a4c73d001845c9d50be56e8799c5e.tar.gz chromium_src-5cacf70cc85a4c73d001845c9d50be56e8799c5e.tar.bz2 |
exo: Initial Wayland keyboard support.
This adds support for keyboard events to Exosphere and the
Wayland bindings.
BUG=549781
TEST=exo_unittest --gtest_filter=KeyboardTest.*
Review URL: https://codereview.chromium.org/1508013002
Cr-Commit-Position: refs/heads/master@{#364187}
-rw-r--r-- | components/exo.gypi | 17 | ||||
-rw-r--r-- | components/exo/BUILD.gn | 5 | ||||
-rw-r--r-- | components/exo/keyboard.cc | 119 | ||||
-rw-r--r-- | components/exo/keyboard.h | 65 | ||||
-rw-r--r-- | components/exo/keyboard_delegate.h | 54 | ||||
-rw-r--r-- | components/exo/keyboard_unittest.cc | 192 | ||||
-rw-r--r-- | components/exo/pointer.h | 2 | ||||
-rw-r--r-- | components/exo/wayland/BUILD.gn | 15 | ||||
-rw-r--r-- | components/exo/wayland/server.cc | 187 | ||||
-rw-r--r-- | ui/events/BUILD.gn | 1 | ||||
-rw-r--r-- | ui/events/keycodes/scoped_xkb.h (renamed from ui/events/ozone/layout/xkb/scoped_xkb.h) | 6 | ||||
-rw-r--r-- | ui/events/ozone/BUILD.gn | 1 | ||||
-rw-r--r-- | ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h | 2 |
13 files changed, 656 insertions, 10 deletions
diff --git a/components/exo.gypi b/components/exo.gypi index dbc92db..3b96f44 100644 --- a/components/exo.gypi +++ b/components/exo.gypi @@ -30,6 +30,9 @@ 'exo/buffer.h', 'exo/display.cc', 'exo/display.h', + 'exo/keyboard.cc', + 'exo/keyboard.h', + 'exo/keyboard_delegate.h', 'exo/pointer.cc', 'exo/pointer.h', 'exo/pointer_delegate.h', @@ -57,10 +60,12 @@ '..', ], 'dependencies': [ - '../base/base.gyp:base', + '../base/base.gyp:base', '../skia/skia.gyp:skia', - '../third_party/wayland/wayland.gyp:wayland_server', '../third_party/wayland-protocols/wayland-protocols.gyp:xdg_shell_protocol', + '../third_party/wayland/wayland.gyp:wayland_server', + '../ui/events/events.gyp:dom_keycode_converter', + '../ui/events/events.gyp:events_base', 'exo', ], 'sources': [ @@ -76,6 +81,14 @@ '../third_party/mesa/mesa.gyp:wayland_drm_protocol', ], }], + ['use_xkbcommon==1', { + 'dependencies': [ + '../build/linux/system.gyp:xkbcommon', + ], + 'defines': [ + 'USE_XKBCOMMON', + ], + }], ], }, ], diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn index 8cd2dc0..bfeebf1 100644 --- a/components/exo/BUILD.gn +++ b/components/exo/BUILD.gn @@ -11,6 +11,9 @@ source_set("exo") { "buffer.h", "display.cc", "display.h", + "keyboard.cc", + "keyboard.h", + "keyboard_delegate.h", "pointer.cc", "pointer.h", "pointer_delegate.h", @@ -70,6 +73,7 @@ source_set("unit_tests") { sources = [ "buffer_unittest.cc", "display_unittest.cc", + "keyboard_unittest.cc", "pointer_unittest.cc", "shared_memory_unittest.cc", "shell_surface_unittest.cc", @@ -89,6 +93,7 @@ source_set("unit_tests") { "//testing/gmock", "//testing/gtest", "//ui/aura", + "//ui/events:dom_keycode_converter", "//ui/events:test_support", "//ui/gfx", "//ui/keyboard", diff --git a/components/exo/keyboard.cc b/components/exo/keyboard.cc new file mode 100644 index 0000000..4a7c5c3 --- /dev/null +++ b/components/exo/keyboard.cc @@ -0,0 +1,119 @@ +// Copyright 2015 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 "components/exo/keyboard.h" + +#include "ash/shell.h" +#include "components/exo/keyboard_delegate.h" +#include "components/exo/surface.h" +#include "ui/aura/client/focus_client.h" +#include "ui/aura/window.h" +#include "ui/events/event.h" + +namespace exo { + +//////////////////////////////////////////////////////////////////////////////// +// Keyboard, public: + +Keyboard::Keyboard(KeyboardDelegate* delegate) + : delegate_(delegate), focus_(nullptr), modifier_flags_(0) { + ash::Shell::GetInstance()->AddPreTargetHandler(this); + aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()) + ->AddObserver(this); +} + +Keyboard::~Keyboard() { + delegate_->OnKeyboardDestroying(this); + if (focus_) + focus_->RemoveSurfaceObserver(this); + aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()) + ->RemoveObserver(this); + ash::Shell::GetInstance()->RemovePreTargetHandler(this); +} + +//////////////////////////////////////////////////////////////////////////////// +// ui::EventHandler overrides: + +void Keyboard::OnKeyEvent(ui::KeyEvent* event) { + const int kModifierMask = ui::EF_CAPS_LOCK_DOWN | ui::EF_SHIFT_DOWN | + ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | + ui::EF_COMMAND_DOWN | ui::EF_ALTGR_DOWN | + ui::EF_MOD3_DOWN | ui::EF_NUM_LOCK_DOWN; + int modifier_flags = event->flags() & kModifierMask; + if (modifier_flags != modifier_flags_) { + modifier_flags_ = modifier_flags; + if (focus_) + delegate_->OnKeyboardModifiers(modifier_flags_); + } + + switch (event->type()) { + case ui::ET_KEY_PRESSED: { + auto it = + std::find(pressed_keys_.begin(), pressed_keys_.end(), event->code()); + if (it == pressed_keys_.end()) { + if (focus_) + delegate_->OnKeyboardKey(event->time_stamp(), event->code(), true); + + pressed_keys_.push_back(event->code()); + } + } break; + case ui::ET_KEY_RELEASED: { + auto it = + std::find(pressed_keys_.begin(), pressed_keys_.end(), event->code()); + if (it != pressed_keys_.end()) { + if (focus_) + delegate_->OnKeyboardKey(event->time_stamp(), event->code(), false); + + pressed_keys_.erase(it); + } + } break; + default: + NOTREACHED(); + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// aura::client::FocusChangeObserver overrides: + +void Keyboard::OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) { + Surface* gained_focus_surface = + gained_focus ? GetEffectiveFocus(gained_focus) : nullptr; + if (gained_focus_surface != focus_) { + if (focus_) { + delegate_->OnKeyboardLeave(focus_); + focus_->RemoveSurfaceObserver(this); + focus_ = nullptr; + } + if (gained_focus_surface) { + delegate_->OnKeyboardModifiers(modifier_flags_); + delegate_->OnKeyboardEnter(gained_focus_surface, pressed_keys_); + focus_ = gained_focus_surface; + focus_->AddSurfaceObserver(this); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// SurfaceObserver overrides: + +void Keyboard::OnSurfaceDestroying(Surface* surface) { + DCHECK(surface == focus_); + focus_ = nullptr; + surface->RemoveSurfaceObserver(this); +} + +//////////////////////////////////////////////////////////////////////////////// +// Keyboard, private: + +Surface* Keyboard::GetEffectiveFocus(aura::Window* window) const { + Surface* focus = Surface::AsSurface(window); + if (!focus) + return nullptr; + + return delegate_->CanAcceptKeyboardEventsForSurface(focus) ? focus : nullptr; +} + +} // namespace exo diff --git a/components/exo/keyboard.h b/components/exo/keyboard.h new file mode 100644 index 0000000..51d3084 --- /dev/null +++ b/components/exo/keyboard.h @@ -0,0 +1,65 @@ +// Copyright 2015 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. + +#ifndef COMPONENTS_EXO_KEYBOARD_H_ +#define COMPONENTS_EXO_KEYBOARD_H_ + +#include <vector> + +#include "base/macros.h" +#include "components/exo/surface_observer.h" +#include "ui/aura/client/focus_change_observer.h" +#include "ui/events/event_handler.h" + +namespace ui { +enum class DomCode; +class Event; +class KeyEvent; +} + +namespace exo { +class KeyboardDelegate; +class Surface; + +// This class implements a client keyboard that represents one or more keyboard +// devices. +class Keyboard : public ui::EventHandler, + public aura::client::FocusChangeObserver, + public SurfaceObserver { + public: + explicit Keyboard(KeyboardDelegate* delegate); + ~Keyboard() override; + + // Overridden from ui::EventHandler: + void OnKeyEvent(ui::KeyEvent* event) override; + + // Overridden aura::client::FocusChangeObserver: + void OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) override; + + // Overridden from SurfaceObserver: + void OnSurfaceDestroying(Surface* surface) override; + + private: + // Returns the effective focus for |window|. + Surface* GetEffectiveFocus(aura::Window* window) const; + + // The delegate instance that all events are dispatched to. + KeyboardDelegate* delegate_; + + // The current focus surface for the keyboard. + Surface* focus_; + + // Vector of currently pressed keys. + std::vector<ui::DomCode> pressed_keys_; + + // Current set of modifier flags. + int modifier_flags_; + + DISALLOW_COPY_AND_ASSIGN(Keyboard); +}; + +} // namespace exo + +#endif // COMPONENTS_EXO_KEYBOARD_H_ diff --git a/components/exo/keyboard_delegate.h b/components/exo/keyboard_delegate.h new file mode 100644 index 0000000..e68d429 --- /dev/null +++ b/components/exo/keyboard_delegate.h @@ -0,0 +1,54 @@ +// Copyright 2015 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. + +#ifndef COMPONENTS_EXO_KEYBOARD_DELEGATE_H_ +#define COMPONENTS_EXO_KEYBOARD_DELEGATE_H_ + +#include <vector> + +#include "base/time/time.h" + +namespace ui { +enum class DomCode; +} + +namespace exo { +class Keyboard; +class Surface; + +// Handles events on keyboards in context-specific ways. +class KeyboardDelegate { + public: + // Called at the top of the keyboard's destructor, to give observers a + // chance to remove themselves. + virtual void OnKeyboardDestroying(Keyboard* keyboard) = 0; + + // This should return true if |surface| is a valid target for this keyboard. + // E.g. the surface is owned by the same client as the keyboard. + virtual bool CanAcceptKeyboardEventsForSurface(Surface* surface) const = 0; + + // Called when keyboard focus enters a new valid target surface. + virtual void OnKeyboardEnter( + Surface* surface, + const std::vector<ui::DomCode>& pressed_keys) = 0; + + // Called when keyboard focus leaves a valid target surface. + virtual void OnKeyboardLeave(Surface* surface) = 0; + + // Called when pkeyboard key state changed. |pressed| is true when |key| + // was pressed and false if it was released. + virtual void OnKeyboardKey(base::TimeDelta time_stamp, + ui::DomCode key, + bool pressed) = 0; + + // Called when keyboard modifier state changed. + virtual void OnKeyboardModifiers(int modifier_flags) = 0; + + protected: + virtual ~KeyboardDelegate() {} +}; + +} // namespace exo + +#endif // COMPONENTS_EXO_KEYBOARD_DELEGATE_H_ diff --git a/components/exo/keyboard_unittest.cc b/components/exo/keyboard_unittest.cc new file mode 100644 index 0000000..32427f7 --- /dev/null +++ b/components/exo/keyboard_unittest.cc @@ -0,0 +1,192 @@ +// Copyright 2015 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 "ash/shell.h" +#include "components/exo/buffer.h" +#include "components/exo/keyboard.h" +#include "components/exo/keyboard_delegate.h" +#include "components/exo/shell_surface.h" +#include "components/exo/surface.h" +#include "components/exo/test/exo_test_base.h" +#include "components/exo/test/exo_test_helper.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "third_party/khronos/GLES2/gl2.h" +#include "ui/aura/client/focus_client.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/test/event_generator.h" + +namespace exo { +namespace { + +using KeyboardTest = test::ExoTestBase; + +class MockKeyboardDelegate : public KeyboardDelegate { + public: + MockKeyboardDelegate() {} + + // Overridden from KeyboardDelegate: + MOCK_METHOD1(OnKeyboardDestroying, void(Keyboard*)); + MOCK_CONST_METHOD1(CanAcceptKeyboardEventsForSurface, bool(Surface*)); + MOCK_METHOD2(OnKeyboardEnter, + void(Surface*, const std::vector<ui::DomCode>&)); + MOCK_METHOD1(OnKeyboardLeave, void(Surface*)); + MOCK_METHOD3(OnKeyboardKey, void(base::TimeDelta, ui::DomCode, bool)); + MOCK_METHOD1(OnKeyboardModifiers, void(int)); +}; + +TEST_F(KeyboardTest, OnKeyboardEnter) { + scoped_ptr<Surface> surface(new Surface); + scoped_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get())); + shell_surface->SetToplevel(); + gfx::Size buffer_size(10, 10); + scoped_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size).Pass(), + GL_TEXTURE_2D)); + surface->Attach(buffer.get()); + surface->Commit(); + + MockKeyboardDelegate delegate; + scoped_ptr<Keyboard> keyboard(new Keyboard(&delegate)); + + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); + generator.PressKey(ui::VKEY_A, 0); + + aura::client::FocusClient* focus_client = + aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); + EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get())) + .WillOnce(testing::Return(false)); + focus_client->FocusWindow(surface.get()); + + EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get())) + .WillOnce(testing::Return(true)); + ui::DomCode expected_pressed_keys[] = {ui::DomCode::KEY_A}; + EXPECT_CALL(delegate, OnKeyboardModifiers(0)); + EXPECT_CALL(delegate, + OnKeyboardEnter(surface.get(), + std::vector<ui::DomCode>( + expected_pressed_keys, + expected_pressed_keys + + arraysize(expected_pressed_keys)))); + focus_client->FocusWindow(nullptr); + focus_client->FocusWindow(surface.get()); + + EXPECT_CALL(delegate, OnKeyboardDestroying(keyboard.get())); + keyboard.reset(); +} + +TEST_F(KeyboardTest, OnKeyboardLeave) { + scoped_ptr<Surface> surface(new Surface); + scoped_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get())); + shell_surface->SetToplevel(); + gfx::Size buffer_size(10, 10); + scoped_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size).Pass(), + GL_TEXTURE_2D)); + surface->Attach(buffer.get()); + surface->Commit(); + + MockKeyboardDelegate delegate; + scoped_ptr<Keyboard> keyboard(new Keyboard(&delegate)); + + aura::client::FocusClient* focus_client = + aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); + EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get())) + .WillOnce(testing::Return(true)); + EXPECT_CALL(delegate, OnKeyboardModifiers(0)); + EXPECT_CALL(delegate, + OnKeyboardEnter(surface.get(), std::vector<ui::DomCode>())); + focus_client->FocusWindow(surface.get()); + + EXPECT_CALL(delegate, OnKeyboardLeave(surface.get())); + focus_client->FocusWindow(nullptr); + + EXPECT_CALL(delegate, OnKeyboardDestroying(keyboard.get())); + keyboard.reset(); +} + +TEST_F(KeyboardTest, OnKeyboardKey) { + scoped_ptr<Surface> surface(new Surface); + scoped_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get())); + shell_surface->SetToplevel(); + gfx::Size buffer_size(10, 10); + scoped_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size).Pass(), + GL_TEXTURE_2D)); + surface->Attach(buffer.get()); + surface->Commit(); + + MockKeyboardDelegate delegate; + scoped_ptr<Keyboard> keyboard(new Keyboard(&delegate)); + + aura::client::FocusClient* focus_client = + aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); + EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get())) + .WillOnce(testing::Return(true)); + EXPECT_CALL(delegate, OnKeyboardModifiers(0)); + EXPECT_CALL(delegate, + OnKeyboardEnter(surface.get(), std::vector<ui::DomCode>())); + focus_client->FocusWindow(surface.get()); + + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); + // This should only generate one press event for KEY_A. + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::KEY_A, true)); + generator.PressKey(ui::VKEY_A, 0); + generator.PressKey(ui::VKEY_A, 0); + generator.ReleaseKey(ui::VKEY_B, 0); + + // This should only generate one release event for KEY_A. + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::KEY_A, false)); + generator.ReleaseKey(ui::VKEY_A, 0); + generator.ReleaseKey(ui::VKEY_A, 0); + + EXPECT_CALL(delegate, OnKeyboardDestroying(keyboard.get())); + keyboard.reset(); +} + +TEST_F(KeyboardTest, OnKeyboardModifiers) { + scoped_ptr<Surface> surface(new Surface); + scoped_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get())); + shell_surface->SetToplevel(); + gfx::Size buffer_size(10, 10); + scoped_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size).Pass(), + GL_TEXTURE_2D)); + surface->Attach(buffer.get()); + surface->Commit(); + + MockKeyboardDelegate delegate; + scoped_ptr<Keyboard> keyboard(new Keyboard(&delegate)); + + aura::client::FocusClient* focus_client = + aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); + EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get())) + .WillOnce(testing::Return(true)); + EXPECT_CALL(delegate, OnKeyboardModifiers(0)); + EXPECT_CALL(delegate, + OnKeyboardEnter(surface.get(), std::vector<ui::DomCode>())); + focus_client->FocusWindow(surface.get()); + + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); + // This should generate a modifier event. + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::KEY_A, true)); + EXPECT_CALL(delegate, OnKeyboardModifiers(ui::EF_SHIFT_DOWN)); + generator.PressKey(ui::VKEY_A, ui::EF_SHIFT_DOWN); + + // This should generate another modifier event. + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::KEY_B, true)); + EXPECT_CALL(delegate, + OnKeyboardModifiers(ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN)); + generator.PressKey(ui::VKEY_B, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN); + + // This should generate a third modifier event. + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::KEY_B, false)); + EXPECT_CALL(delegate, OnKeyboardModifiers(0)); + generator.ReleaseKey(ui::VKEY_B, 0); + + EXPECT_CALL(delegate, OnKeyboardDestroying(keyboard.get())); + keyboard.reset(); +} + +} // namespace +} // namespace exo diff --git a/components/exo/pointer.h b/components/exo/pointer.h index 1ba2af9..13aa47f 100644 --- a/components/exo/pointer.h +++ b/components/exo/pointer.h @@ -20,7 +20,7 @@ namespace exo { class PointerDelegate; class Surface; -// This class implementes a client pointer that represents one or more input +// This class implements a client pointer that represents one or more input // devices, such as mice, which control the pointer location and pointer focus. class Pointer : public ui::EventHandler, public SurfaceObserver { public: diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn index 1d07dd5..4b18408 100644 --- a/components/exo/wayland/BUILD.gn +++ b/components/exo/wayland/BUILD.gn @@ -2,8 +2,15 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/linux/pkg_config.gni") import("//build/config/ui.gni") +if (use_xkbcommon) { + pkg_config("xkbcommon") { + packages = [ "xkbcommon" ] + } +} + source_set("wayland") { sources = [ "scoped_wl.cc", @@ -20,11 +27,19 @@ source_set("wayland") { "//skia", "//third_party/wayland:wayland_server", "//third_party/wayland-protocols:xdg_shell_protocol", + "//ui/events:dom_keycode_converter", + "//ui/events:events_base", ] if (use_ozone) { deps += [ "//third_party/mesa:wayland_drm_protocol" ] } + + if (use_xkbcommon) { + configs += [ ":xkbcommon" ] + + defines += [ "USE_XKBCOMMON" ] + } } source_set("unit_tests") { diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc index abd703b..e0a1f72 100644 --- a/components/exo/wayland/server.cc +++ b/components/exo/wayland/server.cc @@ -16,6 +16,8 @@ #include "base/strings/utf_string_conversions.h" #include "components/exo/buffer.h" #include "components/exo/display.h" +#include "components/exo/keyboard.h" +#include "components/exo/keyboard_delegate.h" #include "components/exo/pointer.h" #include "components/exo/pointer_delegate.h" #include "components/exo/shared_memory.h" @@ -24,11 +26,17 @@ #include "components/exo/surface.h" #include "third_party/skia/include/core/SkRegion.h" #include "ui/aura/window_property.h" +#include "ui/events/keycodes/dom/keycode_converter.h" #if defined(USE_OZONE) #include <wayland-drm-server-protocol.h> #endif +#if defined(USE_XKBCOMMON) +#include <xkbcommon/xkbcommon.h> +#include "ui/events/keycodes/scoped_xkb.h" +#endif + DECLARE_WINDOW_PROPERTY_TYPE(wl_resource*); namespace exo { @@ -382,10 +390,11 @@ void bind_shm(wl_client* client, void* data, uint32_t version, uint32_t id) { wl_shm_send_format(resource, supported_format.shm_format); } +#if defined(USE_OZONE) + //////////////////////////////////////////////////////////////////////////////// // wl_drm_interface: -#if defined(USE_OZONE) const struct drm_supported_format { uint32_t drm_format; gfx::BufferFormat buffer_format; @@ -1081,6 +1090,158 @@ void pointer_release(wl_client* client, wl_resource* resource) { const struct wl_pointer_interface pointer_implementation = {pointer_set_cursor, pointer_release}; +#if defined(USE_XKBCOMMON) + +//////////////////////////////////////////////////////////////////////////////// +// wl_keyboard_interface: + +// Keyboard delegate class that accepts events for surfaces owned by the same +// client as a keyboard resource. +class WaylandKeyboardDelegate : public KeyboardDelegate { + public: + explicit WaylandKeyboardDelegate(wl_resource* keyboard_resource) + : keyboard_resource_(keyboard_resource), + xkb_context_(xkb_context_new(XKB_CONTEXT_NO_FLAGS)), + // TODO(reveman): Keep keymap synchronized with the keymap used by + // chromium and the host OS. + xkb_keymap_(xkb_keymap_new_from_names(xkb_context_.get(), + nullptr, + XKB_KEYMAP_COMPILE_NO_FLAGS)), + xkb_state_(xkb_state_new(xkb_keymap_.get())) { + scoped_ptr<char, base::FreeDeleter> keymap_string( + xkb_keymap_get_as_string(xkb_keymap_.get(), XKB_KEYMAP_FORMAT_TEXT_V1)); + DCHECK(keymap_string.get()); + size_t keymap_size = strlen(keymap_string.get()) + 1; + base::SharedMemory shared_keymap; + bool rv = shared_keymap.CreateAndMapAnonymous(keymap_size); + DCHECK(rv); + memcpy(shared_keymap.memory(), keymap_string.get(), keymap_size); + wl_keyboard_send_keymap(keyboard_resource_, + WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, + shared_keymap.handle().fd, keymap_size); + } + + // Overridden from KeyboardDelegate: + void OnKeyboardDestroying(Keyboard* keyboard) override { delete this; } + bool CanAcceptKeyboardEventsForSurface(Surface* surface) const override { + wl_resource* surface_resource = surface->GetProperty(kSurfaceResourceKey); + // We can accept events for this surface if the client is the same as the + // keyboard. + return surface_resource && + wl_resource_get_client(surface_resource) == client(); + } + void OnKeyboardEnter(Surface* surface, + const std::vector<ui::DomCode>& pressed_keys) override { + wl_resource* surface_resource = surface->GetProperty(kSurfaceResourceKey); + DCHECK(surface_resource); + wl_array keys; + wl_array_init(&keys); + for (auto key : pressed_keys) { + uint32_t* value = + static_cast<uint32_t*>(wl_array_add(&keys, sizeof(uint32_t))); + DCHECK(value); + *value = DomCodeToKey(key); + } + wl_keyboard_send_enter(keyboard_resource_, next_serial(), surface_resource, + &keys); + wl_array_release(&keys); + wl_client_flush(client()); + } + void OnKeyboardLeave(Surface* surface) override { + wl_resource* surface_resource = surface->GetProperty(kSurfaceResourceKey); + DCHECK(surface_resource); + wl_keyboard_send_leave(keyboard_resource_, next_serial(), surface_resource); + wl_client_flush(client()); + } + void OnKeyboardKey(base::TimeDelta time_stamp, + ui::DomCode key, + bool pressed) override { + wl_keyboard_send_key(keyboard_resource_, next_serial(), + time_stamp.InMilliseconds(), DomCodeToKey(key), + pressed ? WL_KEYBOARD_KEY_STATE_PRESSED + : WL_KEYBOARD_KEY_STATE_RELEASED); + wl_client_flush(client()); + } + void OnKeyboardModifiers(int modifier_flags) override { + xkb_state_update_mask(xkb_state_.get(), + ModifierFlagsToXkbModifiers(modifier_flags), 0, 0, 0, + 0, 0); + wl_keyboard_send_modifiers( + keyboard_resource_, next_serial(), + xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_DEPRESSED), + xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_LOCKED), + xkb_state_serialize_mods(xkb_state_.get(), XKB_STATE_MODS_LATCHED), + xkb_state_serialize_layout(xkb_state_.get(), + XKB_STATE_LAYOUT_EFFECTIVE)); + wl_client_flush(client()); + } + + private: + // Returns the corresponding key given a dom code. + uint32_t DomCodeToKey(ui::DomCode code) const { + // This assumes KeycodeConverter has been built with evdev/xkb codes. + xkb_keycode_t xkb_keycode = static_cast<xkb_keycode_t>( + ui::KeycodeConverter::DomCodeToNativeKeycode(code)); + + // Keycodes are offset by 8 in Xkb. + DCHECK_GE(xkb_keycode, 8u); + return xkb_keycode - 8; + } + + // Returns a set of Xkb modififers given a set of modifier flags. + uint32_t ModifierFlagsToXkbModifiers(int modifier_flags) { + struct { + ui::EventFlags flag; + const char* xkb_name; + } modifiers[] = { + {ui::EF_SHIFT_DOWN, XKB_MOD_NAME_SHIFT}, + {ui::EF_CAPS_LOCK_DOWN, XKB_MOD_NAME_CAPS}, + {ui::EF_CONTROL_DOWN, XKB_MOD_NAME_CTRL}, + {ui::EF_ALT_DOWN, XKB_MOD_NAME_ALT}, + {ui::EF_NUM_LOCK_DOWN, XKB_MOD_NAME_NUM}, + {ui::EF_MOD3_DOWN, "Mod3"}, + {ui::EF_COMMAND_DOWN, XKB_MOD_NAME_LOGO}, + {ui::EF_ALTGR_DOWN, "Mod5"}, + }; + uint32_t xkb_modifiers = 0; + for (auto modifier : modifiers) { + if (modifier_flags & modifier.flag) { + xkb_modifiers |= + 1 << xkb_keymap_mod_get_index(xkb_keymap_.get(), modifier.xkb_name); + } + } + return xkb_modifiers; + } + + // The client who own this keyboard instance. + wl_client* client() const { + return wl_resource_get_client(keyboard_resource_); + } + + // Returns the next serial to use for keyboard events. + uint32_t next_serial() const { + return wl_display_next_serial(wl_client_get_display(client())); + } + + // The keyboard resource associated with the keyboard. + wl_resource* const keyboard_resource_; + + // The Xkb state used for the keyboard. + scoped_ptr<xkb_context, ui::XkbContextDeleter> xkb_context_; + scoped_ptr<xkb_keymap, ui::XkbKeymapDeleter> xkb_keymap_; + scoped_ptr<xkb_state, ui::XkbStateDeleter> xkb_state_; + + DISALLOW_COPY_AND_ASSIGN(WaylandKeyboardDelegate); +}; + +void keyboard_release(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +const struct wl_keyboard_interface keyboard_implementation = {keyboard_release}; + +#endif + //////////////////////////////////////////////////////////////////////////////// // wl_seat_interface: @@ -1098,7 +1259,25 @@ void seat_get_pointer(wl_client* client, wl_resource* resource, uint32_t id) { } void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) { +#if defined(USE_XKBCOMMON) + uint32_t version = wl_resource_get_version(resource); + wl_resource* keyboard_resource = + wl_resource_create(client, &wl_keyboard_interface, version, id); + if (!keyboard_resource) { + wl_resource_post_no_memory(resource); + return; + } + + SetImplementation(keyboard_resource, &keyboard_implementation, + make_scoped_ptr(new Keyboard( + new WaylandKeyboardDelegate(keyboard_resource)))); + + // TODO(reveman): Keep repeat info synchronized with chromium and the host OS. + if (version >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) + wl_keyboard_send_repeat_info(keyboard_resource, 40, 500); +#else NOTIMPLEMENTED(); +#endif } void seat_get_touch(wl_client* client, wl_resource* resource, uint32_t id) { @@ -1123,7 +1302,11 @@ void bind_seat(wl_client* client, void* data, uint32_t version, uint32_t id) { if (version >= WL_SEAT_NAME_SINCE_VERSION) wl_seat_send_name(resource, "default"); - wl_seat_send_capabilities(resource, WL_SEAT_CAPABILITY_POINTER); + uint32_t capabilities = WL_SEAT_CAPABILITY_POINTER; +#if defined(USE_XKBCOMMON) + capabilities |= WL_SEAT_CAPABILITY_KEYBOARD; +#endif + wl_seat_send_capabilities(resource, capabilities); } } // namespace diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn index 4dc4ccf..a1a65ae 100644 --- a/ui/events/BUILD.gn +++ b/ui/events/BUILD.gn @@ -86,6 +86,7 @@ component("events_base") { sources += [ "keycodes/keyboard_code_conversion_xkb.cc", "keycodes/keyboard_code_conversion_xkb.h", + "keycodes/scoped_xkb.h", "keycodes/xkb_keysym.h", ] } diff --git a/ui/events/ozone/layout/xkb/scoped_xkb.h b/ui/events/keycodes/scoped_xkb.h index 66ec295..5eb701f 100644 --- a/ui/events/ozone/layout/xkb/scoped_xkb.h +++ b/ui/events/keycodes/scoped_xkb.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_EVENTS_OZONE_LAYOUT_XKB_SCOPED_XKB_H_ -#define UI_EVENTS_OZONE_LAYOUT_XKB_SCOPED_XKB_H_ +#ifndef UI_EVENTS_KEYCODES_SCOPED_XKB_H_ +#define UI_EVENTS_KEYCODES_SCOPED_XKB_H_ #include <xkbcommon/xkbcommon.h> @@ -25,4 +25,4 @@ struct XkbKeymapDeleter { } // namespace ui -#endif // UI_EVENTS_OZONE_LAYOUT_XKB_SCOPED_XKB_H_ +#endif // UI_EVENTS_KEYCODES_SCOPED_XKB_H_ diff --git a/ui/events/ozone/BUILD.gn b/ui/events/ozone/BUILD.gn index c30ba34..2bef777 100644 --- a/ui/events/ozone/BUILD.gn +++ b/ui/events/ozone/BUILD.gn @@ -201,7 +201,6 @@ component("events_ozone_layout") { configs += [ ":xkbcommon" ] sources += [ - "layout/xkb/scoped_xkb.h", "layout/xkb/xkb.h", "layout/xkb/xkb_evdev_codes.cc", "layout/xkb/xkb_evdev_codes.h", diff --git a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h index 6714e57..ea2d4de 100644 --- a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h +++ b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h @@ -14,9 +14,9 @@ #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" #include "base/task_runner.h" +#include "ui/events/keycodes/scoped_xkb.h" #include "ui/events/ozone/layout/events_ozone_layout_export.h" #include "ui/events/ozone/layout/keyboard_layout_engine.h" -#include "ui/events/ozone/layout/xkb/scoped_xkb.h" #include "ui/events/ozone/layout/xkb/xkb_key_code_converter.h" namespace ui { |