The Pedigree Project 0.1
TextIO.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "TextIO.h"
21#include "pedigree/kernel/ActivityDiagnostics.h"
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/Log.h"
24#include "pedigree/kernel/machine/InputManager.h"
25#include "pedigree/kernel/machine/Machine.h"
26#include "pedigree/kernel/machine/Vga.h"
27#include "pedigree/kernel/process/Mutex.h"
28#include "pedigree/kernel/process/Scheduler.h"
29#include "pedigree/kernel/process/Thread.h"
30#include "pedigree/kernel/processor/MemoryRegion.h"
31#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
32#include "pedigree/kernel/processor/Processor.h"
33#include "pedigree/kernel/processor/ProcessorInformation.h"
34#include "pedigree/kernel/processor/VirtualAddressSpace.h"
35#include "pedigree/kernel/processor/types.h"
36#include "pedigree/kernel/time/Time.h"
37#include "pedigree/kernel/utilities/Buffer.h"
38#include "pedigree/kernel/utilities/StaticString.h"
39#include "pedigree/kernel/utilities/String.h"
40#include "pedigree/kernel/utilities/utility.h"
41
42#include "modules/system/vfs/File.h"
43
44class Filesystem;
45class Process;
46
48#define ALT_KEY (1ULL << 60)
49#define SHIFT_KEY (1ULL << 61)
50#define CTRL_KEY (1ULL << 62)
51#define SPECIAL_KEY (1ULL << 63)
52
53static int startFlipThread(void* param);
54
55TextIO::TextIO(String str, size_t inode, Filesystem* pParentFS, File* pParent)
56 : File(str, 0, 0, 0, inode, pParentFS, 0, pParent),
57 m_bInitialised(false),
58 m_bControlSeq(false),
59 m_bBracket(false),
60 m_bParenthesis(false),
61 m_bParams(false),
62 m_bQuestionMark(false),
63 m_CursorX(0),
64 m_CursorY(0),
65 m_SavedCursorX(0),
66 m_SavedCursorY(0),
67 m_ScrollStart(0),
68 m_ScrollEnd(0),
69 m_LeftMargin(0),
70 m_RightMargin(0),
71 m_CurrentParam(0),
72 m_Params(),
73 m_Fore(TextIO::LightGrey),
74 m_Back(TextIO::Black),
75 m_Backbuffer("TextIO Backbuffer"),
76 m_pFramebuffer(0),
77 m_pBackbuffer(0),
78 m_pVga(0),
79 m_pVterm(0),
80 m_pVtermScreen(0),
81 m_pVtermState(0),
82 m_VtermDirty(),
83 m_VtermScrollback(),
84 m_VtermScrollbackLines(0),
85 m_TabStops(),
86 m_OutBuffer(TEXTIO_BUFFER_SIZE),
87 m_G0('B'),
88 m_G1('B'),
89 m_bUtf8(false),
90 m_nCharacter(0),
91 m_nUtf8Handled(0),
92 m_bActive(false),
93 m_Lock(),
94 m_FlipWake(0),
95 m_FlipThread(),
96 m_bOwnsConsole(false),
97 m_InputMode(TextIO::Standard) {
98 size_t backbufferSize = BACKBUFFER_STRIDE * BACKBUFFER_ROWS * sizeof(VgaCell);
99 size_t backbufferPages = (backbufferSize + PhysicalMemoryManager::getPageSize() - 1) /
101
103 m_Backbuffer, backbufferPages, 0,
105 ERROR("TextIO: failed to allocate backbuffer!");
106 } else {
107 m_pBackbuffer = reinterpret_cast<VgaCell*>(m_Backbuffer.virtualAddress());
108 }
109
110 clearBackbuffer();
111
112 // r/w for root user/group, no access for everyone else.
113 setPermissionsOnly(FILE_GR | FILE_GW | FILE_UR | FILE_UW);
114 setUidOnly(0);
115 setGidOnly(0);
116
118 InputManager::instance().installCallback(InputManager::MachineKey, inputCallback, this);
119}
120
121TextIO::~TextIO() {
122 // Callback removal is a synchronous drain. Retire it before any state the
123 // callback can reach begins disappearing.
124 InputManager::instance().removeCallback(inputCallback, this);
125
126 // Join to the flip thread now that we're terminating.
127 m_bInitialised = false;
129 m_FlipThread.join();
130
131 {
133 destroyVterm();
134 }
135 m_pBackbuffer = 0;
136 m_Backbuffer.free();
137}
138
139bool TextIO::initialise(bool bClear) {
140 if (m_bInitialised) {
141 {
143 m_bInitialised = false;
145 }
146 m_FlipThread.join();
147 }
148
150
151 // Move into not-initialised mode, reset any held state.
152 m_bInitialised = false;
153 m_bActive = false;
154 m_bControlSeq = false;
155 m_bBracket = false;
156 m_bParams = false;
157 m_bQuestionMark = false;
158 m_pFramebuffer = 0;
159 m_CurrentParam = 0;
160 m_CursorX = m_CursorY = 0;
161 m_ScrollStart = m_ScrollEnd = 0;
162 m_LeftMargin = m_RightMargin = 0;
163 m_SavedCursorX = m_SavedCursorY = 0;
164 m_CurrentModes = 0;
165 ByteSet(m_Params, 0, sizeof(size_t) * MAX_TEXTIO_PARAMS);
166 ByteSet(m_TabStops, 0, BACKBUFFER_STRIDE);
167 m_InputMode = Standard;
168
169 m_pVga = Machine::instance().getVga(0);
170 if (m_pVga) {
171 m_pVga->setLargestTextMode();
172 m_pFramebuffer = *m_pVga;
173 if (m_pFramebuffer != 0) {
174 if (bClear) {
175 if (isPrimary()) {
176 ByteSet(m_pFramebuffer, 0,
177 m_pVga->getNumRows() * m_pVga->getNumCols() * sizeof(uint16_t));
178 }
179
181 }
182
183 m_bInitialised = true;
184 m_ScrollStart = 0;
185 m_ScrollEnd =
186 m_pVga->getNumRows() > BACKBUFFER_ROWS ? BACKBUFFER_ROWS - 1 : m_pVga->getNumRows() - 1;
187 m_LeftMargin = 0;
188 m_RightMargin =
189 m_pVga->getNumCols() > BACKBUFFER_STRIDE ? BACKBUFFER_STRIDE : m_pVga->getNumCols();
190
191 m_CurrentModes = AnsiVt52 | CharacterSetG0;
192
193 initialiseVterm();
194
195 // Set default tab stops.
196 for (size_t i = 0; i < BACKBUFFER_STRIDE; i += 8)
197 m_TabStops[i] = '|';
198
199 m_G0 = m_G1 = 'B';
200
201 m_NextInterval = BLINK_OFF_PERIOD;
202 }
203 }
204
205 if (m_bInitialised) {
206 while (m_FlipWake.tryAcquire()) {
207 }
208 Thread* flipThread =
209 new Thread(Scheduler::instance().getKernelProcess(), startFlipThread, this);
210 m_FlipThread.adopt(flipThread);
211 m_FlipThread->setName("TextIO flip thread");
212 }
213
214 return m_bInitialised;
215}
216
217void TextIO::initialiseVterm() {
218 destroyVterm();
219
220 size_t rows = m_pVga->getNumRows();
221 size_t cols = m_pVga->getNumCols();
222 if (rows > BACKBUFFER_ROWS)
223 rows = BACKBUFFER_ROWS;
224 if (cols > BACKBUFFER_STRIDE)
225 cols = BACKBUFFER_STRIDE;
226 if (!rows || !cols)
227 return;
228
229 m_pVterm = vterm_new(static_cast<int>(rows), static_cast<int>(cols));
230 if (!m_pVterm) {
231 ERROR("TextIO: failed to allocate libvterm state");
232 return;
233 }
234
235 vterm_set_utf8(m_pVterm, 1);
236 m_pVtermScreen = vterm_obtain_screen(m_pVterm);
237 m_pVtermState = vterm_obtain_state(m_pVterm);
238
239 static const VTermScreenCallbacks callbacks = {
240 vtermDamage, vtermMoveRect, vtermMoveCursor, vtermSetTermProp, vtermBell,
241 vtermResize, vtermScrollbackPush, vtermScrollbackPop, vtermScrollbackClear};
242 vterm_screen_set_callbacks(m_pVtermScreen, &callbacks, this);
243 vterm_screen_enable_reflow(m_pVtermScreen, true);
244 vterm_screen_enable_altscreen(m_pVtermScreen, true);
245 vterm_screen_set_damage_merge(m_pVtermScreen, VTERM_DAMAGE_SCROLL);
246 vterm_output_set_callback(m_pVterm, vtermOutput, this);
247 vterm_screen_reset(m_pVtermScreen, 1);
248
249 m_VtermScrollbackLines = 0;
250 ByteSet(m_VtermDirty, 1, sizeof(m_VtermDirty));
251 syncVtermRect({0, static_cast<int>(rows), 0, static_cast<int>(cols)});
252}
253
254void TextIO::destroyVterm() {
255 if (m_pVterm)
256 vterm_free(m_pVterm);
257 m_pVterm = 0;
258 m_pVtermScreen = 0;
259 m_pVtermState = 0;
260 m_VtermScrollbackLines = 0;
261}
262
263void TextIO::syncVtermRect(VTermRect rect) {
264 if (!m_pVtermScreen || !m_pBackbuffer)
265 return;
266
267 if (rect.start_row < 0)
268 rect.start_row = 0;
269 if (rect.start_col < 0)
270 rect.start_col = 0;
271 if (rect.end_row > BACKBUFFER_ROWS)
272 rect.end_row = BACKBUFFER_ROWS;
273 if (rect.end_col > BACKBUFFER_STRIDE)
274 rect.end_col = BACKBUFFER_STRIDE;
275
276 for (int row = rect.start_row; row < rect.end_row; ++row) {
277 for (int col = rect.start_col; col < rect.end_col; ++col) {
278 VTermPos pos = {row, col};
279 VTermScreenCell cell;
280 if (vterm_screen_get_cell(m_pVtermScreen, pos, &cell)) {
281 copyVtermCell(cell, m_pBackbuffer[row * BACKBUFFER_STRIDE + col]);
282 markVtermCell(pos);
283 }
284 }
285 }
286}
287
288void TextIO::syncVtermCell(VTermPos pos) {
289 syncVtermRect({pos.row, pos.row + 1, pos.col, pos.col + 1});
290}
291
292void TextIO::markVtermRect(VTermRect rect) {
293 if (rect.start_row < 0)
294 rect.start_row = 0;
295 if (rect.start_col < 0)
296 rect.start_col = 0;
297 if (rect.end_row > BACKBUFFER_ROWS)
298 rect.end_row = BACKBUFFER_ROWS;
299 if (rect.end_col > BACKBUFFER_STRIDE)
300 rect.end_col = BACKBUFFER_STRIDE;
301
302 for (int row = rect.start_row; row < rect.end_row; ++row)
303 for (int col = rect.start_col; col < rect.end_col; ++col)
304 m_VtermDirty[row * BACKBUFFER_STRIDE + col] = 1;
305}
306
307void TextIO::markVtermCell(VTermPos pos) {
308 if (pos.row >= 0 && pos.row < BACKBUFFER_ROWS && pos.col >= 0 && pos.col < BACKBUFFER_STRIDE)
309 m_VtermDirty[pos.row * BACKBUFFER_STRIDE + pos.col] = 1;
310}
311
312TextIO::VgaColour TextIO::vtermColour(VTermColor colour, bool foreground) const {
313 if ((foreground && VTERM_COLOR_IS_DEFAULT_FG(&colour)) ||
314 (!foreground && VTERM_COLOR_IS_DEFAULT_BG(&colour)))
315 return foreground ? LightGrey : Black;
316
317 if (m_pVtermScreen)
318 vterm_screen_convert_color_to_rgb(m_pVtermScreen, &colour);
319
320 static const uint8_t palette[16][3] = {
321 {0, 0, 0}, {0, 0, 170}, {0, 170, 0}, {0, 170, 170}, {170, 0, 0}, {170, 0, 170},
322 {170, 85, 0}, {170, 170, 170}, {85, 85, 85}, {85, 85, 255}, {85, 255, 85}, {85, 255, 255},
323 {255, 85, 85}, {255, 85, 255}, {255, 255, 85}, {255, 255, 255}};
324
325 uint8_t red = colour.rgb.red, green = colour.rgb.green, blue = colour.rgb.blue;
326 unsigned bestDistance = ~0U;
327 size_t best = 0;
328 for (size_t i = 0; i < 16; ++i) {
329 int dr = static_cast<int>(red) - palette[i][0];
330 int dg = static_cast<int>(green) - palette[i][1];
331 int db = static_cast<int>(blue) - palette[i][2];
332 unsigned distance = static_cast<unsigned>(dr * dr + dg * dg + db * db);
333 if (distance < bestDistance) {
334 bestDistance = distance;
335 best = i;
336 }
337 }
338 return static_cast<VgaColour>(best);
339}
340
341void TextIO::copyVtermCell(const VTermScreenCell& source, VgaCell& destination) {
342 destination.character = source.chars[0] ? translate(source.chars[0]) : ' ';
343 destination.fore = vtermColour(source.fg, true);
344 destination.back = vtermColour(source.bg, false);
345 destination.flags = 0;
346 if (source.attrs.bold)
347 destination.flags |= Bright;
348 if (source.attrs.blink)
349 destination.flags |= Blink;
350 if (source.attrs.reverse)
351 destination.flags |= Inverse;
352 destination.hidden = source.attrs.conceal;
353}
354
355void TextIO::storeVtermScrollback(const VTermScreenCell* cells, int cols) {
356 if (!cells || cols <= 0)
357 return;
358
359 if (m_VtermScrollbackLines < VTERM_SCROLLBACK_LINES)
360 ++m_VtermScrollbackLines;
361 else
362 MemoryCopy(m_VtermScrollback[0], m_VtermScrollback[1],
363 (VTERM_SCROLLBACK_LINES - 1) * sizeof(m_VtermScrollback[0]));
364
365 VgaCell* line = m_VtermScrollback[m_VtermScrollbackLines - 1];
366 ByteSet(line, 0, sizeof(m_VtermScrollback[0]));
367 size_t count = static_cast<size_t>(cols);
368 if (count > BACKBUFFER_STRIDE)
369 count = BACKBUFFER_STRIDE;
370 for (size_t col = 0; col < count; ++col)
371 copyVtermCell(cells[col], line[col]);
372}
373
374int TextIO::popVtermScrollback(VTermScreenCell* cells, int cols) {
375 if (!cells || cols <= 0 || !m_VtermScrollbackLines)
376 return 0;
377
378 size_t count = static_cast<size_t>(cols);
379 if (count > BACKBUFFER_STRIDE)
380 count = BACKBUFFER_STRIDE;
381 for (size_t col = 0; col < count; ++col) {
382 VgaCell& source = m_VtermScrollback[m_VtermScrollbackLines - 1][col];
383 VTermScreenCell& destination = cells[col];
384 ByteSet(&destination, 0, sizeof(destination));
385 destination.chars[0] = source.character;
386 destination.width = 1;
387 destination.fg.type = VTERM_COLOR_INDEXED;
388 static const uint8_t vtermPalette[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15};
389 destination.fg.indexed.idx = vtermPalette[source.fore & 0x0F];
390 destination.bg.type = VTERM_COLOR_INDEXED;
391 destination.bg.indexed.idx = vtermPalette[source.back & 0x0F];
392 destination.attrs.bold = (source.flags & Bright) != 0;
393 destination.attrs.blink = (source.flags & Blink) != 0;
394 destination.attrs.reverse = (source.flags & Inverse) != 0;
395 destination.attrs.conceal = source.hidden;
396 }
397 --m_VtermScrollbackLines;
398 return 1;
399}
400
401void TextIO::vtermOutput(const char* bytes, size_t length, void* user) {
402 TextIO* console = static_cast<TextIO*>(user);
403 if (console->m_OutBuffer.writeAvailable(bytes, length, true) != length)
404 WARNING("TextIO: output buffer is full or closed, dropping terminal response");
405}
406
407int TextIO::vtermDamage(VTermRect rect, void* user) {
408 TextIO* console = static_cast<TextIO*>(user);
409 console->syncVtermRect(rect);
410 return 1;
411}
412
413int TextIO::vtermMoveRect(VTermRect dest, VTermRect src, void* user) {
414 TextIO* console = static_cast<TextIO*>(user);
415 if (!console->m_pBackbuffer)
416 return 0;
417 int rows = dest.end_row - dest.start_row;
418 int cols = dest.end_col - dest.start_col;
419 if (rows <= 0 || cols <= 0 || src.end_row - src.start_row != rows ||
420 src.end_col - src.start_col != cols)
421 return 0;
422
423 int rowStep = dest.start_row > src.start_row ? -1 : 1;
424 for (int i = 0; i < rows; ++i) {
425 int row = rowStep > 0 ? i : rows - 1 - i;
426 for (int j = 0; j < cols; ++j) {
427 int sourceCol = src.start_col + j;
428 int destCol = dest.start_col + j;
429 if (dest.start_col > src.start_col) {
430 sourceCol = src.end_col - 1 - j;
431 destCol = dest.end_col - 1 - j;
432 }
433 VTermPos source = {src.start_row + row, sourceCol};
434 VTermPos target = {dest.start_row + row, destCol};
435 if (source.row >= 0 && source.row < BACKBUFFER_ROWS && source.col >= 0 &&
436 source.col < BACKBUFFER_STRIDE && target.row >= 0 && target.row < BACKBUFFER_ROWS &&
437 target.col >= 0 && target.col < BACKBUFFER_STRIDE)
438 console->m_pBackbuffer[target.row * BACKBUFFER_STRIDE + target.col] =
439 console->m_pBackbuffer[source.row * BACKBUFFER_STRIDE + source.col];
440 }
441 }
442 console->markVtermRect(dest);
443 return 1;
444}
445
446int TextIO::vtermMoveCursor(VTermPos pos, VTermPos oldPos, int visible, void* user) {
447 TextIO* console = static_cast<TextIO*>(user);
448 console->m_CursorX = pos.col;
449 console->m_CursorY = pos.row;
450 console->markVtermCell(oldPos);
451 console->markVtermCell(pos);
452 if (console->m_pVga && console->isPrimary())
453 console->m_pVga->moveCursor(pos.col, pos.row);
454 return 1;
455}
456
457int TextIO::vtermSetTermProp(VTermProp prop, VTermValue* value, void* user) {
458 TextIO* console = static_cast<TextIO*>(user);
459 if (prop == VTERM_PROP_REVERSE && value && value->boolean)
460 console->m_CurrentModes |= Screen;
461 else if (prop == VTERM_PROP_REVERSE)
462 console->m_CurrentModes &= ~Screen;
463 return 1;
464}
465
466int TextIO::vtermBell(void*) {
467 return 1;
468}
469
470int TextIO::vtermResize(int rows, int cols, void* user) {
471 TextIO* console = static_cast<TextIO*>(user);
472 if (rows < 1 || cols < 1)
473 return 0;
474 console->markVtermRect({0, rows, 0, cols});
475 return 1;
476}
477
478int TextIO::vtermScrollbackPush(int cols, const VTermScreenCell* cells, void* user) {
479 static_cast<TextIO*>(user)->storeVtermScrollback(cells, cols);
480 return 1;
481}
482
483int TextIO::vtermScrollbackPop(int cols, VTermScreenCell* cells, void* user) {
484 return static_cast<TextIO*>(user)->popVtermScrollback(cells, cols);
485}
486
487int TextIO::vtermScrollbackClear(void* user) {
488 static_cast<TextIO*>(user)->m_VtermScrollbackLines = 0;
489 return 1;
490}
491
492void TextIO::writeStr(const char* s, size_t len) {
493 if (!m_bInitialised) {
494 FATAL("TextIO misused: successfully call initialise() first.");
495 }
496
497 if (!s) {
498 ERROR("TextIO: null string passed in.");
499 return;
500 }
501
502 m_bActive = true;
503
504 bool usedVterm = false;
505 {
507 if (!m_bInitialised)
508 return;
509
510 if (m_pVterm) {
511 vterm_input_write(m_pVterm, s, len);
512 vterm_screen_flush_damage(m_pVtermScreen);
513 usedVterm = true;
514 }
515 }
516
517 if (usedVterm) {
518 if (isPrimary() && m_pVga)
519 m_pVga->moveCursor(m_CursorX, m_CursorY);
520 flip();
521 if (m_OutBuffer.canRead(false))
522 dataChanged();
523 return;
524 }
525
526 const char* orig = s;
527 while ((*s) && (len--)) {
528 // UTF8 -> UTF32 conversion.
529 uint8_t byte = *reinterpret_cast<const uint8_t*>(s);
530 if (m_bUtf8) {
531 if (m_nUtf8Handled >= 6) {
532 m_nUtf8Handled -= 6;
533 m_nCharacter |= (byte & 0x3F) << m_nUtf8Handled;
534
535 if (m_nUtf8Handled) {
536 ++s;
537 continue;
538 }
539 }
540
541 if ((m_nUtf8Handled == 0) || ((byte & 0xC0) != 0x80)) {
542 if (m_nUtf8Handled > 0)
543 ERROR(
544 "TextIO: expected a continuation byte, but didn't "
545 "get one");
546
547 // All good to use m_nCharacter now!
548 m_bUtf8 = false;
549
550 // If we terminated due to a byte that is not a continuation, we
551 // need to adjust the string pointer so we end up handling this
552 // character again, as a character that is not part of this UTF8
553 // sequence.
554 if (((byte & 0xC0) != 0x80) && (s != orig)) {
555 --s;
556 ++len;
557 }
558
559 // Ignore the codepoint if it is bad.
560 if (m_nCharacter > 0x10FFFF) {
561 ERROR("TextIO: invalid UTF8 sequence encountered.");
562 continue;
563 }
564 } else if (m_nUtf8Handled < 6) {
565 ERROR("TextIO: too many continuation bytes for a UTF8 sequence!");
566 m_bUtf8 = false;
567 ++s;
568 continue;
569 }
570 } else if ((byte & 0xC0) == 0xC0) {
571 m_bUtf8 = true;
572
573 uint8_t thisByte = *reinterpret_cast<const uint8_t*>(s);
574 if ((thisByte & 0xF8) == 0xF0) {
575 // 4-byte sequence.
576 m_nCharacter = (thisByte & 0x7) << 18;
577 m_nUtf8Handled = 18;
578 } else if ((thisByte & 0xF0) == 0xE0) {
579 // 3-byte sequence.
580 m_nCharacter = (thisByte & 0xF) << 12;
581 m_nUtf8Handled = 12;
582 } else if ((thisByte & 0xE0) == 0xC0) {
583 // 2-byte sequence.
584 m_nCharacter = (thisByte & 0x1F) << 6;
585 m_nUtf8Handled = 6;
586 } else {
587 ERROR(
588 "TextIO: invalid UTF8 leading byte (possible 5- or "
589 "6-byte sequence?)");
590 m_bUtf8 = false;
591 }
592
593 ++s;
594 continue;
595 } else if ((byte & 0x80) == 0x80) {
596 ERROR("TextIO: invalid ASCII character " << byte << " (not a UTF8 leading byte)");
597 ++s;
598 continue;
599 } else
600 m_nCharacter = *s;
601
602 if (m_bControlSeq && m_bBracket) {
603 switch (m_nCharacter) {
604 case '"':
605 case '$':
606 case '!':
607 case '>':
608 // Eat unhandled characters.
609 break;
610
611 case 0x08:
612 doBackspace();
613 break;
614
615 case '\n':
616 case 0x0B:
617 if (m_CurrentModes & LineFeedNewLine)
618 doCarriageReturn();
619 doLinefeed();
620 break;
621
622 case '\r':
623 doCarriageReturn();
624 break;
625
626 case '?':
627 m_bQuestionMark = true;
628 break;
629
630 case '0':
631 case '1':
632 case '2':
633 case '3':
634 case '4':
635 case '5':
636 case '6':
637 case '7':
638 case '8':
639 case '9':
640 m_Params[m_CurrentParam] = (m_Params[m_CurrentParam] * 10) + (m_nCharacter - '0');
641 m_bParams = true;
642 break;
643
644 case ';':
645 ++m_CurrentParam;
646 if (m_CurrentParam >= MAX_TEXTIO_PARAMS)
647 FATAL("TextIO: too many parameters!");
648 break;
649
650 case 'A':
651 // Cursor up.
652 if (m_CursorY) {
653 if (m_bParams && m_Params[0])
654 m_CursorY -= m_Params[0];
655 else
656 --m_CursorY;
657 }
658
659 if (m_CursorY < m_ScrollStart)
660 m_CursorY = m_ScrollStart;
661
662 m_bControlSeq = false;
663 break;
664
665 case 'B':
666 // Cursor down.
667 if (m_bParams && m_Params[0])
668 m_CursorY += m_Params[0];
669 else
670 ++m_CursorY;
671
672 if (m_CursorY > m_ScrollEnd)
673 m_CursorY = m_ScrollEnd;
674
675 m_bControlSeq = false;
676 break;
677
678 case 'C':
679 // Cursor right.
680 if (m_bParams && m_Params[0])
681 m_CursorX += m_Params[0];
682 else
683 ++m_CursorX;
684
685 if (m_CursorX >= m_RightMargin)
686 m_CursorX = m_RightMargin - 1;
687
688 m_bControlSeq = false;
689 break;
690
691 case 'D':
692 // Cursor left.
693 if (m_CursorX) {
694 if (m_bParams && m_Params[0])
695 m_CursorX -= m_Params[0];
696 else
697 --m_CursorX;
698 }
699
700 if (m_CursorX < m_LeftMargin)
701 m_CursorX = m_LeftMargin;
702
703 m_bControlSeq = false;
704 break;
705
706 case 'H':
707 case 'f':
708 // CUP/HVP commands
709 if (m_bParams) {
710 size_t xmove = m_Params[1] ? m_Params[1] - 1 : 0;
711 size_t ymove = m_Params[0] ? m_Params[0] - 1 : 0;
712
713 // Set X/Y
714 goHome(xmove, ymove);
715 } else {
716 // Reset X/Y
717 goHome();
718 }
719
720 m_bControlSeq = false;
721 break;
722
723 case 'J':
724 if ((!m_bParams) || (!m_Params[0])) {
725 eraseEOS();
726 } else if (m_Params[0] == 1) {
727 eraseSOS();
728 } else if (m_Params[0] == 2) {
729 // Erase entire screen, move to home.
730 eraseScreen(' ');
731 goHome();
732 }
733 m_bControlSeq = false;
734 break;
735
736 case 'K':
737 if ((!m_bParams) || (!m_Params[0])) {
738 eraseEOL();
739 } else if (m_Params[0] == 1) {
740 // Erase to start of line.
741 eraseSOL();
742 } else if (m_Params[0] == 2) {
743 // Erase entire line.
744 eraseLine();
745 }
746 m_bControlSeq = false;
747 break;
748
749 case 'c':
750 if (m_Params[0]) {
751 ERROR(
752 "TextIO: Device Attributes command with non-zero "
753 "parameter.");
754 } else {
755 // We mosly support the 'Advanced Video Option'.
756 // (apart from underline/blink)
757 const char* attribs = "\033[?1;2c";
758 m_OutBuffer.write(const_cast<char*>(attribs), StringLength(attribs));
759 }
760 m_bControlSeq = false;
761 break;
762
763 case 'g':
764 if (m_Params[0]) {
765 if (m_Params[0] == 3) {
766 ByteSet(m_TabStops, 0, BACKBUFFER_STRIDE);
767 }
768 } else {
769 m_TabStops[m_CursorX] = 0;
770 }
771 m_bControlSeq = false;
772 break;
773
774 case 'h':
775 case 'l': {
776 int modesToChange = 0;
777
778 if (m_bQuestionMark & m_bParams) {
779 for (size_t i = 0; i <= m_CurrentParam; ++i) {
780 switch (m_Params[i]) {
781 case 1:
782 modesToChange |= CursorKey;
783 break;
784 case 2:
785 modesToChange |= AnsiVt52;
786 break;
787 case 3:
788 modesToChange |= Column;
789 break;
790 case 4:
791 modesToChange |= Scrolling;
792 break;
793 case 5:
794 modesToChange |= Screen;
795 break;
796 case 6:
797 modesToChange |= Origin;
798 break;
799 case 7:
800 modesToChange |= AutoWrap;
801 break;
802 case 8:
803 modesToChange |= AutoRepeat;
804 break;
805 case 9:
806 modesToChange |= Interlace;
807 break;
808 default:
809 WARNING(
810 "TextIO: unknown 'DEC Private Mode "
811 "Set' mode '"
812 << m_Params[i] << "'");
813 break;
814 }
815 }
816 } else if (m_bParams) {
817 for (size_t i = 0; i <= m_CurrentParam; ++i) {
818 switch (m_Params[i]) {
819 case 20:
820 modesToChange |= LineFeedNewLine;
821 break;
822 default:
823 WARNING("TextIO: unknown 'Set Mode' mode '" << m_Params[i] << "'");
824 break;
825 }
826 }
827 }
828
829 if (m_nCharacter == 'h') {
830 // Set modes.
831 m_CurrentModes |= modesToChange;
832
833 // Setting modes
834 if (modesToChange & Origin) {
835 // Reset origin to margins.
836 m_CursorX = m_LeftMargin;
837 m_CursorY = m_ScrollStart;
838 } else if (modesToChange & Column) {
839 m_RightMargin = BACKBUFFER_COLS_WIDE;
840
841 // Clear screen as a side-effect.
842 eraseScreen(' ');
843
844 // Reset margins.
845 m_LeftMargin = 0;
846 m_ScrollStart = 0;
847 m_ScrollEnd = BACKBUFFER_ROWS - 1;
848
849 // Home the cursor.
850 m_CursorX = 0;
851 m_CursorY = 0;
852 }
853 } else {
854 // Reset modes.
855 m_CurrentModes &= ~(modesToChange);
856
857 // Resetting modes
858 if (modesToChange & Origin) {
859 // Reset origin to top left corner.
860 m_CursorX = 0;
861 m_CursorY = 0;
862 } else if (modesToChange & Column) {
863 m_RightMargin = BACKBUFFER_COLS_NORMAL;
864
865 // Clear screen as a side-effect.
866 eraseScreen(' ');
867
868 // Reset margins.
869 m_LeftMargin = 0;
870 m_ScrollStart = 0;
871 m_ScrollEnd = BACKBUFFER_ROWS - 1;
872
873 // Home the cursor.
874 m_CursorX = 0;
875 m_CursorY = 0;
876 }
877 }
878
879 m_bControlSeq = false;
880 } break;
881
882 case 'm':
883 for (size_t i = 0; i <= m_CurrentParam; ++i) {
884 switch (m_Params[i]) {
885 case 0:
886 // Reset all attributes.
887 m_Fore = LightGrey;
888 m_Back = Black;
889 m_CurrentModes &= ~(Inverse | Bright | Blink);
890 break;
891
892 case 1:
893 if (!(m_CurrentModes & Bright)) {
894 m_CurrentModes |= Bright;
895 }
896 break;
897
898 case 2:
899 if (m_CurrentModes & Bright) {
900 m_CurrentModes &= ~Bright;
901 }
902 break;
903
904 case 5:
905 // Set blinking text.
906 if (!(m_CurrentModes & Blink)) {
907 m_CurrentModes |= Blink;
908 }
909 break;
910
911 case 7:
912 if (!(m_CurrentModes & Inverse)) {
913 m_CurrentModes |= Inverse;
914 }
915 break;
916
917 case 30:
918 case 31:
919 case 32:
920 case 33:
921 case 34:
922 case 35:
923 case 36:
924 case 37:
925 setColour(&m_Fore, m_Params[i] - 30, m_CurrentModes & Bright);
926 break;
927 case 38:
928 if (m_Params[i + 1] == 5) {
929 setColour(&m_Fore, m_Params[i + 2], m_CurrentModes & Bright);
930 i += 3;
931 }
932 break;
933 case 39:
934 setColour(&m_Back, 7, m_CurrentModes & Bright);
935 break;
936
937 case 40:
938 case 41:
939 case 42:
940 case 43:
941 case 44:
942 case 45:
943 case 46:
944 case 47:
945 setColour(&m_Back, m_Params[i] - 40);
946 break;
947 case 48:
948 if (m_Params[i + 1] == 5) {
949 setColour(&m_Back, m_Params[i + 2]);
950 i += 3;
951 }
952 break;
953 case 49:
954 setColour(&m_Back, 0);
955 break;
956
957 case 90:
958 case 91:
959 case 92:
960 case 93:
961 case 94:
962 case 95:
963 case 96:
964 case 97:
965 setColour(&m_Fore, m_Params[i] - 90, true);
966 break;
967
968 case 100:
969 case 101:
970 case 102:
971 case 103:
972 case 104:
973 case 105:
974 case 106:
975 case 107:
976 setColour(&m_Back, m_Params[i] - 100, true);
977 break;
978
979 default:
980 WARNING(
981 "TextIO: unhandled 'Set Attribute Mode' "
982 "command "
983 << Dec << m_Params[i] << Hex << ".");
984 break;
985 }
986 }
987 m_bControlSeq = false;
988 break;
989
990 case 'n':
991 switch (m_Params[0]) {
992 case 5: {
993 // Report ready with no malfunctions detected.
994 const char* status = "\033[0n";
995 m_OutBuffer.write(const_cast<char*>(status), StringLength(status));
996 } break;
997 case 6: {
998 // Report cursor position.
999 // CPR - \e[ Y ; X R
1000 NormalStaticString response("\033[");
1001
1002 ssize_t reportX = m_CursorX + 1;
1003 ssize_t reportY = m_CursorY + 1;
1004
1005 if (m_CurrentModes & Origin) {
1006 // Only report relative if the cursor is within
1007 // the margins and scroll region! Otherwise,
1008 // absolute.
1009 if ((reportX > m_LeftMargin) && (reportX <= m_RightMargin))
1010 reportX -= m_LeftMargin;
1011 if ((reportY > m_ScrollStart) && (reportY <= m_ScrollEnd))
1012 reportY -= m_ScrollStart;
1013 }
1014
1015 response.append(reportY);
1016 response.append(";");
1017 response.append(reportX);
1018 response.append("R");
1019 m_OutBuffer.write(const_cast<char*>(static_cast<const char*>(response)),
1020 response.length());
1021 } break;
1022 default:
1023 NOTICE("TextIO: unknown device status request " << Dec << m_Params[0] << Hex << ".");
1024 break;
1025 }
1026 m_bControlSeq = false;
1027 break;
1028
1029 case 'p':
1030 // Depending on parameters and symbols in the sequence, this
1031 // could be "Set Conformance Level" (DECSCL),
1032 // "Soft Terminal Reset" (DECSTR), etc, etc... so ignore for
1033 // now.
1035 WARNING(
1036 "TextIO: dropping command after seeing 'p' command "
1037 "sequence terminator.");
1038 m_bControlSeq = false;
1039 break;
1040
1041 case 'q':
1042 // Load LEDs
1044 m_bControlSeq = false;
1045 break;
1046
1047 case 'r':
1048 if (m_bParams) {
1049 m_ScrollStart = m_Params[0] - 1;
1050 m_ScrollEnd = m_Params[1] - 1;
1051
1052 if (m_ScrollStart >= BACKBUFFER_ROWS)
1053 m_ScrollStart = BACKBUFFER_ROWS - 1;
1054 if (m_ScrollEnd >= BACKBUFFER_ROWS)
1055 m_ScrollEnd = BACKBUFFER_ROWS - 1;
1056 } else {
1057 m_ScrollStart = 0;
1058 m_ScrollEnd = BACKBUFFER_ROWS - 1;
1059 }
1060
1061 if (m_ScrollStart > m_ScrollEnd) {
1062 size_t tmp = m_ScrollStart;
1063 m_ScrollStart = m_ScrollEnd;
1064 m_ScrollEnd = tmp;
1065 }
1066
1067 goHome();
1068
1069 m_bControlSeq = false;
1070 break;
1071
1072 case 's':
1073 m_SavedCursorX = m_CursorX;
1074 m_SavedCursorY = m_CursorY;
1075 m_bControlSeq = false;
1076 break;
1077
1078 case 'u':
1079 m_CursorX = m_SavedCursorX;
1080 m_CursorY = m_SavedCursorY;
1081 m_bControlSeq = false;
1082 break;
1083
1084 case 'x':
1085 // Request Terminal Parameters
1086 if (m_Params[0] > 1) {
1087 ERROR(
1088 "TextIO: invalid 'sol' parameter for 'Request "
1089 "Terminal Parameters'");
1090 } else {
1091 // Send back a parameter report.
1092 // Parameters:
1093 // * Reporting on request
1094 // * No parity
1095 // * 8 bits per character
1096 // * 19200 bits per second xspeed
1097 // * 19200 bits per second rspeed
1098 // * 16x bit rate multiplier
1099 // * No STP option, so no flags
1100 const char* termparms = 0;
1101 if (m_Params[0])
1102 termparms = "\033[3;1;1;120;120;1;0x";
1103 else
1104 termparms = "\033[2;1;1;120;120;1;0x";
1105 m_OutBuffer.write(const_cast<char*>(termparms), StringLength(termparms));
1106 }
1107 m_bControlSeq = false;
1108 break;
1109
1110 case 'y':
1111 // Invoke Confidence Test (no-op)
1112 m_bControlSeq = false;
1113 break;
1114
1115 default:
1116 ERROR("TextIO: unknown control sequence character '" << m_nCharacter << "'!");
1117 m_bControlSeq = false;
1118 break;
1119 }
1120 } else if (m_bControlSeq && (!m_bBracket) && (!m_bParenthesis)) {
1121 switch (m_nCharacter) {
1122 case 0x08:
1123 doBackspace();
1124 break;
1125
1126 case 'A':
1127 if (m_CursorY > m_ScrollStart)
1128 --m_CursorY;
1129 m_bControlSeq = false;
1130 break;
1131
1132 case 'B':
1133 if (m_CursorY < m_ScrollEnd)
1134 ++m_CursorY;
1135 m_bControlSeq = false;
1136 break;
1137
1138 case 'C':
1139 ++m_CursorX;
1140 if (m_CursorX >= m_RightMargin)
1141 m_CursorX = m_RightMargin - 1;
1142 m_bControlSeq = false;
1143 break;
1144
1145 case 'D':
1146 if (m_CurrentModes & AnsiVt52) {
1147 // Index - cursor down one line, scroll if necessary.
1148 doLinefeed();
1149 } else {
1150 // Cursor Left
1151 if (m_CursorX > m_LeftMargin)
1152 --m_CursorX;
1153 }
1154 m_bControlSeq = false;
1155 break;
1156
1157 case 'E':
1158 // Next Line - move to start of next line.
1159 doCarriageReturn();
1160 doLinefeed();
1161 m_bControlSeq = false;
1162 break;
1163
1164 case 'F':
1165 case 'G':
1166 ERROR("TextIO: graphics mode is not implemented.");
1167 m_bControlSeq = false;
1168 break;
1169
1170 case 'H':
1171 if (m_CurrentModes & AnsiVt52) {
1172 // Horizontal tabulation set.
1173 m_TabStops[m_CursorX] = '|';
1174 } else {
1175 // Cursor to Home
1176 m_CursorX = 0;
1177 m_CursorY = 0;
1178 }
1179 m_bControlSeq = false;
1180 break;
1181
1182 case 'M':
1183 case 'I':
1184 // Reverse Index - cursor up one line, or scroll up if at
1185 // top.
1186 --m_CursorY;
1187 checkScroll();
1188 m_bControlSeq = false;
1189 break;
1190
1191 case 'J':
1192 eraseEOS();
1193 m_bControlSeq = false;
1194 break;
1195
1196 case 'K':
1197 eraseEOL();
1198 m_bControlSeq = false;
1199 break;
1200
1201 case 'Y': {
1202 uint8_t row = (*(++s)) - 0x20;
1203 uint8_t col = (*(++s)) - 0x20;
1204
1206 m_CursorX = col;
1207 m_CursorY = row;
1208 }
1209 m_bControlSeq = false;
1210 break;
1211
1212 case 'Z': {
1213 const char* identifier = 0;
1214 if (m_CurrentModes & AnsiVt52)
1215 identifier = "\033[?1;2c";
1216 else
1217 identifier = "\033/Z";
1218 m_OutBuffer.write(const_cast<char*>(identifier), StringLength(identifier));
1219 }
1220 m_bControlSeq = false;
1221 break;
1222
1223 case '#':
1224 // DEC commands
1225 ++s;
1226 m_nCharacter = *s;
1227 switch (m_nCharacter) {
1228 case '8':
1229 // DEC Screen Alignment Test (DECALN)
1230 // Fills screen with 'E' characters.
1231 eraseScreen('E');
1232 break;
1233
1234 default:
1235 ERROR("TextIO: unknown DEC command '" << m_nCharacter << "'");
1236 break;
1237 }
1238 m_bControlSeq = false;
1239 break;
1240
1241 case '=':
1243 ERROR("TextIO: alternate keypad mode is not implemented.");
1244 m_bControlSeq = false;
1245 break;
1246
1247 case '<':
1248 m_CurrentModes |= AnsiVt52;
1249 m_bControlSeq = false;
1250 break;
1251
1252 case '>':
1254 ERROR("TextIO: alternate keypad mode is not implemented.");
1255 m_bControlSeq = false;
1256 break;
1257
1258 case '[':
1259 m_bBracket = true;
1260 break;
1261
1262 case '(':
1263 case ')':
1264 case '*':
1265 case '+':
1266 case '-':
1267 case '.':
1268 case '/': {
1269 char curr = m_nCharacter;
1270 char next = *(++s);
1271
1272 // Portugese or DEC supplementary graphics (to ignore VT300
1273 // command)
1274 if (next == '%')
1275 next = *(++s);
1276
1277 if ((next >= '0' && next <= '2') || (next >= 'A' && next <= 'B')) {
1278 // Designate G0 character set.
1279 if (curr == '(')
1280 m_G0 = next;
1281 // Designate G1 character set.
1282 else if (curr == ')')
1283 m_G1 = next;
1284 else
1285 WARNING(
1286 "TextIO: only 'ESC(C' and 'ESC)C' are "
1287 "supported on a VT100.");
1288 }
1289 m_bControlSeq = false;
1290 } break;
1291
1292 case '7':
1293 m_SavedCursorX = m_CursorX;
1294 m_SavedCursorY = m_CursorY;
1295 m_bControlSeq = false;
1296 break;
1297
1298 case '8':
1299 m_CursorX = m_SavedCursorX;
1300 m_CursorY = m_SavedCursorY;
1301 m_bControlSeq = false;
1302 break;
1303
1304 case 'c':
1305 // Power-up reset!
1306 initialise(true);
1307 m_bControlSeq = false;
1308 break;
1309
1310 default:
1311 ERROR("TextIO: unknown escape sequence character '" << m_nCharacter << "'!");
1312 m_bControlSeq = false;
1313 break;
1314 }
1315 } else {
1316 if (m_nCharacter == '\033') {
1317 m_bControlSeq = true;
1318 m_bBracket = false;
1319 m_bParams = false;
1320 m_bParenthesis = false;
1321 m_bQuestionMark = true;
1322 m_CurrentParam = 0;
1323 ByteSet(m_Params, 0, sizeof(size_t) * MAX_TEXTIO_PARAMS);
1324 } else {
1325 switch (m_nCharacter) {
1326 case 0x05: {
1327 // Reply with our answerback.
1328 const char* answerback = "\033[1;2c";
1329 m_OutBuffer.write(const_cast<char*>(answerback), StringLength(answerback));
1330 } break;
1331 case 0x08:
1332 doBackspace();
1333 break;
1334 case 0x09:
1335 doHorizontalTab();
1336 break;
1337 case '\r':
1338 doCarriageReturn();
1339 break;
1340 case '\n':
1341 case 0x0B:
1342 case 0x0C:
1343 if (m_CurrentModes & LineFeedNewLine)
1344 doCarriageReturn();
1345 doLinefeed();
1346 break;
1347 case 0x0E:
1348 // Shift-Out - invoke G1 character set.
1349 m_CurrentModes &= ~CharacterSetG0;
1350 m_CurrentModes |= CharacterSetG1;
1351 break;
1352 case 0x0F:
1353 // Shift-In - invoke G0 character set.
1354 m_CurrentModes &= ~CharacterSetG1;
1355 m_CurrentModes |= CharacterSetG0;
1356 break;
1357 default:
1358
1359 uint8_t c = translate(m_nCharacter);
1360
1361 uint8_t characterSet = m_G0;
1362 if (m_CurrentModes & CharacterSetG1)
1363 characterSet = m_G1;
1364
1365 if (characterSet >= '0' && characterSet <= '2') {
1366 switch (c) {
1367 case '_':
1368 c = ' ';
1369 break; // Blank
1370
1371 // Symbols and line control.
1372 case 'a':
1373 c = 0xB2;
1374 break; // Checkerboard
1375 case 'b':
1376 c = 0xAF;
1377 break; // Horizontal tab
1378 case 'c':
1379 c = 0x9F;
1380 break; // Form feed
1381 case 'h': // Newline
1382 case 'e': // Linefeed
1383 c = 'n';
1384 break;
1385 case 'i':
1386 c = 'v';
1387 break; // Vertical tab.
1388 case 'd':
1389 c = 'r';
1390 break; // Carriage return
1391 case 'f':
1392 c = 0xF8;
1393 break; // Degree symbol
1394 case 'g':
1395 c = 0xF1;
1396 break; // Plus-minus
1397
1398 // Line-drawing.
1399 case 'j':
1400 c = 0xBC;
1401 break; // Lower right corner
1402 case 'k':
1403 c = 0xBB;
1404 break; // Upper right corner
1405 case 'l':
1406 c = 0xC9;
1407 break; // Upper left corner
1408 case 'm':
1409 c = 0xC8;
1410 break; // Lower left corner
1411 case 'n':
1412 c = 0xCE;
1413 break; // Crossing lines.
1414 case 'q':
1415 c = 0xCD;
1416 break; // Horizontal line.
1417 case 't':
1418 c = 0xCC;
1419 break; // Left 'T'
1420 case 'u':
1421 c = 0xB9;
1422 break; // Right 'T'
1423 case 'v':
1424 c = 0xCA;
1425 break; // Bottom 'T'
1426 case 'w':
1427 c = 0xCB;
1428 break; // Top 'T'
1429 case 'x':
1430 c = 0xBA;
1431 break; // Vertical bar
1432 }
1433 }
1434
1435 if (c >= ' ') {
1436 // We must handle wrapping *just before* we write
1437 // the next printable, because otherwise things
1438 // like BS at the right margin fail to work
1439 // correctly.
1440 checkWrap();
1441
1442 if (m_CursorX < BACKBUFFER_STRIDE) {
1443 LockGuard<Mutex> guard(m_Lock);
1444 VgaCell* pCell = &m_pBackbuffer[(m_CursorY * BACKBUFFER_STRIDE) + m_CursorX];
1445 pCell->character = c;
1446 pCell->fore = m_Fore;
1447 pCell->back = m_Back;
1448 pCell->flags = m_CurrentModes;
1449 ++m_CursorX;
1450 } else {
1451 ERROR(
1452 "TextIO: X co-ordinate is beyond the end "
1453 "of a backbuffer line: "
1454 << m_CursorX << " vs " << BACKBUFFER_STRIDE << "?");
1455 }
1456 }
1457 break;
1458 }
1459 }
1460 }
1461
1462 if (m_CursorX < m_LeftMargin) {
1463 WARNING("TextIO: X co-ordinate ended up befor the left margin.");
1464 m_CursorX = m_LeftMargin;
1465 }
1466
1467 ++s;
1468 }
1469
1470 // Assume we moved the cursor, and update where it is displayed
1471 // accordingly.
1472 if (isPrimary()) {
1473 m_pVga->moveCursor(m_CursorX, m_CursorY);
1474 }
1475
1476 // This write is now complete.
1477 flip();
1478
1479 // Wake up anything waiting on output from us if needed.
1480 if (m_OutBuffer.canRead(false))
1481 dataChanged();
1482}
1483
1484void TextIO::setColour(TextIO::VgaColour* which, size_t param, bool bBright) {
1485 switch (param) {
1486 case 0:
1487 *which = bBright ? DarkGrey : Black;
1488 break;
1489 case 1:
1490 *which = bBright ? LightRed : Red;
1491 break;
1492 case 2:
1493 *which = bBright ? LightGreen : Green;
1494 break;
1495 case 3:
1496 *which = bBright ? Yellow : Orange;
1497 break;
1498 case 4:
1499 *which = bBright ? LightBlue : Blue;
1500 break;
1501 case 5:
1502 *which = bBright ? LightMagenta : Magenta;
1503 break;
1504 case 6:
1505 *which = bBright ? LightCyan : Cyan;
1506 break;
1507 case 7:
1508 *which = bBright ? White : LightGrey;
1509 break;
1510 default:
1511 break;
1512 }
1513}
1514
1515void TextIO::doBackspace() {
1516 // If we are at a position where we would expect to wrap, step back one
1517 // extra character position so we don't wrap.
1518 if (m_CursorX == m_RightMargin)
1519 --m_CursorX;
1520
1521 // Backspace will not do anything if we are already on the left margin.
1522 if (m_CursorX > m_LeftMargin)
1523 --m_CursorX;
1524}
1525
1526void TextIO::doLinefeed() {
1527 ++m_CursorY;
1528 checkScroll();
1529}
1530
1531void TextIO::doCarriageReturn() {
1532 m_CursorX = m_LeftMargin;
1533}
1534
1535void TextIO::doHorizontalTab() {
1536 bool tabStopFound = false;
1537
1538 // Move to the next tab stop from the current position.
1539 for (ssize_t x = (m_CursorX + 1); x < m_RightMargin; ++x) {
1540 if (m_TabStops[x] != 0) {
1541 m_CursorX = x;
1542 tabStopFound = true;
1543 break;
1544 }
1545 }
1546
1547 if (!tabStopFound) {
1548 // Tab to the right margin, if no tab stop was found at all.
1549 m_CursorX = m_RightMargin - 1;
1550 } else if (m_CursorX >= m_RightMargin)
1551 m_CursorX = m_RightMargin - 1;
1552}
1553
1554void TextIO::checkScroll() {
1555 LockGuard<Mutex> guard(m_Lock);
1556
1557 // Handle scrolling, which can take place due to linefeeds and
1558 // other such cursor movements.
1559 if (m_CursorY < m_ScrollStart) {
1560 // By how much have we exceeded the scroll region?
1561 size_t numRows = (m_ScrollStart - m_CursorY);
1562
1563 // Top of the scrolling area
1564 size_t sourceRow = m_ScrollStart;
1565 size_t destRow = m_ScrollStart + numRows;
1566
1567 // Bottom of the scrolling area
1568 size_t sourceEnd = m_ScrollEnd + 1 - numRows;
1569
1570 // Move data.
1571 MemoryCopy(&m_pBackbuffer[destRow * BACKBUFFER_STRIDE],
1572 &m_pBackbuffer[sourceRow * BACKBUFFER_STRIDE],
1573 (sourceEnd - sourceRow) * BACKBUFFER_STRIDE * sizeof(VgaCell));
1574
1575 // Clear out the start of the region now.
1576 for (size_t i = 0; i < ((destRow - sourceRow) * BACKBUFFER_STRIDE); ++i) {
1577 VgaCell* pCell = &m_pBackbuffer[(sourceRow * BACKBUFFER_STRIDE) + i];
1578 pCell->character = ' ';
1579 pCell->back = m_Back;
1580 pCell->fore = m_Fore;
1581 pCell->flags = 0;
1582 }
1583
1584 m_CursorY = m_ScrollStart;
1585 } else if (m_CursorY > m_ScrollEnd) {
1586 // By how much have we exceeded the scroll region?
1587 size_t numRows = (m_CursorY - m_ScrollEnd);
1588
1589 // At what position is the top of the scroll?
1590 // ie, to where are we moving the data into place?
1591 size_t startOffset = m_ScrollStart * BACKBUFFER_STRIDE;
1592
1593 // Where are we pulling data from?
1594 size_t fromOffset = (m_ScrollStart + numRows) * BACKBUFFER_STRIDE;
1595
1596 // How many rows are we moving? This is the distance from
1597 // the 'from' offset to the end of the scroll region.
1598 size_t movedRows = ((m_ScrollEnd + 1) * BACKBUFFER_STRIDE) - fromOffset;
1599
1600 // Where do we begin blanking from?
1601 size_t blankFrom = (((m_ScrollEnd + 1) - numRows) * BACKBUFFER_STRIDE);
1602
1603 // How much blanking do we need to do?
1604 size_t blankLength = ((m_ScrollEnd + 1) * BACKBUFFER_STRIDE) - blankFrom;
1605
1606 MemoryCopy(&m_pBackbuffer[startOffset], &m_pBackbuffer[fromOffset],
1607 movedRows * sizeof(VgaCell));
1608 for (size_t i = 0; i < blankLength; ++i) {
1609 VgaCell* pCell = &m_pBackbuffer[blankFrom + i];
1610 pCell->character = ' ';
1611 pCell->back = m_Back;
1612 pCell->fore = m_Fore;
1613 pCell->flags = 0;
1614 }
1615
1616 m_CursorY = m_ScrollEnd;
1617 }
1618}
1619
1620void TextIO::checkWrap() {
1621 if (m_CursorX >= m_RightMargin) {
1622 // Default autowrap mode is off - new characters at
1623 // the right margin replace any that are already there.
1624 if (m_CurrentModes & AutoWrap) {
1625 m_CursorX = m_LeftMargin;
1626 ++m_CursorY;
1627
1628 checkScroll();
1629 } else {
1630 m_CursorX = m_RightMargin - 1;
1631 }
1632 }
1633}
1634
1635void TextIO::eraseSOS() {
1636 // Erase to the start of the line.
1637 eraseSOL();
1638
1639 LockGuard<Mutex> guard(m_Lock);
1640
1641 // Erase the screen above, and this line.
1642 for (ssize_t y = 0; y < m_CursorY; ++y) {
1643 for (size_t x = 0; x < BACKBUFFER_STRIDE; ++x) {
1644 VgaCell* pCell = &m_pBackbuffer[(y * BACKBUFFER_STRIDE) + x];
1645 pCell->character = ' ';
1646 pCell->fore = m_Fore;
1647 pCell->back = m_Back;
1648 pCell->flags = 0;
1649 }
1650 }
1651}
1652
1653void TextIO::eraseEOS() {
1654 // Erase to the end of line first...
1655 eraseEOL();
1656
1657 LockGuard<Mutex> guard(m_Lock);
1658
1659 // Then the rest of the screen.
1660 for (size_t y = m_CursorY + 1; y < BACKBUFFER_ROWS; ++y) {
1661 for (size_t x = 0; x < BACKBUFFER_STRIDE; ++x) {
1662 VgaCell* pCell = &m_pBackbuffer[(y * BACKBUFFER_STRIDE) + x];
1663 pCell->character = ' ';
1664 pCell->back = m_Back;
1665 pCell->fore = m_Fore;
1666 pCell->flags = 0;
1667 }
1668 }
1669}
1670
1671void TextIO::eraseEOL() {
1672 LockGuard<Mutex> guard(m_Lock);
1673
1674 // Erase to end of line.
1675 for (size_t x = m_CursorX; x < BACKBUFFER_STRIDE; ++x) {
1676 VgaCell* pCell = &m_pBackbuffer[(m_CursorY * BACKBUFFER_STRIDE) + x];
1677 pCell->character = ' ';
1678 pCell->back = m_Back;
1679 pCell->fore = m_Fore;
1680 pCell->flags = 0;
1681 }
1682}
1683
1684void TextIO::eraseSOL() {
1685 LockGuard<Mutex> guard(m_Lock);
1686
1687 for (ssize_t x = 0; x <= m_CursorX; ++x) {
1688 VgaCell* pCell = &m_pBackbuffer[(m_CursorY * BACKBUFFER_STRIDE) + x];
1689 pCell->character = ' ';
1690 pCell->fore = m_Fore;
1691 pCell->back = m_Back;
1692 pCell->flags = 0;
1693 }
1694}
1695
1696void TextIO::eraseLine() {
1697 LockGuard<Mutex> guard(m_Lock);
1698
1699 for (size_t x = 0; x < BACKBUFFER_STRIDE; ++x) {
1700 VgaCell* pCell = &m_pBackbuffer[(m_CursorY * BACKBUFFER_STRIDE) + x];
1701 pCell->character = ' ';
1702 pCell->fore = m_Fore;
1703 pCell->back = m_Back;
1704 pCell->flags = 0;
1705 }
1706}
1707
1708void TextIO::eraseScreen(uint8_t character) {
1709 LockGuard<Mutex> guard(m_Lock);
1710
1711 for (size_t y = 0; y < BACKBUFFER_ROWS; ++y) {
1712 for (size_t x = 0; x < BACKBUFFER_STRIDE; ++x) {
1713 VgaCell* pCell = &m_pBackbuffer[(y * BACKBUFFER_STRIDE) + x];
1714 pCell->character = character;
1715 pCell->fore = m_Fore;
1716 pCell->back = m_Back;
1717 pCell->flags = 0;
1718 }
1719 }
1720}
1721
1722void TextIO::goHome(ssize_t xmove, ssize_t ymove) {
1723 // Reset X/Y
1724 if (m_CurrentModes & Origin) {
1725 m_CursorX = m_LeftMargin + xmove;
1726 m_CursorY = m_ScrollStart + ymove;
1727 } else {
1728 m_CursorX = xmove;
1729 m_CursorY = ymove;
1730 }
1731}
1732
1734 ByteSet(m_pBackbuffer, 0, BACKBUFFER_STRIDE * BACKBUFFER_ROWS * sizeof(VgaCell));
1735}
1736
1737void TextIO::flip(bool timer, bool hideState) {
1738 LockGuard<Mutex> guard(m_Lock);
1739
1740#if PEDIGREE_ACTIVITY_DIAGNOSTICS
1741 const uint64_t activityStart = ActivityDiagnostics::timestamp();
1742 size_t activityCells = 0;
1743#endif
1744
1745 const VgaColour defaultBack = Black, defaultFore = LightGrey;
1746
1747 // Avoid flipping if we do not have a VGA instance.
1748 if (!m_pVga)
1749 return;
1750
1751 // Avoid flipping if we do not own the VGA instance.
1752 if (!isPrimary())
1753 return;
1754
1755 // Avoid flipping if we aren't active.
1756 if (!m_bActive)
1757 return;
1758
1759 size_t numRows = m_pVga->getNumRows();
1760 size_t numCols = m_pVga->getNumCols();
1761 if (numRows > BACKBUFFER_ROWS)
1762 numRows = BACKBUFFER_ROWS;
1763 if (numCols > BACKBUFFER_STRIDE)
1764 numCols = BACKBUFFER_STRIDE;
1765
1766 for (size_t y = 0; y < numRows; ++y) {
1767 for (size_t x = 0; x < numCols; ++x) {
1768 size_t dirtyIndex = (y * BACKBUFFER_STRIDE) + x;
1769 VgaCell* pCell = &m_pBackbuffer[(y * BACKBUFFER_STRIDE) + x];
1770 if (m_pVterm && !timer && !m_VtermDirty[dirtyIndex])
1771 continue;
1772 if (timer && !(pCell->flags & Blink))
1773 continue;
1774 if (timer) {
1775 if (pCell->flags & Blink)
1776 pCell->hidden = hideState;
1777 else
1778 pCell->hidden = false; // Unhide if blink removed.
1779 }
1780
1781 VgaColour fore = pCell->fore;
1782 VgaColour back = pCell->back;
1783
1784 // Bold.
1785 if ((pCell->flags & Bright) && (fore < DarkGrey))
1786 fore = adjustColour(fore, true);
1787
1788 if (pCell->flags & Inverse) {
1789 // Invert colours.
1790 VgaColour tmp = fore;
1791 fore = back;
1792 back = tmp;
1793 }
1794
1795 uint8_t attrib = (back << 4) | (fore & 0x0F);
1796 if (m_CurrentModes & Screen) {
1797 // DECSCNM only applies to cells without colours.
1798 if (pCell->fore == defaultFore && pCell->back == defaultBack) {
1799 attrib = (fore << 4) | (back & 0x0F);
1800 }
1801 }
1802
1803 uint16_t front = (pCell->hidden ? ' ' : pCell->character) | (attrib << 8);
1804 m_pFramebuffer[(y * numCols) + x] = front;
1805#if PEDIGREE_ACTIVITY_DIAGNOSTICS
1806 ++activityCells;
1807#endif
1808 if (m_pVterm)
1809 m_VtermDirty[dirtyIndex] = 0;
1810 }
1811 }
1812 m_pVga->flush();
1813#if PEDIGREE_ACTIVITY_DIAGNOSTICS
1814 ActivityDiagnostics::recordFramebufferFlip(activityCells,
1815 ActivityDiagnostics::timestamp() - activityStart);
1816#endif
1817}
1818
1819uint64_t TextIO::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock) {
1820 return m_OutBuffer.read(reinterpret_cast<char*>(buffer), size, bCanBlock);
1821}
1822
1823uint64_t TextIO::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock) {
1824 writeStr(reinterpret_cast<const char*>(buffer), size);
1825 return size;
1826}
1827
1828int TextIO::select(bool bWriting, int timeout) {
1829 if (bWriting) {
1830 return m_OutBuffer.canWrite(timeout > 0) ? 1 : 0;
1831 } else {
1832 return m_OutBuffer.canRead(timeout > 0) ? 1 : 0;
1833 }
1834}
1835
1836void TextIO::flipThread() {
1837 while (m_bInitialised) {
1838 bool bBlinkOn = m_NextInterval != BLINK_ON_PERIOD;
1839 if (bBlinkOn)
1840 m_NextInterval = BLINK_ON_PERIOD;
1841 else
1842 m_NextInterval = BLINK_OFF_PERIOD;
1843
1844 // Flip now, triggered by the passage of time.
1845 flip(true, !bBlinkOn);
1846
1847 // Wait for the next trigger time.
1848 if (!m_FlipWake.acquire(1, m_NextInterval / 1000, (m_NextInterval % 1000) * 1000) &&
1849 Processor::information().getCurrentThread()->getUnwindState() != Thread::Continue) {
1850 return;
1851 }
1852 }
1853}
1854
1855uint8_t TextIO::translate(uint32_t codepoint) {
1856 // Translate codepoints into Code Page 437 representation.
1857 switch (codepoint) {
1858 case 0x00C7:
1859 return 0x80;
1860 case 0x00FC:
1861 return 0x81;
1862 case 0x00E9:
1863 return 0x82;
1864 case 0x00E2:
1865 return 0x83;
1866 case 0x00E4:
1867 return 0x84; // ä
1868 case 0x00E0:
1869 return 0x85;
1870 case 0x00E5:
1871 return 0x86;
1872 case 0x00E7:
1873 return 0x87;
1874 case 0x00EA:
1875 return 0x88;
1876 case 0x00EB:
1877 return 0x89;
1878 case 0x00E8:
1879 return 0x8A;
1880 case 0x00EF:
1881 return 0x8B;
1882 case 0x00EE:
1883 return 0x8C;
1884 case 0x00EC:
1885 return 0x8D;
1886 case 0x00C4:
1887 return 0x8E;
1888 case 0x00C5:
1889 return 0x8F;
1890 case 0x00C9:
1891 return 0x90;
1892 case 0x00E6:
1893 return 0x91;
1894 case 0x00C6:
1895 return 0x92;
1896 case 0x00F4:
1897 return 0x93;
1898 case 0x00F6:
1899 return 0x94;
1900 case 0x00F2:
1901 return 0x95;
1902 case 0x00FB:
1903 return 0x96;
1904 case 0x00F9:
1905 return 0x97;
1906 case 0x00FF:
1907 return 0x98;
1908 case 0x00D6:
1909 return 0x99;
1910 case 0x00DC:
1911 return 0x9A;
1912 case 0x00A2:
1913 return 0x9B;
1914 case 0x00A3:
1915 return 0x9C;
1916 case 0x00A5:
1917 return 0x9D;
1918 case 0x20A7:
1919 return 0x9E;
1920 case 0x0192:
1921 return 0x9F;
1922 case 0x00E1:
1923 return 0xA0;
1924 case 0x00ED:
1925 return 0xA1;
1926 case 0x00F3:
1927 return 0xA2;
1928 case 0x00FA:
1929 return 0xA3;
1930 case 0x00F1:
1931 return 0xA4;
1932 case 0x00D1:
1933 return 0xA5;
1934 case 0x00AA:
1935 return 0xA6;
1936 case 0x00BA:
1937 return 0xA7;
1938 case 0x00BF:
1939 return 0xA8;
1940 case 0x2310:
1941 return 0xA9;
1942 case 0x00AC:
1943 return 0xAA;
1944 case 0x00BD:
1945 return 0xAB;
1946 case 0x00BC:
1947 return 0xAC;
1948 case 0x00A1:
1949 return 0xAD;
1950 case 0x00AB:
1951 return 0xAE; // «
1952 case 0x00BB:
1953 return 0xAF; // »
1954 case 0x2591:
1955 return 0xB0;
1956 case 0x2592:
1957 return 0xB1;
1958 case 0x2593:
1959 return 0xB2;
1960 case 0x2502:
1961 return 0xB3;
1962 case 0x2524:
1963 return 0xB4;
1964 case 0x2561:
1965 return 0xB5;
1966 case 0x2562:
1967 return 0xB6;
1968 case 0x2556:
1969 return 0xB7;
1970 case 0x2555:
1971 return 0xB8;
1972 case 0x2563:
1973 return 0xB9;
1974 case 0x2551:
1975 return 0xBA;
1976 case 0x2557:
1977 return 0xBB;
1978 case 0x255D:
1979 return 0xBC;
1980 case 0x255C:
1981 return 0xBD;
1982 case 0x255B:
1983 return 0xBE;
1984 case 0x2510:
1985 return 0xBF;
1986 case 0x2514:
1987 return 0xC0;
1988 case 0x2534:
1989 return 0xC1;
1990 case 0x252C:
1991 return 0xC2;
1992 case 0x251C:
1993 return 0xC3;
1994 case 0x2500:
1995 return 0xC4;
1996 case 0x253C:
1997 return 0xC5;
1998 case 0x255E:
1999 return 0xC6;
2000 case 0x255F:
2001 return 0xC7;
2002 case 0x255A:
2003 return 0xC8;
2004 case 0x2554:
2005 return 0xC9;
2006 case 0x2569:
2007 return 0xCA;
2008 case 0x2566:
2009 return 0xCB;
2010 case 0x2560:
2011 return 0xCC;
2012 case 0x2550:
2013 return 0xCD;
2014 case 0x256C:
2015 return 0xCE;
2016 case 0x2567:
2017 return 0xCF;
2018 case 0x2568:
2019 return 0xD0;
2020 case 0x2564:
2021 return 0xD1;
2022 case 0x2565:
2023 return 0xD2;
2024 case 0x2559:
2025 return 0xD3;
2026 case 0x2558:
2027 return 0xD4;
2028 case 0x2552:
2029 return 0xD5;
2030 case 0x2553:
2031 return 0xD6;
2032 case 0x256B:
2033 return 0xD7;
2034 case 0x256A:
2035 return 0xD8;
2036 case 0x2518:
2037 return 0xD9;
2038 case 0x250C:
2039 return 0xDA;
2040 case 0x2588:
2041 return 0xDB;
2042 case 0x2584:
2043 return 0xDC;
2044 case 0x258C:
2045 return 0xDD;
2046 case 0x2590:
2047 return 0xDE;
2048 case 0x2580:
2049 return 0xDF;
2050 case 0x03B1:
2051 return 0xE0;
2052 case 0x00DF:
2053 return 0xE1;
2054 case 0x0393:
2055 return 0xE2;
2056 case 0x03C0:
2057 return 0xE3;
2058 case 0x03A3:
2059 return 0xE4;
2060 case 0x03C3:
2061 return 0xE5;
2062 case 0x00B5:
2063 return 0xE6;
2064 case 0x03C4:
2065 return 0xE7;
2066 case 0x03A6:
2067 return 0xE8;
2068 case 0x0398:
2069 return 0xE9;
2070 case 0x03A9:
2071 return 0xEA;
2072 case 0x03B4:
2073 return 0xEB;
2074 case 0x221E:
2075 return 0xEC;
2076 case 0x03C6:
2077 return 0xED;
2078 case 0x03B5:
2079 return 0xEE;
2080 case 0x2229:
2081 return 0xEF;
2082 case 0x2261:
2083 return 0xF0;
2084 case 0x00B1:
2085 return 0xF1;
2086 case 0x2265:
2087 return 0xF2;
2088 case 0x2264:
2089 return 0xF3;
2090 case 0x2320:
2091 return 0xF4;
2092 case 0x2321:
2093 return 0xF5;
2094 case 0x00F7:
2095 return 0xF6;
2096 case 0x2248:
2097 return 0xF7;
2098 case 0x00B0:
2099 return 0xF8;
2100 case 0x2219:
2101 return 0xF9;
2102 case 0x00B7:
2103 return 0xFA;
2104 case 0x221A:
2105 return 0xFB;
2106 case 0x207F:
2107 return 0xFC;
2108 case 0x00B2:
2109 return 0xFD;
2110 case 0x25A0:
2111 return 0xFE;
2112 case 0x00A0:
2113 return 0xFF;
2114 }
2115
2116 if (codepoint <= 0xFF)
2117 return codepoint & 0xFF;
2118 else
2119 return 219; // ASCII shaded box.
2120}
2121
2122static int startFlipThread(void* param) {
2123 TextIO* tio = reinterpret_cast<TextIO*>(param);
2124 tio->flipThread();
2125 return 0;
2126}
2127
2128void TextIO::inputCallback(InputManager::InputNotification& in) {
2129 if (!in.meta) {
2130 return;
2131 }
2132
2133 TextIO* p = reinterpret_cast<TextIO*>(in.meta);
2134 p->handleInput(in);
2135}
2136
2137void TextIO::handleInput(InputManager::InputNotification& in) {
2138 // Drop input if we are not the console owner.
2139 if (!isPrimary())
2140 return;
2141
2142 if (m_InputMode == Raw) {
2143 if (in.type != InputManager::MachineKey) {
2144 return;
2145 }
2146
2147 uint8_t buf = in.data.rawkey.scancode | (in.data.rawkey.keyUp ? 0x80 : 0);
2148 if (m_OutBuffer.writeAvailable(reinterpret_cast<char*>(&buf), sizeof(buf), true) !=
2149 sizeof(buf)) {
2150 WARNING("TextIO: input buffer is full or closed, dropping keypress");
2151 return;
2152 }
2153
2154 dataChanged();
2155 return;
2156 }
2157
2158 if (in.type != InputManager::Key) {
2159 // not actually keyboard input - ignore
2160 return;
2161 }
2162
2163 uint64_t c = in.data.key.key;
2164
2165 int direction = -1;
2166 if (c & SPECIAL_KEY) {
2167 uint32_t k = c & 0xFFFFFFFFULL;
2168 char* str = reinterpret_cast<char*>(&k);
2169
2170 if (!StringCompareN(str, "left", 4)) {
2171 direction = 0; // left
2172 } else if (!StringCompareN(str, "righ", 4)) {
2173 direction = 1; // right
2174 } else if (!StringCompareN(str, "up", 2)) {
2175 direction = 2; // up
2176 } else if (!StringCompareN(str, "down", 4)) {
2177 direction = 3; // down
2178 } else {
2179 // unhandled special key, don't send to app
2180 return;
2181 }
2182 } else if (c & CTRL_KEY) {
2183 // CTRL-key = unprintable (ie, CTRL-C, CTRL-U)
2184 c &= 0x1F;
2185 }
2186
2187 if (c == '\n')
2188 c = '\r'; // Enter key (ie, return) - CRtoNL.
2189
2190 const char* input = nullptr;
2191 size_t inputLength = 0;
2192 char buf[4] = {};
2193 if (direction >= 0) {
2194 switch (direction) {
2195 case 0:
2196 input = "\033[D";
2197 inputLength = 3;
2198 break;
2199 case 1:
2200 input = "\033[C";
2201 inputLength = 3;
2202 break;
2203 case 2:
2204 input = "\033[A";
2205 inputLength = 3;
2206 break;
2207 case 3:
2208 input = "\033[B";
2209 inputLength = 3;
2210 break;
2211 default:
2212 break;
2213 }
2214 } else if (c & ALT_KEY) {
2215 // ALT escaped key
2216 c &= 0x7F;
2217 buf[0] = '\033';
2218 buf[1] = static_cast<char>(c & 0xFF);
2219 input = buf;
2220 inputLength = 2;
2221 } else if (c) {
2222 inputLength = String::Utf32ToUtf8(c & 0xFFFFFFFF, buf);
2223 input = buf;
2224 }
2225
2226 if (!inputLength) {
2227 return;
2228 }
2229
2230 // Input callbacks run in thread context. Wait out reader contention without
2231 // waiting for capacity or splitting a terminal escape sequence.
2232 if (m_OutBuffer.writeAvailable(input, inputLength, true) != inputLength) {
2233 WARNING("TextIO: input buffer is full or closed, dropping keypress");
2234 return;
2235 }
2236
2237 dataChanged();
2238}
2239
2241 // Set ourselves as the primary and get straight to work loading our own
2242 // terminal state (instead of the previous one's)
2243 {
2244 LockGuard<Mutex> guard(m_Lock);
2245 m_bOwnsConsole = true;
2246 ByteSet(m_VtermDirty, 1, sizeof(m_VtermDirty));
2247 if (m_pVga) {
2248 m_pVga->moveCursor(m_CursorX, m_CursorY);
2249 }
2250 }
2251 flip();
2252}
2253
2255 LockGuard<Mutex> guard(m_Lock);
2256 m_bOwnsConsole = false;
2257}
2258
2259bool TextIO::isPrimary() const {
2260 return m_bOwnsConsole;
2261}
2262
2263void TextIO::setMode(InputMode mode) {
2264 m_InputMode = mode;
2265}
2266
2267TextIO::InputMode TextIO::getMode() const {
2268 return m_InputMode;
2269}
size_t write(const T *buffer, size_t count, bool block=true)
Definition Buffer.cc:103
bool canRead(bool block)
Definition Buffer.cc:525
bool canWrite(bool block)
Definition Buffer.cc:491
size_t writeAvailable(const T *buffer, size_t count, bool atomic=false)
Definition Buffer.cc:201
size_t read(T *buffer, size_t count, bool block=true)
Definition Buffer.cc:296
Definition File.h:75
void dataChanged()
Definition File.cc:1452
static const int Key
static InputManager & instance()
Singleton design.
void installCallback(CallbackType filter, callback_t callback, void *meta=0)
Installs a callback.
void removeCallback(callback_t callback, void *meta=0)
Removes a callback.
virtual Vga * getVga(size_t n)=0
void join()
Definition OwnedThread.h:49
static PhysicalMemoryManager & instance()
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
LargeStaticString str
Definition Process.h:1029
Spinlock m_Lock
Definition Process.h:1204
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
void release(size_t n=1)
Definition Semaphore.cc:549
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:484
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:355
static size_t Utf32ToUtf8(uint32_t utf32, char *utf8)
Definition String.cc:538
uint8_t m_G0
Definition TextIO.h:296
bool initialise(bool bClear=true)
Definition TextIO.cc:139
void unmarkPrimary()
Definition TextIO.cc:2254
InputMode getMode() const
Definition TextIO.cc:2267
bool m_bUtf8
Definition TextIO.h:302
void flip(bool timer=false, bool hideState=false)
Definition TextIO.cc:1737
Buffer< char > m_OutBuffer
Definition TextIO.h:290
Mutex m_Lock
Definition TextIO.h:321
uint32_t m_nCharacter
Definition TextIO.h:305
void writeStr(const char *s, size_t len)
Definition TextIO.cc:492
bool m_bActive
Definition TextIO.h:314
bool isPrimary() const
Definition TextIO.cc:2259
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition TextIO.cc:1823
void clearBackbuffer()
Definition TextIO.cc:1733
size_t m_nUtf8Handled
Definition TextIO.h:308
@ Inverse
AKA: 'Negative'.
Definition TextIO.h:97
@ Bright
AKA: 'Bold'.
Definition TextIO.h:100
bool m_bOwnsConsole
Definition TextIO.h:332
void markPrimary()
Definition TextIO.cc:2240
void setMode(InputMode mode)
Definition TextIO.cc:2263
uint64_t m_NextInterval
Definition TextIO.h:299
Semaphore m_FlipWake
Definition TextIO.h:326
uint8_t translate(uint32_t codepoint)
Definition TextIO.cc:1855
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition TextIO.cc:1819
virtual int select(bool bWriting=false, int timeout=0)
Definition TextIO.cc:1828
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:517
virtual void moveCursor(size_t nX, size_t nY)=0
virtual bool setLargestTextMode()=0
virtual size_t getNumRows()=0
virtual size_t getNumCols()=0
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
bool keyUp
Whether this is a keyUp event or not.
uint8_t scancode
Machine-specific scancode for the key.