Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc

Issue 13355002: Implement chrome.brailleDisplayPrivate API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Style fixes and add support for two versions of libbrlapi when dlopening it. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698