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

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

Issue 1130563002: Moterm part 1: Add MotermModel, a "model" for terminal emulation. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: oops 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_model.h ('k') | apps/moterm/moterm_model_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_model.h"
6
7 #include <string.h>
8
9 #include <algorithm>
10 #include <limits>
11
12 #include "base/logging.h"
13
14 namespace {
15
16 // Moterm -> teken conversions:
17
18 teken_pos_t MotermToTekenSize(const MotermModel::Size& size) {
19 DCHECK_LE(size.rows, std::numeric_limits<teken_unit_t>::max());
20 DCHECK_LE(size.columns, std::numeric_limits<teken_unit_t>::max());
21 teken_pos_t rv = {static_cast<teken_unit_t>(size.rows),
22 static_cast<teken_unit_t>(size.columns)};
23 return rv;
24 }
25
26 // Teken -> moterm conversions:
27
28 MotermModel::Position TekenToMotermPosition(const teken_pos_t& position) {
29 return MotermModel::Position(static_cast<int>(position.tp_row),
30 static_cast<int>(position.tp_col));
31 }
32
33 MotermModel::Size TekenToMotermSize(const teken_pos_t& size) {
34 return MotermModel::Size(size.tp_row, size.tp_col);
35 }
36
37 MotermModel::Rectangle TekenToMotermRectangle(const teken_rect_t& rectangle) {
38 return MotermModel::Rectangle(
39 static_cast<int>(rectangle.tr_begin.tp_row),
40 static_cast<int>(rectangle.tr_begin.tp_col),
41 rectangle.tr_end.tp_row - rectangle.tr_begin.tp_row,
42 rectangle.tr_end.tp_col - rectangle.tr_begin.tp_col);
43 }
44
45 MotermModel::Color TekenToMotermColor(teken_color_t color, bool bold) {
46 static const uint8_t rgb[TC_NCOLORS][3] = {
47 {0x00, 0x00, 0x00}, // Black.
48 {0x80, 0x00, 0x00}, // Red.
49 {0x00, 0x80, 0x00}, // Green.
50 {0x80, 0x80, 0x00}, // Yellow (even if teken thinks it's brown).
51 {0x00, 0x00, 0x80}, // Blue.
52 {0x80, 0x00, 0x80}, // Magenta.
53 {0x00, 0x80, 0x80}, // Cyan.
54 {0xc0, 0xc0, 0xc0} // White.
55 };
56 static const uint8_t bold_rgb[TC_NCOLORS][3] = {
57 {0x80, 0x80, 0x80}, // Black.
58 {0xff, 0x00, 0x00}, // Red.
59 {0x00, 0xff, 0x00}, // Green.
60 {0xff, 0xff, 0x00}, // Yellow (even if teken thinks it's brown).
61 {0x00, 0x00, 0xff}, // Blue.
62 {0xff, 0x00, 0xff}, // Magenta.
63 {0x00, 0xff, 0xff}, // Cyan.
64 {0xff, 0xff, 0xff} // White.
65 };
66 DCHECK_LT(color, static_cast<unsigned>(TC_NCOLORS));
67 return bold ? MotermModel::Color(bold_rgb[color][0], bold_rgb[color][1],
68 bold_rgb[color][2])
69 : MotermModel::Color(rgb[color][0], rgb[color][1], rgb[color][2]);
70 }
71
72 // Utility functions:
73
74 MotermModel::Rectangle EnclosingRectangle(const MotermModel::Rectangle& rect1,
75 const MotermModel::Rectangle& rect2) {
76 if (rect1.IsEmpty())
77 return rect2;
78 if (rect2.IsEmpty())
79 return rect1;
80
81 int start_row = std::min(rect1.position.row, rect2.position.row);
82 int start_col = std::min(rect1.position.column, rect2.position.column);
83 // TODO(vtl): Some theoretical overflows here.
84 int end_row =
85 std::max(rect1.position.row + static_cast<int>(rect1.size.rows),
86 rect2.position.row + static_cast<int>(rect2.size.rows));
87 int end_col =
88 std::max(rect1.position.column + static_cast<int>(rect1.size.columns),
89 rect2.position.column + static_cast<int>(rect2.size.columns));
90 DCHECK_LE(start_row, end_row);
91 DCHECK_LE(start_col, end_col);
92 return MotermModel::Rectangle(start_row, start_col,
93 static_cast<unsigned>(end_row - start_row),
94 static_cast<unsigned>(end_col - start_col));
95 }
96
97 } // namespace
98
99 const MotermModel::Attributes MotermModel::kAttributesBold;
100 const MotermModel::Attributes MotermModel::kAttributesUnderline;
101 const MotermModel::Attributes MotermModel::kAttributesBlink;
102
103 const unsigned MotermModel::kMaxRows;
104 const unsigned MotermModel::kMaxColumns;
105
106 MotermModel::MotermModel(const Size& max_size, const Size& size)
107 : max_size_(max_size), terminal_(), current_state_changes_() {
108 DCHECK_GT(max_size_.rows, 0u);
109 DCHECK_LE(max_size_.rows, kMaxRows);
110 DCHECK_GT(max_size_.columns, 0u);
111 DCHECK_LE(max_size_.columns, kMaxColumns);
112
113 DCHECK_GT(size.rows, 0u);
114 DCHECK_LE(size.rows, max_size_.rows);
115 DCHECK_GT(size.columns, 0u);
116 DCHECK_LE(size.columns, max_size_.columns);
117
118 size_t num_chars = max_size_.rows * max_size_.columns;
119 characters_.reset(new teken_char_t[num_chars]);
120 memset(characters_.get(), 0, num_chars * sizeof(characters_[0]));
121 attributes_.reset(new teken_attr_t[num_chars]);
122 memset(attributes_.get(), 0, num_chars * sizeof(attributes_[0]));
123
124 static const teken_funcs_t callbacks = {&MotermModel::OnBellThunk,
125 &MotermModel::OnCursorThunk,
126 &MotermModel::OnPutcharThunk,
127 &MotermModel::OnFillThunk,
128 &MotermModel::OnCopyThunk,
129 &MotermModel::OnParamThunk,
130 &MotermModel::OnRespondThunk};
131 teken_init(&terminal_, &callbacks, this);
132
133 teken_pos_t s = MotermToTekenSize(size);
134 teken_set_winsize(&terminal_, &s);
135 }
136
137 MotermModel::~MotermModel() {
138 }
139
140 void MotermModel::ProcessInput(const void* input_bytes,
141 size_t num_input_bytes,
142 StateChanges* state_changes) {
143 DCHECK(state_changes);
144 DCHECK(!current_state_changes_);
145 current_state_changes_ = state_changes;
146
147 // Get the initial cursor position, so we'll be able to tell if it moved.
148 teken_pos_t initial_cursor_pos = *teken_get_cursor(&terminal_);
149
150 // Note: This may call some of our callbacks.
151 teken_input(&terminal_, input_bytes, num_input_bytes);
152
153 teken_pos_t final_cursor_pos = *teken_get_cursor(&terminal_);
154 if (initial_cursor_pos.tp_row != final_cursor_pos.tp_row ||
155 initial_cursor_pos.tp_col != final_cursor_pos.tp_col) {
156 state_changes->cursor_moved = true;
157 // Update dirty rect to include old and new cursor positions.
158 current_state_changes_->dirty_rect = EnclosingRectangle(
159 current_state_changes_->dirty_rect,
160 Rectangle(initial_cursor_pos.tp_row, initial_cursor_pos.tp_col, 1, 1));
161 current_state_changes_->dirty_rect = EnclosingRectangle(
162 current_state_changes_->dirty_rect,
163 Rectangle(final_cursor_pos.tp_row, final_cursor_pos.tp_col, 1, 1));
164 }
165
166 current_state_changes_ = nullptr;
167 }
168
169 MotermModel::Size MotermModel::GetSize() const {
170 // Teken isn't const-correct, sadly.
171 return TekenToMotermSize(
172 *teken_get_winsize(const_cast<teken_t*>(&terminal_)));
173 }
174
175 MotermModel::Position MotermModel::GetCursorPosition() const {
176 // Teken isn't const-correct, sadly.
177 return TekenToMotermPosition(
178 *teken_get_cursor(const_cast<teken_t*>(&terminal_)));
179 }
180
181 MotermModel::CharacterInfo MotermModel::GetCharacterInfoAt(
182 const Position& position) const {
183 DCHECK_GE(position.row, 0);
184 DCHECK_LT(position.row, static_cast<int>(GetSize().rows));
185 DCHECK_GE(position.column, 0);
186 DCHECK_LT(position.column, static_cast<int>(GetSize().columns));
187
188 uint32_t ch = characters_[position.row * max_size_.columns + position.column];
189 const teken_attr_t& teken_attr =
190 attributes_[position.row * max_size_.columns + position.column];
191 Color fg = TekenToMotermColor(teken_attr.ta_fgcolor,
192 (teken_attr.ta_format & TF_BOLD));
193 Color bg = TekenToMotermColor(teken_attr.ta_bgcolor, false);
194 Attributes attr = 0;
195 if ((teken_attr.ta_format & TF_BOLD))
196 attr |= kAttributesBold;
197 if ((teken_attr.ta_format & TF_UNDERLINE))
198 attr |= kAttributesUnderline;
199 if ((teken_attr.ta_format & TF_BLINK))
200 attr |= kAttributesBlink;
201 if ((teken_attr.ta_format & TF_REVERSE))
202 std::swap(fg, bg);
203 return CharacterInfo(ch, attr, fg, bg);
204 }
205
206 void MotermModel::SetSize(const Size& size, bool reset) {
207 DCHECK_GT(size.rows, 1u);
208 DCHECK_LE(size.rows, max_size_.rows);
209 DCHECK_GT(size.columns, 1u);
210 DCHECK_LE(size.columns, max_size_.columns);
211 teken_pos_t teken_size = {static_cast<teken_unit_t>(size.rows),
212 static_cast<teken_unit_t>(size.columns)};
213 if (reset) {
214 teken_set_winsize_noreset(&terminal_, &teken_size);
215 } else {
216 // We'll try a bit harder to keep a sensible cursor position.
217 teken_pos_t cursor_pos =
218 *teken_get_cursor(const_cast<teken_t*>(&terminal_));
219 teken_set_winsize(&terminal_, &teken_size);
220 if (cursor_pos.tp_row >= teken_size.tp_row)
221 cursor_pos.tp_row = teken_size.tp_row - 1;
222 if (cursor_pos.tp_col >= teken_size.tp_col)
223 cursor_pos.tp_col = teken_size.tp_col - 1;
224 teken_set_cursor(&terminal_, &cursor_pos);
225 }
226 }
227
228 void MotermModel::OnBell() {
229 DCHECK(current_state_changes_);
230 current_state_changes_->bell_count++;
231 }
232
233 void MotermModel::OnCursor(const teken_pos_t* pos) {
234 DCHECK(current_state_changes_);
235 // Don't do anything. We'll just compare initial and final cursor positions.
236 }
237
238 void MotermModel::OnPutchar(const teken_pos_t* pos,
239 teken_char_t ch,
240 const teken_attr_t* attr) {
241 character_at(pos->tp_row, pos->tp_col) = ch;
242 attribute_at(pos->tp_row, pos->tp_col) = *attr;
243
244 // Update dirty rect.
245 DCHECK(current_state_changes_);
246 current_state_changes_->dirty_rect =
247 EnclosingRectangle(current_state_changes_->dirty_rect,
248 Rectangle(pos->tp_row, pos->tp_col, 1, 1));
249 }
250
251 void MotermModel::OnFill(const teken_rect_t* rect,
252 teken_char_t ch,
253 const teken_attr_t* attr) {
254 for (size_t row = rect->tr_begin.tp_row; row < rect->tr_end.tp_row; row++) {
255 for (size_t col = rect->tr_begin.tp_col; col < rect->tr_end.tp_col; col++) {
256 character_at(row, col) = ch;
257 attribute_at(row, col) = *attr;
258 }
259 }
260
261 // Update dirty rect.
262 DCHECK(current_state_changes_);
263 current_state_changes_->dirty_rect = EnclosingRectangle(
264 current_state_changes_->dirty_rect, TekenToMotermRectangle(*rect));
265 }
266
267 void MotermModel::OnCopy(const teken_rect_t* rect, const teken_pos_t* pos) {
268 unsigned height = rect->tr_end.tp_row - rect->tr_begin.tp_row;
269 unsigned width = rect->tr_end.tp_col - rect->tr_begin.tp_col;
270
271 // This is really a "move" (like |memmove()|) -- overlaps are likely. Process
272 // the rows depending on which way (vertically) we're moving.
273 if (pos->tp_row <= rect->tr_begin.tp_row) {
274 // Start from the top row.
275 for (unsigned row = 0; row < height; row++) {
276 // Use |memmove()| here, to in case we're not moving vertically.
277 memmove(&character_at(pos->tp_row + row, pos->tp_col),
278 &character_at(rect->tr_begin.tp_row + row, pos->tp_col),
279 width * sizeof(characters_[0]));
280 memmove(&attribute_at(pos->tp_row + row, pos->tp_col),
281 &attribute_at(rect->tr_begin.tp_row + row, pos->tp_col),
282 width * sizeof(attributes_[0]));
283 }
284 } else {
285 // Start from the bottom row.
286 for (unsigned row = height; row > 0;) {
287 row--;
288 // We can use |memcpy()| here.
289 memcpy(&character_at(pos->tp_row + row, pos->tp_col),
290 &character_at(rect->tr_begin.tp_row + row, pos->tp_col),
291 width * sizeof(characters_[0]));
292 memcpy(&attribute_at(pos->tp_row + row, pos->tp_col),
293 &attribute_at(rect->tr_begin.tp_row + row, pos->tp_col),
294 width * sizeof(attributes_[0]));
295 }
296 }
297
298 // Update dirty rect.
299 DCHECK(current_state_changes_);
300 current_state_changes_->dirty_rect = EnclosingRectangle(
301 current_state_changes_->dirty_rect,
302 Rectangle(static_cast<int>(pos->tp_row), static_cast<int>(pos->tp_col),
303 width, height));
304 }
305
306 void MotermModel::OnParam(int cmd, unsigned val) {
307 // TODO(vtl)
308 NOTIMPLEMENTED();
309 }
310
311 void MotermModel::OnRespond(const void* buf, size_t size) {
312 // TODO(vtl)
313 NOTIMPLEMENTED();
314 }
315
316 // static
317 void MotermModel::OnBellThunk(void* ctx) {
318 DCHECK(ctx);
319 return static_cast<MotermModel*>(ctx)->OnBell();
320 }
321
322 // static
323 void MotermModel::OnCursorThunk(void* ctx, const teken_pos_t* pos) {
324 DCHECK(ctx);
325 return static_cast<MotermModel*>(ctx)->OnCursor(pos);
326 }
327
328 // static
329 void MotermModel::OnPutcharThunk(void* ctx,
330 const teken_pos_t* pos,
331 teken_char_t ch,
332 const teken_attr_t* attr) {
333 DCHECK(ctx);
334 return static_cast<MotermModel*>(ctx)->OnPutchar(pos, ch, attr);
335 }
336
337 // static
338 void MotermModel::OnFillThunk(void* ctx,
339 const teken_rect_t* rect,
340 teken_char_t ch,
341 const teken_attr_t* attr) {
342 DCHECK(ctx);
343 return static_cast<MotermModel*>(ctx)->OnFill(rect, ch, attr);
344 }
345
346 // static
347 void MotermModel::OnCopyThunk(void* ctx,
348 const teken_rect_t* rect,
349 const teken_pos_t* pos) {
350 DCHECK(ctx);
351 return static_cast<MotermModel*>(ctx)->OnCopy(rect, pos);
352 }
353
354 // static
355 void MotermModel::OnParamThunk(void* ctx, int cmd, unsigned val) {
356 DCHECK(ctx);
357 return static_cast<MotermModel*>(ctx)->OnParam(cmd, val);
358 }
359
360 // static
361 void MotermModel::OnRespondThunk(void* ctx, const void* buf, size_t size) {
362 DCHECK(ctx);
363 return static_cast<MotermModel*>(ctx)->OnRespond(buf, size);
364 }
OLDNEW
« no previous file with comments | « apps/moterm/moterm_model.h ('k') | apps/moterm/moterm_model_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698