Index: chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc |
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 |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ccca763bebfe65be8d1fc3379b23b1f43fbec818 |
--- /dev/null |
+++ b/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc |
@@ -0,0 +1,409 @@ |
+// 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_display_private/braille_controller.h" |
+ |
+#include <algorithm> |
+#include <cerrno> |
+#include <cstring> |
+#include <vector> |
+ |
+#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 "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_display_private { |
+namespace { |
+// Default virtual terminal. This can be overriden by setting the |
+// WINDOWPATH environment variable. This is only used when not running |
+// under CrhomeOS (that is in aura for a Linux desktop). |
+// TODO(plundblad): Find a way to detect the controlling terminal of the |
+// X server. |
+static const int kDefaultTtyLinux = 7; |
+#if defined(OS_CHROMEOS) |
+// The GUI is always running on vt1 in 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 WriteDots(const std::string& cells) OVERRIDE; |
+ virtual void AddObserver(BrailleObserver* observer) OVERRIDE; |
+ virtual void RemoveObserver(BrailleObserver* observer) OVERRIDE; |
+ |
+ private: |
+ class Connection : public MessageLoopForIO::Watcher { |
dmazzoni
2013/09/06 16:49:53
Since there's only one connection at a time, why i
|
+ public: |
+ explicit Connection(BrailleControllerImpl* braille_controller) : |
+ braille_controller_(braille_controller) { |
+ } |
+ |
+ virtual ~Connection() { |
+ Disconnect(); |
+ } |
+ |
+ bool Connect() { |
dmazzoni
2013/09/06 16:49:53
Don't inline nontrivial functions in the class def
|
+ DCHECK(!handle_); |
+ if (!braille_controller_->libbrlapi_loader_.loaded()) { |
+ return false; |
+ } |
+ 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)) { |
+ LOG(ERROR) << "Couldn't watch file descriptor " << fd; |
+ Disconnect(); |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ void Disconnect() { |
+ if (handle_ == NULL) { |
+ return; |
+ } |
+ fd_controller_.StopWatchingFileDescriptor(); |
+ braille_controller_->libbrlapi_loader_.brlapi__closeConnection( |
+ handle_.get()); |
+ handle_.reset(); |
+ } |
+ |
+ bool Connected() { return handle_; } |
+ |
+ brlapi_handle_t* GetHandle() { return handle_.get(); } |
+ |
+ // Gets the total size of the display, which may be 0 if no display is |
+ // present, returning true on success. Note that this is cached in the |
+ // brlapi client so it is cheap. |
+ bool GetDisplaySize(size_t* size) { |
+ if (!handle_) { |
+ return false; |
+ } |
+ unsigned int columns, rows; |
+ if (braille_controller_->libbrlapi_loader_.brlapi__getDisplaySize( |
+ handle_.get(), &columns, &rows) < 0) { |
+ return false; |
+ } |
+ *size = columns * rows; |
+ return true; |
+ } |
+ |
+ // MessageLoopForIO::Watcher |
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE { |
+ braille_controller_->DispatchKeys(); |
+ } |
+ |
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {} |
+ |
+ private: |
+ scoped_ptr<brlapi_handle_t, base::FreeDeleter> handle_; |
+ MessageLoopForIO::FileDescriptorWatcher fd_controller_; |
+ BrailleControllerImpl* braille_controller_; |
+ }; |
+ BrailleControllerImpl(); |
dmazzoni
2013/09/06 16:49:53
Nit: blank line before
|
+ virtual ~BrailleControllerImpl(); |
+ void TryLoadLibBrlApi(); |
+ void StartConnections(); |
+ void StopConnections(); |
+ void OnSocketDirChanged(const base::FilePath& path, bool error); |
+ void ReconnectAll(); |
+ void UpdateConnections(); |
+ void TryConnection(); |
+ void DispatchKeys(); |
+ scoped_ptr<KeyEvent> MapKeyCode(brlapi_keyCode_t code); |
dmazzoni
2013/09/06 16:49:53
Nit: blank line between functions and data members
|
+ void DispatchKeyEvent(scoped_ptr<KeyEvent> event); |
dmazzoni
2013/09/06 16:49:53
Nit: put all functions, then all data members rath
|
+ brlapi_error_t* BrlApiError(); |
+ std::string BrlApiStrError(); |
+ LibBrlApiLoader libbrlapi_loader_; |
+ // Manipulated on the IO thread. |
+ Connection connection_; |
+ base::FilePathWatcher file_path_watcher_; |
+ // Manipulated on the UI thread. |
+ ObserverList<BrailleObserver> observers_; |
+ bool watching_dir_; |
+ friend struct DefaultSingletonTraits<BrailleControllerImpl>; |
+ DISALLOW_COPY_AND_ASSIGN(BrailleControllerImpl); |
+}; |
+ |
+BrailleController::BrailleController() { |
+} |
+ |
+BrailleController::~BrailleController() { |
+} |
+ |
+// static |
+BrailleController* BrailleController::GetInstance() { |
+ return BrailleControllerImpl::GetInstance(); |
+} |
+ |
+BrailleControllerImpl::BrailleControllerImpl() |
+ : connection_(this), watching_dir_(false) { |
+ TryLoadLibBrlApi(); |
+} |
+ |
+BrailleControllerImpl::~BrailleControllerImpl() { |
+} |
+ |
+void BrailleControllerImpl::TryLoadLibBrlApi() { |
+ if (libbrlapi_loader_.loaded()) |
+ return; |
+ // These versions of libbrlapi work the same for the functions we |
+ // are using. (0.6.0 adds brlapi_writeWText). |
+ static const char* kSupportedVersions[] = { |
+ "libbrlapi.so.0.5", |
+ "libbrlapi.so.0.6" |
+ }; |
+ for (size_t i = 0; i < arraysize(kSupportedVersions); ++i) { |
+ if (libbrlapi_loader_.Load(kSupportedVersions[i])) |
+ return; |
+ } |
+ LOG(ERROR) << "Couldn't load libbrlapi: " << strerror(errno); |
+} |
+ |
+// static |
+BrailleControllerImpl* BrailleControllerImpl::GetInstance() { |
+ return Singleton<BrailleControllerImpl, |
+ LeakySingletonTraits<BrailleControllerImpl> >::get(); |
+} |
+ |
+scoped_ptr<base::DictionaryValue> BrailleControllerImpl::GetDisplayState() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ DisplayState displayState; |
+ if (connection_.Connected()) { |
+ size_t size; |
+ if (!connection_.GetDisplaySize(&size)) { |
+ LOG(ERROR) << "Couldn't get braille display size " << BrlApiStrError(); |
+ connection_.Disconnect(); |
+ } else if (size > 0) { // size == 0 means no display present. |
+ displayState.available = true; |
+ displayState.text_cell_count.reset(new int(size)); |
+ } |
+ } |
+ return displayState.ToValue().Pass(); |
+} |
+ |
+void BrailleControllerImpl::WriteDots(const std::string& cells) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ if (!libbrlapi_loader_.loaded()) |
+ return; |
+ if (connection_.Connected()) { |
+ brlapi_handle_t* handle = connection_.GetHandle(); |
+ size_t size; |
+ if (!connection_.GetDisplaySize(&size)) { |
+ LOG(ERROR) << "Couldn't get display size " << BrlApiStrError(); |
+ connection_.Disconnect(); |
+ } |
+ std::vector<unsigned char> sizedCells(size); |
+ std::memcpy(&sizedCells[0], cells.data(), std::min(cells.size(), size)); |
+ if (size > cells.size()) |
+ std::fill(sizedCells.begin() + cells.size(), sizedCells.end(), 0); |
+ if (libbrlapi_loader_.brlapi__writeDots(handle, &sizedCells[0]) < 0) { |
+ LOG(ERROR) << "Couldn't write to brlapi: " << BrlApiStrError(); |
+ connection_.Disconnect(); |
+ } |
+ } |
+} |
+ |
+void BrailleControllerImpl::AddObserver(BrailleObserver* 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(BrailleObserver* observer) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ observers_.RemoveObserver(observer); |
+} |
+ |
+void BrailleControllerImpl::StartConnections() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ base::FilePath brlapi_dir(BRLAPI_SOCKETPATH); |
+ LOG(INFO) << "Watching brlapi directory: " << 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(ERROR) << "Error watching brlapi directory: " << path.value(); |
+ return; |
+ } |
+ LOG(INFO) << "BrlAPI directory changed"; |
+ BrowserThread::PostDelayedTask(BrowserThread::IO, FROM_HERE, |
+ base::Bind( |
+ &BrailleControllerImpl::UpdateConnections, |
+ base::Unretained(this)), |
+ TimeDelta::FromSeconds(1)); |
+ |
+} |
+ |
+void BrailleControllerImpl::UpdateConnections() { |
+ if (!connection_.Connected()) |
+ TryConnection(); |
+} |
+ |
+void BrailleControllerImpl::TryConnection() { |
+ 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; |
+ if (libbrlapi_loader_.brlapi__enterTtyModeWithPath( |
+ connection_.GetHandle(), path, pathElements, NULL) < 0) { |
+ LOG(ERROR) << "brlapi: couldn't enter tty mode: " << 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) { |
+ brlapi_expandedKeyCode_t expanded; |
+ if (libbrlapi_loader_.brlapi_expandKeyCode(code, &expanded) != 0) { |
+ LOG(ERROR) << "Couldn't expand key 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; |
+ // The 8 low-order bits in the argument contains the dots. |
+ result->braille_dots.reset(new int(expanded.argument & 0xf)); |
+ 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() { |
+ brlapi_handle_t* handle = connection_.GetHandle(); |
+ DCHECK(handle != NULL); |
+ brlapi_keyCode_t code; |
+ while (true) { |
+ int result = libbrlapi_loader_.brlapi__readKey(handle, 0 /*wait*/, &code); |
+ if (result < 0) { // Error. |
+ brlapi_error_t* err = BrlApiError(); |
+ if (err->brlerrno == BRLAPI_ERROR_LIBCERR && err->libcerrno == EINTR) |
+ continue; |
+ // Disconnect on other errors. |
+ LOG(ERROR) << "BrlAPI error: " << BrlApiStrError(); |
+ connection_.Disconnect(); |
+ return; |
+ } else if (result == 0) { // No more data. |
+ return; |
+ } |
+ scoped_ptr<KeyEvent> event = MapKeyCode(code); |
+ if (event) |
+ DispatchKeyEvent(event.Pass()); |
+ } |
+} |
+ |
+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(BrailleObserver, observers_, OnKeyEvent(*event)); |
+} |
+ |
+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_display_private |
+} // namespace api |
+} // namespace extensions |