mirror of https://github.com/fairyglade/ly.git
				
				
				
			Shift+Tab for previous input
This commit is contained in:
		
							parent
							
								
									1d3148201c
								
							
						
					
					
						commit
						4122dfac16
					
				
							
								
								
									
										23
									
								
								build.zig
								
								
								
								
							
							
						
						
									
										23
									
								
								build.zig
								
								
								
								
							|  | @ -17,19 +17,6 @@ pub fn build(b: *std.Build) void { | ||||||
|     const target = b.standardTargetOptions(.{}); |     const target = b.standardTargetOptions(.{}); | ||||||
|     const optimize = b.standardOptimizeOption(.{}); |     const optimize = b.standardOptimizeOption(.{}); | ||||||
| 
 | 
 | ||||||
|     const c_args = [_][]const u8{ |  | ||||||
|         "-std=c99", |  | ||||||
|         "-pedantic", |  | ||||||
|         "-g", |  | ||||||
|         "-Wall", |  | ||||||
|         "-Wextra", |  | ||||||
|         "-Werror=vla", |  | ||||||
|         "-Wno-unused-parameter", |  | ||||||
|         "-D_DEFAULT_SOURCE", |  | ||||||
|         "-D_POSIX_C_SOURCE=200809L", |  | ||||||
|         "-D_XOPEN_SOURCE", |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const exe = b.addExecutable(.{ |     const exe = b.addExecutable(.{ | ||||||
|         .name = "ly", |         .name = "ly", | ||||||
|         .root_source_file = .{ .path = "src/main.zig" }, |         .root_source_file = .{ .path = "src/main.zig" }, | ||||||
|  | @ -45,19 +32,11 @@ pub fn build(b: *std.Build) void { | ||||||
|     const clap = b.dependency("clap", .{ .target = target, .optimize = optimize }); |     const clap = b.dependency("clap", .{ .target = target, .optimize = optimize }); | ||||||
|     exe.root_module.addImport("clap", clap.module("clap")); |     exe.root_module.addImport("clap", clap.module("clap")); | ||||||
| 
 | 
 | ||||||
|  |     exe.addIncludePath(.{ .path = "include" }); | ||||||
|     exe.linkSystemLibrary("pam"); |     exe.linkSystemLibrary("pam"); | ||||||
|     exe.linkSystemLibrary("xcb"); |     exe.linkSystemLibrary("xcb"); | ||||||
|     exe.linkLibC(); |     exe.linkLibC(); | ||||||
| 
 | 
 | ||||||
|     exe.addIncludePath(.{ .path = "dep/termbox_next/src" }); |  | ||||||
| 
 |  | ||||||
|     exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/input.c" }, .flags = &c_args }); |  | ||||||
|     exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/memstream.c" }, .flags = &c_args }); |  | ||||||
|     exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/ringbuffer.c" }, .flags = &c_args }); |  | ||||||
|     exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/term.c" }, .flags = &c_args }); |  | ||||||
|     exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/termbox.c" }, .flags = &c_args }); |  | ||||||
|     exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/utf8.c" }, .flags = &c_args }); |  | ||||||
| 
 |  | ||||||
|     b.installArtifact(exe); |     b.installArtifact(exe); | ||||||
| 
 | 
 | ||||||
|     const run_cmd = b.addRunArtifact(exe); |     const run_cmd = b.addRunArtifact(exe); | ||||||
|  |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| bin |  | ||||||
| obj |  | ||||||
| src/demo/*.o |  | ||||||
| src/demo/keyboard |  | ||||||
| src/demo/output |  | ||||||
| src/demo/paint |  | ||||||
| src/demo/truecolor |  | ||||||
|  | @ -1,19 +0,0 @@ | ||||||
| Copyright (C) 2010-2013 nsf <no.smile.face@gmail.com> |  | ||||||
| 
 |  | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
| of this software and associated documentation files (the "Software"), to deal |  | ||||||
| in the Software without restriction, including without limitation the rights |  | ||||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
| copies of the Software, and to permit persons to whom the Software is |  | ||||||
| furnished to do so, subject to the following conditions: |  | ||||||
| 
 |  | ||||||
| The above copyright notice and this permission notice shall be included in |  | ||||||
| all copies or substantial portions of the Software. |  | ||||||
| 
 |  | ||||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
| THE SOFTWARE. |  | ||||||
|  | @ -1,57 +0,0 @@ | ||||||
| # Termbox |  | ||||||
| [Termbox](https://github.com/nsf/termbox) |  | ||||||
| was a promising Text User Interface (TUI) library. |  | ||||||
| Unfortunately, its original author |  | ||||||
| [changed his mind](https://github.com/nsf/termbox/issues/37#issuecomment-261075481) |  | ||||||
| about consoles and despite the |  | ||||||
| [community's efforts](https://github.com/nsf/termbox/pull/104#issuecomment-300308156) |  | ||||||
| to keep the library's development going, preferred to let it die. Before it happened, |  | ||||||
| [some people](https://wiki.musl-libc.org/alternatives.html) |  | ||||||
| already noticed the robustness of the initial architecture |  | ||||||
| [became compromised](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff#commitcomment-3790714) |  | ||||||
| in a nonsensical refactoring frenzy. Now, the author refuses to merge features |  | ||||||
| like true-color support, invoking some |  | ||||||
| [spurious correlations](https://github.com/nsf/termbox/pull/104#issuecomment-300292223) |  | ||||||
| we will discuss no further. |  | ||||||
| 
 |  | ||||||
| ## The new Termbox-next |  | ||||||
| This fork was made to restore the codebase to its original quality (before |  | ||||||
| [66c3f91](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff)) |  | ||||||
| while providing all the functionnalities of the current implementation. |  | ||||||
| This was achieved by branching at |  | ||||||
| [a2e217f](https://github.com/nsf/termbox/commit/a2e217f0fb97e6bbd589136ea3945f9d5a123d81) |  | ||||||
| and cherry-picking all the commits up to |  | ||||||
| [d63b83a](https://github.com/nsf/termbox/commit/d63b83af04e0fd6da836bb8f37e5cec72a1dc95a) |  | ||||||
| if they weren't harmful. |  | ||||||
| 
 |  | ||||||
| ## Changes |  | ||||||
| A lot of things changed during the process: |  | ||||||
|  - *waf*, the original build system, was completely removed from the |  | ||||||
|    project and replaced by make. |  | ||||||
|  - anything related to python was removed as well |  | ||||||
| 
 |  | ||||||
| ## Getting started |  | ||||||
| Termbox's interface only consists of 12 functions: |  | ||||||
| ``` |  | ||||||
| tb_init() // initialization |  | ||||||
| tb_shutdown() // shutdown |  | ||||||
| 
 |  | ||||||
| tb_width() // width of the terminal screen |  | ||||||
| tb_height() // height of the terminal screen |  | ||||||
| 
 |  | ||||||
| tb_clear() // clear buffer |  | ||||||
| tb_present() // sync internal buffer with terminal |  | ||||||
| 
 |  | ||||||
| tb_put_cell() |  | ||||||
| tb_change_cell() |  | ||||||
| tb_blit() // drawing functions |  | ||||||
| 
 |  | ||||||
| tb_select_input_mode() // change input mode |  | ||||||
| tb_peek_event() // peek a keyboard event |  | ||||||
| tb_poll_event() // wait for a keyboard event |  | ||||||
| ``` |  | ||||||
| See src/termbox.h header file for full detail. |  | ||||||
| 
 |  | ||||||
| ## TL;DR |  | ||||||
| `make` to build a static version of the lib under bin/termbox.a |  | ||||||
| `cd src/demo && make` to build the example programs in src/demo/ |  | ||||||
|  | @ -1,827 +0,0 @@ | ||||||
| #include <assert.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <stdarg.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include "termbox.h" |  | ||||||
| 
 |  | ||||||
| struct key |  | ||||||
| { |  | ||||||
| 	unsigned char x; |  | ||||||
| 	unsigned char y; |  | ||||||
| 	uint32_t ch; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #define STOP {0,0,0} |  | ||||||
| struct key K_ESC[] = {{1, 1, 'E'}, {2, 1, 'S'}, {3, 1, 'C'}, STOP}; |  | ||||||
| struct key K_F1[] = {{6, 1, 'F'}, {7, 1, '1'}, STOP}; |  | ||||||
| struct key K_F2[] = {{9, 1, 'F'}, {10, 1, '2'}, STOP}; |  | ||||||
| struct key K_F3[] = {{12, 1, 'F'}, {13, 1, '3'}, STOP}; |  | ||||||
| struct key K_F4[] = {{15, 1, 'F'}, {16, 1, '4'}, STOP}; |  | ||||||
| struct key K_F5[] = {{19, 1, 'F'}, {20, 1, '5'}, STOP}; |  | ||||||
| struct key K_F6[] = {{22, 1, 'F'}, {23, 1, '6'}, STOP}; |  | ||||||
| struct key K_F7[] = {{25, 1, 'F'}, {26, 1, '7'}, STOP}; |  | ||||||
| struct key K_F8[] = {{28, 1, 'F'}, {29, 1, '8'}, STOP}; |  | ||||||
| struct key K_F9[] = {{33, 1, 'F'}, {34, 1, '9'}, STOP}; |  | ||||||
| struct key K_F10[] = {{36, 1, 'F'}, {37, 1, '1'}, {38, 1, '0'}, STOP}; |  | ||||||
| struct key K_F11[] = {{40, 1, 'F'}, {41, 1, '1'}, {42, 1, '1'}, STOP}; |  | ||||||
| struct key K_F12[] = {{44, 1, 'F'}, {45, 1, '1'}, {46, 1, '2'}, STOP}; |  | ||||||
| struct key K_PRN[] = {{50, 1, 'P'}, {51, 1, 'R'}, {52, 1, 'N'}, STOP}; |  | ||||||
| struct key K_SCR[] = {{54, 1, 'S'}, {55, 1, 'C'}, {56, 1, 'R'}, STOP}; |  | ||||||
| struct key K_BRK[] = {{58, 1, 'B'}, {59, 1, 'R'}, {60, 1, 'K'}, STOP}; |  | ||||||
| struct key K_LED1[] = {{66, 1, '-'}, STOP}; |  | ||||||
| struct key K_LED2[] = {{70, 1, '-'}, STOP}; |  | ||||||
| struct key K_LED3[] = {{74, 1, '-'}, STOP}; |  | ||||||
| 
 |  | ||||||
| struct key K_TILDE[] = {{1, 4, '`'}, STOP}; |  | ||||||
| struct key K_TILDE_SHIFT[] = {{1, 4, '~'}, STOP}; |  | ||||||
| struct key K_1[] = {{4, 4, '1'}, STOP}; |  | ||||||
| struct key K_1_SHIFT[] = {{4, 4, '!'}, STOP}; |  | ||||||
| struct key K_2[] = {{7, 4, '2'}, STOP}; |  | ||||||
| struct key K_2_SHIFT[] = {{7, 4, '@'}, STOP}; |  | ||||||
| struct key K_3[] = {{10, 4, '3'}, STOP}; |  | ||||||
| struct key K_3_SHIFT[] = {{10, 4, '#'}, STOP}; |  | ||||||
| struct key K_4[] = {{13, 4, '4'}, STOP}; |  | ||||||
| struct key K_4_SHIFT[] = {{13, 4, '$'}, STOP}; |  | ||||||
| struct key K_5[] = {{16, 4, '5'}, STOP}; |  | ||||||
| struct key K_5_SHIFT[] = {{16, 4, '%'}, STOP}; |  | ||||||
| struct key K_6[] = {{19, 4, '6'}, STOP}; |  | ||||||
| struct key K_6_SHIFT[] = {{19, 4, '^'}, STOP}; |  | ||||||
| struct key K_7[] = {{22, 4, '7'}, STOP}; |  | ||||||
| struct key K_7_SHIFT[] = {{22, 4, '&'}, STOP}; |  | ||||||
| struct key K_8[] = {{25, 4, '8'}, STOP}; |  | ||||||
| struct key K_8_SHIFT[] = {{25, 4, '*'}, STOP}; |  | ||||||
| struct key K_9[] = {{28, 4, '9'}, STOP}; |  | ||||||
| struct key K_9_SHIFT[] = {{28, 4, '('}, STOP}; |  | ||||||
| struct key K_0[] = {{31, 4, '0'}, STOP}; |  | ||||||
| struct key K_0_SHIFT[] = {{31, 4, ')'}, STOP}; |  | ||||||
| struct key K_MINUS[] = {{34, 4, '-'}, STOP}; |  | ||||||
| struct key K_MINUS_SHIFT[] = {{34, 4, '_'}, STOP}; |  | ||||||
| struct key K_EQUALS[] = {{37, 4, '='}, STOP}; |  | ||||||
| struct key K_EQUALS_SHIFT[] = {{37, 4, '+'}, STOP}; |  | ||||||
| struct key K_BACKSLASH[] = {{40, 4, '\\'}, STOP}; |  | ||||||
| struct key K_BACKSLASH_SHIFT[] = {{40, 4, '|'}, STOP}; |  | ||||||
| struct key K_BACKSPACE[] = {{44, 4, 0x2190}, {45, 4, 0x2500}, {46, 4, 0x2500}, STOP}; |  | ||||||
| struct key K_INS[] = {{50, 4, 'I'}, {51, 4, 'N'}, {52, 4, 'S'}, STOP}; |  | ||||||
| struct key K_HOM[] = {{54, 4, 'H'}, {55, 4, 'O'}, {56, 4, 'M'}, STOP}; |  | ||||||
| struct key K_PGU[] = {{58, 4, 'P'}, {59, 4, 'G'}, {60, 4, 'U'}, STOP}; |  | ||||||
| struct key K_K_NUMLOCK[] = {{65, 4, 'N'}, STOP}; |  | ||||||
| struct key K_K_SLASH[] = {{68, 4, '/'}, STOP}; |  | ||||||
| struct key K_K_STAR[] = {{71, 4, '*'}, STOP}; |  | ||||||
| struct key K_K_MINUS[] = {{74, 4, '-'}, STOP}; |  | ||||||
| 
 |  | ||||||
| struct key K_TAB[] = {{1, 6, 'T'}, {2, 6, 'A'}, {3, 6, 'B'}, STOP}; |  | ||||||
| struct key K_q[] = {{6, 6, 'q'}, STOP}; |  | ||||||
| struct key K_Q[] = {{6, 6, 'Q'}, STOP}; |  | ||||||
| struct key K_w[] = {{9, 6, 'w'}, STOP}; |  | ||||||
| struct key K_W[] = {{9, 6, 'W'}, STOP}; |  | ||||||
| struct key K_e[] = {{12, 6, 'e'}, STOP}; |  | ||||||
| struct key K_E[] = {{12, 6, 'E'}, STOP}; |  | ||||||
| struct key K_r[] = {{15, 6, 'r'}, STOP}; |  | ||||||
| struct key K_R[] = {{15, 6, 'R'}, STOP}; |  | ||||||
| struct key K_t[] = {{18, 6, 't'}, STOP}; |  | ||||||
| struct key K_T[] = {{18, 6, 'T'}, STOP}; |  | ||||||
| struct key K_y[] = {{21, 6, 'y'}, STOP}; |  | ||||||
| struct key K_Y[] = {{21, 6, 'Y'}, STOP}; |  | ||||||
| struct key K_u[] = {{24, 6, 'u'}, STOP}; |  | ||||||
| struct key K_U[] = {{24, 6, 'U'}, STOP}; |  | ||||||
| struct key K_i[] = {{27, 6, 'i'}, STOP}; |  | ||||||
| struct key K_I[] = {{27, 6, 'I'}, STOP}; |  | ||||||
| struct key K_o[] = {{30, 6, 'o'}, STOP}; |  | ||||||
| struct key K_O[] = {{30, 6, 'O'}, STOP}; |  | ||||||
| struct key K_p[] = {{33, 6, 'p'}, STOP}; |  | ||||||
| struct key K_P[] = {{33, 6, 'P'}, STOP}; |  | ||||||
| struct key K_LSQB[] = {{36, 6, '['}, STOP}; |  | ||||||
| struct key K_LCUB[] = {{36, 6, '{'}, STOP}; |  | ||||||
| struct key K_RSQB[] = {{39, 6, ']'}, STOP}; |  | ||||||
| struct key K_RCUB[] = {{39, 6, '}'}, STOP}; |  | ||||||
| struct key K_ENTER[] = |  | ||||||
| { |  | ||||||
| 	{43, 6, 0x2591}, {44, 6, 0x2591}, {45, 6, 0x2591}, {46, 6, 0x2591}, |  | ||||||
| 	{43, 7, 0x2591}, {44, 7, 0x2591}, {45, 7, 0x21B5}, {46, 7, 0x2591}, |  | ||||||
| 	{41, 8, 0x2591}, {42, 8, 0x2591}, {43, 8, 0x2591}, {44, 8, 0x2591}, |  | ||||||
| 	{45, 8, 0x2591}, {46, 8, 0x2591}, STOP |  | ||||||
| }; |  | ||||||
| struct key K_DEL[] = {{50, 6, 'D'}, {51, 6, 'E'}, {52, 6, 'L'}, STOP}; |  | ||||||
| struct key K_END[] = {{54, 6, 'E'}, {55, 6, 'N'}, {56, 6, 'D'}, STOP}; |  | ||||||
| struct key K_PGD[] = {{58, 6, 'P'}, {59, 6, 'G'}, {60, 6, 'D'}, STOP}; |  | ||||||
| struct key K_K_7[] = {{65, 6, '7'}, STOP}; |  | ||||||
| struct key K_K_8[] = {{68, 6, '8'}, STOP}; |  | ||||||
| struct key K_K_9[] = {{71, 6, '9'}, STOP}; |  | ||||||
| struct key K_K_PLUS[] = {{74, 6, ' '}, {74, 7, '+'}, {74, 8, ' '}, STOP}; |  | ||||||
| 
 |  | ||||||
| struct key K_CAPS[] = {{1, 8, 'C'}, {2, 8, 'A'}, {3, 8, 'P'}, {4, 8, 'S'}, STOP}; |  | ||||||
| struct key K_a[] = {{7, 8, 'a'}, STOP}; |  | ||||||
| struct key K_A[] = {{7, 8, 'A'}, STOP}; |  | ||||||
| struct key K_s[] = {{10, 8, 's'}, STOP}; |  | ||||||
| struct key K_S[] = {{10, 8, 'S'}, STOP}; |  | ||||||
| struct key K_d[] = {{13, 8, 'd'}, STOP}; |  | ||||||
| struct key K_D[] = {{13, 8, 'D'}, STOP}; |  | ||||||
| struct key K_f[] = {{16, 8, 'f'}, STOP}; |  | ||||||
| struct key K_F[] = {{16, 8, 'F'}, STOP}; |  | ||||||
| struct key K_g[] = {{19, 8, 'g'}, STOP}; |  | ||||||
| struct key K_G[] = {{19, 8, 'G'}, STOP}; |  | ||||||
| struct key K_h[] = {{22, 8, 'h'}, STOP}; |  | ||||||
| struct key K_H[] = {{22, 8, 'H'}, STOP}; |  | ||||||
| struct key K_j[] = {{25, 8, 'j'}, STOP}; |  | ||||||
| struct key K_J[] = {{25, 8, 'J'}, STOP}; |  | ||||||
| struct key K_k[] = {{28, 8, 'k'}, STOP}; |  | ||||||
| struct key K_K[] = {{28, 8, 'K'}, STOP}; |  | ||||||
| struct key K_l[] = {{31, 8, 'l'}, STOP}; |  | ||||||
| struct key K_L[] = {{31, 8, 'L'}, STOP}; |  | ||||||
| struct key K_SEMICOLON[] = {{34, 8, ';'}, STOP}; |  | ||||||
| struct key K_PARENTHESIS[] = {{34, 8, ':'}, STOP}; |  | ||||||
| struct key K_QUOTE[] = {{37, 8, '\''}, STOP}; |  | ||||||
| struct key K_DOUBLEQUOTE[] = {{37, 8, '"'}, STOP}; |  | ||||||
| struct key K_K_4[] = {{65, 8, '4'}, STOP}; |  | ||||||
| struct key K_K_5[] = {{68, 8, '5'}, STOP}; |  | ||||||
| struct key K_K_6[] = {{71, 8, '6'}, STOP}; |  | ||||||
| 
 |  | ||||||
| struct key K_LSHIFT[] = {{1, 10, 'S'}, {2, 10, 'H'}, {3, 10, 'I'}, {4, 10, 'F'}, {5, 10, 'T'}, STOP}; |  | ||||||
| struct key K_z[] = {{9, 10, 'z'}, STOP}; |  | ||||||
| struct key K_Z[] = {{9, 10, 'Z'}, STOP}; |  | ||||||
| struct key K_x[] = {{12, 10, 'x'}, STOP}; |  | ||||||
| struct key K_X[] = {{12, 10, 'X'}, STOP}; |  | ||||||
| struct key K_c[] = {{15, 10, 'c'}, STOP}; |  | ||||||
| struct key K_C[] = {{15, 10, 'C'}, STOP}; |  | ||||||
| struct key K_v[] = {{18, 10, 'v'}, STOP}; |  | ||||||
| struct key K_V[] = {{18, 10, 'V'}, STOP}; |  | ||||||
| struct key K_b[] = {{21, 10, 'b'}, STOP}; |  | ||||||
| struct key K_B[] = {{21, 10, 'B'}, STOP}; |  | ||||||
| struct key K_n[] = {{24, 10, 'n'}, STOP}; |  | ||||||
| struct key K_N[] = {{24, 10, 'N'}, STOP}; |  | ||||||
| struct key K_m[] = {{27, 10, 'm'}, STOP}; |  | ||||||
| struct key K_M[] = {{27, 10, 'M'}, STOP}; |  | ||||||
| struct key K_COMMA[] = {{30, 10, ','}, STOP}; |  | ||||||
| struct key K_LANB[] = {{30, 10, '<'}, STOP}; |  | ||||||
| struct key K_PERIOD[] = {{33, 10, '.'}, STOP}; |  | ||||||
| struct key K_RANB[] = {{33, 10, '>'}, STOP}; |  | ||||||
| struct key K_SLASH[] = {{36, 10, '/'}, STOP}; |  | ||||||
| struct key K_QUESTION[] = {{36, 10, '?'}, STOP}; |  | ||||||
| struct key K_RSHIFT[] = {{42, 10, 'S'}, {43, 10, 'H'}, {44, 10, 'I'}, {45, 10, 'F'}, {46, 10, 'T'}, STOP}; |  | ||||||
| struct key K_ARROW_UP[] = {{54, 10, '('}, {55, 10, 0x2191}, {56, 10, ')'}, STOP}; |  | ||||||
| struct key K_K_1[] = {{65, 10, '1'}, STOP}; |  | ||||||
| struct key K_K_2[] = {{68, 10, '2'}, STOP}; |  | ||||||
| struct key K_K_3[] = {{71, 10, '3'}, STOP}; |  | ||||||
| struct key K_K_ENTER[] = {{74, 10, 0x2591}, {74, 11, 0x2591}, {74, 12, 0x2591}, STOP}; |  | ||||||
| 
 |  | ||||||
| struct key K_LCTRL[] = {{1, 12, 'C'}, {2, 12, 'T'}, {3, 12, 'R'}, {4, 12, 'L'}, STOP}; |  | ||||||
| struct key K_LWIN[] = {{6, 12, 'W'}, {7, 12, 'I'}, {8, 12, 'N'}, STOP}; |  | ||||||
| struct key K_LALT[] = {{10, 12, 'A'}, {11, 12, 'L'}, {12, 12, 'T'}, STOP}; |  | ||||||
| struct key K_SPACE[] = |  | ||||||
| { |  | ||||||
| 	{14, 12, ' '}, {15, 12, ' '}, {16, 12, ' '}, {17, 12, ' '}, {18, 12, ' '}, |  | ||||||
| 	{19, 12, 'S'}, {20, 12, 'P'}, {21, 12, 'A'}, {22, 12, 'C'}, {23, 12, 'E'}, |  | ||||||
| 	{24, 12, ' '}, {25, 12, ' '}, {26, 12, ' '}, {27, 12, ' '}, {28, 12, ' '}, |  | ||||||
| 	STOP |  | ||||||
| }; |  | ||||||
| struct key K_RALT[] = {{30, 12, 'A'}, {31, 12, 'L'}, {32, 12, 'T'}, STOP}; |  | ||||||
| struct key K_RWIN[] = {{34, 12, 'W'}, {35, 12, 'I'}, {36, 12, 'N'}, STOP}; |  | ||||||
| struct key K_RPROP[] = {{38, 12, 'P'}, {39, 12, 'R'}, {40, 12, 'O'}, {41, 12, 'P'}, STOP}; |  | ||||||
| struct key K_RCTRL[] = {{43, 12, 'C'}, {44, 12, 'T'}, {45, 12, 'R'}, {46, 12, 'L'}, STOP}; |  | ||||||
| struct key K_ARROW_LEFT[] = {{50, 12, '('}, {51, 12, 0x2190}, {52, 12, ')'}, STOP}; |  | ||||||
| struct key K_ARROW_DOWN[] = {{54, 12, '('}, {55, 12, 0x2193}, {56, 12, ')'}, STOP}; |  | ||||||
| struct key K_ARROW_RIGHT[] = {{58, 12, '('}, {59, 12, 0x2192}, {60, 12, ')'}, STOP}; |  | ||||||
| struct key K_K_0[] = {{65, 12, ' '}, {66, 12, '0'}, {67, 12, ' '}, {68, 12, ' '}, STOP}; |  | ||||||
| struct key K_K_PERIOD[] = {{71, 12, '.'}, STOP}; |  | ||||||
| 
 |  | ||||||
| struct combo |  | ||||||
| { |  | ||||||
| 	struct key* keys[6]; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct combo combos[] = |  | ||||||
| { |  | ||||||
| 	{{K_TILDE, K_2, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_A, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_B, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_C, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_D, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_E, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_F, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_G, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_I, K_TAB, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_J, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_K, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_L, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_N, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_O, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_P, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_Q, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_R, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_S, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_T, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_U, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_V, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_W, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_X, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_Y, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_Z, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_RSQB, K_5, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_6, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL, 0}}, |  | ||||||
| 	{{K_SPACE, 0}}, |  | ||||||
| 	{{K_1_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_DOUBLEQUOTE, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_3_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_4_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_5_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_7_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_QUOTE, 0}}, |  | ||||||
| 	{{K_9_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_0_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_8_SHIFT, K_K_STAR, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_EQUALS_SHIFT, K_K_PLUS, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_COMMA, 0}}, |  | ||||||
| 	{{K_MINUS, K_K_MINUS, 0}}, |  | ||||||
| 	{{K_PERIOD, K_K_PERIOD, 0}}, |  | ||||||
| 	{{K_SLASH, K_K_SLASH, 0}}, |  | ||||||
| 	{{K_0, K_K_0, 0}}, |  | ||||||
| 	{{K_1, K_K_1, 0}}, |  | ||||||
| 	{{K_2, K_K_2, 0}}, |  | ||||||
| 	{{K_3, K_K_3, 0}}, |  | ||||||
| 	{{K_4, K_K_4, 0}}, |  | ||||||
| 	{{K_5, K_K_5, 0}}, |  | ||||||
| 	{{K_6, K_K_6, 0}}, |  | ||||||
| 	{{K_7, K_K_7, 0}}, |  | ||||||
| 	{{K_8, K_K_8, 0}}, |  | ||||||
| 	{{K_9, K_K_9, 0}}, |  | ||||||
| 	{{K_PARENTHESIS, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_SEMICOLON, 0}}, |  | ||||||
| 	{{K_LANB, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_EQUALS, 0}}, |  | ||||||
| 	{{K_RANB, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_QUESTION, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_2_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_A, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_B, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_C, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_D, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_E, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_F, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_G, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_H, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_I, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_J, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_K, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_L, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_M, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_N, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_O, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_P, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_Q, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_R, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_S, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_T, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_U, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_V, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_W, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_X, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_Y, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_Z, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_LSQB, 0}}, |  | ||||||
| 	{{K_BACKSLASH, 0}}, |  | ||||||
| 	{{K_RSQB, 0}}, |  | ||||||
| 	{{K_6_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_MINUS_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_TILDE, 0}}, |  | ||||||
| 	{{K_a, 0}}, |  | ||||||
| 	{{K_b, 0}}, |  | ||||||
| 	{{K_c, 0}}, |  | ||||||
| 	{{K_d, 0}}, |  | ||||||
| 	{{K_e, 0}}, |  | ||||||
| 	{{K_f, 0}}, |  | ||||||
| 	{{K_g, 0}}, |  | ||||||
| 	{{K_h, 0}}, |  | ||||||
| 	{{K_i, 0}}, |  | ||||||
| 	{{K_j, 0}}, |  | ||||||
| 	{{K_k, 0}}, |  | ||||||
| 	{{K_l, 0}}, |  | ||||||
| 	{{K_m, 0}}, |  | ||||||
| 	{{K_n, 0}}, |  | ||||||
| 	{{K_o, 0}}, |  | ||||||
| 	{{K_p, 0}}, |  | ||||||
| 	{{K_q, 0}}, |  | ||||||
| 	{{K_r, 0}}, |  | ||||||
| 	{{K_s, 0}}, |  | ||||||
| 	{{K_t, 0}}, |  | ||||||
| 	{{K_u, 0}}, |  | ||||||
| 	{{K_v, 0}}, |  | ||||||
| 	{{K_w, 0}}, |  | ||||||
| 	{{K_x, 0}}, |  | ||||||
| 	{{K_y, 0}}, |  | ||||||
| 	{{K_z, 0}}, |  | ||||||
| 	{{K_LCUB, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_BACKSLASH_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_RCUB, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_TILDE_SHIFT, K_LSHIFT, K_RSHIFT, 0}}, |  | ||||||
| 	{{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct combo func_combos[] = |  | ||||||
| { |  | ||||||
| 	{{K_F1, 0}}, |  | ||||||
| 	{{K_F2, 0}}, |  | ||||||
| 	{{K_F3, 0}}, |  | ||||||
| 	{{K_F4, 0}}, |  | ||||||
| 	{{K_F5, 0}}, |  | ||||||
| 	{{K_F6, 0}}, |  | ||||||
| 	{{K_F7, 0}}, |  | ||||||
| 	{{K_F8, 0}}, |  | ||||||
| 	{{K_F9, 0}}, |  | ||||||
| 	{{K_F10, 0}}, |  | ||||||
| 	{{K_F11, 0}}, |  | ||||||
| 	{{K_F12, 0}}, |  | ||||||
| 	{{K_INS, 0}}, |  | ||||||
| 	{{K_DEL, 0}}, |  | ||||||
| 	{{K_HOM, 0}}, |  | ||||||
| 	{{K_END, 0}}, |  | ||||||
| 	{{K_PGU, 0}}, |  | ||||||
| 	{{K_PGD, 0}}, |  | ||||||
| 	{{K_ARROW_UP, 0}}, |  | ||||||
| 	{{K_ARROW_DOWN, 0}}, |  | ||||||
| 	{{K_ARROW_LEFT, 0}}, |  | ||||||
| 	{{K_ARROW_RIGHT, 0}} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void print_tb(const char* str, int x, int y, uint32_t fg, uint32_t bg) |  | ||||||
| { |  | ||||||
| 	while (*str) |  | ||||||
| 	{ |  | ||||||
| 		uint32_t uni; |  | ||||||
| 		str += utf8_char_to_unicode(&uni, str); |  | ||||||
| 		tb_change_cell(x, y, uni, fg, bg); |  | ||||||
| 		x++; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void printf_tb(int x, int y, uint32_t fg, uint32_t bg, const char* fmt, ...) |  | ||||||
| { |  | ||||||
| 	char buf[4096]; |  | ||||||
| 	va_list vl; |  | ||||||
| 	va_start(vl, fmt); |  | ||||||
| 	vsnprintf(buf, sizeof(buf), fmt, vl); |  | ||||||
| 	va_end(vl); |  | ||||||
| 	print_tb(buf, x, y, fg, bg); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void draw_key(struct key* k, uint32_t fg, uint32_t bg) |  | ||||||
| { |  | ||||||
| 	while (k->x) |  | ||||||
| 	{ |  | ||||||
| 		tb_change_cell(k->x + 2, k->y + 4, k->ch, fg, bg); |  | ||||||
| 		k++; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void draw_keyboard() |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	tb_change_cell(0, 0, 0x250C, TB_WHITE, TB_DEFAULT); |  | ||||||
| 	tb_change_cell(79, 0, 0x2510, TB_WHITE, TB_DEFAULT); |  | ||||||
| 	tb_change_cell(0, 23, 0x2514, TB_WHITE, TB_DEFAULT); |  | ||||||
| 	tb_change_cell(79, 23, 0x2518, TB_WHITE, TB_DEFAULT); |  | ||||||
| 
 |  | ||||||
| 	for (i = 1; i < 79; ++i) |  | ||||||
| 	{ |  | ||||||
| 		tb_change_cell(i, 0, 0x2500, TB_WHITE, TB_DEFAULT); |  | ||||||
| 		tb_change_cell(i, 23, 0x2500, TB_WHITE, TB_DEFAULT); |  | ||||||
| 		tb_change_cell(i, 17, 0x2500, TB_WHITE, TB_DEFAULT); |  | ||||||
| 		tb_change_cell(i, 4, 0x2500, TB_WHITE, TB_DEFAULT); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (i = 1; i < 23; ++i) |  | ||||||
| 	{ |  | ||||||
| 		tb_change_cell(0, i, 0x2502, TB_WHITE, TB_DEFAULT); |  | ||||||
| 		tb_change_cell(79, i, 0x2502, TB_WHITE, TB_DEFAULT); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_change_cell(0, 17, 0x251C, TB_WHITE, TB_DEFAULT); |  | ||||||
| 	tb_change_cell(79, 17, 0x2524, TB_WHITE, TB_DEFAULT); |  | ||||||
| 	tb_change_cell(0, 4, 0x251C, TB_WHITE, TB_DEFAULT); |  | ||||||
| 	tb_change_cell(79, 4, 0x2524, TB_WHITE, TB_DEFAULT); |  | ||||||
| 
 |  | ||||||
| 	for (i = 5; i < 17; ++i) |  | ||||||
| 	{ |  | ||||||
| 		tb_change_cell(1, i, 0x2588, TB_YELLOW, TB_YELLOW); |  | ||||||
| 		tb_change_cell(78, i, 0x2588, TB_YELLOW, TB_YELLOW); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	draw_key(K_ESC, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F1, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F2, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F3, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F4, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F5, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F6, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F7, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F8, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F9, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F10, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F11, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_F12, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_PRN, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_SCR, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_BRK, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_LED1, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_LED2, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_LED3, TB_WHITE, TB_BLUE); |  | ||||||
| 
 |  | ||||||
| 	draw_key(K_TILDE, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_1, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_2, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_3, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_4, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_5, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_6, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_7, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_8, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_9, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_0, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_MINUS, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_EQUALS, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_BACKSLASH, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_BACKSPACE, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_INS, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_HOM, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_PGU, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_NUMLOCK, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_SLASH, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_STAR, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_MINUS, TB_WHITE, TB_BLUE); |  | ||||||
| 
 |  | ||||||
| 	draw_key(K_TAB, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_q, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_w, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_e, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_r, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_t, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_y, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_u, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_i, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_o, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_p, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_LSQB, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_RSQB, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_ENTER, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_DEL, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_END, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_PGD, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_7, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_8, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_9, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_PLUS, TB_WHITE, TB_BLUE); |  | ||||||
| 
 |  | ||||||
| 	draw_key(K_CAPS, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_a, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_s, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_d, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_f, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_g, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_h, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_j, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_k, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_l, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_SEMICOLON, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_QUOTE, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_4, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_5, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_6, TB_WHITE, TB_BLUE); |  | ||||||
| 
 |  | ||||||
| 	draw_key(K_LSHIFT, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_z, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_x, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_c, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_v, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_b, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_n, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_m, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_COMMA, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_PERIOD, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_SLASH, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_RSHIFT, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_ARROW_UP, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_1, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_2, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_3, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_ENTER, TB_WHITE, TB_BLUE); |  | ||||||
| 
 |  | ||||||
| 	draw_key(K_LCTRL, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_LWIN, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_LALT, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_SPACE, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_RCTRL, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_RPROP, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_RWIN, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_RALT, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_ARROW_LEFT, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_ARROW_DOWN, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_ARROW_RIGHT, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_0, TB_WHITE, TB_BLUE); |  | ||||||
| 	draw_key(K_K_PERIOD, TB_WHITE, TB_BLUE); |  | ||||||
| 
 |  | ||||||
| 	printf_tb(33, 1, TB_MAGENTA | TB_BOLD, TB_DEFAULT, "Keyboard demo!"); |  | ||||||
| 	printf_tb(21, 2, TB_MAGENTA, TB_DEFAULT, |  | ||||||
| 		"(press CTRL+X and then CTRL+Q to exit)"); |  | ||||||
| 	printf_tb(15, 3, TB_MAGENTA, TB_DEFAULT, |  | ||||||
| 		"(press CTRL+X and then CTRL+C to change input mode)"); |  | ||||||
| 
 |  | ||||||
| 	int inputmode = tb_select_input_mode(0); |  | ||||||
| 	char inputmode_str[64]; |  | ||||||
| 
 |  | ||||||
| 	if (inputmode & TB_INPUT_ESC) |  | ||||||
| 	{ |  | ||||||
| 		sprintf(inputmode_str, "TB_INPUT_ESC"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (inputmode & TB_INPUT_ALT) |  | ||||||
| 	{ |  | ||||||
| 		sprintf(inputmode_str, "TB_INPUT_ALT"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (inputmode & TB_INPUT_MOUSE) |  | ||||||
| 	{ |  | ||||||
| 		sprintf(inputmode_str + 12, " | TB_INPUT_MOUSE"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	printf_tb(3, 18, TB_WHITE, TB_DEFAULT, "Input mode: %s", inputmode_str); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const char* funckeymap(int k) |  | ||||||
| { |  | ||||||
| 	static const char* fcmap[] = |  | ||||||
| 	{ |  | ||||||
| 		"CTRL+2, CTRL+~", |  | ||||||
| 		"CTRL+A", |  | ||||||
| 		"CTRL+B", |  | ||||||
| 		"CTRL+C", |  | ||||||
| 		"CTRL+D", |  | ||||||
| 		"CTRL+E", |  | ||||||
| 		"CTRL+F", |  | ||||||
| 		"CTRL+G", |  | ||||||
| 		"CTRL+H, BACKSPACE", |  | ||||||
| 		"CTRL+I, TAB", |  | ||||||
| 		"CTRL+J", |  | ||||||
| 		"CTRL+K", |  | ||||||
| 		"CTRL+L", |  | ||||||
| 		"CTRL+M, ENTER", |  | ||||||
| 		"CTRL+N", |  | ||||||
| 		"CTRL+O", |  | ||||||
| 		"CTRL+P", |  | ||||||
| 		"CTRL+Q", |  | ||||||
| 		"CTRL+R", |  | ||||||
| 		"CTRL+S", |  | ||||||
| 		"CTRL+T", |  | ||||||
| 		"CTRL+U", |  | ||||||
| 		"CTRL+V", |  | ||||||
| 		"CTRL+W", |  | ||||||
| 		"CTRL+X", |  | ||||||
| 		"CTRL+Y", |  | ||||||
| 		"CTRL+Z", |  | ||||||
| 		"CTRL+3, ESC, CTRL+[", |  | ||||||
| 		"CTRL+4, CTRL+\\", |  | ||||||
| 		"CTRL+5, CTRL+]", |  | ||||||
| 		"CTRL+6", |  | ||||||
| 		"CTRL+7, CTRL+/, CTRL+_", |  | ||||||
| 		"SPACE" |  | ||||||
| 	}; |  | ||||||
| 	static const char* fkmap[] = |  | ||||||
| 	{ |  | ||||||
| 		"F1", |  | ||||||
| 		"F2", |  | ||||||
| 		"F3", |  | ||||||
| 		"F4", |  | ||||||
| 		"F5", |  | ||||||
| 		"F6", |  | ||||||
| 		"F7", |  | ||||||
| 		"F8", |  | ||||||
| 		"F9", |  | ||||||
| 		"F10", |  | ||||||
| 		"F11", |  | ||||||
| 		"F12", |  | ||||||
| 		"INSERT", |  | ||||||
| 		"DELETE", |  | ||||||
| 		"HOME", |  | ||||||
| 		"END", |  | ||||||
| 		"PGUP", |  | ||||||
| 		"PGDN", |  | ||||||
| 		"ARROW UP", |  | ||||||
| 		"ARROW DOWN", |  | ||||||
| 		"ARROW LEFT", |  | ||||||
| 		"ARROW RIGHT" |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	if (k == TB_KEY_CTRL_8) |  | ||||||
| 	{ |  | ||||||
| 		return "CTRL+8, BACKSPACE 2";    // 0x7F
 |  | ||||||
| 	} |  | ||||||
| 	else if (k >= TB_KEY_ARROW_RIGHT && k <= 0xFFFF) |  | ||||||
| 	{ |  | ||||||
| 		return fkmap[0xFFFF - k]; |  | ||||||
| 	} |  | ||||||
| 	else if (k <= TB_KEY_SPACE) |  | ||||||
| 	{ |  | ||||||
| 		return fcmap[k]; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return "UNKNOWN"; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void pretty_print_press(struct tb_event* ev) |  | ||||||
| { |  | ||||||
| 	char buf[7]; |  | ||||||
| 	buf[utf8_unicode_to_char(buf, ev->ch)] = '\0'; |  | ||||||
| 	printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Key: "); |  | ||||||
| 	printf_tb(8, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->key); |  | ||||||
| 	printf_tb(8, 20, TB_GREEN, TB_DEFAULT, "hex:     0x%X", ev->key); |  | ||||||
| 	printf_tb(8, 21, TB_CYAN, TB_DEFAULT, "octal:   0%o", ev->key); |  | ||||||
| 	printf_tb(8, 22, TB_RED, TB_DEFAULT, "string:  %s", funckeymap(ev->key)); |  | ||||||
| 
 |  | ||||||
| 	printf_tb(54, 19, TB_WHITE, TB_DEFAULT, "Char: "); |  | ||||||
| 	printf_tb(60, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->ch); |  | ||||||
| 	printf_tb(60, 20, TB_GREEN, TB_DEFAULT, "hex:     0x%X", ev->ch); |  | ||||||
| 	printf_tb(60, 21, TB_CYAN, TB_DEFAULT, "octal:   0%o", ev->ch); |  | ||||||
| 	printf_tb(60, 22, TB_RED, TB_DEFAULT, "string:  %s", buf); |  | ||||||
| 
 |  | ||||||
| 	printf_tb(54, 18, TB_WHITE, TB_DEFAULT, "Modifier: %s", |  | ||||||
| 		(ev->mod) ? "TB_MOD_ALT" : "none"); |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void pretty_print_resize(struct tb_event* ev) |  | ||||||
| { |  | ||||||
| 	printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Resize event: %d x %d", ev->w, ev->h); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int counter = 0; |  | ||||||
| 
 |  | ||||||
| void  pretty_print_mouse(struct tb_event* ev) |  | ||||||
| { |  | ||||||
| 	printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Mouse event: %d x %d", ev->x, ev->y); |  | ||||||
| 	char* btn = ""; |  | ||||||
| 
 |  | ||||||
| 	switch (ev->key) |  | ||||||
| 	{ |  | ||||||
| 		case TB_KEY_MOUSE_LEFT: |  | ||||||
| 			btn = "MouseLeft: %d"; |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case TB_KEY_MOUSE_MIDDLE: |  | ||||||
| 			btn = "MouseMiddle: %d"; |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case TB_KEY_MOUSE_RIGHT: |  | ||||||
| 			btn = "MouseRight: %d"; |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case TB_KEY_MOUSE_WHEEL_UP: |  | ||||||
| 			btn = "MouseWheelUp: %d"; |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case TB_KEY_MOUSE_WHEEL_DOWN: |  | ||||||
| 			btn = "MouseWheelDown: %d"; |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case TB_KEY_MOUSE_RELEASE: |  | ||||||
| 			btn = "MouseRelease: %d"; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	counter++; |  | ||||||
| 	printf_tb(43, 19, TB_WHITE, TB_DEFAULT, "Key: "); |  | ||||||
| 	printf_tb(48, 19, TB_YELLOW, TB_DEFAULT, btn, counter); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void dispatch_press(struct tb_event* ev) |  | ||||||
| { |  | ||||||
| 	if (ev->mod & TB_MOD_ALT) |  | ||||||
| 	{ |  | ||||||
| 		draw_key(K_LALT, TB_WHITE, TB_RED); |  | ||||||
| 		draw_key(K_RALT, TB_WHITE, TB_RED); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct combo* k = 0; |  | ||||||
| 
 |  | ||||||
| 	if (ev->key >= TB_KEY_ARROW_RIGHT) |  | ||||||
| 	{ |  | ||||||
| 		k = &func_combos[0xFFFF - ev->key]; |  | ||||||
| 	} |  | ||||||
| 	else if (ev->ch < 128) |  | ||||||
| 	{ |  | ||||||
| 		if (ev->ch == 0 && ev->key < 128) |  | ||||||
| 		{ |  | ||||||
| 			k = &combos[ev->key]; |  | ||||||
| 		} |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 			k = &combos[ev->ch]; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (!k) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct key** keys = k->keys; |  | ||||||
| 
 |  | ||||||
| 	while (*keys) |  | ||||||
| 	{ |  | ||||||
| 		draw_key(*keys, TB_WHITE, TB_RED); |  | ||||||
| 		keys++; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int main(int argc, char** argv) |  | ||||||
| { |  | ||||||
| 	(void) argc; |  | ||||||
| 	(void) argv; |  | ||||||
| 	int ret; |  | ||||||
| 
 |  | ||||||
| 	ret = tb_init(); |  | ||||||
| 
 |  | ||||||
| 	if (ret) |  | ||||||
| 	{ |  | ||||||
| 		fprintf(stderr, "tb_init() failed with error code %d\n", ret); |  | ||||||
| 		return 1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE); |  | ||||||
| 	struct tb_event ev; |  | ||||||
| 
 |  | ||||||
| 	tb_clear(); |  | ||||||
| 	draw_keyboard(); |  | ||||||
| 	tb_present(); |  | ||||||
| 	int inputmode = 0; |  | ||||||
| 	int ctrlxpressed = 0; |  | ||||||
| 
 |  | ||||||
| 	while (tb_poll_event(&ev)) |  | ||||||
| 	{ |  | ||||||
| 		switch (ev.type) |  | ||||||
| 		{ |  | ||||||
| 			case TB_EVENT_KEY: |  | ||||||
| 				if (ev.key == TB_KEY_CTRL_Q && ctrlxpressed) |  | ||||||
| 				{ |  | ||||||
| 					tb_shutdown(); |  | ||||||
| 					return 0; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if (ev.key == TB_KEY_CTRL_C && ctrlxpressed) |  | ||||||
| 				{ |  | ||||||
| 					static int chmap[] = |  | ||||||
| 					{ |  | ||||||
| 						TB_INPUT_ESC | TB_INPUT_MOUSE, // 101
 |  | ||||||
| 						TB_INPUT_ALT | TB_INPUT_MOUSE, // 110
 |  | ||||||
| 						TB_INPUT_ESC,                  // 001
 |  | ||||||
| 						TB_INPUT_ALT,                  // 010
 |  | ||||||
| 					}; |  | ||||||
| 					inputmode++; |  | ||||||
| 
 |  | ||||||
| 					if (inputmode >= 4) |  | ||||||
| 					{ |  | ||||||
| 						inputmode = 0; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					tb_select_input_mode(chmap[inputmode]); |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if (ev.key == TB_KEY_CTRL_X) |  | ||||||
| 				{ |  | ||||||
| 					ctrlxpressed = 1; |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					ctrlxpressed = 0; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				tb_clear(); |  | ||||||
| 				draw_keyboard(); |  | ||||||
| 				dispatch_press(&ev); |  | ||||||
| 				pretty_print_press(&ev); |  | ||||||
| 				tb_present(); |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_EVENT_RESIZE: |  | ||||||
| 				tb_clear(); |  | ||||||
| 				draw_keyboard(); |  | ||||||
| 				pretty_print_resize(&ev); |  | ||||||
| 				tb_present(); |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_EVENT_MOUSE: |  | ||||||
| 				tb_clear(); |  | ||||||
| 				draw_keyboard(); |  | ||||||
| 				pretty_print_mouse(&ev); |  | ||||||
| 				tb_present(); |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			default: |  | ||||||
| 				break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_shutdown(); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  | @ -1,30 +0,0 @@ | ||||||
| CC=gcc |  | ||||||
| FLAGS=-std=c99 -pedantic -Wall -Werror -g -static  |  | ||||||
| INCL=-I../ |  | ||||||
| BIND=../../bin |  | ||||||
| 
 |  | ||||||
| %.o:%.c |  | ||||||
| 	@echo "building source object $@" |  | ||||||
| 	@$(CC) $(INCL) $(FLAGS) -c -o $@ $< |  | ||||||
| 
 |  | ||||||
| all:keyboard output paint truecolor |  | ||||||
| 
 |  | ||||||
| keyboard:keyboard.o |  | ||||||
| 	@echo "compiling $@" |  | ||||||
| 	@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a |  | ||||||
| 
 |  | ||||||
| output:output.o |  | ||||||
| 	@echo "compiling $@" |  | ||||||
| 	@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a |  | ||||||
| 
 |  | ||||||
| paint:paint.o |  | ||||||
| 	@echo "compiling $@" |  | ||||||
| 	@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a |  | ||||||
| 
 |  | ||||||
| truecolor:truecolor.o |  | ||||||
| 	@echo "compiling $@" |  | ||||||
| 	@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a |  | ||||||
| 
 |  | ||||||
| clean: |  | ||||||
| 	@echo "cleaning workspace" |  | ||||||
| 	@rm -rf *.o keyboard output paint truecolor |  | ||||||
|  | @ -1,156 +0,0 @@ | ||||||
| #include <stdio.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include "../termbox.h" |  | ||||||
| 
 |  | ||||||
| static const char chars[] = "nnnnnnnnnbbbbbbbbbuuuuuuuuuBBBBBBBBB"; |  | ||||||
| 
 |  | ||||||
| static const uint32_t all_attrs[] = |  | ||||||
| { |  | ||||||
| 	0, |  | ||||||
| 	TB_BOLD, |  | ||||||
| 	TB_UNDERLINE, |  | ||||||
| 	TB_BOLD | TB_UNDERLINE, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static int next_char(int current) |  | ||||||
| { |  | ||||||
| 	current++; |  | ||||||
| 
 |  | ||||||
| 	if (!chars[current]) |  | ||||||
| 	{ |  | ||||||
| 		current = 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return current; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void draw_line(int x, int y, uint32_t bg) |  | ||||||
| { |  | ||||||
| 	int a, c; |  | ||||||
| 	int current_char = 0; |  | ||||||
| 
 |  | ||||||
| 	for (a = 0; a < 4; a++) |  | ||||||
| 	{ |  | ||||||
| 		for (c = TB_DEFAULT; c <= TB_WHITE; c++) |  | ||||||
| 		{ |  | ||||||
| 			uint32_t fg = all_attrs[a] | c; |  | ||||||
| 			tb_change_cell(x, y, chars[current_char], fg, bg); |  | ||||||
| 			current_char = next_char(current_char); |  | ||||||
| 			x++; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void print_combinations_table(int sx, int sy, const uint32_t* attrs, |  | ||||||
| 	int attrs_n) |  | ||||||
| { |  | ||||||
| 	int i, c; |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < attrs_n; i++) |  | ||||||
| 	{ |  | ||||||
| 		for (c = TB_DEFAULT; c <= TB_WHITE; c++) |  | ||||||
| 		{ |  | ||||||
| 			uint32_t bg = attrs[i] | c; |  | ||||||
| 			draw_line(sx, sy, bg); |  | ||||||
| 			sy++; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void draw_all() |  | ||||||
| { |  | ||||||
| 	tb_clear(); |  | ||||||
| 
 |  | ||||||
| 	tb_select_output_mode(TB_OUTPUT_NORMAL); |  | ||||||
| 	static const uint32_t col1[] = {0, TB_BOLD}; |  | ||||||
| 	static const uint32_t col2[] = {TB_REVERSE}; |  | ||||||
| 	print_combinations_table(1, 1, col1, 2); |  | ||||||
| 	print_combinations_table(2 + strlen(chars), 1, col2, 1); |  | ||||||
| 	tb_present(); |  | ||||||
| 
 |  | ||||||
| 	tb_select_output_mode(TB_OUTPUT_GRAYSCALE); |  | ||||||
| 	int c, x, y; |  | ||||||
| 
 |  | ||||||
| 	for (x = 0, y = 23; x < 24; ++x) |  | ||||||
| 	{ |  | ||||||
| 		tb_change_cell(x, y, '@', x, 0); |  | ||||||
| 		tb_change_cell(x + 25, y, ' ', 0, x); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_present(); |  | ||||||
| 
 |  | ||||||
| 	tb_select_output_mode(TB_OUTPUT_216); |  | ||||||
| 	y++; |  | ||||||
| 
 |  | ||||||
| 	for (c = 0, x = 0; c < 216; ++c, ++x) |  | ||||||
| 	{ |  | ||||||
| 		if (!(x % 24)) |  | ||||||
| 		{ |  | ||||||
| 			x = 0; |  | ||||||
| 			++y; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		tb_change_cell(x, y, '@', c, 0); |  | ||||||
| 		tb_change_cell(x + 25, y, ' ', 0, c); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_present(); |  | ||||||
| 
 |  | ||||||
| 	tb_select_output_mode(TB_OUTPUT_256); |  | ||||||
| 	y++; |  | ||||||
| 
 |  | ||||||
| 	for (c = 0, x = 0; c < 256; ++c, ++x) |  | ||||||
| 	{ |  | ||||||
| 		if (!(x % 24)) |  | ||||||
| 		{ |  | ||||||
| 			x = 0; |  | ||||||
| 			++y; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		tb_change_cell(x, y, '+', c | ((y & 1) ? TB_UNDERLINE : 0), 0); |  | ||||||
| 		tb_change_cell(x + 25, y, ' ', 0, c); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_present(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int main(int argc, char** argv) |  | ||||||
| { |  | ||||||
| 	(void)argc; |  | ||||||
| 	(void)argv; |  | ||||||
| 	int ret = tb_init(); |  | ||||||
| 
 |  | ||||||
| 	if (ret) |  | ||||||
| 	{ |  | ||||||
| 		fprintf(stderr, "tb_init() failed with error code %d\n", ret); |  | ||||||
| 		return 1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	draw_all(); |  | ||||||
| 
 |  | ||||||
| 	struct tb_event ev; |  | ||||||
| 
 |  | ||||||
| 	while (tb_poll_event(&ev)) |  | ||||||
| 	{ |  | ||||||
| 		switch (ev.type) |  | ||||||
| 		{ |  | ||||||
| 			case TB_EVENT_KEY: |  | ||||||
| 				switch (ev.key) |  | ||||||
| 				{ |  | ||||||
| 					case TB_KEY_ESC: |  | ||||||
| 						goto done; |  | ||||||
| 						break; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_EVENT_RESIZE: |  | ||||||
| 				draw_all(); |  | ||||||
| 				break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| done: |  | ||||||
| 	tb_shutdown(); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  | @ -1,183 +0,0 @@ | ||||||
| #include "../termbox.h" |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <string.h> |  | ||||||
| 
 |  | ||||||
| static int curCol = 0; |  | ||||||
| static int curRune = 0; |  | ||||||
| static struct tb_cell* backbuf; |  | ||||||
| static int bbw = 0, bbh = 0; |  | ||||||
| 
 |  | ||||||
| static const uint32_t runes[] = |  | ||||||
| { |  | ||||||
| 	0x20, // ' '
 |  | ||||||
| 	0x2591, // '░'
 |  | ||||||
| 	0x2592, // '▒'
 |  | ||||||
| 	0x2593, // '▓'
 |  | ||||||
| 	0x2588, // '█'
 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #define len(a) (sizeof(a)/sizeof(a[0])) |  | ||||||
| 
 |  | ||||||
| static const uint32_t colors[] = |  | ||||||
| { |  | ||||||
| 	TB_BLACK, |  | ||||||
| 	TB_RED, |  | ||||||
| 	TB_GREEN, |  | ||||||
| 	TB_YELLOW, |  | ||||||
| 	TB_BLUE, |  | ||||||
| 	TB_MAGENTA, |  | ||||||
| 	TB_CYAN, |  | ||||||
| 	TB_WHITE, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void updateAndDrawButtons(int* current, int x, int y, int mx, int my, int n, |  | ||||||
| 	void (*attrFunc)(int, uint32_t*, uint32_t*, uint32_t*)) |  | ||||||
| { |  | ||||||
| 	int lx = x; |  | ||||||
| 	int ly = y; |  | ||||||
| 
 |  | ||||||
| 	for (int i = 0; i < n; i++) |  | ||||||
| 	{ |  | ||||||
| 		if (lx <= mx && mx <= lx + 3 && ly <= my && my <= ly + 1) |  | ||||||
| 		{ |  | ||||||
| 			*current = i; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		uint32_t r; |  | ||||||
| 		uint32_t fg, bg; |  | ||||||
| 		(*attrFunc)(i, &r, &fg, &bg); |  | ||||||
| 		tb_change_cell(lx + 0, ly + 0, r, fg, bg); |  | ||||||
| 		tb_change_cell(lx + 1, ly + 0, r, fg, bg); |  | ||||||
| 		tb_change_cell(lx + 2, ly + 0, r, fg, bg); |  | ||||||
| 		tb_change_cell(lx + 3, ly + 0, r, fg, bg); |  | ||||||
| 		tb_change_cell(lx + 0, ly + 1, r, fg, bg); |  | ||||||
| 		tb_change_cell(lx + 1, ly + 1, r, fg, bg); |  | ||||||
| 		tb_change_cell(lx + 2, ly + 1, r, fg, bg); |  | ||||||
| 		tb_change_cell(lx + 3, ly + 1, r, fg, bg); |  | ||||||
| 		lx += 4; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	lx = x; |  | ||||||
| 	ly = y; |  | ||||||
| 
 |  | ||||||
| 	for (int i = 0; i < n; i++) |  | ||||||
| 	{ |  | ||||||
| 		if (*current == i) |  | ||||||
| 		{ |  | ||||||
| 			uint32_t fg = TB_RED | TB_BOLD; |  | ||||||
| 			uint32_t bg = TB_DEFAULT; |  | ||||||
| 			tb_change_cell(lx + 0, ly + 2, '^', fg, bg); |  | ||||||
| 			tb_change_cell(lx + 1, ly + 2, '^', fg, bg); |  | ||||||
| 			tb_change_cell(lx + 2, ly + 2, '^', fg, bg); |  | ||||||
| 			tb_change_cell(lx + 3, ly + 2, '^', fg, bg); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		lx += 4; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void runeAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg) |  | ||||||
| { |  | ||||||
| 	*r = runes[i]; |  | ||||||
| 	*fg = TB_DEFAULT; |  | ||||||
| 	*bg = TB_DEFAULT; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void colorAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg) |  | ||||||
| { |  | ||||||
| 	*r = ' '; |  | ||||||
| 	*fg = TB_DEFAULT; |  | ||||||
| 	*bg = colors[i]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void updateAndRedrawAll(int mx, int my) |  | ||||||
| { |  | ||||||
| 	tb_clear(); |  | ||||||
| 
 |  | ||||||
| 	if (mx != -1 && my != -1) |  | ||||||
| 	{ |  | ||||||
| 		backbuf[bbw * my + mx].ch = runes[curRune]; |  | ||||||
| 		backbuf[bbw * my + mx].fg = colors[curCol]; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	memcpy(tb_cell_buffer(), backbuf, sizeof(struct tb_cell)*bbw * bbh); |  | ||||||
| 	int h = tb_height(); |  | ||||||
| 	updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), runeAttrFunc); |  | ||||||
| 	updateAndDrawButtons(&curCol, 0, h - 3, mx, my, len(colors), colorAttrFunc); |  | ||||||
| 	tb_present(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void reallocBackBuffer(int w, int h) |  | ||||||
| { |  | ||||||
| 	bbw = w; |  | ||||||
| 	bbh = h; |  | ||||||
| 
 |  | ||||||
| 	if (backbuf) |  | ||||||
| 	{ |  | ||||||
| 		free(backbuf); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	backbuf = calloc(sizeof(struct tb_cell), w * h); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int main(int argv, char** argc) |  | ||||||
| { |  | ||||||
| 	(void)argc; |  | ||||||
| 	(void)argv; |  | ||||||
| 	int code = tb_init(); |  | ||||||
| 
 |  | ||||||
| 	if (code < 0) |  | ||||||
| 	{ |  | ||||||
| 		fprintf(stderr, "termbox init failed, code: %d\n", code); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE); |  | ||||||
| 	int w = tb_width(); |  | ||||||
| 	int h = tb_height(); |  | ||||||
| 	reallocBackBuffer(w, h); |  | ||||||
| 	updateAndRedrawAll(-1, -1); |  | ||||||
| 
 |  | ||||||
| 	for (;;) |  | ||||||
| 	{ |  | ||||||
| 		struct tb_event ev; |  | ||||||
| 		int mx = -1; |  | ||||||
| 		int my = -1; |  | ||||||
| 		int t = tb_poll_event(&ev); |  | ||||||
| 
 |  | ||||||
| 		if (t == -1) |  | ||||||
| 		{ |  | ||||||
| 			tb_shutdown(); |  | ||||||
| 			fprintf(stderr, "termbox poll event error\n"); |  | ||||||
| 			return -1; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		switch (t) |  | ||||||
| 		{ |  | ||||||
| 			case TB_EVENT_KEY: |  | ||||||
| 				if (ev.key == TB_KEY_ESC) |  | ||||||
| 				{ |  | ||||||
| 					tb_shutdown(); |  | ||||||
| 					return 0; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_EVENT_MOUSE: |  | ||||||
| 				if (ev.key == TB_KEY_MOUSE_LEFT) |  | ||||||
| 				{ |  | ||||||
| 					mx = ev.x; |  | ||||||
| 					my = ev.y; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_EVENT_RESIZE: |  | ||||||
| 				reallocBackBuffer(ev.w, ev.h); |  | ||||||
| 				break; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		updateAndRedrawAll(mx, my); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,69 +0,0 @@ | ||||||
| #include "termbox.h" |  | ||||||
| 
 |  | ||||||
| int main() |  | ||||||
| { |  | ||||||
| 	tb_init(); |  | ||||||
| 	tb_select_output_mode(TB_OUTPUT_TRUECOLOR); |  | ||||||
| 	int w = tb_width(); |  | ||||||
| 	int h = tb_height(); |  | ||||||
| 	uint32_t bg = 0x000000, fg = 0x000000; |  | ||||||
| 	tb_clear(); |  | ||||||
| 	int z = 0; |  | ||||||
| 
 |  | ||||||
| 	for (int y = 1; y < h; y++) |  | ||||||
| 	{ |  | ||||||
| 		for (int x = 1; x < w; x++) |  | ||||||
| 		{ |  | ||||||
| 			uint32_t ch; |  | ||||||
| 			utf8_char_to_unicode(&ch, "x"); |  | ||||||
| 			fg = 0; |  | ||||||
| 
 |  | ||||||
| 			if (z % 2 == 0) |  | ||||||
| 			{ |  | ||||||
| 				fg |= TB_BOLD; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (z % 3 == 0) |  | ||||||
| 			{ |  | ||||||
| 				fg |= TB_UNDERLINE; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (z % 5 == 0) |  | ||||||
| 			{ |  | ||||||
| 				fg |= TB_REVERSE; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			tb_change_cell(x, y, ch, fg, bg); |  | ||||||
| 			bg += 0x000101; |  | ||||||
| 			z++; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		bg += 0x080000; |  | ||||||
| 
 |  | ||||||
| 		if (bg > 0xFFFFFF) |  | ||||||
| 		{ |  | ||||||
| 			bg = 0; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_present(); |  | ||||||
| 
 |  | ||||||
| 	while (1) |  | ||||||
| 	{ |  | ||||||
| 		struct tb_event ev; |  | ||||||
| 		int t = tb_poll_event(&ev); |  | ||||||
| 
 |  | ||||||
| 		if (t == -1) |  | ||||||
| 		{ |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (t == TB_EVENT_KEY) |  | ||||||
| 		{ |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tb_shutdown(); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  | @ -1,319 +0,0 @@ | ||||||
| #include <assert.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <stdbool.h> |  | ||||||
| 
 |  | ||||||
| #include "term.h" |  | ||||||
| 
 |  | ||||||
| #define BUFFER_SIZE_MAX 16 |  | ||||||
| 
 |  | ||||||
| // if s1 starts with s2 returns 1, else 0
 |  | ||||||
| static int starts_with(const char* s1, const char* s2) |  | ||||||
| { |  | ||||||
| 	// nice huh?
 |  | ||||||
| 	while (*s2) |  | ||||||
| 	{ |  | ||||||
| 		if (*s1++ != *s2++) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int parse_mouse_event(struct tb_event* event, const char* buf, int len) |  | ||||||
| { |  | ||||||
| 	if ((len >= 6) && starts_with(buf, "\033[M")) |  | ||||||
| 	{ |  | ||||||
| 		// X10 mouse encoding, the simplest one
 |  | ||||||
| 		// \033 [ M Cb Cx Cy
 |  | ||||||
| 		int b = buf[3] - 32; |  | ||||||
| 
 |  | ||||||
| 		switch (b & 3) |  | ||||||
| 		{ |  | ||||||
| 			case 0: |  | ||||||
| 				if ((b & 64) != 0) |  | ||||||
| 				{ |  | ||||||
| 					event->key = TB_KEY_MOUSE_WHEEL_UP; |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					event->key = TB_KEY_MOUSE_LEFT; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case 1: |  | ||||||
| 				if ((b & 64) != 0) |  | ||||||
| 				{ |  | ||||||
| 					event->key = TB_KEY_MOUSE_WHEEL_DOWN; |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					event->key = TB_KEY_MOUSE_MIDDLE; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case 2: |  | ||||||
| 				event->key = TB_KEY_MOUSE_RIGHT; |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case 3: |  | ||||||
| 				event->key = TB_KEY_MOUSE_RELEASE; |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			default: |  | ||||||
| 				return -6; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
 |  | ||||||
| 
 |  | ||||||
| 		if ((b & 32) != 0) |  | ||||||
| 		{ |  | ||||||
| 			event->mod |= TB_MOD_MOTION; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// the coord is 1,1 for upper left
 |  | ||||||
| 		event->x = (uint8_t)buf[4] - 1 - 32; |  | ||||||
| 		event->y = (uint8_t)buf[5] - 1 - 32; |  | ||||||
| 		return 6; |  | ||||||
| 	} |  | ||||||
| 	else if (starts_with(buf, "\033[<") || starts_with(buf, "\033[")) |  | ||||||
| 	{ |  | ||||||
| 		// xterm 1006 extended mode or urxvt 1015 extended mode
 |  | ||||||
| 		// xterm: \033 [ < Cb ; Cx ; Cy (M or m)
 |  | ||||||
| 		// urxvt: \033 [ Cb ; Cx ; Cy M
 |  | ||||||
| 		int i, mi = -1, starti = -1; |  | ||||||
| 		int isM, isU, s1 = -1, s2 = -1; |  | ||||||
| 		int n1 = 0, n2 = 0, n3 = 0; |  | ||||||
| 
 |  | ||||||
| 		for (i = 0; i < len; i++) |  | ||||||
| 		{ |  | ||||||
| 			// We search the first (s1) and the last (s2) ';'
 |  | ||||||
| 			if (buf[i] == ';') |  | ||||||
| 			{ |  | ||||||
| 				if (s1 == -1) |  | ||||||
| 				{ |  | ||||||
| 					s1 = i; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				s2 = i; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// We search for the first 'm' or 'M'
 |  | ||||||
| 			if ((buf[i] == 'm' || buf[i] == 'M') && mi == -1) |  | ||||||
| 			{ |  | ||||||
| 				mi = i; |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (mi == -1) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// whether it's a capital M or not
 |  | ||||||
| 		isM = (buf[mi] == 'M'); |  | ||||||
| 
 |  | ||||||
| 		if (buf[2] == '<') |  | ||||||
| 		{ |  | ||||||
| 			isU = 0; |  | ||||||
| 			starti = 3; |  | ||||||
| 		} |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 			isU = 1; |  | ||||||
| 			starti = 2; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (s1 == -1 || s2 == -1 || s1 == s2) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		n1 = strtoul(&buf[starti], NULL, 10); |  | ||||||
| 		n2 = strtoul(&buf[s1 + 1], NULL, 10); |  | ||||||
| 		n3 = strtoul(&buf[s2 + 1], NULL, 10); |  | ||||||
| 
 |  | ||||||
| 		if (isU) |  | ||||||
| 		{ |  | ||||||
| 			n1 -= 32; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		switch (n1 & 3) |  | ||||||
| 		{ |  | ||||||
| 			case 0: |  | ||||||
| 				if ((n1 & 64) != 0) |  | ||||||
| 				{ |  | ||||||
| 					event->key = TB_KEY_MOUSE_WHEEL_UP; |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					event->key = TB_KEY_MOUSE_LEFT; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case 1: |  | ||||||
| 				if ((n1 & 64) != 0) |  | ||||||
| 				{ |  | ||||||
| 					event->key = TB_KEY_MOUSE_WHEEL_DOWN; |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					event->key = TB_KEY_MOUSE_MIDDLE; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case 2: |  | ||||||
| 				event->key = TB_KEY_MOUSE_RIGHT; |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case 3: |  | ||||||
| 				event->key = TB_KEY_MOUSE_RELEASE; |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			default: |  | ||||||
| 				return mi + 1; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (!isM) |  | ||||||
| 		{ |  | ||||||
| 			// on xterm mouse release is signaled by lowercase m
 |  | ||||||
| 			event->key = TB_KEY_MOUSE_RELEASE; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
 |  | ||||||
| 
 |  | ||||||
| 		if ((n1 & 32) != 0) |  | ||||||
| 		{ |  | ||||||
| 			event->mod |= TB_MOD_MOTION; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		event->x = (uint8_t)n2 - 1; |  | ||||||
| 		event->y = (uint8_t)n3 - 1; |  | ||||||
| 		return mi + 1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // convert escape sequence to event, and return consumed bytes on success (failure == 0)
 |  | ||||||
| static int parse_escape_seq(struct tb_event* event, const char* buf, int len) |  | ||||||
| { |  | ||||||
| 	int mouse_parsed = parse_mouse_event(event, buf, len); |  | ||||||
| 
 |  | ||||||
| 	if (mouse_parsed != 0) |  | ||||||
| 	{ |  | ||||||
| 		return mouse_parsed; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// it's pretty simple here, find 'starts_with' match and return success, else return failure
 |  | ||||||
| 	int i; |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; keys[i]; i++) |  | ||||||
| 	{ |  | ||||||
| 		if (starts_with(buf, keys[i])) |  | ||||||
| 		{ |  | ||||||
| 			event->ch = 0; |  | ||||||
| 			event->key = 0xFFFF - i; |  | ||||||
| 			return strlen(keys[i]); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool extract_event(struct tb_event* event, struct ringbuffer* inbuf, |  | ||||||
| 	int inputmode) |  | ||||||
| { |  | ||||||
| 	char buf[BUFFER_SIZE_MAX + 1]; |  | ||||||
| 	int nbytes = ringbuffer_data_size(inbuf); |  | ||||||
| 
 |  | ||||||
| 	if (nbytes > BUFFER_SIZE_MAX) |  | ||||||
| 	{ |  | ||||||
| 		nbytes = BUFFER_SIZE_MAX; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (nbytes == 0) |  | ||||||
| 	{ |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ringbuffer_read(inbuf, buf, nbytes); |  | ||||||
| 	buf[nbytes] = '\0'; |  | ||||||
| 
 |  | ||||||
| 	if (buf[0] == '\033') |  | ||||||
| 	{ |  | ||||||
| 		int n = parse_escape_seq(event, buf, nbytes); |  | ||||||
| 
 |  | ||||||
| 		if (n != 0) |  | ||||||
| 		{ |  | ||||||
| 			bool success = true; |  | ||||||
| 
 |  | ||||||
| 			if (n < 0) |  | ||||||
| 			{ |  | ||||||
| 				success = false; |  | ||||||
| 				n = -n; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			ringbuffer_pop(inbuf, 0, n); |  | ||||||
| 			return success; |  | ||||||
| 		} |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 			// it's not escape sequence, then it's ALT or ESC, check inputmode
 |  | ||||||
| 			if (inputmode & TB_INPUT_ESC) |  | ||||||
| 			{ |  | ||||||
| 				// if we're in escape mode, fill ESC event, pop buffer, return success
 |  | ||||||
| 				event->ch = 0; |  | ||||||
| 				event->key = TB_KEY_ESC; |  | ||||||
| 				event->mod = 0; |  | ||||||
| 				ringbuffer_pop(inbuf, 0, 1); |  | ||||||
| 				return true; |  | ||||||
| 			} |  | ||||||
| 			else if (inputmode & TB_INPUT_ALT) |  | ||||||
| 			{ |  | ||||||
| 				// if we're in alt mode, set ALT modifier to event and redo parsing
 |  | ||||||
| 				event->mod = TB_MOD_ALT; |  | ||||||
| 				ringbuffer_pop(inbuf, 0, 1); |  | ||||||
| 				return extract_event(event, inbuf, inputmode); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			assert(!"never got here"); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	//  if we're here, this is not an escape sequence and not an alt sequence
 |  | ||||||
| 	//  so, it's a FUNCTIONAL KEY or a UNICODE character
 |  | ||||||
| 
 |  | ||||||
| 	// first of all check if it's a functional key*/
 |  | ||||||
| 	if ((unsigned char)buf[0] <= TB_KEY_SPACE || |  | ||||||
| 		(unsigned char)buf[0] == TB_KEY_BACKSPACE2) |  | ||||||
| 	{ |  | ||||||
| 		// fill event, pop buffer, return success
 |  | ||||||
| 		event->ch = 0; |  | ||||||
| 		event->key = (uint16_t)buf[0]; |  | ||||||
| 		ringbuffer_pop(inbuf, 0, 1); |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// feh... we got utf8 here
 |  | ||||||
| 
 |  | ||||||
| 	// check if there is all bytes
 |  | ||||||
| 	if (nbytes >= utf8_char_length(buf[0])) |  | ||||||
| 	{ |  | ||||||
| 		// everything ok, fill event, pop buffer, return success
 |  | ||||||
| 		utf8_char_to_unicode(&event->ch, buf); |  | ||||||
| 		event->key = 0; |  | ||||||
| 		ringbuffer_pop(inbuf, 0, utf8_char_length(buf[0])); |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return false; |  | ||||||
| } |  | ||||||
|  | @ -1,36 +0,0 @@ | ||||||
| #include <string.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include "memstream.h" |  | ||||||
| 
 |  | ||||||
| void memstream_init(struct memstream* s, int fd, void* buffer, size_t len) |  | ||||||
| { |  | ||||||
| 	s->file = fd; |  | ||||||
| 	s->data = buffer; |  | ||||||
| 	s->pos = 0; |  | ||||||
| 	s->capa = len; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void memstream_flush(struct memstream* s) |  | ||||||
| { |  | ||||||
| 	write(s->file, s->data, s->pos); |  | ||||||
| 	s->pos = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void memstream_write(struct memstream* s, void* source, size_t len) |  | ||||||
| { |  | ||||||
| 	unsigned char* data = source; |  | ||||||
| 
 |  | ||||||
| 	if (s->pos + len > s->capa) |  | ||||||
| 	{ |  | ||||||
| 		memstream_flush(s); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	memcpy(s->data + s->pos, data, len); |  | ||||||
| 	s->pos += len; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void memstream_puts(struct memstream* s, const char* str) |  | ||||||
| { |  | ||||||
| 	memstream_write(s, (void*) str, strlen(str)); |  | ||||||
| } |  | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| #ifndef H_MEMSTREAM |  | ||||||
| #define H_MEMSTREAM |  | ||||||
| 
 |  | ||||||
| #include <stddef.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| 
 |  | ||||||
| struct memstream |  | ||||||
| { |  | ||||||
| 	size_t  pos; |  | ||||||
| 	size_t capa; |  | ||||||
| 	int file; |  | ||||||
| 	unsigned char* data; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void memstream_init(struct memstream* s, int fd, void* buffer, size_t len); |  | ||||||
| void memstream_flush(struct memstream* s); |  | ||||||
| void memstream_write(struct memstream* s, void* source, size_t len); |  | ||||||
| void memstream_puts(struct memstream* s, const char* str); |  | ||||||
| 
 |  | ||||||
| #endif |  | ||||||
|  | @ -1,195 +0,0 @@ | ||||||
| #include "ringbuffer.h" |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <stddef.h> // for ptrdiff_t |  | ||||||
| 
 |  | ||||||
| int init_ringbuffer(struct ringbuffer* r, size_t size) |  | ||||||
| { |  | ||||||
| 	r->buf = (char*)malloc(size); |  | ||||||
| 
 |  | ||||||
| 	if (!r->buf) |  | ||||||
| 	{ |  | ||||||
| 		return ERINGBUFFER_ALLOC_FAIL; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r->size = size; |  | ||||||
| 	clear_ringbuffer(r); |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void free_ringbuffer(struct ringbuffer* r) |  | ||||||
| { |  | ||||||
| 	free(r->buf); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void clear_ringbuffer(struct ringbuffer* r) |  | ||||||
| { |  | ||||||
| 	r->begin = 0; |  | ||||||
| 	r->end = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| size_t ringbuffer_free_space(struct ringbuffer* r) |  | ||||||
| { |  | ||||||
| 	if (r->begin == 0 && r->end == 0) |  | ||||||
| 	{ |  | ||||||
| 		return r->size; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (r->begin < r->end) |  | ||||||
| 	{ |  | ||||||
| 		return r->size - (r->end - r->begin) - 1; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		return r->begin - r->end - 1; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| size_t ringbuffer_data_size(struct ringbuffer* r) |  | ||||||
| { |  | ||||||
| 	if (r->begin == 0 && r->end == 0) |  | ||||||
| 	{ |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (r->begin <= r->end) |  | ||||||
| 	{ |  | ||||||
| 		return r->end - r->begin + 1; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		return r->size - (r->begin - r->end) + 1; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size) |  | ||||||
| { |  | ||||||
| 	if (ringbuffer_free_space(r) < size) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (r->begin == 0 && r->end == 0) |  | ||||||
| 	{ |  | ||||||
| 		memcpy(r->buf, data, size); |  | ||||||
| 		r->begin = r->buf; |  | ||||||
| 		r->end = r->buf + size - 1; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r->end++; |  | ||||||
| 
 |  | ||||||
| 	if (r->begin < r->end) |  | ||||||
| 	{ |  | ||||||
| 		if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size) |  | ||||||
| 		{ |  | ||||||
| 			// we can fit without cut
 |  | ||||||
| 			memcpy(r->end, data, size); |  | ||||||
| 			r->end += size - 1; |  | ||||||
| 		} |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 			// make a cut
 |  | ||||||
| 			size_t s = r->buf + r->size - r->end; |  | ||||||
| 			memcpy(r->end, data, s); |  | ||||||
| 			size -= s; |  | ||||||
| 			memcpy(r->buf, (char*)data + s, size); |  | ||||||
| 			r->end = r->buf + size - 1; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		memcpy(r->end, data, size); |  | ||||||
| 		r->end += size - 1; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size) |  | ||||||
| { |  | ||||||
| 	if (ringbuffer_data_size(r) < size) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	int need_clear = 0; |  | ||||||
| 
 |  | ||||||
| 	if (ringbuffer_data_size(r) == size) |  | ||||||
| 	{ |  | ||||||
| 		need_clear = 1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (r->begin < r->end) |  | ||||||
| 	{ |  | ||||||
| 		if (data) |  | ||||||
| 		{ |  | ||||||
| 			memcpy(data, r->begin, size); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		r->begin += size; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size) |  | ||||||
| 		{ |  | ||||||
| 			if (data) |  | ||||||
| 			{ |  | ||||||
| 				memcpy(data, r->begin, size); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			r->begin += size; |  | ||||||
| 		} |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 			size_t s = r->buf + r->size - r->begin; |  | ||||||
| 
 |  | ||||||
| 			if (data) |  | ||||||
| 			{ |  | ||||||
| 				memcpy(data, r->begin, s); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			size -= s; |  | ||||||
| 
 |  | ||||||
| 			if (data) |  | ||||||
| 			{ |  | ||||||
| 				memcpy((char*)data + s, r->buf, size); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			r->begin = r->buf + size; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (need_clear) |  | ||||||
| 	{ |  | ||||||
| 		clear_ringbuffer(r); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ringbuffer_read(struct ringbuffer* r, void* data, size_t size) |  | ||||||
| { |  | ||||||
| 	if (ringbuffer_data_size(r) < size) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (r->begin < r->end) |  | ||||||
| 	{ |  | ||||||
| 		memcpy(data, r->begin, size); |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size) |  | ||||||
| 		{ |  | ||||||
| 			memcpy(data, r->begin, size); |  | ||||||
| 		} |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 			size_t s = r->buf + r->size - r->begin; |  | ||||||
| 			memcpy(data, r->begin, s); |  | ||||||
| 			size -= s; |  | ||||||
| 			memcpy((char*)data + s, r->buf, size); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,26 +0,0 @@ | ||||||
| #ifndef H_RINGBUFFER |  | ||||||
| #define H_RINGBUFFER |  | ||||||
| 
 |  | ||||||
| #include <stddef.h> |  | ||||||
| 
 |  | ||||||
| #define ERINGBUFFER_ALLOC_FAIL -1 |  | ||||||
| 
 |  | ||||||
| struct ringbuffer |  | ||||||
| { |  | ||||||
| 	char* buf; |  | ||||||
| 	size_t size; |  | ||||||
| 
 |  | ||||||
| 	char* begin; |  | ||||||
| 	char* end; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| int init_ringbuffer(struct ringbuffer* r, size_t size); |  | ||||||
| void free_ringbuffer(struct ringbuffer* r); |  | ||||||
| void clear_ringbuffer(struct ringbuffer* r); |  | ||||||
| size_t ringbuffer_free_space(struct ringbuffer* r); |  | ||||||
| size_t ringbuffer_data_size(struct ringbuffer* r); |  | ||||||
| void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size); |  | ||||||
| void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size); |  | ||||||
| void ringbuffer_read(struct ringbuffer* r, void* data, size_t size); |  | ||||||
| 
 |  | ||||||
| #endif |  | ||||||
|  | @ -1,412 +0,0 @@ | ||||||
| #include <sys/stat.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| 
 |  | ||||||
| #include "term.h" |  | ||||||
| #define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h" |  | ||||||
| #define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l" |  | ||||||
| 
 |  | ||||||
| #define EUNSUPPORTED_TERM -1 |  | ||||||
| 
 |  | ||||||
| // rxvt-256color
 |  | ||||||
| static const char* rxvt_256color_keys[] = |  | ||||||
| { |  | ||||||
| 	"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", |  | ||||||
| 	"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", |  | ||||||
| 	"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", |  | ||||||
| 	"\033[A", "\033[B", "\033[D", "\033[C", NULL |  | ||||||
| }; |  | ||||||
| static const char* rxvt_256color_funcs[] = |  | ||||||
| { |  | ||||||
| 	"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", |  | ||||||
| 	"\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", |  | ||||||
| 	"\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // Eterm
 |  | ||||||
| static const char* eterm_keys[] = |  | ||||||
| { |  | ||||||
| 	"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", |  | ||||||
| 	"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", |  | ||||||
| 	"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", |  | ||||||
| 	"\033[A", "\033[B", "\033[D", "\033[C", NULL |  | ||||||
| }; |  | ||||||
| static const char* eterm_funcs[] = |  | ||||||
| { |  | ||||||
| 	"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", |  | ||||||
| 	"\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", |  | ||||||
| 	"", "", "", "", |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // screen
 |  | ||||||
| static const char* screen_keys[] = |  | ||||||
| { |  | ||||||
| 	"\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", |  | ||||||
| 	"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", |  | ||||||
| 	"\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~", |  | ||||||
| 	"\033OA", "\033OB", "\033OD", "\033OC", NULL |  | ||||||
| }; |  | ||||||
| static const char* screen_funcs[] = |  | ||||||
| { |  | ||||||
| 	"\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l", |  | ||||||
| 	"\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", |  | ||||||
| 	"\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // rxvt-unicode
 |  | ||||||
| static const char* rxvt_unicode_keys[] = |  | ||||||
| { |  | ||||||
| 	"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", |  | ||||||
| 	"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", |  | ||||||
| 	"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", |  | ||||||
| 	"\033[A", "\033[B", "\033[D", "\033[C", NULL |  | ||||||
| }; |  | ||||||
| static const char* rxvt_unicode_funcs[] = |  | ||||||
| { |  | ||||||
| 	"\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l", |  | ||||||
| 	"\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m", |  | ||||||
| 	"\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // linux
 |  | ||||||
| static const char* linux_keys[] = |  | ||||||
| { |  | ||||||
| 	"\033[[A", "\033[[B", "\033[[C", "\033[[D", "\033[[E", "\033[17~", |  | ||||||
| 	"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", |  | ||||||
| 	"\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~", |  | ||||||
| 	"\033[A", "\033[B", "\033[D", "\033[C", NULL |  | ||||||
| }; |  | ||||||
| static const char* linux_funcs[] = |  | ||||||
| { |  | ||||||
| 	"", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", |  | ||||||
| 	"\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "", |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // xterm
 |  | ||||||
| static const char* xterm_keys[] = |  | ||||||
| { |  | ||||||
| 	"\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", "\033[18~", |  | ||||||
| 	"\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~", |  | ||||||
| 	"\033[3~", "\033OH", "\033OF", "\033[5~", "\033[6~", "\033OA", "\033OB", |  | ||||||
| 	"\033OD", "\033OC", NULL |  | ||||||
| }; |  | ||||||
| static const char* xterm_funcs[] = |  | ||||||
| { |  | ||||||
| 	"\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l", |  | ||||||
| 	"\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", |  | ||||||
| 	"\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct term |  | ||||||
| { |  | ||||||
| 	const char* name; |  | ||||||
| 	const char** keys; |  | ||||||
| 	const char** funcs; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static struct term terms[] = |  | ||||||
| { |  | ||||||
| 	{"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs}, |  | ||||||
| 	{"Eterm", eterm_keys, eterm_funcs}, |  | ||||||
| 	{"screen", screen_keys, screen_funcs}, |  | ||||||
| 	{"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs}, |  | ||||||
| 	{"linux", linux_keys, linux_funcs}, |  | ||||||
| 	{"xterm", xterm_keys, xterm_funcs}, |  | ||||||
| 	{0, 0, 0}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static int init_from_terminfo = 0; |  | ||||||
| const char** keys; |  | ||||||
| const char** funcs; |  | ||||||
| 
 |  | ||||||
| static int try_compatible(const char* term, const char* name, |  | ||||||
| 	const char** tkeys, const char** tfuncs) |  | ||||||
| { |  | ||||||
| 	if (strstr(term, name)) |  | ||||||
| 	{ |  | ||||||
| 		keys = tkeys; |  | ||||||
| 		funcs = tfuncs; |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return EUNSUPPORTED_TERM; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int init_term_builtin(void) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	const char* term = getenv("TERM"); |  | ||||||
| 
 |  | ||||||
| 	if (term) |  | ||||||
| 	{ |  | ||||||
| 		for (i = 0; terms[i].name; i++) |  | ||||||
| 		{ |  | ||||||
| 			if (!strcmp(terms[i].name, term)) |  | ||||||
| 			{ |  | ||||||
| 				keys = terms[i].keys; |  | ||||||
| 				funcs = terms[i].funcs; |  | ||||||
| 				return 0; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// let's do some heuristic, maybe it's a compatible terminal
 |  | ||||||
| 		if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// let's assume that 'cygwin' is xterm compatible
 |  | ||||||
| 		if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return EUNSUPPORTED_TERM; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // terminfo
 |  | ||||||
| static char* read_file(const char* file) |  | ||||||
| { |  | ||||||
| 	FILE* f = fopen(file, "rb"); |  | ||||||
| 
 |  | ||||||
| 	if (!f) |  | ||||||
| 	{ |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct stat st; |  | ||||||
| 
 |  | ||||||
| 	if (fstat(fileno(f), &st) != 0) |  | ||||||
| 	{ |  | ||||||
| 		fclose(f); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	char* data = malloc(st.st_size); |  | ||||||
| 
 |  | ||||||
| 	if (!data) |  | ||||||
| 	{ |  | ||||||
| 		fclose(f); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) |  | ||||||
| 	{ |  | ||||||
| 		fclose(f); |  | ||||||
| 		free(data); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	fclose(f); |  | ||||||
| 	return data; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static char* terminfo_try_path(const char* path, const char* term) |  | ||||||
| { |  | ||||||
| 	char tmp[4096]; |  | ||||||
| 	snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term); |  | ||||||
| 	tmp[sizeof(tmp) - 1] = '\0'; |  | ||||||
| 	char* data = read_file(tmp); |  | ||||||
| 
 |  | ||||||
| 	if (data) |  | ||||||
| 	{ |  | ||||||
| 		return data; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// fallback to darwin specific dirs structure
 |  | ||||||
| 	snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term); |  | ||||||
| 	tmp[sizeof(tmp) - 1] = '\0'; |  | ||||||
| 	return read_file(tmp); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static char* load_terminfo(void) |  | ||||||
| { |  | ||||||
| 	char tmp[4096]; |  | ||||||
| 	const char* term = getenv("TERM"); |  | ||||||
| 
 |  | ||||||
| 	if (!term) |  | ||||||
| 	{ |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// if TERMINFO is set, no other directory should be searched
 |  | ||||||
| 	const char* terminfo = getenv("TERMINFO"); |  | ||||||
| 
 |  | ||||||
| 	if (terminfo) |  | ||||||
| 	{ |  | ||||||
| 		return terminfo_try_path(terminfo, term); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// next, consider ~/.terminfo
 |  | ||||||
| 	const char* home = getenv("HOME"); |  | ||||||
| 
 |  | ||||||
| 	if (home) |  | ||||||
| 	{ |  | ||||||
| 		snprintf(tmp, sizeof(tmp), "%s/.terminfo", home); |  | ||||||
| 		tmp[sizeof(tmp) - 1] = '\0'; |  | ||||||
| 		char* data = terminfo_try_path(tmp, term); |  | ||||||
| 
 |  | ||||||
| 		if (data) |  | ||||||
| 		{ |  | ||||||
| 			return data; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// next, TERMINFO_DIRS
 |  | ||||||
| 	const char* dirs = getenv("TERMINFO_DIRS"); |  | ||||||
| 
 |  | ||||||
| 	if (dirs) |  | ||||||
| 	{ |  | ||||||
| 		snprintf(tmp, sizeof(tmp), "%s", dirs); |  | ||||||
| 		tmp[sizeof(tmp) - 1] = '\0'; |  | ||||||
| 		char* dir = strtok(tmp, ":"); |  | ||||||
| 
 |  | ||||||
| 		while (dir) |  | ||||||
| 		{ |  | ||||||
| 			const char* cdir = dir; |  | ||||||
| 
 |  | ||||||
| 			if (strcmp(cdir, "") == 0) |  | ||||||
| 			{ |  | ||||||
| 				cdir = "/usr/share/terminfo"; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			char* data = terminfo_try_path(cdir, term); |  | ||||||
| 
 |  | ||||||
| 			if (data) |  | ||||||
| 			{ |  | ||||||
| 				return data; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			dir = strtok(0, ":"); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// fallback to /usr/share/terminfo
 |  | ||||||
| 	return terminfo_try_path("/usr/share/terminfo", term); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #define TI_MAGIC 0432 |  | ||||||
| #define TI_ALT_MAGIC 542 |  | ||||||
| #define TI_HEADER_LENGTH 12 |  | ||||||
| #define TB_KEYS_NUM 22 |  | ||||||
| 
 |  | ||||||
| static const char* terminfo_copy_string(char* data, int str, int table) |  | ||||||
| { |  | ||||||
| 	const int16_t off = *(int16_t*)(data + str); |  | ||||||
| 	const char* src = data + table + off; |  | ||||||
| 	int len = strlen(src); |  | ||||||
| 	char* dst = malloc(len + 1); |  | ||||||
| 	strcpy(dst, src); |  | ||||||
| 	return dst; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const int16_t ti_funcs[] = |  | ||||||
| { |  | ||||||
| 	28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const int16_t ti_keys[] = |  | ||||||
| { |  | ||||||
| 	// apparently not a typo; 67 is F10 for whatever reason
 |  | ||||||
| 	66, 68, 69, 70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, |  | ||||||
| 	81, 87, 61, 79, 83, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| int init_term(void) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	char* data = load_terminfo(); |  | ||||||
| 
 |  | ||||||
| 	if (!data) |  | ||||||
| 	{ |  | ||||||
| 		init_from_terminfo = 0; |  | ||||||
| 		return init_term_builtin(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	int16_t* header = (int16_t*)data; |  | ||||||
| 
 |  | ||||||
| 	const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2; |  | ||||||
| 
 |  | ||||||
| 	if ((header[1] + header[2]) % 2) |  | ||||||
| 	{ |  | ||||||
| 		// old quirk to align everything on word boundaries
 |  | ||||||
| 		header[2] += 1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	const int str_offset = TI_HEADER_LENGTH + |  | ||||||
| 		header[1] + header[2] +	number_sec_len * header[3]; |  | ||||||
| 	const int table_offset = str_offset + 2 * header[4]; |  | ||||||
| 
 |  | ||||||
| 	keys = malloc(sizeof(const char*) * (TB_KEYS_NUM + 1)); |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < TB_KEYS_NUM; i++) |  | ||||||
| 	{ |  | ||||||
| 		keys[i] = terminfo_copy_string(data, |  | ||||||
| 				str_offset + 2 * ti_keys[i], table_offset); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	keys[i] = NULL; |  | ||||||
| 
 |  | ||||||
| 	funcs = malloc(sizeof(const char*) * T_FUNCS_NUM); |  | ||||||
| 
 |  | ||||||
| 	// the last two entries are reserved for mouse. because the table offset is
 |  | ||||||
| 	// not there, the two entries have to fill in manually
 |  | ||||||
| 	for (i = 0; i < T_FUNCS_NUM - 2; i++) |  | ||||||
| 	{ |  | ||||||
| 		funcs[i] = terminfo_copy_string(data, |  | ||||||
| 				str_offset + 2 * ti_funcs[i], table_offset); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	funcs[T_FUNCS_NUM - 2] = ENTER_MOUSE_SEQ; |  | ||||||
| 	funcs[T_FUNCS_NUM - 1] = EXIT_MOUSE_SEQ; |  | ||||||
| 	init_from_terminfo = 1; |  | ||||||
| 	free(data); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void shutdown_term(void) |  | ||||||
| { |  | ||||||
| 	if (init_from_terminfo) |  | ||||||
| 	{ |  | ||||||
| 		int i; |  | ||||||
| 
 |  | ||||||
| 		for (i = 0; i < TB_KEYS_NUM; i++) |  | ||||||
| 		{ |  | ||||||
| 			free((void*)keys[i]); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// the last two entries are reserved for mouse. because the table offset
 |  | ||||||
| 		// is not there, the two entries have to fill in manually and do not
 |  | ||||||
| 		// need to be freed.
 |  | ||||||
| 		for (i = 0; i < T_FUNCS_NUM - 2; i++) |  | ||||||
| 		{ |  | ||||||
| 			free((void*)funcs[i]); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		free(keys); |  | ||||||
| 		free(funcs); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,38 +0,0 @@ | ||||||
| #ifndef H_TERM |  | ||||||
| #define H_TERM |  | ||||||
| 
 |  | ||||||
| #include "termbox.h" |  | ||||||
| #include "ringbuffer.h" |  | ||||||
| #include <stdbool.h> |  | ||||||
| 
 |  | ||||||
| #define EUNSUPPORTED_TERM -1 |  | ||||||
| 
 |  | ||||||
| enum |  | ||||||
| { |  | ||||||
| 	T_ENTER_CA, |  | ||||||
| 	T_EXIT_CA, |  | ||||||
| 	T_SHOW_CURSOR, |  | ||||||
| 	T_HIDE_CURSOR, |  | ||||||
| 	T_CLEAR_SCREEN, |  | ||||||
| 	T_SGR0, |  | ||||||
| 	T_UNDERLINE, |  | ||||||
| 	T_BOLD, |  | ||||||
| 	T_BLINK, |  | ||||||
| 	T_REVERSE, |  | ||||||
| 	T_ENTER_KEYPAD, |  | ||||||
| 	T_EXIT_KEYPAD, |  | ||||||
| 	T_ENTER_MOUSE, |  | ||||||
| 	T_EXIT_MOUSE, |  | ||||||
| 	T_FUNCS_NUM, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| extern const char** keys; |  | ||||||
| extern const char** funcs; |  | ||||||
| 
 |  | ||||||
| // true on success, false on failure
 |  | ||||||
| bool extract_event(struct tb_event* event, struct ringbuffer* inbuf, |  | ||||||
| 	int inputmode); |  | ||||||
| int init_term(void); |  | ||||||
| void shutdown_term(void); |  | ||||||
| 
 |  | ||||||
| #endif |  | ||||||
|  | @ -1,885 +0,0 @@ | ||||||
| #include "term.h" |  | ||||||
| #include "termbox.h" |  | ||||||
| #include "memstream.h" |  | ||||||
| 
 |  | ||||||
| #include <assert.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
| 
 |  | ||||||
| #include <fcntl.h> |  | ||||||
| #include <signal.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <sys/select.h> |  | ||||||
| #include <sys/ioctl.h> |  | ||||||
| #include <sys/time.h> |  | ||||||
| #include <termios.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <wchar.h> |  | ||||||
| 
 |  | ||||||
| struct cellbuf |  | ||||||
| { |  | ||||||
| 	int width; |  | ||||||
| 	int height; |  | ||||||
| 	struct tb_cell* cells; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)] |  | ||||||
| #define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1) |  | ||||||
| #define LAST_COORD_INIT -1 |  | ||||||
| 
 |  | ||||||
| static struct termios orig_tios; |  | ||||||
| 
 |  | ||||||
| static struct cellbuf back_buffer; |  | ||||||
| static struct cellbuf front_buffer; |  | ||||||
| static unsigned char write_buffer_data[32 * 1024]; |  | ||||||
| static struct memstream write_buffer; |  | ||||||
| 
 |  | ||||||
| static int termw = -1; |  | ||||||
| static int termh = -1; |  | ||||||
| 
 |  | ||||||
| static int inputmode = TB_INPUT_ESC; |  | ||||||
| static int outputmode = TB_OUTPUT_NORMAL; |  | ||||||
| 
 |  | ||||||
| static struct ringbuffer inbuf; |  | ||||||
| 
 |  | ||||||
| static int out; |  | ||||||
| static FILE* in; |  | ||||||
| 
 |  | ||||||
| static int out_fileno; |  | ||||||
| static int in_fileno; |  | ||||||
| 
 |  | ||||||
| static int winch_fds[2]; |  | ||||||
| 
 |  | ||||||
| static int lastx = LAST_COORD_INIT; |  | ||||||
| static int lasty = LAST_COORD_INIT; |  | ||||||
| static int cursor_x = -1; |  | ||||||
| static int cursor_y = -1; |  | ||||||
| 
 |  | ||||||
| static uint32_t background = TB_DEFAULT; |  | ||||||
| static uint32_t foreground = TB_DEFAULT; |  | ||||||
| 
 |  | ||||||
| static void write_cursor(int x, int y); |  | ||||||
| static void write_sgr(uint32_t fg, uint32_t bg); |  | ||||||
| 
 |  | ||||||
| static void cellbuf_init(struct cellbuf* buf, int width, int height); |  | ||||||
| static void cellbuf_resize(struct cellbuf* buf, int width, int height); |  | ||||||
| static void cellbuf_clear(struct cellbuf* buf); |  | ||||||
| static void cellbuf_free(struct cellbuf* buf); |  | ||||||
| 
 |  | ||||||
| static void update_size(void); |  | ||||||
| static void update_term_size(void); |  | ||||||
| static void send_attr(uint32_t fg, uint32_t bg); |  | ||||||
| static void send_char(int x, int y, uint32_t c); |  | ||||||
| static void send_clear(void); |  | ||||||
| static void sigwinch_handler(int xxx); |  | ||||||
| static int wait_fill_event(struct tb_event* event, struct timeval* timeout); |  | ||||||
| 
 |  | ||||||
| // may happen in a different thread
 |  | ||||||
| static volatile int buffer_size_change_request; |  | ||||||
| 
 |  | ||||||
| int tb_init_file(const char* name) |  | ||||||
| { |  | ||||||
| 	out = open(name, O_WRONLY); |  | ||||||
| 	in = fopen(name, "r"); |  | ||||||
| 
 |  | ||||||
| 	if (out == -1 || !in) |  | ||||||
| 	{ |  | ||||||
| 		if (out != -1) |  | ||||||
| 		{ |  | ||||||
| 			close(out); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (in) |  | ||||||
| 		{ |  | ||||||
| 			fclose(in); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return TB_EFAILED_TO_OPEN_TTY; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	out_fileno = out; |  | ||||||
| 	in_fileno = fileno(in); |  | ||||||
| 
 |  | ||||||
| 	if (init_term() < 0) |  | ||||||
| 	{ |  | ||||||
| 		close(out); |  | ||||||
| 		fclose(in); |  | ||||||
| 
 |  | ||||||
| 		return TB_EUNSUPPORTED_TERMINAL; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (pipe(winch_fds) < 0) |  | ||||||
| 	{ |  | ||||||
| 		close(out); |  | ||||||
| 		fclose(in); |  | ||||||
| 
 |  | ||||||
| 		return TB_EPIPE_TRAP_ERROR; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct sigaction sa; |  | ||||||
| 
 |  | ||||||
| 	memset(&sa, 0, sizeof(sa)); |  | ||||||
| 	sa.sa_handler = sigwinch_handler; |  | ||||||
| 	sa.sa_flags = 0; |  | ||||||
| 	sigaction(SIGWINCH, &sa, 0); |  | ||||||
| 	tcgetattr(out_fileno, &orig_tios); |  | ||||||
| 
 |  | ||||||
| 	struct termios tios; |  | ||||||
| 
 |  | ||||||
| 	memcpy(&tios, &orig_tios, sizeof(tios)); |  | ||||||
| 	tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |  | ||||||
| 			| INLCR | IGNCR | ICRNL | IXON); |  | ||||||
| 	tios.c_oflag &= ~OPOST; |  | ||||||
| 	tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); |  | ||||||
| 	tios.c_cflag &= ~(CSIZE | PARENB); |  | ||||||
| 	tios.c_cflag |= CS8; |  | ||||||
| 	tios.c_cc[VMIN] = 0; |  | ||||||
| 	tios.c_cc[VTIME] = 0; |  | ||||||
| 	tcsetattr(out_fileno, TCSAFLUSH, &tios); |  | ||||||
| 
 |  | ||||||
| 	memstream_init(&write_buffer, out_fileno, write_buffer_data, |  | ||||||
| 		sizeof(write_buffer_data)); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_ENTER_CA]); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_ENTER_KEYPAD]); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]); |  | ||||||
| 	send_clear(); |  | ||||||
| 
 |  | ||||||
| 	update_term_size(); |  | ||||||
| 	cellbuf_init(&back_buffer, termw, termh); |  | ||||||
| 	cellbuf_init(&front_buffer, termw, termh); |  | ||||||
| 	cellbuf_clear(&back_buffer); |  | ||||||
| 	cellbuf_clear(&front_buffer); |  | ||||||
| 	init_ringbuffer(&inbuf, 4096); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int tb_init(void) |  | ||||||
| { |  | ||||||
| 	return tb_init_file("/dev/tty"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void tb_shutdown(void) |  | ||||||
| { |  | ||||||
| 	if (termw == -1) |  | ||||||
| 	{ |  | ||||||
| 		fputs("tb_shutdown() should not be called twice.", stderr); |  | ||||||
| 		abort(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_SGR0]); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_EXIT_CA]); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_EXIT_KEYPAD]); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]); |  | ||||||
| 	memstream_flush(&write_buffer); |  | ||||||
| 	tcsetattr(out_fileno, TCSAFLUSH, &orig_tios); |  | ||||||
| 
 |  | ||||||
| 	shutdown_term(); |  | ||||||
| 	close(out); |  | ||||||
| 	fclose(in); |  | ||||||
| 	close(winch_fds[0]); |  | ||||||
| 	close(winch_fds[1]); |  | ||||||
| 
 |  | ||||||
| 	cellbuf_free(&back_buffer); |  | ||||||
| 	cellbuf_free(&front_buffer); |  | ||||||
| 	free_ringbuffer(&inbuf); |  | ||||||
| 	termw = termh = -1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void tb_present(void) |  | ||||||
| { |  | ||||||
| 	int x, y, w, i; |  | ||||||
| 	struct tb_cell* back, *front; |  | ||||||
| 
 |  | ||||||
| 	// invalidate cursor position
 |  | ||||||
| 	lastx = LAST_COORD_INIT; |  | ||||||
| 	lasty = LAST_COORD_INIT; |  | ||||||
| 
 |  | ||||||
| 	if (buffer_size_change_request) |  | ||||||
| 	{ |  | ||||||
| 		update_size(); |  | ||||||
| 		buffer_size_change_request = 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (y = 0; y < front_buffer.height; ++y) |  | ||||||
| 	{ |  | ||||||
| 		for (x = 0; x < front_buffer.width;) |  | ||||||
| 		{ |  | ||||||
| 			back = &CELL(&back_buffer, x, y); |  | ||||||
| 			front = &CELL(&front_buffer, x, y); |  | ||||||
| 			w = wcwidth(back->ch); |  | ||||||
| 
 |  | ||||||
| 			if (w < 1) |  | ||||||
| 			{ |  | ||||||
| 				w = 1; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (memcmp(back, front, sizeof(struct tb_cell)) == 0) |  | ||||||
| 			{ |  | ||||||
| 				x += w; |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			memcpy(front, back, sizeof(struct tb_cell)); |  | ||||||
| 			send_attr(back->fg, back->bg); |  | ||||||
| 
 |  | ||||||
| 			if (w > 1 && x >= front_buffer.width - (w - 1)) |  | ||||||
| 			{ |  | ||||||
| 				// Not enough room for wide ch, so send spaces
 |  | ||||||
| 				for (i = x; i < front_buffer.width; ++i) |  | ||||||
| 				{ |  | ||||||
| 					send_char(i, y, ' '); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| 				send_char(x, y, back->ch); |  | ||||||
| 
 |  | ||||||
| 				for (i = 1; i < w; ++i) |  | ||||||
| 				{ |  | ||||||
| 					front = &CELL(&front_buffer, x + i, y); |  | ||||||
| 					front->ch = 0; |  | ||||||
| 					front->fg = back->fg; |  | ||||||
| 					front->bg = back->bg; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			x += w; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) |  | ||||||
| 	{ |  | ||||||
| 		write_cursor(cursor_x, cursor_y); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	memstream_flush(&write_buffer); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void tb_set_cursor(int cx, int cy) |  | ||||||
| { |  | ||||||
| 	if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy)) |  | ||||||
| 	{ |  | ||||||
| 		memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy)) |  | ||||||
| 	{ |  | ||||||
| 		memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	cursor_x = cx; |  | ||||||
| 	cursor_y = cy; |  | ||||||
| 
 |  | ||||||
| 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) |  | ||||||
| 	{ |  | ||||||
| 		write_cursor(cursor_x, cursor_y); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void tb_put_cell(int x, int y, const struct tb_cell* cell) |  | ||||||
| { |  | ||||||
| 	if ((unsigned)x >= (unsigned)back_buffer.width) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if ((unsigned)y >= (unsigned)back_buffer.height) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	CELL(&back_buffer, x, y) = *cell; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg, uint32_t bg) |  | ||||||
| { |  | ||||||
| 	struct tb_cell c = {ch, fg, bg}; |  | ||||||
| 	tb_put_cell(x, y, &c); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void tb_blit(int x, int y, int w, int h, const struct tb_cell* cells) |  | ||||||
| { |  | ||||||
| 	if (x + w < 0 || x >= back_buffer.width) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (y + h < 0 || y >= back_buffer.height) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	int xo = 0, yo = 0, ww = w, hh = h; |  | ||||||
| 
 |  | ||||||
| 	if (x < 0) |  | ||||||
| 	{ |  | ||||||
| 		xo = -x; |  | ||||||
| 		ww -= xo; |  | ||||||
| 		x = 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (y < 0) |  | ||||||
| 	{ |  | ||||||
| 		yo = -y; |  | ||||||
| 		hh -= yo; |  | ||||||
| 		y = 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (ww > back_buffer.width - x) |  | ||||||
| 	{ |  | ||||||
| 		ww = back_buffer.width - x; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (hh > back_buffer.height - y) |  | ||||||
| 	{ |  | ||||||
| 		hh = back_buffer.height - y; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	int sy; |  | ||||||
| 	struct tb_cell* dst = &CELL(&back_buffer, x, y); |  | ||||||
| 	const struct tb_cell* src = cells + yo * w + xo; |  | ||||||
| 	size_t size = sizeof(struct tb_cell) * ww; |  | ||||||
| 
 |  | ||||||
| 	for (sy = 0; sy < hh; ++sy) |  | ||||||
| 	{ |  | ||||||
| 		memcpy(dst, src, size); |  | ||||||
| 		dst += back_buffer.width; |  | ||||||
| 		src += w; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct tb_cell* tb_cell_buffer(void) |  | ||||||
| { |  | ||||||
| 	return back_buffer.cells; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int tb_poll_event(struct tb_event* event) |  | ||||||
| { |  | ||||||
| 	return wait_fill_event(event, 0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int tb_peek_event(struct tb_event* event, int timeout) |  | ||||||
| { |  | ||||||
| 	struct timeval tv; |  | ||||||
| 	tv.tv_sec = timeout / 1000; |  | ||||||
| 	tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000; |  | ||||||
| 	return wait_fill_event(event, &tv); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int tb_width(void) |  | ||||||
| { |  | ||||||
| 	return termw; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int tb_height(void) |  | ||||||
| { |  | ||||||
| 	return termh; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void tb_clear(void) |  | ||||||
| { |  | ||||||
| 	if (buffer_size_change_request) |  | ||||||
| 	{ |  | ||||||
| 		update_size(); |  | ||||||
| 		buffer_size_change_request = 0; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	cellbuf_clear(&back_buffer); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int tb_select_input_mode(int mode) |  | ||||||
| { |  | ||||||
| 	if (mode) |  | ||||||
| 	{ |  | ||||||
| 		if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0) |  | ||||||
| 		{ |  | ||||||
| 			mode |= TB_INPUT_ESC; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// technically termbox can handle that, but let's be nice
 |  | ||||||
| 		// and show here what mode is actually used
 |  | ||||||
| 		if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT)) |  | ||||||
| 		{ |  | ||||||
| 			mode &= ~TB_INPUT_ALT; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		inputmode = mode; |  | ||||||
| 
 |  | ||||||
| 		if (mode & TB_INPUT_MOUSE) |  | ||||||
| 		{ |  | ||||||
| 			memstream_puts(&write_buffer, funcs[T_ENTER_MOUSE]); |  | ||||||
| 			memstream_flush(&write_buffer); |  | ||||||
| 		} |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 			memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]); |  | ||||||
| 			memstream_flush(&write_buffer); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return inputmode; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int tb_select_output_mode(int mode) |  | ||||||
| { |  | ||||||
| 	if (mode) |  | ||||||
| 	{ |  | ||||||
| 		outputmode = mode; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return outputmode; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void tb_set_clear_attributes(uint32_t fg, uint32_t bg) |  | ||||||
| { |  | ||||||
| 	foreground = fg; |  | ||||||
| 	background = bg; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static unsigned convertnum(uint32_t num, char* buf) |  | ||||||
| { |  | ||||||
| 	unsigned i, l = 0; |  | ||||||
| 	int ch; |  | ||||||
| 
 |  | ||||||
| 	do |  | ||||||
| 	{ |  | ||||||
| 		buf[l++] = '0' + (num % 10); |  | ||||||
| 		num /= 10; |  | ||||||
| 	} |  | ||||||
| 	while (num); |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < l / 2; i++) |  | ||||||
| 	{ |  | ||||||
| 		ch = buf[i]; |  | ||||||
| 		buf[i] = buf[l - 1 - i]; |  | ||||||
| 		buf[l - 1 - i] = ch; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return l; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #define WRITE_LITERAL(X) memstream_write(&write_buffer, (X), sizeof(X) -1) |  | ||||||
| #define WRITE_INT(X) memstream_write(&write_buffer, buf, convertnum((X), buf)) |  | ||||||
| 
 |  | ||||||
| static void write_cursor(int x, int y) |  | ||||||
| { |  | ||||||
| 	char buf[32]; |  | ||||||
| 	WRITE_LITERAL("\033["); |  | ||||||
| 	WRITE_INT(y + 1); |  | ||||||
| 	WRITE_LITERAL(";"); |  | ||||||
| 	WRITE_INT(x + 1); |  | ||||||
| 	WRITE_LITERAL("H"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void write_sgr(uint32_t fg, uint32_t bg) |  | ||||||
| { |  | ||||||
| 	char buf[32]; |  | ||||||
| 
 |  | ||||||
| 	if (outputmode != TB_OUTPUT_TRUECOLOR && fg == TB_DEFAULT && bg == TB_DEFAULT) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	switch (outputmode) |  | ||||||
| 	{ |  | ||||||
| 		case TB_OUTPUT_TRUECOLOR: |  | ||||||
| 			WRITE_LITERAL("\033[38;2;"); |  | ||||||
| 			WRITE_INT(fg >> 16 & 0xFF); |  | ||||||
| 			WRITE_LITERAL(";"); |  | ||||||
| 			WRITE_INT(fg >> 8 & 0xFF); |  | ||||||
| 			WRITE_LITERAL(";"); |  | ||||||
| 			WRITE_INT(fg & 0xFF); |  | ||||||
| 			WRITE_LITERAL(";48;2;"); |  | ||||||
| 			WRITE_INT(bg >> 16 & 0xFF); |  | ||||||
| 			WRITE_LITERAL(";"); |  | ||||||
| 			WRITE_INT(bg >> 8 & 0xFF); |  | ||||||
| 			WRITE_LITERAL(";"); |  | ||||||
| 			WRITE_INT(bg & 0xFF); |  | ||||||
| 			WRITE_LITERAL("m"); |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case TB_OUTPUT_256: |  | ||||||
| 		case TB_OUTPUT_216: |  | ||||||
| 		case TB_OUTPUT_GRAYSCALE: |  | ||||||
| 			WRITE_LITERAL("\033["); |  | ||||||
| 
 |  | ||||||
| 			if (fg != TB_DEFAULT) |  | ||||||
| 			{ |  | ||||||
| 				WRITE_LITERAL("38;5;"); |  | ||||||
| 				WRITE_INT(fg); |  | ||||||
| 
 |  | ||||||
| 				if (bg != TB_DEFAULT) |  | ||||||
| 				{ |  | ||||||
| 					WRITE_LITERAL(";"); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (bg != TB_DEFAULT) |  | ||||||
| 			{ |  | ||||||
| 				WRITE_LITERAL("48;5;"); |  | ||||||
| 				WRITE_INT(bg); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			WRITE_LITERAL("m"); |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case TB_OUTPUT_NORMAL: |  | ||||||
| 		default: |  | ||||||
| 			WRITE_LITERAL("\033["); |  | ||||||
| 
 |  | ||||||
| 			if (fg != TB_DEFAULT) |  | ||||||
| 			{ |  | ||||||
| 				WRITE_LITERAL("3"); |  | ||||||
| 				WRITE_INT(fg - 1); |  | ||||||
| 
 |  | ||||||
| 				if (bg != TB_DEFAULT) |  | ||||||
| 				{ |  | ||||||
| 					WRITE_LITERAL(";"); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (bg != TB_DEFAULT) |  | ||||||
| 			{ |  | ||||||
| 				WRITE_LITERAL("4"); |  | ||||||
| 				WRITE_INT(bg - 1); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			WRITE_LITERAL("m"); |  | ||||||
| 			break; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void cellbuf_init(struct cellbuf* buf, int width, int height) |  | ||||||
| { |  | ||||||
| 	buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height); |  | ||||||
| 	assert(buf->cells); |  | ||||||
| 	buf->width = width; |  | ||||||
| 	buf->height = height; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void cellbuf_resize(struct cellbuf* buf, int width, int height) |  | ||||||
| { |  | ||||||
| 	if (buf->width == width && buf->height == height) |  | ||||||
| 	{ |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	int oldw = buf->width; |  | ||||||
| 	int oldh = buf->height; |  | ||||||
| 	struct tb_cell* oldcells = buf->cells; |  | ||||||
| 
 |  | ||||||
| 	cellbuf_init(buf, width, height); |  | ||||||
| 	cellbuf_clear(buf); |  | ||||||
| 
 |  | ||||||
| 	int minw = (width < oldw) ? width : oldw; |  | ||||||
| 	int minh = (height < oldh) ? height : oldh; |  | ||||||
| 	int i; |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < minh; ++i) |  | ||||||
| 	{ |  | ||||||
| 		struct tb_cell* csrc = oldcells + (i * oldw); |  | ||||||
| 		struct tb_cell* cdst = buf->cells + (i * width); |  | ||||||
| 		memcpy(cdst, csrc, sizeof(struct tb_cell) * minw); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	free(oldcells); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void cellbuf_clear(struct cellbuf* buf) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
| 	int ncells = buf->width * buf->height; |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < ncells; ++i) |  | ||||||
| 	{ |  | ||||||
| 		buf->cells[i].ch = ' '; |  | ||||||
| 		buf->cells[i].fg = foreground; |  | ||||||
| 		buf->cells[i].bg = background; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void cellbuf_free(struct cellbuf* buf) |  | ||||||
| { |  | ||||||
| 	free(buf->cells); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void get_term_size(int* w, int* h) |  | ||||||
| { |  | ||||||
| 	struct winsize sz; |  | ||||||
| 	memset(&sz, 0, sizeof(sz)); |  | ||||||
| 
 |  | ||||||
| 	ioctl(out_fileno, TIOCGWINSZ, &sz); |  | ||||||
| 
 |  | ||||||
| 	if (w) |  | ||||||
| 	{ |  | ||||||
| 		*w = sz.ws_col; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (h) |  | ||||||
| 	{ |  | ||||||
| 		*h = sz.ws_row; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void send_attr(uint32_t fg, uint32_t bg) |  | ||||||
| { |  | ||||||
| #define LAST_ATTR_INIT 0xFFFFFFFF |  | ||||||
| 	static uint32_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT; |  | ||||||
| 
 |  | ||||||
| 	if (fg != lastfg || bg != lastbg) |  | ||||||
| 	{ |  | ||||||
| 		memstream_puts(&write_buffer, funcs[T_SGR0]); |  | ||||||
| 		uint32_t fgcol; |  | ||||||
| 		uint32_t bgcol; |  | ||||||
| 
 |  | ||||||
| 		switch (outputmode) |  | ||||||
| 		{ |  | ||||||
| 			case TB_OUTPUT_TRUECOLOR: |  | ||||||
| 				fgcol = fg; |  | ||||||
| 				bgcol = bg; |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_OUTPUT_256: |  | ||||||
| 				fgcol = fg & 0xFF; |  | ||||||
| 				bgcol = bg & 0xFF; |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_OUTPUT_216: |  | ||||||
| 				fgcol = fg & 0xFF; |  | ||||||
| 
 |  | ||||||
| 				if (fgcol > 215) |  | ||||||
| 				{ |  | ||||||
| 					fgcol = 7; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				bgcol = bg & 0xFF; |  | ||||||
| 
 |  | ||||||
| 				if (bgcol > 215) |  | ||||||
| 				{ |  | ||||||
| 					bgcol = 0; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				fgcol += 0x10; |  | ||||||
| 				bgcol += 0x10; |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_OUTPUT_GRAYSCALE: |  | ||||||
| 				fgcol = fg & 0xFF; |  | ||||||
| 
 |  | ||||||
| 				if (fgcol > 23) |  | ||||||
| 				{ |  | ||||||
| 					fgcol = 23; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				bgcol = bg & 0xFF; |  | ||||||
| 
 |  | ||||||
| 				if (bgcol > 23) |  | ||||||
| 				{ |  | ||||||
| 					bgcol = 0; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				fgcol += 0xe8; |  | ||||||
| 				bgcol += 0xe8; |  | ||||||
| 				break; |  | ||||||
| 
 |  | ||||||
| 			case TB_OUTPUT_NORMAL: |  | ||||||
| 			default: |  | ||||||
| 				fgcol = fg & 0x0F; |  | ||||||
| 				bgcol = bg & 0x0F; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (fg & TB_BOLD) |  | ||||||
| 		{ |  | ||||||
| 			memstream_puts(&write_buffer, funcs[T_BOLD]); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (bg & TB_BOLD) |  | ||||||
| 		{ |  | ||||||
| 			memstream_puts(&write_buffer, funcs[T_BLINK]); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (fg & TB_UNDERLINE) |  | ||||||
| 		{ |  | ||||||
| 			memstream_puts(&write_buffer, funcs[T_UNDERLINE]); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if ((fg & TB_REVERSE) || (bg & TB_REVERSE)) |  | ||||||
| 		{ |  | ||||||
| 			memstream_puts(&write_buffer, funcs[T_REVERSE]); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		write_sgr(fgcol, bgcol); |  | ||||||
| 
 |  | ||||||
| 		lastfg = fg; |  | ||||||
| 		lastbg = bg; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void send_char(int x, int y, uint32_t c) |  | ||||||
| { |  | ||||||
| 	char buf[7]; |  | ||||||
| 	int bw = utf8_unicode_to_char(buf, c); |  | ||||||
| 	buf[bw] = '\0'; |  | ||||||
| 
 |  | ||||||
| 	if (x - 1 != lastx || y != lasty) |  | ||||||
| 	{ |  | ||||||
| 		write_cursor(x, y); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	lastx = x; |  | ||||||
| 	lasty = y; |  | ||||||
| 
 |  | ||||||
| 	if (!c) |  | ||||||
| 	{ |  | ||||||
| 		buf[0] = ' '; // replace 0 with whitespace
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	memstream_puts(&write_buffer, buf); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void send_clear(void) |  | ||||||
| { |  | ||||||
| 	send_attr(foreground, background); |  | ||||||
| 	memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]); |  | ||||||
| 
 |  | ||||||
| 	if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y)) |  | ||||||
| 	{ |  | ||||||
| 		write_cursor(cursor_x, cursor_y); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	memstream_flush(&write_buffer); |  | ||||||
| 
 |  | ||||||
| 	// we need to invalidate cursor position too and these two vars are
 |  | ||||||
| 	// used only for simple cursor positioning optimization, cursor
 |  | ||||||
| 	// actually may be in the correct place, but we simply discard
 |  | ||||||
| 	// optimization once and it gives us simple solution for the case when
 |  | ||||||
| 	// cursor moved
 |  | ||||||
| 	lastx = LAST_COORD_INIT; |  | ||||||
| 	lasty = LAST_COORD_INIT; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void sigwinch_handler(int xxx) |  | ||||||
| { |  | ||||||
| 	(void) xxx; |  | ||||||
| 	const int zzz = 1; |  | ||||||
| 	write(winch_fds[1], &zzz, sizeof(int)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void update_size(void) |  | ||||||
| { |  | ||||||
| 	update_term_size(); |  | ||||||
| 	cellbuf_resize(&back_buffer, termw, termh); |  | ||||||
| 	cellbuf_resize(&front_buffer, termw, termh); |  | ||||||
| 	cellbuf_clear(&front_buffer); |  | ||||||
| 	send_clear(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void update_term_size(void) |  | ||||||
| { |  | ||||||
| 	struct winsize sz; |  | ||||||
| 	memset(&sz, 0, sizeof(sz)); |  | ||||||
| 
 |  | ||||||
| 	ioctl(out_fileno, TIOCGWINSZ, &sz); |  | ||||||
| 
 |  | ||||||
| 	termw = sz.ws_col; |  | ||||||
| 	termh = sz.ws_row; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int wait_fill_event(struct tb_event* event, struct timeval* timeout) |  | ||||||
| { |  | ||||||
| #define ENOUGH_DATA_FOR_INPUT_PARSING 128 |  | ||||||
| 	int result; |  | ||||||
| 	char buf[ENOUGH_DATA_FOR_INPUT_PARSING]; |  | ||||||
| 	fd_set events; |  | ||||||
| 	memset(event, 0, sizeof(struct tb_event)); |  | ||||||
| 
 |  | ||||||
| 	// try to extract event from input buffer, return on success
 |  | ||||||
| 	event->type = TB_EVENT_KEY; |  | ||||||
| 
 |  | ||||||
| 	if (extract_event(event, &inbuf, inputmode)) |  | ||||||
| 	{ |  | ||||||
| 		return event->type; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// it looks like input buffer is incomplete, let's try the short path
 |  | ||||||
| 	size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in); |  | ||||||
| 
 |  | ||||||
| 	if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in)) |  | ||||||
| 	{ |  | ||||||
| 		clearerr(in); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (r > 0) |  | ||||||
| 	{ |  | ||||||
| 		if (ringbuffer_free_space(&inbuf) < r) |  | ||||||
| 		{ |  | ||||||
| 			return -1; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		ringbuffer_push(&inbuf, buf, r); |  | ||||||
| 
 |  | ||||||
| 		if (extract_event(event, &inbuf, inputmode)) |  | ||||||
| 		{ |  | ||||||
| 			return event->type; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// no stuff in FILE's internal buffer, block in select
 |  | ||||||
| 	while (1) |  | ||||||
| 	{ |  | ||||||
| 		FD_ZERO(&events); |  | ||||||
| 		FD_SET(in_fileno, &events); |  | ||||||
| 		FD_SET(winch_fds[0], &events); |  | ||||||
| 		int maxfd = (winch_fds[0] > in_fileno) ? winch_fds[0] : in_fileno; |  | ||||||
| 		result = select(maxfd + 1, &events, 0, 0, timeout); |  | ||||||
| 
 |  | ||||||
| 		if (!result) |  | ||||||
| 		{ |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (FD_ISSET(in_fileno, &events)) |  | ||||||
| 		{ |  | ||||||
| 			event->type = TB_EVENT_KEY; |  | ||||||
| 			size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in); |  | ||||||
| 
 |  | ||||||
| 			if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in)) |  | ||||||
| 			{ |  | ||||||
| 				clearerr(in); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (r == 0) |  | ||||||
| 			{ |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// if there is no free space in input buffer, return error
 |  | ||||||
| 			if (ringbuffer_free_space(&inbuf) < r) |  | ||||||
| 			{ |  | ||||||
| 				return -1; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// fill buffer
 |  | ||||||
| 			ringbuffer_push(&inbuf, buf, r); |  | ||||||
| 
 |  | ||||||
| 			if (extract_event(event, &inbuf, inputmode)) |  | ||||||
| 			{ |  | ||||||
| 				return event->type; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (FD_ISSET(winch_fds[0], &events)) |  | ||||||
| 		{ |  | ||||||
| 			event->type = TB_EVENT_RESIZE; |  | ||||||
| 			int zzz = 0; |  | ||||||
| 			read(winch_fds[0], &zzz, sizeof(int)); |  | ||||||
| 			buffer_size_change_request = 1; |  | ||||||
| 			get_term_size(&event->w, &event->h); |  | ||||||
| 			return TB_EVENT_RESIZE; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,307 +0,0 @@ | ||||||
| #ifndef H_TERMBOX |  | ||||||
| #define H_TERMBOX |  | ||||||
| #include <stdint.h> |  | ||||||
| 
 |  | ||||||
| // shared objects
 |  | ||||||
| #if __GNUC__ >= 4 |  | ||||||
| 	#define SO_IMPORT __attribute__((visibility("default"))) |  | ||||||
| #else |  | ||||||
| 	#define SO_IMPORT |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| // c++
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| extern "C" { |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| // Key constants. See also struct tb_event's key field.
 |  | ||||||
| // These are a safe subset of terminfo keys, which exist on all popular
 |  | ||||||
| // terminals. Termbox uses only them to stay truly portable.
 |  | ||||||
| #define TB_KEY_F1               (0xFFFF-0) |  | ||||||
| #define TB_KEY_F2               (0xFFFF-1) |  | ||||||
| #define TB_KEY_F3               (0xFFFF-2) |  | ||||||
| #define TB_KEY_F4               (0xFFFF-3) |  | ||||||
| #define TB_KEY_F5               (0xFFFF-4) |  | ||||||
| #define TB_KEY_F6               (0xFFFF-5) |  | ||||||
| #define TB_KEY_F7               (0xFFFF-6) |  | ||||||
| #define TB_KEY_F8               (0xFFFF-7) |  | ||||||
| #define TB_KEY_F9               (0xFFFF-8) |  | ||||||
| #define TB_KEY_F10              (0xFFFF-9) |  | ||||||
| #define TB_KEY_F11              (0xFFFF-10) |  | ||||||
| #define TB_KEY_F12              (0xFFFF-11) |  | ||||||
| #define TB_KEY_INSERT           (0xFFFF-12) |  | ||||||
| #define TB_KEY_DELETE           (0xFFFF-13) |  | ||||||
| #define TB_KEY_HOME             (0xFFFF-14) |  | ||||||
| #define TB_KEY_END              (0xFFFF-15) |  | ||||||
| #define TB_KEY_PGUP             (0xFFFF-16) |  | ||||||
| #define TB_KEY_PGDN             (0xFFFF-17) |  | ||||||
| #define TB_KEY_ARROW_UP         (0xFFFF-18) |  | ||||||
| #define TB_KEY_ARROW_DOWN       (0xFFFF-19) |  | ||||||
| #define TB_KEY_ARROW_LEFT       (0xFFFF-20) |  | ||||||
| #define TB_KEY_ARROW_RIGHT      (0xFFFF-21) |  | ||||||
| #define TB_KEY_MOUSE_LEFT       (0xFFFF-22) |  | ||||||
| #define TB_KEY_MOUSE_RIGHT      (0xFFFF-23) |  | ||||||
| #define TB_KEY_MOUSE_MIDDLE     (0xFFFF-24) |  | ||||||
| #define TB_KEY_MOUSE_RELEASE    (0xFFFF-25) |  | ||||||
| #define TB_KEY_MOUSE_WHEEL_UP   (0xFFFF-26) |  | ||||||
| #define TB_KEY_MOUSE_WHEEL_DOWN (0xFFFF-27) |  | ||||||
| 
 |  | ||||||
| // These are all ASCII code points below SPACE character and a BACKSPACE key.
 |  | ||||||
| #define TB_KEY_CTRL_TILDE       0x00 |  | ||||||
| #define TB_KEY_CTRL_2           0x00 // clash with 'CTRL_TILDE'
 |  | ||||||
| #define TB_KEY_CTRL_A           0x01 |  | ||||||
| #define TB_KEY_CTRL_B           0x02 |  | ||||||
| #define TB_KEY_CTRL_C           0x03 |  | ||||||
| #define TB_KEY_CTRL_D           0x04 |  | ||||||
| #define TB_KEY_CTRL_E           0x05 |  | ||||||
| #define TB_KEY_CTRL_F           0x06 |  | ||||||
| #define TB_KEY_CTRL_G           0x07 |  | ||||||
| #define TB_KEY_BACKSPACE        0x08 |  | ||||||
| #define TB_KEY_CTRL_H           0x08 // clash with 'CTRL_BACKSPACE'
 |  | ||||||
| #define TB_KEY_TAB              0x09 |  | ||||||
| #define TB_KEY_CTRL_I           0x09 // clash with 'TAB'
 |  | ||||||
| #define TB_KEY_CTRL_J           0x0A |  | ||||||
| #define TB_KEY_CTRL_K           0x0B |  | ||||||
| #define TB_KEY_CTRL_L           0x0C |  | ||||||
| #define TB_KEY_ENTER            0x0D |  | ||||||
| #define TB_KEY_CTRL_M           0x0D // clash with 'ENTER'
 |  | ||||||
| #define TB_KEY_CTRL_N           0x0E |  | ||||||
| #define TB_KEY_CTRL_O           0x0F |  | ||||||
| #define TB_KEY_CTRL_P           0x10 |  | ||||||
| #define TB_KEY_CTRL_Q           0x11 |  | ||||||
| #define TB_KEY_CTRL_R           0x12 |  | ||||||
| #define TB_KEY_CTRL_S           0x13 |  | ||||||
| #define TB_KEY_CTRL_T           0x14 |  | ||||||
| #define TB_KEY_CTRL_U           0x15 |  | ||||||
| #define TB_KEY_CTRL_V           0x16 |  | ||||||
| #define TB_KEY_CTRL_W           0x17 |  | ||||||
| #define TB_KEY_CTRL_X           0x18 |  | ||||||
| #define TB_KEY_CTRL_Y           0x19 |  | ||||||
| #define TB_KEY_CTRL_Z           0x1A |  | ||||||
| #define TB_KEY_ESC              0x1B |  | ||||||
| #define TB_KEY_CTRL_LSQ_BRACKET 0x1B // clash with 'ESC'
 |  | ||||||
| #define TB_KEY_CTRL_3           0x1B // clash with 'ESC'
 |  | ||||||
| #define TB_KEY_CTRL_4           0x1C |  | ||||||
| #define TB_KEY_CTRL_BACKSLASH   0x1C // clash with 'CTRL_4'
 |  | ||||||
| #define TB_KEY_CTRL_5           0x1D |  | ||||||
| #define TB_KEY_CTRL_RSQ_BRACKET 0x1D // clash with 'CTRL_5'
 |  | ||||||
| #define TB_KEY_CTRL_6           0x1E |  | ||||||
| #define TB_KEY_CTRL_7           0x1F |  | ||||||
| #define TB_KEY_CTRL_SLASH       0x1F // clash with 'CTRL_7'
 |  | ||||||
| #define TB_KEY_CTRL_UNDERSCORE  0x1F // clash with 'CTRL_7'
 |  | ||||||
| #define TB_KEY_SPACE            0x20 |  | ||||||
| #define TB_KEY_BACKSPACE2       0x7F |  | ||||||
| #define TB_KEY_CTRL_8           0x7F // clash with 'BACKSPACE2'
 |  | ||||||
| 
 |  | ||||||
| // These are non-existing ones.
 |  | ||||||
| // #define TB_KEY_CTRL_1 clash with '1'
 |  | ||||||
| // #define TB_KEY_CTRL_9 clash with '9'
 |  | ||||||
| // #define TB_KEY_CTRL_0 clash with '0'
 |  | ||||||
| 
 |  | ||||||
| // Alt modifier constant, see tb_event.mod field and tb_select_input_mode function.
 |  | ||||||
| // Mouse-motion modifier
 |  | ||||||
| #define TB_MOD_ALT    0x01 |  | ||||||
| #define TB_MOD_MOTION 0x02 |  | ||||||
| 
 |  | ||||||
| // Colors (see struct tb_cell's fg and bg fields).
 |  | ||||||
| #define TB_DEFAULT 0x00 |  | ||||||
| #define TB_BLACK   0x01 |  | ||||||
| #define TB_RED     0x02 |  | ||||||
| #define TB_GREEN   0x03 |  | ||||||
| #define TB_YELLOW  0x04 |  | ||||||
| #define TB_BLUE    0x05 |  | ||||||
| #define TB_MAGENTA 0x06 |  | ||||||
| #define TB_CYAN    0x07 |  | ||||||
| #define TB_WHITE   0x08 |  | ||||||
| 
 |  | ||||||
| //  Attributes, it is possible to use multiple attributes by combining them
 |  | ||||||
| //  using bitwise OR ('|'). Although, colors cannot be combined. But you can
 |  | ||||||
| //  combine attributes and a single color. See also struct tb_cell's fg and bg
 |  | ||||||
| //  fields.
 |  | ||||||
| #define TB_BOLD      0x01000000 |  | ||||||
| #define TB_UNDERLINE 0x02000000 |  | ||||||
| #define TB_REVERSE   0x04000000 |  | ||||||
| 
 |  | ||||||
| // A cell, single conceptual entity on the terminal screen. The terminal screen
 |  | ||||||
| // is basically a 2d array of cells. It has the following fields:
 |  | ||||||
| // - 'ch' is a unicode character
 |  | ||||||
| // - 'fg' foreground color and attributes
 |  | ||||||
| // - 'bg' background color and attributes
 |  | ||||||
| struct tb_cell |  | ||||||
| { |  | ||||||
| 	uint32_t ch; |  | ||||||
| 	uint32_t fg; |  | ||||||
| 	uint32_t bg; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #define TB_EVENT_KEY    1 |  | ||||||
| #define TB_EVENT_RESIZE 2 |  | ||||||
| #define TB_EVENT_MOUSE  3 |  | ||||||
| 
 |  | ||||||
| // An event, single interaction from the user. The 'mod' and 'ch' fields are
 |  | ||||||
| // valid if 'type' is TB_EVENT_KEY. The 'w' and 'h' fields are valid if 'type'
 |  | ||||||
| // is TB_EVENT_RESIZE. The 'x' and 'y' fields are valid if 'type' is
 |  | ||||||
| // TB_EVENT_MOUSE. The 'key' field is valid if 'type' is either TB_EVENT_KEY
 |  | ||||||
| // or TB_EVENT_MOUSE. The fields 'key' and 'ch' are mutually exclusive; only
 |  | ||||||
| // one of them can be non-zero at a time.
 |  | ||||||
| struct tb_event |  | ||||||
| { |  | ||||||
| 	uint8_t type; |  | ||||||
| 	uint8_t mod; // modifiers to either 'key' or 'ch' below
 |  | ||||||
| 	uint16_t key; // one of the TB_KEY_* constants
 |  | ||||||
| 	uint32_t ch; // unicode character
 |  | ||||||
| 	int32_t w; |  | ||||||
| 	int32_t h; |  | ||||||
| 	int32_t x; |  | ||||||
| 	int32_t y; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| //  Error codes returned by tb_init(). All of them are self-explanatory, except
 |  | ||||||
| //  the pipe trap error. Termbox uses unix pipes in order to deliver a message
 |  | ||||||
| //  from a signal handler (SIGWINCH) to the main event reading loop. Honestly in
 |  | ||||||
| //  most cases you should just check the returned code as < 0.
 |  | ||||||
| #define TB_EUNSUPPORTED_TERMINAL -1 |  | ||||||
| #define TB_EFAILED_TO_OPEN_TTY   -2 |  | ||||||
| #define TB_EPIPE_TRAP_ERROR      -3 |  | ||||||
| 
 |  | ||||||
| // Initializes the termbox library. This function should be called before any
 |  | ||||||
| // other functions. Function tb_init is same as tb_init_file("/dev/tty"). After successful initialization, the library must be
 |  | ||||||
| // finalized using the tb_shutdown() function.
 |  | ||||||
| SO_IMPORT int tb_init(void); |  | ||||||
| SO_IMPORT int tb_init_file(const char* name); |  | ||||||
| SO_IMPORT void tb_shutdown(void); |  | ||||||
| 
 |  | ||||||
| // Returns the size of the internal back buffer (which is the same as
 |  | ||||||
| // terminal's window size in characters). The internal buffer can be resized
 |  | ||||||
| // after tb_clear() or tb_present() function calls. Both dimensions have an
 |  | ||||||
| // unspecified negative value when called before tb_init() or after
 |  | ||||||
| // tb_shutdown().
 |  | ||||||
| SO_IMPORT int tb_width(void); |  | ||||||
| SO_IMPORT int tb_height(void); |  | ||||||
| 
 |  | ||||||
| // Clears the internal back buffer using TB_DEFAULT color or the
 |  | ||||||
| // color/attributes set by tb_set_clear_attributes() function.
 |  | ||||||
| SO_IMPORT void tb_clear(void); |  | ||||||
| SO_IMPORT void tb_set_clear_attributes(uint32_t fg, uint32_t bg); |  | ||||||
| 
 |  | ||||||
| // Synchronizes the internal back buffer with the terminal.
 |  | ||||||
| SO_IMPORT void tb_present(void); |  | ||||||
| 
 |  | ||||||
| #define TB_HIDE_CURSOR -1 |  | ||||||
| 
 |  | ||||||
| // Sets the position of the cursor. Upper-left character is (0, 0). If you pass
 |  | ||||||
| // TB_HIDE_CURSOR as both coordinates, then the cursor will be hidden. Cursor
 |  | ||||||
| // is hidden by default.
 |  | ||||||
| SO_IMPORT void tb_set_cursor(int cx, int cy); |  | ||||||
| 
 |  | ||||||
| // Changes cell's parameters in the internal back buffer at the specified
 |  | ||||||
| // position.
 |  | ||||||
| SO_IMPORT void tb_put_cell(int x, int y, const struct tb_cell* cell); |  | ||||||
| SO_IMPORT void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg, |  | ||||||
| 	uint32_t bg); |  | ||||||
| 
 |  | ||||||
| // Copies the buffer from 'cells' at the specified position, assuming the
 |  | ||||||
| // buffer is a two-dimensional array of size ('w' x 'h'), represented as a
 |  | ||||||
| // one-dimensional buffer containing lines of cells starting from the top.
 |  | ||||||
| // (DEPRECATED: use tb_cell_buffer() instead and copy memory on your own)
 |  | ||||||
| SO_IMPORT void tb_blit(int x, int y, int w, int h, const struct tb_cell* cells); |  | ||||||
| 
 |  | ||||||
| // Returns a pointer to internal cell back buffer. You can get its dimensions
 |  | ||||||
| // using tb_width() and tb_height() functions. The pointer stays valid as long
 |  | ||||||
| // as no tb_clear() and tb_present() calls are made. The buffer is
 |  | ||||||
| // one-dimensional buffer containing lines of cells starting from the top.
 |  | ||||||
| SO_IMPORT struct tb_cell* tb_cell_buffer(void); |  | ||||||
| 
 |  | ||||||
| #define TB_INPUT_CURRENT 0 // 000
 |  | ||||||
| #define TB_INPUT_ESC     1 // 001
 |  | ||||||
| #define TB_INPUT_ALT     2 // 010
 |  | ||||||
| #define TB_INPUT_MOUSE   4 // 100
 |  | ||||||
| 
 |  | ||||||
| //  Sets the termbox input mode. Termbox has two input modes:
 |  | ||||||
| //  1. Esc input mode.
 |  | ||||||
| //    When ESC sequence is in the buffer and it doesn't match any known
 |  | ||||||
| //    ESC sequence => ESC means TB_KEY_ESC.
 |  | ||||||
| //  2. Alt input mode.
 |  | ||||||
| //    When ESC sequence is in the buffer and it doesn't match any known
 |  | ||||||
| //    sequence => ESC enables TB_MOD_ALT modifier for the next keyboard event.
 |  | ||||||
| //
 |  | ||||||
| //  You can also apply TB_INPUT_MOUSE via bitwise OR operation to either of the
 |  | ||||||
| //  modes (e.g. TB_INPUT_ESC | TB_INPUT_MOUSE). If none of the main two modes
 |  | ||||||
| //  were set, but the mouse mode was, TB_INPUT_ESC mode is used. If for some
 |  | ||||||
| //  reason you've decided to use (TB_INPUT_ESC | TB_INPUT_ALT) combination, it
 |  | ||||||
| //  will behave as if only TB_INPUT_ESC was selected.
 |  | ||||||
| //
 |  | ||||||
| //  If 'mode' is TB_INPUT_CURRENT, it returns the current input mode.
 |  | ||||||
| //
 |  | ||||||
| //  Default termbox input mode is TB_INPUT_ESC.
 |  | ||||||
| SO_IMPORT int tb_select_input_mode(int mode); |  | ||||||
| 
 |  | ||||||
| #define TB_OUTPUT_CURRENT   0 |  | ||||||
| #define TB_OUTPUT_NORMAL    1 |  | ||||||
| #define TB_OUTPUT_256       2 |  | ||||||
| #define TB_OUTPUT_216       3 |  | ||||||
| #define TB_OUTPUT_GRAYSCALE 4 |  | ||||||
| #define TB_OUTPUT_TRUECOLOR 5 |  | ||||||
| 
 |  | ||||||
| // Sets the termbox output mode. Termbox has three output options:
 |  | ||||||
| // 1. TB_OUTPUT_NORMAL     => [1..8]
 |  | ||||||
| //   This mode provides 8 different colors:
 |  | ||||||
| //   black, red, green, yellow, blue, magenta, cyan, white
 |  | ||||||
| //   Shortcut: TB_BLACK, TB_RED, ...
 |  | ||||||
| //   Attributes: TB_BOLD, TB_UNDERLINE, TB_REVERSE
 |  | ||||||
| //
 |  | ||||||
| //   Example usage:
 |  | ||||||
| //       tb_change_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED);
 |  | ||||||
| //
 |  | ||||||
| // 2. TB_OUTPUT_256        => [0..256]
 |  | ||||||
| //   In this mode you can leverage the 256 terminal mode:
 |  | ||||||
| //   0x00 - 0x07: the 8 colors as in TB_OUTPUT_NORMAL
 |  | ||||||
| //   0x08 - 0x0f: TB_* | TB_BOLD
 |  | ||||||
| //   0x10 - 0xe7: 216 different colors
 |  | ||||||
| //   0xe8 - 0xff: 24 different shades of grey
 |  | ||||||
| //
 |  | ||||||
| //   Example usage:
 |  | ||||||
| //       tb_change_cell(x, y, '@', 184, 240);
 |  | ||||||
| //       tb_change_cell(x, y, '@', 0xb8, 0xf0);
 |  | ||||||
| //
 |  | ||||||
| // 3. TB_OUTPUT_216        => [0..216]
 |  | ||||||
| //   This mode supports the 3rd range of the 256 mode only.
 |  | ||||||
| //   But you don't need to provide an offset.
 |  | ||||||
| //
 |  | ||||||
| // 4. TB_OUTPUT_GRAYSCALE  => [0..23]
 |  | ||||||
| //   This mode supports the 4th range of the 256 mode only.
 |  | ||||||
| //   But you dont need to provide an offset.
 |  | ||||||
| //
 |  | ||||||
| // 5. TB_OUTPUT_TRUECOLOR  => [0x000000..0xFFFFFF]
 |  | ||||||
| //   This mode supports 24-bit true color. Format is 0xRRGGBB.
 |  | ||||||
| //
 |  | ||||||
| // Execute build/src/demo/output to see its impact on your terminal.
 |  | ||||||
| //
 |  | ||||||
| // If 'mode' is TB_OUTPUT_CURRENT, it returns the current output mode.
 |  | ||||||
| //
 |  | ||||||
| // Default termbox output mode is TB_OUTPUT_NORMAL.
 |  | ||||||
| SO_IMPORT int tb_select_output_mode(int mode); |  | ||||||
| 
 |  | ||||||
| // Wait for an event up to 'timeout' milliseconds and fill the 'event'
 |  | ||||||
| // structure with it, when the event is available. Returns the type of the
 |  | ||||||
| // event (one of TB_EVENT_* constants) or -1 if there was an error or 0 in case
 |  | ||||||
| // there were no event during 'timeout' period.
 |  | ||||||
| SO_IMPORT int tb_peek_event(struct tb_event* event, int timeout); |  | ||||||
| 
 |  | ||||||
| // Wait for an event forever and fill the 'event' structure with it, when the
 |  | ||||||
| // event is available. Returns the type of the event (one of TB_EVENT_
 |  | ||||||
| // constants) or -1 if there was an error.
 |  | ||||||
| SO_IMPORT int tb_poll_event(struct tb_event* event); |  | ||||||
| 
 |  | ||||||
| // Utility utf8 functions.
 |  | ||||||
| #define TB_EOF -1 |  | ||||||
| SO_IMPORT int utf8_char_length(char c); |  | ||||||
| SO_IMPORT int utf8_char_to_unicode(uint32_t* out, const char* c); |  | ||||||
| SO_IMPORT int utf8_unicode_to_char(char* out, uint32_t c); |  | ||||||
| 
 |  | ||||||
| // c++
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #endif |  | ||||||
|  | @ -1,106 +0,0 @@ | ||||||
| #include "termbox.h" |  | ||||||
| 
 |  | ||||||
| static const unsigned char utf8_length[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, |  | ||||||
| 	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, |  | ||||||
| 	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, |  | ||||||
| 	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, |  | ||||||
| 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |  | ||||||
| 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |  | ||||||
| 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |  | ||||||
| 	4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const unsigned char utf8_mask[6] = |  | ||||||
| { |  | ||||||
| 	0x7F, |  | ||||||
| 	0x1F, |  | ||||||
| 	0x0F, |  | ||||||
| 	0x07, |  | ||||||
| 	0x03, |  | ||||||
| 	0x01 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| int utf8_char_length(char c) |  | ||||||
| { |  | ||||||
| 	return utf8_length[(unsigned char)c]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int utf8_char_to_unicode(uint32_t* out, const char* c) |  | ||||||
| { |  | ||||||
| 	if (*c == 0) |  | ||||||
| 	{ |  | ||||||
| 		return TB_EOF; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	int i; |  | ||||||
| 	unsigned char len = utf8_char_length(*c); |  | ||||||
| 	unsigned char mask = utf8_mask[len - 1]; |  | ||||||
| 	uint32_t result = c[0] & mask; |  | ||||||
| 
 |  | ||||||
| 	for (i = 1; i < len; ++i) |  | ||||||
| 	{ |  | ||||||
| 		result <<= 6; |  | ||||||
| 		result |= c[i] & 0x3f; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	*out = result; |  | ||||||
| 	return (int)len; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int utf8_unicode_to_char(char* out, uint32_t c) |  | ||||||
| { |  | ||||||
| 	int len = 0; |  | ||||||
| 	int first; |  | ||||||
| 	int i; |  | ||||||
| 
 |  | ||||||
| 	if (c < 0x80) |  | ||||||
| 	{ |  | ||||||
| 		first = 0; |  | ||||||
| 		len = 1; |  | ||||||
| 	} |  | ||||||
| 	else if (c < 0x800) |  | ||||||
| 	{ |  | ||||||
| 		first = 0xc0; |  | ||||||
| 		len = 2; |  | ||||||
| 	} |  | ||||||
| 	else if (c < 0x10000) |  | ||||||
| 	{ |  | ||||||
| 		first = 0xe0; |  | ||||||
| 		len = 3; |  | ||||||
| 	} |  | ||||||
| 	else if (c < 0x200000) |  | ||||||
| 	{ |  | ||||||
| 		first = 0xf0; |  | ||||||
| 		len = 4; |  | ||||||
| 	} |  | ||||||
| 	else if (c < 0x4000000) |  | ||||||
| 	{ |  | ||||||
| 		first = 0xf8; |  | ||||||
| 		len = 5; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		first = 0xfc; |  | ||||||
| 		len = 6; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (i = len - 1; i > 0; --i) |  | ||||||
| 	{ |  | ||||||
| 		out[i] = (c & 0x3f) | 0x80; |  | ||||||
| 		c >>= 6; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	out[0] = c | first; |  | ||||||
| 
 |  | ||||||
| 	return len; |  | ||||||
| } |  | ||||||
|  | @ -1,27 +0,0 @@ | ||||||
| --style=break |  | ||||||
| --indent=force-tab=4 |  | ||||||
| --indent-classes |  | ||||||
| --indent-switches |  | ||||||
| --indent-namespaces |  | ||||||
| --indent-after-parens |  | ||||||
| --indent-continuation=1 |  | ||||||
| --indent-preproc-block |  | ||||||
| --indent-preproc-define |  | ||||||
| --indent-preproc-cond |  | ||||||
| --indent-col1-comments |  | ||||||
| --min-conditional-indent=0 |  | ||||||
| --max-continuation-indent=40 |  | ||||||
| --break-blocks |  | ||||||
| --pad-oper |  | ||||||
| --pad-comma |  | ||||||
| --pad-header |  | ||||||
| --unpad-paren |  | ||||||
| --align-pointer=type |  | ||||||
| --align-reference=type |  | ||||||
| --break-one-line-headers |  | ||||||
| --add-braces |  | ||||||
| --attach-return-type |  | ||||||
| --attach-return-type-decl |  | ||||||
| --remove-comment-prefix |  | ||||||
| --max-code-length=80 |  | ||||||
| --mode=c |  | ||||||
|  | @ -1,108 +0,0 @@ | ||||||
| #!/usr/bin/env python |  | ||||||
| 
 |  | ||||||
| import sys, os, subprocess |  | ||||||
| 
 |  | ||||||
| def escaped(s): |  | ||||||
| 	return s.replace("\033", "\\033") |  | ||||||
| 
 |  | ||||||
| def tput(term, name): |  | ||||||
| 	try: |  | ||||||
| 		return subprocess.check_output(['tput', '-T%s' % term, name]).decode() |  | ||||||
| 	except subprocess.CalledProcessError as e: |  | ||||||
| 		return e.output.decode() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def w(s): |  | ||||||
| 	if s == None: |  | ||||||
| 		return |  | ||||||
| 	sys.stdout.write(s) |  | ||||||
| 
 |  | ||||||
| terminals = { |  | ||||||
| 	'xterm' : 'xterm', |  | ||||||
| 	'rxvt-256color' : 'rxvt_256color', |  | ||||||
| 	'rxvt-unicode' : 'rxvt_unicode', |  | ||||||
| 	'linux' : 'linux', |  | ||||||
| 	'Eterm' : 'eterm', |  | ||||||
| 	'screen' : 'screen' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| keys = [ |  | ||||||
| 	"F1",		"kf1", |  | ||||||
| 	"F2",		"kf2", |  | ||||||
| 	"F3",		"kf3", |  | ||||||
| 	"F4",		"kf4", |  | ||||||
| 	"F5",		"kf5", |  | ||||||
| 	"F6",		"kf6", |  | ||||||
| 	"F7",		"kf7", |  | ||||||
| 	"F8",		"kf8", |  | ||||||
| 	"F9",		"kf9", |  | ||||||
| 	"F10",		"kf10", |  | ||||||
| 	"F11",		"kf11", |  | ||||||
| 	"F12",		"kf12", |  | ||||||
| 	"INSERT",	"kich1", |  | ||||||
| 	"DELETE",	"kdch1", |  | ||||||
| 	"HOME",		"khome", |  | ||||||
| 	"END",		"kend", |  | ||||||
| 	"PGUP",		"kpp", |  | ||||||
| 	"PGDN",		"knp", |  | ||||||
| 	"KEY_UP",	"kcuu1", |  | ||||||
| 	"KEY_DOWN",	"kcud1", |  | ||||||
| 	"KEY_LEFT",	"kcub1", |  | ||||||
| 	"KEY_RIGHT",	"kcuf1" |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| funcs = [ |  | ||||||
| 	"T_ENTER_CA",		"smcup", |  | ||||||
| 	"T_EXIT_CA",		"rmcup", |  | ||||||
| 	"T_SHOW_CURSOR",	"cnorm", |  | ||||||
| 	"T_HIDE_CURSOR",	"civis", |  | ||||||
| 	"T_CLEAR_SCREEN",	"clear", |  | ||||||
| 	"T_SGR0",		"sgr0", |  | ||||||
| 	"T_UNDERLINE",		"smul", |  | ||||||
| 	"T_BOLD",		"bold", |  | ||||||
| 	"T_BLINK",		"blink", |  | ||||||
| 	"T_REVERSE",            "rev", |  | ||||||
| 	"T_ENTER_KEYPAD",	"smkx", |  | ||||||
| 	"T_EXIT_KEYPAD",	"rmkx" |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| def iter_pairs(iterable): |  | ||||||
| 	iterable = iter(iterable) |  | ||||||
| 	while True: |  | ||||||
| 		yield (next(iterable), next(iterable)) |  | ||||||
| 
 |  | ||||||
| def do_term(term, nick): |  | ||||||
| 	w("// %s\n" % term) |  | ||||||
| 	w("static const char *%s_keys[] = {\n\t" % nick) |  | ||||||
| 	for k, v in iter_pairs(keys): |  | ||||||
| 		w('"') |  | ||||||
| 		w(escaped(tput(term, v))) |  | ||||||
| 		w('",') |  | ||||||
| 	w(" 0\n};\n") |  | ||||||
| 	w("static const char *%s_funcs[] = {\n\t" % nick) |  | ||||||
| 	for k,v in iter_pairs(funcs): |  | ||||||
| 		w('"') |  | ||||||
| 		if v == "sgr": |  | ||||||
| 			w("\\033[3%d;4%dm") |  | ||||||
| 		elif v == "cup": |  | ||||||
| 			w("\\033[%d;%dH") |  | ||||||
| 		else: |  | ||||||
| 			w(escaped(tput(term, v))) |  | ||||||
| 		w('", ') |  | ||||||
| 	w("\n};\n\n") |  | ||||||
| 
 |  | ||||||
| def do_terms(d): |  | ||||||
| 	w("static struct term {\n") |  | ||||||
| 	w("\tconst char *name;\n") |  | ||||||
| 	w("\tconst char **keys;\n") |  | ||||||
| 	w("\tconst char **funcs;\n") |  | ||||||
| 	w("} terms[] = {\n") |  | ||||||
| 	for k, v in d.items(): |  | ||||||
| 		w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v)) |  | ||||||
| 	w("\t{0, 0, 0},\n") |  | ||||||
| 	w("};\n") |  | ||||||
| 
 |  | ||||||
| for k,v in terminals.items(): |  | ||||||
| 	do_term(k, v) |  | ||||||
| 
 |  | ||||||
| do_terms(terminals) |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -145,15 +145,15 @@ pub fn draw(self: *Matrix) void { | ||||||
|         var y: u64 = 1; |         var y: u64 = 1; | ||||||
|         while (y <= self.terminal_buffer.height) : (y += 1) { |         while (y <= self.terminal_buffer.height) : (y += 1) { | ||||||
|             const dot = self.dots[buf_width * y + x]; |             const dot = self.dots[buf_width * y + x]; | ||||||
|             var fg: u32 = @intCast(termbox.TB_GREEN); |             var fg: u16 = @intCast(termbox.TB_GREEN); | ||||||
| 
 | 
 | ||||||
|             if (dot.value == -1 or dot.value == ' ') { |             if (dot.value == -1 or dot.value == ' ') { | ||||||
|                 termbox.tb_change_cell(@intCast(x), @intCast(y - 1), ' ', fg, termbox.TB_DEFAULT); |                 _ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), ' ', fg, termbox.TB_DEFAULT); | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (dot.is_head) fg = @intCast(termbox.TB_WHITE | termbox.TB_BOLD); |             if (dot.is_head) fg = @intCast(termbox.TB_WHITE | termbox.TB_BOLD); | ||||||
|             termbox.tb_change_cell(@intCast(x), @intCast(y - 1), @intCast(dot.value), fg, termbox.TB_DEFAULT); |             _ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), @intCast(dot.value), fg, termbox.TB_DEFAULT); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,7 +3,8 @@ const builtin = @import("builtin"); | ||||||
| const Allocator = std.mem.Allocator; | const Allocator = std.mem.Allocator; | ||||||
| 
 | 
 | ||||||
| pub const termbox = @cImport({ | pub const termbox = @cImport({ | ||||||
|     @cInclude("termbox.h"); |     @cDefine("TB_IMPL", {}); | ||||||
|  |     @cInclude("termbox2.h"); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| pub const pam = @cImport({ | pub const pam = @cImport({ | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								src/main.zig
								
								
								
								
							
							
						
						
									
										27
									
								
								src/main.zig
								
								
								
								
							|  | @ -33,7 +33,7 @@ pub fn signalHandler(i: c_int) callconv(.C) void { | ||||||
|         _ = std.c.waitpid(session_pid, &status, 0); |         _ = std.c.waitpid(session_pid, &status, 0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     termbox.tb_shutdown(); |     _ = termbox.tb_shutdown(); | ||||||
|     std.c.exit(i); |     std.c.exit(i); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -143,7 +143,7 @@ pub fn main() !void { | ||||||
| 
 | 
 | ||||||
|     // Initialize termbox |     // Initialize termbox | ||||||
|     _ = termbox.tb_init(); |     _ = termbox.tb_init(); | ||||||
|     defer termbox.tb_shutdown(); |     defer _ = termbox.tb_shutdown(); | ||||||
| 
 | 
 | ||||||
|     const act = std.posix.Sigaction{ |     const act = std.posix.Sigaction{ | ||||||
|         .handler = .{ .handler = &signalHandler }, |         .handler = .{ .handler = &signalHandler }, | ||||||
|  | @ -152,8 +152,8 @@ pub fn main() !void { | ||||||
|     }; |     }; | ||||||
|     try std.posix.sigaction(std.posix.SIG.TERM, &act, null); |     try std.posix.sigaction(std.posix.SIG.TERM, &act, null); | ||||||
| 
 | 
 | ||||||
|     _ = termbox.tb_select_output_mode(termbox.TB_OUTPUT_NORMAL); |     _ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL); | ||||||
|     termbox.tb_clear(); |     _ = termbox.tb_clear(); | ||||||
| 
 | 
 | ||||||
|     // Needed to reset termbox after auth |     // Needed to reset termbox after auth | ||||||
|     const tb_termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO); |     const tb_termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO); | ||||||
|  | @ -271,7 +271,7 @@ pub fn main() !void { | ||||||
|         if (!update or config.animation != .none) { |         if (!update or config.animation != .none) { | ||||||
|             if (!update) std.time.sleep(std.time.ns_per_ms * 100); |             if (!update) std.time.sleep(std.time.ns_per_ms * 100); | ||||||
| 
 | 
 | ||||||
|             termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer() |             _ = termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer() | ||||||
| 
 | 
 | ||||||
|             const width: u64 = @intCast(termbox.tb_width()); |             const width: u64 = @intCast(termbox.tb_width()); | ||||||
|             const height: u64 = @intCast(termbox.tb_height()); |             const height: u64 = @intCast(termbox.tb_height()); | ||||||
|  | @ -307,7 +307,7 @@ pub fn main() !void { | ||||||
|         if (update) { |         if (update) { | ||||||
|             // If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally |             // If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally | ||||||
|             if (auth_fails < 10) { |             if (auth_fails < 10) { | ||||||
|                 termbox.tb_clear(); |                 _ = termbox.tb_clear(); | ||||||
| 
 | 
 | ||||||
|                 switch (config.animation) { |                 switch (config.animation) { | ||||||
|                     .none => {}, |                     .none => {}, | ||||||
|  | @ -435,7 +435,7 @@ pub fn main() !void { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             termbox.tb_present(); |             _ = termbox.tb_present(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var timeout: i32 = -1; |         var timeout: i32 = -1; | ||||||
|  | @ -513,6 +513,15 @@ pub fn main() !void { | ||||||
|                 }; |                 }; | ||||||
|                 update = true; |                 update = true; | ||||||
|             }, |             }, | ||||||
|  |             termbox.TB_KEY_BACK_TAB => { | ||||||
|  |                 active_input = switch (active_input) { | ||||||
|  |                     .session => .password, | ||||||
|  |                     .login => .session, | ||||||
|  |                     .password => .login, | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 update = true; | ||||||
|  |             }, | ||||||
|             termbox.TB_KEY_ENTER => { |             termbox.TB_KEY_ENTER => { | ||||||
|                 if (config.save) save_last_settings: { |                 if (config.save) save_last_settings: { | ||||||
|                     var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings; |                     var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings; | ||||||
|  | @ -566,8 +575,8 @@ pub fn main() !void { | ||||||
| 
 | 
 | ||||||
|                 try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); |                 try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); | ||||||
|                 if (auth_fails < 10) { |                 if (auth_fails < 10) { | ||||||
|                     termbox.tb_clear(); |                     _ = termbox.tb_clear(); | ||||||
|                     termbox.tb_present(); |                     _ = termbox.tb_present(); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 update = true; |                 update = true; | ||||||
|  |  | ||||||
|  | @ -110,25 +110,25 @@ pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) | ||||||
|     self.box_y = y1; |     self.box_y = y1; | ||||||
| 
 | 
 | ||||||
|     if (show_borders) { |     if (show_borders) { | ||||||
|         termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.border_fg, self.bg); |         _ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.border_fg, self.bg); | ||||||
|         termbox.tb_change_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, self.border_fg, self.bg); |         _ = termbox.tb_set_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, self.border_fg, self.bg); | ||||||
|         termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.border_fg, self.bg); |         _ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.border_fg, self.bg); | ||||||
|         termbox.tb_change_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.border_fg, self.bg); |         _ = termbox.tb_set_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.border_fg, self.bg); | ||||||
| 
 | 
 | ||||||
|         var c1 = utils.initCell(self.box_chars.top, self.border_fg, self.bg); |         var c1 = utils.initCell(self.box_chars.top, self.border_fg, self.bg); | ||||||
|         var c2 = utils.initCell(self.box_chars.bottom, self.border_fg, self.bg); |         var c2 = utils.initCell(self.box_chars.bottom, self.border_fg, self.bg); | ||||||
| 
 | 
 | ||||||
|         for (0..self.box_width) |i| { |         for (0..self.box_width) |i| { | ||||||
|             termbox.tb_put_cell(@intCast(x1 + i), @intCast(y1 - 1), &c1); |             _ = utils.putCell(@intCast(x1 + i), @intCast(y1 - 1), &c1); | ||||||
|             termbox.tb_put_cell(@intCast(x1 + i), @intCast(y2), &c2); |             _ = utils.putCell(@intCast(x1 + i), @intCast(y2), &c2); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         c1.ch = self.box_chars.left; |         c1.ch = self.box_chars.left; | ||||||
|         c2.ch = self.box_chars.right; |         c2.ch = self.box_chars.right; | ||||||
| 
 | 
 | ||||||
|         for (0..self.box_height) |i| { |         for (0..self.box_height) |i| { | ||||||
|             termbox.tb_put_cell(@intCast(x1 - 1), @intCast(y1 + i), &c1); |             _ = utils.putCell(@intCast(x1 - 1), @intCast(y1 + i), &c1); | ||||||
|             termbox.tb_put_cell(@intCast(x2), @intCast(y1 + i), &c2); |             _ = utils.putCell(@intCast(x2), @intCast(y1 + i), &c2); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -137,7 +137,7 @@ pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) | ||||||
| 
 | 
 | ||||||
|         for (0..self.box_height) |y| { |         for (0..self.box_height) |y| { | ||||||
|             for (0..self.box_width) |x| { |             for (0..self.box_width) |x| { | ||||||
|                 termbox.tb_put_cell(@intCast(x1 + x), @intCast(y1 + y), &blank); |                 _ = utils.putCell(@intCast(x1 + x), @intCast(y1 + y), &blank); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -166,7 +166,7 @@ pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64) void { | ||||||
| 
 | 
 | ||||||
|     var i = x; |     var i = x; | ||||||
|     while (utf8.nextCodepoint()) |codepoint| : (i += 1) { |     while (utf8.nextCodepoint()) |codepoint| : (i += 1) { | ||||||
|         termbox.tb_change_cell(@intCast(i), yc, codepoint, self.fg, self.bg); |         _ = termbox.tb_set_cell(@intCast(i), yc, codepoint, self.fg, self.bg); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -180,7 +180,7 @@ pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64, | ||||||
| 
 | 
 | ||||||
|     var i = x; |     var i = x; | ||||||
|     while (utf8.nextCodepoint()) |codepoint| : (i += 1) { |     while (utf8.nextCodepoint()) |codepoint| : (i += 1) { | ||||||
|         termbox.tb_change_cell(@intCast(i), yc, codepoint, self.fg, self.bg); |         _ = termbox.tb_set_cell(@intCast(i), yc, codepoint, self.fg, self.bg); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -188,5 +188,5 @@ pub fn drawCharMultiple(self: TerminalBuffer, char: u8, x: u64, y: u64, length: | ||||||
|     const yc: c_int = @intCast(y); |     const yc: c_int = @intCast(y); | ||||||
|     const cell = utils.initCell(char, self.fg, self.bg); |     const cell = utils.initCell(char, self.fg, self.bg); | ||||||
| 
 | 
 | ||||||
|     for (0..length) |xx| termbox.tb_put_cell(@intCast(x + xx), yc, &cell); |     for (0..length) |xx| _ = utils.putCell(@intCast(x + xx), yc, &cell); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -185,7 +185,7 @@ pub fn handle(self: *Desktop, maybe_event: ?*termbox.tb_event, insert_mode: bool | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     termbox.tb_set_cursor(@intCast(self.x + 2), @intCast(self.y)); |     _ = termbox.tb_set_cursor(@intCast(self.x + 2), @intCast(self.y)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn draw(self: Desktop) void { | pub fn draw(self: Desktop) void { | ||||||
|  | @ -198,8 +198,8 @@ pub fn draw(self: Desktop) void { | ||||||
|     const y = self.buffer.box_y + self.buffer.margin_box_v + 2; |     const y = self.buffer.box_y + self.buffer.margin_box_v + 2; | ||||||
|     self.buffer.drawLabel(environment.specifier, x, y); |     self.buffer.drawLabel(environment.specifier, x, y); | ||||||
| 
 | 
 | ||||||
|     termbox.tb_change_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg); |     _ = termbox.tb_set_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg); | ||||||
|     termbox.tb_change_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg); |     _ = termbox.tb_set_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg); | ||||||
| 
 | 
 | ||||||
|     self.buffer.drawLabel(environment.name, self.x + 2, self.y); |     self.buffer.drawLabel(environment.name, self.x + 2, self.y); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -78,14 +78,21 @@ pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event, insert_mode: bool) ! | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y)); |     _ = termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn draw(self: Text) void { | pub fn draw(self: Text) void { | ||||||
|     const length = @min(self.text.items.len, self.visible_length); |     const length = @min(self.text.items.len, self.visible_length); | ||||||
|     if (length == 0) return; |     if (length == 0) return; | ||||||
| 
 | 
 | ||||||
|     const visible_slice = if (self.text.items.len > self.visible_length and self.cursor < self.text.items.len) self.text.items[self.visible_start..(self.visible_length + self.visible_start)] else self.text.items[self.visible_start..]; |     const visible_slice = vs: { | ||||||
|  |         if (self.text.items.len > self.visible_length and self.cursor < self.text.items.len) { | ||||||
|  |             break :vs self.text.items[self.visible_start..(self.visible_length + self.visible_start)]; | ||||||
|  |         } else { | ||||||
|  |             break :vs self.text.items[self.visible_start..]; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     self.buffer.drawLabel(visible_slice, self.x, self.y); |     self.buffer.drawLabel(visible_slice, self.x, self.y); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ const interop = @import("../interop.zig"); | ||||||
| 
 | 
 | ||||||
| const termbox = interop.termbox; | const termbox = interop.termbox; | ||||||
| 
 | 
 | ||||||
| pub fn initCell(ch: u32, fg: u32, bg: u32) termbox.tb_cell { | pub fn initCell(ch: u32, fg: u16, bg: u16) termbox.tb_cell { | ||||||
|     return .{ |     return .{ | ||||||
|         .ch = ch, |         .ch = ch, | ||||||
|         .fg = fg, |         .fg = fg, | ||||||
|  | @ -11,6 +11,10 @@ pub fn initCell(ch: u32, fg: u32, bg: u32) termbox.tb_cell { | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | pub fn putCell(x: i32, y: i32, cell: *const termbox.tb_cell) c_int { | ||||||
|  |     return termbox.tb_set_cell(x, y, cell.ch, cell.fg, cell.bg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Every codepoint is assumed to have a width of 1. | // Every codepoint is assumed to have a width of 1. | ||||||
| // Since ly should be running in a tty, this should be fine. | // Since ly should be running in a tty, this should be fine. | ||||||
| pub fn strWidth(str: []const u8) !u8 { | pub fn strWidth(str: []const u8) !u8 { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue