1 // This file is part of the utio library, a terminal I/O library.
3 // Copyright (C) 2004 by Mike Sharov <msharov@users.sourceforge.net>
4 // This file is free software, distributed under the MIT License.
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.
26 uint16_t m_StringTableSize
;
29 INTEGRAL_STREAMABLE(STerminfoHeader
);
35 //----------------------------------------------------------------------
37 /// Default constructor.
38 CTerminfo::CTerminfo (void)
53 /// Default constructor.
54 CTerminfo::CContext::CContext (void)
59 m_FgColor (lightgray
),
64 //----------------------------------------------------------------------
66 //----------------------------------------------------------------------
68 /// Loads terminfo entry \p termname into \p buf
69 void CTerminfo::LoadEntry (memblock
& buf
, const char* termname
) const
74 const char* defTiPath
= getenv ("TERMINFO");
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
)
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
);
99 if (h
.m_Magic
!= TERMINFO_MAGIC
)
100 throw domain_error ("corrupt terminfo file");
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
);
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
);
128 /// Writes a terminfo entry into stream \p os
129 void CTerminfo::write (ostream
& os
) const
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
)
164 LoadEntry (tibuf
, termname
);
167 CacheFrequentValues();
168 ObtainTerminalParameters();
171 /// Caches frequently used, but badly formatted caps.
172 void CTerminfo::CacheFrequentValues (void)
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
))
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.
202 if ((sp
= getenv("LINES")))
204 if ((sp
= getenv("COLUMNS")))
205 m_nColumns
= atoi(sp
);
206 // Next, try asking the VT
207 if (!m_nRows
|| !m_nColumns
) {
209 if (!ioctl (STDIN_FILENO
, TIOCGWINSZ
, &ws
)) {
210 m_nColumns
= ws
.ws_col
;
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
))
218 if ((m_nColumns
= GetNumber (ti::columns
)) == dim_t(ti::no_value
))
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())
252 const progvalue_t v
= m_Ctx
.m_ProgStack
.back();
253 m_Ctx
.m_ProgStack
.pop_back();
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
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');
286 case '{': continue; // %{number}
287 case '\'': if (*(i
- 1) == '%') { // %'A' or %'\017'
288 if (*(i
+ 1) != '\\')
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
)));
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();
322 result
+= string::value_type('0' + (n
% 10));
323 } while ((n
/= 10) || --width
> 0);
324 reverse (result
.begin() + iSize
, result
.end());
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
]);
339 /// Loads terminal strings produced by special keys into \p ksv.
340 void CTerminfo::LoadKeystrings (keystrings_t
& ksv
) const
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
)
352 //----------------------------------------------------------------------
353 // Screen output interface
354 //----------------------------------------------------------------------
356 /// Clears the screen.
357 CTerminfo::capout_t
CTerminfo::Clear (void) const
361 return (GetString (ti::clear_screen
));
364 /// Resets the saved terminal state without doing anything to the terminal.
365 void CTerminfo::ResetState (void) const
368 m_Ctx
.m_FgColor
= lightgray
;
369 m_Ctx
.m_BgColor
= black
;
374 /// Resets the terminal to a sane state.
375 CTerminfo::capout_t
CTerminfo::Reset (void) const
378 return (GetString (ti::reset_1string
));
381 /// Stops all attributes, including color.
382 CTerminfo::capout_t
CTerminfo::AllAttrsOff (void) const
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
));
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
428 attrs
&= ~(1 << a_bold
);
429 else if (fg
>= 8 && fg
< color_Last
) {
430 attrs
|= (1 << a_bold
);
434 attrs
&= ~(1 << a_blink
);
435 else if (bg
>= 8 && bg
< color_Last
) {
436 attrs
|= (1 << a_blink
);
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
)
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
)
515 const capout_t sgr
= GetString (ti::set_attributes
);
516 if (sgr
== string::empty_string
) {
517 size_t nToOff (0), nToOn (0);
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
);
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
;
531 for (uoff_t i
= 0; i
< attr_Last
; ++ i
, mask
<<= 1)
532 if ((a
& mask
) && (nToOff
|| !(oldAttrs
& mask
)))
533 s
+= AttrOn (EAttribute (i
));
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
;
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
;
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';
624 MoveTo (i
, j
, m_Ctx
.m_Output
);
626 uint16_t dattr (data
->m_Attrs
& BitMask(uint16_t,attr_Last
));
628 dc
= SubstituteChar(dc
);
629 dattr
|= (1 << a_altcharset
);
631 if (!(dattr
& (1 << a_altcharset
)) && !isprint(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
);
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 //----------------------------------------------------------------------