summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreveman <reveman@chromium.org>2015-12-09 14:50:02 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-09 22:50:58 +0000
commit5cacf70cc85a4c73d001845c9d50be56e8799c5e (patch)
treee219f945a8a6f8b125c536b65e7aa20804a4aab6
parent22cecce9f8fba6f5e369901196b33bcc8dc604bd (diff)
downloadchromium_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.gypi17
-rw-r--r--components/exo/BUILD.gn5
-rw-r--r--components/exo/keyboard.cc119
-rw-r--r--components/exo/keyboard.h65
-rw-r--r--components/exo/keyboard_delegate.h54
-rw-r--r--components/exo/keyboard_unittest.cc192
-rw-r--r--components/exo/pointer.h2
-rw-r--r--components/exo/wayland/BUILD.gn15
-rw-r--r--components/exo/wayland/server.cc187
-rw-r--r--ui/events/BUILD.gn1
-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.gn1
-rw-r--r--ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h2
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 {