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 ~BrlapiConnectionImpl() override
{ Disconnect(); }
38 ConnectResult
Connect(const OnDataReadyCallback
& on_data_ready
) override
;
39 void Disconnect() override
;
40 bool Connected() override
{ return handle_
; }
41 brlapi_error_t
* BrlapiError() override
;
42 std::string
BrlapiStrError() override
;
43 bool GetDisplaySize(size_t* size
) override
;
44 bool WriteDots(const unsigned char* cells
) override
;
45 int ReadKey(brlapi_keyCode_t
* keyCode
) override
;
47 // MessageLoopForIO::Watcher
48 void OnFileCanReadWithoutBlocking(int fd
) override
{ on_data_ready_
.Run(); }
50 void OnFileCanWriteWithoutBlocking(int fd
) override
{}
53 bool CheckConnected();
54 ConnectResult
ConnectResultForError();
56 LibBrlapiLoader
* libbrlapi_loader_
;
57 scoped_ptr
<brlapi_handle_t
, base::FreeDeleter
> handle_
;
58 MessageLoopForIO::FileDescriptorWatcher fd_controller_
;
59 OnDataReadyCallback on_data_ready_
;
61 DISALLOW_COPY_AND_ASSIGN(BrlapiConnectionImpl
);
64 BrlapiConnection::BrlapiConnection() {
67 BrlapiConnection::~BrlapiConnection() {
70 scoped_ptr
<BrlapiConnection
> BrlapiConnection::Create(
71 LibBrlapiLoader
* loader
) {
72 DCHECK(loader
->loaded());
73 return scoped_ptr
<BrlapiConnection
>(new BrlapiConnectionImpl(loader
));
76 BrlapiConnection::ConnectResult
BrlapiConnectionImpl::Connect(
77 const OnDataReadyCallback
& on_data_ready
) {
79 handle_
.reset((brlapi_handle_t
*) malloc(
80 libbrlapi_loader_
->brlapi_getHandleSize()));
81 int fd
= libbrlapi_loader_
->brlapi__openConnection(handle_
.get(), NULL
, NULL
);
84 VLOG(1) << "Error connecting to brlapi: " << BrlapiStrError();
85 return ConnectResultForError();
89 #if defined(OS_CHROMEOS)
90 if (base::SysInfo::IsRunningOnChromeOS())
91 path
[pathElements
++] = kDefaultTtyChromeOS
;
93 if (pathElements
== 0 && getenv("WINDOWPATH") == NULL
)
94 path
[pathElements
++] = kDefaultTtyLinux
;
95 if (libbrlapi_loader_
->brlapi__enterTtyModeWithPath(
96 handle_
.get(), path
, pathElements
, NULL
) < 0) {
97 LOG(ERROR
) << "brlapi: couldn't enter tty mode: " << BrlapiStrError();
99 return CONNECT_ERROR_RETRY
;
103 if (!GetDisplaySize(&size
)) {
104 // Error already logged.
106 return CONNECT_ERROR_RETRY
;
109 // A display size of 0 means no display connected. We can't reliably
110 // detect when a display gets connected, so fail and let the caller
113 VLOG(1) << "No braille display connected";
115 return CONNECT_ERROR_RETRY
;
118 const brlapi_keyCode_t extraKeys
[] = {
119 BRLAPI_KEY_TYPE_CMD
| BRLAPI_KEY_CMD_OFFLINE
,
120 // brltty 5.1 converts dot input to Unicode characters unless we
121 // explicitly accept this command.
122 BRLAPI_KEY_TYPE_CMD
| BRLAPI_KEY_CMD_PASSDOTS
,
124 if (libbrlapi_loader_
->brlapi__acceptKeys(
125 handle_
.get(), brlapi_rangeType_command
, extraKeys
,
126 arraysize(extraKeys
)) < 0) {
127 LOG(ERROR
) << "Couldn't acceptKeys: " << BrlapiStrError();
129 return CONNECT_ERROR_RETRY
;
132 if (!MessageLoopForIO::current()->WatchFileDescriptor(
133 fd
, true, MessageLoopForIO::WATCH_READ
, &fd_controller_
, this)) {
134 LOG(ERROR
) << "Couldn't watch file descriptor " << fd
;
136 return CONNECT_ERROR_RETRY
;
139 on_data_ready_
= on_data_ready
;
141 return CONNECT_SUCCESS
;
144 void BrlapiConnectionImpl::Disconnect() {
148 fd_controller_
.StopWatchingFileDescriptor();
149 libbrlapi_loader_
->brlapi__closeConnection(
154 brlapi_error_t
* BrlapiConnectionImpl::BrlapiError() {
155 return libbrlapi_loader_
->brlapi_error_location();
158 std::string
BrlapiConnectionImpl::BrlapiStrError() {
159 return libbrlapi_loader_
->brlapi_strerror(BrlapiError());
162 bool BrlapiConnectionImpl::GetDisplaySize(size_t* size
) {
163 if (!CheckConnected()) {
166 unsigned int columns
, rows
;
167 if (libbrlapi_loader_
->brlapi__getDisplaySize(
168 handle_
.get(), &columns
, &rows
) < 0) {
169 LOG(ERROR
) << "Couldn't get braille display size " << BrlapiStrError();
172 *size
= columns
* rows
;
176 bool BrlapiConnectionImpl::WriteDots(const unsigned char* cells
) {
177 if (!CheckConnected())
179 if (libbrlapi_loader_
->brlapi__writeDots(handle_
.get(), cells
) < 0) {
180 VLOG(1) << "Couldn't write to brlapi: " << BrlapiStrError();
186 int BrlapiConnectionImpl::ReadKey(brlapi_keyCode_t
* key_code
) {
187 if (!CheckConnected())
189 return libbrlapi_loader_
->brlapi__readKey(
190 handle_
.get(), 0 /*wait*/, key_code
);
193 bool BrlapiConnectionImpl::CheckConnected() {
195 BrlapiError()->brlerrno
= BRLAPI_ERROR_ILLEGAL_INSTRUCTION
;
201 BrlapiConnection::ConnectResult
BrlapiConnectionImpl::ConnectResultForError() {
202 const brlapi_error_t
* error
= BrlapiError();
203 // For the majority of users, the socket file will never exist because
204 // the daemon is never run. Avoid retrying in this case, relying on
205 // the socket directory to change and trigger further tries if the
206 // daemon comes up later on.
207 if (error
->brlerrno
== BRLAPI_ERROR_LIBCERR
208 && error
->libcerrno
== ENOENT
) {
209 return CONNECT_ERROR_NO_RETRY
;
211 return CONNECT_ERROR_RETRY
;
214 } // namespace braille_display_private
216 } // namespace extensions