The Pedigree Project  0.1
Xterm.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 "Xterm.h"
21 #include "Font.h"
22 #include "Terminal.h"
23 #include <string.h>
24 #include <sys/klog.h>
25 #include <time.h>
26 
27 #define XTERM_BOLD 0x1
28 #define XTERM_UNDERLINE 0x2
29 #define XTERM_INVERSE 0x4
30 #define XTERM_BRIGHTFG 0x8
31 #define XTERM_BRIGHTBG 0x10
32 #define XTERM_BORDER 0x20
33 #define XTERM_ITALIC 0x40
34 
35 #define C_BLACK 0
36 #define C_RED 1
37 #define C_GREEN 2
38 #define C_YELLOW 3
39 #define C_BLUE 4
40 #define C_MAGENTA 5
41 #define C_CYAN 6
42 #define C_WHITE 7
43 
44 #define C_BRIGHT 8
45 
46 // Set to 1 to use Framebuffer::line instead of Unicode lines.
47 // Fairly buggy unfortunately.
48 #define USE_FRAMEBUFFER_LINES 1
49 
50 #include "environment.h"
51 
52 #include "pedigree/native/config/Config.h"
53 #include "pedigree/native/graphics/Graphics.h"
54 
55 #include <cairo/cairo.h>
56 
57 // RGBA -> BGRA
58 
59 extern uint32_t g_Colours[];
60 extern uint32_t g_BrightColours[];
61 
62 uint8_t g_DefaultFg = 7;
63 uint8_t g_DefaultBg = 0;
64 
65 bool g_FontsPrecached = false;
66 
67 static void getXtermColorFromDb(const char *colorName, uint8_t &color)
68 {
69 // Use defaults on Linux.
70 #ifndef TARGET_LINUX
71  // The query string
72  std::string sQuery;
73 
74  // Create the query string
75  sQuery += "select r from 'colour_scheme' where name='";
76  sQuery += colorName;
77  sQuery += "';";
78 
79  // Query the database
80  Config::Result *pResult = Config::query(sQuery.c_str());
81 
82  // Did the query fail?
83  if (!pResult)
84  {
85  klog(LOG_ALERT, "TUI: Error looking up '%s' colour.", colorName);
86  return;
87  }
88  if (!pResult->succeeded())
89  {
90  klog(
91  LOG_ALERT, "TUI: Error looking up '%s' colour: %s\n", colorName,
92  pResult->errorMessage().c_str());
93  delete pResult;
94  return;
95  }
96 
97  // Get the color from the query result
98  color = pResult->getNum(0, "r");
99 
100  // Dispose of the query result
101  delete pResult;
102 #endif
103 }
104 
105 Xterm::Xterm(
106  PedigreeGraphics::Framebuffer *pFramebuffer, size_t nWidth, size_t nHeight,
107  size_t offsetLeft, size_t offsetTop, Terminal *pT, class Widget *pWidget,
108  Tui *pTui, Font *pNormalFont, Font *pBoldFont)
109  : m_ActiveBuffer(0), m_Cmd(), m_OsCtl(), m_bChangingState(false),
110  m_bContainedBracket(false), m_bContainedParen(false),
111  m_bIsOsControl(false), m_Flags(0), m_Modes(0), m_TabStops(0), m_SavedX(0),
112  m_SavedY(0), m_pT(pT), m_bFbMode(false), m_pWidget(pWidget), m_pTui(pTui)
113 {
114  setFonts(pNormalFont, pBoldFont);
115 
116  size_t width = nWidth / m_pNormalFont->getWidth();
117  size_t height = nHeight / m_pNormalFont->getHeight();
118 
119  m_pWindows[0] = new Window(
120  height, width, pFramebuffer, 1000, offsetLeft, offsetTop, nWidth, this);
121  m_pWindows[1] = new Window(
122  height, width, pFramebuffer, 1000, offsetLeft, offsetTop, nWidth, this);
123 
124  getXtermColorFromDb("xterm-bg", g_DefaultBg);
125  getXtermColorFromDb("xterm-fg", g_DefaultFg);
126 
127  // Set default tab stops.
128  m_TabStops = new char[getStride()];
129  memset(m_TabStops, 0, getStride());
130  for (size_t i = 0; i < getStride(); i += 8)
131  {
132  m_TabStops[i] = '|';
133  }
134 
135  // Set default modes.
136  m_Modes |= AnsiVt52;
137 
138  m_Cmd.cur_param = 0;
139  m_Cmd.has_param = false;
140 
141  m_OsCtl.cur_param = 0;
142  m_OsCtl.has_param = false;
143 }
144 
145 Xterm::~Xterm()
146 {
147  delete[] m_TabStops;
148  delete m_pWindows[0];
149  delete m_pWindows[1];
150 }
151 
152 void Xterm::processKey(uint64_t key)
153 {
154  if (key & Keyboard::Special)
155  {
156  // Handle specially.
157  uint32_t utf32 = key & ~0UL;
158  char *str = reinterpret_cast<char *>(&utf32);
159  if (!strncmp(str, "left", 4))
160  {
161  m_pT->addToQueue('\e');
162  if ((m_Modes & AnsiVt52) == AnsiVt52)
163  {
164  if (m_Modes & CursorKey)
165  m_pT->addToQueue('O');
166  else
167  m_pT->addToQueue('[');
168  }
169  m_pT->addToQueue('D');
170  }
171  if (!strncmp(str, "righ", 4))
172  {
173  m_pT->addToQueue('\e');
174  if ((m_Modes & AnsiVt52) == AnsiVt52)
175  {
176  if (m_Modes & CursorKey)
177  m_pT->addToQueue('O');
178  else
179  m_pT->addToQueue('[');
180  }
181  m_pT->addToQueue('C');
182  }
183  if (!strncmp(str, "up", 2))
184  {
185  m_pT->addToQueue('\e');
186  if ((m_Modes & AnsiVt52) == AnsiVt52)
187  {
188  if (m_Modes & CursorKey)
189  m_pT->addToQueue('O');
190  else
191  m_pT->addToQueue('[');
192  }
193  m_pT->addToQueue('A');
194  }
195  if (!strncmp(str, "down", 4))
196  {
197  m_pT->addToQueue('\e');
198  if ((m_Modes & AnsiVt52) == AnsiVt52)
199  {
200  if (m_Modes & CursorKey)
201  m_pT->addToQueue('O');
202  else
203  m_pT->addToQueue('[');
204  }
205  m_pT->addToQueue('B');
206  }
207  }
208  else if (key & Keyboard::Alt)
209  {
210  // Xterm ALT escape = ESC <char>
211  m_pT->addToQueue('\e');
212  m_pT->addToQueue(key & 0x7F);
213  }
214  else if (key == '\n' || key == '\r')
215  {
216  m_pT->addToQueue('\r');
217  if (m_Modes & LineFeedNewLine)
218  m_pT->addToQueue('\n');
219  }
220  else
221  {
222  uint32_t utf32 = key & 0xFFFFFFFF;
223 
224  // Begin Utf-32 -> Utf-8 conversion.
225  char buf[4];
226  size_t nbuf = 0;
227  if (utf32 <= 0x7F)
228  {
229  buf[0] = utf32 & 0x7F;
230  nbuf = 1;
231  }
232  else if (utf32 <= 0x7FF)
233  {
234  buf[0] = 0xC0 | ((utf32 >> 6) & 0x1F);
235  buf[1] = 0x80 | (utf32 & 0x3F);
236  nbuf = 2;
237  }
238  else if (utf32 <= 0xFFFF)
239  {
240  buf[0] = 0xE0 | ((utf32 >> 12) & 0x0F);
241  buf[1] = 0x80 | ((utf32 >> 6) & 0x3F);
242  buf[2] = 0x80 | (utf32 & 0x3F);
243  nbuf = 3;
244  }
245  else if (utf32 <= 0x10FFFF)
246  {
247  buf[0] = 0xE0 | ((utf32 >> 18) & 0x07);
248  buf[1] = 0x80 | ((utf32 >> 12) & 0x3F);
249  buf[2] = 0x80 | ((utf32 >> 6) & 0x3F);
250  buf[3] = 0x80 | (utf32 & 0x3F);
251  nbuf = 4;
252  }
253 
254  // End Utf-32 -> Utf-8 conversion.
255  for (size_t i = 0; i < nbuf; i++)
256  m_pT->addToQueue(buf[i]);
257  }
258 
259  m_pT->addToQueue(0, true);
260 }
261 
262 bool Xterm::setFlagsForUtf32(uint32_t utf32)
263 {
264  size_t oldFlags = m_Flags;
265  switch (utf32)
266  {
267  case '\e':
268  m_Flags |= Escape;
269  break;
270 
271  case '?':
272  m_Flags |= Question;
273  break;
274 
275  case ' ':
276  m_Flags |= Space;
277  break;
278 
279  case '#':
280  m_Flags |= Hash;
281  break;
282 
283  case '%':
284  m_Flags |= Percent;
285  break;
286 
287  case '(':
288  m_Flags |= LeftRound;
289  break;
290 
291  case ')':
292  m_Flags |= RightRound;
293  break;
294 
295  case '[':
296  m_Flags |= LeftSquare;
297  break;
298 
299  case ']':
300  m_Flags |= RightSquare;
301  break;
302 
303  case '_':
304  m_Flags |= Underscore;
305  break;
306 
307  case '/':
308  m_Flags |= Slash;
309  break;
310 
311  case '.':
312  m_Flags |= Period;
313  break;
314 
315  case '-':
316  m_Flags |= Minus;
317  break;
318 
319  case '+':
320  m_Flags |= Plus;
321  break;
322 
323  case '*':
324  m_Flags |= Asterisk;
325  break;
326 
327  case '\\':
328  m_Flags |= Backslash;
329  break;
330 
331  case '!':
332  m_Flags |= Bang;
333  break;
334 
335  case '\'':
336  m_Flags |= Apostrophe;
337  break;
338 
339  case '"':
340  m_Flags |= Quote;
341  break;
342 
343  case '$':
344  m_Flags |= Dollar;
345  break;
346 
347  default:
348  break;
349  }
350 
351  return oldFlags != m_Flags;
352 }
353 
354 void Xterm::write(uint32_t utf32, DirtyRectangle &rect)
355 {
356 #ifdef XTERM_DEBUG_EXTRA
357  klog(LOG_INFO, "XTerm::write(%c/%x)", (char) utf32, utf32);
358 #endif
359 
360  // Special cases, for controls that require readahead.
361  if (m_Flags & Vt52SetCursorWaitY)
362  {
363  m_Cmd.params[0] = (char) utf32 - 32;
364  m_Flags &= ~Vt52SetCursorWaitY;
365  m_Flags |= Vt52SetCursorWaitX;
366  return;
367  }
368  else if (m_Flags & Vt52SetCursorWaitX)
369  {
370  m_Cmd.params[1] = (char) utf32 - 32;
371  m_pWindows[m_ActiveBuffer]->setCursor(
372  m_Cmd.params[1], m_Cmd.params[0], rect);
373  m_Flags = 0;
374  return;
375  }
376 
377  if (!m_Flags)
378  {
379  switch (utf32)
380  {
381  case 0x05:
382  {
383  // Reply with answerback.
384  const char *answerback = "\e[1;2c";
385  while (*answerback)
386  {
387  m_pT->addToQueue(*answerback);
388  ++answerback;
389  }
390  }
391  case 0x08:
392  m_pWindows[m_ActiveBuffer]->backspace(rect);
393  break;
394 
395  case '\n':
396  case 0x0B:
397  case 0x0C:
398  if (m_Modes & LineFeedNewLine)
399  m_pWindows[m_ActiveBuffer]->cursorDownAndLeftToMargin(rect);
400  else
401  m_pWindows[m_ActiveBuffer]->cursorDown(1, rect);
402  break;
403 
404  case '\t':
405  m_pWindows[m_ActiveBuffer]->cursorTab(rect);
406  break;
407 
408  case '\r':
409  m_pWindows[m_ActiveBuffer]->cursorLeftToMargin(rect);
410  break;
411 
412  case 0x0E:
413  case 0x0F:
414  // SHIFT-IN, SHIFT-OUT (character sets)
415  break;
416 
417  case '\e':
418  m_Flags = Escape;
419 
420  m_Cmd.cur_param = 0;
421  m_Cmd.has_param = false;
422  memset(
423  m_Cmd.params, 0,
424  sizeof(m_Cmd.params[0]) * XTERM_MAX_PARAMS);
425 
426  m_OsCtl.cur_param = 0;
427  m_OsCtl.has_param = false;
428  for (size_t i = 0; i < XTERM_MAX_PARAMS; ++i)
429  {
430  m_OsCtl.params[i] = "";
431  }
432  break;
433 
434  default:
435  // Handle line drawing.
436  if (m_pWindows[m_ActiveBuffer]->getLineRenderMode())
437  {
438  switch (utf32)
439  {
440  case 'j':
441  utf32 = 0x2518;
442  break; // Lower right corner
443  case 'k':
444  utf32 = 0x2510;
445  break; // Upper right corner
446  case 'l':
447  utf32 = 0x250c;
448  break; // Upper left corner
449  case 'm':
450  utf32 = 0x2514;
451  break; // Lower left corner
452  case 'n':
453  utf32 = 0x253c;
454  break; // Crossing lines.
455  case 'q':
456  utf32 = 0x2500;
457  break; // Horizontal line.
458  case 't':
459  utf32 = 0x251c;
460  break; // Left 'T'
461  case 'u':
462  utf32 = 0x2524;
463  break; // Right 'T'
464  case 'v':
465  utf32 = 0x2534;
466  break; // Bottom 'T'
467  case 'w':
468  utf32 = 0x252c;
469  break; // Top 'T'
470  case 'x':
471  utf32 = 0x2502;
472  break; // Vertical bar
473  default:;
474  }
475  }
476 
477  // Printable character?
478  if (utf32 >= 32)
479  {
480  m_pWindows[m_ActiveBuffer]->addChar(utf32, rect);
481  }
482  }
483  }
484  else if (
485  (m_Flags & (Escape | LeftSquare | RightSquare | Underscore)) ==
486  (Escape | LeftSquare))
487  {
488  setFlagsForUtf32(utf32);
489 
490  switch (utf32)
491  {
492  case 0x08:
493  // Backspace within control sequence.
494  m_pWindows[m_ActiveBuffer]->backspace(rect);
495  break;
496 
497  case '\n':
498  case 0x0B:
499  if (m_Modes & LineFeedNewLine)
500  m_pWindows[m_ActiveBuffer]->cursorLeftToMargin(rect);
501  m_pWindows[m_ActiveBuffer]->cursorDown(1, rect);
502  break;
503 
504  case '\r':
505  m_pWindows[m_ActiveBuffer]->cursorLeftToMargin(rect);
506  break;
507 
508  case '0':
509  case '1':
510  case '2':
511  case '3':
512  case '4':
513  case '5':
514  case '6':
515  case '7':
516  case '8':
517  case '9':
518  m_Cmd.params[m_Cmd.cur_param] =
519  m_Cmd.params[m_Cmd.cur_param] * 10 + (utf32 - '0');
520  m_Cmd.has_param = true;
521  break;
522 
523  case ';':
524  case ',':
525  m_Cmd.cur_param++;
526  break;
527 
528  case '@':
529  m_pWindows[m_ActiveBuffer]->insertCharacters(
530  m_Cmd.params[0] ? m_Cmd.params[0] : 1, rect);
531  m_Flags = 0;
532  break;
533 
534  case 'A':
535  if (m_Cmd.has_param && m_Cmd.params[0])
536  m_pWindows[m_ActiveBuffer]->cursorUpWithinMargin(
537  m_Cmd.params[0], rect);
538  else
539  m_pWindows[m_ActiveBuffer]->cursorUpWithinMargin(1, rect);
540  m_Flags = 0;
541  break;
542 
543  case 'B':
544  if (m_Cmd.has_param && m_Cmd.params[0])
545  m_pWindows[m_ActiveBuffer]->cursorDownWithinMargin(
546  m_Cmd.params[0], rect);
547  else
548  m_pWindows[m_ActiveBuffer]->cursorDownWithinMargin(1, rect);
549  m_Flags = 0;
550  break;
551 
552  case 'C':
553  if (m_Cmd.has_param && m_Cmd.params[0])
554  m_pWindows[m_ActiveBuffer]->cursorRightWithinMargin(
555  m_Cmd.params[0], rect);
556  else
557  m_pWindows[m_ActiveBuffer]->cursorRightWithinMargin(
558  1, rect);
559  m_Flags = 0;
560  break;
561 
562  case 'D':
563  if (m_Cmd.has_param && m_Cmd.params[0])
564  m_pWindows[m_ActiveBuffer]->cursorLeftWithinMargin(
565  m_Cmd.params[0], rect);
566  else
567  m_pWindows[m_ActiveBuffer]->cursorLeftWithinMargin(1, rect);
568  m_Flags = 0;
569  break;
570 
571  case 'E':
572  m_pWindows[m_ActiveBuffer]->cursorLeftToMargin(rect);
573  m_pWindows[m_ActiveBuffer]->cursorDown(
574  m_Cmd.params[0] ? m_Cmd.params[0] : 1, rect);
575  m_Flags = 0;
576  break;
577 
578  case 'F':
579  m_pWindows[m_ActiveBuffer]->cursorLeftToMargin(rect);
580  m_pWindows[m_ActiveBuffer]->cursorUp(
581  m_Cmd.params[0] ? m_Cmd.params[0] : 1, rect);
582  m_Flags = 0;
583  break;
584 
585  case 'G':
586  // Absolute column reference. (XTERM)
587  m_pWindows[m_ActiveBuffer]->setCursorX(
588  (m_Cmd.params[0]) ? m_Cmd.params[0] - 1 : 0, rect);
589  m_Flags = 0;
590  break;
591 
592  case 'H':
593  case 'f':
594  {
595  if (m_Cmd.has_param)
596  {
597  size_t x = (m_Cmd.params[1]) ? m_Cmd.params[1] - 1 : 0;
598  size_t y = (m_Cmd.params[0]) ? m_Cmd.params[0] - 1 : 0;
599  if (m_Modes & Origin)
600  {
601  m_pWindows[m_ActiveBuffer]->setCursorRelOrigin(
602  x, y, rect);
603  }
604  else
605  {
606  m_pWindows[m_ActiveBuffer]->setCursor(x, y, rect);
607  }
608  }
609  else
610  {
611  if (m_Modes & Origin)
612  {
613  m_pWindows[m_ActiveBuffer]->cursorToOrigin();
614  }
615  else
616  {
617  m_pWindows[m_ActiveBuffer]->setCursor(0, 0, rect);
618  }
619  }
620  m_Flags = 0;
621  }
622  break;
623 
624  case 'I':
625  if (!m_Cmd.params[0])
626  ++m_Cmd.params[0];
627 
628  for (int n = 0; n < m_Cmd.params[0]; ++n)
629  {
630  m_pWindows[m_ActiveBuffer]->cursorTab(rect);
631  }
632  m_Flags = 0;
633  break;
634 
635  case 'J':
636  switch (m_Cmd.params[0])
637  {
638  case 0: // Erase down.
639  m_pWindows[m_ActiveBuffer]->eraseDown(rect);
640  break;
641  case 1: // Erase up.
642  m_pWindows[m_ActiveBuffer]->eraseUp(rect);
643  break;
644  case 2: // Erase entire screen and move to home.
645  m_pWindows[m_ActiveBuffer]->eraseScreen(rect);
646  break;
647  }
648  m_Flags = 0;
649  break;
650 
651  case 'K':
652  switch (m_Cmd.params[0])
653  {
654  case 0: // Erase end of line.
655  m_pWindows[m_ActiveBuffer]->eraseEOL(rect);
656  break;
657  case 1: // Erase start of line.
658  m_pWindows[m_ActiveBuffer]->eraseSOL(rect);
659  break;
660  case 2: // Erase entire line.
661  m_pWindows[m_ActiveBuffer]->eraseLine(rect);
662  break;
663  }
664  m_Flags = 0;
665  break;
666 
667  case 'L':
668  m_pWindows[m_ActiveBuffer]->insertLines(
669  m_Cmd.params[0] ? m_Cmd.params[0] : 1, rect);
670  m_Flags = 0;
671  break;
672 
673  case 'M':
674  m_pWindows[m_ActiveBuffer]->deleteLines(
675  m_Cmd.params[0] ? m_Cmd.params[0] : 1, rect);
676  m_Flags = 0;
677  break;
678 
679  case 'P':
680  m_pWindows[m_ActiveBuffer]->deleteCharacters(
681  (m_Cmd.params[0]) ? m_Cmd.params[0] : 1, rect);
682  m_Flags = 0;
683  break;
684 
685  case 'S':
686  {
687  size_t nScrollLines = (m_Cmd.params[0]) ? m_Cmd.params[0] : 1;
688  m_pWindows[m_ActiveBuffer]->scrollDown(nScrollLines, rect);
689  m_Flags = 0;
690  }
691  break;
692 
693  case 'T':
694  if (m_Flags & RightAngle)
695  {
697  }
698  else if (m_Cmd.cur_param > 1)
699  {
700  klog(
701  LOG_INFO,
702  "XTERM: highlight mouse tracking is not supported.");
703  }
704  else
705  {
706  size_t nScrollLines =
707  (m_Cmd.params[0]) ? m_Cmd.params[0] : 1;
708  m_pWindows[m_ActiveBuffer]->scrollUp(nScrollLines, rect);
709  }
710  m_Flags = 0;
711  break;
712 
713  case 'X': // Erase characters (XTERM)
714  m_pWindows[m_ActiveBuffer]->eraseChars(
715  (m_Cmd.params[0]) ? m_Cmd.params[0] - 1 : 1, rect);
716  m_Flags = 0;
717  break;
718 
719  case 'Z':
720  if (!m_Cmd.params[0])
721  ++m_Cmd.params[0];
722 
723  for (int n = 0; n < m_Cmd.params[0]; ++n)
724  {
725  m_pWindows[m_ActiveBuffer]->cursorTabBack(rect);
726  }
727  m_Flags = 0;
728  break;
729 
730  case 'c':
731  if (m_Cmd.params[0])
732  {
733  klog(
734  LOG_INFO, "XTERM: Device Attributes command with "
735  "non-zero parameter");
736  }
737  else if (m_Flags & RightAngle)
738  {
739  // Secondary Device Attributes
740  const char *attribs = "\e[?85;95;0c";
741  while (*attribs)
742  {
743  m_pT->addToQueue(*attribs);
744  ++attribs;
745  }
746  }
747  else
748  {
749  // Primary Device Attributes
750  const char *attribs = "\e[?1;2c";
751  while (*attribs)
752  {
753  m_pT->addToQueue(*attribs);
754  ++attribs;
755  }
756  }
757  m_Flags = 0;
758  break;
759 
760  case 'd':
761  // Absolute row reference. (XTERM)
762  m_pWindows[m_ActiveBuffer]->setCursorY(
763  (m_Cmd.params[0]) ? m_Cmd.params[0] - 1 : 0, rect);
764  m_Flags = 0;
765  break;
766 
767  case 'e':
768  // Relative row reference. (XTERM)
769  m_pWindows[m_ActiveBuffer]->cursorDown(
770  (m_Cmd.params[0]) ? m_Cmd.params[0] - 1 : 1, rect);
771  m_Flags = 0;
772  break;
773 
774  case 'g':
775  if ((!m_Cmd.has_param) || (m_Cmd.params[0] == 0))
776  m_pWindows[m_ActiveBuffer]->clearTabStop();
777  else if (m_Cmd.has_param && (m_Cmd.params[0] == 3))
778  m_pWindows[m_ActiveBuffer]->clearAllTabStops();
779  m_Flags = 0;
780  break;
781 
782  case 'h':
783  case 'l':
784  {
785  size_t modesToChange = 0;
786 
787  if (m_Flags & Question)
788  {
789  // DEC private mode set.
790  for (int i = 0; i <= m_Cmd.cur_param; ++i)
791  {
792  switch (m_Cmd.params[i])
793  {
794  case 1:
795  modesToChange |= CursorKey;
796  break;
797  case 2:
798  modesToChange |= AnsiVt52;
799  break;
800  case 3:
801  modesToChange |= Column;
802  break;
803  case 4:
804  modesToChange |= Scrolling;
805  break;
806  case 5:
807  modesToChange |= Screen;
808  break;
809  case 6:
810  modesToChange |= Origin;
811  break;
812  case 7:
813  modesToChange |= AutoWrap;
814  break;
815  case 8:
816  modesToChange |= AutoRepeat;
817  break;
818  case 40:
820  klog(LOG_INFO, "(Dis)Allowing 80->132 mode.");
821  break;
822  case 45:
823  klog(LOG_INFO, "Reverse-wraparound mode.");
824  break;
825  case 67:
826  modesToChange |= AppKeypad;
827  break;
828  case 69:
829  modesToChange |= Margin;
830  break;
831  case 1048:
832  case 1049:
833  if (utf32 == 'h')
834  {
835  m_SavedX = m_pWindows[m_ActiveBuffer]
836  ->getCursorX();
837  m_SavedY = m_pWindows[m_ActiveBuffer]
838  ->getCursorY();
839  }
840  else
841  {
842  m_pWindows[m_ActiveBuffer]->setCursor(
843  m_SavedX, m_SavedY, rect);
844  }
845  case 47:
846  case 1047:
847  // 1048 is only save/restore cursor.
848  if (m_Cmd.params[i] != 1048)
849  {
850  m_ActiveBuffer = (utf32 == 'h') ? 1 : 0;
851  if ((utf32 == 'h') &&
852  (m_Cmd.params[i] == 1049))
853  {
854  // Clear new buffer.
855  m_pWindows[m_ActiveBuffer]->eraseScreen(
856  rect);
857  }
858  else
859  {
860  // Merely switch to the new buffer.
861  m_pWindows[m_ActiveBuffer]->renderAll(
862  rect, m_pWindows[0]);
863  }
864  }
865  break;
866  default:
867  klog(
868  LOG_INFO,
869  "XTERM: unknown DEC Private Mode %d",
870  m_Cmd.params[i]);
871  break;
872  }
873  }
874  }
875  else
876  {
877  for (int i = 0; i <= m_Cmd.cur_param; ++i)
878  {
879  switch (m_Cmd.params[i])
880  {
881  case 4:
882  modesToChange |= Insert;
883  break;
884  case 20:
885  modesToChange |= LineFeedNewLine;
886  break;
887  default:
888  klog(
889  LOG_INFO, "XTERM: unknown standard mode %d",
890  m_Cmd.params[i]);
891  break;
892  }
893  }
894  }
895 
896  if (utf32 == 'h')
897  {
898  m_Modes |= modesToChange;
899  }
900  else
901  {
902  m_Modes &= ~(modesToChange);
903  }
904 
905  // Setting some modes causes things to change.
906  if (modesToChange & Origin)
907  {
908  if (m_Modes & Origin)
909  {
910  // Reset origin to margins.
911  m_pWindows[m_ActiveBuffer]->cursorToOrigin();
912  }
913  else
914  {
915  // Move to top left corner.
916  m_pWindows[m_ActiveBuffer]->setCursor(0, 0, rect);
917  }
918  }
919 
920  if (modesToChange & Column)
921  {
922  // Reset margins.
923  if (m_Modes & Column)
924  {
925  m_pWindows[m_ActiveBuffer]->setMargins(0, XTERM_WIDE);
926  }
927  else
928  {
929  m_pWindows[m_ActiveBuffer]->setMargins(
930  0, XTERM_STANDARD);
931  }
932 
933  m_pWindows[m_ActiveBuffer]->eraseScreen(rect);
934  m_pWindows[m_ActiveBuffer]->setScrollRegion(-1, -1);
935 
936  // Cursor to home.
937  m_pWindows[m_ActiveBuffer]->setCursor(0, 0, rect);
938  }
939 
940  if (modesToChange & Screen)
941  {
942  renderAll(rect);
943  }
944  }
945  m_Flags = 0;
946  break;
947 
948  case 'm':
949  {
950  // Character attributes.
951  for (int i = 0; i < m_Cmd.cur_param + 1; i++)
952  {
953  switch (m_Cmd.params[i])
954  {
955  case 0:
956  {
957  // Reset all attributes.
958  m_pWindows[m_ActiveBuffer]->setFlags(0);
959  m_pWindows[m_ActiveBuffer]->setForeColour(
960  g_DefaultFg);
961  m_pWindows[m_ActiveBuffer]->setBackColour(
962  g_DefaultBg);
963  break;
964  }
965  case 1:
966  {
967  // Bold
968  uint8_t flags =
969  m_pWindows[m_ActiveBuffer]->getFlags();
970  if (!(flags & XTERM_BOLD))
971  {
972  flags |= XTERM_BOLD;
973  m_pWindows[m_ActiveBuffer]->setFlags(flags);
974  }
975  break;
976  }
977  case 3:
978  {
979  // Italic
980  uint8_t flags =
981  m_pWindows[m_ActiveBuffer]->getFlags();
982  if (!(flags & XTERM_ITALIC))
983  {
984  flags |= XTERM_ITALIC;
985  m_pWindows[m_ActiveBuffer]->setFlags(flags);
986  }
987  break;
988  }
989  case 4:
990  {
991  // Underline
992  uint8_t flags =
993  m_pWindows[m_ActiveBuffer]->getFlags();
994  if (!(flags & XTERM_UNDERLINE))
995  {
996  flags |= XTERM_UNDERLINE;
997  m_pWindows[m_ActiveBuffer]->setFlags(flags);
998  }
999  break;
1000  }
1001  case 7:
1002  {
1003  // Inverse
1004  uint8_t flags =
1005  m_pWindows[m_ActiveBuffer]->getFlags();
1006  if (flags & XTERM_INVERSE)
1007  flags &= ~XTERM_INVERSE;
1008  else
1009  flags |= XTERM_INVERSE;
1010  m_pWindows[m_ActiveBuffer]->setFlags(flags);
1011  break;
1012  }
1013  case 22:
1014  {
1015  uint8_t flags =
1016  m_pWindows[m_ActiveBuffer]->getFlags();
1017  flags &= ~XTERM_ITALIC;
1018  m_pWindows[m_ActiveBuffer]->setFlags(flags);
1019  break;
1020  }
1021  case 23:
1022  {
1023  uint8_t flags =
1024  m_pWindows[m_ActiveBuffer]->getFlags();
1025  flags &= ~XTERM_ITALIC;
1026  m_pWindows[m_ActiveBuffer]->setFlags(flags);
1027  break;
1028  }
1029  case 24:
1030  {
1031  uint8_t flags =
1032  m_pWindows[m_ActiveBuffer]->getFlags();
1033  flags &= ~XTERM_UNDERLINE;
1034  m_pWindows[m_ActiveBuffer]->setFlags(flags);
1035  break;
1036  }
1037  case 27:
1038  {
1039  uint8_t flags =
1040  m_pWindows[m_ActiveBuffer]->getFlags();
1041  flags &= ~XTERM_INVERSE;
1042  m_pWindows[m_ActiveBuffer]->setFlags(flags);
1043  break;
1044  }
1045  case 30:
1046  case 31:
1047  case 32:
1048  case 33:
1049  case 34:
1050  case 35:
1051  case 36:
1052  case 37:
1053  // Foreground.
1054  m_pWindows[m_ActiveBuffer]->setForeColour(
1055  m_Cmd.params[i] - 30);
1056  break;
1057  case 38:
1058  // xterm-256 foreground
1059  if (m_Cmd.params[i + 1] == 5)
1060  {
1061  m_pWindows[m_ActiveBuffer]->setForeColour(
1062  m_Cmd.params[i + 2]);
1063  i += 3;
1064  }
1065  break;
1066  case 39:
1067  m_pWindows[m_ActiveBuffer]->setForeColour(
1068  g_DefaultFg);
1069  break;
1070  case 40:
1071  case 41:
1072  case 42:
1073  case 43:
1074  case 44:
1075  case 45:
1076  case 46:
1077  case 47:
1078  // Background.
1079  m_pWindows[m_ActiveBuffer]->setBackColour(
1080  m_Cmd.params[i] - 40);
1081  break;
1082  case 48:
1083  // xterm-256 background
1084  if (m_Cmd.params[i + 1] == 5)
1085  {
1086  m_pWindows[m_ActiveBuffer]->setBackColour(
1087  m_Cmd.params[i + 2]);
1088  i += 3;
1089  }
1090  break;
1091  case 49:
1092  m_pWindows[m_ActiveBuffer]->setForeColour(
1093  g_DefaultBg);
1094  break;
1095 
1096  default:
1097  // Do nothing.
1098  klog(
1099  LOG_INFO,
1100  "XTERM: unknown character attribute %d",
1101  m_Cmd.params[i]);
1102  break;
1103  }
1104  }
1105  m_Flags = 0;
1106  }
1107  break;
1108 
1109  case 'n':
1110  if ((m_Flags & RightAngle) == 0)
1111  {
1112  // Device Status Reports
1113  switch (m_Cmd.params[0])
1114  {
1115  case 5:
1116  {
1117  // Ready, no malfunction status.
1118  const char *status = "\e[0n";
1119  while (*status)
1120  {
1121  m_pT->addToQueue(*status);
1122  ++status;
1123  }
1124  }
1125  break;
1126 
1127  case 6:
1128  {
1129  size_t reportX =
1130  m_pWindows[m_ActiveBuffer]->getCursorX() + 1;
1131  size_t reportY =
1132  m_pWindows[m_ActiveBuffer]->getCursorY() + 1;
1133 
1134  if (m_Modes & Origin)
1135  {
1136  reportX = m_pWindows[m_ActiveBuffer]
1137  ->getCursorXRelOrigin() +
1138  1;
1139  reportY = m_pWindows[m_ActiveBuffer]
1140  ->getCursorYRelOrigin() +
1141  1;
1142  }
1143 
1144  // Report cursor position.
1145  char buf[128];
1146  sprintf(buf, "\e[%zd;%zdR", reportY, reportX);
1147  const char *p = (const char *) buf;
1148  while (*p)
1149  {
1150  m_pT->addToQueue(*p);
1151  ++p;
1152  }
1153  }
1154  break;
1155 
1156  default:
1157  klog(
1158  LOG_INFO,
1159  "XTERM: unknown device status request %d",
1160  m_Cmd.params[0]);
1161  break;
1162  }
1163  }
1164  m_Flags = 0;
1165  break;
1166 
1167  case 'p':
1168  // Could be soft terminal reset, request ANSI mode, set resource
1169  // level, etc...
1170  m_Flags = 0;
1171  break;
1172 
1173  case 'q':
1174  if (m_Flags & Space)
1175  {
1176  // Set cursor style.
1177  }
1178 
1179  m_Flags = 0;
1180  break;
1181 
1182  case 'r':
1183  if ((m_Flags & Question) == 0)
1184  {
1185  if (m_Cmd.has_param)
1186  {
1187  m_pWindows[m_ActiveBuffer]->setScrollRegion(
1188  m_Cmd.params[0] - 1, m_Cmd.params[1] - 1);
1189  }
1190  else
1191  {
1192  m_pWindows[m_ActiveBuffer]->setScrollRegion(-1, -1);
1193  }
1194 
1195  if (m_Modes & Origin)
1196  {
1197  m_pWindows[m_ActiveBuffer]->cursorToOrigin();
1198  }
1199  else
1200  {
1201  m_pWindows[m_ActiveBuffer]->setCursor(0, 0, rect);
1202  }
1203  }
1204  m_Flags = 0;
1205  break;
1206 
1207  case 's':
1208  if (!m_Cmd.has_param)
1209  {
1210  if ((m_Modes & Margin) == 0)
1211  {
1212  m_SavedX = m_pWindows[m_ActiveBuffer]->getCursorX();
1213  m_SavedY = m_pWindows[m_ActiveBuffer]->getCursorY();
1214  }
1215  }
1216  else if (m_Modes & Margin)
1217  {
1218  // Set left/right margins.
1219  m_pWindows[m_ActiveBuffer]->setMargins(
1220  (m_Cmd.params[0]) ? m_Cmd.params[0] - 1 : ~0,
1221  (m_Cmd.params[1]) ? m_Cmd.params[1] - 1 : ~0);
1222  }
1223  m_Flags = 0;
1224  break;
1225 
1226  case 'u':
1227  if ((m_Flags & Space) == 0)
1228  {
1229  m_pWindows[m_ActiveBuffer]->setCursor(
1230  m_SavedX, m_SavedY, rect);
1231  }
1232  m_Flags = 0;
1233  break;
1234 
1235  case 'x':
1236  if ((m_Flags & Asterisk) == 0)
1237  {
1238  if (m_Cmd.params[0] <= 1)
1239  {
1240  const char *termparams = 0;
1241  if (m_Cmd.params[0] == 1)
1242  termparams = "\e[3;1;1;120;120;1;0x";
1243  else
1244  termparams = "\e[2;1;1;120;120;1;0x";
1245 
1246  while (*termparams)
1247  {
1248  m_pT->addToQueue(*termparams);
1249  ++termparams;
1250  }
1251  }
1252  }
1253  m_Flags = 0;
1254  break;
1255 
1256  default:
1257  break;
1258  }
1259  }
1260  else if (
1261  (m_Flags & (Escape | LeftSquare | RightSquare | Underscore)) == Escape)
1262  {
1263  bool seenFlag = setFlagsForUtf32(utf32);
1264 
1265  switch (utf32)
1266  {
1267  case 0x08:
1268  // Backspace within escape sequence.
1269  m_pWindows[m_ActiveBuffer]->backspace(rect);
1270  break;
1271 
1272  case '0':
1273  if (m_Flags & LeftRound) // \e(0
1274  {
1275  // Set DEC Special Character and Line Drawing Set
1276  m_pWindows[m_ActiveBuffer]->setLineRenderMode(true);
1277  }
1278  m_Flags = 0;
1279  break;
1280 
1281  case '3':
1282  klog(LOG_ALERT, "XTERM: double-height lines not supported");
1283  m_Flags = 0;
1284  break;
1285 
1286  case '4':
1287  klog(LOG_ALERT, "XTERM: double-height lines not supported");
1288  m_Flags = 0;
1289  break;
1290 
1291  case '5':
1292  // Single-width line set.
1293  m_Flags = 0;
1294  break;
1295 
1296  case '6':
1297  klog(LOG_ALERT, "XTERM: double-width lines not supported");
1298  m_Flags = 0;
1299  break;
1300 
1301  case '7':
1302  m_SavedX = m_pWindows[m_ActiveBuffer]->getCursorX();
1303  m_SavedY = m_pWindows[m_ActiveBuffer]->getCursorY();
1304  m_Flags = 0;
1305  break;
1306 
1307  case '8':
1308  if (m_Flags & Hash)
1309  {
1310  // DECALN: DEC Screen Alignment Test (fill screen with 'E')
1311 
1312  // DECALN resets margins
1313  if (m_Modes & Column)
1314  {
1315  m_pWindows[m_ActiveBuffer]->setMargins(0, XTERM_WIDE);
1316  }
1317  else
1318  {
1319  m_pWindows[m_ActiveBuffer]->setMargins(
1320  0, XTERM_STANDARD);
1321  }
1322  m_pWindows[m_ActiveBuffer]->setScrollRegion(-1, -1);
1323 
1324  // Fill the space with 'E'.
1325  m_pWindows[m_ActiveBuffer]->fillChar('E', rect);
1326 
1327  // Cursor to home.
1328  m_pWindows[m_ActiveBuffer]->setCursor(0, 0, rect);
1329  }
1330  else
1331  {
1332  m_pWindows[m_ActiveBuffer]->setCursor(
1333  m_SavedX, m_SavedY, rect);
1334  }
1335  m_Flags = 0;
1336  break;
1337 
1338  case '<':
1339  m_Modes |= AnsiVt52;
1340  m_Flags = 0;
1341  break;
1342 
1343  case '>':
1345  m_Flags = 0;
1346  break;
1347 
1348  case 'A':
1349  m_pWindows[m_ActiveBuffer]->cursorUpWithinMargin(1, rect);
1350  m_Flags = 0;
1351  break;
1352 
1353  case 'B':
1354  if (m_Flags & LeftRound) // \e(B
1355  {
1356  // Set USASCII character set.
1357  m_pWindows[m_ActiveBuffer]->setLineRenderMode(false);
1358  }
1359  else
1360  {
1361  m_pWindows[m_ActiveBuffer]->cursorDownWithinMargin(1, rect);
1362  }
1363  m_Flags = 0;
1364  break;
1365 
1366  case 'C':
1367  m_pWindows[m_ActiveBuffer]->cursorRightWithinMargin(1, rect);
1368  m_Flags = 0;
1369  break;
1370 
1371  case 'D':
1372  if (m_Modes & AnsiVt52)
1373  {
1374  m_pWindows[m_ActiveBuffer]->cursorDown(1, rect);
1375  }
1376  else
1377  {
1378  m_pWindows[m_ActiveBuffer]->cursorLeftWithinMargin(1, rect);
1379  }
1380  m_Flags = 0;
1381  break;
1382 
1383  case 'E':
1384  m_pWindows[m_ActiveBuffer]->cursorDownAndLeftToMargin(rect);
1385  m_Flags = 0;
1386  break;
1387 
1388  case 'F':
1390  // \eF is enabled by the hpLowerleftBugCompat resource
1391 
1393  m_Flags = 0;
1394  break;
1395 
1396  case 'G':
1398  m_Flags = 0;
1399  break;
1400 
1401  case 'H':
1402  if (m_Modes & AnsiVt52)
1403  {
1404  // Set tab stop.
1405  m_pWindows[m_ActiveBuffer]->setTabStop();
1406  }
1407  else
1408  {
1409  // Cursor to home.
1410  m_pWindows[m_ActiveBuffer]->setCursor(0, 0, rect);
1411  }
1412  m_Flags = 0;
1413  break;
1414 
1415  case 'I':
1416  if (!(m_Modes & AnsiVt52))
1417  {
1418  // Reverse line feed.
1419  m_pWindows[m_ActiveBuffer]->cursorUp(1, rect);
1420  }
1421  m_Flags = 0;
1422  break;
1423 
1424  case 'J':
1425  if (!(m_Modes & AnsiVt52))
1426  {
1427  m_pWindows[m_ActiveBuffer]->eraseDown(rect);
1428  }
1429  m_Flags = 0;
1430  break;
1431 
1432  case 'K':
1433  if (!(m_Modes & AnsiVt52))
1434  {
1435  m_pWindows[m_ActiveBuffer]->eraseEOL(rect);
1436  }
1437  m_Flags = 0;
1438  break;
1439 
1440  case 'M':
1441  if ((m_Modes & AnsiVt52) && !(m_Flags & Space))
1442  {
1443  // Reverse Index.
1444  m_pWindows[m_ActiveBuffer]->cursorUp(1, rect);
1445  }
1446  m_Flags = 0;
1447  break;
1448 
1449  case 'N':
1450  case 'O':
1451  // Single shift select for G2/3 character sets.
1452  m_Flags = 0;
1453  break;
1454 
1455  case 'Y':
1456  if (!(m_Modes & AnsiVt52))
1457  {
1458  // Set cursor position.
1459  m_Flags |= Vt52SetCursorWaitY;
1460  }
1461  else
1462  {
1463  m_Flags = 0;
1464  }
1465  break;
1466 
1467  case 'Z':
1468  // Terminal identification.
1469  {
1470  const char *answerback = 0;
1471  if (m_Modes & AnsiVt52)
1472  answerback = "\e[1;2c";
1473  else
1474  answerback = "\e/Z";
1475 
1476  while (*answerback)
1477  {
1478  m_pT->addToQueue(*answerback);
1479  ++answerback;
1480  }
1481  }
1482  m_Flags = 0;
1483  break;
1484 
1485  default:
1486  if (!seenFlag)
1487  {
1488  klog(LOG_INFO, "XTERM: unknown ESCAPE control '%c'", utf32);
1489  m_Flags = 0;
1490  }
1491  break;
1492  }
1493  }
1494  else if (
1495  (m_Flags & (Escape | LeftSquare | RightSquare | Underscore)) ==
1496  (Escape | RightSquare))
1497  {
1498  switch (utf32)
1499  {
1500  case '\007':
1501  case 0x9C:
1502  if (!m_OsCtl.has_param)
1503  {
1504  klog(
1505  LOG_INFO,
1506  "XTERM: not enough parameters for OS control");
1507  }
1508  else
1509  {
1510  if (m_OsCtl.params[0] == "0" || m_OsCtl.params[0] == "1" ||
1511  m_OsCtl.params[0] == "2")
1512  {
1513  if (m_pWidget)
1514  {
1515  m_pWidget->setTitle(m_OsCtl.params[1]);
1516  }
1517  }
1518  else
1519  {
1520  klog(
1521  LOG_INFO, "XTERM: unhandled OS control '%s'",
1522  m_OsCtl.params[0].c_str());
1523  }
1524  }
1525  m_Flags = 0;
1526  break;
1527 
1528  case ';':
1529  case ':':
1530  m_OsCtl.cur_param++;
1531  break;
1532 
1533  default:
1534  if (utf32 >= ' ')
1535  {
1536  m_OsCtl.params[m_OsCtl.cur_param] +=
1537  static_cast<char>(utf32);
1538  }
1539  break;
1540  }
1541  }
1542  else if (
1543  (m_Flags & (Escape | LeftSquare | RightSquare | Underscore)) ==
1544  (Escape | Underscore))
1545  {
1546  // No application program commands in xterm.
1547  if (utf32 == 0x9C)
1548  m_Flags = 0;
1549  }
1550 
1551  m_pT->addToQueue(0, true);
1552 }
1553 
1555 {
1556  m_pWindows[m_ActiveBuffer]->renderAll(rect, m_pWindows[m_ActiveBuffer]);
1557 }
1558 
1559 Xterm::Window::Window(
1560  size_t nRows, size_t nCols, PedigreeGraphics::Framebuffer *pFb,
1561  size_t nMaxScrollback, size_t offsetLeft, size_t offsetTop, size_t fbWidth,
1562  Xterm *parent)
1563  : m_pBuffer(0), m_BufferLength(0), m_pFramebuffer(pFb), m_FbWidth(fbWidth),
1564  m_Width(nCols), m_Height(nRows), m_Stride(XTERM_MIN_WIDTH),
1565  m_OffsetLeft(offsetLeft), m_OffsetTop(offsetTop),
1566  m_nMaxScrollback(nMaxScrollback), m_CursorX(0), m_CursorY(0),
1567  m_ScrollStart(0), m_ScrollEnd(nRows - 1), m_pInsert(0), m_pView(0),
1568  m_Fg(g_DefaultFg), m_Bg(g_DefaultBg), m_Flags(0), m_bCursorFilled(true),
1569  m_bLineRender(false), m_pParentXterm(parent)
1570 {
1571 #ifdef XTERM_DEBUG
1572  klog(LOG_INFO, "Xterm::Window::Window() dimensions %zdx%zd", nCols, nRows);
1573 #endif
1574 
1575  if (m_Width > m_Stride)
1576  m_Stride = m_Width;
1577 
1578  // Using malloc() instead of new[] so we can use realloc()
1579  m_pBuffer = reinterpret_cast<TermChar *>(
1580  malloc(m_Stride * m_Height * sizeof(TermChar)));
1581 
1582  m_BufferLength = m_Stride * m_Height;
1583 
1584  TermChar blank;
1585  blank.fore = m_Fg;
1586  blank.back = m_Bg;
1587  blank.utf32 = ' ';
1588  blank.flags = 0;
1589  for (size_t i = 0; i < m_Stride * m_Height; i++)
1590  m_pBuffer[i] = blank;
1591 
1592  if (m_pParentXterm->m_pCairo)
1593  {
1594  cairo_save(m_pParentXterm->m_pCairo);
1595  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
1596 
1597  cairo_set_source_rgba(
1598  m_pParentXterm->m_pCairo, ((g_Colours[m_Bg] >> 16) & 0xFF) / 256.0,
1599  ((g_Colours[m_Bg] >> 8) & 0xFF) / 256.0,
1600  ((g_Colours[m_Bg]) & 0xFF) / 256.0, 0.8);
1601 
1602  cairo_rectangle(
1603  m_pParentXterm->m_pCairo, m_OffsetLeft, m_OffsetTop,
1604  nCols * m_pParentXterm->m_pNormalFont->getWidth(),
1605  nRows * m_pParentXterm->m_pNormalFont->getHeight());
1606  cairo_fill(m_pParentXterm->m_pCairo);
1607 
1608  cairo_restore(m_pParentXterm->m_pCairo);
1609  }
1610 
1611  m_pInsert = m_pView = m_pBuffer;
1612 
1613  m_LeftMargin = 0;
1614  m_RightMargin = m_Width;
1615 }
1616 
1617 Xterm::Window::~Window()
1618 {
1619  free(m_pBuffer);
1620 }
1621 
1622 void Xterm::Window::showCursor(DirtyRectangle &rect)
1623 {
1624 #ifdef XTERM_DEBUG
1625  klog(LOG_INFO, "Xterm::Window::showCursor");
1626 #endif
1627 
1628  render(rect, m_bCursorFilled ? XTERM_INVERSE : XTERM_BORDER);
1629 }
1630 
1631 void Xterm::Window::hideCursor(DirtyRectangle &rect)
1632 {
1633 #ifdef XTERM_DEBUG
1634  klog(LOG_INFO, "Xterm::Window::hideCursor");
1635 #endif
1636 
1637  render(rect);
1638 }
1639 
1640 void Xterm::Window::resize(size_t nWidth, size_t nHeight, bool bActive)
1641 {
1642 #ifdef XTERM_DEBUG
1643  klog(LOG_INFO, "Xterm::Window::resize(%zd, %zd)", nWidth, nHeight);
1644 #endif
1645 
1646  m_pFramebuffer = 0;
1647 
1648  size_t cols = nWidth / m_pParentXterm->m_pNormalFont->getWidth();
1649  size_t rows = nHeight / m_pParentXterm->m_pNormalFont->getHeight();
1650 
1651 #ifdef XTERM_DEBUG
1652  klog(LOG_INFO, " -> cols %zd, %zd", nWidth, nHeight);
1653 #endif
1654 
1655  if (bActive && m_Bg && m_pParentXterm->m_pCairo)
1656  {
1657  size_t bg = m_Bg;
1658  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
1659  {
1660  bg = m_Fg;
1661  }
1662 
1663  cairo_save(m_pParentXterm->m_pCairo);
1664  cairo_set_source_rgba(
1665  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
1666  ((g_Colours[bg] >> 8) & 0xFF) / 256.0,
1667  ((g_Colours[bg]) & 0xFF) / 256.0, 1.0);
1668 
1669  cairo_rectangle(
1670  m_pParentXterm->m_pCairo, m_OffsetLeft, m_OffsetTop, nWidth,
1671  nHeight);
1672  cairo_fill(m_pParentXterm->m_pCairo);
1673 
1674  cairo_restore(m_pParentXterm->m_pCairo);
1675  }
1676 
1677  size_t previousStride = m_Stride;
1678 
1679  if (m_Stride < XTERM_MIN_WIDTH)
1680  m_Stride = XTERM_MIN_WIDTH;
1681 
1682  if (cols > m_Stride)
1683  m_Stride = cols;
1684 
1685  TermChar *newBuf = reinterpret_cast<TermChar *>(
1686  malloc(m_Stride * rows * sizeof(TermChar)));
1687 
1688  TermChar blank;
1689  blank.fore = m_Fg;
1690  blank.back = m_Bg;
1691  blank.utf32 = ' ';
1692  blank.flags = 0;
1693  for (size_t i = 0; i < m_Stride * rows; i++)
1694  newBuf[i] = blank;
1695 
1696  for (size_t r = 0; r < m_Height; r++)
1697  {
1698  if (r >= rows)
1699  break;
1700  for (size_t c = 0; c < m_Stride; c++)
1701  {
1702  if (c >= cols)
1703  break;
1704 
1705  newBuf[r * m_Stride + c] = m_pInsert[r * previousStride + c];
1706  }
1707  }
1708 
1709  free(m_pBuffer);
1710 
1711  m_pInsert = m_pView = m_pBuffer = newBuf;
1712  m_BufferLength = rows * m_Stride;
1713 
1714  if (m_RightMargin > (ssize_t) cols)
1715  m_RightMargin = cols;
1716  if (m_LeftMargin > (ssize_t) cols)
1717  m_LeftMargin = cols;
1718 
1719  m_FbWidth = cols * m_pParentXterm->m_pNormalFont->getWidth();
1720  m_Width = cols;
1721  m_Height = rows;
1722  m_ScrollStart = 0;
1723  m_ScrollEnd = rows - 1;
1724  if (m_CursorX >= m_RightMargin)
1725  m_CursorX = m_RightMargin - 1;
1726  if (m_CursorY > m_ScrollEnd)
1727  m_CursorY = m_ScrollEnd;
1728 
1729  // Set default tab stops.
1730  delete[] m_pParentXterm->m_TabStops;
1731  m_pParentXterm->m_TabStops = new char[m_Stride];
1732  memset(m_pParentXterm->m_TabStops, 0, m_Stride);
1733  for (size_t i = 0; i < m_Stride; i += 8)
1734  {
1735  m_pParentXterm->m_TabStops[i] = '|';
1736  }
1737 }
1738 
1739 void Xterm::Window::setScrollRegion(int start, int end)
1740 {
1741  if (start == -1)
1742  start = 0;
1743  if (end == -1)
1744  end = m_Height - 1;
1745 
1746 #ifdef XTERM_DEBUG
1747  klog(LOG_INFO, "Xterm::Window::setScrollRegion(%d, %d)", start, end);
1748 #endif
1749 
1750  m_ScrollStart = start;
1751  m_ScrollEnd = end;
1752 
1753  if (m_ScrollStart > m_ScrollEnd)
1754  {
1755  size_t tmp = m_ScrollStart;
1756  m_ScrollStart = m_ScrollEnd;
1757  m_ScrollEnd = tmp;
1758  }
1759 
1760  if (m_ScrollStart >= (ssize_t) m_Height)
1761  m_ScrollStart = m_Height - 1;
1762  if (m_ScrollEnd >= (ssize_t) m_Height)
1763  m_ScrollEnd = m_Height - 1;
1764 }
1765 
1766 void Xterm::Window::setForeColour(uint8_t fgColour)
1767 {
1768  m_Fg = fgColour;
1769 }
1770 
1771 void Xterm::Window::setBackColour(uint8_t bgColour)
1772 {
1773  m_Bg = bgColour;
1774 }
1775 
1776 void Xterm::Window::setFlags(uint8_t flags)
1777 {
1778  m_Flags = flags;
1779 }
1780 
1781 uint8_t Xterm::Window::getFlags()
1782 {
1783  return m_Flags;
1784 }
1785 
1786 void Xterm::Window::setMargins(size_t left, size_t right)
1787 {
1788 #ifdef XTERM_DEBUG
1789  klog(LOG_INFO, "Xterm::Window::setMargins(%zd, %zd)", left, right);
1790 #endif
1791 
1792  if (left > m_Stride)
1793  left = m_Stride;
1794  if (right > m_Stride)
1795  right = m_Stride;
1796 
1797  m_LeftMargin = left;
1798  m_RightMargin = right;
1799 }
1800 
1801 void Xterm::Window::renderAll(DirtyRectangle &rect, Xterm::Window *pPrevious)
1802 {
1803  TermChar *pOld = (pPrevious) ? pPrevious->m_pInsert : 0;
1804  TermChar *pNew = m_pInsert;
1805 
1806  // "Cleverer" full redraw - only redraw those glyphs that are different from
1807  // the previous window.
1808  for (size_t y = 0; y < m_Height; y++)
1809  {
1810  for (size_t x = 0; x < m_Width; --x)
1811  {
1812  if ((!pOld) || (pOld[y * m_Stride + x] != pNew[y * m_Stride + x]) ||
1813  (m_pParentXterm->getModes() !=
1814  pPrevious->m_pParentXterm->getModes()))
1815  {
1816  render(rect, 0, x, y);
1817  }
1818  }
1819  }
1820 }
1821 
1822 void Xterm::Window::renderArea(
1823  DirtyRectangle &rect, size_t x, size_t y, size_t w, size_t h)
1824 {
1825  if (x == ~0UL)
1826  x = 0;
1827  if (y == ~0UL)
1828  y = 0;
1829  if (w == ~0UL)
1830  w = m_Width;
1831  if (h == ~0UL)
1832  h = m_Height;
1833 
1834  for (size_t cy = y; cy < (y + h); ++cy)
1835  {
1836  for (size_t cx = x; cx < (x + w); ++cx)
1837  {
1838  render(rect, 0, cx, cy);
1839  }
1840  }
1841 }
1842 
1843 void Xterm::Window::setChar(uint32_t utf32, size_t x, size_t y)
1844 {
1845  if (x > m_Stride)
1846  return;
1847  if (y > m_Height)
1848  return;
1849 
1850  TermChar *c = &m_pInsert[y * m_Stride + x];
1851 
1852  c->fore = m_Fg;
1853  c->back = m_Bg;
1854 
1855  c->flags = m_Flags;
1856  c->utf32 = utf32;
1857 }
1858 
1859 Xterm::Window::TermChar Xterm::Window::getChar(size_t x, size_t y)
1860 {
1861  if (x == ~0UL)
1862  x = m_CursorX;
1863  if (y == ~0UL)
1864  y = m_CursorY;
1865  return m_pInsert[y * m_Stride + x];
1866 }
1867 
1868 void Xterm::Window::cursorDown(size_t n, DirtyRectangle &rect)
1869 {
1870 #ifdef XTERM_DEBUG
1871  klog(LOG_INFO, "Xterm::Window::cursorDown(%zd)", n);
1872 #endif
1873 
1874  m_CursorY += n;
1875  checkScroll(rect);
1876 }
1877 
1878 void Xterm::Window::cursorUp(size_t n, DirtyRectangle &rect)
1879 {
1880 #ifdef XTERM_DEBUG
1881  klog(LOG_INFO, "Xterm::Window::cursorUp(%zd)", n);
1882 #endif
1883 
1884  m_CursorY -= n;
1885  checkScroll(rect);
1886 }
1887 
1888 void Xterm::Window::cursorUpWithinMargin(size_t n, DirtyRectangle &rect)
1889 {
1890 #ifdef XTERM_DEBUG
1891  klog(LOG_INFO, "Xterm::Window::cursorUpWithinMargin(%zd)", n);
1892 #endif
1893 
1894  m_CursorY -= n;
1895  if (m_CursorY < m_ScrollStart)
1896  m_CursorY = m_ScrollStart;
1897 }
1898 
1899 void Xterm::Window::cursorLeftWithinMargin(size_t n, DirtyRectangle &)
1900 {
1901 #ifdef XTERM_DEBUG
1902  klog(LOG_INFO, "Xterm::Window::cursorLeftWithinMargin(%zd)", n);
1903 #endif
1904 
1905  m_CursorX -= n;
1906 
1907  if (m_CursorX < m_LeftMargin)
1908  m_CursorX = m_LeftMargin;
1909 }
1910 
1911 void Xterm::Window::cursorRightWithinMargin(size_t n, DirtyRectangle &)
1912 {
1913 #ifdef XTERM_DEBUG
1914  klog(LOG_INFO, "Xterm::Window::cursorRightWithinMargin(%zd)", n);
1915 #endif
1916 
1917  m_CursorX += n;
1918 
1919  if (m_CursorX >= m_RightMargin)
1920  m_CursorX = m_RightMargin - 1;
1921 }
1922 
1923 void Xterm::Window::cursorDownWithinMargin(size_t n, DirtyRectangle &)
1924 {
1925 #ifdef XTERM_DEBUG
1926  klog(LOG_INFO, "Xterm::Window::cursorDownWithinMargin(%zd)", n);
1927 #endif
1928 
1929  m_CursorY += n;
1930 
1931  if (m_CursorY > m_ScrollEnd)
1932  m_CursorY = m_ScrollEnd;
1933 }
1934 
1935 void Xterm::Window::backspace(DirtyRectangle &rect)
1936 {
1937 #ifdef XTERM_DEBUG
1938  klog(LOG_INFO, "Xterm::Window::backspace()");
1939 #endif
1940 
1941  if (m_CursorX == m_RightMargin)
1942  --m_CursorX;
1943 
1944  if (m_CursorX > m_LeftMargin)
1945  --m_CursorX;
1946 }
1947 
1948 void Xterm::Window::render(
1949  DirtyRectangle &rect, size_t flags, size_t x, size_t y)
1950 {
1951  if (x == ~0UL)
1952  x = m_CursorX;
1953  if (y == ~0UL)
1954  y = m_CursorY;
1955  if (x > m_Width || y > m_Height)
1956  {
1957  return;
1958  }
1959 
1960  TermChar c = m_pView[y * m_Stride + x];
1961 
1962  c.flags |= flags;
1963 
1964  uint32_t fg = g_Colours[c.fore];
1965  if (c.flags & XTERM_BRIGHTFG)
1966  fg = g_BrightColours[c.fore];
1967  uint32_t bg = g_Colours[c.back];
1968  if (c.flags & XTERM_BRIGHTBG)
1969  bg = g_BrightColours[c.back];
1970 
1971  if (c.flags & XTERM_INVERSE)
1972  {
1973  uint32_t tmp = fg;
1974  fg = bg;
1975  bg = tmp;
1976  }
1977 
1978  if (m_pParentXterm->getModes() & Screen)
1979  {
1980  // DECSCNM only applies to cells without custom color.
1981  if (c.fore == g_DefaultFg && c.back == g_DefaultBg)
1982  {
1983  uint32_t tmp = fg;
1984  fg = bg;
1985  bg = tmp;
1986  }
1987  }
1988 
1989  uint32_t utf32 = c.utf32;
1990 
1991  // Ensure the painted area is marked dirty.
1992  rect.point(
1993  (x * m_pParentXterm->m_pNormalFont->getWidth()) + m_OffsetLeft,
1994  (y * m_pParentXterm->m_pNormalFont->getHeight()) + m_OffsetTop);
1995  rect.point(
1996  ((x + 1) * m_pParentXterm->m_pNormalFont->getWidth()) + m_OffsetLeft,
1997  ((y + 1) * m_pParentXterm->m_pNormalFont->getHeight()) + m_OffsetTop);
1998 
1999  Font *pFont = m_pParentXterm->m_pNormalFont;
2000  bool bBold = (c.flags & XTERM_BOLD) == XTERM_BOLD;
2001  bool bItalic = (c.flags & XTERM_ITALIC) == XTERM_ITALIC;
2002  bool bUnderline = (c.flags & XTERM_UNDERLINE) == XTERM_UNDERLINE;
2003 
2004  pFont->render(
2005  m_pFramebuffer, utf32, (x * pFont->getWidth()) + m_OffsetLeft,
2006  (y * pFont->getHeight()) + m_OffsetTop, fg, bg, true, bBold, bItalic,
2007  bUnderline);
2008 
2009  if (c.flags & XTERM_BORDER)
2010  {
2011  // Border around the cell.
2012  cairo_save(m_pParentXterm->m_pCairo);
2013  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2014 
2015  cairo_set_line_width(m_pParentXterm->m_pCairo, 1.0);
2016  cairo_set_line_join(m_pParentXterm->m_pCairo, CAIRO_LINE_JOIN_MITER);
2017  cairo_set_antialias(m_pParentXterm->m_pCairo, CAIRO_ANTIALIAS_NONE);
2018 
2019  cairo_set_source_rgba(
2020  m_pParentXterm->m_pCairo, ((fg >> 16) & 0xFF) / 256.0,
2021  ((fg >> 8) & 0xFF) / 256.0, (fg & 0xFF) / 256.0, 0.8);
2022 
2023  cairo_rectangle(
2024  m_pParentXterm->m_pCairo,
2025  (x * pFont->getWidth()) + m_OffsetLeft + 1,
2026  (y * pFont->getHeight()) + m_OffsetTop + 1, pFont->getWidth() - 2,
2027  pFont->getHeight() - 2);
2028  cairo_stroke(m_pParentXterm->m_pCairo);
2029 
2030  cairo_restore(m_pParentXterm->m_pCairo);
2031  }
2032 }
2033 
2034 void Xterm::Window::scrollRegionUp(size_t numRows, DirtyRectangle &rect)
2035 {
2036 #ifdef XTERM_DEBUG
2037  klog(LOG_INFO, "Xterm::Window::scrollRegionUp(%zd)", numRows);
2038 #endif
2039 
2040  size_t targetY = m_ScrollStart;
2041  size_t sourceY = m_ScrollStart + numRows;
2042 
2043  size_t movedRows = (m_ScrollEnd + 1) - sourceY;
2044  size_t blankFrom = (m_ScrollEnd + 1) - numRows;
2045  size_t blankLength = (m_ScrollEnd + 1) - blankFrom;
2046 
2047  // Because we need to bitblit to copy, we need to flush anything that hasn't
2048  // been written just yet.
2049  rect.point(
2050  m_OffsetLeft,
2051  m_OffsetTop + (targetY + m_pParentXterm->m_pNormalFont->getHeight()));
2052  rect.point(
2053  m_OffsetLeft + m_FbWidth,
2054  m_OffsetTop + ((blankFrom + blankLength) *
2055  m_pParentXterm->m_pNormalFont->getHeight()));
2056  m_pParentXterm->m_pTui->redraw(rect);
2057  rect.reset();
2058 
2059  cairo_save(m_pParentXterm->m_pCairo);
2060 
2061  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2062 
2063  // Copying the scroll region.
2064  cairo_rectangle(
2065  m_pParentXterm->m_pCairo, m_OffsetLeft,
2066  m_OffsetTop + (targetY * m_pParentXterm->m_pNormalFont->getHeight()),
2067  m_FbWidth - m_OffsetLeft,
2068  movedRows * m_pParentXterm->m_pNormalFont->getHeight());
2069 
2070  cairo_push_group(m_pParentXterm->m_pCairo);
2071  cairo_set_source_surface(
2072  m_pParentXterm->m_pCairo, m_pParentXterm->m_pCairoSurface, 0,
2073  -((double) (numRows * m_pParentXterm->m_pNormalFont->getHeight())));
2074  cairo_fill_preserve(m_pParentXterm->m_pCairo);
2075  cairo_pop_group_to_source(m_pParentXterm->m_pCairo);
2076 
2077  // cairo_fill(m_pParentXterm->m_pCairo);
2078 
2079  size_t bg = m_Bg;
2080  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2081  {
2082  bg = m_Fg;
2083  }
2084 
2085  cairo_set_source_rgba(
2086  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2087  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2088  0.8);
2089 
2090  // Blanking the region that used to exist.
2091  cairo_rectangle(
2092  m_pParentXterm->m_pCairo, m_OffsetLeft,
2093  m_OffsetTop + (blankFrom * m_pParentXterm->m_pNormalFont->getHeight()),
2094  m_FbWidth - m_OffsetLeft,
2095  blankLength * m_pParentXterm->m_pNormalFont->getHeight());
2096  // cairo_fill(m_pParentXterm->m_pCairo);
2097 
2098  cairo_restore(m_pParentXterm->m_pCairo);
2099 
2100  rect.point(
2101  m_OffsetLeft,
2102  m_OffsetTop + (targetY + m_pParentXterm->m_pNormalFont->getHeight()));
2103  rect.point(
2104  m_FbWidth, m_OffsetTop + ((blankFrom + blankLength) *
2105  m_pParentXterm->m_pNormalFont->getHeight()));
2106 
2107  memmove(
2108  &m_pInsert[targetY * m_Stride], &m_pInsert[sourceY * m_Stride],
2109  movedRows * m_Stride * sizeof(TermChar));
2110 
2111  TermChar blank;
2112  blank.fore = m_Fg;
2113  blank.back = m_Bg;
2114  blank.utf32 = ' ';
2115  blank.flags = 0;
2116  for (size_t i = blankFrom * m_Stride;
2117  i < (blankFrom + blankLength) * m_Stride; i++)
2118  m_pInsert[i] = blank;
2119 
2120  renderArea(rect, 0, targetY, m_Width, movedRows + blankLength);
2121 }
2122 
2123 void Xterm::Window::scrollRegionDown(size_t numRows, DirtyRectangle &rect)
2124 {
2125 #ifdef XTERM_DEBUG
2126  klog(LOG_INFO, "Xterm::Window::scrollRegionDown(%zd)", numRows);
2127 #endif
2128 
2129  size_t targetY = m_ScrollStart + numRows;
2130  size_t sourceY = m_ScrollStart;
2131 
2132  size_t movedRows = (m_ScrollEnd + 1) - targetY;
2133  size_t blankFrom = sourceY;
2134  size_t blankLength = numRows;
2135 
2136  // Because we need to bitblit to copy, we need to flush anything that hasn't
2137  // been written just yet.
2138  rect.point(
2139  m_OffsetLeft,
2140  m_OffsetTop + (sourceY + m_pParentXterm->m_pNormalFont->getHeight()));
2141  rect.point(
2142  m_OffsetLeft + m_FbWidth,
2143  m_OffsetTop + (movedRows * m_pParentXterm->m_pNormalFont->getHeight()));
2144  m_pParentXterm->m_pTui->redraw(rect);
2145  rect.reset();
2146 
2147  cairo_save(m_pParentXterm->m_pCairo);
2148 
2149  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2150 
2151  // Copying the scroll region.
2152  cairo_rectangle(
2153  m_pParentXterm->m_pCairo, m_OffsetLeft,
2154  m_OffsetTop + (targetY * m_pParentXterm->m_pNormalFont->getHeight()),
2155  m_FbWidth - m_OffsetLeft,
2156  movedRows * m_pParentXterm->m_pNormalFont->getHeight());
2157 
2158  cairo_push_group(m_pParentXterm->m_pCairo);
2159  cairo_set_source_surface(
2160  m_pParentXterm->m_pCairo, m_pParentXterm->m_pCairoSurface, 0,
2161  (double) (numRows * m_pParentXterm->m_pNormalFont->getHeight()));
2162  cairo_fill_preserve(m_pParentXterm->m_pCairo);
2163  cairo_pop_group_to_source(m_pParentXterm->m_pCairo);
2164 
2165  // cairo_fill(m_pParentXterm->m_pCairo);
2166 
2167  size_t bg = m_Bg;
2168  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2169  {
2170  bg = m_Fg;
2171  }
2172 
2173  cairo_set_source_rgba(
2174  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2175  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2176  0.8);
2177 
2178  // Blanking the region that used to exist.
2179  cairo_rectangle(
2180  m_pParentXterm->m_pCairo, m_OffsetLeft,
2181  m_OffsetTop + (blankFrom * m_pParentXterm->m_pNormalFont->getHeight()),
2182  m_FbWidth - m_OffsetLeft,
2183  blankLength * m_pParentXterm->m_pNormalFont->getHeight());
2184  // cairo_fill(m_pParentXterm->m_pCairo);
2185 
2186  cairo_restore(m_pParentXterm->m_pCairo);
2187 
2188  rect.point(
2189  m_OffsetLeft,
2190  m_OffsetTop + (targetY + m_pParentXterm->m_pNormalFont->getHeight()));
2191  rect.point(
2192  m_FbWidth, m_OffsetTop + ((blankFrom + blankLength) *
2193  m_pParentXterm->m_pNormalFont->getHeight()));
2194 
2195  memmove(
2196  &m_pInsert[targetY * m_Stride], &m_pInsert[sourceY * m_Stride],
2197  movedRows * m_Stride * sizeof(TermChar));
2198 
2199  TermChar blank;
2200  blank.fore = m_Fg;
2201  blank.back = m_Bg;
2202  blank.utf32 = ' ';
2203  blank.flags = 0;
2204  for (size_t i = blankFrom * m_Stride;
2205  i < (blankFrom + blankLength) * m_Stride; i++)
2206  m_pInsert[i] = blank;
2207 
2208  renderArea(rect, 0, blankFrom, m_Width, movedRows + blankLength);
2209 }
2210 
2211 void Xterm::Window::setCursorRelOrigin(size_t x, size_t y, DirtyRectangle &rect)
2212 {
2213 #ifdef XTERM_DEBUG
2214  klog(LOG_INFO, "Xterm::Window::setCursorRelOrigin(%zd, %zd)", x, y);
2215 #endif
2216 
2217  x += m_LeftMargin;
2218  y += m_ScrollStart;
2219  setCursor(x, y, rect);
2220 }
2221 
2222 void Xterm::Window::setCursor(size_t x, size_t y, DirtyRectangle &rect)
2223 {
2224 #ifdef XTERM_DEBUG
2225  klog(LOG_INFO, "Xterm::Window::setCursor(%zd, %zd)", x, y);
2226 #endif
2227 
2228  m_CursorX = x;
2229  m_CursorY = y;
2230 }
2231 
2232 void Xterm::Window::setCursorX(size_t x, DirtyRectangle &rect)
2233 {
2234 #ifdef XTERM_DEBUG
2235  klog(LOG_INFO, "Xterm::Window::setCursorX(%zd)", x);
2236 #endif
2237 
2238  setCursor(x, m_CursorY, rect);
2239 }
2240 void Xterm::Window::setCursorY(size_t y, DirtyRectangle &rect)
2241 {
2242 #ifdef XTERM_DEBUG
2243  klog(LOG_INFO, "Xterm::Window::setCursorY(%zd)", y);
2244 #endif
2245 
2246  setCursor(m_CursorX, y, rect);
2247 }
2248 
2249 ssize_t Xterm::Window::getCursorX() const
2250 {
2251  return m_CursorX;
2252 }
2253 ssize_t Xterm::Window::getCursorY() const
2254 {
2255  return m_CursorY;
2256 }
2257 
2259 {
2260  if ((m_CursorX > m_LeftMargin) && (m_CursorX <= m_RightMargin))
2261  return m_CursorX - m_LeftMargin;
2262 
2263  return m_CursorX;
2264 }
2265 
2266 ssize_t Xterm::Window::getCursorYRelOrigin() const
2267 {
2268  if ((m_CursorY > m_ScrollStart) && (m_CursorY <= m_ScrollEnd))
2269  return m_CursorY - m_ScrollStart;
2270 
2271  return m_CursorY;
2272 }
2273 
2274 void Xterm::Window::cursorToOrigin()
2275 {
2276 #ifdef XTERM_DEBUG
2277  klog(LOG_INFO, "Xterm::Window::cursorToOrigin()");
2278 #endif
2279 
2280  m_CursorX = m_LeftMargin;
2281  m_CursorY = m_ScrollStart;
2282 }
2283 
2284 void Xterm::Window::cursorLeft(DirtyRectangle &rect)
2285 {
2286 #ifdef XTERM_DEBUG
2287  klog(LOG_INFO, "Xterm::Window::cursorLeft()");
2288 #endif
2289 
2290  if (m_CursorX > m_LeftMargin)
2291  --m_CursorX;
2292 }
2293 
2294 void Xterm::Window::cursorLeftNum(size_t n, DirtyRectangle &rect)
2295 {
2296 #ifdef XTERM_DEBUG
2297  klog(LOG_INFO, "Xterm::Window::cursorLeftNum(%zd)", n);
2298 #endif
2299 
2300  m_CursorX -= n;
2301 
2302  if (m_CursorX < m_LeftMargin)
2303  m_CursorX = m_LeftMargin;
2304 }
2305 
2306 void Xterm::Window::cursorDownAndLeftToMargin(DirtyRectangle &rect)
2307 {
2308 #ifdef XTERM_DEBUG
2309  klog(LOG_INFO, "Xterm::Window::cursorDownAndLeftToMargin()");
2310 #endif
2311 
2312  cursorDown(1, rect);
2313  m_CursorX = m_LeftMargin;
2314 }
2315 
2316 void Xterm::Window::cursorLeftToMargin(DirtyRectangle &rect)
2317 {
2318 #ifdef XTERM_DEBUG
2319  klog(LOG_INFO, "Xterm::Window::cursorLeftToMargin()");
2320 #endif
2321 
2322  m_CursorX = m_LeftMargin;
2323 }
2324 
2325 void Xterm::Window::cursorTab(DirtyRectangle &rect)
2326 {
2327 #ifdef XTERM_DEBUG
2328  klog(LOG_INFO, "Xterm::Window::cursorTab()");
2329 #endif
2330 
2331  bool tabStopFound = false;
2332  for (ssize_t x = (m_CursorX + 1); x < m_RightMargin; ++x)
2333  {
2334  if (m_pParentXterm->m_TabStops[x] != 0)
2335  {
2336  m_CursorX = x;
2337  tabStopFound = true;
2338  break;
2339  }
2340  }
2341 
2342  if (!tabStopFound)
2343  {
2344  m_CursorX = m_RightMargin - 1;
2345  }
2346  else if (m_CursorX >= m_RightMargin)
2347  m_CursorX = m_RightMargin - 1;
2348 }
2349 
2350 void Xterm::Window::cursorTabBack(DirtyRectangle &rect)
2351 {
2352 #ifdef XTERM_DEBUG
2353  klog(LOG_INFO, "Xterm::Window::cursorTabBack()");
2354 #endif
2355 
2356  bool tabStopFound = false;
2357  for (ssize_t x = (m_CursorX - 1); x >= 0; --x)
2358  {
2359  if (m_pParentXterm->m_TabStops[x] != 0)
2360  {
2361  m_CursorX = x;
2362  tabStopFound = true;
2363  break;
2364  }
2365  }
2366 
2367  if (!tabStopFound)
2368  {
2369  m_CursorX = m_LeftMargin;
2370  }
2371  else if (m_CursorX < m_LeftMargin)
2372  m_CursorX = m_LeftMargin;
2373 }
2374 
2375 void Xterm::Window::fillChar(uint32_t utf32, DirtyRectangle &rect)
2376 {
2377 #ifdef XTERM_DEBUG
2378  klog(LOG_INFO, "Xterm::Window::fillChar(%c)", (char) utf32);
2379 #endif
2380 
2381  for (ssize_t y = m_ScrollStart; y < m_ScrollEnd; ++y)
2382  {
2383  for (ssize_t x = m_LeftMargin; x < m_RightMargin; ++x)
2384  {
2385  setChar(utf32, x, y);
2386  }
2387  }
2388 
2389  renderArea(
2390  rect, m_LeftMargin, m_ScrollStart, m_RightMargin - m_LeftMargin,
2391  m_ScrollEnd - m_ScrollStart);
2392 }
2393 
2394 void Xterm::Window::addChar(uint32_t utf32, DirtyRectangle &rect)
2395 {
2396 #ifdef XTERM_DEBUG_EXTRA
2397  klog(
2398  LOG_INFO, "Xterm::Window::addChar(%c) [@ %zd, %zd]", (char) utf32,
2399  m_CursorX, m_CursorY);
2400 #endif
2401 
2402  if (utf32 >= ' ')
2403  {
2404  checkWrap(rect);
2405 
2406  if (m_CursorX >= (ssize_t) m_Stride)
2407  return;
2408 
2409  if (m_pParentXterm->getModes() & Insert)
2410  {
2411  // We need some space, first.
2412  insertCharacters(1, rect);
2413  }
2414 
2415  TermChar tc = getChar();
2416  setChar(utf32, m_CursorX, m_CursorY);
2417  if (getChar() != tc)
2418  render(rect);
2419 
2420  ++m_CursorX;
2421  }
2422 }
2423 
2424 void Xterm::Window::scrollUp(size_t n, DirtyRectangle &rect)
2425 {
2426 #ifdef XTERM_DEBUG
2427  klog(LOG_INFO, "Xterm::Window::scrollUp(%zd)", n);
2428 #endif
2429 
2430  scrollRegionDown(n, rect);
2431 }
2432 
2433 void Xterm::Window::scrollDown(size_t n, DirtyRectangle &rect)
2434 {
2435 #ifdef XTERM_DEBUG
2436  klog(LOG_INFO, "Xterm::Window::scrollDown(%zd)", n);
2437 #endif
2438 
2439  scrollRegionUp(n, rect);
2440 }
2441 
2443 {
2444 #ifdef XTERM_DEBUG
2445  klog(LOG_INFO, "Xterm::Window::eraseScreen()");
2446 #endif
2447 
2448  cairo_save(m_pParentXterm->m_pCairo);
2449  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2450 
2451  size_t bg = m_Bg;
2452  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2453  {
2454  bg = m_Fg;
2455  }
2456 
2457  cairo_set_source_rgba(
2458  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2459  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2460  0.8);
2461 
2462  cairo_rectangle(
2463  m_pParentXterm->m_pCairo, 0, 0, m_FbWidth,
2464  m_Height * m_pParentXterm->m_pNormalFont->getHeight());
2465  // cairo_fill(m_pParentXterm->m_pCairo);
2466 
2467  cairo_restore(m_pParentXterm->m_pCairo);
2468 
2469  // One good fillRect should do the job nicely.
2470  // m_pFramebuffer->rect(0, 0, m_FbWidth, m_Height *
2471  // m_pParentXterm->m_pNormalFont->getHeight(), g_Colours[m_Bg],
2472  // PedigreeGraphics::Bits24_Rgb);
2473 
2474  rect.point(m_OffsetLeft, m_OffsetTop);
2475  rect.point(
2476  m_OffsetLeft + m_FbWidth,
2477  m_OffsetTop + (m_Height * m_pParentXterm->m_pNormalFont->getHeight()));
2478 
2479  for (size_t row = 0; row < m_Height; row++)
2480  {
2481  for (size_t col = 0; col < m_Width; col++)
2482  {
2483  setChar(' ', col, row);
2484  }
2485  }
2486 
2487  renderArea(rect);
2488 }
2489 
2491 {
2492 #ifdef XTERM_DEBUG
2493  klog(LOG_INFO, "Xterm::Window::eraseEOL()");
2494 #endif
2495 
2496  size_t l = (m_CursorX * m_pParentXterm->m_pNormalFont->getWidth());
2497 
2498  cairo_save(m_pParentXterm->m_pCairo);
2499  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2500 
2501  size_t bg = m_Bg;
2502  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2503  {
2504  bg = m_Fg;
2505  }
2506 
2507  cairo_set_source_rgba(
2508  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2509  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2510  0.8);
2511 
2512  cairo_rectangle(
2513  m_pParentXterm->m_pCairo, m_OffsetLeft + l,
2514  m_OffsetTop + (m_CursorY * m_pParentXterm->m_pNormalFont->getHeight()),
2515  m_FbWidth - l, m_pParentXterm->m_pNormalFont->getHeight());
2516  // cairo_fill(m_pParentXterm->m_pCairo);
2517 
2518  cairo_restore(m_pParentXterm->m_pCairo);
2519 
2520  // Again, one fillRect should do it.
2521  // m_pFramebuffer->rect(l, m_CursorY *
2522  // m_pParentXterm->m_pNormalFont->getHeight(), m_FbWidth - l,
2523  // m_pParentXterm->m_pNormalFont->getHeight(), g_Colours[m_Bg],
2524  // PedigreeGraphics::Bits24_Rgb);
2525 
2526  rect.point(
2527  m_OffsetLeft,
2528  m_OffsetTop + (m_CursorY * m_pParentXterm->m_pNormalFont->getHeight()));
2529  rect.point(
2530  m_OffsetLeft + m_FbWidth,
2531  m_OffsetTop +
2532  ((m_CursorY + 1) * m_pParentXterm->m_pNormalFont->getHeight()));
2533 
2534  size_t row = m_CursorY;
2535  for (size_t col = m_CursorX; col < m_Width; col++)
2536  {
2537  setChar(' ', col, row);
2538  }
2539 
2540  renderArea(rect, m_CursorX, m_CursorY, m_Width, 1);
2541 }
2542 
2544 {
2545 #ifdef XTERM_DEBUG
2546  klog(LOG_INFO, "Xterm::Window::eraseSOL()");
2547 #endif
2548 
2549  cairo_save(m_pParentXterm->m_pCairo);
2550  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2551 
2552  size_t bg = m_Bg;
2553  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2554  {
2555  bg = m_Fg;
2556  }
2557 
2558  cairo_set_source_rgba(
2559  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2560  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2561  0.8);
2562 
2563  cairo_rectangle(
2564  m_pParentXterm->m_pCairo, m_OffsetLeft,
2565  m_OffsetTop + (m_CursorY * m_pParentXterm->m_pNormalFont->getHeight()),
2566  (m_CursorX + 1) * m_pParentXterm->m_pNormalFont->getWidth(),
2567  m_pParentXterm->m_pNormalFont->getHeight());
2568  // cairo_fill(m_pParentXterm->m_pCairo);
2569 
2570  cairo_restore(m_pParentXterm->m_pCairo);
2571 
2572  rect.point(
2573  m_OffsetLeft,
2574  m_OffsetTop + (m_CursorY * m_pParentXterm->m_pNormalFont->getHeight()));
2575  rect.point(
2576  m_OffsetLeft +
2577  ((m_CursorX + 1) * m_pParentXterm->m_pNormalFont->getWidth()),
2578  m_OffsetTop +
2579  ((m_CursorY + 1) * m_pParentXterm->m_pNormalFont->getHeight()));
2580 
2581  size_t row = m_CursorY;
2582  for (ssize_t col = 0; col <= m_CursorX; col++)
2583  {
2584  setChar(' ', col, row);
2585  }
2586 
2587  renderArea(rect, 0, m_CursorY, m_CursorX, 1);
2588 }
2589 
2591 {
2592 #ifdef XTERM_DEBUG
2593  klog(LOG_INFO, "Xterm::Window::eraseLine()");
2594 #endif
2595 
2596  cairo_save(m_pParentXterm->m_pCairo);
2597  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2598 
2599  size_t bg = m_Bg;
2600  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2601  {
2602  bg = m_Fg;
2603  }
2604 
2605  cairo_set_source_rgba(
2606  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2607  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2608  0.8);
2609 
2610  cairo_rectangle(
2611  m_pParentXterm->m_pCairo, m_OffsetLeft,
2612  m_OffsetTop + (m_CursorY * m_pParentXterm->m_pNormalFont->getHeight()),
2613  m_FbWidth - m_OffsetLeft, m_pParentXterm->m_pNormalFont->getHeight());
2614  // cairo_fill(m_pParentXterm->m_pCairo);
2615 
2616  cairo_restore(m_pParentXterm->m_pCairo);
2617 
2618  rect.point(
2619  m_OffsetLeft,
2620  m_OffsetTop + (m_CursorY * m_pParentXterm->m_pNormalFont->getHeight()));
2621  rect.point(
2622  m_OffsetLeft + m_FbWidth,
2623  m_OffsetTop +
2624  ((m_CursorY + 1) * m_pParentXterm->m_pNormalFont->getHeight()));
2625 
2626  size_t row = m_CursorY;
2627  for (size_t col = 0; col < m_Width; col++)
2628  {
2629  setChar(' ', col, row);
2630  }
2631 
2632  renderArea(rect, 0, m_CursorY, m_Width, 1);
2633 }
2634 
2636 {
2637 #ifdef XTERM_DEBUG
2638  klog(LOG_INFO, "Xterm::Window::eraseChars(%zd)", n);
2639 #endif
2640 
2641  // Again, one fillRect should do it.
2642  size_t left = (m_CursorX * m_pParentXterm->m_pNormalFont->getWidth());
2643  if ((m_CursorX + (ssize_t) n) > m_RightMargin)
2644  n = m_RightMargin - m_CursorX;
2645  size_t width = n * m_pParentXterm->m_pNormalFont->getWidth();
2646 
2647  cairo_save(m_pParentXterm->m_pCairo);
2648  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2649 
2650  size_t bg = m_Bg;
2651  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2652  {
2653  bg = m_Fg;
2654  }
2655 
2656  cairo_set_source_rgba(
2657  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2658  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2659  0.8);
2660 
2661  cairo_rectangle(
2662  m_pParentXterm->m_pCairo, left,
2663  m_CursorY * m_pParentXterm->m_pNormalFont->getHeight(), width,
2664  m_pParentXterm->m_pNormalFont->getHeight());
2665  // cairo_fill(m_pParentXterm->m_pCairo);
2666 
2667  cairo_restore(m_pParentXterm->m_pCairo);
2668 
2669  // m_pFramebuffer->rect(left, m_CursorY *
2670  // m_pParentXterm->m_pNormalFont->getHeight(), width,
2671  // m_pParentXterm->m_pNormalFont->getHeight(), g_Colours[m_Bg],
2672  // PedigreeGraphics::Bits24_Rgb);
2673 
2674  rect.point(
2675  m_OffsetLeft,
2676  m_OffsetTop + (m_CursorY * m_pParentXterm->m_pNormalFont->getHeight()));
2677  rect.point(
2678  m_OffsetLeft + width,
2679  m_OffsetTop +
2680  ((m_CursorY + 1) * m_pParentXterm->m_pNormalFont->getHeight()));
2681 
2682  size_t row = m_CursorY;
2683  for (size_t col = m_CursorX; col < (m_CursorX + n); col++)
2684  {
2685  setChar(' ', col, row);
2686  }
2687 
2688  renderArea(rect, m_CursorX, m_CursorY, n, 1);
2689 }
2690 
2692 {
2693 #ifdef XTERM_DEBUG
2694  klog(LOG_INFO, "Xterm::Window::eraseUp()");
2695 #endif
2696 
2697  // Erase to the start of the line first. Essentially, we're erasing from the
2698  // current position to the top of the screen.
2699  eraseSOL(rect);
2700 
2701  cairo_save(m_pParentXterm->m_pCairo);
2702  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2703 
2704  size_t bg = m_Bg;
2705  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2706  {
2707  bg = m_Fg;
2708  }
2709 
2710  cairo_set_source_rgba(
2711  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2712  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2713  0.8);
2714 
2715  cairo_rectangle(
2716  m_pParentXterm->m_pCairo, m_OffsetLeft, m_OffsetTop, m_FbWidth,
2717  m_pParentXterm->m_pNormalFont->getHeight() * m_CursorY);
2718  // cairo_fill(m_pParentXterm->m_pCairo);
2719 
2720  cairo_restore(m_pParentXterm->m_pCairo);
2721 
2722  // m_pFramebuffer->rect(0, 0, m_FbWidth,
2723  // m_pParentXterm->m_pNormalFont->getHeight() * m_CursorY, g_Colours[m_Bg],
2724  // PedigreeGraphics::Bits24_Rgb);
2725 
2726  rect.point(m_OffsetLeft, m_OffsetTop);
2727  rect.point(
2728  m_OffsetLeft + m_FbWidth,
2729  m_OffsetTop +
2730  ((m_CursorY + 1) * m_pParentXterm->m_pNormalFont->getHeight()));
2731 
2732  for (ssize_t row = 0; row < m_CursorY; row++)
2733  {
2734  for (size_t col = 0; col < m_Width; col++)
2735  {
2736  setChar(' ', col, row);
2737  }
2738  }
2739 
2740  renderArea(rect, 0, 0, m_Width, m_CursorY);
2741 }
2742 
2744 {
2745 #ifdef XTERM_DEBUG
2746  klog(LOG_INFO, "Xterm::Window::eraseDown()");
2747 #endif
2748 
2749  // Erase to the end of the line first. Essentially, we're erasing from the
2750  // current position to the end of the screen.
2751  eraseEOL(rect);
2752 
2753  size_t eraseStart = m_CursorY + 1;
2754 
2755  size_t top = eraseStart * m_pParentXterm->m_pNormalFont->getHeight();
2756 
2757  cairo_save(m_pParentXterm->m_pCairo);
2758  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2759 
2760  size_t bg = m_Bg;
2761  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2762  {
2763  bg = m_Fg;
2764  }
2765 
2766  cairo_set_source_rgba(
2767  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2768  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2769  0.8);
2770 
2771  cairo_rectangle(
2772  m_pParentXterm->m_pCairo, m_OffsetLeft, m_OffsetTop + top, m_FbWidth,
2773  m_pParentXterm->m_pNormalFont->getHeight() * (m_Height - eraseStart));
2774  // cairo_fill(m_pParentXterm->m_pCairo);
2775 
2776  cairo_restore(m_pParentXterm->m_pCairo);
2777 
2778  // m_pFramebuffer->rect(0, top, m_FbWidth,
2779  // m_pParentXterm->m_pNormalFont->getHeight() * (m_Height - eraseStart),
2780  // g_Colours[m_Bg], PedigreeGraphics::Bits24_Rgb);
2781 
2782  rect.point(m_OffsetLeft, top + m_OffsetTop);
2783  rect.point(
2784  m_OffsetLeft + m_FbWidth,
2785  top + m_OffsetTop +
2786  ((m_Height - eraseStart) *
2787  m_pParentXterm->m_pNormalFont->getHeight()));
2788 
2789  for (size_t row = eraseStart; row < m_Height; row++)
2790  {
2791  for (size_t col = 0; col < m_Width; col++)
2792  {
2793  setChar(' ', col, row);
2794  }
2795  }
2796 
2797  renderArea(rect, 0, eraseStart, m_Width, m_Height - eraseStart);
2798 }
2799 
2801 {
2802 #ifdef XTERM_DEBUG
2803  klog(LOG_INFO, "Xterm::Window::deleteCharacters(%zd)", n);
2804 #endif
2805 
2806  // Start of the delete region
2807  ssize_t deleteStart = m_CursorX;
2808 
2809  // End of the delete region
2810  ssize_t deleteEnd = deleteStart + n;
2811  if (deleteEnd > m_RightMargin)
2812  deleteEnd = m_RightMargin;
2813 
2814  // Number of characters to shift
2815  ssize_t numChars = m_RightMargin - deleteEnd;
2816 
2817  // Shift all the characters across from the end of the delete area to the
2818  // start.
2819  memmove(
2820  &m_pInsert[(m_CursorY * m_Stride) + deleteStart],
2821  &m_pInsert[(m_CursorY * m_Stride) + deleteEnd],
2822  numChars * sizeof(TermChar));
2823 
2824  // Now that the characters have been shifted, clear the space after
2825  // the region we copied.
2826  size_t left =
2827  (m_RightMargin - n) * m_pParentXterm->m_pNormalFont->getWidth();
2828  size_t top = m_CursorY * m_pParentXterm->m_pNormalFont->getHeight();
2829 
2830  cairo_save(m_pParentXterm->m_pCairo);
2831  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2832 
2833  size_t bg = m_Bg;
2834  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2835  {
2836  bg = m_Fg;
2837  }
2838 
2839  cairo_set_source_rgba(
2840  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2841  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2842  0.8);
2843 
2844  cairo_rectangle(
2845  m_pParentXterm->m_pCairo, m_OffsetLeft + left, m_OffsetTop + top,
2846  n * m_pParentXterm->m_pNormalFont->getWidth(),
2847  m_pParentXterm->m_pNormalFont->getHeight());
2848  // cairo_fill(m_pParentXterm->m_pCairo);
2849 
2850  cairo_restore(m_pParentXterm->m_pCairo);
2851 
2852  // Update the moved section
2853  ssize_t row = m_CursorY, col = 0;
2854  for (col = (m_RightMargin - n); col < m_RightMargin; col++)
2855  {
2856  setChar(' ', col, row);
2857  }
2858  for (col = deleteStart; col < m_RightMargin; col++)
2859  {
2860  render(rect, 0, col, row);
2861  }
2862 
2863  renderArea(rect, deleteStart, m_CursorY, m_RightMargin - deleteStart, 1);
2864 }
2865 
2867 {
2868 #ifdef XTERM_DEBUG
2869  klog(LOG_INFO, "Xterm::Window::insertCharacters(%zd)", n);
2870 #endif
2871 
2872  // Start of the insertion region
2873  ssize_t insertStart = m_CursorX;
2874 
2875  // End of the insertion region
2876  ssize_t insertEnd = insertStart + n;
2877 
2878  // Number of characters to shift.
2879  ssize_t numChars = m_RightMargin - insertEnd;
2880 
2881  // Shift characters.
2882  memmove(
2883  &m_pInsert[(m_CursorY * m_Stride) + insertEnd],
2884  &m_pInsert[(m_CursorY * m_Stride) + insertStart],
2885  numChars * sizeof(TermChar));
2886 
2887  // Now that the characters have been shifted, clear the space inside the
2888  // region we inserted
2889  size_t left = insertStart * m_pParentXterm->m_pNormalFont->getWidth();
2890  size_t top = m_CursorY * m_pParentXterm->m_pNormalFont->getHeight();
2891 
2892  cairo_save(m_pParentXterm->m_pCairo);
2893  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2894 
2895  size_t bg = m_Bg;
2896  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg))
2897  {
2898  bg = m_Fg;
2899  }
2900 
2901  cairo_set_source_rgba(
2902  m_pParentXterm->m_pCairo, ((g_Colours[bg] >> 16) & 0xFF) / 256.0,
2903  ((g_Colours[bg] >> 8) & 0xFF) / 256.0, ((g_Colours[bg]) & 0xFF) / 256.0,
2904  0.8);
2905 
2906  cairo_rectangle(
2907  m_pParentXterm->m_pCairo, m_OffsetLeft + left, m_OffsetTop + top,
2908  n * m_pParentXterm->m_pNormalFont->getWidth(),
2909  m_pParentXterm->m_pNormalFont->getHeight());
2910  // cairo_fill(m_pParentXterm->m_pCairo);
2911 
2912  cairo_restore(m_pParentXterm->m_pCairo);
2913 
2914  rect.point(m_OffsetLeft + left, m_OffsetTop + top);
2915  rect.point(
2916  m_OffsetLeft + left + (n * m_pParentXterm->m_pNormalFont->getWidth()),
2917  m_OffsetTop + top + m_pParentXterm->m_pNormalFont->getHeight());
2918 
2919  // Update the inserted section
2920  ssize_t row = m_CursorY, col = 0;
2921  for (col = insertStart; col < insertEnd; col++)
2922  {
2923  setChar(' ', col, row);
2924  }
2925 
2926  // Update the moved section
2927  for (col = insertStart; col < m_RightMargin; col++)
2928  {
2929  render(rect, 0, col, row);
2930  }
2931 
2932  renderArea(rect, insertStart, m_CursorY, m_RightMargin - insertStart, 1);
2933 }
2934 
2936 {
2937 #ifdef XTERM_DEBUG
2938  klog(LOG_INFO, "Xterm::Window::insertLines(%zd)", n);
2939 #endif
2940 
2941  if (m_CursorY + (ssize_t) n >= m_ScrollEnd)
2942  n = m_ScrollEnd - m_CursorY;
2943 
2944  // This will perform the correct scroll down, but then we need to erase
2945  // the current line to actually give the illusion of insertion.
2946  eraseLine(rect);
2947  scrollRegionDown(n, rect);
2948 }
2949 
2951 {
2952 #ifdef XTERM_DEBUG
2953  klog(LOG_INFO, "Xterm::Window::deleteLines(%zd)", n);
2954 #endif
2955 
2956  if (m_CursorY + (ssize_t) n >= m_ScrollEnd)
2957  n = m_ScrollEnd - m_CursorY;
2958 
2959  // This will scroll the rest of the lines into this one.
2960  scrollRegionUp(n, rect);
2961 }
2962 
2963 void Xterm::Window::lineRender(uint32_t utf32, DirtyRectangle &rect)
2964 {
2965  klog(LOG_NOTICE, "line render: %c", utf32);
2966 
2967  size_t left = m_OffsetLeft +
2968  (m_LeftMargin * m_pParentXterm->m_pNormalFont->getWidth()) +
2969  (m_CursorX * m_pParentXterm->m_pNormalFont->getWidth());
2970  size_t top = m_OffsetTop +
2971  (m_ScrollStart * m_pParentXterm->m_pNormalFont->getHeight()) +
2972  (m_CursorY * m_pParentXterm->m_pNormalFont->getHeight());
2973 
2974  checkWrap(rect);
2975  if (m_CursorX < m_RightMargin)
2976  ++m_CursorX;
2977 
2978  size_t fullWidth = m_pParentXterm->m_pNormalFont->getWidth();
2979  size_t fullHeight = m_pParentXterm->m_pNormalFont->getHeight();
2980 
2981  size_t halfWidth = (fullWidth / 2) + 1;
2982  size_t halfHeight = (fullHeight / 2) + 1;
2983 
2984  size_t bg = m_Bg;
2985  size_t fg = m_Fg;
2986  if ((m_pParentXterm->getModes() & Screen) && (bg == g_DefaultBg) &&
2987  (fg == g_DefaultFg))
2988  {
2989  bg = m_Fg;
2990  fg = m_Bg;
2991  }
2992 
2993  uint32_t bgColourInt = g_Colours[bg];
2994  uint32_t fgColourInt = g_Colours[fg];
2995 
2996  cairo_save(m_pParentXterm->m_pCairo);
2997  cairo_set_operator(m_pParentXterm->m_pCairo, CAIRO_OPERATOR_SOURCE);
2998 
2999  cairo_set_source_rgba(
3000  m_pParentXterm->m_pCairo, ((bgColourInt >> 16) & 0xFF) / 256.0,
3001  ((bgColourInt >> 8) & 0xFF) / 256.0, (bgColourInt & 0xFF) / 256.0, 0.8);
3002 
3003  cairo_rectangle(m_pParentXterm->m_pCairo, left, top, fullWidth, fullHeight);
3004  cairo_fill(m_pParentXterm->m_pCairo);
3005 
3006  cairo_set_source_rgba(
3007  m_pParentXterm->m_pCairo, ((fgColourInt >> 16) & 0xFF) / 256.0,
3008  ((fgColourInt >> 8) & 0xFF) / 256.0, (fgColourInt & 0xFF) / 256.0, 1.0);
3009 
3010  // m_pFramebuffer->rect(left, top, fullWidth, fullHeight, bgColourInt,
3011  // PedigreeGraphics::Bits24_Rgb);
3012 
3013  rect.point(m_OffsetLeft + left, m_OffsetTop + top);
3014  rect.point(m_OffsetLeft + left + fullWidth, m_OffsetTop + top + fullHeight);
3015 
3016  switch (utf32 & 0xFF)
3017  {
3018  case 'j':
3019  // Bottom right corner
3020 
3021  // Middle left to Center
3022  cairo_move_to(m_pParentXterm->m_pCairo, left, top + halfHeight);
3023  cairo_line_to(
3024  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3025  cairo_stroke(m_pParentXterm->m_pCairo);
3026 
3027  // m_pFramebuffer->line(left, top + halfHeight, left + halfWidth,
3028  // top + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3029 
3030  // Center to Middle top
3031  cairo_move_to(m_pParentXterm->m_pCairo, left + halfWidth, top);
3032  cairo_line_to(
3033  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3034  cairo_stroke(m_pParentXterm->m_pCairo);
3035 
3036  // m_pFramebuffer->line(left + halfWidth, top, left + halfWidth, top
3037  // + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3038  break;
3039  case 'k':
3040  // Upper right corner
3041 
3042  // Middle left to Center
3043  cairo_move_to(m_pParentXterm->m_pCairo, left, top + halfHeight);
3044  cairo_line_to(
3045  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3046  cairo_stroke(m_pParentXterm->m_pCairo);
3047 
3048  // m_pFramebuffer->line(left, top + halfHeight, left + halfWidth,
3049  // top + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3050 
3051  // Center to Middle bottom
3052  cairo_move_to(
3053  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3054  cairo_line_to(
3055  m_pParentXterm->m_pCairo, left + halfWidth, top + fullHeight);
3056  cairo_stroke(m_pParentXterm->m_pCairo);
3057 
3058  // m_pFramebuffer->line(left + halfWidth, top + halfHeight, left +
3059  // halfWidth, top + fullHeight, fgColourInt,
3060  // PedigreeGraphics::Bits24_Rgb);
3061  break;
3062  case 'l':
3063  // Upper left corner
3064 
3065  // Center to Middle right
3066  cairo_move_to(
3067  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3068  cairo_line_to(
3069  m_pParentXterm->m_pCairo, left + fullWidth, top + halfHeight);
3070  cairo_stroke(m_pParentXterm->m_pCairo);
3071 
3072  // m_pFramebuffer->line(left + halfWidth, top + halfHeight, left +
3073  // fullWidth, top + halfHeight, fgColourInt,
3074  // PedigreeGraphics::Bits24_Rgb);
3075 
3076  // Center to Middle bottom
3077  cairo_move_to(
3078  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3079  cairo_line_to(
3080  m_pParentXterm->m_pCairo, left + halfWidth, top + fullHeight);
3081  cairo_stroke(m_pParentXterm->m_pCairo);
3082 
3083  // m_pFramebuffer->line(left + halfWidth, top + halfHeight, left +
3084  // halfWidth, top + fullHeight, fgColourInt,
3085  // PedigreeGraphics::Bits24_Rgb);
3086  break;
3087  case 'm':
3088  // Lower left corner
3089 
3090  // Center to Middle top
3091  cairo_move_to(m_pParentXterm->m_pCairo, left + halfWidth, top);
3092  cairo_line_to(
3093  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3094  cairo_stroke(m_pParentXterm->m_pCairo);
3095 
3096  // m_pFramebuffer->line(left + halfWidth, top, left + halfWidth, top
3097  // + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3098 
3099  // Center to Middle right
3100  cairo_move_to(
3101  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3102  cairo_line_to(
3103  m_pParentXterm->m_pCairo, left + fullWidth, top + halfHeight);
3104  cairo_stroke(m_pParentXterm->m_pCairo);
3105 
3106  // m_pFramebuffer->line(left + halfWidth, top + halfHeight, left +
3107  // fullWidth, top + halfHeight, fgColourInt,
3108  // PedigreeGraphics::Bits24_Rgb);
3109  break;
3110  case 'n':
3111  // Crossing lines
3112 
3113  // Middle left to Middle right
3114  cairo_move_to(m_pParentXterm->m_pCairo, left, top + halfHeight);
3115  cairo_line_to(
3116  m_pParentXterm->m_pCairo, left + fullWidth, top + halfHeight);
3117  cairo_stroke(m_pParentXterm->m_pCairo);
3118 
3119  // m_pFramebuffer->line(left, top + halfHeight, left + fullWidth,
3120  // top + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3121 
3122  // Middle top to Middle bottom
3123  cairo_move_to(m_pParentXterm->m_pCairo, left + halfWidth, top);
3124  cairo_line_to(
3125  m_pParentXterm->m_pCairo, left + halfWidth, top + fullHeight);
3126  cairo_stroke(m_pParentXterm->m_pCairo);
3127 
3128  // m_pFramebuffer->line(left + halfWidth, top, left + halfWidth, top
3129  // + fullHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3130  break;
3131  case 'q':
3132  // Horizontal line
3133 
3134  // Middle left to Middle right
3135  cairo_move_to(m_pParentXterm->m_pCairo, left, top + halfHeight);
3136  cairo_line_to(
3137  m_pParentXterm->m_pCairo, left + fullWidth, top + halfHeight);
3138  cairo_stroke(m_pParentXterm->m_pCairo);
3139 
3140  // m_pFramebuffer->line(left, top + halfHeight, left + fullWidth,
3141  // top + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3142  break;
3143  case 't':
3144  // Left 'T'
3145  cairo_move_to(m_pParentXterm->m_pCairo, left + halfWidth, top);
3146  cairo_line_to(
3147  m_pParentXterm->m_pCairo, left + halfWidth, top + fullHeight);
3148  cairo_stroke(m_pParentXterm->m_pCairo);
3149 
3150  // m_pFramebuffer->line(left + halfWidth, top, left + halfWidth, top
3151  // + fullHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3152  cairo_move_to(
3153  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3154  cairo_line_to(
3155  m_pParentXterm->m_pCairo, left + fullWidth, top + halfHeight);
3156  cairo_stroke(m_pParentXterm->m_pCairo);
3157 
3158  // m_pFramebuffer->line(left + halfWidth, top + halfHeight, left +
3159  // fullWidth, top + halfHeight, fgColourInt,
3160  // PedigreeGraphics::Bits24_Rgb);
3161  break;
3162  case 'u':
3163  // Right 'T'
3164 
3165  // Middle top to Middle bottom
3166  cairo_move_to(m_pParentXterm->m_pCairo, left + halfWidth, top);
3167  cairo_line_to(
3168  m_pParentXterm->m_pCairo, left + halfWidth, top + fullHeight);
3169  cairo_stroke(m_pParentXterm->m_pCairo);
3170 
3171  // m_pFramebuffer->line(left + halfWidth, top, left + halfWidth, top
3172  // + fullHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3173 
3174  // Middle left to Center
3175  cairo_move_to(m_pParentXterm->m_pCairo, left, top + halfHeight);
3176  cairo_line_to(
3177  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3178  cairo_stroke(m_pParentXterm->m_pCairo);
3179 
3180  // m_pFramebuffer->line(left, top + halfHeight, left + halfWidth,
3181  // top + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3182  break;
3183  case 'v':
3184  // Bottom 'T'
3185 
3186  // Middle left to Middle right
3187  cairo_move_to(m_pParentXterm->m_pCairo, left, top + halfHeight);
3188  cairo_line_to(
3189  m_pParentXterm->m_pCairo, left + fullWidth, top + halfHeight);
3190  cairo_stroke(m_pParentXterm->m_pCairo);
3191 
3192  // m_pFramebuffer->line(left, top + halfHeight, left + fullWidth,
3193  // top + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3194 
3195  // Middle top to Center
3196  cairo_move_to(m_pParentXterm->m_pCairo, left + halfWidth, top);
3197  cairo_line_to(
3198  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3199  cairo_stroke(m_pParentXterm->m_pCairo);
3200 
3201  // m_pFramebuffer->line(left + halfWidth, top, left + halfWidth, top
3202  // + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3203  break;
3204  case 'w':
3205  // Top 'T'
3206 
3207  // Middle left to Middle right
3208  cairo_move_to(m_pParentXterm->m_pCairo, left, top + halfHeight);
3209  cairo_line_to(
3210  m_pParentXterm->m_pCairo, left + fullWidth, top + halfHeight);
3211  cairo_stroke(m_pParentXterm->m_pCairo);
3212 
3213  // m_pFramebuffer->line(left, top + halfHeight, left + fullWidth,
3214  // top + halfHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3215 
3216  // Middle bottom to Center
3217  cairo_move_to(
3218  m_pParentXterm->m_pCairo, left + halfWidth, top + halfHeight);
3219  cairo_line_to(
3220  m_pParentXterm->m_pCairo, left + halfWidth, top + fullHeight);
3221  cairo_stroke(m_pParentXterm->m_pCairo);
3222 
3223  // m_pFramebuffer->line(left + halfWidth, top + halfHeight, left +
3224  // halfWidth, top + fullHeight, fgColourInt,
3225  // PedigreeGraphics::Bits24_Rgb);
3226  break;
3227  case 'x':
3228  // Vertical line
3229 
3230  // Middle top to Middle bottom
3231  cairo_move_to(m_pParentXterm->m_pCairo, left + halfWidth, top);
3232  cairo_line_to(
3233  m_pParentXterm->m_pCairo, left + halfWidth, top + fullHeight);
3234  cairo_stroke(m_pParentXterm->m_pCairo);
3235 
3236  // m_pFramebuffer->line(left + halfWidth, top, left + halfWidth, top
3237  // + fullHeight, fgColourInt, PedigreeGraphics::Bits24_Rgb);
3238  break;
3239  default:
3240  break;
3241  }
3242 
3243  cairo_restore(m_pParentXterm->m_pCairo);
3244 }
3245 
3247 {
3248  if (m_CursorX >= m_RightMargin)
3249  {
3250  // Default autowrap mode is off - new characters at
3251  // the right margin replace any that are already there.
3252  if (m_pParentXterm->getModes() & AutoWrap)
3253  {
3254  m_CursorX = m_LeftMargin;
3255  ++m_CursorY;
3256 
3257  checkScroll(rect);
3258  }
3259  else
3260  {
3261  m_CursorX = m_RightMargin - 1;
3262  }
3263  }
3264 }
3265 
3267 {
3268  // Handle scrolling, which can take place due to linefeeds and
3269  // other such cursor movements.
3270  if (m_CursorY < m_ScrollStart)
3271  {
3272  // By how much have we exceeded the scroll region?
3273  size_t numRows = (m_ScrollStart - m_CursorY);
3274  scrollRegionDown(numRows, rect);
3275  m_CursorY = m_ScrollStart;
3276  }
3277  else if (m_CursorY > m_ScrollEnd)
3278  {
3279  // By how much have we exceeded the scroll region?
3280  size_t numRows = (m_CursorY - m_ScrollEnd);
3281  scrollRegionUp(numRows, rect);
3282  m_CursorY = m_ScrollEnd;
3283  }
3284 }
3285 
3286 void Xterm::Window::invert(DirtyRectangle &rect)
3287 {
3288 #ifdef XTERM_DEBUG
3289  klog(LOG_INFO, "Xterm::Window::invert()");
3290 #endif
3291 
3292  // Invert the entire screen, if using default colours.
3293  for (size_t y = 0; y < m_Height; y++)
3294  {
3295  for (size_t x = 0; x < m_Width; x++)
3296  {
3297  TermChar *pChar = &m_pView[(y * m_Stride) + x];
3298  if ((pChar->fore == g_DefaultFg) && (pChar->back == g_DefaultBg))
3299  {
3300  uint8_t fore = pChar->fore;
3301  pChar->fore = pChar->back;
3302  pChar->back = fore;
3303  }
3304  }
3305  }
3306 
3307  // Redraw.
3308  renderAll(rect, this);
3309 }
3310 
3312 {
3313 #ifdef XTERM_DEBUG
3314  klog(LOG_INFO, "Xterm::Window::setTabStop() [@%zd]", m_CursorX);
3315 #endif
3316 
3317  m_pParentXterm->m_TabStops[m_CursorX] = '|';
3318 }
3319 
3321 {
3322 #ifdef XTERM_DEBUG
3323  klog(LOG_INFO, "Xterm::Window::clearTabStop() [@%zd]", m_CursorX);
3324 #endif
3325 
3326  m_pParentXterm->m_TabStops[m_CursorX] = 0;
3327 }
3328 
3330 {
3331 #ifdef XTERM_DEBUG
3332  klog(LOG_INFO, "Xterm::Window::clearAllTabStops()");
3333 #endif
3334 
3335  memset(m_pParentXterm->m_TabStops, 0, m_Stride);
3336 }
Definition: tui.h:36
void processKey(uint64_t key)
Definition: Xterm.cc:152
void write(uint32_t utf32, DirtyRectangle &rect)
Definition: Xterm.cc:354
void eraseLine(DirtyRectangle &rect)
Definition: Xterm.cc:2590
ssize_t getCursorXRelOrigin() const
Definition: Xterm.cc:2258
void renderAll(DirtyRectangle &rect)
Definition: Xterm.cc:1554
Definition: Font.h:33
void insertLines(size_t n, DirtyRectangle &rect)
Definition: Xterm.cc:2935
void eraseScreen(DirtyRectangle &rect)
Definition: Xterm.cc:2442
Definition: winman.h:195
void eraseDown(DirtyRectangle &rect)
Definition: Xterm.cc:2743
void clearAllTabStops()
Definition: Xterm.cc:3329
void setTabStop()
Definition: Xterm.cc:3311
void insertCharacters(size_t n, DirtyRectangle &rect)
Definition: Xterm.cc:2866
void eraseChars(size_t n, DirtyRectangle &rect)
Definition: Xterm.cc:2635
Definition: Xterm.h:44
size_t getNum(size_t row, size_t col)
Returns the value in column &#39;col&#39; of the result, in number form.
Result * query(const char *sql)
void eraseUp(DirtyRectangle &rect)
Definition: Xterm.cc:2691
void eraseEOL(DirtyRectangle &rect)
Definition: Xterm.cc:2490
bool succeeded()
Returns true if the result is valid, false if there was an error.
Definition: Widget.h:63
void deleteLines(size_t n, DirtyRectangle &rect)
Definition: Xterm.cc:2950
void eraseSOL(DirtyRectangle &rect)
Definition: Xterm.cc:2543
void checkScroll(DirtyRectangle &rect)
Definition: Xterm.cc:3266
void deleteCharacters(size_t n, DirtyRectangle &rect)
Definition: Xterm.cc:2800
std::string errorMessage(size_t buffSz=256)
Returns the error message.
void checkWrap(DirtyRectangle &rect)
Definition: Xterm.cc:3246
void clearTabStop()
Definition: Xterm.cc:3320