diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | Makefile | 46 | ||||
| -rw-r--r-- | README.md | 87 | ||||
| -rw-r--r-- | demo/colors16/color16.ha | 65 | ||||
| -rw-r--r-- | demo/fullcolor/fullcolor.ha | 59 | ||||
| -rw-r--r-- | demo/hjkl/hjkl.ha | 190 | ||||
| -rw-r--r-- | demo/size/size.ha | 35 | ||||
| -rw-r--r-- | termbox2/README | 41 | ||||
| -rw-r--r-- | termbox2/attrw+attrw16.ha | 35 | ||||
| -rw-r--r-- | termbox2/attrw+attrw32.ha | 35 | ||||
| -rw-r--r-- | termbox2/attrw.ha | 39 | ||||
| -rw-r--r-- | termbox2/egc+noegc.ha | 30 | ||||
| -rw-r--r-- | termbox2/egc.ha | 31 | ||||
| -rw-r--r-- | termbox2/termbox2.ha | 605 | ||||
| m--------- | vendor/termbox2 | 0 |
16 files changed, 1304 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65c3d19 --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | TODO | ||
| 2 | build | ||
| 3 | **/*.o | ||
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6714657 --- /dev/null +++ b/.gitmodules | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | [submodule "vendor/termbox2"] | ||
| 2 | path = vendor/termbox2 | ||
| 3 | url = https://github.com/termbox/termbox2 | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ef76fbd --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | HARE=hare | ||
| 2 | DESTDIR= | ||
| 3 | PREFIX=/usr/local | ||
| 4 | SRCDIR=$(PREFIX)/src | ||
| 5 | HARESRCDIR=$(SRCDIR)/hare | ||
| 6 | THIRDPARTYDIR=$(HARESRCDIR)/third-party | ||
| 7 | |||
| 8 | TB_DIR=vendor/termbox2 | ||
| 9 | TB_OBJ=termbox2.o | ||
| 10 | BUILD_DIR=build | ||
| 11 | DEMO_DIR=demo | ||
| 12 | |||
| 13 | all: demos | ||
| 14 | |||
| 15 | install: | ||
| 16 | install -Dm644 termbox2/* $(DESTDIR)$(THIRDPARTYDIR)/termbox2/ | ||
| 17 | |||
| 18 | uninstall: | ||
| 19 | rm -rf $(DESTDIR)$(THIRDPARTYDIR)/termbox2 | ||
| 20 | |||
| 21 | demos: $(BUILD_DIR) | ||
| 22 | mkdir -p $(BUILD_DIR)/$(DEMO_DIR) | ||
| 23 | for d in $(DEMO_DIR)/*; \ | ||
| 24 | do $(HARE) build -ltermbox2 -o $(BUILD_DIR)/$$d $$d; \ | ||
| 25 | done | ||
| 26 | |||
| 27 | local: $(BUILD_DIR) termbox2/$(TB_OBJ) | ||
| 28 | mkdir -p $(BUILD_DIR)/$(DEMO_DIR) | ||
| 29 | for d in $(DEMO_DIR)/*; \ | ||
| 30 | do $(HARE) build -lc -o $(BUILD_DIR)/$$d $$d; \ | ||
| 31 | done | ||
| 32 | |||
| 33 | $(BUILD_DIR): | ||
| 34 | mkdir -p $(BUILD_DIR) | ||
| 35 | |||
| 36 | termbox2/$(TB_OBJ): $(TB_DIR)/termbox2.o | ||
| 37 | cp $(TB_DIR)/termbox2.o termbox2/ | ||
| 38 | |||
| 39 | $(TB_DIR)/termbox2.o: | ||
| 40 | cd $(TB_DIR) && make termbox2.o | ||
| 41 | |||
| 42 | clean: | ||
| 43 | rm -rf $(BUILD_DIR) termbox2/$(TB_OBJ) | ||
| 44 | [ -d $(TB_DIR) ] && cd $(TB_DIR) && make clean | ||
| 45 | |||
| 46 | .PHONY: all clean demos install local-demos uninstall | ||
diff --git a/README.md b/README.md new file mode 100644 index 0000000..7620429 --- /dev/null +++ b/README.md | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | # Hare bindings for termbox2 | ||
| 2 | |||
| 3 | This repository contains [hare](https://harelang.org) bindings for the | ||
| 4 | terminal I/O library [termbox2](https://github.com/termbox/termbox2). | ||
| 5 | |||
| 6 | There are some differences between the Hare bindings and the original | ||
| 7 | C code, for example the fact that the prefixes `tb_` and `TB_` have been | ||
| 8 | replaced by the namespace `termbox2::`. See the haredoc documentation | ||
| 9 | in `termbox2/README` for more information. | ||
| 10 | |||
| 11 | ## Using this library | ||
| 12 | |||
| 13 | There are two ways to build termbox2 for this library. Both involve | ||
| 14 | building termbox2 as a standalone library or object file, rather than | ||
| 15 | including it as a header-only library as one could do in a C program. | ||
| 16 | |||
| 17 | ### Using the system's termbox2 (default) | ||
| 18 | |||
| 19 | This method assumes that termbox2 is already installed as a library | ||
| 20 | system-wide, for example by using your OS's package manager or by | ||
| 21 | installing it from source using `make install_lib`. | ||
| 22 | |||
| 23 | Once termbox2 is installed, you can run `make install`. This will | ||
| 24 | copy the hare bindings to `/usr/local/src/hare/third-party/` so that | ||
| 25 | you can include them in your Hare project as any other module. Do | ||
| 26 | not forget to add the `-ltermbox2` option to build any program that | ||
| 27 | uses this library. | ||
| 28 | |||
| 29 | To uninstall the bindings, run `make uninstall`. | ||
| 30 | |||
| 31 | You can also run `make demos` (or simply `make`) to build the demo | ||
| 32 | programs contained in `demo/`, and then you can run them from the | ||
| 33 | `build/` folder. For example, to run the `hjkl` demo you can run | ||
| 34 | `make && ./build/demo/hjkl`. | ||
| 35 | |||
| 36 | ### Using a local termbox2 | ||
| 37 | |||
| 38 | This method is useful if you just want to experiment with termbox2 | ||
| 39 | programming in Hare, for example by running and modifying the demos | ||
| 40 | in `demo/`, without installing anything system-wide. | ||
| 41 | |||
| 42 | To do this, the original library is provided as a | ||
| 43 | [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) | ||
| 44 | in this repository. You can download it with: | ||
| 45 | |||
| 46 | ``` | ||
| 47 | git submodule init | ||
| 48 | ``` | ||
| 49 | |||
| 50 | (Alternative, you can place the source code for termbox2 in | ||
| 51 | `vendor/termbox2`). Then you can build termbox2 locally with | ||
| 52 | |||
| 53 | ``` | ||
| 54 | make local | ||
| 55 | ``` | ||
| 56 | |||
| 57 | This will directly build all the demo programs from `demo/`, placing | ||
| 58 | the resulting binaries in `build/demo`. | ||
| 59 | |||
| 60 | Technical note: this will include the compiled termbox object file | ||
| 61 | `termbox.o` directly in the compiled binaries. | ||
| 62 | |||
| 63 | To build the demo programs using this method, run `make local-demos`. | ||
| 64 | |||
| 65 | ## Notes on build options | ||
| 66 | |||
| 67 | The original termbox2 library offers several build options via `TB_OPT_*` | ||
| 68 | constants. Two of them, `TB_OPT_ATTR_W` and `TB_OPT_EGC`, are relevant for | ||
| 69 | the API. | ||
| 70 | |||
| 71 | * `TB_OPT_ATTR_W` (in the Hare bindings: `termbox2::OPT_ATTR_W`) can be | ||
| 72 | either `16`, `32` or `64`, and is used to enable more output attributer | ||
| 73 | (e.g. truecolor). The termbox2 sources claim that the default value is | ||
| 74 | `16`, but this is only true when including termbox2 as a header-only | ||
| 75 | library. When building it as an external library, the default is `64`. | ||
| 76 | Since these bindings are meant to be used with termbox2 compiled as an | ||
| 77 | external library, we use `64` as default. The hare build tags `attrw16` | ||
| 78 | and `attrw32` are offered if one wants to use a different value. | ||
| 79 | |||
| 80 | * `TB_OPT_EGC` (in the Hare bindings: `termbox2::OPT_EGC`) can be set or | ||
| 81 | not set (in the bindings: it is a boolean value, `true` for set, `false` | ||
| 82 | for not set). Similarly to `TB_OPT_ATTR_W`, it is by default set when | ||
| 83 | building termbox2 as an external library, unlike when including it as a | ||
| 84 | header-only library. An `noegc` tag is available to disable this option. | ||
| 85 | |||
| 86 | Note: the consistency of these two build options between the C library | ||
| 87 | and the Hare bindings is checked and enforced in `termbox2::init()`. | ||
diff --git a/demo/colors16/color16.ha b/demo/colors16/color16.ha new file mode 100644 index 0000000..3121b93 --- /dev/null +++ b/demo/colors16/color16.ha | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | use fmt; | ||
| 2 | use tb = termbox2; | ||
| 3 | |||
| 4 | fn paint_cell(x: size, y: size, c: tb::uattr) void = | ||
| 5 | tb::set_cell(x: int, y: int, ' ', tb::COLOR.DEFAULT, c)!; | ||
| 6 | |||
| 7 | fn show_colors() size = { | ||
| 8 | let colors = [ | ||
| 9 | tb::COLOR.BLACK, | ||
| 10 | tb::COLOR.RED, | ||
| 11 | tb::COLOR.GREEN, | ||
| 12 | tb::COLOR.YELLOW, | ||
| 13 | tb::COLOR.BLUE, | ||
| 14 | tb::COLOR.MAGENTA, | ||
| 15 | tb::COLOR.CYAN, | ||
| 16 | tb::COLOR.WHITE, | ||
| 17 | ]; | ||
| 18 | |||
| 19 | tb::print(0, 0, tb::COLOR.DEFAULT, tb::COLOR.DEFAULT, | ||
| 20 | "8 regular colors:")!; | ||
| 21 | tb::print(0, 2, tb::COLOR.DEFAULT, tb::COLOR.DEFAULT, | ||
| 22 | "8 bright colors:")!; | ||
| 23 | for (let i: size = 0; i < len(colors); i += 1) { | ||
| 24 | // 8 regular colors. | ||
| 25 | paint_cell(i, 1, colors[i]); | ||
| 26 | |||
| 27 | // 8 bright colors. | ||
| 28 | paint_cell(i, 3, colors[i] | tb::ATTRIBUTE.BRIGHT); | ||
| 29 | }; | ||
| 30 | |||
| 31 | return 4; | ||
| 32 | }; | ||
| 33 | |||
| 34 | export fn main() int = { | ||
| 35 | let e = tb::init(); | ||
| 36 | if (e is tb::err) | ||
| 37 | fmt::fatalf(tb::strerror(e as tb::err)); | ||
| 38 | defer tb::shutdown()!; | ||
| 39 | |||
| 40 | let w = tb::width()!; | ||
| 41 | let h = tb::height()!; | ||
| 42 | tb::print(0, (h - 1): int, tb::COLOR.DEFAULT, tb::COLOR.DEFAULT, | ||
| 43 | "Press any key to exit")!; | ||
| 44 | |||
| 45 | show_colors(); | ||
| 46 | |||
| 47 | for (true) { | ||
| 48 | let e = tb::present(); | ||
| 49 | if (e is tb::err) | ||
| 50 | fmt::fatalf(tb::strerror(e as tb::err)); | ||
| 51 | match (tb::poll_event()) { | ||
| 52 | case let e: tb::err => { | ||
| 53 | if (e == tb::ERR.NO_EVENT) | ||
| 54 | continue | ||
| 55 | else | ||
| 56 | tb::print(0, h:int -1, | ||
| 57 | tb::COLOR.DEFAULT, tb::COLOR.DEFAULT, | ||
| 58 | "Unexpected error {}!", tb::strerror(e))!; | ||
| 59 | }; | ||
| 60 | case tb::key_event => break; | ||
| 61 | case (tb::mouse_event | tb::resize_event) => continue; | ||
| 62 | }; | ||
| 63 | }; | ||
| 64 | return 0; | ||
| 65 | }; | ||
diff --git a/demo/fullcolor/fullcolor.ha b/demo/fullcolor/fullcolor.ha new file mode 100644 index 0000000..5d46c7c --- /dev/null +++ b/demo/fullcolor/fullcolor.ha | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | use fmt; | ||
| 2 | use tb = termbox2; | ||
| 3 | |||
| 4 | fn paint_cell(x: size, y: size, c: tb::uattr) void = | ||
| 5 | tb::set_cell(x: int, y: int, ' ', tb::COLOR.DEFAULT, c)!; | ||
| 6 | |||
| 7 | fn show_colors(wmin: size, hmin: size, wmax: size, hmax: size) void = { | ||
| 8 | tb::set_output_mode(tb::OUTPUT.TRUECOLOR)!; | ||
| 9 | tb::print(wmin: int, hmin: int, tb::COLOR.DEFAULT, tb::COLOR.DEFAULT, | ||
| 10 | "Color palette sample:")!; | ||
| 11 | hmin = hmin + 1; | ||
| 12 | |||
| 13 | let nsamples = (wmax - wmin +1) * (hmax - hmin + 1); | ||
| 14 | let step = 0xffffff / nsamples; | ||
| 15 | for (let y = hmin; y < hmax; y += 1) { | ||
| 16 | for (let x = wmin; x < wmax; x += 1) { | ||
| 17 | let c: tb::uattr = ((y - hmin) * wmax + (x - wmin)) * step; | ||
| 18 | paint_cell(x, y, c); | ||
| 19 | }; | ||
| 20 | }; | ||
| 21 | }; | ||
| 22 | |||
| 23 | export fn main() int = { | ||
| 24 | let e = tb::init(); | ||
| 25 | if (e is tb::err) | ||
| 26 | fmt::fatalf(tb::strerror(e as tb::err)); | ||
| 27 | defer tb::shutdown()!; | ||
| 28 | |||
| 29 | let w = tb::width()!; | ||
| 30 | let h = tb::height()!; | ||
| 31 | tb::print(0, (h - 1): int, tb::COLOR.DEFAULT, tb::COLOR.DEFAULT, | ||
| 32 | "Press any key to exit")!; | ||
| 33 | |||
| 34 | switch (tb::OPT_ATTR_W) { | ||
| 35 | case 64 => show_colors(0, 0, w, h-1); | ||
| 36 | case => tb::print(0, ((h+1)/2): int, tb::COLOR.DEFAULT, tb::COLOR.DEFAULT, | ||
| 37 | "This demo is only available with truecolor support, here " | ||
| 38 | "tb::OPT_ATTR_W is {}", tb::OPT_ATTR_W)!; | ||
| 39 | }; | ||
| 40 | |||
| 41 | for (true) { | ||
| 42 | let e = tb::present(); | ||
| 43 | if (e is tb::err) | ||
| 44 | fmt::fatalf(tb::strerror(e as tb::err)); | ||
| 45 | match (tb::poll_event()) { | ||
| 46 | case let e: tb::err => { | ||
| 47 | if (e == tb::ERR.NO_EVENT) | ||
| 48 | continue | ||
| 49 | else | ||
| 50 | tb::print(0, h:int -1, | ||
| 51 | tb::COLOR.DEFAULT, tb::COLOR.DEFAULT, | ||
| 52 | "Unexpected error {}!", tb::strerror(e))!; | ||
| 53 | }; | ||
| 54 | case tb::key_event => break; | ||
| 55 | case (tb::mouse_event | tb::resize_event) => continue; | ||
| 56 | }; | ||
| 57 | }; | ||
| 58 | return 0; | ||
| 59 | }; | ||
diff --git a/demo/hjkl/hjkl.ha b/demo/hjkl/hjkl.ha new file mode 100644 index 0000000..487ebd3 --- /dev/null +++ b/demo/hjkl/hjkl.ha | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | use fmt; | ||
| 2 | use tb = termbox2; | ||
| 3 | |||
| 4 | def bg = tb::COLOR.DEFAULT; | ||
| 5 | def fg = tb::COLOR.DEFAULT | tb::ATTRIBUTE.BOLD; | ||
| 6 | def exit_help: str = "Use :q to exit (colon, q, enter). Sad to see you go :("; | ||
| 7 | |||
| 8 | let wpos: (int, int) = (-1, -1); | ||
| 9 | let msg: str = "Use HJKL to move the widget around"; | ||
| 10 | let colon_mode: bool = false; | ||
| 11 | let exit_attempts: size = 0; | ||
| 12 | |||
| 13 | type cmd = enum { | ||
| 14 | UP, | ||
| 15 | DOWN, | ||
| 16 | LEFT, | ||
| 17 | RIGHT, | ||
| 18 | COLON, | ||
| 19 | ARROW, | ||
| 20 | Q, | ||
| 21 | ENTER, | ||
| 22 | EXIT_ATTEMPT, | ||
| 23 | NONE, | ||
| 24 | }; | ||
| 25 | |||
| 26 | fn draw_small_widget_thing(w: size, h: size) void = { | ||
| 27 | if (wpos.0 < 4) wpos.0 += 1 | ||
| 28 | else if (wpos.0 > w: int - 5) wpos.0 -= 1 | ||
| 29 | else if (wpos.1 < 2) wpos.1 += 1 | ||
| 30 | else if (wpos.1 > h: int - 3) wpos.1 -= 1; | ||
| 31 | |||
| 32 | tb::print(wpos.0, wpos.1 - 1, fg, bg, "K")!; | ||
| 33 | tb::print(wpos.0, wpos.1 + 1, fg, bg, "J")!; | ||
| 34 | tb::print(wpos.0 - 2, wpos.1, fg, bg, "H + L")!; | ||
| 35 | }; | ||
| 36 | |||
| 37 | fn paint(x: int, y: int, c: tb::uattr) void = | ||
| 38 | tb::set_cell(x, y, ' ', tb::COLOR.DEFAULT, c)!; | ||
| 39 | |||
| 40 | fn paint_col3(x: int, y: int, c: tb::uattr) void = { | ||
| 41 | paint(x, y-1, c); | ||
| 42 | paint(x, y, c); | ||
| 43 | paint(x, y+1, c); | ||
| 44 | }; | ||
| 45 | |||
| 46 | fn paint_row5(x: int, y: int, c: tb::uattr) void = { | ||
| 47 | paint(x-2, y, c); | ||
| 48 | paint(x-1, y, c); | ||
| 49 | paint(x, y, c); | ||
| 50 | paint(x+1, y, c); | ||
| 51 | paint(x+2, y, c); | ||
| 52 | }; | ||
| 53 | |||
| 54 | fn paint_cell(x: int, y: int, c: tb::uattr, s: str) void = { | ||
| 55 | paint_col3(x-2, y, c); | ||
| 56 | paint_row5(x, y-1, c); | ||
| 57 | tb::print(x, y, fg, bg, s)!; | ||
| 58 | paint_row5(x, y+1, c); | ||
| 59 | paint_col3(x+2, y, c); | ||
| 60 | }; | ||
| 61 | |||
| 62 | fn draw_large_widget_thing(w: size, h: size) void = { | ||
| 63 | if (wpos.0 < 10) wpos.0 += 1 | ||
| 64 | else if (wpos.0 > w: int - 11) wpos.0 -= 1 | ||
| 65 | else if (wpos.1 < 7) wpos.1 += 1 | ||
| 66 | else if (wpos.1 > h: int - 8) wpos.1 -= 1; | ||
| 67 | |||
| 68 | let c = tb::COLOR.BLUE; | ||
| 69 | paint_cell(wpos.0-6, wpos.1, c, "H"); | ||
| 70 | paint_cell(wpos.0+6, wpos.1, c, "L"); | ||
| 71 | paint_cell(wpos.0, wpos.1-4, c, "K"); | ||
| 72 | paint_cell(wpos.0, wpos.1+4, c, "J"); | ||
| 73 | paint_col3(wpos.0, wpos.1, c); | ||
| 74 | paint_row5(wpos.0, wpos.1, c); | ||
| 75 | }; | ||
| 76 | |||
| 77 | fn draw_widget_thing(w: size, h: size) void = { | ||
| 78 | if (w < 30 || h < 15) | ||
| 79 | draw_small_widget_thing(w, h) | ||
| 80 | else | ||
| 81 | draw_large_widget_thing(w, h); | ||
| 82 | }; | ||
| 83 | |||
| 84 | fn draw_bottom_bar(w: size, h: size) void = { | ||
| 85 | tb::print(0, h: int - 1, fg, bg, msg)!; | ||
| 86 | }; | ||
| 87 | |||
| 88 | fn handle_events_and_exit() bool = { | ||
| 89 | let e = tb::poll_event(); | ||
| 90 | if (e is tb::err) | ||
| 91 | return false; | ||
| 92 | |||
| 93 | return match (e as tb::event) { | ||
| 94 | case tb::resize_event => yield false; | ||
| 95 | case tb::mouse_event => yield { | ||
| 96 | msg = "A mouse? Are you serious?"; | ||
| 97 | yield false; | ||
| 98 | }; | ||
| 99 | case let ke: tb::key_event => yield handle_key_event_and_exit(ke); | ||
| 100 | }; | ||
| 101 | }; | ||
| 102 | |||
| 103 | fn is_arrow(k: tb::key) bool = | ||
| 104 | k == tb::KEY.ARROW_UP || k == tb::KEY.ARROW_DOWN || | ||
| 105 | k == tb::KEY.ARROW_LEFT || k == tb::KEY.ARROW_RIGHT; | ||
| 106 | |||
| 107 | fn is_exit_attempt(k: tb::key) bool = | ||
| 108 | k == tb::KEY.CTRL_Q || k == tb::KEY.CTRL_C || k == tb::KEY.CTRL_X || | ||
| 109 | k == tb::KEY.CTRL_Z || k == tb::KEY.CTRL_D || | ||
| 110 | k == tb::KEY.DELETE || k == tb::KEY.BACKSPACE || k == tb::KEY.ESC; | ||
| 111 | |||
| 112 | fn get_cmd(ke: tb::key_event) cmd = match (ke.key_or_ch) { | ||
| 113 | case let r: rune => yield | ||
| 114 | if (r == 'h') cmd::LEFT | ||
| 115 | else if (r == 'j') cmd::DOWN | ||
| 116 | else if (r == 'k') cmd::UP | ||
| 117 | else if (r == 'l') cmd::RIGHT | ||
| 118 | else if (r == ':') cmd::COLON | ||
| 119 | else if (r == 'q') cmd::Q | ||
| 120 | else if (r == 'Q') cmd::EXIT_ATTEMPT | ||
| 121 | else cmd::NONE; | ||
| 122 | case let k: tb::key => yield | ||
| 123 | if (k == tb::KEY.ENTER) cmd::ENTER | ||
| 124 | else if (is_arrow(k)) cmd::ARROW | ||
| 125 | else if (is_exit_attempt(k)) cmd::EXIT_ATTEMPT | ||
| 126 | else cmd::NONE; | ||
| 127 | }; | ||
| 128 | |||
| 129 | fn exit_attempt() void = { | ||
| 130 | exit_attempts += 1; | ||
| 131 | msg = if (exit_attempts < 3) "Trying to get out? :3" | ||
| 132 | else if (exit_attempts < 6) "It's just like Vim!" | ||
| 133 | else if (exit_attempts < 8) "Uhm no, that won't work" | ||
| 134 | else if (exit_attempts < 10) "Ok, I'll tell you..." | ||
| 135 | else exit_help; | ||
| 136 | }; | ||
| 137 | |||
| 138 | fn handle_key_event_and_exit(ke: tb::key_event) bool = { | ||
| 139 | let c = get_cmd(ke); | ||
| 140 | |||
| 141 | if (c == cmd::COLON) | ||
| 142 | colon_mode = true; | ||
| 143 | |||
| 144 | if (colon_mode) { | ||
| 145 | if (msg == ":q" && c == cmd::ENTER) | ||
| 146 | return true; | ||
| 147 | |||
| 148 | msg = if (msg == ":") { | ||
| 149 | yield if (c == cmd::Q) ":q" else exit_help; | ||
| 150 | } else if (c == cmd::COLON) ":" else exit_help; | ||
| 151 | |||
| 152 | return false; | ||
| 153 | }; | ||
| 154 | |||
| 155 | switch (c) { | ||
| 156 | case cmd::UP => wpos.1 -= 1; | ||
| 157 | case cmd::DOWN => wpos.1 += 1; | ||
| 158 | case cmd::LEFT => wpos.0 -= 1; | ||
| 159 | case cmd::RIGHT => wpos.0 += 1; | ||
| 160 | case cmd::ARROW => msg = "No arrows allowed!"; | ||
| 161 | case cmd::COLON => colon_mode = true; | ||
| 162 | case cmd::EXIT_ATTEMPT => exit_attempt(); | ||
| 163 | case cmd::Q => exit_attempt(); | ||
| 164 | case => yield; | ||
| 165 | }; | ||
| 166 | |||
| 167 | return false; | ||
| 168 | }; | ||
| 169 | |||
| 170 | export fn main() void = { | ||
| 171 | let e = tb::init(); | ||
| 172 | if (e is tb::err) | ||
| 173 | fmt::fatalf(tb::strerror(e as tb::err)); | ||
| 174 | defer tb::shutdown()!; | ||
| 175 | |||
| 176 | wpos = (tb::width()!: int / 2, tb::height()!: int / 2); | ||
| 177 | for (true) { | ||
| 178 | tb::clear()!; | ||
| 179 | |||
| 180 | let w = tb::width()!; | ||
| 181 | let h = tb::height()!; | ||
| 182 | |||
| 183 | draw_widget_thing(w, h); | ||
| 184 | draw_bottom_bar(w, h); | ||
| 185 | tb::present()!; | ||
| 186 | |||
| 187 | if (handle_events_and_exit()) break; | ||
| 188 | }; | ||
| 189 | return; | ||
| 190 | }; | ||
diff --git a/demo/size/size.ha b/demo/size/size.ha new file mode 100644 index 0000000..55b6a3c --- /dev/null +++ b/demo/size/size.ha | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | use fmt; | ||
| 2 | use tb = termbox2; | ||
| 3 | |||
| 4 | export fn main() void = { | ||
| 5 | let e = tb::init(); | ||
| 6 | if (e is tb::err) | ||
| 7 | fmt::fatalf(tb::strerror(e as tb::err)); | ||
| 8 | defer tb::shutdown()!; | ||
| 9 | |||
| 10 | let w = tb::width()!; | ||
| 11 | let h = tb::height()!; | ||
| 12 | tb::print(0, 0, tb::COLOR.GREEN, tb::COLOR.BLACK, | ||
| 13 | "Terminal width: {}", w)!; | ||
| 14 | tb::print(0, 1, tb::COLOR.GREEN, tb::COLOR.BLACK, | ||
| 15 | "Terminal height: {}", h)!; | ||
| 16 | tb::print(0, 2, tb::COLOR.WHITE, tb::COLOR.BLACK, | ||
| 17 | "Press any key to exit")!; | ||
| 18 | for (true) { | ||
| 19 | let e = tb::present(); | ||
| 20 | if (e is tb::err) | ||
| 21 | fmt::fatalf(tb::strerror(e as tb::err)); | ||
| 22 | match (tb::poll_event()) { | ||
| 23 | case let e: tb::err => { | ||
| 24 | if (e == tb::ERR.NO_EVENT) | ||
| 25 | continue | ||
| 26 | else | ||
| 27 | tb::print(0, 4, tb::COLOR.WHITE, tb::COLOR.BLACK, | ||
| 28 | "Unexpected error {}!", tb::strerror(e))!; | ||
| 29 | }; | ||
| 30 | case tb::key_event => break; | ||
| 31 | case (tb::mouse_event | tb::resize_event) => continue; | ||
| 32 | }; | ||
| 33 | }; | ||
| 34 | return; | ||
| 35 | }; | ||
diff --git a/termbox2/README b/termbox2/README new file mode 100644 index 0000000..e28c58c --- /dev/null +++ b/termbox2/README | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | This module provides bindings for termbox2. | ||
| 2 | |||
| 3 | Some changes to the API have been made to enable using this library in a | ||
| 4 | more Hare-idiomatic style. In particular: | ||
| 5 | - The tb_ and TB_ prefixes have been removed, as the namespace termbox2:: | ||
| 6 | is used instead. We recommend importing this library as tb, so that one | ||
| 7 | can write e.g. tb::init(), keeping it similar to the original tb_init(). | ||
| 8 | - Functions return a value that can be either a valid result (e.g. int, | ||
| 9 | void, str...) or an int-backed error [[termbox2::err]]. The error codes | ||
| 10 | are consistent with the original library and can be interpreted with | ||
| 11 | [[termbox2::strerror]]. Some additional codes for binding-related errors | ||
| 12 | are provided. | ||
| 13 | - Similarly to the previous point, some functions that use to return | ||
| 14 | multiple values by taking pointer parameters now simply return a tuple, | ||
| 15 | see for example [[termbox2::get_fds]]. | ||
| 16 | - The tb_event type has changed into a tagged union of 3 types, | ||
| 17 | see [[termbox2::event]]. | ||
| 18 | - The four tb_print* functions have been replaced by a single | ||
| 19 | [[termbox2::print]] function that uses Hare-style string formatting. | ||
| 20 | Similarly, tb_send and tb_sendf have replaced by only one function | ||
| 21 | [[termbox2::send]]. | ||
| 22 | - The constants provided by ther termbox2 API, such as those for key codes, | ||
| 23 | colors and attributes, have been split into various structures, such as | ||
| 24 | [[termbox2::KEY]], [[termbox2::COLOR]] and [[termbox2::ATTRIBUTE]]. | ||
| 25 | |||
| 26 | Some functionality has been outright removed. If you are interested in | ||
| 27 | having any of the following available in these bindings, don't hesitate | ||
| 28 | to send a patch or a feature request to sebastiano@tronto.net: | ||
| 29 | - The tb_get_cell function is not available. This function makes it | ||
| 30 | possible to navigate through cells via pointer arithmetic, but since | ||
| 31 | the Hare [[termbox2::cell]] type does not have the same size as the | ||
| 32 | original tb_cell, this would not translate cleanly. | ||
| 33 | - The tb_iswprint, tb_wcwidth and tb_utf8_* have functions have been | ||
| 34 | removed. Use the Hare standard libary functions [[encoding::utf8::]] | ||
| 35 | and [[strings::]] instead. | ||
| 36 | - The tb_last_errno function has been removed. | ||
| 37 | - The TB_CAP_* code constants have been removed. | ||
| 38 | |||
| 39 | Links: | ||
| 40 | - Main repository: https://git.tronto.net/hare-termbox2 | ||
| 41 | - termbox2: https://github.com/termbox/termbox2 | ||
diff --git a/termbox2/attrw+attrw16.ha b/termbox2/attrw+attrw16.ha new file mode 100644 index 0000000..69f457d --- /dev/null +++ b/termbox2/attrw+attrw16.ha | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Integer width of fg and bg attributes. Valid values (assuming system | ||
| 2 | // support) are 16, 32, and 64; see [[termbox2::uattr]]. 32 or 64 enables | ||
| 3 | // OUTPUT.TRUECOLOR, see [[termbox2::OUTPUT]] and | ||
| 4 | // [[termbox2::set_output_mode]]. 64 enables additional style attributes. | ||
| 5 | // Larger values consume more memory in exchange for more features. | ||
| 6 | // Defaults to 64. | ||
| 7 | export def OPT_ATTR_W: size = 16; | ||
| 8 | |||
| 9 | // Unsigned integer type for cell attributes, see [[termbox2::ATTRIBUTE]]. | ||
| 10 | export type uattr = u16; | ||
| 11 | |||
| 12 | // Colors (bitwise), see [[termbox2::cell]]. | ||
| 13 | export def COLOR = struct { | ||
| 14 | DEFAULT: uattr = 0x0000, | ||
| 15 | BLACK: uattr = 0x0001, | ||
| 16 | RED: uattr = 0x0002, | ||
| 17 | GREEN: uattr = 0x0003, | ||
| 18 | YELLOW: uattr = 0x0004, | ||
| 19 | BLUE: uattr = 0x0005, | ||
| 20 | MAGENTA: uattr = 0x0006, | ||
| 21 | CYAN: uattr = 0x0007, | ||
| 22 | WHITE: uattr = 0x0008, | ||
| 23 | }; | ||
| 24 | |||
| 25 | // Style attributes (bitwise), see termbox2::cell. | ||
| 26 | export def ATTRIBUTE = struct { | ||
| 27 | BOLD: uattr = 0x0100, | ||
| 28 | UNDERLINE: uattr = 0x0200, | ||
| 29 | REVERSE: uattr = 0x0400, | ||
| 30 | ITALIC: uattr = 0x0800, | ||
| 31 | BLINK: uattr = 0x1000, | ||
| 32 | HI_BLACK: uattr = 0x2000, | ||
| 33 | BRIGHT: uattr = 0x4000, | ||
| 34 | DIM: uattr = 0x8000, | ||
| 35 | }; | ||
diff --git a/termbox2/attrw+attrw32.ha b/termbox2/attrw+attrw32.ha new file mode 100644 index 0000000..9f5f885 --- /dev/null +++ b/termbox2/attrw+attrw32.ha | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Integer width of fg and bg attributes. Valid values (assuming system | ||
| 2 | // support) are 16, 32, and 64; see [[termbox2::uattr]]. 32 or 64 enables | ||
| 3 | // OUTPUT.TRUECOLOR, see [[termbox2::OUTPUT]] and | ||
| 4 | // [[termbox2::set_output_mode]]. 64 enables additional style attributes. | ||
| 5 | // Larger values consume more memory in exchange for more features. | ||
| 6 | // Defaults to 64. | ||
| 7 | export def OPT_ATTR_W: size = 32; | ||
| 8 | |||
| 9 | // Unsigned integer type for cell attributes, see [[termbox2::ATTRIBUTE]]. | ||
| 10 | export type uattr = u32; | ||
| 11 | |||
| 12 | // Colors (bitwise), see [[termbox2::cell]]. | ||
| 13 | export def COLORS = struct { | ||
| 14 | DEFAULT: uattr = 0x0000, | ||
| 15 | BLACK: uattr = 0x0001, | ||
| 16 | RED: uattr = 0x0002, | ||
| 17 | GREEN: uattr = 0x0003, | ||
| 18 | YELLOW: uattr = 0x0004, | ||
| 19 | BLUE: uattr = 0x0005, | ||
| 20 | MAGENTA: uattr = 0x0006, | ||
| 21 | CYAN: uattr = 0x0007, | ||
| 22 | WHITE: uattr = 0x0008, | ||
| 23 | }; | ||
| 24 | |||
| 25 | // Style attributes (bitwise), see [[termbox2::cell]]. | ||
| 26 | export def ATTRIBUTE = struct { | ||
| 27 | BOLD: uattr = 0x01000000, | ||
| 28 | UNDERLINE: uattr = 0x02000000, | ||
| 29 | REVERSE: uattr = 0x04000000, | ||
| 30 | ITALIC: uattr = 0x08000000, | ||
| 31 | BLINK: uattr = 0x10000000, | ||
| 32 | HI_BLACK: uattr = 0x20000000, | ||
| 33 | BRIGHT: uattr = 0x40000000, | ||
| 34 | DIM: uattr = 0x80000000, | ||
| 35 | }; | ||
diff --git a/termbox2/attrw.ha b/termbox2/attrw.ha new file mode 100644 index 0000000..d337b8f --- /dev/null +++ b/termbox2/attrw.ha | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Integer width of fg and bg attributes. Valid values (assuming system | ||
| 2 | // support) are 16, 32, and 64; see [[termbox2::uattr]]. 32 or 64 enables | ||
| 3 | // OUTPUT.TRUECOLOR, see [[termbox2::OUTPUT]] and | ||
| 4 | // [[termbox2::set_output_mode]]. 64 enables additional style attributes. | ||
| 5 | // Larger values consume more memory in exchange for more features. | ||
| 6 | // Defaults to 64. | ||
| 7 | export def OPT_ATTR_W: size = 64; | ||
| 8 | |||
| 9 | // Unsigned integer type for cell attributes, see [[termbox2::ATTRIBUTE]]. | ||
| 10 | export type uattr = u64; | ||
| 11 | |||
| 12 | // Colors (bitwise), see [[termbox2::cell]]. | ||
| 13 | export def COLOR = struct { | ||
| 14 | DEFAULT: uattr = 0x0000, | ||
| 15 | BLACK: uattr = 0x0001, | ||
| 16 | RED: uattr = 0x0002, | ||
| 17 | GREEN: uattr = 0x0003, | ||
| 18 | YELLOW: uattr = 0x0004, | ||
| 19 | BLUE: uattr = 0x0005, | ||
| 20 | MAGENTA: uattr = 0x0006, | ||
| 21 | CYAN: uattr = 0x0007, | ||
| 22 | WHITE: uattr = 0x0008, | ||
| 23 | }; | ||
| 24 | |||
| 25 | // Style attributes (bitwise), see [[termbox2::cell]]. | ||
| 26 | export def ATTRIBUTE = struct { | ||
| 27 | BOLD: uattr = 0x01000000, | ||
| 28 | UNDERLINE: uattr = 0x02000000, | ||
| 29 | REVERSE: uattr = 0x04000000, | ||
| 30 | ITALIC: uattr = 0x08000000, | ||
| 31 | BLINK: uattr = 0x10000000, | ||
| 32 | HI_BLACK: uattr = 0x20000000, | ||
| 33 | BRIGHT: uattr = 0x40000000, | ||
| 34 | DIM: uattr = 0x80000000, | ||
| 35 | STRIKEOUT: uattr = 0x0000000100000000, | ||
| 36 | UNDERLINE_2: uattr = 0x0000000200000000, | ||
| 37 | OVERLINE: uattr = 0x0000000400000000, | ||
| 38 | INVISIBLE: uattr = 0x0000000800000000, | ||
| 39 | }; | ||
diff --git a/termbox2/egc+noegc.ha b/termbox2/egc+noegc.ha new file mode 100644 index 0000000..40d1b33 --- /dev/null +++ b/termbox2/egc+noegc.ha | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | // If set, enable extended grapheme cluster support (see | ||
| 2 | // [[termbox2::extend_cell]] and [[termbox2::set_cell_ex]]). Consumes more | ||
| 3 | // memory. Defaults on. | ||
| 4 | export def OPT_EGC: bool = false; | ||
| 5 | |||
| 6 | // A cell in a 2d grid representing the terminal screen. | ||
| 7 | // | ||
| 8 | // The terminal screen is represented as a 2d array of cells. The structure is | ||
| 9 | // optimized for dealing with single-width (wcwidth==1) Unicode codepoints, | ||
| 10 | // however some support for grapheme clusters (e.g., combining diacritical | ||
| 11 | // marks) and wide codepoints (e.g., Hiragana) is provided through ech via | ||
| 12 | // [[termbox2::set_cell_ex]]. ech is only valid if it is non-empty, otherwise | ||
| 13 | // ch is used. | ||
| 14 | // | ||
| 15 | // For non-single-width codepoints, given W=wcwidth(ch || ech): | ||
| 16 | // | ||
| 17 | // - when W<=0: termbox forces a single-width cell. Callers should avoid this | ||
| 18 | // if aiming to render text accurately. Callers may use | ||
| 19 | // [[termbox2::set_cell_ex]] or [[termbox2::print]] or similar to render | ||
| 20 | // W==0 combining characters. | ||
| 21 | // - when W>=2: termbox zeroes out the following W-1 cells and skips | ||
| 22 | // sending them to the tty. So, e.g., if the caller sets x=0,y=0 to a | ||
| 23 | // W==2 codepoint, the caller's next set should be at x=2,y=0. Anything | ||
| 24 | // set at x=1,y=0 will be ignored. If there are not enough columns | ||
| 25 | // remaining on th eline to render W cells, spaces are sent instead. | ||
| 26 | export type cell = struct { | ||
| 27 | ch: u32, // a Unicode codepoint | ||
| 28 | fg: uattr, // bitwise foreground attributes | ||
| 29 | bg: uattr, // bitwise background attributes | ||
| 30 | }; | ||
diff --git a/termbox2/egc.ha b/termbox2/egc.ha new file mode 100644 index 0000000..a14e7ef --- /dev/null +++ b/termbox2/egc.ha | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // If set, enable extended grapheme cluster support (see | ||
| 2 | // [[termbox2::extend_cell]] and [[termbox2::set_cell_ex]]). Consumes more | ||
| 3 | // memory. Defaults on. | ||
| 4 | export def OPT_EGC: bool = true; | ||
| 5 | |||
| 6 | // A cell in a 2d grid representing the terminal screen. | ||
| 7 | // | ||
| 8 | // The terminal screen is represented as a 2d array of cells. The structure is | ||
| 9 | // optimized for dealing with single-width (wcwidth==1) Unicode codepoints, | ||
| 10 | // however some support for grapheme clusters (e.g., combining diacritical | ||
| 11 | // marks) and wide codepoints (e.g., Hiragana) is provided through ech via | ||
| 12 | // [[termbox2::set_cell_ex]]. ech is only valid if it is non-empty, otherwise | ||
| 13 | // ch is used. | ||
| 14 | // | ||
| 15 | // For non-single-width codepoints, given W=wcwidth(ch || ech): | ||
| 16 | // | ||
| 17 | // - when W<=0: termbox forces a single-width cell. Callers should avoid this | ||
| 18 | // if aiming to render text accurately. Callers may use | ||
| 19 | // [[termbox2::set_cell_ex]] or [[termbox2::print]] or similar to render | ||
| 20 | // W==0 combining characters. | ||
| 21 | // - when W>=2: termbox zeroes out the following W-1 cells and skips | ||
| 22 | // sending them to the tty. So, e.g., if the caller sets x=0,y=0 to a | ||
| 23 | // W==2 codepoint, the caller's next set should be at x=2,y=0. Anything | ||
| 24 | // set at x=1,y=0 will be ignored. If there are not enough columns | ||
| 25 | // remaining on th eline to render W cells, spaces are sent instead. | ||
| 26 | export type cell = struct { | ||
| 27 | ch: u32, // a Unicode codepoint | ||
| 28 | fg: uattr, // bitwise foreground attributes | ||
| 29 | bg: uattr, // bitwise background attributes | ||
| 30 | ech: []rune, // a grapheme cluster of Unicode codepoints | ||
| 31 | }; | ||
diff --git a/termbox2/termbox2.ha b/termbox2/termbox2.ha new file mode 100644 index 0000000..f8476ee --- /dev/null +++ b/termbox2/termbox2.ha | |||
| @@ -0,0 +1,605 @@ | |||
| 1 | use encoding::utf8; | ||
| 2 | use fmt; | ||
| 3 | use types::c; | ||
| 4 | |||
| 5 | // Type for key values, see [[termbox2::KEY]]. | ||
| 6 | export type key = u16; | ||
| 7 | |||
| 8 | // ASCII constants, see [[termbox2::key_event]].key. | ||
| 9 | export def KEY = struct { | ||
| 10 | CTRL_TILDE: key = 0x00, | ||
| 11 | CTRL_2: key = 0x00, // clash with CTRL_TILDE | ||
| 12 | CTRL_A: key = 0x01, | ||
| 13 | CTRL_B: key = 0x02, | ||
| 14 | CTRL_C: key = 0x03, | ||
| 15 | CTRL_D: key = 0x04, | ||
| 16 | CTRL_E: key = 0x05, | ||
| 17 | CTRL_F: key = 0x06, | ||
| 18 | CTRL_G: key = 0x07, | ||
| 19 | BACKSPACE: key = 0x08, | ||
| 20 | CTRL_H: key = 0x08, // clash with CTRL_BACKSPACE | ||
| 21 | TAB: key = 0x09, | ||
| 22 | CTRL_I: key = 0x09, // clash with TAB | ||
| 23 | CTRL_J: key = 0x0a, | ||
| 24 | CTRL_K: key = 0x0b, | ||
| 25 | CTRL_L: key = 0x0c, | ||
| 26 | ENTER: key = 0x0d, | ||
| 27 | CTRL_M: key = 0x0d, // clash with ENTER | ||
| 28 | CTRL_N: key = 0x0e, | ||
| 29 | CTRL_O: key = 0x0f, | ||
| 30 | CTRL_P: key = 0x10, | ||
| 31 | CTRL_Q: key = 0x11, | ||
| 32 | CTRL_R: key = 0x12, | ||
| 33 | CTRL_S: key = 0x13, | ||
| 34 | CTRL_T: key = 0x14, | ||
| 35 | CTRL_U: key = 0x15, | ||
| 36 | CTRL_V: key = 0x16, | ||
| 37 | CTRL_W: key = 0x17, | ||
| 38 | CTRL_X: key = 0x18, | ||
| 39 | CTRL_Y: key = 0x19, | ||
| 40 | CTRL_Z: key = 0x1a, | ||
| 41 | ESC: key = 0x1b, | ||
| 42 | CTRL_LSQ_BRACKET: key = 0x1b, // clash with 'ESC' | ||
| 43 | CTRL_3: key = 0x1b, // clash with 'ESC' | ||
| 44 | CTRL_4: key = 0x1c, | ||
| 45 | CTRL_BACKSLASH: key = 0x1c, // clash with 'CTRL_4' | ||
| 46 | CTRL_5: key = 0x1d, | ||
| 47 | CTRL_RSQ_BRACKET: key = 0x1d, // clash with 'CTRL_5' | ||
| 48 | CTRL_6: key = 0x1e, | ||
| 49 | CTRL_7: key = 0x1f, | ||
| 50 | CTRL_SLASH: key = 0x1f, // clash with 'CTRL_7' | ||
| 51 | CTRL_UNDERSCORE: key = 0x1f, // clash with 'CTRL_7' | ||
| 52 | SPACE: key = 0x20, | ||
| 53 | BACKSPACE2: key = 0x7f, | ||
| 54 | CTRL_8: key = 0x7f, // clash with 'BACKSPACE2' | ||
| 55 | F1: key = (0xffff - 0), | ||
| 56 | F2: key = (0xffff - 1), | ||
| 57 | F3: key = (0xffff - 2), | ||
| 58 | F4: key = (0xffff - 3), | ||
| 59 | F5: key = (0xffff - 4), | ||
| 60 | F6: key = (0xffff - 5), | ||
| 61 | F7: key = (0xffff - 6), | ||
| 62 | F8: key = (0xffff - 7), | ||
| 63 | F9: key = (0xffff - 8), | ||
| 64 | F10: key = (0xffff - 9), | ||
| 65 | F11: key = (0xffff - 10), | ||
| 66 | F12: key = (0xffff - 11), | ||
| 67 | INSERT: key = (0xffff - 12), | ||
| 68 | DELETE: key = (0xffff - 13), | ||
| 69 | HOME: key = (0xffff - 14), | ||
| 70 | END: key = (0xffff - 15), | ||
| 71 | PGUP: key = (0xffff - 16), | ||
| 72 | PGDN: key = (0xffff - 17), | ||
| 73 | ARROW_UP: key = (0xffff - 18), | ||
| 74 | ARROW_DOWN: key = (0xffff - 19), | ||
| 75 | ARROW_LEFT: key = (0xffff - 20), | ||
| 76 | ARROW_RIGHT: key = (0xffff - 21), | ||
| 77 | BACK_TAB: key = (0xffff - 22), | ||
| 78 | MOUSE_LEFT: key = (0xffff - 23), | ||
| 79 | MOUSE_RIGHT: key = (0xffff - 24), | ||
| 80 | MOUSE_MIDDLE: key = (0xffff - 25), | ||
| 81 | MOUSE_RELEASE: key = (0xffff - 26), | ||
| 82 | MOUSE_WHEEL_UP: key = (0xffff - 27), | ||
| 83 | MOUSE_WHEEL_DOWN: key = (0xffff - 28), | ||
| 84 | }; | ||
| 85 | |||
| 86 | // Type for key modifiers, see [[termbox2::MOD]]. | ||
| 87 | export type mod = u8; | ||
| 88 | |||
| 89 | // Key modifiers (bitwise), see [[termbox2::event]].mod. | ||
| 90 | export def MOD = struct { | ||
| 91 | ALT: mod = 1, | ||
| 92 | CTRL: mod = 2, | ||
| 93 | SHIFT: mod = 4, | ||
| 94 | MOTION: mod = 8, | ||
| 95 | }; | ||
| 96 | |||
| 97 | // Type for input modes, see [[termbox2::INPUT]]. | ||
| 98 | export type input_mode = int; | ||
| 99 | |||
| 100 | // Input modes (bitwise), see [[termbox2::set_input_mode]]. | ||
| 101 | export def INPUT = struct { | ||
| 102 | CURRENT: input_mode = 0, | ||
| 103 | ESC: input_mode = 1, | ||
| 104 | ALT: input_mode = 2, | ||
| 105 | MOUSE: input_mode = 4, | ||
| 106 | }; | ||
| 107 | |||
| 108 | // Type for output modes, see [[termbox2::OUTPUT]]. | ||
| 109 | export type output_mode = int; | ||
| 110 | |||
| 111 | // Output modes, see [[termbox2::set_output_mode]]. | ||
| 112 | export def OUTPUT = struct { | ||
| 113 | CURRENT: output_mode = 0, | ||
| 114 | NORMAL: output_mode = 1, | ||
| 115 | _256: output_mode = 2, | ||
| 116 | _216: output_mode = 3, | ||
| 117 | GRAYSCALE: output_mode = 4, | ||
| 118 | TRUECOLOR: output_mode = 5, // only available if OPT_ATTR_W >= 32 | ||
| 119 | }; | ||
| 120 | |||
| 121 | // Type for error return values, see [[termbox2::ERR]]. | ||
| 122 | export type err = !int; | ||
| 123 | |||
| 124 | // Common error return values. | ||
| 125 | // | ||
| 126 | // Library behavior is undefined after receiving ERR.MEM. | ||
| 127 | // Callers may attempt reinitializing by freeing memory, invoking | ||
| 128 | // [[termbox2::shutdown]], then [[termbox2::init]]. | ||
| 129 | export def ERR = struct { | ||
| 130 | ERR: err = -1, | ||
| 131 | NEED_MORE: err = -2, | ||
| 132 | INIT_ALREADY: err = -3, | ||
| 133 | INIT_OPEN: err = -4, | ||
| 134 | MEM: err = -5, | ||
| 135 | NO_EVENT: err = -6, | ||
| 136 | NO_TERM: err = -7, | ||
| 137 | NOT_INIT: err = -8, | ||
| 138 | OUT_OF_BOUNDS: err = -9, | ||
| 139 | READ: err = -10, | ||
| 140 | RESIZE_IOCTL: err = -11, | ||
| 141 | RESIZE_PIPE: err = -12, | ||
| 142 | RESIZE_SIGACTION: err = -13, | ||
| 143 | POLL: err = -14, | ||
| 144 | SELECT: err = -14, | ||
| 145 | TCGETATTR: err = -15, | ||
| 146 | TCSETATTR: err = -16, | ||
| 147 | UNSUPPORTED_TERM: err = -17, | ||
| 148 | RESIZE_WRITE: err = -18, | ||
| 149 | RESIZE_POLL: err = -19, | ||
| 150 | RESIZE_SELECT: err = -19, | ||
| 151 | RESIZE_READ: err = -20, | ||
| 152 | RESIZE_SSCANF: err = -21, | ||
| 153 | CAP_COLLISION: err = -22, | ||
| 154 | HARE_BUILD: err = -9990, // Additional error type | ||
| 155 | HARE_INVALID_UTF8: err = -9991, // Additional error type | ||
| 156 | HARE_OTHER: err = -9992, // Additional error type | ||
| 157 | }; | ||
| 158 | |||
| 159 | // An incoming key press event. Note that there is overlap between | ||
| 160 | // MOD.CTRL and KEY.CTRL_* keys. MOD.CTRL and MOD.SHIFT are only set | ||
| 161 | // as modifiers to KEY.ARROW_*. | ||
| 162 | export type key_event = struct { | ||
| 163 | mod: mod, // Modifier | ||
| 164 | key_or_ch: (key | rune), // A [[termbox2::KEY]] or a Unicode code point | ||
| 165 | }; | ||
| 166 | |||
| 167 | // An incoming resize event. | ||
| 168 | export type resize_event = struct { | ||
| 169 | w: i32, // resize width | ||
| 170 | h: i32, // resize height | ||
| 171 | }; | ||
| 172 | |||
| 173 | // An incoming mouse event. | ||
| 174 | export type mouse_event = struct { | ||
| 175 | key: key, // Mouse button, e.g. termbox2::KEY.MOUSE_LEFT | ||
| 176 | x: i32, | ||
| 177 | y: i32, | ||
| 178 | }; | ||
| 179 | |||
| 180 | // The C data structure for an event, for compatibility. | ||
| 181 | type tb_event = struct { | ||
| 182 | t: u8, | ||
| 183 | mod: u8, | ||
| 184 | key: u16, | ||
| 185 | ch: u32, | ||
| 186 | w: i32, | ||
| 187 | h: i32, | ||
| 188 | x: i32, | ||
| 189 | y: i32, | ||
| 190 | }; | ||
| 191 | |||
| 192 | def TB_EVENT_KEY: u8 = 1; | ||
| 193 | def TB_EVENT_RESIZE: u8 = 2; | ||
| 194 | def TB_EVENT_MOUSE: u8 = 3; | ||
| 195 | fn tb_event_to_event(e: tb_event) (event | err) = switch (e.t) { | ||
| 196 | case TB_EVENT_KEY => yield key_event { | ||
| 197 | mod = e.mod, | ||
| 198 | key_or_ch = if (e.key != 0) e.key: key else e.ch: rune, | ||
| 199 | }; | ||
| 200 | case TB_EVENT_RESIZE => yield resize_event { | ||
| 201 | w = e.w, | ||
| 202 | h = e.h, | ||
| 203 | }; | ||
| 204 | case TB_EVENT_MOUSE => yield mouse_event { | ||
| 205 | key = e.key, | ||
| 206 | x = e.x, | ||
| 207 | y = e.y, | ||
| 208 | }; | ||
| 209 | case => yield ERR.HARE_OTHER; | ||
| 210 | }; | ||
| 211 | |||
| 212 | // Any incoming event from the tty. | ||
| 213 | export type event = (key_event | resize_event | mouse_event); | ||
| 214 | |||
| 215 | fn build_opts_match() bool = | ||
| 216 | (has_egc() == OPT_EGC) && | ||
| 217 | (attr_width() == OPT_ATTR_W) && | ||
| 218 | (has_truecolor() == (OPT_ATTR_W == 32 || OPT_ATTR_W == 64)); | ||
| 219 | |||
| 220 | fn void_or_err(r: int) (void | err) = if (r < 0) r: err; | ||
| 221 | fn int_or_err(r: int) (int | err) = if (r < 0) r: err else r; | ||
| 222 | fn size_or_err(r: int) (size | err) = if (r < 0) r: err else r: size; | ||
| 223 | fn str_or_err(cstr: *c::char) (str | err) = match(c::tostr(cstr)) { | ||
| 224 | case utf8::invalid => yield ERR.HARE_INVALID_UTF8; | ||
| 225 | case let s: str => yield s; | ||
| 226 | }; | ||
| 227 | |||
| 228 | // Initialize the termbox library. This function should be called before | ||
| 229 | // any other functions. After successful initialization, the library must | ||
| 230 | // be finalized using [[termbox2::shutdown]]. | ||
| 231 | export fn init() (void | err) = | ||
| 232 | if (!build_opts_match()) | ||
| 233 | ERR.HARE_BUILD | ||
| 234 | else | ||
| 235 | void_or_err(tb_init()); | ||
| 236 | @symbol("tb_init") fn tb_init() int; | ||
| 237 | |||
| 238 | // Same as [[termbox2::init]], but allows to specify a file path in place | ||
| 239 | // of the default /dev/tty. | ||
| 240 | export fn init_file(path: str) (void | err) = { | ||
| 241 | let c_path = c::fromstr(path)!; | ||
| 242 | defer free(c_path); | ||
| 243 | return if (!build_opts_match()) | ||
| 244 | ERR.HARE_BUILD | ||
| 245 | else | ||
| 246 | void_or_err(tb_init_file(c_path)); | ||
| 247 | }; | ||
| 248 | @symbol("tb_init_file") fn tb_init_file(cstr: *c::char) int; | ||
| 249 | |||
| 250 | // Same as [[termbox2::init]], but allows to specify an output | ||
| 251 | // file descriptor. | ||
| 252 | export fn init_fd(ttyfd: int) (void | err) = | ||
| 253 | if (!build_opts_match()) | ||
| 254 | ERR.HARE_BUILD | ||
| 255 | else | ||
| 256 | void_or_err(tb_init_fd(ttyfd)); | ||
| 257 | @symbol("tb_init_fd") fn tb_init_fd(fd: int) int; | ||
| 258 | |||
| 259 | // Same as [[termbox2::init]], but allows to specify input and output | ||
| 260 | // file descriptors. | ||
| 261 | export fn init_rwfd(rfd: int, wfd: int) (void | err) = | ||
| 262 | if (!build_opts_match()) | ||
| 263 | ERR.HARE_BUILD | ||
| 264 | else | ||
| 265 | void_or_err(tb_init_rwfd(rfd, wfd)); | ||
| 266 | @symbol("tb_init_rwfd") fn tb_init_rwfd(rfd: int, wfd: int) int; | ||
| 267 | |||
| 268 | // Finalizes the library. | ||
| 269 | export fn shutdown() (void | err) = void_or_err(tb_shutdown()); | ||
| 270 | @symbol("tb_shutdown") fn tb_shutdown() int; | ||
| 271 | |||
| 272 | // Return the width of the internal back buffer (which is the same as the | ||
| 273 | // terminal's window width in columns). The internal buffer can be resized | ||
| 274 | // after [[termbox2::clear]] or [[termbox2::present]] calls. Returns ERR.ERR | ||
| 275 | // when called before [[termbox2::init]] or after [[termbox2::shutdown]]. | ||
| 276 | export fn width() (size | err) = size_or_err(tb_width()); | ||
| 277 | @symbol("tb_width") fn tb_width() int; | ||
| 278 | |||
| 279 | // Return the height of the internal back buffer (which is the same as the | ||
| 280 | // terminal's window width in rows). The internal buffer can be resized | ||
| 281 | // after [[termbox2::clear]] or [[termbox2::present]] calls. Returns ERR.ERR | ||
| 282 | // when called before [[termbox2::init]] or after [[termbox2::shutdown]]. | ||
| 283 | export fn height() (size | err) = size_or_err(tb_height()); | ||
| 284 | @symbol("tb_height") fn tb_height() int; | ||
| 285 | |||
| 286 | // Clear the internal back buffer using [[termbox2::ATTRIBUTE]].DEFAULT. | ||
| 287 | export fn clear() (void | err) = void_or_err(tb_clear()); | ||
| 288 | @symbol("tb_clear") fn tb_clear() int; | ||
| 289 | |||
| 290 | // Clear the internal back buffer using the specified attributes. | ||
| 291 | export fn set_clear_attrs(fg: uattr, bg: uattr) (void | err) = | ||
| 292 | void_or_err(tb_set_clear_attrs(fg, bg)); | ||
| 293 | @symbol("tb_set_clear_attrs") fn tb_set_clear_attrs(fg: uattr, bg: uattr) int; | ||
| 294 | |||
| 295 | // Synchronize the internal back buffer with the terminal by | ||
| 296 | // writing to tty. | ||
| 297 | export fn present() (void | err) = void_or_err(tb_present()); | ||
| 298 | @symbol("tb_present") fn tb_present() int; | ||
| 299 | |||
| 300 | // Clear the internal front buffer effectively forcing a complete re-render | ||
| 301 | // of the back buffer to the tty. It is not necessary to call this under | ||
| 302 | // normal circumstances. | ||
| 303 | export fn invalidate() (void | err) = void_or_err(tb_invalidate()); | ||
| 304 | @symbol("tb_invalidate") fn tb_invalidate() int; | ||
| 305 | |||
| 306 | // Set the position of the cursor. Upper-left cell is (0, 0). | ||
| 307 | export fn set_cursor(cx: int, cy: int) (void | err) = | ||
| 308 | void_or_err(tb_set_cursor(cx, cy)); | ||
| 309 | @symbol("tb_set_cursor") fn tb_set_cursor(x: int, y: int) int; | ||
| 310 | |||
| 311 | // Hide the cursor. | ||
| 312 | export fn hide_cursor() (void | err) = void_or_err(tb_hide_cursor()); | ||
| 313 | @symbol("tb_hide_cursor") fn tb_hide_cursor() int; | ||
| 314 | |||
| 315 | // Set cell contents in the internal back buffer at the specified position. | ||
| 316 | // Non-printable runes are replaced with U+FFFD at render time. | ||
| 317 | export fn set_cell( | ||
| 318 | x: int, | ||
| 319 | y: int, | ||
| 320 | ch: rune, | ||
| 321 | fg: uattr, | ||
| 322 | bg: uattr, | ||
| 323 | ) (void | err) = void_or_err(tb_set_cell(x, y, ch: u32, fg, bg)); | ||
| 324 | @symbol("tb_set_cell") fn tb_set_cell( | ||
| 325 | x: int, | ||
| 326 | y: int, | ||
| 327 | ch: u32, | ||
| 328 | fg: uattr, | ||
| 329 | bg: uattr, | ||
| 330 | ) int; | ||
| 331 | |||
| 332 | // Set extended cell contents. Only available when OPT_ATTR_W >= 32. | ||
| 333 | // See also [[termbox2::set_cell]] and [[termbox2::cell]]. | ||
| 334 | export fn set_cell_ex( | ||
| 335 | x: int, | ||
| 336 | y: int, | ||
| 337 | ch: []rune, | ||
| 338 | fg: uattr, | ||
| 339 | bg: uattr, | ||
| 340 | ) (void | err) = | ||
| 341 | void_or_err(tb_set_cell_ex(x, y, &ch[0]: *u32, len(ch), fg, bg)); | ||
| 342 | @symbol("tb_set_cell_ex") fn tb_set_cell_ex( | ||
| 343 | x: int, | ||
| 344 | y: int, | ||
| 345 | ch: *u32, | ||
| 346 | n: size, | ||
| 347 | fg: uattr, | ||
| 348 | bg: uattr, | ||
| 349 | ) int; | ||
| 350 | |||
| 351 | // Add one character to the cell's ech. Only available when OPT_ATTR_W >= 32. | ||
| 352 | // See also [[termbox2::set_cell_ex]] and [[termbox2::cell]]. | ||
| 353 | export fn extend_cell(x: int, y: int, ch: rune) (void | err) = | ||
| 354 | void_or_err(tb_extend_cell(x, y, ch: u32)); | ||
| 355 | @symbol("tb_extend_cell") fn tb_extend_cell(x: int, y: int, ch: u32) int; | ||
| 356 | |||
| 357 | // Set the input mode, see [[termbox2::INPUT]]. | ||
| 358 | // | ||
| 359 | // Termbox has two input modes: | ||
| 360 | // - INPUT.ESC When escape (\x1b) is in the buffer and there's no match | ||
| 361 | // for an escape sequence, a key event for [[termbox2::KEY]].ESC is returned. | ||
| 362 | // - INPUT.ALT When escape (\x1b) is in the buffer and there's no match | ||
| 363 | // for an an escape sequence, the next keyboard event is returned with a | ||
| 364 | // [[termbox2::MOD]].ALT modifier. | ||
| 365 | // | ||
| 366 | // You can also apply termbox2::INPUT.MOUSE via bitwise OR operation to either | ||
| 367 | // of the modes (e.g., INPUT.ALT | INPUT.MOUSE) to receive | ||
| 368 | // [[termbox2::mouse_event]] events. If none of the main two modes were | ||
| 369 | // set, but the mouse mode was, INPUT.ESC is used. If for some reason you've | ||
| 370 | // decided to use INPUT.ESC | INPUT.ALT, it will behave as if only INPUT.ESC | ||
| 371 | // was selected. | ||
| 372 | // | ||
| 373 | // If mode is INPUT.CURRENT, return the current input mode. | ||
| 374 | // | ||
| 375 | // The default input mode is INPUT.ESC. | ||
| 376 | export fn set_input_mode(mode: input_mode) (int | err) = | ||
| 377 | int_or_err(tb_set_input_mode(mode: int)); | ||
| 378 | @symbol("tb_set_input_mode") fn tb_set_input_mode(mode: int) int; | ||
| 379 | |||
| 380 | // Set the output mode, see [[termbox2::OUTPUT]]. | ||
| 381 | // | ||
| 382 | // Termbox has multiple output modes: | ||
| 383 | // | ||
| 384 | // 1. OUTPUT.NORMAL => [0..8] | ||
| 385 | // | ||
| 386 | // This mode provides 8 different colors (see [[termbox2::COLOR]]), plus | ||
| 387 | // COLOR.DEFAULT which skips sending a color code (i.e., uses the terminal's | ||
| 388 | // default color). Colors may be bitwise OR'd with style attributes, see | ||
| 389 | // [[termbox2::ATTRIBUTE]]. Extra attributes are available if OPT_ATTR_W == 64. | ||
| 390 | // | ||
| 391 | // Some notes: ATTRIBUTE.REVERSE and ATTRIBUTE.BRIGHT can be applied as either | ||
| 392 | // fg or bg attributes for the same effect. The rest of the attributes apply to | ||
| 393 | // fg only and are ignored as bg attributes. | ||
| 394 | // | ||
| 395 | // Example usage: | ||
| 396 | // | ||
| 397 | // let fg = termbox2::COLOR.BLACK | termbox2::ATTRIBUTE.BOLD; | ||
| 398 | // let bg = termbox2::COLOR.RED; | ||
| 399 | // termbox2::set_cell(x, y, '@', fg, bg); | ||
| 400 | // | ||
| 401 | // 2. OUTPUT._256 => [0..255] + ATTRIBUTE.HI_BLACK | ||
| 402 | // | ||
| 403 | // In this mode you get 256 distinct colors (plus default): | ||
| 404 | // - 0x00 (1): ATTRIBUTE.DEFAULT | ||
| 405 | // - ATTRIBUTE.HI_BLACK (1): COLOR.BLACK in OUTPUT.NORMAL | ||
| 406 | // - 0x01..0x07 (7): the next 7 colors as in OUTPUT.NORMAL | ||
| 407 | // - 0x08..0x0f (8): bright versions of the above | ||
| 408 | // - 0x10..0xe7 (216): 216 different colors | ||
| 409 | // - 0xe8..0xff (24): 24 different shades of gray | ||
| 410 | // | ||
| 411 | // All style attributes except ATTRIBUTE.BRIGHT may be bitwise OR'd as in | ||
| 412 | // OUTPUT.NORMAL. | ||
| 413 | // | ||
| 414 | // Note COLOR.BLACK must be used for black, as 0x00 represents default. | ||
| 415 | // | ||
| 416 | // 3. OUTPUT._216 => [0..216] | ||
| 417 | // | ||
| 418 | // This mode supports the 216-color range of OUTPUT._256 only, but you | ||
| 419 | // don't need to provide an offset: | ||
| 420 | // - 0x00 (1): COLOR.DEFAULT | ||
| 421 | // - 0x01..0xd8 (216): 216 different colors | ||
| 422 | // | ||
| 423 | // 4. OUTPUT.GRAYSCALE => [0..24] | ||
| 424 | // | ||
| 425 | // This mode supports the 24-color range of OUTPUT._256 only, but you | ||
| 426 | // don't need to provide an offset: | ||
| 427 | // - 0x00 (1): COLOR.DEFAULT | ||
| 428 | // - 0x01..0x18 (24): 24 different shades of gray | ||
| 429 | // | ||
| 430 | // 5. OUTPUT.TRUECOLOR => [0x000000..0xffffff] + ATTRIBUTE.HI_BLACK | ||
| 431 | // | ||
| 432 | // This mode provides 24-bit color on supported terminals. | ||
| 433 | // The format is 0xRRGGBB. | ||
| 434 | // | ||
| 435 | // All style attributes except ATTRIBUTE.BRIGHT may be bitwise OR'd as in | ||
| 436 | // OUTPUT.NORMAL. | ||
| 437 | // | ||
| 438 | // Note ATTRIBUTE.HI_BLACK must be used for black, as 0x000000 represents | ||
| 439 | // default. | ||
| 440 | // | ||
| 441 | // To use the terminal default color (i.e., to not send an escape code), pass | ||
| 442 | // COLOR.DEFAULT. For convenience, the value 0 is interpreted as COLOR.DEFAULT | ||
| 443 | // in all modes. | ||
| 444 | // | ||
| 445 | // Note, cell attributes persist after switching output modes. Any translation | ||
| 446 | // between, for example, OUTPUT.NORMAL's COLOR.RED and OUTPUT.TRUECOLOR's | ||
| 447 | // 0xff0000 must be performed by the caller. Also note that cells previously | ||
| 448 | // rendered in one mode may persist unchanged until the front buffer is cleared | ||
| 449 | // (such as after a resize event) at which point it will be re-interpreted and | ||
| 450 | // flushed according to the current mode. Callers may invoke | ||
| 451 | // [[termbox2::invalidate]] if it is desirable to immediately re-interpret | ||
| 452 | // and flush the entire screen according to the current mode. | ||
| 453 | // | ||
| 454 | // Note, not all terminals support all output modes, especially beyond | ||
| 455 | // OUTPUT.NORMAL. There is also no very reliable way to determine color | ||
| 456 | // support dynamically. If portability is desired, callers are recommended to | ||
| 457 | // use OUTPUT.NORMAL or make output mode end-user configurable. The same | ||
| 458 | // advice applies to style attributes. | ||
| 459 | // | ||
| 460 | // If mode is OUTPUT.CURRENT, return the current output mode. | ||
| 461 | // | ||
| 462 | // The default output mode is OUTPUT.NORMAL. | ||
| 463 | export fn set_output_mode(mode: output_mode) (int | err) = | ||
| 464 | int_or_err(tb_set_output_mode(mode: int)); | ||
| 465 | @symbol("tb_set_output_mode") fn tb_set_output_mode(mode: int) int; | ||
| 466 | |||
| 467 | // Wait for an event up to timeout_ms milliseconds and return it. If no event | ||
| 468 | // is available within the timeout period, ERR.NO_EVENT is returned. On a resize | ||
| 469 | // event, the underlying select(2) call may be interrupted, yielding a return | ||
| 470 | // code of ERR.POLL. | ||
| 471 | export fn peek_event(timeout_ms: size) (event | err) = { | ||
| 472 | let e = tb_event { | ||
| 473 | t = 0, | ||
| 474 | mod = 0, | ||
| 475 | key = 0, | ||
| 476 | ch = 0, | ||
| 477 | w = 0, | ||
| 478 | h = 0, | ||
| 479 | x = 0, | ||
| 480 | y = 0, | ||
| 481 | }; | ||
| 482 | let r = tb_peek_event(&e, timeout_ms: int); | ||
| 483 | return if (r < 0) r: err else tb_event_to_event(e); | ||
| 484 | }; | ||
| 485 | @symbol("tb_peek_event") fn tb_peek_event(ev: *tb_event, t: int) int; | ||
| 486 | |||
| 487 | // Same as [[termbox2::peek_event]] except no timeout. | ||
| 488 | export fn poll_event() (event | err) = { | ||
| 489 | let e = tb_event { | ||
| 490 | t = 0, | ||
| 491 | mod = 0, | ||
| 492 | key = 0, | ||
| 493 | ch = 0, | ||
| 494 | w = 0, | ||
| 495 | h = 0, | ||
| 496 | x = 0, | ||
| 497 | y = 0, | ||
| 498 | }; | ||
| 499 | let r = tb_poll_event(&e); | ||
| 500 | return if (r < 0) r: err else tb_event_to_event(e); | ||
| 501 | }; | ||
| 502 | @symbol("tb_poll_event") fn tb_poll_event(ev: *tb_event) int; | ||
| 503 | |||
| 504 | // Internal termbox fds that can be used with poll(2), select(2), etc. | ||
| 505 | // externally. Callers must invoke [[termbox2::poll_event]] or | ||
| 506 | // [[termbox2::peek_event]] if fds become readable. | ||
| 507 | export fn get_fds() ((int, int) | err) = { | ||
| 508 | let ttyfd: int = 0; | ||
| 509 | let resizefd: int = 0; | ||
| 510 | let r = tb_get_fds(&ttyfd, &resizefd); | ||
| 511 | return if (r < 0) r: err else (ttyfd, resizefd); | ||
| 512 | }; | ||
| 513 | @symbol("tb_get_fds") fn tb_get_fds(ttyfd: *int, resizefd: *int) int; | ||
| 514 | |||
| 515 | // Print function. On success, it returns the width of the printed string. | ||
| 516 | // Strings are interpreted as UTF-8. | ||
| 517 | // | ||
| 518 | // No attempt is made to do proper grapheme cluster parsing. With | ||
| 519 | // [[termbox2::OPT_EGC]] enabled, as a very coarse approximation, codepoints | ||
| 520 | // of width 0 are tacked on to the previous codepoint via | ||
| 521 | // [[termbox2::extend_cell]]. | ||
| 522 | // | ||
| 523 | // Non-printable characters (iswprint(3)) and truncated UTF-8 byte sequences | ||
| 524 | // are replaced with U+FFFD. | ||
| 525 | // | ||
| 526 | // Newlines (\n) are supported with the caveat that the return value will be | ||
| 527 | // the width of the string as if it were on a single line. | ||
| 528 | // | ||
| 529 | // If the starting coordinate is out of bounds, ERR.OUT_OF_BOUNDS is returned. | ||
| 530 | // If the starting coordinates is in bounds, but goes out of bounds, then the | ||
| 531 | // out-of-bounds portions of the string are ignored. | ||
| 532 | // | ||
| 533 | // For finer control, use [[termbox2::set_cell]]. | ||
| 534 | export fn print( | ||
| 535 | x: int, | ||
| 536 | y: int, | ||
| 537 | fg: uattr, | ||
| 538 | bg: uattr, | ||
| 539 | fmt_str: str, | ||
| 540 | args: fmt::field... | ||
| 541 | ) (size | err) = { | ||
| 542 | let s = fmt::asprintf(fmt_str, args...)!; | ||
| 543 | defer free(s); | ||
| 544 | let cstr = c::fromstr(s)!; | ||
| 545 | defer free(cstr); | ||
| 546 | let outw: size = 0; | ||
| 547 | let e = tb_print_ex(x, y, fg, bg, &outw, cstr); | ||
| 548 | return if (e < 0) e: err else outw; | ||
| 549 | }; | ||
| 550 | @symbol("tb_print_ex") fn tb_print_ex( | ||
| 551 | x: int, | ||
| 552 | y: int, | ||
| 553 | fg: uattr, | ||
| 554 | bg: uattr, | ||
| 555 | out_w: *size, | ||
| 556 | cstr: const *c::char, | ||
| 557 | ) int; | ||
| 558 | |||
| 559 | // Send raw bytes to the terminal. | ||
| 560 | export fn send(buf: []u8) (void | err) = | ||
| 561 | void_or_err(tb_send(&buf[0]: *c::char, len(buf))); | ||
| 562 | @symbol("tb_send") fn tb_send(buf: const *c::char, nbuf: size) int; | ||
| 563 | |||
| 564 | // Returns a string explaining the given error code. The caller must free | ||
| 565 | // the return value. | ||
| 566 | export fn strerror(e: err) str = switch (e) { | ||
| 567 | case ERR.HARE_BUILD => | ||
| 568 | yield fmt::asprintf( | ||
| 569 | "Cannot initialize Termbox because of a mismatch " | ||
| 570 | "between the C and Hare build options.\n" | ||
| 571 | "Hare OPT_EGC is {} and C TB_OPT_EGC is {}\n" | ||
| 572 | "Hare OPT_ATTR_W {} and C TB_OPT_ATTR_W is {}", | ||
| 573 | OPT_EGC, has_egc(), OPT_ATTR_W, attr_width() | ||
| 574 | )!; | ||
| 575 | case ERR.HARE_INVALID_UTF8 => | ||
| 576 | yield fmt::asprintf("Invalid UTF8 sequence recieved from tb")!; | ||
| 577 | case ERR.HARE_OTHER => | ||
| 578 | yield fmt::asprintf("Unknown error in the Hare bindings")!; | ||
| 579 | case => yield match (str_or_err(tb_strerror(e: int))) { | ||
| 580 | case err => yield fmt::asprintf( | ||
| 581 | "Unexpected error while interpreting previous error" | ||
| 582 | )!; | ||
| 583 | case let estr: str => yield estr; | ||
| 584 | }; | ||
| 585 | }; | ||
| 586 | @symbol("tb_strerror") fn tb_strerror(e: int) const *c::char; | ||
| 587 | |||
| 588 | // Returns true if the the C termbox2 library was compiled with truecolor | ||
| 589 | // support (i.e., if [[termbox2::OPT_ATTR_W]] is 32 or 64). | ||
| 590 | export fn has_truecolor() bool = tb_has_truecolor() != 0; | ||
| 591 | @symbol("tb_has_truecolor") fn tb_has_truecolor() int; | ||
| 592 | |||
| 593 | // Returns true if the the C termbox2 library was compiled with EGC | ||
| 594 | // support (i.e., if [[termbox2::OPT_EGC]] is true). | ||
| 595 | export fn has_egc() bool = tb_has_egc() != 0; | ||
| 596 | @symbol("tb_has_egc") fn tb_has_egc() int; | ||
| 597 | |||
| 598 | // Returns the value of the TB_OPT_ATTR_W compile time option of the | ||
| 599 | // C termbox2 library. | ||
| 600 | export fn attr_width() size = tb_attr_width(): size; | ||
| 601 | @symbol("tb_attr_width") fn tb_attr_width() int; | ||
| 602 | |||
| 603 | // Returns the string version of the C termbox2 library. | ||
| 604 | export fn version() (str | err) = str_or_err(tb_version()); | ||
| 605 | @symbol("tb_version") fn tb_version() const *c::char; | ||
diff --git a/vendor/termbox2 b/vendor/termbox2 new file mode 160000 | |||
| Subproject cdf62e9990d8b200768780080fb10a4e2f68005 | |||
