diff options
9 files changed, 271 insertions, 69 deletions
diff --git a/build/linux/system.gyp b/build/linux/system.gyp index b798256..6a7cae0 100644 --- a/build/linux/system.gyp +++ b/build/linux/system.gyp @@ -746,7 +746,6 @@ '--link-directly=<(linux_link_libbrlapi)', 'brlapi_getHandleSize', 'brlapi_error_location', - 'brlapi_expandKeyCode', 'brlapi_strerror', 'brlapi__acceptKeys', 'brlapi__openConnection', diff --git a/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc b/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc index 60eaa5e..96a79de 100644 --- a/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc +++ b/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc @@ -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()); } diff --git a/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h b/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h index 20fbf9d..41c2b82 100644 --- a/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h +++ b/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h @@ -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); diff --git a/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc b/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc index cfa1712..ae2300c 100644 --- a/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc +++ b/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc @@ -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")); diff --git a/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc b/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc new file mode 100644 index 0000000..20225c4 --- /dev/null +++ b/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc @@ -0,0 +1,171 @@ +// 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 diff --git a/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.h b/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.h new file mode 100644 index 0000000..177e058 --- /dev/null +++ b/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.h @@ -0,0 +1,23 @@ +// 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_ diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 20b5ff0..588b601 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -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', { diff --git a/chrome/common/extensions/api/braille_display_private.idl b/chrome/common/extensions/api/braille_display_private.idl index 166b932..0c7a3e6 100644 --- a/chrome/common/extensions/api/braille_display_private.idl +++ b/chrome/common/extensions/api/braille_display_private.idl @@ -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. diff --git a/chrome/test/data/extensions/api_test/braille_display_private/key_events/test.js b/chrome/test/data/extensions/api_test/braille_display_private/key_events/test.js index 875e4be..7437bb0 100644 --- a/chrome/test/data/extensions/api_test/braille_display_private/key_events/test.js +++ b/chrome/test/data/extensions/api_test/braille_display_private/key_events/test.js @@ -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; |