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