Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 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/braille_controll er.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <cerrno> | |
| 9 #include <cstring> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/bind_helpers.h" | |
| 14 #include "base/chromeos/chromeos_version.h" | |
| 15 #include "base/files/file_path_watcher.h" | |
| 16 #include "base/memory/scoped_ptr.h" | |
| 17 #include "base/memory/singleton.h" | |
| 18 #include "base/message_loop/message_loop.h" | |
| 19 #include "base/observer_list.h" | |
| 20 #include "base/time/time.h" | |
| 21 #include "content/public/browser/browser_thread.h" | |
| 22 #include "library_loaders/libbrlapi.h" | |
| 23 | |
| 24 namespace extensions { | |
| 25 using content::BrowserThread; | |
| 26 using base::MessageLoopForIO; | |
| 27 using base::TimeDelta; | |
| 28 namespace api { | |
| 29 namespace braille_display_private { | |
| 30 namespace { | |
| 31 // Default virtual terminal. This can be overriden by setting the | |
| 32 // WINDOWPATH environment variable. This is only used when not running | |
| 33 // under CrhomeOS (that is in aura for a Linux desktop). | |
| 34 // TODO(plundblad): Find a way to detect the controlling terminal of the | |
| 35 // X server. | |
| 36 static const int kDefaultTtyLinux = 7; | |
| 37 #if defined(OS_CHROMEOS) | |
| 38 // The GUI is always running on vt1 in ChromeOS. | |
| 39 static const int kDefaultTtyChromeOS = 1; | |
| 40 #endif | |
| 41 } // namespace | |
| 42 | |
| 43 class BrailleControllerImpl : public BrailleController { | |
| 44 public: | |
| 45 static BrailleControllerImpl* GetInstance(); | |
| 46 virtual scoped_ptr<base::DictionaryValue> GetDisplayState() OVERRIDE; | |
| 47 virtual void WriteDots(const std::string& cells) OVERRIDE; | |
| 48 virtual void AddObserver(BrailleObserver* observer) OVERRIDE; | |
| 49 virtual void RemoveObserver(BrailleObserver* observer) OVERRIDE; | |
| 50 | |
| 51 private: | |
| 52 class Connection : public MessageLoopForIO::Watcher { | |
|
dmazzoni
2013/09/06 16:49:53
Since there's only one connection at a time, why i
| |
| 53 public: | |
| 54 explicit Connection(BrailleControllerImpl* braille_controller) : | |
| 55 braille_controller_(braille_controller) { | |
| 56 } | |
| 57 | |
| 58 virtual ~Connection() { | |
| 59 Disconnect(); | |
| 60 } | |
| 61 | |
| 62 bool Connect() { | |
|
dmazzoni
2013/09/06 16:49:53
Don't inline nontrivial functions in the class def
| |
| 63 DCHECK(!handle_); | |
| 64 if (!braille_controller_->libbrlapi_loader_.loaded()) { | |
| 65 return false; | |
| 66 } | |
| 67 handle_.reset((brlapi_handle_t*) malloc( | |
| 68 braille_controller_->libbrlapi_loader_.brlapi_getHandleSize())); | |
| 69 int fd = braille_controller_->libbrlapi_loader_.brlapi__openConnection( | |
| 70 handle_.get(), NULL, NULL); | |
| 71 if (fd < 0) { | |
| 72 handle_.reset(); | |
| 73 LOG(ERROR) << "Error connecting to brlapi: " | |
| 74 << braille_controller_->BrlApiStrError(); | |
| 75 return false; | |
| 76 } | |
| 77 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
| 78 fd, true, MessageLoopForIO::WATCH_READ, &fd_controller_, this)) { | |
| 79 LOG(ERROR) << "Couldn't watch file descriptor " << fd; | |
| 80 Disconnect(); | |
| 81 return false; | |
| 82 } | |
| 83 return true; | |
| 84 } | |
| 85 | |
| 86 void Disconnect() { | |
| 87 if (handle_ == NULL) { | |
| 88 return; | |
| 89 } | |
| 90 fd_controller_.StopWatchingFileDescriptor(); | |
| 91 braille_controller_->libbrlapi_loader_.brlapi__closeConnection( | |
| 92 handle_.get()); | |
| 93 handle_.reset(); | |
| 94 } | |
| 95 | |
| 96 bool Connected() { return handle_; } | |
| 97 | |
| 98 brlapi_handle_t* GetHandle() { return handle_.get(); } | |
| 99 | |
| 100 // Gets the total size of the display, which may be 0 if no display is | |
| 101 // present, returning true on success. Note that this is cached in the | |
| 102 // brlapi client so it is cheap. | |
| 103 bool GetDisplaySize(size_t* size) { | |
| 104 if (!handle_) { | |
| 105 return false; | |
| 106 } | |
| 107 unsigned int columns, rows; | |
| 108 if (braille_controller_->libbrlapi_loader_.brlapi__getDisplaySize( | |
| 109 handle_.get(), &columns, &rows) < 0) { | |
| 110 return false; | |
| 111 } | |
| 112 *size = columns * rows; | |
| 113 return true; | |
| 114 } | |
| 115 | |
| 116 // MessageLoopForIO::Watcher | |
| 117 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE { | |
| 118 braille_controller_->DispatchKeys(); | |
| 119 } | |
| 120 | |
| 121 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {} | |
| 122 | |
| 123 private: | |
| 124 scoped_ptr<brlapi_handle_t, base::FreeDeleter> handle_; | |
| 125 MessageLoopForIO::FileDescriptorWatcher fd_controller_; | |
| 126 BrailleControllerImpl* braille_controller_; | |
| 127 }; | |
| 128 BrailleControllerImpl(); | |
|
dmazzoni
2013/09/06 16:49:53
Nit: blank line before
| |
| 129 virtual ~BrailleControllerImpl(); | |
| 130 void TryLoadLibBrlApi(); | |
| 131 void StartConnections(); | |
| 132 void StopConnections(); | |
| 133 void OnSocketDirChanged(const base::FilePath& path, bool error); | |
| 134 void ReconnectAll(); | |
| 135 void UpdateConnections(); | |
| 136 void TryConnection(); | |
| 137 void DispatchKeys(); | |
| 138 scoped_ptr<KeyEvent> MapKeyCode(brlapi_keyCode_t code); | |
|
dmazzoni
2013/09/06 16:49:53
Nit: blank line between functions and data members
| |
| 139 void DispatchKeyEvent(scoped_ptr<KeyEvent> event); | |
|
dmazzoni
2013/09/06 16:49:53
Nit: put all functions, then all data members rath
| |
| 140 brlapi_error_t* BrlApiError(); | |
| 141 std::string BrlApiStrError(); | |
| 142 LibBrlApiLoader libbrlapi_loader_; | |
| 143 // Manipulated on the IO thread. | |
| 144 Connection connection_; | |
| 145 base::FilePathWatcher file_path_watcher_; | |
| 146 // Manipulated on the UI thread. | |
| 147 ObserverList<BrailleObserver> observers_; | |
| 148 bool watching_dir_; | |
| 149 friend struct DefaultSingletonTraits<BrailleControllerImpl>; | |
| 150 DISALLOW_COPY_AND_ASSIGN(BrailleControllerImpl); | |
| 151 }; | |
| 152 | |
| 153 BrailleController::BrailleController() { | |
| 154 } | |
| 155 | |
| 156 BrailleController::~BrailleController() { | |
| 157 } | |
| 158 | |
| 159 // static | |
| 160 BrailleController* BrailleController::GetInstance() { | |
| 161 return BrailleControllerImpl::GetInstance(); | |
| 162 } | |
| 163 | |
| 164 BrailleControllerImpl::BrailleControllerImpl() | |
| 165 : connection_(this), watching_dir_(false) { | |
| 166 TryLoadLibBrlApi(); | |
| 167 } | |
| 168 | |
| 169 BrailleControllerImpl::~BrailleControllerImpl() { | |
| 170 } | |
| 171 | |
| 172 void BrailleControllerImpl::TryLoadLibBrlApi() { | |
| 173 if (libbrlapi_loader_.loaded()) | |
| 174 return; | |
| 175 // These versions of libbrlapi work the same for the functions we | |
| 176 // are using. (0.6.0 adds brlapi_writeWText). | |
| 177 static const char* kSupportedVersions[] = { | |
| 178 "libbrlapi.so.0.5", | |
| 179 "libbrlapi.so.0.6" | |
| 180 }; | |
| 181 for (size_t i = 0; i < arraysize(kSupportedVersions); ++i) { | |
| 182 if (libbrlapi_loader_.Load(kSupportedVersions[i])) | |
| 183 return; | |
| 184 } | |
| 185 LOG(ERROR) << "Couldn't load libbrlapi: " << strerror(errno); | |
| 186 } | |
| 187 | |
| 188 // static | |
| 189 BrailleControllerImpl* BrailleControllerImpl::GetInstance() { | |
| 190 return Singleton<BrailleControllerImpl, | |
| 191 LeakySingletonTraits<BrailleControllerImpl> >::get(); | |
| 192 } | |
| 193 | |
| 194 scoped_ptr<base::DictionaryValue> BrailleControllerImpl::GetDisplayState() { | |
| 195 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
| 196 DisplayState displayState; | |
| 197 if (connection_.Connected()) { | |
| 198 size_t size; | |
| 199 if (!connection_.GetDisplaySize(&size)) { | |
| 200 LOG(ERROR) << "Couldn't get braille display size " << BrlApiStrError(); | |
| 201 connection_.Disconnect(); | |
| 202 } else if (size > 0) { // size == 0 means no display present. | |
| 203 displayState.available = true; | |
| 204 displayState.text_cell_count.reset(new int(size)); | |
| 205 } | |
| 206 } | |
| 207 return displayState.ToValue().Pass(); | |
| 208 } | |
| 209 | |
| 210 void BrailleControllerImpl::WriteDots(const std::string& cells) { | |
| 211 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
| 212 if (!libbrlapi_loader_.loaded()) | |
| 213 return; | |
| 214 if (connection_.Connected()) { | |
| 215 brlapi_handle_t* handle = connection_.GetHandle(); | |
| 216 size_t size; | |
| 217 if (!connection_.GetDisplaySize(&size)) { | |
| 218 LOG(ERROR) << "Couldn't get display size " << BrlApiStrError(); | |
| 219 connection_.Disconnect(); | |
| 220 } | |
| 221 std::vector<unsigned char> sizedCells(size); | |
| 222 std::memcpy(&sizedCells[0], cells.data(), std::min(cells.size(), size)); | |
| 223 if (size > cells.size()) | |
| 224 std::fill(sizedCells.begin() + cells.size(), sizedCells.end(), 0); | |
| 225 if (libbrlapi_loader_.brlapi__writeDots(handle, &sizedCells[0]) < 0) { | |
| 226 LOG(ERROR) << "Couldn't write to brlapi: " << BrlApiStrError(); | |
| 227 connection_.Disconnect(); | |
| 228 } | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 void BrailleControllerImpl::AddObserver(BrailleObserver* observer) { | |
| 233 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 234 observers_.AddObserver(observer); | |
| 235 if (libbrlapi_loader_.loaded() && !watching_dir_) { | |
| 236 watching_dir_ = true; | |
| 237 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 238 base::Bind(&BrailleControllerImpl::StartConnections, | |
| 239 base::Unretained(this))); | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 void BrailleControllerImpl::RemoveObserver(BrailleObserver* observer) { | |
| 244 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 245 observers_.RemoveObserver(observer); | |
| 246 } | |
| 247 | |
| 248 void BrailleControllerImpl::StartConnections() { | |
| 249 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
| 250 base::FilePath brlapi_dir(BRLAPI_SOCKETPATH); | |
| 251 LOG(INFO) << "Watching brlapi directory: " << BRLAPI_SOCKETPATH; | |
| 252 if (!file_path_watcher_.Watch( | |
| 253 brlapi_dir, false, base::Bind( | |
| 254 &BrailleControllerImpl::OnSocketDirChanged, | |
| 255 base::Unretained(this)))) { | |
| 256 LOG(WARNING) << "Couldn't watch brlapi directory " << BRLAPI_SOCKETPATH; | |
| 257 return; | |
| 258 } | |
| 259 UpdateConnections(); | |
| 260 } | |
| 261 | |
| 262 void BrailleControllerImpl::OnSocketDirChanged(const base::FilePath& path, | |
| 263 bool error) { | |
| 264 if (error) { | |
| 265 LOG(ERROR) << "Error watching brlapi directory: " << path.value(); | |
| 266 return; | |
| 267 } | |
| 268 LOG(INFO) << "BrlAPI directory changed"; | |
| 269 BrowserThread::PostDelayedTask(BrowserThread::IO, FROM_HERE, | |
| 270 base::Bind( | |
| 271 &BrailleControllerImpl::UpdateConnections, | |
| 272 base::Unretained(this)), | |
| 273 TimeDelta::FromSeconds(1)); | |
| 274 | |
| 275 } | |
| 276 | |
| 277 void BrailleControllerImpl::UpdateConnections() { | |
| 278 if (!connection_.Connected()) | |
| 279 TryConnection(); | |
| 280 } | |
| 281 | |
| 282 void BrailleControllerImpl::TryConnection() { | |
| 283 if (!connection_.Connect()) { | |
| 284 LOG(ERROR) << "Couldn't connect to brlapi\n"; | |
| 285 return; | |
| 286 } | |
| 287 int path[2] = {0, 0}; | |
| 288 int pathElements = 0; | |
| 289 #if defined(OS_CHROMEOS) | |
| 290 if (base::chromeos::IsRunningOnChromeOS()) | |
| 291 path[pathElements++] = kDefaultTtyChromeOS; | |
| 292 #endif | |
| 293 if (pathElements == 0 && getenv("WINDOWPATH") == NULL) | |
| 294 path[pathElements++] = kDefaultTtyLinux; | |
| 295 if (libbrlapi_loader_.brlapi__enterTtyModeWithPath( | |
| 296 connection_.GetHandle(), path, pathElements, NULL) < 0) { | |
| 297 LOG(ERROR) << "brlapi: couldn't enter tty mode: " << BrlApiStrError(); | |
| 298 connection_.Disconnect(); | |
| 299 return; | |
| 300 } | |
| 301 const brlapi_keyCode_t extraKeys[] = { | |
| 302 BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_OFFLINE, | |
| 303 }; | |
| 304 if (libbrlapi_loader_.brlapi__acceptKeys( | |
| 305 connection_.GetHandle(), | |
| 306 brlapi_rangeType_command, | |
| 307 extraKeys, | |
| 308 arraysize(extraKeys)) < 0) { | |
| 309 LOG(ERROR) << "Couldn't acceptKeys: " << BrlApiStrError(); | |
| 310 connection_.Disconnect(); | |
| 311 return; | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 scoped_ptr<KeyEvent> BrailleControllerImpl::MapKeyCode(brlapi_keyCode_t code) { | |
| 316 brlapi_expandedKeyCode_t expanded; | |
| 317 if (libbrlapi_loader_.brlapi_expandKeyCode(code, &expanded) != 0) { | |
| 318 LOG(ERROR) << "Couldn't expand key key code " << code; | |
| 319 return scoped_ptr<KeyEvent>(); | |
| 320 } | |
| 321 scoped_ptr<KeyEvent> result(new KeyEvent); | |
| 322 result->command = KEY_COMMAND_NONE; | |
| 323 switch (expanded.type) { | |
| 324 case BRLAPI_KEY_TYPE_CMD: | |
| 325 switch (expanded.command) { | |
| 326 case BRLAPI_KEY_CMD_LNUP: | |
| 327 result->command = KEY_COMMAND_LINE_UP; | |
| 328 break; | |
| 329 case BRLAPI_KEY_CMD_LNDN: | |
| 330 result->command = KEY_COMMAND_LINE_DOWN; | |
| 331 break; | |
| 332 case BRLAPI_KEY_CMD_FWINLT: | |
| 333 result->command = KEY_COMMAND_PAN_LEFT; | |
| 334 break; | |
| 335 case BRLAPI_KEY_CMD_FWINRT: | |
| 336 result->command = KEY_COMMAND_PAN_RIGHT; | |
| 337 break; | |
| 338 case BRLAPI_KEY_CMD_TOP: | |
| 339 result->command = KEY_COMMAND_TOP; | |
| 340 break; | |
| 341 case BRLAPI_KEY_CMD_BOT: | |
| 342 result->command = KEY_COMMAND_BOTTOM; | |
| 343 break; | |
| 344 case BRLAPI_KEY_CMD_ROUTE: | |
| 345 result->command = KEY_COMMAND_ROUTING; | |
| 346 result->display_position.reset(new int(expanded.argument)); | |
| 347 break; | |
| 348 case BRLAPI_KEY_CMD_PASSDOTS: | |
| 349 result->command = KEY_COMMAND_DOTS; | |
| 350 // The 8 low-order bits in the argument contains the dots. | |
| 351 result->braille_dots.reset(new int(expanded.argument & 0xf)); | |
| 352 if ((expanded.argument & BRLAPI_DOTC) != 0) | |
| 353 result->space_key.reset(new bool(true)); | |
| 354 break; | |
| 355 } | |
| 356 break; | |
| 357 } | |
| 358 if (result->command == KEY_COMMAND_NONE) | |
| 359 result.reset(); | |
| 360 return result.Pass(); | |
| 361 } | |
| 362 | |
| 363 void BrailleControllerImpl::DispatchKeys() { | |
| 364 brlapi_handle_t* handle = connection_.GetHandle(); | |
| 365 DCHECK(handle != NULL); | |
| 366 brlapi_keyCode_t code; | |
| 367 while (true) { | |
| 368 int result = libbrlapi_loader_.brlapi__readKey(handle, 0 /*wait*/, &code); | |
| 369 if (result < 0) { // Error. | |
| 370 brlapi_error_t* err = BrlApiError(); | |
| 371 if (err->brlerrno == BRLAPI_ERROR_LIBCERR && err->libcerrno == EINTR) | |
| 372 continue; | |
| 373 // Disconnect on other errors. | |
| 374 LOG(ERROR) << "BrlAPI error: " << BrlApiStrError(); | |
| 375 connection_.Disconnect(); | |
| 376 return; | |
| 377 } else if (result == 0) { // No more data. | |
| 378 return; | |
| 379 } | |
| 380 scoped_ptr<KeyEvent> event = MapKeyCode(code); | |
| 381 if (event) | |
| 382 DispatchKeyEvent(event.Pass()); | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 void BrailleControllerImpl::DispatchKeyEvent(scoped_ptr<KeyEvent> event) { | |
| 387 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
| 388 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 389 base::Bind( | |
| 390 &BrailleControllerImpl::DispatchKeyEvent, | |
| 391 base::Unretained(this), | |
| 392 base::Passed(&event))); | |
| 393 return; | |
| 394 } | |
| 395 FOR_EACH_OBSERVER(BrailleObserver, observers_, OnKeyEvent(*event)); | |
| 396 } | |
| 397 | |
| 398 brlapi_error_t* BrailleControllerImpl::BrlApiError() { | |
| 399 DCHECK(libbrlapi_loader_.loaded()); | |
| 400 return libbrlapi_loader_.brlapi_error_location(); | |
| 401 } | |
| 402 | |
| 403 std::string BrailleControllerImpl::BrlApiStrError() { | |
| 404 return libbrlapi_loader_.brlapi_strerror(BrlApiError()); | |
| 405 } | |
| 406 | |
| 407 } // namespace braille_display_private | |
| 408 } // namespace api | |
| 409 } // namespace extensions | |
| OLD | NEW |