Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4171)

Unified Diff: chrome/browser/extensions/api/braille_private/braille_controller.cc

Issue 13355002: Implement chrome.brailleDisplayPrivate API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/api/braille_private/braille_controller.cc
diff --git a/chrome/browser/extensions/api/braille_private/braille_controller.cc b/chrome/browser/extensions/api/braille_private/braille_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3f7a50c618f85d6a9b2deefe536794a1a2d82ef5
--- /dev/null
+++ b/chrome/browser/extensions/api/braille_private/braille_controller.cc
@@ -0,0 +1,469 @@
+// Copyright (c) 2013 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_private/braille_controller.h"
+
+#include <errno.h>
+#include <map>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/chromeos/chromeos_version.h"
+#include "base/files/file_path_watcher.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#include "base/observer_list.h"
+#include "base/time/time.h"
+#include "chrome/common/extensions/api/braille_private.h"
+#include "content/public/browser/browser_thread.h"
+#include "library_loaders/libbrlapi.h"
+
+namespace extensions {
+using content::BrowserThread;
+using base::MessageLoopForIO;
+using base::TimeDelta;
+namespace api {
+namespace braille_private {
+namespace {
+// Default virtual terminal. This can be overriden by setting the
+// WINDOWPATH environment variable.
+// TODO(plundblad): Find a way to detect the controlling terminal of the
+// X server.
+static const int kDefaultTtyLinux = 7;
+#if defined(OS_CHROMEOS)
+static const int kDefaultTtyChromeOS = 1;
+#endif
+} // namespace
+
+class BrailleControllerImpl : public BrailleController {
+ public:
+ static BrailleControllerImpl* GetInstance();
+ virtual scoped_ptr<base::DictionaryValue> GetDisplayState() OVERRIDE;
+ virtual void WriteText(const std::string& text, int cursor) OVERRIDE;
+ virtual void AddObserver(Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(Observer* observer) OVERRIDE;
+ private:
+ class Connection : public MessageLoopForIO::Watcher {
+ public:
+ Connection(BrailleControllerImpl* braille_controller) :
+ braille_controller_(braille_controller) {
+ DCHECK(braille_controller_->libbrlapi_loader_.loaded());
+ }
+
+ virtual ~Connection() {
+ Disconnect();
+ }
+
+ bool Connect() {
+ DCHECK(!handle_);
+ handle_.reset((brlapi_handle_t*) malloc(
+ braille_controller_->libbrlapi_loader_.brlapi_getHandleSize()));
+ int fd = braille_controller_->libbrlapi_loader_.brlapi__openConnection(
+ handle_.get(), NULL, NULL);
+ if (fd < 0) {
+ handle_.reset();
+ LOG(ERROR) << "Error connecting to brlapi: "
+ << braille_controller_->BrlApiStrError();
+ return false;
+ }
+ if (!MessageLoopForIO::current()->WatchFileDescriptor(
+ fd, true, MessageLoopForIO::WATCH_READ, &fd_controller_, this)) {
+ Disconnect();
+ return false;
+ }
+ return true;
+ }
+
+ void Disconnect() {
+ if (handle_ == NULL) {
+ return;
+ }
+ braille_controller_->libbrlapi_loader_.brlapi__closeConnection(
+ handle_.get());
+ handle_.reset();
+ }
+
+ bool Connected() { return handle_; }
+
+ brlapi_handle_t* GetHandle() { return handle_.get(); }
+
+ MessageLoopForIO::FileDescriptorWatcher* GetFDController() {
+ return &fd_controller_;
+ }
+
+ // MessageLoopForIO::Watcher
+ virtual void OnFileCanReadWithoutBlocking(int fd) {
+ LOG(ERROR) << "Braille fd " << fd << " ready";
+ braille_controller_->DispatchKeys(this);
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int fd) {}
+
+ private:
+ scoped_ptr<brlapi_handle_t, base::FreeDeleter> handle_;
+ MessageLoopForIO::FileDescriptorWatcher fd_controller_;
+ BrailleControllerImpl* braille_controller_;
+ };
+ typedef std::map<unsigned long, Connection*> WindowIdToConnectionMap;
+ BrailleControllerImpl();
+ virtual ~BrailleControllerImpl();
+ virtual void AddWindowOnIOThread(unsigned long windowId) OVERRIDE;
+ virtual void RemoveWindowOnIOThread(unsigned long windowId) OVERRIDE;
+ void StartConnections();
+ void StopConnections();
+ void OnSocketDirChanged(const base::FilePath& path, bool error);
+ void DisconnectAll();
+ void ReconnectAll();
+ void UpdateConnections();
+ void TryConnection(unsigned long windowId, Connection* connection);
+ void DispatchKeys(Connection* connection);
+ scoped_ptr<KeyEvent> MapKeyCode(brlapi_keyCode_t code);
+ void DispatchKeyEvent(scoped_ptr<KeyEvent> event);
+ brlapi_error_t* BrlApiError();
+ std::string BrlApiStrError();
+ LibBrlApiLoader libbrlapi_loader_;
+ // Manipulated on the IO thread.
+ WindowIdToConnectionMap connections_;
+ base::FilePathWatcher file_path_watcher_;
+ // Manipulated on the UI thread.
+ ObserverList<Observer> observers_;
+ bool watching_dir_;
+ friend struct DefaultSingletonTraits<BrailleControllerImpl>;
+ DISALLOW_COPY_AND_ASSIGN(BrailleControllerImpl);
+};
+
+BrailleController::BrailleController() {
+}
+
+BrailleController::~BrailleController() {
+}
+
+// static
+BrailleController* BrailleController::GetInstance() {
+ return BrailleControllerImpl::GetInstance();
+}
+
+// static
+void BrailleController::AddWindow(unsigned long windowId) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrailleController::AddWindow,
+ windowId));
+ } else {
+ GetInstance()->AddWindowOnIOThread(windowId);
+ }
+}
+
+// static
+void BrailleController::RemoveWindow(unsigned long windowId) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrailleController::RemoveWindow,
+ windowId));
+ } else {
+ GetInstance()->RemoveWindowOnIOThread(windowId);
+ }
+}
+
+BrailleControllerImpl::BrailleControllerImpl() {
+ if (!libbrlapi_loader_.Load("libbrlapi.so.0.5")) {
+ LOG(ERROR) << "Couldn't load libbrlapi: " << strerror(errno);
+ return;
+ }
+}
+
+BrailleControllerImpl::~BrailleControllerImpl() {
+}
+
+// static
+BrailleControllerImpl* BrailleControllerImpl::GetInstance() {
+ return Singleton<BrailleControllerImpl,
+ LeakySingletonTraits<BrailleControllerImpl> >::get();
+}
+
+scoped_ptr<base::DictionaryValue> BrailleControllerImpl::GetDisplayState()
+ OVERRIDE {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ DisplayState displayState;
+ for (WindowIdToConnectionMap::const_iterator i = connections_.begin();
+ i != connections_.end(); ++i) {
+ if (!i->second) {
+ continue;
+ }
+ brlapi_handle_t* handle = i->second->GetHandle();
+ unsigned int columns, lines;
+ if (libbrlapi_loader_.brlapi__getDisplaySize(handle, &columns, &lines)
+ < 0) {
+ // TODO: handle error.
+ continue;
+ }
+ if (columns > 0) {
+ displayState.available = true;
+ displayState.text_cells.reset(new int(columns * lines));
+ break;
+ }
+ }
+ return displayState.ToValue().Pass();
+}
+
+void BrailleControllerImpl::WriteText(
+ const std::string& text, int cursor) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ if (!libbrlapi_loader_.loaded()) {
+ return;
+ }
+ // Only ascii for testing.
+ std::string asciiText;
+ asciiText.reserve(text.length());
+ for (std::string::const_iterator i = text.begin(); i != text.end(); ++i) {
+ if (static_cast<unsigned char>(*i) < 0x80) {
+ asciiText += *i;
+ } else {
+ asciiText.append("?");
+ }
+ }
+ if (cursor < 0) {
+ cursor = BRLAPI_CURSOR_OFF;
+ } else {
+ cursor += 1; // ONe-based in brlapi.
+ }
+ for (WindowIdToConnectionMap::const_iterator i = connections_.begin();
+ i != connections_.end(); ++i) {
+ if (!i->second) {
+ continue;
+ }
+ brlapi_handle_t* handle = i->second->GetHandle();
+ if (libbrlapi_loader_.brlapi__writeText(
+ handle, cursor, asciiText.c_str()) < 0) {
+ LOG(ERROR) << "Couldn't write to window %lu" << i->first;
+ }
+ }
+}
+
+void BrailleControllerImpl::AddObserver(Observer* observer) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ observers_.AddObserver(observer);
+ if (libbrlapi_loader_.loaded() && !watching_dir_) {
+ watching_dir_ = true;
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrailleControllerImpl::StartConnections,
+ base::Unretained(this)));
+ }
+}
+
+void BrailleControllerImpl::RemoveObserver(Observer* observer) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ observers_.RemoveObserver(observer);
+}
+
+void BrailleControllerImpl::AddWindowOnIOThread(unsigned long windowId) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ LOG(ERROR) << "Setting up braille for window " << windowId;
+ if (!libbrlapi_loader_.loaded()) {
+ LOG(INFO) << "libbrlapi not loaded";
+ return;
+ }
+ WindowIdToConnectionMap::iterator it = connections_.find(windowId);
+ if (it != connections_.end()) {
+ LOG(WARNING) << "BrlAPI connection already exists for window " << windowId;
+ } else {
+ it = connections_.insert(
+ std::make_pair(windowId, new Connection(this))).first;
+ }
+ TryConnection(it->first, it->second);
+}
+
+void BrailleControllerImpl::RemoveWindowOnIOThread(unsigned long windowId) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ LOG(ERROR) << "Tearing down braille for window " << windowId;
+ if (!libbrlapi_loader_.loaded()) {
+ return;
+ }
+ WindowIdToConnectionMap::iterator it = connections_.find(windowId);
+ if (it == connections_.end()) {
+ return;
+ }
+ delete it->second;
+ connections_.erase(it);
+}
+
+void BrailleControllerImpl::StartConnections() {
+ base::FilePath brlapi_dir(BRLAPI_SOCKETPATH);
+ if (!file_path_watcher_.Watch(
+ brlapi_dir, false, base::Bind(
+ &BrailleControllerImpl::OnSocketDirChanged,
+ base::Unretained(this)))) {
+ LOG(WARNING) << "Couldn't watch brlapi directory " << BRLAPI_SOCKETPATH;
+ return;
+ }
+ UpdateConnections();
+}
+
+void BrailleControllerImpl::OnSocketDirChanged(const base::FilePath& path,
+ bool error) {
+ if (error) {
+ LOG(WARNING) << "Error watching brlapi path: " << path.value();
+ return;
+ }
+ LOG(ERROR) << "BrlAPI directory changed";
+ BrowserThread::PostDelayedTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &BrailleControllerImpl::UpdateConnections,
+ base::Unretained(this)),
+ TimeDelta::FromSeconds(1));
+
+}
+
+void BrailleControllerImpl::DisconnectAll() {
+ for (WindowIdToConnectionMap::iterator i = connections_.begin();
+ i != connections_.end(); ++i) {
+ delete i->second;
+ }
+ connections_.clear();
+}
+
+void BrailleControllerImpl::ReconnectAll() {
+ DisconnectAll();
+ UpdateConnections();
+}
+
+void BrailleControllerImpl::UpdateConnections() {
+ for (WindowIdToConnectionMap::const_iterator i = connections_.begin();
+ i != connections_.end(); ++i) {
+ if (i->second->Connected()) {
+ continue;
+ }
+ TryConnection(i->first, i->second);
+ }
+}
+
+void BrailleControllerImpl::TryConnection(unsigned long windowId,
+ Connection* connection) {
+ if (!connection->Connect()) {
+ LOG(ERROR) << "Couldn't connect to brlapi\n";
+ return;
+ }
+ int path[2] = {0, 0};
+ int pathElements = 0;
+#if defined(OS_CHROMEOS)
+ if (base::chromeos::IsRunningOnChromeOS()) {
+ path[pathElements++] = kDefaultTtyChromeOS;
+ }
+#endif
+ if (pathElements == 0 && getenv("WINDOWPATH") == NULL) {
+ path[pathElements++] = kDefaultTtyLinux;
+ path[pathElements++] = windowId;
+ }
+ if (libbrlapi_loader_.brlapi__enterTtyModeWithPath(
+ connection->GetHandle(), path, pathElements, NULL) < 0) {
+ LOG(ERROR) << "brlapi: couldn't enter tty mode: " << BrlApiStrError();
+ connection->Disconnect();
+ return;
+ }
+ if (libbrlapi_loader_.brlapi__writeText(
+ connection->GetHandle(), 0, "Chrome") < 0) {
+ LOG(ERROR) << "Couldn't write window title to brlapi: "
+ << BrlApiStrError();
+ connection->Disconnect();
+ return;
+ }
+ const brlapi_keyCode_t extraKeys[] = {
+ BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_OFFLINE,
+ };
+ if (libbrlapi_loader_.brlapi__acceptKeys(
+ connection->GetHandle(),
+ brlapi_rangeType_command,
+ extraKeys,
+ arraysize(extraKeys)) < 0) {
+ LOG(ERROR) << "Couldn't acceptKeys: " << BrlApiStrError();
+ connection->Disconnect();
+ return;
+ }
+}
+
+scoped_ptr<KeyEvent> BrailleControllerImpl::MapKeyCode(brlapi_keyCode_t code) {
+ // TODO: Make this a table.
+ brlapi_expandedKeyCode_t expanded;
+ if (libbrlapi_loader_.brlapi_expandKeyCode(code, &expanded) != 0) {
+ // TODO: Log error.
+ return scoped_ptr<KeyEvent>();
+ }
+ KeyCommand keyCommand = KEY_COMMAND_NONE;
+ switch (expanded.type) {
+ case BRLAPI_KEY_TYPE_CMD:
+ switch (expanded.command) {
+ case BRLAPI_KEY_CMD_LNUP:
+ keyCommand = KEY_COMMAND_LINE_UP;
+ break;
+ case BRLAPI_KEY_CMD_LNDN:
+ keyCommand = KEY_COMMAND_LINE_DOWN;
+ break;
+ case BRLAPI_KEY_CMD_FWINLT:
+ keyCommand = KEY_COMMAND_PAN_LEFT;
+ break;
+ case BRLAPI_KEY_CMD_FWINRT:
+ keyCommand = KEY_COMMAND_PAN_RIGHT;
+ break;
+ }
+ break;
+ }
+ scoped_ptr<KeyEvent> result;
+ if (keyCommand != KEY_COMMAND_NONE) {
+ result.reset(new KeyEvent());
+ result->command = keyCommand;
+ }
+ return result.Pass();
+}
+
+void BrailleControllerImpl::DispatchKeys(Connection* connection) {
+ brlapi_handle_t* handle = connection->GetHandle();
+ brlapi_keyCode_t code;
+ while (true) {
+ int result = libbrlapi_loader_.brlapi__readKey(handle, 0 /*wait*/, &code);
+ if (result < 0) {
+ brlapi_error_t* err = BrlApiError();
+ if (err->brlerrno == BRLAPI_ERROR_LIBCERR && err->libcerrno == EINTR) {
+ continue;
+ }
+ break;
+ } else if (result == 0) {
+ return;
+ }
+ LOG(ERROR) << "Got key: %ld" << code;
+ scoped_ptr<KeyEvent> event = MapKeyCode(code);
+ if (event) {
+ LOG(ERROR) << "Dispatching key";
+ DispatchKeyEvent(event.Pass());
+ }
+ }
+
+ // Error, disconnect everything.
+ LOG(ERROR) << "BrlAPI error: " << BrlApiStrError();
+ DisconnectAll();
+}
+
+void BrailleControllerImpl::DispatchKeyEvent(scoped_ptr<KeyEvent> event) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &BrailleControllerImpl::DispatchKeyEvent,
+ base::Unretained(this),
+ base::Passed(&event)));
+ return;
+ }
+ FOR_EACH_OBSERVER(Observer, observers_, OnKeyEvent(event->ToValue()));
+}
+
+brlapi_error_t* BrailleControllerImpl::BrlApiError() {
+ DCHECK(libbrlapi_loader_.loaded());
+ return libbrlapi_loader_.brlapi_error_location();
+}
+
+std::string BrailleControllerImpl::BrlApiStrError() {
+ return libbrlapi_loader_.brlapi_strerror(BrlApiError());
+}
+} // namespace braille_private
+} // namespace api
+} // namespace extensions

Powered by Google App Engine
This is Rietveld 408576698