Home | History | Annotate | Download | only in braille_display_private
      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.
      4 
      5 #include "chrome/browser/extensions/api/braille_display_private/brlapi_connection.h"
      6 
      7 #include <errno.h>
      8 
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/sys_info.h"
     11 
     12 namespace extensions {
     13 using base::MessageLoopForIO;
     14 namespace api {
     15 namespace braille_display_private {
     16 
     17 namespace {
     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
     22 // X server.
     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;
     27 #endif
     28 }  // namespace
     29 
     30 class BrlapiConnectionImpl : public BrlapiConnection,
     31                              MessageLoopForIO::Watcher {
     32  public:
     33   explicit BrlapiConnectionImpl(LibBrlapiLoader* loader) :
     34       libbrlapi_loader_(loader) {}
     35 
     36   virtual ~BrlapiConnectionImpl() {
     37     Disconnect();
     38   }
     39 
     40   virtual ConnectResult Connect(const OnDataReadyCallback& on_data_ready)
     41       OVERRIDE;
     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;
     49 
     50   // MessageLoopForIO::Watcher
     51   virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
     52     on_data_ready_.Run();
     53   }
     54 
     55   virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
     56 
     57  private:
     58   bool CheckConnected();
     59   ConnectResult ConnectResultForError();
     60 
     61   LibBrlapiLoader* libbrlapi_loader_;
     62   scoped_ptr<brlapi_handle_t, base::FreeDeleter> handle_;
     63   MessageLoopForIO::FileDescriptorWatcher fd_controller_;
     64   OnDataReadyCallback on_data_ready_;
     65 
     66   DISALLOW_COPY_AND_ASSIGN(BrlapiConnectionImpl);
     67 };
     68 
     69 BrlapiConnection::BrlapiConnection() {
     70 }
     71 
     72 BrlapiConnection::~BrlapiConnection() {
     73 }
     74 
     75 scoped_ptr<BrlapiConnection> BrlapiConnection::Create(
     76     LibBrlapiLoader* loader) {
     77   DCHECK(loader->loaded());
     78   return scoped_ptr<BrlapiConnection>(new BrlapiConnectionImpl(loader));
     79 }
     80 
     81 BrlapiConnection::ConnectResult BrlapiConnectionImpl::Connect(
     82     const OnDataReadyCallback& on_data_ready) {
     83   DCHECK(!handle_);
     84   handle_.reset((brlapi_handle_t*) malloc(
     85       libbrlapi_loader_->brlapi_getHandleSize()));
     86   int fd = libbrlapi_loader_->brlapi__openConnection(handle_.get(), NULL, NULL);
     87   if (fd < 0) {
     88     handle_.reset();
     89     VLOG(1) << "Error connecting to brlapi: " << BrlapiStrError();
     90     return ConnectResultForError();
     91   }
     92   int path[2] = {0, 0};
     93   int pathElements = 0;
     94 #if defined(OS_CHROMEOS)
     95   if (base::SysInfo::IsRunningOnChromeOS())
     96     path[pathElements++] = kDefaultTtyChromeOS;
     97 #endif
     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();
    103     Disconnect();
    104     return CONNECT_ERROR_RETRY;
    105   }
    106 
    107   size_t size;
    108   if (!GetDisplaySize(&size)) {
    109     // Error already logged.
    110     Disconnect();
    111     return CONNECT_ERROR_RETRY;
    112   }
    113 
    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
    116   // retry connecting.
    117   if (size == 0) {
    118     VLOG(1) << "No braille display connected";
    119     Disconnect();
    120     return CONNECT_ERROR_RETRY;
    121   }
    122 
    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,
    128   };
    129   if (libbrlapi_loader_->brlapi__acceptKeys(
    130           handle_.get(), brlapi_rangeType_command, extraKeys,
    131           arraysize(extraKeys)) < 0) {
    132     LOG(ERROR) << "Couldn't acceptKeys: " << BrlapiStrError();
    133     Disconnect();
    134     return CONNECT_ERROR_RETRY;
    135   }
    136 
    137   if (!MessageLoopForIO::current()->WatchFileDescriptor(
    138           fd, true, MessageLoopForIO::WATCH_READ, &fd_controller_, this)) {
    139     LOG(ERROR) << "Couldn't watch file descriptor " << fd;
    140     Disconnect();
    141     return CONNECT_ERROR_RETRY;
    142   }
    143 
    144   on_data_ready_ = on_data_ready;
    145 
    146   return CONNECT_SUCCESS;
    147 }
    148 
    149 void BrlapiConnectionImpl::Disconnect() {
    150   if (!handle_) {
    151     return;
    152   }
    153   fd_controller_.StopWatchingFileDescriptor();
    154   libbrlapi_loader_->brlapi__closeConnection(
    155       handle_.get());
    156   handle_.reset();
    157 }
    158 
    159 brlapi_error_t* BrlapiConnectionImpl::BrlapiError() {
    160   return libbrlapi_loader_->brlapi_error_location();
    161 }
    162 
    163 std::string BrlapiConnectionImpl::BrlapiStrError() {
    164   return libbrlapi_loader_->brlapi_strerror(BrlapiError());
    165 }
    166 
    167 bool BrlapiConnectionImpl::GetDisplaySize(size_t* size) {
    168   if (!CheckConnected()) {
    169     return false;
    170   }
    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();
    175     return false;
    176   }
    177   *size = columns * rows;
    178   return true;
    179 }
    180 
    181 bool BrlapiConnectionImpl::WriteDots(const unsigned char* cells) {
    182   if (!CheckConnected())
    183     return false;
    184   if (libbrlapi_loader_->brlapi__writeDots(handle_.get(), cells) < 0) {
    185     VLOG(1) << "Couldn't write to brlapi: " << BrlapiStrError();
    186     return false;
    187   }
    188   return true;
    189 }
    190 
    191 int BrlapiConnectionImpl::ReadKey(brlapi_keyCode_t* key_code) {
    192   if (!CheckConnected())
    193     return -1;
    194   return libbrlapi_loader_->brlapi__readKey(
    195       handle_.get(), 0 /*wait*/, key_code);
    196 }
    197 
    198 bool BrlapiConnectionImpl::CheckConnected() {
    199   if (!handle_) {
    200     BrlapiError()->brlerrno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
    201     return false;
    202   }
    203   return true;
    204 }
    205 
    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;
    215   }
    216   return CONNECT_ERROR_RETRY;
    217 }
    218 
    219 }  // namespace braille_display_private
    220 }  // namespace api
    221 }  // namespace extensions
    222