5 * (c) 2024 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
35 /*----- Header files ------------------------------------------------------*/
43 #ifndef MLIB_GPRINTF_H
47 /*----- Attribute flags ---------------------------------------------------*/
49 /* Line style attributes. */
50 #define TTAF_LNMASK 0x0003u /* line style mask */
51 #define TTAF_LNSHIFT 0 /* line style shift */
53 TTLN_NONE, /* no line */
54 TTLN_ULINE, /* underline */
55 TTLN_UULINE, /* double underline */
59 /* Weight attributes. */
60 #define TTAF_WTMASK 0x000cu /* weight mask */
61 #define TTAF_WTSHIFT 2 /* weight shift */
63 TTWT_MED, /* medium */
64 TTWT_BOLD, /* bold/bright */
65 TTWT_DIM, /* light/dim */
69 /* Miscellaneous attributes. */
70 #define TTAF_INVV 0x0010u /* inverse video */
71 #define TTAF_STRIKE 0x0020u /* strike out */
72 #define TTAF_ITAL 0x0040u /* italic/oblique */
74 /* Colour space attributes. */
75 #define TTAF_FGSPCMASK 0x1c00u /* foreground space mask */
76 #define TTAF_FGSPCSHIFT 10 /* foreground space shift */
77 #define TTAF_BGSPCMASK 0xe000u /* background space mask */
78 #define TTAF_BGSPCSHIFT 13 /* background space shift */
80 TTCSPC_NONE, /* no colour */
81 TTCSPC_1BPC, /* one bit per channel */
82 TTCSPC_1BPCBR, /* one bit per channel, brighter */
83 TTCSPC_4LPC, /* four levels per channel */
84 TTCSPC_8LGS, /* eight levels greyscale */
85 TTCSPC_6LPC, /* six levels per channel */
86 TTCSPC_24LGS, /* 24 levels greyscale */
87 TTCSPC_8BPC, /* eight bits per channel */
91 /*----- Specifying colours ------------------------------------------------*/
93 /* One-bit-per-channel colours. */
99 #define TTCOL_RED (TT1BPC_RED)
100 #define TTCOL_GRN (TT1BPC_GRN)
101 #define TTCOL_YLW (TT1BPC_RED | TT1BPC_GRN)
102 #define TTCOL_BLU (TT1BPC_BLU)
103 #define TTCOL_MGN (TT1BPC_RED | TT1BPC_BLU)
104 #define TTCOL_CYN (TT1BPC_GRN | TT1BPC_BLU)
105 #define TTCOL_WHT (TT1BPC_RED | TT1BPC_GRN | TT1BPC_BLU)
106 #define TTCOL_BRBLK (TTCOL_BLK | TT1BPC_BRI)
107 #define TTCOL_BRRED (TTCOL_RED | TT1BPC_BRI)
108 #define TTCOL_BRGRN (TTCOL_GRN | TT1BPC_BRI)
109 #define TTCOL_BRYLW (TTCOL_YLW | TT1BPC_BRI)
110 #define TTCOL_BRBLU (TTCOL_BLU | TT1BPC_BRI)
111 #define TTCOL_BRMGN (TTCOL_MGN | TT1BPC_BRI)
112 #define TTCOL_BRCYN (TTCOL_CYN | TT1BPC_BRI)
113 #define TTCOL_BRWHT (TTCOL_WHT | TT1BPC_BRI)
115 /* Two-bits-per-channel colours. */
116 #define TTCOL_MK2B(r, g, b) (((r) << 4) | ((g) << 2) | ((b) << 0))
117 #define TTCOL_2BR(col) (((col) >> 4)&0x03)
118 #define TTCOL_2BG(col) (((col) >> 2)&0x03)
119 #define TTCOL_2BB(col) (((col) >> 0)&0x03)
121 /* Six-levels-per-channel colours. */
122 #define TTCOL_MK6L(r, g, b) (36*(r) + 6*(g) + (b))
123 #define TTCOL_6LR(col) ((col)/36)
124 #define TTCOL_6LG(col) (((col)/6)%6)
125 #define TTCOL_6LB(col) ((col)%6)
127 /* Eight-bits-per-channel colours. */
128 #define TTCOL_MK8B(r, g, b) \
129 (((uint32)(r) << 16) | ((g) << 8) | ((b) << 0))
130 #define TTCOL_8BR(col) (((col) >> 16)&0xff)
131 #define TTCOL_8BG(col) (((col) >> 8)&0xff)
132 #define TTCOL_8BB(col) (((col) >> 0)&0xff)
134 /*----- Terminal capabilities ---------------------------------------------*/
136 /* Attribute capabilities. */
137 #define TTACF_ULINE 0x00000001u /* underline */
138 #define TTACF_UULINE 0x00000002u /* double underline */
139 #define TTACF_BOLD 0x00000004u /* bold/bright */
140 #define TTACF_DIM 0x00000008u /* light/dim */
141 #define TTACF_INVV 0x00000010u /* inverse video */
142 #define TTACF_STRIKE 0x00000020u /* strikeout */
143 #define TTACF_ITAL 0x00000040u /* italic/oblique */
144 #define TTACF_FG 0x00000080u /* set foreground colour */
145 #define TTACF_BG 0x00000100u /* set background colour */
146 #define TTACF_1BPC 0x00100000u /* one-bit-per-channel space */
147 #define TTACF_1BPCBR 0x00200000u /* one-bit-per-channel bright space */
148 #define TTACF_4LPC 0x00400000u /* four-levels-per-channel space */
149 #define TTACF_8LGS 0x00800000u /* 8-levels-greyscale space */
150 #define TTACF_6LPC 0x01000000u /* six-levels-per-channel space */
151 #define TTACF_24LGS 0x02000000u /* 24-levels-greyscale space */
152 #define TTACF_8BPC 0x04000000u /* eight-bits-per-channel space */
153 #define TTACF_CSPCMASK 0xfff00000u
156 #define TTMF_AUTOM 0x00000001u /* automatic margins */
157 #define TTMF_FSCRN 0x00000002u /* full-screen mode */
158 #define TTMF_CVIS 0x00000004u /* visible cursor */
159 #define TTMF_INS 0x00000008u /* insert mode */
160 #define TTMF_DEL 0x00000010u /* delete mode */
162 /* Other capabilities. */
163 #define TTCF_RELMV 0x00000100u /* relative cursor motion */
164 #define TTCF_ABSMV 0x00000200u /* absolute cursor motion */
165 #define TTCF_MIXMV 0x00000400u /* mixed (y-abs, x-rel) motion */
166 #define TTCF_MMARG 0x00000800u /* proper magic margins */
167 #define TTCF_BGER 0x00001000u /* erasure uses background colour */
168 #define TTCF_ERCH 0x00002000u /* erase characters */
169 #define TTCF_ERBOL 0x00004000u /* erase from beginning of line */
170 #define TTCF_EREOL 0x00008000u /* erase to end of line */
171 #define TTCF_ERBOD 0x00010000u /* erase from beginning of display */
172 #define TTCF_EREOD 0x00020000u /* erase to end of display */
173 #define TTCF_ERDSP 0x00040000u /* erase display */
174 #define TTCF_INSCH 0x00080000u /* insert character */
175 #define TTCF_INSLN 0x00100000u /* insert line */
176 #define TTCF_DELCH 0x00200000u /* delete character */
177 #define TTCF_DELLN 0x00400000u /* delete line */
179 /*----- Other constants ---------------------------------------------------*/
182 #define TTOF_XHOME 0u /* absolute horizontal motion */
183 #define TTOF_XCUR 1u /* relative horizontal motion */
184 #define TTOF_YHOME 0u /* absolute vertical motion */
185 #define TTOF_YCUR 2u /* relative vertical motion */
186 #define TTORG_HOME (TTOF_XHOME | TTOF_YHOME)
187 #define TTORG_CUR (TTOF_XCUR | TTOF_YCUR)
190 #define TTEF_LINE 0u /* erase within line */
191 #define TTEF_DSP 1u /* erase whole display */
192 #define TTEF_BEGIN 2u /* erase from beginning */
193 #define TTEF_END 4u /* erase to end */
195 /* Insertion and deletion. */
196 #define TTIDF_CH 0u /* insert/delete characters */
197 #define TTIDF_LN 1u /* insert/delete lines */
199 /* Backend implementations. */
201 TTBK_END, /* list end marker */
202 TTBK_TERMCAP, /* traditional `termcap' */
203 TTBK_TERMINFO, /* standard `terminfo' */
204 TTBK_UNIBI, /* modern `Unibilium' */
205 TTBK_ANSI, /* assume modern terminal conventions */
209 /*----- Data structures ---------------------------------------------------*/
211 /* An attribute setting. */
213 uint32 f, _res0; /* attribute flags, reserved */
214 uint32 fg, bg; /* foreground/background colours */
216 #define TTY_ATTR_INIT { 0, 0, 0, 0 }
218 /* A menu of attribute requests. */
219 struct tty_attrlist {
220 uint32 cap_mask, cap_eq; /* capabilities to select */
221 struct tty_attr attr; /* attributes to set */
223 #define TTY_ATTRLIST_CLEAR { 0, 0, TTY_ATTR_INIT }
224 #define TTY_ATTRLIST_END { 0, 1, TTY_ATTR_INIT }
226 /* A terminal logical state. */
229 struct tty_attr attr;
232 /* Terminal control state. */
234 const struct tty_ops *ops; /* operations table (opaque) */
235 FILE *fpin, *fpout; /* input and output streams */
236 unsigned baud; /* (output) baud rate (bits/s) */
237 unsigned ht, wd; /* terminal dimensions */
238 unsigned f; /* flags */
239 #define TTF_BORROW 1u /* don't close the streams */
240 uint32 acaps, ocaps; /* attribute and other capabilities */
241 struct tty_state st; /* current terminal state */
244 /*----- Lifecycle and maintenance -----------------------------------------*/
246 /* --- @tty_open@ --- *
248 * Arguments: @FILE *fp@ = stream open on terminal, or null
249 * @unsigned f@ = flags (@TTF_...@)
250 * @const unsigned *backends@ = ordered list of backends to try
252 * Returns: Pointer to terminal control block, or null on error.
254 * Use: Open a terminal and return a @struct tty *@ terminal control
257 * If @fp@ is provided, then it will be used for terminal
258 * output. If @fp@ is @stdout@, then input (not currently
259 * supported) will come from @stdin@; otherwise, input comes
260 * from @fp@. If @fp@ is null and @TTF_OPEN@ is set, then
261 * @tty_open@ will attempt to open a terminal for itself: if
262 * @stdin@ is interactive then it used for input; if @stdout@ --
263 * or, failing that, @stderr@ -- is interactive, then it is used
264 * for output; otherwise %|/dev/tty|% is opened and used. If
265 * @fp@ is null and @TTF_OPEN@ is not set, then a usable
266 * terminal control block is still returned, but output cannot
267 * be sent directly to the terminal -- since there isn't one.
269 * If @TTF_BORROW@ is set, then the stream will not be closed by
270 * @tty_close@. (This flag is ignored if @fp@ is null.)
272 * If @beckends@ is provided, then it points to a vector of
273 * @TTBK_...@ constants describing the backends to be tried in
274 * order. The vector is terminated by @TTBK_END@; if this is
275 * found, then a null pointer is returned.
277 * A null control block pointer is valid for all @tty@
278 * functions: most will just immediately report failure, but
279 * there won't be any crashing.
282 #define TTF_OPEN 256u /* open terminal if @fp@ is null */
283 extern struct tty *tty_open(FILE */*fp*/, unsigned /*f*/,
284 const unsigned */*backends*/);
286 /* --- @tty_close@ --- *
288 * Arguments: @struct tty *tty@ = control block pointer
292 * Use: Closes a terminal, releasing the control block and any
293 * resources it held. In particular, if the terminal was opened
294 * without @TTF_BORROW@, then the output stream is closed.
297 extern void tty_close(struct tty */*tty*/);
299 /* --- @tty_resized@ --- *
301 * Arguments: @struct tty *tty@ = control block pointer
303 * Returns: Zero if the size hasn't changed, %$+1$% if the size has
304 * changed, or %$-1$% on error.
306 * Use: Update the terminal width and height. Call this after
307 * receiving @SIGWINCH@, or otherwise periodically, to avoid
311 extern int tty_resized(struct tty */*tty*/);
313 /*----- Output and control functions --------------------------------------*/
315 /* These functions come in pairs. The unmarked version sends output directly
316 * to the terminal's output stream, and will obviously fail (though not
317 * crash) if this is null; the version whose names ends with @...g@ accepts
318 * an additional pair of arguments @gops@ and @go@ giving an alternative
319 * destination for output.
322 /* --- @tty_setattr@, @tty_setattrg@--- *
324 * Arguments: @struct tty *tty@ = control block pointer
325 * @const struct gprintf_ops *gops, void *go@ = output
327 * @const struct tty_attr *a@ = pointer to attributes to set, or
330 * Returns: Zero on success, %$-1$% on error.
332 * Use: Set the indicated formatting attributes on following output.
333 * If @a@ is null, then clear all attributes, just as if
336 * If you only ever request attributes which are advertised in
337 * the terminals capapbility masked, then you'll always get what
338 * you asked for. Otherwise, the provided attributes will be
339 * `clamped', i.e., modified so as to accommodate the terminal's
340 * shortcomings. In simple cases, unsupported attributes may
341 * just be dropped; but they can also be substituted, e.g.,
342 * single underlining for double, or approximate colours for
343 * unsupported colours.
346 extern int tty_setattr(struct tty */*tty*/, const struct tty_attr */*a*/);
347 extern int tty_setattrg(struct tty */*tty*/,
348 const struct gprintf_ops */*gops*/, void */*go*/,
349 const struct tty_attr */*a*/);
351 /* --- @tty_setattrlist@, @tty_setattrlistg@ --- *
353 * Arguments: @struct tty *tty@ = control block pointer
354 * @const struct gprintf_ops *gops, void *go@ = output
356 * @const struct tty_attrlist *aa@ = pointer to attribute list
359 * Returns: Zero on success, %$-1$% on error.
361 * Use: Search the list for an entry matching the terminal's
362 * capabilities, i.e., @tty->acaps&a->cap_mask == a->cap_eq@.
363 * The attributes in the first such entry are set, as if by
366 * The list is terminated by an entry with @cap_mask == 0@ --
367 * though it will be checked like any other before ending the
368 * search. In particular, this means that an entry with
369 * @cap_mask == cap_eq == 0@ is a `catch-all', and its
370 * attributes will be set if no earlier matching entry could be
371 * found, while an entry with @cap_mask == 0@ and @cap_eq != 0@
372 * terminates the search without setting any attributes.
375 extern int tty_setattrlist(struct tty */*tty*/,
376 const struct tty_attrlist */*aa*/);
377 extern int tty_setattrlistg(struct tty */*tty*/,
378 const struct gprintf_ops */*gops*/, void */*go*/,
379 const struct tty_attrlist */*aa*/);
381 /* --- @tty_setmodes@, @tty_setmodesg@ --- *
383 * Arguments: @struct tty *tty@ = control block pointer
384 * @const struct gprintf_ops *gops, void *go@ = output
386 * @uint32 modes_bic, modes_xor@ = masks to apply to the modes
389 * Returns: Zero on success, %$-1$% on error.
391 * Use: Adjust the terminal display modes: specifically, the modes
392 * are adjusted to be @(modes&~modes_bic) ^ modes_xor@.
393 * Mode bits which aren't supported by the terminal are
397 extern int tty_setmodes(struct tty */*tty*/,
398 uint32 /*modes_bic*/, uint32 /*modes_xor*/);
399 extern int tty_setmodesg(struct tty */*tty*/,
400 const struct gprintf_ops */*gops*/, void */*go*/,
401 uint32 /*modes_bic*/, uint32 /*modes_xor*/);
403 /* --- @tty_move@, @tty_moveg@ --- *
405 * Arguments: @struct tty *tty@ = control block pointer
406 * @const struct gprintf_ops *gops, void *go@ = output
408 * @unsigned orig@ = origin
409 * @int y, x@ = new cursor position
411 * Returns: Zero on success, %$-1$% on error.
413 * Use: Move the cursor. Coordinates are numbered starting with 0.
415 * The @y@ position is interpreted relative to the origin given
416 * by @orig@: if @TTOF_YCUR@ is set, then the motion is relative
417 * to the current cursor row; otherwise, it is relative to
418 * the top `home' row. Similarly, the @x@ position is
419 * interpreted relative to the current cursor column if
420 * @TTOF_XCUR is set, otherwise relative to the leftmost
423 * Not all terminals are capable of all kinds of motions:
424 * @TTCF_ABSMV@ is set if absolute motion is possible, and
425 * @TTCF_RELMV@ is set if relative motion is possible. The
426 * @TTCF_MIXMV@ bit indicates that the combination of absolute-y
427 * and relative-x motion is possible; note that the combination
428 * of relative-y and absolute-x is always possible if relative
429 * motion is possible at all.
431 * The above notwithstanding, all terminals are assumed capable
432 * of moving the cursor to the start of either the current line
433 * @tty_move(tty, TTOF_YCUR | TTOF_XHOME, 0, 0)@, or of the next
434 * line @tty_move(tty, TTOF_YCUR | TTOF_XHOME, +1, 0)@.
437 extern int tty_move(struct tty */*tty*/,
438 unsigned /*orig*/, int /*y*/, int /*x*/);
439 extern int tty_moveg(struct tty */*tty*/,
440 const struct gprintf_ops */*gops*/, void */*go*/,
441 unsigned /*orig*/, int /*y*/, int /*x*/);
443 /* --- @tty_repeat@, @tty_repeatg@ --- *
445 * Arguments: @struct tty *tty@ = control block pointer
446 * @const struct gprintf_ops *gops, void *go@ = output
448 * @int ch@ = character to write
449 * @unsigned n@ = number of copies
451 * Returns: Zero on success, %$-1$% on error.
453 * Use: Write @n@ copies of the character @ch@ to the terminal.
454 * (Some terminals have a special control sequence for doing
458 extern int tty_repeat(struct tty */*tty*/, int /*ch*/, unsigned /*n*/);
459 extern int tty_repeatg(struct tty */*tty*/,
460 const struct gprintf_ops */*gops*/, void */*go*/,
461 int /*ch*/, unsigned /*n*/);
463 /* --- @tty_erase@, @tty_eraseg@ --- *
465 * Arguments: @struct tty *tty@ = control block pointer
466 * @const struct gprintf_ops *gops, void *go@ = output
468 * @unsigned f@ = flags
470 * Returns: Zero on success, %$-1$% on error.
472 * Use: Erase portions of the current line or the whole display.
474 * If @TTEF_DSP@ is set, then the whole display is affected. If
475 * @TTEF_BEGIN@ is set, then the display is erased starting from
476 * the top left and ending at and including the cursor
477 * position. If @TTEF_END@ is set, then the display is erased
478 * starting from and including the cursor position, and ending
479 * at the bottom right. If both flags are set, then,
480 * additionally, the cursor is moved to its `home' position at
483 * If @TTF_DSP@ is not set, then the current line is affected.
484 * If @TTEF_BEGIN@ is set, then the line is erased starting from
485 * the left and ending at and including the cursor position. If
486 * @TTEF_END@ is set, then the line is erased starting from and
487 * including the cursor position, and ending at the right hand
490 * If the @TTCF_BGER@ capability is set, then the erased
491 * positions take on the current background colour; otherwise,
492 * they have the default background colour.
495 extern int tty_erase(struct tty */*tty*/, unsigned /*f*/);
496 extern int tty_eraseg(struct tty */*tty*/,
497 const struct gprintf_ops */*gops*/, void */*go*/,
500 /* --- @tty_erch@, @tty_erchg@ --- *
502 * Arguments: @struct tty *tty@ = control block pointer
503 * @const struct gprintf_ops *gops, void *go@ = output
505 * @unsigned n@ = number of characters to erase
507 * Returns: Zero on success, %$-1$% on error.
509 * Use: Erase a number of characters, starting from and including the
510 * current cursor position.
512 * If the @TTCF_BGER@ capability is set, then the erased
513 * positions take on the current background colour; otherwise,
514 * they have the default background colour.
517 extern int tty_erch(struct tty */*tty*/, unsigned /*n*/);
518 extern int tty_erchg(struct tty */*tty*/,
519 const struct gprintf_ops */*gops*/, void */*go*/,
522 /* --- @tty_ins@, @tty_insg@ --- *
524 * Arguments: @struct tty *tty@ = control block pointer
525 * @const struct gprintf_ops *gops, void *go@ = output
527 * @unsigned f@ = flags
528 * @unsigned n@ = number of items to insert
530 * Returns: Zero on success, %$-1$% on error.
532 * Use: Insert a number of blank characters or lines.
534 * If @TTIDF_LN@ is set, then insert @n@ blank lines above the
535 * current line. The cursor must be at the far left of the
538 * Otherwise, insert @n@ empty character spaces at the cursor
542 extern int tty_ins(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/);
543 extern int tty_insg(struct tty */*tty*/,
544 const struct gprintf_ops */*gops*/, void */*go*/,
545 unsigned /*f*/, unsigned /*n*/);
547 /* --- @tty_inch@ --- *
549 * Arguments: @struct tty *tty@ = control block pointer
550 * @const struct gprintf_ops *gops, void *go@ = output
552 * @int ch@ = character to insert
554 * Returns: Zero on success, %$-1$% on error.
556 * Use: Insert a single character.
558 * If the @TTMF_INS@ mode is advertised, then insert mode must
559 * be set before calling this function.
562 extern int tty_inch(struct tty */*tty*/, int /*ch*/);
563 extern int tty_inchg(struct tty */*tty*/,
564 const struct gprintf_ops */*gops*/, void */*go*/,
567 /* --- @tty_del@, @tty_delg@ --- *
569 * Arguments: @struct tty *tty@ = control block pointer
570 * @const struct gprintf_ops *gops, void *go@ = output
572 * @unsigned f@ = flags
573 * @unsigned n@ = number of items to delete
575 * Returns: Zero on success, %$-1$% on error.
577 * Use: Delete a number of characters or lines.
579 * If @TTIDF_LN@ is set, then delete @n@ blank lines, starting
580 * with the current line line. The cursor must be at the far
583 * Otherwise, delete @n@ characters at the cursor position.
586 extern int tty_del(struct tty */*tty*/, unsigned /*f*/, unsigned /*n*/);
587 extern int tty_delg(struct tty */*tty*/,
588 const struct gprintf_ops */*gops*/, void */*go*/,
589 unsigned /*f*/, unsigned /*n*/);
591 /* --- @tty_restore@, @tty_restoreg@ --- *
593 * Arguments: @struct tty *tty@ = control block pointer
594 * @const struct gprintf_ops *gops, void *go@ = output
596 * @const struct tty_state *st@ = state to restore
598 * Returns: Zero on success, %$-1$% on error.
600 * Use: Restore the terminal modes and attributes to match a
601 * state previously captured by copying @tty->st@.
604 extern int tty_restore(struct tty */*tty*/, const struct tty_state */*st*/);
605 extern int tty_restoreg(struct tty */*tty*/,
606 const struct gprintf_ops */*gops*/, void */*go*/,
607 const struct tty_state */*st*/);
609 /*----- That's all, folks -------------------------------------------------*/