aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.tmt637
-rw-r--r--fbcon.c244
-rw-r--r--meson.build19
-rw-r--r--tmt.c500
-rw-r--r--tmt.h140
5 files changed, 156 insertions, 1384 deletions
diff --git a/README.tmt b/README.tmt
deleted file mode 100644
index f3819df..0000000
--- a/README.tmt
+++ /dev/null
@@ -1,637 +0,0 @@
-
-============================================
-libtmt - a simple terminal emulation library
-============================================
-
-libtmt is the Tiny Mock Terminal Library. It provides emulation of a classic
-smart text terminal, by maintaining an in-memory screen image. Sending text
-and command sequences to libtmt causes it to update this in-memory image,
-which can then be examined and rendered however the user sees fit.
-
-The imagined primary goal for libtmt is to for terminal emulators and
-multiplexers; it provides the terminal emulation layer for the `mtm`_
-terminal multiplexer, for example. Other uses include screen-scraping and
-automated test harnesses.
-
-libtmt is similar in purpose to `libtsm`_, but considerably smaller (500
-lines versus 6500 lines). libtmt is also, in this author's humble opinion,
-considerably easier to use.
-
-.. _`mtm`: https://github.com/deadpixi/mtm
-.. _`libtsm`: https://www.freedesktop.org/wiki/Software/kmscon/libtsm/
-
-Major Features and Advantages
-=============================
-
-Works Out-of-the-Box
- libtmt emulates a well-known terminal type (`ansi`), the definition of
- which has been in the terminfo database since at least 1995. There's no
- need to install a custom terminfo entry. There's no claiming to be an
- xterm but only emulating a small subset of its features. Any program
- using terminfo works automatically: this includes vim, emacs, mc,
- cmus, nano, nethack, ...
-
-Portable
- Written in pure C99.
- Optionally, the POSIX-mandated `wcwidth` function can be used, which
- provides minimal support for combining characters.
-
-Small
- Less than 500 lines of C, including comments and whitespace.
-
-Free
- Released under a BSD-style license, free for commercial and
- non-commerical use, with no restrictions on source code release or
- redistribution.
-
-Simple
- Only 8 functions to learn, and really you can get by with 6!
-
-International
- libtmt internally uses wide characters exclusively, and uses your C
- library's multibyte encoding functions.
- This means that the library automatically supports any encoding that
- your operating system does.
-
-How to Use libtmt
-=================
-
-libtmt is a single C file and a single header. Just include these files
-in your project and you should be good to go.
-
-By default, libtmt uses only ISO standard C99 features,
-but see `Compile-Time Options`_ below.
-
-Example Code
-------------
-
-Below is a simple program fragment giving the flavor of libtmt.
-Note that another good example is the `mtm`_ terminal multiplexer:
-
-.. _`mtm`: https://github.com/deadpixi/mtm
-
-.. code:: c
-
- #include <stdio.h>
- #include <stdlib.h>
- #include "tmt.h"
-
- /* Forward declaration of a callback.
- * libtmt will call this function when the terminal's state changes.
- */
- void callback(tmt_msg_t m, TMT *vt, const void *a, void *p);
-
- int
- main(void)
- {
- /* Open a virtual terminal with 2 lines and 10 columns.
- * The first NULL is just a pointer that will be provided to the
- * callback; it can be anything. The second NULL specifies that
- * we want to use the default Alternate Character Set; this
- * could be a pointer to a wide string that has the desired
- * characters to be displayed when in ACS mode.
- */
- TMT *vt = tmt_open(2, 10, callback, NULL, NULL);
- if (!vt)
- return perror("could not allocate terminal"), EXIT_FAILURE;
-
- /* Write some text to the terminal, using escape sequences to
- * use a bold rendition.
- *
- * The final argument is the length of the input; 0 means that
- * libtmt will determine the length dynamically using strlen.
- */
- tmt_write(vt, "\033[1mhello, world (in bold!)\033[0m", 0);
-
- /* Writing input to the virtual terminal can (and in this case, did)
- * call the callback letting us know the screen was updated. See the
- * callback below to see how that works.
- */
- tmt_close(vt);
- return EXIT_SUCCESS;
- }
-
- void
- callback(tmt_msg_t m, TMT *vt, const void *a, void *p)
- {
- /* grab a pointer to the virtual screen */
- const TMTSCREEN *s = tmt_screen(vt);
- const TMTPOINT *c = tmt_cursor(vt);
-
- switch (m){
- case TMT_MSG_BELL:
- /* the terminal is requesting that we ring the bell/flash the
- * screen/do whatever ^G is supposed to do; a is NULL
- */
- printf("bing!\n");
- break;
-
- case TMT_MSG_UPDATE:
- /* the screen image changed; a is a pointer to the TMTSCREEN */
- for (size_t r = 0; r < s->nline; r++){
- if (s->lines[r]->dirty){
- for (size_t c = 0; c < s->ncol; c++){
- printf("contents of %zd,%zd: %lc (%s bold)\n", r, c,
- s->lines[r]->chars[c].c,
- s->lines[r]->chars[c].a.bold? "is" : "is not");
- }
- }
- }
-
- /* let tmt know we've redrawn the screen */
- tmt_clean(vt);
- break;
-
- case TMT_MSG_ANSWER:
- /* the terminal has a response to give to the program; a is a
- * pointer to a string */
- printf("terminal answered %s\n", (const char *)a);
- break;
-
- case TMT_MSG_MOVED:
- /* the cursor moved; a is a pointer to the cursor's TMTPOINT */
- printf("cursor is now at %zd,%zd\n", c->r, c->c);
- break;
- }
- }
-
-Data Types and Enumerations
----------------------------
-
-.. code:: c
-
- /* an opaque structure */
- typedef struct TMT TMT;
-
- /* possible messages sent to the callback */
- typedef enum{
- TMT_MSG_MOVED, /* the cursor changed position */
- TMT_MSG_UPDATE, /* the screen image changed */
- TMT_MSG_ANSWER, /* the terminal responded to a query */
- TMT_MSG_BELL /* the terminal bell was rung */
- } tmt_msg_T;
-
- /* a callback for the library
- * m is one of the message constants above
- * vt is a pointer to the vt structure
- * r is NULL for TMT_MSG_BELL
- * is a pointer to the cursor's TMTPOINT for TMT_MSG_MOVED
- * is a pointer to the terminal's TMTSCREEN for TMT_MSG_UPDATE
- * is a pointer to a string for TMT_MSG_ANSWER
- * p is whatever was passed to tmt_open (see below).
- */
- typedef void (*TMTCALLBACK)(tmt_msg_t m, struct TMT *vt,
- const void *r, void *p);
-
- /* color definitions */
- typedef enum{
- TMT_COLOR_BLACK,
- TMT_COLOR_RED,
- TMT_COLOR_GREEN,
- TMT_COLOR_YELLOW,
- TMT_COLOR_BLUE,
- TMT_COLOR_MAGENTA,
- TMT_COLOR_CYAN,
- TMT_COLOR_WHITE,
- TMT_COLOR_DEFAULT /* whatever the host terminal wants it to mean */
- } tmt_color_t;
-
- /* graphical rendition */
- typedef struct TMTATTRS TMTATTRS;
- struct TMTATTRS{
- bool bold; /* character is bold */
- bool dim; /* character is half-bright */
- bool underline; /* character is underlined */
- bool blink; /* character is blinking */
- bool reverse; /* character is in reverse video */
- bool invisible; /* character is invisible */
- tmt_color_t fg; /* character foreground color */
- tmt_color_t bg; /* character background color */
- };
-
- /* characters */
- typedef struct TMTCHAR TMTCHAR;
- struct TMTCHAR{
- wchar_t c; /* the character */
- TMTATTRS a; /* its rendition */
- };
-
- /* a position on the screen; upper left corner is 0,0 */
- typedef struct TMTPOINT TMTPOINT;
- struct TMTPOINT{
- size_t r; /* row */
- size_t c; /* column */
- };
-
- /* a line of characters on the screen;
- * every line is always as wide as the screen
- */
- typedef struct TMTLINE TMTLINE;
- struct TMTLINE{
- bool dirty; /* line has changed since it was last drawn */
- TMTCHAR chars; /* the contents of the line */
- };
-
- /* a virtual terminal screen image */
- typedef struct TMTSCREEN TMTSCREEN;
- struct TMTSCREEN{
- size_t nline; /* number of rows */
- size_t ncol; /* number of columns */
- TMTLINE **lines; /* the lines on the screen */
- };
-
-Functions
----------
-
-`TMT *tmt_open(size_t nrows, size_t ncols, TMTCALLBACK cb, VOID *p, const wchar *acs);`
- Creates a new virtual terminal, with `nrows` rows and `ncols` columns.
- The callback `cb` will be called on updates, and passed `p` as a final
- argument. See the definition of `tmt_msg_t` above for possible values
- of each argument to the callback.
-
- Terminals must have a size of at least two rows and two columns.
-
- `acs` specifies the characters to use when in Alternate Character Set
- (ACS) mode. The default string (used if `NULL` is specified) is::
-
- L"><^v#+:o##+++++~---_++++|<>*!fo"
-
- See `Alternate Character Set`_ for more information.
-
- Note that the callback must be ready to be called immediately, as
- it will be called after initialization of the terminal is done, but
- before the call to `tmt_open` returns.
-
-`void tmt_close(TMT *vt)`
- Close and free all resources associated with `vt`.
-
-`bool tmt_resize(TMT *vt, size_t nrows, size_t ncols)`
- Resize the virtual terminal to have `nrows` rows and `ncols` columns.
- The contents of the area in common between the two sizes will be preserved.
-
- Terminals must have a size of at least two rows and two columns.
-
- If this function returns false, the resize failed (only possible in
- out-of-memory conditions or invalid sizes). If this happens, the terminal
- is trashed and the only valid operation is the close the terminal.
-
-`void tmt_write(TMT *vt, const char *s, size_t n);`
- Write the provided string to the terminal, interpreting any escape
- sequences contained threin, and update the screen image. The last
- argument is the length of the input. If set to 0, the length is
- determined using `strlen`.
-
- The terminal's callback function may be invoked one or more times before
- a call to this function returns.
-
- The string is converted internally to a wide-character string using the
- system's current multibyte encoding. Each terminal maintains a private
- multibyte decoding state, and correctly handles mulitbyte characters that
- span multiple calls to this function (that is, the final byte(s) of `s`
- may be a partial mulitbyte character to be completed on the next call).
-
-`const TMTSCREEN *tmt_screen(const TMT *vt);`
- Returns a pointer to the terminal's screen image.
-
-`const TMTPOINT *tmt_cursor(cosnt TMT *vt);`
- Returns a pointer to the terminal's cursor position.
-
-`void tmt_clean(TMT *vt);`
- Call this after receiving a `TMT_MSG_UPDATE` or `TMT_MSG_MOVED` callback
- to let the library know that the program has handled all reported changes
- to the screen image.
-
-`void tmt_reset(TMT *vt);`
- Resets the virtual terminal to its default state (colors, multibyte
- decoding state, rendition, etc).
-
-Special Keys
-------------
-
-To send special keys to a program that is using libtmt for its display,
-write one of the `TMT_KEY_*` strings to that program's standard input
-(*not* to libtmt; it makes no sense to send any of these constants to
-libtmt itself).
-
-The following macros are defined, and are all constant strings:
-
-- TMT_KEY_UP
-- TMT_KEY_DOWN
-- TMT_KEY_RIGHT
-- TMT_KEY_LEFT
-- TMT_KEY_HOME
-- TMT_KEY_END
-- TMT_KEY_INSERT
-- TMT_KEY_BACKSPACE
-- TMT_KEY_ESCAPE
-- TMT_KEY_BACK_TAB
-- TMT_KEY_PAGE_UP
-- TMT_KEY_PAGE_DOWN
-- TMT_KEY_F1 through TMT_KEY_F10
-
-Note also that the classic PC console sent the enter key as
-a carriage return, not a linefeed. Many programs don't care,
-but some do.
-
-Compile-Time Options
---------------------
-
-There are two preprocessor macros that affect libtmt:
-
-`TMT_INVALID_CHAR`
- Define this to a wide-character. This character will be added to
- the virtual display when an invalid multibyte character sequence
- is encountered.
-
- By default (if you don't define it as something else before compiling),
- this is `((wchar_t)0xfffd)`, which is the codepoint for the Unicode
- 'REPLACEMENT CHARACTER'. Note that your system might not use Unicode,
- and its wide-character type might not be able to store a constant as
- large as `0xfffd`, in which case you'll want to use an alternative.
-
-`TMT_HAS_WCWIDTH`
- By default, libtmt uses only standard C99 features. If you define
- TMT_HAS_WCWIDTH before compiling, libtmt will use the POSIX `wcwidth`
- function to detect combining characters.
-
- Note that combining characters are still not handled particularly
- well, regardless of whether this was defined. Also note that what
- your C library's `wcwidth` considers a combining character and what
- the written language in question considers one could be different.
-
-Alternate Character Set
------------------------
-
-The terminal can be switched to and from its "Alternate Character Set" (ACS)
-using escape sequences. The ACS traditionally contained box-drawing and other
-semigraphic characters.
-
-The characters in the ACS are configurable at runtime, by passing a wide string
-to `tmt_open`. The default if none is provided (i.e. the argument is `NULL`)
-uses ASCII characters to approximate the traditional characters.
-
-The string passed to `tmt_open` must be 31 characters long. The characters,
-and their default ASCII-safe values, are in order:
-
-- RIGHT ARROW ">"
-- LEFT ARROW "<"
-- UP ARROW "^"
-- DOWN ARROW "v"
-- BLOCK "#"
-- DIAMOND "+"
-- CHECKERBOARD "#"
-- DEGREE "o"
-- PLUS/MINUS "+"
-- BOARD ":"
-- LOWER RIGHT CORNER "+"
-- UPPER RIGHT CORNER "+"
-- UPPER LEFT CORNER "+"
-- LOWER LEFT CORNER "+"
-- CROSS "+"
-- SCAN LINE 1 "~"
-- SCAN LINE 3 "-"
-- HORIZONTAL LINE "-"
-- SCAN LINE 7 "-"
-- SCAN LINE 9 "_"
-- LEFT TEE "+"
-- RIGHT TEE "+"
-- BOTTOM TEE "+"
-- TOP TEE "+"
-- VERTICAL LINE "|"
-- LESS THAN OR EQUAL "<"
-- GREATER THAN OR EQUAL ">"
-- PI "*"
-- NOT EQUAL "!"
-- POUND STERLING "f"
-- BULLET "o"
-
-If your system's wide character type's character set corresponds to the
-Universal Character Set (UCS/Unicode), the following wide string is a
-good option to use::
-
- L"→←↑↓■◆▒°±▒┘┐┌└┼⎺───⎽├┤┴┬│≤≥π≠£•"
-
-**Note that multibyte decoding is disabled in ACS mode.** The traditional
-implementations of the "ansi" terminal type (i.e. IBM PCs and compatibles)
-had no concept of multibyte encodings and used the character codes
-outside the ASCII range for various special semigraphic characters.
-(Technically they had an entire alternate character set as well via the
-code page mechanism, but that's beyond the scope of this explanation.)
-
-The end result is that the terminfo definition of "ansi" sends characters
-with the high bit set when in ACS mode. This breaks several multibyte
-encoding schemes (including, most importantly, UTF-8).
-
-As a result, libtmt does not attempt to decode multibyte characters in
-ACS mode, since that would break the multibyte encoding, the semigraphic
-characters, or both.
-
-In general this isn't a problem, since programs explicitly switch to and
-from ACS mode using escape sequences.
-
-When in ACS mode, bytes that are not special members of the alternate
-character set (that is, bytes not mapped to the string provided to
-`tmt_open`) are passed unchanged to the terminal.
-
-Supported Input and Escape Sequences
-====================================
-
-Internally libtmt uses your C library's/compiler's idea of a wide character
-for all characters, so you should be able to use whatever characters you want
-when writing to the virtual terminal (but see `Alternate Character Set`_).
-
-The following escape sequences are recognized and will be processed
-specially.
-
-In the descriptions below, "ESC" means a literal escape character and "Ps"
-means zero or more decimal numeric arguments separated by semicolons.
-In descriptions "P1", "P2", etc, refer to the first parameter, second
-parameter, and so on. If a required parameter is omitted, it defaults
-to the smallest meaningful value (zero if the command accepts zero as
-an argument, one otherwise). Any number of parameters may be passed,
-but any after the first eight are ignored.
-
-Unless explicitly stated below, cursor motions past the edges of the screen
-are ignored and do not result in scrolling. When characters are moved,
-the spaces left behind are filled with blanks and any characters moved
-off the edges of the screen are lost.
-
-====================== ======================================================================
-Sequence Action
-====================== ======================================================================
-0x07 (Bell) Callback with TMT_MSG_BELL
-0x08 (Backspace) Cursor left one cell
-0x09 (Tab) Cursor to next tab stop or end of line
-0x0a (Carriage Return) Cursor to first cell on this line
-0x0d (Linefeed) Cursor to same column one line down, scroll if needed
-ESC H Set a tabstop in this column
-ESC 7 Save cursor position and current graphical state
-ESC 8 Restore saved cursor position and current graphical state
-ESC c Reset terminal to default state
-ESC [ Ps A Cursor up P1 rows
-ESC [ Ps B Cursor down P1 rows
-ESC [ Ps C Cursor right P1 columns
-ESC [ Ps D Cursor left P1 columns
-ESC [ Ps E Cursor to first column of line P1 rows down from current
-ESC [ Ps F Cursor to first column of line P1 rows up from current
-ESC [ Ps G Cursor to column P1
-ESC [ Ps d Cursor to row P1
-ESC [ Ps H Cursor to row P1, column P2
-ESC [ Ps f Alias for ESC [ Ps H
-ESC [ Ps I Cursor to next tab stop
-ESC [ Ps J Clear screen
- P1 == 0: from cursor to end of screen
- P1 == 1: from beginning of screen to cursor
- P1 == 2: entire screen
-ESC [ Ps K Clear line
- P1 == 0: from cursor to end of line
- P1 == 1: from beginning of line to cursor
- P1 == 2: entire line
-ESC [ Ps L Insert P1 lines at cursor, scrolling lines below down
-ESC [ Ps M Delete P1 lines at cursor, scrolling lines below up
-ESC [ Ps P Delete P1 characters at cursor, moving characters to the right over
-ESC [ Ps S Scroll screen up P1 lines
-ESC [ Ps T Scroll screen down P1 lines
-ESC [ Ps X Erase P1 characters at cursor (overwrite with spaces)
-ESC [ Ps Z Go to previous tab stop
-ESC [ Ps b Repeat previous character P1 times
-ESC [ Ps c Callback with TMT_MSG_ANSWER "\033[?6c"
-ESC [ Ps g If P1 == 3, clear all tabstops
-ESC [ Ps h If P1 == 25, show the cursor (if it was hidden)
-ESC [ Ps m Change graphical rendition state; see below
-ESC [ Ps l If P1 == 25, hide the cursor
-ESC [ Ps n If P1 == 6, callback with TMT_MSG_ANSWER "\033[%d;%dR"
- with cursor row, column
-ESC [ Ps s Alias for ESC 7
-ESC [ Ps u Alias for ESC 8
-ESC [ Ps @ Insert P1 blank spaces at cursor, moving characters to the right over
-====================== ======================================================================
-
-For the `ESC [ Ps m` escape sequence above ("Set Graphic Rendition"),
-up to eight parameters may be passed; the results are cumulative:
-
-============== =================================================
-Rendition Code Meaning
-============== =================================================
-0 Reset all graphic rendition attributes to default
-1 Bold
-2 Dim (half bright)
-4 Underline
-5 Blink
-7 Reverse video
-8 Invisible
-10 Leave ACS mode
-11 Enter ACS mode
-22 Bold off
-23 Dim (half bright) off
-24 Underline off
-25 Blink off
-27 Reverse video off
-28 Invisible off
-30 Foreground black
-31 Foreground red
-32 Foreground green
-33 Foreground yellow
-34 Foreground blue
-35 Foreground magenta
-36 Foreground cyan
-37 Foreground white
-39 Foreground default color
-40 Background black
-41 Background red
-42 Background green
-43 Background yellow
-44 Background blue
-45 Background magenta
-46 Background cyan
-47 Background white
-49 Background default color
-============== =================================================
-
-Other escape sequences are recognized but ignored. This includes escape
-sequences for switching out codesets (officially, all code sets are defined
-as equivalent in libtmt), and the various "Media Copy" escape sequences
-used to print output on paper (officially, there is no printer attached
-to libtmt).
-
-Additionally, "?" characters are stripped out of escape sequence parameter
-lists for compatibility purposes.
-
-Known Issues
-============
-
-- Combining characters are "handled" by ignoring them
- (when compiled with `TMT_HAS_WCWIDTH`) or by printing them separately.
-- Double-width characters are rendered as single-width invalid
- characters.
-- The documentation and error messages are available only in English.
-
-Frequently Asked Questions
-==========================
-
-What programs work with libtmt?
--------------------------------
-
-Pretty much all of them. Any program that doesn't assume what terminal
-it's running under should work without problem; this includes any program
-that uses the terminfo, termcap, or (pd|n)?curses libraries. Any program
-that assumes it's running under some specific terminal might fail if its
-assumption is wrong, and not just under libtmt.
-
-I've tested quite a few applications in libtmt and they've worked flawlessly:
-vim, GNU emacs, nano, cmus, mc (Midnight Commander), and others just work
-with no changes.
-
-What programs don't work with libtmt?
--------------------------------------
-
-Breakage with libtmt is of two kinds: breakage due to assuming a terminal
-type, and reduced functionality.
-
-In all my testing, I only found one program that didn't work correctly by
-default with libtmt: recent versions of Debian's `apt`_ assume a terminal
-with definable scrolling regions to draw a fancy progress bar during
-package installation. Using apt in its default configuration in libtmt will
-result in a corrupted display (that can be fixed by clearing the screen).
-
-.. _`apt`: https://wiki.debian.org/Apt
-
-In my honest opinion, this is a bug in apt: it shouldn't assume the type
-of terminal it's running in.
-
-The second kind of breakage is when not all of a program's features are
-available. The biggest missing feature here is mouse support: libtmt
-doesn't, and probably never will, support mouse tracking. I know of many
-programs that *can* use mouse tracking in a terminal, but I don't know
-of any that *require* it. Most (if not all?) programs of this kind would
-still be completely usable in libtmt.
-
-License
--------
-
-Copyright (c) 2017 Rob King
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-- Neither the name of the copyright holder nor the
- names of contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS,
-COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/fbcon.c b/fbcon.c
index b5fd57c..9ec115c 100644
--- a/fbcon.c
+++ b/fbcon.c
@@ -26,12 +26,12 @@
#include <libudev.h>
#include <libinput.h>
#include <xkbcommon/xkbcommon.h>
+#include <libtsm.h>
#include "fbtools.h"
#include "drmtools.h"
#include "vt.h"
#include "kbd.h"
-#include "tmt.h"
/* ---------------------------------------------------------------------- */
@@ -40,14 +40,15 @@ static char *font_name = "monospace";
static int font_size = 16;
static gfxstate *gfx;
-static cairo_surface_t *surface1;
-static cairo_surface_t *surface2;
-static cairo_t *context1;
-static cairo_t *context2;
-cairo_font_extents_t extents;
-
-static TMT *vt;
-static int clear, dirty, pty;
+static cairo_font_extents_t extents;
+static struct cairo_state {
+ cairo_surface_t *surface;
+ cairo_t *context;
+ tsm_age_t age;
+ int clear;
+} state1, state2;
+
+static int dirty, pty;
static struct udev *udev;
static struct libinput *kbd;
@@ -62,6 +63,9 @@ static struct xkb_rule_names layout = {
.options = NULL,
};
+static struct tsm_screen *vts;
+static struct tsm_vte *vte;
+
int debug = 0;
/* ---------------------------------------------------------------------- */
@@ -118,7 +122,8 @@ static void console_switch_resume(void)
{
gfx->restore_display();
libinput_resume(kbd);
- clear++;
+ state1.clear++;
+ state2.clear++;
dirty++;
}
@@ -182,12 +187,7 @@ static void xkb_configure(void)
/* ---------------------------------------------------------------------- */
-struct color {
- float r;
- float g;
- float b;
-};
-
+#if 0
static struct color tmt_colors_normal[] = {
[ TMT_COLOR_BLACK ] = { .r = 0.0, .g = 0.0, .b = 0.0 },
[ TMT_COLOR_RED ] = { .r = 0.7, .g = 0.0, .b = 0.0 },
@@ -230,80 +230,123 @@ struct color *tmt_background(struct TMTATTRS *a)
bg = TMT_COLOR_BLACK;
return tmt_colors_normal + bg;
}
+#endif
/* ---------------------------------------------------------------------- */
-static void render(void)
+struct color {
+ float r;
+ float g;
+ float b;
+};
+
+static const struct color black = { 0, 0, 0 };
+static const struct color white = { 1, 1, 1 };
+
+void tsm_log_cb(void *data, const char *file, int line,
+ const char *func, const char *subs, unsigned int sev,
+ const char *format, va_list args)
{
- static bool second;
- const TMTSCREEN *s = tmt_screen(vt);
- const TMTPOINT *cursor = tmt_cursor(vt);
- cairo_t *context;
- int line, col, tx, ty;
- wchar_t ws[2];
- char utf8[10];
+ if (sev == 7 /* debug */ && !debug)
+ return;
- if (surface2)
- second = !second;
- context = second ? context2 : context1;
+ fprintf(stderr, "<%d> ", sev);
+ if (file)
+ fprintf(stderr, "[%s:%d] ", file, line);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+}
- tx = (gfx->hdisplay - (extents.max_x_advance * s->ncol)) / 2;
- ty = (gfx->vdisplay - (extents.height * s->nline)) / 2;
+void tsm_write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data)
+{
+}
- if (clear) {
- cairo_set_source_rgb(context, 0, 0, 0);
- cairo_paint(context);
+int tsm_draw_cb(struct tsm_screen *con, uint32_t id,
+ const uint32_t *ch, size_t len,
+ unsigned int width, unsigned int posx, unsigned int posy,
+ const struct tsm_screen_attr *attr,
+ tsm_age_t age, void *data)
+{
+ struct cairo_state *s = data;
+ struct color bg = {
+ .r = attr->br / 255.0,
+ .g = attr->bg / 255.0,
+ .b = attr->bb / 255.0,
+ };
+ struct color fg = {
+ .r = attr->fr / 255.0,
+ .g = attr->fg / 255.0,
+ .b = attr->fb / 255.0,
+ };
+ int sw = tsm_screen_get_width(con) * extents.max_x_advance;
+ int sh = tsm_screen_get_height(con) * extents.height;
+ int tx = (gfx->hdisplay - sw) / 2;
+ int ty = (gfx->vdisplay - sh) / 2;
+ wchar_t ws[8];
+ char utf8[32];
+ int i;
+
+ if (posx == tsm_screen_get_cursor_x(con) &&
+ posy == tsm_screen_get_cursor_y(con) &&
+ !(tsm_screen_get_flags(con) & TSM_SCREEN_HIDE_CURSOR)) {
+ bg = white;
+ fg = black;
}
- for (line = 0; line < s->nline; line++) {
- for (col = 0; col < s->ncol; col++) {
- TMTCHAR *c = &s->lines[line]->chars[col];
- struct color *bg = tmt_background(&c->a);
- struct color *fg = tmt_foreground(&c->a);
+ /* background */
+ cairo_rectangle(s->context,
+ tx + posx * extents.max_x_advance,
+ ty + posy * extents.height,
+ extents.max_x_advance * width,
+ extents.height);
+ cairo_set_source_rgb(s->context, bg.r, bg.g, bg.b);
+ cairo_fill(s->context);
+
+ /* char */
+ cairo_move_to(s->context,
+ tx + posx * extents.max_x_advance,
+ ty + posy * extents.height + extents.ascent);
+ cairo_set_source_rgb(s->context, fg.r, fg.g, fg.b);
+ for (i = 0; i < len && i < ARRAY_SIZE(ws)-1; i++)
+ ws[i] = ch[i];
+ ws[i] = 0;
+ wcstombs(utf8, ws, sizeof(utf8));
+ cairo_show_text(s->context, utf8);
- if (cursor->r == line && cursor->c == col) {
- bg = tmt_colors_normal + TMT_COLOR_WHITE;
- fg = tmt_colors_normal + TMT_COLOR_BLACK;
- }
+ return 0;
+}
- /* background */
- cairo_rectangle(context,
- tx + col * extents.max_x_advance,
- ty + line * extents.height,
- extents.max_x_advance,
- extents.height);
- cairo_set_source_rgb(context, bg->r, bg->g, bg->b);
- cairo_fill(context);
-
- /* char */
- cairo_move_to(context,
- tx + col * extents.max_x_advance,
- ty + line * extents.height + extents.ascent);
- cairo_set_source_rgb(context, fg->r, fg->g, fg->b);
- ws[0] = c->c;
- ws[1] = 0;
- wcstombs(utf8, ws, sizeof(utf8));
- cairo_show_text(context, utf8);
- }
+static void cairo_render(void)
+{
+ static bool second;
+ struct cairo_state *s;
+
+ if (state2.surface)
+ second = !second;
+ s = second ? &state2 : &state1;
+
+ if (s->clear) {
+ s->clear = 0;
+ cairo_set_source_rgb(s->context, 0, 0, 0);
+ cairo_paint(s->context);
+ s->age = 0;
}
- cairo_show_page(context);
+ s->age = tsm_screen_draw(vts, tsm_draw_cb, s);
+ cairo_show_page(s->context);
if (gfx->flush_display)
gfx->flush_display(second);
}
-static void tmt_callback(tmt_msg_t m, TMT *vt, const void *a, void *p)
+static void cairo_state_init(struct cairo_state *s,
+ const char *font_name, int font_size)
{
- switch (m) {
- case TMT_MSG_UPDATE:
- dirty++;
- break;
- case TMT_MSG_ANSWER:
- write(pty, a, strlen(a));
- default:
- break;
- }
+ s->context = cairo_create(s->surface);
+ cairo_select_font_face(s->context, font_name,
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(s->context, font_size);
}
static void child_exec_shell(struct winsize *win)
@@ -335,7 +378,7 @@ static void child_exec_shell(struct winsize *win)
/* prepare environment, run shell */
snprintf(lines, sizeof(lines), "%d", win->ws_row);
snprintf(columns, sizeof(columns), "%d", win->ws_col);
- setenv("TERM", "ansi", true);
+ setenv("TERM", "xterm-256color", true);
setenv("LINES", lines, true);
setenv("COLUMNS", columns, true);
@@ -402,28 +445,20 @@ int main(int argc, char *argv[])
}
/* init cairo */
- surface1 = cairo_image_surface_create_for_data(gfx->mem,
- gfx->fmt->cairo,
- gfx->hdisplay,
- gfx->vdisplay,
- gfx->stride);
- context1 = cairo_create(surface1);
- cairo_select_font_face(context1, font_name,
- CAIRO_FONT_SLANT_NORMAL,
- CAIRO_FONT_WEIGHT_NORMAL);
- cairo_set_font_size(context1, font_size);
- cairo_font_extents(context1, &extents);
+ state1.surface = cairo_image_surface_create_for_data(gfx->mem,
+ gfx->fmt->cairo,
+ gfx->hdisplay,
+ gfx->vdisplay,
+ gfx->stride);
+ cairo_state_init(&state1, font_name, font_size);
+ cairo_font_extents(state1.context, &extents);
if (gfx->mem2) {
- surface2 = cairo_image_surface_create_for_data(gfx->mem2,
- gfx->fmt->cairo,
- gfx->hdisplay,
- gfx->vdisplay,
- gfx->stride);
- context2 = cairo_create(surface2);
- cairo_select_font_face(context2, font_name,
- CAIRO_FONT_SLANT_NORMAL,
- CAIRO_FONT_WEIGHT_NORMAL);
- cairo_set_font_size(context2, font_size);
+ state2.surface = cairo_image_surface_create_for_data(gfx->mem2,
+ gfx->fmt->cairo,
+ gfx->hdisplay,
+ gfx->vdisplay,
+ gfx->stride);
+ cairo_state_init(&state2, font_name, font_size);
}
/* init libinput */
@@ -450,7 +485,12 @@ int main(int argc, char *argv[])
win.ws_row = gfx->vdisplay / extents.height;
win.ws_xpixel = extents.max_x_advance;
win.ws_ypixel = extents.height;
- vt = tmt_open(win.ws_row, win.ws_col, tmt_callback, NULL, NULL);
+
+ tsm_screen_new(&vts, tsm_log_cb, NULL);
+ tsm_screen_resize(vts, win.ws_col, win.ws_row);
+ tsm_vte_new(&vte, vts, tsm_write_cb, NULL, tsm_log_cb, NULL);
+
+ /* run shell */
child = forkpty(&pty, NULL, NULL, &win);
if (0 == child) {
child_exec_shell(&win);
@@ -460,14 +500,17 @@ int main(int argc, char *argv[])
}
/* parent */
- clear++;
+ state1.clear++;
+ state2.clear++;
dirty++;
for (;;) {
fd_set set;
int rc, max;
- if (dirty)
- render();
+ if (dirty) {
+ cairo_render();
+ dirty = 0;
+ }
max = 0;
FD_ZERO(&set);
@@ -489,8 +532,10 @@ int main(int argc, char *argv[])
break; /* read error */
if (rc == 0)
break; /* no data -> EOF */
- if (rc > 0)
- tmt_write(vt, buf, rc);
+ if (rc > 0) {
+ tsm_vte_input(vte, buf, rc);
+ dirty++;
+ }
}
if (FD_ISSET(input, &set)) {
@@ -520,6 +565,7 @@ int main(int argc, char *argv[])
if (rc > 0)
write(pty, buf, rc);
}
+ dirty++;
}
break;
default:
diff --git a/meson.build b/meson.build
index 5746eb9..af71b23 100644
--- a/meson.build
+++ b/meson.build
@@ -27,6 +27,7 @@ webp_dep = dependency('libwebp', required : false)
udev_dep = dependency('libudev')
input_dep = dependency('libinput')
xkb_dep = dependency('xkbcommon')
+tsm_dep = dependency('libtsm', required : false)
# other library deps
cc = meson.get_compiler('c')
@@ -127,15 +128,17 @@ executable('fbpdf',
install : true)
# build fbcon
-fbcon_srcs = [ 'fbcon.c', 'drmtools.c', 'fbtools.c', 'gfx.c', 'vt.c', 'kbd.c', 'tmt.c' ]
-fbcon_deps = [ drm_dep, cairo_dep, util_dep, udev_dep, input_dep, xkb_dep ]
+fbcon_srcs = [ 'fbcon.c', 'drmtools.c', 'fbtools.c', 'gfx.c', 'vt.c', 'kbd.c' ]
+fbcon_deps = [ drm_dep, cairo_dep, util_dep, udev_dep, input_dep, xkb_dep, tsm_dep ]
-executable('fbcon',
- sources : fbcon_srcs,
- dependencies : fbcon_deps,
- install : true)
-install_data('fbcon.desktop',
- install_dir : 'share/wayland-sessions')
+if tsm_dep.found()
+ executable('fbcon',
+ sources : fbcon_srcs,
+ dependencies : fbcon_deps,
+ install : true)
+ install_data('fbcon.desktop',
+ install_dir : 'share/wayland-sessions')
+endif
# build kbdtest
executable('kbdtest',
diff --git a/tmt.c b/tmt.c
deleted file mode 100644
index 26c122e..0000000
--- a/tmt.c
+++ /dev/null
@@ -1,500 +0,0 @@
-/* Copyright (c) 2017 Rob King
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the copyright holder nor the
- * names of contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS,
- * COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <limits.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "tmt.h"
-
-#define BUF_MAX 100
-#define PAR_MAX 8
-#define TAB 8
-#define MAX(x, y) (((size_t)(x) > (size_t)(y)) ? (size_t)(x) : (size_t)(y))
-#define MIN(x, y) (((size_t)(x) < (size_t)(y)) ? (size_t)(x) : (size_t)(y))
-#define CLINE(vt) (vt)->screen.lines[MIN((vt)->curs.r, (vt)->screen.nline - 1)]
-
-#define P0(x) (vt->pars[x])
-#define P1(x) (vt->pars[x]? vt->pars[x] : 1)
-#define CB(vt, m, a) ((vt)->cb? (vt)->cb(m, vt, a, (vt)->p) : (void)0)
-#define INESC ((vt)->state)
-
-#define COMMON_VARS \
- TMTSCREEN *s = &vt->screen; \
- TMTPOINT *c = &vt->curs; \
- TMTLINE *l = CLINE(vt); \
- TMTCHAR *t = vt->tabs->chars
-
-#define HANDLER(name) static void name (TMT *vt) { COMMON_VARS;
-
-struct TMT{
- TMTPOINT curs, oldcurs;
- TMTATTRS attrs, oldattrs;
-
- bool dirty, acs, ignored;
- TMTSCREEN screen;
- TMTLINE *tabs;
-
- TMTCALLBACK cb;
- void *p;
- const wchar_t *acschars;
-
- mbstate_t ms;
- size_t nmb;
- char mb[BUF_MAX + 1];
-
- size_t pars[PAR_MAX];
- size_t npar;
- size_t arg;
- enum {S_NUL, S_ESC, S_ARG} state;
-};
-
-static TMTATTRS defattrs = {.fg = TMT_COLOR_DEFAULT, .bg = TMT_COLOR_DEFAULT};
-static void writecharatcurs(TMT *vt, wchar_t w);
-
-static wchar_t
-tacs(const TMT *vt, unsigned char c)
-{
- /* The terminfo alternate character set for ANSI. */
- static unsigned char map[] = {0020U, 0021U, 0030U, 0031U, 0333U, 0004U,
- 0261U, 0370U, 0361U, 0260U, 0331U, 0277U,
- 0332U, 0300U, 0305U, 0176U, 0304U, 0304U,
- 0304U, 0137U, 0303U, 0264U, 0301U, 0302U,
- 0263U, 0363U, 0362U, 0343U, 0330U, 0234U,
- 0376U};
- for (size_t i = 0; i < sizeof(map); i++) if (map[i] == c)
- return vt->acschars[i];
- return (wchar_t)c;
-}
-
-static void
-dirtylines(TMT *vt, size_t s, size_t e)
-{
- vt->dirty = true;
- for (size_t i = s; i < e; i++)
- vt->screen.lines[i]->dirty = true;
-}
-
-static void
-clearline(TMT *vt, TMTLINE *l, size_t s, size_t e)
-{
- vt->dirty = l->dirty = true;
- for (size_t i = s; i < e && i < vt->screen.ncol; i++){
- l->chars[i].a = defattrs;
- l->chars[i].c = L' ';
- }
-}
-
-static void
-clearlines(TMT *vt, size_t r, size_t n)
-{
- for (size_t i = r; i < r + n && i < vt->screen.nline; i++)
- clearline(vt, vt->screen.lines[i], 0, vt->screen.ncol);
-}
-
-static void
-scrup(TMT *vt, size_t r, size_t n)
-{
- n = MIN(n, vt->screen.nline - 1 - r);
-
- if (n){
- TMTLINE *buf[n];
-
- memcpy(buf, vt->screen.lines + r, n * sizeof(TMTLINE *));
- memmove(vt->screen.lines + r, vt->screen.lines + r + n,
- (vt->screen.nline - n - r) * sizeof(TMTLINE *));
- memcpy(vt->screen.lines + (vt->screen.nline - n),
- buf, n * sizeof(TMTLINE *));
-
- clearlines(vt, vt->screen.nline - n, n);
- dirtylines(vt, r, vt->screen.nline);
- }
-}
-
-static void
-scrdn(TMT *vt, size_t r, size_t n)
-{
- n = MIN(n, vt->screen.nline - 1 - r);
-
- if (n){
- TMTLINE *buf[n];
-
- memcpy(buf, vt->screen.lines + (vt->screen.nline - n),
- n * sizeof(TMTLINE *));
- memmove(vt->screen.lines + r + n, vt->screen.lines + r,
- (vt->screen.nline - n - r) * sizeof(TMTLINE *));
- memcpy(vt->screen.lines + r, buf, n * sizeof(TMTLINE *));
-
- clearlines(vt, r, n);
- dirtylines(vt, r, vt->screen.nline);
- }
-}
-
-HANDLER(ed)
- size_t b = 0;
- size_t e = s->nline;
-
- switch (P0(0)){
- case 0: b = c->r + 1; clearline(vt, l, c->c, vt->screen.ncol); break;
- case 1: e = c->r - 1; clearline(vt, l, 0, c->c); break;
- case 2: /* use defaults */ break;
- default: /* do nothing */ return;
- }
-
- clearlines(vt, b, e - b);
-}
-
-HANDLER(ich)
- size_t n = P1(0); /* XXX use MAX */
- if (n > s->ncol - c->c - 1) n = s->ncol - c->c - 1;
-
- memmove(l->chars + c->c + n, l->chars + c->c,
- MIN(s->ncol - 1 - c->c,
- (s->ncol - c->c - n - 1)) * sizeof(TMTCHAR));
- clearline(vt, l, c->c, n);
-}
-
-HANDLER(dch)
- size_t n = P1(0); /* XXX use MAX */
- if (n > s->ncol - c->c) n = s->ncol - c->c;
-
- memmove(l->chars + c->c, l->chars + c->c + n,
- (s->ncol - c->c - n) * sizeof(TMTCHAR));
-
- clearline(vt, l, s->ncol - c->c - n, s->ncol);
-}
-
-HANDLER(el)
- switch (P0(0)){
- case 0: clearline(vt, l, c->c, vt->screen.ncol); break;
- case 1: clearline(vt, l, 0, MIN(c->c + 1, s->ncol - 1)); break;
- case 2: clearline(vt, l, 0, vt->screen.ncol); break;
- }
-}
-
-HANDLER(sgr)
- #define FGBG(c) *(P0(i) < 40? &vt->attrs.fg : &vt->attrs.bg) = c
- for (size_t i = 0; i < vt->npar; i++) switch (P0(i)){
- case 0: vt->attrs = defattrs; break;
- case 1: case 22: vt->attrs.bold = P0(0) < 20; break;
- case 2: case 23: vt->attrs.dim = P0(0) < 20; break;
- case 4: case 24: vt->attrs.underline = P0(0) < 20; break;
- case 5: case 25: vt->attrs.blink = P0(0) < 20; break;
- case 7: case 27: vt->attrs.reverse = P0(0) < 20; break;
- case 8: case 28: vt->attrs.invisible = P0(0) < 20; break;
- case 10: case 11: vt->acs = P0(0) > 10; break;
- case 30: case 40: FGBG(TMT_COLOR_BLACK); break;
- case 31: case 41: FGBG(TMT_COLOR_RED); break;
- case 32: case 42: FGBG(TMT_COLOR_GREEN); break;
- case 33: case 43: FGBG(TMT_COLOR_YELLOW); break;
- case 34: case 44: FGBG(TMT_COLOR_BLUE); break;
- case 35: case 45: FGBG(TMT_COLOR_MAGENTA); break;
- case 36: case 46: FGBG(TMT_COLOR_CYAN); break;
- case 37: case 47: FGBG(TMT_COLOR_WHITE); break;
- case 39: case 49: FGBG(TMT_COLOR_DEFAULT); break;
- }
-}
-
-HANDLER(rep)
- if (!c->c) return;
- wchar_t r = l->chars[c->c - 1].c;
- for (size_t i = 0; i < P1(0); i++)
- writecharatcurs(vt, r);
-}
-
-HANDLER(dsr)
- char r[BUF_MAX + 1] = {0};
- snprintf(r, BUF_MAX, "\033[%zd;%zdR", c->r, c->c);
- CB(vt, TMT_MSG_ANSWER, (const char *)r);
-}
-
-HANDLER(resetparser)
- memset(vt->pars, 0, sizeof(vt->pars));
- vt->state = vt->npar = vt->arg = vt->ignored = (bool)0;
-}
-
-HANDLER(consumearg)
- if (vt->npar < PAR_MAX)
- vt->pars[vt->npar++] = vt->arg;
- vt->arg = 0;
-}
-
-HANDLER(fixcursor)
- c->r = MIN(c->r, s->nline - 1);
- c->c = MIN(c->c, s->ncol - 1);
-}
-
-static bool
-handlechar(TMT *vt, char i)
-{
- COMMON_VARS;
-
- char cs[] = {i, 0};
- #define ON(S, C, A) if (vt->state == (S) && strchr(C, i)){ A; return true;}
- #define DO(S, C, A) ON(S, C, consumearg(vt); if (!vt->ignored) {A;} \
- fixcursor(vt); resetparser(vt););
-
- DO(S_NUL, "\x07", CB(vt, TMT_MSG_BELL, NULL))
- DO(S_NUL, "\x08", if (c->c) c->c--)
- DO(S_NUL, "\x09", while (++c->c < s->ncol - 1 && t[c->c].c != L'*'))
- DO(S_NUL, "\x0a", c->r < s->nline - 1? (void)c->r++ : scrup(vt, 0, 1))
- DO(S_NUL, "\x0d", c->c = 0)
- ON(S_NUL, "\x1b", vt->state = S_ESC)
- ON(S_ESC, "\x1b", vt->state = S_ESC)
- DO(S_ESC, "H", t[c->c].c = L'*')
- DO(S_ESC, "7", vt->oldcurs = vt->curs; vt->oldattrs = vt->attrs)
- DO(S_ESC, "8", vt->curs = vt->oldcurs; vt->attrs = vt->oldattrs)
- ON(S_ESC, "+*()", vt->ignored = true; vt->state = S_ARG)
- DO(S_ESC, "c", tmt_reset(vt))
- ON(S_ESC, "[", vt->state = S_ARG)
- ON(S_ARG, "\x1b", vt->state = S_ESC)
- ON(S_ARG, ";", consumearg(vt))
- ON(S_ARG, "?", (void)0)
- ON(S_ARG, "0123456789", vt->arg = vt->arg * 10 + atoi(cs))
- DO(S_ARG, "A", c->r = MAX(c->r - P1(0), 0))
- DO(S_ARG, "B", c->r = MIN(c->r + P1(0), s->nline - 1))
- DO(S_ARG, "C", c->c = MIN(c->c + P1(0), s->ncol - 1))
- DO(S_ARG, "D", c->c = MIN(c->c - P1(0), c->c))
- DO(S_ARG, "E", c->c = 0; c->r = MIN(c->r + P1(0), s->nline - 1))
- DO(S_ARG, "F", c->c = 0; c->r = MAX(c->r - P1(0), 0))
- DO(S_ARG, "G", c->c = MIN(P1(0) - 1, s->ncol - 1))
- DO(S_ARG, "d", c->r = MIN(P1(0) - 1, s->nline - 1))
- DO(S_ARG, "Hf", c->r = P1(0) - 1; c->c = P1(1) - 1)
- DO(S_ARG, "I", while (++c->c < s->ncol - 1 && t[c->c].c != L'*'))
- DO(S_ARG, "J", ed(vt))
- DO(S_ARG, "K", el(vt))
- DO(S_ARG, "L", scrdn(vt, c->r, P1(0)))
- DO(S_ARG, "M", scrup(vt, c->r, P1(0)))
- DO(S_ARG, "P", dch(vt))
- DO(S_ARG, "S", scrup(vt, 0, P1(0)))
- DO(S_ARG, "T", scrdn(vt, 0, P1(0)))
- DO(S_ARG, "X", clearline(vt, l, c->c, P1(0)))
- DO(S_ARG, "Z", while (c->c && t[--c->c].c != L'*'))
- DO(S_ARG, "b", rep(vt));
- DO(S_ARG, "c", CB(vt, TMT_MSG_ANSWER, "\033[?6c"))
- DO(S_ARG, "g", if (P0(0) == 3) clearline(vt, vt->tabs, 0, s->ncol))
- DO(S_ARG, "m", sgr(vt))
- DO(S_ARG, "n", if (P0(0) == 6) dsr(vt))
- DO(S_ARG, "h", if (P0(0) == 25) CB(vt, TMT_MSG_CURSOR, "t"))
- DO(S_ARG, "i", (void)0)
- DO(S_ARG, "l", if (P0(0) == 25) CB(vt, TMT_MSG_CURSOR, "f"))
- DO(S_ARG, "s", vt->oldcurs = vt->curs; vt->oldattrs = vt->attrs)
- DO(S_ARG, "u", vt->curs = vt->oldcurs; vt->attrs = vt->oldattrs)
- DO(S_ARG, "@", ich(vt))
-
- return resetparser(vt), false;
-}
-
-static void
-notify(TMT *vt, bool update, bool moved)
-{
- if (update) CB(vt, TMT_MSG_UPDATE, &vt->screen);
- if (moved) CB(vt, TMT_MSG_MOVED, &vt->curs);
-}
-
-static TMTLINE *
-allocline(TMT *vt, TMTLINE *o, size_t n, size_t pc)
-{
- TMTLINE *l = realloc(o, sizeof(TMTLINE) + n * sizeof(TMTCHAR));
- if (!l) return NULL;
-
- clearline(vt, l, pc, n);
- return l;
-}
-
-static void
-freelines(TMT *vt, size_t s, size_t n, bool screen)
-{
- for (size_t i = s; vt->screen.lines && i < s + n; i++){
- free(vt->screen.lines[i]);
- vt->screen.lines[i] = NULL;
- }
- if (screen) free(vt->screen.lines);
-}
-
-TMT *
-tmt_open(size_t nline, size_t ncol, TMTCALLBACK cb, void *p,
- const wchar_t *acs)
-{
- TMT *vt = calloc(1, sizeof(TMT));
- if (!nline || !ncol || !vt) return free(vt), NULL;
-
- /* ASCII-safe defaults for box-drawing characters. */
- vt->acschars = acs? acs : L"><^v#+:o##+++++~---_++++|<>*!fo";
- vt->cb = cb;
- vt->p = p;
-
- if (!tmt_resize(vt, nline, ncol)) return tmt_close(vt), NULL;
- return vt;
-}
-
-void
-tmt_close(TMT *vt)
-{
- free(vt->tabs);
- freelines(vt, 0, vt->screen.nline, true);
- free(vt);
-}
-
-bool
-tmt_resize(TMT *vt, size_t nline, size_t ncol)
-{
- if (nline < 2 || ncol < 2) return false;
- if (nline < vt->screen.nline)
- freelines(vt, nline, vt->screen.nline - nline, false);
-
- TMTLINE **l = realloc(vt->screen.lines, nline * sizeof(TMTLINE *));
- if (!l) return false;
-
- size_t pc = vt->screen.ncol;
- vt->screen.lines = l;
- vt->screen.ncol = ncol;
- for (size_t i = 0; i < nline; i++){
- TMTLINE *nl = NULL;
- if (i >= vt->screen.nline)
- nl = vt->screen.lines[i] = allocline(vt, NULL, ncol, 0);
- else
- nl = allocline(vt, vt->screen.lines[i], ncol, pc);
-
- if (!nl) return false;
- vt->screen.lines[i] = nl;
- }
- vt->screen.nline = nline;
-
- vt->tabs = allocline(vt, vt->tabs, ncol, 0);
- if (!vt->tabs) return free(l), false;
- vt->tabs->chars[0].c = vt->tabs->chars[ncol - 1].c = L'*';
- for (size_t i = 0; i < ncol; i++) if (i % TAB == 0)
- vt->tabs->chars[i].c = L'*';
-
- fixcursor(vt);
- dirtylines(vt, 0, nline);
- notify(vt, true, true);
- return true;
-}
-
-static void
-writecharatcurs(TMT *vt, wchar_t w)
-{
- COMMON_VARS;
-
- #ifdef TMT_HAS_WCWIDTH
- extern int wcwidth(wchar_t c);
- if (wcwidth(w) > 1) w = TMT_INVALID_CHAR;
- if (wcwidth(w) < 0) return;
- #endif
-
- CLINE(vt)->chars[vt->curs.c].c = w;
- CLINE(vt)->chars[vt->curs.c].a = vt->attrs;
- CLINE(vt)->dirty = vt->dirty = true;
-
- if (c->c < s->ncol - 1)
- c->c++;
- else{
- c->c = 0;
- c->r++;
- }
-
- if (c->r >= s->nline){
- c->r = s->nline - 1;
- scrup(vt, 0, 1);
- }
-}
-
-static inline size_t
-testmbchar(TMT *vt)
-{
- mbstate_t ts = vt->ms;
- return vt->nmb? mbrtowc(NULL, vt->mb, vt->nmb, &ts) : (size_t)-2;
-}
-
-static inline wchar_t
-getmbchar(TMT *vt)
-{
- wchar_t c = 0;
- size_t n = mbrtowc(&c, vt->mb, vt->nmb, &vt->ms);
- vt->nmb = 0;
- return (n == (size_t)-1 || n == (size_t)-2)? TMT_INVALID_CHAR : c;
-}
-
-void
-tmt_write(TMT *vt, const char *s, size_t n)
-{
- TMTPOINT oc = vt->curs;
- n = n? n : strlen(s);
-
- for (size_t p = 0; p < n; p++){
- if (handlechar(vt, s[p]))
- continue;
- else if (vt->acs)
- writecharatcurs(vt, tacs(vt, (unsigned char)s[p]));
- else if (vt->nmb >= BUF_MAX)
- writecharatcurs(vt, getmbchar(vt));
- else{
- switch (testmbchar(vt)){
- case (size_t)-1: writecharatcurs(vt, getmbchar(vt)); break;
- case (size_t)-2: vt->mb[vt->nmb++] = s[p]; break;
- }
-
- if (testmbchar(vt) <= MB_LEN_MAX)
- writecharatcurs(vt, getmbchar(vt));
- }
- }
-
- notify(vt, vt->dirty, memcmp(&oc, &vt->curs, sizeof(oc)) != 0);
-}
-
-const TMTSCREEN *
-tmt_screen(const TMT *vt)
-{
- return &vt->screen;
-}
-
-const TMTPOINT *
-tmt_cursor(const TMT *vt)
-{
- return &vt->curs;
-}
-
-void
-tmt_clean(TMT *vt)
-{
- for (size_t i = 0; i < vt->screen.nline; i++)
- vt->dirty = vt->screen.lines[i]->dirty = false;
-}
-
-void
-tmt_reset(TMT *vt)
-{
- vt->curs.r = vt->curs.c = vt->oldcurs.r = vt->oldcurs.c = vt->acs = (bool)0;
- resetparser(vt);
- vt->attrs = vt->oldattrs = defattrs;
- memset(&vt->ms, 0, sizeof(vt->ms));
- clearlines(vt, 0, vt->screen.nline);
- CB(vt, TMT_MSG_CURSOR, "t");
- notify(vt, true, true);
-}
diff --git a/tmt.h b/tmt.h
deleted file mode 100644
index ae0ddbb..0000000
--- a/tmt.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/* Copyright (c) 2017 Rob King
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the copyright holder nor the
- * names of contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS,
- * COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef TMT_H
-#define TMT_H
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <wchar.h>
-
-/**** INVALID WIDE CHARACTER */
-#ifndef TMT_INVALID_CHAR
-#define TMT_INVALID_CHAR ((wchar_t)0xfffd)
-#endif
-
-/**** INPUT SEQUENCES */
-#define TMT_KEY_UP "\033[A"
-#define TMT_KEY_DOWN "\033[B"
-#define TMT_KEY_RIGHT "\033[C"
-#define TMT_KEY_LEFT "\033[D"
-#define TMT_KEY_HOME "\033[H"
-#define TMT_KEY_END "\033[Y"
-#define TMT_KEY_INSERT "\033[L"
-#define TMT_KEY_BACKSPACE "\x08"
-#define TMT_KEY_ESCAPE "\x1b"
-#define TMT_KEY_BACK_TAB "\033[Z"
-#define TMT_KEY_PAGE_UP "\033[V"
-#define TMT_KEY_PAGE_DOWN "\033[U"
-#define TMT_KEY_F1 "\033OP"
-#define TMT_KEY_F2 "\033OQ"
-#define TMT_KEY_F3 "\033OR"
-#define TMT_KEY_F4 "\033OS"
-#define TMT_KEY_F5 "\033OT"
-#define TMT_KEY_F6 "\033OU"
-#define TMT_KEY_F7 "\033OV"
-#define TMT_KEY_F8 "\033OW"
-#define TMT_KEY_F9 "\033OX"
-#define TMT_KEY_F10 "\033OY"
-
-/**** BASIC DATA STRUCTURES */
-typedef struct TMT TMT;
-
-typedef enum{
- TMT_COLOR_DEFAULT = -1,
- TMT_COLOR_BLACK = 1,
- TMT_COLOR_RED,
- TMT_COLOR_GREEN,
- TMT_COLOR_YELLOW,
- TMT_COLOR_BLUE,
- TMT_COLOR_MAGENTA,
- TMT_COLOR_CYAN,
- TMT_COLOR_WHITE,
- TMT_COLOR_MAX
-} tmt_color_t;
-
-typedef struct TMTATTRS TMTATTRS;
-struct TMTATTRS{
- bool bold;
- bool dim;
- bool underline;
- bool blink;
- bool reverse;
- bool invisible;
- tmt_color_t fg;
- tmt_color_t bg;
-};
-
-typedef struct TMTCHAR TMTCHAR;
-struct TMTCHAR{
- wchar_t c;
- TMTATTRS a;
-};
-
-typedef struct TMTPOINT TMTPOINT;
-struct TMTPOINT{
- size_t r;
- size_t c;
-};
-
-typedef struct TMTLINE TMTLINE;
-struct TMTLINE{
- bool dirty;
- TMTCHAR chars[];
-};
-
-typedef struct TMTSCREEN TMTSCREEN;
-struct TMTSCREEN{
- size_t nline;
- size_t ncol;
-
- TMTLINE **lines;
-};
-
-/**** CALLBACK SUPPORT */
-typedef enum{
- TMT_MSG_MOVED,
- TMT_MSG_UPDATE,
- TMT_MSG_ANSWER,
- TMT_MSG_BELL,
- TMT_MSG_CURSOR
-} tmt_msg_t;
-
-typedef void (*TMTCALLBACK)(tmt_msg_t m, struct TMT *v, const void *r, void *p);
-
-/**** PUBLIC FUNCTIONS */
-TMT *tmt_open(size_t nline, size_t ncol, TMTCALLBACK cb, void *p,
- const wchar_t *acs);
-void tmt_close(TMT *vt);
-bool tmt_resize(TMT *vt, size_t nline, size_t ncol);
-void tmt_write(TMT *vt, const char *s, size_t n);
-const TMTSCREEN *tmt_screen(const TMT *vt);
-const TMTPOINT *tmt_cursor(const TMT *vt);
-void tmt_clean(TMT *vt);
-void tmt_reset(TMT *vt);
-
-#endif