// Copyright 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/brlapi_connection.h" #include <errno.h> #include "base/message_loop/message_loop.h" #include "base/sys_info.h" namespace extensions { using base::MessageLoopForIO; 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 Crhome OS (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 Chrome OS. static const int kDefaultTtyChromeOS = 1; #endif } // namespace class BrlapiConnectionImpl : public BrlapiConnection, MessageLoopForIO::Watcher { public: explicit BrlapiConnectionImpl(LibBrlapiLoader* loader) : libbrlapi_loader_(loader) {} virtual ~BrlapiConnectionImpl() { Disconnect(); } virtual ConnectResult Connect(const OnDataReadyCallback& on_data_ready) override; virtual void Disconnect() override; virtual bool Connected() override { return handle_; } virtual brlapi_error_t* BrlapiError() override; virtual std::string BrlapiStrError() override; virtual bool GetDisplaySize(size_t* size) override; virtual bool WriteDots(const unsigned char* cells) override; virtual int ReadKey(brlapi_keyCode_t* keyCode) override; // MessageLoopForIO::Watcher virtual void OnFileCanReadWithoutBlocking(int fd) override { on_data_ready_.Run(); } virtual void OnFileCanWriteWithoutBlocking(int fd) override {} private: bool CheckConnected(); ConnectResult ConnectResultForError(); LibBrlapiLoader* libbrlapi_loader_; scoped_ptr<brlapi_handle_t, base::FreeDeleter> handle_; MessageLoopForIO::FileDescriptorWatcher fd_controller_; OnDataReadyCallback on_data_ready_; DISALLOW_COPY_AND_ASSIGN(BrlapiConnectionImpl); }; BrlapiConnection::BrlapiConnection() { } BrlapiConnection::~BrlapiConnection() { } scoped_ptr<BrlapiConnection> BrlapiConnection::Create( LibBrlapiLoader* loader) { DCHECK(loader->loaded()); return scoped_ptr<BrlapiConnection>(new BrlapiConnectionImpl(loader)); } BrlapiConnection::ConnectResult BrlapiConnectionImpl::Connect( const OnDataReadyCallback& on_data_ready) { DCHECK(!handle_); handle_.reset((brlapi_handle_t*) malloc( libbrlapi_loader_->brlapi_getHandleSize())); int fd = libbrlapi_loader_->brlapi__openConnection(handle_.get(), NULL, NULL); if (fd < 0) { handle_.reset(); VLOG(1) << "Error connecting to brlapi: " << BrlapiStrError(); return ConnectResultForError(); } int path[2] = {0, 0}; int pathElements = 0; #if defined(OS_CHROMEOS) if (base::SysInfo::IsRunningOnChromeOS()) path[pathElements++] = kDefaultTtyChromeOS; #endif if (pathElements == 0 && getenv("WINDOWPATH") == NULL) path[pathElements++] = kDefaultTtyLinux; if (libbrlapi_loader_->brlapi__enterTtyModeWithPath( handle_.get(), path, pathElements, NULL) < 0) { LOG(ERROR) << "brlapi: couldn't enter tty mode: " << BrlapiStrError(); Disconnect(); return CONNECT_ERROR_RETRY; } size_t size; if (!GetDisplaySize(&size)) { // Error already logged. Disconnect(); return CONNECT_ERROR_RETRY; } // A display size of 0 means no display connected. We can't reliably // detect when a display gets connected, so fail and let the caller // retry connecting. if (size == 0) { VLOG(1) << "No braille display connected"; Disconnect(); return CONNECT_ERROR_RETRY; } const brlapi_keyCode_t extraKeys[] = { BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_OFFLINE, // brltty 5.1 converts dot input to Unicode characters unless we // explicitly accept this command. BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_PASSDOTS, }; if (libbrlapi_loader_->brlapi__acceptKeys( handle_.get(), brlapi_rangeType_command, extraKeys, arraysize(extraKeys)) < 0) { LOG(ERROR) << "Couldn't acceptKeys: " << BrlapiStrError(); Disconnect(); return CONNECT_ERROR_RETRY; } if (!MessageLoopForIO::current()->WatchFileDescriptor( fd, true, MessageLoopForIO::WATCH_READ, &fd_controller_, this)) { LOG(ERROR) << "Couldn't watch file descriptor " << fd; Disconnect(); return CONNECT_ERROR_RETRY; } on_data_ready_ = on_data_ready; return CONNECT_SUCCESS; } void BrlapiConnectionImpl::Disconnect() { if (!handle_) { return; } fd_controller_.StopWatchingFileDescriptor(); libbrlapi_loader_->brlapi__closeConnection( handle_.get()); handle_.reset(); } brlapi_error_t* BrlapiConnectionImpl::BrlapiError() { return libbrlapi_loader_->brlapi_error_location(); } std::string BrlapiConnectionImpl::BrlapiStrError() { return libbrlapi_loader_->brlapi_strerror(BrlapiError()); } bool BrlapiConnectionImpl::GetDisplaySize(size_t* size) { if (!CheckConnected()) { return false; } unsigned int columns, rows; if (libbrlapi_loader_->brlapi__getDisplaySize( handle_.get(), &columns, &rows) < 0) { LOG(ERROR) << "Couldn't get braille display size " << BrlapiStrError(); return false; } *size = columns * rows; return true; } bool BrlapiConnectionImpl::WriteDots(const unsigned char* cells) { if (!CheckConnected()) return false; if (libbrlapi_loader_->brlapi__writeDots(handle_.get(), cells) < 0) { VLOG(1) << "Couldn't write to brlapi: " << BrlapiStrError(); return false; } return true; } int BrlapiConnectionImpl::ReadKey(brlapi_keyCode_t* key_code) { if (!CheckConnected()) return -1; return libbrlapi_loader_->brlapi__readKey( handle_.get(), 0 /*wait*/, key_code); } bool BrlapiConnectionImpl::CheckConnected() { if (!handle_) { BrlapiError()->brlerrno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION; return false; } return true; } BrlapiConnection::ConnectResult BrlapiConnectionImpl::ConnectResultForError() { const brlapi_error_t* error = BrlapiError(); // For the majority of users, the socket file will never exist because // the daemon is never run. Avoid retrying in this case, relying on // the socket directory to change and trigger further tries if the // daemon comes up later on. if (error->brlerrno == BRLAPI_ERROR_LIBCERR && error->libcerrno == ENOENT) { return CONNECT_ERROR_NO_RETRY; } return CONNECT_ERROR_RETRY; } } // namespace braille_display_private } // namespace api } // namespace extensions