Fixed the terminal not fully functional error
[utio.git] / ti.cc
blob84a37c7ff9584d8d7e83cbea9aed7361178c0bdb
1 // This file is part of the utio library, a terminal I/O library.
2 //
3 // Copyright (C) 2004 by Mike Sharov <msharov@users.sourceforge.net>
4 // This file is free software, distributed under the MIT License.
5 //
6 // ti.cc
7 //
9 #include "ti.h"
10 #include <unistd.h>
11 #include <sys/ioctl.h>
13 //----------------------------------------------------------------------
15 #ifndef DOXYGEN_SHOULD_SKIP_THIS
17 #define TERMINFO_MAGIC 0432
19 /// The header of the terminfo file
20 struct STerminfoHeader {
21 uint16_t m_Magic; ///< Equal to TERMINFO_MAGIC constant above.
22 uint16_t m_NamesSize;
23 uint16_t m_nBooleans;
24 uint16_t m_nNumbers;
25 uint16_t m_nStrings;
26 uint16_t m_StringTableSize;
29 INTEGRAL_STREAMABLE(STerminfoHeader);
31 #endif
33 namespace utio {
35 //----------------------------------------------------------------------
37 /// Default constructor.
38 CTerminfo::CTerminfo (void)
39 : m_Name (),
40 m_Booleans (),
41 m_Numbers (),
42 m_StringOffsets (),
43 m_StringTable (),
44 m_AcsMap (),
45 m_Ctx (),
46 m_nColors (16),
47 m_nPairs (64),
48 m_nColumns (80),
49 m_nRows (24)
53 /// Default constructor.
54 CTerminfo::CContext::CContext (void)
55 : m_Output (),
56 m_ProgStack (),
57 m_Pos (-1, -1),
58 m_Attrs (0),
59 m_FgColor (lightgray),
60 m_BgColor (black)
64 //----------------------------------------------------------------------
65 // Terminfo loading
66 //----------------------------------------------------------------------
68 /// Loads terminfo entry \p termname into \p buf
69 void CTerminfo::LoadEntry (memblock& buf, const char* termname) const
71 string tipath;
72 if (!termname)
73 termname = "linux";
74 const char* defTiPath = getenv ("TERMINFO");
75 if (!defTiPath)
76 defTiPath = "/usr/share/terminfo";
77 if (access (defTiPath, R_OK | X_OK))
78 throw runtime_error ("could not find the terminfo database; please set $TERMINFO environment variable to point to it");
79 tipath.format ("%s/%c/%s", defTiPath, termname[0], termname);
80 if (access (tipath.c_str(), R_OK))
81 throw runtime_error ("could not find the terminfo description for your terminal; please update your terminfo database");
82 buf.read_file (tipath.c_str());
85 /// Reads the terminfo entry from stream \p is.
86 void CTerminfo::read (istream& is)
88 // First the header.
89 STerminfoHeader h;
90 is >> h;
91 #if BYTE_ORDER == BIG_ENDIAN
92 h.m_Magic = le_to_native (h.m_Magic);
93 h.m_NamesSize = le_to_native (h.m_NamesSize);
94 h.m_nBooleans = le_to_native (h.m_nBooleans);
95 h.m_nNumbers = le_to_native (h.m_nNumbers);
96 h.m_nStrings = le_to_native (h.m_nStrings);
97 h.m_StringTableSize = le_to_native (h.m_StringTableSize);
98 #endif
99 if (h.m_Magic != TERMINFO_MAGIC)
100 throw domain_error ("corrupt terminfo file");
102 // The names section
103 is.read_strz (m_Name);
105 // The boolean section
106 m_Booleans.resize (h.m_nBooleans);
107 nr_container_read (is, m_Booleans);
108 is >> ios::talign<number_t>();
110 // The numbers section
111 m_Numbers.resize (h.m_nNumbers);
112 nr_container_read (is, m_Numbers);
114 // The string offsets section
115 m_StringOffsets.resize (h.m_nStrings);
116 nr_container_read (is, m_StringOffsets);
118 // The stringtable
119 m_StringTable.resize (h.m_StringTableSize);
120 is.read (m_StringTable.begin(), m_StringTable.size());
122 #if BYTE_ORDER == BIG_ENDIAN
123 foreach (numvec_t::iterator, i, m_Numbers) *i = le_to_native (*i);
124 foreach (stroffvec_t::iterator, i, m_StringOffsets) *i = le_to_native (*i);
125 #endif
128 /// Writes a terminfo entry into stream \p os
129 void CTerminfo::write (ostream& os) const
131 STerminfoHeader h;
132 h.m_Magic = native_to_le (TERMINFO_MAGIC);
133 h.m_NamesSize = native_to_le (m_Name.size() + 1);
134 h.m_nBooleans = native_to_le (m_Booleans.size());
135 h.m_nNumbers = native_to_le (m_Numbers.size());
136 h.m_nStrings = native_to_le (m_StringOffsets.size());
137 h.m_StringTableSize = native_to_le (m_StringTable.size());
138 os.write_strz (m_Name);
139 nr_container_write (os, m_Booleans);
140 os << ios::talign<number_t>();
141 foreach (numvec_t::const_iterator, i, m_Numbers) os << native_to_le (*i);
142 foreach (stroffvec_t::const_iterator, i, m_StringOffsets) os << native_to_le (*i);
143 os.write (m_StringTable.begin(), m_StringTable.size());
146 /// Returns the written size of the terminfo entry.
147 size_t CTerminfo::stream_size (void) const
149 return (stream_size_of(STerminfoHeader()) +
150 Align (m_Name.size() + m_Booleans.size(), __alignof__(number_t)) +
151 m_Numbers.size() * sizeof(number_t) +
152 m_StringOffsets.size() * sizeof(stroffset_t) +
153 m_StringTable.size());
156 /// Loads the terminfo entry \p termname.
157 void CTerminfo::Load (const char* termname)
159 if (!termname || !*termname)
160 termname = getenv ("TERM");
161 if (!termname || !*termname)
162 termname = "linux";
163 memblock tibuf;
164 LoadEntry (tibuf, termname);
165 istream is (tibuf);
166 read (is);
167 CacheFrequentValues();
168 ObtainTerminalParameters();
171 /// Caches frequently used, but badly formatted caps.
172 void CTerminfo::CacheFrequentValues (void)
174 // Color stuff.
175 m_nColors = GetNumber (ti::max_colors);
176 m_nPairs = GetNumber (ti::max_pairs);
177 if (m_nColors == uint16_t(ti::no_value))
178 m_nColors = color_Last;
179 if (m_nPairs == uint16_t(ti::no_value))
180 m_nPairs = 64;
182 // Decode the ACS capability, if present.
183 const string acsString (GetString (ti::acs_chars));
184 for (uoff_t i = 0; i < acs_Last; ++ i)
185 m_AcsMap[i] = c_AcscInfo[i].m_Default;
186 if (!acsString.empty()) {
187 const SAcscInfo* cFirst = c_AcscInfo;
188 const SAcscInfo* cLast = cFirst + acs_Last;
189 for (string::const_iterator i = acsString.begin(); i < acsString.end(); i += 2)
190 for (const SAcscInfo* cFound = cFirst; cFound < cLast; ++ cFound)
191 if (cFound->m_vt100Code == *i)
192 m_AcsMap [distance (cFirst, cFound)] = *(i + 1);
196 /// Queries the terminal parameters (such as the screen size)
197 void CTerminfo::ObtainTerminalParameters (void)
199 m_nRows = m_nColumns = 0;
200 // The environment variables seem to be the ones that lie least often.
201 const char* sp;
202 if ((sp = getenv("LINES")))
203 m_nRows = atoi(sp);
204 if ((sp = getenv("COLUMNS")))
205 m_nColumns = atoi(sp);
206 // Next, try asking the VT
207 if (!m_nRows || !m_nColumns) {
208 struct winsize ws;
209 if (!ioctl (STDIN_FILENO, TIOCGWINSZ, &ws)) {
210 m_nColumns = ws.ws_col;
211 m_nRows = ws.ws_row;
214 // Try to fallback to the terminfo entries, or to 80x24.
215 if (!m_nRows || !m_nColumns) {
216 if ((m_nRows = GetNumber (ti::lines)) == dim_t(ti::no_value))
217 m_nRows = 24;
218 if ((m_nColumns = GetNumber (ti::columns)) == dim_t(ti::no_value))
219 m_nColumns = 80;
223 //----------------------------------------------------------------------
224 // Terminfo values access interface
225 //----------------------------------------------------------------------
227 /// Gets boolean value \p i.
228 bool CTerminfo::GetBool (ti::EBooleans i) const
230 return (size_t(i) < m_Booleans.size() ? (m_Booleans[i] > 0) : false);
233 /// Gets numeral value \p i.
234 CTerminfo::number_t CTerminfo::GetNumber (ti::ENumbers i) const
236 return (size_t(i) < m_Numbers.size() ? m_Numbers[i] : number_t(ti::no_value));
239 /// Gets string value \p i.
240 CTerminfo::capout_t CTerminfo::GetString (ti::EStrings i) const
242 if (size_t(i) >= m_StringOffsets.size() || m_StringOffsets[i] == stroffset_t(ti::no_value))
243 return (string::empty_string);
244 return (m_StringTable.begin() + m_StringOffsets[i]);
247 /// Pops a value from the program stack.
248 CTerminfo::progvalue_t CTerminfo::PSPop (void) const
250 if (m_Ctx.m_ProgStack.empty())
251 return (0);
252 const progvalue_t v = m_Ctx.m_ProgStack.back();
253 m_Ctx.m_ProgStack.pop_back();
254 return (v);
257 /// Pushes \p v onto the program stack.
258 void CTerminfo::PSPush (progvalue_t v) const
260 m_Ctx.m_ProgStack.push_back (v);
263 /// Runs the % opcodes in \p program and appends to \p result.
264 void CTerminfo::RunStringProgram (const char* program, string& result, progargs_t args) const
266 bool bCondValue = false;
267 const string prgstr (program);
268 foreach (string::const_iterator, i, prgstr) {
269 if (*i != '%') { // Output normal data
270 result += *i;
271 continue;
273 int width = 0, base = 0;
274 switch (*++i) { // beware the excessive use of fallthrough :)
275 case '%': result += *i; break; // %% outputs %.
276 case 'i': ++args[0]; ++args[1]; break; // %i adds 1 to i two arguments.
277 case 'c': result += char(PSPop()); break;
278 case 'x': base = 16; continue;
279 case '0': if (!base) base = 8;
280 case '1': case '2': case '3': case '4': // part of %d width field
281 case '5': case '6': case '7': case '8':
282 case '9': if (!base) base = 10;
283 width = width * base + (*i - '0');
284 continue;
285 case '\\': base = 0;
286 case '{': continue; // %{number}
287 case '\'': if (*(i - 1) == '%') { // %'A' or %'\017'
288 if (*(i + 1) != '\\')
289 width = *++i;
290 continue;
292 case '}': PSPush (width); break;
293 // Binary operands are in infix (reversed) order
294 case '+': PSPush (PSPop() + PSPop()); break;
295 case '-': PSPush (-PSPop() + PSPop()); break;
296 case '*': PSPush (PSPop() * PSPop()); break;
297 case '/': PSPush (PSPop() / PSPopNonzero());break;
298 case 'm': PSPush (PSPop() % PSPopNonzero());break;
299 case '|': PSPush (PSPop() | PSPop()); break;
300 case '&': PSPush (PSPop() & PSPop()); break;
301 case '^': PSPush (PSPop() ^ PSPop()); break;
302 case '>': PSPush (PSPop() < PSPop()); break;
303 case '<': PSPush (PSPop() > PSPop()); break;
304 case '=': PSPush (PSPop() == PSPop()); break;
305 case 'A': PSPush (PSPop() && PSPop()); break;
306 case 'O': PSPush (PSPop() || PSPop()); break;
307 case '!': PSPush (!PSPop()); break;
308 case '~': PSPush (~PSPop()); break;
309 case 't': bCondValue = PSPop();
310 case 'e': if ((bCondValue = !bCondValue)) { // this also supports elsif
311 uoff_t elseLoc = prgstr.find ("%e", i - prgstr.begin());
312 uoff_t endLoc = prgstr.find ("%;", i - prgstr.begin());
313 --(i = prgstr.iat (min (elseLoc, endLoc)));
315 case '?':
316 case ';': break;
317 case 'p': PSPush (args [min (uoff_t(*++i - '1'), args.size() - 1)]); break; // %p[0-9] pushes numbered parameter.
318 case 'd': { // %d prints the top of the stack and pops the stack.
319 progvalue_t n = PSPop();
320 const size_t iSize = result.size();
321 do {
322 result += string::value_type('0' + (n % 10));
323 } while ((n /= 10) || --width > 0);
324 reverse (result.begin() + iSize, result.end());
325 break; }
330 /// Replaces \p c with a terminal-specific accelerated value, if available.
331 wchar_t CTerminfo::SubstituteChar (wchar_t c) const
333 for (uoff_t i = 0; i < acs_Last; ++ i)
334 if (c_AcscInfo[i].m_Unicode == c)
335 return (m_AcsMap[i]);
336 return (c);
339 /// Loads terminal strings produced by special keys into \p ksv.
340 void CTerminfo::LoadKeystrings (keystrings_t& ksv) const
342 ksv.clear();
343 for (uoff_t i = 0; i < VectorSize(c_KeyToStringMap); ++i) {
344 const char* ksvp = GetString (ti::EStrings (c_KeyToStringMap [i]));
345 if (!*ksvp && i == kv_Esc - kv_First)
346 ksvp = "\x1B";
347 ksv += ksvp;
348 ksv += '\0';
352 //----------------------------------------------------------------------
353 // Screen output interface
354 //----------------------------------------------------------------------
356 /// Clears the screen.
357 CTerminfo::capout_t CTerminfo::Clear (void) const
359 m_Ctx.m_Pos[0] = 0;
360 m_Ctx.m_Pos[1] = 0;
361 return (GetString (ti::clear_screen));
364 /// Resets the saved terminal state without doing anything to the terminal.
365 void CTerminfo::ResetState (void) const
367 m_Ctx.m_Attrs = 0;
368 m_Ctx.m_FgColor = lightgray;
369 m_Ctx.m_BgColor = black;
370 m_Ctx.m_Pos[0] = 0;
371 m_Ctx.m_Pos[1] = 0;
374 /// Resets the terminal to a sane state.
375 CTerminfo::capout_t CTerminfo::Reset (void) const
377 ResetState();
378 return (GetString (ti::reset_1string));
381 /// Stops all attributes, including color.
382 CTerminfo::capout_t CTerminfo::AllAttrsOff (void) const
384 m_Ctx.m_Attrs = 0;
385 m_Ctx.m_FgColor = lightgray;
386 m_Ctx.m_BgColor = black;
387 return (GetString (ti::exit_attribute_mode));
390 /// Updates cached screen information.
391 void CTerminfo::Update (void)
393 ObtainTerminalParameters();
396 /// Appends move(x,y) string to s.
397 void CTerminfo::MoveTo (coord_t x, coord_t y, rstrbuf_t s) const
399 RunStringProgram (GetString (ti::cursor_address), s, progargs_t(y, x));
400 m_Ctx.m_Pos[0] = x;
401 m_Ctx.m_Pos[1] = y;
404 /// Moves the cursor to \p x, \p y.
405 CTerminfo::strout_t CTerminfo::MoveTo (coord_t x, coord_t y) const
407 m_Ctx.m_Output.clear();
408 MoveTo (x, y, m_Ctx.m_Output);
409 return (m_Ctx.m_Output);
412 /// Sets the color to \p fg on \p bg.
413 CTerminfo::strout_t CTerminfo::Color (EColor fg, EColor bg) const
415 m_Ctx.m_Output.clear();
416 Color (fg, bg, m_Ctx.m_Output);
417 return (m_Ctx.m_Output);
420 /// Truncates color values to supported range and sets attributes to compensate.
421 void CTerminfo::NormalizeColor (EColor& fg, EColor& bg, uint16_t& attrs) const
423 if (fg > color_Last)
424 fg = lightgray;
425 if (bg > color_Last)
426 bg = black;
427 if (fg < 8)
428 attrs &= ~(1 << a_bold);
429 else if (fg >= 8 && fg < color_Last) {
430 attrs |= (1 << a_bold);
431 fg = EColor(fg - 8);
433 if (bg < 8)
434 attrs &= ~(1 << a_blink);
435 else if (bg >= 8 && bg < color_Last) {
436 attrs |= (1 << a_blink);
437 bg = EColor(bg - 8);
441 /// Sets the color using normalized values (i.e. no attribute setting)
442 void CTerminfo::NColor (EColor fg, EColor bg, rstrbuf_t s) const
444 if (m_Ctx.m_FgColor != fg && fg != color_Preserve)
445 RunStringProgram (GetString (ti::set_a_foreground), s, progargs_t(fg));
446 if (m_Ctx.m_BgColor != bg && bg != color_Preserve)
447 RunStringProgram (GetString (ti::set_a_background), s, progargs_t(bg));
448 m_Ctx.m_FgColor = fg;
449 m_Ctx.m_BgColor = bg;
452 /// Sets the color to \p fg on \p bg, appending result to \p s.
453 void CTerminfo::Color (EColor fg, EColor bg, rstrbuf_t s) const
455 uint16_t newAttrs = m_Ctx.m_Attrs;
456 NormalizeColor (fg, bg, newAttrs);
457 if (m_Ctx.m_Attrs != newAttrs)
458 Attrs (newAttrs, s);
459 NColor (fg, bg, s);
462 /// Starts attribute \p a.
463 CTerminfo::capout_t CTerminfo::AttrOn (EAttribute a) const
465 static const ti::EStrings as [attr_Last] = {
466 /* a_standout */ ti::enter_standout_mode,
467 /* a_underline */ ti::enter_underline_mode,
468 /* a_reverse */ ti::enter_reverse_mode,
469 /* a_blink */ ti::enter_blink_mode,
470 /* a_halfbright */ ti::enter_dim_mode,
471 /* a_bold */ ti::enter_bold_mode,
472 /* a_invisible */ ti::enter_secure_mode,
473 /* a_protect */ ti::enter_protected_mode,
474 /* a_altcharset */ ti::enter_alt_charset_mode,
475 /* a_italic */ ti::enter_italics_mode,
476 /* a_subscript */ ti::enter_subscript_mode,
477 /* a_superscript */ ti::enter_superscript_mode,
479 m_Ctx.m_Attrs |= (1 << a);
480 return (a < attr_Last ? GetString (as[a]) : string::empty_string);
483 /// \brief Stops attribute \p a.
484 /// Note that this is not too reliable and will quite likely
485 /// turn off all your attributes (including colors) instead.
487 CTerminfo::capout_t CTerminfo::AttrOff (EAttribute a) const
489 static const ti::EStrings as [attr_Last] = {
490 /* a_standout */ ti::exit_standout_mode,
491 /* a_underline */ ti::exit_underline_mode,
492 /* a_reverse */ ti::exit_attribute_mode,
493 /* a_blink */ ti::exit_attribute_mode,
494 /* a_halfbright */ ti::exit_attribute_mode,
495 /* a_bold */ ti::exit_attribute_mode,
496 /* a_invisible */ ti::exit_attribute_mode,
497 /* a_protect */ ti::exit_attribute_mode,
498 /* a_altcharset */ ti::exit_alt_charset_mode,
499 /* a_italic */ ti::exit_italics_mode,
500 /* a_subscript */ ti::exit_subscript_mode,
501 /* a_superscript */ ti::exit_superscript_mode,
503 const uint16_t newAttrs (m_Ctx.m_Attrs & ~(1 << a));
504 if (a >= attr_Last || (as[a] == ti::exit_attribute_mode && GetString(ti::set_attributes) != string::empty_string))
505 return (Attrs (newAttrs));
506 m_Ctx.m_Attrs = newAttrs;
507 return (GetString (as[a]));
510 /// Same as Attrs, but it appends to m_Output
511 void CTerminfo::Attrs (uint16_t a, rstrbuf_t s) const
513 if (m_Ctx.m_Attrs == a)
514 return;
515 const capout_t sgr = GetString (ti::set_attributes);
516 if (sgr == string::empty_string) {
517 size_t nToOff (0), nToOn (0);
518 uint16_t mask (1);
519 for (uoff_t i = 0; i < attr_Last; ++ i, mask <<= 1) {
520 nToOff += (m_Ctx.m_Attrs & mask) && !(a & mask);
521 nToOn += !(m_Ctx.m_Attrs & mask) && (a & mask);
523 const uint16_t oldAttrs (m_Ctx.m_Attrs);
524 if (nToOff) {
525 s += GetString (ti::exit_attribute_mode);
526 s += GetString (ti::exit_alt_charset_mode);
527 m_Ctx.m_FgColor = lightgray;
528 m_Ctx.m_BgColor = black;
530 mask = 1;
531 for (uoff_t i = 0; i < attr_Last; ++ i, mask <<= 1)
532 if ((a & mask) && (nToOff || !(oldAttrs & mask)))
533 s += AttrOn (EAttribute (i));
534 } else {
535 progargs_t pa;
536 for (uoff_t i = 0; i < pa.size(); ++ i)
537 pa[i] = (a >> i) & 1;
538 RunStringProgram (sgr, s, pa);
539 m_Ctx.m_FgColor = lightgray;
540 m_Ctx.m_BgColor = black;
542 m_Ctx.m_Attrs = a;
545 /// Sets all attributes to values in \p a (masked by EAttribute)
546 CTerminfo::strout_t CTerminfo::Attrs (uint16_t a) const
548 m_Ctx.m_Output.clear();
549 Attrs (a, m_Ctx.m_Output);
550 return (m_Ctx.m_Output);
553 /// Draws a box in the given location using ACS characters.
554 CTerminfo::strout_t CTerminfo::Box (coord_t x, coord_t y, dim_t w, dim_t h) const
556 m_Ctx.m_Output = AttrOn (a_altcharset);
557 MoveTo (x, y, m_Ctx.m_Output);
559 m_Ctx.m_Output += AcsChar (acs_UpperLeftCorner);
560 fill_n (back_inserter(m_Ctx.m_Output), w - 2, AcsChar (acs_HLine));
561 m_Ctx.m_Output += AcsChar (acs_UpperRightCorner);
563 for (dim_t yi = 1; yi < h - 1; ++ yi) {
564 MoveTo (x, y + yi, m_Ctx.m_Output);
565 m_Ctx.m_Output += AcsChar (acs_VLine);
566 MoveTo (x + w - 1, y + yi, m_Ctx.m_Output);
567 m_Ctx.m_Output += AcsChar (acs_VLine);
570 MoveTo (x, y + h - 1, m_Ctx.m_Output);
571 m_Ctx.m_Output += AcsChar (acs_LowerLeftCorner);
572 fill_n (back_inserter(m_Ctx.m_Output), w - 2, AcsChar (acs_HLine));
573 m_Ctx.m_Output += AcsChar (acs_LowerRightCorner);
575 Attrs ((m_Ctx.m_Attrs & ~(1 << a_altcharset)), m_Ctx.m_Output);
576 return (m_Ctx.m_Output);
579 /// Draws a rectangle filled with \p c.
580 CTerminfo::strout_t CTerminfo::Bar (coord_t x, coord_t y, dim_t w, dim_t h, char c) const
582 m_Ctx.m_Output = AttrOn (a_altcharset);
583 for (dim_t yi = 0; yi < h; ++ yi) {
584 MoveTo (x, y + yi, m_Ctx.m_Output);
585 fill_n (back_inserter(m_Ctx.m_Output), w, c);
587 Attrs ((m_Ctx.m_Attrs & ~(1 << a_altcharset)), m_Ctx.m_Output);
588 return (m_Ctx.m_Output);
591 /// Draws a horizontal line.
592 CTerminfo::strout_t CTerminfo::HLine (coord_t x, coord_t y, dim_t w) const
594 return (Bar (x, y, w, 1, AcsChar(acs_HLine)));
597 /// Draws a vertical line.
598 CTerminfo::strout_t CTerminfo::VLine (coord_t x, coord_t y, dim_t h) const
600 return (Bar (x, y, 1, h, AcsChar(acs_VLine)));
603 /// Draws character \p data into the given box. 0-valued characters are transparent.
604 CTerminfo::strout_t CTerminfo::Image (coord_t x, coord_t y, dim_t w, dim_t h, const CCharCell* data) const
606 assert (data && "Image should only be called with valid data");
607 assert (x >= 0 && y >= 0 && x + w <= Width() && y + h <= Height() && "Clip the image data before passing it in. CGC::Clip can do it.");
609 const uint16_t oldAttrs (m_Ctx.m_Attrs);
610 const EColor oldFg (EColor(m_Ctx.m_FgColor)), oldBg (EColor(m_Ctx.m_BgColor));
612 m_Ctx.m_Output = GetString(ti::ena_acs);
613 for (coord_t j = y; j < y + h; ++ j) {
614 for (coord_t i = x; i < x + w; ++ i, ++ data) {
615 wchar_t dc = data->m_Char;
616 if (!dc)
617 continue;
618 if (i != m_Ctx.m_Pos[0] || j != m_Ctx.m_Pos[1]) {
619 if (i == 0 && j == m_Ctx.m_Pos[1] + 1) {
620 m_Ctx.m_Output += '\n';
621 m_Ctx.m_Pos[0] = 0;
622 ++ m_Ctx.m_Pos[1];
623 } else
624 MoveTo (i, j, m_Ctx.m_Output);
626 uint16_t dattr (data->m_Attrs & BitMask(uint16_t,attr_Last));
627 if (dc > CHAR_MAX) {
628 dc = SubstituteChar(dc);
629 dattr |= (1 << a_altcharset);
631 if (!(dattr & (1 << a_altcharset)) && !isprint(dc))
632 dc = ' ';
633 EColor fg (EColor(data->m_FgColor)), bg (EColor(data->m_BgColor));
634 NormalizeColor (fg, bg, dattr);
635 Attrs (dattr, m_Ctx.m_Output);
636 NColor (fg, bg, m_Ctx.m_Output);
637 m_Ctx.m_Output += char(dc);
638 ++ m_Ctx.m_Pos[0];
641 Attrs (oldAttrs, m_Ctx.m_Output);
642 NColor (oldFg, oldBg, m_Ctx.m_Output);
643 return (m_Ctx.m_Output);
646 //----------------------------------------------------------------------
648 // First two values are from the terminfo manpage.
649 // The unicode value is from UnicodeData.txt from www.unicode.org
650 const CTerminfo::SAcscInfo CTerminfo::c_AcscInfo [acs_Last] = {
651 { '}', 'f', acsv_PoundSign },
652 { '.', 'v', acsv_DownArrow },
653 { ',', '<', acsv_LeftArrow },
654 { '+', '>', acsv_RightArrow },
655 { '-', '^', acsv_UpArrow },
656 { 'h', '#', acsv_Board },
657 { '~', 'o', acsv_Bullet },
658 { 'a', ':', acsv_Checkerboard },
659 { 'f','\\', acsv_Degree },
660 { '`', '+', acsv_Diamond },
661 { 'z', '>', acsv_GreaterEqual },
662 { '{', '*', acsv_Pi },
663 { 'q', '-', acsv_HLine },
664 { 'i', '#', acsv_Lantern },
665 { 'n', '+', acsv_Plus },
666 { 'y', '<', acsv_LessEqual },
667 { 'm', '+', acsv_LowerLeftCorner },
668 { 'j', '+', acsv_LowerRightCorner },
669 { '|', '!', acsv_NotEqual },
670 { 'g', '#', acsv_PlusMinus },
671 { 'o', '~', acsv_Scanline1 },
672 { 'p', '-', acsv_Scanline3 },
673 { 'r', '-', acsv_Scanline7 },
674 { 's', '_', acsv_Scanline9 },
675 { '0', '#', acsv_Block },
676 { 'w', '+', acsv_TopTee },
677 { 'u', '+', acsv_RightTee },
678 { 't', '+', acsv_LeftTee },
679 { 'v', '+', acsv_BottomTee },
680 { 'l', '+', acsv_UpperLeftCorner },
681 { 'k', '+', acsv_UpperRightCorner },
682 { 'x', '|', acsv_VLine }
685 const int16_t CTerminfo::c_KeyToStringMap [kv_nKeys] = {
686 /* kv_Esc */ ti::key_command,
687 /* kv_Backspace */ ti::key_backspace,
688 /* kv_Backtab */ ti::key_btab,
689 /* kv_Begin */ ti::key_beg,
690 /* kv_CATab */ ti::key_catab,
691 /* kv_CTab */ ti::key_ctab,
692 /* kv_Cancel */ ti::key_cancel,
693 /* kv_Center */ ti::key_b2,
694 /* kv_Clear */ ti::key_clear,
695 /* kv_ClearToEOL */ ti::key_eol,
696 /* kv_ClearToEOS */ ti::key_eos,
697 /* kv_Close */ ti::key_close,
698 /* kv_Command */ ti::key_command,
699 /* kv_Copy */ ti::key_copy,
700 /* kv_Create */ ti::key_create,
701 /* kv_Delete */ ti::key_dc,
702 /* kv_DeleteLine */ ti::key_dl,
703 /* kv_Down */ ti::key_down,
704 /* kv_DownLeft */ ti::key_c1,
705 /* kv_DownRight */ ti::key_c3,
706 /* kv_End */ ti::key_end,
707 /* kv_Exit */ ti::key_exit,
708 /* kv_F0 */ ti::key_f0,
709 /* kv_F1 */ ti::key_f1,
710 /* kv_F2 */ ti::key_f2,
711 /* kv_F3 */ ti::key_f3,
712 /* kv_F4 */ ti::key_f4,
713 /* kv_F5 */ ti::key_f5,
714 /* kv_F6 */ ti::key_f6,
715 /* kv_F7 */ ti::key_f7,
716 /* kv_F8 */ ti::key_f8,
717 /* kv_F9 */ ti::key_f9,
718 /* kv_F10 */ ti::key_f10,
719 /* kv_F11 */ ti::key_f11,
720 /* kv_F12 */ ti::key_f12,
721 /* kv_F13 */ ti::key_f13,
722 /* kv_F14 */ ti::key_f14,
723 /* kv_F15 */ ti::key_f15,
724 /* kv_F16 */ ti::key_f16,
725 /* kv_F17 */ ti::key_f17,
726 /* kv_F18 */ ti::key_f18,
727 /* kv_F19 */ ti::key_f19,
728 /* kv_F20 */ ti::key_f20,
729 /* kv_F21 */ ti::key_f21,
730 /* kv_F22 */ ti::key_f22,
731 /* kv_F23 */ ti::key_f23,
732 /* kv_F24 */ ti::key_f24,
733 /* kv_F25 */ ti::key_f25,
734 /* kv_F26 */ ti::key_f26,
735 /* kv_F27 */ ti::key_f27,
736 /* kv_F28 */ ti::key_f28,
737 /* kv_F29 */ ti::key_f29,
738 /* kv_F30 */ ti::key_f30,
739 /* kv_F31 */ ti::key_f31,
740 /* kv_F32 */ ti::key_f32,
741 /* kv_F33 */ ti::key_f33,
742 /* kv_F34 */ ti::key_f34,
743 /* kv_F35 */ ti::key_f35,
744 /* kv_F36 */ ti::key_f36,
745 /* kv_F37 */ ti::key_f37,
746 /* kv_F38 */ ti::key_f38,
747 /* kv_F39 */ ti::key_f39,
748 /* kv_F40 */ ti::key_f40,
749 /* kv_F41 */ ti::key_f41,
750 /* kv_F42 */ ti::key_f42,
751 /* kv_F43 */ ti::key_f43,
752 /* kv_F44 */ ti::key_f44,
753 /* kv_F45 */ ti::key_f45,
754 /* kv_F46 */ ti::key_f46,
755 /* kv_F47 */ ti::key_f47,
756 /* kv_F48 */ ti::key_f48,
757 /* kv_F49 */ ti::key_f49,
758 /* kv_F50 */ ti::key_f50,
759 /* kv_F51 */ ti::key_f51,
760 /* kv_F52 */ ti::key_f52,
761 /* kv_F53 */ ti::key_f53,
762 /* kv_F54 */ ti::key_f54,
763 /* kv_F55 */ ti::key_f55,
764 /* kv_F56 */ ti::key_f56,
765 /* kv_F57 */ ti::key_f57,
766 /* kv_F58 */ ti::key_f58,
767 /* kv_F59 */ ti::key_f59,
768 /* kv_F60 */ ti::key_f60,
769 /* kv_F61 */ ti::key_f61,
770 /* kv_F62 */ ti::key_f62,
771 /* kv_F63 */ ti::key_f63,
772 /* kv_Find */ ti::key_find,
773 /* kv_Help */ ti::key_help,
774 /* kv_Home */ ti::key_home,
775 /* kv_Insert */ ti::key_ic,
776 /* kv_InsertLine */ ti::key_il,
777 /* kv_Left */ ti::key_left,
778 /* kv_Mark */ ti::key_mark,
779 /* kv_Message */ ti::key_message,
780 /* kv_Mouse */ ti::key_mouse,
781 /* kv_Move */ ti::key_move,
782 /* kv_Next */ ti::key_next,
783 /* kv_Open */ ti::key_open,
784 /* kv_Options */ ti::key_options,
785 /* kv_PageDown */ ti::key_npage,
786 /* kv_PageUp */ ti::key_ppage,
787 /* kv_Previous */ ti::key_previous,
788 /* kv_Print */ ti::key_print,
789 /* kv_Redo */ ti::key_redo,
790 /* kv_Reference */ ti::key_reference,
791 /* kv_Refresh */ ti::key_refresh,
792 /* kv_Replace */ ti::key_replace,
793 /* kv_Restart */ ti::key_restart,
794 /* kv_Resume */ ti::key_resume,
795 /* kv_Right */ ti::key_right,
796 /* kv_Save */ ti::key_save,
797 /* kv_Select */ ti::key_select,
798 /* kv_ShiftBegin */ ti::key_sbeg,
799 /* kv_ShiftCancel */ ti::key_scancel,
800 /* kv_ShiftCommand */ ti::key_scommand,
801 /* kv_ShiftCopy */ ti::key_scopy,
802 /* kv_ShiftCreate */ ti::key_screate,
803 /* kv_ShiftDelete */ ti::key_sdc,
804 /* kv_ShiftDeleteLine */ ti::key_sdl,
805 /* kv_ShiftEnd */ ti::key_send,
806 /* kv_ShiftEndOfLine */ ti::key_seol,
807 /* kv_ShiftExit */ ti::key_sexit,
808 /* kv_ShiftFind */ ti::key_sfind,
809 /* kv_ShiftHelp */ ti::key_shelp,
810 /* kv_ShiftHome */ ti::key_shome,
811 /* kv_ShiftInsert */ ti::key_sic,
812 /* kv_ShiftLeft */ ti::key_sleft,
813 /* kv_ShiftMessage */ ti::key_smessage,
814 /* kv_ShiftMove */ ti::key_smove,
815 /* kv_ShiftNext */ ti::key_snext,
816 /* kv_ShiftOptions */ ti::key_soptions,
817 /* kv_ShiftPrevious */ ti::key_sprevious,
818 /* kv_ShiftPrint */ ti::key_sprint,
819 /* kv_ShiftRedo */ ti::key_sredo,
820 /* kv_ShiftReplace */ ti::key_sreplace,
821 /* kv_ShiftResume */ ti::key_srsume,
822 /* kv_ShiftRight */ ti::key_sright,
823 /* kv_ShiftSave */ ti::key_ssave,
824 /* kv_ShiftSuspend */ ti::key_ssuspend,
825 /* kv_ShiftTab */ ti::key_stab,
826 /* kv_ShiftUndo */ ti::key_sundo,
827 /* kv_Suspend */ ti::key_suspend,
828 /* kv_Undo */ ti::key_undo,
829 /* kv_Up */ ti::key_up,
830 /* kv_UpLeft */ ti::key_a1,
831 /* kv_UpRight */ ti::key_a3
834 //----------------------------------------------------------------------
836 } // namespace utio