Skip to content

Commit dd66ee4

Browse files
committed
add windows support via codemod script
1 parent ed68bcc commit dd66ee4

3 files changed

Lines changed: 231 additions & 2 deletions

File tree

.github/workflows/termbox_test.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
name: termbox_test
22
on: [push, pull_request]
33
jobs:
4-
termbox_test_job:
4+
posix:
55
runs-on: ubuntu-latest
66
steps:
77
- uses: actions/checkout@v2
88
- run: make clean test
99
- run: CFLAGS='-UTB_LIB_OPTS' make clean test # non-egc, non-truecolor
10+
win32:
11+
runs-on: windows-latest
12+
steps:
13+
- uses: actions/checkout@v2
14+
- shell: bash
15+
run: ./windows.sh && cp -vf termbox2_win.h termbox2.h
16+
- shell: cmd
17+
run: |
18+
cl /c /I. /DTB_IMPL demo/keyboard.c
19+
cl /c /I. /DTB_IMPL /DTB_LIB_OPTS demo/keyboard.c

termbox2.h

100644100755
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,17 @@ SOFTWARE.
4343
#include <stdio.h>
4444
#include <stdlib.h>
4545
#include <string.h>
46+
#include <wchar.h>
47+
// __posix_header_start
4648
#include <sys/ioctl.h>
4749
#include <sys/select.h>
4850
#include <sys/stat.h>
4951
#include <sys/time.h>
5052
#include <sys/types.h>
5153
#include <termios.h>
5254
#include <unistd.h>
53-
#include <wchar.h>
5455
#include <wctype.h>
56+
// __posix_header_end
5557

