From 425ef692da7e74112f88f0b368f3286dba84f846 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Thu, 18 Jun 2020 19:45:40 -0700 Subject: feat: working parser for rc shell language --- bin/dvtm-editor.c | 180 ---- bin/mkblddir | 17 - compile_commands.json | 709 ++++++++++++++- include/libn.h | 56 +- include/u.h | 5 +- sys/cmd/cat/cat.c | 2 +- sys/cmd/dvtm/buffer.c | 326 ------- sys/cmd/dvtm/buffer.h | 0 sys/cmd/dvtm/dvtm-editor.c | 7 +- sys/cmd/dvtm/dvtm.c | 116 +-- sys/cmd/dvtm/dvtm.c.old | 1810 -------------------------------------- sys/cmd/dvtm/dvtm.h | 9 +- sys/cmd/dvtm/events.c | 1690 ------------------------------------ sys/cmd/dvtm/rules.mk | 9 +- sys/cmd/dvtm/term.c | 395 --------- sys/cmd/dvtm/term.h | 481 ---------- sys/cmd/dvtm/vt.c | 1282 +++++++++++++++++++-------- sys/cmd/dvtm/vt.c.old | 2074 -------------------------------------------- sys/cmd/dvtm/vt.h | 8 +- sys/cmd/dvtm/window.c | 44 - sys/cmd/dwm/client.c | 8 +- sys/cmd/dwm/config.h | 8 +- sys/cmd/rc/code.c | 356 ++++++++ sys/cmd/rc/code.dep | 166 ++++ sys/cmd/rc/exec.c | 19 + sys/cmd/rc/io.c | 435 ++++++++++ sys/cmd/rc/lex.c | 415 +++++++++ sys/cmd/rc/main.c | 86 ++ sys/cmd/rc/parse.c | 430 +++++++++ sys/cmd/rc/rc.h | 254 ++++++ sys/cmd/rc/rules.mk | 21 + sys/cmd/rc/simple.c | 13 + sys/cmd/rc/tree.c | 144 +++ sys/cmd/rc/util.c | 40 + sys/cmd/rc/var.c | 108 +++ sys/cmd/rc/word.c | 64 ++ sys/cmd/rules.mk | 27 +- sys/cmd/term/term.c | 5 +- sys/cmd/term/term.info | 37 +- sys/libn/error.c | 10 + sys/libn/memory.c | 5 - sys/libn/rules.mk | 2 + sys/libterm/buffer.c | 326 ------- sys/libterm/escseq.c | 0 sys/libterm/events.c | 1692 ------------------------------------ sys/libterm/input.c | 108 --- sys/libterm/term.c | 256 ++++-- sys/libterm/term.h | 365 ++------ sys/libterm/window.c | 426 ++++++++- sys/rules.mk | 6 +- 50 files changed, 5004 insertions(+), 10048 deletions(-) delete mode 100644 bin/dvtm-editor.c delete mode 100755 bin/mkblddir delete mode 100644 sys/cmd/dvtm/buffer.c delete mode 100644 sys/cmd/dvtm/buffer.h delete mode 100644 sys/cmd/dvtm/dvtm.c.old delete mode 100644 sys/cmd/dvtm/events.c delete mode 100644 sys/cmd/dvtm/term.c delete mode 100644 sys/cmd/dvtm/term.h delete mode 100644 sys/cmd/dvtm/vt.c.old delete mode 100644 sys/cmd/dvtm/window.c create mode 100644 sys/cmd/rc/code.c create mode 100644 sys/cmd/rc/code.dep create mode 100644 sys/cmd/rc/exec.c create mode 100644 sys/cmd/rc/io.c create mode 100644 sys/cmd/rc/lex.c create mode 100644 sys/cmd/rc/main.c create mode 100644 sys/cmd/rc/parse.c create mode 100644 sys/cmd/rc/rc.h create mode 100644 sys/cmd/rc/rules.mk create mode 100644 sys/cmd/rc/simple.c create mode 100644 sys/cmd/rc/tree.c create mode 100644 sys/cmd/rc/util.c create mode 100644 sys/cmd/rc/var.c create mode 100644 sys/cmd/rc/word.c delete mode 100644 sys/libterm/buffer.c delete mode 100644 sys/libterm/escseq.c delete mode 100644 sys/libterm/events.c delete mode 100644 sys/libterm/input.c diff --git a/bin/dvtm-editor.c b/bin/dvtm-editor.c deleted file mode 100644 index 45a61b2..0000000 --- a/bin/dvtm-editor.c +++ /dev/null @@ -1,180 +0,0 @@ -/* Invoke $EDITOR as a filter. - * - * Copyright (c) 2016 Dmitry Bogatov - * Copyright (c) 2017 Marc André Tanner - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void error(const char *msg, ...) { - va_list ap; - va_start(ap, msg); - vfprintf(stderr, msg, ap); - va_end(ap); - if (errno) - fprintf(stderr, ": %s", strerror(errno)); - fprintf(stderr, "\n"); -} - -int main(int argc, char *argv[]) -{ - int exit_status = EXIT_FAILURE, tmp_write = -1; - - const char *editor = getenv("DVTM_EDITOR"); - if (!editor) - editor = getenv("VISUAL"); - if (!editor) - editor = getenv("EDITOR"); - if (!editor) - editor = "vi"; - - char tempname[] = "/tmp/dvtm-editor.XXXXXX"; - if ((tmp_write = mkstemp(tempname)) == -1) { - error("failed to open temporary file `%s'", tempname); - goto err; - } - - /* POSIX does not mandates modes of temporary file. */ - if (fchmod(tmp_write, 0600) == -1) { - error("failed to change mode of temporary file `%s'", tempname); - goto err; - } - - char buffer[2048]; - ssize_t bytes; - while ((bytes = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) { - do { - ssize_t written = write(tmp_write, buffer, bytes); - if (written == -1) { - error("failed to write data to temporary file `%s'", - tempname); - goto err; - } - bytes -= written; - } while (bytes > 0); - } - - if (fsync(tmp_write) == -1) { - error("failed to fsync temporary file `%s'", tempname); - goto err; - } - - struct stat stat_before; - if (fstat(tmp_write, &stat_before) == -1) { - error("failed to stat newly created temporary file `%s'", tempname); - goto err; - } - - if (close(tmp_write) == -1) { - error("failed to close temporary file `%s'", tempname); - goto err; - } - - pid_t pid = fork(); - if (pid == -1) { - error("failed to fork editor process"); - goto err; - } else if (pid == 0) { - int tty = open("/dev/tty", O_RDWR); - if (tty == -1) { - error("failed to open /dev/tty"); - _exit(1); - } - - if (dup2(tty, STDIN_FILENO) == -1) { - error("failed to set tty as stdin"); - _exit(1); - } - - if (dup2(tty, STDOUT_FILENO) == -1) { - error("failed to set tty as stdout"); - _exit(1); - } - - if (dup2(tty, STDERR_FILENO) == -1) { - error("failed to set tty as stderr"); - _exit(1); - } - - close(tty); - - const char *editor_argv[argc+2]; - editor_argv[0] = editor; - for (int i = 1; i < argc; i++) - editor_argv[i] = argv[i]; - editor_argv[argc] = tempname; - editor_argv[argc+1] = NULL; - - execvp(editor, (char* const*)editor_argv); - error("failed to exec editor process `%s'", editor); - _exit(127); - } - - int status; - if (waitpid(pid, &status, 0) == -1) { - error("waitpid failed"); - goto err; - } - if (!WIFEXITED(status)) { - error("editor invocation failed"); - goto err; - } - if ((status = WEXITSTATUS(status)) != 0) { - error("editor terminated with exit status: %d", status); - goto err; - } - - int tmp_read = open(tempname, O_RDONLY); - if (tmp_read == -1) { - error("failed to open for reading of edited temporary file `%s'", - tempname); - goto err; - } - - struct stat stat_after; - if (fstat(tmp_read, &stat_after) == -1) { - error("failed to stat edited temporary file `%s'", tempname); - goto err; - } - - if (stat_before.st_mtime == stat_after.st_mtime) - goto ok; /* no modifications */ - - while ((bytes = read(tmp_read, buffer, sizeof(buffer))) > 0) { - do { - ssize_t written = write(STDOUT_FILENO, buffer, bytes); - if (written == -1) { - error("failed to write data to stdout"); - goto err; - } - bytes -= written; - } while (bytes > 0); - } - -ok: - exit_status = EXIT_SUCCESS; -err: - if (tmp_write != -1) - unlink(tempname); - return exit_status; -} diff --git a/bin/mkblddir b/bin/mkblddir deleted file mode 100755 index bb646e0..0000000 --- a/bin/mkblddir +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/python - -import os - -ROOT = "/home/nolln/root" -SRC = "sys" -BUILD = "build" -TEST = "test" -IGNORED = ["build", "include", "lib", "bin", ".git", "vendor", "obj", "dep", ".generated"] - -if __name__ == "__main__": - for root, dirs, _ in os.walk(f"{ROOT}/{SRC}"): - dirs[:] = [d for d in dirs if d not in IGNORED] - for newroot in [BUILD, TEST]: - blddir = f"{ROOT}/{newroot}/{root[len(ROOT)+len(SRC)+2:]}" - if not os.path.exists(blddir): - os.mkdir(blddir) diff --git a/compile_commands.json b/compile_commands.json index a2985d6..02f4556 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -8,19 +8,16 @@ "-fno-strict-aliasing", "-fwrapv", "-fms-extensions", - "-D_XOPEN_SOURCE=700", - "-D_XOPEN_SOURCE_EXTENDED", - "-DNDEBUG", "-I", "include", "-isystem", "include/vendor/libc", "-o", - "build/cmd/dvtm/hook.o", - "sys/cmd/dvtm/hook.c" + "build/libbio/io/newick.o", + "sys/libbio/io/newick.c" ], "directory": "/home/nolln/root", - "file": "sys/cmd/dvtm/hook.c" + "file": "sys/libbio/io/newick.c" }, { "arguments": [ @@ -31,19 +28,16 @@ "-fno-strict-aliasing", "-fwrapv", "-fms-extensions", - "-D_XOPEN_SOURCE=700", - "-D_XOPEN_SOURCE_EXTENDED", - "-DNDEBUG", "-I", "include", "-isystem", "include/vendor/libc", "-o", - "build/cmd/dvtm/vt.o", - "sys/cmd/dvtm/vt.c" + "build/libbio/io/fasta.o", + "sys/libbio/io/fasta.c" ], "directory": "/home/nolln/root", - "file": "sys/cmd/dvtm/vt.c" + "file": "sys/libbio/io/fasta.c" }, { "arguments": [ @@ -54,18 +48,697 @@ "-fno-strict-aliasing", "-fwrapv", "-fms-extensions", - "-D_XOPEN_SOURCE=700", - "-D_XOPEN_SOURCE_EXTENDED", - "-DNDEBUG", "-I", "include", "-isystem", "include/vendor/libc", "-o", - "build/cmd/dvtm/dvtm.o", - "sys/cmd/dvtm/dvtm.c" + "build/cmd/echo/echo.o", + "sys/cmd/echo/echo.c" ], "directory": "/home/nolln/root", - "file": "sys/cmd/dvtm/dvtm.c" + "file": "sys/cmd/echo/echo.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libn/string.o", + "sys/libn/string.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libn/string.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/cmd/filter/filter.o", + "sys/cmd/filter/filter.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/filter/filter.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libn/sort.o", + "sys/libn/sort.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libn/sort.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libn/mmap.o", + "sys/libn/mmap.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libn/mmap.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libmath/basic.o", + "sys/libmath/basic.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libmath/basic.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libbio/phylo.o", + "sys/libbio/phylo.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libbio/phylo.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libn/random.o", + "sys/libn/random.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libn/random.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libmath/blas1.o", + "sys/libmath/blas1.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libmath/blas1.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-o", + "build/cmd/menu/menu.o", + "sys/cmd/menu/menu.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/menu/menu.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libmath/blas2.o", + "sys/libmath/blas2.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libmath/blas2.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I", + "include", + "-o", + "build/cmd/dwm/drw.o", + "sys/cmd/dwm/drw.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/dwm/drw.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-o", + "build/cmd/menu/drw.o", + "sys/cmd/menu/drw.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/menu/drw.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I", + "include", + "-o", + "build/cmd/dwm/util.o", + "sys/cmd/dwm/util.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/dwm/util.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libn/memory.o", + "sys/libn/memory.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libn/memory.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I", + "include", + "-o", + "build/cmd/dwm/hook.o", + "sys/cmd/dwm/hook.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/dwm/hook.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-D_GNU_SOURCE", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libmath/blas.o", + "sys/libmath/blas.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libmath/blas.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libbio/simulate.o", + "sys/libbio/simulate.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libbio/simulate.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libmath/blas3.o", + "sys/libmath/blas3.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libmath/blas3.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-o", + "build/cmd/menu/util.o", + "sys/cmd/menu/util.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/menu/util.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libbio/align.o", + "sys/libbio/align.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libbio/align.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libbio/test.o", + "sys/libbio/test.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libbio/test.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I", + "include", + "-o", + "build/cmd/dwm/client.o", + "sys/cmd/dwm/client.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/dwm/client.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-D_GNU_SOURCE", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libmath/test.o", + "sys/libmath/test.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libmath/test.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libn/test.o", + "sys/libn/test.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libn/test.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I", + "include", + "-o", + "build/cmd/term/x.o", + "sys/cmd/term/x.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/term/x.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/libn/fs.o", + "sys/libn/fs.c" + ], + "directory": "/home/nolln/root", + "file": "sys/libn/fs.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I", + "include", + "-o", + "build/cmd/dwm/dwm.o", + "sys/cmd/dwm/dwm.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/dwm/dwm.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/cmd/cat/cat.o", + "sys/cmd/cat/cat.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/cat/cat.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I/usr/include/freetype2", + "-I/usr/include/libpng16", + "-I/usr/include/harfbuzz", + "-I/usr/include/glib-2.0", + "-I/usr/lib/glib-2.0/include", + "-I", + "include", + "-o", + "build/cmd/term/term.o", + "sys/cmd/term/term.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/term/term.c" + }, + { + "arguments": [ + "gcc", + "-c", + "-g", + "-march=native", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-I", + "include", + "-isystem", + "include/vendor/libc", + "-o", + "build/cmd/ic/ic.o", + "sys/cmd/ic/ic.c" + ], + "directory": "/home/nolln/root", + "file": "sys/cmd/ic/ic.c" } ] \ No newline at end of file diff --git a/include/libn.h b/include/libn.h index f30a761..81ddc9f 100644 --- a/include/libn.h +++ b/include/libn.h @@ -100,7 +100,7 @@ uintptr coro·yield(Coro *c, uintptr arg); error coro·free(Coro *c); // ----------------------------------------------------------------------------- -// Strings +// strings typedef byte* string; @@ -161,7 +161,7 @@ int utf8·isspace(rune r); int utf8·istitle(rune r); // ----------------------------------------------------------------------------- -// I/O +// i/o typedef FILE Stream; typedef struct stat io·Stat; @@ -299,6 +299,37 @@ typedef struct mmap·Reader mmap·Reader mmap·open(byte *name); error mmap·close(mmap·Reader rdr); +// ----------------------------------------------------------------------------- +// filesystem + +#define iota(x) 1 << (x) +enum +{ + fs·preorder = iota(0), + fs·nolinks = iota(1), + fs·verbose = iota(2), +}; +#undef iota + +typedef struct fs·Walker fs·Walker; +typedef struct fs·History fs·History; + +struct fs·Walker +{ + int fd, lev, max, err; + uchar flags : 4; + fs·History *hist; + struct { + void *data; + void (*func)(void *data, char *relp, char *absp, io·Stat* info); + }; + char *base, *end, path[4096]; +}; + +int fs·init(fs·Walker *, char *path); +void fs·fini(fs·Walker *); +void fs·walk(fs·Walker *); + // ----------------------------------------------------------------------------- // libflate // NOTE: Experimental! @@ -339,6 +370,7 @@ vlong gz·seek(gz·Stream *s, long off, enum SeekPos whence); // ----------------------------------------------------------------------------- // error handling functions +void exits(char *s); void errorf(byte* fmt, ...); void verrorf(byte* fmt, va_list args); void panicf(byte *fmt, ...); @@ -389,16 +421,16 @@ uint64 rng·randi(int max); #endif extern char *argv0; -#define ARGBEGIN for((argv0?0:(argv0=*argv)),argv++,argc--;\ - argv[0] && argv[0][0]=='-' && argv[0][1];\ - argc--, argv++) {\ - byte *_args, *_argt;\ - rune _argc;\ - _args = &argv[0][1];\ - if(_args[0]=='-' && _args[1]==0){\ - argc--; argv++; break;\ - }\ - _argc = 0;\ +#define ARGBEGIN for((argv0?0:(argv0=*argv)),argv++,argc--; \ + argv[0] && argv[0][0]=='-' && argv[0][1]; \ + argc--, argv++) { \ + byte *_args, *_argt; \ + rune _argc; \ + _args = &argv[0][1]; \ + if(_args[0]=='-' && _args[1]==0){ \ + argc--; argv++; break; \ + } \ + _argc = 0; \ while(*_args && (_args += utf8·bytetorune(&_argc, _args)))\ switch(_argc) #define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc); diff --git a/include/u.h b/include/u.h index 617e41b..3cf3c7b 100644 --- a/include/u.h +++ b/include/u.h @@ -59,5 +59,6 @@ typedef int error; #define arrlen(Array) (sizeof(Array) / sizeof((Array)[0])) #define arrend(Array) ((Array) + arrlen(Array)) -#define MAX(x, y) ((x) >= (y) ? (x) : (y)) -#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define CLAMP(x, lo, hi) (((x) < (lo)) ? (lo) : (((x) > (hi)) ? (hi) : (x))) diff --git a/sys/cmd/cat/cat.c b/sys/cmd/cat/cat.c index 847ec0c..a82cfe4 100644 --- a/sys/cmd/cat/cat.c +++ b/sys/cmd/cat/cat.c @@ -6,7 +6,7 @@ void cat(Stream *f, byte *name) { long n; - byte buf[8192]; + static byte buf[8192]; while(n = io·read(f, 1, arrlen(buf), buf), n > 0) { if (io·write(stdout, 1, n, buf) != n) { diff --git a/sys/cmd/dvtm/buffer.c b/sys/cmd/dvtm/buffer.c deleted file mode 100644 index b903e71..0000000 --- a/sys/cmd/dvtm/buffer.c +++ /dev/null @@ -1,326 +0,0 @@ -#include "term.h" - -/* row operations */ -void -zero(Row *row, int start, int len) -{ - int i; - Cell cell = { - .r = L'\0', - .pen = {0}, - }; - - for (i = start; i < len + start; i++) - row->cells[i] = cell; - row->dirty = true; -} - -void -roll(Row *start, Row *end, int count) -{ - int n = end - start; - - count %= n; - if (count < 0) - count += n; - - if (count) { - char buf[count * sizeof(Row)]; - memcpy(buf, start, count * sizeof(Row)); - memmove(start, start + count, (n - count) * sizeof(Row)); - memcpy(end - count, buf, count * sizeof(Row)); - for (Row *row = start; row < end; row++) - row->dirty = true; - } -} - -/* buffer operations */ -void -bclear(Buffer *b) -{ - int i; - Cell cell = { - .r = L'\0', - .pen = { - .state = PenNormal, - .col.fg = -1, - .col.bg = -1, - }, - }; - - for (i = 0; i < b->nrow; i++) { - Row *row = b->row + i; - for (int j = 0; j < b->ncol; j++) { - row->cells[j] = cell; - row->dirty = true; - } - } -} - -void -bfree(Buffer *b) -{ - int i; - - for (i = 0; i < b->nrow; i++) - free(b->row[i].cells); - - free(b->row); - - for (i = 0; i < b->scroll.size; i++) - free(b->scroll.buf[i].cells); - - free(b->scroll.buf); - free(b->tabs); -} - -void -bscroll(Buffer *b, int s) -{ - /* work in screenfuls */ - int ssz = b->scroll.bot - b->scroll.top; - - if (s > ssz) { - bscroll(b, ssz); - bscroll(b, s - ssz); - return; - } - if (s < -ssz) { - bscroll(b, -ssz); - bscroll(b, s + ssz); - return; - } - - b->scroll.above += s; - if (b->scroll.above >= b->scroll.size) - b->scroll.above = b->scroll.size; - - if (s > 0 && b->scroll.size) { - for (int i = 0; i < s; i++) { - Row tmp = b->scroll.top[i]; - b->scroll.top[i] = b->scroll.buf[b->scroll.index]; - b->scroll.buf[b->scroll.index] = tmp; - - b->scroll.index++; - if (b->scroll.index == b->scroll.size) - b->scroll.index = 0; - } - } - roll(b->scroll.top, b->scroll.bot, s); - if (s < 0 && b->scroll.size) { - for (int i = (-s) - 1; i >= 0; i--) { - b->scroll.index--; - if (b->scroll.index == -1) - b->scroll.index = b->scroll.size - 1; - - Row tmp = b->scroll.top[i]; - b->scroll.top[i] = b->scroll.buf[b->scroll.index]; - b->scroll.buf[b->scroll.index] = tmp; - b->scroll.top[i].dirty = true; - } - } -} - -void -bresize(Buffer *b, int nrow, int ncol) -{ - Row *row = b->row; - - if (b->nrow != nrow) { - if (b->crow >= row + nrow) { - /* scroll up instead of simply chopping off bottom */ - bscroll(b, (b->crow - b->row) - nrow + 1); - } - while (b->nrow > nrow) { - free(row[b->nrow - 1].cells); - b->nrow--; - } - - row = realloc(row, sizeof(Row) * nrow); - } - - if (b->maxcols < ncol) { - for (int r = 0; r < b->nrow; r++) { - row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol); - if (b->ncol < ncol) - zero(row + r, b->ncol, ncol - b->ncol); - row[r].dirty = true; - } - Row *sbuf = b->scroll.buf; - for (int r = 0; r < b->scroll.size; r++) { - sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol); - if (b->ncol < ncol) - zero(sbuf + r, b->ncol, ncol - b->ncol); - } - b->tabs = realloc(b->tabs, sizeof(*b->tabs) * ncol); - for (int c = b->ncol; c < ncol; c++) - b->tabs[c] = !(c & 7); - b->maxcols = ncol; - b->ncol = ncol; - } else if (b->ncol != ncol) { - for (int r = 0; r < b->nrow; r++) - row[r].dirty = true; - b->ncol = ncol; - } - - int deltarows = 0; - if (b->nrow < nrow) { - while (b->nrow < nrow) { - row[b->nrow].cells = calloc(b->maxcols, sizeof(Cell)); - zero(row + b->nrow, 0, b->maxcols); - b->nrow++; - } - - /* prepare for backfill */ - if (b->crow >= b->scroll.bot - 1) { - deltarows = b->row + nrow - b->crow - 1; - if (deltarows > b->scroll.above) - deltarows = b->scroll.above; - } - } - - b->crow += row - b->row; - b->scroll.top = row; - b->scroll.bot = row + nrow; - b->row = row; - - /* perform backfill */ - if (deltarows > 0) { - bscroll(b, -deltarows); - b->crow += deltarows; - } -} - -bool -binit(Buffer *b, int rows, int cols, int size) -{ - b->pen.state = PenNormal; - b->pen.col.fg = b->pen.col.fg = -1; - - if (size < 0) - size = 0; - if (size && !(b->scroll.buf = calloc(size, sizeof(Row)))) - return false; - - b->scroll.size = size; - bresize(b, rows, cols); - return true; -} - -void -bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) -{ - if (bs) - *bs = nil; - if (be) - *be = nil; - if (as) - *as = nil; - if (ae) - *ae = nil; - if (!b->scroll.size) - return; - - if (b->scroll.above) { - if (bs) - *bs = &b->scroll.buf[(b->scroll.index - b->scroll.above + b->scroll.size) % b->scroll.size]; - if (be) - *be = &b->scroll.buf[(b->scroll.index-1 + b->scroll.size) % b->scroll.size]; - } - if (b->scroll.below) { - if (as) - *as = &b->scroll.buf[b->scroll.index]; - if (ae) - *ae = &b->scroll.buf[(b->scroll.index + b->scroll.below-1) % b->scroll.size]; - } -} - -Row * -browfirst(Buffer *b) -{ - Row *bstart; - if (!b->scroll.size || !b->scroll.above) - return b->row; - bboundary(b, &bstart, nil, nil, nil); - return bstart; -} - -Row * -browlast(Buffer *b) -{ - Row *aend; - if (!b->scroll.size || !b->scroll.below) - return b->row + b->nrow - 1; - bboundary(b, nil, nil, nil, &aend); - return aend; -} - -Row * -brownext(Buffer *b, Row *row) -{ - Row *before_start, *before_end, *after_start, *after_end; - Row *first = b->row, *last = b->row + b->nrow - 1; - - if (!row) - return nil; - - bboundary(b, &before_start, &before_end, &after_start, &after_end); - - if (row >= first && row < last) - return ++row; - if (row == last) - return after_start; - if (row == before_end) - return first; - if (row == after_end) - return nil; - if (row == &b->scroll.buf[b->scroll.size - 1]) - return b->scroll.buf; - return ++row; -} - -Row * -bprevrow(Buffer *b, Row *row) -{ - Row *before_start, *before_end, *after_start, *after_end; - Row *first = b->row, *last = b->row + b->nrow - 1; - - if (!row) - return nil; - - bboundary(b, &before_start, &before_end, &after_start, &after_end); - - if (row > first && row <= last) - return --row; - if (row == first) - return before_end; - if (row == before_start) - return nil; - if (row == after_start) - return last; - if (row == b->scroll.buf) - return &b->scroll.buf[b->scroll.size - 1]; - return --row; -} - -void -brender(Buffer *b, Term *t) -{ - int r, c, n; - char u[UTFmax+1]; - Row *row; - Cell *cell; - - for (r = 0; r < b->nrow; r++) { - row = b->row + r; - if (!row->dirty) - continue; - - for (c = 0; c < b->ncol; c++) { - cell = row->cells + c; - tsetpen(t, cell->pen); - n = utf8·runetobyte(u, &cell->r); - twrite(t, n, u); - } - } -} diff --git a/sys/cmd/dvtm/buffer.h b/sys/cmd/dvtm/buffer.h deleted file mode 100644 index e69de29..0000000 diff --git a/sys/cmd/dvtm/dvtm-editor.c b/sys/cmd/dvtm/dvtm-editor.c index 45a61b2..e8685e9 100644 --- a/sys/cmd/dvtm/dvtm-editor.c +++ b/sys/cmd/dvtm/dvtm-editor.c @@ -26,7 +26,9 @@ #include #include -static void error(const char *msg, ...) { +static +void +error(const char *msg, ...) { va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); @@ -36,7 +38,8 @@ static void error(const char *msg, ...) { fprintf(stderr, "\n"); } -int main(int argc, char *argv[]) +int +main(int argc, char *argv[]) { int exit_status = EXIT_FAILURE, tmp_write = -1; diff --git a/sys/cmd/dvtm/dvtm.c b/sys/cmd/dvtm/dvtm.c index 68f4b8a..3c0f52b 100644 --- a/sys/cmd/dvtm/dvtm.c +++ b/sys/cmd/dvtm/dvtm.c @@ -5,7 +5,6 @@ uint waw, wah, wax, way; Client *clients = nil; char *title; const char *dvtm_name = "dvtm"; - Screen screen = { .mfact = MFACT, .nmaster = NMASTER, .history = SCROLL_HISTORY }; static Client *stack = nil; static Client *sel = nil; @@ -17,15 +16,7 @@ static uint tagset[2] = { 1, 1 }; static bool mouse_events_enabled = ENABLE_MOUSE; static Layout *layout = layouts; -static StatusBar bar = -{ - .fd = -1, - .lastpos = BAR_POS, - .pos = BAR_POS, - .autohide = BAR_AUTOHIDE, - .h = 1 -}; - +static StatusBar bar = { .fd = -1, .lastpos = BAR_POS, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 }; static CmdFifo cmdfifo = { .fd = -1 }; static const char *shell; static Register copyreg; @@ -33,6 +24,7 @@ static volatile sig_atomic_t running = true; static bool runinall = false; /* function implementations */ + static void eprint(const char *errstr, ...) @@ -195,8 +187,7 @@ show_border(void) { return (bar.pos != BAR_OFF) || (clients && clients->next); } -static -void +static void draw_border(Client *c) { char t = '\0'; int x, y, maxlen, attrs = NORMAL_ATTR; @@ -228,17 +219,13 @@ draw_border(Client *c) { wmove(c->window, y, x); } -static -void -draw_content(Client *c) -{ +static void +draw_content(Client *c) { vt_draw(c->term, c->window, c->has_title_line, 0); } -static -void -draw(Client *c) -{ +static void +draw(Client *c) { if (is_content_visible(c)) { redrawwin(c->window); draw_content(c); @@ -248,10 +235,8 @@ draw(Client *c) wnoutrefresh(c->window); } -static -void -draw_all(void) -{ +static void +draw_all(void) { if (!nextvisible(clients)) { sel = nil; curs_set(0); @@ -275,10 +260,8 @@ draw_all(void) draw(sel); } -static -void -arrange(void) -{ +static void +arrange(void) { uint m = 0, n = 0; for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { c->order = ++n; @@ -313,13 +296,10 @@ arrange(void) draw_all(); } -static -void -attach(Client *c) -{ +static void +attach(Client *c) { if (clients) clients->prev = c; - c->next = clients; c->prev = nil; clients = c; @@ -327,10 +307,8 @@ attach(Client *c) c->order = o; } -static -void -attachafter(Client *c, Client *a) -{ /* attach c after a */ +static void +attachafter(Client *c, Client *a) { /* attach c after a */ if (c == a) return; if (!a) @@ -347,18 +325,14 @@ attachafter(Client *c, Client *a) } } -static -void -attachstack(Client *c) -{ +static void +attachstack(Client *c) { c->snext = stack; stack = c; } -static -void -detach(Client *c) -{ +static void +detach(Client *c) { Client *d; if (c->prev) c->prev->next = c->next; @@ -372,10 +346,8 @@ detach(Client *c) c->next = c->prev = nil; } -static -void -settitle(Client *c) -{ +static void +settitle(Client *c) { char *term, *t = title; if (!t && sel == c && *c->title) t = c->title; @@ -385,19 +357,15 @@ settitle(Client *c) } } -static -void -detachstack(Client *c) -{ +static void +detachstack(Client *c) { Client **tc; - for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext) - ; + for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext); *tc = c->snext; } void -focus(Client *c) -{ +focus(Client *c) { if (!c) for (c = stack; c && !isvisible(c); c = c->snext); if (sel == c) @@ -450,8 +418,7 @@ applycolorrules(Client *c) static void -term_title_handler(Vt *term, const char *title) -{ +term_title_handler(Vt *term, const char *title) { Client *c = (Client *)vt_data_get(term); if (title) strncpy(c->title, title, sizeof(c->title) - 1); @@ -1718,7 +1685,7 @@ main(int argc, char *argv[]) KeyCombo keys; uint key_index = 0; memset(keys, 0, sizeof(keys)); - sigset_t emptysigs, blockset; + sigset_t emptyset, blockset; setenv("DVTM", VERSION, 1); if (!parse_args(argc, argv)) { @@ -1726,7 +1693,7 @@ main(int argc, char *argv[]) startup(nil); } - sigemptyset(&emptysigs); + sigemptyset(&emptyset); sigemptyset(&blockset); sigaddset(&blockset, SIGWINCH); sigaddset(&blockset, SIGCHLD); @@ -1734,23 +1701,23 @@ main(int argc, char *argv[]) while (running) { int r, nfds = 0; - fd_set rdrs; /* set of file descriptors we watch */ + fd_set rd; if (screen.need_resize) { resize_screen(); screen.need_resize = false; } - FD_ZERO(&rdrs); - FD_SET(STDIN_FILENO, &rdrs); + FD_ZERO(&rd); + FD_SET(STDIN_FILENO, &rd); if (cmdfifo.fd != -1) { - FD_SET(cmdfifo.fd, &rdrs); + FD_SET(cmdfifo.fd, &rd); nfds = cmdfifo.fd; } if (bar.fd != -1) { - FD_SET(bar.fd, &rdrs); + FD_SET(bar.fd, &rd); nfds = MAX(nfds, bar.fd); } @@ -1764,13 +1731,13 @@ main(int argc, char *argv[]) continue; } int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app); - FD_SET(pty, &rdrs); + FD_SET(pty, &rd); nfds = MAX(nfds, pty); - c = c->next; + c = c->next; } doupdate(); - r = pselect(nfds + 1, &rdrs, nil, nil, nil, &emptysigs); + r = pselect(nfds + 1, &rd, nil, nil, nil, &emptyset); if (r < 0) { if (errno == EINTR) @@ -1779,7 +1746,7 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (FD_ISSET(STDIN_FILENO, &rdrs)) { + if (FD_ISSET(STDIN_FILENO, &rd)) { /* NOTE: this is the input handling step */ int code = getch(); if (code >= 0) { @@ -1804,19 +1771,18 @@ main(int argc, char *argv[]) keypress(code); } } - /* no data available on pty's */ - if (r == 1) + if (r == 1) /* no data available on pty's */ continue; } - if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rdrs)) + if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd)) handle_cmdfifo(); - if (bar.fd != -1 && FD_ISSET(bar.fd, &rdrs)) + if (bar.fd != -1 && FD_ISSET(bar.fd, &rd)) handle_statusbar(); for (Client *c = clients; c; c = c->next) { - if (FD_ISSET(vt_pty_get(c->term), &rdrs)) { + if (FD_ISSET(vt_pty_get(c->term), &rd)) { if (vt_process(c->term) < 0 && errno == EIO) { if (c->editor) c->editor_died = true; diff --git a/sys/cmd/dvtm/dvtm.c.old b/sys/cmd/dvtm/dvtm.c.old deleted file mode 100644 index 749ca1d..0000000 --- a/sys/cmd/dvtm/dvtm.c.old +++ /dev/null @@ -1,1810 +0,0 @@ -#include "dvtm.h" - -/* global variables */ -uint waw, wah, wax, way; -Client *clients = nil; -char *title; -const char *dvtm_name = "dvtm"; - -Screen screen = { .mfact = MFACT, .nmaster = NMASTER, .history = SCROLL_HISTORY }; -static Client *stack = nil; -static Client *sel = nil; -static Client *lastsel = nil; -static Client *msel = nil; - -static uint seltags; -static uint tagset[2] = { 1, 1 }; -static bool mouse_events_enabled = ENABLE_MOUSE; -static Layout *layout = layouts; - -static StatusBar bar = { .fd = -1, .lastpos = BAR_POS, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 }; -static CmdFifo cmdfifo = { .fd = -1 }; -static const char *shell; -static Register copyreg; -static volatile sig_atomic_t running = true; -static bool runinall = false; - -/* function implementations */ -static -void -eprint(const char *errstr, ...) -{ - va_list ap; - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); -} - -static -void -fatal(const char *errstr, ...) -{ - va_list ap; - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); - exit(EXIT_FAILURE); -} - -static -bool -isarrange(void (*func)()) -{ - return func == layout->arrange; -} - -static -bool -isvisible(Client *c) -{ - return c->tags & tagset[seltags]; -} - -static -bool -is_content_visible(Client *c) -{ - if (!c) - return false; - if (isarrange(fullscreen)) - return sel == c; - return isvisible(c) && !c->minimized; -} - -Client* -nextvisible(Client *c) -{ - for (; c && !isvisible(c); c = c->next); - return c; -} - -static -void -updatebarpos(void) -{ - bar.y = 0; - wax = 0; - way = 0; - wah = screen.h; - waw = screen.w; - if (bar.pos == BAR_TOP) { - wah -= bar.h; - way += bar.h; - } else if (bar.pos == BAR_BOTTOM) { - wah -= bar.h; - bar.y = wah; - } -} - -static -void -hidebar(void) -{ - if (bar.pos != BAR_OFF) { - bar.lastpos = bar.pos; - bar.pos = BAR_OFF; - } -} - -static -void -showbar(void) -{ - if (bar.pos == BAR_OFF) - bar.pos = bar.lastpos; -} - -static -void -drawbar(void) -{ - int sx, sy, x, y, width; - uint occupied = 0, urgent = 0; - if (bar.pos == BAR_OFF) - return; - - for (Client *c = clients; c; c = c->next) { - occupied |= c->tags; - if (c->urgent) - urgent |= c->tags; - } - - getyx(stdscr, sy, sx); - attrset(BAR_ATTR); - move(bar.y, 0); - - for (uint i = 0; i < arrlen(tags); i++){ - if (tagset[seltags] & (1 << i)) - attrset(TAG_SEL); - else if (urgent & (1 << i)) - attrset(TAG_URGENT); - else if (occupied & (1 << i)) - attrset(TAG_OCCUPIED); - else - attrset(TAG_NORMAL); - printw(TAG_SYMBOL, tags[i]); - } - - attrset(runinall ? TAG_SEL : TAG_NORMAL); - addstr(layout->symbol); - attrset(TAG_NORMAL); - - getyx(stdscr, y, x); - (void)y; - int maxwidth = screen.w - x - 2; - - addch(BAR_BEGIN); - attrset(BAR_ATTR); - - wchar_t wbuf[sizeof bar.text]; - size_t numchars = mbstowcs(wbuf, bar.text, sizeof bar.text); - - if (numchars != (size_t)-1 && (width = wcswidth(wbuf, maxwidth)) != -1) { - int pos; - for (pos = 0; pos + width < maxwidth; pos++) - addch(' '); - - for (size_t i = 0; i < numchars; i++) { - pos += wcwidth(wbuf[i]); - if (pos > maxwidth) - break; - addnwstr(wbuf+i, 1); - } - - clrtoeol(); - } - - attrset(TAG_NORMAL); - mvaddch(bar.y, screen.w - 1, BAR_END); - attrset(NORMAL_ATTR); - move(sy, sx); - wnoutrefresh(stdscr); -} - -static -int -show_border(void) { - return (bar.pos != BAR_OFF) || (clients && clients->next); -} - -static void -draw_border(Client *c) { - char t = '\0'; - int x, y, maxlen, attrs = NORMAL_ATTR; - - if (!show_border()) - return; - if (sel != c && c->urgent) - attrs = URGENT_ATTR; - if (sel == c || (runinall && !c->minimized)) - attrs = SELECTED_ATTR; - - wattrset(c->window, attrs); - getyx(c->window, y, x); - mvwhline(c->window, 0, 0, ACS_HLINE, c->w); - maxlen = c->w - 10; - if (maxlen < 0) - maxlen = 0; - if ((size_t)maxlen < sizeof(c->title)) { - t = c->title[maxlen]; - c->title[maxlen] = '\0'; - } - - mvwprintw(c->window, 0, 2, "[%s%s#%d]", - *c->title ? c->title : "", - *c->title ? " | " : "", - c->order); - if (t) - c->title[maxlen] = t; - wmove(c->window, y, x); -} - -static void -draw_content(Client *c) { - vt_draw(c->term, c->window, c->has_title_line, 0); -} - -static void -draw(Client *c) { - if (is_content_visible(c)) { - redrawwin(c->window); - draw_content(c); - } - if (!isarrange(fullscreen) || sel == c) - draw_border(c); - wnoutrefresh(c->window); -} - -static void -draw_all(void) { - if (!nextvisible(clients)) { - sel = nil; - curs_set(0); - erase(); - drawbar(); - doupdate(); - return; - } - - if (!isarrange(fullscreen)) { - for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { - if (c != sel) - draw(c); - } - } - /* as a last step the selected window is redrawn, - * this has the effect that the cursor position is - * accurate - */ - if (sel) - draw(sel); -} - -static void -arrange(void) { - uint m = 0, n = 0; - for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { - c->order = ++n; - if (c->minimized) - m++; - } - erase(); - attrset(NORMAL_ATTR); - if (bar.fd == -1 && bar.autohide) { - if ((!clients || !clients->next) && n == 1) - hidebar(); - else - showbar(); - updatebarpos(); - } - if (m && !isarrange(fullscreen)) - wah--; - layout->arrange(); - if (m && !isarrange(fullscreen)) { - uint i = 0, nw = waw / m, nx = wax; - for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { - if (c->minimized) { - resize(c, nx, way+wah, ++i == m ? waw - nx : nw, 1); - nx += nw; - } - } - wah++; - } - focus(nil); - wnoutrefresh(stdscr); - drawbar(); - draw_all(); -} - -static void -attach(Client *c) { - if (clients) - clients->prev = c; - c->next = clients; - c->prev = nil; - clients = c; - for (int o = 1; c; c = nextvisible(c->next), o++) - c->order = o; -} - -static void -attachafter(Client *c, Client *a) { /* attach c after a */ - if (c == a) - return; - if (!a) - for (a = clients; a && a->next; a = a->next); - - if (a) { - if (a->next) - a->next->prev = c; - c->next = a->next; - c->prev = a; - a->next = c; - for (int o = a->order; c; c = nextvisible(c->next)) - c->order = ++o; - } -} - -static void -attachstack(Client *c) { - c->snext = stack; - stack = c; -} - -static void -detach(Client *c) { - Client *d; - if (c->prev) - c->prev->next = c->next; - if (c->next) { - c->next->prev = c->prev; - for (d = nextvisible(c->next); d; d = nextvisible(d->next)) - --d->order; - } - if (c == clients) - clients = c->next; - c->next = c->prev = nil; -} - -static void -settitle(Client *c) { - char *term, *t = title; - if (!t && sel == c && *c->title) - t = c->title; - if (t && (term = getenv("TERM")) && !strstr(term, "linux")) { - printf("\033]0;%s\007", t); - fflush(stdout); - } -} - -static void -detachstack(Client *c) { - Client **tc; - for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext); - *tc = c->snext; -} - -void -focus(Client *c) { - if (!c) - for (c = stack; c && !isvisible(c); c = c->snext); - if (sel == c) - return; - lastsel = sel; - sel = c; - if (lastsel) { - lastsel->urgent = false; - if (!isarrange(fullscreen)) { - draw_border(lastsel); - wnoutrefresh(lastsel->window); - } - } - - if (c) { - detachstack(c); - attachstack(c); - settitle(c); - c->urgent = false; - if (isarrange(fullscreen)) { - draw(c); - } else { - draw_border(c); - wnoutrefresh(c->window); - } - } - curs_set(c && !c->minimized && vt_cursor_visible(c->term)); -} - -static -void -applycolorrules(Client *c) -{ - const ColorRule *r = colorrules; - int fg = r->color->fg, bg = r->color->bg; - attr_t attrs = r->attrs; - - for (uint i = 1; i < arrlen(colorrules); i++) { - r = &colorrules[i]; - if (strstr(c->title, r->title)) { - attrs = r->attrs; - fg = r->color->fg; - bg = r->color->bg; - break; - } - } - - vt_default_colors_set(c->term, attrs, fg, bg); -} - -static -void -term_title_handler(Vt *term, const char *title) { - Client *c = (Client *)vt_data_get(term); - if (title) - strncpy(c->title, title, sizeof(c->title) - 1); - c->title[title ? sizeof(c->title) - 1 : 0] = '\0'; - settitle(c); - if (!isarrange(fullscreen) || sel == c) - draw_border(c); - applycolorrules(c); -} - -static -void -term_urgent_handler(Vt *term) { - Client *c = (Client *)vt_data_get(term); - c->urgent = true; - printf("\a"); - fflush(stdout); - drawbar(); - if (!isarrange(fullscreen) && sel != c && isvisible(c)) - draw_border(c); -} - -static -void -move_client(Client *c, int x, int y) -{ - if (c->x == x && c->y == y) - return; - debug("moving, x: %d y: %d\n", x, y); - if (mvwin(c->window, y, x) == ERR) { - eprint("error moving, x: %d y: %d\n", x, y); - } else { - c->x = x; - c->y = y; - } -} - -static -void -resize_client(Client *c, int w, int h) -{ - bool has_title_line = show_border(); - bool resize_window = c->w != w || c->h != h; - if (resize_window) { - debug("resizing, w: %d h: %d\n", w, h); - if (wresize(c->window, h, w) == ERR) { - eprint("error resizing, w: %d h: %d\n", w, h); - } else { - c->w = w; - c->h = h; - } - } - if (resize_window || c->has_title_line != has_title_line) { - c->has_title_line = has_title_line; - vt_resize(c->app, h - has_title_line, w); - if (c->editor) - vt_resize(c->editor, h - has_title_line, w); - } -} - -void -resize(Client *c, int x, int y, int w, int h) -{ - resize_client(c, w, h); - move_client(c, x, y); -} - -static -Client* -get_client_by_coord(uint x, unsigned int y) { - if (y < way || y >= way+wah) - return nil; - if (isarrange(fullscreen)) - return sel; - for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { - if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) { - debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order); - return c; - } - } - return nil; -} - -static -void -sigchld_handler(int sig) { - int errsv = errno; - int status; - pid_t pid; - - while ((pid = waitpid(-1, &status, WNOHANG)) != 0) { - if (pid == -1) { - if (errno == ECHILD) { - /* no more child processes */ - break; - } - eprint("waitpid: %s\n", strerror(errno)); - break; - } - - debug("child with pid %d died\n", pid); - - for (Client *c = clients; c; c = c->next) { - if (c->pid == pid) { - c->died = true; - break; - } - if (c->editor && vt_pid_get(c->editor) == pid) { - c->editor_died = true; - break; - } - } - } - - errno = errsv; -} - -static -void -sigwinch_handler(int sig) { - screen.need_resize = true; -} - -static -void -sigterm_handler(int sig) { - running = false; -} - -static -void -resize_screen(void) -{ - struct winsize ws; - - if (ioctl(0, TIOCGWINSZ, &ws) == -1) { - getmaxyx(stdscr, screen.h, screen.w); - } else { - screen.w = ws.ws_col; - screen.h = ws.ws_row; - } - - debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h); - - resizeterm(screen.h, screen.w); - wresize(stdscr, screen.h, screen.w); - updatebarpos(); - clear(); - arrange(); -} - -static -KeyBinding* -keybinding(KeyCombo keys, uint keycount) -{ - for (uint b = 0; b < arrlen(bindings); b++) { - for (uint k = 0; k < keycount; k++) { - if (keys[k] != bindings[b].keys[k]) - break; - if (k == keycount - 1) - return &bindings[b]; - } - } - return nil; -} - -static -uint -bitoftag(const char *tag) -{ - uint i; - if (!tag) - return ~0; - for (i = 0; (i < arrlen(tags)) && strcmp(tags[i], tag); i++); - return (i < arrlen(tags)) ? (1 << i) : 0; -} - -static void -tagschanged() { - bool allminimized = true; - for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { - if (!c->minimized) { - allminimized = false; - break; - } - } - if (allminimized && nextvisible(clients)) { - focus(nil); - toggleminimize(nil); - } - arrange(); -} - -void -tag(const char *args[]) -{ - if (!sel) - return; - sel->tags = bitoftag(args[0]) & TAGMASK; - tagschanged(); -} - -void -tagid(const char *args[]) -{ - if (!args[0] || !args[1]) - return; - - const int win_id = atoi(args[0]); - for (Client *c = clients; c; c = c->next) { - if (c->id == win_id) { - uint ntags = c->tags; - for (uint i = 1; i < MAX_ARGS && args[i]; i++) { - if (args[i][0] == '+') - ntags |= bitoftag(args[i]+1); - else if (args[i][0] == '-') - ntags &= ~bitoftag(args[i]+1); - else - ntags = bitoftag(args[i]); - } - ntags &= TAGMASK; - if (ntags) { - c->tags = ntags; - tagschanged(); - } - return; - } - } -} - -void -toggletag(const char *args[]) -{ - if (!sel) - return; - uint newtags = sel->tags ^ (bitoftag(args[0]) & TAGMASK); - if (newtags) { - sel->tags = newtags; - tagschanged(); - } -} - -void -toggleview(const char *args[]) -{ - uint newtagset = tagset[seltags] ^ (bitoftag(args[0]) & TAGMASK); - if (newtagset) { - tagset[seltags] = newtagset; - tagschanged(); - } -} - -void -view(const char *args[]) -{ - uint newtagset = bitoftag(args[0]) & TAGMASK; - if (tagset[seltags] != newtagset && newtagset) { - seltags ^= 1; /* toggle sel tagset */ - tagset[seltags] = newtagset; - tagschanged(); - } -} - -void -viewprevtag(const char *args[]) -{ - seltags ^= 1; - tagschanged(); -} - -static -void -keypress(int code) -{ - int key = -1; - uint len = 1; - char buf[8] = { '\e' }; - - if (code == '\e') { - /* pass characters following escape to the underlying app */ - nodelay(stdscr, TRUE); - for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++) { - if (t > 255) { - key = t; - break; - } - buf[len] = t; - } - nodelay(stdscr, FALSE); - } - - for (Client *c = runinall ? nextvisible(clients) : sel; c; c = nextvisible(c->next)) { - if (is_content_visible(c)) { - c->urgent = false; - if (code == '\e') - vt_write(c->term, buf, len); - else - vt_keypress(c->term, code); - - if (key != -1) - vt_keypress(c->term, key); - } - if (!runinall) - break; - } -} - -static -void -mouse_setup(void) -{ -#ifdef CONFIG_MOUSE - mmask_t mask = 0; - - if (mouse_events_enabled) { - mask = BUTTON1_CLICKED | BUTTON2_CLICKED; - for (uint i = 0; i < arrlen(buttons); i++) - mask |= buttons[i].mask; - } - mousemask(mask, nil); -#endif /* CONFIG_MOUSE */ -} - -static bool -checkshell(const char *shell) { - if (shell == nil || *shell == '\0' || *shell != '/') - return false; - if (!strcmp(strrchr(shell, '/')+1, dvtm_name)) - return false; - if (access(shell, X_OK)) - return false; - return true; -} - -static const char * -getshell(void) { - const char *shell = getenv("SHELL"); - struct passwd *pw; - - if (checkshell(shell)) - return shell; - if ((pw = getpwuid(getuid())) && checkshell(pw->pw_shell)) - return pw->pw_shell; - return "/bin/sh"; -} - -static -void -setup(void) -{ - shell = getshell(); - setlocale(LC_CTYPE, ""); - initscr(); - start_color(); - noecho(); - nonl(); - keypad(stdscr, TRUE); - mouse_setup(); - raw(); - vt_init(); - vt_keytable_set(keytable, arrlen(keytable)); - for (uint i = 0; i < arrlen(colors); i++) { - if (COLORS == 256) { - if (colors[i].fg256) - colors[i].fg = colors[i].fg256; - if (colors[i].bg256) - colors[i].bg = colors[i].bg256; - } - colors[i].pair = vt_color_reserve(colors[i].fg, colors[i].bg); - } - resize_screen(); - - struct sigaction sa; - memset(&sa, 0, sizeof sa); - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sa.sa_handler = sigwinch_handler; - sigaction(SIGWINCH, &sa, nil); - sa.sa_handler = sigchld_handler; - sigaction(SIGCHLD, &sa, nil); - sa.sa_handler = sigterm_handler; - sigaction(SIGTERM, &sa, nil); - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, nil); -} - -static -void -destroy(Client *c) { - if (sel == c) - focusnextnm(nil); - detach(c); - detachstack(c); - if (sel == c) { - Client *next = nextvisible(clients); - if (next) { - focus(next); - toggleminimize(nil); - } else { - sel = nil; - } - } - if (lastsel == c) - lastsel = nil; - werase(c->window); - wnoutrefresh(c->window); - vt_destroy(c->term); - delwin(c->window); - if (!clients && arrlen(actions)) { - if (!strcmp(c->cmd, shell)) - quit(nil); - else - create(nil); - } - free(c); - arrange(); -} - -static -void -cleanup(void) { - while (clients) - destroy(clients); - vt_shutdown(); - endwin(); - free(copyreg.data); - if (bar.fd > 0) - close(bar.fd); - if (bar.file) - unlink(bar.file); - if (cmdfifo.fd > 0) - close(cmdfifo.fd); - if (cmdfifo.file) - unlink(cmdfifo.file); -} - -static -char *getcwd_by_pid(Client *c) { - if (!c) - return nil; - char buf[32]; - snprintf(buf, sizeof buf, "/proc/%d/cwd", c->pid); - return realpath(buf, nil); -} - -void -create(const char *args[]) -{ - const char *pargs[4] = { shell, nil }; - char buf[8], *cwd = nil; - const char *env[] = { - "DVTM_WINDOW_ID", buf, - nil - }; - - if (args && args[0]) { - pargs[1] = "-c"; - pargs[2] = args[0]; - pargs[3] = nil; - } - Client *c = calloc(1, sizeof(Client)); - if (!c) - return; - c->tags = tagset[seltags]; - c->id = ++cmdfifo.id; - snprintf(buf, sizeof buf, "%d", c->id); - - if (!(c->window = newwin(wah, waw, way, wax))) { - free(c); - return; - } - - c->term = c->app = vt_create(screen.h, screen.w, screen.history); - if (!c->term) { - delwin(c->window); - free(c); - return; - } - - if (args && args[0]) { - c->cmd = args[0]; - char name[PATH_MAX]; - strncpy(name, args[0], sizeof(name)); - name[sizeof(name)-1] = '\0'; - strncpy(c->title, basename(name), sizeof(c->title)); - } else { - c->cmd = shell; - } - - if (args && args[1]) - strncpy(c->title, args[1], sizeof(c->title)); - c->title[sizeof(c->title)-1] = '\0'; - - if (args && args[2]) - cwd = !strcmp(args[2], "$CWD") ? getcwd_by_pid(sel) : (char*)args[2]; - c->pid = vt_forkpty(c->term, shell, pargs, cwd, env, nil, nil); - if (args && args[2] && !strcmp(args[2], "$CWD")) - free(cwd); - vt_data_set(c->term, c); - vt_title_handler_set(c->term, term_title_handler); - vt_urgent_handler_set(c->term, term_urgent_handler); - applycolorrules(c); - c->x = wax; - c->y = way; - debug("client with pid %d forked\n", c->pid); - attach(c); - focus(c); - arrange(); -} - -void -copymode(const char *args[]) -{ - if (!args || !args[0] || !sel || sel->editor) - return; - - bool colored = strstr(args[0], "pager") != nil; - - if (!(sel->editor = vt_create(sel->h - sel->has_title_line, sel->w, 0))) - return; - - int *to = &sel->editor_fds[0]; - int *from = strstr(args[0], "editor") ? &sel->editor_fds[1] : nil; - sel->editor_fds[0] = sel->editor_fds[1] = -1; - - const char *argv[3] = { args[0], nil, nil }; - char argline[32]; - int line = vt_content_start(sel->app); - snprintf(argline, sizeof(argline), "+%d", line); - argv[1] = argline; - - if (vt_forkpty(sel->editor, args[0], argv, nil, nil, to, from) < 0) { - vt_destroy(sel->editor); - sel->editor = nil; - return; - } - - sel->term = sel->editor; - - if (sel->editor_fds[0] != -1) { - char *buf = nil; - size_t len = vt_content_get(sel->app, &buf, colored); - char *cur = buf; - while (len > 0) { - ssize_t res = write(sel->editor_fds[0], cur, len); - if (res < 0) { - if (errno == EAGAIN || errno == EINTR) - continue; - break; - } - cur += res; - len -= res; - } - free(buf); - close(sel->editor_fds[0]); - sel->editor_fds[0] = -1; - } - - if (args[1]) - vt_write(sel->editor, args[1], strlen(args[1])); -} - -void -focusn(const char *args[]) -{ - for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { - if (c->order == atoi(args[0])) { - focus(c); - if (c->minimized) - toggleminimize(nil); - return; - } - } -} - -void -focusid(const char *args[]) -{ - if (!args[0]) - return; - - const int win_id = atoi(args[0]); - for (Client *c = clients; c; c = c->next) { - if (c->id == win_id) { - focus(c); - if (c->minimized) - toggleminimize(nil); - if (!isvisible(c)) { - c->tags |= tagset[seltags]; - tagschanged(); - } - return; - } - } -} - -void -focusnext(const char *args[]) -{ - Client *c; - if (!sel) - return; - for (c = sel->next; c && !isvisible(c); c = c->next); - if (!c) - for (c = clients; c && !isvisible(c); c = c->next); - if (c) - focus(c); -} - -void -focusnextnm(const char *args[]) -{ - if (!sel) - return; - Client *c = sel; - do { - c = nextvisible(c->next); - if (!c) - c = nextvisible(clients); - } while (c->minimized && c != sel); - focus(c); -} - -void -focusprev(const char *args[]) -{ - Client *c; - if (!sel) - return; - for (c = sel->prev; c && !isvisible(c); c = c->prev); - if (!c) { - for (c = clients; c && c->next; c = c->next); - for (; c && !isvisible(c); c = c->prev); - } - if (c) - focus(c); -} - -void -focusprevnm(const char *args[]) -{ - if (!sel) - return; - Client *c = sel; - do { - for (c = c->prev; c && !isvisible(c); c = c->prev); - if (!c) { - for (c = clients; c && c->next; c = c->next); - for (; c && !isvisible(c); c = c->prev); - } - } while (c && c != sel && c->minimized); - focus(c); -} - -void -focuslast(const char *args[]) -{ - if (lastsel) - focus(lastsel); -} - -void -focusup(const char *args[]) -{ - if (!sel) - return; - /* avoid vertical separator, hence +1 in x direction */ - Client *c = get_client_by_coord(sel->x + 1, sel->y - 1); - if (c) - focus(c); - else - focusprev(args); -} - -void -focusdown(const char *args[]) -{ - if (!sel) - return; - Client *c = get_client_by_coord(sel->x, sel->y + sel->h); - if (c) - focus(c); - else - focusnext(args); -} - -void -focusleft(const char *args[]) -{ - if (!sel) - return; - Client *c = get_client_by_coord(sel->x - 2, sel->y); - if (c) - focus(c); - else - focusprev(args); -} - -void -focusright(const char *args[]) -{ - if (!sel) - return; - Client *c = get_client_by_coord(sel->x + sel->w + 1, sel->y); - if (c) - focus(c); - else - focusnext(args); -} - -void -killclient(const char *args[]) -{ - if (!sel) - return; - debug("killing client with pid: %d\n", sel->pid); - kill(-sel->pid, SIGKILL); -} - -void -paste(const char *args[]) -{ - if (sel && copyreg.data) - vt_write(sel->term, copyreg.data, copyreg.len); -} - -void -quit(const char *args[]) -{ - cleanup(); - exit(EXIT_SUCCESS); -} - -void -redraw(const char *args[]) -{ - for (Client *c = clients; c; c = c->next) { - if (!c->minimized) { - vt_dirty(c->term); - wclear(c->window); - wnoutrefresh(c->window); - } - } - resize_screen(); -} - -void -scrollback(const char *args[]) -{ - if (!is_content_visible(sel)) - return; - - if (!args[0] || atoi(args[0]) < 0) - vt_scroll(sel->term, -sel->h/2); - else - vt_scroll(sel->term, sel->h/2); - - draw(sel); - curs_set(vt_cursor_visible(sel->term)); -} - -void -send(const char *args[]) -{ - if (sel && args && args[0]) - vt_write(sel->term, args[0], strlen(args[0])); -} - -void -setlayout(const char *args[]) -{ - uint i; - - if (!args || !args[0]) { - if (++layout == &layouts[arrlen(layouts)]) - layout = &layouts[0]; - } else { - for (i = 0; i < arrlen(layouts); i++) - if (!strcmp(args[0], layouts[i].symbol)) - break; - if (i == arrlen(layouts)) - return; - layout = &layouts[i]; - } - arrange(); -} - -void -incnmaster(const char *args[]) -{ - int delta; - - if (isarrange(fullscreen) || isarrange(grid)) - return; - /* arg handling, manipulate nmaster */ - if (args[0] == nil) { - screen.nmaster = NMASTER; - } else if (sscanf(args[0], "%d", &delta) == 1) { - if (args[0][0] == '+' || args[0][0] == '-') - screen.nmaster += delta; - else - screen.nmaster = delta; - if (screen.nmaster < 1) - screen.nmaster = 1; - } - arrange(); -} - -void -setmfact(const char *args[]) -{ - float delta; - - if (isarrange(fullscreen) || isarrange(grid)) - return; - /* arg handling, manipulate mfact */ - if (args[0] == nil) { - screen.mfact = MFACT; - } else if (sscanf(args[0], "%f", &delta) == 1) { - if (args[0][0] == '+' || args[0][0] == '-') - screen.mfact += delta; - else - screen.mfact = delta; - if (screen.mfact < 0.1) - screen.mfact = 0.1; - else if (screen.mfact > 0.9) - screen.mfact = 0.9; - } - arrange(); -} - -void -startup(const char *args[]) -{ - for (uint i = 0; i < arrlen(actions); i++) - actions[i].cmd(actions[i].args); -} - -void -togglebar(const char *args[]) -{ - if (bar.pos == BAR_OFF) - showbar(); - else - hidebar(); - bar.autohide = false; - updatebarpos(); - redraw(nil); -} - -void -togglebarpos(const char *args[]) -{ - switch (bar.pos == BAR_OFF ? bar.lastpos : bar.pos) { - case BAR_TOP: - bar.pos = BAR_BOTTOM; - break; - case BAR_BOTTOM: - bar.pos = BAR_TOP; - break; - } - updatebarpos(); - redraw(nil); -} - -void -toggleminimize(const char *args[]) -{ - Client *c, *m, *t; - uint n; - if (!sel) - return; - /* the last window can't be minimized */ - if (!sel->minimized) { - for (n = 0, c = nextvisible(clients); c; c = nextvisible(c->next)) - if (!c->minimized) - n++; - if (n == 1) - return; - } - sel->minimized = !sel->minimized; - m = sel; - /* check whether the master client was minimized */ - if (sel == nextvisible(clients) && sel->minimized) { - c = nextvisible(sel->next); - detach(c); - attach(c); - focus(c); - detach(m); - for (; c && (t = nextvisible(c->next)) && !t->minimized; c = t); - attachafter(m, c); - } else if (m->minimized) { - /* non master window got minimized move it above all other - * minimized ones */ - focusnextnm(nil); - detach(m); - for (c = nextvisible(clients); c && (t = nextvisible(c->next)) && !t->minimized; c = t); - attachafter(m, c); - } else { /* window is no longer minimized, move it to the master area */ - vt_dirty(m->term); - detach(m); - attach(m); - } - arrange(); -} - -void -togglemouse(const char *args[]) -{ - mouse_events_enabled = !mouse_events_enabled; - mouse_setup(); -} - -void -togglerunall(const char *args[]) -{ - runinall = !runinall; - drawbar(); - draw_all(); -} - -void -zoom(const char *args[]) -{ - Client *c; - - if (!sel) - return; - if (args && args[0]) - focusn(args); - if ((c = sel) == nextvisible(clients)) - if (!(c = nextvisible(c->next))) - return; - detach(c); - attach(c); - focus(c); - if (c->minimized) - toggleminimize(nil); - arrange(); -} - -/* commands for use by mouse bindings */ -void -mouse_focus(const char *args[]) -{ - focus(msel); - if (msel->minimized) - toggleminimize(nil); -} - -void -mouse_fullscreen(const char *args[]) -{ - mouse_focus(nil); - setlayout(isarrange(fullscreen) ? nil : args); -} - -void -mouse_minimize(const char *args[]) -{ - focus(msel); - toggleminimize(nil); -} - -void -mouse_zoom(const char *args[]) -{ - focus(msel); - zoom(nil); -} - -static -Cmd * -get_cmd_by_name(const char *name) { - for (uint i = 0; i < arrlen(commands); i++) { - if (!strcmp(name, commands[i].name)) - return &commands[i]; - } - return nil; -} - -static -void -handle_cmdfifo(void) { - int r; - char *p, *s, cmdbuf[512], c; - Cmd *cmd; - - r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1); - if (r <= 0) { - cmdfifo.fd = -1; - return; - } - - cmdbuf[r] = '\0'; - p = cmdbuf; - while (*p) { - /* find the command name */ - for (; *p == ' ' || *p == '\n'; p++); - for (s = p; *p && *p != ' ' && *p != '\n'; p++); - if ((c = *p)) - *p++ = '\0'; - if (*s && (cmd = get_cmd_by_name(s)) != nil) { - bool quote = false; - int argc = 0; - const char *args[MAX_ARGS], *arg; - memset(args, 0, sizeof(args)); - /* if arguments were specified in config.h ignore the one given via - * the named pipe and thus skip everything until we find a new line - */ - if (cmd->action.args[0] || c == '\n') { - debug("execute %s", s); - cmd->action.cmd(cmd->action.args); - while (*p && *p != '\n') - p++; - continue; - } - /* no arguments were given in config.h so we parse the command line */ - while (*p == ' ') - p++; - arg = p; - for (; (c = *p); p++) { - switch (*p) { - case '\\': - /* remove the escape character '\\' move every - * following character to the left by one position - */ - switch (p[1]) { - case '\\': - case '\'': - case '\"': { - char *t = p+1; - do { - t[-1] = *t; - } while (*t++); - } - } - break; - case '\'': - case '\"': - quote = !quote; - break; - case ' ': - if (!quote) { - case '\n': - /* remove trailing quote if there is one */ - if (*(p - 1) == '\'' || *(p - 1) == '\"') - *(p - 1) = '\0'; - *p++ = '\0'; - /* remove leading quote if there is one */ - if (*arg == '\'' || *arg == '\"') - arg++; - if (argc < MAX_ARGS) - args[argc++] = arg; - - while (*p == ' ') - ++p; - arg = p--; - } - break; - } - - if (c == '\n' || *p == '\n') { - if (!*p) - p++; - debug("execute %s", s); - for(int i = 0; i < argc; i++) - debug(" %s", args[i]); - debug("\n"); - cmd->action.cmd(args); - break; - } - } - } - } -} - -static void -handle_mouse(void) { -#ifdef CONFIG_MOUSE - MEVENT event; - uint i; - if (getmouse(&event) != OK) - return; - msel = get_client_by_coord(event.x, event.y); - - if (!msel) - return; - - debug("mouse x:%d y:%d cx:%d cy:%d mask:%d\n", event.x, event.y, event.x - msel->x, event.y - msel->y, event.bstate); - - vt_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate); - - for (i = 0; i < arrlen(buttons); i++) { - if (event.bstate & buttons[i].mask) - buttons[i].action.cmd(buttons[i].action.args); - } - - msel = nil; -#endif /* CONFIG_MOUSE */ -} - -static void -handle_statusbar(void) { - char *p; - int r; - switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) { - case -1: - strncpy(bar.text, strerror(errno), sizeof bar.text - 1); - bar.text[sizeof bar.text - 1] = '\0'; - bar.fd = -1; - break; - case 0: - bar.fd = -1; - break; - default: - bar.text[r] = '\0'; - p = bar.text + r - 1; - for (; p >= bar.text && *p == '\n'; *p-- = '\0'); - for (; p >= bar.text && *p != '\n'; --p); - if (p >= bar.text) - memmove(bar.text, p + 1, strlen(p)); - drawbar(); - } -} - -static void -handle_editor(Client *c) { - if (!copyreg.data && (copyreg.data = malloc(screen.history))) - copyreg.size = screen.history; - copyreg.len = 0; - while (c->editor_fds[1] != -1 && copyreg.len < copyreg.size) { - ssize_t len = read(c->editor_fds[1], copyreg.data + copyreg.len, copyreg.size - copyreg.len); - if (len == -1) { - if (errno == EINTR) - continue; - break; - } - if (len == 0) - break; - copyreg.len += len; - if (copyreg.len == copyreg.size) { - copyreg.size *= 2; - if (!(copyreg.data = realloc(copyreg.data, copyreg.size))) { - copyreg.size = 0; - copyreg.len = 0; - } - } - } - c->editor_died = false; - c->editor_fds[1] = -1; - vt_destroy(c->editor); - c->editor = nil; - c->term = c->app; - vt_dirty(c->term); - draw_content(c); - wnoutrefresh(c->window); -} - -static int -open_or_create_fifo(const char *name, const char **name_created) { - struct stat info; - int fd; - - do { - if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) { - if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) { - *name_created = name; - continue; - } - fatal("%s\n", strerror(errno)); - } - } while (fd == -1); - - if (fstat(fd, &info) == -1) - fatal("%s\n", strerror(errno)); - if (!S_ISFIFO(info.st_mode)) - fatal("%s is not a named pipe\n", name); - return fd; -} - -static -void -usage(void) { - cleanup(); - eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] " - "[-s status-fifo] [-c cmd-fifo] [cmd...]\n"); - exit(EXIT_FAILURE); -} - -static -bool -parse_args(int argc, char *argv[]) { - bool init = false; - const char *name = argv[0]; - - if (name && (name = strrchr(name, '/'))) - dvtm_name = name + 1; - if (!getenv("ESCDELAY")) - set_escdelay(100); - for (int arg = 1; arg < argc; arg++) { - if (argv[arg][0] != '-') { - const char *args[] = { argv[arg], nil, nil }; - if (!init) { - setup(); - init = true; - } - create(args); - continue; - } - if (argv[arg][1] != 'v' && argv[arg][1] != 'M' && (arg + 1) >= argc) - usage(); - switch (argv[arg][1]) { - case 'v': - puts("dvtm-"VERSION); - exit(EXIT_SUCCESS); - case 'M': - mouse_events_enabled = !mouse_events_enabled; - break; - case 'm': { - char *mod = argv[++arg]; - if (mod[0] == '^' && mod[1]) - *mod = CTRL(mod[1]); - for (uint b = 0; b < arrlen(bindings); b++) - if (bindings[b].keys[0] == MOD) - bindings[b].keys[0] = *mod; - break; - } - case 'd': - set_escdelay(atoi(argv[++arg])); - if (ESCDELAY < 50) - set_escdelay(50); - else if (ESCDELAY > 1000) - set_escdelay(1000); - break; - case 'h': - screen.history = atoi(argv[++arg]); - break; - case 't': - title = argv[++arg]; - break; - case 's': - bar.fd = open_or_create_fifo(argv[++arg], &bar.file); - updatebarpos(); - break; - case 'c': { - const char *fifo; - cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file); - if (!(fifo = realpath(argv[arg], nil))) - fatal("%s\n", strerror(errno)); - setenv("DVTM_CMD_FIFO", fifo, 1); - break; - } - default: - usage(); - } - } - return init; -} - -int -main(int argc, char *argv[]) -{ - KeyCombo keys; - uint key_index = 0; - memset(keys, 0, sizeof(keys)); - sigset_t emptyset, blockset; - - setenv("DVTM", VERSION, 1); - if (!parse_args(argc, argv)) { - setup(); - startup(nil); - } - - sigemptyset(&emptyset); - sigemptyset(&blockset); - sigaddset(&blockset, SIGWINCH); - sigaddset(&blockset, SIGCHLD); - sigprocmask(SIG_BLOCK, &blockset, nil); - - while (running) { - int r, nfds = 0; - fd_set rd; - - if (screen.need_resize) { - resize_screen(); - screen.need_resize = false; - } - - FD_ZERO(&rd); - FD_SET(STDIN_FILENO, &rd); - - if (cmdfifo.fd != -1) { - FD_SET(cmdfifo.fd, &rd); - nfds = cmdfifo.fd; - } - - if (bar.fd != -1) { - FD_SET(bar.fd, &rd); - nfds = MAX(nfds, bar.fd); - } - - for (Client *c = clients; c;) { - if (c->editor && c->editor_died) - handle_editor(c); - if (!c->editor && c->died) { - Client *t = c->next; - destroy(c); - c = t; - continue; - } - int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app); - FD_SET(pty, &rd); - nfds = MAX(nfds, pty); - c = c->next; - } - - doupdate(); - r = pselect(nfds + 1, &rd, nil, nil, nil, &emptyset); - - if (r < 0) { - if (errno == EINTR) - continue; - perror("select()"); - exit(EXIT_FAILURE); - } - - if (FD_ISSET(STDIN_FILENO, &rd)) { - /* NOTE: this is the input handling step */ - int code = getch(); - if (code >= 0) { - keys[key_index++] = code; - KeyBinding *binding = nil; - - if (code == KEY_MOUSE) { - key_index = 0; - handle_mouse(); - } else if ((binding = keybinding(keys, key_index))) { - uint key_length = MAX_KEYS; - while (key_length > 1 && !binding->keys[key_length-1]) - key_length--; - if (key_index == key_length) { - binding->action.cmd(binding->action.args); - key_index = 0; - memset(keys, 0, sizeof(keys)); - } - } else { - key_index = 0; - memset(keys, 0, sizeof(keys)); - keypress(code); - } - } - if (r == 1) /* no data available on pty's */ - continue; - } - - if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd)) - handle_cmdfifo(); - - if (bar.fd != -1 && FD_ISSET(bar.fd, &rd)) - handle_statusbar(); - - for (Client *c = clients; c; c = c->next) { - if (FD_ISSET(vt_pty_get(c->term), &rd)) { - if (vt_process(c->term) < 0 && errno == EIO) { - if (c->editor) - c->editor_died = true; - else - c->died = true; - continue; - } - } - - if (c != sel && is_content_visible(c)) { - draw_content(c); - wnoutrefresh(c->window); - } - } - - if (is_content_visible(sel)) { - draw_content(sel); - curs_set(vt_cursor_visible(sel->term)); - wnoutrefresh(sel->window); - } - } - - cleanup(); - return 0; -} diff --git a/sys/cmd/dvtm/dvtm.h b/sys/cmd/dvtm/dvtm.h index f89f517..a777e18 100644 --- a/sys/cmd/dvtm/dvtm.h +++ b/sys/cmd/dvtm/dvtm.h @@ -19,11 +19,9 @@ #include "vt.h" /* types */ -typedef struct Term Term; -typedef struct Screen Screen; typedef struct Layout Layout; -typedef struct Window Window; typedef struct Client Client; +typedef struct Screen Screen; typedef struct Cmd Cmd; typedef struct CmdFifo CmdFifo; @@ -36,7 +34,6 @@ typedef struct StatusBar StatusBar; struct Screen { - Term *backend; float mfact; uint nmaster; int history; @@ -52,7 +49,7 @@ struct Layout struct Client { - Window *window; + WINDOW *window; Vt *term; Vt *editor, *app; /* meta data */ @@ -72,7 +69,6 @@ struct Client uint tags; }; -#if 0 typedef struct { int fg; int bg; @@ -86,7 +82,6 @@ typedef struct { attr_t attrs; Color *color; } ColorRule; -#endif #define ALT(k) ((k) + (161 - 'a')) diff --git a/sys/cmd/dvtm/events.c b/sys/cmd/dvtm/events.c deleted file mode 100644 index c4f544b..0000000 --- a/sys/cmd/dvtm/events.c +++ /dev/null @@ -1,1690 +0,0 @@ -#include "term.h" - -#include - -#define bufcount(in) in->buf.c - in->buf.b - -enum { - NodeKey, - NodeArr, -}; - -struct Node -{ - int type; -}; - -struct KeyNode -{ - struct Node; - struct KeyInfo key; -}; - -struct ArrNode -{ - struct Node; - uchar min, max; - Node *arr[]; -}; - -// ----------------------------------------------------------------------- -// loads data into trie - -static enum KeyEvent peekmousekey(Input *in, Key *key, ulong *nb); - -#define FuncNameMax 10 -static struct { - char *name; - int type; - int sym; - int mods; -} funcs[] = -{ - /* THIS LIST MUST REMAIN SORTED ALPHABETICALLY! */ - { "backspace", KeySym, SymBackspace, 0 }, - { "begin", KeySym, SymBegin, 0 }, - { "beg", KeySym, SymBegin, 0 }, - { "btab", KeySym, SymTab, ModShift}, - { "cancel", KeySym, SymCancel, 0 }, - { "clear", KeySym, SymClear, 0 }, - { "close", KeySym, SymClose, 0 }, - { "command", KeySym, SymCommand, 0 }, - { "copy", KeySym, SymCopy, 0 }, - { "dc", KeySym, SymDelete, 0 }, - { "down", KeySym, SymDown, 0 }, - { "end", KeySym, SymEnd, 0 }, - { "enter", KeySym, SymEnter, 0 }, - { "exit", KeySym, SymExit, 0 }, - { "find", KeySym, SymFind, 0 }, - { "help", KeySym, SymHelp, 0 }, - { "home", KeySym, SymHome, 0 }, - { "ic", KeySym, SymInsert, 0 }, - { "left", KeySym, SymLeft, 0 }, - { "mark", KeySym, SymMark, 0 }, - { "message", KeySym, SymMessage, 0 }, - { "move", KeySym, SymMove, 0 }, - { "next", KeySym, SymPagedown, 0 }, // Not quite, but it's the best we can do - { "npage", KeySym, SymPagedown, 0 }, - { "open", KeySym, SymOpen, 0 }, - { "options", KeySym, SymOptions, 0 }, - { "ppage", KeySym, SymPageup, 0 }, - { "previous", KeySym, SymPageup, 0 }, // Not quite, but it's the best we can do - { "print", KeySym, SymPrint, 0 }, - { "redo", KeySym, SymRedo, 0 }, - { "reference", KeySym, SymReference, 0 }, - { "refresh", KeySym, SymRefresh, 0 }, - { "replace", KeySym, SymReplace, 0 }, - { "restart", KeySym, SymRestart, 0 }, - { "resume", KeySym, SymResume, 0 }, - { "right", KeySym, SymRight, 0 }, - { "save", KeySym, SymSave, 0 }, - { "select", KeySym, SymSelect, 0 }, - { "suspend", KeySym, SymSuspend, 0 }, - { "undo", KeySym, SymUndo, 0 }, - { "up", KeySym, SymUp, 0 }, - { nil }, -}; - -// ----------------------------------------------------------------------- -// utility functions - -static -int -stricmp(char *s, char *t) -{ - if (s == nil) - if (t == nil) - return 0; - else - return -strlen(t); - else - if (t == nil) - return +strlen(s); - - int d; - for (; *s && *t; s++, t++) { - d = tolower(*s) - tolower(*t); - if (d < 0 || d > 0) - return d; - } - - /* XXX: not sure about passing in 0 here */ - return tolower(*s) - tolower(*t); -} - -// ----------------------------------------------------------------------- -// bytes -> keysymbols - -static -Node * -keynode(int type, int sym, int mask, int set) -{ - struct KeyNode *n; - n = malloc(sizeof(*n)); - if (!n) - panicf("out of memory"); - - n->type = NodeKey; - n->key.type = type; - n->key.sym = sym; - n->key.modmask = mask; - n->key.modset = set; - - return (Node *)n; -} - -static -Node * -arrnode(uchar min, uchar max) -{ - int nb, i; - struct ArrNode *n; - nb = ((int)max-min+1)*sizeof(*n->arr); - n = malloc(sizeof(*n) + nb); - if (!n) - panicf("out of memory"); - - n->type = NodeArr; - n->min = min, n->max = max; - for (i = 0; i <= max-min; i++) - n->arr[i] = nil; - - return (Node *)n; -} - - -static -Node * -nlookup(Node *n, uchar b) -{ - struct ArrNode *arr; - switch (n->type) { - case NodeKey: - panicf("attempting to subdivide a leaf key node"); - return nil; - case NodeArr: - arr = (struct ArrNode *)n; - if (b < arr->min || b > arr->max) - return nil; - return arr->arr[b - arr->min]; - default: - panicf("unrecognized key node type"); - return nil; - } -} - -static -Node * -compress(Node *root) -{ - int i; - uchar min, max; - struct ArrNode *n, *new; - - if (!root) - return root; - - switch (root->type) { - case NodeKey: - return root; - case NodeArr: - n = (struct ArrNode *)root; - /* find the zeros */ - for (min = 0; !n->arr[min]; min++) { - if (min == 255 && !n->arr[min]) { - free(n); - return arrnode(1, 0); - } - } - - for (max = 0xff; !n->arr[max]; max--) - ; - - new = (struct ArrNode *)arrnode(min, max); - - for (i = min; i <= max; i++) - new->arr[i-min] = compress(n->arr[i]); - - free(n); - return (Node*)new; - default: - panicf("unrecognized key node type"); - return nil; - } - - return root; -} - -static -void -teardown(Node *n) -{ - int i; - struct ArrNode *arr; - - switch (n->type) { - case NodeKey: - break; - case NodeArr: - arr = (struct ArrNode *)n; - for (i = arr->min; i <= arr->max; i++) - if (arr->arr[i - arr->min]) - teardown(arr->arr[i - arr->min]); - break; - default: - return; - } - - free(n); -} - -static -int -insert(Node *root, Node *n, char *seq) -{ - int pos; - uchar b; - Node *nn; - struct ArrNode *a; - - for (pos = 0; (b = seq[pos]); pos++) { - nn = nlookup(root, b); - if (!nn) - break; - root = nn; - a = (struct ArrNode *)root; - } - - for (; (b = seq[pos]); pos++) { - nn = (seq[pos+1]) ? arrnode(0, 0xff) : n; - if (!nn) - return 0; - - if (root->type != NodeArr) - panicf("inserted child node onto leaf 'key' node"); - - a = (struct ArrNode*)root; - if (b < a->min || b > a->max) - panicf("out of bound trie insertion"); - - a->arr[b-a->min] = root = nn; - } - - return 1; -} - -/* unibilium helpers */ -static -enum unibi_string -nametostr(char *name) -{ - enum unibi_string ret; - for (ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++) - if (!strcmp(unibi_name_str(ret), name)) - return ret; - - return -1; -} - -static -char * -unibi_lookup(unibi_term *info, char *name) -{ - enum unibi_string idx; - if ((idx = nametostr(name)) == -1) - return nil; - - return (char*)unibi_get_str(info, idx); -} - -static -Node * -tryloadkey(unibi_term *info, char *name, char **val, struct KeyInfo *key) -{ - char *v; - - v = unibi_lookup(info, name); - if (!v || v[0] == 0) - return nil; - - *val = v; - return keynode(key->type, key->sym, key->modmask, key->modset); -} - -static -Node * -loadtermkeys(unibi_term *info) -{ - int i; - struct Node *root, *n; - char *val, name[5 + FuncNameMax + 1]; - - root = arrnode(0, 0xff); - if (!root) - panicf("out of memory"); - - /* load key syms */ - for (i = 0; funcs[i].name; i++) { - sprintf(name, "key_%s", funcs[i].name); - n = tryloadkey(info, name, &val, &(struct KeyInfo){ - .type = funcs[i].type, - .sym = funcs[i].sym, - .modmask = funcs[i].mods, - .modset = funcs[i].mods, - }); - - /* try shift modified */ - if (!n) - n = tryloadkey(info, name, &val, &(struct KeyInfo){ - .type = funcs[i].type, - .sym = funcs[i].sym, - .modmask = funcs[i].mods | ModShift, - .modset = funcs[i].mods | ModShift, - }); - - /* give up */ - if (n) - insert(root, n, val); - } - - /* load function keys */ - for (i = 1; i < 0xff; i++) { - sprintf(name, "key_f%d", i); - n = tryloadkey(info, name, &val, &(struct KeyInfo){ - .type = KeyFunc, - .sym = i, - .modmask = 0, - .modset = 0, - }); - if (!n) - break; - - insert(root, n, val); - } - - /* load mouse keys */ - val = unibi_lookup(info, "key_mouse"); - if (val && !strcmp(val, "\x1b[M")) { - n = keynode(KeyMouse, 0, 0, 0); - insert(root, n, val); - } - - return compress(root); -} - -static -enum KeyEvent -peeksym(Input *in, Key *key, int force, ulong *nb) -{ - int res; - uchar *b, *o; - Node *root; - struct KeyNode *kn; - - root = in->keys; - b = in->buf.b; - while (b <= in->buf.c) { - printf("checking '%s'\n", b); - root = nlookup(root, *b); - if (!root) - break; - b++; - - if (root->type != NodeKey) - continue; - - kn = (struct KeyNode*)root; - if (kn->key.type == KeyMouse) { - o = in->buf.b, in->buf.b = b; - res = peekmousekey(in, key, nb); - in->buf.b = o; - - if (res == EvKey) - *nb += b - in->buf.b; - - return res; - } - key->type = kn->key.type; - key->mods = kn->key.modset; - key->code.sym = kn->key.type; - *nb = b - in->buf.b; - - return EvKey; - } - - if (root && !force) - return EvAgain; - - return EvNil; -} - -// ----------------------------------------------------------------------- -// misc commands - -static -inline -void -setpos(Key *key, int row, int col) -{ - row = MIN(row, 0xfff); - col = MIN(col, 0xfff); - - key->code.mouse[1] = (row & 0x0ff); - key->code.mouse[2] = (col & 0x0ff); - key->code.mouse[3] = (row & 0xf00) >> 8 | (col & 0x300) >> 4; -} - -static -inline -void -getpos(Key *key, int *row, int *col) -{ - if (col) - *col = ((uchar)key->code.mouse[1] | ((uchar)key->code.mouse[3] & 0x0f) << 8) - 1; - - if (row) - *row = ((uchar)key->code.mouse[2] | ((uchar)key->code.mouse[3] & 0x70) << 4) - 1; -} - -// ----------------------------------------------------------------------- -// csi/ss3 commands - -static int csiinit; - -static struct KeyInfo ss3[64]; -static char ss3kpalts[64]; - -static struct KeyInfo csiss3[64]; -static enum KeyEvent (*do_csi[64])(Input *in, Key *key, int cmd, int narg, long *arg); - -static struct KeyInfo csifuncs[35]; - -/* csi/ss3 cmd keys */ -static -enum KeyEvent -do_csi_full(Input *in, Key *key, int cmd, int narg, long *arg) -{ - if(narg > 1 && arg[1] != -1) - key->mods = arg[1] - 1; - else - key->mods = 0; - - key->type = csiss3[cmd - 0x40].type; - key->code.sym = csiss3[cmd - 0x40].sym; - key->mods &= ~(csiss3[cmd - 0x40].modmask); - key->mods |= csiss3[cmd - 0x40].modset; - - if(key->code.sym == SymUnknown) - return EvNil; - - return EvKey; -} - -static -void -put_csi_full(int type, int sym, int modset, int modmask, uchar cmd) -{ - if(cmd < 0x40 || cmd >= 0x80) - return; - - csiss3[cmd - 0x40].type = type; - csiss3[cmd - 0x40].sym = sym; - csiss3[cmd - 0x40].modset = modset; - csiss3[cmd - 0x40].modmask = modmask; - - do_csi[cmd - 0x40] = &do_csi_full; -} - -/* ss3 kpad keys */ -static -void -put_ss3_kpalt(int type, int sym, uchar cmd, char kpalt) -{ - if(cmd < 0x40 || cmd >= 0x80) - return; - - ss3[cmd - 0x40].type = type; - ss3[cmd - 0x40].sym = sym; - ss3[cmd - 0x40].modset = 0; - ss3[cmd - 0x40].modmask= 0; - ss3kpalts[cmd - 0x40] = kpalt; -} - -/* csi number ~func keys */ -static void emitcodepoint(Input *in, rune r, Key *key); - -static -enum KeyEvent -do_csi_func(Input *in, Key *key, int cmd, int narg, long *arg) -{ - if (narg > 1 && arg[1] != -1) - key->mods = arg[1] - 1; - else - key->mods = 0; - - key->type = KeySym; - - if (arg[0] == 27) { - int mod = key->mods; - emitcodepoint(in, (rune)arg[2], key); - key->mods |= mod; - } else if (arg[0] >= 0 && arg[0] < arrlen(csifuncs)) { - key->type = csifuncs[arg[0]].type; - key->code.sym = csifuncs[arg[0]].sym; - key->mods &= ~(csifuncs[arg[0]].modmask); - key->mods |= csifuncs[arg[0]].modset; - } else - key->code.sym = SymUnknown; - - if (key->code.sym == SymUnknown) - return EvNil; - - return EvKey; -} - -static -void -put_csi_func(int type, int sym, int num) -{ - if(num >= arrlen(csifuncs)) - return; - - csifuncs[num].type = type; - csifuncs[num].sym = sym; - csifuncs[num].modset = 0; - csifuncs[num].modmask = 0; - - do_csi['~' - 0x40] = &do_csi_func; -} - -/* CSI u extended unicode keys */ -static -enum KeyEvent -do_csi_u(Input *in, Key *key, int cmd, int narg, long *arg) -{ - switch(cmd) { - case 'u': { - if(narg > 1 && arg[1] != -1) - key->mods = arg[1] - 1; - else - key->mods = 0; - - int mod = key->mods; - key->type = KeySym; - emitcodepoint(in, arg[0], key); - key->mods |= mod; - - return EvKey; - } - default: - return EvNil; - } -} - -/* csi m/M mouse events */ - -static -enum KeyEvent -do_csi_m(Input *in, Key *key, int cmd, int narg, long *arg) -{ - int initial = cmd >> 8; - cmd &= 0xff; - - switch(cmd) { - case 'M': - case 'm': - break; - default: - return EvNil; - } - - // rxvt protocol - if(!initial && narg >= 3) { - key->type = KeyMouse; - key->code.mouse[0] = arg[0]; - - key->mods = (key->code.mouse[0] & 0x1c) >> 2; - key->code.mouse[0] &= ~0x1c; - - setpos(key, arg[1], arg[2]); - - return EvKey; - } - - if(initial == '<' && narg >= 3) { // SGR protocol - key->type = KeyMouse; - key->code.mouse[0] = arg[0]; - - key->mods = (key->code.mouse[0] & 0x1c) >> 2; - key->code.mouse[0] &= ~0x1c; - - setpos(key, arg[1], arg[2]); - - if(cmd == 'm') // release - key->code.mouse[3] |= 0x80; - - return EvKey; - } - - return EvNil; -} - -/* csi ? R position events */ - -static -enum KeyEvent -do_csi_R(Input *in, Key *key, int cmd, int narg, long *arg) -{ - switch(cmd) { - case 'R'|'?'<<8: - if(narg < 2) - return EvNil; - key->type = KeyPosition; - setpos(key, arg[1], arg[0]); - return EvKey; - default: - return do_csi_full(in, key, cmd, narg, arg); - } -} - -/* csi $y mode status events */ - -static -enum KeyEvent -do_csi_y(Input *in, Key *key, int cmd, int narg, long *arg) -{ - switch (cmd) { - case 'y'|'$'<<16: - case 'y'|'$'<<16 | '?'<<8: - if (narg < 2) - return EvNil; - - key->type = KeyModeReport; - key->code.mouse[0] = (cmd >> 8); - key->code.mouse[1] = arg[0] >> 8; - key->code.mouse[2] = arg[0] & 0xff; - key->code.mouse[3] = arg[1]; - return EvKey; - - default: - return EvNil; - } -} - -/* parse csi events */ -static -enum KeyEvent -parse_csi(Input *in, ulong introlen, ulong *csi_len, ulong *nargs, long args[], unsigned long *commandp) -{ - ulong csi_end = introlen; - - while (csi_end < bufcount(in)) { - if (in->buf.b[csi_end] >= 0x40 && in->buf.b[csi_end] < 0x80) - break; - csi_end++; - } - - if(csi_end >= bufcount(in)) - return EvAgain; - - uchar cmd = in->buf.b[csi_end]; - *commandp = cmd; - - char present = 0; - int argi = 0; - - ulong p = introlen; - - // See if there is an initial byte - if (in->buf.b[p] >= '<' && in->buf.b[p] <= '?') { - *commandp |= (in->buf.b[p] << 8); - p++; - } - - // Now attempt to parse out up number;number;... separated values - while (p < csi_end) { - uchar c = in->buf.b[p]; - - if (c >= '0' && c <= '9') { - if (!present) { - args[argi] = c - '0'; - present = 1; - } else { - args[argi] = (args[argi] * 10) + c - '0'; - } - } else if(c == ';') { - if (!present) - args[argi] = -1; - present = 0; - argi++; - - if(argi > 16) - break; - } else if (c >= 0x20 && c <= 0x2f) { - *commandp |= c << 16; - break; - } - p++; - } - - if(present) - argi++; - - *nargs = argi; - *csi_len = csi_end + 1; - - return EvKey; -} - -static -void -loadctrlkeys(void) -{ - int i; - for(i = 0; i < 64; i++) { - csiss3[i].sym = SymUnknown; - ss3[i].sym = SymUnknown; - ss3kpalts[i] = 0; - } - - for(i = 0; i < arrlen(csifuncs); i++) - csifuncs[i].sym = SymUnknown; - - put_csi_full(KeySym, SymUp, 0, 0, 'A'); - put_csi_full(KeySym, SymDown, 0, 0, 'B'); - put_csi_full(KeySym, SymRight,0, 0, 'C'); - put_csi_full(KeySym, SymLeft, 0, 0, 'D'); - put_csi_full(KeySym, SymBegin,0, 0, 'E'); - put_csi_full(KeySym, SymEnd, 0, 0, 'F'); - put_csi_full(KeySym, SymHome, 0, 0, 'H'); - put_csi_full(KeySym, 1, 0, 0, 'P'); - put_csi_full(KeySym, 2, 0, 0, 'Q'); - put_csi_full(KeySym, 3, 0, 0, 'R'); - put_csi_full(KeySym, 4, 0, 0, 'S'); - - put_csi_full(KeySym, SymTab, ModShift, ModShift, 'Z'); - - put_ss3_kpalt(KeySym, SymKpenter, 'M', 0); - put_ss3_kpalt(KeySym, SymKpequals, 'X', '='); - put_ss3_kpalt(KeySym, SymKpmult, 'j', '*'); - put_ss3_kpalt(KeySym, SymKpplus, 'k', '+'); - put_ss3_kpalt(KeySym, SymKpcomma, 'l', ','); - put_ss3_kpalt(KeySym, SymKpminus, 'm', '-'); - put_ss3_kpalt(KeySym, SymKpperiod, 'n', '.'); - put_ss3_kpalt(KeySym, SymKpdiv, 'o', '/'); - put_ss3_kpalt(KeySym, SymKp0, 'p', '0'); - put_ss3_kpalt(KeySym, SymKp1, 'q', '1'); - put_ss3_kpalt(KeySym, SymKp2, 'r', '2'); - put_ss3_kpalt(KeySym, SymKp3, 's', '3'); - put_ss3_kpalt(KeySym, SymKp4, 't', '4'); - put_ss3_kpalt(KeySym, SymKp5, 'u', '5'); - put_ss3_kpalt(KeySym, SymKp6, 'v', '6'); - put_ss3_kpalt(KeySym, SymKp7, 'w', '7'); - put_ss3_kpalt(KeySym, SymKp8, 'x', '8'); - put_ss3_kpalt(KeySym, SymKp9, 'y', '9'); - - put_csi_func(KeySym, SymFind, 1); - put_csi_func(KeySym, SymInsert, 2); - put_csi_func(KeySym, SymDelete, 3); - put_csi_func(KeySym, SymSelect, 4); - put_csi_func(KeySym, SymPageup, 5); - put_csi_func(KeySym, SymPagedown, 6); - put_csi_func(KeySym, SymHome, 7); - put_csi_func(KeySym, SymEnd, 8); - - put_csi_func(KeyFunc, 1, 11); - put_csi_func(KeyFunc, 2, 12); - put_csi_func(KeyFunc, 3, 13); - put_csi_func(KeyFunc, 4, 14); - put_csi_func(KeyFunc, 5, 15); - put_csi_func(KeyFunc, 6, 17); - put_csi_func(KeyFunc, 7, 18); - put_csi_func(KeyFunc, 8, 19); - put_csi_func(KeyFunc, 9, 20); - put_csi_func(KeyFunc, 10, 21); - put_csi_func(KeyFunc, 11, 23); - put_csi_func(KeyFunc, 12, 24); - put_csi_func(KeyFunc, 13, 25); - put_csi_func(KeyFunc, 14, 26); - put_csi_func(KeyFunc, 15, 28); - put_csi_func(KeyFunc, 16, 29); - put_csi_func(KeyFunc, 17, 31); - put_csi_func(KeyFunc, 18, 32); - put_csi_func(KeyFunc, 19, 33); - put_csi_func(KeyFunc, 20, 34); - - do_csi['u' - 0x40] = &do_csi_u; - do_csi['M' - 0x40] = &do_csi_m; - do_csi['m' - 0x40] = &do_csi_m; - do_csi['R' - 0x40] = &do_csi_R; - do_csi['y' - 0x40] = &do_csi_y; - - csiinit = 1; -} - -static -enum KeyEvent -peekcsi(Input *in, ulong introlen, Key *key, int force, ulong *nb) -{ - ulong csi_len; - long arg[16]; - ulong nargs = arrlen(arg); - ulong cmd; - - enum KeyEvent ev = parse_csi(in, introlen, &csi_len, &nargs, arg, &cmd); - - if (ev== EvAgain) { - if(!force) - return ev; - - emitcodepoint(in, '[', key); - key->mods |= ModAlt; - *nb = introlen; - return EvKey; - } - // Mouse in X10 encoding consumes the next 3 bytes also - if (cmd == 'M' && nargs < 3) { - in->buf.b += csi_len; - ev = peekmousekey(in, key, nb); - in->buf.b -= csi_len; - - if (ev == EvKey) - *nb += csi_len; - - return ev; - } - - ev = EvNil; - - // We know from the logic above that cmd must be >= 0x40 and < 0x80 - if (do_csi[(cmd & 0xff) - 0x40]) - ev = (*do_csi[(cmd & 0xff) - 0x40])(in, key, cmd, nargs, arg); - - if (ev == EvNil) { - key->type = KeyUnknownCSI; - key->code.num = cmd; - key->mods = 0; - - in->buf.off = csi_len - introlen; - *nb = introlen; /* dont advance yet */ - return EvKey; - } - - *nb = csi_len; - return ev; -} - -static -enum KeyEvent -peekss3(Input *in, ulong introlen, Key *key, int force, ulong *nb) -{ - if(bufcount(in) < introlen + 1) { - if(!force) - return EvAgain; - - emitcodepoint(in, 'O', key); - key->mods |= ModAlt; - *nb= bufcount(in); - return EvKey; - } - uchar cmd = in->buf.b[introlen]; - - if(cmd < 0x40 || cmd >= 0x80) - return EvNil; - - key->type = csiss3[cmd - 0x40].type; - key->code.sym = csiss3[cmd - 0x40].sym; - key->mods = csiss3[cmd - 0x40].modset; - - if (key->code.sym == SymUnknown) { - if (in->flags & FlagConvertKP && ss3kpalts[cmd - 0x40]) { - key->type = KeyUnicode; - key->code.pt = ss3kpalts[cmd - 0x40]; - key->mods = 0; - - key->utf8[0] = key->code.pt; - key->utf8[1] = 0; - } else { - key->type = ss3[cmd - 0x40].type; - key->code.sym = ss3[cmd - 0x40].sym; - key->mods = ss3[cmd - 0x40].modset; - } - } - - if(key->code.sym == SymUnknown) - return EvNil; - - *nb = introlen + 1; - - return EvKey; -} - -static -enum KeyEvent -peekctrl(Input *in, ulong introlen, Key *key, int force, ulong *nb) -{ - ulong str_end = introlen; - - while(str_end < bufcount(in)) { - if (in->buf.b[str_end] == 0x9c) // ST - break; - if (in->buf.b[str_end] == 0x1b && - (str_end + 1) < bufcount(in) && - in->buf.b[str_end+1] == 0x5c) // ESC-prefixed ST - break; - - str_end++; - } - - if (str_end >= bufcount(in)) - return EvAgain; - - *nb = str_end + 1; - if(in->buf.b[str_end] == 0x1b) - (*nb)++; - - // XXX: read carefully - if(in->savedcsi) - free(in->savedcsi); - - ulong len = str_end - introlen; - - in->nsavedcsi++; - in->savedcsi = malloc(len + 1); - - strncpy(in->savedcsi, (char *)in->buf.b + introlen, len); - in->savedcsi[len] = 0; - - key->type = (in->buf.b[introlen-1] & 0x1f) == 0x10 ? KeyDCS : KeyOSC; - key->code.num = in->nsavedcsi; - key->mods = 0; - - return EvKey; -} - -static -enum KeyEvent -peekesc(Input *in, Key *key, int force, ulong *nb) -{ - if (bufcount(in) == 0) - return in->closed ? EvEOF : EvNil; - - switch (*in->buf.b) { - case 0x1b: - if(bufcount(in) < 2) - return EvNil; - - switch(in->buf.b[1]) { - case 0x4f: // ESC-prefixed SS3 - return peekss3(in, 2, key, force, nb); - - case 0x50: // ESC-prefixed DCS - case 0x5d: // ESC-prefixed OSC - return peekctrl(in, 2, key, force, nb); - - case 0x5b: // ESC-prefixed CSI - return peekcsi(in, 2, key, force, nb); - } - - return EvNil; - - case 0x8f: // SS3 - return peekss3(in, 1, key, force, nb); - - case 0x90: // DCS - case 0x9d: // OSC - return peekctrl(in, 1, key, force, nb); - - case 0x9b: // CSI - return peekcsi(in, 1, key, force, nb); - } - - return EvNil; -} - - -// ----------------------------------------------------------------------- -// internal functions - -static -int -registername(Input *in, int sym, char *name) -{ - if (!sym) - sym = in->nkeynm; - - if(sym >= in->nkeynm) { - char **tmp = realloc(in->keynm, sizeof(*tmp) * (sym + 1)); - if(!tmp) - return -1; - - in->keynm = tmp; - - // Fill in the hole - for(int i = in->nkeynm; i < sym; i++) - in->keynm[i] = nil; - - in->nkeynm = sym + 1; - } - - in->keynm[sym] = name; - - return sym; -} - -static -int -registerc0(Input *in, int sym, int modset, int modmask, uchar ctrl, char *name) -{ - if(ctrl >= 0x20) { - errno = EINVAL; - return -1; - } - - if (name) - sym = registername(in, sym, name); - - in->c0[ctrl].sym = sym; - in->c0[ctrl].modset = modset; - in->c0[ctrl].modmask = modmask; - - return sym; -} - -static -void -advance(Input *in, ulong nb) -{ - if (in->buf.c < in->buf.b + nb) { - in->buf.b = in->buf.c; - return; - } - - in->buf.b += nb; -} - -static -void -slidebuffer(Input *in) -{ - static const ulong halfway = arrlen(in->buf.bytes) / 2; - if (in->buf.b - in->buf.bytes > halfway) { - memmove(in->buf.bytes, in->buf.bytes + halfway, halfway); - in->buf.b -= halfway; - in->buf.c -= halfway; - } -} - -static -void -harmonize(Input *in, Key *key) -{ - int flags = in->hflag; - - if (flags & HarmonizeSpace) { - if (key->type == KeyUnicode && key->code.pt == 0x20) { - key->type = KeySym; - key->code.sym = SymSpace; - } - } else { - if (key->type == KeySym && key->code.sym == SymSpace) { - key->type = KeyUnicode; - key->code.pt = 0x20; - utf8·runetobyte((char*)key->utf8, &key->code.pt); - } - } - - if (flags & HarmonizeDelBS) { - if (key->type == KeySym && key->code.sym == SymDel) { - key->code.sym = SymBackspace; - } - } -} - -static -void -emitcodepoint(Input *in, rune r, Key *key) -{ - if (r == 0) { - key->type = KeySym; - key->code.sym = SymSpace; - key->mods = ModCtrl; - goto harmonize; - } - if (r < 0x20) { - key->code.pt = 0; - key->mods = 0; - if (!(in->flags & FlagNoInterpret) && in->c0[r].sym != SymUnknown) { - key->code.sym = in->c0[r].sym; - key->mods |= in->c0[r].modset; - } - if (!key->code.sym) { - key->type = KeyUnicode; - if (r+0x40 >= 'A' && r+0x40 <= 'Z') - // it's a letter - use lowercase instead - key->code.pt = r + 0x60; - else - key->code.pt = r + 0x40; - key->mods = ModCtrl; - } else - key->type = KeySym; - goto harmonize; - } - if (r == 0x7f && !(in->flags & FlagNoInterpret)) { - // ascii del - key->type = KeySym; - key->code.sym = SymDel; - key->mods = 0; - goto harmonize; - } - if (r >= 0x20 && r < 0x80) { - // ascii lowbyte range - key->type = KeyUnicode; - key->code.pt = r; - key->mods = 0; - goto harmonize; - } - if (r >= 0x80 && r < 0xa0) { - // UTF-8 never starts with a C1 byte. So we can be sure of these - key->type = KeyUnicode; - key->code.pt = r - 0x40; - key->mods = ModCtrl|ModAlt; - goto harmonize; - } - key->type = KeyUnicode; - key->code.pt = r; - key->mods = 0; - -harmonize: - harmonize(in, key); - utf8·runetobyte((char*)key->utf8, &key->code.pt); -} - -static -enum KeyEvent -peekmousekey(Input *in, Key *key, ulong *nb) -{ - if (in->buf.c - in->buf.b < 3) - return EvAgain; - - key->type = KeyMouse; - key->code.mouse[0] = in->buf.c[0] - 0x20; - key->code.mouse[1] = in->buf.c[1] - 0x20; - key->code.mouse[2] = in->buf.c[2] - 0x20; - key->code.mouse[3] = 0; - - key->mods = (key->code.mouse[0] & 0x1c) >> 2; - key->code.mouse[0] &= ~0x1c; - - *nb = 3; - return EvKey; -} - -enum KeyEvent peekkey(Input *in, Key *key, int force, ulong *nb); - -static -enum KeyEvent -peeksimplekey(Input *in, Key *key, int force, ulong *nb) -{ - uchar c, *b; - int n; - rune r; - enum KeyEvent ev; - - b = in->buf.b; - c = *b; - - if (c == 0x1b) { - if (bufcount(in) == 1) { - if (!force) - return EvAgain; - goto ascii; - } - in->buf.b++; - ev = peekkey(in, key, force, nb); - in->buf.b--; - - switch (ev) { - case EvKey: - key->mods |= ModAlt; - (*nb)++; - /* fallthrough */ - case EvNil: case EvEOF: - case EvAgain: case EvErr: - return ev; - } - } - if (c == 0xa0) - goto ascii; - if (in->flags & FlagUTF8) { - n = utf8·bytetorune(&r, (char*)in->buf.b); - *nb = n; /* store the number of bytes */ - if (n > bufcount(in)) { - if (!force) - return EvAgain; - r = RuneErr; - *nb = bufcount(in); - } - key->type = KeyUnicode; - key->mods = 0; - goto utf8; - } - /* if we are here just emit raw byte */ - key->type = KeyUnicode; - key->code.pt = c; - key->mods = 0; - key->utf8[0] = c; - key->utf8[1] = 0; - *nb = 1; - return EvKey; - -ascii: - *nb = 1; - r = c; -utf8: - emitcodepoint(in, r, key); - return EvKey; -} - -// ----------------------------------------------------------------------- -// exported functions - -Input * -makeinput(int fd, int flags, unibi_term *info) -{ - int i; - Input *in; - char *e; - - if (!(in = malloc(sizeof(in)))) - panicf("out of memory"); - - in->fd = fd; - if (!(flags & (FlagRaw|FlagUTF8))) { - if (((e = getenv("LANG")) || (e = getenv("LC_MESSAGES")) || (e = getenv("LC_ALL"))) && - (e = strchr(e, '.')) && e++ && (!stricmp(e, "UTF-8") || !stricmp(e, "UTF8"))) - flags |= FlagUTF8; - else - flags |= FlagRaw; - } - in->flags = flags; - in->wait = 50; /* in msec */ - in->closed = 0; - in->started = 0; - in->hasold = 0; - in->keys = loadtermkeys(info); - - /* initialize buffer */ - in->buf.c = in->buf.b = in->buf.bytes; - in->buf.e = arrend(in->buf.bytes); - in->buf.off = 0; - memset(in->buf.bytes, 0, arrlen(in->buf.bytes)); - - /* initialize names */ - for (i = 0; i < 32; i++) - in->c0[i].sym = SymNone; - - registerc0(in, SymTab, 0x09, 0, 0, nil); - registerc0(in, SymEnter, 0x0d, 0, 0, nil); - registerc0(in, SymEscape, 0x1b, 0, 0, nil); - - /* load in csi */ - in->nsavedcsi = 0; - in->savedcsi = nil; - loadctrlkeys(); - - return in; -} - -void -freeinput(Input *in) -{ - // free(in); -} - -int -startrecord(Input *in) -{ - struct termios new; - if (in->started) - return 1; - - if (in->fd != -1 && !(in->flags & FlagNoTermIOS)) { - if (tcgetattr(in->fd, &new) == 0) { - in->oldterm = new; - in->hasold = 1; - - new.c_iflag &= ~(IXON|INLCR|ICRNL); - new.c_lflag &= ~(ICANON|ECHO|IEXTEN); - - new.c_cc[VMIN] = 1; - new.c_cc[VTIME] = 0; - - if (in->flags & FlagCtrlC) - new.c_lflag &= ~ISIG; - else { - /* Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT */ - new.c_cc[VQUIT] = _POSIX_VDISABLE; - new.c_cc[VSUSP] = _POSIX_VDISABLE; - /* Some OSes have Ctrl-Y==VDSUSP */ -# ifdef VDSUSP - new.c_cc[VDSUSP] = _POSIX_VDISABLE; -# endif - } - tcsetattr(in->fd, TCSANOW, &new); - } - } - - in->started = 1; - return 1; -} - -int -stoprecord(Input *in) -{ - if (!in->started) - return 1; - - if (in->hasold) - tcsetattr(in->fd, TCSANOW, &in->oldterm); - - in->started = 0; - return 1; -} - -enum KeyEvent -peekkey(Input *in, Key *key, int force, ulong *nb) -{ - int i, again = 0; - enum KeyEvent ev; - static enum KeyEvent (*peek[2])(Input *, Key *, int, ulong *) = { peeksym, peekesc }; - - if (!in->started) { - errno = EINVAL; - return EvErr; - } - - if (in->buf.off) { - in->buf.b += in->buf.off; - in->buf.off = 0; - } - - for (i = 0; i < arrlen(peek); i++) { - ev = peek[i](in, key, force, nb); - switch (ev) { - case EvKey: - slidebuffer(in); - /* fallthrough */ - case EvEOF: - case EvErr: - return ev; - case EvAgain: - if (!force) - again = 1; - /* fallthrough */ - case EvNil: - break; - } - } - if (again) - return EvAgain; - - return peeksimplekey(in, key, force, nb); -} - -enum KeyEvent -getkey(Input *in, Key *key) -{ - ulong nb; - enum KeyEvent ev; - - ev = peekkey(in, key, 0, &nb); - switch (ev) { - case EvKey: - advance(in, nb); - break; - case EvAgain: - peekkey(in, key, 1, &nb); - /* get nb but don't advance */ - break; - default: - ; - } - return ev; -} - -enum KeyEvent -demandkey(Input *in, Key *key) -{ - ulong nb; - enum KeyEvent ev; - - ev = peekkey(in, key, 1, &nb); - if (ev == EvKey) - advance(in, nb); - - return ev; -} - -enum KeyEvent -isreadablekey(Input *in) -{ - int n; - if (in->fd == -1) { - errno = EBADF; - return EvErr; - } - - /* reset to beginning of buffer */ - if (in->buf.b > in->buf.bytes) { - n = in->buf.b - in->buf.bytes; - memmove(in->buf.bytes, in->buf.b, n); - in->buf.b = in->buf.bytes; - in->buf.c = in->buf.b + n; - } - -read: - n = read(in->fd, in->buf.c, in->buf.e-in->buf.c); - if (n == -1) { - if (errno == EAGAIN) - return EvNil; - if (errno == EINTR && !(in->flags & FlagEintr)) - goto read; - else - return EvErr; - } - if (n < 1) { - in->closed = 1; - return EvNil; - } - in->buf.c += n; - return EvAgain; -} - -enum KeyEvent -waitkey(Input *in, Key *key) -{ - enum KeyEvent ev; - struct pollfd p; - - if (in->fd == -1) { - errno = EBADF; - return EvErr; - } - - for (;;) { - ev = getkey(in, key); - switch (ev) { - case EvKey: case EvEOF: case EvErr: - return ev; - case EvNil: - ev = isreadablekey(in); - if (ev == EvErr) - return ev; - break; - - case EvAgain: - /* can't wait any longer */ - if (in->closed) - return demandkey(in, key); - poll: - p.fd = in->fd; - p.events = POLLIN; - - if (poll(&p, 1, in->wait) == -1) { - if (errno == EINTR && !(in->flags & FlagEintr)) - goto poll; - return EvErr; - } - - if (p.revents & (POLLIN|POLLHUP|POLLERR)) - ev = isreadablekey(in); - else - ev = EvNil; - - if (ev == EvErr) - return ev; - if (ev == EvNil) - return demandkey(in, key); - break; - } - } - /* unreachable */ -} - -enum KeyEvent -decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col) -{ - if(key->type != KeyMouse) - return EvNil; - - if(button) - *button = 0; - - getpos(key, row, col); - - if(!ev) - return EvKey; - - int btn = 0; - int code = key->code.mouse[0]; - int drag = code & 0x20; - code &= ~0x3c; - - switch(code) { - case 0: - case 1: - case 2: - *ev = drag ? MouseDrag : MousePress; - btn = code + 1; - break; - - case 3: - *ev = MouseRelease; - // no button hint - break; - - case 64: - case 65: - *ev = drag ? MouseDrag : MousePress; - btn = code + 4 - 64; - break; - - default: - *ev = MouseNil; - } - - if (button) - *button = btn; - - if (key->code.mouse[3] & 0x80) - *ev = MouseRelease; - - return EvKey; -} - -enum KeyEvent -decodepos(Input *in, Key *key, int *row, int *col) -{ - if (key->type != KeyPosition) - return EvNil; - - getpos(key, row, col); - - return EvKey; -} - -enum KeyEvent -decodemode(Input *in, Key *key, int *init, int *mode, int *val) -{ - if (key->type != KeyModeReport) - return EvNil; - - if (init) - *init = key->code.mouse[0]; - - if (mode) - *mode = (key->code.mouse[1] << 8) | key->code.mouse[2]; - - if (val) - *val = key->code.mouse[3]; - - return EvKey; -} - -char * -keyname(Input *in, int sym) -{ - if (sym == SymUnknown || sym >= in->nkeynm) - return ""; - - return in->keynm[sym]; -} - -// ----------------------------------------------------------------------- -// main point of entry - -static -void -printkey(Input *in, Key *key) -{ - enum MouseEvent ev; - int button, line, col; - int init, mode, val; - - switch(key->type) { - case KeyUnicode: - fprintf(stderr, "Unicode codepoint=U+%04dx utf8='%s'", key->code.pt, key->utf8); - break; - case KeyFunc: - fprintf(stderr, "Function F%d", key->code.num); - break; - case KeySym: - fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, keyname(in, key->code.sym)); - break; - case KeyMouse: - decodemouse(in, key, &ev, &button, &line, &col); - fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col); - break; - case KeyPosition: - decodepos(in, key, &line, &col); - fprintf(stderr, "Position report pos=(%d,%d)\n", line, col); - break; - case KeyModeReport: - decodemode(in, key, &init, &mode, &val); - fprintf(stderr, "Mode report mode=%s %d val=%d\n", init == '?' ? "DEC" : "ANSI", mode, val); - break; - case KeyDCS: - fprintf(stderr, "Device Control String"); - break; - case KeyOSC: - fprintf(stderr, "Operating System Control"); - break; - case KeyUnknownCSI: - fprintf(stderr, "unknown CSI\n"); - break; - } - - mode = key->mods; - fprintf(stderr, " mod=%s%s%s+%02x", - (mode & ModCtrl ? "" : ""), - (mode & ModAlt ? "" : ""), - (mode & ModShift? "" : ""), - mode & ~(ModCtrl|ModAlt|ModShift)); -} - -int -main() -{ - char *name; - Input *in; - unibi_term *info; - Key key; - enum KeyEvent ev; - - name = getenv("TERM"); - info = unibi_from_term(name); - in = makeinput(0, FlagSpaceSymbol, info); - - int n = 0; /* for debugging purposes only */ - startrecord(in); - while ((ev = waitkey(in, &key)) != EvEOF) { - switch (ev) { - case EvKey: - printkey(in, &key); - printf("\n"); - break; - case EvNil: - printf("\n"); - case EvAgain: - printf("\n"); - default: - ; - } - n++; - if (n > 200) - break; - } - stoprecord(in); - freeinput(in); -} diff --git a/sys/cmd/dvtm/rules.mk b/sys/cmd/dvtm/rules.mk index 5b681af..f12aa17 100644 --- a/sys/cmd/dvtm/rules.mk +++ b/sys/cmd/dvtm/rules.mk @@ -2,10 +2,9 @@ include share/push.mk # Local sources SRCS_$(d) := \ -$(d)/term.c \ -# $(d)/events.c \ -# $(d)/vt.c \ -# $(d)/dvtm.c +$(d)/hook.c \ +$(d)/vt.c \ +$(d)/dvtm.c # needed for additional editor target AUX := $(d)/dvtm-editor.o @@ -22,7 +21,7 @@ BINS_$(d) := $(DVTM) $(DVTM-ED) include share/paths.mk $(DVTM): TCFLAGS = -D_XOPEN_SOURCE=700 -D_XOPEN_SOURCE_EXTENDED -DNDEBUG -$(DVTM): TCLIBS = -lunibilium $(OBJ_DIR)/libn/libn.a +$(DVTM): TCLIBS = -lncursesw $(OBJ_DIR)/libn/libn.a $(DVTM): $(OBJS_$(d)) $(COMPLINK) diff --git a/sys/cmd/dvtm/term.c b/sys/cmd/dvtm/term.c deleted file mode 100644 index cda2bbe..0000000 --- a/sys/cmd/dvtm/term.c +++ /dev/null @@ -1,395 +0,0 @@ -#include "term.h" - -struct ExtraInfo -{ - char *enteralt; - char *exitalt; - - char *entermouse; - char *exitmouse; -}; - -static -struct ExtraInfo vt200 = -{ - .enteralt = "\e[?1049h", - .exitalt = "\e[?1049l", - - .entermouse = "\e[?1049h\e[?1006l", - .exitmouse = "\e[?1002l\e[?1006l", -}; - -// ----------------------------------------------------------------------- -// database lookup - -static -char* -tryinfostr(Term *t, enum unibi_string s) -{ - char *val = (char*)unibi_get_str(t->info, s); - /* TODO: provide fallbacks */ - return val; -} - -static -char* -guessinfostr(Term *t, enum unibi_string s, char *guess) -{ - char *val = (char*)unibi_get_str(t->info, s); - if (!val) - return guess; - return val; -} - -static -char* -getinfostr(Term *t, enum unibi_string s) -{ - char *val = tryinfostr(t, s); - if (!val) - panicf("required term info string '%s' missing", unibi_name_str(s)); - - return val; -} - -static -char * -tryextrastr(Term *t, char *name) -{ - const char *nm; - size_t max = unibi_count_ext_str(t->info); - for (size_t i = 0; i < max; i++) { - nm = unibi_get_ext_str_name(t->info, i); - if (nm && !strcmp(nm, name)) { - return (char *)nm; - } - } - return nil; -} - -static -char * -guessextrastr(Term *t, char *name, char *guess) -{ - char *s; - if ((s = tryextrastr(t, name))) - return s; - - return guess; -} - -static -int -tryextrabool(Term *t, char *name) -{ - const char *nm; - size_t max = unibi_count_ext_bool(t->info); - for (size_t i = 0; i < max; i++) { - nm = unibi_get_ext_bool_name(t->info, i); - if (nm && !strcmp(nm, name)) { - return (int)i; - } - } - return -1; -} - -/* formats escape strings and writes to output */ -static void tfmt(Term *t, char *esc, int n, ...); - -// ----------------------------------------------------------------------- -// exported pen methods - -// ----------------------------------------------------------------------- -// exported term methods - -static -char * -ttmpbuf(Term *t, int len) -{ - if (t->tmp.len >= len) - return t->tmp.b; - - /* TODO: error handling */ - return (t->tmp.b = realloc(t->tmp.b, len)); -} - -void -tinit(Term *t) -{ - t->name = getenv("TERM"); - t->info = unibi_from_term(t->name); - if (!t->info) - panicf("could not identify terminal"); - - t->mode.mouse = 0; - t->mode.cursorvis = 1; - t->mode.altscreen = 0; - - t->cap.colors = unibi_get_num(t->info, unibi_max_colors); - t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase); - - /* TODO */ - t->input = nil; - t->root = nil; - t->pen = (Pen){0}; - - /* fill in buffers */ - t->buf.c = t->buf.b; - t->tmp.b = nil; - t->tmp.len = 0; - - /* get all term info format strings */ - t->esc.cup = getinfostr(t, unibi_cursor_address); - t->esc.vpa = tryinfostr(t, unibi_row_address); - t->esc.hpa = tryinfostr(t, unibi_column_address); - t->esc.cuu = getinfostr(t, unibi_parm_up_cursor); - t->esc.cuu1 = tryinfostr(t, unibi_cursor_up); - t->esc.cud = getinfostr(t, unibi_parm_down_cursor); - t->esc.cud1 = tryinfostr(t, unibi_cursor_down); - t->esc.cuf = getinfostr(t, unibi_parm_right_cursor); - t->esc.cuf1 = tryinfostr(t, unibi_cursor_right); - t->esc.cub = getinfostr(t, unibi_parm_left_cursor); - t->esc.cub1 = tryinfostr(t, unibi_cursor_left); - t->esc.ich = getinfostr(t, unibi_parm_ich); - t->esc.ich1 = tryinfostr(t, unibi_insert_character); - t->esc.dch = getinfostr(t, unibi_parm_dch); - t->esc.dch1 = tryinfostr(t, unibi_delete_character); - t->esc.il = getinfostr(t, unibi_parm_insert_line); - t->esc.il1 = tryinfostr(t, unibi_insert_line); - t->esc.dl = getinfostr(t, unibi_parm_delete_line); - t->esc.dl1 = tryinfostr(t, unibi_delete_line); - t->esc.ech = getinfostr(t, unibi_erase_chars); - t->esc.ed2 = getinfostr(t, unibi_clear_screen); - t->esc.stbm = getinfostr(t, unibi_change_scroll_region); - t->esc.sgr = getinfostr(t, unibi_set_attributes); - t->esc.sgr0 = getinfostr(t, unibi_exit_attribute_mode); - t->esc.sgr_i0 = tryinfostr(t, unibi_exit_italics_mode); - t->esc.sgr_i1 = tryinfostr(t, unibi_enter_italics_mode); - t->esc.sgr_fg = getinfostr(t, unibi_set_a_foreground); - t->esc.sgr_bg = getinfostr(t, unibi_set_a_background); - t->esc.sm_csr = getinfostr(t, unibi_cursor_normal); - t->esc.rm_csr = getinfostr(t, unibi_cursor_invisible); - - /* extensions to terminfo */ - t->esc.ext.rgbf = guessextrastr(t, "setrgbf", "\x1b[38;2;%p1%d;%p2%d;%p3%dm"); - t->esc.ext.rgbb = guessextrastr(t, "setrgbb", "\x1b[48;2;%p1%d;%p2%d;%p3%dm"); - - /* acs characters */ - t->acs.vline = tryinfostr(t, unibi_acs_vline); - t->acs.hline = tryinfostr(t, unibi_acs_hline); - t->acs.plus = tryinfostr(t, unibi_acs_plus); - t->acs.ltee = tryinfostr(t, unibi_acs_ltee); - t->acs.rtee = tryinfostr(t, unibi_acs_rtee); - t->acs.ttee = tryinfostr(t, unibi_acs_ttee); - t->acs.btee = tryinfostr(t, unibi_acs_btee); - t->acs.ulcorner = tryinfostr(t, unibi_acs_ulcorner); - t->acs.urcorner = tryinfostr(t, unibi_acs_urcorner); - t->acs.llcorner = tryinfostr(t, unibi_acs_llcorner); - t->acs.lrcorner = tryinfostr(t, unibi_acs_lrcorner); -} - -void -tfini(Term *t) -{ - if (t->mode.mouse) - twrite(t, 0, vt200.exitmouse); - if (!t->mode.cursorvis) - tfmt(t, t->esc.rm_csr, 0); - if (t->mode.altscreen) - twrite(t, 0, vt200.exitalt); - - tfmt(t, t->esc.sgr0, 0); -} - -void -tflush(Term *t) -{ - if (t->fd != -1) - write(t->fd, t->buf.b, t->buf.c - t->buf.b); - - t->buf.c = t->buf.b; -} - -void -twrite(Term *t, long len, char *s) -{ - int n; - if (!len) - len = strlen(s); - - while (len > 0) { - n = MIN(len, arrend(t->buf.b) - t->buf.c); - memcpy(t->buf.c, s, n); - t->buf.c += n; - len -= n; - tflush(t); - } -} - -void -tsetpen(Term *t, Pen new) -{ - int c; - ushort ic, in; - Pen cur = t->pen; - if (!memcmp(&new, &cur, sizeof(new))) - return; - - /* attributes */ - tfmt(t, t->esc.sgr, 9, - 0, /* standout */ - new.state & PenUnderline, - new.state & PenReverse, - new.state & PenBlink, - new.state & PenDim, - new.state & PenBold, - new.state & PenInvis, - 0, /* protect */ - 0); /* alt */ - - ic = cur.state & PenItalic; - in = new.state & PenItalic; - if (ic & ~in) - tfmt(t, t->esc.sgr_i0, 0); - else if (~ic & in) - tfmt(t, t->esc.sgr_i1, 0); - - /* fg/bg color */ - /* TODO: add a check for if the terminal supports true color */ - /* TODO: add a check for negative indices */ - if (new.state & PenTrueClr) { - tfmt(t, t->esc.ext.rgbf, 3, new.rgb.fg.r, new.rgb.fg.g, new.rgb.fg.b); - tfmt(t, t->esc.ext.rgbb, 3, new.rgb.bg.r, new.rgb.bg.g, new.rgb.bg.b); - } else { - tfmt(t, t->esc.sgr_fg, 1, new.col.fg); - tfmt(t, t->esc.sgr_bg, 1, new.col.bg); - } - - t->pen = new; -} - -static -void -tfmt(Term *t, char *esc, int n, ...) -{ - int i; - long len; - va_list args; - unibi_var_t param[9]; - char buf[64], *c = buf; - - if (!esc) - panicf("no terminfo escape string given"); - - va_start(args, n); - for (i = 0; i < arrlen(param) && i < n; i++) - param[i] = unibi_var_from_num(va_arg(args, int)); - va_end(args); - - len = unibi_run(esc, param, c, sizeof(buf)); - if (len < arrlen(buf)) { - c = ttmpbuf(t, len); - unibi_run(esc, param, c, len); - } - - twrite(t, len, c); -} - -/* absolute move */ -int -tgoto(Term *t, int row, int col) -{ - if (row != -1 && col != -1) - tfmt(t, t->esc.cup, 2, row, col); - else if (row != -1) { - if (!t->esc.vpa) - return 0; - tfmt(t, t->esc.vpa, 1, row); - } else if (col != -1) { - if (col == 0) { - twrite(t, 1, "\r"); - return 1; - } - if (t->esc.hpa) - tfmt(t, t->esc.hpa, 1, col); - else if (t->esc.cuf) { - twrite(t, 1, "\r"); - tfmt(t, t->esc.cuf, 1, col); - } else - return 0; - } else - return 0; /* unreachable */ - - return 1; -} - -/* relative move */ -void -tjump(Term *t, int down, int right) -{ - if (down == 1 && t->esc.cud1) - tfmt(t, t->esc.cud1, 0); - else if (down == -1 && t->esc.cuu1) - tfmt(t, t->esc.cuu1, 0); - else if (down > 0) - tfmt(t, t->esc.cud, 1, down); - else if (down < 0) - tfmt(t, t->esc.cuu, 1, -down); - - if (right == 1 && t->esc.cuf1) - tfmt(t, t->esc.cuf1, 0); - else if (right == -1 && t->esc.cub1) - tfmt (t, t->esc.cub1, 0); - else if (right > 0) - tfmt(t, t->esc.cuf, 1, right); - else if( right < 0) - tfmt(t, t->esc.cub, 1, -right); -} - -void -tdel(Term *t, int num) -{ - char *c, buf[64]; - if (num < 1) - return; - - /* TODO: allow for not moving */ - if (t->cap.bce) { - tfmt(t, t->esc.ech, 1, num); - tjump(t, 0, num); - } else { - c = buf; - memset(c, ' ', arrlen(buf)); - while(num > 64) { - twrite(t, arrlen(buf), c); - num -= arrlen(buf); - } - twrite(t, num, c); - } -} - -void -tclear(Term *t) -{ - tfmt(t, t->esc.ed2, 0); -} - -// ----------------------------------------------------------------------- -// testing entry - -int -main() -{ - Term t; - tinit(&t); - - printf("%s: %s\n", unibi_short_name_str(unibi_set_a_foreground), t.esc.sgr_fg); - printf("%s: %s\n", unibi_short_name_str(unibi_set_a_background), t.esc.sgr_bg); - tfmt(&t, t.esc.ext.rgbf, 3, 10, 10, 100); - tfmt(&t, t.esc.ext.rgbb, 3, 100, 0, 100); - twrite(&t, 0, "hello world\n"); - twrite(&t, 0, "hello world\n"); - twrite(&t, 0, t.acs.hline); - - tfini(&t); -} diff --git a/sys/cmd/dvtm/term.h b/sys/cmd/dvtm/term.h deleted file mode 100644 index 53c0850..0000000 --- a/sys/cmd/dvtm/term.h +++ /dev/null @@ -1,481 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#define iota(x) 1 << (x) -/* - * obtained from: - * https://invisible-island.net/ncurses/man/curs_add_wch.3x.html - */ -#define ACSRUNES \ - /* name utf8 ascii acsc*/ \ - ACSRUNE("block", 0x25ae, '#', '0') \ - ACSRUNE("board", 0x2592, '#', 'h') \ - ACSRUNE("btee", 0x2534, '+', 'v') \ - ACSRUNE("bullet", 0x00b7, 'o', '~') \ - ACSRUNE("ckboard", 0x2592, ':', 'a') \ - ACSRUNE("darrow", 0x2193, 'v', '.') \ - ACSRUNE("degree", 0x00b0, '\'','f') \ - ACSRUNE("diamond", 0x25c6, '+', '`') \ - ACSRUNE("gequal", 0x2265, '>', '>') \ - ACSRUNE("hline", 0x2500, '-', 'q') \ - ACSRUNE("antern", 0x2603, '#', 'i') \ - ACSRUNE("larrow", 0x2190, '<', ',') \ - ACSRUNE("lequal", 0x2264, '<', 'y') \ - ACSRUNE("llcorner", 0x2514, '+', 'm') \ - ACSRUNE("lrcorner", 0x2518, '+', 'j') \ - ACSRUNE("ltee", 0x2524, '+', 't') \ - ACSRUNE("nequal", 0x2260, '!', '|') \ - ACSRUNE("pi", 0x03c0, '*', '{') \ - ACSRUNE("plminus", 0x00b1, '#', 'g') \ - ACSRUNE("plus", 0x253c, '+', 'n') \ - ACSRUNE("rarrow", 0x2192, '>', '+') \ - ACSRUNE("rtee", 0x251c, '+', 'u') \ - ACSRUNE("s1", 0x23ba, '-', 'o') \ - ACSRUNE("s3", 0x23bb, '-', 'p') \ - ACSRUNE("s7", 0x23bc, '-', 'r') \ - ACSRUNE("s9", 0x23bd, '_', 's') \ - ACSRUNE("sterling", 0x00a3, 'f', '}') \ - ACSRUNE("ttee", 0x252c, '+', 'w') \ - ACSRUNE("uarrow", 0x2191, '^', '-') \ - ACSRUNE("ulcorner", 0x250c, '+', 'l') \ - ACSRUNE("urcorner", 0x2510, '+', 'k') \ - ACSRUNE("vline", 0x2502, '|', 'x') \ - /* thick versions */ \ - ACSRUNE("t_btee", 0x253b, '+', 'V') \ - ACSRUNE("t_hline", 0x2501, '-', 'Q') \ - ACSRUNE("t_llcorner", 0x2517, '+', 'M') \ - ACSRUNE("t_lrcorner", 0x251b, '+', 'J') \ - ACSRUNE("t_ltee", 0x252b, '+', 'T') \ - ACSRUNE("t_plus", 0x254b, '+', 'N') \ - ACSRUNE("t_rtee", 0x2523, '+', 'U') \ - ACSRUNE("t_ttee", 0x2533, '+', 'W') \ - ACSRUNE("t_ulcorner", 0x250f, '+', 'L') \ - ACSRUNE("t_urcorner", 0x2513, '+', 'K') \ - ACSRUNE("t_vline", 0x2503, '|', 'X') \ - /* double version */ \ - ACSRUNE("d_btee", 0x2569, '+', 'H') \ - ACSRUNE("d_hline", 0x2550, '-', 'R') \ - ACSRUNE("d_llcorner", 0x255a, '+', 'D') \ - ACSRUNE("d_lrcorner", 0x255d, '+', 'A') \ - ACSRUNE("d_ltee", 0x2560, '+', 'F') \ - ACSRUNE("d_plus", 0x256c, '+', 'E') \ - ACSRUNE("d_rtee", 0x2563, '+', 'G') \ - ACSRUNE("d_ttee", 0x2566, '+', 'I') \ - ACSRUNE("d_ulcorner", 0x2554, '+', 'C') \ - ACSRUNE("d_urcorner", 0x2557, '+', 'B') \ - ACSRUNE("d_vline", 0x2551, '|', 'Y') - -/* enums */ - -/* key symbols */ -enum -{ - SymUnknown = -1, - SymNone = 0, - - /* special names in c0 */ - SymBackspace, SymTab, SymEnter, SymEscape, - - /* special names in g0 */ - SymSpace, SymDel, - - /* special keys */ - SymUp, SymDown, SymLeft, SymRight, SymBegin, SymFind, SymInsert, - SymDelete, SymSelect, SymPageup, SymPagedown, SymHome, SymEnd, - - /* special keys from terminfo */ - SymCancel, SymClear, SymClose, SymCommand, SymCopy, SymExit, - SymHelp, SymMark, SymMessage, SymMove, SymOpen, SymOptions, - SymPrint, SymRedo, SymReference, SymRefresh, SymReplace, - SymRestart, SymResume, SymSave, SymSuspend, SymUndo, - - /* numeric keypad special keys */ - SymKp0, SymKp1, SymKp2, SymKp3, SymKp4, SymKp5, SymKp6, SymKp7, SymKp8, - SymKp9, SymKpenter, SymKpplus, SymKpminus, SymKpmult, SymKpdiv, SymKpcomma, - SymKpperiod, SymKpequals, - - /* et cetera ad nauseum */ - NumSyms -}; - -/* key type */ -enum -{ - KeyUnicode, - KeyFunc, - KeySym, - KeyMouse, - KeyPosition, - KeyModeReport, - KeyDCS, - KeyOSC, - /* add other recognised types here */ - - KeyUnknownCSI = -1 -}; - -/* key events */ -enum KeyEvent -{ - EvNil, - EvKey, - EvEOF, - EvAgain, - EvErr, -}; - -enum MouseEvent -{ - MouseNil, - MousePress, - MouseDrag, - MouseRelease, -}; - -enum -{ - ModShift = iota(0), - ModAlt = iota(1), - ModCtrl = iota(2), -}; - -enum -{ - FlagNoInterpret = iota(0), - FlagConvertKP = iota(1), - FlagRaw = iota(2), - FlagUTF8 = iota(3), - FlagNoTermIOS = iota(4), - FlagSpaceSymbol = iota(5), - FlagCtrlC = iota(6), - FlagEintr = iota(7), -}; - -enum -{ - HarmonizeSpace = iota(0), - HarmonizeDelBS = iota(1), -}; - -/* types */ -typedef struct RGB8 RGB8; -typedef struct Pen Pen; - -typedef struct Cell Cell; -typedef struct Row Row; -typedef struct Buffer Buffer; -typedef struct Window Window; - -typedef struct Node Node; -typedef struct Key Key; -typedef struct Input Input; - -typedef struct Term Term; - -struct RGB8 -{ - uint8 r, g, b; -}; - -enum -{ - PenNormal = 0, - PenBold = iota(0), - PenDim = iota(1), - PenInvis = iota(2), - PenItalic = iota(3), - PenReverse = iota(4), - PenStrike = iota(5), - PenUnderline = iota(6), - PenBlink = iota(7), - /* ... */ - PenTrueClr = iota(15), -}; - -struct Pen -{ - ushort state; - union { - /* 256 color (legacy) */ - struct { - sshort fg : 8, bg : 8; /* 0 - 255 or COLOUR_DEFAULT */ - } col; - /* true color (modern) */ - struct { - RGB8 fg, bg; - } rgb; - }; -}; - -/* outputs */ -struct Cell { - rune r; - Pen pen; -}; - -struct Row { - Cell *cells; - uint dirty:1; -}; - -/* Buffer holding the current window content (as an array) as well - * as the scroll back buffer content (as a circular/ring buffer). - * - * If new content is added to terminal the view port slides down and the - * previously top most line is moved into the scroll back buffer at postion - * scroll.index. This index will eventually wrap around and thus overwrite - * the oldest lines. - * - * In the scenerio below a scroll up has been performed. That is 'scroll.above' - * lines still lie above the current view port. Further scrolling up will show - * them. Similarly 'scroll.below' is the amount of lines below the current - * viewport. - * - * The function buffer_boundary sets the row pointers to the start/end range - * of the section delimiting the region before/after the viewport. The functions - * buffer_row_{first,last} return the first/last logical row. And - * buffer_row_{next,prev} allows to iterate over the logical lines in either - * direction. - * - * scroll back buffer - * - * scroll_buf->+----------------+-----+ - * | | | ^ \ - * | before | | | | - * current terminal content | viewport | | | | - * | | | | - * +----------------+-----+\ | | | s > scroll.above - * ^ | | i | \ | | i | c | - * | | | n | \ | | n | r | - * | | v | \ | | v | o | - * r | | i | \ | | i | l / - * o | viewport | s | >|<- scroll.index | s | l \ - * w | | i | / | | i | | - * s | | b | / | after | b | s > scroll.below - * | | l | / | viewport | l | i | - * v | | e | / | | e | z / - * +----------------+-----+/ | unused | | e - * <- maxcols -> | scroll back | | - * <- cols -> | buffer | | | - * | | | | - * | | | v - * roll_buf + scroll.size->+----------------+-----+ - * <- maxcols -> - * <- cols -> - */ - -struct Buffer { - Row *row; /* array of Row pointers of size 'rows' */ - Row *crow; /* row on which the cursor currently resides */ - bool *tabs; /* a boolean flag for each column whether it is a tab */ - struct { - Row *buf; /* a ring buffer holding the scroll back content */ - Row *top; /* row in lines where scrolling region starts */ - Row *bot; /* row in lines where scrolling region ends */ - int size; /* maximal capacity of scroll back buffer (in lines) */ - int index; /* current index into the ring buffer */ - int above; /* number of lines above current viewport */ - int below; /* number of lines below current viewport */ - } scroll; - Pen pen, spen; - int nrow, ncol; /* current dimension of buffer */ - int maxcols; /* allocated cells (maximal cols over time) */ - int srow, scol; /* saved cursor row/column (zero based) */ -}; - -/* input */ -struct Key -{ - int type; - int mods; - uchar utf8[UTFmax+1]; - union { - rune pt; - int num; - int sym; - char mouse[4]; - } code; -}; - -struct KeyInfo -{ - int type; - int sym; - int modmask; - int modset; -}; - -/* make opaque? */ -struct Input -{ - int fd; - int flags; - int hflag; - int wait; /* in ms */ - - struct termios oldterm; - - /* buffer */ - struct { - long off; - uchar *b, *c, *e, bytes[256]; - } buf; - - /* modifiers */ - char closed : 1; - char started : 1; - char hasold : 1; - - /* key data */ - Node *keys; - struct KeyInfo c0[32]; - - char **keynm; - int nkeynm; - - int nsavedcsi; - char *savedcsi; -}; - -struct Term -{ - /* meta data */ - char *name; - unibi_term *info; - struct { - uint altscreen : 1; - uint cursorvis : 1; - uint mouse : 1; - } mode; - struct { - uint bce : 1; - int colors; - } cap; - - /* input capture */ - Input *input; - - /* output display */ - Window *root; - Pen pen; - - /* output raw text */ - int fd; - struct { - char *c, b[512]; - } buf; - - struct { - int len; - char *b; - } tmp; - - /* info */ - struct { - /* Positioning */ - char *cup; // cursor_address - char *vpa; // row_address == vertical position absolute - char *hpa; // column_address = horizontal position absolute - - /* Moving */ - char *cuu; char *cuu1; // Cursor Up - char *cud; char *cud1; // Cursor Down - char *cuf; char *cuf1; // Cursor Forward == Right - char *cub; char *cub1; // Cursor Backward == Left - - /* Editing */ - char *ich; char *ich1; // Insert Character - char *dch; char *dch1; // Delete Character - char *il; char *il1; // Insert Line - char *dl; char *dl1; // Delete Line - char *ech; // Erase Character - char *ed2; // Erase Data 2 == Clear screen - char *stbm; // Set Top/Bottom Margins - - /* formatting */ - char *sgr; // Select Graphic Rendition - char *sgr0; // Exit Attribute Mode - char *sgr_i0, *sgr_i1; // SGR italic off/on - char *sgr_fg; // SGR foreground colour - char *sgr_bg; // SGR background colour - - /* Mode setting/clearing */ - char *sm_csr; char *rm_csr; // Set/reset mode: Cursor visible - - /* augmentations to terminfo */ - struct { - char *rgbf; // rgb foreground - char *rgbb; // rgb background - char *smxx; // strikethrough - char *smulx; // curly underline - } ext; - } esc; - - /* basic shapes */ - struct { - rune block; - rune board; - rune hline; - rune vline; - rune plus; - rune ltee; - rune rtee; - rune ttee; - rune btee; - rune ulcorner; - rune urcorner; - rune llcorner; - rune lrcorner; - } acs; -}; - -/* exported functions */ - -/* buffer.c */ -void zero(Row *row, int start, int len); -void roll(Row *start, Row *end, int count); - -void bclear(Buffer *b); -void bfree(Buffer *b); -void bscroll(Buffer *b, int s); -void bresize(Buffer *b, int rows, int cols); -bool binit(Buffer *b, int rows, int cols, int size); -void brender(Buffer *b, Term *t); -void bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) ; -Row *browfirst(Buffer *b); -Row *browlast(Buffer *b); -Row *brownext(Buffer *b, Row *row); -Row *bprevrow(Buffer *b, Row *row); - -/* input.c */ -Input *makeinput(int fd, int flags, unibi_term *info); -void freeinput(Input *in); -int startrecord(Input *in); -int stoprecord(Input *in); -char *keyname(Input *in, int sym); - -enum KeyEvent waitkey(Input *in, Key *key); /* block until next keypress */ -enum KeyEvent getkey(Input *in, Key *key); /* grab key if we can */ -enum KeyEvent demandkey(Input *in, Key *key); /* grab now and interpret as best we can */ -enum KeyEvent isreadablekey(Input *in); /* reads from input and reports the event w/o advance */ - -/* unpack key event into useful data */ -enum KeyEvent decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col); -enum KeyEvent decodepos(Input *in, Key *key, int *row, int *col); -enum KeyEvent decodemode(Input *in, Key *key, int *init, int *mode, int *val); - -/* term.c */ -void tinit(Term *t); -void tfini(Term *t); -void tflush(Term *t); -void twrite(Term *t, long len, char *s); -int tgoto(Term *t, int row, int col); -void tjump(Term *t, int down, int right); -void tdel(Term *t, int num); -void tsetpen(Term *t, Pen pen); -void tclear(Term *t); diff --git a/sys/cmd/dvtm/vt.c b/sys/cmd/dvtm/vt.c index 4e1f427..aff4ab0 100644 --- a/sys/cmd/dvtm/vt.c +++ b/sys/cmd/dvtm/vt.c @@ -9,8 +9,6 @@ #include #include -#include "term.h" - #if defined(__linux__) || defined(__CYGWIN__) # include #elif defined(__FreeBSD__) || defined(__DragonFly__) @@ -19,121 +17,609 @@ # include #endif +#include "vt.h" + +#ifndef NCURSES_ATTR_SHIFT +# define NCURSES_ATTR_SHIFT 8 +#endif + +#ifndef NCURSES_ACS +# ifdef PDCURSES +# define NCURSES_ACS(c) (acs_map[(uchar)(c)]) +# else /* BSD curses */ +# define NCURSES_ACS(c) (_acs_map[(uchar)(c)]) +# endif +#endif + +#ifdef NCURSES_VERSION + +#ifndef NCURSES_EXT_COLORS +# define NCURSES_EXT_COLORS 0 +#endif + +#if !NCURSES_EXT_COLORS +# define MAX_COLOR_PAIRS MIN(COLOR_PAIRS, 256) +#endif + +#endif + +#ifndef MAX_COLOR_PAIRS +# define MAX_COLOR_PAIRS COLOR_PAIRS +#endif + +#if defined _AIX && defined CTRL +# undef CTRL +#endif +#ifndef CTRL +# define CTRL(k) ((k) & 0x1F) +#endif + #define IS_CONTROL(ch) !((ch) & 0xffffff60UL) -typedef struct Vt Vt; +static bool is_utf8, has_default_colors; +static int color_pairs_reserved, color_pairs_max, color_pair_current; +static short *color2palette, default_fg, default_bg; +static char vt_term[32]; + +typedef struct { + wchar_t text; + attr_t attr; + union { + struct { + short fg; + short bg; + }; + struct { + uint8 r; + uint8 g; + uint8 b; + }; + }; +} Cell; + +typedef struct { + Cell *cells; + uint dirty:1; +} Row; + +/* Buffer holding the current terminal window content (as an array) as well + * as the scroll back buffer content (as a circular/ring buffer). + * + * If new content is added to terminal the view port slides down and the + * previously top most line is moved into the scroll back buffer at postion + * scroll_index. This index will eventually wrap around and thus overwrite + * the oldest lines. + * + * In the scenerio below a scroll up has been performed. That is 'scroll_above' + * lines still lie above the current view port. Further scrolling up will show + * them. Similarly 'scroll_below' is the amount of lines below the current + * viewport. + * + * The function buffer_boundary sets the row pointers to the start/end range + * of the section delimiting the region before/after the viewport. The functions + * buffer_row_{first,last} return the first/last logical row. And + * buffer_row_{next,prev} allows to iterate over the logical lines in either + * direction. + * + * scroll back buffer + * + * scroll_buf->+----------------+-----+ + * | | | ^ \ + * | before | | | | + * current terminal content | viewport | | | | + * | | | | + * +----------------+-----+\ | | | s > scroll_above + * ^ | | i | \ | | i | c | + * | | | n | \ | | n | r | + * | | v | \ | | v | o | + * r | | i | \ | | i | l / + * o | viewport | s | >|<- scroll_index | s | l \ + * w | | i | / | | i | | + * s | | b | / | after | b | s > scroll_below + * | | l | / | viewport | l | i | + * v | | e | / | | e | z / + * +----------------+-----+/ | unused | | e + * <- maxcols -> | scroll back | | + * <- cols -> | buffer | | | + * | | | | + * | | | v + * scroll_buf + scroll_size->+----------------+-----+ + * <- maxcols -> + * <- cols -> + */ +typedef struct { + Row *lines; /* array of Row pointers of size 'rows' */ + Row *curs_row; /* row on which the cursor currently resides */ + Row *scroll_buf; /* a ring buffer holding the scroll back content */ + Row *scroll_top; /* row in lines where scrolling region starts */ + Row *scroll_bot; /* row in lines where scrolling region ends */ + bool *tabs; /* a boolean flag for each column whether it is a tab */ + int scroll_size; /* maximal capacity of scroll back buffer (in lines) */ + int scroll_index; /* current index into the ring buffer */ + int scroll_above; /* number of lines above current viewport */ + int scroll_below; /* number of lines below current viewport */ + int rows, cols; /* current dimension of buffer */ + int maxcols; /* allocated cells (maximal cols over time) */ + attr_t curattrs, savattrs; /* current and saved attributes for cells */ + int curs_col; /* current cursor column (zero based) */ + int curs_srow, curs_scol; /* saved cursor row/colmn (zero based) */ + short curfg, curbg; /* current fore and background colors */ + short savfg, savbg; /* saved colors */ +} Buffer; struct Vt { - Buffer buf[2]; /* normal & alternative screen buffer */ - Buffer *buffer; /* currently active buffer (one of the above) */ - Pen pen; /* default pen */ - int pty; /* master side pty file descriptor */ - pid_t pid; /* process id of the process running in this vt */ + Buffer buffer_normal; /* normal screen buffer */ + Buffer buffer_alternate; /* alternate screen buffer */ + Buffer *buffer; /* currently active buffer (one of the above) */ + attr_t defattrs; /* attributes to use for normal/empty cells */ + int deffg, defbg; /* colors to use for back normal/empty cells (white/black) */ + int pty; /* master side pty file descriptor */ + pid_t pid; /* process id of the process running in this vt */ /* flags */ - char title[256]; /* xterm style window title */ - void *data; /* user supplied data */ - uint seen_input : 1; - uint insert : 1; - uint escaped : 1; - uint curshid : 1; - uint curskeymode : 1; - uint bell : 1; - uint relposmode : 1; - uint mousetrack : 1; - uint graphmode : 1; - uint savgraphmode : 1; + uint seen_input:1; + uint insert:1; + uint escaped:1; + uint curshid:1; + uint curskeymode:1; + uint bell:1; + uint relposmode:1; + uint mousetrack:1; + uint graphmode:1; + uint savgraphmode:1; bool charsets[2]; /* buffers and parsing state */ char rbuf[BUFSIZ]; char ebuf[BUFSIZ]; uint rlen, elen; - int srow, scol; /* last known offset to display start row, start column */ + int srow, scol; /* last known offset to display start row, start column */ + char title[256]; /* xterm style window title */ + vt_title_handler_t title_handler; /* hook which is called when title changes */ + vt_urgent_handler_t urgent_handler; /* hook which is called upon bell */ + void *data; /* user supplied data */ }; -/* forward declares */ - -void vt·noscroll(Vt *t); -void vt·dirty(Vt *t); -void vt·scroll(Vt *t, int rows); +static const char *keytable[KEY_MAX+1] = { + [KEY_ENTER] = "\r", + ['\n'] = "\n", + /* for the arrow keys the CSI / SS3 sequences are not stored here + * because they depend on the current cursor terminal mode + */ + [KEY_UP] = "A", + [KEY_DOWN] = "B", + [KEY_RIGHT] = "C", + [KEY_LEFT] = "D", +#ifdef KEY_SUP + [KEY_SUP] = "\e[1;2A", +#endif +#ifdef KEY_SDOWN + [KEY_SDOWN] = "\e[1;2B", +#endif + [KEY_SRIGHT] = "\e[1;2C", + [KEY_SLEFT] = "\e[1;2D", + [KEY_BACKSPACE] = "\177", + [KEY_IC] = "\e[2~", + [KEY_DC] = "\e[3~", + [KEY_PPAGE] = "\e[5~", + [KEY_NPAGE] = "\e[6~", + [KEY_HOME] = "\e[7~", + [KEY_END] = "\e[8~", + [KEY_BTAB] = "\e[Z", + [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */ + [KEY_F(1)] = "\e[11~", + [KEY_F(2)] = "\e[12~", + [KEY_F(3)] = "\e[13~", + [KEY_F(4)] = "\e[14~", + [KEY_F(5)] = "\e[15~", + [KEY_F(6)] = "\e[17~", + [KEY_F(7)] = "\e[18~", + [KEY_F(8)] = "\e[19~", + [KEY_F(9)] = "\e[20~", + [KEY_F(10)] = "\e[21~", + [KEY_F(11)] = "\e[23~", + [KEY_F(12)] = "\e[24~", + [KEY_F(13)] = "\e[23~", + [KEY_F(14)] = "\e[24~", + [KEY_F(15)] = "\e[25~", + [KEY_F(16)] = "\e[26~", + [KEY_F(17)] = "\e[28~", + [KEY_F(18)] = "\e[29~", + [KEY_F(19)] = "\e[31~", + [KEY_F(20)] = "\e[32~", + [KEY_F(21)] = "\e[33~", + [KEY_F(22)] = "\e[34~", + [KEY_RESIZE] = "", +#ifdef KEY_EVENT + [KEY_EVENT] = "", +#endif +}; static void puttab(Vt *t, int count); -static void process_nonprinting(Vt *t, rune r); -static void sendcurs(Vt *t); +static void process_nonprinting(Vt *t, wchar_t wc); +static void send_curs(Vt *t); -/* globals */ -static int isutf8; -static char vtname[36]; +const static +attr_t +build_attrs(attr_t curattrs) +{ + return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff)) >> NCURSES_ATTR_SHIFT; +} static void -cclamp(Vt *t) +row_set(Row *row, int start, int len, Buffer *t) { - Buffer *b = t->buffer; - Row *lines = t->relposmode ? b->scroll.top : b->lines; - int rows = t->relposmode ? b->scroll.bot - b->scroll.top : b->rows; + Cell cell = { + .text = L'\0', + .attr = t ? build_attrs(t->curattrs) : 0, + .fg = t ? t->curfg : -1, + .bg = t ? t->curbg : -1, + }; - if (b->crow < lines) - b->crow = lines; - if (b->crow >= lines + rows) - b->crow = lines + rows - 1; - if (b->ccol < 0) - b->ccol = 0; - if (b->ccol >= b->cols) - b->ccol = b->cols - 1; + for (int i = start; i < len + start; i++) + row->cells[i] = cell; + row->dirty = true; } static void -clinedown(Vt *t) +row_roll(Row *start, Row *end, int count) { - Buffer *b = t->buffer; - zero(b->crow, b->cols, b->maxcols - b->cols); - b->crow++; - if (b->crow < b->scroll.bot) + int n = end - start; + + count %= n; + if (count < 0) + count += n; + + if (count) { + char buf[count * sizeof(Row)]; + memcpy(buf, start, count * sizeof(Row)); + memmove(start, start + count, (n - count) * sizeof(Row)); + memcpy(end - count, buf, count * sizeof(Row)); + for (Row *row = start; row < end; row++) + row->dirty = true; + } +} + +static +void +buffer_clear(Buffer *b) +{ + Cell cell = { + .text = L'\0', + .attr = A_NORMAL, + .fg = -1, + .bg = -1, + }; + + for (int i = 0; i < b->rows; i++) { + Row *row = b->lines + i; + for (int j = 0; j < b->cols; j++) { + row->cells[j] = cell; + row->dirty = true; + } + } +} + +static +void +buffer_free(Buffer *b) +{ + for (int i = 0; i < b->rows; i++) + free(b->lines[i].cells); + free(b->lines); + for (int i = 0; i < b->scroll_size; i++) + free(b->scroll_buf[i].cells); + free(b->scroll_buf); + free(b->tabs); +} + +static +void +buffer_scroll(Buffer *b, int s) +{ + /* work in screenfuls */ + int ssz = b->scroll_bot - b->scroll_top; + if (s > ssz) { + buffer_scroll(b, ssz); + buffer_scroll(b, s - ssz); + return; + } + if (s < -ssz) { + buffer_scroll(b, -ssz); + buffer_scroll(b, s + ssz); return; + } + + b->scroll_above += s; + if (b->scroll_above >= b->scroll_size) + b->scroll_above = b->scroll_size; + + if (s > 0 && b->scroll_size) { + for (int i = 0; i < s; i++) { + Row tmp = b->scroll_top[i]; + b->scroll_top[i] = b->scroll_buf[b->scroll_index]; + b->scroll_buf[b->scroll_index] = tmp; + + b->scroll_index++; + if (b->scroll_index == b->scroll_size) + b->scroll_index = 0; + } + } + row_roll(b->scroll_top, b->scroll_bot, s); + if (s < 0 && b->scroll_size) { + for (int i = (-s) - 1; i >= 0; i--) { + b->scroll_index--; + if (b->scroll_index == -1) + b->scroll_index = b->scroll_size - 1; + + Row tmp = b->scroll_top[i]; + b->scroll_top[i] = b->scroll_buf[b->scroll_index]; + b->scroll_buf[b->scroll_index] = tmp; + b->scroll_top[i].dirty = true; + } + } +} + +static +void +buffer_resize(Buffer *b, int rows, int cols) +{ + Row *lines = b->lines; + + if (b->rows != rows) { + if (b->curs_row >= lines + rows) { + /* scroll up instead of simply chopping off bottom */ + buffer_scroll(b, (b->curs_row - b->lines) - rows + 1); + } + while (b->rows > rows) { + free(lines[b->rows - 1].cells); + b->rows--; + } + + lines = realloc(lines, sizeof(Row) * rows); + } + + if (b->maxcols < cols) { + for (int row = 0; row < b->rows; row++) { + lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols); + if (b->cols < cols) + row_set(lines + row, b->cols, cols - b->cols, nil); + lines[row].dirty = true; + } + Row *sbuf = b->scroll_buf; + for (int row = 0; row < b->scroll_size; row++) { + sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols); + if (b->cols < cols) + row_set(sbuf + row, b->cols, cols - b->cols, nil); + } + b->tabs = realloc(b->tabs, sizeof(*b->tabs) * cols); + for (int col = b->cols; col < cols; col++) + b->tabs[col] = !(col & 7); + b->maxcols = cols; + b->cols = cols; + } else if (b->cols != cols) { + for (int row = 0; row < b->rows; row++) + lines[row].dirty = true; + b->cols = cols; + } + + int deltarows = 0; + if (b->rows < rows) { + while (b->rows < rows) { + lines[b->rows].cells = calloc(b->maxcols, sizeof(Cell)); + row_set(lines + b->rows, 0, b->maxcols, b); + b->rows++; + } + + /* prepare for backfill */ + if (b->curs_row >= b->scroll_bot - 1) { + deltarows = b->lines + rows - b->curs_row - 1; + if (deltarows > b->scroll_above) + deltarows = b->scroll_above; + } + } + + b->curs_row += lines - b->lines; + b->scroll_top = lines; + b->scroll_bot = lines + rows; + b->lines = lines; + + /* perform backfill */ + if (deltarows > 0) { + buffer_scroll(b, -deltarows); + b->curs_row += deltarows; + } +} + +static +bool +buffer_init(Buffer *b, int rows, int cols, int scroll_size) +{ + b->curattrs = A_NORMAL; /* white text over black background */ + b->curfg = b->curbg = -1; + if (scroll_size < 0) + scroll_size = 0; + if (scroll_size && !(b->scroll_buf = calloc(scroll_size, sizeof(Row)))) + return false; + b->scroll_size = scroll_size; + buffer_resize(b, rows, cols); + return true; +} + +static +void +buffer_boundry(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) +{ + if (bs) + *bs = nil; + if (be) + *be = nil; + if (as) + *as = nil; + if (ae) + *ae = nil; + if (!b->scroll_size) + return; + + if (b->scroll_above) { + if (bs) + *bs = &b->scroll_buf[(b->scroll_index - b->scroll_above + b->scroll_size) % b->scroll_size]; + if (be) + *be = &b->scroll_buf[(b->scroll_index-1 + b->scroll_size) % b->scroll_size]; + } + if (b->scroll_below) { + if (as) + *as = &b->scroll_buf[b->scroll_index]; + if (ae) + *ae = &b->scroll_buf[(b->scroll_index + b->scroll_below-1) % b->scroll_size]; + } +} - vt·noscroll(t); +static +Row * +buffer_row_first(Buffer *b) { + Row *bstart; + if (!b->scroll_size || !b->scroll_above) + return b->lines; + buffer_boundry(b, &bstart, nil, nil, nil); + return bstart; +} - b->crow = b->scroll.bot - 1; - bscroll(b, 1); - zero(b->crow, 0, b->cols); +static +Row * +buffer_row_last(Buffer *b) { + Row *aend; + if (!b->scroll_size || !b->scroll_below) + return b->lines + b->rows - 1; + buffer_boundry(b, nil, nil, nil, &aend); + return aend; } - + +static +Row * +buffer_row_next(Buffer *b, Row *row) +{ + Row *before_start, *before_end, *after_start, *after_end; + Row *first = b->lines, *last = b->lines + b->rows - 1; + + if (!row) + return nil; + + buffer_boundry(b, &before_start, &before_end, &after_start, &after_end); + + if (row >= first && row < last) + return ++row; + if (row == last) + return after_start; + if (row == before_end) + return first; + if (row == after_end) + return nil; + if (row == &b->scroll_buf[b->scroll_size - 1]) + return b->scroll_buf; + return ++row; +} + +static +Row * +buffer_row_prev(Buffer *b, Row *row) +{ + Row *before_start, *before_end, *after_start, *after_end; + Row *first = b->lines, *last = b->lines + b->rows - 1; + + if (!row) + return nil; + + buffer_boundry(b, &before_start, &before_end, &after_start, &after_end); + + if (row > first && row <= last) + return --row; + if (row == first) + return before_end; + if (row == before_start) + return nil; + if (row == after_start) + return last; + if (row == b->scroll_buf) + return &b->scroll_buf[b->scroll_size - 1]; + return --row; +} + static void -csave(Vt *t) +cursor_clamp(Vt *t) { Buffer *b = t->buffer; - b->scrow = b->crow - b->lines; - b->sccol = b->ccol; + Row *lines = t->relposmode ? b->scroll_top : b->lines; + int rows = t->relposmode ? b->scroll_bot - b->scroll_top : b->rows; + + if (b->curs_row < lines) + b->curs_row = lines; + if (b->curs_row >= lines + rows) + b->curs_row = lines + rows - 1; + if (b->curs_col < 0) + b->curs_col = 0; + if (b->curs_col >= b->cols) + b->curs_col = b->cols - 1; } static void -crestore(Vt *t) +cursor_line_down(Vt *t) { Buffer *b = t->buffer; - b->crow = b->lines + b->scrow; - b->ccol = b->sccol; - cclamp(t); + row_set(b->curs_row, b->cols, b->maxcols - b->cols, nil); + b->curs_row++; + if (b->curs_row < b->scroll_bot) + return; + + vt_noscroll(t); + + b->curs_row = b->scroll_bot - 1; + buffer_scroll(b, 1); + row_set(b->curs_row, 0, b->cols, b); } static void -savepen(Vt *t) +cursor_save(Vt *t) { Buffer *b = t->buffer; - b->spen = b->pen; + b->curs_srow = b->curs_row - b->lines; + b->curs_scol = b->curs_col; } static void -loadpen(Vt *t) +cursor_restore(Vt *t) { Buffer *b = t->buffer; - b->pen = b->spen; + b->curs_row = b->lines + b->curs_srow; + b->curs_col = b->curs_scol; + cursor_clamp(t); } +static +void +attributes_save(Vt *t) +{ + Buffer *b = t->buffer; + b->savattrs = b->curattrs; + b->savfg = b->curfg; + b->savbg = b->curbg; + t->savgraphmode = t->graphmode; +} + +static +void +attributes_restore(Vt *t) +{ + Buffer *b = t->buffer; + b->curattrs = b->savattrs; + b->curfg = b->savfg; + b->curbg = b->savbg; + t->graphmode = t->savgraphmode; +} static void @@ -170,85 +656,89 @@ interpret_csi_sgr(Vt *t, int param[], int pcount) Buffer *b = t->buffer; if (pcount == 0) { /* special case: reset attributes */ - b->pen.state = PenNormal; - b->pen.col.fg = b->pen.col.bg = -1; + b->curattrs = A_NORMAL; + b->curfg = b->curbg = -1; return; } for (int i = 0; i < pcount; i++) { switch (param[i]) { case 0: - b->pen.state = PenNormal; - b->pen.col.fg = b->pen.col.bg = -1; + b->curattrs = A_NORMAL; + b->curfg = b->curbg = -1; break; case 1: - b->pen.state |= PenBold; + b->curattrs |= A_BOLD; break; case 2: - b->pen.state |= PenDim; + b->curattrs |= A_DIM; break; +#ifdef A_ITALIC case 3: - b->pen.state |= PenItalic; + b->curattrs |= A_ITALIC; break; +#endif case 4: - b->pen.state |= PenUnderline; + b->curattrs |= A_UNDERLINE; break; case 5: - b->pen.state |= PenBlink; + b->curattrs |= A_BLINK; break; case 7: - b->pen.state |= PenReverse; + b->curattrs |= A_REVERSE; break; case 8: - b->pen.state |= PenInvis; + b->curattrs |= A_INVIS; break; case 22: - b->pen.state &= ~(PenBold | PenDim); + b->curattrs &= ~(A_BOLD | A_DIM); break; +#ifdef A_ITALIC case 23: - b->pen.state &= ~PenItalic; + b->curattrs &= ~A_ITALIC; break; +#endif case 24: - b->pen.state &= ~PenUnderline; + b->curattrs &= ~A_UNDERLINE; break; case 25: - b->pen.state &= ~PenBlink; + b->curattrs &= ~A_BLINK; break; case 27: - b->pen.state &= ~PenReverse; + b->curattrs &= ~A_REVERSE; break; case 28: - b->pen.state &= ~PenInvis; + b->curattrs &= ~A_INVIS; break; case 30 ... 37: /* fg */ - b->pen.col.fg = param[i] - 30; + b->curfg = param[i] - 30; break; case 38: if ((i + 2) < pcount && param[i + 1] == 5) { - b->pen.col.fg = param[i + 2]; + b->curfg = param[i + 2]; i += 2; } break; case 39: - b->pen.col.fg = -1; + b->curfg = -1; break; case 40 ... 47: /* bg */ - b->pen.col.bg = param[i] - 40; + b->curbg = param[i] - 40; break; case 48: if ((i + 2) < pcount && param[i + 1] == 5) { - b->pen.col.bg = param[i + 2]; + b->curbg = param[i + 2]; i += 2; } break; case 49: - b->pen.col.bg = -1; + b->curbg = -1; break; - case 90 ... 97: /* hi fg */ - b->pen.col.fg = param[i] - 82; + case 90 ... 97: /* hi fg */ + b->curfg = param[i] - 82; break; case 100 ... 107: /* hi bg */ - b->pen.col.bg = param[i] - 92; + b->curbg = param[i] - 92; break; default: break; @@ -264,27 +754,27 @@ interpret_csi_ed(Vt *t, int param[], int pcount) Row *row, *start, *end; Buffer *b = t->buffer; - savepen(t); - b->pen.state = PenNormal; - b->pen.col.fg = b->pen.col.bg = -1; + attributes_save(t); + b->curattrs = A_NORMAL; + b->curfg = b->curbg = -1; if (pcount && param[0] == 2) { start = b->lines; - end = b->lines + b->rows; + end = b->lines + b->rows; } else if (pcount && param[0] == 1) { start = b->lines; - end = b->crow; - zero(b->crow, 0, b->ccol + 1); + end = b->curs_row; + row_set(b->curs_row, 0, b->curs_col + 1, b); } else { - zero(b->crow, b->ccol, b->cols - b->ccol); - start = b->crow + 1; - end = b->lines + b->rows; + row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b); + start = b->curs_row + 1; + end = b->lines + b->rows; } for (row = start; row < end; row++) - zero(row, 0, b->cols); + row_set(row, 0, b->cols, b); - loadpen(t); + attributes_restore(t); } /* interprets a 'move cursor' (CUP) escape sequence */ @@ -292,21 +782,21 @@ static void interpret_csi_cup(Vt *t, int param[], int pcount) { - Buffer *b = t->buffer; - Row *lines = t->relposmode ? b->scroll.top : b->lines; + Buffer *b = t->buffer; + Row *lines = t->relposmode ? b->scroll_top : b->lines; if (pcount == 0) { - b->crow = lines; - b->ccol = 0; + b->curs_row = lines; + b->curs_col = 0; } else if (pcount == 1) { - b->crow = lines + param[0] - 1; - b->ccol = 0; + b->curs_row = lines + param[0] - 1; + b->curs_col = 0; } else { - b->crow = lines + param[0] - 1; - b->ccol = param[1] - 1; + b->curs_row = lines + param[0] - 1; + b->curs_col = param[1] - 1; } - cclamp(t); + cursor_clamp(t); } /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL, @@ -320,37 +810,37 @@ interpret_csi_c(Vt *t, char verb, int param[], int pcount) switch (verb) { case 'A': - b->crow -= n; + b->curs_row -= n; break; case 'B': case 'e': - b->crow += n; + b->curs_row += n; break; case 'C': case 'a': - b->ccol += n; + b->curs_col += n; break; case 'D': - b->ccol -= n; + b->curs_col -= n; break; case 'E': - b->crow += n; - b->ccol = 0; + b->curs_row += n; + b->curs_col = 0; break; case 'F': - b->crow -= n; - b->ccol = 0; + b->curs_row -= n; + b->curs_col = 0; break; case 'G': case '`': - b->ccol = n - 1; + b->curs_col = n - 1; break; case 'd': - b->crow = b->lines + n - 1; + b->curs_row = b->lines + n - 1; break; } - cclamp(t); + cursor_clamp(t); } /* Interpret the 'erase line' escape sequence */ @@ -361,13 +851,13 @@ interpret_csi_el(Vt *t, int param[], int pcount) Buffer *b = t->buffer; switch (pcount ? param[0] : 0) { case 1: - zero(b->crow, 0, b->ccol + 1); + row_set(b->curs_row, 0, b->curs_col + 1, b); break; case 2: - zero(b->crow, 0, b->cols); + row_set(b->curs_row, 0, b->cols, b); break; default: - zero(b->crow, b->ccol, b->cols - b->ccol); + row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b); break; } } @@ -378,16 +868,16 @@ void interpret_csi_ich(Vt *t, int param[], int pcount) { Buffer *b = t->buffer; - Row *row = b->crow; + Row *row = b->curs_row; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->ccol + n > b->cols) - n = b->cols - b->ccol; + if (b->curs_col + n > b->cols) + n = b->cols - b->curs_col; - for (int i = b->cols - 1; i >= b->ccol + n; i--) + for (int i = b->cols - 1; i >= b->curs_col + n; i--) row->cells[i] = row->cells[i - n]; - zero(row, b->ccol, n); + row_set(row, b->curs_col, n, b); } /* Interpret the 'delete chars' sequence (DCH) */ @@ -396,16 +886,16 @@ void interpret_csi_dch(Vt *t, int param[], int pcount) { Buffer *b = t->buffer; - Row *row = b->crow; + Row *row = b->curs_row; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->ccol + n > b->cols) - n = b->cols - b->ccol; + if (b->curs_col + n > b->cols) + n = b->cols - b->curs_col; - for (int i = b->ccol; i < b->cols - n; i++) + for (int i = b->curs_col; i < b->cols - n; i++) row->cells[i] = row->cells[i + n]; - zero(row, b->cols - n, n); + row_set(row, b->cols - n, n, b); } /* Interpret an 'insert line' sequence (IL) */ @@ -416,13 +906,13 @@ interpret_csi_il(Vt *t, int param[], int pcount) Buffer *b = t->buffer; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->crow + n >= b->scroll.bot) { - for (Row *row = b->crow; row < b->scroll.bot; row++) - zero(row, 0, b->cols); + if (b->curs_row + n >= b->scroll_bot) { + for (Row *row = b->curs_row; row < b->scroll_bot; row++) + row_set(row, 0, b->cols, b); } else { - roll(b->crow, b->scroll.bot, -n); - for (Row *row = b->crow; row < b->crow + n; row++) - zero(row, 0, b->cols); + row_roll(b->curs_row, b->scroll_bot, -n); + for (Row *row = b->curs_row; row < b->curs_row + n; row++) + row_set(row, 0, b->cols, b); } } @@ -434,13 +924,13 @@ interpret_csi_dl(Vt *t, int param[], int pcount) Buffer *b = t->buffer; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->crow + n >= b->scroll.bot) { - for (Row *row = b->crow; row < b->scroll.bot; row++) - zero(row, 0, b->cols); + if (b->curs_row + n >= b->scroll_bot) { + for (Row *row = b->curs_row; row < b->scroll_bot; row++) + row_set(row, 0, b->cols, b); } else { - roll(b->crow, b->scroll.bot, n); - for (Row *row = b->scroll.bot - n; row < b->scroll.bot; row++) - zero(row, 0, b->cols); + row_roll(b->curs_row, b->scroll_bot, n); + for (Row *row = b->scroll_bot - n; row < b->scroll_bot; row++) + row_set(row, 0, b->cols, b); } } @@ -452,10 +942,10 @@ interpret_csi_ech(Vt *t, int param[], int pcount) Buffer *b = t->buffer; int n = (pcount && param[0] > 0) ? param[0] : 1; - if (b->ccol + n > b->cols) - n = b->cols - b->ccol; + if (b->curs_col + n > b->cols) + n = b->cols - b->curs_col; - zero(b->crow, b->ccol, n); + row_set(b->curs_row, b->curs_col, n, b); } /* Interpret a 'set scrolling region' (DECSTBM) sequence */ @@ -468,8 +958,8 @@ interpret_csi_decstbm(Vt *t, int param[], int pcount) switch (pcount) { case 0: - b->scroll.top = b->lines; - b->scroll.bot = b->lines + b->rows; + b->scroll_top = b->lines; + b->scroll_bot = b->lines + b->rows; break; case 2: new_top = param[0] - 1; @@ -487,15 +977,15 @@ interpret_csi_decstbm(Vt *t, int param[], int pcount) /* check for range validity */ if (new_top < new_bot) { - b->scroll.top = b->lines + new_top; - b->scroll.bot = b->lines + new_bot; + b->scroll_top = b->lines + new_top; + b->scroll_bot = b->lines + new_bot; } break; default: return; /* malformed */ } - b->crow = b->scroll.top; - b->ccol = 0; + b->curs_row = b->scroll_top; + b->curs_col = 0; } static @@ -530,17 +1020,17 @@ interpret_csi_priv_mode(Vt *t, int param[], int pcount, bool set) case 47: /* use alternate/normal screen buffer */ case 1047: if (!set) - bclear(&t->buf[1]); - t->buffer = set ? &t->buf[1] : &t->buf[0]; - vt·dirty(t); + buffer_clear(&t->buffer_alternate); + t->buffer = set ? &t->buffer_alternate : &t->buffer_normal; + vt_dirty(t); if (param[i] != 1049) break; /* fall through */ case 1048: /* save/restore cursor */ if (set) - csave(t); + cursor_save(t); else - crestore(t); + cursor_restore(t); break; case 1000: /* enable/disable normal mouse tracking */ t->mousetrack = set; @@ -633,10 +1123,10 @@ interpret_csi(Vt *t) interpret_csi_ech(t, csiparam, param_count); break; case 'S': /* SU: scroll up */ - vt·scroll(t, param_count ? -csiparam[0] : -1); + vt_scroll(t, param_count ? -csiparam[0] : -1); break; case 'T': /* SD: scroll down */ - vt·scroll(t, param_count ? csiparam[0] : 1); + vt_scroll(t, param_count ? csiparam[0] : 1); break; case 'Z': /* CBT: cursor backward tabulation */ puttab(t, param_count ? -csiparam[0] : -1); @@ -644,7 +1134,7 @@ interpret_csi(Vt *t) case 'g': /* TBC: tabulation clear */ switch (param_count ? csiparam[0] : 0) { case 0: - b->tabs[b->ccol] = false; + b->tabs[b->curs_col] = false; break; case 3: memset(b->tabs, 0, sizeof(*b->tabs) * b->maxcols); @@ -655,14 +1145,14 @@ interpret_csi(Vt *t) interpret_csi_decstbm(t, csiparam, param_count); break; case 's': /* save cursor location */ - csave(t); + cursor_save(t); break; case 'u': /* restore cursor location */ - crestore(t); + cursor_restore(t); break; case 'n': /* query cursor location */ if (param_count == 1 && csiparam[0] == 6) - sendcurs(t); + send_curs(t); break; default: break; @@ -675,8 +1165,8 @@ void interpret_csi_ind(Vt *t) { Buffer *b = t->buffer; - if (b->crow < b->lines + b->rows - 1) - b->crow++; + if (b->curs_row < b->lines + b->rows - 1) + b->curs_row++; } /* Interpret a 'reverse index' (RI) sequence */ @@ -685,11 +1175,11 @@ void interpret_csi_ri(Vt *t) { Buffer *b = t->buffer; - if (b->crow > b->scroll.top) - b->crow--; + if (b->curs_row > b->scroll_top) + b->curs_row--; else { - roll(b->scroll.top, b->scroll.bot, -1); - zero(b->scroll.top, 0, b->cols); + row_roll(b->scroll_top, b->scroll_bot, -1); + row_set(b->scroll_top, 0, b->cols, b); } } @@ -699,9 +1189,9 @@ void interpret_csi_nel(Vt *t) { Buffer *b = t->buffer; - if (b->crow < b->lines + b->rows - 1) { - b->crow++; - b->ccol = 0; + if (b->curs_row < b->lines + b->rows - 1) { + b->curs_row++; + b->curs_col = 0; } } @@ -730,8 +1220,8 @@ interpret_osc(Vt *t) switch (command) { case 0: /* icon name and window title */ case 2: /* window title */ - // if (t->title_handler) - // t->title_handler(t, data+1); + if (t->title_handler) + t->title_handler(t, data+1); break; case 1: /* icon name */ break; @@ -786,12 +1276,12 @@ try_interpret_escape_seq(Vt *t) } break; case '7': /* DECSC: save cursor and attributes */ - savepen(t); - csave(t); + attributes_save(t); + cursor_save(t); goto handled; case '8': /* DECRC: restore cursor and attributes */ - loadpen(t); - crestore(t); + attributes_restore(t); + cursor_restore(t); goto handled; case 'D': /* IND: index */ interpret_csi_ind(t); @@ -803,7 +1293,7 @@ try_interpret_escape_seq(Vt *t) interpret_csi_nel(t); goto handled; case 'H': /* HTS: horizontal tab set */ - t->buffer->tabs[t->buffer->ccol] = true; + t->buffer->tabs[t->buffer->curs_col] = true; goto handled; default: goto cancel; @@ -833,17 +1323,17 @@ puttab(Vt *t, int count) { Buffer *b = t->buffer; int direction = count >= 0 ? 1 : -1; - for (int col = b->ccol + direction; count; col += direction) { + for (int col = b->curs_col + direction; count; col += direction) { if (col < 0) { - b->ccol = 0; + b->curs_col = 0; break; } if (col >= b->cols) { - b->ccol = b->cols - 1; + b->curs_col = b->cols - 1; break; } if (b->tabs[col]) { - b->ccol = col; + b->curs_col = col; count -= direction; } } @@ -851,31 +1341,31 @@ puttab(Vt *t, int count) static void -process_nonprinting(Vt *t, rune r) +process_nonprinting(Vt *t, wchar_t wc) { Buffer *b = t->buffer; - switch (r) { + switch (wc) { case '\e': /* ESC */ new_escape_sequence(t); break; case '\a': /* BEL */ - // if (t->urgent_handler) - // t->urgent_handler(t); + if (t->urgent_handler) + t->urgent_handler(t); break; case '\b': /* BS */ - if (b->ccol > 0) - b->ccol--; + if (b->curs_col > 0) + b->curs_col--; break; case '\t': /* HT */ puttab(t, 1); break; case '\r': /* CR */ - b->ccol = 0; + b->curs_col = 0; break; case '\v': /* VT */ case '\f': /* FF */ case '\n': /* LF */ - clinedown(t); + cursor_line_down(t); break; case '\016': /* SO: shift out, invoke the G1 character set */ t->graphmode = t->charsets[1]; @@ -893,7 +1383,7 @@ is_utf8_locale(void) const char *cset = nl_langinfo(CODESET); if (!cset) cset = "ANSI_X3.4-1968"; - isutf8 = !strcmp(cset, "UTF-8"); + is_utf8 = !strcmp(cset, "UTF-8"); } static @@ -917,18 +1407,17 @@ get_vt100_graphic(char c) 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e }; - if (isutf8) + if (is_utf8) return vt100_utf8[c - 0x41]; - // else if (strchr(vt100_acs, c)) - // return NCURSES_ACS(c); + else if (strchr(vt100_acs, c)) + return NCURSES_ACS(c); return '\0'; } static void -putrune(Vt *t, rune r) +put_wc(Vt *t, wchar_t wc) { - Cell blank; int width = 0; if (!t->seen_input) { @@ -938,61 +1427,54 @@ putrune(Vt *t, rune r) if (t->escaped) { if (t->elen + 1 < sizeof(t->ebuf)) { - t->ebuf[t->elen] = r; + t->ebuf[t->elen] = wc; t->ebuf[++t->elen] = '\0'; try_interpret_escape_seq(t); } else { cancel_escape_sequence(t); } - } else if (IS_CONTROL(r)) { - process_nonprinting(t, r); + } else if (IS_CONTROL(wc)) { + process_nonprinting(t, wc); } else { if (t->graphmode) { - if (r >= 0x41 && r <= 0x7e) { - wchar_t gc = get_vt100_graphic(r); + if (wc >= 0x41 && wc <= 0x7e) { + wchar_t gc = get_vt100_graphic(wc); if (gc) - r = gc; + wc = gc; } width = 1; - } else if ((width = wcwidth(r)) < 1) { + } else if ((width = wcwidth(wc)) < 1) { width = 1; } Buffer *b = t->buffer; - blank = (Cell){ - .r = L'\0', - .pen = (Pen){ - .state = b->pen.state, - .col = b->pen.col - }, - }; - if (width == 2 && b->ccol == b->cols - 1) { - b->crow->cells[b->ccol++] = blank; - b->crow->dirty = true; + Cell blank_cell = { L'\0', build_attrs(b->curattrs), b->curfg, b->curbg }; + if (width == 2 && b->curs_col == b->cols - 1) { + b->curs_row->cells[b->curs_col++] = blank_cell; + b->curs_row->dirty = true; } - if (b->ccol >= b->cols) { - b->ccol = 0; - clinedown(t); + if (b->curs_col >= b->cols) { + b->curs_col = 0; + cursor_line_down(t); } if (t->insert) { - Cell *src = b->crow->cells + b->ccol; + Cell *src = b->curs_row->cells + b->curs_col; Cell *dest = src + width; - size_t len = b->cols - b->ccol - width; + size_t len = b->cols - b->curs_col - width; memmove(dest, src, len * sizeof *dest); } - b->crow->cells[b->ccol] = blank; - b->crow->cells[b->ccol++].r = r; - b->crow->dirty = true; - + b->curs_row->cells[b->curs_col] = blank_cell; + b->curs_row->cells[b->curs_col++].text = wc; + b->curs_row->dirty = true; if (width == 2) - b->crow->cells[b->ccol++] = blank; + b->curs_row->cells[b->curs_col++] = blank_cell; } } int -vt·process(Vt *t) +vt_process(Vt *t) { int res; uint pos = 0; @@ -1004,17 +1486,18 @@ vt·process(Vt *t) return -1; } + /* read from the pty */ res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen); if (res < 0) return -1; + /* process the bytes you read */ t->rlen += res; while (pos < t->rlen) { - rune r; - size_t len; + wchar_t wc; + ssize_t len; - // XXX: convert this to use utf8 functions - len = (ssize_t)mbrtowc((wchar*)&r, t->rbuf + pos, t->rlen - pos, &ps); + len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &ps); if (len == -2) { t->rlen -= pos; memmove(t->rbuf, t->rbuf + pos, t->rlen); @@ -1023,11 +1506,11 @@ vt·process(Vt *t) if (len == -1) { len = 1; - r = t->rbuf[pos]; + wc = t->rbuf[pos]; } pos += len ? len : 1; - putrune(t, r); + put_wc(t, wc); } t->rlen -= pos; @@ -1035,9 +1518,16 @@ vt·process(Vt *t) return 0; } -/* size is the number of rows kept in the scrollback */ +void +vt_default_colors_set(Vt *t, attr_t attrs, short fg, short bg) +{ + t->defattrs = attrs; + t->deffg = fg; + t->defbg = bg; +} + Vt * -vt·make(int rows, int cols, int size) +vt_create(int rows, int cols, int scroll_size) { if (rows <= 0 || cols <= 0) return nil; @@ -1047,14 +1537,11 @@ vt·make(int rows, int cols, int size) return nil; t->pty = -1; - t->pen = (Pen) { - .state = PenNormal, - .col = {-1, -1}, - }; - t->buffer = &t->buf[0]; + t->deffg = t->defbg = -1; + t->buffer = &t->buffer_normal; - if (!binit(&t->buf[0], rows, cols, size) || - !binit(&t->buf[1], rows, cols, 0)) { + if (!buffer_init(&t->buffer_normal, rows, cols, scroll_size) || + !buffer_init(&t->buffer_alternate, rows, cols, 0)) { free(t); return nil; } @@ -1063,34 +1550,34 @@ vt·make(int rows, int cols, int size) } void -vt·resize(Vt *t, int rows, int cols) +vt_resize(Vt *t, int rows, int cols) { struct winsize ws = { .ws_row = rows, .ws_col = cols }; if (rows <= 0 || cols <= 0) return; - vt·noscroll(t); - bresize(&t->buf[0], rows, cols); - bresize(&t->buf[1], rows, cols); - cclamp(t); + vt_noscroll(t); + buffer_resize(&t->buffer_normal, rows, cols); + buffer_resize(&t->buffer_alternate, rows, cols); + cursor_clamp(t); ioctl(t->pty, TIOCSWINSZ, &ws); kill(-t->pid, SIGWINCH); } void -vt·free(Vt *t) +vt_destroy(Vt *t) { if (!t) return; - bfree(&t->buf[0]); - bfree(&t->buf[1]); + buffer_free(&t->buffer_normal); + buffer_free(&t->buffer_alternate); close(t->pty); free(t); } void -vt·dirty(Vt *t) +vt_dirty(Vt *t) { Buffer *b = t->buffer; for (Row *row = b->lines, *end = row + b->rows; row < end; row++) @@ -1098,49 +1585,51 @@ vt·dirty(Vt *t) } void -vt·draw(Vt *t, Window *win, int srow, int scol) +vt_draw(Vt *t, WINDOW *win, int srow, int scol) { - int i, j; - Cell *cell, *prev; Buffer *b = t->buffer; if (srow != t->srow || scol != t->scol) { - vt·dirty(t); + vt_dirty(t); t->srow = srow; t->scol = scol; } - for (i = 0; i < b->rows; i++) { + for (int i = 0; i < b->rows; i++) { Row *row = b->lines + i; if (!row->dirty) continue; wmove(win, srow + i, scol); - for (j = 0; j < b->cols; j++) { - prev = cell; + Cell *cell = nil; + for (int j = 0; j < b->cols; j++) { + Cell *prev_cell = cell; cell = row->cells + j; - if (!prev || !peneq(cell->pen, prev->pen)) { - if (cell->pen.state == PenNormal) - cell->pen.state = t->pen.state; - if (cell->pen.col.fg == -1) - cell->pen.col.fg = t->pen.col.fg; - if (cell->pen.col.bg == -1) - cell->pen.col.bg = t->pen.col.bg; - // wattrset(win, cell->attr << NCURSES_ATTR_SHIFT); - // wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), nil); + if (!prev_cell || cell->attr != prev_cell->attr + || cell->fg != prev_cell->fg + || cell->bg != prev_cell->bg) { + if (cell->attr == A_NORMAL) + cell->attr = t->defattrs; + if (cell->fg == -1) + cell->fg = t->deffg; + if (cell->bg == -1) + cell->bg = t->defbg; + wattrset(win, cell->attr << NCURSES_ATTR_SHIFT); + wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), nil); } - if (isutf8 && cell->r >= RuneSync) { + if (is_utf8 && cell->text >= 128) { char buf[MB_CUR_MAX + 1]; - size_t len = wcrtomb(buf, cell->r, nil); + size_t len = wcrtomb(buf, cell->text, nil); if (len > 0) { waddnstr(win, buf, len); - if (wcwidth(cell->r) > 1) + if (wcwidth(cell->text) > 1) j++; } - } else - waddch(win, cell->r > ' ' ? cell->r: ' '); + } else { + waddch(win, cell->text > ' ' ? cell->text : ' '); + } } int x, y; @@ -1152,36 +1641,36 @@ vt·draw(Vt *t, Window *win, int srow, int scol) row->dirty = false; } - wmove(win, srow + b->crow - b->lines, scol + b->ccol); + wmove(win, srow + b->curs_row - b->lines, scol + b->curs_col); } void -vt·scroll(Vt *t, int rows) +vt_scroll(Vt *t, int rows) { Buffer *b = t->buffer; - if (!b->scroll.size) + if (!b->scroll_size) return; if (rows < 0) { /* scroll back */ - if (rows < -b->scroll.above) - rows = -b->scroll.above; + if (rows < -b->scroll_above) + rows = -b->scroll_above; } else { /* scroll forward */ - if (rows > b->scroll.below) - rows = b->scroll.below; + if (rows > b->scroll_below) + rows = b->scroll_below; } - bscroll(b, rows); - b->scroll.below -= rows; + buffer_scroll(b, rows); + b->scroll_below -= rows; } void -vt·noscroll(Vt *t) +vt_noscroll(Vt *t) { - int below = t->buffer->scroll.below; - if (below) - vt·scroll(t, below); + int scroll_below = t->buffer->scroll_below; + if (scroll_below) + vt_scroll(t, scroll_below); } pid_t -vt·forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from) +vt_forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from) { int vt2ed[2], ed2vt[2]; struct winsize ws; @@ -1228,7 +1717,7 @@ vt·forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const cha for (const char **envp = env; envp && envp[0]; envp += 2) setenv(envp[0], envp[1], 1); - setenv("TERM", vtname, 1); + setenv("TERM", vt_term, 1); if (cwd) chdir(cwd); @@ -1257,13 +1746,13 @@ vt_pty_get(Vt *t) return t->pty; } -uintptr -vt·write(Vt *t, const char *buf, size_t len) +ssize_t +vt_write(Vt *t, const char *buf, size_t len) { - uintptr res, ret = len; + ssize_t ret = len; while (len > 0) { - res = write(t->pty, buf, len); + ssize_t res = write(t->pty, buf, len); if (res < 0) { if (errno != EAGAIN && errno != EINTR) return -1; @@ -1278,19 +1767,18 @@ vt·write(Vt *t, const char *buf, size_t len) static void -sendcurs(Vt *t) +send_curs(Vt *t) { Buffer *b = t->buffer; char keyseq[16]; - snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->crow - b->lines), b->ccol); - vt·write(t, keyseq, strlen(keyseq)); + snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->curs_row - b->lines), b->curs_col); + vt_write(t, keyseq, strlen(keyseq)); } -#if 0 void -vt·keypress(Vt *t, int keycode) +vt_keypress(Vt *t, int keycode) { - vt·noscroll(t); + vt_noscroll(t); if (keycode >= 0 && keycode <= KEY_MAX && keytable[keycode]) { switch (keycode) { @@ -1299,23 +1787,26 @@ vt·keypress(Vt *t, int keycode) case KEY_RIGHT: case KEY_LEFT: { char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] }; - vt·write(t, keyseq, sizeof keyseq); + vt_write(t, keyseq, sizeof keyseq); break; } default: - vt·write(t, keytable[keycode], strlen(keytable[keycode])); + vt_write(t, keytable[keycode], strlen(keytable[keycode])); } } else if (keycode <= UCHAR_MAX) { char c = keycode; - vt·write(t, &c, 1); + vt_write(t, &c, 1); } else { +#ifndef NDEBUG fprintf(stderr, "unhandled key %#o\n", keycode); +#endif } } void -vt·mouse(Vt *t, int x, int y, mmask_t mask) +vt_mouse(Vt *t, int x, int y, mmask_t mask) { +#ifdef NCURSES_MOUSE_VERSION char seq[6] = { '\e', '[', 'M' }, state = 0, button = 0; if (!t->mousetrack) @@ -1341,16 +1832,16 @@ vt·mouse(Vt *t, int x, int y, mmask_t mask) seq[4] = 32 + x; seq[5] = 32 + y; - vt·write(t, seq, sizeof seq); + vt_write(t, seq, sizeof seq); if (mask & (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) { /* send a button release event */ button = 3; seq[3] = 32 + button + state; - vt·write(t, seq, sizeof seq); + vt_write(t, seq, sizeof seq); } +#endif /* NCURSES_MOUSE_VERSION */ } -#endif static uint @@ -1363,8 +1854,124 @@ color_hash(short fg, short bg) return fg * (COLORS + 2) + bg; } +int +vt_rgb_get(Vt *t, uint8 r, uint8 g, uint8 b) +{ + return 0; +} + + +int +vt_color_get(Vt *t, short fg, short bg) +{ + if (fg >= COLORS) + fg = (t ? t->deffg : default_fg); + if (bg >= COLORS) + bg = (t ? t->defbg : default_bg); + + if (!has_default_colors) { + if (fg == -1) + fg = (t && t->deffg != -1 ? t->deffg : default_fg); + if (bg == -1) + bg = (t && t->defbg != -1 ? t->defbg : default_bg); + } + + if (!color2palette || (fg == -1 && bg == -1)) + return 0; + uint index = color_hash(fg, bg); + if (color2palette[index] == 0) { + short oldfg, oldbg; + for (;;) { + if (++color_pair_current >= color_pairs_max) + color_pair_current = color_pairs_reserved + 1; + pair_content(color_pair_current, &oldfg, &oldbg); + uint old_index = color_hash(oldfg, oldbg); + if (color2palette[old_index] >= 0) { + if (init_pair(color_pair_current, fg, bg) == OK) { + color2palette[old_index] = 0; + color2palette[index] = color_pair_current; + } + break; + } + } + } + + int color_pair = color2palette[index]; + return color_pair >= 0 ? color_pair : -color_pair; +} + +int +vt_color_reserve(short fg, short bg) +{ + if (!color2palette || fg >= COLORS || bg >= COLORS) + return 0; + + if (!has_default_colors && fg == -1) + fg = default_fg; + if (!has_default_colors && bg == -1) + bg = default_bg; + if (fg == -1 && bg == -1) + return 0; + + uint index = color_hash(fg, bg); + if (color2palette[index] >= 0) { + if (init_pair(color_pairs_reserved + 1, fg, bg) == OK) + color2palette[index] = -(++color_pairs_reserved); + } + int color_pair = color2palette[index]; + return color_pair >= 0 ? color_pair : -color_pair; +} + +int +vt_rgb_reserve(short fg, short bg) +{ + if (!color2palette || fg >= COLORS || bg >= COLORS) + return 0; + + if (!has_default_colors && fg == -1) + fg = default_fg; + if (!has_default_colors && bg == -1) + bg = default_bg; + if (fg == -1 && bg == -1) + return 0; + + uint index = color_hash(fg, bg); + if (color2palette[index] >= 0) { + if (init_pair(color_pairs_reserved + 1, fg, bg) == OK) + color2palette[index] = -(++color_pairs_reserved); + } + int color_pair = color2palette[index]; + return color_pair >= 0 ? color_pair : -color_pair; +} + +static +void +init_colors(void) +{ + pair_content(0, &default_fg, &default_bg); + if (default_fg == -1) + default_fg = COLOR_WHITE; + if (default_bg == -1) + default_bg = COLOR_BLACK; + has_default_colors = (use_default_colors() == OK); + color_pairs_max = MIN(MAX_COLOR_PAIRS, SHRT_MAX); + + if (COLORS) + color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(int)); + /* + * XXX: On undefined color-pairs NetBSD curses pair_content() set fg + * and bg to default colors while ncurses set them respectively to + * 0 and 0. Initialize all color-pairs in order to have consistent + * behaviour despite the implementation used. + */ + for (short i = 1; i < color_pairs_max; i++) + init_extended_pair(i, 0, 0); + + vt_color_reserve(COLOR_WHITE, COLOR_BLACK); +} + void -vt·init(void) +vt_init(void) { init_colors(); is_utf8_locale(); @@ -1372,17 +1979,11 @@ vt·init(void) if (!term) term = "dvtm"; - snprintf(vtname, sizeof vtname, "%s%s", term, COLORS >= 256 ? "-256color" : ""); -} - -void -vt·shutdown(void) -{ + snprintf(vt_term, sizeof vt_term, "%s%s", term, COLORS >= 256 ? "-256color" : ""); } -#if 0 void -vt·setkeytable(const char * const keytable_overlay[], int count) +vt_keytable_set(const char * const keytable_overlay[], int count) { for (int k = 0; k < count && k < KEY_MAX; k++) { const char *keyseq = keytable_overlay[k]; @@ -1392,47 +1993,52 @@ vt·setkeytable(const char * const keytable_overlay[], int count) } void -vt·settitlecb(Vt *t, vt_title_handler_t handler) +vt_shutdown(void) +{ + free(color2palette); +} + +void +vt_title_handler_set(Vt *t, vt_title_handler_t handler) { t->title_handler = handler; } void -vt·seturgentcb(Vt *t, vt_urgent_handler_t handler) +vt_urgent_handler_set(Vt *t, vt_urgent_handler_t handler) { t->urgent_handler = handler; } -#endif void -vt·setdata(Vt *t, void *data) +vt_data_set(Vt *t, void *data) { t->data = data; } void * -vt·getdata(Vt *t) +vt_data_get(Vt *t) { return t->data; } bool -vt·cursorvisible(Vt *t) +vt_cursor_visible(Vt *t) { - return t->buffer->scroll.below ? false : !t->curshid; + return t->buffer->scroll_below ? false : !t->curshid; } pid_t -vt·pid(Vt *t) +vt_pid_get(Vt *t) { return t->pid; } size_t -vt·content(Vt *t, char **buf, bool colored) +vt_content_get(Vt *t, char **buf, bool colored) { Buffer *b = t->buffer; - int lines = b->scroll.above + b->scroll.below + b->rows + 1; + int lines = b->scroll_above + b->scroll_below + b->rows + 1; size_t size = lines * ((b->cols + 1) * ((colored ? 64 : 0) + MB_CUR_MAX)); mbstate_t ps; memset(&ps, 0, sizeof(ps)); @@ -1441,28 +2047,28 @@ vt·content(Vt *t, char **buf, bool colored) return 0; char *s = *buf; - Cell *prev = nil; + Cell *prev_cell = nil; - for (Row *row = browfirst(b); row; row = brownext(b, row)) { + for (Row *row = buffer_row_first(b); row; row = buffer_row_next(b, row)) { size_t len = 0; char *last_non_space = s; for (int col = 0; col < b->cols; col++) { Cell *cell = row->cells + col; if (colored) { int esclen = 0; - if (!prev || cell->attr != prev->attr) { + if (!prev_cell || cell->attr != prev_cell->attr) { attr_t attr = cell->attr << NCURSES_ATTR_SHIFT; esclen = sprintf(s, "\033[0%s%s%s%s%s%sm", - attr & PenBold ? ";1" : "", - attr & PenDim ? ";2" : "", - attr & PenUnderline ? ";4" : "", - attr & PenBlink ? ";5" : "", - attr & PenReverse ? ";7" : "", - attr & PenInvis ? ";8" : ""); + attr & A_BOLD ? ";1" : "", + attr & A_DIM ? ";2" : "", + attr & A_UNDERLINE ? ";4" : "", + attr & A_BLINK ? ";5" : "", + attr & A_REVERSE ? ";7" : "", + attr & A_INVIS ? ";8" : ""); if (esclen > 0) s += esclen; } - if (!prev || cell->fg != prev->fg || cell->attr != prev->attr) { + if (!prev_cell || cell->fg != prev_cell->fg || cell->attr != prev_cell->attr) { if (cell->fg == -1) esclen = sprintf(s, "\033[39m"); else @@ -1470,7 +2076,7 @@ vt·content(Vt *t, char **buf, bool colored) if (esclen > 0) s += esclen; } - if (!prev || cell->bg != prev->bg || cell->attr != prev->attr) { + if (!prev_cell || cell->bg != prev_cell->bg || cell->attr != prev_cell->attr) { if (cell->bg == -1) esclen = sprintf(s, "\033[49m"); else @@ -1478,7 +2084,7 @@ vt·content(Vt *t, char **buf, bool colored) if (esclen > 0) s += esclen; } - prev = cell; + prev_cell = cell; } if (cell->text) { len = wcrtomb(s, cell->text, &ps); @@ -1500,7 +2106,7 @@ vt·content(Vt *t, char **buf, bool colored) } int -vt·contentstart(Vt *t) +vt_content_start(Vt *t) { - return t->buffer->scroll.above; + return t->buffer->scroll_above; } diff --git a/sys/cmd/dvtm/vt.c.old b/sys/cmd/dvtm/vt.c.old deleted file mode 100644 index d858472..0000000 --- a/sys/cmd/dvtm/vt.c.old +++ /dev/null @@ -1,2074 +0,0 @@ -/* See license for details */ -#include -#include - -#include -#include -#include - -#include -#include - -#if defined(__linux__) || defined(__CYGWIN__) -# include -#elif defined(__FreeBSD__) || defined(__DragonFly__) -# include -#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) -# include -#endif - -#include "vt.h" - -#ifndef NCURSES_ATTR_SHIFT -# define NCURSES_ATTR_SHIFT 8 -#endif - -#ifndef NCURSES_ACS -# ifdef PDCURSES -# define NCURSES_ACS(c) (acs_map[(uchar)(c)]) -# else /* BSD curses */ -# define NCURSES_ACS(c) (_acs_map[(uchar)(c)]) -# endif -#endif - -#ifdef NCURSES_VERSION - -#ifndef NCURSES_EXT_COLORS -# define NCURSES_EXT_COLORS 0 -#endif - -#if !NCURSES_EXT_COLORS -# define MAX_COLOR_PAIRS MIN(COLOR_PAIRS, 256) -#endif - -#endif - -#ifndef MAX_COLOR_PAIRS -# define MAX_COLOR_PAIRS COLOR_PAIRS -#endif - -#if defined _AIX && defined CTRL -# undef CTRL -#endif -#ifndef CTRL -# define CTRL(k) ((k) & 0x1F) -#endif - -#define IS_CONTROL(ch) !((ch) & 0xffffff60UL) - -static bool is_utf8, has_default_colors; -static int color_pairs_reserved, color_pairs_max, color_pair_current; -static int *color2palette, default_fg, default_bg; -static char vt_term[32]; - -typedef struct { - wchar_t text; - attr_t attr; - int fg; - int bg; -} Cell; - -typedef struct { - Cell *cells; - uint dirty:1; -} Row; - -/* Buffer holding the current terminal window content (as an array) as well - * as the scroll back buffer content (as a circular/ring buffer). - * - * If new content is added to terminal the view port slides down and the - * previously top most line is moved into the scroll back buffer at postion - * scroll_index. This index will eventually wrap around and thus overwrite - * the oldest lines. - * - * In the scenerio below a scroll up has been performed. That is 'scroll_above' - * lines still lie above the current view port. Further scrolling up will show - * them. Similarly 'scroll_below' is the amount of lines below the current - * viewport. - * - * The function buffer_boundary sets the row pointers to the start/end range - * of the section delimiting the region before/after the viewport. The functions - * buffer_row_{first,last} return the first/last logical row. And - * buffer_row_{next,prev} allows to iterate over the logical lines in either - * direction. - * - * scroll back buffer - * - * scroll_buf->+----------------+-----+ - * | | | ^ \ - * | before | | | | - * current terminal content | viewport | | | | - * | | | | - * +----------------+-----+\ | | | s > scroll_above - * ^ | | i | \ | | i | c | - * | | | n | \ | | n | r | - * | | v | \ | | v | o | - * r | | i | \ | | i | l / - * o | viewport | s | >|<- scroll_index | s | l \ - * w | | i | / | | i | | - * s | | b | / | after | b | s > scroll_below - * | | l | / | viewport | l | i | - * v | | e | / | | e | z / - * +----------------+-----+/ | unused | | e - * <- maxcols -> | scroll back | | - * <- cols -> | buffer | | | - * | | | | - * | | | v - * roll_buf + scroll_size->+----------------+-----+ - * <- maxcols -> - * <- cols -> - */ -typedef struct { - Row *lines; /* array of Row pointers of size 'rows' */ - Row *curs_row; /* row on which the cursor currently resides */ - Row *scroll_buf; /* a ring buffer holding the scroll back content */ - Row *scroll_top; /* row in lines where scrolling region starts */ - Row *scroll_bot; /* row in lines where scrolling region ends */ - bool *tabs; /* a boolean flag for each column whether it is a tab */ - int scroll_size; /* maximal capacity of scroll back buffer (in lines) */ - int scroll_index; /* current index into the ring buffer */ - int scroll_above; /* number of lines above current viewport */ - int scroll_below; /* number of lines below current viewport */ - int rows, cols; /* current dimension of buffer */ - int maxcols; /* allocated cells (maximal cols over time) */ - attr_t curattrs, savattrs; /* current and saved attributes for cells */ - int curs_col; /* current cursor column (zero based) */ - int curs_srow, curs_scol; /* saved cursor row/colmn (zero based) */ - short curfg, curbg; /* current fore and background colors */ - short savfg, savbg; /* saved colors */ -} Buffer; - -struct Vt { - Buffer buffer_normal; /* normal screen buffer */ - Buffer buffer_alternate; /* alternate screen buffer */ - Buffer *buffer; /* currently active buffer (one of the above) */ - attr_t defattrs; /* attributes to use for normal/empty cells */ - int deffg, defbg; /* colors to use for back normal/empty cells (white/black) */ - int pty; /* master side pty file descriptor */ - pid_t pid; /* process id of the process running in this vt */ - /* flags */ - uint seen_input:1; - uint insert:1; - uint escaped:1; - uint curshid:1; - uint curskeymode:1; - uint bell:1; - uint relposmode:1; - uint mousetrack:1; - uint graphmode:1; - uint savgraphmode:1; - bool charsets[2]; - /* buffers and parsing state */ - char rbuf[BUFSIZ]; - char ebuf[BUFSIZ]; - uint rlen, elen; - int srow, scol; /* last known offset to display start row, start column */ - char title[256]; /* xterm style window title */ - vt_title_handler_t title_handler; /* hook which is called when title changes */ - vt_urgent_handler_t urgent_handler; /* hook which is called upon bell */ - void *data; /* user supplied data */ -}; - -#if 0 -static const char *keytable[KEY_MAX+1] = { - [KEY_ENTER] = "\r", - ['\n'] = "\n", - /* for the arrow keys the CSI / SS3 sequences are not stored here - * because they depend on the current cursor terminal mode - */ - [KEY_UP] = "A", - [KEY_DOWN] = "B", - [KEY_RIGHT] = "C", - [KEY_LEFT] = "D", -#ifdef KEY_SUP - [KEY_SUP] = "\e[1;2A", -#endif -#ifdef KEY_SDOWN - [KEY_SDOWN] = "\e[1;2B", -#endif - [KEY_SRIGHT] = "\e[1;2C", - [KEY_SLEFT] = "\e[1;2D", - [KEY_BACKSPACE] = "\177", - [KEY_IC] = "\e[2~", - [KEY_DC] = "\e[3~", - [KEY_PPAGE] = "\e[5~", - [KEY_NPAGE] = "\e[6~", - [KEY_HOME] = "\e[7~", - [KEY_END] = "\e[8~", - [KEY_BTAB] = "\e[Z", - [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */ - [KEY_F(1)] = "\e[11~", - [KEY_F(2)] = "\e[12~", - [KEY_F(3)] = "\e[13~", - [KEY_F(4)] = "\e[14~", - [KEY_F(5)] = "\e[15~", - [KEY_F(6)] = "\e[17~", - [KEY_F(7)] = "\e[18~", - [KEY_F(8)] = "\e[19~", - [KEY_F(9)] = "\e[20~", - [KEY_F(10)] = "\e[21~", - [KEY_F(11)] = "\e[23~", - [KEY_F(12)] = "\e[24~", - [KEY_F(13)] = "\e[23~", - [KEY_F(14)] = "\e[24~", - [KEY_F(15)] = "\e[25~", - [KEY_F(16)] = "\e[26~", - [KEY_F(17)] = "\e[28~", - [KEY_F(18)] = "\e[29~", - [KEY_F(19)] = "\e[31~", - [KEY_F(20)] = "\e[32~", - [KEY_F(21)] = "\e[33~", - [KEY_F(22)] = "\e[34~", - [KEY_RESIZE] = "", -#ifdef KEY_EVENT - [KEY_EVENT] = "", -#endif -}; -#endif - -static void puttab(Vt *t, int count); -static void process_nonprinting(Vt *t, wchar_t wc); -static void send_curs(Vt *t); - -const static -attr_t -build_attrs(attr_t curattrs) -{ - return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff)) >> NCURSES_ATTR_SHIFT; -} - -static -void -row_set(Row *row, int start, int len, Buffer *t) -{ - Cell cell = { - .text = L'\0', - .attr = t ? build_attrs(t->curattrs) : 0, - .fg = t ? t->curfg : -1, - .bg = t ? t->curbg : -1, - }; - - for (int i = start; i < len + start; i++) - row->cells[i] = cell; - row->dirty = true; -} - -static -void -row_roll(Row *start, Row *end, int count) -{ - int n = end - start; - - count %= n; - if (count < 0) - count += n; - - if (count) { - char buf[count * sizeof(Row)]; - memcpy(buf, start, count * sizeof(Row)); - memmove(start, start + count, (n - count) * sizeof(Row)); - memcpy(end - count, buf, count * sizeof(Row)); - for (Row *row = start; row < end; row++) - row->dirty = true; - } -} - -static -void -buffer_clear(Buffer *b) -{ - Cell cell = { - .text = L'\0', - .attr = A_NORMAL, - .fg = -1, - .bg = -1, - }; - - for (int i = 0; i < b->rows; i++) { - Row *row = b->lines + i; - for (int j = 0; j < b->cols; j++) { - row->cells[j] = cell; - row->dirty = true; - } - } -} - -static -void -buffer_free(Buffer *b) -{ - for (int i = 0; i < b->rows; i++) - free(b->lines[i].cells); - free(b->lines); - for (int i = 0; i < b->scroll_size; i++) - free(b->scroll_buf[i].cells); - free(b->scroll_buf); - free(b->tabs); -} - -static -void -buffer_scroll(Buffer *b, int s) -{ - /* work in screenfuls */ - int ssz = b->scroll_bot - b->scroll_top; - if (s > ssz) { - buffer_scroll(b, ssz); - buffer_scroll(b, s - ssz); - return; - } - if (s < -ssz) { - buffer_scroll(b, -ssz); - buffer_scroll(b, s + ssz); - return; - } - - b->scroll_above += s; - if (b->scroll_above >= b->scroll_size) - b->scroll_above = b->scroll_size; - - if (s > 0 && b->scroll_size) { - for (int i = 0; i < s; i++) { - Row tmp = b->scroll_top[i]; - b->scroll_top[i] = b->scroll_buf[b->scroll_index]; - b->scroll_buf[b->scroll_index] = tmp; - - b->scroll_index++; - if (b->scroll_index == b->scroll_size) - b->scroll_index = 0; - } - } - row_roll(b->scroll_top, b->scroll_bot, s); - if (s < 0 && b->scroll_size) { - for (int i = (-s) - 1; i >= 0; i--) { - b->scroll_index--; - if (b->scroll_index == -1) - b->scroll_index = b->scroll_size - 1; - - Row tmp = b->scroll_top[i]; - b->scroll_top[i] = b->scroll_buf[b->scroll_index]; - b->scroll_buf[b->scroll_index] = tmp; - b->scroll_top[i].dirty = true; - } - } -} - -static -void -buffer_resize(Buffer *b, int rows, int cols) -{ - Row *lines = b->lines; - - if (b->rows != rows) { - if (b->curs_row >= lines + rows) { - /* scroll up instead of simply chopping off bottom */ - buffer_scroll(b, (b->curs_row - b->lines) - rows + 1); - } - while (b->rows > rows) { - free(lines[b->rows - 1].cells); - b->rows--; - } - - lines = realloc(lines, sizeof(Row) * rows); - } - - if (b->maxcols < cols) { - for (int row = 0; row < b->rows; row++) { - lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols); - if (b->cols < cols) - row_set(lines + row, b->cols, cols - b->cols, nil); - lines[row].dirty = true; - } - Row *sbuf = b->scroll_buf; - for (int row = 0; row < b->scroll_size; row++) { - sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols); - if (b->cols < cols) - row_set(sbuf + row, b->cols, cols - b->cols, nil); - } - b->tabs = realloc(b->tabs, sizeof(*b->tabs) * cols); - for (int col = b->cols; col < cols; col++) - b->tabs[col] = !(col & 7); - b->maxcols = cols; - b->cols = cols; - } else if (b->cols != cols) { - for (int row = 0; row < b->rows; row++) - lines[row].dirty = true; - b->cols = cols; - } - - int deltarows = 0; - if (b->rows < rows) { - while (b->rows < rows) { - lines[b->rows].cells = calloc(b->maxcols, sizeof(Cell)); - row_set(lines + b->rows, 0, b->maxcols, b); - b->rows++; - } - - /* prepare for backfill */ - if (b->curs_row >= b->scroll_bot - 1) { - deltarows = b->lines + rows - b->curs_row - 1; - if (deltarows > b->scroll_above) - deltarows = b->scroll_above; - } - } - - b->curs_row += lines - b->lines; - b->scroll_top = lines; - b->scroll_bot = lines + rows; - b->lines = lines; - - /* perform backfill */ - if (deltarows > 0) { - buffer_scroll(b, -deltarows); - b->curs_row += deltarows; - } -} - -static -bool -buffer_init(Buffer *b, int rows, int cols, int scroll_size) -{ - b->curattrs = A_NORMAL; /* white text over black background */ - b->curfg = b->curbg = -1; - if (scroll_size < 0) - scroll_size = 0; - if (scroll_size && !(b->scroll_buf = calloc(scroll_size, sizeof(Row)))) - return false; - b->scroll_size = scroll_size; - buffer_resize(b, rows, cols); - return true; -} - -static -void -buffer_boundry(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) -{ - if (bs) - *bs = nil; - if (be) - *be = nil; - if (as) - *as = nil; - if (ae) - *ae = nil; - if (!b->scroll_size) - return; - - if (b->scroll_above) { - if (bs) - *bs = &b->scroll_buf[(b->scroll_index - b->scroll_above + b->scroll_size) % b->scroll_size]; - if (be) - *be = &b->scroll_buf[(b->scroll_index-1 + b->scroll_size) % b->scroll_size]; - } - if (b->scroll_below) { - if (as) - *as = &b->scroll_buf[b->scroll_index]; - if (ae) - *ae = &b->scroll_buf[(b->scroll_index + b->scroll_below-1) % b->scroll_size]; - } -} - -static -Row * -buffer_row_first(Buffer *b) { - Row *bstart; - if (!b->scroll_size || !b->scroll_above) - return b->lines; - buffer_boundry(b, &bstart, nil, nil, nil); - return bstart; -} - -static -Row * -buffer_row_last(Buffer *b) { - Row *aend; - if (!b->scroll_size || !b->scroll_below) - return b->lines + b->rows - 1; - buffer_boundry(b, nil, nil, nil, &aend); - return aend; -} - -static -Row * -buffer_row_next(Buffer *b, Row *row) -{ - Row *before_start, *before_end, *after_start, *after_end; - Row *first = b->lines, *last = b->lines + b->rows - 1; - - if (!row) - return nil; - - buffer_boundry(b, &before_start, &before_end, &after_start, &after_end); - - if (row >= first && row < last) - return ++row; - if (row == last) - return after_start; - if (row == before_end) - return first; - if (row == after_end) - return nil; - if (row == &b->scroll_buf[b->scroll_size - 1]) - return b->scroll_buf; - return ++row; -} - -static -Row * -buffer_row_prev(Buffer *b, Row *row) -{ - Row *before_start, *before_end, *after_start, *after_end; - Row *first = b->lines, *last = b->lines + b->rows - 1; - - if (!row) - return nil; - - buffer_boundry(b, &before_start, &before_end, &after_start, &after_end); - - if (row > first && row <= last) - return --row; - if (row == first) - return before_end; - if (row == before_start) - return nil; - if (row == after_start) - return last; - if (row == b->scroll_buf) - return &b->scroll_buf[b->scroll_size - 1]; - return --row; -} - -static -void -cursor_clamp(Vt *t) -{ - Buffer *b = t->buffer; - Row *lines = t->relposmode ? b->scroll_top : b->lines; - int rows = t->relposmode ? b->scroll_bot - b->scroll_top : b->rows; - - if (b->curs_row < lines) - b->curs_row = lines; - if (b->curs_row >= lines + rows) - b->curs_row = lines + rows - 1; - if (b->curs_col < 0) - b->curs_col = 0; - if (b->curs_col >= b->cols) - b->curs_col = b->cols - 1; -} - -static -void -cursor_line_down(Vt *t) -{ - Buffer *b = t->buffer; - row_set(b->curs_row, b->cols, b->maxcols - b->cols, nil); - b->curs_row++; - if (b->curs_row < b->scroll_bot) - return; - - vt_noscroll(t); - - b->curs_row = b->scroll_bot - 1; - buffer_scroll(b, 1); - row_set(b->curs_row, 0, b->cols, b); -} - -static -void -cursor_save(Vt *t) -{ - Buffer *b = t->buffer; - b->curs_srow = b->curs_row - b->lines; - b->curs_scol = b->curs_col; -} - -static -void -cursor_restore(Vt *t) -{ - Buffer *b = t->buffer; - b->curs_row = b->lines + b->curs_srow; - b->curs_col = b->curs_scol; - cursor_clamp(t); -} - -static -void -attributes_save(Vt *t) -{ - Buffer *b = t->buffer; - b->savattrs = b->curattrs; - b->savfg = b->curfg; - b->savbg = b->curbg; - t->savgraphmode = t->graphmode; -} - -static -void -attributes_restore(Vt *t) -{ - Buffer *b = t->buffer; - b->curattrs = b->savattrs; - b->curfg = b->savfg; - b->curbg = b->savbg; - t->graphmode = t->savgraphmode; -} - -static -void -new_escape_sequence(Vt *t) -{ - t->escaped = true; - t->elen = 0; - t->ebuf[0] = '\0'; -} - -static -void -cancel_escape_sequence(Vt *t) -{ - t->escaped = false; - t->elen = 0; - t->ebuf[0] = '\0'; -} - -static -bool -is_valid_csi_ender(int c) -{ - return (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c == '@' || c == '`'); -} - -/* interprets a 'set attribute' (SGR) CSI escape sequence */ -static -void -interpret_csi_sgr(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - if (pcount == 0) { - /* special case: reset attributes */ - b->curattrs = A_NORMAL; - b->curfg = b->curbg = -1; - return; - } - - for (int i = 0; i < pcount; i++) { - switch (param[i]) { - case 0: - b->curattrs = A_NORMAL; - b->curfg = b->curbg = -1; - break; - case 1: - b->curattrs |= A_BOLD; - break; - case 2: - b->curattrs |= A_DIM; - break; -#ifdef A_ITALIC - case 3: - b->curattrs |= A_ITALIC; - break; -#endif - case 4: - b->curattrs |= A_UNDERLINE; - break; - case 5: - b->curattrs |= A_BLINK; - break; - case 7: - b->curattrs |= A_REVERSE; - break; - case 8: - b->curattrs |= A_INVIS; - break; - case 22: - b->curattrs &= ~(A_BOLD | A_DIM); - break; -#ifdef A_ITALIC - case 23: - b->curattrs &= ~A_ITALIC; - break; -#endif - case 24: - b->curattrs &= ~A_UNDERLINE; - break; - case 25: - b->curattrs &= ~A_BLINK; - break; - case 27: - b->curattrs &= ~A_REVERSE; - break; - case 28: - b->curattrs &= ~A_INVIS; - break; - case 30 ... 37: /* fg */ - b->curfg = param[i] - 30; - break; - case 38: - if ((i + 2) < pcount && param[i + 1] == 5) { - b->curfg = param[i + 2]; - i += 2; - } - break; - case 39: - b->curfg = -1; - break; - case 40 ... 47: /* bg */ - b->curbg = param[i] - 40; - break; - case 48: - if ((i + 2) < pcount && param[i + 1] == 5) { - b->curbg = param[i + 2]; - i += 2; - } - break; - case 49: - b->curbg = -1; - break; - case 90 ... 97: /* hi fg */ - b->curfg = param[i] - 82; - break; - case 100 ... 107: /* hi bg */ - b->curbg = param[i] - 92; - break; - default: - break; - } - } -} - -/* interprets an 'erase display' (ED) escape sequence */ -static -void -interpret_csi_ed(Vt *t, int param[], int pcount) -{ - Row *row, *start, *end; - Buffer *b = t->buffer; - - attributes_save(t); - b->curattrs = A_NORMAL; - b->curfg = b->curbg = -1; - - if (pcount && param[0] == 2) { - start = b->lines; - end = b->lines + b->rows; - } else if (pcount && param[0] == 1) { - start = b->lines; - end = b->curs_row; - row_set(b->curs_row, 0, b->curs_col + 1, b); - } else { - row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b); - start = b->curs_row + 1; - end = b->lines + b->rows; - } - - for (row = start; row < end; row++) - row_set(row, 0, b->cols, b); - - attributes_restore(t); -} - -/* interprets a 'move cursor' (CUP) escape sequence */ -static -void -interpret_csi_cup(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - Row *lines = t->relposmode ? b->scroll_top : b->lines; - - if (pcount == 0) { - b->curs_row = lines; - b->curs_col = 0; - } else if (pcount == 1) { - b->curs_row = lines + param[0] - 1; - b->curs_col = 0; - } else { - b->curs_row = lines + param[0] - 1; - b->curs_col = param[1] - 1; - } - - cursor_clamp(t); -} - -/* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL, - * CPL, CHA, HPR, VPA, VPR, HPA */ -static -void -interpret_csi_c(Vt *t, char verb, int param[], int pcount) -{ - Buffer *b = t->buffer; - int n = (pcount && param[0] > 0) ? param[0] : 1; - - switch (verb) { - case 'A': - b->curs_row -= n; - break; - case 'B': - case 'e': - b->curs_row += n; - break; - case 'C': - case 'a': - b->curs_col += n; - break; - case 'D': - b->curs_col -= n; - break; - case 'E': - b->curs_row += n; - b->curs_col = 0; - break; - case 'F': - b->curs_row -= n; - b->curs_col = 0; - break; - case 'G': - case '`': - b->curs_col = n - 1; - break; - case 'd': - b->curs_row = b->lines + n - 1; - break; - } - - cursor_clamp(t); -} - -/* Interpret the 'erase line' escape sequence */ -static -void -interpret_csi_el(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - switch (pcount ? param[0] : 0) { - case 1: - row_set(b->curs_row, 0, b->curs_col + 1, b); - break; - case 2: - row_set(b->curs_row, 0, b->cols, b); - break; - default: - row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b); - break; - } -} - -/* Interpret the 'insert blanks' sequence (ICH) */ -static -void -interpret_csi_ich(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - Row *row = b->curs_row; - int n = (pcount && param[0] > 0) ? param[0] : 1; - - if (b->curs_col + n > b->cols) - n = b->cols - b->curs_col; - - for (int i = b->cols - 1; i >= b->curs_col + n; i--) - row->cells[i] = row->cells[i - n]; - - row_set(row, b->curs_col, n, b); -} - -/* Interpret the 'delete chars' sequence (DCH) */ -static -void -interpret_csi_dch(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - Row *row = b->curs_row; - int n = (pcount && param[0] > 0) ? param[0] : 1; - - if (b->curs_col + n > b->cols) - n = b->cols - b->curs_col; - - for (int i = b->curs_col; i < b->cols - n; i++) - row->cells[i] = row->cells[i + n]; - - row_set(row, b->cols - n, n, b); -} - -/* Interpret an 'insert line' sequence (IL) */ -static -void -interpret_csi_il(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - int n = (pcount && param[0] > 0) ? param[0] : 1; - - if (b->curs_row + n >= b->scroll_bot) { - for (Row *row = b->curs_row; row < b->scroll_bot; row++) - row_set(row, 0, b->cols, b); - } else { - row_roll(b->curs_row, b->scroll_bot, -n); - for (Row *row = b->curs_row; row < b->curs_row + n; row++) - row_set(row, 0, b->cols, b); - } -} - -/* Interpret a 'delete line' sequence (DL) */ -static -void -interpret_csi_dl(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - int n = (pcount && param[0] > 0) ? param[0] : 1; - - if (b->curs_row + n >= b->scroll_bot) { - for (Row *row = b->curs_row; row < b->scroll_bot; row++) - row_set(row, 0, b->cols, b); - } else { - row_roll(b->curs_row, b->scroll_bot, n); - for (Row *row = b->scroll_bot - n; row < b->scroll_bot; row++) - row_set(row, 0, b->cols, b); - } -} - -/* Interpret an 'erase characters' (ECH) sequence */ -static -void -interpret_csi_ech(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - int n = (pcount && param[0] > 0) ? param[0] : 1; - - if (b->curs_col + n > b->cols) - n = b->cols - b->curs_col; - - row_set(b->curs_row, b->curs_col, n, b); -} - -/* Interpret a 'set scrolling region' (DECSTBM) sequence */ -static -void -interpret_csi_decstbm(Vt *t, int param[], int pcount) -{ - Buffer *b = t->buffer; - int new_top, new_bot; - - switch (pcount) { - case 0: - b->scroll_top = b->lines; - b->scroll_bot = b->lines + b->rows; - break; - case 2: - new_top = param[0] - 1; - new_bot = param[1]; - - /* clamp to bounds */ - if (new_top < 0) - new_top = 0; - if (new_top >= b->rows) - new_top = b->rows - 1; - if (new_bot < 0) - new_bot = 0; - if (new_bot >= b->rows) - new_bot = b->rows; - - /* check for range validity */ - if (new_top < new_bot) { - b->scroll_top = b->lines + new_top; - b->scroll_bot = b->lines + new_bot; - } - break; - default: - return; /* malformed */ - } - b->curs_row = b->scroll_top; - b->curs_col = 0; -} - -static -void -interpret_csi_mode(Vt *t, int param[], int pcount, bool set) -{ - for (int i = 0; i < pcount; i++) { - switch (param[i]) { - case 4: /* insert/replace mode */ - t->insert = set; - break; - } - } -} - -static -void -interpret_csi_priv_mode(Vt *t, int param[], int pcount, bool set) -{ - for (int i = 0; i < pcount; i++) { - switch (param[i]) { - case 1: /* set application/normal cursor key mode (DECCKM) */ - t->curskeymode = set; - break; - case 6: /* set origin to relative/absolute (DECOM) */ - t->relposmode = set; - break; - case 25: /* make cursor visible/invisible (DECCM) */ - t->curshid = !set; - break; - case 1049: /* combine 1047 + 1048 */ - case 47: /* use alternate/normal screen buffer */ - case 1047: - if (!set) - buffer_clear(&t->buffer_alternate); - t->buffer = set ? &t->buffer_alternate : &t->buffer_normal; - vt_dirty(t); - if (param[i] != 1049) - break; - /* fall through */ - case 1048: /* save/restore cursor */ - if (set) - cursor_save(t); - else - cursor_restore(t); - break; - case 1000: /* enable/disable normal mouse tracking */ - t->mousetrack = set; - break; - } - } -} - -static -void -interpret_csi(Vt *t) -{ - Buffer *b = t->buffer; - int csiparam[16]; - uint param_count = 0; - const char *p = t->ebuf + 1; - char verb = t->ebuf[t->elen - 1]; - - /* parse numeric parameters */ - for (p += (t->ebuf[1] == '?'); *p; p++) { - if (IS_CONTROL(*p)) { - process_nonprinting(t, *p); - } else if (*p == ';') { - if (param_count >= arrlen(csiparam)) - return; /* too long! */ - csiparam[param_count++] = 0; - } else if (isdigit((uchar)*p)) { - if (param_count == 0) - csiparam[param_count++] = 0; - csiparam[param_count - 1] *= 10; - csiparam[param_count - 1] += *p - '0'; - } - } - - if (t->ebuf[1] == '?') { - switch (verb) { - case 'h': - case 'l': /* private set/reset mode */ - interpret_csi_priv_mode(t, csiparam, param_count, verb == 'h'); - break; - } - return; - } - - /* delegate handling depending on command character (verb) */ - switch (verb) { - case 'h': - case 'l': /* set/reset mode */ - interpret_csi_mode(t, csiparam, param_count, verb == 'h'); - break; - case 'm': /* set attribute */ - interpret_csi_sgr(t, csiparam, param_count); - break; - case 'J': /* erase display */ - interpret_csi_ed(t, csiparam, param_count); - break; - case 'H': - case 'f': /* move cursor */ - interpret_csi_cup(t, csiparam, param_count); - break; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - case 'G': - case 'e': - case 'a': - case 'd': - case '`': /* relative move */ - interpret_csi_c(t, verb, csiparam, param_count); - break; - case 'K': /* erase line */ - interpret_csi_el(t, csiparam, param_count); - break; - case '@': /* insert characters */ - interpret_csi_ich(t, csiparam, param_count); - break; - case 'P': /* delete characters */ - interpret_csi_dch(t, csiparam, param_count); - break; - case 'L': /* insert lines */ - interpret_csi_il(t, csiparam, param_count); - break; - case 'M': /* delete lines */ - interpret_csi_dl(t, csiparam, param_count); - break; - case 'X': /* erase chars */ - interpret_csi_ech(t, csiparam, param_count); - break; - case 'S': /* SU: scroll up */ - vt_scroll(t, param_count ? -csiparam[0] : -1); - break; - case 'T': /* SD: scroll down */ - vt_scroll(t, param_count ? csiparam[0] : 1); - break; - case 'Z': /* CBT: cursor backward tabulation */ - puttab(t, param_count ? -csiparam[0] : -1); - break; - case 'g': /* TBC: tabulation clear */ - switch (param_count ? csiparam[0] : 0) { - case 0: - b->tabs[b->curs_col] = false; - break; - case 3: - memset(b->tabs, 0, sizeof(*b->tabs) * b->maxcols); - break; - } - break; - case 'r': /* set scrolling region */ - interpret_csi_decstbm(t, csiparam, param_count); - break; - case 's': /* save cursor location */ - cursor_save(t); - break; - case 'u': /* restore cursor location */ - cursor_restore(t); - break; - case 'n': /* query cursor location */ - if (param_count == 1 && csiparam[0] == 6) - send_curs(t); - break; - default: - break; - } -} - -/* Interpret an 'index' (IND) sequence */ -static -void -interpret_csi_ind(Vt *t) -{ - Buffer *b = t->buffer; - if (b->curs_row < b->lines + b->rows - 1) - b->curs_row++; -} - -/* Interpret a 'reverse index' (RI) sequence */ -static -void -interpret_csi_ri(Vt *t) -{ - Buffer *b = t->buffer; - if (b->curs_row > b->scroll_top) - b->curs_row--; - else { - row_roll(b->scroll_top, b->scroll_bot, -1); - row_set(b->scroll_top, 0, b->cols, b); - } -} - -/* Interpret a 'next line' (NEL) sequence */ -static -void -interpret_csi_nel(Vt *t) -{ - Buffer *b = t->buffer; - if (b->curs_row < b->lines + b->rows - 1) { - b->curs_row++; - b->curs_col = 0; - } -} - -/* Interpret a 'select character set' (SCS) sequence */ -static -void -interpret_csi_scs(Vt *t) -{ - /* ESC ( sets G0, ESC ) sets G1 */ - t->charsets[!!(t->ebuf[0] == ')')] = (t->ebuf[1] == '0'); - t->graphmode = t->charsets[0]; -} - -/* Interpret an 'operating system command' (OSC) sequence */ -static -void -interpret_osc(Vt *t) -{ - /* ESC ] command ; data BEL - * ESC ] command ; data ESC \\ - * Note that BEL or ESC \\ have already been replaced with NUL. - */ - char *data = nil; - int command = strtoul(t->ebuf + 1, &data, 10); - if (data && *data == ';') { - switch (command) { - case 0: /* icon name and window title */ - case 2: /* window title */ - if (t->title_handler) - t->title_handler(t, data+1); - break; - case 1: /* icon name */ - break; - default: -#ifndef NDEBUG - fprintf(stderr, "unknown OSC command: %d\n", command); -#endif - break; - } - } -} - -static -void -try_interpret_escape_seq(Vt *t) -{ - char lastchar = t->ebuf[t->elen - 1]; - - if (!*t->ebuf) - return; - - switch (*t->ebuf) { - case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECFPP */ - if (t->elen == 2) { - if (lastchar == '8') { /* DECALN */ - interpret_csi_ed(t, (int []){ 2 }, 1); - goto handled; - } - goto cancel; - } - break; - case '(': - case ')': - if (t->elen == 2) { - interpret_csi_scs(t); - goto handled; - } - break; - case ']': /* OSC - operating system command */ - if (lastchar == '\a' || - (lastchar == '\\' && t->elen >= 2 && t->ebuf[t->elen - 2] == '\e')) { - t->elen -= lastchar == '\a' ? 1 : 2; - t->ebuf[t->elen] = '\0'; - interpret_osc(t); - goto handled; - } - break; - case '[': /* CSI - control sequence introducer */ - if (is_valid_csi_ender(lastchar)) { - interpret_csi(t); - goto handled; - } - break; - case '7': /* DECSC: save cursor and attributes */ - attributes_save(t); - cursor_save(t); - goto handled; - case '8': /* DECRC: restore cursor and attributes */ - attributes_restore(t); - cursor_restore(t); - goto handled; - case 'D': /* IND: index */ - interpret_csi_ind(t); - goto handled; - case 'M': /* RI: reverse index */ - interpret_csi_ri(t); - goto handled; - case 'E': /* NEL: next line */ - interpret_csi_nel(t); - goto handled; - case 'H': /* HTS: horizontal tab set */ - t->buffer->tabs[t->buffer->curs_col] = true; - goto handled; - default: - goto cancel; - } - - if (t->elen + 1 >= sizeof(t->ebuf)) { -cancel: -#ifndef NDEBUG - fprintf(stderr, "cancelled: \\033"); - for (uint i = 0; i < t->elen; i++) { - if (isprint(t->ebuf[i])) { - fputc(t->ebuf[i], stderr); - } else { - fprintf(stderr, "\\%03o", t->ebuf[i]); - } - } - fputc('\n', stderr); -#endif -handled: - cancel_escape_sequence(t); - } -} - -static -void -puttab(Vt *t, int count) -{ - Buffer *b = t->buffer; - int direction = count >= 0 ? 1 : -1; - for (int col = b->curs_col + direction; count; col += direction) { - if (col < 0) { - b->curs_col = 0; - break; - } - if (col >= b->cols) { - b->curs_col = b->cols - 1; - break; - } - if (b->tabs[col]) { - b->curs_col = col; - count -= direction; - } - } -} - -static -void -process_nonprinting(Vt *t, wchar_t wc) -{ - Buffer *b = t->buffer; - switch (wc) { - case '\e': /* ESC */ - new_escape_sequence(t); - break; - case '\a': /* BEL */ - if (t->urgent_handler) - t->urgent_handler(t); - break; - case '\b': /* BS */ - if (b->curs_col > 0) - b->curs_col--; - break; - case '\t': /* HT */ - puttab(t, 1); - break; - case '\r': /* CR */ - b->curs_col = 0; - break; - case '\v': /* VT */ - case '\f': /* FF */ - case '\n': /* LF */ - cursor_line_down(t); - break; - case '\016': /* SO: shift out, invoke the G1 character set */ - t->graphmode = t->charsets[1]; - break; - case '\017': /* SI: shift in, invoke the G0 character set */ - t->graphmode = t->charsets[0]; - break; - } -} - -static -void -is_utf8_locale(void) -{ - const char *cset = nl_langinfo(CODESET); - if (!cset) - cset = "ANSI_X3.4-1968"; - is_utf8 = !strcmp(cset, "UTF-8"); -} - -static -wchar_t -get_vt100_graphic(char c) -{ - static char vt100_acs[] = "`afgjklmnopqrstuvwxyz{|}~"; - - /* - * 5f-7e standard vt100 - * 40-5e rxvt extension for extra curses acs chars - */ - static uint16_t const vt100_utf8[62] = { - 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47 - 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f - 0, 0, 0, 0, 0, 0, 0, 0, // 50-57 - 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f - 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67 - 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f - 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77 - 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e - }; - - if (is_utf8) - return vt100_utf8[c - 0x41]; - else if (strchr(vt100_acs, c)) - return NCURSES_ACS(c); - return '\0'; -} - -static -void -put_wc(Vt *t, wchar_t wc) -{ - int width = 0; - - if (!t->seen_input) { - t->seen_input = 1; - kill(-t->pid, SIGWINCH); - } - - if (t->escaped) { - if (t->elen + 1 < sizeof(t->ebuf)) { - t->ebuf[t->elen] = wc; - t->ebuf[++t->elen] = '\0'; - try_interpret_escape_seq(t); - } else { - cancel_escape_sequence(t); - } - } else if (IS_CONTROL(wc)) { - process_nonprinting(t, wc); - } else { - if (t->graphmode) { - if (wc >= 0x41 && wc <= 0x7e) { - wchar_t gc = get_vt100_graphic(wc); - if (gc) - wc = gc; - } - width = 1; - } else if ((width = wcwidth(wc)) < 1) { - width = 1; - } - Buffer *b = t->buffer; - Cell blank_cell = { L'\0', build_attrs(b->curattrs), b->curfg, b->curbg }; - if (width == 2 && b->curs_col == b->cols - 1) { - b->curs_row->cells[b->curs_col++] = blank_cell; - b->curs_row->dirty = true; - } - - if (b->curs_col >= b->cols) { - b->curs_col = 0; - cursor_line_down(t); - } - - if (t->insert) { - Cell *src = b->curs_row->cells + b->curs_col; - Cell *dest = src + width; - size_t len = b->cols - b->curs_col - width; - memmove(dest, src, len * sizeof *dest); - } - - b->curs_row->cells[b->curs_col] = blank_cell; - b->curs_row->cells[b->curs_col++].text = wc; - b->curs_row->dirty = true; - if (width == 2) - b->curs_row->cells[b->curs_col++] = blank_cell; - } -} - -int -vt_process(Vt *t) -{ - int res; - uint pos = 0; - mbstate_t ps; - memset(&ps, 0, sizeof(ps)); - - if (t->pty < 0) { - errno = EINVAL; - return -1; - } - - res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen); - if (res < 0) - return -1; - - t->rlen += res; - while (pos < t->rlen) { - wchar_t wc; - ssize_t len; - - len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &ps); - if (len == -2) { - t->rlen -= pos; - memmove(t->rbuf, t->rbuf + pos, t->rlen); - return 0; - } - - if (len == -1) { - len = 1; - wc = t->rbuf[pos]; - } - - pos += len ? len : 1; - put_wc(t, wc); - } - - t->rlen -= pos; - memmove(t->rbuf, t->rbuf + pos, t->rlen); - return 0; -} - -void -vt_default_colors_set(Vt *t, attr_t attrs, int fg, int bg) -{ - t->defattrs = attrs; - t->deffg = fg; - t->defbg = bg; -} - -Vt * -vt_create(int rows, int cols, int scroll_size) -{ - if (rows <= 0 || cols <= 0) - return nil; - - Vt *t = calloc(1, sizeof(Vt)); - if (!t) - return nil; - - t->pty = -1; - t->deffg = t->defbg = -1; - t->buffer = &t->buffer_normal; - - if (!buffer_init(&t->buffer_normal, rows, cols, scroll_size) || - !buffer_init(&t->buffer_alternate, rows, cols, 0)) { - free(t); - return nil; - } - - return t; -} - -void -vt_resize(Vt *t, int rows, int cols) -{ - struct winsize ws = { .ws_row = rows, .ws_col = cols }; - - if (rows <= 0 || cols <= 0) - return; - - vt_noscroll(t); - buffer_resize(&t->buffer_normal, rows, cols); - buffer_resize(&t->buffer_alternate, rows, cols); - cursor_clamp(t); - ioctl(t->pty, TIOCSWINSZ, &ws); - kill(-t->pid, SIGWINCH); -} - -void -vt_destroy(Vt *t) -{ - if (!t) - return; - buffer_free(&t->buffer_normal); - buffer_free(&t->buffer_alternate); - close(t->pty); - free(t); -} - -void -vt_dirty(Vt *t) -{ - Buffer *b = t->buffer; - for (Row *row = b->lines, *end = row + b->rows; row < end; row++) - row->dirty = true; -} - -void -vt_draw(Vt *t, WINDOW *win, int srow, int scol) -{ - Buffer *b = t->buffer; - - if (srow != t->srow || scol != t->scol) { - vt_dirty(t); - t->srow = srow; - t->scol = scol; - } - - for (int i = 0; i < b->rows; i++) { - Row *row = b->lines + i; - - if (!row->dirty) - continue; - - wmove(win, srow + i, scol); - Cell *cell = nil; - for (int j = 0; j < b->cols; j++) { - Cell *prev_cell = cell; - cell = row->cells + j; - if (!prev_cell || cell->attr != prev_cell->attr - || cell->fg != prev_cell->fg - || cell->bg != prev_cell->bg) { - if (cell->attr == A_NORMAL) - cell->attr = t->defattrs; - if (cell->fg == -1) - cell->fg = t->deffg; - if (cell->bg == -1) - cell->bg = t->defbg; - wattrset(win, cell->attr << NCURSES_ATTR_SHIFT); - wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), nil); - } - - if (is_utf8 && cell->text >= 128) { - char buf[MB_CUR_MAX + 1]; - size_t len = wcrtomb(buf, cell->text, nil); - if (len > 0) { - waddnstr(win, buf, len); - if (wcwidth(cell->text) > 1) - j++; - } - } else { - waddch(win, cell->text > ' ' ? cell->text : ' '); - } - } - - int x, y; - getyx(win, y, x); - (void)y; - if (x && x < b->cols - 1) - whline(win, ' ', b->cols - x); - - row->dirty = false; - } - - wmove(win, srow + b->curs_row - b->lines, scol + b->curs_col); -} - -void -vt_scroll(Vt *t, int rows) -{ - Buffer *b = t->buffer; - if (!b->scroll_size) - return; - if (rows < 0) { /* scroll back */ - if (rows < -b->scroll_above) - rows = -b->scroll_above; - } else { /* scroll forward */ - if (rows > b->scroll_below) - rows = b->scroll_below; - } - buffer_scroll(b, rows); - b->scroll_below -= rows; -} - -void -vt_noscroll(Vt *t) -{ - int scroll_below = t->buffer->scroll_below; - if (scroll_below) - vt_scroll(t, scroll_below); -} - -pid_t -vt_forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from) -{ - int vt2ed[2], ed2vt[2]; - struct winsize ws; - ws.ws_row = t->buffer->rows; - ws.ws_col = t->buffer->cols; - ws.ws_xpixel = ws.ws_ypixel = 0; - - if (to && pipe(vt2ed)) { - *to = -1; - to = nil; - } - if (from && pipe(ed2vt)) { - *from = -1; - from = nil; - } - - pid_t pid = forkpty(&t->pty, nil, nil, &ws); - if (pid < 0) - return -1; - - if (pid == 0) { - setsid(); - - sigset_t emptyset; - sigemptyset(&emptyset); - sigprocmask(SIG_SETMASK, &emptyset, nil); - - if (to) { - close(vt2ed[1]); - dup2(vt2ed[0], STDIN_FILENO); - close(vt2ed[0]); - } - - if (from) { - close(ed2vt[0]); - dup2(ed2vt[1], STDOUT_FILENO); - close(ed2vt[1]); - } - - int maxfd = sysconf(_SC_OPEN_MAX); - for (int fd = 3; fd < maxfd; fd++) - if (close(fd) == -1 && errno == EBADF) - break; - - for (const char **envp = env; envp && envp[0]; envp += 2) - setenv(envp[0], envp[1], 1); - setenv("TERM", vt_term, 1); - - if (cwd) - chdir(cwd); - - execvp(p, (char *const *)argv); - fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]); - exit(1); - } - - if (to) { - close(vt2ed[0]); - *to = vt2ed[1]; - } - - if (from) { - close(ed2vt[1]); - *from = ed2vt[0]; - } - - return t->pid = pid; -} - -int -vt_pty_get(Vt *t) -{ - return t->pty; -} - -ssize_t -vt_write(Vt *t, const char *buf, size_t len) -{ - ssize_t ret = len; - - while (len > 0) { - ssize_t res = write(t->pty, buf, len); - if (res < 0) { - if (errno != EAGAIN && errno != EINTR) - return -1; - continue; - } - buf += res; - len -= res; - } - - return ret; -} - -static -void -send_curs(Vt *t) -{ - Buffer *b = t->buffer; - char keyseq[16]; - snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->curs_row - b->lines), b->curs_col); - vt_write(t, keyseq, strlen(keyseq)); -} - -void -vt_keypress(Vt *t, int keycode) -{ - vt_noscroll(t); - - if (keycode >= 0 && keycode <= KEY_MAX && keytable[keycode]) { - switch (keycode) { - case KEY_UP: - case KEY_DOWN: - case KEY_RIGHT: - case KEY_LEFT: { - char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] }; - vt_write(t, keyseq, sizeof keyseq); - break; - } - default: - vt_write(t, keytable[keycode], strlen(keytable[keycode])); - } - } else if (keycode <= UCHAR_MAX) { - char c = keycode; - vt_write(t, &c, 1); - } else { -#ifndef NDEBUG - fprintf(stderr, "unhandled key %#o\n", keycode); -#endif - } -} - -void -vt_mouse(Vt *t, int x, int y, mmask_t mask) -{ -#ifdef NCURSES_MOUSE_VERSION - char seq[6] = { '\e', '[', 'M' }, state = 0, button = 0; - - if (!t->mousetrack) - return; - - if (mask & (BUTTON1_PRESSED | BUTTON1_CLICKED)) - button = 0; - else if (mask & (BUTTON2_PRESSED | BUTTON2_CLICKED)) - button = 1; - else if (mask & (BUTTON3_PRESSED | BUTTON3_CLICKED)) - button = 2; - else if (mask & (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED)) - button = 3; - - if (mask & BUTTON_SHIFT) - state |= 4; - if (mask & BUTTON_ALT) - state |= 8; - if (mask & BUTTON_CTRL) - state |= 16; - - seq[3] = 32 + button + state; - seq[4] = 32 + x; - seq[5] = 32 + y; - - vt_write(t, seq, sizeof seq); - - if (mask & (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) { - /* send a button release event */ - button = 3; - seq[3] = 32 + button + state; - vt_write(t, seq, sizeof seq); - } -#endif /* NCURSES_MOUSE_VERSION */ -} - -static -uint -color_hash(short fg, short bg) -{ - if (fg == -1) - fg = COLORS; - if (bg == -1) - bg = COLORS + 1; - return fg * (COLORS + 2) + bg; -} - -int -vt_color_get(Vt *t, int fg, int bg) -{ - if (fg >= COLORS) - fg = (t ? t->deffg : default_fg); - if (bg >= COLORS) - bg = (t ? t->defbg : default_bg); - - if (!has_default_colors) { - if (fg == -1) - fg = (t && t->deffg != -1 ? t->deffg : default_fg); - if (bg == -1) - bg = (t && t->defbg != -1 ? t->defbg : default_bg); - } - - if (!color2palette || (fg == -1 && bg == -1)) - return 0; - uint index = color_hash(fg, bg); - if (color2palette[index] == 0) { - int oldfg, oldbg; - for (;;) { - if (++color_pair_current >= color_pairs_max) - color_pair_current = color_pairs_reserved + 1; - extended_pair_content(color_pair_current, &oldfg, &oldbg); - uint old_index = color_hash(oldfg, oldbg); - if (color2palette[old_index] >= 0) { - if (init_extended_pair(color_pair_current, fg, bg) == OK) { - color2palette[old_index] = 0; - color2palette[index] = color_pair_current; - } - break; - } - } - } - - int color_pair = color2palette[index]; - return color_pair >= 0 ? color_pair : -color_pair; -} - -int -vt_color_reserve(int fg, int bg) -{ - if (!color2palette || fg >= COLORS || bg >= COLORS) - return 0; - - if (!has_default_colors && fg == -1) - fg = default_fg; - if (!has_default_colors && bg == -1) - bg = default_bg; - if (fg == -1 && bg == -1) - return 0; - - uint index = color_hash(fg, bg); - if (color2palette[index] >= 0) { - if (init_extended_pair(color_pairs_reserved + 1, fg, bg) == OK) - color2palette[index] = -(++color_pairs_reserved); - } - int color_pair = color2palette[index]; - return color_pair >= 0 ? color_pair : -color_pair; -} - -static -void -init_colors(void) -{ - extended_pair_content(0, &default_fg, &default_bg); - if (default_fg == -1) - default_fg = COLOR_WHITE; - if (default_bg == -1) - default_bg = COLOR_BLACK; - has_default_colors = (use_default_colors() == OK); - color_pairs_max = MIN(MAX_COLOR_PAIRS, SHRT_MAX); - - if (COLORS) - color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(int)); - /* - * XXX: On undefined color-pairs NetBSD curses pair_content() set fg - * and bg to default colors while ncurses set them respectively to - * 0 and 0. Initialize all color-pairs in order to have consistent - * behaviour despite the implementation used. - */ - for (short i = 1; i < color_pairs_max; i++) - init_extended_pair(i, 0, 0); - - vt_color_reserve(COLOR_WHITE, COLOR_BLACK); -} - -void -vt_init(void) -{ - init_colors(); - is_utf8_locale(); - char *term = getenv("DVTM_TERM"); - if (!term) - term = "dvtm"; - - snprintf(vt_term, sizeof vt_term, "%s%s", term, COLORS >= 256 ? "-256color" : ""); -} - -void -vt_keytable_set(const char * const keytable_overlay[], int count) -{ - for (int k = 0; k < count && k < KEY_MAX; k++) { - const char *keyseq = keytable_overlay[k]; - if (keyseq) - keytable[k] = keyseq; - } -} - -void -vt_shutdown(void) -{ - free(color2palette); -} - -void -vt_title_handler_set(Vt *t, vt_title_handler_t handler) -{ - t->title_handler = handler; -} - -void -vt_urgent_handler_set(Vt *t, vt_urgent_handler_t handler) -{ - t->urgent_handler = handler; -} - -void -vt_data_set(Vt *t, void *data) -{ - t->data = data; -} - -void * -vt_data_get(Vt *t) -{ - return t->data; -} - -bool -vt_cursor_visible(Vt *t) -{ - return t->buffer->scroll_below ? false : !t->curshid; -} - -pid_t -vt_pid_get(Vt *t) -{ - return t->pid; -} - -size_t -vt_content_get(Vt *t, char **buf, bool colored) -{ - Buffer *b = t->buffer; - int lines = b->scroll_above + b->scroll_below + b->rows + 1; - size_t size = lines * ((b->cols + 1) * ((colored ? 64 : 0) + MB_CUR_MAX)); - mbstate_t ps; - memset(&ps, 0, sizeof(ps)); - - if (!(*buf = malloc(size))) - return 0; - - char *s = *buf; - Cell *prev_cell = nil; - - for (Row *row = buffer_row_first(b); row; row = buffer_row_next(b, row)) { - size_t len = 0; - char *last_non_space = s; - for (int col = 0; col < b->cols; col++) { - Cell *cell = row->cells + col; - if (colored) { - int esclen = 0; - if (!prev_cell || cell->attr != prev_cell->attr) { - attr_t attr = cell->attr << NCURSES_ATTR_SHIFT; - esclen = sprintf(s, "\033[0%s%s%s%s%s%sm", - attr & A_BOLD ? ";1" : "", - attr & A_DIM ? ";2" : "", - attr & A_UNDERLINE ? ";4" : "", - attr & A_BLINK ? ";5" : "", - attr & A_REVERSE ? ";7" : "", - attr & A_INVIS ? ";8" : ""); - if (esclen > 0) - s += esclen; - } - if (!prev_cell || cell->fg != prev_cell->fg || cell->attr != prev_cell->attr) { - if (cell->fg == -1) - esclen = sprintf(s, "\033[39m"); - else - esclen = sprintf(s, "\033[38;5;%dm", cell->fg); - if (esclen > 0) - s += esclen; - } - if (!prev_cell || cell->bg != prev_cell->bg || cell->attr != prev_cell->attr) { - if (cell->bg == -1) - esclen = sprintf(s, "\033[49m"); - else - esclen = sprintf(s, "\033[48;5;%dm", cell->bg); - if (esclen > 0) - s += esclen; - } - prev_cell = cell; - } - if (cell->text) { - len = wcrtomb(s, cell->text, &ps); - if (len > 0) - s += len; - last_non_space = s; - } else if (len) { - len = 0; - } else { - *s++ = ' '; - } - } - - s = last_non_space; - *s++ = '\n'; - } - - return s - *buf; -} - -int -vt_content_start(Vt *t) -{ - return t->buffer->scroll_above; -} diff --git a/sys/cmd/dvtm/vt.h b/sys/cmd/dvtm/vt.h index 3d4d129..dfc94b3 100644 --- a/sys/cmd/dvtm/vt.h +++ b/sys/cmd/dvtm/vt.h @@ -2,7 +2,7 @@ #pragma once #include -// #include +#include #include #include @@ -18,7 +18,7 @@ void vt_init(void); void vt_shutdown(void); void vt_keytable_set(char const * const keytable_overlay[], int count); -void vt_default_colors_set(Vt*, attr_t attrs, int fg, int bg); +void vt_default_colors_set(Vt*, attr_t attrs, short fg, short bg); void vt_title_handler_set(Vt*, vt_title_handler_t); void vt_urgent_handler_set(Vt*, vt_urgent_handler_t); void vt_data_set(Vt*, void *); @@ -37,8 +37,8 @@ ssize_t vt_write(Vt*, const char *buf, size_t len); void vt_mouse(Vt*, int x, int y, mmask_t mask); void vt_dirty(Vt*); void vt_draw(Vt*, WINDOW *win, int startrow, int startcol); -int vt_color_get(Vt*, int fg, int bg); -int vt_color_reserve(int fg, int bg); +int vt_color_get(Vt*, short fg, short bg); +int vt_color_reserve(short fg, short bg); void vt_scroll(Vt*, int rows); void vt_noscroll(Vt*); diff --git a/sys/cmd/dvtm/window.c b/sys/cmd/dvtm/window.c deleted file mode 100644 index fec3997..0000000 --- a/sys/cmd/dvtm/window.c +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include - -#include "term.h" - -typedef struct Rect Rect; - -struct Rect -{ - int top, left, rows, cols; -}; - -struct Window -{ - Buffer buffer[2], *buf; - Rect area; /* on screen */ - Pen pen, spen; /* current and saved pen */ - uint curvis : 1; - uint damage : 1; -}; - -/* functions */ - -Window * -makewindow(Window *root, Rect area, int history) -{ - Window *w; - w = calloc(1, sizeof(*w)); - if (!w) - panicf("out of memory"); - - w->pen = (Pen) { - .state = PenNormal, - .col = {-1, -1}, - }; - - if (!binit(w->buffer+0, area.rows, area.cols, history) || - !binit(w->buffer+1, area.rows, area.cols, 0)) { - free(w); - return nil; - } - w->buf = w->buffer; - return w; -} diff --git a/sys/cmd/dwm/client.c b/sys/cmd/dwm/client.c index 783190a..50d4da9 100644 --- a/sys/cmd/dwm/client.c +++ b/sys/cmd/dwm/client.c @@ -362,22 +362,18 @@ setfocus(Client *c) void setfullscreen(Client *c, int fullscreen) { - + static ulong opacity = 0xFFFFFFFFul; if (fullscreen && !c->isfullscreen) { - // ulong rect[4] = { 0, 0, c->w, c->h }; XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, PropModeReplace, (uchar*)&netatom[NetWMFullscreen], 1); - - static ulong opacity = 0xFFFFFFFFul; XChangeProperty(dpy, c->win, netatom[NetWMWindowOpacity], XA_CARDINAL, 32, PropModeReplace, (uchar *)&opacity, 1L); - // XChangeProperty(dpy, c->win, netatom[NetWMOpaqueRegion], XA_CARDINAL, 32, - // PropModeReplace, (uchar*)&rect, arrlen(rect)); c->isfullscreen = 1; c->oldstate = c->isfloating; c->oldbw = c->bw; c->bw = 0; c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); XRaiseWindow(dpy, c->win); diff --git a/sys/cmd/dwm/config.h b/sys/cmd/dwm/config.h index ce5b196..053b9f8 100644 --- a/sys/cmd/dwm/config.h +++ b/sys/cmd/dwm/config.h @@ -60,9 +60,9 @@ static Layout layouts[] = { static char *menucmd[] = { "menu_run", nil }; static char *termcmd[] = { "term", nil }; static char *webscmd[] = { "qutebrowser", nil }; -static char *upvolcmd[] = { "pactl", "set-sink-volume", "0", "+5%", nil }; -static char *lovolcmd[] = { "pactl", "set-sink-volume", "0", "-5%", nil }; -static char *novolcmd[] = { "pactl", "set-sink-mute", "0", "toggle", nil }; +static char *upvolcmd[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%", nil }; +static char *lovolcmd[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%", nil }; +static char *novolcmd[] = { "pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle", nil }; #define XK_lovol XF86XK_AudioLowerVolume #define XK_upvol XF86XK_AudioRaiseVolume @@ -86,7 +86,7 @@ static Key keys[] = { { MODKEY, XK_o, incnmaster, {.i = -1 } }, { MODKEY, XK_h, setmfact, {.f = -0.05} }, { MODKEY, XK_l, setmfact, {.f = +0.05} }, - { MODKEY, XK_Return, zoom, {0} }, + { MODKEY|ShiftMask, XK_Return, zoom, {0} }, { MODKEY, XK_Tab, view, {0} }, { MODKEY|ShiftMask, XK_q, killclient, {0} }, { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[0]} }, diff --git a/sys/cmd/rc/code.c b/sys/cmd/rc/code.c new file mode 100644 index 0000000..f38dd43 --- /dev/null +++ b/sys/cmd/rc/code.c @@ -0,0 +1,356 @@ +#include "rc.h" + +#define delcode 100 +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] + +#define emitf(x) ((code.ip!=code.end || morecode()), code.ip++->f = (x)) +#define emiti(x) ((code.ip!=code.end || morecode()), code.ip++->i = (x)) +#define emits(x) ((code.ip!=code.end || morecode()), code.ip++->s = (x)) + +static struct +{ + int cap; + Code *buf, *ip, *end; +} code; + +static +int +morecode(void) +{ + code.cap += delcode; + code.buf = erealloc(code.buf, code.cap*sizeof(*code.buf)); + code.end = code.ip + delcode; + memset(code.ip, 0, delcode*sizeof(*code.buf)); + + return 0; +} + +static +void +stuffdot(Code *p) +{ + int a; + + a = p - code.buf; + if (code.ip <= p || p < code.buf) + panic("bad address %d in stuffdot", a); + codebuf[a].i = code.ip-code.buf; +} + +static +void +rcc(Tree *t, int eflag) +{ + Code *p, *q; + Tree *tt; + + if (!t) + return; + + switch(t->type) { + default: + pfmt(errio, "bad type %d in rc compiler\n", t->type); + break; + case '$': + emitf(Xmark); + rcc(c0, eflag); + emitf(Xdol); + break; + case '"': + emitf(Xmark); + rcc(c0, eflag); + emitf(Xqdol); + break; + case Asub: + emitf(Xmark); + rcc(c0, eflag); + emitf(Xmark); + rcc(c1, eflag); + emitf(Xsub); + break; + case '&': + emitf(Xasync); + if(havefork){ + p = emiti(0); + rcc(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + break; + case ';': + rcc(c0, eflag); + rcc(c1, eflag); + break; + case '^': + emitf(Xmark); + rcc(c1, eflag); + emitf(Xmark); + rcc(c0, eflag); + emitf(Xconc); + break; + case '`': + emitf(Xbackq); + if(havefork){ + p = emiti(0); + rcc(c0, 0); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + break; + case Aandand: + rcc(c0, 0); + emitf(Xtrue); + p = emiti(0); + rcc(c1, eflag); + stuffdot(p); + break; + case Aargs: + rcc(c1, eflag); + rcc(c0, eflag); + break; + case Kbang: + rcc(c0, eflag); + emitf(Xbang); + break; + case Aparen: + case Abrace: + rcc(c0, eflag); + break; + case Acount: + emitf(Xmark); + rcc(c0, eflag); + emitf(Xcount); + break; + case Kfunc: + emitf(Xmark); + rcc(c0, eflag); + if(c1){ + emitf(Xfn); + p = emiti(0); + emits(fnstr(c1)); + rcc(c1, eflag); + emitf(Xunlocal); /* get rid of $* */ + emitf(Xreturn); + stuffdot(p); + } + else + emitf(Xdelfn); + break; + case Kif: + rcc(c0, 0); + emitf(Xif); + p = emiti(0); + rcc(c1, eflag); + emitf(Xwastrue); + stuffdot(p); + break; + case Kelse: + if(!runq->iflast) + error("`else' does not follow `if(...)'"); + emitf(Xelse); + p = emiti(0); + rcc(c0, eflag); + stuffdot(p); + break; + case Aoror: + rcc(c0, 0); + emitf(Xfalse); + p = emiti(0); + rcc(c1, eflag); + stuffdot(p); + break; + case Aparen: + rcc(c0, eflag); + break; + case Asimple: + emitf(Xmark); + rcc(c0, eflag); + emitf(Xsimple); + if(eflag) + emitf(Xeflag); + break; + case Ksubsh: + emitf(Xsubshell); + if(havefork){ + p = emiti(0); + rcc(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + if(eflag) + emitf(Xeflag); + break; + case Kswitch: + codeswitch(t, eflag); + break; + case Ktwiddle: + emitf(Xmark); + rcc(c1, eflag); + emitf(Xmark); + rcc(c0, eflag); + emitf(Xmatch); + if(eflag) + emitf(Xeflag); + break; + case Kwhile: + q = code.ip; + rcc(c0, 0); + if(q==code.ip) + emitf(Xsettrue); /* empty condition == while(true) */ + emitf(Xtrue); + p = emiti(0); + rcc(c1, eflag); + emitf(Xjump); + emiti(q); + stuffdot(p); + break; + case Awords: + rcc(c1, eflag); + rcc(c0, eflag); + break; + case Kfor: + emitf(Xmark); + if(c1){ + rcc(c1, eflag); + emitf(Xglob); + } + else{ + emitf(Xmark); + emitf(Xword); + emits(strdup("*")); + emitf(Xdol); + } + emitf(Xmark); /* dummy value for Xlocal */ + emitf(Xmark); + rcc(c0, eflag); + emitf(Xlocal); + p = emitf(Xfor); + q = emiti(0); + rcc(c2, eflag); + emitf(Xjump); + emiti(p-code.buf); + stuffdot(q); + emitf(Xunlocal); + break; + case Aword: + emitf(Xword); + emits(strdup(t->str)); + break; + case Adup: + if(t->rtype==Adupfd){ + emitf(Xdup); + emiti(t->fd0); + emiti(t->fd1); + } + else{ + emitf(Xclose); + emiti(t->fd0); + } + rcc(c1, eflag); + emitf(Xpopredir); + break; + case Apipefd: + emitf(Xpipefd); + emiti(t->rtype); + if(havefork){ + p = emiti(0); + rcc(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + } + break; + case Aredir: + emitf(Xmark); + rcc(c0, eflag); + emitf(Xglob); + switch(t->rtype){ + case Rappend: + emitf(Xappend); + break; + case Rwrite: + emitf(Xwrite); + break; + case Rread: + case Rhere: + emitf(Xread); + break; + case Rrdwr: + emitf(Xrdwr); + break; + } + emiti(t->fd0); + rcc(c1, eflag); + emitf(Xpopredir); + break; + case '=': + tt = t; + for(;t && t->type=='=';t = c2); + if(t){ + for(t = tt;t->type=='=';t = c2){ + emitf(Xmark); + rcc(c1, eflag); + emitf(Xmark); + rcc(c0, eflag); + emitf(Xlocal); + } + rcc(t, eflag); + for(t = tt; t->type=='='; t = c2) + emitf(Xunlocal); + } + else{ + for(t = tt;t;t = c2){ + emitf(Xmark); + rcc(c1, eflag); + emitf(Xmark); + rcc(c0, eflag); + emitf(Xassign); + } + } + t = tt; /* so tests below will work */ + break; + case Apipe: + emitf(Xpipe); + emiti(t->fd0); + emiti(t->fd1); + if(havefork){ + p = emiti(0); + q = emiti(0); + rcc(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + q = emiti(0); + } + rcc(c1, eflag); + emitf(Xreturn); + stuffdot(q); + emitf(Xpipewait); + break; + } + if(t->type!=Kelse && t->type!=';') + runq->iflast = t->type==IF; + else if(c0) runq->iflast = c0->type==IF; +} + +Code* +compile(Tree *t) +{ + code.cap = delcode; + code.buf = code.ip = emalloc(code.cap*sizeof *code.buf); + code.end = code.ip + code.cap; + + emiti(0); + rcc(t, 0); + emitf(Xreturn); + emitf(nil); + + return code.buf; +} diff --git a/sys/cmd/rc/code.dep b/sys/cmd/rc/code.dep new file mode 100644 index 0000000..7fdd4bc --- /dev/null +++ b/sys/cmd/rc/code.dep @@ -0,0 +1,166 @@ +#if 0 +// simple example code +error +cd(Args args) +{ + switch (args.len) { + case 0: + errorf("reached cd with no arguments!"); + return 1; + case 1: + one: + errorf("sh: expected argument to command 'cd'"); + return 1; + case 2: + if (args.a[1] == nil) + goto one; + break; + default: + errorf("sh: too many arguments to command 'cd'"); + return 1; + } + if (chdir(args.a[1])) + errorf("cd fail: %s", strerror(errno)); + + return 0; +} + +error +quit(Args args) +{ + exit(0); +} + +Builtin builtins[] = { + { "cd", cd }, + { "exit", quit }, +}; + +void +clear(Header *arr) +{ + arr->len = 0; +} + +int +readline(Code *code) +{ + int n, b; + + n = code->len; +getchar: + if (code->len >= code->cap) { + code->cap += 100; + code->s = realloc(code->s, code->cap); + } + /* TODO: unicode? */ + switch ((b = getchar())) { + case EOF: + n = -1; + goto null; + case '\n': + n = code->len - n; + null: + code->s[code->len] = '\0'; + break; + default: + code->s[code->len++] = b; + goto getchar; + } + + return n; +} + +/* TODO: unicode */ +int +readargs(Code code, Args *args) +{ + if (args->a) + clear(&args->hdr); + else { + args->cap += 20; + args->a = realloc(args->a, args->cap); + } + + args->a[args->len++] = code.s; + while (*code.s) { + if (!isspace(*code.s++)) + continue; + + code.s[-1] = '\0'; + /* consume all remaining space */ + while (isspace(*code.s)) + code.s++; + + if (args->len >= args->cap-1) { + args->cap += 20; + args->a = realloc(args->a, args->cap); + } + args->a[args->len++] = code.s; + } + /* nil acts as a sentinel value */ + args->a[args->len] = nil; + + return args->len; +} + +error +execute(Args args) +{ + int i, status; + pid_t cid, wid; + + for (i = 0; i < arrlen(builtins); i++) { + if (strcmp(args.a[0], builtins[i].cmd) == 0) + return builtins[i].func(args); + } + + if ((cid = fork()) == 0) { + if (execvp(args.a[0], args.a) == -1) + errorf("exec failed: %s", strerror(errno)); + exit(1); + } else if (cid > 0) + do + wid = waitpid(cid, &status, WUNTRACED); + while (!WIFEXITED(status) && !WIFSIGNALED(status)); + else + errorf("fork failed: %s", strerror(errno)); + + return status; +} + +static +void +flush(void) +{ + io·flush(stdout); +} + +static +void +prompt(void) +{ + printf(";"); + flush(); +} + +int +main(int argc, char *argv[]) +{ + int i, err, n; + Code code = {0}; + Args args = {0}; + + ARGBEGIN { + } ARGEND; + + do { + clear(&code.hdr); + prompt(); + + n = readline(&code); + readargs(code, &args); + err = execute(args); + } while (!err && n > 0); +} +#endif diff --git a/sys/cmd/rc/exec.c b/sys/cmd/rc/exec.c new file mode 100644 index 0000000..2879678 --- /dev/null +++ b/sys/cmd/rc/exec.c @@ -0,0 +1,19 @@ +#include "rc.h" + +void +Xerror(char *s) +{ + +} + +void +Xmark(void) +{ + pushlist(); +} + +void +Xword(void) +{ + pushword(shell->ip++->s); +} diff --git a/sys/cmd/rc/io.c b/sys/cmd/rc/io.c new file mode 100644 index 0000000..8cc2f5b --- /dev/null +++ b/sys/cmd/rc/io.c @@ -0,0 +1,435 @@ +#include "rc.h" + +#undef bufsize +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] + +#define bufsize 512 +#define strsize 100 + +//------------------------------------------------------------------------ +// buffer operations + +/* open file */ +Io* +openfd(int fd) +{ + Io *f; + + f = emalloc(sizeof *f + bufsize); + f->fd = fd; + f->b = f->e = f->buf; + return f; +} + +/* open string */ +Io* +openstr(void) +{ + Io *f; + + f = emalloc(sizeof *f + strsize + 1); + f->fd = -1; + f->b = f->buf; + f->e = f->buf+strsize; + memset(f->b, 0, strsize+1); + + return f; +} + +/* open core (not nil terminated) */ +Io* +opencore(int len, char *s) +{ + Io *f; + + f = emalloc(sizeof *f + len); + f->fd = -1; + f->b = f->buf; + f->e = f->buf+len; + memcpy(f->b, s, len); + + return f; +} + +void +rewindio(Io *f) +{ + if (f->fd < 0) + f->b = f->buf; + else { + f->b = f->e = f->buf; + lseek(f->fd, 0, 0); + } +} + +void +closeio(Io *f) +{ + if (f->fd >= 0) + close(f->fd); + + efree(f); +} + +/* has the chance to realloc */ +void +flush(Io **fp) +{ + int n; + char *s; + Io *f; + + f = *fp; + if (f->fd < 0) { + n = f->e - f->b; + f = erealloc(f, sizeof *f + n + strsize + 1); + if (!f) + panic("can't realloc %d bytes in flush", n+strsize+1); + f->b = f->buf+n; + f->e = f->buf+n+strsize; + memset(f->b, 0, strsize+1); + } else { + n = f->b - f->buf; + if (n && write(f->fd, f->buf, n) < 0) { + write(3, "write error\n", 12); + if (ntrap) + dotrap(); + } + f->b = f->buf; + f->e = f->buf + bufsize; + } + + *fp = f; +} + +//------------------------------------------------------------------------ +// read from io + +int +rchr(Io *f) +{ + int n; + if (f->b == f->e) { + if (f->fd < 0 || (n = read(f->fd, f->buf, bufsize)) <= 0) + return EOF; + + f->b = f->buf; + f->e = f->b + n; + } + + return *f->b++&0xFF; +} + +//------------------------------------------------------------------------ +// printf functionality + +/* character literal */ +int +pchr(Io *f, int c) +{ + if (f->b == f->e) + flush(&f); + + return *f->b++=c; +} + +/* quote */ +void +pquo(Io *f, char *s) +{ + pchr(f, '\''); + for (; *s; s++) + if (*s == '\'') + pfmt(f, "''"); + else + pchr(f, *s); + pchr(f, '\''); +} + +/* word */ +void +pwrd(Io *f, char *s) +{ + char *t; + for (t = s; *t; t++) + if (!wordchr(*t)) + break; + if (t == s || *t) + pquo(f, s); + else + pstr(f, s); +} + +/* pointer */ +void +pptr(Io *f, void *v) +{ + int n; + uintptr p; + + p = (uintptr)v; + if ((sizeof(uintptr) == sizeof(uvlong)) && p >>32) + for (n = 60; n >= 32; n-=4) + pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); + for (n = 28; n >= 0; n-=4) + pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); +} + +/* string */ +void +pstr(Io *f, char *s) +{ + if (!s) + s = ""; + + while (*s) + pchr(f, *s++); +} + +/* decimal */ +void +pdec(Io *f, int n) +{ + if (n < 0) { + n = -n; + pchr(f, '-'); + if (n >= 0) { + pdec(f, n); + return; + } + n = 1 - n; + pdec(f, n/10); + pchr(f, n%10+'1'); + return; + } + + if (n > 9) + pdec(f, n/10); + pchr(f, n%10+'0'); +} + +/* octal */ +void +poct(Io *f, uint n) +{ + if (n > 7) + poct(f, n>>3); + pchr(f, (n&7)+'0'); +} + +/* value */ +void +pval(Io *f, Word *a) +{ + if (a) { + while (a->link && a->link->word) { + pwrd(f, a->word); + pchr(f, ' '); + a = a->link; + } + pwrd(f, a->word); + } +} + +/* tree */ +static +void +pdeglob(Io *f, char *s) +{ + while(*s){ + if(*s==GLOB) + s++; + pchr(f, *s++); + } +} + +void +pcmd(Io *f, Tree *t) +{ + if(!t) + return; + + switch(t->type){ + default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); + break; + case '$': pfmt(f, "$%t", c0); + break; + case '"': pfmt(f, "$\"%t", c0); + break; + case '&': pfmt(f, "%t&", c0); + break; + case '^': pfmt(f, "%t^%t", c0, c1); + break; + case '`': pfmt(f, "`%t", c0); + break; + case Aandand: pfmt(f, "%t && %t", c0, c1); + break; + case Kbang: pfmt(f, "! %t", c0); + break; + case Abrace: pfmt(f, "{%t}", c0); + break; + case Acount: pfmt(f, "$#%t", c0); + break; + case Kfunc: pfmt(f, "fn %t %t", c0, c1); + break; + case Kif: pfmt(f, "if%t%t", c0, c1); + break; + case Kelse: pfmt(f, "else %t", c0); + break; + case Aoror: pfmt(f, "%t || %t", c0, c1); + break; + case Aparen: pfmt(f, "(%t)", c0); + break; + case Asub: pfmt(f, "$%t(%t)", c0, c1); + break; + case Asimple: pfmt(f, "%t", c0); + break; + case Ksubsh: pfmt(f, "@ %t", c0); + break; + case Kswitch: pfmt(f, "switch %t %t", c0, c1); + break; + case Ktwiddle: pfmt(f, "~ %t %t", c0, c1); + break; + case Kwhile: pfmt(f, "while %t%t", c0, c1); + break; + case Aargs: + if(c0==0) + pfmt(f, "%t", c1); + else if(c1==0) + pfmt(f, "%t", c0); + else + pfmt(f, "%t %t", c0, c1); + break; + case ';': + if(c0){ + if(c1) + pfmt(f, "%t%c%t", c0, '\n', c1); + else pfmt(f, "%t", c0); + } + else pfmt(f, "%t", c1); + break; + case Awords: + if(c0) + pfmt(f, "%t ", c0); + pfmt(f, "%t", c1); + break; + case Kfor: + pfmt(f, "for(%t", c0); + if(c1) + pfmt(f, " in %t", c1); + pfmt(f, ")%t", c2); + break; + case Aword: + if(t->quoted) + pfmt(f, "%Q", t->str); + else + pdeglob(f, t->str); + break; + case Adup: + if(t->redir.type==Rdupfd) + pfmt(f, ">[%d=%d]", t->redir.fd[1], t->redir.fd[0]); /* yes, fd1, then fd0; read lex.c */ + else + pfmt(f, ">[%d=]", t->redir.fd[0]); + pfmt(f, "%t", c1); + break; + case Apipefd: + case Aredir: + switch(t->redir.type){ + case Rhere: + pchr(f, '<'); + case Rread: + case Rrdwr: + pchr(f, '<'); + if(t->redir.type==Rrdwr) + pchr(f, '>'); + if(t->redir.fd[0]!=0) + pfmt(f, "[%d]", t->redir.fd[0]); + break; + case Rappend: + pchr(f, '>'); + case Rwrite: + pchr(f, '>'); + if(t->redir.fd[0]!=1) + pfmt(f, "[%d]", t->redir.fd[0]); + break; + } + pfmt(f, "%t", c0); + if(c1) + pfmt(f, " %t", c1); + break; + case '=': + pfmt(f, "%t=%t", c0, c1); + if(c2) + pfmt(f, " %t", c2); + break; + case Apipe: + pfmt(f, "%t|", c0); + if(t->redir.fd[1]==0){ + if(t->redir.fd[0]!=1) + pfmt(f, "[%d]", t->redir.fd[0]); + } + else pfmt(f, "[%d=%d]", t->redir.fd[0], t->redir.fd[1]); + pfmt(f, "%t", c1); + break; + } +} + +/* rc specific printf */ +static int pfmtlev; + +void +vpfmt(Io *f, char *fmt, va_list args) +{ + char err[124]; + + pfmtlev++; + for (; *fmt; fmt++) + if (*fmt!='%') + pchr(f, *fmt); + else switch(*++fmt) { + case '\0': + break; + case 'c': + pchr(f, va_arg(args, int)); + break; + case 'd': + pdec(f, va_arg(args, int)); + break; + case 'o': + poct(f, va_arg(args, uint)); + break; + case 'p': + pptr(f, va_arg(args, void*)); + break; + case 'Q': + pquo(f, va_arg(args, char*)); + break; + case 'q': + pwrd(f, va_arg(args, char*)); + break; + case 's': + pstr(f, va_arg(args, char*)); + break; + case 't': + pcmd(f, va_arg(args, Tree*)); + break; + case 'v': + pval(f, va_arg(args, Word*)); + break; + } + if (--pfmtlev==0) + flush(&f); +} + +void +pfmt(Io *f, char *fmt, ...) +{ + va_list args; + char err[124]; + + va_start(args, fmt); + vpfmt(f, fmt, args); + va_end(args); +} diff --git a/sys/cmd/rc/lex.c b/sys/cmd/rc/lex.c new file mode 100644 index 0000000..1415f5c --- /dev/null +++ b/sys/cmd/rc/lex.c @@ -0,0 +1,415 @@ +#include "rc.h" + +#define onebyte(c) ((c&0x80)==0x00) +#define twobyte(c) ((c&0xe0)==0xc0) +#define threebyte(c) ((c&0xf0)==0xe0) +#define fourbyte(c) ((c&0xf8)==0xf0) + +// ----------------------------------------------------------------------- +// globals + +static int lastc, nextc=EOF, lastdol, lastword, doprompt = 1; +static char buf[8*1024]; + +// ----------------------------------------------------------------------- +// utilities + +static uchar nwordc[256] = +{ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int +wordchr(char c) +{ + return !nwordc[c] && c!=EOF; +} + + +static uchar nquotec[256] = +{ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int +quotechr(char c) +{ + return !nquotec[c] && c!=EOF; +} + +static uchar nvarc[256] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +int +varchr(char c) +{ + return !nvarc[c] && c!=EOF; +} + +static +void +prompt(void) +{ + shell->cmd.line++; + doprompt = 0; +} + +/* lookahead one byte */ +static +int +lookahead(void) +{ + int c; + + if(nextc != EOF) + return nextc; + + if(shell->cmd.eof) + return EOF; + if(doprompt) + prompt(); + + c = rchr(shell->cmd.io); + doprompt = c == '\n' || c == EOF; + + if(c == EOF) + shell->cmd.eof++; + + return nextc = c; +} + +/* consumes the lookahead */ +static +int +advance(void) +{ + int c = lookahead(); + lastc = nextc, nextc = EOF; + + return c; +} + +/* + * advance until we no longer hit horizontal space + * consumes all comments + */ +static +void +skipws(void) +{ + int c; + for(;;) { + c = lookahead(); + if(c=='#'){ + for(;;){ + c = lookahead(); + if(c=='\n' || c==EOF) + break; + advance(); + } + } + if(c==' ' || c=='\t') + advance(); + else + return; + } +} + +/* advance until we no longer hit any space */ +void +skipnl(void) +{ + int c; + for(;;) { + skipws(); + if ((c = lookahead()) != '\n') + return; + advance(); + } +} + +/* advance if next char is equal to c */ +static +int +nextis(int c) +{ + if(lookahead()==c) { + advance(); + return 1; + } + return 0; +} + +/* functions to append to our write buffer */ +static +char* +putbyte(char *s, int c) +{ + if(!s) + return s; + if(s == arrend(buf)){ + *s = 0; + rcerror("out of buffer space"); + return nil; + } + *s++ = c; + return s; +} + +static +char* +putrune(char *s, int c) +{ + s = putbyte(s, c); + if (onebyte(c)) + return s; + if (twobyte(c)) + return putbyte(s, advance()); + if (threebyte(c)) { + putbyte(s, advance()); + return putbyte(s, advance()); + } + if (fourbyte(c)) { + putbyte(s, advance()); + putbyte(s, advance()); + return putbyte(s, advance()); + } + rcerror("malformed utf8 stream"); + return nil; +} + +// ----------------------------------------------------------------------- +// main exports + +void +rcerror(char *fmt, ...) +{ + va_list args; + + pfmt(errio, "rc:"); + if (shell->cmd.io) + pfmt(errio, "%s:%d ", shell->cmd.name, shell->cmd.line); + + va_start(args, fmt); + vpfmt(errio, fmt, args); + va_end(args); + + pfmt(errio, "\n"); + + flush(&errio); + lastword = lastdol = 0; + while (lastc != '\n' && lastc != EOF) + advance(); +} + +/* word is only modified in the event of a lexed word */ +int +lex(Tree **node) +{ + int c; + char *w = buf; + /* + * NOTE: + * we inject tokens into the lexer based on context if last token = word: + * if we see a (, then we interpret that as a subscript + * otherwise, if the next character is the first char of a word, we return a ^ operator. + */ + if(lastword){ + lastword=0; + c = lookahead(); + if(c=='('){ + advance(); + return Alparen; + } + if(quotechr(c)) + return Acarot; + } + + skipws(); + switch(c=advance()) { + case EOF: + lastdol = 0; + return EOF; + case '$': + lastdol = 1; + if(nextis('#')) + return Acount; + if (nextis('"')) + return Aquote; + return Adol; + case '&': + lastdol = 0; + if(nextis('&')) + return Aandand; + return Aand; + + case '!': + return Kbang; + case '@': + return Ksubsh; + case '~': + return Ktwiddle; + + case '|': + lastdol = 0; + if(nextis('|')){ + skipnl(); + return Aoror; + } + (*node) = newtree(); + (*node)->type = Apipe; + (*node)->redir.fd[0] = 0; + (*node)->redir.fd[1] = 1; + goto redir; + case '>': + (*node) = newtree(); + (*node)->type = Aredir; + if (nextis(c)) + (*node)->redir.type = Rappend; + else + (*node)->redir.type = Rwrite; + (*node)->redir.fd[0] = 1; + goto redir; + case '<': + (*node) = newtree(); + (*node)->type = Aredir; + if(nextis(c)) + (*node)->redir.type = Rhere; + else if(nextis('>')) + (*node)->redir.type = Rrdwr; + else + (*node)->redir.type = Rread; + (*node)->redir.fd[0] = 0; + /* fallthrough */ + redir: + if(nextis('[')) { + c = advance(); + if(c < '0' || '9' < c) { + redirerr: + rcerror("incorrect redirection syntax"); + return EOF; + } + (*node)->redir.fd[0] = 0; + do { + (*node)->redir.fd[0] = 10*(*node)->redir.fd[0]+(c-'0'); + c = advance(); + } while('0'<=c && c<='9'); + + if(c == '=') { + if((*node)->type == Aredir) + (*node)->type = Adup; + c = advance(); + if('0'<=c && c<='9') { + (*node)->redir.type = Rdupfd; + (*node)->redir.fd[1] = (*node)->redir.fd[0]; + (*node)->redir.fd[0] = 0; + do { + (*node)->redir.fd[0] = 10*(*node)->redir.fd[0]+(c-'0'); + c = advance(); + } while('0'<=c && c<='9'); + } else { + if((*node)->type == Apipe) + goto redirerr; + (*node)->redir.type = Rclose; + } + } + if (c != ']' + ||(*node)->type==Adup && ((*node)->redir.type==Rhere || (*node)->redir.type==Rappend)) + goto redirerr; + } + if ((c = ((*node)->type)) == Apipe) + skipnl(); + return c; + + case '\'': + lastdol = 0; + lastword = 1; + for(;;){ + c = advance(); + if(c==EOF) + break; + if(c=='\''){ + if(lookahead()!='\'') + break; + advance(); + } + w = putrune(w, c); + } + *w = 0; + *node = wordnode(buf); + (*node)->quoted = 1; + return Aword; + } + if (!wordchr(c)) { + lastdol = 0; + return c; + } + for(;;){ + if(c=='*'||c=='['||c=='?'||c==GLOB) + w = putbyte(w, GLOB); + w = putrune(w, c); + c = lookahead(); + if(lastdol?!varchr(c):!wordchr(c)) + break; + advance(); + } + *w = 0; + + if ((c = kwlookup(buf)) == -1) { + (*node) = wordnode(buf); + (*node)->type = c = Aword; + (*node)->quoted = 0; + lastword = 1; + } + + lastdol = 0; + return c; +} diff --git a/sys/cmd/rc/main.c b/sys/cmd/rc/main.c new file mode 100644 index 0000000..baaf6bc --- /dev/null +++ b/sys/cmd/rc/main.c @@ -0,0 +1,86 @@ +#include "rc.h" + +/* globals */ +Thread *shell = nil; +int ntrap = 0; +Io *errio; + +/* main execution */ + +void +dotrap(void) +{ + exit(1); +} + +void +bootup(Code *c, int off, Var *vars) +{ + Thread *sh; + + alloc(sh); + sh->code = c, c->i++; + sh->ip = sh->code + off; + sh->local = vars; + sh->stack = nil; + + sh->link = shell, shell = sh; +} + +int +main(int argc, char *argv[]) +{ + int i; + Code *ip, sh[32]; + + ARGBEGIN { + } ARGEND; + + errio = openfd(2); + + initkw(); + + ip = sh; + memset(sh, 0, sizeof(sh)); + /* + * NOTE: first element of code is a reference count + * bootup runs: + * 1. *=argv[1:] + * 2. . rcmain $* + */ +#if 0 + ip++->i = 1; + ip++->f = Xmark; + ip++->f = Xword; + ip++->s = "*"; + ip++->f = Xassign; + ip++->f = Xmark; + ip++->f = Xmark; + ip++->s = "*"; + ip++->f = Xdol; + ip++->s = "rcmain"; + ip++->f = Xword; + ip++->s = "."; + ip++->f = Xsimple; + ip++->f = Xexit; + ip++->i = 0; + + bootup(sh, 1, nil); + pushlist(); + for (i = argc-1; i != 0; i--) + pushword(argv[i]); + + for (;;) { + shell->ip++->f(); + if (ntrap) + dotrap(); + } +#else + + bootup(sh, 1, nil); + shell->cmd.io = openfd(0); + parse(); + +#endif + exit(0); +} diff --git a/sys/cmd/rc/parse.c b/sys/cmd/rc/parse.c new file mode 100644 index 0000000..d963c12 --- /dev/null +++ b/sys/cmd/rc/parse.c @@ -0,0 +1,430 @@ +#include "rc.h" + +// ----------------------------------------------------------------------- +// global data + +static int lasta, nexta=EOF; +static Tree *node; /* if token was lexed as a tree node (redirs and words), its here */ + +static uchar prectab[256] = { + [Kif] = 1, [Kfor] = 1, [Kswitch] = 1, [Kelse] = 1, + [Aandand] = 2, [Aoror] = 2, + [Kbang] = 3, [Ksubsh] = 3, + [Apipe] = 4, + [Acarot] = 5, + [Adol] = 6, [Acount] = 6, [Aquote] = 6, + [Asub] = 7, +}; + +// ----------------------------------------------------------------------- +// helpers + +static +int +lookahead(void) +{ + int tok; + + if (nexta != EOF) + return nexta; + + tok = lex(&node); + return nexta = tok; +} + +static +int +advance(void) +{ + int tok = lookahead(); + lasta = nexta, nexta = EOF; + node = nil; + + return tok; +} + +static +int +nextis(int tok) +{ + if (lookahead() == tok) { + advance(); + return 1; + } + return 0; +} + +// ----------------------------------------------------------------------- +// subparsers + +static Tree *word(void); +static Tree *comword(void); +static Tree *cmd(int prec); + +static +Tree* +body(void) +{ + int tok; + Tree *l, *r; + l = cmd(1); +loop: + switch((tok=lookahead())){ + case '&': + l = tree1('&', l); + /* fallthrough */ + case ';': case '\n': + advance(); + r = cmd(1); + l = tree2(';', l, r); + goto loop; + default: + ; + } + + return l; +} + +static +Tree* +brace(void) +{ + Tree *t; + + if (!nextis('{')) + rcerror("not a brace"); + t = tree1(Abrace, body()); + if (!nextis('}')) + rcerror("unmatched brace"); + + return t; +} + +static +Tree* +paren(void) +{ + Tree *t; + + if (!nextis('(')) + rcerror("not a paren"); + t = tree1(Aparen, body()); + if (!nextis(')')) + rcerror("unmatched paren"); + + return t; +} + +/* TODO: fill in */ +static +Tree* +heredoc(Tree* t) +{ + return t; +} + +static +Tree* +redir(void) +{ + int tok; + Tree *t; + + switch (tok = lookahead()) { + case Adup: + t = node; + advance(); + break; + case Aredir: + advance(); + t = hang1(node, (node->redir.type == Rhere) ? heredoc(word()) : word()); + break; + default: + t = nil; + } + + return t; +} + +static +Tree* +epilog(void) +{ + Tree *t, *tt; + + t = redir(); + while((tt = redir())) + t = hang2(t, t->child[0], tt); + + return t; +} + +static +Tree* +sword(void) +{ + int tok; + if (Kstart < (tok=lookahead()) && tok < Kend) + return node; + + return comword(); +} + +static +Tree* +word(void) +{ + int tok; + Tree *t; + + t = sword(); + while(nextis('^')) + t = tree2('^', t, sword()); + + return t; +} + + +static +Tree* +words(void) +{ + Tree *t, *tt; + t = word(); + while((tt=word())) + t = tree2(Awords, t, tt); + + return t; +} + +static +Tree* +comword(void) +{ + int tok; + Tree *t, *tt; + + switch(tok=lookahead()){ + case Adol: + advance(); + t = word(); + if(nextis('(')) { + t = tree2(Asub, t, words()); + if (!nextis(')')) + rcerror("malformed index expression"); + } + return tree1(Adol, t); + case Acount: + advance(); + return tree1(Acount, word()); + case Atick: + advance(); + return tree1(Atick, brace()); + case Alparen: + return paren(); + case Aredir: + advance(); + t = hang1(node, brace()); + t->type = Apipefd; + return t; + case Aword: + t = node; + advance(); + return t; + } + return nil; +} + +static +Tree* +first(void) +{ + int tok; + Tree *t; + + t = comword(); + while(nextis('^')) { + t = tree2('^', t, word()); + } + + return t; +} + +/* simple _or_ assignment */ +static +Tree* +simple_or_assign(void) +{ + int tok; + Tree *t, *tt; + + /* can't continue */ + if (!(t = first())) + return nil; + + /* is an assignment */ +assign: + if(nextis('=')) + return tree3(Aeq, t, word(), cmd(prectab[Kbang])); + + /* is a 'simple' */ +simple: + switch ((tok=lookahead())) { + case Aredir: + case Adup: + t = tree2(Aargs, t, redir()); + goto simple; + default: + if ((tt = word())) { + t = tree2(Aargs, t, tt); + goto simple; + } + /* fallthrough */ + } + + return simplehang(t); +} + +static +Tree* +opand(void) +{ + int tok; + Tree *t, *tt; + + switch(tok=lookahead()) { + case Kif: + advance(); + t = paren(); + skipnl(); + tt = cmd(prectab[Kif]); + t = tree2(Kif, t, tt); + return t; + + case Kelse: + advance(); + skipnl(); + t = tree1(Kelse, cmd(prectab[Kelse])); + return t; + + case Kfor: + advance(); + if (!nextis('(')) + rcerror("malformed for statement"); + t = word(); + if (nextis(Kin)) { + advance(); + tt = words(); + t = tree3(Kin, t, tt, nil); + } else + t = tree3(Kin, t, nil, nil); + skipnl(); + tt = cmd(prectab[Kfor]); + t->child[2] = tt; + return t; + + case Kswitch: + advance(); + t = word(); + skipnl(); + tt = brace(); + t = tree2(Kswitch, t, tt); + return t; + + case Kfunc: + advance(); + t = words(); + if ((tok=lookahead()) == '{') { + tt = brace(); + t = tree2(Kfunc, t, tt); + } else + t = tree1(Kfunc, t); + return t; + + case Ksubsh: + advance(); + t = tree1(Ksubsh, cmd(prectab[Ksubsh])); + return t; + + case Kbang: + advance(); + t = tree1(Kbang, cmd(prectab[Kbang])); + return t; + + case Ktwiddle: + advance(); + tt = word(); + t = tree2(Ktwiddle, tt, words()); + return t; + + case Albrace: + t = brace(); + tt = epilog(); + return epihang(t, tt); + + case Aredir: /* fallthrough */ + case Adup: + t = redir(); + tt = cmd(prectab[Kbang]); + t = hang2(t, t->child[0], tt); + return t; + } + + return simple_or_assign(); +} + +static +Tree * +cmd(int prec) +{ + int np, tok; + Tree *l, *r, *p; + + if (!(l = opand())) + return nil; + + for(;;) { + tok = lookahead(); + np = prectab[tok]; + if (np < prec) + break; + p = node; + advance(); + r = cmd(np+1); + if (tok == Apipe) + l = hang2(p, l, r); + else + l = tree2(tok, l, r); + } + + return l; +} + +// ----------------------------------------------------------------------- +// main function + +int +parse(void) +{ + int tok; + Tree *t, *tt; + + t = cmd(1); +loop: + switch(tok=lookahead()) { + case '&': + t = tree1('&', t); + /* fallthrough */ + case ';': + advance(); + tt = cmd(1); + t = tree2(';', t, tt); + goto loop; + case '\n': case EOF: + pfmt(errio, "%t", t); + break; + default: + rcerror("unrecognized token: %d", tok); + } + return 0; +} diff --git a/sys/cmd/rc/rc.h b/sys/cmd/rc/rc.h new file mode 100644 index 0000000..ad46a77 --- /dev/null +++ b/sys/cmd/rc/rc.h @@ -0,0 +1,254 @@ +#pragma once + +#include +#include + +#include +#include + +#define alloc(ptr) ptr = emalloc(sizeof(*ptr)) + +// ----------------------------------------------------------------------- +// main enums + +#define GLOB 0x01 + +/* TODO: make sure there are no collisions */ +enum +{ + /* keywords */ + Kstart=11, Kfor, Kin, Kwhile, Kif, Kelse, + Kswitch, Kcase, Kfunc, Ktwiddle, + Kbang, Ksubsh, Kend, + + /* tokens */ + Aword='w', Aredir='r', Adup='d', Asimple='s', + Aargs='A', Awords='W', Abrace='b', Aparen='p', Asub='S', + Apcmd='c', Apipefd='-', Aandand='%', Aoror='@', Acount='#', + + Atick='`', Apipe = '|', Adol='$', Aquote='"', Aand='&', + Alparen = '(', Arparen = ')', Albrace='{', Arbrace='}', + Asemi=';', Acarot='^', Aeq='=', +}; + +enum +{ + Rappend = 1, + Rwrite = 2, + Rread = 3, + Rhere = 4, + Rdupfd = 5, + Rclose = 6, + Rrdwr = 7, +}; + +// ----------------------------------------------------------------------- +// main types + +typedef union Code Code; +typedef struct Word Word; +typedef struct List List; +typedef struct Var Var; +typedef struct Tree Tree; +typedef struct Builtin Builtin; +typedef struct Thread Thread; +typedef struct Io Io; + +union Code +{ + int i; + char *s; + void (*f)(void); +}; + +struct Word +{ + char *word; + Word *link; +}; + +struct List +{ + Word *words; + List *link; +}; + +struct Var +{ + string name; + Word *val; + Code *func; + Var *link; +}; + +struct Tree +{ + ushort type; + uchar quoted : 1; + union { + char *str; + struct { + ushort type; + int fd[2]; + } redir; + }; + + Tree *child[3], *link; +}; + +struct Builtin +{ + char *cmd; + void (*func)(void); +}; + +struct Thread +{ + Code *code, *ip; + List *stack; + Var *local; + struct { + uchar eof : 1; + int line; + char *name; + Io *io; + } cmd; + + int pid; + Tree *nodes; + Thread *link; /* continuation */ +}; + +struct Io +{ + int fd; + uchar *b, *e, buf[]; +}; + +// ----------------------------------------------------------------------- +// global interpreter variables + +extern Thread *shell; +extern int ntrap; +extern int status; + +extern Io *errio; + +extern Builtin builtins[]; +extern Var *globals[1021]; /* for now must be prime */ + +// ----------------------------------------------------------------------- +// interpreter functions (defined in exec.c) + +/* + * notation: + * (var1, var2, ...) : items from stack + * [var1, var2, ...] : items from code stream + * -> moves value + */ +extern void Xmark(void); /* Xmark: delimit stack with new list */ +extern void Xword(void); /* Xword[val] -> (val) */ +extern void Xassign(void); /* Xassign(name, val): assign name to val */ +extern void Xdol(void); /* Xdol(name): get variable value */ +extern void Xsimple(void); /* Xsimple(args): run command */ +extern void Xexit(void); /* Xexit: exit with status */ +extern void Xerror(char *s); /* Xerror: report an error */ +extern void Xparse(void); + +// ----------------------------------------------------------------------- +// shell functions + +/* + * util.c + */ +void *emalloc(uintptr size); +void *erealloc(void *ptr, uintptr size); +void efree(void *); +void panic(char *msg, int n); + +/* + * io.c + */ +Io *openfd(int fd); +Io *openstr(void); +Io *opencore(int len, char *s); +void rewindio(Io *f); +void closeio(Io *f); +void flush(Io **fp); + +/* reads */ +int rchr(Io *f); + +/* writes */ +int pchr(Io *f, int c); +void pquo(Io *f, char *s); +void pwrd(Io *f, char *s); +void pptr(Io *f, void *v); +void pstr(Io *f, char *s); +void pdec(Io *f, int n); +void poct(Io *f, uint n); +void pval(Io *f, Word *a); +void pcmd(Io *f, Tree *t); +void pfmt(Io *f, char *fmt, ...); +void vpfmt(Io *f, char *fmt, va_list args); + +/* + * word.c + */ +void pushlist(void); +void freelist(Word *w); +void poplist(void); + +int count(Word *w); +Word *newword(char *w, Word *link); +void pushword(char *w); + +/* + * tree.c + */ + +Tree *newtree(void); +void freetree(Tree *t); +Tree *tree3(int type, Tree *c0, Tree *c1, Tree *c2); +Tree *tree2(int type, Tree *c0, Tree *c1); +Tree *tree1(int type, Tree *c0); + +Tree *hang1(Tree *p, Tree *c0); +Tree *hang2(Tree *p, Tree *c0, Tree *c1); +Tree *hang3(Tree *p, Tree *c0, Tree *c1, Tree *c2); +Tree *epihang(Tree *c, Tree *epi); +Tree *simplehang(Tree *t); +Tree *wordnode(char *w); + +/* + * var.c + */ + +Var *newvar(char *name, Var *link); +Var *gvlookup(char *name); +Var *vlookup(char *name); + +int kwlookup(char *name); +void initkw(void); + +/* + * lex.c + */ + +void skipnl(void); +int wordchr(char c); + +void rcerror(char *msg, ...); +int lex(Tree **node); + +/* + * parse.c + */ + +int parse(void); + +/* + * main.c + */ + +void dotrap(void); diff --git a/sys/cmd/rc/rules.mk b/sys/cmd/rc/rules.mk new file mode 100644 index 0000000..33d6ba8 --- /dev/null +++ b/sys/cmd/rc/rules.mk @@ -0,0 +1,21 @@ +include share/push.mk + +# Local sources +SRCS_$(d) := \ + $(d)/io.c \ + $(d)/util.c \ + $(d)/var.c \ + $(d)/word.c \ + $(d)/tree.c \ + $(d)/lex.c \ + $(d)/parse.c \ + $(d)/main.c +BINS_$(d) := $(d)/rc + +include share/paths.mk + +# Local rules +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libn/libn.a + $(COMPLINK) + +include share/pop.mk diff --git a/sys/cmd/rc/simple.c b/sys/cmd/rc/simple.c new file mode 100644 index 0000000..f934aa1 --- /dev/null +++ b/sys/cmd/rc/simple.c @@ -0,0 +1,13 @@ +void +Xsimple(void) +{ + Word *a; + Var *v; + + a = shell->stack->words; + if (!a) { + Xerror("empty argument list"); + return; + } + v = vlookup(a->word); +} diff --git a/sys/cmd/rc/tree.c b/sys/cmd/rc/tree.c new file mode 100644 index 0000000..8dca67f --- /dev/null +++ b/sys/cmd/rc/tree.c @@ -0,0 +1,144 @@ +#include "rc.h" + +// ----------------------------------------------------------------------- +// globals + +static Tree *nodes; + +// ----------------------------------------------------------------------- +// exported funcs + +Tree* +newtree(void) +{ + Tree *t; + + alloc(t); + t->str = nil; + t->child[0] = t->child[1] = t->child[2] = nil; + t->redir.fd[0] = t->redir.fd[1] = t->redir.type = 0; + + t->link = nodes, nodes = t; + return t; +} + +void +freetree(Tree *t) +{ + if (!t) + return; + + freetree(t->child[0]); + freetree(t->child[1]); + freetree(t->child[2]); + + if (t->str) + efree(t->str); + efree(t); +} + +void +freenodes(void) +{ + Tree *t, *u; + + for (t = nodes;t;t = u) { + u = t->link; + if (t->str) + efree(t->str); + efree(t); + } + nodes = nil; +} + +/* tree creation */ +Tree* +tree3(int type, Tree *c0, Tree *c1, Tree *c2) +{ + Tree *t; + + t = newtree(); + t->type = type; + t->child[0] = c0; + t->child[1] = c1; + t->child[2] = c2; + + return t; +} + +Tree* +tree2(int type, Tree *c0, Tree *c1) +{ + return tree3(type, c0, c1, nil); +} + +Tree* +tree1(int type, Tree *c0) +{ + return tree3(type, c0, nil, nil); +} + +/* tree hang */ +Tree* +hang1(Tree *p, Tree *c0) +{ + p->child[0] = c0; + return p; +} + +Tree* +hang2(Tree *p, Tree *c0, Tree *c1) +{ + p->child[0] = c0; + p->child[1] = c1; + return p; +} + +Tree* +hang3(Tree *p, Tree *c0, Tree *c1, Tree *c2) +{ + p->child[0] = c0; + p->child[1] = c1; + p->child[2] = c2; + return p; +} + +/* hangs the cmd underneath the epilogue */ +Tree* +epihang(Tree *c, Tree *epi) +{ + Tree *p; + if(!epi) + return c; + for(p=epi;p->child[1];p = p->child[1]) + ; + p->child[1] = c; + return epi; +} + +/* hangs tree t from a new simple node. percolates redirections to root */ +Tree* +simplehang(Tree *t) +{ + Tree *u; + t = tree1(Asimple, t); + for(u = t->child[0];u->type==Aargs;u=u->child[0]) { + if (u->child[1]->type==Adup + || u->child[1]->type==Aredir){ + u->child[1]->child[1] = t; + t = u->child[1]; + u->child[1] = nil; + } + } + return t; +} + +Tree* +wordnode(char *w) +{ + Tree *t = newtree(); + t->type = Aword; + t->str = strdup(w); + + return t; +} diff --git a/sys/cmd/rc/util.c b/sys/cmd/rc/util.c new file mode 100644 index 0000000..02b3611 --- /dev/null +++ b/sys/cmd/rc/util.c @@ -0,0 +1,40 @@ +#include "rc.h" + +void * +emalloc(uintptr n) +{ + void *p = malloc(n); + if (!p) + panic("can't malloc %d bytes", n); + + return p; +} + +void * +erealloc(void *p, uintptr n) +{ + void *new = realloc(p, n); + if (!new) + panic("can't realloc %d bytes", n); + + return new; +} + +void +efree(void *p) +{ + if (p) + free(p); + else + pfmt(errio, "free \n"); +} + +void +panic(char *s, int n) +{ + pfmt(errio, "rc: "); + pfmt(errio, s, n); + pchr(errio, '\n'); + flush(&errio); + abort(); +} diff --git a/sys/cmd/rc/var.c b/sys/cmd/rc/var.c new file mode 100644 index 0000000..dbe7c14 --- /dev/null +++ b/sys/cmd/rc/var.c @@ -0,0 +1,108 @@ +#include "rc.h" + +Var *globals[1021] = { 0 }; + +struct Keyword { + ushort type; + char *name; + struct Keyword *link; +} *keywords[41]; + +// ----------------------------------------------------------------------- +// utility + +static +int +hash(char *s, int n) +{ + int i = 1, h = 0; + while (*s) + h += *s++*i++; + h %= n; + return (h<0)?h+n:h; +} + +// ----------------------------------------------------------------------- +// keywords + +static +void +putkw(int type, char *name) +{ + struct Keyword *kw; + int h = hash(name, arrlen(keywords)); + + alloc(kw); + kw->type = type; + kw->name = name; + kw->link = keywords[h]; + + keywords[h] = kw; +} + +void +initkw(void) +{ + putkw(Kfor, "for"); + putkw(Kin, "in"); + putkw(Kwhile, "while"); + putkw(Kif, "if"); + putkw(Kelse, "else"); + putkw(Kswitch, "switch"); + putkw(Kcase, "case"); + putkw(Kfunc, "func"); +} + +int +kwlookup(char *name) +{ + int t; + struct Keyword *it; + for(t=-1,it = keywords[hash(name, arrlen(keywords))];it;it = it->link) + if(!strcmp(it->name, name)) + t = it->type; + return t; +} + +// ----------------------------------------------------------------------- +// variables + +Var * +newvar(char *name, Var *link) +{ + Var *v; + + alloc(v); + v->name = name; + v->val = 0; + v->func = nil; + v->link = link; + + return v; +} + +/* only global lookup */ + +Var * +gvlookup(char *name) +{ + Var *v; + int h = hash(name, arrlen(globals)); + for (v = globals[h]; v; v = v->link) + if (!strcmp(v->name, name)) + return v; + + return globals[h] = newvar(strdup(name), globals[h]); +} + +/* local + global lookup */ +Var * +vlookup(char *name) +{ + Var *v; + if (shell) + for (v = shell->local; v; v = v->link) + if (!strcmp(v->name, name)) + return v; + return gvlookup(name); +} diff --git a/sys/cmd/rc/word.c b/sys/cmd/rc/word.c new file mode 100644 index 0000000..84ff40c --- /dev/null +++ b/sys/cmd/rc/word.c @@ -0,0 +1,64 @@ +#include "rc.h" + +void +pushlist(void) +{ + List *ls; + + alloc(ls); + ls->words = nil; + ls->link = shell->stack, shell->stack = ls; +} + +void +freelist(Word *w) +{ + Word *it; + while (w) { + it = w->link; + efree(w->word); + efree(w); + w = it; + } +} + +void +poplist(void) +{ + List *ls = shell->stack; + if (!ls) + panicf("shell stack underflow"); + + freelist(ls->words); + shell->stack = ls->link; + efree(ls); +} + +int +count(Word *w) +{ + int n; + for (n=0; w; n++) + w = w->link; + return n; +} + +Word* +newword(char *w, Word *link) +{ + Word *wd; + + alloc(wd); + wd->word = strdup(w); + wd->link = link; + + return wd; +} + +void +pushword(char *w) +{ + if (shell->stack == nil) + panicf("no active stack"); + shell->stack->words = newword(w, shell->stack->words); +} diff --git a/sys/cmd/rules.mk b/sys/cmd/rules.mk index 2364189..8e83e57 100644 --- a/sys/cmd/rules.mk +++ b/sys/cmd/rules.mk @@ -8,22 +8,43 @@ include $(DIR)/rules.mk # DIR := $(d)/cc # include $(DIR)/rules.mk +# DIR := $(d)/cp +# include $(DIR)/rules.mk + +# DIR := $(d)/dvtm +# include $(DIR)/rules.mk + +DIR := $(d)/dwm +include $(DIR)/rules.mk + +DIR := $(d)/echo +include $(DIR)/rules.mk + # DIR := $(d)/edo # include $(DIR)/rules.mk -# DIR := $(d)/rc +DIR := $(d)/filter +include $(DIR)/rules.mk + +DIR := $(d)/ic +include $(DIR)/rules.mk + +# DIR := $(d)/ls # include $(DIR)/rules.mk DIR := $(d)/menu include $(DIR)/rules.mk -DIR := $(d)/dwm +DIR := $(d)/mv +include $(DIR)/rules.mk + +DIR := $(d)/rc include $(DIR)/rules.mk DIR := $(d)/term include $(DIR)/rules.mk -DIR := $(d)/dvtm +DIR := $(d)/walk include $(DIR)/rules.mk include share/pop.mk diff --git a/sys/cmd/term/term.c b/sys/cmd/term/term.c index 6a27a10..f7687b9 100644 --- a/sys/cmd/term/term.c +++ b/sys/cmd/term/term.c @@ -1111,13 +1111,12 @@ tdefcolor(int *attr, int *npar, int l) break; case 5: /* indexed color */ if (*npar + 2 >= l) { - fprintf(stderr, - "erresc(38): Incorrect number of parameters (%d)\n", *npar); + fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n", *npar); break; } *npar += 2; if (!BETWEEN(attr[*npar], 0, 255)) - fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + fprintf(stderr, "erresc: bad color %d\n", attr[*npar]); else idx = attr[*npar]; break; diff --git a/sys/cmd/term/term.info b/sys/cmd/term/term.info index 2f40292..7b90344 100644 --- a/sys/cmd/term/term.info +++ b/sys/cmd/term/term.info @@ -1,4 +1,4 @@ -st-mono| simpleterm monocolor, +term+mono| simpleterm monocolor, acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, am, bce, @@ -193,8 +193,8 @@ st-mono| simpleterm monocolor, Ss=\E[%p1%d q, term| simpleterm, - use=st-mono, - colors#8, + use=term+mono, + colors#8, pairs#64, setab=\E[4%p1%dm, setaf=\E[3%p1%dm, setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, @@ -202,38 +202,49 @@ term| simpleterm, sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, term-256color| simpleterm with 256 colors, - use=st, + use=term, ccc, - colors#256, + colors#256, pairs#32767, oc=\E]104\007, - pairs#32767, # Nicked from xterm-256color initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, +term-direct| simpleterm with true color, + use=term, + RGB, +# Nicked from xterm-direct + colors#0x1000000, pairs#0x7FFFF, + initc@, op=\E[39;49m, + setab=\E[%?%p1%{8}%<%t4%p1%d%e48;2;%p1%{65536}%/%d;%p1%{256} + %/%{255}%&%d;%p1%{255}%&%d%;m, + setaf=\E[%?%p1%{8}%<%t3%p1%d%e38;2;%p1%{65536}%/%d;%p1%{256} + %/%{255}%&%d;%p1%{255}%&%d%;m, + setb@, setf@, + term-meta| simpleterm with meta key, - use=st, + use=term, km, rmm=\E[?1034l, smm=\E[?1034h, rs2=\E[4l\E>\E[?1034h, is2=\E[4l\E>\E[?1034h, -st-meta-256color| simpleterm with meta key and 256 colors, - use=st-256color, +term-meta-256color| simpleterm with meta key and 256 colors, + use=term-256color, km, rmm=\E[?1034l, smm=\E[?1034h, rs2=\E[4l\E>\E[?1034h, is2=\E[4l\E>\E[?1034h, -st-bs| simpleterm with backspace as backspace, - use=st, +term-bs| simpleterm with backspace as backspace, + use=term, kbs=\010, kdch1=\177, -st-bs-256color| simpleterm with backspace as backspace and 256colors, - use=st-256color, +term-bs-256color| simpleterm with backspace as backspace and 256colors, + use=term-256color, kbs=\010, kdch1=\177, diff --git a/sys/libn/error.c b/sys/libn/error.c index 95a534a..a9d684c 100644 --- a/sys/libn/error.c +++ b/sys/libn/error.c @@ -1,6 +1,16 @@ #include #include +void +exits(char *s) +{ + if (s == nil || *s == 0) + exit(0); + + fputs(s, stderr); + exit(1); +} + void errorf(byte* fmt, ...) { diff --git a/sys/libn/memory.c b/sys/libn/memory.c index 31f910e..999819b 100644 --- a/sys/libn/memory.c +++ b/sys/libn/memory.c @@ -194,8 +194,3 @@ memset64(void *dst, uint64 val, uintptr size) ((byte*)dst)[i] = ((byte*)&val)[i&7]; } } - -// ------------------------------------------------------------------------- -// First argument - -char *argv0; diff --git a/sys/libn/rules.mk b/sys/libn/rules.mk index 61a56fe..abcd60d 100644 --- a/sys/libn/rules.mk +++ b/sys/libn/rules.mk @@ -6,11 +6,13 @@ include share/push.mk # Local sources SRCS_$(d) := \ + $(d)/arg.c \ $(d)/bufio.c \ $(d)/coro_unix_x64.s \ $(d)/coro.c \ $(d)/error.c \ $(d)/flate.c \ + $(d)/fs.c \ $(d)/gz.c \ $(d)/io.c \ $(d)/os.c \ diff --git a/sys/libterm/buffer.c b/sys/libterm/buffer.c deleted file mode 100644 index b903e71..0000000 --- a/sys/libterm/buffer.c +++ /dev/null @@ -1,326 +0,0 @@ -#include "term.h" - -/* row operations */ -void -zero(Row *row, int start, int len) -{ - int i; - Cell cell = { - .r = L'\0', - .pen = {0}, - }; - - for (i = start; i < len + start; i++) - row->cells[i] = cell; - row->dirty = true; -} - -void -roll(Row *start, Row *end, int count) -{ - int n = end - start; - - count %= n; - if (count < 0) - count += n; - - if (count) { - char buf[count * sizeof(Row)]; - memcpy(buf, start, count * sizeof(Row)); - memmove(start, start + count, (n - count) * sizeof(Row)); - memcpy(end - count, buf, count * sizeof(Row)); - for (Row *row = start; row < end; row++) - row->dirty = true; - } -} - -/* buffer operations */ -void -bclear(Buffer *b) -{ - int i; - Cell cell = { - .r = L'\0', - .pen = { - .state = PenNormal, - .col.fg = -1, - .col.bg = -1, - }, - }; - - for (i = 0; i < b->nrow; i++) { - Row *row = b->row + i; - for (int j = 0; j < b->ncol; j++) { - row->cells[j] = cell; - row->dirty = true; - } - } -} - -void -bfree(Buffer *b) -{ - int i; - - for (i = 0; i < b->nrow; i++) - free(b->row[i].cells); - - free(b->row); - - for (i = 0; i < b->scroll.size; i++) - free(b->scroll.buf[i].cells); - - free(b->scroll.buf); - free(b->tabs); -} - -void -bscroll(Buffer *b, int s) -{ - /* work in screenfuls */ - int ssz = b->scroll.bot - b->scroll.top; - - if (s > ssz) { - bscroll(b, ssz); - bscroll(b, s - ssz); - return; - } - if (s < -ssz) { - bscroll(b, -ssz); - bscroll(b, s + ssz); - return; - } - - b->scroll.above += s; - if (b->scroll.above >= b->scroll.size) - b->scroll.above = b->scroll.size; - - if (s > 0 && b->scroll.size) { - for (int i = 0; i < s; i++) { - Row tmp = b->scroll.top[i]; - b->scroll.top[i] = b->scroll.buf[b->scroll.index]; - b->scroll.buf[b->scroll.index] = tmp; - - b->scroll.index++; - if (b->scroll.index == b->scroll.size) - b->scroll.index = 0; - } - } - roll(b->scroll.top, b->scroll.bot, s); - if (s < 0 && b->scroll.size) { - for (int i = (-s) - 1; i >= 0; i--) { - b->scroll.index--; - if (b->scroll.index == -1) - b->scroll.index = b->scroll.size - 1; - - Row tmp = b->scroll.top[i]; - b->scroll.top[i] = b->scroll.buf[b->scroll.index]; - b->scroll.buf[b->scroll.index] = tmp; - b->scroll.top[i].dirty = true; - } - } -} - -void -bresize(Buffer *b, int nrow, int ncol) -{ - Row *row = b->row; - - if (b->nrow != nrow) { - if (b->crow >= row + nrow) { - /* scroll up instead of simply chopping off bottom */ - bscroll(b, (b->crow - b->row) - nrow + 1); - } - while (b->nrow > nrow) { - free(row[b->nrow - 1].cells); - b->nrow--; - } - - row = realloc(row, sizeof(Row) * nrow); - } - - if (b->maxcols < ncol) { - for (int r = 0; r < b->nrow; r++) { - row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol); - if (b->ncol < ncol) - zero(row + r, b->ncol, ncol - b->ncol); - row[r].dirty = true; - } - Row *sbuf = b->scroll.buf; - for (int r = 0; r < b->scroll.size; r++) { - sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol); - if (b->ncol < ncol) - zero(sbuf + r, b->ncol, ncol - b->ncol); - } - b->tabs = realloc(b->tabs, sizeof(*b->tabs) * ncol); - for (int c = b->ncol; c < ncol; c++) - b->tabs[c] = !(c & 7); - b->maxcols = ncol; - b->ncol = ncol; - } else if (b->ncol != ncol) { - for (int r = 0; r < b->nrow; r++) - row[r].dirty = true; - b->ncol = ncol; - } - - int deltarows = 0; - if (b->nrow < nrow) { - while (b->nrow < nrow) { - row[b->nrow].cells = calloc(b->maxcols, sizeof(Cell)); - zero(row + b->nrow, 0, b->maxcols); - b->nrow++; - } - - /* prepare for backfill */ - if (b->crow >= b->scroll.bot - 1) { - deltarows = b->row + nrow - b->crow - 1; - if (deltarows > b->scroll.above) - deltarows = b->scroll.above; - } - } - - b->crow += row - b->row; - b->scroll.top = row; - b->scroll.bot = row + nrow; - b->row = row; - - /* perform backfill */ - if (deltarows > 0) { - bscroll(b, -deltarows); - b->crow += deltarows; - } -} - -bool -binit(Buffer *b, int rows, int cols, int size) -{ - b->pen.state = PenNormal; - b->pen.col.fg = b->pen.col.fg = -1; - - if (size < 0) - size = 0; - if (size && !(b->scroll.buf = calloc(size, sizeof(Row)))) - return false; - - b->scroll.size = size; - bresize(b, rows, cols); - return true; -} - -void -bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) -{ - if (bs) - *bs = nil; - if (be) - *be = nil; - if (as) - *as = nil; - if (ae) - *ae = nil; - if (!b->scroll.size) - return; - - if (b->scroll.above) { - if (bs) - *bs = &b->scroll.buf[(b->scroll.index - b->scroll.above + b->scroll.size) % b->scroll.size]; - if (be) - *be = &b->scroll.buf[(b->scroll.index-1 + b->scroll.size) % b->scroll.size]; - } - if (b->scroll.below) { - if (as) - *as = &b->scroll.buf[b->scroll.index]; - if (ae) - *ae = &b->scroll.buf[(b->scroll.index + b->scroll.below-1) % b->scroll.size]; - } -} - -Row * -browfirst(Buffer *b) -{ - Row *bstart; - if (!b->scroll.size || !b->scroll.above) - return b->row; - bboundary(b, &bstart, nil, nil, nil); - return bstart; -} - -Row * -browlast(Buffer *b) -{ - Row *aend; - if (!b->scroll.size || !b->scroll.below) - return b->row + b->nrow - 1; - bboundary(b, nil, nil, nil, &aend); - return aend; -} - -Row * -brownext(Buffer *b, Row *row) -{ - Row *before_start, *before_end, *after_start, *after_end; - Row *first = b->row, *last = b->row + b->nrow - 1; - - if (!row) - return nil; - - bboundary(b, &before_start, &before_end, &after_start, &after_end); - - if (row >= first && row < last) - return ++row; - if (row == last) - return after_start; - if (row == before_end) - return first; - if (row == after_end) - return nil; - if (row == &b->scroll.buf[b->scroll.size - 1]) - return b->scroll.buf; - return ++row; -} - -Row * -bprevrow(Buffer *b, Row *row) -{ - Row *before_start, *before_end, *after_start, *after_end; - Row *first = b->row, *last = b->row + b->nrow - 1; - - if (!row) - return nil; - - bboundary(b, &before_start, &before_end, &after_start, &after_end); - - if (row > first && row <= last) - return --row; - if (row == first) - return before_end; - if (row == before_start) - return nil; - if (row == after_start) - return last; - if (row == b->scroll.buf) - return &b->scroll.buf[b->scroll.size - 1]; - return --row; -} - -void -brender(Buffer *b, Term *t) -{ - int r, c, n; - char u[UTFmax+1]; - Row *row; - Cell *cell; - - for (r = 0; r < b->nrow; r++) { - row = b->row + r; - if (!row->dirty) - continue; - - for (c = 0; c < b->ncol; c++) { - cell = row->cells + c; - tsetpen(t, cell->pen); - n = utf8·runetobyte(u, &cell->r); - twrite(t, n, u); - } - } -} diff --git a/sys/libterm/escseq.c b/sys/libterm/escseq.c deleted file mode 100644 index e69de29..0000000 diff --git a/sys/libterm/events.c b/sys/libterm/events.c deleted file mode 100644 index 80bc99a..0000000 --- a/sys/libterm/events.c +++ /dev/null @@ -1,1692 +0,0 @@ -#include "term.h" - -#include - -#define bufcount(in) in->buf.c - in->buf.b - -enum { - NodeKey, - NodeArr, -}; - -struct Node -{ - int type; -}; - -struct KeyNode -{ - struct Node; - struct KeyInfo key; -}; - -struct ArrNode -{ - struct Node; - uchar min, max; - Node *arr[]; -}; - -// ----------------------------------------------------------------------- -// loads data into trie - -static enum KeyEvent peekmousekey(Input *in, Key *key, ulong *nb); - -#define FuncNameMax 10 -static struct { - char *name; - int type; - int sym; - int mods; -} funcs[] = -{ - /* THIS LIST MUST REMAIN SORTED ALPHABETICALLY! */ - { "backspace", KeySym, SymBackspace, 0 }, - { "begin", KeySym, SymBegin, 0 }, - { "beg", KeySym, SymBegin, 0 }, - { "btab", KeySym, SymTab, ModShift}, - { "cancel", KeySym, SymCancel, 0 }, - { "clear", KeySym, SymClear, 0 }, - { "close", KeySym, SymClose, 0 }, - { "command", KeySym, SymCommand, 0 }, - { "copy", KeySym, SymCopy, 0 }, - { "dc", KeySym, SymDelete, 0 }, - { "down", KeySym, SymDown, 0 }, - { "end", KeySym, SymEnd, 0 }, - { "enter", KeySym, SymEnter, 0 }, - { "exit", KeySym, SymExit, 0 }, - { "find", KeySym, SymFind, 0 }, - { "help", KeySym, SymHelp, 0 }, - { "home", KeySym, SymHome, 0 }, - { "ic", KeySym, SymInsert, 0 }, - { "left", KeySym, SymLeft, 0 }, - { "mark", KeySym, SymMark, 0 }, - { "message", KeySym, SymMessage, 0 }, - { "move", KeySym, SymMove, 0 }, - { "next", KeySym, SymPagedown, 0 }, // Not quite, but it's the best we can do - { "npage", KeySym, SymPagedown, 0 }, - { "open", KeySym, SymOpen, 0 }, - { "options", KeySym, SymOptions, 0 }, - { "ppage", KeySym, SymPageup, 0 }, - { "previous", KeySym, SymPageup, 0 }, // Not quite, but it's the best we can do - { "print", KeySym, SymPrint, 0 }, - { "redo", KeySym, SymRedo, 0 }, - { "reference", KeySym, SymReference, 0 }, - { "refresh", KeySym, SymRefresh, 0 }, - { "replace", KeySym, SymReplace, 0 }, - { "restart", KeySym, SymRestart, 0 }, - { "resume", KeySym, SymResume, 0 }, - { "right", KeySym, SymRight, 0 }, - { "save", KeySym, SymSave, 0 }, - { "select", KeySym, SymSelect, 0 }, - { "suspend", KeySym, SymSuspend, 0 }, - { "undo", KeySym, SymUndo, 0 }, - { "up", KeySym, SymUp, 0 }, - { nil }, -}; - -// ----------------------------------------------------------------------- -// utility functions - -static -int -stricmp(char *s, char *t) -{ - if (s == nil) - if (t == nil) - return 0; - else - return -strlen(t); - else - if (t == nil) - return +strlen(s); - - int d; - for (; *s && *t; s++, t++) { - d = tolower(*s) - tolower(*t); - if (d < 0 || d > 0) - return d; - } - - /* XXX: not sure about passing in 0 here */ - return tolower(*s) - tolower(*t); -} - -// ----------------------------------------------------------------------- -// bytes -> keysymbols - -static -Node * -keynode(int type, int sym, int mask, int set) -{ - struct KeyNode *n; - n = malloc(sizeof(*n)); - if (!n) - panicf("out of memory"); - - n->type = NodeKey; - n->key.type = type; - n->key.sym = sym; - n->key.modmask = mask; - n->key.modset = set; - - return (Node *)n; -} - -static -Node * -arrnode(uchar min, uchar max) -{ - int nb, i; - struct ArrNode *n; - nb = ((int)max-min+1)*sizeof(*n->arr); - n = malloc(sizeof(*n) + nb); - if (!n) - panicf("out of memory"); - - n->type = NodeArr; - n->min = min, n->max = max; - for (i = 0; i <= max-min; i++) - n->arr[i] = nil; - - return (Node *)n; -} - - -static -Node * -nlookup(Node *n, uchar b) -{ - struct ArrNode *arr; - switch (n->type) { - case NodeKey: - panicf("attempting to subdivide a leaf key node"); - return nil; - case NodeArr: - arr = (struct ArrNode *)n; - if (b < arr->min || b > arr->max) - return nil; - return arr->arr[b - arr->min]; - default: - panicf("unrecognized key node type"); - return nil; - } -} - -static -Node * -compress(Node *root) -{ - int i; - uchar min, max; - struct ArrNode *n, *new; - - if (!root) - return root; - - switch (root->type) { - case NodeKey: - return root; - case NodeArr: - n = (struct ArrNode *)root; - /* find the zeros */ - for (min = 0; !n->arr[min]; min++) { - if (min == 255 && !n->arr[min]) { - free(n); - return arrnode(1, 0); - } - } - - for (max = 0xff; !n->arr[max]; max--) - ; - - new = (struct ArrNode *)arrnode(min, max); - - for (i = min; i <= max; i++) - new->arr[i-min] = compress(n->arr[i]); - - free(n); - return (Node*)new; - default: - panicf("unrecognized key node type"); - return nil; - } - - return root; -} - -static -void -teardown(Node *n) -{ - int i; - struct ArrNode *arr; - - switch (n->type) { - case NodeKey: - break; - case NodeArr: - arr = (struct ArrNode *)n; - for (i = arr->min; i <= arr->max; i++) - if (arr->arr[i - arr->min]) - teardown(arr->arr[i - arr->min]); - break; - default: - return; - } - - free(n); -} - -static -int -insert(Node *root, Node *n, char *seq) -{ - int pos; - uchar b; - Node *nn; - struct ArrNode *a; - - for (pos = 0; (b = seq[pos]); pos++) { - nn = nlookup(root, b); - if (!nn) - break; - root = nn; - a = (struct ArrNode *)root; - } - - for (; (b = seq[pos]); pos++) { - nn = (seq[pos+1]) ? arrnode(0, 0xff) : n; - if (!nn) - return 0; - - if (root->type != NodeArr) - panicf("inserted child node onto leaf 'key' node"); - - a = (struct ArrNode*)root; - if (b < a->min || b > a->max) - panicf("out of bound trie insertion"); - - a->arr[b-a->min] = root = nn; - } - - return 1; -} - -/* unibilium helpers */ -static -enum unibi_string -nametostr(char *name) -{ - enum unibi_string ret; - for (ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++) - if (!strcmp(unibi_name_str(ret), name)) - return ret; - - return -1; -} - -static -char * -unibi_lookup(unibi_term *info, char *name) -{ - enum unibi_string idx; - if ((idx = nametostr(name)) == -1) - return nil; - - return (char*)unibi_get_str(info, idx); -} - -static -Node * -tryloadkey(unibi_term *info, char *name, char **val, struct KeyInfo *key) -{ - char *v; - - v = unibi_lookup(info, name); - if (!v || v[0] == 0) - return nil; - - *val = v; - return keynode(key->type, key->sym, key->modmask, key->modset); -} - -static -Node * -loadtermkeys(unibi_term *info) -{ - int i; - struct Node *root, *n; - char *val, name[5 + FuncNameMax + 1]; - - root = arrnode(0, 0xff); - if (!root) - panicf("out of memory"); - - /* load key syms */ - for (i = 0; funcs[i].name; i++) { - sprintf(name, "key_%s", funcs[i].name); - n = tryloadkey(info, name, &val, &(struct KeyInfo){ - .type = funcs[i].type, - .sym = funcs[i].sym, - .modmask = funcs[i].mods, - .modset = funcs[i].mods, - }); - - /* try shift modified */ - if (!n) - n = tryloadkey(info, name, &val, &(struct KeyInfo){ - .type = funcs[i].type, - .sym = funcs[i].sym, - .modmask = funcs[i].mods | ModShift, - .modset = funcs[i].mods | ModShift, - }); - - /* give up */ - if (n) - insert(root, n, val); - } - - /* load function keys */ - for (i = 1; i < 0xff; i++) { - sprintf(name, "key_f%d", i); - n = tryloadkey(info, name, &val, &(struct KeyInfo){ - .type = KeyFunc, - .sym = i, - .modmask = 0, - .modset = 0, - }); - if (!n) - break; - - insert(root, n, val); - } - - /* load mouse keys */ - val = unibi_lookup(info, "key_mouse"); - if (val && !strcmp(val, "\x1b[M")) { - n = keynode(KeyMouse, 0, 0, 0); - insert(root, n, val); - } - - return compress(root); -} - -static -enum KeyEvent -peeksym(Input *in, Key *key, int force, ulong *nb) -{ - int res; - uchar *b, *o; - Node *root; - struct KeyNode *kn; - - root = in->keys; - b = in->buf.b; - while (b <= in->buf.c) { - printf("checking '%s'\n", b); - root = nlookup(root, *b); - if (!root) - break; - b++; - - if (root->type != NodeKey) - continue; - - kn = (struct KeyNode*)root; - if (kn->key.type == KeyMouse) { - o = in->buf.b, in->buf.b = b; - res = peekmousekey(in, key, nb); - in->buf.b = o; - - if (res == EvKey) - *nb += b - in->buf.b; - - return res; - } - key->type = kn->key.type; - key->mods = kn->key.modset; - key->code.sym = kn->key.type; - *nb = b - in->buf.b; - - return EvKey; - } - - if (root && !force) - return EvAgain; - - return EvNil; -} - -// ----------------------------------------------------------------------- -// misc commands - -static -inline -void -setpos(Key *key, int row, int col) -{ - row = MIN(row, 0xfff); - col = MIN(col, 0xfff); - - key->code.mouse[1] = (row & 0x0ff); - key->code.mouse[2] = (col & 0x0ff); - key->code.mouse[3] = (row & 0xf00) >> 8 | (col & 0x300) >> 4; -} - -static -inline -void -getpos(Key *key, int *row, int *col) -{ - if (col) - *col = ((uchar)key->code.mouse[1] | ((uchar)key->code.mouse[3] & 0x0f) << 8) - 1; - - if (row) - *row = ((uchar)key->code.mouse[2] | ((uchar)key->code.mouse[3] & 0x70) << 4) - 1; -} - -// ----------------------------------------------------------------------- -// csi/ss3 commands - -static int csiinit; - -static struct KeyInfo ss3[64]; -static char ss3kpalts[64]; - -static struct KeyInfo csiss3[64]; -static enum KeyEvent (*do_csi[64])(Input *in, Key *key, int cmd, int narg, long *arg); - -static struct KeyInfo csifuncs[35]; - -/* csi/ss3 cmd keys */ -static -enum KeyEvent -do_csi_full(Input *in, Key *key, int cmd, int narg, long *arg) -{ - if(narg > 1 && arg[1] != -1) - key->mods = arg[1] - 1; - else - key->mods = 0; - - key->type = csiss3[cmd - 0x40].type; - key->code.sym = csiss3[cmd - 0x40].sym; - key->mods &= ~(csiss3[cmd - 0x40].modmask); - key->mods |= csiss3[cmd - 0x40].modset; - - if(key->code.sym == SymUnknown) - return EvNil; - - return EvKey; -} - -static -void -put_csi_full(int type, int sym, int modset, int modmask, uchar cmd) -{ - if(cmd < 0x40 || cmd >= 0x80) - return; - - csiss3[cmd - 0x40].type = type; - csiss3[cmd - 0x40].sym = sym; - csiss3[cmd - 0x40].modset = modset; - csiss3[cmd - 0x40].modmask = modmask; - - do_csi[cmd - 0x40] = &do_csi_full; -} - -/* ss3 kpad keys */ -static -void -put_ss3_kpalt(int type, int sym, uchar cmd, char kpalt) -{ - if(cmd < 0x40 || cmd >= 0x80) - return; - - ss3[cmd - 0x40].type = type; - ss3[cmd - 0x40].sym = sym; - ss3[cmd - 0x40].modset = 0; - ss3[cmd - 0x40].modmask= 0; - ss3kpalts[cmd - 0x40] = kpalt; -} - -/* csi number ~func keys */ -static void emitcodepoint(Input *in, rune r, Key *key); - -static -enum KeyEvent -do_csi_func(Input *in, Key *key, int cmd, int narg, long *arg) -{ - if (narg > 1 && arg[1] != -1) - key->mods = arg[1] - 1; - else - key->mods = 0; - - key->type = KeySym; - - if (arg[0] == 27) { - int mod = key->mods; - emitcodepoint(in, (rune)arg[2], key); - key->mods |= mod; - } else if (arg[0] >= 0 && arg[0] < arrlen(csifuncs)) { - key->type = csifuncs[arg[0]].type; - key->code.sym = csifuncs[arg[0]].sym; - key->mods &= ~(csifuncs[arg[0]].modmask); - key->mods |= csifuncs[arg[0]].modset; - } else - key->code.sym = SymUnknown; - - if (key->code.sym == SymUnknown) - return EvNil; - - return EvKey; -} - -static -void -put_csi_func(int type, int sym, int num) -{ - if(num >= arrlen(csifuncs)) - return; - - csifuncs[num].type = type; - csifuncs[num].sym = sym; - csifuncs[num].modset = 0; - csifuncs[num].modmask = 0; - - do_csi['~' - 0x40] = &do_csi_func; -} - -/* CSI u extended unicode keys */ -static -enum KeyEvent -do_csi_u(Input *in, Key *key, int cmd, int narg, long *arg) -{ - switch(cmd) { - case 'u': { - if(narg > 1 && arg[1] != -1) - key->mods = arg[1] - 1; - else - key->mods = 0; - - int mod = key->mods; - key->type = KeySym; - emitcodepoint(in, arg[0], key); - key->mods |= mod; - - return EvKey; - } - default: - return EvNil; - } -} - -/* csi m/M mouse events */ - -static -enum KeyEvent -do_csi_m(Input *in, Key *key, int cmd, int narg, long *arg) -{ - int initial = cmd >> 8; - cmd &= 0xff; - - switch(cmd) { - case 'M': - case 'm': - break; - default: - return EvNil; - } - - // rxvt protocol - if(!initial && narg >= 3) { - key->type = KeyMouse; - key->code.mouse[0] = arg[0]; - - key->mods = (key->code.mouse[0] & 0x1c) >> 2; - key->code.mouse[0] &= ~0x1c; - - setpos(key, arg[1], arg[2]); - - return EvKey; - } - - // SGR protocol - if(initial == '<' && narg >= 3) { - key->type = KeyMouse; - key->code.mouse[0] = arg[0]; - - key->mods = (key->code.mouse[0] & 0x1c) >> 2; - key->code.mouse[0] &= ~0x1c; - - setpos(key, arg[1], arg[2]); - - if(cmd == 'm') // release - key->code.mouse[3] |= 0x80; - - return EvKey; - } - - return EvNil; -} - -/* csi ? R position events */ - -static -enum KeyEvent -do_csi_R(Input *in, Key *key, int cmd, int narg, long *arg) -{ - switch(cmd) { - case 'R'|'?'<<8: - if(narg < 2) - return EvNil; - key->type = KeyPosition; - setpos(key, arg[1], arg[0]); - return EvKey; - default: - return do_csi_full(in, key, cmd, narg, arg); - } -} - -/* csi $y mode status events */ - -static -enum KeyEvent -do_csi_y(Input *in, Key *key, int cmd, int narg, long *arg) -{ - switch (cmd) { - case 'y'|'$'<<16: - case 'y'|'$'<<16 | '?'<<8: - if (narg < 2) - return EvNil; - - key->type = KeyModeReport; - key->code.mouse[0] = (cmd >> 8); - key->code.mouse[1] = arg[0] >> 8; - key->code.mouse[2] = arg[0] & 0xff; - key->code.mouse[3] = arg[1]; - return EvKey; - - default: - return EvNil; - } -} - -/* parse csi events */ -static -enum KeyEvent -parse_csi(Input *in, ulong introlen, ulong *csi_len, ulong *nargs, long args[], unsigned long *commandp) -{ - ulong csi_end = introlen; - - while (csi_end < bufcount(in)) { - if (in->buf.b[csi_end] >= 0x40 && in->buf.b[csi_end] < 0x80) - break; - csi_end++; - } - - if(csi_end >= bufcount(in)) - return EvAgain; - - uchar cmd = in->buf.b[csi_end]; - *commandp = cmd; - - char present = 0; - int argi = 0; - - ulong p = introlen; - - // See if there is an initial byte - if (in->buf.b[p] >= '<' && in->buf.b[p] <= '?') { - *commandp |= (in->buf.b[p] << 8); - p++; - } - - // Now attempt to parse out up number;number;... separated values - while (p < csi_end) { - uchar c = in->buf.b[p]; - - if (c >= '0' && c <= '9') { - if (!present) { - args[argi] = c - '0'; - present = 1; - } else { - args[argi] = (args[argi] * 10) + c - '0'; - } - } else if(c == ';') { - if (!present) - args[argi] = -1; - present = 0; - argi++; - - if(argi > 16) - break; - } else if (c >= 0x20 && c <= 0x2f) { - *commandp |= c << 16; - break; - } - p++; - } - - if(present) - argi++; - - *nargs = argi; - *csi_len = csi_end + 1; - - return EvKey; -} - -static -void -loadctrlkeys(void) -{ - int i; - for(i = 0; i < 64; i++) { - csiss3[i].sym = SymUnknown; - ss3[i].sym = SymUnknown; - ss3kpalts[i] = 0; - } - - for(i = 0; i < arrlen(csifuncs); i++) - csifuncs[i].sym = SymUnknown; - - put_csi_full(KeySym, SymUp, 0, 0, 'A'); - put_csi_full(KeySym, SymDown, 0, 0, 'B'); - put_csi_full(KeySym, SymRight,0, 0, 'C'); - put_csi_full(KeySym, SymLeft, 0, 0, 'D'); - put_csi_full(KeySym, SymBegin,0, 0, 'E'); - put_csi_full(KeySym, SymEnd, 0, 0, 'F'); - put_csi_full(KeySym, SymHome, 0, 0, 'H'); - put_csi_full(KeySym, 1, 0, 0, 'P'); - put_csi_full(KeySym, 2, 0, 0, 'Q'); - put_csi_full(KeySym, 3, 0, 0, 'R'); - put_csi_full(KeySym, 4, 0, 0, 'S'); - - put_csi_full(KeySym, SymTab, ModShift, ModShift, 'Z'); - - put_ss3_kpalt(KeySym, SymKpenter, 'M', 0); - put_ss3_kpalt(KeySym, SymKpequals, 'X', '='); - put_ss3_kpalt(KeySym, SymKpmult, 'j', '*'); - put_ss3_kpalt(KeySym, SymKpplus, 'k', '+'); - put_ss3_kpalt(KeySym, SymKpcomma, 'l', ','); - put_ss3_kpalt(KeySym, SymKpminus, 'm', '-'); - put_ss3_kpalt(KeySym, SymKpperiod, 'n', '.'); - put_ss3_kpalt(KeySym, SymKpdiv, 'o', '/'); - put_ss3_kpalt(KeySym, SymKp0, 'p', '0'); - put_ss3_kpalt(KeySym, SymKp1, 'q', '1'); - put_ss3_kpalt(KeySym, SymKp2, 'r', '2'); - put_ss3_kpalt(KeySym, SymKp3, 's', '3'); - put_ss3_kpalt(KeySym, SymKp4, 't', '4'); - put_ss3_kpalt(KeySym, SymKp5, 'u', '5'); - put_ss3_kpalt(KeySym, SymKp6, 'v', '6'); - put_ss3_kpalt(KeySym, SymKp7, 'w', '7'); - put_ss3_kpalt(KeySym, SymKp8, 'x', '8'); - put_ss3_kpalt(KeySym, SymKp9, 'y', '9'); - - put_csi_func(KeySym, SymFind, 1); - put_csi_func(KeySym, SymInsert, 2); - put_csi_func(KeySym, SymDelete, 3); - put_csi_func(KeySym, SymSelect, 4); - put_csi_func(KeySym, SymPageup, 5); - put_csi_func(KeySym, SymPagedown, 6); - put_csi_func(KeySym, SymHome, 7); - put_csi_func(KeySym, SymEnd, 8); - - put_csi_func(KeyFunc, 1, 11); - put_csi_func(KeyFunc, 2, 12); - put_csi_func(KeyFunc, 3, 13); - put_csi_func(KeyFunc, 4, 14); - put_csi_func(KeyFunc, 5, 15); - put_csi_func(KeyFunc, 6, 17); - put_csi_func(KeyFunc, 7, 18); - put_csi_func(KeyFunc, 8, 19); - put_csi_func(KeyFunc, 9, 20); - put_csi_func(KeyFunc, 10, 21); - put_csi_func(KeyFunc, 11, 23); - put_csi_func(KeyFunc, 12, 24); - put_csi_func(KeyFunc, 13, 25); - put_csi_func(KeyFunc, 14, 26); - put_csi_func(KeyFunc, 15, 28); - put_csi_func(KeyFunc, 16, 29); - put_csi_func(KeyFunc, 17, 31); - put_csi_func(KeyFunc, 18, 32); - put_csi_func(KeyFunc, 19, 33); - put_csi_func(KeyFunc, 20, 34); - - do_csi['u' - 0x40] = &do_csi_u; - do_csi['M' - 0x40] = &do_csi_m; - do_csi['m' - 0x40] = &do_csi_m; - do_csi['R' - 0x40] = &do_csi_R; - - do_csi['y' - 0x40] = &do_csi_y; - - csiinit = 1; -} - -static -enum KeyEvent -peekcsi(Input *in, ulong introlen, Key *key, int force, ulong *nb) -{ - ulong csi_len; - long arg[16]; - ulong nargs = arrlen(arg); - ulong cmd; - - enum KeyEvent ev = parse_csi(in, introlen, &csi_len, &nargs, arg, &cmd); - - if (ev== EvAgain) { - if(!force) - return ev; - - emitcodepoint(in, '[', key); - key->mods |= ModAlt; - *nb = introlen; - return EvKey; - } - // Mouse in X10 encoding consumes the next 3 bytes also - if (cmd == 'M' && nargs < 3) { - in->buf.b += csi_len; - ev = peekmousekey(in, key, nb); - in->buf.b -= csi_len; - - if (ev == EvKey) - *nb += csi_len; - - return ev; - } - - ev = EvNil; - - // We know from the logic above that cmd must be >= 0x40 and < 0x80 - if (do_csi[(cmd & 0xff) - 0x40]) - ev = (*do_csi[(cmd & 0xff) - 0x40])(in, key, cmd, nargs, arg); - - if (ev == EvNil) { - key->type = KeyUnknownCSI; - key->code.num = cmd; - key->mods = 0; - - in->buf.off = csi_len - introlen; - *nb = introlen; /* dont advance yet */ - return EvKey; - } - - *nb = csi_len; - return ev; -} - -static -enum KeyEvent -peekss3(Input *in, ulong introlen, Key *key, int force, ulong *nb) -{ - if(bufcount(in) < introlen + 1) { - if(!force) - return EvAgain; - - emitcodepoint(in, 'O', key); - key->mods |= ModAlt; - *nb= bufcount(in); - return EvKey; - } - uchar cmd = in->buf.b[introlen]; - - if(cmd < 0x40 || cmd >= 0x80) - return EvNil; - - key->type = csiss3[cmd - 0x40].type; - key->code.sym = csiss3[cmd - 0x40].sym; - key->mods = csiss3[cmd - 0x40].modset; - - if (key->code.sym == SymUnknown) { - if (in->flags & FlagConvertKP && ss3kpalts[cmd - 0x40]) { - key->type = KeyUnicode; - key->code.pt = ss3kpalts[cmd - 0x40]; - key->mods = 0; - - key->utf8[0] = key->code.pt; - key->utf8[1] = 0; - } else { - key->type = ss3[cmd - 0x40].type; - key->code.sym = ss3[cmd - 0x40].sym; - key->mods = ss3[cmd - 0x40].modset; - } - } - - if(key->code.sym == SymUnknown) - return EvNil; - - *nb = introlen + 1; - - return EvKey; -} - -static -enum KeyEvent -peekctrl(Input *in, ulong introlen, Key *key, int force, ulong *nb) -{ - ulong str_end = introlen; - - while(str_end < bufcount(in)) { - if (in->buf.b[str_end] == 0x9c) // ST - break; - if (in->buf.b[str_end] == 0x1b && - (str_end + 1) < bufcount(in) && - in->buf.b[str_end+1] == 0x5c) // ESC-prefixed ST - break; - - str_end++; - } - - if (str_end >= bufcount(in)) - return EvAgain; - - *nb = str_end + 1; - if(in->buf.b[str_end] == 0x1b) - (*nb)++; - - // XXX: read carefully - if(in->savedcsi) - free(in->savedcsi); - - ulong len = str_end - introlen; - - in->nsavedcsi++; - in->savedcsi = malloc(len + 1); - - strncpy(in->savedcsi, (char *)in->buf.b + introlen, len); - in->savedcsi[len] = 0; - - key->type = (in->buf.b[introlen-1] & 0x1f) == 0x10 ? KeyDCS : KeyOSC; - key->code.num = in->nsavedcsi; - key->mods = 0; - - return EvKey; -} - -static -enum KeyEvent -peekesc(Input *in, Key *key, int force, ulong *nb) -{ - if (bufcount(in) == 0) - return in->closed ? EvEOF : EvNil; - - switch (*in->buf.b) { - case 0x1b: - if(bufcount(in) < 2) - return EvNil; - - switch(in->buf.b[1]) { - case 0x4f: // ESC-prefixed SS3 - return peekss3(in, 2, key, force, nb); - - case 0x50: // ESC-prefixed DCS - case 0x5d: // ESC-prefixed OSC - return peekctrl(in, 2, key, force, nb); - - case 0x5b: // ESC-prefixed CSI - return peekcsi(in, 2, key, force, nb); - } - - return EvNil; - - case 0x8f: // SS3 - return peekss3(in, 1, key, force, nb); - - case 0x90: // DCS - case 0x9d: // OSC - return peekctrl(in, 1, key, force, nb); - - case 0x9b: // CSI - return peekcsi(in, 1, key, force, nb); - } - - return EvNil; -} - - -// ----------------------------------------------------------------------- -// internal functions - -static -int -registername(Input *in, int sym, char *name) -{ - if (!sym) - sym = in->nkeynm; - - if(sym >= in->nkeynm) { - char **tmp = realloc(in->keynm, sizeof(*tmp) * (sym + 1)); - if(!tmp) - return -1; - - in->keynm = tmp; - - // Fill in the hole - for(int i = in->nkeynm; i < sym; i++) - in->keynm[i] = nil; - - in->nkeynm = sym + 1; - } - - in->keynm[sym] = name; - - return sym; -} - -static -int -registerc0(Input *in, int sym, int modset, int modmask, uchar ctrl, char *name) -{ - if(ctrl >= 0x20) { - errno = EINVAL; - return -1; - } - - if (name) - sym = registername(in, sym, name); - - in->c0[ctrl].sym = sym; - in->c0[ctrl].modset = modset; - in->c0[ctrl].modmask = modmask; - - return sym; -} - -static -void -advance(Input *in, ulong nb) -{ - if (in->buf.c < in->buf.b + nb) { - in->buf.b = in->buf.c; - return; - } - - in->buf.b += nb; -} - -static -void -slidebuffer(Input *in) -{ - static const ulong halfway = arrlen(in->buf.bytes) / 2; - if (in->buf.b - in->buf.bytes > halfway) { - memmove(in->buf.bytes, in->buf.bytes + halfway, halfway); - in->buf.b -= halfway; - in->buf.c -= halfway; - } -} - -static -void -harmonize(Input *in, Key *key) -{ - int flags = in->hflag; - - if (flags & HarmonizeSpace) { - if (key->type == KeyUnicode && key->code.pt == 0x20) { - key->type = KeySym; - key->code.sym = SymSpace; - } - } else { - if (key->type == KeySym && key->code.sym == SymSpace) { - key->type = KeyUnicode; - key->code.pt = 0x20; - utf8·runetobyte((char*)key->utf8, &key->code.pt); - } - } - - if (flags & HarmonizeDelBS) { - if (key->type == KeySym && key->code.sym == SymDel) { - key->code.sym = SymBackspace; - } - } -} - -static -void -emitcodepoint(Input *in, rune r, Key *key) -{ - if (r == 0) { - key->type = KeySym; - key->code.sym = SymSpace; - key->mods = ModCtrl; - goto harmonize; - } - if (r < 0x20) { - key->code.pt = 0; - key->mods = 0; - if (!(in->flags & FlagNoInterpret) && in->c0[r].sym != SymUnknown) { - key->code.sym = in->c0[r].sym; - key->mods |= in->c0[r].modset; - } - if (!key->code.sym) { - key->type = KeyUnicode; - if (r+0x40 >= 'A' && r+0x40 <= 'Z') - // it's a letter - use lowercase instead - key->code.pt = r + 0x60; - else - key->code.pt = r + 0x40; - key->mods = ModCtrl; - } else - key->type = KeySym; - goto harmonize; - } - if (r == 0x7f && !(in->flags & FlagNoInterpret)) { - // ascii del - key->type = KeySym; - key->code.sym = SymDel; - key->mods = 0; - goto harmonize; - } - if (r >= 0x20 && r < 0x80) { - // ascii lowbyte range - key->type = KeyUnicode; - key->code.pt = r; - key->mods = 0; - goto harmonize; - } - if (r >= 0x80 && r < 0xa0) { - // UTF-8 never starts with a C1 byte. So we can be sure of these - key->type = KeyUnicode; - key->code.pt = r - 0x40; - key->mods = ModCtrl|ModAlt; - goto harmonize; - } - key->type = KeyUnicode; - key->code.pt = r; - key->mods = 0; - -harmonize: - harmonize(in, key); - utf8·runetobyte((char*)key->utf8, &key->code.pt); -} - -static -enum KeyEvent -peekmousekey(Input *in, Key *key, ulong *nb) -{ - if (in->buf.c - in->buf.b < 3) - return EvAgain; - - key->type = KeyMouse; - key->code.mouse[0] = in->buf.c[0] - 0x20; - key->code.mouse[1] = in->buf.c[1] - 0x20; - key->code.mouse[2] = in->buf.c[2] - 0x20; - key->code.mouse[3] = 0; - - key->mods = (key->code.mouse[0] & 0x1c) >> 2; - key->code.mouse[0] &= ~0x1c; - - *nb = 3; - return EvKey; -} - -enum KeyEvent peekkey(Input *in, Key *key, int force, ulong *nb); - -static -enum KeyEvent -peeksimplekey(Input *in, Key *key, int force, ulong *nb) -{ - uchar c, *b; - int n; - rune r; - enum KeyEvent ev; - - b = in->buf.b; - c = *b; - - if (c == 0x1b) { - if (bufcount(in) == 1) { - if (!force) - return EvAgain; - goto ascii; - } - in->buf.b++; - ev = peekkey(in, key, force, nb); - in->buf.b--; - - switch (ev) { - case EvKey: - key->mods |= ModAlt; - (*nb)++; - /* fallthrough */ - case EvNil: case EvEOF: - case EvAgain: case EvErr: - return ev; - } - } - if (c == 0xa0) - goto ascii; - if (in->flags & FlagUTF8) { - n = utf8·bytetorune(&r, (char*)in->buf.b); - *nb = n; /* store the number of bytes */ - if (n > bufcount(in)) { - if (!force) - return EvAgain; - r = RuneErr; - *nb = bufcount(in); - } - key->type = KeyUnicode; - key->mods = 0; - goto utf8; - } - /* if we are here just emit raw byte */ - key->type = KeyUnicode; - key->code.pt = c; - key->mods = 0; - key->utf8[0] = c; - key->utf8[1] = 0; - *nb = 1; - return EvKey; - -ascii: - *nb = 1; - r = c; -utf8: - emitcodepoint(in, r, key); - return EvKey; -} - -// ----------------------------------------------------------------------- -// exported functions - -Input * -makeinput(int fd, int flags, unibi_term *info) -{ - int i; - Input *in; - char *e; - - if (!(in = malloc(sizeof(in)))) - panicf("out of memory"); - - in->fd = fd; - if (!(flags & (FlagRaw|FlagUTF8))) { - if (((e = getenv("LANG")) || (e = getenv("LC_MESSAGES")) || (e = getenv("LC_ALL"))) && - (e = strchr(e, '.')) && e++ && (!stricmp(e, "UTF-8") || !stricmp(e, "UTF8"))) - flags |= FlagUTF8; - else - flags |= FlagRaw; - } - in->flags = flags; - in->wait = 50; /* in msec */ - in->closed = 0; - in->started = 0; - in->hasold = 0; - in->keys = loadtermkeys(info); - - /* initialize buffer */ - in->buf.c = in->buf.b = in->buf.bytes; - in->buf.e = arrend(in->buf.bytes); - in->buf.off = 0; - memset(in->buf.bytes, 0, arrlen(in->buf.bytes)); - - /* initialize names */ - for (i = 0; i < 32; i++) - in->c0[i].sym = SymNone; - - registerc0(in, SymTab, 0x09, 0, 0, nil); - registerc0(in, SymEnter, 0x0d, 0, 0, nil); - registerc0(in, SymEscape, 0x1b, 0, 0, nil); - - /* load in csi */ - in->nsavedcsi = 0; - in->savedcsi = nil; - loadctrlkeys(); - - return in; -} - -void -freeinput(Input *in) -{ - // free(in); -} - -int -startrecord(Input *in) -{ - struct termios new; - if (in->started) - return 1; - - if (in->fd != -1 && !(in->flags & FlagNoTermIOS)) { - if (tcgetattr(in->fd, &new) == 0) { - in->oldterm = new; - in->hasold = 1; - - new.c_iflag &= ~(IXON|INLCR|ICRNL); - new.c_lflag &= ~(ICANON|ECHO|IEXTEN); - - new.c_cc[VMIN] = 1; - new.c_cc[VTIME] = 0; - - if (in->flags & FlagCtrlC) - new.c_lflag &= ~ISIG; - else { - /* Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT */ - new.c_cc[VQUIT] = _POSIX_VDISABLE; - new.c_cc[VSUSP] = _POSIX_VDISABLE; - /* Some OSes have Ctrl-Y==VDSUSP */ -# ifdef VDSUSP - new.c_cc[VDSUSP] = _POSIX_VDISABLE; -# endif - } - tcsetattr(in->fd, TCSANOW, &new); - } - } - - in->started = 1; - return 1; -} - -int -stoprecord(Input *in) -{ - if (!in->started) - return 1; - - if (in->hasold) - tcsetattr(in->fd, TCSANOW, &in->oldterm); - - in->started = 0; - return 1; -} - -enum KeyEvent -peekkey(Input *in, Key *key, int force, ulong *nb) -{ - int i, again = 0; - enum KeyEvent ev; - static enum KeyEvent (*peek[2])(Input *, Key *, int, ulong *) = { peeksym, peekesc }; - - if (!in->started) { - errno = EINVAL; - return EvErr; - } - - if (in->buf.off) { - in->buf.b += in->buf.off; - in->buf.off = 0; - } - - for (i = 0; i < arrlen(peek); i++) { - ev = peek[i](in, key, force, nb); - switch (ev) { - case EvKey: - slidebuffer(in); - /* fallthrough */ - case EvEOF: - case EvErr: - return ev; - case EvAgain: - if (!force) - again = 1; - /* fallthrough */ - case EvNil: - continue; - } - } - if (again) - return EvAgain; - - return peeksimplekey(in, key, force, nb); -} - -enum KeyEvent -getkey(Input *in, Key *key) -{ - ulong nb; - enum KeyEvent ev; - - ev = peekkey(in, key, 0, &nb); - switch (ev) { - case EvKey: - advance(in, nb); - break; - case EvAgain: - peekkey(in, key, 1, &nb); - /* get nb but don't advance */ - break; - default: - ; - } - return ev; -} - -enum KeyEvent -demandkey(Input *in, Key *key) -{ - ulong nb; - enum KeyEvent ev; - - ev = peekkey(in, key, 1, &nb); - if (ev == EvKey) - advance(in, nb); - - return ev; -} - -enum KeyEvent -isreadablekey(Input *in) -{ - int n; - if (in->fd == -1) { - errno = EBADF; - return EvErr; - } - - /* reset to beginning of buffer */ - if (in->buf.b > in->buf.bytes) { - n = in->buf.b - in->buf.bytes; - memmove(in->buf.bytes, in->buf.b, n); - in->buf.b = in->buf.bytes; - in->buf.c = in->buf.b + n; - } - -read: - n = read(in->fd, in->buf.c, in->buf.e-in->buf.c); - if (n == -1) { - if (errno == EAGAIN) - return EvNil; - if (errno == EINTR && !(in->flags & FlagEintr)) - goto read; - else - return EvErr; - } - if (n < 1) { - in->closed = 1; - return EvNil; - } - in->buf.c += n; - return EvAgain; -} - -enum KeyEvent -waitkey(Input *in, Key *key) -{ - enum KeyEvent ev; - struct pollfd p; - - if (in->fd == -1) { - errno = EBADF; - return EvErr; - } - - for (;;) { - ev = getkey(in, key); - switch (ev) { - case EvKey: case EvEOF: case EvErr: - return ev; - case EvNil: - ev = isreadablekey(in); - if (ev == EvErr) - return ev; - break; - - case EvAgain: - /* can't wait any longer */ - if (in->closed) - return demandkey(in, key); - poll: - p.fd = in->fd; - p.events = POLLIN; - - if (poll(&p, 1, in->wait) == -1) { - if (errno == EINTR && !(in->flags & FlagEintr)) - goto poll; - return EvErr; - } - - if (p.revents & (POLLIN|POLLHUP|POLLERR)) - ev = isreadablekey(in); - else - ev = EvNil; - - if (ev == EvErr) - return ev; - if (ev == EvNil) - return demandkey(in, key); - break; - } - } - /* unreachable */ -} - -enum KeyEvent -decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col) -{ - if(key->type != KeyMouse) - return EvNil; - - if(button) - *button = 0; - - getpos(key, row, col); - - if(!ev) - return EvKey; - - int btn = 0; - int code = key->code.mouse[0]; - int drag = code & 0x20; - code &= ~0x3c; - - switch(code) { - case 0: - case 1: - case 2: - *ev = drag ? MouseDrag : MousePress; - btn = code + 1; - break; - - case 3: - *ev = MouseRelease; - // no button hint - break; - - case 64: - case 65: - *ev = drag ? MouseDrag : MousePress; - btn = code + 4 - 64; - break; - - default: - *ev = MouseNil; - } - - if (button) - *button = btn; - - if (key->code.mouse[3] & 0x80) - *ev = MouseRelease; - - return EvKey; -} - -enum KeyEvent -decodepos(Input *in, Key *key, int *row, int *col) -{ - if (key->type != KeyPosition) - return EvNil; - - getpos(key, row, col); - - return EvKey; -} - -enum KeyEvent -decodemode(Input *in, Key *key, int *init, int *mode, int *val) -{ - if (key->type != KeyModeReport) - return EvNil; - - if (init) - *init = key->code.mouse[0]; - - if (mode) - *mode = (key->code.mouse[1] << 8) | key->code.mouse[2]; - - if (val) - *val = key->code.mouse[3]; - - return EvKey; -} - -char * -keyname(Input *in, int sym) -{ - if (sym == SymUnknown || sym >= in->nkeynm) - return ""; - - return in->keynm[sym]; -} - -// ----------------------------------------------------------------------- -// main point of entry - -static -void -printkey(Input *in, Key *key) -{ - enum MouseEvent ev; - int button, line, col; - int init, mode, val; - - switch(key->type) { - case KeyUnicode: - fprintf(stderr, "Unicode codepoint=U+%04dx utf8='%s'", key->code.pt, key->utf8); - break; - case KeyFunc: - fprintf(stderr, "Function F%d", key->code.num); - break; - case KeySym: - fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, keyname(in, key->code.sym)); - break; - case KeyMouse: - decodemouse(in, key, &ev, &button, &line, &col); - fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col); - break; - case KeyPosition: - decodepos(in, key, &line, &col); - fprintf(stderr, "Position report pos=(%d,%d)\n", line, col); - break; - case KeyModeReport: - decodemode(in, key, &init, &mode, &val); - fprintf(stderr, "Mode report mode=%s %d val=%d\n", init == '?' ? "DEC" : "ANSI", mode, val); - break; - case KeyDCS: - fprintf(stderr, "Device Control String"); - break; - case KeyOSC: - fprintf(stderr, "Operating System Control"); - break; - case KeyUnknownCSI: - fprintf(stderr, "unknown CSI\n"); - break; - } - - mode = key->mods; - fprintf(stderr, " mod=%s%s%s+%02x", - (mode & ModCtrl ? "" : ""), - (mode & ModAlt ? "" : ""), - (mode & ModShift? "" : ""), - mode & ~(ModCtrl|ModAlt|ModShift)); -} - -int -main() -{ - char *name; - Input *in; - unibi_term *info; - Key key; - enum KeyEvent ev; - - name = getenv("TERM"); - info = unibi_from_term(name); - in = makeinput(0, FlagSpaceSymbol, info); - - int n = 0; /* for debugging purposes only */ - startrecord(in); - while ((ev = waitkey(in, &key)) != EvEOF) { - switch (ev) { - case EvKey: - printkey(in, &key); - printf("\n"); - break; - case EvNil: - printf("\n"); - case EvAgain: - printf("\n"); - default: - ; - } - n++; - if (n > 200) - break; - } - stoprecord(in); - freeinput(in); -} diff --git a/sys/libterm/input.c b/sys/libterm/input.c deleted file mode 100644 index 038e8df..0000000 --- a/sys/libterm/input.c +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include -#include - -#define iota(x) 1 << (x) - -// ----------------------------------------------------------------------- -// enums - -enum -{ - KeyRune, KeyFunc, KeySym, - KeyMouse, KeyPosition, KeyModeReport, - KeyDCS, KeyOSC, /* add other recognised types here */ - KeyUnknownCSI = -1 -}; - -enum KeyEvent -{ - EvNil, EvKey, EvEOF, EvAgain, EvErr, -}; - -enum -{ - SymUnknown = -1, - SymNone = 0, - - /* special names in c0 */ - SymBackspace, SymTab, SymEnter, SymEscape, - - /* special names in g0 */ - SymSpace, SymDel, - - /* special keys */ - SymUp, SymDown, SymLeft, SymRight, SymBegin, SymFind, SymInsert, - SymDelete, SymSelect, SymPageUp, SymPageDown, SymHome, SymEnd, - - /* special keys from terminfo */ - SymCancel, SymClear, SymClose, SymCommand, SymCopy, SymExit, - SymHelp, SymMark, SymMessage, SymMove, SymOpen, SymOptions, - SymPrint, SymRedo, SymReference, SymRefresh, SymReplace, - SymRestart, SymResume, SymSave, SymSuspend, SymUndo, - - /* numeric keypad special keys */ - SymKp0, SymKp1, SymKp2, SymKp3, SymKp4, SymKp5, SymKp6, SymKp7, SymKp8, - SymKp9, SymKpEnter, SymKpPlus, SymKpMinus, SymKpMul, SymKpDiv, SymKpComma, - SymKpDot, SymKpEq, - - /* et cetera ad nauseum */ - NumSyms -}; - -enum MouseEvent -{ - MouseNil, MousePress, MouseDrag, MouseRelease, -}; - -enum -{ - ModShift = iota(0), - ModAlt = iota(1), - ModCtrl = iota(2), -}; - -// ----------------------------------------------------------------------- -// types - -typedef struct Input Input; - -struct Input -{ - int fd; - int wait; /* in msec */ - struct { - char *b, *c, bytes[1024]; - } rbuf; /* read buffer */ - struct { - char *b, *c, bytes[1024]; - } ebuf; /* escape buffer */ -}; - -// ----------------------------------------------------------------------- -// globals - -// ----------------------------------------------------------------------- -// utility functions - -// ----------------------------------------------------------------------- -// implementation - -Input * -makeinput(int fd, int flags, unibi_term *info) -{ - int i; - Input *in; - char *e; - - if (!(in = calloc(1, sizeof(in)))) - panicf("out of memory"); - - in->fd = fd; - in->wait = 50; /* in msec */ - - /* initialize buffers */ - in->rbuf.c = in->rbuf.b = in->rbuf.bytes; - - return in; -} diff --git a/sys/libterm/term.c b/sys/libterm/term.c index cda2bbe..11591fc 100644 --- a/sys/libterm/term.c +++ b/sys/libterm/term.c @@ -1,5 +1,8 @@ #include "term.h" +#include +#include + struct ExtraInfo { char *enteralt; @@ -19,6 +22,8 @@ struct ExtraInfo vt200 = .exitmouse = "\e[?1002l\e[?1006l", }; +static Term *sigwinchhead; + // ----------------------------------------------------------------------- // database lookup @@ -78,26 +83,9 @@ guessextrastr(Term *t, char *name, char *guess) return guess; } -static -int -tryextrabool(Term *t, char *name) -{ - const char *nm; - size_t max = unibi_count_ext_bool(t->info); - for (size_t i = 0; i < max; i++) { - nm = unibi_get_ext_bool_name(t->info, i); - if (nm && !strcmp(nm, name)) { - return (int)i; - } - } - return -1; -} - /* formats escape strings and writes to output */ static void tfmt(Term *t, char *esc, int n, ...); - -// ----------------------------------------------------------------------- -// exported pen methods +static void tclear(Term *t); // ----------------------------------------------------------------------- // exported term methods @@ -113,14 +101,25 @@ ttmpbuf(Term *t, int len) return (t->tmp.b = realloc(t->tmp.b, len)); } -void -tinit(Term *t) +void twrite(Term *t, long len, char *s); +void tlistensigwinch(Term *t); + +Term* +tmake(void) { + Term *t; + + t = calloc(1, sizeof(*t)); + + /* meta data */ t->name = getenv("TERM"); t->info = unibi_from_term(t->name); if (!t->info) panicf("could not identify terminal"); + t->fd = 1; // stdout + tlistensigwinch(t); + t->mode.mouse = 0; t->mode.cursorvis = 1; t->mode.altscreen = 0; @@ -128,13 +127,23 @@ tinit(Term *t) t->cap.colors = unibi_get_num(t->info, unibi_max_colors); t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase); - /* TODO */ - t->input = nil; - t->root = nil; - t->pen = (Pen){0}; + /* initialize root window (get current size)*/ + struct winsize ws = { 0 }; + if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1) + goto bad; - /* fill in buffers */ - t->buf.c = t->buf.b; + t->root = wmake(nil, 0, 0, ws.ws_col, ws.ws_row, 0); + + t->root->curvis = 1; + t->root->blink = 0; + + t->pen = (Pen){ + .state = PenNormal, + .col = {.fg = -1, .bg = -1}, + }; + + /* fill in output buffers */ + t->buf.c = t->buf.b; t->tmp.b = nil; t->tmp.len = 0; @@ -174,22 +183,16 @@ tinit(Term *t) t->esc.ext.rgbf = guessextrastr(t, "setrgbf", "\x1b[38;2;%p1%d;%p2%d;%p3%dm"); t->esc.ext.rgbb = guessextrastr(t, "setrgbb", "\x1b[48;2;%p1%d;%p2%d;%p3%dm"); - /* acs characters */ - t->acs.vline = tryinfostr(t, unibi_acs_vline); - t->acs.hline = tryinfostr(t, unibi_acs_hline); - t->acs.plus = tryinfostr(t, unibi_acs_plus); - t->acs.ltee = tryinfostr(t, unibi_acs_ltee); - t->acs.rtee = tryinfostr(t, unibi_acs_rtee); - t->acs.ttee = tryinfostr(t, unibi_acs_ttee); - t->acs.btee = tryinfostr(t, unibi_acs_btee); - t->acs.ulcorner = tryinfostr(t, unibi_acs_ulcorner); - t->acs.urcorner = tryinfostr(t, unibi_acs_urcorner); - t->acs.llcorner = tryinfostr(t, unibi_acs_llcorner); - t->acs.lrcorner = tryinfostr(t, unibi_acs_lrcorner); + return t; + +bad: + panicf("failed to initialize terminal instance"); + free(t); + return nil; } void -tfini(Term *t) +tfree(Term *t) { if (t->mode.mouse) twrite(t, 0, vt200.exitmouse); @@ -199,6 +202,56 @@ tfini(Term *t) twrite(t, 0, vt200.exitalt); tfmt(t, t->esc.sgr0, 0); + tclear(t); + free(t); +} + +/* handle resize events */ +void +tresize(Term *t) +{ + if (t->fd == -1) + return; + + struct winsize ws = { 0 }; + if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1) + return; + + printf("[%d,%d]\n", ws.ws_col, ws.ws_row); + if (t->root->w != ws.ws_col || t->root->h != ws.ws_row) + wresize(t->root, ws.ws_col, ws.ws_row); +} + +static +void +sigwinch(int num) +{ + Term *it; + for (it = sigwinchhead; it; it = it->link) + tresize(it); +} + +void +tlistensigwinch(Term *t) +{ + sigset_t new, old; + Term *it; + + sigemptyset(&new); + sigaddset(&new, SIGWINCH); + sigprocmask(SIG_BLOCK, &new, &old); + + if (!sigwinchhead) { + sigaction(SIGWINCH, &(struct sigaction){ .sa_handler = sigwinch }, nil); + sigwinchhead = t; + } else { + it = sigwinchhead; + while (it->link) + it = it->link; + it->link = t; + } + + sigprocmask(SIG_SETMASK, &old, nil); } void @@ -217,12 +270,14 @@ twrite(Term *t, long len, char *s) if (!len) len = strlen(s); - while (len > 0) { - n = MIN(len, arrend(t->buf.b) - t->buf.c); - memcpy(t->buf.c, s, n); - t->buf.c += n; - len -= n; +loop: + n = MIN(len, arrend(t->buf.b) - t->buf.c); + memcpy(t->buf.c, s, n); + t->buf.c += n; + len -= n; + if (len) { tflush(t); + goto loop; } } @@ -256,8 +311,8 @@ tsetpen(Term *t, Pen new) /* fg/bg color */ /* TODO: add a check for if the terminal supports true color */ - /* TODO: add a check for negative indices */ - if (new.state & PenTrueClr) { + /* TODO: deal w/ negative indices properly */ + if (new.state & PenRGB) { tfmt(t, t->esc.ext.rgbf, 3, new.rgb.fg.r, new.rgb.fg.g, new.rgb.fg.b); tfmt(t, t->esc.ext.rgbb, 3, new.rgb.bg.r, new.rgb.bg.g, new.rgb.bg.b); } else { @@ -282,12 +337,13 @@ tfmt(Term *t, char *esc, int n, ...) panicf("no terminfo escape string given"); va_start(args, n); - for (i = 0; i < arrlen(param) && i < n; i++) + for (i = 0; i < arrlen(param) && i < n; i++) { param[i] = unibi_var_from_num(va_arg(args, int)); + } va_end(args); len = unibi_run(esc, param, c, sizeof(buf)); - if (len < arrlen(buf)) { + if (len >= arrlen(buf)) { c = ttmpbuf(t, len); unibi_run(esc, param, c, len); } @@ -296,6 +352,7 @@ tfmt(Term *t, char *esc, int n, ...) } /* absolute move */ +static int tgoto(Term *t, int row, int col) { @@ -324,6 +381,7 @@ tgoto(Term *t, int row, int col) } /* relative move */ +static void tjump(Term *t, int down, int right) { @@ -346,50 +404,86 @@ tjump(Term *t, int down, int right) tfmt(t, t->esc.cub, 1, -right); } +static void -tdel(Term *t, int num) +tclear(Term *t) { - char *c, buf[64]; - if (num < 1) - return; - - /* TODO: allow for not moving */ - if (t->cap.bce) { - tfmt(t, t->esc.ech, 1, num); - tjump(t, 0, num); - } else { - c = buf; - memset(c, ' ', arrlen(buf)); - while(num > 64) { - twrite(t, arrlen(buf), c); - num -= arrlen(buf); - } - twrite(t, num, c); - } + tfmt(t, t->esc.ed2, 0); } void -tclear(Term *t) +tblit(Term *t, Window *win) { - tfmt(t, t->esc.ed2, 0); + int r, c, n, j; + Row *row; + char u[UTFmax+1] = {0}; + + j = 0; + tgoto(t, win->top, win->left); + for (r = 0; r < win->h; r++) { + row = win->row + r; + if (!row->dirty) { + j++; + continue; + } + + if (j) { + tjump(t, j, 0); + j = 0; + } + + for (c = 0; c < win->w; c++) { + tsetpen(t, row->cells[c].pen); + n = utf8·runetobyte(u, &row->cells[c].txt); + twrite(t, n, u); + } + + row->dirty = 0; + } + + tflush(t); } // ----------------------------------------------------------------------- -// testing entry +// testing int main() { - Term t; - tinit(&t); - - printf("%s: %s\n", unibi_short_name_str(unibi_set_a_foreground), t.esc.sgr_fg); - printf("%s: %s\n", unibi_short_name_str(unibi_set_a_background), t.esc.sgr_bg); - tfmt(&t, t.esc.ext.rgbf, 3, 10, 10, 100); - tfmt(&t, t.esc.ext.rgbb, 3, 100, 0, 100); - twrite(&t, 0, "hello world\n"); - twrite(&t, 0, "hello world\n"); - twrite(&t, 0, t.acs.hline); - - tfini(&t); + int i; + Term *t; + Window *win; + + t = tmake(); + win = t->root; + tclear(t); + + win->pen = (Pen){ + .state = PenNormal, + .col = {.fg=-1, .bg=-1}, + }; + for (i = 0; i < 2000; i++) + wputrune(win, 'a'); + + tblit(t, win); + + win->cur.row = 10; + win->cur.col = 0; + + win->pen = (Pen){ + .state=PenNormal|PenRGB, + .rgb={.fg={200, 100, 100}, .bg={0, 0, 0} }, + }; + + for (i = 0; i < 500; i++) + wputrune(win, 'b'); + + tblit(t, win); + + sleep(5); + wscroll(win, 10); + tblit(t, win); + sleep(5); + + tfree(t); } diff --git a/sys/libterm/term.h b/sys/libterm/term.h index acae95f..6bd2f6b 100644 --- a/sys/libterm/term.h +++ b/sys/libterm/term.h @@ -7,165 +7,11 @@ #include #define iota(x) 1 << (x) -/* - * obtained from: - * https://invisible-island.net/ncurses/man/curs_add_wch.3x.html - */ -#define ACSRUNES \ - /* name utf8 ascii acsc*/ \ - ACSRUNE("block", 0x25ae, '#', '0') \ - ACSRUNE("board", 0x2592, '#', 'h') \ - ACSRUNE("btee", 0x2534, '+', 'v') \ - ACSRUNE("bullet", 0x00b7, 'o', '~') \ - ACSRUNE("ckboard", 0x2592, ':', 'a') \ - ACSRUNE("darrow", 0x2193, 'v', '.') \ - ACSRUNE("degree", 0x00b0, '\'','f') \ - ACSRUNE("diamond", 0x25c6, '+', '`') \ - ACSRUNE("gequal", 0x2265, '>', '>') \ - ACSRUNE("hline", 0x2500, '-', 'q') \ - ACSRUNE("antern", 0x2603, '#', 'i') \ - ACSRUNE("larrow", 0x2190, '<', ',') \ - ACSRUNE("lequal", 0x2264, '<', 'y') \ - ACSRUNE("llcorner", 0x2514, '+', 'm') \ - ACSRUNE("lrcorner", 0x2518, '+', 'j') \ - ACSRUNE("ltee", 0x2524, '+', 't') \ - ACSRUNE("nequal", 0x2260, '!', '|') \ - ACSRUNE("pi", 0x03c0, '*', '{') \ - ACSRUNE("plminus", 0x00b1, '#', 'g') \ - ACSRUNE("plus", 0x253c, '+', 'n') \ - ACSRUNE("rarrow", 0x2192, '>', '+') \ - ACSRUNE("rtee", 0x251c, '+', 'u') \ - ACSRUNE("s1", 0x23ba, '-', 'o') \ - ACSRUNE("s3", 0x23bb, '-', 'p') \ - ACSRUNE("s7", 0x23bc, '-', 'r') \ - ACSRUNE("s9", 0x23bd, '_', 's') \ - ACSRUNE("sterling", 0x00a3, 'f', '}') \ - ACSRUNE("ttee", 0x252c, '+', 'w') \ - ACSRUNE("uarrow", 0x2191, '^', '-') \ - ACSRUNE("ulcorner", 0x250c, '+', 'l') \ - ACSRUNE("urcorner", 0x2510, '+', 'k') \ - ACSRUNE("vline", 0x2502, '|', 'x') \ - /* thick versions */ \ - ACSRUNE("t_btee", 0x253b, '+', 'V') \ - ACSRUNE("t_hline", 0x2501, '-', 'Q') \ - ACSRUNE("t_llcorner", 0x2517, '+', 'M') \ - ACSRUNE("t_lrcorner", 0x251b, '+', 'J') \ - ACSRUNE("t_ltee", 0x252b, '+', 'T') \ - ACSRUNE("t_plus", 0x254b, '+', 'N') \ - ACSRUNE("t_rtee", 0x2523, '+', 'U') \ - ACSRUNE("t_ttee", 0x2533, '+', 'W') \ - ACSRUNE("t_ulcorner", 0x250f, '+', 'L') \ - ACSRUNE("t_urcorner", 0x2513, '+', 'K') \ - ACSRUNE("t_vline", 0x2503, '|', 'X') \ - /* double version */ \ - ACSRUNE("d_btee", 0x2569, '+', 'H') \ - ACSRUNE("d_hline", 0x2550, '-', 'R') \ - ACSRUNE("d_llcorner", 0x255a, '+', 'D') \ - ACSRUNE("d_lrcorner", 0x255d, '+', 'A') \ - ACSRUNE("d_ltee", 0x2560, '+', 'F') \ - ACSRUNE("d_plus", 0x256c, '+', 'E') \ - ACSRUNE("d_rtee", 0x2563, '+', 'G') \ - ACSRUNE("d_ttee", 0x2566, '+', 'I') \ - ACSRUNE("d_ulcorner", 0x2554, '+', 'C') \ - ACSRUNE("d_urcorner", 0x2557, '+', 'B') \ - ACSRUNE("d_vline", 0x2551, '|', 'Y') - -/* enums */ - -/* key symbols */ -enum -{ - SymUnknown = -1, - SymNone = 0, - - /* special names in c0 */ - SymBackspace, SymTab, SymEnter, SymEscape, - - /* special names in g0 */ - SymSpace, SymDel, - - /* special keys */ - SymUp, SymDown, SymLeft, SymRight, SymBegin, SymFind, SymInsert, - SymDelete, SymSelect, SymPageup, SymPagedown, SymHome, SymEnd, - - /* special keys from terminfo */ - SymCancel, SymClear, SymClose, SymCommand, SymCopy, SymExit, - SymHelp, SymMark, SymMessage, SymMove, SymOpen, SymOptions, - SymPrint, SymRedo, SymReference, SymRefresh, SymReplace, - SymRestart, SymResume, SymSave, SymSuspend, SymUndo, - - /* numeric keypad special keys */ - SymKp0, SymKp1, SymKp2, SymKp3, SymKp4, SymKp5, SymKp6, SymKp7, SymKp8, - SymKp9, SymKpenter, SymKpplus, SymKpminus, SymKpmult, SymKpdiv, SymKpcomma, - SymKpperiod, SymKpequals, - - /* et cetera ad nauseum */ - NumSyms -}; - -/* key type */ -enum -{ - KeyUnicode, - KeyFunc, - KeySym, - KeyMouse, - KeyPosition, - KeyModeReport, - KeyDCS, - KeyOSC, - /* add other recognised types here */ - - KeyUnknownCSI = -1 -}; - -/* key events */ -enum KeyEvent -{ - EvNil, - EvKey, - EvEOF, - EvAgain, - EvErr, -}; - -enum MouseEvent -{ - MouseNil, - MousePress, - MouseDrag, - MouseRelease, -}; - -enum -{ - ModShift = iota(0), - ModAlt = iota(1), - ModCtrl = iota(2), -}; - -enum -{ - FlagNoInterpret = iota(0), - FlagConvertKP = iota(1), - FlagRaw = iota(2), - FlagUTF8 = iota(3), - FlagNoTermIOS = iota(4), - FlagSpaceSymbol = iota(5), - FlagCtrlC = iota(6), - FlagEintr = iota(7), -}; - -enum -{ - HarmonizeSpace = iota(0), - HarmonizeDelBS = iota(1), -}; -/* types */ typedef struct RGB8 RGB8; typedef struct Pen Pen; +typedef struct Dot Dot; typedef struct Cell Cell; typedef struct Row Row; typedef struct Buffer Buffer; @@ -194,7 +40,7 @@ enum PenUnderline = iota(6), PenBlink = iota(7), /* ... */ - PenTrueClr = iota(15), + PenRGB = iota(15), }; struct Pen @@ -213,38 +59,29 @@ struct Pen }; /* outputs */ -struct Cell { - rune r; +struct Cell +{ + rune txt; Pen pen; }; -struct Row { +struct Row +{ Cell *cells; - uint dirty:1; + uint dirty : 1; }; -/* Buffer holding the current window content (as an array) as well - * as the scroll back buffer content (as a circular/ring buffer). - * - * If new content is added to terminal the view port slides down and the - * previously top most line is moved into the scroll back buffer at postion - * scroll.index. This index will eventually wrap around and thus overwrite - * the oldest lines. - * - * In the scenerio below a scroll up has been performed. That is 'scroll.above' - * lines still lie above the current view port. Further scrolling up will show - * them. Similarly 'scroll.below' is the amount of lines below the current - * viewport. - * - * The function buffer_boundary sets the row pointers to the start/end range - * of the section delimiting the region before/after the viewport. The functions - * buffer_row_{first,last} return the first/last logical row. And - * buffer_row_{next,prev} allows to iterate over the logical lines in either - * direction. +struct Dot +{ + int row, col; +}; + +/* + * scroll.top & scroll.bot are pointers into the viewport. * * scroll back buffer * - * scroll_buf->+----------------+-----+ + * scroll.buf->+----------------+-----+ * | | | ^ \ * | before | | | | * current terminal content | viewport | | | | @@ -253,39 +90,48 @@ struct Row { * ^ | | i | \ | | i | c | * | | | n | \ | | n | r | * | | v | \ | | v | o | - * r | | i | \ | | i | l / - * o | viewport | s | >|<- scroll.index | s | l \ - * w | | i | / | | i | | - * s | | b | / | after | b | s > scroll.below + * | | i | \ | | i | l / + * | buffer | s | >|<- scroll.index | s | l \ + * h | | i | / | | i | | + * | | b | / | after | b | s > scroll.below * | | l | / | viewport | l | i | * v | | e | / | | e | z / * +----------------+-----+/ | unused | | e - * <- maxcols -> | scroll back | | - * <- cols -> | buffer | | | + * <- maxw -> | scroll back | | + * <- w -> | buffer | | | * | | | | * | | | v - * roll_buf + scroll.size->+----------------+-----+ - * <- maxcols -> - * <- cols -> + * scroll.buf + scroll.size->+----------------+-----+ + * <- maxw -> + * <- w -> */ -struct Buffer { - Row *row; /* array of Row pointers of size 'rows' */ - Row *crow; /* row on which the cursor currently resides */ - bool *tabs; /* a boolean flag for each column whether it is a tab */ +struct Buffer +{ + int w, h; /* dimension of buffer */ + Pen pen; /* default attributes */ + int maxw; /* allocated cells (maximal cols over time) */ + Row *row; /* array of row pointers of size 'h' */ struct { - Row *buf; /* a ring buffer holding the scroll back content */ - Row *top; /* row in lines where scrolling region starts */ - Row *bot; /* row in lines where scrolling region ends */ - int size; /* maximal capacity of scroll back buffer (in lines) */ - int index; /* current index into the ring buffer */ - int above; /* number of lines above current viewport */ - int below; /* number of lines below current viewport */ + Row *buf; + Row *top; + Row *bot; + int size; + int index; + int above; + int below; } scroll; - Pen pen, spen; - int nrow, ncol; /* current dimension of buffer */ - int maxcols; /* allocated cells (maximal cols over time) */ - int srow, scol; /* saved cursor row/column (zero based) */ + Dot cur, save; /* cursor position within buffer */ +}; + +struct Window +{ + struct Buffer; + int top, left; + uchar curvis : 1; + uchar blink : 2; + + Window *parent, *child, *link; }; /* input */ @@ -310,61 +156,57 @@ struct KeyInfo int modset; }; -/* make opaque? */ struct Input { int fd; int flags; - int hflag; int wait; /* in ms */ + /* modifiers */ + uchar closed : 1; + uchar started : 1; + uchar hasold : 1; + struct termios oldterm; /* buffer */ struct { long off; uchar *b, *c, *e, bytes[256]; - } buf; - - /* modifiers */ - char closed : 1; - char started : 1; - char hasold : 1; + } rbuf; + struct { + uchar *s, bytes[256]; + } ebuf; /* key data */ Node *keys; struct KeyInfo c0[32]; - - char **keynm; - int nkeynm; - - int nsavedcsi; - char *savedcsi; }; + struct Term { /* meta data */ char *name; unibi_term *info; struct { - uint altscreen : 1; - uint cursorvis : 1; - uint mouse : 1; + uchar altscreen : 1; + uchar cursorvis : 1; + uchar mouse : 1; } mode; struct { - uint bce : 1; - int colors; + uchar bce : 1; + int colors; } cap; /* input capture */ - Input *input; + Input input; /* output display */ Window *root; Pen pen; - /* output raw text */ + /* raw text to pty */ int fd; struct { char *c, b[512]; @@ -416,74 +258,13 @@ struct Term } ext; } esc; - /* basic shapes */ - struct { - rune block; - rune board; - rune hline; - rune vline; - rune plus; - rune ltee; - rune rtee; - rune ttee; - rune btee; - rune ulcorner; - rune urcorner; - rune llcorner; - rune lrcorner; - } acs; + Term *link; }; -/* - * exported functions - */ +/* functions */ +void tresize(Term *t); -#if 0 -/* buffer.c */ -void zero(Row *row, int start, int len); -void roll(Row *start, Row *end, int count); - -void bclear(Buffer *b); -void bfree(Buffer *b); -void bscroll(Buffer *b, int s); -void bresize(Buffer *b, int rows, int cols); -bool binit(Buffer *b, int rows, int cols, int size); -void brender(Buffer *b, Term *t); -void bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) ; -Row *browfirst(Buffer *b); -Row *browlast(Buffer *b); -Row *brownext(Buffer *b, Row *row); -Row *bprevrow(Buffer *b, Row *row); - -/* input.c */ -Input *makeinput(int fd, int flags, unibi_term *info); -void freeinput(Input *in); -int startrecord(Input *in); -int stoprecord(Input *in); -char *keyname(Input *in, int sym); - -enum KeyEvent term·waitkey(Input *in, Key *key); /* block until next keypress */ -enum KeyEvent term·getkey(Input *in, Key *key); /* grab key if we can */ -enum KeyEvent term·demandkey(Input *in, Key *key); /* grab now and interpret as best we can */ - -/* unpack key event into useful data */ -enum KeyEvent decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col); -enum KeyEvent decodepos(Input *in, Key *key, int *row, int *col); -enum KeyEvent decodemode(Input *in, Key *key, int *init, int *mode, int *val); -#endif - -/* term.c */ -void term·init(Term *t); -void term·fini(Term *t); -void term·flush(Term *t); -void term·write(Term *t, long len, char *s); -int term·goto(Term *t, int row, int col); -void term·jump(Term *t, int down, int right); -void term·del(Term *t, int num); -void term·setpen(Term *t, Pen pen); -void term·clear(Term *t); - -/* input.c */ -enum KeyEvent term·waitkey(Term *t, Key *key); /* block until next keypress */ -enum KeyEvent term·getkey(Term *t, Key *key); /* grab key if we can */ -enum KeyEvent term·demandkey(Term *t, Key *key); /* grab now and interpret as best we can */ +Window *wmake(Window *root, int top, int left, int w, int h, int scroll); +void wresize(Window *root, int w, int h); +void wputrune(Window *win, rune r); +void wscroll(Window *win, int s); diff --git a/sys/libterm/window.c b/sys/libterm/window.c index fec3997..5d36c8b 100644 --- a/sys/libterm/window.c +++ b/sys/libterm/window.c @@ -1,44 +1,408 @@ -#include -#include - #include "term.h" -typedef struct Rect Rect; +// ----------------------------------------------------------------------- +// buffers -struct Rect +static +void +zero(Row *row, int start, int len) { - int top, left, rows, cols; -}; + int i; + Cell cell = { + .txt = L' ', + .pen = { + .state = PenNormal, + .col.fg = -1, + .col.bg = -1, + }, + }; + + for (i = start; i < len + start; i++) + row->cells[i] = cell; + row->dirty = 1; +} -struct Window +static +void +roll(Row *start, Row *end, int count) { - Buffer buffer[2], *buf; - Rect area; /* on screen */ - Pen pen, spen; /* current and saved pen */ - uint curvis : 1; - uint damage : 1; -}; + int n = end - start; -/* functions */ + /* enforce circularity */ + count %= n; + if (count < 0) + count += n; -Window * -makewindow(Window *root, Rect area, int history) + if (count) { + char buf[count * sizeof(Row)]; /* XXX: remove VLA */ + memcpy(buf, start, count * sizeof(Row)); + memmove(start, start + count, (n - count) * sizeof(Row)); + memcpy(end - count, buf, count * sizeof(Row)); + + for (Row *row = start; row < end; row++) + row->dirty = 1; + } +} + +/* buffer operations */ +static +void +bclear(Buffer *b) { - Window *w; - w = calloc(1, sizeof(*w)); - if (!w) - panicf("out of memory"); + int i; + Cell cell = { + .txt = L' ', + .pen = { + .state = PenNormal, + .col.fg = -1, + .col.bg = -1, + }, + }; + + for (i = 0; i < b->h; i++) { + Row *row = b->row + i; + for (int j = 0; j < b->w; j++) { + row->cells[j] = cell; + row->dirty = 1; + } + } +} - w->pen = (Pen) { - .state = PenNormal, - .col = {-1, -1}, - }; +static +void +bfini(Buffer *b) +{ + int i; - if (!binit(w->buffer+0, area.rows, area.cols, history) || - !binit(w->buffer+1, area.rows, area.cols, 0)) { - free(w); - return nil; + for (i = 0; i < b->h; i++) + free(b->row[i].cells); + + free(b->row); + + if (b->scroll.size) { + for (i = 0; i < b->scroll.size; i++) + free(b->scroll.buf[i].cells); + + free(b->scroll.buf); + } +} + +static +void +bscroll(Buffer *b, int s) +{ + Row tmp; + int i, ssz = b->scroll.bot - b->scroll.top; + + /* work in quanta of screen size */ + if (s > ssz) { + bscroll(b, ssz); + bscroll(b, s - ssz); + return; + } + if (s < -ssz) { + bscroll(b, -ssz); + bscroll(b, s + ssz); + return; + } + + b->scroll.above += s; + b->scroll.above = CLAMP(b->scroll.above, 0, b->scroll.size); + + if (s > 0) { + if (b->scroll.size) { + for (i = 0; i < s; i++) { + tmp = b->scroll.top[i]; + b->scroll.top[i] = b->scroll.buf[b->scroll.index]; + b->scroll.buf[b->scroll.index] = tmp; + + b->scroll.index++; + if (b->scroll.index == b->scroll.size) + b->scroll.index = 0; + } + } else + for (i = 0; i < s; i++) + zero(b->scroll.top+i, 0, b->maxw); + } + + roll(b->scroll.top, b->scroll.bot, s); + + if (s < 0) { + if (b->scroll.size) { + for (i = (-s) - 1; i >= 0; i--) { + b->scroll.index--; + if (b->scroll.index == -1) + b->scroll.index = b->scroll.size - 1; + + tmp = b->scroll.top[i]; + + b->scroll.top[i] = b->scroll.buf[b->scroll.index]; + b->scroll.buf[b->scroll.index] = tmp; + b->scroll.top[i].dirty = 1; + } + } else + for (i = (-s) - 1; i >= 0; i--) + zero(b->scroll.top+i, 0, b->maxw); + } +} + +static +void +bresize(Buffer *b, int nrow, int ncol) +{ + int r, d; + Row *row = b->row; + Row *cur = row + b->cur.row; + + if (b->h != nrow) { + /* scroll if we can */ + if (cur >= row + nrow) + bscroll(b, b->cur.row - nrow + 1); + while (b->h > nrow) { + free(row[b->h - 1].cells); + b->h--; + } + + row = realloc(row, sizeof(Row) * nrow); + } + + if (b->maxw < ncol) { + /* expand each row */ + for (r = 0; r < b->h; r++) { + row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol); + if (b->h < ncol) + zero(row + r, b->w, ncol - b->w); + row[r].dirty = 1; + } + /* expand the scroll buffer */ + Row *sbuf = b->scroll.buf; + for (r = 0; r < b->scroll.size; r++) { + sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol); + if (b->w < ncol) + zero(sbuf + r, b->w, ncol - b->w); + } + b->maxw = b->w = ncol; + } else if (b->w != ncol) { + for (r = 0; r < b->h; r++) + row[r].dirty = 1; + b->w = ncol; + } + + d = 0; + if (b->h < nrow) { + while (b->h < nrow) { + row[b->h].cells = calloc(b->maxw, sizeof(Cell)); + zero(row + b->h, 0, b->maxw); + b->h++; + } + + /* prepare for backfill */ + if (cur >= b->scroll.bot - 1) { + d = b->row + nrow - cur - 1; + if (d > b->scroll.above) + d = b->scroll.above; + } } - w->buf = w->buffer; - return w; + + b->cur.row += row - b->row; + b->scroll.top = row; + b->scroll.bot = row + nrow; + b->row = row; + + /* perform backfill */ + if (d > 0) { + bscroll(b, -d); + b->cur.row += d; + } +} + +static +bool +binit(Buffer *b, int cols, int rows, int scroll) +{ + int size; + + b->pen.state = PenNormal; + b->pen.col.fg = b->pen.col.bg = -1; + + size = MAX(scroll, 0); + if (size && !(b->scroll.buf = calloc(size, sizeof(Row)))) + return false; + + b->scroll.size = size; + bresize(b, rows, cols); + + b->cur = (Dot){0}; + b->save = b->cur; + + return true; +} + +static +void +bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) +{ + if (bs) + *bs = nil; + if (be) + *be = nil; + if (as) + *as = nil; + if (ae) + *ae = nil; + if (!b->scroll.size) + return; + + if (b->scroll.above) { + if (bs) + *bs = &b->scroll.buf[(b->scroll.index - b->scroll.above + b->scroll.size) % b->scroll.size]; + if (be) + *be = &b->scroll.buf[(b->scroll.index-1 + b->scroll.size) % b->scroll.size]; + } + if (b->scroll.below) { + if (as) + *as = &b->scroll.buf[b->scroll.index]; + if (ae) + *ae = &b->scroll.buf[(b->scroll.index + b->scroll.below-1) % b->scroll.size]; + } +} + +static +Row * +browfirst(Buffer *b) +{ + Row *bstart; + if (!b->scroll.size || !b->scroll.above) + return b->row; + bboundary(b, &bstart, nil, nil, nil); + return bstart; +} + +static +Row * +browlast(Buffer *b) +{ + Row *aend; + if (!b->scroll.size || !b->scroll.below) + return b->row + b->h - 1; + bboundary(b, nil, nil, nil, &aend); + return aend; +} + +static +Row * +brownext(Buffer *b, Row *row) +{ + Row *before_start, *before_end, *after_start, *after_end; + Row *first = b->row, *last = b->row + b->h - 1; + + if (!row) + return nil; + + bboundary(b, &before_start, &before_end, &after_start, &after_end); + + if (row >= first && row < last) + return ++row; + if (row == last) + return after_start; + if (row == before_end) + return first; + if (row == after_end) + return nil; + if (row == &b->scroll.buf[b->scroll.size - 1]) + return b->scroll.buf; + return ++row; +} + +static +Row * +bprevrow(Buffer *b, Row *row) +{ + Row *before_start, *before_end, *after_start, *after_end; + Row *first = b->row, *last = b->row + b->h - 1; + + if (!row) + return nil; + + bboundary(b, &before_start, &before_end, &after_start, &after_end); + + if (row > first && row <= last) + return --row; + if (row == first) + return before_end; + if (row == before_start) + return nil; + if (row == after_start) + return last; + if (row == b->scroll.buf) + return &b->scroll.buf[b->scroll.size - 1]; + return --row; +} + +// ----------------------------------------------------------------------- +// windows + +Window * +wmake(Window *root, int top, int left, int w, int h, int scroll) +{ + Window *child, *it; + + child = calloc(1, sizeof(*child)); + child->top = top; + child->left = left; + child->parent = root; + if (root) { + if (root->child) { + for (it = root->child; it->link != nil; it = it->link) + ; + it->link = child; + } else + root->child = child; + + child->curvis = root->curvis; + child->blink = root->blink; + } + + if (!binit((Buffer*)child, w, h, scroll)) { + free(child); + return nil; + } + + return child; +} + +void +wfree(Window *win) +{ + free(win); +} + +void +wresize(Window *win, int w, int h) +{ + bresize((Buffer*)win, w, h); +} + +/* TODO: more sophisticated damage tracking */ +void +wputrune(Window *win, rune r) +{ + Row *row = win->row + win->cur.row; + Cell *cell = row->cells + win->cur.col; + + cell->pen = win->pen; + cell->txt = r; + + if (win->cur.col++ >= win->w) { + win->cur.col = 0; + if (win->cur.row++ >= win->h) + win->cur.row = win->h-1; + } + row->dirty = 1; +} + +void +wscroll(Window *win, int s) +{ + bscroll((Buffer*)win, s); } diff --git a/sys/rules.mk b/sys/rules.mk index fa33346..a98e84c 100644 --- a/sys/rules.mk +++ b/sys/rules.mk @@ -11,13 +11,13 @@ include $(DIR)/rules.mk DIR := $(d)/libn include $(DIR)/rules.mk +# DIR := $(d)/libterm +# include $(DIR)/rules.mk + DIR := $(d)/libmath include $(DIR)/rules.mk DIR := $(d)/libbio include $(DIR)/rules.mk -DIR := $(d)/libgpu -include $(DIR)/rules.mk - include share/pop.mk -- cgit v1.2.1