Support standard keyboard keys emulated by the braille drivers.

This plumbs through keys like backspace and enter and arrow keys.
It also provides Unicode characters for displays with query keyboards etc.

BUG=310285
R=dtseng@chromium.org, kalman@chromium.org

Review URL: https://codereview.chromium.org/297833015

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273171 0039d316-1c4b-4281-b951-d872f2087c98
parent 515e6b57
......@@ -746,7 +746,6 @@
'--link-directly=<(linux_link_libbrlapi)',
'brlapi_getHandleSize',
'brlapi_error_location',
'brlapi_expandKeyCode',
'brlapi_strerror',
'brlapi__acceptKeys',
'brlapi__openConnection',
......
......@@ -13,6 +13,7 @@
#include "base/bind_helpers.h"
#include "base/time/time.h"
#include "chrome/browser/extensions/api/braille_display_private/brlapi_connection.h"
#include "chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.h"
#include "content/public/browser/browser_thread.h"
namespace extensions {
......@@ -29,11 +30,6 @@ const int64 kConnectionDelayMs = 500;
// How long to periodically retry connecting after a brltty restart.
// Some displays are slow to connect.
const int64 kConnectRetryTimeout = 20000;
// Bitmask for all braille dots in a key command argument, which coincides
// with the representation in the braille_dots member of the KeyEvent
// class.
const int kAllDots = BRLAPI_DOT1 | BRLAPI_DOT2 | BRLAPI_DOT3 | BRLAPI_DOT4 |
BRLAPI_DOT5 | BRLAPI_DOT6 | BRLAPI_DOT7 | BRLAPI_DOT8;
} // namespace
BrailleController::BrailleController() {
......@@ -268,53 +264,6 @@ scoped_ptr<BrlapiConnection> BrailleControllerImpl::CreateBrlapiConnection() {
return BrlapiConnection::Create(&libbrlapi_loader_);
}
scoped_ptr<KeyEvent> BrailleControllerImpl::MapKeyCode(brlapi_keyCode_t code) {
brlapi_expandedKeyCode_t expanded;
if (libbrlapi_loader_.brlapi_expandKeyCode(code, &expanded) != 0) {
LOG(ERROR) << "Couldn't expand key code " << code;
return scoped_ptr<KeyEvent>();
}
scoped_ptr<KeyEvent> result(new KeyEvent);
result->command = KEY_COMMAND_NONE;
switch (expanded.type) {
case BRLAPI_KEY_TYPE_CMD:
switch (expanded.command) {
case BRLAPI_KEY_CMD_LNUP:
result->command = KEY_COMMAND_LINE_UP;
break;
case BRLAPI_KEY_CMD_LNDN:
result->command = KEY_COMMAND_LINE_DOWN;
break;
case BRLAPI_KEY_CMD_FWINLT:
result->command = KEY_COMMAND_PAN_LEFT;
break;
case BRLAPI_KEY_CMD_FWINRT:
result->command = KEY_COMMAND_PAN_RIGHT;
break;
case BRLAPI_KEY_CMD_TOP:
result->command = KEY_COMMAND_TOP;
break;
case BRLAPI_KEY_CMD_BOT:
result->command = KEY_COMMAND_BOTTOM;
break;
case BRLAPI_KEY_CMD_ROUTE:
result->command = KEY_COMMAND_ROUTING;
result->display_position.reset(new int(expanded.argument));
break;
case BRLAPI_KEY_CMD_PASSDOTS:
result->command = KEY_COMMAND_DOTS;
result->braille_dots.reset(new int(expanded.argument & kAllDots));
if ((expanded.argument & BRLAPI_DOTC) != 0)
result->space_key.reset(new bool(true));
break;
}
break;
}
if (result->command == KEY_COMMAND_NONE)
result.reset();
return result.Pass();
}
void BrailleControllerImpl::DispatchKeys() {
DCHECK(connection_.get());
brlapi_keyCode_t code;
......@@ -331,7 +280,7 @@ void BrailleControllerImpl::DispatchKeys() {
} else if (result == 0) { // No more data.
return;
}
scoped_ptr<KeyEvent> event = MapKeyCode(code);
scoped_ptr<KeyEvent> event = BrlapiKeyCodeToEvent(code);
if (event)
DispatchKeyEvent(event.Pass());
}
......
......@@ -61,7 +61,6 @@ class BrailleControllerImpl : public BrailleController {
void Disconnect();
scoped_ptr<BrlapiConnection> CreateBrlapiConnection();
void DispatchKeys();
scoped_ptr<KeyEvent> MapKeyCode(brlapi_keyCode_t code);
void DispatchKeyEvent(scoped_ptr<KeyEvent> event);
void DispatchOnDisplayStateChanged(scoped_ptr<DisplayState> new_state);
......
......@@ -42,6 +42,9 @@ namespace {
const char kTestUserName[] = "owner@invalid.domain";
// Used to make ReadKeys return an error.
brlapi_keyCode_t kErrorKeyCode = BRLAPI_KEY_MAX;
} // namespace
// Data maintained by the mock BrlapiConnection. This data lives throughout
......@@ -114,9 +117,9 @@ class MockBrlapiConnection : public BrlapiConnection {
virtual int ReadKey(brlapi_keyCode_t* key_code) OVERRIDE {
if (!data_->pending_keys.empty()) {
int queued_key_code = data_->pending_keys.front();
brlapi_keyCode_t queued_key_code = data_->pending_keys.front();
data_->pending_keys.pop_front();
if (queued_key_code < 0) {
if (queued_key_code == kErrorKeyCode) {
data_->error.brlerrno = BRLAPI_ERROR_EOF;
return -1; // Signal error.
}
......@@ -194,6 +197,8 @@ IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, WriteDots) {
IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, KeyEvents) {
connection_data_.display_size = 11;
// Braille navigation commands.
connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
BRLAPI_KEY_CMD_LNUP);
connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
......@@ -208,17 +213,54 @@ IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, KeyEvents) {
BRLAPI_KEY_CMD_BOT);
connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
BRLAPI_KEY_CMD_ROUTE | 5);
// Space (0) and all combinations of dots.
// Braille display standard keyboard emulation.
// An ascii character.
connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_SYM | 'A');
// A non-ascii 'latin1' character. Small letter a with ring above.
connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_SYM | 0xE5);
// A non-latin1 Unicode character. LATIN SMALL LETTER A WITH MACRON.
connection_data_.pending_keys.push_back(
BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_SYM_UNICODE | 0x100);
// A Unicode character outside the BMP. CAT FACE WITH TEARS OF JOY.
// With anticipation for the first emoji-enabled braille display.
connection_data_.pending_keys.push_back(
BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_SYM_UNICODE | 0x1F639);
// Invalid Unicode character.
connection_data_.pending_keys.push_back(
BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_SYM_UNICODE | 0x110000);
// Non-alphanumeric function keys.
// Backspace.
connection_data_.pending_keys.push_back(
BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_SYM_BACKSPACE);
// Shift+Tab.
connection_data_.pending_keys.push_back(
BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_FLG_SHIFT | BRLAPI_KEY_SYM_TAB);
// Alt+F3. (0-based).
connection_data_.pending_keys.push_back(
BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_FLG_META |
(BRLAPI_KEY_SYM_FUNCTION + 2));
// ctrl+dot1+dot2.
connection_data_.pending_keys.push_back(
BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_FLG_CONTROL | BRLAPI_KEY_CMD_PASSDOTS |
BRLAPI_DOT1 | BRLAPI_DOT2);
// Braille dot keys, all combinations including space (0).
for (int i = 0; i < 256; ++i) {
connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
BRLAPI_KEY_CMD_PASSDOTS | i);
}
ASSERT_TRUE(RunComponentExtensionTest("braille_display_private/key_events"));
}
IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, DisplayStateChanges) {
connection_data_.display_size = 11;
connection_data_.pending_keys.push_back(-1);
connection_data_.pending_keys.push_back(kErrorKeyCode);
connection_data_.reappear_on_disconnect = true;
ASSERT_TRUE(RunComponentExtensionTest(
"braille_display_private/display_state_changes"));
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversion_utils.h"
namespace extensions {
namespace api {
namespace braille_display_private {
namespace {
// Bitmask for all braille dots in a key command argument, which coincides
// with the representation in the braille_dots member of the KeyEvent
// class.
const int kAllDots = BRLAPI_DOT1 | BRLAPI_DOT2 | BRLAPI_DOT3 | BRLAPI_DOT4 |
BRLAPI_DOT5 | BRLAPI_DOT6 | BRLAPI_DOT7 | BRLAPI_DOT8;
// Maximum Latin 1 character keyboard symbol.
const brlapi_keyCode_t kMaxLatin1KeySym = 0xff;
// Range of function keys that we support.
// See ui/events/keycodes/dom4/keycode_converter_data.h for the list of all
// key codes.
const brlapi_keyCode_t kMinFunctionKey = BRLAPI_KEY_SYM_FUNCTION;
const brlapi_keyCode_t kMaxFunctionKey = BRLAPI_KEY_SYM_FUNCTION + 23;
// Maps the keyboard modifier flags to their corresponding flags in a
// |KeyEvent|.
void MapModifierFlags(brlapi_keyCode_t code, KeyEvent* event) {
if (code & BRLAPI_KEY_FLG_CONTROL)
event->ctrl_key.reset(new bool(true));
if (code & BRLAPI_KEY_FLG_META)
event->alt_key.reset(new bool(true));
if (code & BRLAPI_KEY_FLG_SHIFT)
event->shift_key.reset(new bool(true));
}
// Maps a brlapi keysym, which is similar to an X keysym into the
// provided event.
// See ui/events/keycodes/dom4/keycode_converter_data.cc for the full
// list of key codes.
void MapKeySym(brlapi_keyCode_t code, KeyEvent* event) {
brlapi_keyCode_t key_sym = code & BRLAPI_KEY_CODE_MASK;
if (key_sym < kMaxLatin1KeySym ||
(key_sym & BRLAPI_KEY_SYM_UNICODE) != 0) {
uint32 code_point = key_sym & ~BRLAPI_KEY_SYM_UNICODE;
if (!base::IsValidCharacter(code_point))
return;
event->standard_key_char.reset(new std::string);
base::WriteUnicodeCharacter(code_point, event->standard_key_char.get());
} else if (key_sym >= kMinFunctionKey && key_sym <= kMaxFunctionKey) {
// Function keys are 0-based here, so we need to add one to get e.g.
// 'F1' for the first key.
int function_key_number = key_sym - kMinFunctionKey + 1;
event->standard_key_code.reset(
new std::string(base::StringPrintf("F%d", function_key_number)));
} else {
// Explicitly map the keys that brlapi provides.
const char* code_string;
switch (key_sym) {
case BRLAPI_KEY_SYM_BACKSPACE:
code_string = "Backspace";
break;
case BRLAPI_KEY_SYM_TAB:
code_string = "Tab";
break;
case BRLAPI_KEY_SYM_LINEFEED:
code_string = "Enter";
break;
case BRLAPI_KEY_SYM_ESCAPE:
code_string = "Escape";
break;
case BRLAPI_KEY_SYM_HOME:
code_string = "Home";
break;
case BRLAPI_KEY_SYM_LEFT:
code_string = "ArrowLeft";
break;
case BRLAPI_KEY_SYM_UP:
code_string = "ArrowUp";
break;
case BRLAPI_KEY_SYM_RIGHT:
code_string = "ArrowRight";
break;
case BRLAPI_KEY_SYM_DOWN:
code_string = "ArrowDown";
break;
case BRLAPI_KEY_SYM_PAGE_UP:
code_string = "PageUp";
break;
case BRLAPI_KEY_SYM_PAGE_DOWN:
code_string = "PageDown";
break;
case BRLAPI_KEY_SYM_END:
code_string = "End";
break;
case BRLAPI_KEY_SYM_INSERT:
code_string = "Insert";
break;
case BRLAPI_KEY_SYM_DELETE:
code_string = "Delete";
break;
default:
return;
}
event->standard_key_code.reset(new std::string(code_string));
}
MapModifierFlags(code, event);
event->command = KEY_COMMAND_STANDARD_KEY;
}
void MapCommand(brlapi_keyCode_t code, KeyEvent* event) {
brlapi_keyCode_t argument = code & BRLAPI_KEY_CMD_ARG_MASK;
switch (code & BRLAPI_KEY_CODE_MASK) {
case BRLAPI_KEY_CMD_LNUP:
event->command = KEY_COMMAND_LINE_UP;
break;
case BRLAPI_KEY_CMD_LNDN:
event->command = KEY_COMMAND_LINE_DOWN;
break;
case BRLAPI_KEY_CMD_FWINLT:
event->command = KEY_COMMAND_PAN_LEFT;
break;
case BRLAPI_KEY_CMD_FWINRT:
event->command = KEY_COMMAND_PAN_RIGHT;
break;
case BRLAPI_KEY_CMD_TOP:
event->command = KEY_COMMAND_TOP;
break;
case BRLAPI_KEY_CMD_BOT:
event->command = KEY_COMMAND_BOTTOM;
break;
default:
switch (code & BRLAPI_KEY_CMD_BLK_MASK) {
case BRLAPI_KEY_CMD_ROUTE:
event->command = KEY_COMMAND_ROUTING;
event->display_position.reset(new int(argument));
break;
case BRLAPI_KEY_CMD_PASSDOTS:
event->command = KEY_COMMAND_DOTS;
event->braille_dots.reset(new int(argument & kAllDots));
MapModifierFlags(code, event);
break;
}
}
}
} // namespace
scoped_ptr<KeyEvent> BrlapiKeyCodeToEvent(brlapi_keyCode_t code) {
scoped_ptr<KeyEvent> result(new KeyEvent);
result->command = KEY_COMMAND_NONE;
switch (code & BRLAPI_KEY_TYPE_MASK) {
case BRLAPI_KEY_TYPE_SYM:
MapKeySym(code, result.get());
break;
case BRLAPI_KEY_TYPE_CMD:
MapCommand(code, result.get());
break;
}
if (result->command == KEY_COMMAND_NONE)
result.reset();
return result.Pass();
}
} // namespace braille_display_private
} // namespace api
} // namespace extensions
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_API_BRAILLE_DISPLAY_PRIVATE_BRLAPI_KEYCODE_MAP_H_
#define CHROME_BROWSER_EXTENSIONS_API_BRAILLE_DISPLAY_PRIVATE_BRLAPI_KEYCODE_MAP_H_
#include "base/memory/scoped_ptr.h"
#include "chrome/common/extensions/api/braille_display_private.h"
#include "library_loaders/libbrlapi.h"
namespace extensions {
namespace api {
namespace braille_display_private {
// Maps a 64 bit BrlAPI keycode to a braille |KeyEvent| object.
scoped_ptr<KeyEvent> BrlapiKeyCodeToEvent(brlapi_keyCode_t code);
} // namespace braille_display_private
} // namespace api
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_BRAILLE_DISPLAY_PRIVATE_BRLAPI_KEYCODE_MAP_H_
......@@ -191,6 +191,8 @@
'browser/extensions/api/braille_display_private/braille_display_private_api.cc',
'browser/extensions/api/braille_display_private/brlapi_connection.cc',
'browser/extensions/api/braille_display_private/brlapi_connection.h',
'browser/extensions/api/braille_display_private/brlapi_keycode_map.cc',
'browser/extensions/api/braille_display_private/brlapi_keycode_map.h',
'browser/extensions/api/braille_display_private/stub_braille_controller.cc',
'browser/extensions/api/braille_display_private/stub_braille_controller.h',
'browser/extensions/api/browser/browser_api.cc',
......@@ -1219,6 +1221,7 @@
'sources!': [
'browser/extensions/api/braille_display_private/braille_controller_brlapi.cc',
'browser/extensions/api/braille_display_private/brlapi_connection.cc',
'browser/extensions/api/braille_display_private/brlapi_keycode_map.cc',
],
}],
['enable_webrtc==0', {
......
......@@ -28,8 +28,15 @@ namespace brailleDisplayPrivate {
// Braille dot keys that were pressed, stored in the low-order bits.
// Dot 1 is stored in bit 0, dot2 in bit 1, etc.
long? brailleDots;
// DOM keyboard event for a key that corresponds to a standard key.
DOMString? standardKeyName;
// DOM keyboard event code. This is present when command is standard_key
// and the braille display event represents a non-alphanumeric key such
// as an arrow key or function key.
// The value is as defined by the |code| property in
// http://www.w3.org/TR/uievents/#keyboard-event-interface
DOMString? standardKeyCode;
// DOM keyboard event character value. This is present if the
// braille key event corresponds to a character.
DOMString? standardKeyChar;
// Whether the space key was pressed.
boolean? spaceKey;
// Whether the alt key was pressed.
......
......@@ -8,16 +8,25 @@
var pass = chrome.test.callbackPass;
var EXPECTED_EVENTS = [
{ "command": "line_up" },
{ "command": "line_down" },
{ "command": "pan_left" },
{ "command": "pan_right" },
{ "command": "top" },
{ "command": "bottom" },
{ "command": "routing", "displayPosition": 5 },
{ command: "line_up" },
{ command: "line_down" },
{ command: "pan_left" },
{ command: "pan_right" },
{ command: "top" },
{ command: "bottom" },
{ command: "routing", "displayPosition": 5 },
{ command: "standard_key", standardKeyChar: "A" },
{ command: "standard_key", standardKeyChar: "\u00E5" },
{ command: "standard_key", standardKeyChar: "\u0100" },
// UTF-16 of U+1F639.
{ command: "standard_key", standardKeyChar: "\uD83D\uDE39" },
{ command: "standard_key", standardKeyCode: "Backspace" },
{ command: "standard_key", standardKeyCode: "Tab", shiftKey: true },
{ command: "standard_key", standardKeyCode: "F3", altKey: true },
{ command: "dots", ctrlKey: true, brailleDots: 0x1 | 0x2}
]
for (var i = 0; i < 256; ++i) {
EXPECTED_EVENTS.push({ "command": "dots", "brailleDots": i });
EXPECTED_EVENTS.push({ command: "dots", "brailleDots": i });
}
var event_number = 0;
......
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