5658
#ifdef PATH_MAX
5759
#define TB_PATH_MAX PATH_MAX
@@ -2354,9 +2356,12 @@ int tb_init_rwfd(int rfd, int wfd) {
23542356
int rv;
23552357

23562358
tb_reset();
2359+
2360+
// __posix_start
23572361
global.ttyfd = isatty(rfd) ? rfd : (isatty(wfd) ? wfd : -1);
23582362
global.rfd = rfd;
23592363
global.wfd = wfd;
2364+
// __posix_end
23602365

23612366
do {
23622367
if_err_break(rv, init_term_attrs());
@@ -2836,7 +2841,9 @@ const char *tb_strerror(int err) {
28362841
case TB_ERR_RESIZE_POLL:
28372842
case TB_ERR_RESIZE_READ:
28382843
default:
2844+
// __posix_start
28392845
strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf));
2846+
// __posix_end
28402847
return (const char *)global.errbuf;
28412848
}
28422849
}
@@ -3176,6 +3183,8 @@ static int tb_deinit(void) {
31763183
bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);
31773184
bytebuf_flush(&global.out, global.wfd);
31783185
}
3186+
3187+
// __posix_start
31793188
if (global.ttyfd >= 0) {
31803189
if (global.has_orig_tios) {
31813190
tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios);
@@ -3192,6 +3201,7 @@ static int tb_deinit(void) {
31923201
sigaction(SIGWINCH, &sa, NULL);
31933202
if (global.resize_pipefd[0] >= 0) close(global.resize_pipefd[0]);
31943203
if (global.resize_pipefd[1] >= 0) close(global.resize_pipefd[1]);
3204+
// __posix_end
31953205

31963206
cellbuf_free(&global.back);
31973207
cellbuf_free(&global.front);
@@ -4191,12 +4201,16 @@ static int bytebuf_shift(struct bytebuf *b, size_t n) {
41914201

41924202
static int bytebuf_flush(struct bytebuf *b, int fd) {
41934203
if (b->len <= 0) return TB_OK;
4204+
4205+
// __posix_start
41944206
ssize_t write_rv = write(fd, b->buf, b->len);
41954207
if (write_rv < 0 || (size_t)write_rv != b->len) {
41964208
// Note, errno will be 0 on partial write
41974209
global.last_errno = errno;
41984210
return TB_ERR;
41994211
}
4212+
// __posix_end
4213+
42004214
b->len = 0;
42014215
return TB_OK;
42024216
}

windows.sh

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
main() {
5+
init_files "$@"
6+
alter_headers
7+
alter_structs
8+
alter_errors
9+
alter_functions
10+
}
11+
12+
init_files() {
13+
in=${1:-termbox2.h}
14+
out=${1:-termbox2_win.h}
15+
cp -vf "$in" "$out"
16+
tmp=$(mktemp)
17+
}
18+
19+
alter_headers() {
20+
my_sed '/__posix_header_start/,/__posix_header_end/c\#include <windows.h>\n#include <synchapi.h>\n'
21+
}
22+
23+
alter_structs() {
24+
my_sed 's/^(\s+)int (rfd|wfd|ttyfd);/\1HANDLE \2;/g'
25+
my_sed 's/^(\s+)struct termios orig_tios;/\1DWORD orig_tios[2];/g'
26+
}
27+
28+
alter_errors() {
29+
local err_line
30+
local err_line_num
31+
local err_val
32+
err_line=$(grep -E -n '^#define TB_ERR_.+[0-9]+$' "$out" | tail -n1)
33+
err_line_num=$(cut -d: -f1 <<<"$err_line")
34+
err_val=$(awk '{print $NF}' <<<"$err_line")
35+
my_sed "${err_line_num}a #define TB_ERR_WIN_UNSUPPORTED $((err_val - 5))"
36+
my_sed "${err_line_num}a #define TB_ERR_WIN_NO_STDIO $((err_val - 4))"
37+
my_sed "${err_line_num}a #define TB_ERR_WIN_SET_CONMODE $((err_val - 3))"
38+
my_sed "${err_line_num}a #define TB_ERR_WIN_GET_CONMODE $((err_val - 2))"
39+
my_sed "${err_line_num}a #define TB_ERR_WIN_RESIZE $((err_val - 1))"
40+
my_sed '/case TB_ERR:/i \ case TB_ERR_WIN_UNSUPPORTED: return "Unsupporrted (Windows)";'
41+
my_sed '/case TB_ERR:/i \ case TB_ERR_WIN_NO_STDIO: return "Stdio not available (Windows)";'
42+
my_sed '/case TB_ERR:/i \ case TB_ERR_WIN_SET_CONMODE: return "Failed to set console mode (Windows)";'
43+
my_sed '/case TB_ERR:/i \ case TB_ERR_WIN_GET_CONMODE: return "Failed to get console mode (Windows)";'
44+
my_sed '/case TB_ERR:/i \ case TB_ERR_WIN_RESIZE: return "Failed to resize console (Windows)";'
45+
}
46+
47+
alter_functions() {
48+
replace_function 'int tb_get_fds' <<<' return TB_ERR_WIN_UNSUPPORTED;'
49+
replace_function 'static int update_term_size_via_esc' <<<' return TB_ERR;'
50+
replace_function 'static int load_terminfo' <<<' return TB_ERR;'
51+
replace_function 'static void handle_resize' <<<' (void)sig;';
52+
replace_function 'static int init_resize_handler' <<<' return TB_OK;'
53+
54+
my_sed 's/getenv\("TERM"\)/"xterm-256color"/'
55+
56+
alter_function 'const char \*tb_strerror' <<'EOD'
57+
snprintf(global.errbuf, sizeof(global.errbuf), "Unknown error (Windows)");
58+
EOD
59+
60+
replace_function 'int tb_init_file' <<'EOD'
61+
if (global.initialized) return TB_ERR_INIT_ALREADY;
62+
if (strcmp(path, "/dev/tty")) return TB_ERR_WIN_UNSUPPORTED;
63+
return tb_init_fd(-1);
64+
EOD
65+
66+
my_sed 's/^(\s+)global\.(rfd|wfd|ttyfd) = -1;$/\1global.\2 = NULL;/'
67+
alter_function 'int tb_init_rwfd' <<'EOD'
68+
if (rfd != -1 || wfd != -1) return TB_ERR_WIN_UNSUPPORTED;
69+
global.rfd = GetStdHandle(STD_INPUT_HANDLE);
70+
global.wfd = GetStdHandle(STD_OUTPUT_HANDLE);
71+
if (!global.rfd || global.rfd == INVALID_HANDLE_VALUE) return TB_ERR_WIN_NO_STDIO;
72+
if (!global.wfd || global.wfd == INVALID_HANDLE_VALUE) return TB_ERR_WIN_NO_STDIO;
73+
global.ttyfd = global.wfd;
74+
global.ttyfd_open = 1;
75+
EOD
76+
77+
alter_function 'static int tb_deinit' <<'EOD'
78+
if (global.has_orig_tios) {
79+
SetConsoleMode(global.rfd, global.orig_tios[0]);
80+
SetConsoleMode(global.wfd, global.orig_tios[1]);
81+
}
82+
EOD
83+
84+
my_sed 's/int bytebuf_flush\(struct bytebuf \*b, int fd\)/int bytebuf_flush(struct bytebuf *b, HANDLE fd)/'
85+
alter_function 'static int bytebuf_flush' <<'EOD'
86+
DWORD nw;
87+
if (!WriteConsoleA(fd, b->buf, (DWORD)b->len, &nw, NULL) || nw != b->len) {
88+
return TB_ERR; // TODO: GetLastError
89+
}
90+
EOD
91+
92+
replace_function 'static int init_term_attrs' <<'EOD'
93+
if ( !SetConsoleCP(CP_UTF8)
94+
|| !SetConsoleOutputCP(CP_UTF8)) {
95+
return TB_ERR_WIN_SET_CONMODE;
96+
} else if (!GetConsoleMode(global.rfd, &global.orig_tios[0])
97+
|| !GetConsoleMode(global.wfd, &global.orig_tios[1])
98+
|| !(global.has_orig_tios = 1)) {
99+
return TB_ERR_WIN_GET_CONMODE;
100+
} else if (!SetConsoleMode(global.rfd, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT)
101+
|| !SetConsoleMode(global.wfd, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) {
102+
return TB_ERR_WIN_SET_CONMODE;
103+
}
104+
return TB_OK;
105+
EOD
106+
107+
108+
replace_function 'static int update_term_size' <<'EOD'
109+
CONSOLE_SCREEN_BUFFER_INFO info;
110+
if (!GetConsoleScreenBufferInfo(global.wfd, &info)) {
111+
return TB_ERR_WIN_RESIZE;
112+
}
113+
global.width = info.dwSize.X;
114+
global.height = info.dwSize.Y;
115+
return TB_OK;
116+
EOD
117+
118+
replace_function 'static int init_term_caps' <<'EOD'
119+
(void)load_terminfo;
120+
(void)parse_terminfo_caps;
121+
return load_builtin_caps();
122+
EOD
123+
124+
replace_function 'static int wait_event' <<'EOD'
125+
int rv;
126+
INPUT_RECORD buf[TB_OPT_READ_BUF];
127+
128+
memset(event, 0, sizeof(*event));
129+
if_ok_return(rv, extract_event(event));
130+
131+
do {
132+
DWORD wait_rv = WaitForSingleObject(global.rfd, (DWORD)timeout);
133+
if (wait_rv == WAIT_TIMEOUT) {
134+
return TB_ERR_NO_EVENT;
135+
} else if (wait_rv != WAIT_OBJECT_0) {
136+
return TB_ERR_POLL;
137+
}
138+
139+
DWORD nevent = 0;
140+
if (!ReadConsoleInputA(global.rfd, buf, TB_OPT_READ_BUF, &nevent)) {
141+
return TB_ERR_READ;
142+
}
143+
144+
DWORD i;
145+
for (i = 0; i < nevent; i++) {
146+
if (buf[i].EventType == KEY_EVENT) {
147+
if (buf[i].Event.KeyEvent.bKeyDown &&
148+
buf[i].Event.KeyEvent.uChar.AsciiChar != 0)
149+
{
150+
bytebuf_nputs(&global.in,
151+
&(buf[i].Event.KeyEvent.uChar.AsciiChar), 1);
152+
}
153+
}
154+
}
155+
156+
for (i = 0; i < nevent; i++) {
157+
if (buf[i].EventType == WINDOW_BUFFER_SIZE_EVENT) {
158+
if_err_return(rv, update_term_size());
159+
if_err_return(rv, resize_cellbufs());
160+
event->type = TB_EVENT_RESIZE;
161+
event->w = global.width;
162+
event->h = global.height;
163+
return TB_OK;
164+
}
165+
}
166+
167+
memset(event, 0, sizeof(*event));
168+
if_ok_return(rv, extract_event(event));
169+
} while (timeout == -1);
170+
171+
return rv;
172+
EOD
173+
}
174+
175+
alter_function() {
176+
local fsig=$1
177+
local fcode=$(cat)
178+
awk -va=0 -vb=0 -vfcode="$fcode" \
179+
"/^${fsig}\(.* {$/ { a=1; print; next }
180+
a==1 && /__posix_start/ { b=1 }
181+
b==0 { print }
182+
b==1 && /__posix_end/ { print fcode; b=0; a=0 }" \
183+
"$out" >"$tmp"
184+
mv -f "$tmp" "$out"
185+
echo "alter_function $fsig"
186+
}
187+
188+
replace_function() {
189+
local fsig=$1
190+
local fbody=$(cat)
191+
awk -va=0 -vfbody="$fbody" \
192+
"/^${fsig}\(.* {$/ { a=1; print; next }
193+
a==0 { print }
194+
a==1 && /^}$/ { print fbody; print; a=0 }" \
195+
"$out" >"$tmp"
196+
mv -f "$tmp" "$out"
197+
echo "replace_function $fsig"
198+
}
199+
200+
my_sed() {
201+
sed -i -E "$@" "$out"
202+
echo "sed ${1:-}"
203+
}
204+
205+
main "$@"

0 commit comments

Comments
 (0)