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

Side by Side Diff: apps/moterm/moterm_driver.cc

Issue 1128333002: Moterm part 2: Add MotermDriver, a terminal "driver". (Closed) Base URL: https://github.com/domokit/mojo.git@moterm_model
Patch Set: rebased again Created 5 years, 7 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
« no previous file with comments | « apps/moterm/moterm_driver.h ('k') | apps/moterm/moterm_driver_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 "apps/moterm/moterm_driver.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 #include "base/logging.h"
11 #include "mojo/services/files/public/interfaces/types.mojom.h"
12
13 const uint8_t kEOT = 4;
14 const uint8_t kNL = 10;
15 const uint8_t kCR = 13;
16 const uint8_t kDEL = 127;
17
18 MotermDriver::PendingRead::PendingRead(uint32_t num_bytes,
19 const ReadCallback& callback)
20 : num_bytes(num_bytes), callback(callback) {
21 }
22
23 MotermDriver::PendingRead::~PendingRead() {
24 }
25
26 // static
27 base::WeakPtr<MotermDriver> MotermDriver::Create(
28 Client* client,
29 mojo::InterfaceRequest<mojo::files::File> request) {
30 return (new MotermDriver(client, request.Pass()))->weak_factory_.GetWeakPtr();
31 }
32
33 void MotermDriver::Detach() {
34 client_ = nullptr;
35 delete this;
36 }
37
38 void MotermDriver::SendData(const void* bytes, size_t num_bytes) {
39 DCHECK(client_);
40 DCHECK(!is_closed_);
41
42 if (icanon_) {
43 for (size_t i = 0; i < num_bytes; i++)
44 HandleCanonicalModeInput(static_cast<const uint8_t*>(bytes)[i]);
45 } else {
46 // If not in canonical ("cooked") mode, just send data.
47 const uint8_t* b = static_cast<const uint8_t*>(bytes);
48 for (size_t i = 0; i < num_bytes; i++)
49 send_data_queue_.push_back(b[i]);
50
51 CompletePendingReads();
52 }
53 }
54
55 MotermDriver::MotermDriver(Client* client,
56 mojo::InterfaceRequest<mojo::files::File> request)
57 : client_(client),
58 is_closed_(false),
59 binding_(this, request.Pass()),
60 icanon_(true),
61 icrnl_(true),
62 veof_(kEOT),
63 verase_(kDEL),
64 onlcr_(true),
65 weak_factory_(this) {
66 DCHECK(client_);
67 }
68
69 MotermDriver::~MotermDriver() {
70 if (client_) {
71 // Invalidate weak pointers now, to make sure the client doesn't call us.
72 weak_factory_.InvalidateWeakPtrs();
73 client_->OnDestroyed();
74 }
75 }
76
77 void MotermDriver::HandleCanonicalModeInput(uint8_t ch) {
78 DCHECK(icanon_);
79
80 // Translate CR to NL, if appropriate.
81 if (ch == kCR && icrnl_)
82 ch = kNL;
83
84 // Newline.
85 // TODO(vtl): In addition to NL, we're supposed to handle settable EOL and
86 // EOL2.
87 if (ch == kNL) { // Newline.
88 input_line_queue_.push_back(ch);
89 HandleOutput(&ch, 1);
90 FlushInputLine();
91 return;
92 }
93
94 // EOF at beginning of line.
95 if (ch == veof_ && input_line_queue_.empty()) {
96 // Don't add the character. Just flush and nuke ourselves.
97 FlushInputLine();
98 delete this;
99 return;
100 }
101
102 if (ch == verase_) { // Erase (backspace).
103 if (!input_line_queue_.empty()) {
104 // TODO(vtl): This is wrong for utf-8 (see Linux's IUTF8).
105 input_line_queue_.pop_back();
106 HandleOutput(reinterpret_cast<const uint8_t*>("\x08 \x08"), 3);
107 } // Else do nothing.
108 return;
109 }
110
111 // Everything else.
112 // TODO(vtl): Probably want to display control characters as ^X (e.g.).
113 // TODO(vtl): Handle ^W.
114 input_line_queue_.push_back(ch);
115 HandleOutput(&ch, 1);
116 }
117
118 void MotermDriver::CompletePendingReads() {
119 while (send_data_queue_.size() && pending_read_queue_.size()) {
120 PendingRead pending_read = pending_read_queue_.front();
121 pending_read_queue_.pop_front();
122
123 size_t data_size = std::min(static_cast<size_t>(pending_read.num_bytes),
124 send_data_queue_.size());
125 mojo::Array<uint8_t> data(data_size);
126 for (size_t i = 0; i < data_size; i++) {
127 data[i] = send_data_queue_[i];
128 // In canonical mode, each read only gets a single line.
129 if (icanon_ && data[i] == kNL) {
130 data_size = i + 1;
131 data.resize(data_size);
132 break;
133 }
134 }
135 send_data_queue_.erase(send_data_queue_.begin(),
136 send_data_queue_.begin() + data_size);
137
138 pending_read.callback.Run(mojo::files::ERROR_OK, data.Pass());
139 }
140 }
141
142 void MotermDriver::FlushInputLine() {
143 for (size_t i = 0; i < input_line_queue_.size(); i++)
144 send_data_queue_.push_back(input_line_queue_[i]);
145 input_line_queue_.clear();
146 CompletePendingReads();
147 }
148
149 void MotermDriver::HandleOutput(const uint8_t* bytes, size_t num_bytes) {
150 // Can we be smarter and not always copy?
151 std::vector<uint8_t> translated_bytes;
152 translated_bytes.reserve(num_bytes);
153
154 for (size_t i = 0; i < num_bytes; i++) {
155 uint8_t ch = bytes[i];
156 if (ch == kNL && onlcr_) {
157 translated_bytes.push_back(kCR);
158 translated_bytes.push_back(kNL);
159 } else {
160 translated_bytes.push_back(ch);
161 }
162 }
163
164 // TODO(vtl): It seems extremely unlikely that we'd overlow a |uint32_t| here
165 // (but it's theoretically possible). But perhaps we should handle it more
166 // gracefully if it ever comes up.
167 CHECK_LE(translated_bytes.size(), std::numeric_limits<uint32_t>::max());
168 client_->OnDataReceived(translated_bytes.data(),
169 static_cast<uint32_t>(translated_bytes.size()));
170 }
171
172 void MotermDriver::Close(const CloseCallback& callback) {
173 if (is_closed_) {
174 callback.Run(mojo::files::ERROR_CLOSED);
175 return;
176 }
177
178 callback.Run(mojo::files::ERROR_OK);
179
180 // TODO(vtl): Call pending read callbacks?
181
182 client_->OnClosed();
183 }
184
185 void MotermDriver::Read(uint32_t num_bytes_to_read,
186 int64_t offset,
187 mojo::files::Whence whence,
188 const ReadCallback& callback) {
189 if (is_closed_) {
190 callback.Run(mojo::files::ERROR_CLOSED, mojo::Array<uint8_t>());
191 return;
192 }
193
194 if (offset != 0 || whence != mojo::files::WHENCE_FROM_CURRENT) {
195 // TODO(vtl): Is this the "right" behavior?
196 callback.Run(mojo::files::ERROR_INVALID_ARGUMENT, mojo::Array<uint8_t>());
197 return;
198 }
199
200 if (!num_bytes_to_read) {
201 callback.Run(mojo::files::ERROR_OK, mojo::Array<uint8_t>());
202 return;
203 }
204
205 pending_read_queue_.push_back(PendingRead(num_bytes_to_read, callback));
206 CompletePendingReads();
207 }
208
209 void MotermDriver::Write(mojo::Array<uint8_t> bytes_to_write,
210 int64_t offset,
211 mojo::files::Whence whence,
212 const WriteCallback& callback) {
213 DCHECK(!bytes_to_write.is_null());
214
215 if (is_closed_) {
216 callback.Run(mojo::files::ERROR_CLOSED, 0);
217 return;
218 }
219
220 if (offset != 0 || whence != mojo::files::WHENCE_FROM_CURRENT) {
221 // TODO(vtl): Is this the "right" behavior?
222 callback.Run(mojo::files::ERROR_INVALID_ARGUMENT, 0);
223 return;
224 }
225
226 if (!bytes_to_write.size()) {
227 callback.Run(mojo::files::ERROR_OK, 0);
228 return;
229 }
230
231 HandleOutput(static_cast<const uint8_t*>(&bytes_to_write.front()),
232 bytes_to_write.size());
233
234 // TODO(vtl): Is this OK if the client detached (and we're destroyed?).
235 callback.Run(mojo::files::ERROR_OK,
236 static_cast<uint32_t>(bytes_to_write.size()));
237 }
238
239 void MotermDriver::ReadToStream(mojo::ScopedDataPipeProducerHandle source,
240 int64_t offset,
241 mojo::files::Whence whence,
242 int64_t num_bytes_to_read,
243 const ReadToStreamCallback& callback) {
244 if (is_closed_) {
245 callback.Run(mojo::files::ERROR_CLOSED);
246 return;
247 }
248
249 // TODO(vtl)
250 NOTIMPLEMENTED();
251 callback.Run(mojo::files::ERROR_UNIMPLEMENTED);
252 }
253
254 void MotermDriver::WriteFromStream(mojo::ScopedDataPipeConsumerHandle sink,
255 int64_t offset,
256 mojo::files::Whence whence,
257 const WriteFromStreamCallback& callback) {
258 if (is_closed_) {
259 callback.Run(mojo::files::ERROR_CLOSED);
260 return;
261 }
262
263 // TODO(vtl)
264 NOTIMPLEMENTED();
265 callback.Run(mojo::files::ERROR_UNIMPLEMENTED);
266 }
267
268 void MotermDriver::Tell(const TellCallback& callback) {
269 if (is_closed_) {
270 callback.Run(mojo::files::ERROR_CLOSED, 0);
271 return;
272 }
273
274 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
275 // unsupported/EINVAL is better.)
276 callback.Run(mojo::files::ERROR_UNAVAILABLE, 0);
277 }
278
279 void MotermDriver::Seek(int64_t offset,
280 mojo::files::Whence whence,
281 const SeekCallback& callback) {
282 if (is_closed_) {
283 callback.Run(mojo::files::ERROR_CLOSED, 0);
284 return;
285 }
286
287 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
288 // unsupported/EINVAL is better.)
289 callback.Run(mojo::files::ERROR_UNAVAILABLE, 0);
290 }
291
292 void MotermDriver::Stat(const StatCallback& callback) {
293 if (is_closed_) {
294 callback.Run(mojo::files::ERROR_CLOSED, nullptr);
295 return;
296 }
297
298 // TODO(vtl)
299 NOTIMPLEMENTED();
300 callback.Run(mojo::files::ERROR_UNIMPLEMENTED, nullptr);
301 }
302
303 void MotermDriver::Truncate(int64_t size, const TruncateCallback& callback) {
304 if (is_closed_) {
305 callback.Run(mojo::files::ERROR_CLOSED);
306 return;
307 }
308
309 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
310 // unsupported/EINVAL is better.)
311 callback.Run(mojo::files::ERROR_UNAVAILABLE);
312 }
313
314 void MotermDriver::Touch(mojo::files::TimespecOrNowPtr atime,
315 mojo::files::TimespecOrNowPtr mtime,
316 const TouchCallback& callback) {
317 if (is_closed_) {
318 callback.Run(mojo::files::ERROR_CLOSED);
319 return;
320 }
321
322 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
323 // unsupported/EINVAL is better.)
324 callback.Run(mojo::files::ERROR_UNAVAILABLE);
325 }
326
327 void MotermDriver::Dup(mojo::InterfaceRequest<mojo::files::File> file,
328 const DupCallback& callback) {
329 if (is_closed_) {
330 callback.Run(mojo::files::ERROR_CLOSED);
331 return;
332 }
333
334 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
335 // unsupported/EINVAL is better.)
336 callback.Run(mojo::files::ERROR_UNAVAILABLE);
337 }
338
339 void MotermDriver::Reopen(mojo::InterfaceRequest<mojo::files::File> file,
340 uint32_t open_flags,
341 const ReopenCallback& callback) {
342 if (is_closed_) {
343 callback.Run(mojo::files::ERROR_CLOSED);
344 return;
345 }
346
347 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
348 // unsupported/EINVAL is better.)
349 callback.Run(mojo::files::ERROR_UNAVAILABLE);
350 }
351
352 void MotermDriver::AsBuffer(const AsBufferCallback& callback) {
353 if (is_closed_) {
354 callback.Run(mojo::files::ERROR_CLOSED, mojo::ScopedSharedBufferHandle());
355 return;
356 }
357
358 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
359 // unsupported/EINVAL is better.)
360 callback.Run(mojo::files::ERROR_UNAVAILABLE,
361 mojo::ScopedSharedBufferHandle());
362 }
363
364 void MotermDriver::Ioctl(uint32_t request,
365 mojo::Array<uint32_t> in_values,
366 const IoctlCallback& callback) {
367 if (is_closed_) {
368 callback.Run(mojo::files::ERROR_CLOSED, mojo::Array<uint32_t>());
369 return;
370 }
371
372 // TODO(vtl)
373 callback.Run(mojo::files::ERROR_UNIMPLEMENTED, mojo::Array<uint32_t>());
374 }
OLDNEW
« no previous file with comments | « apps/moterm/moterm_driver.h ('k') | apps/moterm/moterm_driver_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698