| 1 | /* SPDX-License-Identifier: BSD-2-Clause */ |
| 2 | |
| 3 | /* Copyright (C) 2022-2026 Mintsuki and contributors. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright notice, |
| 9 | * this list of conditions and the following disclaimer. |
| 10 | * |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
| 12 | * this list of conditions and the following disclaimer in the documentation |
| 13 | * and/or other materials provided with the distribution. |
| 14 | * |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 25 | * POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | |
| 28 | #ifdef __cplusplus |
| 29 | #error "Please do not compile Flanterm as C++ code! Flanterm should be compiled as C99 or newer." |
| 30 | #endif |
| 31 | |
| 32 | #ifndef __STDC_VERSION__ |
| 33 | #error "Flanterm must be compiled as C99 or newer." |
| 34 | #endif |
| 35 | |
| 36 | #include <stdint.h> |
| 37 | #include <stddef.h> |
| 38 | #include <stdbool.h> |
| 39 | |
| 40 | #ifndef FLANTERM_IN_FLANTERM |
| 41 | #define FLANTERM_IN_FLANTERM |
| 42 | #endif |
| 43 | |
| 44 | #include "flanterm.h" |
| 45 | |
| 46 | // Tries to implement this standard for terminfo |
| 47 | // https://man7.org/linux/man-pages/man4/console_codes.4.html |
| 48 | |
| 49 | static const uint32_t col256[] = { |
| 50 | 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, |
| 51 | 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, |
| 52 | 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, |
| 53 | 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, |
| 54 | 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, |
| 55 | 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, |
| 56 | 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, |
| 57 | 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, |
| 58 | 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, |
| 59 | 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, |
| 60 | 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, |
| 61 | 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, |
| 62 | 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, |
| 63 | 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, |
| 64 | 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, |
| 65 | 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, |
| 66 | 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, |
| 67 | 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, |
| 68 | 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, |
| 69 | 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, |
| 70 | 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, |
| 71 | 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, |
| 72 | 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, |
| 73 | 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, |
| 74 | 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, |
| 75 | 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, |
| 76 | 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, |
| 77 | 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, |
| 78 | 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, |
| 79 | 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee |
| 80 | }; |
| 81 | |
| 82 | #define CHARSET_DEFAULT 0 |
| 83 | #define CHARSET_DEC_SPECIAL 1 |
| 84 | |
| 85 | void flanterm_context_reinit(struct flanterm_context *ctx) { |
| 86 | ctx->tab_size = 8; |
| 87 | ctx->autoflush = true; |
| 88 | ctx->cursor_enabled = true; |
| 89 | ctx->scroll_enabled = true; |
| 90 | ctx->wrap_enabled = true; |
| 91 | ctx->origin_mode = false; |
| 92 | ctx->control_sequence = false; |
| 93 | ctx->escape = false; |
| 94 | ctx->osc = false; |
| 95 | ctx->osc_escape = false; |
| 96 | ctx->rrr = false; |
| 97 | ctx->discard_next = false; |
| 98 | ctx->bold = false; |
| 99 | ctx->bg_bold = false; |
| 100 | ctx->reverse_video = false; |
| 101 | ctx->dec_private = false; |
| 102 | ctx->insert_mode = false; |
| 103 | ctx->csi_unhandled = false; |
| 104 | ctx->unicode_remaining = 0; |
| 105 | ctx->g_select = 0; |
| 106 | ctx->charsets[0] = CHARSET_DEFAULT; |
| 107 | ctx->charsets[1] = CHARSET_DEC_SPECIAL; |
| 108 | ctx->current_charset = 0; |
| 109 | ctx->escape_offset = 0; |
| 110 | ctx->esc_values_i = 0; |
| 111 | ctx->saved_cursor_x = 0; |
| 112 | ctx->saved_cursor_y = 0; |
| 113 | ctx->current_primary = (size_t)-1; |
| 114 | ctx->current_bg = (size_t)-1; |
| 115 | ctx->saved_state_charsets[0] = CHARSET_DEFAULT; |
| 116 | ctx->saved_state_charsets[1] = CHARSET_DEC_SPECIAL; |
| 117 | ctx->saved_state_wrap_enabled = true; |
| 118 | ctx->saved_state_current_primary = (size_t)-1; |
| 119 | ctx->saved_state_current_bg = (size_t)-1; |
| 120 | ctx->saved_state_scroll_top_margin = 0; |
| 121 | ctx->saved_state_scroll_bottom_margin = ctx->rows; |
| 122 | ctx->last_printed_char = ' '; |
| 123 | ctx->last_was_graphic = false; |
| 124 | ctx->scroll_top_margin = 0; |
| 125 | ctx->scroll_bottom_margin = ctx->rows; |
| 126 | } |
| 127 | |
| 128 | static void flanterm_putchar(struct flanterm_context *ctx, uint8_t c); |
| 129 | |
| 130 | void flanterm_write(struct flanterm_context *ctx, const char *buf, size_t count) { |
| 131 | for (size_t i = 0; i < count; i++) { |
| 132 | flanterm_putchar(ctx, c: buf[i]); |
| 133 | } |
| 134 | |
| 135 | if (ctx->autoflush) { |
| 136 | ctx->double_buffer_flush(ctx); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | static void sgr(struct flanterm_context *ctx) { |
| 141 | size_t i = 0; |
| 142 | |
| 143 | if (!ctx->esc_values_i) |
| 144 | goto def; |
| 145 | |
| 146 | for (; i < ctx->esc_values_i; i++) { |
| 147 | size_t offset; |
| 148 | |
| 149 | if (ctx->esc_values[i] == 0) { |
| 150 | def: |
| 151 | if (ctx->reverse_video) { |
| 152 | ctx->reverse_video = false; |
| 153 | ctx->swap_palette(ctx); |
| 154 | } |
| 155 | ctx->bold = false; |
| 156 | ctx->bg_bold = false; |
| 157 | ctx->current_primary = (size_t)-1; |
| 158 | ctx->current_bg = (size_t)-1; |
| 159 | ctx->set_text_bg_default(ctx); |
| 160 | ctx->set_text_fg_default(ctx); |
| 161 | continue; |
| 162 | } |
| 163 | |
| 164 | else if (ctx->esc_values[i] == 1) { |
| 165 | ctx->bold = true; |
| 166 | if (ctx->current_primary == (size_t)-2) { |
| 167 | // RGB/256-color; bold does not alter the colour |
| 168 | } else if (ctx->current_primary != (size_t)-1) { |
| 169 | if (!ctx->reverse_video) { |
| 170 | ctx->set_text_fg_bright(ctx, ctx->current_primary); |
| 171 | } else { |
| 172 | ctx->set_text_bg_bright(ctx, ctx->current_primary); |
| 173 | } |
| 174 | } else { |
| 175 | if (!ctx->reverse_video) { |
| 176 | ctx->set_text_fg_default_bright(ctx); |
| 177 | } else { |
| 178 | ctx->set_text_bg_default_bright(ctx); |
| 179 | } |
| 180 | } |
| 181 | continue; |
| 182 | } |
| 183 | |
| 184 | else if (ctx->esc_values[i] == 2 |
| 185 | || ctx->esc_values[i] == 3 |
| 186 | || ctx->esc_values[i] == 4 |
| 187 | || ctx->esc_values[i] == 8) { |
| 188 | continue; |
| 189 | } |
| 190 | |
| 191 | else if (ctx->esc_values[i] == 5) { |
| 192 | ctx->bg_bold = true; |
| 193 | if (ctx->current_bg == (size_t)-2) { |
| 194 | // RGB/256-color; bold does not alter the colour |
| 195 | } else if (ctx->current_bg != (size_t)-1) { |
| 196 | if (!ctx->reverse_video) { |
| 197 | ctx->set_text_bg_bright(ctx, ctx->current_bg); |
| 198 | } else { |
| 199 | ctx->set_text_fg_bright(ctx, ctx->current_bg); |
| 200 | } |
| 201 | } else { |
| 202 | if (!ctx->reverse_video) { |
| 203 | ctx->set_text_bg_default_bright(ctx); |
| 204 | } else { |
| 205 | ctx->set_text_fg_default_bright(ctx); |
| 206 | } |
| 207 | } |
| 208 | continue; |
| 209 | } |
| 210 | |
| 211 | else if (ctx->esc_values[i] == 22) { |
| 212 | ctx->bold = false; |
| 213 | if (ctx->current_primary == (size_t)-2) { |
| 214 | // RGB/256-color; unbold does not alter the colour |
| 215 | } else if (ctx->current_primary != (size_t)-1) { |
| 216 | if (!ctx->reverse_video) { |
| 217 | ctx->set_text_fg(ctx, ctx->current_primary); |
| 218 | } else { |
| 219 | ctx->set_text_bg(ctx, ctx->current_primary); |
| 220 | } |
| 221 | } else { |
| 222 | if (!ctx->reverse_video) { |
| 223 | ctx->set_text_fg_default(ctx); |
| 224 | } else { |
| 225 | ctx->set_text_bg_default(ctx); |
| 226 | } |
| 227 | } |
| 228 | continue; |
| 229 | } |
| 230 | |
| 231 | else if (ctx->esc_values[i] == 23 |
| 232 | || ctx->esc_values[i] == 24 |
| 233 | || ctx->esc_values[i] == 28) { |
| 234 | continue; |
| 235 | } |
| 236 | |
| 237 | else if (ctx->esc_values[i] == 25) { |
| 238 | ctx->bg_bold = false; |
| 239 | if (ctx->current_bg == (size_t)-2) { |
| 240 | // RGB/256-color; unbold does not alter the colour |
| 241 | } else if (ctx->current_bg != (size_t)-1) { |
| 242 | if (!ctx->reverse_video) { |
| 243 | ctx->set_text_bg(ctx, ctx->current_bg); |
| 244 | } else { |
| 245 | ctx->set_text_fg(ctx, ctx->current_bg); |
| 246 | } |
| 247 | } else { |
| 248 | if (!ctx->reverse_video) { |
| 249 | ctx->set_text_bg_default(ctx); |
| 250 | } else { |
| 251 | ctx->set_text_fg_default(ctx); |
| 252 | } |
| 253 | } |
| 254 | continue; |
| 255 | } |
| 256 | |
| 257 | else if (ctx->esc_values[i] >= 30 && ctx->esc_values[i] <= 37) { |
| 258 | offset = 30; |
| 259 | ctx->current_primary = ctx->esc_values[i] - offset; |
| 260 | |
| 261 | if (ctx->reverse_video) { |
| 262 | goto set_bg; |
| 263 | } |
| 264 | |
| 265 | set_fg: |
| 266 | if ((ctx->bold && !ctx->reverse_video) |
| 267 | || (ctx->bg_bold && ctx->reverse_video)) { |
| 268 | ctx->set_text_fg_bright(ctx, ctx->esc_values[i] - offset); |
| 269 | } else { |
| 270 | ctx->set_text_fg(ctx, ctx->esc_values[i] - offset); |
| 271 | } |
| 272 | continue; |
| 273 | } |
| 274 | |
| 275 | else if (ctx->esc_values[i] >= 40 && ctx->esc_values[i] <= 47) { |
| 276 | offset = 40; |
| 277 | ctx->current_bg = ctx->esc_values[i] - offset; |
| 278 | |
| 279 | if (ctx->reverse_video) { |
| 280 | goto set_fg; |
| 281 | } |
| 282 | |
| 283 | set_bg: |
| 284 | if ((ctx->bold && ctx->reverse_video) |
| 285 | || (ctx->bg_bold && !ctx->reverse_video)) { |
| 286 | ctx->set_text_bg_bright(ctx, ctx->esc_values[i] - offset); |
| 287 | } else { |
| 288 | ctx->set_text_bg(ctx, ctx->esc_values[i] - offset); |
| 289 | } |
| 290 | continue; |
| 291 | } |
| 292 | |
| 293 | else if (ctx->esc_values[i] >= 90 && ctx->esc_values[i] <= 97) { |
| 294 | offset = 90; |
| 295 | ctx->current_primary = ctx->esc_values[i] - offset; |
| 296 | |
| 297 | if (ctx->reverse_video) { |
| 298 | goto set_bg_bright; |
| 299 | } |
| 300 | |
| 301 | set_fg_bright: |
| 302 | ctx->set_text_fg_bright(ctx, ctx->esc_values[i] - offset); |
| 303 | continue; |
| 304 | } |
| 305 | |
| 306 | else if (ctx->esc_values[i] >= 100 && ctx->esc_values[i] <= 107) { |
| 307 | offset = 100; |
| 308 | ctx->current_bg = ctx->esc_values[i] - offset; |
| 309 | |
| 310 | if (ctx->reverse_video) { |
| 311 | goto set_fg_bright; |
| 312 | } |
| 313 | |
| 314 | set_bg_bright: |
| 315 | ctx->set_text_bg_bright(ctx, ctx->esc_values[i] - offset); |
| 316 | continue; |
| 317 | } |
| 318 | |
| 319 | else if (ctx->esc_values[i] == 39) { |
| 320 | ctx->current_primary = (size_t)-1; |
| 321 | |
| 322 | if (ctx->reverse_video) { |
| 323 | ctx->swap_palette(ctx); |
| 324 | } |
| 325 | |
| 326 | if (!ctx->bold) { |
| 327 | ctx->set_text_fg_default(ctx); |
| 328 | } else { |
| 329 | ctx->set_text_fg_default_bright(ctx); |
| 330 | } |
| 331 | |
| 332 | if (ctx->reverse_video) { |
| 333 | ctx->swap_palette(ctx); |
| 334 | } |
| 335 | |
| 336 | continue; |
| 337 | } |
| 338 | |
| 339 | else if (ctx->esc_values[i] == 49) { |
| 340 | ctx->current_bg = (size_t)-1; |
| 341 | |
| 342 | if (ctx->reverse_video) { |
| 343 | ctx->swap_palette(ctx); |
| 344 | } |
| 345 | |
| 346 | if (!ctx->bg_bold) { |
| 347 | ctx->set_text_bg_default(ctx); |
| 348 | } else { |
| 349 | ctx->set_text_bg_default_bright(ctx); |
| 350 | } |
| 351 | |
| 352 | if (ctx->reverse_video) { |
| 353 | ctx->swap_palette(ctx); |
| 354 | } |
| 355 | |
| 356 | continue; |
| 357 | } |
| 358 | |
| 359 | else if (ctx->esc_values[i] == 7) { |
| 360 | if (!ctx->reverse_video) { |
| 361 | ctx->reverse_video = true; |
| 362 | ctx->swap_palette(ctx); |
| 363 | } |
| 364 | continue; |
| 365 | } |
| 366 | |
| 367 | else if (ctx->esc_values[i] == 27) { |
| 368 | if (ctx->reverse_video) { |
| 369 | ctx->reverse_video = false; |
| 370 | ctx->swap_palette(ctx); |
| 371 | } |
| 372 | continue; |
| 373 | } |
| 374 | |
| 375 | // 256/RGB |
| 376 | else if (ctx->esc_values[i] == 38 || ctx->esc_values[i] == 48) { |
| 377 | bool fg = ctx->esc_values[i] == 38; |
| 378 | bool render_fg = ctx->reverse_video ? !fg : fg; |
| 379 | |
| 380 | i++; |
| 381 | if (i >= ctx->esc_values_i) { |
| 382 | break; |
| 383 | } |
| 384 | |
| 385 | switch (ctx->esc_values[i]) { |
| 386 | case 2: { // RGB |
| 387 | if (i + 3 >= ctx->esc_values_i) { |
| 388 | goto out; |
| 389 | } |
| 390 | |
| 391 | uint32_t rgb_value = 0; |
| 392 | |
| 393 | rgb_value |= (ctx->esc_values[i + 1] & 0xff) << 16; |
| 394 | rgb_value |= (ctx->esc_values[i + 2] & 0xff) << 8; |
| 395 | rgb_value |= (ctx->esc_values[i + 3] & 0xff); |
| 396 | |
| 397 | i += 3; |
| 398 | |
| 399 | if (fg) { |
| 400 | ctx->current_primary = (size_t)-2; |
| 401 | } else { |
| 402 | ctx->current_bg = (size_t)-2; |
| 403 | } |
| 404 | |
| 405 | (render_fg ? ctx->set_text_fg_rgb : ctx->set_text_bg_rgb)(ctx, rgb_value); |
| 406 | |
| 407 | break; |
| 408 | } |
| 409 | case 5: { // 256 colors |
| 410 | if (i + 1 >= ctx->esc_values_i) { |
| 411 | goto out; |
| 412 | } |
| 413 | |
| 414 | uint32_t col = ctx->esc_values[i + 1]; |
| 415 | |
| 416 | i++; |
| 417 | |
| 418 | if (col < 8) { |
| 419 | if (fg) { |
| 420 | ctx->current_primary = (size_t)-2; |
| 421 | } else { |
| 422 | ctx->current_bg = (size_t)-2; |
| 423 | } |
| 424 | (render_fg ? ctx->set_text_fg : ctx->set_text_bg)(ctx, col); |
| 425 | } else if (col < 16) { |
| 426 | if (fg) { |
| 427 | ctx->current_primary = (size_t)-2; |
| 428 | } else { |
| 429 | ctx->current_bg = (size_t)-2; |
| 430 | } |
| 431 | (render_fg ? ctx->set_text_fg_bright : ctx->set_text_bg_bright)(ctx, col - 8); |
| 432 | } else if (col < 256) { |
| 433 | if (fg) { |
| 434 | ctx->current_primary = (size_t)-2; |
| 435 | } else { |
| 436 | ctx->current_bg = (size_t)-2; |
| 437 | } |
| 438 | uint32_t rgb_value = col256[col - 16]; |
| 439 | (render_fg ? ctx->set_text_fg_rgb : ctx->set_text_bg_rgb)(ctx, rgb_value); |
| 440 | } |
| 441 | |
| 442 | break; |
| 443 | } |
| 444 | default: continue; |
| 445 | } |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | out:; |
| 450 | } |
| 451 | |
| 452 | static void save_state(struct flanterm_context *ctx); |
| 453 | static void restore_state(struct flanterm_context *ctx); |
| 454 | |
| 455 | static void dec_private_parse(struct flanterm_context *ctx, uint8_t c) { |
| 456 | ctx->dec_private = false; |
| 457 | |
| 458 | if (ctx->esc_values_i == 0) { |
| 459 | return; |
| 460 | } |
| 461 | |
| 462 | bool set; |
| 463 | |
| 464 | switch (c) { |
| 465 | case 'h': |
| 466 | set = true; break; |
| 467 | case 'l': |
| 468 | set = false; break; |
| 469 | default: |
| 470 | return; |
| 471 | } |
| 472 | |
| 473 | for (size_t i = 0; i < ctx->esc_values_i; i++) { |
| 474 | switch (ctx->esc_values[i]) { |
| 475 | case 6: |
| 476 | ctx->origin_mode = set; |
| 477 | ctx->set_cursor_pos(ctx, 0, set ? ctx->scroll_top_margin : 0); |
| 478 | break; |
| 479 | case 7: |
| 480 | ctx->wrap_enabled = set; |
| 481 | break; |
| 482 | case 25: |
| 483 | ctx->cursor_enabled = set; |
| 484 | break; |
| 485 | case 1049: |
| 486 | if (set) { |
| 487 | save_state(ctx); |
| 488 | ctx->scroll_top_margin = 0; |
| 489 | ctx->scroll_bottom_margin = ctx->rows; |
| 490 | ctx->clear(ctx, true); |
| 491 | } else { |
| 492 | ctx->clear(ctx, true); |
| 493 | restore_state(ctx); |
| 494 | } |
| 495 | break; |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | if (ctx->callback != NULL) { |
| 500 | ctx->callback(ctx, FLANTERM_CB_DEC, ctx->esc_values_i, (uintptr_t)ctx->esc_values, c); |
| 501 | } |
| 502 | } |
| 503 | |
| 504 | static void linux_private_parse(struct flanterm_context *ctx) { |
| 505 | if (ctx->esc_values_i == 0) { |
| 506 | return; |
| 507 | } |
| 508 | |
| 509 | if (ctx->callback != NULL) { |
| 510 | ctx->callback(ctx, FLANTERM_CB_LINUX, ctx->esc_values_i, (uintptr_t)ctx->esc_values, 0); |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | static void mode_toggle(struct flanterm_context *ctx, uint8_t c) { |
| 515 | if (ctx->esc_values_i == 0) { |
| 516 | return; |
| 517 | } |
| 518 | |
| 519 | bool set; |
| 520 | |
| 521 | switch (c) { |
| 522 | case 'h': |
| 523 | set = true; break; |
| 524 | case 'l': |
| 525 | set = false; break; |
| 526 | default: |
| 527 | return; |
| 528 | } |
| 529 | |
| 530 | switch (ctx->esc_values[0]) { |
| 531 | case 4: |
| 532 | ctx->insert_mode = set; return; |
| 533 | } |
| 534 | |
| 535 | if (ctx->callback != NULL) { |
| 536 | ctx->callback(ctx, FLANTERM_CB_MODE, ctx->esc_values_i, (uintptr_t)ctx->esc_values, c); |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | static void osc_finalize(struct flanterm_context *ctx) { |
| 541 | if (ctx->callback != NULL) { |
| 542 | // Parse the leading OSC number and skip past the semicolon. |
| 543 | uint64_t osc_num = 0; |
| 544 | size_t i = 0; |
| 545 | while (i < ctx->osc_buf_i && ctx->osc_buf[i] >= '0' && ctx->osc_buf[i] <= '9') { |
| 546 | osc_num = osc_num * 10 + (ctx->osc_buf[i] - '0'); |
| 547 | i++; |
| 548 | } |
| 549 | if (i < ctx->osc_buf_i && ctx->osc_buf[i] == ';') { |
| 550 | i++; |
| 551 | } |
| 552 | ctx->callback(ctx, FLANTERM_CB_OSC, osc_num, ctx->osc_buf_i - i, (uintptr_t)&ctx->osc_buf[i]); |
| 553 | } |
| 554 | } |
| 555 | |
| 556 | static bool osc_parse(struct flanterm_context *ctx, uint8_t c) { |
| 557 | // ESC \ terminates an OSC sequence cleanly |
| 558 | // but if ESC is followed by non-\, report failure from osc_parse and |
| 559 | // try parsing the character as another escape code |
| 560 | if (ctx->osc_escape) { |
| 561 | if (c == '\\') { |
| 562 | osc_finalize(ctx); |
| 563 | ctx->osc = false; |
| 564 | ctx->osc_escape = false; |
| 565 | ctx->escape = false; |
| 566 | return true; |
| 567 | } else { |
| 568 | ctx->osc_escape = false; |
| 569 | ctx->osc = false; |
| 570 | // escape stays true here |
| 571 | return false; |
| 572 | } |
| 573 | } |
| 574 | switch (c) { |
| 575 | case 0x1b: |
| 576 | ctx->osc_escape = true; |
| 577 | break; |
| 578 | // BEL is the other terminator |
| 579 | case '\a': |
| 580 | osc_finalize(ctx); |
| 581 | ctx->osc_escape = false; |
| 582 | ctx->osc = false; |
| 583 | ctx->escape = false; |
| 584 | break; |
| 585 | default: |
| 586 | if (ctx->osc_buf_i < sizeof(ctx->osc_buf)) { |
| 587 | ctx->osc_buf[ctx->osc_buf_i++] = c; |
| 588 | } |
| 589 | break; |
| 590 | } |
| 591 | return true; |
| 592 | } |
| 593 | |
| 594 | static bool execute_c0(struct flanterm_context *ctx, uint8_t c) { |
| 595 | size_t x, y; |
| 596 | switch (c) { |
| 597 | case '\a': |
| 598 | if (ctx->callback != NULL) { |
| 599 | ctx->callback(ctx, FLANTERM_CB_BELL, 0, 0, 0); |
| 600 | } |
| 601 | return true; |
| 602 | case '\b': |
| 603 | ctx->get_cursor_pos(ctx, &x, &y); |
| 604 | if (x > 0) { |
| 605 | ctx->set_cursor_pos(ctx, x - 1, y); |
| 606 | } |
| 607 | return true; |
| 608 | case '\t': |
| 609 | ctx->get_cursor_pos(ctx, &x, &y); |
| 610 | x = (x / ctx->tab_size + 1) * ctx->tab_size; |
| 611 | if (x >= ctx->cols) { |
| 612 | x = ctx->cols - 1; |
| 613 | } |
| 614 | ctx->set_cursor_pos(ctx, x, y); |
| 615 | return true; |
| 616 | case 0x0b: |
| 617 | case 0x0c: |
| 618 | case '\n': |
| 619 | ctx->get_cursor_pos(ctx, &x, &y); |
| 620 | if (y == ctx->scroll_bottom_margin - 1) { |
| 621 | ctx->scroll(ctx); |
| 622 | ctx->set_cursor_pos(ctx, x, y); |
| 623 | } else if (y < ctx->rows - 1) { |
| 624 | ctx->set_cursor_pos(ctx, x, y + 1); |
| 625 | } |
| 626 | return true; |
| 627 | case '\r': |
| 628 | ctx->get_cursor_pos(ctx, &x, &y); |
| 629 | ctx->set_cursor_pos(ctx, 0, y); |
| 630 | return true; |
| 631 | case 14: |
| 632 | ctx->current_charset = 1; |
| 633 | return true; |
| 634 | case 15: |
| 635 | ctx->current_charset = 0; |
| 636 | return true; |
| 637 | default: |
| 638 | return false; |
| 639 | } |
| 640 | } |
| 641 | |
| 642 | static void control_sequence_parse(struct flanterm_context *ctx, uint8_t c) { |
| 643 | if (ctx->escape_offset == 2) { |
| 644 | switch (c) { |
| 645 | case '[': |
| 646 | ctx->discard_next = true; |
| 647 | goto cleanup; |
| 648 | case '?': |
| 649 | ctx->dec_private = true; |
| 650 | return; |
| 651 | } |
| 652 | } |
| 653 | |
| 654 | if (c < 0x20 && c != 0x1b) { |
| 655 | execute_c0(ctx, c); |
| 656 | return; |
| 657 | } |
| 658 | |
| 659 | if (c >= '0' && c <= '9') { |
| 660 | if (ctx->esc_values_i == FLANTERM_MAX_ESC_VALUES) { |
| 661 | return; |
| 662 | } |
| 663 | ctx->rrr = true; |
| 664 | if (ctx->esc_values[ctx->esc_values_i] > UINT32_MAX / 10) { |
| 665 | return; |
| 666 | } |
| 667 | ctx->esc_values[ctx->esc_values_i] *= 10; |
| 668 | uint32_t digit = c - '0'; |
| 669 | if (ctx->esc_values[ctx->esc_values_i] > UINT32_MAX - digit) { |
| 670 | ctx->esc_values[ctx->esc_values_i] = UINT32_MAX; |
| 671 | return; |
| 672 | } |
| 673 | ctx->esc_values[ctx->esc_values_i] += digit; |
| 674 | return; |
| 675 | } |
| 676 | |
| 677 | if (ctx->rrr == true) { |
| 678 | ctx->esc_values_i++; |
| 679 | ctx->rrr = false; |
| 680 | if (c == ';') |
| 681 | return; |
| 682 | } else if (c == ';') { |
| 683 | if (ctx->esc_values_i == FLANTERM_MAX_ESC_VALUES) { |
| 684 | return; |
| 685 | } |
| 686 | ctx->esc_values[ctx->esc_values_i] = 0; |
| 687 | ctx->esc_values_i++; |
| 688 | return; |
| 689 | } |
| 690 | |
| 691 | size_t esc_default; |
| 692 | switch (c) { |
| 693 | case 'J': case 'K': case 'q': case 'm': case 'c': case ']': |
| 694 | esc_default = 0; break; |
| 695 | default: |
| 696 | esc_default = 1; break; |
| 697 | } |
| 698 | |
| 699 | for (size_t i = ctx->esc_values_i; i < FLANTERM_MAX_ESC_VALUES; i++) { |
| 700 | ctx->esc_values[i] = esc_default; |
| 701 | } |
| 702 | |
| 703 | if (esc_default != 0) { |
| 704 | for (size_t i = 0; i < ctx->esc_values_i; i++) { |
| 705 | if (ctx->esc_values[i] == 0) { |
| 706 | ctx->esc_values[i] = esc_default; |
| 707 | } |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | if (ctx->dec_private == true) { |
| 712 | dec_private_parse(ctx, c); |
| 713 | goto cleanup; |
| 714 | } |
| 715 | |
| 716 | // CSI sequences are terminated by a byte in [0x40,0x7E] |
| 717 | // so skip all bytes until the terminator byte |
| 718 | if (ctx->csi_unhandled) { |
| 719 | if (c == 0x1B) { |
| 720 | ctx->csi_unhandled = false; |
| 721 | ctx->control_sequence = false; |
| 722 | ctx->escape_offset = 0; |
| 723 | return; |
| 724 | } |
| 725 | if (c >= 0x40 && c <= 0x7E) { |
| 726 | ctx->csi_unhandled = false; |
| 727 | goto cleanup; |
| 728 | } |
| 729 | return; |
| 730 | } |
| 731 | |
| 732 | bool r = ctx->scroll_enabled; |
| 733 | ctx->scroll_enabled = false; |
| 734 | bool saved_wrap = ctx->wrap_enabled; |
| 735 | ctx->wrap_enabled = true; |
| 736 | size_t x, y; |
| 737 | ctx->get_cursor_pos(ctx, &x, &y); |
| 738 | |
| 739 | switch (c) { |
| 740 | // ESC aborts the current CSI and starts a new escape sequence |
| 741 | case 0x1B: |
| 742 | ctx->scroll_enabled = r; |
| 743 | ctx->wrap_enabled = saved_wrap; |
| 744 | ctx->control_sequence = false; |
| 745 | ctx->escape_offset = 0; |
| 746 | return; |
| 747 | case 'F': |
| 748 | x = 0; |
| 749 | // FALLTHRU |
| 750 | case 'A': { |
| 751 | if (ctx->esc_values[0] > y) |
| 752 | ctx->esc_values[0] = y; |
| 753 | size_t dest_y = y - ctx->esc_values[0]; |
| 754 | size_t min_y = ctx->origin_mode ? ctx->scroll_top_margin : 0; |
| 755 | if (dest_y < min_y) { |
| 756 | dest_y = min_y; |
| 757 | } |
| 758 | ctx->set_cursor_pos(ctx, x, dest_y); |
| 759 | break; |
| 760 | } |
| 761 | case 'E': |
| 762 | x = 0; |
| 763 | // FALLTHRU |
| 764 | case 'e': |
| 765 | case 'B': { |
| 766 | if (y + ctx->esc_values[0] > ctx->rows - 1) |
| 767 | ctx->esc_values[0] = (ctx->rows - 1) - y; |
| 768 | size_t dest_y = y + ctx->esc_values[0]; |
| 769 | size_t max_y = ctx->origin_mode ? ctx->scroll_bottom_margin : ctx->rows; |
| 770 | if (dest_y >= max_y) { |
| 771 | dest_y = max_y - 1; |
| 772 | } |
| 773 | ctx->set_cursor_pos(ctx, x, dest_y); |
| 774 | break; |
| 775 | } |
| 776 | case 'a': |
| 777 | case 'C': |
| 778 | if (x + ctx->esc_values[0] > ctx->cols - 1) |
| 779 | ctx->esc_values[0] = (ctx->cols - 1) - x; |
| 780 | ctx->set_cursor_pos(ctx, x + ctx->esc_values[0], y); |
| 781 | break; |
| 782 | case 'D': |
| 783 | if (ctx->esc_values[0] > x) |
| 784 | ctx->esc_values[0] = x; |
| 785 | ctx->set_cursor_pos(ctx, x - ctx->esc_values[0], y); |
| 786 | break; |
| 787 | case 'c': |
| 788 | if (ctx->callback != NULL) { |
| 789 | ctx->callback(ctx, FLANTERM_CB_PRIVATE_ID, 0, 0, 0); |
| 790 | } |
| 791 | break; |
| 792 | case 'd': { |
| 793 | if (ctx->esc_values[0] != 0) { |
| 794 | ctx->esc_values[0]--; |
| 795 | } |
| 796 | size_t max_row = ctx->rows; |
| 797 | size_t row_offset = 0; |
| 798 | if (ctx->origin_mode) { |
| 799 | max_row = ctx->scroll_bottom_margin - ctx->scroll_top_margin; |
| 800 | row_offset = ctx->scroll_top_margin; |
| 801 | } |
| 802 | if (ctx->esc_values[0] >= max_row) |
| 803 | ctx->esc_values[0] = max_row - 1; |
| 804 | ctx->set_cursor_pos(ctx, x, ctx->esc_values[0] + row_offset); |
| 805 | break; |
| 806 | } |
| 807 | case 'G': |
| 808 | case '`': |
| 809 | if (ctx->esc_values[0] != 0) { |
| 810 | ctx->esc_values[0]--; |
| 811 | } |
| 812 | if (ctx->esc_values[0] >= ctx->cols) |
| 813 | ctx->esc_values[0] = ctx->cols - 1; |
| 814 | ctx->set_cursor_pos(ctx, ctx->esc_values[0], y); |
| 815 | break; |
| 816 | case 'H': |
| 817 | case 'f': { |
| 818 | if (ctx->esc_values[0] != 0) { |
| 819 | ctx->esc_values[0]--; |
| 820 | } |
| 821 | if (ctx->esc_values[1] != 0) { |
| 822 | ctx->esc_values[1]--; |
| 823 | } |
| 824 | size_t max_row = ctx->rows; |
| 825 | size_t row_offset = 0; |
| 826 | if (ctx->origin_mode) { |
| 827 | max_row = ctx->scroll_bottom_margin - ctx->scroll_top_margin; |
| 828 | row_offset = ctx->scroll_top_margin; |
| 829 | } |
| 830 | if (ctx->esc_values[1] >= ctx->cols) { |
| 831 | ctx->esc_values[1] = ctx->cols - 1; |
| 832 | } |
| 833 | if (ctx->esc_values[0] >= max_row) { |
| 834 | ctx->esc_values[0] = max_row - 1; |
| 835 | } |
| 836 | ctx->set_cursor_pos(ctx, ctx->esc_values[1], ctx->esc_values[0] + row_offset); |
| 837 | break; |
| 838 | } |
| 839 | case 'M': { |
| 840 | if (y < ctx->scroll_top_margin || y >= ctx->scroll_bottom_margin) { |
| 841 | break; |
| 842 | } |
| 843 | size_t old_scroll_top_margin = ctx->scroll_top_margin; |
| 844 | ctx->scroll_top_margin = y; |
| 845 | size_t max_count = ctx->scroll_bottom_margin - y; |
| 846 | size_t count = ctx->esc_values[0] > max_count ? max_count : ctx->esc_values[0]; |
| 847 | for (size_t i = 0; i < count; i++) { |
| 848 | ctx->scroll(ctx); |
| 849 | } |
| 850 | ctx->scroll_top_margin = old_scroll_top_margin; |
| 851 | break; |
| 852 | } |
| 853 | case 'L': { |
| 854 | if (y < ctx->scroll_top_margin || y >= ctx->scroll_bottom_margin) { |
| 855 | break; |
| 856 | } |
| 857 | size_t old_scroll_top_margin = ctx->scroll_top_margin; |
| 858 | ctx->scroll_top_margin = y; |
| 859 | size_t max_count = ctx->scroll_bottom_margin - y; |
| 860 | size_t count = ctx->esc_values[0] > max_count ? max_count : ctx->esc_values[0]; |
| 861 | for (size_t i = 0; i < count; i++) { |
| 862 | ctx->revscroll(ctx); |
| 863 | } |
| 864 | ctx->scroll_top_margin = old_scroll_top_margin; |
| 865 | break; |
| 866 | } |
| 867 | case 'n': |
| 868 | switch (ctx->esc_values[0]) { |
| 869 | case 5: |
| 870 | if (ctx->callback != NULL) { |
| 871 | ctx->callback(ctx, FLANTERM_CB_STATUS_REPORT, 0, 0, 0); |
| 872 | } |
| 873 | break; |
| 874 | case 6: |
| 875 | if (ctx->callback != NULL) { |
| 876 | size_t report_y = ctx->origin_mode && y >= ctx->scroll_top_margin |
| 877 | ? y - ctx->scroll_top_margin : y; |
| 878 | ctx->callback(ctx, FLANTERM_CB_POS_REPORT, x + 1, report_y + 1, 0); |
| 879 | } |
| 880 | break; |
| 881 | } |
| 882 | break; |
| 883 | case 'q': |
| 884 | if (ctx->callback != NULL) { |
| 885 | ctx->callback(ctx, FLANTERM_CB_KBD_LEDS, ctx->esc_values[0], 0, 0); |
| 886 | } |
| 887 | break; |
| 888 | case 'J': |
| 889 | switch (ctx->esc_values[0]) { |
| 890 | case 0: { |
| 891 | // Erase from cursor to end: clear rest of current line, |
| 892 | // then clear full lines below, using explicit cursor |
| 893 | // positioning to avoid scroll region wrapping limits. |
| 894 | ctx->set_cursor_pos(ctx, x, y); |
| 895 | for (size_t xc = x; xc < ctx->cols; xc++) { |
| 896 | ctx->raw_putchar(ctx, ' '); |
| 897 | } |
| 898 | for (size_t yc = y + 1; yc < ctx->rows; yc++) { |
| 899 | ctx->set_cursor_pos(ctx, 0, yc); |
| 900 | for (size_t xc = 0; xc < ctx->cols; xc++) { |
| 901 | ctx->raw_putchar(ctx, ' '); |
| 902 | } |
| 903 | } |
| 904 | ctx->set_cursor_pos(ctx, x, y); |
| 905 | break; |
| 906 | } |
| 907 | case 1: { |
| 908 | // Erase from start to cursor: clear full lines above, |
| 909 | // then clear current line up to and including cursor. |
| 910 | for (size_t yc = 0; yc < y; yc++) { |
| 911 | ctx->set_cursor_pos(ctx, 0, yc); |
| 912 | for (size_t xc = 0; xc < ctx->cols; xc++) { |
| 913 | ctx->raw_putchar(ctx, ' '); |
| 914 | } |
| 915 | } |
| 916 | ctx->set_cursor_pos(ctx, 0, y); |
| 917 | for (size_t xc = 0; xc <= x; xc++) { |
| 918 | ctx->raw_putchar(ctx, ' '); |
| 919 | } |
| 920 | ctx->set_cursor_pos(ctx, x, y); |
| 921 | break; |
| 922 | } |
| 923 | case 2: |
| 924 | case 3: |
| 925 | ctx->clear(ctx, false); |
| 926 | break; |
| 927 | } |
| 928 | break; |
| 929 | case '@': { |
| 930 | size_t n = ctx->esc_values[0]; |
| 931 | if (n == 0) { |
| 932 | break; |
| 933 | } |
| 934 | if (n > ctx->cols - x) { |
| 935 | n = ctx->cols - x; |
| 936 | } |
| 937 | for (size_t i = ctx->cols - 1; i >= x + n; i--) { |
| 938 | ctx->move_character(ctx, i, y, i - n, y); |
| 939 | } |
| 940 | ctx->set_cursor_pos(ctx, x, y); |
| 941 | for (size_t i = 0; i < n; i++) { |
| 942 | ctx->raw_putchar(ctx, ' '); |
| 943 | } |
| 944 | ctx->set_cursor_pos(ctx, x, y); |
| 945 | break; |
| 946 | } |
| 947 | case 'P': |
| 948 | if (ctx->esc_values[0] > ctx->cols - x) |
| 949 | ctx->esc_values[0] = ctx->cols - x; |
| 950 | for (size_t i = x + ctx->esc_values[0]; i < ctx->cols; i++) |
| 951 | ctx->move_character(ctx, i - ctx->esc_values[0], y, i, y); |
| 952 | ctx->set_cursor_pos(ctx, ctx->cols - ctx->esc_values[0], y); |
| 953 | // FALLTHRU |
| 954 | case 'X': { |
| 955 | size_t cx, cy; |
| 956 | ctx->get_cursor_pos(ctx, &cx, &cy); |
| 957 | ctx->set_cursor_pos(ctx, cx, cy); |
| 958 | size_t remaining = ctx->cols - cx; |
| 959 | size_t count = ctx->esc_values[0] > remaining ? remaining : ctx->esc_values[0]; |
| 960 | for (size_t i = 0; i < count; i++) |
| 961 | ctx->raw_putchar(ctx, ' '); |
| 962 | ctx->set_cursor_pos(ctx, x, y); |
| 963 | break; |
| 964 | } |
| 965 | case 'm': |
| 966 | sgr(ctx); |
| 967 | break; |
| 968 | case 's': |
| 969 | ctx->get_cursor_pos(ctx, &ctx->saved_cursor_x, &ctx->saved_cursor_y); |
| 970 | break; |
| 971 | case 'u': |
| 972 | ctx->set_cursor_pos(ctx, ctx->saved_cursor_x, ctx->saved_cursor_y); |
| 973 | break; |
| 974 | case 'K': |
| 975 | switch (ctx->esc_values[0]) { |
| 976 | case 0: { |
| 977 | ctx->set_cursor_pos(ctx, x, y); |
| 978 | for (size_t i = x; i < ctx->cols; i++) |
| 979 | ctx->raw_putchar(ctx, ' '); |
| 980 | ctx->set_cursor_pos(ctx, x, y); |
| 981 | break; |
| 982 | } |
| 983 | case 1: { |
| 984 | ctx->set_cursor_pos(ctx, 0, y); |
| 985 | for (size_t i = 0; i <= x; i++) |
| 986 | ctx->raw_putchar(ctx, ' '); |
| 987 | ctx->set_cursor_pos(ctx, x, y); |
| 988 | break; |
| 989 | } |
| 990 | case 2: { |
| 991 | ctx->set_cursor_pos(ctx, 0, y); |
| 992 | for (size_t i = 0; i < ctx->cols; i++) |
| 993 | ctx->raw_putchar(ctx, ' '); |
| 994 | ctx->set_cursor_pos(ctx, x, y); |
| 995 | break; |
| 996 | } |
| 997 | } |
| 998 | break; |
| 999 | case 'r': |
| 1000 | ctx->scroll_top_margin = 0; |
| 1001 | ctx->scroll_bottom_margin = ctx->rows; |
| 1002 | if (ctx->esc_values_i > 0) { |
| 1003 | ctx->scroll_top_margin = ctx->esc_values[0] - 1; |
| 1004 | } |
| 1005 | if (ctx->esc_values_i > 1) { |
| 1006 | ctx->scroll_bottom_margin = ctx->esc_values[1]; |
| 1007 | } |
| 1008 | if (ctx->scroll_top_margin >= ctx->rows |
| 1009 | || ctx->scroll_bottom_margin > ctx->rows |
| 1010 | || ctx->scroll_top_margin >= (ctx->scroll_bottom_margin - 1)) { |
| 1011 | ctx->scroll_top_margin = 0; |
| 1012 | ctx->scroll_bottom_margin = ctx->rows; |
| 1013 | } |
| 1014 | ctx->set_cursor_pos(ctx, 0, ctx->origin_mode ? ctx->scroll_top_margin : 0); |
| 1015 | break; |
| 1016 | case 'l': |
| 1017 | case 'h': |
| 1018 | mode_toggle(ctx, c); |
| 1019 | break; |
| 1020 | case 'S': { |
| 1021 | size_t region = ctx->scroll_bottom_margin - ctx->scroll_top_margin; |
| 1022 | size_t count = ctx->esc_values[0] > region ? region : ctx->esc_values[0]; |
| 1023 | for (size_t i = 0; i < count; i++) { |
| 1024 | ctx->scroll(ctx); |
| 1025 | } |
| 1026 | break; |
| 1027 | } |
| 1028 | case 'T': { |
| 1029 | size_t region = ctx->scroll_bottom_margin - ctx->scroll_top_margin; |
| 1030 | size_t count = ctx->esc_values[0] > region ? region : ctx->esc_values[0]; |
| 1031 | for (size_t i = 0; i < count; i++) { |
| 1032 | ctx->revscroll(ctx); |
| 1033 | } |
| 1034 | break; |
| 1035 | } |
| 1036 | case 'b': { |
| 1037 | if (!ctx->last_was_graphic) { |
| 1038 | break; |
| 1039 | } |
| 1040 | ctx->scroll_enabled = r; |
| 1041 | ctx->wrap_enabled = saved_wrap; |
| 1042 | size_t count = ctx->esc_values[0] > 65535 ? 65535 : ctx->esc_values[0]; |
| 1043 | for (size_t i = 0; i < count; i++) { |
| 1044 | if (ctx->insert_mode == true) { |
| 1045 | size_t ix, iy; |
| 1046 | ctx->get_cursor_pos(ctx, &ix, &iy); |
| 1047 | for (size_t j = ctx->cols - 1; j > ix; j--) { |
| 1048 | ctx->move_character(ctx, j, iy, j - 1, iy); |
| 1049 | } |
| 1050 | } |
| 1051 | ctx->raw_putchar(ctx, ctx->last_printed_char); |
| 1052 | } |
| 1053 | break; |
| 1054 | } |
| 1055 | case ']': |
| 1056 | linux_private_parse(ctx); |
| 1057 | break; |
| 1058 | default: |
| 1059 | if (c >= 0x40 && c <= 0x7E) { |
| 1060 | break; |
| 1061 | } |
| 1062 | ctx->scroll_enabled = r; |
| 1063 | ctx->wrap_enabled = saved_wrap; |
| 1064 | ctx->csi_unhandled = true; |
| 1065 | return; |
| 1066 | } |
| 1067 | |
| 1068 | ctx->scroll_enabled = r; |
| 1069 | ctx->wrap_enabled = saved_wrap; |
| 1070 | |
| 1071 | cleanup: |
| 1072 | ctx->control_sequence = false; |
| 1073 | ctx->escape = false; |
| 1074 | } |
| 1075 | |
| 1076 | static void restore_state(struct flanterm_context *ctx) { |
| 1077 | ctx->bold = ctx->saved_state_bold; |
| 1078 | ctx->bg_bold = ctx->saved_state_bg_bold; |
| 1079 | ctx->reverse_video = ctx->saved_state_reverse_video; |
| 1080 | ctx->origin_mode = ctx->saved_state_origin_mode; |
| 1081 | ctx->wrap_enabled = ctx->saved_state_wrap_enabled; |
| 1082 | ctx->current_charset = ctx->saved_state_current_charset; |
| 1083 | ctx->charsets[0] = ctx->saved_state_charsets[0]; |
| 1084 | ctx->charsets[1] = ctx->saved_state_charsets[1]; |
| 1085 | ctx->current_primary = ctx->saved_state_current_primary; |
| 1086 | ctx->current_bg = ctx->saved_state_current_bg; |
| 1087 | ctx->scroll_top_margin = ctx->saved_state_scroll_top_margin; |
| 1088 | ctx->scroll_bottom_margin = ctx->saved_state_scroll_bottom_margin; |
| 1089 | |
| 1090 | ctx->restore_state(ctx); |
| 1091 | } |
| 1092 | |
| 1093 | static void save_state(struct flanterm_context *ctx) { |
| 1094 | ctx->save_state(ctx); |
| 1095 | |
| 1096 | ctx->saved_state_bold = ctx->bold; |
| 1097 | ctx->saved_state_bg_bold = ctx->bg_bold; |
| 1098 | ctx->saved_state_reverse_video = ctx->reverse_video; |
| 1099 | ctx->saved_state_origin_mode = ctx->origin_mode; |
| 1100 | ctx->saved_state_wrap_enabled = ctx->wrap_enabled; |
| 1101 | ctx->saved_state_current_charset = ctx->current_charset; |
| 1102 | ctx->saved_state_charsets[0] = ctx->charsets[0]; |
| 1103 | ctx->saved_state_charsets[1] = ctx->charsets[1]; |
| 1104 | ctx->saved_state_current_primary = ctx->current_primary; |
| 1105 | ctx->saved_state_current_bg = ctx->current_bg; |
| 1106 | ctx->saved_state_scroll_top_margin = ctx->scroll_top_margin; |
| 1107 | ctx->saved_state_scroll_bottom_margin = ctx->scroll_bottom_margin; |
| 1108 | } |
| 1109 | |
| 1110 | static void escape_parse(struct flanterm_context *ctx, uint8_t c) { |
| 1111 | ctx->escape_offset++; |
| 1112 | |
| 1113 | if (ctx->osc == true) { |
| 1114 | // ESC \ is one of the two possible terminators of OSC sequences, |
| 1115 | // so osc_parse consumes ESC. |
| 1116 | // If it is then followed by \ it cleans correctly, |
| 1117 | // otherwise it returns false, and it tries parsing it as another escape sequence |
| 1118 | if (osc_parse(ctx, c)) { |
| 1119 | return; |
| 1120 | } |
| 1121 | // OSC aborted by ESC + non-backslash; reset offset for new sequence |
| 1122 | ctx->escape_offset = 1; |
| 1123 | } |
| 1124 | |
| 1125 | if (ctx->control_sequence == true) { |
| 1126 | control_sequence_parse(ctx, c); |
| 1127 | return; |
| 1128 | } |
| 1129 | |
| 1130 | size_t x, y; |
| 1131 | ctx->get_cursor_pos(ctx, &x, &y); |
| 1132 | |
| 1133 | switch (c) { |
| 1134 | case 0x1b: |
| 1135 | ctx->escape_offset = 0; |
| 1136 | return; |
| 1137 | case ']': |
| 1138 | ctx->osc_escape = false; |
| 1139 | ctx->osc = true; |
| 1140 | ctx->osc_buf_i = 0; |
| 1141 | return; |
| 1142 | case '[': |
| 1143 | for (size_t i = 0; i < FLANTERM_MAX_ESC_VALUES; i++) |
| 1144 | ctx->esc_values[i] = 0; |
| 1145 | ctx->esc_values_i = 0; |
| 1146 | ctx->rrr = false; |
| 1147 | ctx->csi_unhandled = false; |
| 1148 | ctx->control_sequence = true; |
| 1149 | return; |
| 1150 | case '7': |
| 1151 | save_state(ctx); |
| 1152 | break; |
| 1153 | case '8': |
| 1154 | restore_state(ctx); |
| 1155 | break; |
| 1156 | case 'c': |
| 1157 | if (ctx->reverse_video) { |
| 1158 | ctx->swap_palette(ctx); |
| 1159 | } |
| 1160 | flanterm_context_reinit(ctx); |
| 1161 | ctx->set_text_bg_default(ctx); |
| 1162 | ctx->set_text_fg_default(ctx); |
| 1163 | ctx->clear(ctx, true); |
| 1164 | save_state(ctx); |
| 1165 | break; |
| 1166 | case 'D': |
| 1167 | if (y == ctx->scroll_bottom_margin - 1) { |
| 1168 | ctx->scroll(ctx); |
| 1169 | ctx->set_cursor_pos(ctx, x, y); |
| 1170 | } else if (y < ctx->rows - 1) { |
| 1171 | ctx->set_cursor_pos(ctx, x, y + 1); |
| 1172 | } |
| 1173 | break; |
| 1174 | case 'E': |
| 1175 | if (y == ctx->scroll_bottom_margin - 1) { |
| 1176 | ctx->scroll(ctx); |
| 1177 | ctx->set_cursor_pos(ctx, 0, y); |
| 1178 | } else if (y < ctx->rows - 1) { |
| 1179 | ctx->set_cursor_pos(ctx, 0, y + 1); |
| 1180 | } else { |
| 1181 | ctx->set_cursor_pos(ctx, 0, y); |
| 1182 | } |
| 1183 | break; |
| 1184 | case 'M': |
| 1185 | // "Reverse linefeed" |
| 1186 | if (y == ctx->scroll_top_margin) { |
| 1187 | ctx->revscroll(ctx); |
| 1188 | ctx->set_cursor_pos(ctx, x, y); |
| 1189 | } else if (y > 0) { |
| 1190 | ctx->set_cursor_pos(ctx, x, y - 1); |
| 1191 | } |
| 1192 | break; |
| 1193 | case 'Z': |
| 1194 | if (ctx->callback != NULL) { |
| 1195 | ctx->callback(ctx, FLANTERM_CB_PRIVATE_ID, 0, 0, 0); |
| 1196 | } |
| 1197 | break; |
| 1198 | case '(': |
| 1199 | case ')': |
| 1200 | ctx->g_select = c - '\''; |
| 1201 | break; |
| 1202 | } |
| 1203 | |
| 1204 | ctx->escape = false; |
| 1205 | } |
| 1206 | |
| 1207 | static bool dec_special_print(struct flanterm_context *ctx, uint8_t c) { |
| 1208 | #define FLANTERM_DEC_SPCL_PRN(C) ctx->last_printed_char = (C); ctx->last_was_graphic = true; ctx->raw_putchar(ctx, (C)); return true; |
| 1209 | switch (c) { |
| 1210 | case '`': FLANTERM_DEC_SPCL_PRN(0x04) |
| 1211 | case '0': FLANTERM_DEC_SPCL_PRN(0xdb) |
| 1212 | case '-': FLANTERM_DEC_SPCL_PRN(0x18) |
| 1213 | case ',': FLANTERM_DEC_SPCL_PRN(0x1b) |
| 1214 | case '.': FLANTERM_DEC_SPCL_PRN(0x19) |
| 1215 | case 'a': FLANTERM_DEC_SPCL_PRN(0xb1) |
| 1216 | case 'f': FLANTERM_DEC_SPCL_PRN(0xf8) |
| 1217 | case 'g': FLANTERM_DEC_SPCL_PRN(0xf1) |
| 1218 | case 'h': FLANTERM_DEC_SPCL_PRN(0xb0) |
| 1219 | case 'j': FLANTERM_DEC_SPCL_PRN(0xd9) |
| 1220 | case 'k': FLANTERM_DEC_SPCL_PRN(0xbf) |
| 1221 | case 'l': FLANTERM_DEC_SPCL_PRN(0xda) |
| 1222 | case 'm': FLANTERM_DEC_SPCL_PRN(0xc0) |
| 1223 | case 'n': FLANTERM_DEC_SPCL_PRN(0xc5) |
| 1224 | case 'q': FLANTERM_DEC_SPCL_PRN(0xc4) |
| 1225 | case 's': FLANTERM_DEC_SPCL_PRN(0x5f) |
| 1226 | case 't': FLANTERM_DEC_SPCL_PRN(0xc3) |
| 1227 | case 'u': FLANTERM_DEC_SPCL_PRN(0xb4) |
| 1228 | case 'v': FLANTERM_DEC_SPCL_PRN(0xc1) |
| 1229 | case 'w': FLANTERM_DEC_SPCL_PRN(0xc2) |
| 1230 | case 'x': FLANTERM_DEC_SPCL_PRN(0xb3) |
| 1231 | case 'y': FLANTERM_DEC_SPCL_PRN(0xf3) |
| 1232 | case 'z': FLANTERM_DEC_SPCL_PRN(0xf2) |
| 1233 | case '~': FLANTERM_DEC_SPCL_PRN(0xfa) |
| 1234 | case '_': FLANTERM_DEC_SPCL_PRN(0xff) |
| 1235 | case '+': FLANTERM_DEC_SPCL_PRN(0x1a) |
| 1236 | case '{': FLANTERM_DEC_SPCL_PRN(0xe3) |
| 1237 | case '}': FLANTERM_DEC_SPCL_PRN(0x9c) |
| 1238 | } |
| 1239 | #undef FLANTERM_DEC_SPCL_PRN |
| 1240 | |
| 1241 | return false; |
| 1242 | } |
| 1243 | |
| 1244 | // Following wcwidth related code inherited from: |
| 1245 | // https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c |
| 1246 | |
| 1247 | struct interval { |
| 1248 | uint32_t first; |
| 1249 | uint32_t last; |
| 1250 | }; |
| 1251 | |
| 1252 | /* auxiliary function for binary search in interval table */ |
| 1253 | static int bisearch(uint32_t ucs, const struct interval *table, int max) { |
| 1254 | int min = 0; |
| 1255 | int mid; |
| 1256 | |
| 1257 | if (ucs < table[0].first || ucs > table[max].last) |
| 1258 | return 0; |
| 1259 | while (max >= min) { |
| 1260 | mid = (min + max) / 2; |
| 1261 | if (ucs > table[mid].last) |
| 1262 | min = mid + 1; |
| 1263 | else if (ucs < table[mid].first) |
| 1264 | max = mid - 1; |
| 1265 | else |
| 1266 | return 1; |
| 1267 | } |
| 1268 | |
| 1269 | return 0; |
| 1270 | } |
| 1271 | |
| 1272 | static int mk_wcwidth(uint32_t ucs) { |
| 1273 | /* sorted list of non-overlapping intervals of zero-width characters */ |
| 1274 | /* Unicode 17.0.0 */ |
| 1275 | static const struct interval combining[] = { |
| 1276 | { 0x0300, 0x036F }, { 0x0483, 0x0489 }, { 0x0591, 0x05BD }, |
| 1277 | { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, |
| 1278 | { 0x05C7, 0x05C7 }, { 0x0610, 0x061A }, { 0x061C, 0x061C }, |
| 1279 | { 0x064B, 0x065F }, { 0x0670, 0x0670 }, { 0x06D6, 0x06DC }, |
| 1280 | { 0x06DF, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, |
| 1281 | { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, |
| 1282 | { 0x07EB, 0x07F3 }, { 0x07FD, 0x07FD }, { 0x0816, 0x0819 }, |
| 1283 | { 0x081B, 0x0823 }, { 0x0825, 0x0827 }, { 0x0829, 0x082D }, |
| 1284 | { 0x0859, 0x085B }, { 0x0897, 0x089F }, { 0x08CA, 0x08E1 }, |
| 1285 | { 0x08E3, 0x0902 }, { 0x093A, 0x093A }, { 0x093C, 0x093C }, |
| 1286 | { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0957 }, |
| 1287 | { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, |
| 1288 | { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, |
| 1289 | { 0x09FE, 0x09FE }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, |
| 1290 | { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, |
| 1291 | { 0x0A51, 0x0A51 }, { 0x0A70, 0x0A71 }, { 0x0A75, 0x0A75 }, |
| 1292 | { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, |
| 1293 | { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, |
| 1294 | { 0x0AFA, 0x0AFF }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, |
| 1295 | { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B44 }, { 0x0B4D, 0x0B4D }, |
| 1296 | { 0x0B55, 0x0B56 }, { 0x0B62, 0x0B63 }, { 0x0B82, 0x0B82 }, |
| 1297 | { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C00, 0x0C00 }, |
| 1298 | { 0x0C04, 0x0C04 }, { 0x0C3C, 0x0C3C }, { 0x0C3E, 0x0C40 }, |
| 1299 | { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, |
| 1300 | { 0x0C62, 0x0C63 }, { 0x0C81, 0x0C81 }, { 0x0CBC, 0x0CBC }, |
| 1301 | { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, |
| 1302 | { 0x0CE2, 0x0CE3 }, { 0x0D00, 0x0D01 }, { 0x0D3B, 0x0D3C }, |
| 1303 | { 0x0D41, 0x0D44 }, { 0x0D4D, 0x0D4D }, { 0x0D62, 0x0D63 }, |
| 1304 | { 0x0D81, 0x0D81 }, { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, |
| 1305 | { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, |
| 1306 | { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EBC }, |
| 1307 | { 0x0EC8, 0x0ECE }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, |
| 1308 | { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, |
| 1309 | { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F8D, 0x0F97 }, |
| 1310 | { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, |
| 1311 | { 0x1032, 0x1037 }, { 0x1039, 0x103A }, { 0x103D, 0x103E }, |
| 1312 | { 0x1058, 0x1059 }, { 0x105E, 0x1060 }, { 0x1071, 0x1074 }, |
| 1313 | { 0x1082, 0x1082 }, { 0x1085, 0x1086 }, { 0x108D, 0x108D }, |
| 1314 | { 0x109D, 0x109D }, { 0x1160, 0x11FF }, { 0x135D, 0x135F }, |
| 1315 | { 0x1712, 0x1714 }, { 0x1732, 0x1733 }, { 0x1752, 0x1753 }, |
| 1316 | { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, |
| 1317 | { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, |
| 1318 | { 0x180B, 0x180F }, { 0x1885, 0x1886 }, { 0x18A9, 0x18A9 }, |
| 1319 | { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, |
| 1320 | { 0x1939, 0x193B }, { 0x1A17, 0x1A18 }, { 0x1A1B, 0x1A1B }, |
| 1321 | { 0x1A56, 0x1A56 }, { 0x1A58, 0x1A5E }, { 0x1A60, 0x1A60 }, |
| 1322 | { 0x1A62, 0x1A62 }, { 0x1A65, 0x1A6C }, { 0x1A73, 0x1A7C }, |
| 1323 | { 0x1A7F, 0x1A7F }, { 0x1AB0, 0x1ADD }, { 0x1AE0, 0x1AEB }, |
| 1324 | { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, |
| 1325 | { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, |
| 1326 | { 0x1B80, 0x1B81 }, { 0x1BA2, 0x1BA5 }, { 0x1BA8, 0x1BA9 }, |
| 1327 | { 0x1BAB, 0x1BAD }, { 0x1BE6, 0x1BE6 }, { 0x1BE8, 0x1BE9 }, |
| 1328 | { 0x1BED, 0x1BED }, { 0x1BEF, 0x1BF1 }, { 0x1C2C, 0x1C33 }, |
| 1329 | { 0x1C36, 0x1C37 }, { 0x1CD0, 0x1CD2 }, { 0x1CD4, 0x1CE0 }, |
| 1330 | { 0x1CE2, 0x1CE8 }, { 0x1CED, 0x1CED }, { 0x1CF4, 0x1CF4 }, |
| 1331 | { 0x1CF8, 0x1CF9 }, { 0x1DC0, 0x1DFF }, { 0x200B, 0x200F }, |
| 1332 | { 0x2028, 0x202E }, { 0x2060, 0x206F }, { 0x20D0, 0x20F0 }, |
| 1333 | { 0x2CEF, 0x2CF1 }, { 0x2D7F, 0x2D7F }, { 0x2DE0, 0x2DFF }, |
| 1334 | { 0x302A, 0x302D }, { 0x3099, 0x309A }, { 0x3164, 0x3164 }, |
| 1335 | { 0xA66F, 0xA672 }, { 0xA674, 0xA67D }, { 0xA69E, 0xA69F }, |
| 1336 | { 0xA6F0, 0xA6F1 }, { 0xA802, 0xA802 }, { 0xA806, 0xA806 }, |
| 1337 | { 0xA80B, 0xA80B }, { 0xA825, 0xA826 }, { 0xA82C, 0xA82C }, |
| 1338 | { 0xA8C4, 0xA8C5 }, { 0xA8E0, 0xA8F1 }, { 0xA8FF, 0xA8FF }, |
| 1339 | { 0xA926, 0xA92D }, { 0xA947, 0xA951 }, { 0xA980, 0xA982 }, |
| 1340 | { 0xA9B3, 0xA9B3 }, { 0xA9B6, 0xA9B9 }, { 0xA9BC, 0xA9BD }, |
| 1341 | { 0xA9E5, 0xA9E5 }, { 0xAA29, 0xAA2E }, { 0xAA31, 0xAA32 }, |
| 1342 | { 0xAA35, 0xAA36 }, { 0xAA43, 0xAA43 }, { 0xAA4C, 0xAA4C }, |
| 1343 | { 0xAA7C, 0xAA7C }, { 0xAAB0, 0xAAB0 }, { 0xAAB2, 0xAAB4 }, |
| 1344 | { 0xAAB7, 0xAAB8 }, { 0xAABE, 0xAABF }, { 0xAAC1, 0xAAC1 }, |
| 1345 | { 0xAAEC, 0xAAED }, { 0xAAF6, 0xAAF6 }, { 0xABE5, 0xABE5 }, |
| 1346 | { 0xABE8, 0xABE8 }, { 0xABED, 0xABED }, { 0xD7B0, 0xD7FF }, |
| 1347 | { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE2F }, |
| 1348 | { 0xFEFF, 0xFEFF }, { 0xFFA0, 0xFFA0 }, { 0xFFF0, 0xFFFB }, |
| 1349 | { 0x101FD, 0x101FD }, { 0x102E0, 0x102E0 }, { 0x10376, 0x1037A }, |
| 1350 | { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, |
| 1351 | { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x10AE5, 0x10AE6 }, |
| 1352 | { 0x10D24, 0x10D27 }, { 0x10D69, 0x10D6D }, { 0x10EAB, 0x10EAC }, |
| 1353 | { 0x10EFA, 0x10EFF }, { 0x10F46, 0x10F50 }, { 0x10F82, 0x10F85 }, |
| 1354 | { 0x11001, 0x11001 }, { 0x11038, 0x11046 }, { 0x11070, 0x11070 }, |
| 1355 | { 0x11073, 0x11074 }, { 0x1107F, 0x11081 }, { 0x110B3, 0x110B6 }, |
| 1356 | { 0x110B9, 0x110BA }, { 0x110C2, 0x110C2 }, { 0x11100, 0x11102 }, |
| 1357 | { 0x11127, 0x1112B }, { 0x1112D, 0x11134 }, { 0x11173, 0x11173 }, |
| 1358 | { 0x11180, 0x11181 }, { 0x111B6, 0x111BE }, { 0x111C9, 0x111CC }, |
| 1359 | { 0x111CF, 0x111CF }, { 0x1122F, 0x11231 }, { 0x11234, 0x11234 }, |
| 1360 | { 0x11236, 0x11237 }, { 0x1123E, 0x1123E }, { 0x11241, 0x11241 }, |
| 1361 | { 0x112DF, 0x112DF }, { 0x112E3, 0x112EA }, { 0x11300, 0x11301 }, |
| 1362 | { 0x1133B, 0x1133C }, { 0x11340, 0x11340 }, { 0x11366, 0x1136C }, |
| 1363 | { 0x11370, 0x11374 }, { 0x113BB, 0x113C0 }, { 0x113CE, 0x113CE }, |
| 1364 | { 0x113D0, 0x113D0 }, { 0x113D2, 0x113D2 }, { 0x113E1, 0x113E2 }, |
| 1365 | { 0x11438, 0x1143F }, { 0x11442, 0x11444 }, { 0x11446, 0x11446 }, |
| 1366 | { 0x1145E, 0x1145E }, { 0x114B3, 0x114B8 }, { 0x114BA, 0x114BA }, |
| 1367 | { 0x114BF, 0x114C0 }, { 0x114C2, 0x114C3 }, { 0x115B2, 0x115B5 }, |
| 1368 | { 0x115BC, 0x115BD }, { 0x115BF, 0x115C0 }, { 0x115DC, 0x115DD }, |
| 1369 | { 0x11633, 0x1163A }, { 0x1163D, 0x1163D }, { 0x1163F, 0x11640 }, |
| 1370 | { 0x116AB, 0x116AB }, { 0x116AD, 0x116AD }, { 0x116B0, 0x116B5 }, |
| 1371 | { 0x116B7, 0x116B7 }, { 0x1171D, 0x1171D }, { 0x1171F, 0x1171F }, |
| 1372 | { 0x11722, 0x11725 }, { 0x11727, 0x1172B }, { 0x1182F, 0x11837 }, |
| 1373 | { 0x11839, 0x1183A }, { 0x1193B, 0x1193C }, { 0x1193E, 0x1193E }, |
| 1374 | { 0x11943, 0x11943 }, { 0x119D4, 0x119D7 }, { 0x119DA, 0x119DB }, |
| 1375 | { 0x119E0, 0x119E0 }, { 0x11A01, 0x11A0A }, { 0x11A33, 0x11A38 }, |
| 1376 | { 0x11A3B, 0x11A3E }, { 0x11A47, 0x11A47 }, { 0x11A51, 0x11A56 }, |
| 1377 | { 0x11A59, 0x11A5B }, { 0x11A8A, 0x11A96 }, { 0x11A98, 0x11A99 }, |
| 1378 | { 0x11B60, 0x11B60 }, { 0x11B62, 0x11B64 }, { 0x11B66, 0x11B66 }, |
| 1379 | { 0x11C30, 0x11C36 }, { 0x11C38, 0x11C3D }, { 0x11C3F, 0x11C3F }, |
| 1380 | { 0x11C92, 0x11CA7 }, { 0x11CAA, 0x11CB0 }, { 0x11CB2, 0x11CB3 }, |
| 1381 | { 0x11CB5, 0x11CB6 }, { 0x11D31, 0x11D36 }, { 0x11D3A, 0x11D3A }, |
| 1382 | { 0x11D3C, 0x11D3D }, { 0x11D3F, 0x11D45 }, { 0x11D47, 0x11D47 }, |
| 1383 | { 0x11D90, 0x11D91 }, { 0x11D95, 0x11D95 }, { 0x11D97, 0x11D97 }, |
| 1384 | { 0x11EF3, 0x11EF4 }, { 0x11F00, 0x11F01 }, { 0x11F36, 0x11F3A }, |
| 1385 | { 0x11F40, 0x11F40 }, { 0x11F42, 0x11F42 }, { 0x11F5A, 0x11F5A }, |
| 1386 | { 0x13430, 0x13440 }, { 0x13447, 0x13455 }, { 0x1611E, 0x16129 }, |
| 1387 | { 0x1612D, 0x1612F }, { 0x16AF0, 0x16AF4 }, { 0x16B30, 0x16B36 }, |
| 1388 | { 0x16F4F, 0x16F4F }, { 0x16F8F, 0x16F92 }, { 0x16FE4, 0x16FE4 }, |
| 1389 | { 0x1BC9D, 0x1BC9E }, { 0x1BCA0, 0x1BCA3 }, { 0x1CF00, 0x1CF2D }, |
| 1390 | { 0x1CF30, 0x1CF46 }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, |
| 1391 | { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, { 0x1D242, 0x1D244 }, |
| 1392 | { 0x1DA00, 0x1DA36 }, { 0x1DA3B, 0x1DA6C }, { 0x1DA75, 0x1DA75 }, |
| 1393 | { 0x1DA84, 0x1DA84 }, { 0x1DA9B, 0x1DA9F }, { 0x1DAA1, 0x1DAAF }, |
| 1394 | { 0x1E000, 0x1E006 }, { 0x1E008, 0x1E018 }, { 0x1E01B, 0x1E021 }, |
| 1395 | { 0x1E023, 0x1E024 }, { 0x1E026, 0x1E02A }, { 0x1E08F, 0x1E08F }, |
| 1396 | { 0x1E130, 0x1E136 }, { 0x1E2AE, 0x1E2AE }, { 0x1E2EC, 0x1E2EF }, |
| 1397 | { 0x1E4EC, 0x1E4EF }, { 0x1E5EE, 0x1E5EF }, { 0x1E6E3, 0x1E6E3 }, |
| 1398 | { 0x1E6E6, 0x1E6E6 }, { 0x1E6EE, 0x1E6EF }, { 0x1E6F5, 0x1E6F5 }, |
| 1399 | { 0x1E8D0, 0x1E8D6 }, { 0x1E944, 0x1E94A }, { 0xE0000, 0xE0FFF } |
| 1400 | }; |
| 1401 | |
| 1402 | /* test for 8-bit control characters */ |
| 1403 | if (ucs == 0) |
| 1404 | return 0; |
| 1405 | if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) |
| 1406 | return -1; |
| 1407 | |
| 1408 | /* binary search in table of non-spacing characters */ |
| 1409 | if (bisearch(ucs, table: combining, |
| 1410 | max: sizeof(combining) / sizeof(struct interval) - 1)) |
| 1411 | return 0; |
| 1412 | |
| 1413 | /* sorted list of non-overlapping intervals of wide/fullwidth characters */ |
| 1414 | /* Unicode 17.0.0 - East_Asian_Width W and F */ |
| 1415 | static const struct interval wide[] = { |
| 1416 | { 0x1100, 0x115F }, { 0x231A, 0x231B }, { 0x2329, 0x232A }, |
| 1417 | { 0x23E9, 0x23EC }, { 0x23F0, 0x23F0 }, { 0x23F3, 0x23F3 }, |
| 1418 | { 0x25FD, 0x25FE }, { 0x2614, 0x2615 }, { 0x2630, 0x2637 }, |
| 1419 | { 0x2648, 0x2653 }, { 0x267F, 0x267F }, { 0x268A, 0x268F }, |
| 1420 | { 0x2693, 0x2693 }, { 0x26A1, 0x26A1 }, { 0x26AA, 0x26AB }, |
| 1421 | { 0x26BD, 0x26BE }, { 0x26C4, 0x26C5 }, { 0x26CE, 0x26CE }, |
| 1422 | { 0x26D4, 0x26D4 }, { 0x26EA, 0x26EA }, { 0x26F2, 0x26F3 }, |
| 1423 | { 0x26F5, 0x26F5 }, { 0x26FA, 0x26FA }, { 0x26FD, 0x26FD }, |
| 1424 | { 0x2705, 0x2705 }, { 0x270A, 0x270B }, { 0x2728, 0x2728 }, |
| 1425 | { 0x274C, 0x274C }, { 0x274E, 0x274E }, { 0x2753, 0x2755 }, |
| 1426 | { 0x2757, 0x2757 }, { 0x2795, 0x2797 }, { 0x27B0, 0x27B0 }, |
| 1427 | { 0x27BF, 0x27BF }, { 0x2B1B, 0x2B1C }, { 0x2B50, 0x2B50 }, |
| 1428 | { 0x2B55, 0x2B55 }, { 0x2E80, 0x2E99 }, { 0x2E9B, 0x2EF3 }, |
| 1429 | { 0x2F00, 0x2FD5 }, { 0x2FF0, 0x303E }, { 0x3041, 0x3096 }, |
| 1430 | { 0x3099, 0x30FF }, { 0x3105, 0x312F }, { 0x3131, 0x318E }, |
| 1431 | { 0x3190, 0x31E5 }, { 0x31EF, 0x321E }, { 0x3220, 0x3247 }, |
| 1432 | { 0x3250, 0xA48C }, { 0xA490, 0xA4C6 }, { 0xA960, 0xA97C }, |
| 1433 | { 0xAC00, 0xD7A3 }, { 0xF900, 0xFAFF }, { 0xFE10, 0xFE19 }, |
| 1434 | { 0xFE30, 0xFE52 }, { 0xFE54, 0xFE66 }, { 0xFE68, 0xFE6B }, |
| 1435 | { 0xFF01, 0xFF60 }, { 0xFFE0, 0xFFE6 }, |
| 1436 | { 0x16FE0, 0x16FE4 }, { 0x16FF0, 0x16FF6 }, |
| 1437 | { 0x17000, 0x18CD5 }, { 0x18CFF, 0x18D1E }, { 0x18D80, 0x18DF2 }, |
| 1438 | { 0x1AFF0, 0x1AFF3 }, { 0x1AFF5, 0x1AFFB }, |
| 1439 | { 0x1AFFD, 0x1AFFE }, { 0x1B000, 0x1B122 }, { 0x1B132, 0x1B132 }, |
| 1440 | { 0x1B150, 0x1B152 }, { 0x1B155, 0x1B155 }, { 0x1B164, 0x1B167 }, |
| 1441 | { 0x1B170, 0x1B2FB }, { 0x1D300, 0x1D356 }, { 0x1D360, 0x1D376 }, |
| 1442 | { 0x1F004, 0x1F004 }, { 0x1F0CF, 0x1F0CF }, { 0x1F18E, 0x1F18E }, |
| 1443 | { 0x1F191, 0x1F19A }, { 0x1F200, 0x1F202 }, { 0x1F210, 0x1F23B }, |
| 1444 | { 0x1F240, 0x1F248 }, { 0x1F250, 0x1F251 }, { 0x1F260, 0x1F265 }, |
| 1445 | { 0x1F300, 0x1F320 }, { 0x1F32D, 0x1F335 }, { 0x1F337, 0x1F37C }, |
| 1446 | { 0x1F37E, 0x1F393 }, { 0x1F3A0, 0x1F3CA }, { 0x1F3CF, 0x1F3D3 }, |
| 1447 | { 0x1F3E0, 0x1F3F0 }, { 0x1F3F4, 0x1F3F4 }, { 0x1F3F8, 0x1F43E }, |
| 1448 | { 0x1F440, 0x1F440 }, { 0x1F442, 0x1F4FC }, { 0x1F4FF, 0x1F53D }, |
| 1449 | { 0x1F54B, 0x1F54E }, { 0x1F550, 0x1F567 }, { 0x1F57A, 0x1F57A }, |
| 1450 | { 0x1F595, 0x1F596 }, { 0x1F5A4, 0x1F5A4 }, { 0x1F5FB, 0x1F64F }, |
| 1451 | { 0x1F680, 0x1F6C5 }, { 0x1F6CC, 0x1F6CC }, { 0x1F6D0, 0x1F6D2 }, |
| 1452 | { 0x1F6D5, 0x1F6D8 }, { 0x1F6DC, 0x1F6DF }, { 0x1F6EB, 0x1F6EC }, |
| 1453 | { 0x1F6F4, 0x1F6FC }, { 0x1F7E0, 0x1F7EB }, { 0x1F7F0, 0x1F7F0 }, |
| 1454 | { 0x1F90C, 0x1F93A }, { 0x1F93C, 0x1F945 }, { 0x1F947, 0x1F9FF }, |
| 1455 | { 0x1FA70, 0x1FA7C }, { 0x1FA80, 0x1FA8A }, { 0x1FA8E, 0x1FAC6 }, |
| 1456 | { 0x1FAC8, 0x1FAC8 }, { 0x1FACD, 0x1FADC }, { 0x1FADF, 0x1FAEA }, |
| 1457 | { 0x1FAEF, 0x1FAF8 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD } |
| 1458 | }; |
| 1459 | |
| 1460 | if (bisearch(ucs, table: wide, |
| 1461 | max: sizeof(wide) / sizeof(struct interval) - 1)) |
| 1462 | return 2; |
| 1463 | |
| 1464 | return 1; |
| 1465 | } |
| 1466 | |
| 1467 | // End of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c inherited code |
| 1468 | |
| 1469 | static int unicode_to_cp437(uint64_t code_point) { |
| 1470 | // Braille patterns U+2800-U+28FF: approximate using CP437 block/shade characters. |
| 1471 | // Braille dot layout (bit positions): |
| 1472 | // bit0 bit3 (row 0) |
| 1473 | // bit1 bit4 (row 1) |
| 1474 | // bit2 bit5 (row 2) |
| 1475 | // bit6 bit7 (row 3) |
| 1476 | if (code_point >= 0x2800 && code_point <= 0x28ff) { |
| 1477 | uint32_t dots = (uint32_t)(code_point - 0x2800); |
| 1478 | |
| 1479 | if (dots == 0) return 0x20; |
| 1480 | if (dots == 0xff) return 0xdb; |
| 1481 | |
| 1482 | bool has_top = dots & 0x1b; |
| 1483 | bool has_bottom = dots & 0xe4; |
| 1484 | bool has_left = dots & 0x47; |
| 1485 | bool has_right = dots & 0xb8; |
| 1486 | |
| 1487 | if (has_top && !has_bottom) return 0xdf; // ▀ |
| 1488 | if (has_bottom && !has_top) return 0xdc; // ▄ |
| 1489 | if (has_left && !has_right) return 0xdd; // ▌ |
| 1490 | if (has_right && !has_left) return 0xde; // ▐ |
| 1491 | |
| 1492 | // Count set bits for density-based shade |
| 1493 | uint32_t n = dots - ((dots >> 1) & 0x55); |
| 1494 | n = (n & 0x33) + ((n >> 2) & 0x33); |
| 1495 | n = (n + (n >> 4)) & 0x0f; |
| 1496 | |
| 1497 | if (n <= 2) return 0xb0; // ░ |
| 1498 | if (n <= 4) return 0xb1; // ▒ |
| 1499 | if (n <= 6) return 0xb2; // ▓ |
| 1500 | return 0xdb; // █ |
| 1501 | } |
| 1502 | |
| 1503 | switch (code_point) { |
| 1504 | case 0x263a: return 1; |
| 1505 | case 0x263b: return 2; |
| 1506 | case 0x2665: return 3; |
| 1507 | case 0x2666: return 4; |
| 1508 | case 0x25c6: return 4; |
| 1509 | case 0x2663: return 5; |
| 1510 | case 0x2660: return 6; |
| 1511 | case 0x2022: return 7; |
| 1512 | case 0x25d8: return 8; |
| 1513 | case 0x25cb: return 9; |
| 1514 | case 0x25d9: return 10; |
| 1515 | case 0x2642: return 11; |
| 1516 | case 0x2640: return 12; |
| 1517 | case 0x266a: return 13; |
| 1518 | case 0x266b: return 14; |
| 1519 | case 0x263c: return 15; |
| 1520 | case 0x00a4: return 15; |
| 1521 | case 0x25ba: return 16; |
| 1522 | case 0x25b6: return 16; |
| 1523 | case 0x25c4: return 17; |
| 1524 | case 0x25c0: return 17; |
| 1525 | case 0x2195: return 18; |
| 1526 | case 0x203c: return 19; |
| 1527 | case 0x00b6: return 20; |
| 1528 | case 0x00a7: return 21; |
| 1529 | case 0x25ac: return 22; |
| 1530 | case 0x21a8: return 23; |
| 1531 | case 0x2191: return 24; |
| 1532 | case 0x2193: return 25; |
| 1533 | case 0x2192: return 26; |
| 1534 | case 0x2190: return 27; |
| 1535 | case 0x221f: return 28; |
| 1536 | case 0x2194: return 29; |
| 1537 | case 0x25b2: return 30; |
| 1538 | case 0x25bc: return 31; |
| 1539 | |
| 1540 | case 0x00a8: return 0x22; |
| 1541 | case 0x00b4: return 0x27; |
| 1542 | case 0x00b8: return 0x2c; |
| 1543 | case 0x00ad: return 0x2d; |
| 1544 | case 0x00c0: return 0x41; |
| 1545 | case 0x00c1: return 0x41; |
| 1546 | case 0x00c2: return 0x41; |
| 1547 | case 0x00c3: return 0x41; |
| 1548 | case 0x00a9: return 0x43; |
| 1549 | case 0x00d0: return 0x44; |
| 1550 | case 0x00c8: return 0x45; |
| 1551 | case 0x00ca: return 0x45; |
| 1552 | case 0x00cb: return 0x45; |
| 1553 | case 0x00cc: return 0x49; |
| 1554 | case 0x00cd: return 0x49; |
| 1555 | case 0x00ce: return 0x49; |
| 1556 | case 0x00cf: return 0x49; |
| 1557 | case 0x212a: return 0x4b; |
| 1558 | case 0x00d2: return 0x4f; |
| 1559 | case 0x00d3: return 0x4f; |
| 1560 | case 0x00d4: return 0x4f; |
| 1561 | case 0x00d5: return 0x4f; |
| 1562 | case 0x00ae: return 0x52; |
| 1563 | case 0x00d9: return 0x55; |
| 1564 | case 0x00da: return 0x55; |
| 1565 | case 0x00db: return 0x55; |
| 1566 | case 0x00dd: return 0x59; |
| 1567 | case 0x23bd: return 0x5f; |
| 1568 | case 0x00e3: return 0x61; |
| 1569 | case 0x00f5: return 0x6f; |
| 1570 | case 0x00d7: return 0x78; |
| 1571 | case 0x00fd: return 0x79; |
| 1572 | case 0x00a6: return 0x7c; |
| 1573 | |
| 1574 | case 0x2302: return 127; |
| 1575 | case 0x00c7: return 128; |
| 1576 | case 0x00fc: return 129; |
| 1577 | case 0x00e9: return 130; |
| 1578 | case 0x00e2: return 131; |
| 1579 | case 0x00e4: return 132; |
| 1580 | case 0x00e0: return 133; |
| 1581 | case 0x00e5: return 134; |
| 1582 | case 0x00e7: return 135; |
| 1583 | case 0x00ea: return 136; |
| 1584 | case 0x00eb: return 137; |
| 1585 | case 0x00e8: return 138; |
| 1586 | case 0x00ef: return 139; |
| 1587 | case 0x00ee: return 140; |
| 1588 | case 0x00ec: return 141; |
| 1589 | case 0x00c4: return 142; |
| 1590 | case 0x00c5: return 143; |
| 1591 | case 0x212b: return 143; |
| 1592 | case 0x00c9: return 144; |
| 1593 | case 0x00e6: return 145; |
| 1594 | case 0x00c6: return 146; |
| 1595 | case 0x00f4: return 147; |
| 1596 | case 0x00f6: return 148; |
| 1597 | case 0x00f2: return 149; |
| 1598 | case 0x00fb: return 150; |
| 1599 | case 0x00f9: return 151; |
| 1600 | case 0x00ff: return 152; |
| 1601 | case 0x00d6: return 153; |
| 1602 | case 0x00dc: return 154; |
| 1603 | case 0x00a2: return 155; |
| 1604 | case 0x00a3: return 156; |
| 1605 | case 0x00a5: return 157; |
| 1606 | case 0x20a7: return 158; |
| 1607 | case 0x0192: return 159; |
| 1608 | case 0x00e1: return 160; |
| 1609 | case 0x00ed: return 161; |
| 1610 | case 0x00f3: return 162; |
| 1611 | case 0x00fa: return 163; |
| 1612 | case 0x00f1: return 164; |
| 1613 | case 0x00d1: return 165; |
| 1614 | case 0x00aa: return 166; |
| 1615 | case 0x00ba: return 167; |
| 1616 | case 0x00bf: return 168; |
| 1617 | case 0x2310: return 169; |
| 1618 | case 0x00ac: return 170; |
| 1619 | case 0x00bd: return 171; |
| 1620 | case 0x00bc: return 172; |
| 1621 | case 0x00a1: return 173; |
| 1622 | case 0x00ab: return 174; |
| 1623 | case 0x00bb: return 175; |
| 1624 | case 0x2591: return 176; |
| 1625 | case 0x2592: return 177; |
| 1626 | case 0x2593: return 178; |
| 1627 | case 0x2502: return 179; |
| 1628 | case 0x2524: return 180; |
| 1629 | case 0x2561: return 181; |
| 1630 | case 0x2562: return 182; |
| 1631 | case 0x2556: return 183; |
| 1632 | case 0x2555: return 184; |
| 1633 | case 0x2563: return 185; |
| 1634 | case 0x2551: return 186; |
| 1635 | case 0x2557: return 187; |
| 1636 | case 0x255d: return 188; |
| 1637 | case 0x255c: return 189; |
| 1638 | case 0x255b: return 190; |
| 1639 | case 0x2510: return 191; |
| 1640 | case 0x2514: return 192; |
| 1641 | case 0x2534: return 193; |
| 1642 | case 0x252c: return 194; |
| 1643 | case 0x251c: return 195; |
| 1644 | case 0x2500: return 196; |
| 1645 | case 0x253c: return 197; |
| 1646 | case 0x255e: return 198; |
| 1647 | case 0x255f: return 199; |
| 1648 | case 0x255a: return 200; |
| 1649 | case 0x2554: return 201; |
| 1650 | case 0x2569: return 202; |
| 1651 | case 0x2566: return 203; |
| 1652 | case 0x2560: return 204; |
| 1653 | case 0x2550: return 205; |
| 1654 | case 0x256c: return 206; |
| 1655 | case 0x2567: return 207; |
| 1656 | case 0x2568: return 208; |
| 1657 | case 0x2564: return 209; |
| 1658 | case 0x2565: return 210; |
| 1659 | case 0x2559: return 211; |
| 1660 | case 0x2558: return 212; |
| 1661 | case 0x2552: return 213; |
| 1662 | case 0x2553: return 214; |
| 1663 | case 0x256b: return 215; |
| 1664 | case 0x256a: return 216; |
| 1665 | case 0x2518: return 217; |
| 1666 | case 0x250c: return 218; |
| 1667 | case 0x2588: return 219; |
| 1668 | case 0x2584: return 220; |
| 1669 | case 0x258c: return 221; |
| 1670 | case 0x2590: return 222; |
| 1671 | case 0x2580: return 223; |
| 1672 | case 0x03b1: return 224; |
| 1673 | case 0x00df: return 225; |
| 1674 | case 0x03b2: return 225; |
| 1675 | case 0x0393: return 226; |
| 1676 | case 0x03c0: return 227; |
| 1677 | case 0x03a3: return 228; |
| 1678 | case 0x03c3: return 229; |
| 1679 | case 0x00b5: return 230; |
| 1680 | case 0x03bc: return 230; |
| 1681 | case 0x03c4: return 231; |
| 1682 | case 0x03a6: return 232; |
| 1683 | case 0x00d8: return 232; |
| 1684 | case 0x0398: return 233; |
| 1685 | case 0x03a9: return 234; |
| 1686 | case 0x2126: return 234; |
| 1687 | case 0x03b4: return 235; |
| 1688 | case 0x00f0: return 235; |
| 1689 | case 0x221e: return 236; |
| 1690 | case 0x03c6: return 237; |
| 1691 | case 0x00f8: return 237; |
| 1692 | case 0x03b5: return 238; |
| 1693 | case 0x2208: return 238; |
| 1694 | case 0x2229: return 239; |
| 1695 | case 0x2261: return 240; |
| 1696 | case 0x00b1: return 241; |
| 1697 | case 0x2265: return 242; |
| 1698 | case 0x2264: return 243; |
| 1699 | case 0x2320: return 244; |
| 1700 | case 0x2321: return 245; |
| 1701 | case 0x00f7: return 246; |
| 1702 | case 0x2248: return 247; |
| 1703 | case 0x00b0: return 248; |
| 1704 | case 0x2219: return 249; |
| 1705 | case 0x00b7: return 250; |
| 1706 | case 0x221a: return 251; |
| 1707 | case 0x207f: return 252; |
| 1708 | case 0x00b2: return 253; |
| 1709 | case 0x25a0: return 254; |
| 1710 | case 0xfffd: return 254; |
| 1711 | case 0x00a0: return 255; |
| 1712 | |
| 1713 | // Approximate mappings for Unicode characters without exact CP437 equivalents |
| 1714 | |
| 1715 | // Rounded/arc box drawing corners |
| 1716 | case 0x256d: return 0xda; // ╭ → ┌ |
| 1717 | case 0x256e: return 0xbf; // ╮ → ┐ |
| 1718 | case 0x256f: return 0xd9; // ╯ → ┘ |
| 1719 | case 0x2570: return 0xc0; // ╰ → └ |
| 1720 | |
| 1721 | // Diagonal box drawing |
| 1722 | case 0x2571: return 0x2f; // ╱ → / |
| 1723 | case 0x2572: return 0x5c; // ╲ → \ (backslash) |
| 1724 | case 0x2573: return 0x58; // ╳ → X |
| 1725 | |
| 1726 | // Heavy box drawing → single-line equivalents |
| 1727 | case 0x2501: return 0xc4; // ━ → ─ |
| 1728 | case 0x2503: return 0xb3; // ┃ → │ |
| 1729 | case 0x250f: return 0xda; // ┏ → ┌ |
| 1730 | case 0x2513: return 0xbf; // ┓ → ┐ |
| 1731 | case 0x2517: return 0xc0; // ┗ → └ |
| 1732 | case 0x251b: return 0xd9; // ┛ → ┘ |
| 1733 | case 0x2523: return 0xc3; // ┣ → ├ |
| 1734 | case 0x252b: return 0xb4; // ┫ → ┤ |
| 1735 | case 0x2533: return 0xc2; // ┳ → ┬ |
| 1736 | case 0x253b: return 0xc1; // ┻ → ┴ |
| 1737 | case 0x254b: return 0xc5; // ╋ → ┼ |
| 1738 | |
| 1739 | // Mixed heavy/light box drawing corners |
| 1740 | case 0x250d: return 0xda; // ┍ → ┌ |
| 1741 | case 0x250e: return 0xda; // ┎ → ┌ |
| 1742 | case 0x2511: return 0xbf; // ┑ → ┐ |
| 1743 | case 0x2512: return 0xbf; // ┒ → ┐ |
| 1744 | case 0x2515: return 0xc0; // ┕ → └ |
| 1745 | case 0x2516: return 0xc0; // ┖ → └ |
| 1746 | case 0x2519: return 0xd9; // ┙ → ┘ |
| 1747 | case 0x251a: return 0xd9; // ┚ → ┘ |
| 1748 | |
| 1749 | // Mixed heavy/light box drawing T-pieces |
| 1750 | case 0x251d: return 0xc3; // ┝ → ├ |
| 1751 | case 0x251e: return 0xc3; // ┞ → ├ |
| 1752 | case 0x251f: return 0xc3; // ┟ → ├ |
| 1753 | case 0x2520: return 0xc3; // ┠ → ├ |
| 1754 | case 0x2521: return 0xc3; // ┡ → ├ |
| 1755 | case 0x2522: return 0xc3; // ┢ → ├ |
| 1756 | case 0x2525: return 0xb4; // ┥ → ┤ |
| 1757 | case 0x2526: return 0xb4; // ┦ → ┤ |
| 1758 | case 0x2527: return 0xb4; // ┧ → ┤ |
| 1759 | case 0x2528: return 0xb4; // ┨ → ┤ |
| 1760 | case 0x2529: return 0xb4; // ┩ → ┤ |
| 1761 | case 0x252a: return 0xb4; // ┪ → ┤ |
| 1762 | case 0x252d: return 0xc2; // ┭ → ┬ |
| 1763 | case 0x252e: return 0xc2; // ┮ → ┬ |
| 1764 | case 0x252f: return 0xc2; // ┯ → ┬ |
| 1765 | case 0x2530: return 0xc2; // ┰ → ┬ |
| 1766 | case 0x2531: return 0xc2; // ┱ → ┬ |
| 1767 | case 0x2532: return 0xc2; // ┲ → ┬ |
| 1768 | case 0x2535: return 0xc1; // ┵ → ┴ |
| 1769 | case 0x2536: return 0xc1; // ┶ → ┴ |
| 1770 | case 0x2537: return 0xc1; // ┷ → ┴ |
| 1771 | case 0x2538: return 0xc1; // ┸ → ┴ |
| 1772 | case 0x2539: return 0xc1; // ┹ → ┴ |
| 1773 | case 0x253a: return 0xc1; // ┺ → ┴ |
| 1774 | |
| 1775 | // Mixed heavy/light box drawing crosses |
| 1776 | case 0x253d: return 0xc5; // ┽ → ┼ |
| 1777 | case 0x253e: return 0xc5; // ┾ → ┼ |
| 1778 | case 0x253f: return 0xc5; // ┿ → ┼ |
| 1779 | case 0x2540: return 0xc5; // ╀ → ┼ |
| 1780 | case 0x2541: return 0xc5; // ╁ → ┼ |
| 1781 | case 0x2542: return 0xc5; // ╂ → ┼ |
| 1782 | case 0x2543: return 0xc5; // ╃ → ┼ |
| 1783 | case 0x2544: return 0xc5; // ╄ → ┼ |
| 1784 | case 0x2545: return 0xc5; // ╅ → ┼ |
| 1785 | case 0x2546: return 0xc5; // ╆ → ┼ |
| 1786 | case 0x2547: return 0xc5; // ╇ → ┼ |
| 1787 | case 0x2548: return 0xc5; // ╈ → ┼ |
| 1788 | case 0x2549: return 0xc5; // ╉ → ┼ |
| 1789 | case 0x254a: return 0xc5; // ╊ → ┼ |
| 1790 | |
| 1791 | // Dashed/dotted box drawing → solid equivalents |
| 1792 | case 0x2504: return 0xc4; // ┄ → ─ |
| 1793 | case 0x2505: return 0xc4; // ┅ → ─ |
| 1794 | case 0x2506: return 0xb3; // ┆ → │ |
| 1795 | case 0x2507: return 0xb3; // ┇ → │ |
| 1796 | case 0x2508: return 0xc4; // ┈ → ─ |
| 1797 | case 0x2509: return 0xc4; // ┉ → ─ |
| 1798 | case 0x250a: return 0xb3; // ┊ → │ |
| 1799 | case 0x250b: return 0xb3; // ┋ → │ |
| 1800 | |
| 1801 | // Box drawing half-lines and fragments |
| 1802 | case 0x2574: return 0xc4; // ╴ → ─ |
| 1803 | case 0x2575: return 0xb3; // ╵ → │ |
| 1804 | case 0x2576: return 0xc4; // ╶ → ─ |
| 1805 | case 0x2577: return 0xb3; // ╷ → │ |
| 1806 | case 0x2578: return 0xc4; // ╸ → ─ |
| 1807 | case 0x2579: return 0xb3; // ╹ → │ |
| 1808 | case 0x257a: return 0xc4; // ╺ → ─ |
| 1809 | case 0x257b: return 0xb3; // ╻ → │ |
| 1810 | case 0x257c: return 0xc4; // ╼ → ─ |
| 1811 | case 0x257d: return 0xb3; // ╽ → │ |
| 1812 | case 0x257e: return 0xc4; // ╾ → ─ |
| 1813 | case 0x257f: return 0xb3; // ╿ → │ |
| 1814 | |
| 1815 | // Triangle variants → filled equivalents |
| 1816 | case 0x25b3: return 30; // △ → ▲ |
| 1817 | case 0x25b5: return 30; // ▵ → ▲ |
| 1818 | case 0x25b7: return 16; // ▷ → ► |
| 1819 | case 0x25b9: return 16; // ▹ → ► |
| 1820 | case 0x25bd: return 31; // ▽ → ▼ |
| 1821 | case 0x25bf: return 31; // ▿ → ▼ |
| 1822 | case 0x25c1: return 17; // ◁ → ◄ |
| 1823 | case 0x25c3: return 17; // ◃ → ◄ |
| 1824 | |
| 1825 | // Fractional block elements → closest CP437 block |
| 1826 | case 0x2581: return 0xdc; // ▁ (lower 1/8) → ▄ |
| 1827 | case 0x2582: return 0xdc; // ▂ (lower 1/4) → ▄ |
| 1828 | case 0x2583: return 0xdc; // ▃ (lower 3/8) → ▄ |
| 1829 | case 0x2585: return 0xdc; // ▅ (lower 5/8) → ▄ |
| 1830 | case 0x2586: return 0xdb; // ▆ (lower 3/4) → █ |
| 1831 | case 0x2587: return 0xdb; // ▇ (lower 7/8) → █ |
| 1832 | case 0x2589: return 0xdb; // ▉ (left 7/8) → █ |
| 1833 | case 0x258a: return 0xdb; // ▊ (left 3/4) → █ |
| 1834 | case 0x258b: return 0xdd; // ▋ (left 5/8) → ▌ |
| 1835 | case 0x258d: return 0xdd; // ▍ (left 3/8) → ▌ |
| 1836 | case 0x258e: return 0xdd; // ▎ (left 1/4) → ▌ |
| 1837 | case 0x258f: return 0xdd; // ▏ (left 1/8) → ▌ |
| 1838 | case 0x2594: return 0xdf; // ▔ (upper 1/8) → ▀ |
| 1839 | case 0x2595: return 0xde; // ▕ (right 1/8) → ▐ |
| 1840 | |
| 1841 | // Quadrant block elements |
| 1842 | case 0x2596: return 0xdc; // ▖ → ▄ |
| 1843 | case 0x2597: return 0xdc; // ▗ → ▄ |
| 1844 | case 0x2598: return 0xdf; // ▘ → ▀ |
| 1845 | case 0x2599: return 0xdb; // ▙ → █ |
| 1846 | case 0x259a: return 0xb1; // ▚ → ▒ |
| 1847 | case 0x259b: return 0xdb; // ▛ → █ |
| 1848 | case 0x259c: return 0xdb; // ▜ → █ |
| 1849 | case 0x259d: return 0xdf; // ▝ → ▀ |
| 1850 | case 0x259e: return 0xb1; // ▞ → ▒ |
| 1851 | case 0x259f: return 0xdb; // ▟ → █ |
| 1852 | |
| 1853 | // Circles and bullets |
| 1854 | case 0x25cf: return 0x07; // ● → • |
| 1855 | case 0x25c9: return 0x0a; // ◉ → ◙ |
| 1856 | case 0x25ef: return 0x09; // ◯ → ○ |
| 1857 | case 0x25e6: return 0x09; // ◦ → ○ |
| 1858 | case 0x25aa: return 0xfe; // ▪ → ■ |
| 1859 | case 0x25fc: return 0xfe; // ◼ → ■ |
| 1860 | |
| 1861 | // Typographic punctuation |
| 1862 | case 0x2013: return 0x2d; // – (en dash) → - |
| 1863 | case 0x2014: return 0x2d; // — (em dash) → - |
| 1864 | case 0x2018: return 0x27; // ' (left single quote) → ' |
| 1865 | case 0x2019: return 0x27; // ' (right single quote) → ' |
| 1866 | case 0x201c: return 0x22; // " (left double quote) → " |
| 1867 | case 0x201d: return 0x22; // " (right double quote) → " |
| 1868 | case 0x2026: return 0xfa; // … (ellipsis) → · |
| 1869 | case 0x2212: return 0x2d; // − (minus sign) → - |
| 1870 | |
| 1871 | // Check marks |
| 1872 | case 0x2713: return 0xfb; // ✓ → √ |
| 1873 | case 0x2714: return 0xfb; // ✔ → √ |
| 1874 | |
| 1875 | // Double arrows → single arrow equivalents |
| 1876 | case 0x21d0: return 27; // ⇐ → ← |
| 1877 | case 0x21d1: return 24; // ⇑ → ↑ |
| 1878 | case 0x21d2: return 26; // ⇒ → → |
| 1879 | case 0x21d3: return 25; // ⇓ → ↓ |
| 1880 | case 0x21d4: return 29; // ⇔ → ↔ |
| 1881 | case 0x21d5: return 18; // ⇕ → ↕ |
| 1882 | |
| 1883 | // Summation sign |
| 1884 | case 0x2211: return 0xe4; // ∑ → Σ |
| 1885 | |
| 1886 | // Horizontal line extension |
| 1887 | case 0x23af: return 0xc4; // ⎯ → ─ |
| 1888 | |
| 1889 | // Media transport symbols |
| 1890 | case 0x23f4: return 17; // ⏴ → ◄ |
| 1891 | case 0x23f5: return 16; // ⏵ → ► |
| 1892 | case 0x23f6: return 30; // ⏶ → ▲ |
| 1893 | case 0x23f7: return 31; // ⏷ → ▼ |
| 1894 | case 0x23f8: return 0xba; // ⏸ → ║ |
| 1895 | case 0x23f9: return 0xfe; // ⏹ → ■ |
| 1896 | case 0x23fa: return 0x07; // ⏺ → • |
| 1897 | |
| 1898 | // Square bracket pieces |
| 1899 | case 0x23a1: return 0xda; // ⎡ → ┌ |
| 1900 | case 0x23a2: return 0xb3; // ⎢ → │ |
| 1901 | case 0x23a3: return 0xc0; // ⎣ → └ |
| 1902 | case 0x23a4: return 0xbf; // ⎤ → ┐ |
| 1903 | case 0x23a5: return 0xb3; // ⎥ → │ |
| 1904 | case 0x23a6: return 0xd9; // ⎦ → ┘ |
| 1905 | |
| 1906 | // Curly bracket pieces |
| 1907 | case 0x23a7: return 0xda; // ⎧ → ┌ |
| 1908 | case 0x23a8: return 0xc3; // ⎨ → ├ |
| 1909 | case 0x23a9: return 0xc0; // ⎩ → └ |
| 1910 | case 0x23aa: return 0xb3; // ⎪ → │ |
| 1911 | case 0x23ab: return 0xbf; // ⎫ → ┐ |
| 1912 | case 0x23ac: return 0xb4; // ⎬ → ┤ |
| 1913 | case 0x23ad: return 0xd9; // ⎭ → ┘ |
| 1914 | case 0x23ae: return 0xb3; // ⎮ → │ |
| 1915 | |
| 1916 | // Vertical box lines |
| 1917 | case 0x23b8: return 0xb3; // ⎸ → │ |
| 1918 | case 0x23b9: return 0xb3; // ⎹ → │ |
| 1919 | |
| 1920 | // Horizontal scan lines (0x23bd already mapped above) |
| 1921 | case 0x23ba: return 0xc4; // ⎺ → ─ |
| 1922 | case 0x23bb: return 0xc4; // ⎻ → ─ |
| 1923 | case 0x23bc: return 0xc4; // ⎼ → ─ |
| 1924 | |
| 1925 | // Dentistry/angle symbols |
| 1926 | case 0x23be: return 0xb3; // ⎾ → │ |
| 1927 | case 0x23bf: return 0xc0; // ⎿ → └ |
| 1928 | |
| 1929 | // Corner brackets |
| 1930 | case 0x231c: return 0xda; // ⌜ → ┌ |
| 1931 | case 0x231d: return 0xbf; // ⌝ → ┐ |
| 1932 | case 0x231e: return 0xc0; // ⌞ → └ |
| 1933 | case 0x231f: return 0xd9; // ⌟ → ┘ |
| 1934 | } |
| 1935 | |
| 1936 | return -1; |
| 1937 | } |
| 1938 | |
| 1939 | static void insert_shift(struct flanterm_context *ctx, size_t count) { |
| 1940 | if (ctx->insert_mode && count > 0) { |
| 1941 | size_t x, y; |
| 1942 | ctx->get_cursor_pos(ctx, &x, &y); |
| 1943 | if (count > ctx->cols - x) { |
| 1944 | count = ctx->cols - x; |
| 1945 | } |
| 1946 | for (size_t i = ctx->cols - 1; i >= x + count; i--) { |
| 1947 | ctx->move_character(ctx, i, y, i - count, y); |
| 1948 | } |
| 1949 | } |
| 1950 | } |
| 1951 | |
| 1952 | static void flanterm_putchar(struct flanterm_context *ctx, uint8_t c) { |
| 1953 | if (ctx->discard_next || (c == 0x18 || c == 0x1a)) { |
| 1954 | ctx->discard_next = false; |
| 1955 | ctx->escape = false; |
| 1956 | ctx->control_sequence = false; |
| 1957 | ctx->unicode_remaining = 0; |
| 1958 | ctx->osc = false; |
| 1959 | ctx->osc_escape = false; |
| 1960 | ctx->g_select = 0; |
| 1961 | ctx->last_was_graphic = false; |
| 1962 | return; |
| 1963 | } |
| 1964 | |
| 1965 | if (ctx->unicode_remaining != 0) { |
| 1966 | if ((c & 0xc0) != 0x80) { |
| 1967 | ctx->unicode_remaining = 0; |
| 1968 | insert_shift(ctx, count: 1); |
| 1969 | ctx->raw_putchar(ctx, 0xfe); |
| 1970 | goto unicode_error; |
| 1971 | } |
| 1972 | |
| 1973 | ctx->unicode_remaining--; |
| 1974 | ctx->code_point |= (uint64_t)(c & 0x3f) << (6 * ctx->unicode_remaining); |
| 1975 | |
| 1976 | // Reject overlong encodings and out-of-range codepoints early |
| 1977 | // by validating the first continuation byte against the lead byte. |
| 1978 | // 3-byte lead E0: first continuation must be >= 0xA0 (code_point >= 0x800) |
| 1979 | // 4-byte lead F0: first continuation must be >= 0x90 (code_point >= 0x10000) |
| 1980 | // 4-byte lead F4: first continuation must be <= 0x8F (code_point <= 0x10FFFF) |
| 1981 | if (ctx->unicode_remaining == 1 && ctx->code_point < 0x800) { |
| 1982 | ctx->unicode_remaining = 0; |
| 1983 | goto unicode_error; |
| 1984 | } |
| 1985 | if (ctx->unicode_remaining == 2 && ctx->code_point < 0x10000) { |
| 1986 | ctx->unicode_remaining = 0; |
| 1987 | goto unicode_error; |
| 1988 | } |
| 1989 | if (ctx->unicode_remaining == 2 && ctx->code_point > 0x10ffff) { |
| 1990 | ctx->unicode_remaining = 0; |
| 1991 | goto unicode_error; |
| 1992 | } |
| 1993 | |
| 1994 | if (ctx->unicode_remaining != 0) { |
| 1995 | return; |
| 1996 | } |
| 1997 | |
| 1998 | if (ctx->code_point >= 0xd800 && ctx->code_point <= 0xdfff) { |
| 1999 | goto unicode_error; |
| 2000 | } |
| 2001 | |
| 2002 | int cc = unicode_to_cp437(code_point: ctx->code_point); |
| 2003 | |
| 2004 | if (cc == -1) { |
| 2005 | int replacement_width = mk_wcwidth(ucs: ctx->code_point); |
| 2006 | if (replacement_width > 0) { |
| 2007 | insert_shift(ctx, count: replacement_width); |
| 2008 | ctx->last_printed_char = 0xfe; |
| 2009 | ctx->last_was_graphic = true; |
| 2010 | ctx->raw_putchar(ctx, 0xfe); |
| 2011 | } |
| 2012 | for (int i = 1; i < replacement_width; i++) { |
| 2013 | ctx->raw_putchar(ctx, ' '); |
| 2014 | } |
| 2015 | } else { |
| 2016 | insert_shift(ctx, count: 1); |
| 2017 | ctx->last_printed_char = cc; |
| 2018 | ctx->last_was_graphic = true; |
| 2019 | ctx->raw_putchar(ctx, cc); |
| 2020 | } |
| 2021 | return; |
| 2022 | } |
| 2023 | |
| 2024 | unicode_error: |
| 2025 | if (c >= 0xc2 && c <= 0xf4) { |
| 2026 | ctx->g_select = 0; |
| 2027 | if (c >= 0xc2 && c <= 0xdf) { |
| 2028 | ctx->unicode_remaining = 1; |
| 2029 | ctx->code_point = (uint64_t)(c & 0x1f) << 6; |
| 2030 | } else if (c >= 0xe0 && c <= 0xef) { |
| 2031 | ctx->unicode_remaining = 2; |
| 2032 | ctx->code_point = (uint64_t)(c & 0x0f) << (6 * 2); |
| 2033 | } else if (c >= 0xf0 && c <= 0xf4) { |
| 2034 | ctx->unicode_remaining = 3; |
| 2035 | ctx->code_point = (uint64_t)(c & 0x07) << (6 * 3); |
| 2036 | } |
| 2037 | return; |
| 2038 | } |
| 2039 | |
| 2040 | if (ctx->escape == true) { |
| 2041 | escape_parse(ctx, c); |
| 2042 | return; |
| 2043 | } |
| 2044 | |
| 2045 | if (ctx->g_select) { |
| 2046 | if (c <= 0x1f || c == 0x7f) { |
| 2047 | ctx->g_select = 0; |
| 2048 | } else { |
| 2049 | ctx->g_select--; |
| 2050 | switch (c) { |
| 2051 | case 'B': |
| 2052 | ctx->charsets[ctx->g_select] = CHARSET_DEFAULT; break; |
| 2053 | case '0': |
| 2054 | ctx->charsets[ctx->g_select] = CHARSET_DEC_SPECIAL; break; |
| 2055 | } |
| 2056 | ctx->g_select = 0; |
| 2057 | return; |
| 2058 | } |
| 2059 | } |
| 2060 | |
| 2061 | if ((c <= 0x1f && c != 0x1b) || c == 0x7f) { |
| 2062 | ctx->last_was_graphic = false; |
| 2063 | } |
| 2064 | |
| 2065 | switch (c) { |
| 2066 | case 0x00: |
| 2067 | case 0x7f: |
| 2068 | return; |
| 2069 | case 0x1b: |
| 2070 | ctx->escape_offset = 0; |
| 2071 | ctx->escape = true; |
| 2072 | return; |
| 2073 | } |
| 2074 | |
| 2075 | if (c < 0x20) { |
| 2076 | execute_c0(ctx, c); |
| 2077 | return; |
| 2078 | } |
| 2079 | |
| 2080 | insert_shift(ctx, count: 1); |
| 2081 | |
| 2082 | // Translate character set |
| 2083 | switch (ctx->charsets[ctx->current_charset]) { |
| 2084 | case CHARSET_DEFAULT: |
| 2085 | break; |
| 2086 | case CHARSET_DEC_SPECIAL: |
| 2087 | if (dec_special_print(ctx, c)) { |
| 2088 | return; |
| 2089 | } |
| 2090 | break; |
| 2091 | } |
| 2092 | |
| 2093 | if (c >= 0x20 && c <= 0x7e) { |
| 2094 | ctx->last_printed_char = c; |
| 2095 | ctx->last_was_graphic = true; |
| 2096 | ctx->raw_putchar(ctx, c); |
| 2097 | } else if (c >= 0x80) { |
| 2098 | ctx->last_printed_char = 0xfe; |
| 2099 | ctx->last_was_graphic = true; |
| 2100 | ctx->raw_putchar(ctx, 0xfe); |
| 2101 | } |
| 2102 | } |
| 2103 | |
| 2104 | void flanterm_flush(struct flanterm_context *ctx) { |
| 2105 | ctx->double_buffer_flush(ctx); |
| 2106 | } |
| 2107 | |
| 2108 | void flanterm_full_refresh(struct flanterm_context *ctx) { |
| 2109 | ctx->full_refresh(ctx); |
| 2110 | } |
| 2111 | |
| 2112 | void flanterm_deinit(struct flanterm_context *ctx, void (*_free)(void *, size_t)) { |
| 2113 | ctx->deinit(ctx, _free); |
| 2114 | } |
| 2115 | |
| 2116 | void flanterm_get_dimensions(struct flanterm_context *ctx, size_t *cols, size_t *rows) { |
| 2117 | *cols = ctx->cols; |
| 2118 | *rows = ctx->rows; |
| 2119 | } |
| 2120 | |
| 2121 | void flanterm_set_autoflush(struct flanterm_context *ctx, bool state) { |
| 2122 | ctx->autoflush = state; |
| 2123 | } |
| 2124 | |
| 2125 | void flanterm_set_callback(struct flanterm_context *ctx, void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t)) { |
| 2126 | ctx->callback = callback; |
| 2127 | } |
| 2128 | |
| 2129 | void flanterm_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y) { |
| 2130 | ctx->get_cursor_pos(ctx, x, y); |
| 2131 | } |
| 2132 | |
| 2133 | void flanterm_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y) { |
| 2134 | if (x >= ctx->cols) { |
| 2135 | x = ctx->cols - 1; |
| 2136 | } |
| 2137 | |
| 2138 | if (y >= ctx->rows) { |
| 2139 | y = ctx->rows - 1; |
| 2140 | } |
| 2141 | |
| 2142 | ctx->set_cursor_pos(ctx, x, y); |
| 2143 | } |
| 2144 | |
| 2145 | void flanterm_set_text_fg(struct flanterm_context *ctx, size_t colour, bool bright) { |
| 2146 | ctx->current_primary = colour; |
| 2147 | |
| 2148 | if (bright) { |
| 2149 | if (!ctx->reverse_video) { |
| 2150 | ctx->set_text_fg_bright(ctx, colour); |
| 2151 | } else { |
| 2152 | ctx->set_text_bg_bright(ctx, colour); |
| 2153 | } |
| 2154 | } else { |
| 2155 | if (!ctx->reverse_video) { |
| 2156 | ctx->set_text_fg(ctx, colour); |
| 2157 | } else { |
| 2158 | ctx->set_text_bg(ctx, colour); |
| 2159 | } |
| 2160 | } |
| 2161 | } |
| 2162 | |
| 2163 | void flanterm_set_text_bg(struct flanterm_context *ctx, size_t colour, bool bright) { |
| 2164 | ctx->current_bg = colour; |
| 2165 | |
| 2166 | if (bright) { |
| 2167 | if (!ctx->reverse_video) { |
| 2168 | ctx->set_text_bg_bright(ctx, colour); |
| 2169 | } else { |
| 2170 | ctx->set_text_fg_bright(ctx, colour); |
| 2171 | } |
| 2172 | } else { |
| 2173 | if (!ctx->reverse_video) { |
| 2174 | ctx->set_text_bg(ctx, colour); |
| 2175 | } else { |
| 2176 | ctx->set_text_fg(ctx, colour); |
| 2177 | } |
| 2178 | } |
| 2179 | } |
| 2180 | |
| 2181 | void flanterm_reset_text_fg(struct flanterm_context *ctx) { |
| 2182 | ctx->current_primary = (size_t)-1; |
| 2183 | |
| 2184 | if (!ctx->bold) { |
| 2185 | if (!ctx->reverse_video) { |
| 2186 | ctx->set_text_fg_default(ctx); |
| 2187 | } else { |
| 2188 | ctx->set_text_bg_default(ctx); |
| 2189 | } |
| 2190 | } else { |
| 2191 | if (!ctx->reverse_video) { |
| 2192 | ctx->set_text_fg_default_bright(ctx); |
| 2193 | } else { |
| 2194 | ctx->set_text_bg_default_bright(ctx); |
| 2195 | } |
| 2196 | } |
| 2197 | } |
| 2198 | |
| 2199 | void flanterm_reset_text_bg(struct flanterm_context *ctx) { |
| 2200 | ctx->current_bg = (size_t)-1; |
| 2201 | |
| 2202 | if (!ctx->bg_bold) { |
| 2203 | if (!ctx->reverse_video) { |
| 2204 | ctx->set_text_bg_default(ctx); |
| 2205 | } else { |
| 2206 | ctx->set_text_fg_default(ctx); |
| 2207 | } |
| 2208 | } else { |
| 2209 | if (!ctx->reverse_video) { |
| 2210 | ctx->set_text_bg_default_bright(ctx); |
| 2211 | } else { |
| 2212 | ctx->set_text_fg_default_bright(ctx); |
| 2213 | } |
| 2214 | } |
| 2215 | } |
| 2216 | |
| 2217 | void flanterm_clear(struct flanterm_context *ctx, bool move) { |
| 2218 | ctx->clear(ctx, move); |
| 2219 | } |
| 2220 | |
| 2221 | |