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

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: Fix stylistic issues from last review round. 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/files/file_path_watcher.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/singleton.h"
17 #include "base/observer_list.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/extensions/api/braille_display_private/brlapi_connectio n.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "library_loaders/libbrlapi.h"
22
23 namespace extensions {
24 using content::BrowserThread;
25 using base::TimeDelta;
26 namespace api {
27 namespace braille_display_private {
28
29 namespace {
30 // Delay between detecting a directory update and trying to connect
31 // to the brlapi.
32 const int64 kConnectionDelayMs = 1000;
33 }
34
35 class BrailleControllerImpl : public BrailleController {
36 public:
37 static BrailleControllerImpl* GetInstance();
38 virtual scoped_ptr<base::DictionaryValue> GetDisplayState() OVERRIDE;
39 virtual void WriteDots(const std::string& cells) OVERRIDE;
40 virtual void AddObserver(BrailleObserver* observer) OVERRIDE;
41 virtual void RemoveObserver(BrailleObserver* observer) OVERRIDE;
42 virtual void SetCreateBrlapiConnectionForTesting(
43 const CreateBrlapiConnectionFunction& callback) OVERRIDE;
44
45 private:
46 BrailleControllerImpl();
47 virtual ~BrailleControllerImpl();
48 void TryLoadLibBrlApi();
49 void StartConnecting();
50 void OnSocketDirChanged(const base::FilePath& path, bool error);
51 void TryToConnect();
52 scoped_ptr<BrlapiConnection> CreateBrlapiConnection();
53 void DispatchKeys();
54 scoped_ptr<KeyEvent> MapKeyCode(brlapi_keyCode_t code);
55 void DispatchKeyEvent(scoped_ptr<KeyEvent> event);
56
57 LibBrlapiLoader libbrlapi_loader_;
58 CreateBrlapiConnectionFunction create_brlapi_connection_function_;
59
60 // Manipulated on the IO thread.
61 scoped_ptr<BrlapiConnection> connection_;
62 base::FilePathWatcher file_path_watcher_;
63
64 // Manipulated on the UI thread.
65 ObserverList<BrailleObserver> observers_;
66 bool watching_dir_;
67
68 friend struct DefaultSingletonTraits<BrailleControllerImpl>;
69
70 DISALLOW_COPY_AND_ASSIGN(BrailleControllerImpl);
71 };
72
73 BrailleController::BrailleController() {
74 }
75
76 BrailleController::~BrailleController() {
77 }
78
79 // static
80 BrailleController* BrailleController::GetInstance() {
81 return BrailleControllerImpl::GetInstance();
82 }
83
84 // static
85 BrailleControllerImpl* BrailleControllerImpl::GetInstance() {
86 return Singleton<BrailleControllerImpl,
87 LeakySingletonTraits<BrailleControllerImpl> >::get();
88 }
89
90 BrailleControllerImpl::BrailleControllerImpl()
91 : watching_dir_(false) {
92 create_brlapi_connection_function_ = base::Bind(
93 &BrailleControllerImpl::CreateBrlapiConnection,
94 base::Unretained(this));
95 }
96
97 BrailleControllerImpl::~BrailleControllerImpl() {
98 }
99
100 void BrailleControllerImpl::TryLoadLibBrlApi() {
101 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
102 if (libbrlapi_loader_.loaded())
103 return;
104 // These versions of libbrlapi work the same for the functions we
105 // are using. (0.6.0 adds brlapi_writeWText).
106 static const char* kSupportedVersions[] = {
107 "libbrlapi.so.0.5",
108 "libbrlapi.so.0.6"
109 };
110 for (size_t i = 0; i < arraysize(kSupportedVersions); ++i) {
111 if (libbrlapi_loader_.Load(kSupportedVersions[i]))
112 return;
113 }
114 LOG(WARNING) << "Couldn't load libbrlapi: " << strerror(errno);
115 }
116
117 scoped_ptr<base::DictionaryValue> BrailleControllerImpl::GetDisplayState() {
118 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
119 if (!watching_dir_) {
120 watching_dir_ = true;
121 StartConnecting();
122 }
123 DisplayState displayState;
124 if (connection_.get() && connection_->Connected()) {
125 size_t size;
126 if (!connection_->GetDisplaySize(&size)) {
127 connection_->Disconnect();
128 } else if (size > 0) { // size == 0 means no display present.
129 displayState.available = true;
130 displayState.text_cell_count.reset(new int(size));
131 }
132 }
133 return displayState.ToValue().Pass();
134 }
135
136 void BrailleControllerImpl::WriteDots(const std::string& cells) {
137 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
138 if (connection_ && connection_->Connected()) {
139 size_t size;
140 if (!connection_->GetDisplaySize(&size)) {
141 connection_->Disconnect();
142 }
143 std::vector<unsigned char> sizedCells(size);
144 std::memcpy(&sizedCells[0], cells.data(), std::min(cells.size(), size));
145 if (size > cells.size())
146 std::fill(sizedCells.begin() + cells.size(), sizedCells.end(), 0);
147 if (!connection_->WriteDots(&sizedCells[0]))
148 connection_->Disconnect();
149 }
150 }
151
152 void BrailleControllerImpl::AddObserver(BrailleObserver* observer) {
153 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
154 observers_.AddObserver(observer);
155 }
156
157 void BrailleControllerImpl::RemoveObserver(BrailleObserver* observer) {
158 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
159 observers_.RemoveObserver(observer);
160 }
161
162 void BrailleControllerImpl::SetCreateBrlapiConnectionForTesting(
163 const CreateBrlapiConnectionFunction& function) {
164 if (function.is_null()) {
165 create_brlapi_connection_function_ = base::Bind(
166 &BrailleControllerImpl::CreateBrlapiConnection,
167 base::Unretained(this));
168 } else {
169 create_brlapi_connection_function_ = function;
170 }
171 }
172
173 void BrailleControllerImpl::StartConnecting() {
174 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
175 TryLoadLibBrlApi();
176 if (!libbrlapi_loader_.loaded()) {
177 return;
178 }
179 base::FilePath brlapi_dir(BRLAPI_SOCKETPATH);
180 if (!file_path_watcher_.Watch(
181 brlapi_dir, false, base::Bind(
182 &BrailleControllerImpl::OnSocketDirChanged,
183 base::Unretained(this)))) {
184 LOG(WARNING) << "Couldn't watch brlapi directory " << BRLAPI_SOCKETPATH;
185 return;
186 }
187 TryToConnect();
188 }
189
190 void BrailleControllerImpl::OnSocketDirChanged(const base::FilePath& path,
191 bool error) {
192 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
193 DCHECK(libbrlapi_loader_.loaded());
194 if (error) {
195 LOG(ERROR) << "Error watching brlapi directory: " << path.value();
196 return;
197 }
198 LOG(INFO) << "BrlAPI directory changed";
199 BrowserThread::PostDelayedTask(BrowserThread::IO, FROM_HERE,
200 base::Bind(
201 &BrailleControllerImpl::TryToConnect,
202 base::Unretained(this)),
203 TimeDelta::FromMilliseconds(
204 kConnectionDelayMs));
205 }
206
207 void BrailleControllerImpl::TryToConnect() {
208 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
209 DCHECK(libbrlapi_loader_.loaded());
210 if (!connection_.get())
211 connection_ = create_brlapi_connection_function_.Run();
212 if (connection_.get() && !connection_->Connected()) {
213 if (!connection_->Connect(base::Bind(
214 &BrailleControllerImpl::DispatchKeys,
215 base::Unretained(this))))
216 LOG(WARNING) << "Couldn't connect to brlapi";
217 }
218 }
219
220 scoped_ptr<BrlapiConnection> BrailleControllerImpl::CreateBrlapiConnection() {
221 DCHECK(libbrlapi_loader_.loaded());
222 return BrlapiConnection::Create(&libbrlapi_loader_);
223 }
224
225 scoped_ptr<KeyEvent> BrailleControllerImpl::MapKeyCode(brlapi_keyCode_t code) {
226 brlapi_expandedKeyCode_t expanded;
227 if (libbrlapi_loader_.brlapi_expandKeyCode(code, &expanded) != 0) {
228 LOG(ERROR) << "Couldn't expand key code " << code;
229 return scoped_ptr<KeyEvent>();
230 }
231 scoped_ptr<KeyEvent> result(new KeyEvent);
232 result->command = KEY_COMMAND_NONE;
233 switch (expanded.type) {
234 case BRLAPI_KEY_TYPE_CMD:
235 switch (expanded.command) {
236 case BRLAPI_KEY_CMD_LNUP:
237 result->command = KEY_COMMAND_LINE_UP;
238 break;
239 case BRLAPI_KEY_CMD_LNDN:
240 result->command = KEY_COMMAND_LINE_DOWN;
241 break;
242 case BRLAPI_KEY_CMD_FWINLT:
243 result->command = KEY_COMMAND_PAN_LEFT;
244 break;
245 case BRLAPI_KEY_CMD_FWINRT:
246 result->command = KEY_COMMAND_PAN_RIGHT;
247 break;
248 case BRLAPI_KEY_CMD_TOP:
249 result->command = KEY_COMMAND_TOP;
250 break;
251 case BRLAPI_KEY_CMD_BOT:
252 result->command = KEY_COMMAND_BOTTOM;
253 break;
254 case BRLAPI_KEY_CMD_ROUTE:
255 result->command = KEY_COMMAND_ROUTING;
256 result->display_position.reset(new int(expanded.argument));
257 break;
258 case BRLAPI_KEY_CMD_PASSDOTS:
259 result->command = KEY_COMMAND_DOTS;
260 // The 8 low-order bits in the argument contains the dots.
261 result->braille_dots.reset(new int(expanded.argument & 0xf));
262 if ((expanded.argument & BRLAPI_DOTC) != 0)
263 result->space_key.reset(new bool(true));
264 break;
265 }
266 break;
267 }
268 if (result->command == KEY_COMMAND_NONE)
269 result.reset();
270 return result.Pass();
271 }
272
273 void BrailleControllerImpl::DispatchKeys() {
274 DCHECK(connection_.get());
275 brlapi_keyCode_t code;
276 while (true) {
277 int result = connection_->ReadKey(&code);
278 if (result < 0) { // Error.
279 brlapi_error_t* err = connection_->BrlapiError();
280 if (err->brlerrno == BRLAPI_ERROR_LIBCERR && err->libcerrno == EINTR)
281 continue;
282 // Disconnect on other errors.
283 LOG(ERROR) << "BrlAPI error: " << connection_->BrlapiStrError();
284 connection_->Disconnect();
285 return;
286 } else if (result == 0) { // No more data.
287 return;
288 }
289 scoped_ptr<KeyEvent> event = MapKeyCode(code);
290 if (event)
291 DispatchKeyEvent(event.Pass());
292 }
293 }
294
295 void BrailleControllerImpl::DispatchKeyEvent(scoped_ptr<KeyEvent> event) {
296 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
297 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
298 base::Bind(
299 &BrailleControllerImpl::DispatchKeyEvent,
300 base::Unretained(this),
301 base::Passed(&event)));
302 return;
303 }
304 FOR_EACH_OBSERVER(BrailleObserver, observers_, OnKeyEvent(*event));
305 }
306
307 } // namespace braille_display_private
308 } // namespace api
309 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698