2005-09-15 09:58:31 -04:00
|
|
|
/* Terminal screen drawing routines. */
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "elinks.h"
|
|
|
|
|
|
|
|
#include "config/options.h"
|
|
|
|
#include "intl/charsets.h"
|
2006-05-20 08:59:40 -04:00
|
|
|
#include "main/module.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "osdep/ascii.h"
|
|
|
|
#include "osdep/osdep.h"
|
|
|
|
#include "terminal/color.h"
|
|
|
|
#include "terminal/draw.h"
|
|
|
|
#include "terminal/hardio.h"
|
|
|
|
#include "terminal/kbd.h"
|
|
|
|
#include "terminal/screen.h"
|
|
|
|
#include "terminal/terminal.h"
|
|
|
|
#include "util/conv.h"
|
|
|
|
#include "util/error.h"
|
|
|
|
#include "util/memory.h"
|
|
|
|
#include "util/string.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO: We must use termcap/terminfo if available! --pasky */
|
|
|
|
|
2007-01-24 17:05:36 -05:00
|
|
|
const unsigned char frame_dumb[48] = " ||||++||++++++--|-+||++--|-+----++++++++ ";
|
|
|
|
static const unsigned char frame_vt100[48] = "aaaxuuukkuxkjjjkmvwtqnttmlvwtqnvvwwmmllnnjla ";
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
/* For UTF8 I/O */
|
2007-01-24 17:05:36 -05:00
|
|
|
static const unsigned char frame_vt100_u[48] = {
|
2005-09-15 09:58:31 -04:00
|
|
|
177, 177, 177, 179, 180, 180, 180, 191,
|
|
|
|
191, 180, 179, 191, 217, 217, 217, 191,
|
|
|
|
192, 193, 194, 195, 196, 197, 195, 195,
|
|
|
|
192, 218, 193, 194, 195, 196, 197, 193,
|
|
|
|
193, 194, 194, 192, 192, 218, 218, 197,
|
|
|
|
197, 217, 218, 177, 32, 32, 32, 32
|
|
|
|
};
|
|
|
|
|
2007-05-19 16:32:36 -04:00
|
|
|
/* This is for FreeBSD fonts that place graphics characters in the
|
|
|
|
* 0x80...0x9F range, which ISO 8859 does not use. The characters are
|
|
|
|
* supposed to be sorted according to the codes used in VT100 or in
|
|
|
|
* the terminfo "acsc" capability:
|
|
|
|
*
|
|
|
|
* 0x80 U+2588 '0' ACS_BLOCK
|
|
|
|
* 0x81 U+25C6 '`' ACS_DIAMOND
|
|
|
|
* 0x82 U+2592 'a' ACS_CKBOARD
|
|
|
|
* 0x83 U+2409 'b' -
|
|
|
|
* 0x84 U+240C 'c' -
|
|
|
|
* 0x85 U+240D 'd' -
|
|
|
|
* 0x86 U+240A 'e' -
|
|
|
|
* 0x87 U+00B0 'f' ACS_DEGREE
|
|
|
|
* 0x88 U+00B1 'g' ACS_PLMINUS
|
|
|
|
* 0x89 U+2424 'h' -
|
|
|
|
* 0x8A U+240B 'i' -
|
|
|
|
* 0x8B U+2518 'j' ACS_LRCORNER
|
|
|
|
* 0x8C U+2510 'k' ACS_URCORNER
|
|
|
|
* 0x8D U+250C 'l' ACS_ULCORNER
|
|
|
|
* 0x8E U+2514 'm' ACS_LLCORNER
|
|
|
|
* 0x8F U+253C 'n' ACS_PLUS
|
|
|
|
* 0x90 - 'o' ACS_S1
|
|
|
|
* 0x91 - 'p' ACS_S3
|
|
|
|
* 0x92 U+2500 'q' ACS_HLINE
|
|
|
|
* 0x93 - 'r' ACS_S7
|
|
|
|
* 0x94 - 's' ACS_S9
|
|
|
|
* 0x95 U+251C 't' ACS_LTEE
|
|
|
|
* 0x96 U+2524 'u' ACS_RTEE
|
|
|
|
* 0x97 U+2534 'v' ACS_BTEE
|
|
|
|
* 0x98 U+252C 'w' ACS_TTEE
|
|
|
|
* 0x99 U+2502 'x' ACS_VLINE
|
|
|
|
* 0x9A U+2264 'y' ACS_LEQUAL
|
|
|
|
* 0x9B U+2265 'z' ACS_GEQUAL
|
|
|
|
* 0x9C U+03C0 '{' ACS_PI
|
|
|
|
* 0x9D U+2260 '|' ACS_NEQUAL
|
|
|
|
* 0x9E U+00A3 '}' ACS_STERLING
|
|
|
|
* 0x9F U+00B7 '~' ACS_BULLET
|
|
|
|
*
|
|
|
|
* (Ncurses 5.5 defines ACS_BOARD using 'h' and ACS_LANTERN using 'i',
|
|
|
|
* but those are not the characters meant above.)
|
|
|
|
*
|
|
|
|
* In FreeBSD CVS, src/share/syscons/fonts/iso-8x16.fnt revision 1.1
|
|
|
|
* includes these characters, except it has a space at 0x80. In
|
|
|
|
* revision 1.2 however, all the characters not defined by ISO 8859-1
|
|
|
|
* have been blanked out. This change was made on 2001-11-22 and
|
|
|
|
* included in FreeBSD 4.6.0, which was then released in June 2002.
|
|
|
|
* Yet, support for these characters was added to Links on 2003-11-18
|
|
|
|
* and to ELinks on 2003-12-07, so perhaps we should keep it for now. */
|
2007-01-24 17:05:36 -05:00
|
|
|
static const unsigned char frame_freebsd[48] = {
|
2005-09-15 09:58:31 -04:00
|
|
|
130, 138, 128, 153, 150, 150, 150, 140,
|
|
|
|
140, 150, 153, 140, 139, 139, 139, 140,
|
|
|
|
142, 151, 152, 149, 146, 143, 149, 149,
|
|
|
|
142, 141, 151, 152, 149, 146, 143, 151,
|
|
|
|
151, 152, 152, 142, 142, 141, 141, 143,
|
|
|
|
143, 139, 141, 128, 128, 128, 128, 128,
|
|
|
|
};
|
|
|
|
|
2007-05-19 16:37:34 -04:00
|
|
|
/* For UTF-8 I/O. Derived from frame_freebsd[] by converting the
|
|
|
|
* characters to Unicode and back to CP437. frame_freebsd[1] = 138 =
|
|
|
|
* 0x8a = U+240B SYMBOL FOR VERTICAL TABULATION does not exist in
|
|
|
|
* CP437, so we substitute U+2592 MEDIUM SHADE. */
|
|
|
|
static const unsigned char frame_freebsd_u[48] = {
|
|
|
|
177, 177, 219, 179, 180, 180, 180, 191,
|
|
|
|
191, 180, 179, 191, 217, 217, 217, 191,
|
|
|
|
192, 193, 194, 195, 196, 197, 195, 195,
|
|
|
|
192, 218, 193, 194, 195, 196, 197, 193,
|
|
|
|
193, 194, 194, 192, 192, 218, 218, 197,
|
|
|
|
197, 217, 218, 219, 219, 219, 219, 219,
|
|
|
|
};
|
|
|
|
|
2007-01-24 17:05:36 -05:00
|
|
|
static const unsigned char frame_koi[48] = {
|
2005-09-15 09:58:31 -04:00
|
|
|
144, 145, 146, 129, 135, 178, 180, 167,
|
|
|
|
166, 181, 161, 168, 174, 173, 172, 131,
|
|
|
|
132, 137, 136, 134, 128, 138, 175, 176,
|
|
|
|
171, 165, 187, 184, 177, 160, 190, 185,
|
|
|
|
186, 182, 183, 170, 169, 162, 164, 189,
|
|
|
|
188, 133, 130, 141, 140, 142, 143, 139,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Most of this table is just 176 + <index in table>. */
|
2007-01-24 17:05:36 -05:00
|
|
|
static const unsigned char frame_restrict[48] = {
|
2005-09-15 09:58:31 -04:00
|
|
|
176, 177, 178, 179, 180, 179, 186, 186,
|
|
|
|
205, 185, 186, 187, 188, 186, 205, 191,
|
|
|
|
192, 193, 194, 195, 196, 197, 179, 186,
|
|
|
|
200, 201, 202, 203, 204, 205, 206, 205,
|
|
|
|
196, 205, 196, 186, 205, 205, 186, 186,
|
|
|
|
179, 217, 218, 219, 220, 221, 222, 223,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define TERM_STRING(str) INIT_STRING(str, sizeof(str) - 1)
|
|
|
|
|
2007-03-18 14:25:18 -04:00
|
|
|
/* Like add_string_to_string but has fewer checks to slow it down. */
|
2005-09-15 09:58:31 -04:00
|
|
|
#define add_term_string(str, tstr) \
|
|
|
|
add_bytes_to_string(str, (tstr).source, (tstr).length)
|
|
|
|
|
2007-01-24 17:09:07 -05:00
|
|
|
static const struct string m11_hack_frame_seqs[] = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* end border: */ TERM_STRING("\033[10m"),
|
|
|
|
/* begin border: */ TERM_STRING("\033[11m"),
|
|
|
|
};
|
|
|
|
|
2007-01-24 17:09:07 -05:00
|
|
|
static const struct string vt100_frame_seqs[] = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* end border: */ TERM_STRING("\x0f"),
|
|
|
|
/* begin border: */ TERM_STRING("\x0e"),
|
|
|
|
};
|
|
|
|
|
2007-01-24 17:11:38 -05:00
|
|
|
static const struct string underline_seqs[] = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* begin underline: */ TERM_STRING("\033[24m"),
|
|
|
|
/* end underline: */ TERM_STRING("\033[4m"),
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Used in {add_char*()} and {redraw_screen()} to reduce the logic. It is
|
|
|
|
* updated from terminal._template_.* using option change_hooks. */
|
|
|
|
/* TODO: termcap/terminfo can maybe gradually be introduced via this
|
|
|
|
* structure. We'll see. --jonas */
|
|
|
|
struct screen_driver {
|
|
|
|
LIST_HEAD(struct screen_driver);
|
|
|
|
|
|
|
|
/* The terminal._template_.type. Together with the @name member the
|
|
|
|
* uniquely identify the screen_driver. */
|
|
|
|
enum term_mode_type type;
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
struct screen_driver_opt {
|
|
|
|
/* Charsets when doing UTF8 I/O. */
|
|
|
|
/* [0] is the common charset and [1] is the frame charset.
|
|
|
|
* Test whether to use UTF8 I/O using the use_utf8_io() macro. */
|
|
|
|
int charsets[2];
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
/* The frame translation table. May be NULL. */
|
|
|
|
const unsigned char *frame;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
/* The frame mode setup and teardown sequences. May be NULL. */
|
|
|
|
const struct string *frame_seqs;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
/* The underline mode setup and teardown sequences. May be NULL. */
|
|
|
|
const struct string *underline;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
/* The color mode */
|
|
|
|
enum color_mode color_mode;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
/* These are directly derived from the terminal options. */
|
|
|
|
unsigned int transparent:1;
|
|
|
|
} opt;
|
2006-01-14 16:44:00 -05:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
/* The terminal._template_ name. */
|
|
|
|
unsigned char name[1]; /* XXX: Keep last! */
|
|
|
|
};
|
|
|
|
|
2007-05-19 02:24:08 -04:00
|
|
|
static const struct screen_driver_opt dumb_screen_driver_opt = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* charsets: */ { -1, -1 }, /* No UTF8 I/O */
|
|
|
|
/* frame: */ frame_dumb,
|
|
|
|
/* frame_seqs: */ NULL,
|
|
|
|
/* underline: */ underline_seqs,
|
|
|
|
/* color_mode: */ COLOR_MODE_16,
|
|
|
|
/* transparent: */ 1,
|
|
|
|
};
|
|
|
|
|
2007-05-19 02:24:08 -04:00
|
|
|
static const struct screen_driver_opt vt100_screen_driver_opt = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* charsets: */ { -1, -1 }, /* No UTF8 I/O */
|
|
|
|
/* frame: */ frame_vt100,
|
|
|
|
/* frame_seqs: */ vt100_frame_seqs,
|
|
|
|
/* underline: */ underline_seqs,
|
|
|
|
/* color_mode: */ COLOR_MODE_16,
|
|
|
|
/* transparent: */ 1,
|
|
|
|
};
|
|
|
|
|
2007-05-19 02:24:08 -04:00
|
|
|
static const struct screen_driver_opt linux_screen_driver_opt = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* charsets: */ { -1, -1 }, /* No UTF8 I/O */
|
|
|
|
/* frame: */ NULL, /* No restrict_852 */
|
|
|
|
/* frame_seqs: */ NULL, /* No m11_hack */
|
|
|
|
/* underline: */ underline_seqs,
|
|
|
|
/* color_mode: */ COLOR_MODE_16,
|
|
|
|
/* transparent: */ 1,
|
|
|
|
};
|
|
|
|
|
2007-05-19 02:24:08 -04:00
|
|
|
static const struct screen_driver_opt koi8_screen_driver_opt = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* charsets: */ { -1, -1 }, /* No UTF8 I/O */
|
|
|
|
/* frame: */ frame_koi,
|
|
|
|
/* frame_seqs: */ NULL,
|
|
|
|
/* underline: */ underline_seqs,
|
|
|
|
/* color_mode: */ COLOR_MODE_16,
|
|
|
|
/* transparent: */ 1,
|
|
|
|
};
|
|
|
|
|
2007-05-19 02:24:08 -04:00
|
|
|
static const struct screen_driver_opt freebsd_screen_driver_opt = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* charsets: */ { -1, -1 }, /* No UTF8 I/O */
|
|
|
|
/* frame: */ frame_freebsd,
|
|
|
|
/* frame_seqs: */ NULL, /* No m11_hack */
|
|
|
|
/* underline: */ underline_seqs,
|
|
|
|
/* color_mode: */ COLOR_MODE_16,
|
|
|
|
/* transparent: */ 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* XXX: Keep in sync with enum term_mode_type. */
|
2007-05-19 02:24:08 -04:00
|
|
|
static const struct screen_driver_opt *const screen_driver_opts[] = {
|
|
|
|
/* TERM_DUMB: */ &dumb_screen_driver_opt,
|
|
|
|
/* TERM_VT100: */ &vt100_screen_driver_opt,
|
|
|
|
/* TERM_LINUX: */ &linux_screen_driver_opt,
|
|
|
|
/* TERM_KOI8: */ &koi8_screen_driver_opt,
|
|
|
|
/* TERM_FREEBSD: */ &freebsd_screen_driver_opt,
|
2005-09-15 09:58:31 -04:00
|
|
|
};
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
#define use_utf8_io(driver) ((driver)->opt.charsets[0] != -1)
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
static INIT_LIST_HEAD(active_screen_drivers);
|
|
|
|
|
2007-05-19 02:20:37 -04:00
|
|
|
/* Set driver->opt according to driver->type and term_spec.
|
|
|
|
* Other members of *driver need not have been initialized. */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
2007-05-19 02:15:20 -04:00
|
|
|
set_screen_driver_opt(struct screen_driver *driver, struct option *term_spec)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2007-05-17 04:39:57 -04:00
|
|
|
const int cp = get_opt_codepage_tree(term_spec, "charset");
|
2007-05-17 04:10:14 -04:00
|
|
|
int utf8_io = get_opt_bool_tree(term_spec, "utf_8_io");
|
|
|
|
|
2007-05-19 02:13:56 -04:00
|
|
|
/* Copy all the original options from constants, so that this
|
|
|
|
* function need not carefully restore options one by one. */
|
2007-05-19 02:24:08 -04:00
|
|
|
copy_struct(&driver->opt, screen_driver_opts[driver->type]);
|
2007-05-19 02:13:56 -04:00
|
|
|
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-12-09 14:13:13 -05:00
|
|
|
/* Force UTF-8 I/O if the UTF-8 charset is selected. Various
|
|
|
|
* places assume that the terminal's charset is unibyte if
|
|
|
|
* UTF-8 I/O is disabled. (bug 827) */
|
2007-05-17 04:39:57 -04:00
|
|
|
if (is_cp_utf8(cp))
|
2007-05-17 04:10:14 -04:00
|
|
|
utf8_io = 1;
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.color_mode = get_opt_int_tree(term_spec, "colors");
|
|
|
|
driver->opt.transparent = get_opt_bool_tree(term_spec, "transparency");
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (get_opt_bool_tree(term_spec, "underline")) {
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.underline = underline_seqs;
|
2005-09-15 09:58:31 -04:00
|
|
|
} else {
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.underline = NULL;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2007-05-19 06:48:49 -04:00
|
|
|
if (utf8_io) {
|
2007-05-19 07:04:34 -04:00
|
|
|
driver->opt.charsets[0] = cp;
|
|
|
|
|
2007-05-19 16:40:38 -04:00
|
|
|
/* When we're using UTF-8 I/O, we never need to switch
|
|
|
|
* charsets or fonts for drawing frames, because the
|
|
|
|
* characters encoded in UTF-8 are already unambiguous. */
|
|
|
|
driver->opt.frame_seqs = NULL;
|
|
|
|
|
2007-05-19 06:48:49 -04:00
|
|
|
if (driver->type == TERM_LINUX) {
|
|
|
|
if (get_opt_bool_tree(term_spec, "restrict_852"))
|
|
|
|
driver->opt.frame = frame_restrict;
|
2007-05-19 07:04:34 -04:00
|
|
|
driver->opt.charsets[1] = get_cp_index("cp437");
|
2007-05-19 16:45:38 -04:00
|
|
|
|
2007-05-19 06:48:49 -04:00
|
|
|
} else if (driver->type == TERM_FREEBSD) {
|
2007-05-19 16:37:34 -04:00
|
|
|
driver->opt.frame = frame_freebsd_u;
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.charsets[1] = get_cp_index("cp437");
|
2007-05-19 16:45:38 -04:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
} else if (driver->type == TERM_VT100) {
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.frame = frame_vt100_u;
|
2007-05-19 07:11:03 -04:00
|
|
|
driver->opt.charsets[1] = get_cp_index("cp437");
|
2007-05-19 16:45:38 -04:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
} else if (driver->type == TERM_KOI8) {
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.charsets[1] = get_cp_index("koi8-r");
|
2007-05-19 16:45:38 -04:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
} else {
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.charsets[1] = driver->opt.charsets[0];
|
2007-05-19 07:04:34 -04:00
|
|
|
}
|
2007-05-19 16:45:38 -04:00
|
|
|
|
2007-05-19 06:48:49 -04:00
|
|
|
} else { /* !utf8_io */
|
2007-05-19 07:04:34 -04:00
|
|
|
driver->opt.charsets[0] = -1;
|
|
|
|
|
2007-05-19 06:48:49 -04:00
|
|
|
if (driver->type == TERM_LINUX) {
|
|
|
|
if (get_opt_bool_tree(term_spec, "restrict_852"))
|
|
|
|
driver->opt.frame = frame_restrict;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-19 06:48:49 -04:00
|
|
|
if (get_opt_bool_tree(term_spec, "m11_hack"))
|
|
|
|
driver->opt.frame_seqs = m11_hack_frame_seqs;
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
} else if (driver->type == TERM_FREEBSD) {
|
|
|
|
if (get_opt_bool_tree(term_spec, "m11_hack"))
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.frame_seqs = m11_hack_frame_seqs;
|
2007-05-19 07:07:23 -04:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
} else if (driver->type == TERM_VT100) {
|
2007-05-19 01:58:59 -04:00
|
|
|
driver->opt.frame = frame_vt100;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
2007-05-19 06:48:49 -04:00
|
|
|
} /* !utf8_io */
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
screen_driver_change_hook(struct session *ses, struct option *term_spec,
|
|
|
|
struct option *changed)
|
|
|
|
{
|
|
|
|
enum term_mode_type type = get_opt_int_tree(term_spec, "type");
|
|
|
|
struct screen_driver *driver;
|
|
|
|
unsigned char *name = term_spec->name;
|
|
|
|
int len = strlen(name);
|
|
|
|
|
|
|
|
foreach (driver, active_screen_drivers)
|
|
|
|
if (driver->type == type && !memcmp(driver->name, name, len)) {
|
2007-05-19 02:15:20 -04:00
|
|
|
set_screen_driver_opt(driver, term_spec);
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct screen_driver *
|
|
|
|
add_screen_driver(enum term_mode_type type, struct terminal *term, int env_len)
|
|
|
|
{
|
|
|
|
struct screen_driver *driver;
|
|
|
|
|
|
|
|
/* One byte is reserved for name in struct screen_driver. */
|
|
|
|
driver = mem_alloc(sizeof(*driver) + env_len);
|
|
|
|
if (!driver) return NULL;
|
|
|
|
|
2007-05-19 02:20:37 -04:00
|
|
|
/* These four operations fully initialize *driver. */
|
2005-09-15 09:58:31 -04:00
|
|
|
add_to_list(active_screen_drivers, driver);
|
2007-05-19 02:20:37 -04:00
|
|
|
driver->type = type;
|
2007-05-19 02:15:20 -04:00
|
|
|
set_screen_driver_opt(driver, term->spec);
|
2007-05-19 02:20:37 -04:00
|
|
|
memcpy(driver->name, term->spec->name, env_len + 1);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
term->spec->change_hook = screen_driver_change_hook;
|
|
|
|
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
term->utf8 = use_utf8_io(driver);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-01-14 16:44:00 -05:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
return driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct screen_driver *
|
|
|
|
get_screen_driver(struct terminal *term)
|
|
|
|
{
|
|
|
|
enum term_mode_type type = get_opt_int_tree(term->spec, "type");
|
|
|
|
unsigned char *name = term->spec->name;
|
|
|
|
int len = strlen(name);
|
|
|
|
struct screen_driver *driver;
|
|
|
|
|
|
|
|
foreach (driver, active_screen_drivers) {
|
|
|
|
if (driver->type != type) continue;
|
|
|
|
if (memcmp(driver->name, name, len + 1)) continue;
|
|
|
|
|
|
|
|
/* Some simple probably useless MRU ;) */
|
|
|
|
move_to_top_of_list(active_screen_drivers, driver);
|
|
|
|
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
term->utf8 = use_utf8_io(driver);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
return driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
return add_screen_driver(type, term, len);
|
|
|
|
}
|
|
|
|
|
2006-05-20 08:59:40 -04:00
|
|
|
/* Release private screen drawing utilities. */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
2006-05-20 08:59:40 -04:00
|
|
|
done_screen_drivers(struct module *xxx)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
free_list(active_screen_drivers);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Adds the term code for positioning the cursor at @x and @y to @string.
|
|
|
|
* The template term code is: "\033[<@y>;<@x>H" */
|
|
|
|
static inline struct string *
|
|
|
|
add_cursor_move_to_string(struct string *screen, int y, int x)
|
|
|
|
{
|
|
|
|
#define CURSOR_NUM_LEN 10 /* 10 chars for @y and @x numbers should be more than enough. */
|
|
|
|
unsigned char code[4 + 2 * CURSOR_NUM_LEN + 1];
|
|
|
|
unsigned int length = 2;
|
|
|
|
|
|
|
|
code[0] = '\033';
|
|
|
|
code[1] = '[';
|
|
|
|
|
|
|
|
if (ulongcat(code, &length, y, CURSOR_NUM_LEN, 0) < 0)
|
|
|
|
return screen;
|
|
|
|
|
|
|
|
code[length++] = ';';
|
|
|
|
|
|
|
|
if (ulongcat(code, &length, x, CURSOR_NUM_LEN, 0) < 0)
|
|
|
|
return screen;
|
|
|
|
|
|
|
|
code[length++] = 'H';
|
|
|
|
|
|
|
|
return add_bytes_to_string(screen, code, length);
|
|
|
|
#undef CURSOR_NUM_LEN
|
|
|
|
}
|
|
|
|
|
|
|
|
struct screen_state {
|
|
|
|
unsigned char border;
|
|
|
|
unsigned char underline;
|
|
|
|
unsigned char bold;
|
|
|
|
unsigned char attr;
|
|
|
|
/* Following should match struct screen_char color field. */
|
2006-08-20 15:24:20 -04:00
|
|
|
unsigned char color[SCREEN_COLOR_SIZE];
|
2005-09-15 09:58:31 -04:00
|
|
|
};
|
|
|
|
|
2006-08-19 17:39:40 -04:00
|
|
|
#if defined(CONFIG_TRUE_COLOR)
|
|
|
|
#define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }
|
|
|
|
#elif defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
|
2005-09-15 09:58:31 -04:00
|
|
|
#define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0, { 0xFF, 0xFF } }
|
|
|
|
#else
|
|
|
|
#define INIT_SCREEN_STATE { 0xFF, 0xFF, 0xFF, 0, { 0xFF } }
|
|
|
|
#endif
|
|
|
|
|
2006-08-20 08:51:06 -04:00
|
|
|
#ifdef CONFIG_TRUE_COLOR
|
|
|
|
static inline int
|
|
|
|
compare_color_true(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return !memcmp(a, b, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
compare_bg_color_true(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return (a[3] == b[3] && a[4] == b[4] && a[5] == b[5]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
compare_fg_color_true(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return (a[0] == b[0] && a[1] == b[1] && a[2] == b[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
copy_color_true(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
memcpy(a, b, 6);
|
|
|
|
}
|
2006-08-20 09:19:25 -04:00
|
|
|
|
|
|
|
static inline int
|
|
|
|
background_is_black(unsigned char *a)
|
|
|
|
{
|
|
|
|
static unsigned char b[6] = {0, 0, 0, 0, 0, 0};
|
|
|
|
|
|
|
|
return compare_bg_color_true(a, b);
|
|
|
|
}
|
2006-08-19 17:39:40 -04:00
|
|
|
#endif
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-08-20 08:51:06 -04:00
|
|
|
#if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
|
|
|
|
static inline int
|
|
|
|
compare_color_256(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return (a[0] == b[0] && a[1] == b[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
compare_bg_color_256(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return (a[1] == b[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
compare_fg_color_256(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return (a[0] == b[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
copy_color_256(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
a[0] = b[0];
|
|
|
|
a[1] = b[1];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
compare_color_16(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return (a[0] == b[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
compare_bg_color_16(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return (TERM_COLOR_BACKGROUND_16(a) == TERM_COLOR_BACKGROUND_16(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
compare_fg_color_16(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
return (TERM_COLOR_FOREGROUND_16(a) == TERM_COLOR_FOREGROUND_16(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
copy_color_16(unsigned char *a, unsigned char *b)
|
|
|
|
{
|
|
|
|
a[0] = b[0];
|
|
|
|
}
|
|
|
|
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2005-09-15 09:58:31 -04:00
|
|
|
static inline void
|
|
|
|
add_char_data(struct string *screen, struct screen_driver *driver,
|
2006-01-14 16:44:00 -05:00
|
|
|
unicode_val_T data, unsigned char border)
|
2007-05-19 16:45:38 -04:00
|
|
|
#else /* !CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
static inline void
|
|
|
|
add_char_data(struct string *screen, struct screen_driver *driver,
|
|
|
|
unsigned char data, unsigned char border)
|
2007-05-19 16:45:38 -04:00
|
|
|
#endif /* !CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2007-01-27 03:25:51 -05:00
|
|
|
/* CONFIG_UTF8 use_utf8_io border data add_to_string
|
|
|
|
* ----------- ----------- ------ ---------------- ----------------
|
|
|
|
* not defined 0 0 terminal unibyte terminal unibyte
|
|
|
|
* not defined 0 1 enum border_char border unibyte
|
|
|
|
* not defined 1 0 terminal unibyte UTF-8
|
|
|
|
* not defined 1 1 enum border_char UTF-8
|
|
|
|
* defined 0 0 terminal unibyte terminal unibyte
|
|
|
|
* defined 0 1 enum border_char border unibyte
|
|
|
|
* defined 1 0 UTF-32 UTF-8
|
2007-05-19 07:20:58 -04:00
|
|
|
* defined 1 1 enum border_char UTF-8
|
2007-01-27 04:12:22 -05:00
|
|
|
*
|
2007-05-20 03:54:02 -04:00
|
|
|
* For "UTF-32" above, data can also be UCS_NO_CHAR,
|
|
|
|
* in which case this function must not alter *screen.
|
2007-01-27 03:25:51 -05:00
|
|
|
*/
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
if (border && driver->opt.frame && data >= 176 && data < 224)
|
|
|
|
data = driver->opt.frame[data - 176];
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (use_utf8_io(driver)) {
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2007-05-19 07:20:58 -04:00
|
|
|
if (border) {
|
|
|
|
int charset = driver->opt.charsets[!!border];
|
|
|
|
|
|
|
|
add_to_string(screen, cp2utf8(charset,
|
|
|
|
(unsigned char) data));
|
|
|
|
} else {
|
2007-05-20 03:54:02 -04:00
|
|
|
if (data == UCS_NO_CHAR)
|
|
|
|
return;
|
|
|
|
|
2007-01-27 04:12:22 -05:00
|
|
|
if (!isscreensafe_ucs(data))
|
|
|
|
data = UCS_SPACE;
|
|
|
|
add_to_string(screen, encode_utf8(data));
|
|
|
|
}
|
2007-05-19 16:45:38 -04:00
|
|
|
#else /* !CONFIG_UTF8 */
|
2007-05-19 01:58:59 -04:00
|
|
|
int charset = driver->opt.charsets[!!border];
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-01-27 04:12:22 -05:00
|
|
|
if (border || isscreensafe(data))
|
|
|
|
add_to_string(screen, cp2utf8(charset, data));
|
|
|
|
else /* UCS_SPACE <= 0x7F and so fits in one UTF-8 byte */
|
|
|
|
add_char_to_string(screen, UCS_SPACE);
|
2007-05-19 16:45:38 -04:00
|
|
|
#endif /* !CONFIG_UTF8 */
|
2007-01-27 04:12:22 -05:00
|
|
|
} else {
|
|
|
|
if (border || isscreensafe(data))
|
|
|
|
add_char_to_string(screen, (unsigned char)data);
|
|
|
|
else
|
|
|
|
add_char_to_string(screen, ' ');
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Time critical section. */
|
|
|
|
static inline void
|
|
|
|
add_char16(struct string *screen, struct screen_driver *driver,
|
|
|
|
struct screen_char *ch, struct screen_state *state)
|
|
|
|
{
|
|
|
|
unsigned char border = (ch->attr & SCREEN_ATTR_FRAME);
|
|
|
|
unsigned char underline = (ch->attr & SCREEN_ATTR_UNDERLINE);
|
|
|
|
unsigned char bold = (ch->attr & SCREEN_ATTR_BOLD);
|
|
|
|
|
2006-03-04 18:37:10 -05:00
|
|
|
if (
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-03-04 18:37:10 -05:00
|
|
|
(!use_utf8_io(driver) || ch->data != UCS_NO_CHAR) &&
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2007-05-19 01:58:59 -04:00
|
|
|
border != state->border && driver->opt.frame_seqs
|
2006-03-04 18:37:10 -05:00
|
|
|
) {
|
2005-09-15 09:58:31 -04:00
|
|
|
state->border = border;
|
2007-05-19 01:58:59 -04:00
|
|
|
add_term_string(screen, driver->opt.frame_seqs[!!border]);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2006-03-04 18:37:10 -05:00
|
|
|
if (
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-03-04 18:37:10 -05:00
|
|
|
(!use_utf8_io(driver) || ch->data != UCS_NO_CHAR) &&
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2007-05-19 01:58:59 -04:00
|
|
|
underline != state->underline && driver->opt.underline
|
2006-03-04 18:37:10 -05:00
|
|
|
) {
|
2005-09-15 09:58:31 -04:00
|
|
|
state->underline = underline;
|
2007-05-19 01:58:59 -04:00
|
|
|
add_term_string(screen, driver->opt.underline[!!underline]);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2006-03-04 18:37:10 -05:00
|
|
|
if (
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-03-04 18:37:10 -05:00
|
|
|
(!use_utf8_io(driver) || ch->data != UCS_NO_CHAR) &&
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-03-04 18:37:10 -05:00
|
|
|
bold != state->bold
|
|
|
|
) {
|
2005-09-15 09:58:31 -04:00
|
|
|
state->bold = bold;
|
|
|
|
if (bold) {
|
|
|
|
add_bytes_to_string(screen, "\033[1m", 4);
|
|
|
|
} else {
|
|
|
|
/* Force repainting of the other attributes. */
|
|
|
|
state->color[0] = ch->color[0] + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-04 18:37:10 -05:00
|
|
|
if (
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-03-04 18:37:10 -05:00
|
|
|
(!use_utf8_io(driver) || ch->data != UCS_NO_CHAR) &&
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-08-20 08:51:06 -04:00
|
|
|
!compare_color_16(ch->color, state->color)
|
2006-03-04 18:37:10 -05:00
|
|
|
) {
|
2006-08-20 08:51:06 -04:00
|
|
|
copy_color_16(state->color, ch->color);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
add_bytes_to_string(screen, "\033[0", 3);
|
|
|
|
|
2007-05-19 02:15:20 -04:00
|
|
|
/* @set_screen_driver_opt has set @driver->opt.color_mode
|
2006-12-25 04:25:06 -05:00
|
|
|
* according to terminal-type-specific options.
|
|
|
|
* The caller of @add_char16 has already partially
|
|
|
|
* checked it, but there are still these possibilities:
|
|
|
|
* - COLOR_MODE_MONO. Then don't show colors, but
|
|
|
|
* perhaps use the standout attribute.
|
|
|
|
* - COLOR_MODE_16. Use 16 colors.
|
|
|
|
* - An unsupported color mode. Use 16 colors. */
|
2007-05-19 01:58:59 -04:00
|
|
|
if (driver->opt.color_mode != COLOR_MODE_MONO) {
|
2005-09-15 09:58:31 -04:00
|
|
|
unsigned char code[6] = ";30;40";
|
2006-08-20 08:51:06 -04:00
|
|
|
unsigned char bgcolor = TERM_COLOR_BACKGROUND_16(ch->color);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-08-20 08:51:06 -04:00
|
|
|
code[2] += TERM_COLOR_FOREGROUND_16(ch->color);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
if (!driver->opt.transparent || bgcolor != 0) {
|
2005-09-15 09:58:31 -04:00
|
|
|
code[5] += bgcolor;
|
|
|
|
add_bytes_to_string(screen, code, 6);
|
|
|
|
} else {
|
|
|
|
add_bytes_to_string(screen, code, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (ch->attr & SCREEN_ATTR_STANDOUT) {
|
|
|
|
/* Flip the fore- and background colors for highlighing
|
|
|
|
* purposes. */
|
|
|
|
add_bytes_to_string(screen, ";7", 2);
|
|
|
|
}
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
if (underline && driver->opt.underline) {
|
2005-09-15 09:58:31 -04:00
|
|
|
add_bytes_to_string(screen, ";4", 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the char should be rendered bold. */
|
|
|
|
if (bold) {
|
|
|
|
add_bytes_to_string(screen, ";1", 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
add_bytes_to_string(screen, "m", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
add_char_data(screen, driver, ch->data, border);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
|
2007-01-24 18:04:19 -05:00
|
|
|
static const struct string color256_seqs[] = {
|
2005-09-15 09:58:31 -04:00
|
|
|
/* foreground: */ TERM_STRING("\033[0;38;5;%dm"),
|
|
|
|
/* background: */ TERM_STRING("\033[48;5;%dm"),
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline void
|
2007-01-24 18:04:19 -05:00
|
|
|
add_char_color(struct string *screen, const struct string *seq, unsigned char color)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
unsigned char color_buf[3];
|
|
|
|
unsigned char *color_pos = color_buf;
|
|
|
|
int seq_pos = 0;
|
|
|
|
int color_len = 1;
|
|
|
|
|
|
|
|
check_string_magic(seq);
|
|
|
|
for (; seq->source[seq_pos] != '%'; seq_pos++) ;
|
|
|
|
|
|
|
|
add_bytes_to_string(screen, seq->source, seq_pos);
|
|
|
|
|
|
|
|
if (color < 10) {
|
|
|
|
color_pos += 2;
|
|
|
|
} else {
|
|
|
|
int color2;
|
|
|
|
|
|
|
|
++color_len;
|
|
|
|
if (color < 100) {
|
|
|
|
++color_pos;
|
|
|
|
} else {
|
|
|
|
++color_len;
|
|
|
|
|
|
|
|
if (color < 200) {
|
|
|
|
color_buf[0] = '1';
|
|
|
|
color -= 100;
|
|
|
|
} else {
|
|
|
|
color_buf[0] = '2';
|
|
|
|
color -= 200;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
color2 = (color % 10);
|
|
|
|
color /= 10;
|
|
|
|
color_buf[1] = '0' + color;
|
|
|
|
color = color2;
|
|
|
|
}
|
|
|
|
|
|
|
|
color_buf[2] = '0' + color;
|
|
|
|
|
|
|
|
add_bytes_to_string(screen, color_pos, color_len);
|
|
|
|
|
|
|
|
seq_pos += 2; /* Skip "%d" */
|
|
|
|
add_bytes_to_string(screen, &seq->source[seq_pos], seq->length - seq_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define add_background_color(str, seq, chr) add_char_color(str, &(seq)[1], (chr)->color[1])
|
|
|
|
#define add_foreground_color(str, seq, chr) add_char_color(str, &(seq)[0], (chr)->color[0])
|
|
|
|
|
|
|
|
/* Time critical section. */
|
|
|
|
static inline void
|
|
|
|
add_char256(struct string *screen, struct screen_driver *driver,
|
|
|
|
struct screen_char *ch, struct screen_state *state)
|
|
|
|
{
|
|
|
|
unsigned char attr_delta = (ch->attr ^ state->attr);
|
|
|
|
|
2006-03-04 18:37:10 -05:00
|
|
|
if (
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-03-04 18:37:10 -05:00
|
|
|
(!use_utf8_io(driver) || ch->data != UCS_NO_CHAR) &&
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-03-04 18:37:10 -05:00
|
|
|
attr_delta
|
|
|
|
) {
|
2007-05-19 01:58:59 -04:00
|
|
|
if ((attr_delta & SCREEN_ATTR_FRAME) && driver->opt.frame_seqs) {
|
2005-09-15 09:58:31 -04:00
|
|
|
state->border = !!(ch->attr & SCREEN_ATTR_FRAME);
|
2007-05-19 01:58:59 -04:00
|
|
|
add_term_string(screen, driver->opt.frame_seqs[state->border]);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
if ((attr_delta & SCREEN_ATTR_UNDERLINE) && driver->opt.underline) {
|
2005-09-15 09:58:31 -04:00
|
|
|
state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
|
2007-05-19 01:58:59 -04:00
|
|
|
add_term_string(screen, driver->opt.underline[state->underline]);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (attr_delta & SCREEN_ATTR_BOLD) {
|
|
|
|
if (ch->attr & SCREEN_ATTR_BOLD) {
|
|
|
|
add_bytes_to_string(screen, "\033[1m", 4);
|
|
|
|
} else {
|
|
|
|
/* Force repainting of the other attributes. */
|
|
|
|
state->color[0] = ch->color[0] + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
state->attr = ch->attr;
|
|
|
|
}
|
|
|
|
|
2006-03-04 18:37:10 -05:00
|
|
|
if (
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-03-04 18:37:10 -05:00
|
|
|
(!use_utf8_io(driver) || ch->data != UCS_NO_CHAR) &&
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-08-20 08:51:06 -04:00
|
|
|
!compare_color_256(ch->color, state->color)
|
2006-03-04 18:37:10 -05:00
|
|
|
) {
|
2006-08-20 08:51:06 -04:00
|
|
|
copy_color_256(state->color, ch->color);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
add_foreground_color(screen, color256_seqs, ch);
|
2007-05-19 01:58:59 -04:00
|
|
|
if (!driver->opt.transparent || ch->color[1] != 0) {
|
2005-09-15 09:58:31 -04:00
|
|
|
add_background_color(screen, color256_seqs, ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch->attr & SCREEN_ATTR_BOLD)
|
|
|
|
add_bytes_to_string(screen, "\033[1m", 4);
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
if (ch->attr & SCREEN_ATTR_UNDERLINE && driver->opt.underline) {
|
2005-09-15 09:58:31 -04:00
|
|
|
state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
|
2007-05-19 01:58:59 -04:00
|
|
|
add_term_string(screen, driver->opt.underline[state->underline]);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
add_char_data(screen, driver, ch->data, ch->attr & SCREEN_ATTR_FRAME);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-08-19 17:39:40 -04:00
|
|
|
#ifdef CONFIG_TRUE_COLOR
|
2007-01-24 18:04:19 -05:00
|
|
|
static const struct string color_true_seqs[] = {
|
2006-08-19 17:39:40 -04:00
|
|
|
/* foreground: */ TERM_STRING("\033[0;38;2"),
|
|
|
|
/* background: */ TERM_STRING("\033[48;2"),
|
|
|
|
};
|
|
|
|
#define add_true_background_color(str, seq, chr) add_char_true_color(str, &(seq)[1], &(chr)->color[3])
|
|
|
|
#define add_true_foreground_color(str, seq, chr) add_char_true_color(str, &(seq)[0], &(chr)->color[0])
|
|
|
|
static inline void
|
2007-01-24 18:04:19 -05:00
|
|
|
add_char_true_color(struct string *screen, const struct string *seq, unsigned char *colors)
|
2006-08-19 17:39:40 -04:00
|
|
|
{
|
|
|
|
unsigned char color_buf[3];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
check_string_magic(seq);
|
|
|
|
add_string_to_string(screen, seq);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
unsigned char *color_pos = color_buf;
|
|
|
|
int color_len = 1;
|
|
|
|
unsigned char color = colors[i];
|
|
|
|
|
|
|
|
add_char_to_string(screen, ';');
|
|
|
|
|
|
|
|
if (color < 10) {
|
|
|
|
color_pos += 2;
|
|
|
|
} else {
|
|
|
|
int color2;
|
|
|
|
|
|
|
|
++color_len;
|
|
|
|
if (color < 100) {
|
|
|
|
++color_pos;
|
|
|
|
} else {
|
|
|
|
++color_len;
|
|
|
|
|
|
|
|
if (color < 200) {
|
|
|
|
color_buf[0] = '1';
|
|
|
|
color -= 100;
|
|
|
|
} else {
|
|
|
|
color_buf[0] = '2';
|
|
|
|
color -= 200;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
color2 = (color % 10);
|
|
|
|
color /= 10;
|
|
|
|
color_buf[1] = '0' + color;
|
|
|
|
color = color2;
|
|
|
|
}
|
|
|
|
color_buf[2] = '0' + color;
|
|
|
|
|
|
|
|
add_bytes_to_string(screen, color_pos, color_len);
|
|
|
|
}
|
|
|
|
add_char_to_string(screen, 'm');
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Time critical section. */
|
|
|
|
static inline void
|
|
|
|
add_char_true(struct string *screen, struct screen_driver *driver,
|
|
|
|
struct screen_char *ch, struct screen_state *state)
|
|
|
|
{
|
|
|
|
unsigned char attr_delta = (ch->attr ^ state->attr);
|
|
|
|
|
|
|
|
if (
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-08-19 17:39:40 -04:00
|
|
|
(!use_utf8_io(driver) || ch->data != UCS_NO_CHAR) &&
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-08-19 17:39:40 -04:00
|
|
|
attr_delta
|
|
|
|
) {
|
2007-05-19 01:58:59 -04:00
|
|
|
if ((attr_delta & SCREEN_ATTR_FRAME) && driver->opt.frame_seqs) {
|
2006-08-19 17:39:40 -04:00
|
|
|
state->border = !!(ch->attr & SCREEN_ATTR_FRAME);
|
2007-05-19 01:58:59 -04:00
|
|
|
add_term_string(screen, driver->opt.frame_seqs[state->border]);
|
2006-08-19 17:39:40 -04:00
|
|
|
}
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
if ((attr_delta & SCREEN_ATTR_UNDERLINE) && driver->opt.underline) {
|
2006-08-19 17:39:40 -04:00
|
|
|
state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
|
2007-05-19 01:58:59 -04:00
|
|
|
add_term_string(screen, driver->opt.underline[state->underline]);
|
2006-08-19 17:39:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (attr_delta & SCREEN_ATTR_BOLD) {
|
|
|
|
if (ch->attr & SCREEN_ATTR_BOLD) {
|
|
|
|
add_bytes_to_string(screen, "\033[1m", 4);
|
|
|
|
} else {
|
|
|
|
/* Force repainting of the other attributes. */
|
|
|
|
state->color[0] = ch->color[0] + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
state->attr = ch->attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-08-19 17:39:40 -04:00
|
|
|
(!use_utf8_io(driver) || ch->data != UCS_NO_CHAR) &&
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-08-20 08:51:06 -04:00
|
|
|
!compare_color_true(ch->color, state->color)
|
2006-08-19 17:39:40 -04:00
|
|
|
) {
|
2006-08-20 08:51:06 -04:00
|
|
|
copy_color_true(state->color, ch->color);
|
2006-08-19 17:39:40 -04:00
|
|
|
|
|
|
|
add_true_foreground_color(screen, color_true_seqs, ch);
|
2007-05-19 01:58:59 -04:00
|
|
|
if (!driver->opt.transparent || !background_is_black(ch->color)) {
|
2006-08-19 17:39:40 -04:00
|
|
|
add_true_background_color(screen, color_true_seqs, ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch->attr & SCREEN_ATTR_BOLD)
|
|
|
|
add_bytes_to_string(screen, "\033[1m", 4);
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
if (ch->attr & SCREEN_ATTR_UNDERLINE && driver->opt.underline) {
|
2006-08-19 17:39:40 -04:00
|
|
|
state->underline = !!(ch->attr & SCREEN_ATTR_UNDERLINE);
|
2007-05-19 01:58:59 -04:00
|
|
|
add_term_string(screen, driver->opt.underline[state->underline]);
|
2006-08-19 17:39:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
add_char_data(screen, driver, ch->data, ch->attr & SCREEN_ATTR_FRAME);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-08-20 08:51:06 -04:00
|
|
|
#define add_chars(image_, term_, driver_, state_, ADD_CHAR, compare_bg_color, compare_fg_color) \
|
2005-09-15 09:58:31 -04:00
|
|
|
{ \
|
|
|
|
struct terminal_screen *screen = (term_)->screen; \
|
|
|
|
int y = screen->dirty_from; \
|
|
|
|
int ypos = y * (term_)->width; \
|
|
|
|
int prev_y = -1; \
|
|
|
|
int xmax = (term_)->width - 1; \
|
|
|
|
int ymax = (term_)->height - 1; \
|
|
|
|
struct screen_char *current = &screen->last_image[ypos]; \
|
|
|
|
struct screen_char *pos = &screen->image[ypos]; \
|
|
|
|
struct screen_char *prev_pos = NULL; /* Warning prevention. */ \
|
|
|
|
\
|
|
|
|
int_upper_bound(&screen->dirty_to, ymax); \
|
|
|
|
\
|
|
|
|
for (; y <= screen->dirty_to; y++) { \
|
|
|
|
int is_last_line = (y == ymax); \
|
|
|
|
int x = 0; \
|
|
|
|
\
|
|
|
|
for (; x <= xmax; x++, current++, pos++) { \
|
|
|
|
/* Workaround for terminals without
|
|
|
|
* "eat_newline_glitch (xn)", e.g., the cons25 family
|
|
|
|
* of terminals and cygwin terminal.
|
|
|
|
* It prevents display distortion, but char at bottom
|
|
|
|
* right of terminal will not be drawn.
|
|
|
|
* A better fix would be to correctly detects
|
|
|
|
* terminal type, and/or add a terminal option for
|
|
|
|
* this purpose. */ \
|
|
|
|
\
|
|
|
|
if (is_last_line && x == xmax) \
|
|
|
|
break; \
|
|
|
|
\
|
|
|
|
if (compare_bg_color(pos->color, current->color)) { \
|
|
|
|
/* No update for exact match. */ \
|
|
|
|
if (compare_fg_color(pos->color, current->color)\
|
|
|
|
&& pos->data == current->data \
|
|
|
|
&& pos->attr == current->attr) \
|
|
|
|
continue; \
|
|
|
|
\
|
|
|
|
/* Else if the color match and the data is
|
|
|
|
* ``space''. */ \
|
|
|
|
if (pos->data <= ' ' && current->data <= ' ' \
|
|
|
|
&& pos->attr == current->attr) \
|
|
|
|
continue; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/* Move the cursor when @prev_pos is more than 10 chars
|
|
|
|
* away. */ \
|
|
|
|
if (prev_y != y || prev_pos + 10 <= pos) { \
|
|
|
|
add_cursor_move_to_string(image_, y + 1, x + 1);\
|
|
|
|
prev_pos = pos; \
|
|
|
|
prev_y = y; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
for (; prev_pos <= pos ; prev_pos++) \
|
|
|
|
ADD_CHAR(image_, driver_, prev_pos, state_); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Updating of the terminal screen is done by checking what needs to be updated
|
|
|
|
* using the last screen. */
|
|
|
|
void
|
|
|
|
redraw_screen(struct terminal *term)
|
|
|
|
{
|
|
|
|
struct screen_driver *driver;
|
|
|
|
struct string image;
|
|
|
|
struct screen_state state = INIT_SCREEN_STATE;
|
|
|
|
struct terminal_screen *screen = term->screen;
|
|
|
|
|
|
|
|
if (!screen || screen->dirty_from > screen->dirty_to) return;
|
|
|
|
if (term->master && is_blocked()) return;
|
|
|
|
|
|
|
|
driver = get_screen_driver(term);
|
|
|
|
if (!driver) return;
|
|
|
|
|
|
|
|
if (!init_string(&image)) return;
|
|
|
|
|
2007-05-19 01:58:59 -04:00
|
|
|
switch (driver->opt.color_mode) {
|
2006-12-25 04:25:06 -05:00
|
|
|
default:
|
|
|
|
/* If the desired color mode was not compiled in,
|
|
|
|
* use 16 colors. */
|
2005-09-15 09:58:31 -04:00
|
|
|
case COLOR_MODE_MONO:
|
|
|
|
case COLOR_MODE_16:
|
2006-08-20 08:51:06 -04:00
|
|
|
add_chars(&image, term, driver, &state, add_char16, compare_bg_color_16, compare_fg_color_16);
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
#ifdef CONFIG_88_COLORS
|
|
|
|
case COLOR_MODE_88:
|
2006-08-20 08:51:06 -04:00
|
|
|
add_chars(&image, term, driver, &state, add_char256, compare_bg_color_256, compare_fg_color_256);
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_256_COLORS
|
|
|
|
case COLOR_MODE_256:
|
2006-08-20 08:51:06 -04:00
|
|
|
add_chars(&image, term, driver, &state, add_char256, compare_bg_color_256, compare_fg_color_256);
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
2006-08-19 17:39:40 -04:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_TRUE_COLOR
|
|
|
|
case COLOR_MODE_TRUE_COLOR:
|
2006-08-20 08:51:06 -04:00
|
|
|
add_chars(&image, term, driver, &state, add_char_true, compare_bg_color_true, compare_fg_color_true);
|
2006-08-19 17:39:40 -04:00
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
#endif
|
|
|
|
case COLOR_MODES:
|
|
|
|
case COLOR_MODE_DUMP:
|
2007-05-19 01:58:59 -04:00
|
|
|
INTERNAL("Invalid color mode (%d).", driver->opt.color_mode);
|
2005-09-15 09:58:31 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image.length) {
|
2007-05-19 01:58:59 -04:00
|
|
|
if (driver->opt.color_mode != COLOR_MODE_MONO)
|
2005-09-15 09:58:31 -04:00
|
|
|
add_bytes_to_string(&image, "\033[37;40m", 8);
|
|
|
|
|
|
|
|
add_bytes_to_string(&image, "\033[0m", 4);
|
|
|
|
|
|
|
|
/* If we ended in border state end the frame mode. */
|
2007-05-19 01:58:59 -04:00
|
|
|
if (state.border && driver->opt.frame_seqs)
|
|
|
|
add_term_string(&image, driver->opt.frame_seqs[0]);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Even if nothing was redrawn, we possibly still need to move
|
|
|
|
* cursor. */
|
|
|
|
if (image.length
|
|
|
|
|| screen->cx != screen->lcx
|
|
|
|
|| screen->cy != screen->lcy) {
|
|
|
|
screen->lcx = screen->cx;
|
|
|
|
screen->lcy = screen->cy;
|
|
|
|
|
|
|
|
add_cursor_move_to_string(&image, screen->cy + 1,
|
|
|
|
screen->cx + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image.length) {
|
|
|
|
if (term->master) want_draw();
|
|
|
|
hard_write(term->fdout, image.source, image.length);
|
|
|
|
if (term->master) done_draw();
|
|
|
|
}
|
|
|
|
|
|
|
|
done_string(&image);
|
|
|
|
|
|
|
|
copy_screen_chars(screen->last_image, screen->image, term->width * term->height);
|
|
|
|
screen->dirty_from = term->height;
|
|
|
|
screen->dirty_to = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
erase_screen(struct terminal *term)
|
|
|
|
{
|
|
|
|
if (term->master) {
|
|
|
|
if (is_blocked()) return;
|
|
|
|
want_draw();
|
|
|
|
}
|
|
|
|
|
|
|
|
hard_write(term->fdout, "\033[2J\033[1;1H", 10);
|
|
|
|
if (term->master) done_draw();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
beep_terminal(struct terminal *term)
|
|
|
|
{
|
2006-01-11 14:10:27 -05:00
|
|
|
#ifdef CONFIG_OS_WIN32
|
2005-09-15 09:58:31 -04:00
|
|
|
MessageBeep(MB_ICONEXCLAMATION);
|
|
|
|
#else
|
|
|
|
hard_write(term->fdout, "\a", 1);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
struct terminal_screen *
|
|
|
|
init_screen(void)
|
|
|
|
{
|
|
|
|
struct terminal_screen *screen;
|
|
|
|
|
|
|
|
screen = mem_calloc(1, sizeof(*screen));
|
|
|
|
if (!screen) return NULL;
|
|
|
|
|
|
|
|
screen->lcx = -1;
|
|
|
|
screen->lcy = -1;
|
|
|
|
|
|
|
|
return screen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The two images are allocated in one chunk. */
|
|
|
|
/* TODO: It seems allocation failure here is fatal. We should do something! */
|
|
|
|
void
|
|
|
|
resize_screen(struct terminal *term, int width, int height)
|
|
|
|
{
|
|
|
|
struct terminal_screen *screen;
|
|
|
|
struct screen_char *image;
|
|
|
|
size_t size, bsize;
|
|
|
|
|
|
|
|
assert(term && term->screen);
|
|
|
|
|
|
|
|
screen = term->screen;
|
|
|
|
|
|
|
|
assert(width >= 0);
|
|
|
|
assert(height >= 0);
|
|
|
|
|
|
|
|
size = width * height;
|
|
|
|
if (size <= 0) return;
|
|
|
|
|
|
|
|
bsize = size * sizeof(*image);
|
|
|
|
|
|
|
|
image = mem_realloc(screen->image, bsize * 2);
|
|
|
|
if (!image) return;
|
|
|
|
|
|
|
|
screen->image = image;
|
|
|
|
screen->last_image = image + size;
|
|
|
|
|
|
|
|
memset(screen->image, 0, bsize);
|
|
|
|
memset(screen->last_image, 0xFF, bsize);
|
|
|
|
|
|
|
|
term->width = width;
|
|
|
|
term->height = height;
|
|
|
|
set_screen_dirty(screen, 0, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
done_screen(struct terminal_screen *screen)
|
|
|
|
{
|
|
|
|
mem_free_if(screen->image);
|
|
|
|
mem_free(screen);
|
|
|
|
}
|
2006-05-20 08:59:40 -04:00
|
|
|
|
|
|
|
struct module terminal_screen_module = struct_module(
|
2007-03-22 18:51:56 -04:00
|
|
|
/* Because this module is a submodule of terminal_module,
|
|
|
|
* which is listed main_modules rather than in builtin_modules,
|
|
|
|
* its name does not appear in the user interface and
|
|
|
|
* so need not be translatable. */
|
|
|
|
/* name: */ "Terminal Screen",
|
2006-05-20 08:59:40 -04:00
|
|
|
/* options: */ NULL,
|
|
|
|
/* hooks: */ NULL,
|
|
|
|
/* submodules: */ NULL,
|
|
|
|
/* data: */ NULL,
|
|
|
|
/* init: */ NULL,
|
|
|
|
/* done: */ done_screen_drivers
|
|
|
|
);
|