1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/braille_display_private/brlapi_connection.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/sys_info.h"
12 namespace extensions {
13 using base::MessageLoopForIO;
15 namespace braille_display_private {
18 // Default virtual terminal. This can be overriden by setting the
19 // WINDOWPATH environment variable. This is only used when not running
20 // under Crhome OS (that is in aura for a Linux desktop).
21 // TODO(plundblad): Find a way to detect the controlling terminal of the
23 static const int kDefaultTtyLinux = 7;
24 #if defined(OS_CHROMEOS)
25 // The GUI is always running on vt1 in Chrome OS.
26 static const int kDefaultTtyChromeOS = 1;
30 class BrlapiConnectionImpl : public BrlapiConnection,
31 MessageLoopForIO::Watcher {
33 explicit BrlapiConnectionImpl(LibBrlapiLoader* loader) :
34 libbrlapi_loader_(loader) {}
36 virtual ~BrlapiConnectionImpl() {
40 virtual ConnectResult Connect(const OnDataReadyCallback& on_data_ready)
42 virtual void Disconnect() override;
43 virtual bool Connected() override { return handle_; }
44 virtual brlapi_error_t* BrlapiError() override;
45 virtual std::string BrlapiStrError() override;
46 virtual bool GetDisplaySize(size_t* size) override;
47 virtual bool WriteDots(const unsigned char* cells) override;
48 virtual int ReadKey(brlapi_keyCode_t* keyCode) override;
50 // MessageLoopForIO::Watcher
51 virtual void OnFileCanReadWithoutBlocking(int fd) override {
55 virtual void OnFileCanWriteWithoutBlocking(int fd) override {}
58 bool CheckConnected();
59 ConnectResult ConnectResultForError();
61 LibBrlapiLoader* libbrlapi_loader_;
62 scoped_ptr<brlapi_handle_t, base::FreeDeleter> handle_;
63 MessageLoopForIO::FileDescriptorWatcher fd_controller_;
64 OnDataReadyCallback on_data_ready_;
66 DISALLOW_COPY_AND_ASSIGN(BrlapiConnectionImpl);
69 BrlapiConnection::BrlapiConnection() {
72 BrlapiConnection::~BrlapiConnection() {
75 scoped_ptr<BrlapiConnection> BrlapiConnection::Create(
76 LibBrlapiLoader* loader) {
77 DCHECK(loader->loaded());
78 return scoped_ptr<BrlapiConnection>(new BrlapiConnectionImpl(loader));
81 BrlapiConnection::ConnectResult BrlapiConnectionImpl::Connect(
82 const OnDataReadyCallback& on_data_ready) {
84 handle_.reset((brlapi_handle_t*) malloc(
85 libbrlapi_loader_->brlapi_getHandleSize()));
86 int fd = libbrlapi_loader_->brlapi__openConnection(handle_.get(), NULL, NULL);
89 VLOG(1) << "Error connecting to brlapi: " << BrlapiStrError();
90 return ConnectResultForError();
94 #if defined(OS_CHROMEOS)
95 if (base::SysInfo::IsRunningOnChromeOS())
96 path[pathElements++] = kDefaultTtyChromeOS;
98 if (pathElements == 0 && getenv("WINDOWPATH") == NULL)
99 path[pathElements++] = kDefaultTtyLinux;
100 if (libbrlapi_loader_->brlapi__enterTtyModeWithPath(
101 handle_.get(), path, pathElements, NULL) < 0) {
102 LOG(ERROR) << "brlapi: couldn't enter tty mode: " << BrlapiStrError();
104 return CONNECT_ERROR_RETRY;
108 if (!GetDisplaySize(&size)) {
109 // Error already logged.
111 return CONNECT_ERROR_RETRY;
114 // A display size of 0 means no display connected. We can't reliably
115 // detect when a display gets connected, so fail and let the caller
118 VLOG(1) << "No braille display connected";
120 return CONNECT_ERROR_RETRY;
123 const brlapi_keyCode_t extraKeys[] = {
124 BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_OFFLINE,
125 // brltty 5.1 converts dot input to Unicode characters unless we
126 // explicitly accept this command.
127 BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_PASSDOTS,
129 if (libbrlapi_loader_->brlapi__acceptKeys(
130 handle_.get(), brlapi_rangeType_command, extraKeys,
131 arraysize(extraKeys)) < 0) {
132 LOG(ERROR) << "Couldn't acceptKeys: " << BrlapiStrError();
134 return CONNECT_ERROR_RETRY;
137 if (!MessageLoopForIO::current()->WatchFileDescriptor(
138 fd, true, MessageLoopForIO::WATCH_READ, &fd_controller_, this)) {
139 LOG(ERROR) << "Couldn't watch file descriptor " << fd;
141 return CONNECT_ERROR_RETRY;
144 on_data_ready_ = on_data_ready;
146 return CONNECT_SUCCESS;
149 void BrlapiConnectionImpl::Disconnect() {
153 fd_controller_.StopWatchingFileDescriptor();
154 libbrlapi_loader_->brlapi__closeConnection(
159 brlapi_error_t* BrlapiConnectionImpl::BrlapiError() {
160 return libbrlapi_loader_->brlapi_error_location();
163 std::string BrlapiConnectionImpl::BrlapiStrError() {
164 return libbrlapi_loader_->brlapi_strerror(BrlapiError());
167 bool BrlapiConnectionImpl::GetDisplaySize(size_t* size) {
168 if (!CheckConnected()) {
171 unsigned int columns, rows;
172 if (libbrlapi_loader_->brlapi__getDisplaySize(
173 handle_.get(), &columns, &rows) < 0) {
174 LOG(ERROR) << "Couldn't get braille display size " << BrlapiStrError();
177 *size = columns * rows;
181 bool BrlapiConnectionImpl::WriteDots(const unsigned char* cells) {
182 if (!CheckConnected())
184 if (libbrlapi_loader_->brlapi__writeDots(handle_.get(), cells) < 0) {
185 VLOG(1) << "Couldn't write to brlapi: " << BrlapiStrError();
191 int BrlapiConnectionImpl::ReadKey(brlapi_keyCode_t* key_code) {
192 if (!CheckConnected())
194 return libbrlapi_loader_->brlapi__readKey(
195 handle_.get(), 0 /*wait*/, key_code);
198 bool BrlapiConnectionImpl::CheckConnected() {
200 BrlapiError()->brlerrno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
206 BrlapiConnection::ConnectResult BrlapiConnectionImpl::ConnectResultForError() {
207 const brlapi_error_t* error = BrlapiError();
208 // For the majority of users, the socket file will never exist because
209 // the daemon is never run. Avoid retrying in this case, relying on
210 // the socket directory to change and trigger further tries if the
211 // daemon comes up later on.
212 if (error->brlerrno == BRLAPI_ERROR_LIBCERR
213 && error->libcerrno == ENOENT) {
214 return CONNECT_ERROR_NO_RETRY;
216 return CONNECT_ERROR_RETRY;
219 } // namespace braille_display_private
221 } // namespace extensions