Commit b8876671 authored by Erik Jensen's avatar Erik Jensen Committed by Commit Bot

remoting: Implement initial keyboard layout monitor for Windows.

This uses various Windows APIs to try to glean information about the
current layout. Unfortunately, there appears to be no way to get some of
the needed information in a clean fashion through these APIs, so the
implementation uses various guesses and hacks to try to infer it. In the
future, I'd like to look into reading the layout directly from the
layout files, which contain mapping tables that provide all of the
needed information in a known format.

This currently runs in the network process. That means it can't monitor
active window changes and thus never sends an updated layout after the
initial connection. A follow up CL will run the monitor in the desktop
process and communicate the result back via IPC.

Bug: 1026029
Change-Id: Iaba9c0bb0ef2c60fac249a9af392f9434b9c5f93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1918356
Commit-Queue: Erik Jensen <rkjnsn@chromium.org>
Reviewed-by: default avatarYuwei Huang <yuweih@chromium.org>
Reviewed-by: default avatarGary Kacmarcik <garykac@chromium.org>
Cr-Commit-Position: refs/heads/master@{#720819}
parent a603a371
......@@ -215,6 +215,13 @@ void ChromotingClient::SetCursorShape(
user_interface_->GetCursorShapeStub()->SetCursorShape(cursor_shape);
}
void ChromotingClient::SetKeyboardLayout(
const protocol::KeyboardLayout& layout) {
DCHECK(thread_checker_.CalledOnValidThread());
user_interface_->GetKeyboardLayoutStub()->SetKeyboardLayout(layout);
}
void ChromotingClient::OnConnectionState(
protocol::ConnectionToHost::State state,
protocol::ErrorCode error) {
......
......@@ -98,6 +98,9 @@ class ChromotingClient : public SignalStrategy::Listener,
// CursorShapeStub implementation for receiving cursor shape updates.
void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override;
// KeyboardLayoutStub implementation for sending keyboard layout to client.
void SetKeyboardLayout(const protocol::KeyboardLayout& layout) override;
// ConnectionToHost::HostEventCallback implementation.
void OnConnectionState(protocol::ConnectionToHost::State state,
protocol::ErrorCode error) override;
......
......@@ -95,7 +95,8 @@ struct SessionContext {
} // namespace
class ChromotingSession::Core : public ClientUserInterface,
public protocol::ClipboardStub {
public protocol::ClipboardStub,
public protocol::KeyboardLayoutStub {
public:
Core(ChromotingClientRuntime* runtime,
std::unique_ptr<ClientTelemetryLogger> logger,
......@@ -134,10 +135,14 @@ class ChromotingSession::Core : public ClientUserInterface,
const webrtc::DesktopVector& dpi) override;
protocol::ClipboardStub* GetClipboardStub() override;
protocol::CursorShapeStub* GetCursorShapeStub() override;
protocol::KeyboardLayoutStub* GetKeyboardLayoutStub() override;
// CursorShapeStub implementation.
// ClipboardStub implementation.
void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
// KeyboardLayoutStub implementation.
void SetKeyboardLayout(const protocol::KeyboardLayout& layout) override;
base::WeakPtr<Core> GetWeakPtr();
private:
......@@ -445,11 +450,20 @@ protocol::CursorShapeStub* ChromotingSession::Core::GetCursorShapeStub() {
return session_context_->cursor_shape_stub.get();
}
protocol::KeyboardLayoutStub* ChromotingSession::Core::GetKeyboardLayoutStub() {
return this;
}
void ChromotingSession::Core::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
NOTIMPLEMENTED();
}
void ChromotingSession::Core::SetKeyboardLayout(
const protocol::KeyboardLayout& layout) {
NOTIMPLEMENTED();
}
base::WeakPtr<ChromotingSession::Core> ChromotingSession::Core::GetWeakPtr() {
return weak_ptr_;
}
......
......@@ -22,6 +22,7 @@ namespace protocol {
class ClipboardStub;
class CursorShapeStub;
class ExtensionMessage;
class KeyboardLayoutStub;
class PairingResponse;
} // namespace protocol
......@@ -63,6 +64,9 @@ class ClientUserInterface {
// Get the view's CursorShapeStub implementation.
virtual protocol::CursorShapeStub* GetCursorShapeStub() = 0;
// Get the view's KeyboardLayoutStub implementation.
virtual protocol::KeyboardLayoutStub* GetKeyboardLayoutStub() = 0;
};
} // namespace remoting
......
......@@ -236,6 +236,11 @@ static_library("common") {
"ipc_video_frame_capturer.h",
"it2me_desktop_environment.cc",
"it2me_desktop_environment.h",
"keyboard_layout_monitor.cc",
"keyboard_layout_monitor.h",
"keyboard_layout_monitor_linux.cc",
"keyboard_layout_monitor_mac.cc",
"keyboard_layout_monitor_win.cc",
"me2me_desktop_environment.cc",
"me2me_desktop_environment.h",
"mouse_cursor_monitor_proxy.cc",
......
......@@ -25,6 +25,7 @@
#include "remoting/host/file_transfer/file_transfer_message_handler.h"
#include "remoting/host/host_extension_session.h"
#include "remoting/host/input_injector.h"
#include "remoting/host/keyboard_layout_monitor.h"
#include "remoting/host/mouse_shape_pump.h"
#include "remoting/host/screen_controls.h"
#include "remoting/host/screen_resolution.h"
......@@ -378,6 +379,12 @@ void ClientSession::OnConnectionChannelsConnected() {
new MouseShapePump(desktop_environment_->CreateMouseCursorMonitor(),
connection_->client_stub()));
// Create KeyboardLayoutMonitor to send keyboard layout.
keyboard_layout_monitor_ = KeyboardLayoutMonitor::Create(
base::BindRepeating(&protocol::KeyboardLayoutStub::SetKeyboardLayout,
base::Unretained(connection_->client_stub())));
keyboard_layout_monitor_->Start();
if (pending_video_layout_message_) {
connection_->client_stub()->SetVideoLayout(*pending_video_layout_message_);
pending_video_layout_message_.reset();
......@@ -406,6 +413,7 @@ void ClientSession::OnConnectionClosed(protocol::ErrorCode error) {
// longer valid once ConnectionToClient calls OnConnectionClosed().
audio_stream_.reset();
video_stream_.reset();
keyboard_layout_monitor_.reset();
mouse_shape_pump_.reset();
client_clipboard_factory_.InvalidateWeakPtrs();
input_injector_.reset();
......
......@@ -47,6 +47,7 @@ class AudioStream;
class DesktopEnvironment;
class DesktopEnvironmentFactory;
class InputInjector;
class KeyboardLayoutMonitor;
class MouseShapePump;
class ScreenControls;
......@@ -240,6 +241,7 @@ class ClientSession : public protocol::HostStub,
std::unique_ptr<protocol::VideoStream> video_stream_;
std::unique_ptr<protocol::AudioStream> audio_stream_;
std::unique_ptr<MouseShapePump> mouse_shape_pump_;
std::unique_ptr<KeyboardLayoutMonitor> keyboard_layout_monitor_;
// The set of all capabilities supported by the client.
std::unique_ptr<std::string> client_capabilities_;
......
// Copyright 2019 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 "remoting/host/keyboard_layout_monitor.h"
#include "base/containers/span.h"
#include "ui/events/keycodes/dom/dom_code.h"
namespace remoting {
namespace {
const ui::DomCode kSupportedKeysArray[] = {
ui::DomCode::ALT_LEFT,
ui::DomCode::ALT_RIGHT,
ui::DomCode::ARROW_DOWN,
ui::DomCode::ARROW_RIGHT,
ui::DomCode::ARROW_LEFT,
ui::DomCode::ARROW_UP,
ui::DomCode::BACKQUOTE,
ui::DomCode::BACKSLASH,
ui::DomCode::BACKSPACE,
ui::DomCode::BRACKET_LEFT,
ui::DomCode::BRACKET_RIGHT,
ui::DomCode::CAPS_LOCK,
ui::DomCode::COMMA,
ui::DomCode::CONTEXT_MENU,
ui::DomCode::CONTROL_LEFT,
ui::DomCode::CONTROL_RIGHT,
ui::DomCode::CONVERT,
ui::DomCode::DEL,
ui::DomCode::DIGIT0,
ui::DomCode::DIGIT1,
ui::DomCode::DIGIT2,
ui::DomCode::DIGIT3,
ui::DomCode::DIGIT4,
ui::DomCode::DIGIT5,
ui::DomCode::DIGIT6,
ui::DomCode::DIGIT7,
ui::DomCode::DIGIT8,
ui::DomCode::DIGIT9,
ui::DomCode::END,
ui::DomCode::ENTER,
ui::DomCode::EQUAL,
ui::DomCode::ESCAPE,
ui::DomCode::F1,
ui::DomCode::F2,
ui::DomCode::F3,
ui::DomCode::F4,
ui::DomCode::F5,
ui::DomCode::F6,
ui::DomCode::F7,
ui::DomCode::F8,
ui::DomCode::F9,
ui::DomCode::F10,
ui::DomCode::F11,
ui::DomCode::F12,
ui::DomCode::HOME,
ui::DomCode::INSERT,
ui::DomCode::INTL_BACKSLASH,
ui::DomCode::INTL_RO,
ui::DomCode::INTL_YEN,
ui::DomCode::KANA_MODE,
ui::DomCode::LANG1,
ui::DomCode::LANG2,
ui::DomCode::META_LEFT,
ui::DomCode::META_RIGHT,
ui::DomCode::MINUS,
ui::DomCode::NON_CONVERT,
ui::DomCode::NUM_LOCK,
ui::DomCode::NUMPAD0,
ui::DomCode::NUMPAD1,
ui::DomCode::NUMPAD2,
ui::DomCode::NUMPAD3,
ui::DomCode::NUMPAD4,
ui::DomCode::NUMPAD5,
ui::DomCode::NUMPAD6,
ui::DomCode::NUMPAD7,
ui::DomCode::NUMPAD8,
ui::DomCode::NUMPAD9,
ui::DomCode::NUMPAD_ADD,
ui::DomCode::NUMPAD_COMMA,
ui::DomCode::NUMPAD_DECIMAL,
ui::DomCode::NUMPAD_DIVIDE,
ui::DomCode::NUMPAD_ENTER,
ui::DomCode::NUMPAD_EQUAL,
ui::DomCode::NUMPAD_MULTIPLY,
ui::DomCode::NUMPAD_SUBTRACT,
ui::DomCode::PAGE_DOWN,
ui::DomCode::PAGE_UP,
ui::DomCode::PAUSE,
ui::DomCode::PERIOD,
ui::DomCode::PRINT_SCREEN,
ui::DomCode::QUOTE,
ui::DomCode::SCROLL_LOCK,
ui::DomCode::SEMICOLON,
ui::DomCode::SHIFT_LEFT,
ui::DomCode::SHIFT_RIGHT,
ui::DomCode::SLASH,
ui::DomCode::SPACE,
ui::DomCode::TAB,
ui::DomCode::US_A,
ui::DomCode::US_B,
ui::DomCode::US_C,
ui::DomCode::US_D,
ui::DomCode::US_E,
ui::DomCode::US_F,
ui::DomCode::US_G,
ui::DomCode::US_H,
ui::DomCode::US_I,
ui::DomCode::US_J,
ui::DomCode::US_K,
ui::DomCode::US_L,
ui::DomCode::US_M,
ui::DomCode::US_N,
ui::DomCode::US_O,
ui::DomCode::US_P,
ui::DomCode::US_Q,
ui::DomCode::US_R,
ui::DomCode::US_S,
ui::DomCode::US_T,
ui::DomCode::US_U,
ui::DomCode::US_V,
ui::DomCode::US_W,
ui::DomCode::US_X,
ui::DomCode::US_Y,
ui::DomCode::US_Z,
};
} // namespace
// static
const base::span<const ui::DomCode> KeyboardLayoutMonitor::kSupportedKeys(
kSupportedKeysArray);
} // namespace remoting
// Copyright 2019 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 REMOTING_HOST_KEYBOARD_LAYOUT_MONITOR_H_
#define REMOTING_HOST_KEYBOARD_LAYOUT_MONITOR_H_
#include <memory>
#include "base/callback_forward.h"
#include "base/containers/span.h"
#include "ui/events/keycodes/dom/dom_code.h"
namespace remoting {
namespace protocol {
class KeyboardLayout;
} // namespace protocol
// KeyboardLayoutMonitor implementations are responsible for monitoring the
// active keyboard layout within the CRD session on the host, and triggering a
// callback whenever it changes.
class KeyboardLayoutMonitor {
public:
virtual ~KeyboardLayoutMonitor() = default;
KeyboardLayoutMonitor(const KeyboardLayoutMonitor&) = delete;
KeyboardLayoutMonitor& operator=(const KeyboardLayoutMonitor&) = delete;
// Starts monitoring the keyboard layout. This will generate a callback with
// the current layout, and an additional callback whenever the layout is
// changed. The callback is guaranteed not be be called after the
// KeyboardLayoutMonitor is destroyed.
virtual void Start() = 0;
// Creates a platform-specific KeyboardLayoutMonitor.
static std::unique_ptr<KeyboardLayoutMonitor> Create(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)>);
protected:
KeyboardLayoutMonitor() = default;
// Physical keys to include in the keyboard map.
static const base::span<const ui::DomCode> kSupportedKeys;
};
} // namespace remoting
#endif // REMOTING_HOST_KEYBOARD_LAYOUT_MONITOR_H_
// Copyright 2019 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 "remoting/host/keyboard_layout_monitor.h"
#include "base/callback.h"
namespace remoting {
namespace {
class KeyboardLayoutMonitorLinux : public KeyboardLayoutMonitor {
public:
KeyboardLayoutMonitorLinux(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
}
~KeyboardLayoutMonitorLinux() override = default;
void Start() override {
// TODO(rkjnsn): Poll keyboard layout on Linux.
}
};
} // namespace
std::unique_ptr<KeyboardLayoutMonitor> KeyboardLayoutMonitor::Create(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
return std::make_unique<KeyboardLayoutMonitorLinux>(std::move(callback));
}
} // namespace remoting
// Copyright 2019 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 "remoting/host/keyboard_layout_monitor.h"
#include "base/callback.h"
namespace remoting {
namespace {
class KeyboardLayoutMonitorMac : public KeyboardLayoutMonitor {
public:
KeyboardLayoutMonitorMac(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
}
~KeyboardLayoutMonitorMac() override = default;
void Start() override {
// TODO(rkjnsn): Poll keyboard layout on Mac.
}
};
} // namespace
std::unique_ptr<KeyboardLayoutMonitor> KeyboardLayoutMonitor::Create(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
return std::make_unique<KeyboardLayoutMonitorMac>(std::move(callback));
}
} // namespace remoting
// Copyright 2019 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 "remoting/host/keyboard_layout_monitor.h"
#include <windows.h>
#include <ime.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_local.h"
#include "base/timer/timer.h"
#include "remoting/proto/control.pb.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
namespace remoting {
namespace {
constexpr base::TimeDelta POLL_INTERVAL =
base::TimeDelta::FromMilliseconds(1000);
class KeyboardLayoutMonitorWin : public KeyboardLayoutMonitor {
public:
KeyboardLayoutMonitorWin(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback);
~KeyboardLayoutMonitorWin() override;
void Start() override;
private:
// Check the current layout, and invoke the callback if it has changed.
void QueryLayout();
void ClearDeadKeys(HKL layout);
bool IsNumpadKey(ui::DomCode code);
UINT TranslateVirtualKey(bool numlock_state,
bool shift_state,
UINT virtual_key,
ui::DomCode code);
protocol::LayoutKeyFunction VirtualKeyToLayoutKeyFunction(UINT virtual_key,
LANGID lang);
HKL previous_layout_ = nullptr;
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback_;
base::RepeatingTimer timer_;
};
KeyboardLayoutMonitorWin::KeyboardLayoutMonitorWin(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback)
: callback_(std::move(callback)) {}
KeyboardLayoutMonitorWin::~KeyboardLayoutMonitorWin() = default;
void KeyboardLayoutMonitorWin::Start() {
timer_.Start(FROM_HERE, POLL_INTERVAL, this,
&KeyboardLayoutMonitorWin::QueryLayout);
}
void KeyboardLayoutMonitorWin::QueryLayout() {
// Get the keyboard layout for the active window.
DWORD thread_id = 0;
HWND foreground_window = GetForegroundWindow();
if (foreground_window) {
thread_id = GetWindowThreadProcessId(foreground_window, nullptr);
} else if (previous_layout_ != 0) {
// There's no currently active window, so keep using the previous layout.
return;
}
// If there is no previous layout and there's no active window
// (thread_id == 0), this will return the layout associated with this
// thread, which is better than nothing.
HKL layout = GetKeyboardLayout(thread_id);
if (layout == previous_layout_) {
return;
}
previous_layout_ = layout;
protocol::KeyboardLayout layout_message;
// TODO(rkjnsn): Windows doesn't provide an API to read the keyboard layout
// directly. We can use the various key translation functions to mostly get
// the information we need, but it requires some hacks, could miss some edge
// cases, and modifies the system keyboard state. Windows keyboard layouts
// consist of DLLs providing data tables in a reasonable straight-forward
// format, so it may make sense to look at reading them directly.
// It would be nice if we could use MapVirtualKeyEx to translate virtual
// keys to characters, as it neither is affected by nor modifies the system
// keyboard state. Unfortunately, it provides no way to specify shift
// states, so it can only be used to retrieve the unshifted character for a
// given key. Instead, we use ToUnicodeEx, which is affected by (and
// affects) the system keyboard state, so we start by clearing any queued-up
// dead keys.
ClearDeadKeys(layout);
// Keyboard layouts have a KLLF_ALTGR flag that indicated whether the right
// alt key is AltGr (and thus generates Ctrl+Alt). Unfortunately, there
// doesn't seem to be any way to determine whether the current layout sets
// this flag using the Windows API. As a hack/workaround, we assume that
// right Alt == AltGr if and only if there are keys that generate characters
// when both Ctrl and Alt modifiers are set. This is by no means guaranteed,
// but is expected to be true for common layouts.
bool has_altgr = false;
// Keys/shift levels that function as right Alt. These will be updated to
// AltGr if the keyboard appears to use it. (Most keyboards will have at
// most one of these.)
std::vector<std::pair<std::uint32_t, int>> right_alts;
for (ui::DomCode key : KeyboardLayoutMonitor::kSupportedKeys) {
std::uint32_t usb_code = ui::KeycodeConverter::DomCodeToUsbKeycode(key);
int scancode = ui::KeycodeConverter::DomCodeToNativeKeycode(key);
UINT virtual_key = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK_EX, layout);
if (virtual_key == 0) {
// This key is not mapped in the current layout.
continue;
}
google::protobuf::Map<google::protobuf::uint32,
protocol::KeyboardLayout_KeyAction>& key_actions =
*(*layout_message.mutable_keys())[usb_code].mutable_actions();
for (int shift_level = 0; shift_level < 4; ++shift_level) {
// Mimic Windows's handling of number pad key.
UINT translated_key = TranslateVirtualKey(
/* numlock_state */ true, shift_level & 1, virtual_key, key);
// First check if the key generates a character.
BYTE key_state[256] = {0};
// Modifiers set the high-order bit when pressed.
key_state[VK_SHIFT] = (shift_level & 1) << 7;
key_state[VK_CONTROL] = key_state[VK_MENU] = (shift_level & 2) << 6;
// Locks set the low-order bit when toggled on.
// For now, generate a layout with numlock always on and caps lock
// always off.
// TODO(rkjnsn): Update this when we decide how we want to handle locks
// for the on-screen keyboard.
key_state[VK_NUMLOCK] = 1;
key_state[VK_CAPITAL] = 0;
WCHAR char_buffer[16];
int size = ToUnicodeEx(translated_key, scancode, key_state, char_buffer,
base::size(char_buffer), 0, layout);
if (size < 0) {
// We don't handle dead keys specially for the layout, but we do
// need to clear them from the system keyboard state.
ClearDeadKeys(layout);
size = -size;
}
if (size > 0) {
// The key generated at least one character.
key_actions[shift_level].set_character(
base::UTF16ToUTF8(base::StringPiece16(char_buffer, size)));
if (shift_level > 2) {
has_altgr = true;
}
continue;
}
// If the key didn't generate a character, translate it based on the
// virtual key value.
key_actions[shift_level].set_function(VirtualKeyToLayoutKeyFunction(
translated_key, reinterpret_cast<std::uintptr_t>(layout) & 0xFFFF));
if (translated_key == VK_RMENU) {
right_alts.emplace_back(usb_code, shift_level);
}
}
}
// If any ctrl+alt+key sequence generated a character, assume right Alt is
// AltGr.
if (has_altgr) {
for (std::pair<std::uint32_t, int> right_alt : right_alts) {
(*(*layout_message.mutable_keys())[right_alt.first]
.mutable_actions())[right_alt.second]
.set_function(protocol::LayoutKeyFunction::ALT_GR);
}
} else {
// Remove higher shift levels since there's no way to generate them.
for (auto& key : *layout_message.mutable_keys()) {
key.second.mutable_actions()->erase(2);
key.second.mutable_actions()->erase(3);
}
}
callback_.Run(std::move(layout_message));
}
void KeyboardLayoutMonitorWin::ClearDeadKeys(HKL layout) {
// ToUnicodeEx both is affected by and modifies the current keyboard state,
// which includes the list of currently stored dead keys. Pressing space
// translates previously pressed dead keys to characters, clearing the dead-
// key buffer.
BYTE key_state[256] = {0};
WCHAR char_buffer[16];
ToUnicodeEx(VK_SPACE,
ui::KeycodeConverter::DomCodeToNativeKeycode(ui::DomCode::SPACE),
key_state, char_buffer, base::size(char_buffer), 0, layout);
}
bool KeyboardLayoutMonitorWin::IsNumpadKey(ui::DomCode code) {
// Windows keyboard layouts map number pad keys to virtual keys based on
// their function when num lock is off. E.g., 4 on the number pad generates
// VK_LEFT, the same as the left arrow key. To distinguish them, the layout
// sets the KBDNUMPAD flag on the numlock versions, allowing Windows to
// translate VK_LEFT to VK_NUMPAD4 when numlock is enabled only for the keys
// on the number pad. Unfortunately, the state of the KBDNUMPAD flag for a
// given scan code does not appear to be accessible via the Windows API, so
// for now just assume that all layouts use the same scan codes for the
// number pad.
// TODO(rkjnsn): Figure out if there's a better way to determine this.
switch (code) {
case ui::DomCode::NUMPAD0:
case ui::DomCode::NUMPAD1:
case ui::DomCode::NUMPAD2:
case ui::DomCode::NUMPAD3:
case ui::DomCode::NUMPAD4:
case ui::DomCode::NUMPAD5:
case ui::DomCode::NUMPAD6:
case ui::DomCode::NUMPAD7:
case ui::DomCode::NUMPAD8:
case ui::DomCode::NUMPAD9:
case ui::DomCode::NUMPAD_DECIMAL:
return true;
default:
return false;
}
}
UINT KeyboardLayoutMonitorWin::TranslateVirtualKey(bool numlock_state,
bool shift_state,
UINT virtual_key,
ui::DomCode code) {
// Windows only translates numpad keys when num lock is on and shift is not
// pressed. (Pressing shift when num lock is on will get you navigation, but
// pressing shift when num lock is off will not get you numbers.)
if (!numlock_state || shift_state || !IsNumpadKey(code)) {
return virtual_key;
}
switch (virtual_key) {
case VK_INSERT:
return VK_NUMPAD0;
case VK_END:
return VK_NUMPAD1;
case VK_DOWN:
return VK_NUMPAD2;
case VK_NEXT:
return VK_NUMPAD3;
case VK_LEFT:
return VK_NUMPAD4;
case VK_CLEAR:
return VK_NUMPAD5;
case VK_RIGHT:
return VK_NUMPAD6;
case VK_HOME:
return VK_NUMPAD7;
case VK_UP:
return VK_NUMPAD8;
case VK_PRIOR:
return VK_NUMPAD9;
default:
return virtual_key;
}
}
protocol::LayoutKeyFunction
KeyboardLayoutMonitorWin::VirtualKeyToLayoutKeyFunction(UINT virtual_key,
LANGID lang) {
switch (virtual_key) {
case VK_LCONTROL:
case VK_RCONTROL:
return protocol::LayoutKeyFunction::CONTROL;
case VK_LMENU:
case VK_RMENU:
return protocol::LayoutKeyFunction::ALT;
case VK_LSHIFT:
case VK_RSHIFT:
return protocol::LayoutKeyFunction::SHIFT;
case VK_LWIN:
case VK_RWIN:
return protocol::LayoutKeyFunction::META;
case VK_NUMLOCK:
return protocol::LayoutKeyFunction::NUM_LOCK;
case VK_CAPITAL:
return protocol::LayoutKeyFunction::CAPS_LOCK;
case VK_SCROLL:
return protocol::LayoutKeyFunction::SCROLL_LOCK;
case VK_BACK:
return protocol::LayoutKeyFunction::BACKSPACE;
case VK_RETURN:
return protocol::LayoutKeyFunction::ENTER;
case VK_TAB:
return protocol::LayoutKeyFunction::TAB;
case VK_INSERT:
return protocol::LayoutKeyFunction::INSERT;
case VK_DELETE:
return protocol::LayoutKeyFunction::DELETE_;
case VK_HOME:
return protocol::LayoutKeyFunction::HOME;
case VK_END:
return protocol::LayoutKeyFunction::END;
case VK_PRIOR:
return protocol::LayoutKeyFunction::PAGE_UP;
case VK_NEXT:
return protocol::LayoutKeyFunction::PAGE_DOWN;
case VK_CLEAR:
return protocol::LayoutKeyFunction::CLEAR;
case VK_UP:
return protocol::LayoutKeyFunction::ARROW_UP;
case VK_DOWN:
return protocol::LayoutKeyFunction::ARROW_DOWN;
case VK_LEFT:
return protocol::LayoutKeyFunction::ARROW_LEFT;
case VK_RIGHT:
return protocol::LayoutKeyFunction::ARROW_RIGHT;
case VK_F1:
return protocol::LayoutKeyFunction::F1;
case VK_F2:
return protocol::LayoutKeyFunction::F2;
case VK_F3:
return protocol::LayoutKeyFunction::F3;
case VK_F4:
return protocol::LayoutKeyFunction::F4;
case VK_F5:
return protocol::LayoutKeyFunction::F5;
case VK_F6:
return protocol::LayoutKeyFunction::F6;
case VK_F7:
return protocol::LayoutKeyFunction::F7;
case VK_F8:
return protocol::LayoutKeyFunction::F8;
case VK_F9:
return protocol::LayoutKeyFunction::F9;
case VK_F10:
return protocol::LayoutKeyFunction::F10;
case VK_F11:
return protocol::LayoutKeyFunction::F11;
case VK_F12:
return protocol::LayoutKeyFunction::F12;
case VK_F13:
return protocol::LayoutKeyFunction::F13;
case VK_F14:
return protocol::LayoutKeyFunction::F14;
case VK_F15:
return protocol::LayoutKeyFunction::F15;
case VK_F16:
return protocol::LayoutKeyFunction::F16;
case VK_F17:
return protocol::LayoutKeyFunction::F17;
case VK_F18:
return protocol::LayoutKeyFunction::F18;
case VK_F19:
return protocol::LayoutKeyFunction::F19;
case VK_F20:
return protocol::LayoutKeyFunction::F20;
case VK_F21:
return protocol::LayoutKeyFunction::F21;
case VK_F22:
return protocol::LayoutKeyFunction::F22;
case VK_F23:
return protocol::LayoutKeyFunction::F23;
case VK_F24:
return protocol::LayoutKeyFunction::F24;
case VK_ESCAPE:
return protocol::LayoutKeyFunction::ESCAPE;
case VK_APPS:
return protocol::LayoutKeyFunction::CONTEXT_MENU;
case VK_PAUSE:
return protocol::LayoutKeyFunction::PAUSE;
case VK_PRINT:
return protocol::LayoutKeyFunction::PRINT_SCREEN;
}
// Handle language-specific keys.
if (PRIMARYLANGID(lang) == 0x11) { // Japanese
switch (virtual_key) {
case VK_DBE_SBCSCHAR:
return protocol::LayoutKeyFunction::HANKAKU_ZENKAKU_KANJI;
case VK_CONVERT:
return protocol::LayoutKeyFunction::HENKAN;
case VK_NONCONVERT:
return protocol::LayoutKeyFunction::MUHENKAN;
case VK_DBE_KATAKANA:
case VK_DBE_HIRAGANA:
// TODO(rkjnsn): Make sure it makes sense to use the same key cap for
// both of these.
return protocol::LayoutKeyFunction::KATAKANA_HIRAGANA_ROMAJI;
case VK_DBE_ALPHANUMERIC:
return protocol::LayoutKeyFunction::EISU;
}
} else if (PRIMARYLANGID(lang) == 0x12) { // Korean
switch (virtual_key) {
case VK_HANJA:
return protocol::LayoutKeyFunction::HANJA;
case VK_HANGUL:
return protocol::LayoutKeyFunction::HAN_YEONG;
}
}
return protocol::LayoutKeyFunction::UNKNOWN;
}
} // namespace
std::unique_ptr<KeyboardLayoutMonitor> KeyboardLayoutMonitor::Create(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
return std::make_unique<KeyboardLayoutMonitorWin>(std::move(callback));
}
} // namespace remoting
......@@ -70,6 +70,9 @@ class TestClientStub : public protocol::ClientStub {
// protocol::CursorShapeStub implementation.
void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override;
// protocol::KeyboardLayoutStub implementation.
void SetKeyboardLayout(const protocol::KeyboardLayout& layout) override;
void WaitForDeliverHostMessage(base::TimeDelta max_timeout);
void CheckHostDataMessage(int id, const std::string& data);
......@@ -105,6 +108,9 @@ void TestClientStub::InjectClipboardEvent(
void TestClientStub::SetCursorShape(
const protocol::CursorShapeInfo& cursor_shape) {}
void TestClientStub::SetKeyboardLayout(const protocol::KeyboardLayout& layout) {
}
void TestClientStub::WaitForDeliverHostMessage(base::TimeDelta max_timeout) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop_->QuitClosure(), max_timeout);
......
......@@ -152,8 +152,9 @@ message KeyboardLayout {
// Next ID: 2
message KeyBehavior {
// Maps shift level to key action.
map<int32, KeyAction> actions = 1;
// Maps 0-based shift level to key action. (Note: because this is zero-
// based, it will be one less than the corresponding ISO shift level.)
map<uint32, KeyAction> actions = 1;
}
// Map USB code to key behavior.
......
......@@ -27,6 +27,7 @@ message ControlMessage {
optional ExtensionMessage extension_message = 9;
optional VideoLayout video_layout = 10;
optional SelectDesktopDisplayRequest select_display = 11;
optional KeyboardLayout keyboard_layout = 12;
}
// Defines an event message on the event channel.
......
......@@ -93,9 +93,6 @@ enum LayoutKeyFunction {
MUHENKAN = 55; // NonConvert
KATAKANA_HIRAGANA_ROMAJI = 56; // KanaMode
KANA = 57; // Lang1 (Mac keyboard)
// Note: Windows only identifies the key as "Caps Lock", so the Windows host
// currently sends the CAPS_LOCK function even for shift level 1 (unshifted)
// and doesn't use this function.
EISU = 58; // Unshifted CapsLock (Windows), Lang2 (Mac keyboard)
// Korean
HAN_YEONG = 59; // Lang1
......
......@@ -94,6 +94,7 @@ static_library("protocol") {
"jingle_session.h",
"jingle_session_manager.cc",
"jingle_session_manager.h",
"keyboard_layout_stub.h",
"me2me_host_authenticator_factory.cc",
"me2me_host_authenticator_factory.h",
"message_decoder.cc",
......
......@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "remoting/protocol/clipboard_stub.h"
#include "remoting/protocol/cursor_shape_stub.h"
#include "remoting/protocol/keyboard_layout_stub.h"
namespace remoting {
namespace protocol {
......@@ -23,7 +24,8 @@ class PairingResponse;
class VideoLayout;
class ClientStub : public ClipboardStub,
public CursorShapeStub {
public CursorShapeStub,
public KeyboardLayoutStub {
public:
ClientStub() {}
~ClientStub() override {}
......
......@@ -78,6 +78,12 @@ void HostControlDispatcher::SetCursorShape(
message_pipe()->Send(&message, base::Closure());
}
void HostControlDispatcher::SetKeyboardLayout(const KeyboardLayout& layout) {
ControlMessage message;
message.mutable_keyboard_layout()->CopyFrom(layout);
message_pipe()->Send(&message, base::Closure());
}
void HostControlDispatcher::OnIncomingMessage(
std::unique_ptr<CompoundBuffer> buffer) {
DCHECK(clipboard_stub_);
......
......@@ -40,6 +40,9 @@ class HostControlDispatcher : public ChannelDispatcherBase,
// CursorShapeStub implementation for sending cursor shape to client.
void SetCursorShape(const CursorShapeInfo& cursor_shape) override;
// KeyboardLayoutStub implementation for sending keyboard layout to client.
void SetKeyboardLayout(const KeyboardLayout& layout) override;
// Sets the ClipboardStub that will be called for each incoming clipboard
// message. |clipboard_stub| must outlive this object.
void set_clipboard_stub(ClipboardStub* clipboard_stub) {
......
// Copyright 2019 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.
// Interface for an object that receives keyboard layout events.
#ifndef REMOTING_PROTOCOL_KEYBOARD_LAYOUT_STUB_H_
#define REMOTING_PROTOCOL_KEYBOARD_LAYOUT_STUB_H_
#include "base/macros.h"
namespace remoting {
namespace protocol {
class KeyboardLayout;
// Interface used to inform the client when the host's keyboard layout changes.
class KeyboardLayoutStub {
public:
virtual ~KeyboardLayoutStub() = default;
KeyboardLayoutStub(const KeyboardLayoutStub&) = delete;
KeyboardLayoutStub& operator=(const KeyboardLayoutStub&) = delete;
virtual void SetKeyboardLayout(const KeyboardLayout& keyboard_layout) = 0;
protected:
KeyboardLayoutStub() = default;
};
} // namespace protocol
} // namespace remoting
#endif // REMOTING_PROTOCOL_KEYBOARD_LAYOUT_STUB_H_
......@@ -159,6 +159,9 @@ class MockClientStub : public ClientStub {
// CursorShapeStub mock implementation.
MOCK_METHOD1(SetCursorShape, void(const CursorShapeInfo& cursor_shape));
// KeyboardLayoutStub mock implementation.
MOCK_METHOD1(SetKeyboardLayout, void(const KeyboardLayout& layout));
private:
DISALLOW_COPY_AND_ASSIGN(MockClientStub);
};
......
......@@ -203,6 +203,7 @@ class FakeConnectionEventLogger::CounterClientStub
void InjectClipboardEvent(const protocol::ClipboardEvent& event) override {}
void SetCapabilities(const protocol::Capabilities& capabilities) override {}
void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override {}
void SetKeyboardLayout(const protocol::KeyboardLayout& layout) override {}
void SetPairingResponse(const protocol::PairingResponse& response) override {}
void SetVideoLayout(const protocol::VideoLayout& video_layout) override {}
};
......
......@@ -155,6 +155,9 @@ class ProtocolPerfTest
protocol::CursorShapeStub* GetCursorShapeStub() override {
return &cursor_shape_stub_;
}
protocol::KeyboardLayoutStub* GetKeyboardLayoutStub() override {
return nullptr;
}
// protocol::FrameConsumer interface.
std::unique_ptr<webrtc::DesktopFrame> AllocateFrame(
......
......@@ -266,6 +266,11 @@ protocol::CursorShapeStub* TestChromotingClient::GetCursorShapeStub() {
return this;
}
protocol::KeyboardLayoutStub* TestChromotingClient::GetKeyboardLayoutStub() {
VLOG(1) << "TestChromotingClient::GetKeyboardLayoutStub() Called";
return this;
}
void TestChromotingClient::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
VLOG(1) << "TestChromotingClient::InjectClipboardEvent() Called";
......@@ -276,5 +281,10 @@ void TestChromotingClient::SetCursorShape(
VLOG(1) << "TestChromotingClient::SetCursorShape() Called";
}
void TestChromotingClient::SetKeyboardLayout(
const protocol::KeyboardLayout& layout) {
VLOG(1) << "TestChromotingClient::SetKeyboardLayout() Called";
}
} // namespace test
} // namespace remoting
......@@ -15,6 +15,7 @@
#include "remoting/client/client_user_interface.h"
#include "remoting/protocol/clipboard_filter.h"
#include "remoting/protocol/cursor_shape_stub.h"
#include "remoting/protocol/keyboard_layout_stub.h"
#include "remoting/test/remote_connection_observer.h"
namespace remoting {
......@@ -40,7 +41,8 @@ struct ConnectionSetupInfo;
// A VideoRenderer can be passed in to customize the connection.
class TestChromotingClient : public ClientUserInterface,
public protocol::ClipboardStub,
public protocol::CursorShapeStub {
public protocol::CursorShapeStub,
public protocol::KeyboardLayoutStub {
public:
TestChromotingClient();
explicit TestChromotingClient(
......@@ -90,6 +92,7 @@ class TestChromotingClient : public ClientUserInterface,
const webrtc::DesktopVector& dpi) override;
protocol::ClipboardStub* GetClipboardStub() override;
protocol::CursorShapeStub* GetCursorShapeStub() override;
protocol::KeyboardLayoutStub* GetKeyboardLayoutStub() override;
// protocol::ClipboardStub interface.
void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
......@@ -97,6 +100,9 @@ class TestChromotingClient : public ClientUserInterface,
// protocol::CursorShapeStub interface.
void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override;
// protocol::KeyboardLayoutStub interface.
void SetKeyboardLayout(const protocol::KeyboardLayout& layout) override;
// Tracks the current connection state.
protocol::ConnectionToHost::State connection_to_host_state_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment