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,
28 /*----- Header files ------------------------------------------------------*/
47 # include <unibilium.h>
54 /*----- Data structures ---------------------------------------------------*/
56 /*----- Common support machinery ------------------------------------------*/
58 static uint32 basic_colour_map[] = {
59 /* standard black */ 0x000000,
60 /* standard red */ 0xcc0000,
61 /* standard green */ 0x00cc00,
62 /* standard yellow */ 0xcccc00,
63 /* standard blue */ 0x0000cc,
64 /* standard magenta */ 0xcc00cc,
65 /* standard cyan */ 0x00cccc,
66 /* standard white */ 0xcccccc
67 }, bright_colour_map[] = {
68 /* bright black */ 0x333333,
69 /* bright red */ 0xff3333,
70 /* bright green */ 0x33ff33,
71 /* bright yellow */ 0xffff33,
72 /* bright blue */ 0x3333ff,
73 /* bright magenta */ 0xff33ff,
74 /* bright cyan */ 0x33ffff,
75 /* bright white */ 0xffffff
78 static void clamp_colours(uint32 *space_out, uint32 *colour_out,
79 uint32 space, uint32 colour, uint32 acaps)
81 unsigned r, g, b, y, t, range;
82 uint32 best_colour, best_space; int best_error;
84 #define COMMON_SCALE 3825
86 #define CHECK_RANGE(r) do { \
87 STATIC_ASSERT(COMMON_SCALE%(r) == 0, "common scale doesn't cover " #r); \
90 #define SET_RANGE(r) do { CHECK_RANGE(r); range = (r); } while (0)
92 /* Check the colour space. If it's one that the terminal can handle, then
93 * return the colour unchanged. Otherwise, extract the channel components
98 *space_out = TTCSPC_NONE; *colour_out = 0; return;
100 if (colour >= 8) goto inval;
101 if (acaps&TTACF_1BPC)
102 { *space_out = TTACF_1BPC; *colour_out = colour; return; }
103 colour = basic_colour_map[colour];
104 r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
108 if (colour >= 8) goto inval;
109 if (acaps&TTACF_1BPCBR)
110 { *space_out = TTACF_1BPCBR; *colour_out = colour; return; }
111 colour = bright_colour_map[colour];
112 r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
116 if (colour >= 64) goto inval;
117 if (acaps&TTACF_2BPC)
118 { *space_out = TTACF_2BPC; *colour_out = colour; return; }
119 r = TTCOL_2BR(colour); g = TTCOL_2BG(colour); b = TTCOL_2BB(colour);
123 if (colour >= 8) goto inval;
124 if (acaps&TTACF_8LGS)
125 { *space_out = TTACF_8LGS; *colour_out = colour; return; }
126 r = g = b = colour + 1;
130 if (colour >= 216) goto inval;
131 if (acaps&TTACF_6LPC)
132 { *space_out = TTACF_6LPC; *colour_out = colour; return; }
133 r = TTCOL_6LR(colour); g = TTCOL_6LG(colour); b = TTCOL_6LB(colour);
137 if (colour >= 24) goto inval;
138 if (acaps&TTACF_24LGS)
139 { *space_out = TTACF_24LGS; *colour_out = colour; return; }
140 r = g = b = colour + 1;
144 if (colour >= 0x01000000) goto inval;
145 if (acaps&TTACF_8BPC)
146 { *space_out = TTACF_8BPC; *colour_out = colour; return; }
147 r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
156 /* We didn't get an exact match, so we'll have to make do with what we've
165 t = COMMON_SCALE/range; r *= t; g *= t; b *= t;
166 y = (RWT*r + GWT*g + BWT*b + (RWT + GWT + BWT)/2)/(RWT + GWT + BWT);
168 #define TRY_APPROX(red, grn, blu, spc, clr) do { \
169 int r_err = (red) - r, g_err = (grn) - g, b_err = (blu) - b; \
172 if (r_err < 0) r_err = -r_err; \
173 if (g_err < 0) g_err = -g_err; \
174 if (b_err < 0) b_err = -b_err; \
176 err = r_err + g_err + b_err; \
177 if (best_error < 0 || err < best_error) \
178 { best_error = err; best_space = (spc); best_colour = (clr); } \
181 #define TRY_RGB(spc, lvls) do { \
182 CHECK_RANGE((lvls) - 1); \
183 unsigned sc = COMMON_SCALE/((lvls) - 1); \
184 unsigned rr, gg, bb, clr; \
186 rr = (r + sc/2)/sc; gg = (g + sc/2)/sc; bb = (b + sc/2)/sc; \
187 TRY_APPROX(rr*sc, gg*sc, bb*sc, (spc), (rr*(lvls) + bb)*(lvls) + gg); \
190 #define TRY_GREY(spc, lvls) do { \
191 CHECK_RANGE((lvls) + 1); \
192 unsigned sc = COMMON_SCALE/((lvls) + 1); \
195 yy = (y + sc/2)/sc; \
196 if (yy >= 1 && yy <= (lvls)) \
197 { grey = yy*sc; TRY_APPROX(grey, grey, grey, (spc), grey - 1); } \
200 if (acaps&TTACF_1BPC)
201 for (i = 0; i < 8; i++) {
202 t = basic_colour_map[i];
203 TRY_APPROX(TTCOL_8BR(t), TTCOL_8BG(t), TTCOL_8BB(t), TTCSPC_1BPC, i);
205 if (acaps&TTACF_1BPCBR)
206 for (i = 0; i < 8; i++) {
207 t = bright_colour_map[i];
208 TRY_APPROX(TTCOL_8BR(t), TTCOL_8BG(t), TTCOL_8BB(t), TTCSPC_1BPCBR, i);
210 if (acaps&TTACF_2BPC) TRY_RGB(TTCSPC_2BPC, 4);
211 if (acaps&TTACF_8LGS) TRY_GREY(TTCSPC_8LGS, 8);
212 if (acaps&TTACF_6LPC) TRY_RGB(TTCSPC_6LPC, 6);
213 if (acaps&TTACF_24LGS) TRY_GREY(TTCSPC_24LGS, 24);
214 if (acaps&TTACF_8BPC) TRY_RGB(TTCSPC_8BPC, 256);
224 *space_out = best_space; *colour_out = best_colour;
228 *space_out = TTCSPC_NONE; *colour_out = 0;
231 void tty_clampattr(struct tty_attr *a_out, const struct tty_attr *a,
234 uint32 ff = 0, f = a->f, t;
236 t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
241 if (!acaps&TTACF_ULINE) t = TTLN_NONE;
244 if (acaps&TTACF_UULINE) ;
245 else if (acaps&TTACF_ULINE) t = TTLN_ULNE;
249 if (!acaps&TTACF_STRIKE) t = TTLN_NONE;
254 ff |= t << TTAF_LNSHIFT;
256 t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
258 case TTWT_MED: break;
259 case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
260 case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
261 default: t = TTWD_MED; break;
263 ff |= t << TTAF_WTSHIFT;
265 if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
266 if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
268 if (acaps&TTACF_FG) {
269 clamp_colours(&t, &a_out->fg,
270 (f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg);
271 ff |= t << TTAF_FGSPCSHIFT;
273 if (acaps&TTACF_BG) {
274 clamp_colours(&t, &a_out->bg,
275 (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg);
276 ff |= t << TTAF_BGSPCSHIFT;
279 a_out->f = ff; a_out->_res = 0;
282 /*----- Common machinery for `termcap' and `terminfo' ---------------------*/
284 #if defined(HAVE_TERMINFO) || \
285 defined(HAVE_TERMCAP) || \
286 defined(HAVE_UNIBILIUM)
288 #if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
290 static const struct gprintf_ops *global_gops;
291 static void *global_gout;
293 static int term_putch(int ch)
294 { return (global_gops->putch(global_gout, ch)); }
298 #ifdef HAVE_UNIBILIUM
299 # define UNIBI_(x) unibi_##x
304 #define BASICCAPS(_bool, _int, _str) \
305 _str(carriage_return, cr, cr) \
306 _int(lines, lines, li) _int(columns, cols, co)
308 #define ATTRCAPS(_bool, _int, _str) \
309 _str(exit_attribute_mode, sgr0, me) \
310 _str(enter_underline_mode, smul, us) _str(exit_underline_mode, rmul, ue) \
311 _str(enter_italics_mode, sitm, ZH) _str(exit_italics_mode, ritm, ZR) \
312 _str(enter_bold_mode, bold, md) _str(enter_dim_mode, dim, mh) \
313 _str(enter_reverse_mode, rev, mr) \
314 COLOURCAPS(_bool, _int, _str)
316 #define COLOURCAPS(_bool, _int, _str) \
317 _str(set_a_foreground, setaf, AF) _str(set_a_background, setab, AB) \
318 _str(orig_pair, op, op) _int(max_colors, colors, Co)
320 #define MODECAPS(_bool, _int, _str) \
321 _str(enter_am_mode, smam, SA) _str(exit_am_mode, rmam, RA) \
322 _str(enter_ca_mode, smcup, ti) _str(exit_ca_mode, rmcup, te) \
323 _str(enter_insert_mode, smir, im) _str(exit_insert_mode, rmir, ei)
325 #define MOVECAPS(_bool, _int, _str) \
326 _str(carriage_return, cr, cr) \
327 _str(cursor_home, home, ho) \
328 _str(cusor_address, cup, cm) \
329 _str(row_address, vpa, cv) _str(column_address, hpa, ch) \
330 _str(cursor_left, cub1, le) _(parm_left_cursor, cub, LE) \
331 _str(cursor_right, cuf1, nd) _(parm_right_cursor, cuf, RI) \
332 _str(cursor_up, cuu1, up) _str(parm_up_cursor, cuu, UP) \
333 _str(cursor_down, cud1, do) _str(parm_down_cursor, cud, DO)
335 #define SCROLLCAPS(_bool, _int, _str) \
336 _str(change_scroll_region, csr, cs) \
337 _str(scroll_forward, ind, sf) _str(parm_index, indn, SF) \
338 _str(scroll_reverse, ri, sr) _str(parm_rindex, rin, SR)
340 #define ERASECAPS(_bool, _int, _str) \
341 _str(clr_bol, el1, cb) \
342 _str(clr_eol, el, ce) \
343 _str(clr_eos, ed, cd)
345 #define INSDELCAPS(_bool, _int, _str) \
346 _str(insert_character, ich1, ic) _str(parm_ich, ich, IC) \
347 _str(insert_line, il1, al) _str(parm_insert_line, il, AL) \
348 _str(delete_character, dch1, dc) _str(parm_dch, dch, DC) \
349 _str(delete_line, dl1, dl) _str(parm_delete_line, dl, DL)
351 #define STORECAPS(_bool, _int, _str) \
352 ATTRCAPS(_bool, _int, _str) \
353 MODECAPS(_bool, _int, _str) \
354 MOVECAPS(_bool, _int, _str) \
355 SCROLLCAPS(_bool, _int, _str) \
356 ERASECAPS(_bool, _int, _str) \
357 INSDELCAPS(_bool, _int, _str)
359 #define CAP_XMC magic_cookie_glitch, xmc, sg
360 #define CAP_BCE back_color_erase, bce, ut
361 #define CAP_XHPA row_addr_glitch, xvpa, YD
362 #define CAP_XVPA col_addr_glitch, xhpa, YA
363 #define CAP_MIR move_insert_mode, mir, mi
364 #define CAP_MSGR move_standout_mode, msgr, ms
365 #define CAP_NPC no_pad_char, npc, NP
366 #define CAP_AM auto_left_margin, am, am
367 #define CAP_XENL eat_newline_glitch, xenl, xn
369 #define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
372 #define DEF_STRCAP(info, cap) const char *cap;
373 #define DEF_INTCAP(info, cap) int cap;
374 STORECAPS(DEF_STRCAP, DEF_INTCAP)
379 typedef int boolcapfn(int uix, const char *info, const char *cap, void *arg);
380 typedef int intcapfn(int uix, const char *info, const char *cap, void *arg);
381 typedef const char *strcapfn(int uix, const char *info, const char *cap,
384 #define DEFINE_CAPISH(PRE, pre)
386 static int init_caps(struct tty *tty, struct tty_caps *caps,
387 boolcapfn *boolcap, intcapfn *intcap, strcapfn *strcap,
390 tty->acaps = tty->ocaps = 0;
394 /* Inhale all of the interesting terminal capabilities. */
395 #define GETBOOL(var, info, cap) \
396 caps->info = boolcap(CAPREF(var, info, cap), arg);
397 #define GETINT(var, info, cap) \
398 caps->info = intcap(CAPREF(var, info, cap), arg);
399 #define GETSTR(var, info, cap) \
400 caps->info = strcap(CAPREF(var, info, cap), arg);
401 STORECAPS(GETBOOL, GETINT, GETSTR)
406 #define CLEARCAP(var, info, cap) caps->info = 0;
407 #define CLEAR_CAPS(caplist) do { caplist(CLEARCAP) } while (0)
409 /* Basic capabilities. */
410 if (!caps->cr) caps->cr = "\r";
412 /* Attribute capabilities. */
413 if (intcap(CAPREF(CAP_XMC)) || !caps->sgr0)
414 CLEAR_CAPS(ATTRCAPS);
416 if (caps->smul) tty->acaps |= TTACF_ULINE;
417 if (caps->bold) tty->acaps |= TTACF_BOLD;
418 if (caps->dim) tty->acaps |= TTACF_DIM;
419 if (caps->sitm) tty->acaps |= TTACF_ITAL;
420 if (caps->rev) tty->acaps |= TTACF_INVV;
422 if (!caps->colors >= 8 || (!caps->setaf && !caps->setbf))
423 CLEAR_CAPS(COLOURCAPS);
425 if (caps->setaf) tty->acaps |= TTACF_FG;
426 if (caps->setab) tty->acaps |= TTACF_BG;
427 tty->acaps |= TTACF_1BPC;
428 if (caps->colors >= 16) tty->acaps |= TTACF_1BPCBR;
429 if (caps->colors == 88) tty->acaps |= TTACF_2BPC | TTACF_8LGS;
430 else if (caps->colors >= 256) tty->acaps |= TTACF_6LPC | TTACF_24LGS;
431 if (caps->colors >= 16777216) tty->acaps |= TTACF_8BPC;
432 if (boolcap(CAPREF(CAP_BCE))) tty->ocaps |= TTCF_BGER;
436 /* Motion capabilities. */
437 if (boolcap(CAPREF(CAP_XVPA))) caps->vpa = 0;
438 if (boolcap(CAPREF(CAP_XHPA))) caps->hpa = 0;
439 if (!caps->cub1) caps->cub1 = "\b";
440 if (!caps->cud1) caps->cud1 = "\n";
441 if ((caps->cuf || caps->cuf1) && (caps->cuu || caps->cuu1)) {
442 tty->ocaps |= TTCF_RELMV;
443 if (caps->vpa) tty->ocaps |= TTCF_MIXMV;
445 if (caps->cup || (caps->hpa && caps->vpa)) tty->ocaps |= TTCF_ABSMV;
447 /* Mode capabilities. */
448 if (caps->smam && caps->rmam) tty->ocaps |= TTMF_AUTOM;
449 if (caps->smir && caps->rmir) tty->ocaps |= TTMF_INS;
450 if (boolcap(CAPREF(CAP_AM))) {
451 tty->st.modes |= TTMF_AUTOM;
452 if (boolcap(CAPREF(CAP_XENL))) tty->ocaps |= TTCF_MMARG;
456 if (caps->csr) tty->ocaps |= TTCF_SCRGN;
457 if ((caps->ind || caps->indn) && (caps->ri || caps->rin))
458 tty->ocaps |= TTCF_SCROLL;
461 if (caps->ech) tty->ocaps |= TTCF_ERCH;
462 if (caps->el1) tty->ocaps |= TTCF_ERBOL;
463 if (caps->el) tty->ocaps |= TTCF_EREOL;
464 if (caps->ed) tty->ocaps |= TTCF_EREOD;
466 /* Insertion and deletion. */
467 if (caps->ich || caps->ich1) tty->ocaps |= TTCF_INSCH;
468 if (caps->il || caps->il1) tty->ocaps |= TTCF_INSLN;
469 if (caps->dch || caps->dch1) tty->ocaps |= TTCF_DELCH;
470 if (caps->dl || caps->dl1) tty->ocaps |= TTCF_DELLN;
473 static int caps_setattr(struct tty *tty, const struct tty_caps *caps,
474 const struct gprintf_ops *gops, void *go,
475 const struct tty_attr *a)
479 tty_clampattr(&aa, a, tty->acaps);
483 static int caps_setmodes(struct tty *tty,
484 const struct gprintf_ops *gops, void *go,
485 uint32 modes_bic, uint32 modes_xor);
486 static int caps_move(struct tty *tty,
487 const struct gprintf_ops *gops, void *go,
488 unsigned orig, int y, int x);
489 static int caps_repeat(struct tty *tty,
490 const struct gprintf_ops *gops, void *go,
492 static int caps_erase(struct tty *tty,
493 const struct gprintf_ops *gops, void *go,
495 static int caps_erch(struct tty *tty,
496 const struct gprintf_ops *gops, void *go,
498 static int caps_ins(struct tty *tty,
499 const struct gprintf_ops *gops, void *go,
500 unsigned f, unsigned n);
501 static int caps_del(struct tty *tty,
502 const struct gprintf_ops *gops, void *go,
503 unsigned f, unsigned n);
504 static int caps_setscrgn(struct tty *tty,
505 const struct gprintf_ops *gops, void *go,
506 unsigned y0, unsigned y1);
507 static int caps_scroll(struct tty *tty,
508 const struct gprintf_ops *gops, void *go,
514 /*----- That's all, folks -------------------------------------------------*/