💾 Archived View for gemini.rmf-dev.com › repo › Vaati › tuimarket › files › df920043b6584aedc60fc6db… captured on 2023-03-20 at 18:37:32. Gemini links have been rewritten to link to archived content

View Raw

More Information

➡️ Next capture (2023-09-08)

-=-=-=-=-=-=-

0 /*

1 MIT License

2

3 Copyright (c) 2010-2020 nsf <no.smile.face@gmail.com>

4 2015-2022 Adam Saponara <as@php.net>

5

6 Permission is hereby granted, free of charge, to any person obtaining a copy

7 of this software and associated documentation files (the "Software"), to deal

8 in the Software without restriction, including without limitation the rights

9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

10 copies of the Software, and to permit persons to whom the Software is

11 furnished to do so, subject to the following conditions:

12

13 The above copyright notice and this permission notice shall be included in all

14 copies or substantial portions of the Software.

15

16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

22 SOFTWARE.

23 */

24 /* this file was edited to respect the C89 standard */

25

26 #ifndef __linux__

27 #undef _POSIX_C_SOURCE

28 #include <signal.h>

29 #endif

30

31 #ifdef sun

32 #include <stropts.h>

33 #endif

34

35 #ifndef _XOPEN_SOURCE

36 #define _XOPEN_SOURCE

37 #endif

38

39 #ifndef _DEFAULT_SOURCE

40 #define _DEFAULT_SOURCE

41 #endif

42

43 #include <errno.h>

44 #include <fcntl.h>

45 #include <limits.h>

46 #ifndef __OpenBSD__

47 #include <signal.h>

48 #endif

49 #include <stdarg.h>

50 #include <stdint.h>

51 #include <stdio.h>

52 #include <stdlib.h>

53 #include <string.h>

54 #include <sys/ioctl.h>

55 #include <sys/select.h>

56 #include <sys/stat.h>

57 #include <sys/time.h>

58 #include <sys/types.h>

59 #include <termios.h>

60 #include <unistd.h>

61 #include <wchar.h>

62 #include "termbox.h"

63

64 #ifndef IMAXBEL

65 #define IMAXBEL 0

66 #endif

67

68 void cfmakeraw(struct termios *t) {

69 t->c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|

70 ISTRIP|INLCR|IGNCR|ICRNL|IXON);

71 t->c_oflag &= ~OPOST;

72 t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);

73 t->c_cflag &= ~(CSIZE|PARENB);

74 t->c_cflag |= CS8;

75 }

76

77 #define index_fail ((size_t)-1)

78

79 #define if_err_return(rv, expr) \

80 if (((rv) = (expr)) != TB_OK) \

81 return (rv)

82 #define if_err_break(rv, expr) \

83 if (((rv) = (expr)) != TB_OK) \

84 break

85 #define if_ok_return(rv, expr) \

86 if (((rv) = (expr)) == TB_OK) \

87 return (rv)

88 #define if_ok_or_need_more_return(rv, expr) \

89 if (((rv) = (expr)) == TB_OK || (rv) == TB_ERR_NEED_MORE) \

90 return (rv)

91

92 #define send_literal(rv, a) \

93 if_err_return((rv), bytebuf_nputs(&global.out, (a), sizeof(a) - 1))

94

95 #define send_num(rv, nbuf, n) \

96 if_err_return((rv), bytebuf_nputs(&global.out, (nbuf), \

97 convert_num((n), (nbuf))))

98

99

100 int snprintf_(char* str, size_t sz, const char* fmt, ...) {

101 va_list args;

102 int rv;

103

104 va_start(args, fmt);

105 rv = vsnprintf(str, sz, fmt, args);

106 va_end(args);

107

108 if ((rv) < 0 || (rv) >= (int)(sz))

109 return TB_ERR;

110 return TB_OK;

111 }

112

113 #define if_not_init_return() if (!global.initialized) return TB_ERR_NOT_INIT

114

115 struct bytebuf_t {

116 char *buf;

117 size_t len;

118 size_t cap;

119 };

120

121 struct cellbuf_t {

122 int width;

123 int height;

124 struct tb_cell *cells;

125 };

126

127 struct cap_trie_t {

128 char c;

129 struct cap_trie_t *children;

130 size_t nchildren;

131 int is_leaf;

132 uint16_t key;

133 uint8_t mod;

134 };

135

136 struct tb_global_t {

137 int ttyfd;

138 int rfd;

139 int wfd;

140 int ttyfd_open;

141 int resize_pipefd[2];

142 int width;

143 int height;

144 int cursor_x;

145 int cursor_y;

146 int last_x;

147 int last_y;

148 uintattr_t fg;

149 uintattr_t bg;

150 uintattr_t last_fg;

151 uintattr_t last_bg;

152 int input_mode;

153 int output_mode;

154 char *terminfo;

155 size_t nterminfo;

156 const char *caps[TB_CAP__COUNT];

157 struct cap_trie_t cap_trie;

158 struct bytebuf_t in;

159 struct bytebuf_t out;

160 struct cellbuf_t back;

161 struct cellbuf_t front;

162 struct termios orig_tios;

163 int has_orig_tios;

164 int last_errno;

165 int initialized;

166 int (*fn_extract_esc_pre)(struct tb_event *, size_t *);

167 int (*fn_extract_esc_post)(struct tb_event *, size_t *);

168 char errbuf[1024];

169 };

170

171 static struct tb_global_t global = {0};

172

173 /* BEGIN codegen c */

174 /* Produced by ./codegen.sh on Sun, 19 Sep 2021 01:02:03 +0000 */

175

176 static const int16_t terminfo_cap_indexes[] = {

177 66, /* kf1 (TB_CAP_F1) */

178 68, /* kf2 (TB_CAP_F2) */

179 69, /* kf3 (TB_CAP_F3) */

180 70, /* kf4 (TB_CAP_F4) */

181 71, /* kf5 (TB_CAP_F5) */

182 72, /* kf6 (TB_CAP_F6) */

183 73, /* kf7 (TB_CAP_F7) */

184 74, /* kf8 (TB_CAP_F8) */

185 75, /* kf9 (TB_CAP_F9) */

186 67, /* kf10 (TB_CAP_F10) */

187 216, /* kf11 (TB_CAP_F11) */

188 217, /* kf12 (TB_CAP_F12) */

189 77, /* kich1 (TB_CAP_INSERT) */

190 59, /* kdch1 (TB_CAP_DELETE) */

191 76, /* khome (TB_CAP_HOME) */

192 164, /* kend (TB_CAP_END) */

193 82, /* kpp (TB_CAP_PGUP) */

194 81, /* knp (TB_CAP_PGDN) */

195 87, /* kcuu1 (TB_CAP_ARROW_UP) */

196 61, /* kcud1 (TB_CAP_ARROW_DOWN) */

197 79, /* kcub1 (TB_CAP_ARROW_LEFT) */

198 83, /* kcuf1 (TB_CAP_ARROW_RIGHT) */

199 148, /* kcbt (TB_CAP_BACK_TAB) */

200 28, /* smcup (TB_CAP_ENTER_CA) */

201 40, /* rmcup (TB_CAP_EXIT_CA) */

202 16, /* cnorm (TB_CAP_SHOW_CURSOR) */

203 13, /* civis (TB_CAP_HIDE_CURSOR) */

204 5, /* clear (TB_CAP_CLEAR_SCREEN) */

205 39, /* sgr0 (TB_CAP_SGR0) */

206 36, /* smul (TB_CAP_UNDERLINE) */

207 27, /* bold (TB_CAP_BOLD) */

208 26, /* blink (TB_CAP_BLINK) */

209 311, /* sitm (TB_CAP_ITALIC) */

210 34, /* rev (TB_CAP_REVERSE) */

211 89, /* smkx (TB_CAP_ENTER_KEYPAD) */

212 88, /* rmkx (TB_CAP_EXIT_KEYPAD) */

213 };

214

215 /* xterm */

216 static const char *xterm_caps[] = {

217 "\033OP", /* kf1 (TB_CAP_F1) */

218 "\033OQ", /* kf2 (TB_CAP_F2) */

219 "\033OR", /* kf3 (TB_CAP_F3) */

220 "\033OS", /* kf4 (TB_CAP_F4) */

221 "\033[15~", /* kf5 (TB_CAP_F5) */

222 "\033[17~", /* kf6 (TB_CAP_F6) */

223 "\033[18~", /* kf7 (TB_CAP_F7) */

224 "\033[19~", /* kf8 (TB_CAP_F8) */

225 "\033[20~", /* kf9 (TB_CAP_F9) */

226 "\033[21~", /* kf10 (TB_CAP_F10) */

227 "\033[23~", /* kf11 (TB_CAP_F11) */

228 "\033[24~", /* kf12 (TB_CAP_F12) */

229 "\033[2~", /* kich1 (TB_CAP_INSERT) */

230 "\033[3~", /* kdch1 (TB_CAP_DELETE) */

231 "\033OH", /* khome (TB_CAP_HOME) */

232 "\033OF", /* kend (TB_CAP_END) */

233 "\033[5~", /* kpp (TB_CAP_PGUP) */

234 "\033[6~", /* knp (TB_CAP_PGDN) */

235 "\033OA", /* kcuu1 (TB_CAP_ARROW_UP) */

236 "\033OB", /* kcud1 (TB_CAP_ARROW_DOWN) */

237 "\033OD", /* kcub1 (TB_CAP_ARROW_LEFT) */

238 "\033OC", /* kcuf1 (TB_CAP_ARROW_RIGHT) */

239 "\033[Z", /* kcbt (TB_CAP_BACK_TAB) */

240 "\033[?1049h\033[22;0;0t", /* smcup (TB_CAP_ENTER_CA) */

241 "\033[?1049l\033[23;0;0t", /* rmcup (TB_CAP_EXIT_CA) */

242 "\033[?12l\033[?25h", /* cnorm (TB_CAP_SHOW_CURSOR) */

243 "\033[?25l", /* civis (TB_CAP_HIDE_CURSOR) */

244 "\033[H\033[2J", /* clear (TB_CAP_CLEAR_SCREEN) */

245 "\033(B\033[m", /* sgr0 (TB_CAP_SGR0) */

246 "\033[4m", /* smul (TB_CAP_UNDERLINE) */

247 "\033[1m", /* bold (TB_CAP_BOLD) */

248 "\033[5m", /* blink (TB_CAP_BLINK) */

249 "\033[3m", /* sitm (TB_CAP_ITALIC) */

250 "\033[7m", /* rev (TB_CAP_REVERSE) */

251 "\033[?1h\033=", /* smkx (TB_CAP_ENTER_KEYPAD) */

252 "\033[?1l\033>", /* rmkx (TB_CAP_EXIT_KEYPAD) */

253 };

254

255 /* linux */

256 static const char *linux_caps[] = {

257 "\033[[A", /* kf1 (TB_CAP_F1) */

258 "\033[[B", /* kf2 (TB_CAP_F2) */

259 "\033[[C", /* kf3 (TB_CAP_F3) */

260 "\033[[D", /* kf4 (TB_CAP_F4) */

261 "\033[[E", /* kf5 (TB_CAP_F5) */

262 "\033[17~", /* kf6 (TB_CAP_F6) */

263 "\033[18~", /* kf7 (TB_CAP_F7) */

264 "\033[19~", /* kf8 (TB_CAP_F8) */

265 "\033[20~", /* kf9 (TB_CAP_F9) */

266 "\033[21~", /* kf10 (TB_CAP_F10) */

267 "\033[23~", /* kf11 (TB_CAP_F11) */

268 "\033[24~", /* kf12 (TB_CAP_F12) */

269 "\033[2~", /* kich1 (TB_CAP_INSERT) */

270 "\033[3~", /* kdch1 (TB_CAP_DELETE) */

271 "\033[1~", /* khome (TB_CAP_HOME) */

272 "\033[4~", /* kend (TB_CAP_END) */

273 "\033[5~", /* kpp (TB_CAP_PGUP) */

274 "\033[6~", /* knp (TB_CAP_PGDN) */

275 "\033[A", /* kcuu1 (TB_CAP_ARROW_UP) */

276 "\033[B", /* kcud1 (TB_CAP_ARROW_DOWN) */

277 "\033[D", /* kcub1 (TB_CAP_ARROW_LEFT) */

278 "\033[C", /* kcuf1 (TB_CAP_ARROW_RIGHT) */

279 "\033[Z", /* kcbt (TB_CAP_BACK_TAB) */

280 "", /* smcup (TB_CAP_ENTER_CA) */

281 "", /* rmcup (TB_CAP_EXIT_CA) */

282 "\033[?25h\033[?0c", /* cnorm (TB_CAP_SHOW_CURSOR) */

283 "\033[?25l\033[?1c", /* civis (TB_CAP_HIDE_CURSOR) */

284 "\033[H\033[J", /* clear (TB_CAP_CLEAR_SCREEN) */

285 "\033[m\017", /* sgr0 (TB_CAP_SGR0) */

286 "\033[4m", /* smul (TB_CAP_UNDERLINE) */

287 "\033[1m", /* bold (TB_CAP_BOLD) */

288 "\033[5m", /* blink (TB_CAP_BLINK) */

289 "", /* sitm (TB_CAP_ITALIC) */

290 "\033[7m", /* rev (TB_CAP_REVERSE) */

291 "", /* smkx (TB_CAP_ENTER_KEYPAD) */

292 "", /* rmkx (TB_CAP_EXIT_KEYPAD) */

293 };

294

295 /* screen */

296 static const char *screen_caps[] = {

297 "\033OP", /* kf1 (TB_CAP_F1) */

298 "\033OQ", /* kf2 (TB_CAP_F2) */

299 "\033OR", /* kf3 (TB_CAP_F3) */

300 "\033OS", /* kf4 (TB_CAP_F4) */

301 "\033[15~", /* kf5 (TB_CAP_F5) */

302 "\033[17~", /* kf6 (TB_CAP_F6) */

303 "\033[18~", /* kf7 (TB_CAP_F7) */

304 "\033[19~", /* kf8 (TB_CAP_F8) */

305 "\033[20~", /* kf9 (TB_CAP_F9) */

306 "\033[21~", /* kf10 (TB_CAP_F10) */

307 "\033[23~", /* kf11 (TB_CAP_F11) */

308 "\033[24~", /* kf12 (TB_CAP_F12) */

309 "\033[2~", /* kich1 (TB_CAP_INSERT) */

310 "\033[3~", /* kdch1 (TB_CAP_DELETE) */

311 "\033[1~", /* khome (TB_CAP_HOME) */

312 "\033[4~", /* kend (TB_CAP_END) */

313 "\033[5~", /* kpp (TB_CAP_PGUP) */

314 "\033[6~", /* knp (TB_CAP_PGDN) */

315 "\033OA", /* kcuu1 (TB_CAP_ARROW_UP) */

316 "\033OB", /* kcud1 (TB_CAP_ARROW_DOWN) */

317 "\033OD", /* kcub1 (TB_CAP_ARROW_LEFT) */

318 "\033OC", /* kcuf1 (TB_CAP_ARROW_RIGHT) */

319 "\033[Z", /* kcbt (TB_CAP_BACK_TAB) */

320 "\033[?1049h", /* smcup (TB_CAP_ENTER_CA) */

321 "\033[?1049l", /* rmcup (TB_CAP_EXIT_CA) */

322 "\033[34h\033[?25h", /* cnorm (TB_CAP_SHOW_CURSOR) */

323 "\033[?25l", /* civis (TB_CAP_HIDE_CURSOR) */

324 "\033[H\033[J", /* clear (TB_CAP_CLEAR_SCREEN) */

325 "\033[m\017", /* sgr0 (TB_CAP_SGR0) */

326 "\033[4m", /* smul (TB_CAP_UNDERLINE) */

327 "\033[1m", /* bold (TB_CAP_BOLD) */

328 "\033[5m", /* blink (TB_CAP_BLINK) */

329 "", /* sitm (TB_CAP_ITALIC) */

330 "\033[7m", /* rev (TB_CAP_REVERSE) */

331 "\033[?1h\033=", /* smkx (TB_CAP_ENTER_KEYPAD) */

332 "\033[?1l\033>", /* rmkx (TB_CAP_EXIT_KEYPAD) */

333 };

334

335 /* rxvt-256color */

336 static const char *rxvt_256color_caps[] = {

337 "\033[11~", /* kf1 (TB_CAP_F1) */

338 "\033[12~", /* kf2 (TB_CAP_F2) */

339 "\033[13~", /* kf3 (TB_CAP_F3) */

340 "\033[14~", /* kf4 (TB_CAP_F4) */

341 "\033[15~", /* kf5 (TB_CAP_F5) */

342 "\033[17~", /* kf6 (TB_CAP_F6) */

343 "\033[18~", /* kf7 (TB_CAP_F7) */

344 "\033[19~", /* kf8 (TB_CAP_F8) */

345 "\033[20~", /* kf9 (TB_CAP_F9) */

346 "\033[21~", /* kf10 (TB_CAP_F10) */

347 "\033[23~", /* kf11 (TB_CAP_F11) */

348 "\033[24~", /* kf12 (TB_CAP_F12) */

349 "\033[2~", /* kich1 (TB_CAP_INSERT) */

350 "\033[3~", /* kdch1 (TB_CAP_DELETE) */

351 "\033[7~", /* khome (TB_CAP_HOME) */

352 "\033[8~", /* kend (TB_CAP_END) */

353 "\033[5~", /* kpp (TB_CAP_PGUP) */

354 "\033[6~", /* knp (TB_CAP_PGDN) */

355 "\033[A", /* kcuu1 (TB_CAP_ARROW_UP) */

356 "\033[B", /* kcud1 (TB_CAP_ARROW_DOWN) */

357 "\033[D", /* kcub1 (TB_CAP_ARROW_LEFT) */

358 "\033[C", /* kcuf1 (TB_CAP_ARROW_RIGHT) */

359 "\033[Z", /* kcbt (TB_CAP_BACK_TAB) */

360 "\0337\033[?47h", /* smcup (TB_CAP_ENTER_CA) */

361 "\033[2J\033[?47l\0338", /* rmcup (TB_CAP_EXIT_CA) */

362 "\033[?25h", /* cnorm (TB_CAP_SHOW_CURSOR) */

363 "\033[?25l", /* civis (TB_CAP_HIDE_CURSOR) */

364 "\033[H\033[2J", /* clear (TB_CAP_CLEAR_SCREEN) */

365 "\033[m\017", /* sgr0 (TB_CAP_SGR0) */

366 "\033[4m", /* smul (TB_CAP_UNDERLINE) */

367 "\033[1m", /* bold (TB_CAP_BOLD) */

368 "\033[5m", /* blink (TB_CAP_BLINK) */

369 "", /* sitm (TB_CAP_ITALIC) */

370 "\033[7m", /* rev (TB_CAP_REVERSE) */

371 "\033=", /* smkx (TB_CAP_ENTER_KEYPAD) */

372 "\033>", /* rmkx (TB_CAP_EXIT_KEYPAD) */

373 };

374

375 /* rxvt-unicode */

376 static const char *rxvt_unicode_caps[] = {

377 "\033[11~", /* kf1 (TB_CAP_F1) */

378 "\033[12~", /* kf2 (TB_CAP_F2) */

379 "\033[13~", /* kf3 (TB_CAP_F3) */

380 "\033[14~", /* kf4 (TB_CAP_F4) */

381 "\033[15~", /* kf5 (TB_CAP_F5) */

382 "\033[17~", /* kf6 (TB_CAP_F6) */

383 "\033[18~", /* kf7 (TB_CAP_F7) */

384 "\033[19~", /* kf8 (TB_CAP_F8) */

385 "\033[20~", /* kf9 (TB_CAP_F9) */

386 "\033[21~", /* kf10 (TB_CAP_F10) */

387 "\033[23~", /* kf11 (TB_CAP_F11) */

388 "\033[24~", /* kf12 (TB_CAP_F12) */

389 "\033[2~", /* kich1 (TB_CAP_INSERT) */

390 "\033[3~", /* kdch1 (TB_CAP_DELETE) */

391 "\033[7~", /* khome (TB_CAP_HOME) */

392 "\033[8~", /* kend (TB_CAP_END) */

393 "\033[5~", /* kpp (TB_CAP_PGUP) */

394 "\033[6~", /* knp (TB_CAP_PGDN) */

395 "\033[A", /* kcuu1 (TB_CAP_ARROW_UP) */

396 "\033[B", /* kcud1 (TB_CAP_ARROW_DOWN) */

397 "\033[D", /* kcub1 (TB_CAP_ARROW_LEFT) */

398 "\033[C", /* kcuf1 (TB_CAP_ARROW_RIGHT) */

399 "\033[Z", /* kcbt (TB_CAP_BACK_TAB) */

400 "\033[?1049h", /* smcup (TB_CAP_ENTER_CA) */

401 "\033[r\033[?1049l", /* rmcup (TB_CAP_EXIT_CA) */

402 "\033[?12l\033[?25h", /* cnorm (TB_CAP_SHOW_CURSOR) */

403 "\033[?25l", /* civis (TB_CAP_HIDE_CURSOR) */

404 "\033[H\033[2J", /* clear (TB_CAP_CLEAR_SCREEN) */

405 "\033[m\033(B", /* sgr0 (TB_CAP_SGR0) */

406 "\033[4m", /* smul (TB_CAP_UNDERLINE) */

407 "\033[1m", /* bold (TB_CAP_BOLD) */

408 "\033[5m", /* blink (TB_CAP_BLINK) */

409 "\033[3m", /* sitm (TB_CAP_ITALIC) */

410 "\033[7m", /* rev (TB_CAP_REVERSE) */

411 "\033=", /* smkx (TB_CAP_ENTER_KEYPAD) */

412 "\033>", /* rmkx (TB_CAP_EXIT_KEYPAD) */

413 };

414

415 /* Eterm */

416 static const char *eterm_caps[] = {

417 "\033[11~", /* kf1 (TB_CAP_F1) */

418 "\033[12~", /* kf2 (TB_CAP_F2) */

419 "\033[13~", /* kf3 (TB_CAP_F3) */

420 "\033[14~", /* kf4 (TB_CAP_F4) */

421 "\033[15~", /* kf5 (TB_CAP_F5) */

422 "\033[17~", /* kf6 (TB_CAP_F6) */

423 "\033[18~", /* kf7 (TB_CAP_F7) */

424 "\033[19~", /* kf8 (TB_CAP_F8) */

425 "\033[20~", /* kf9 (TB_CAP_F9) */

426 "\033[21~", /* kf10 (TB_CAP_F10) */

427 "\033[23~", /* kf11 (TB_CAP_F11) */

428 "\033[24~", /* kf12 (TB_CAP_F12) */

429 "\033[2~", /* kich1 (TB_CAP_INSERT) */

430 "\033[3~", /* kdch1 (TB_CAP_DELETE) */

431 "\033[7~", /* khome (TB_CAP_HOME) */

432 "\033[8~", /* kend (TB_CAP_END) */

433 "\033[5~", /* kpp (TB_CAP_PGUP) */

434 "\033[6~", /* knp (TB_CAP_PGDN) */

435 "\033[A", /* kcuu1 (TB_CAP_ARROW_UP) */

436 "\033[B", /* kcud1 (TB_CAP_ARROW_DOWN) */

437 "\033[D", /* kcub1 (TB_CAP_ARROW_LEFT) */

438 "\033[C", /* kcuf1 (TB_CAP_ARROW_RIGHT) */

439 "", /* kcbt (TB_CAP_BACK_TAB) */

440 "\0337\033[?47h", /* smcup (TB_CAP_ENTER_CA) */

441 "\033[2J\033[?47l\0338", /* rmcup (TB_CAP_EXIT_CA) */

442 "\033[?25h", /* cnorm (TB_CAP_SHOW_CURSOR) */

443 "\033[?25l", /* civis (TB_CAP_HIDE_CURSOR) */

444 "\033[H\033[2J", /* clear (TB_CAP_CLEAR_SCREEN) */

445 "\033[m\017", /* sgr0 (TB_CAP_SGR0) */

446 "\033[4m", /* smul (TB_CAP_UNDERLINE) */

447 "\033[1m", /* bold (TB_CAP_BOLD) */

448 "\033[5m", /* blink (TB_CAP_BLINK) */

449 "", /* sitm (TB_CAP_ITALIC) */

450 "\033[7m", /* rev (TB_CAP_REVERSE) */

451 "", /* smkx (TB_CAP_ENTER_KEYPAD) */

452 "", /* rmkx (TB_CAP_EXIT_KEYPAD) */

453 };

454

455 static struct {

456 const char *name;

457 const char **caps;

458 const char *alias;

459 } builtin_terms[] = {

460 {"xterm", xterm_caps, "" },

461 {"linux", linux_caps, "" },

462 {"screen", screen_caps, "tmux"},

463 {"rxvt-256color", rxvt_256color_caps, "" },

464 {"rxvt-unicode", rxvt_unicode_caps, "rxvt"},

465 {"Eterm", eterm_caps, "" },

466 {NULL, NULL, NULL },

467 };

468

469 /* END codegen c */

470 #define TB_MOD_ALL TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT

471

472 static struct {

473 const char *cap;

474 const uint16_t key;

475 const uint8_t mod;

476 } builtin_mod_caps[] = {

477 /* xterm arrows */

478 {"\x1b[1;2A", TB_KEY_ARROW_UP, TB_MOD_SHIFT },

479 {"\x1b[1;3A", TB_KEY_ARROW_UP, TB_MOD_ALT },

480 {"\x1b[1;4A", TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT },

481 {"\x1b[1;5A", TB_KEY_ARROW_UP, TB_MOD_CTRL },

482 {"\x1b[1;6A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_SHIFT },

483 {"\x1b[1;7A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT },

484 {"\x1b[1;8A", TB_KEY_ARROW_UP, TB_MOD_ALL },

485

486 {"\x1b[1;2B", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT },

487 {"\x1b[1;3B", TB_KEY_ARROW_DOWN, TB_MOD_ALT },

488 {"\x1b[1;4B", TB_KEY_ARROW_DOWN, TB_MOD_ALT | TB_MOD_SHIFT },

489 {"\x1b[1;5B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL },

490 {"\x1b[1;6B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_SHIFT },

491 {"\x1b[1;7B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT },

492 {"\x1b[1;8B", TB_KEY_ARROW_DOWN, TB_MOD_ALL },

493

494 {"\x1b[1;2C", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT },

495 {"\x1b[1;3C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT },

496 {"\x1b[1;4C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT },

497 {"\x1b[1;5C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL },

498 {"\x1b[1;6C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_SHIFT },

499 {"\x1b[1;7C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT },

500 {"\x1b[1;8C", TB_KEY_ARROW_RIGHT, TB_MOD_ALL },

501

502 {"\x1b[1;2D", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT },

503 {"\x1b[1;3D", TB_KEY_ARROW_LEFT, TB_MOD_ALT },

504 {"\x1b[1;4D", TB_KEY_ARROW_LEFT, TB_MOD_ALT | TB_MOD_SHIFT },

505 {"\x1b[1;5D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL },

506 {"\x1b[1;6D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_SHIFT },

507 {"\x1b[1;7D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT },

508 {"\x1b[1;8D", TB_KEY_ARROW_LEFT, TB_MOD_ALL },

509

510 /* xterm keys */

511 {"\x1b[1;2H", TB_KEY_HOME, TB_MOD_SHIFT },

512 {"\x1b[1;3H", TB_KEY_HOME, TB_MOD_ALT },

513 {"\x1b[1;4H", TB_KEY_HOME, TB_MOD_ALT | TB_MOD_SHIFT },

514 {"\x1b[1;5H", TB_KEY_HOME, TB_MOD_CTRL },

515 {"\x1b[1;6H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_SHIFT },

516 {"\x1b[1;7H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT },

517 {"\x1b[1;8H", TB_KEY_HOME, TB_MOD_ALL },

518

519 {"\x1b[1;2F", TB_KEY_END, TB_MOD_SHIFT },

520 {"\x1b[1;3F", TB_KEY_END, TB_MOD_ALT },

521 {"\x1b[1;4F", TB_KEY_END, TB_MOD_ALT | TB_MOD_SHIFT },

522 {"\x1b[1;5F", TB_KEY_END, TB_MOD_CTRL },

523 {"\x1b[1;6F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_SHIFT },

524 {"\x1b[1;7F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT },

525 {"\x1b[1;8F", TB_KEY_END, TB_MOD_ALL },

526

527 {"\x1b[2;2~", TB_KEY_INSERT, TB_MOD_SHIFT },

528 {"\x1b[2;3~", TB_KEY_INSERT, TB_MOD_ALT },

529 {"\x1b[2;4~", TB_KEY_INSERT, TB_MOD_ALT | TB_MOD_SHIFT },

530 {"\x1b[2;5~", TB_KEY_INSERT, TB_MOD_CTRL },

531 {"\x1b[2;6~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_SHIFT },

532 {"\x1b[2;7~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT },

533 {"\x1b[2;8~", TB_KEY_INSERT, TB_MOD_ALL },

534

535 {"\x1b[3;2~", TB_KEY_DELETE, TB_MOD_SHIFT },

536 {"\x1b[3;3~", TB_KEY_DELETE, TB_MOD_ALT },

537 {"\x1b[3;4~", TB_KEY_DELETE, TB_MOD_ALT | TB_MOD_SHIFT },

538 {"\x1b[3;5~", TB_KEY_DELETE, TB_MOD_CTRL },

539 {"\x1b[3;6~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_SHIFT },

540 {"\x1b[3;7~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT },

541 {"\x1b[3;8~", TB_KEY_DELETE, TB_MOD_ALL },

542

543 {"\x1b[5;2~", TB_KEY_PGUP, TB_MOD_SHIFT },

544 {"\x1b[5;3~", TB_KEY_PGUP, TB_MOD_ALT },

545 {"\x1b[5;4~", TB_KEY_PGUP, TB_MOD_ALT | TB_MOD_SHIFT },

546 {"\x1b[5;5~", TB_KEY_PGUP, TB_MOD_CTRL },

547 {"\x1b[5;6~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_SHIFT },

548 {"\x1b[5;7~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT },

549 {"\x1b[5;8~", TB_KEY_PGUP, TB_MOD_ALL },

550

551 {"\x1b[6;2~", TB_KEY_PGDN, TB_MOD_SHIFT },

552 {"\x1b[6;3~", TB_KEY_PGDN, TB_MOD_ALT },

553 {"\x1b[6;4~", TB_KEY_PGDN, TB_MOD_ALT | TB_MOD_SHIFT },

554 {"\x1b[6;5~", TB_KEY_PGDN, TB_MOD_CTRL },

555 {"\x1b[6;6~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_SHIFT },

556 {"\x1b[6;7~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT },

557 {"\x1b[6;8~", TB_KEY_PGDN, TB_MOD_ALL },

558

559 {"\x1b[1;2P", TB_KEY_F1, TB_MOD_SHIFT },

560 {"\x1b[1;3P", TB_KEY_F1, TB_MOD_ALT },

561 {"\x1b[1;4P", TB_KEY_F1, TB_MOD_ALT | TB_MOD_SHIFT },

562 {"\x1b[1;5P", TB_KEY_F1, TB_MOD_CTRL },

563 {"\x1b[1;6P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_SHIFT },

564 {"\x1b[1;7P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT },

565 {"\x1b[1;8P", TB_KEY_F1, TB_MOD_ALL },

566

567 {"\x1b[1;2Q", TB_KEY_F2, TB_MOD_SHIFT },

568 {"\x1b[1;3Q", TB_KEY_F2, TB_MOD_ALT },

569 {"\x1b[1;4Q", TB_KEY_F2, TB_MOD_ALT | TB_MOD_SHIFT },

570 {"\x1b[1;5Q", TB_KEY_F2, TB_MOD_CTRL },

571 {"\x1b[1;6Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_SHIFT },

572 {"\x1b[1;7Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT },

573 {"\x1b[1;8Q", TB_KEY_F2, TB_MOD_ALL },

574

575 {"\x1b[1;2R", TB_KEY_F3, TB_MOD_SHIFT },

576 {"\x1b[1;3R", TB_KEY_F3, TB_MOD_ALT },

577 {"\x1b[1;4R", TB_KEY_F3, TB_MOD_ALT | TB_MOD_SHIFT },

578 {"\x1b[1;5R", TB_KEY_F3, TB_MOD_CTRL },

579 {"\x1b[1;6R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_SHIFT },

580 {"\x1b[1;7R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT },

581 {"\x1b[1;8R", TB_KEY_F3, TB_MOD_ALL },

582

583 {"\x1b[1;2S", TB_KEY_F4, TB_MOD_SHIFT },

584 {"\x1b[1;3S", TB_KEY_F4, TB_MOD_ALT },

585 {"\x1b[1;4S", TB_KEY_F4, TB_MOD_ALT | TB_MOD_SHIFT },

586 {"\x1b[1;5S", TB_KEY_F4, TB_MOD_CTRL },

587 {"\x1b[1;6S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_SHIFT },

588 {"\x1b[1;7S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT },

589 {"\x1b[1;8S", TB_KEY_F4, TB_MOD_ALL },

590

591 {"\x1b[15;2~", TB_KEY_F5, TB_MOD_SHIFT },

592 {"\x1b[15;3~", TB_KEY_F5, TB_MOD_ALT },

593 {"\x1b[15;4~", TB_KEY_F5, TB_MOD_ALT | TB_MOD_SHIFT },

594 {"\x1b[15;5~", TB_KEY_F5, TB_MOD_CTRL },

595 {"\x1b[15;6~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_SHIFT },

596 {"\x1b[15;7~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT },

597 {"\x1b[15;8~", TB_KEY_F5, TB_MOD_ALL },

598

599 {"\x1b[17;2~", TB_KEY_F6, TB_MOD_SHIFT },

600 {"\x1b[17;3~", TB_KEY_F6, TB_MOD_ALT },

601 {"\x1b[17;4~", TB_KEY_F6, TB_MOD_ALT | TB_MOD_SHIFT },

602 {"\x1b[17;5~", TB_KEY_F6, TB_MOD_CTRL },

603 {"\x1b[17;6~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_SHIFT },

604 {"\x1b[17;7~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT },

605 {"\x1b[17;8~", TB_KEY_F6, TB_MOD_ALL },

606

607 {"\x1b[18;2~", TB_KEY_F7, TB_MOD_SHIFT },

608 {"\x1b[18;3~", TB_KEY_F7, TB_MOD_ALT },

609 {"\x1b[18;4~", TB_KEY_F7, TB_MOD_ALT | TB_MOD_SHIFT },

610 {"\x1b[18;5~", TB_KEY_F7, TB_MOD_CTRL },

611 {"\x1b[18;6~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_SHIFT },

612 {"\x1b[18;7~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT },

613 {"\x1b[18;8~", TB_KEY_F7, TB_MOD_ALL },

614

615 {"\x1b[19;2~", TB_KEY_F8, TB_MOD_SHIFT },

616 {"\x1b[19;3~", TB_KEY_F8, TB_MOD_ALT },

617 {"\x1b[19;4~", TB_KEY_F8, TB_MOD_ALT | TB_MOD_SHIFT },

618 {"\x1b[19;5~", TB_KEY_F8, TB_MOD_CTRL },

619 {"\x1b[19;6~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_SHIFT },

620 {"\x1b[19;7~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT },

621 {"\x1b[19;8~", TB_KEY_F8, TB_MOD_ALL },

622

623 {"\x1b[20;2~", TB_KEY_F9, TB_MOD_SHIFT },

624 {"\x1b[20;3~", TB_KEY_F9, TB_MOD_ALT },

625 {"\x1b[20;4~", TB_KEY_F9, TB_MOD_ALT | TB_MOD_SHIFT },

626 {"\x1b[20;5~", TB_KEY_F9, TB_MOD_CTRL },

627 {"\x1b[20;6~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_SHIFT },

628 {"\x1b[20;7~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT },

629 {"\x1b[20;8~", TB_KEY_F9, TB_MOD_ALL },

630

631 {"\x1b[21;2~", TB_KEY_F10, TB_MOD_SHIFT },

632 {"\x1b[21;3~", TB_KEY_F10, TB_MOD_ALT },

633 {"\x1b[21;4~", TB_KEY_F10, TB_MOD_ALT | TB_MOD_SHIFT },

634 {"\x1b[21;5~", TB_KEY_F10, TB_MOD_CTRL },

635 {"\x1b[21;6~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_SHIFT },

636 {"\x1b[21;7~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT },

637 {"\x1b[21;8~", TB_KEY_F10, TB_MOD_ALL },

638

639 {"\x1b[23;2~", TB_KEY_F11, TB_MOD_SHIFT },

640 {"\x1b[23;3~", TB_KEY_F11, TB_MOD_ALT },

641 {"\x1b[23;4~", TB_KEY_F11, TB_MOD_ALT | TB_MOD_SHIFT },

642 {"\x1b[23;5~", TB_KEY_F11, TB_MOD_CTRL },

643 {"\x1b[23;6~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_SHIFT },

644 {"\x1b[23;7~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT },

645 {"\x1b[23;8~", TB_KEY_F11, TB_MOD_ALL },

646

647 {"\x1b[24;2~", TB_KEY_F12, TB_MOD_SHIFT },

648 {"\x1b[24;3~", TB_KEY_F12, TB_MOD_ALT },

649 {"\x1b[24;4~", TB_KEY_F12, TB_MOD_ALT | TB_MOD_SHIFT },

650 {"\x1b[24;5~", TB_KEY_F12, TB_MOD_CTRL },

651 {"\x1b[24;6~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_SHIFT },

652 {"\x1b[24;7~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT },

653 {"\x1b[24;8~", TB_KEY_F12, TB_MOD_ALL },

654

655 /* rxvt arrows */

656 {"\x1b[a", TB_KEY_ARROW_UP, TB_MOD_SHIFT },

657 {"\x1b\x1b[A", TB_KEY_ARROW_UP, TB_MOD_ALT },

658 {"\x1b\x1b[a", TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT },

659 {"\x1bOa", TB_KEY_ARROW_UP, TB_MOD_CTRL },

660 {"\x1b\x1bOa", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT },

661

662 {"\x1b[b", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT },

663 {"\x1b\x1b[B", TB_KEY_ARROW_DOWN, TB_MOD_ALT },

664 {"\x1b\x1b[b", TB_KEY_ARROW_DOWN, TB_MOD_ALT | TB_MOD_SHIFT },

665 {"\x1bOb", TB_KEY_ARROW_DOWN, TB_MOD_CTRL },

666 {"\x1b\x1bOb", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT },

667

668 {"\x1b[c", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT },

669 {"\x1b\x1b[C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT },

670 {"\x1b\x1b[c", TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT },

671 {"\x1bOc", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL },

672 {"\x1b\x1bOc", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT },

673

674 {"\x1b[d", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT },

675 {"\x1b\x1b[D", TB_KEY_ARROW_LEFT, TB_MOD_ALT },

676 {"\x1b\x1b[d", TB_KEY_ARROW_LEFT, TB_MOD_ALT | TB_MOD_SHIFT },

677 {"\x1bOd", TB_KEY_ARROW_LEFT, TB_MOD_CTRL },

678 {"\x1b\x1bOd", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT },

679

680 /* rxvt keys */

681 {"\x1b[7$", TB_KEY_HOME, TB_MOD_SHIFT },

682 {"\x1b\x1b[7~", TB_KEY_HOME, TB_MOD_ALT },

683 {"\x1b\x1b[7$", TB_KEY_HOME, TB_MOD_ALT | TB_MOD_SHIFT },

684 {"\x1b[7^", TB_KEY_HOME, TB_MOD_CTRL },

685 {"\x1b[7@", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_SHIFT },

686 {"\x1b\x1b[7^", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT },

687 {"\x1b\x1b[7@", TB_KEY_HOME, TB_MOD_ALL },

688

689 {"\x1b\x1b[8~", TB_KEY_END, TB_MOD_ALT },

690 {"\x1b\x1b[8$", TB_KEY_END, TB_MOD_ALT | TB_MOD_SHIFT },

691 {"\x1b[8^", TB_KEY_END, TB_MOD_CTRL },

692 {"\x1b\x1b[8^", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT },

693 {"\x1b\x1b[8@", TB_KEY_END, TB_MOD_ALL },

694 {"\x1b[8@", TB_KEY_END, TB_MOD_CTRL | TB_MOD_SHIFT },

695 {"\x1b[8$", TB_KEY_END, TB_MOD_SHIFT },

696

697 {"\x1b\x1b[2~", TB_KEY_INSERT, TB_MOD_ALT },

698 {"\x1b\x1b[2$", TB_KEY_INSERT, TB_MOD_ALT | TB_MOD_SHIFT },

699 {"\x1b[2^", TB_KEY_INSERT, TB_MOD_CTRL },

700 {"\x1b\x1b[2^", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT },

701 {"\x1b\x1b[2@", TB_KEY_INSERT, TB_MOD_ALL },

702 {"\x1b[2@", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_SHIFT },

703 {"\x1b[2$", TB_KEY_INSERT, TB_MOD_SHIFT },

704

705 {"\x1b\x1b[3~", TB_KEY_DELETE, TB_MOD_ALT },

706 {"\x1b\x1b[3$", TB_KEY_DELETE, TB_MOD_ALT | TB_MOD_SHIFT },

707 {"\x1b[3^", TB_KEY_DELETE, TB_MOD_CTRL },

708 {"\x1b\x1b[3^", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT },

709 {"\x1b\x1b[3@", TB_KEY_DELETE, TB_MOD_ALL },

710 {"\x1b[3@", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_SHIFT },

711 {"\x1b[3$", TB_KEY_DELETE, TB_MOD_SHIFT },

712

713 {"\x1b\x1b[5~", TB_KEY_PGUP, TB_MOD_ALT },

714 {"\x1b\x1b[5$", TB_KEY_PGUP, TB_MOD_ALT | TB_MOD_SHIFT },

715 {"\x1b[5^", TB_KEY_PGUP, TB_MOD_CTRL },

716 {"\x1b\x1b[5^", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT },

717 {"\x1b\x1b[5@", TB_KEY_PGUP, TB_MOD_ALL },

718 {"\x1b[5@", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_SHIFT },

719 {"\x1b[5$", TB_KEY_PGUP, TB_MOD_SHIFT },

720

721 {"\x1b\x1b[6~", TB_KEY_PGDN, TB_MOD_ALT },

722 {"\x1b\x1b[6$", TB_KEY_PGDN, TB_MOD_ALT | TB_MOD_SHIFT },

723 {"\x1b[6^", TB_KEY_PGDN, TB_MOD_CTRL },

724 {"\x1b\x1b[6^", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT },

725 {"\x1b\x1b[6@", TB_KEY_PGDN, TB_MOD_ALL },

726 {"\x1b[6@", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_SHIFT },

727 {"\x1b[6$", TB_KEY_PGDN, TB_MOD_SHIFT },

728

729 {"\x1b\x1b[11~", TB_KEY_F1, TB_MOD_ALT },

730 {"\x1b\x1b[23~", TB_KEY_F1, TB_MOD_ALT | TB_MOD_SHIFT },

731 {"\x1b[11^", TB_KEY_F1, TB_MOD_CTRL },

732 {"\x1b\x1b[11^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT },

733 {"\x1b\x1b[23^", TB_KEY_F1, TB_MOD_ALL },

734 {"\x1b[23^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_SHIFT },

735 {"\x1b[23~", TB_KEY_F1, TB_MOD_SHIFT },

736

737 {"\x1b\x1b[12~", TB_KEY_F2, TB_MOD_ALT },

738 {"\x1b\x1b[24~", TB_KEY_F2, TB_MOD_ALT | TB_MOD_SHIFT },

739 {"\x1b[12^", TB_KEY_F2, TB_MOD_CTRL },

740 {"\x1b\x1b[12^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT },

741 {"\x1b\x1b[24^", TB_KEY_F2, TB_MOD_ALL },

742 {"\x1b[24^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_SHIFT },

743 {"\x1b[24~", TB_KEY_F2, TB_MOD_SHIFT },

744

745 {"\x1b\x1b[13~", TB_KEY_F3, TB_MOD_ALT },

746 {"\x1b\x1b[25~", TB_KEY_F3, TB_MOD_ALT | TB_MOD_SHIFT },

747 {"\x1b[13^", TB_KEY_F3, TB_MOD_CTRL },

748 {"\x1b\x1b[13^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT },

749 {"\x1b\x1b[25^", TB_KEY_F3, TB_MOD_ALL },

750 {"\x1b[25^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_SHIFT },

751 {"\x1b[25~", TB_KEY_F3, TB_MOD_SHIFT },

752

753 {"\x1b\x1b[14~", TB_KEY_F4, TB_MOD_ALT },

754 {"\x1b\x1b[26~", TB_KEY_F4, TB_MOD_ALT | TB_MOD_SHIFT },

755 {"\x1b[14^", TB_KEY_F4, TB_MOD_CTRL },

756 {"\x1b\x1b[14^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT },

757 {"\x1b\x1b[26^", TB_KEY_F4, TB_MOD_ALL },

758 {"\x1b[26^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_SHIFT },

759 {"\x1b[26~", TB_KEY_F4, TB_MOD_SHIFT },

760

761 {"\x1b\x1b[15~", TB_KEY_F5, TB_MOD_ALT },

762 {"\x1b\x1b[28~", TB_KEY_F5, TB_MOD_ALT | TB_MOD_SHIFT },

763 {"\x1b[15^", TB_KEY_F5, TB_MOD_CTRL },

764 {"\x1b\x1b[15^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT },

765 {"\x1b\x1b[28^", TB_KEY_F5, TB_MOD_ALL },

766 {"\x1b[28^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_SHIFT },

767 {"\x1b[28~", TB_KEY_F5, TB_MOD_SHIFT },

768

769 {"\x1b\x1b[17~", TB_KEY_F6, TB_MOD_ALT },

770 {"\x1b\x1b[29~", TB_KEY_F6, TB_MOD_ALT | TB_MOD_SHIFT },

771 {"\x1b[17^", TB_KEY_F6, TB_MOD_CTRL },

772 {"\x1b\x1b[17^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT },

773 {"\x1b\x1b[29^", TB_KEY_F6, TB_MOD_ALL },

774 {"\x1b[29^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_SHIFT },

775 {"\x1b[29~", TB_KEY_F6, TB_MOD_SHIFT },

776

777 {"\x1b\x1b[18~", TB_KEY_F7, TB_MOD_ALT },

778 {"\x1b\x1b[31~", TB_KEY_F7, TB_MOD_ALT | TB_MOD_SHIFT },

779 {"\x1b[18^", TB_KEY_F7, TB_MOD_CTRL },

780 {"\x1b\x1b[18^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT },

781 {"\x1b\x1b[31^", TB_KEY_F7, TB_MOD_ALL },

782 {"\x1b[31^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_SHIFT },

783 {"\x1b[31~", TB_KEY_F7, TB_MOD_SHIFT },

784

785 {"\x1b\x1b[19~", TB_KEY_F8, TB_MOD_ALT },

786 {"\x1b\x1b[32~", TB_KEY_F8, TB_MOD_ALT | TB_MOD_SHIFT },

787 {"\x1b[19^", TB_KEY_F8, TB_MOD_CTRL },

788 {"\x1b\x1b[19^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT },

789 {"\x1b\x1b[32^", TB_KEY_F8, TB_MOD_ALL },

790 {"\x1b[32^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_SHIFT },

791 {"\x1b[32~", TB_KEY_F8, TB_MOD_SHIFT },

792

793 {"\x1b\x1b[20~", TB_KEY_F9, TB_MOD_ALT },

794 {"\x1b\x1b[33~", TB_KEY_F9, TB_MOD_ALT | TB_MOD_SHIFT },

795 {"\x1b[20^", TB_KEY_F9, TB_MOD_CTRL },

796 {"\x1b\x1b[20^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT },

797 {"\x1b\x1b[33^", TB_KEY_F9, TB_MOD_ALL },

798 {"\x1b[33^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_SHIFT },

799 {"\x1b[33~", TB_KEY_F9, TB_MOD_SHIFT },

800

801 {"\x1b\x1b[21~", TB_KEY_F10, TB_MOD_ALT },

802 {"\x1b\x1b[34~", TB_KEY_F10, TB_MOD_ALT | TB_MOD_SHIFT },

803 {"\x1b[21^", TB_KEY_F10, TB_MOD_CTRL },

804 {"\x1b\x1b[21^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT },

805 {"\x1b\x1b[34^", TB_KEY_F10, TB_MOD_ALL },

806 {"\x1b[34^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_SHIFT },

807 {"\x1b[34~", TB_KEY_F10, TB_MOD_SHIFT },

808

809 {"\x1b\x1b[23~", TB_KEY_F11, TB_MOD_ALT },

810 {"\x1b\x1b[23$", TB_KEY_F11, TB_MOD_ALT | TB_MOD_SHIFT },

811 {"\x1b[23^", TB_KEY_F11, TB_MOD_CTRL },

812 {"\x1b\x1b[23^", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT },

813 {"\x1b\x1b[23@", TB_KEY_F11, TB_MOD_ALL },

814 {"\x1b[23@", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_SHIFT },

815 {"\x1b[23$", TB_KEY_F11, TB_MOD_SHIFT },

816

817 {"\x1b\x1b[24~", TB_KEY_F12, TB_MOD_ALT },

818 {"\x1b\x1b[24$", TB_KEY_F12, TB_MOD_ALT | TB_MOD_SHIFT },

819 {"\x1b[24^", TB_KEY_F12, TB_MOD_CTRL },

820 {"\x1b\x1b[24^", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT },

821 {"\x1b\x1b[24@", TB_KEY_F12, TB_MOD_ALL },

822 {"\x1b[24@", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_SHIFT },

823 {"\x1b[24$", TB_KEY_F12, TB_MOD_SHIFT },

824

825 /* linux console/putty arrows */

826 {"\x1b[A", TB_KEY_ARROW_UP, TB_MOD_SHIFT },

827 {"\x1b[B", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT },

828 {"\x1b[C", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT },

829 {"\x1b[D", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT },

830

831 /* more putty arrows */

832 {"\x1bOA", TB_KEY_ARROW_UP, TB_MOD_CTRL },

833 {"\x1b\x1bOA", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT },

834 {"\x1bOB", TB_KEY_ARROW_DOWN, TB_MOD_CTRL },

835 {"\x1b\x1bOB", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT },

836 {"\x1bOC", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL },

837 {"\x1b\x1bOC", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT },

838 {"\x1bOD", TB_KEY_ARROW_LEFT, TB_MOD_CTRL },

839 {"\x1b\x1bOD", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT },

840

841 {NULL, 0, 0 },

842 };

843

844 static const unsigned char utf8_length[256] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

845 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

846 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

847 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

848 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

849 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

850 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

851 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

852 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

853 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,

854 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5,

855 5, 6, 6, 1, 1};

856

857 static const unsigned char utf8_mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};

858

859 static int tb_reset(void);

860 static int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg,

861 size_t *out_w, const char *fmt, va_list vl);

862 static int init_term_attrs(void);

863 static int init_term_caps(void);

864 static int init_cap_trie(void);

865 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod);

866 static int cap_trie_find(const char *buf, size_t nbuf,

867 struct cap_trie_t **last, size_t *depth);

868 static int cap_trie_deinit(struct cap_trie_t *node);

869 static int init_resize_handler(void);

870 static int send_init_escape_codes(void);

871 static int send_clear(void);

872 static int update_term_size(void);

873 static int update_term_size_via_esc(void);

874 static int init_cellbuf(void);

875 static int tb_deinit(void);

876 static int load_terminfo(void);

877 static int load_terminfo_from_path(const char *path, const char *term);

878 static int read_terminfo_path(const char *path);

879 static int parse_terminfo_caps(void);

880 static int load_builtin_caps(void);

881 static const char *get_terminfo_string(int16_t str_offsets_pos,

882 int16_t str_table_pos, int16_t str_table_len,

883 int16_t str_index);

884 static int wait_event(struct tb_event *event, int timeout);

885 static int extract_event(struct tb_event *event);

886 static int extract_esc(struct tb_event *event);

887 static int extract_esc_user(struct tb_event *event, int is_post);

888 static int extract_esc_cap(struct tb_event *event);

889 static int extract_esc_mouse(struct tb_event *event);

890 static int resize_cellbufs(void);

891 static void handle_resize(int sig);

892 static int send_attr(uintattr_t fg, uintattr_t bg);

893 static int send_sgr(uintattr_t fg, uintattr_t bg, uintattr_t fg_is_default,

894 uintattr_t bg_is_default);

895 static int send_cursor_if(int x, int y);

896 static int send_char(int x, int y, uint32_t ch);

897 static int send_cluster(int x, int y, uint32_t *ch, size_t nch);

898 static int convert_num(uint32_t num, char *buf);

899 static int cell_cmp(struct tb_cell *a, struct tb_cell *b);

900 static int cell_copy(struct tb_cell *dst, struct tb_cell *src);

901 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,

902 uintattr_t fg, uintattr_t bg);

903 static int cell_reserve_ech(struct tb_cell *cell, size_t n);

904 static int cell_free(struct tb_cell *cell);

905 static int cellbuf_init(struct cellbuf_t *c, int w, int h);

906 static int cellbuf_free(struct cellbuf_t *c);

907 static int cellbuf_clear(struct cellbuf_t *c);

908 static int cellbuf_get(struct cellbuf_t *c, int x, int y,

909 struct tb_cell **out);

910 static int cellbuf_resize(struct cellbuf_t *c, int w, int h);

911 static int bytebuf_puts(struct bytebuf_t *b, const char *str);

912 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr);

913 static int bytebuf_shift(struct bytebuf_t *b, size_t n);

914 static int bytebuf_flush(struct bytebuf_t *b, int fd);

915 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz);

916 static int bytebuf_free(struct bytebuf_t *b);

917

918 int tb_init(void) {

919 return tb_init_file("/dev/tty");

920 }

921

922 int tb_init_file(const char *path) {

923

924 int ttyfd;

925

926 if (global.initialized) {

927 return TB_ERR_INIT_ALREADY;

928 }

929 ttyfd = open(path, O_RDWR);

930 if (ttyfd < 0) {

931 global.last_errno = errno;

932 return TB_ERR_INIT_OPEN;

933 }

934 global.ttyfd_open = 1;

935 return tb_init_fd(ttyfd);

936 }

937

938 int tb_init_fd(int ttyfd) {

939 return tb_init_rwfd(ttyfd, ttyfd);

940 }

941

942 int tb_init_rwfd(int rfd, int wfd) {

943 int rv;

944 const char *term;

945

946 tb_reset();

947 global.ttyfd = rfd == wfd && isatty(rfd) ? rfd : -1;

948 global.rfd = rfd;

949 global.wfd = wfd;

950

951 term = getenv("TERM");

952 if (!term) {

953 term = "xterm";

954 setenv("TERM", term, 0);

955 }

956

957 if (strcmp("st-256color", term) == 0) {

958 setenv("TERM", "screen-256color", 1);

959 } else if (strcmp("st", term) == 0) {

960 setenv("TERM", "screen", 1);

961 }

962

963 do {

964 retry:

965 if_err_break(rv, init_term_attrs());

966 if_err_break(rv, init_term_caps());

967 if_err_break(rv, init_cap_trie());

968 if_err_break(rv, init_resize_handler());

969 if_err_break(rv, send_init_escape_codes());

970 if_err_break(rv, send_clear());

971 if_err_break(rv, update_term_size());

972 if_err_break(rv, init_cellbuf());

973 global.initialized = 1;

974 } while (0);

975

976 if (rv != TB_OK) {

977 if (strcmp(term, "xterm")) {

978 setenv("TERM", "xterm", 1);

979 goto retry;

980 }

981 tb_deinit();

982 }

983

984 return rv;

985 }

986

987 int tb_shutdown(void) {

988 if_not_init_return();

989 tb_deinit();

990 return TB_OK;

991 }

992

993 int tb_width(void) {

994 if_not_init_return();

995 return global.width;

996 }

997

998 int tb_height(void) {

999 if_not_init_return();

1000 return global.height;

1001 }

1002

1003 int tb_clear(void) {

1004 if_not_init_return();

1005 return cellbuf_clear(&global.back);

1006 }

1007

1008 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) {

1009 if_not_init_return();

1010 global.fg = fg;

1011 global.bg = bg;

1012 return TB_OK;

1013 }

1014

1015 int tb_present(void) {

1016

1017 int rv, x, y, i;

1018

1019 if_not_init_return();

1020

1021 global.last_x = -1;

1022 global.last_y = -1;

1023

1024 for (y = 0; y < global.front.height; y++) {

1025 for (x = 0; x < global.front.width;) {

1026 struct tb_cell *back, *front;

1027 int w;

1028 if_err_return(rv, cellbuf_get(

1029 &global.back, x, y, &back));

1030 if_err_return(rv, cellbuf_get(

1031 &global.front, x, y, &front));

1032

1033 {

1034 #ifdef TB_OPT_EGC

1035 if (back->nech > 0)

1036 w = wcswidth((wchar_t *)back->ech, back->nech);

1037 else

1038 #endif

1039 /* wcwidth() simply returns -1 on overflow of

1040 * wchar_t */

1041 w = wcwidth((wchar_t)back->ch);

1042 }

1043 if (w < 1) {

1044 w = 1;

1045 }

1046

1047 if (!cell_cmp(back, front)) {

1048 x += w;

1049 continue;

1050 }

1051 cell_copy(front, back);

1052

1053 send_attr(back->fg, back->bg);

1054 if (w > 1 && x >= global.front.width - (w - 1)) {

1055 for (i = x; i < global.front.width; i++) {

1056 send_char(i, y, ' ');

1057 }

1058 } else {

1059 {

1060 #ifdef TB_OPT_EGC

1061 if (back->nech > 0)

1062 send_cluster(x, y, back->ech,

1063 back->nech);

1064 else

1065 #endif

1066 send_char(x, y, back->ch);

1067 }

1068 for (i = 1; i < w; i++) {

1069 struct tb_cell *front_wide;

1070 if_err_return(rv, cellbuf_get(

1071 &global.front, x + i, y,

1072 &front_wide));

1073 if_err_return(rv, cell_set(front_wide,

1074 0, 1, back->fg, back->bg));

1075

1076 }

1077 }

1078 x += w;

1079 }

1080 }

1081

1082 if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));

1083 if_err_return(rv, bytebuf_flush(&global.out, global.wfd));

1084

1085 return TB_OK;

1086 }

1087

1088 int tb_set_cursor(int cx, int cy) {

1089 int rv;

1090 if_not_init_return();

1091 if (cx < 0)

1092 cx = 0;

1093 if (cy < 0)

1094 cy = 0;

1095 if (global.cursor_x == -1) {

1096 if_err_return(rv, bytebuf_puts(&global.out,

1097 global.caps[TB_CAP_SHOW_CURSOR]));

1098 }

1099 if_err_return(rv, send_cursor_if(cx, cy));

1100 global.cursor_x = cx;

1101 global.cursor_y = cy;

1102 return TB_OK;

1103 }

1104

1105 int tb_hide_cursor(void) {

1106 int rv;

1107 if_not_init_return();

1108 if (global.cursor_x >= 0) {

1109 if_err_return(rv, bytebuf_puts(&global.out,

1110 global.caps[TB_CAP_HIDE_CURSOR]));

1111 }

1112 global.cursor_x = -1;

1113 global.cursor_y = -1;

1114 return TB_OK;

1115 }

1116

1117 int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg) {

1118 if_not_init_return();

1119 return tb_set_cell_ex(x, y, &ch, 1, fg, bg);

1120 }

1121

1122 int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg,

1123 uintattr_t bg) {

1124 int rv;

1125 struct tb_cell *cell;

1126 if_not_init_return();

1127 if_err_return(rv, cellbuf_get(&global.back, x, y, &cell));

1128 if_err_return(rv, cell_set(cell, ch, nch, fg, bg));

1129 return TB_OK;

1130 }

1131

1132 int tb_extend_cell(int x, int y, uint32_t ch) {

1133 if_not_init_return();

1134 #ifdef TB_OPT_EGC

1135 int rv;

1136 struct tb_cell *cell;

1137 size_t nech;

1138 if_err_return(rv, cellbuf_get(&global.back, x, y, &cell));

1139 if (cell->nech > 0) { /* append to ech */

1140 nech = cell->nech + 1;

1141 if_err_return(rv, cell_reserve_ech(cell, nech));

1142 cell->ech[nech - 1] = ch;

1143 } else { /* make new ech */

1144 nech = 2;

1145 if_err_return(rv, cell_reserve_ech(cell, nech));

1146 cell->ech[0] = cell->ch;

1147 cell->ech[1] = ch;

1148 }

1149 cell->ech[nech] = '\0';

1150 cell->nech = nech;

1151 return TB_OK;

1152 #else

1153 (void)x;

1154 (void)y;

1155 (void)ch;

1156 return TB_ERR;

1157 #endif

1158 }

1159

1160 int tb_set_input_mode(int mode) {

1161 if_not_init_return();

1162 if (mode == TB_INPUT_CURRENT) {

1163 return global.input_mode;

1164 }

1165

1166 if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0) {

1167 mode |= TB_INPUT_ESC;

1168 }

1169

1170 if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) ==

1171 (TB_INPUT_ESC | TB_INPUT_ALT)) {

1172 mode &= ~TB_INPUT_ALT;

1173 }

1174

1175 if (mode & TB_INPUT_MOUSE) {

1176 bytebuf_puts(&global.out, TB_HARDCAP_ENTER_MOUSE);

1177 bytebuf_flush(&global.out, global.wfd);

1178 } else {

1179 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);

1180 bytebuf_flush(&global.out, global.wfd);

1181 }

1182

1183 global.input_mode = mode;

1184 return TB_OK;

1185 }

1186

1187 int tb_set_output_mode(int mode) {

1188 if_not_init_return();

1189 switch (mode) {

1190 case TB_OUTPUT_CURRENT:

1191 return global.output_mode;

1192 case TB_OUTPUT_NORMAL:

1193 case TB_OUTPUT_256:

1194 case TB_OUTPUT_216:

1195 case TB_OUTPUT_GRAYSCALE:

1196 #ifdef TB_OPT_TRUECOLOR

1197 case TB_OUTPUT_TRUECOLOR:

1198 #endif

1199 global.output_mode = mode;

1200 return TB_OK;

1201 }

1202 return TB_ERR;

1203 }

1204

1205 int tb_peek_event(struct tb_event *event, int timeout_ms) {

1206 if_not_init_return();

1207 return wait_event(event, timeout_ms);

1208 }

1209

1210 int tb_poll_event(struct tb_event *event) {

1211 if_not_init_return();

1212 return wait_event(event, -1);

1213 }

1214

1215 int tb_get_fds(int *ttyfd, int *resizefd) {

1216 if_not_init_return();

1217

1218 *ttyfd = global.rfd;

1219 *resizefd = global.resize_pipefd[0];

1220

1221 return TB_OK;

1222 }

1223

1224 int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str) {

1225 return tb_print_ex(x, y, fg, bg, NULL, str);

1226 }

1227

1228 int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,

1229 const char *str) {

1230 int rv;

1231 uint32_t uni;

1232 int w, ix = x;

1233 if (out_w) {

1234 *out_w = 0;

1235 }

1236 while (*str) {

1237 str += tb_utf8_char_to_unicode(&uni, str);

1238 w = wcwidth((wchar_t)uni);

1239 if (w <= 0) {

1240 w = 1;

1241 }

1242 if (w == 0 && x > ix) {

1243 if_err_return(rv, tb_extend_cell(x - 1, y, uni));

1244 } else {

1245 if_err_return(rv, tb_set_cell(x, y, uni, fg, bg));

1246 }

1247 x += w;

1248 if (out_w) {

1249 *out_w += w;

1250 }

1251 }

1252 return TB_OK;

1253 }

1254

1255 int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt,

1256 ...) {

1257 int rv;

1258 va_list vl;

1259 va_start(vl, fmt);

1260 rv = tb_printf_inner(x, y, fg, bg, NULL, fmt, vl);

1261 va_end(vl);

1262 return rv;

1263 }

1264

1265 int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,

1266 const char *fmt, ...) {

1267 int rv;

1268 va_list vl;

1269 va_start(vl, fmt);

1270 rv = tb_printf_inner(x, y, fg, bg, out_w, fmt, vl);

1271 va_end(vl);

1272 return rv;

1273 }

1274

1275 int tb_send(const char *buf, size_t nbuf) {

1276 return bytebuf_nputs(&global.out, buf, nbuf);

1277 }

1278

1279 int tb_sendf(const char *fmt, ...) {

1280 int rv;

1281 char buf[TB_OPT_PRINTF_BUF];

1282 va_list vl;

1283 va_start(vl, fmt);

1284 rv = vsnprintf(buf, sizeof(buf), fmt, vl);

1285 va_end(vl);

1286 if (rv < 0 || rv >= (int)sizeof(buf)) {

1287 return TB_ERR;

1288 }

1289 return tb_send(buf, (size_t)rv);

1290 }

1291

1292 int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)) {

1293 switch (fn_type) {

1294 case TB_FUNC_EXTRACT_PRE:

1295 global.fn_extract_esc_pre = fn;

1296 return TB_OK;

1297 case TB_FUNC_EXTRACT_POST:

1298 global.fn_extract_esc_post = fn;

1299 return TB_OK;

1300 }

1301 return TB_ERR;

1302 }

1303

1304 struct tb_cell *tb_cell_buffer(void) {

1305 if (!global.initialized)

1306 return NULL;

1307 return global.back.cells;

1308 }

1309

1310 int tb_utf8_char_length(char c) {

1311 return utf8_length[(unsigned char)c];

1312 }

1313

1314 int tb_utf8_char_to_unicode(uint32_t *out, const char *c) {

1315

1316 int i;

1317 unsigned char len, mask;

1318 uint32_t result;

1319

1320 if (*c == 0) {

1321 return TB_ERR;

1322 }

1323

1324 len = tb_utf8_char_length(*c);

1325 mask = utf8_mask[len - 1];

1326 result = c[0] & mask;

1327 for (i = 1; i < len; ++i) {

1328 result <<= 6;

1329 result |= c[i] & 0x3f;

1330 }

1331

1332 *out = result;

1333 return (int)len;

1334 }

1335

1336 int tb_utf8_unicode_to_char(char *out, uint32_t c) {

1337 int len = 0;

1338 int first;

1339 int i;

1340

1341 if (c < 0x80) {

1342 first = 0;

1343 len = 1;

1344 } else if (c < 0x800) {

1345 first = 0xc0;

1346 len = 2;

1347 } else if (c < 0x10000) {

1348 first = 0xe0;

1349 len = 3;

1350 } else if (c < 0x200000) {

1351 first = 0xf0;

1352 len = 4;

1353 } else if (c < 0x4000000) {

1354 first = 0xf8;

1355 len = 5;

1356 } else {

1357 first = 0xfc;

1358 len = 6;

1359 }

1360

1361 for (i = len - 1; i > 0; --i) {

1362 out[i] = (c & 0x3f) | 0x80;

1363 c >>= 6;

1364 }

1365 out[0] = c | first;

1366

1367 return len;

1368 }

1369

1370 int tb_last_errno(void) {

1371 return global.last_errno;

1372 }

1373

1374 const char *tb_strerror(int err) {

1375 switch (err) {

1376 case TB_OK:

1377 return "Success";

1378 case TB_ERR_NEED_MORE:

1379 return "Not enough input";

1380 case TB_ERR_INIT_ALREADY:

1381 return "Termbox initialized already";

1382 case TB_ERR_MEM:

1383 return "Out of memory";

1384 case TB_ERR_NO_EVENT:

1385 return "No event";

1386 case TB_ERR_NO_TERM:

1387 return "No TERM in environment";

1388 case TB_ERR_NOT_INIT:

1389 return "Termbox not initialized";

1390 case TB_ERR_OUT_OF_BOUNDS:

1391 return "Out of bounds";

1392 case TB_ERR_UNSUPPORTED_TERM:

1393 return "Unsupported terminal";

1394 case TB_ERR_CAP_COLLISION:

1395 return "Termcaps collision";

1396 case TB_ERR_RESIZE_SSCANF:

1397 return "Terminal width/height not received by sscanf()"

1398 " after resize";

1399 case TB_ERR:

1400 case TB_ERR_INIT_OPEN:

1401 case TB_ERR_READ:

1402 case TB_ERR_RESIZE_IOCTL:

1403 case TB_ERR_RESIZE_PIPE:

1404 case TB_ERR_RESIZE_SIGACTION:

1405 case TB_ERR_POLL:

1406 case TB_ERR_TCGETATTR:

1407 case TB_ERR_TCSETATTR:

1408 case TB_ERR_RESIZE_WRITE:

1409 case TB_ERR_RESIZE_POLL:

1410 case TB_ERR_RESIZE_READ:

1411 default:

1412 strerror_r(global.last_errno, global.errbuf,

1413 sizeof(global.errbuf));

1414 return (const char *)global.errbuf;

1415 }

1416 }

1417

1418 int tb_has_truecolor(void) {

1419 #ifdef TB_OPT_TRUECOLOR

1420 return 1;

1421 #else

1422 return 0;

1423 #endif

1424 }

1425

1426 int tb_has_egc(void) {

1427 #ifdef TB_OPT_EGC

1428 return 1;

1429 #else

1430 return 0;

1431 #endif

1432 }

1433

1434 const char *tb_version(void) {

1435 return TB_VERSION_STR;

1436 }

1437

1438 static int tb_reset(void) {

1439 int ttyfd_open = global.ttyfd_open;

1440 memset(&global, 0, sizeof(global));

1441 global.ttyfd = -1;

1442 global.rfd = -1;

1443 global.wfd = -1;

1444 global.ttyfd_open = ttyfd_open;

1445 global.resize_pipefd[0] = -1;

1446 global.resize_pipefd[1] = -1;

1447 global.width = -1;

1448 global.height = -1;

1449 global.cursor_x = -1;

1450 global.cursor_y = -1;

1451 global.last_x = -1;

1452 global.last_y = -1;

1453 global.fg = TB_DEFAULT;

1454 global.bg = TB_DEFAULT;

1455 global.last_fg = ~global.fg;

1456 global.last_bg = ~global.bg;

1457 global.input_mode = TB_INPUT_ESC;

1458 global.output_mode = TB_OUTPUT_NORMAL;

1459 return TB_OK;

1460 }

1461

1462 static int init_term_attrs(void) {

1463

1464 struct termios tios;

1465

1466 if (global.ttyfd < 0) {

1467 return TB_OK;

1468 }

1469

1470 if (tcgetattr(global.ttyfd, &global.orig_tios) != 0) {

1471 global.last_errno = errno;

1472 return TB_ERR_TCGETATTR;

1473 }

1474

1475 memcpy(&tios, &global.orig_tios, sizeof(tios));

1476 global.has_orig_tios = 1;

1477

1478 cfmakeraw(&tios);

1479 tios.c_cc[VMIN] = 1;

1480 tios.c_cc[VTIME] = 0;

1481

1482 if (tcsetattr(global.ttyfd, TCSAFLUSH, &tios) != 0) {

1483 global.last_errno = errno;

1484 return TB_ERR_TCSETATTR;

1485 }

1486

1487 return TB_OK;

1488 }

1489

1490 int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,

1491 const char *fmt, va_list vl) {

1492 int rv;

1493 char buf[TB_OPT_PRINTF_BUF];

1494 rv = vsnprintf(buf, sizeof(buf), fmt, vl);

1495 if (rv < 0 || rv >= (int)sizeof(buf)) {

1496 return TB_ERR;

1497 }

1498 return tb_print_ex(x, y, fg, bg, out_w, buf);

1499 }

1500

1501 static int init_term_caps(void) {

1502 if (load_terminfo() == TB_OK) {

1503 return parse_terminfo_caps();

1504 }

1505 return load_builtin_caps();

1506 }

1507

1508 static int init_cap_trie(void) {

1509 int rv, i;

1510

1511 /* Add caps from terminfo or built-in */

1512 for (i = 0; i < TB_CAP__COUNT_KEYS; i++) {

1513 if_err_return(rv,

1514 cap_trie_add(global.caps[i], tb_key_i(i), 0));

1515 }

1516

1517 /* Add built-in mod caps */

1518 for (i = 0; builtin_mod_caps[i].cap != NULL; i++) {

1519 rv = cap_trie_add(builtin_mod_caps[i].cap,

1520 builtin_mod_caps[i].key,

1521 builtin_mod_caps[i].mod);

1522 /*

1523 * Collisions are OK. This can happen if global.caps collides

1524 * with builtin_mod_caps. It is desirable to give precedence to

1525 * global.caps here.

1526 */

1527 if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) {

1528 return rv;

1529 }

1530 }

1531

1532 return TB_OK;

1533 }

1534

1535 static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) {

1536 struct cap_trie_t *next, *node = &global.cap_trie;

1537 size_t i, j;

1538 for (i = 0; cap[i] != '\0'; i++) {

1539 char c = cap[i];

1540 next = NULL;

1541

1542 /* Check if c is already a child of node */

1543 for (j = 0; j < node->nchildren; j++) {

1544 if (node->children[j].c == c) {

1545 next = &node->children[j];

1546 break;

1547 }

1548 }

1549 if (!next) {

1550 /* We need to add a new child to node */

1551 node->nchildren += 1;

1552 node->children = tb_realloc(node->children,

1553 sizeof(*node) * node->nchildren);

1554 if (!node->children) {

1555 return TB_ERR_MEM;

1556 }

1557 next = &node->children[node->nchildren - 1];

1558 memset(next, 0, sizeof(*next));

1559 next->c = c;

1560 }

1561

1562 /* Continue */

1563 node = next;

1564 }

1565

1566 if (node->is_leaf) {

1567 /* Already a leaf here */

1568 return TB_ERR_CAP_COLLISION;

1569 }

1570

1571 node->is_leaf = 1;

1572 node->key = key;

1573 node->mod = mod;

1574 return TB_OK;

1575 }

1576

1577 static int cap_trie_find(const char *buf, size_t nbuf,

1578 struct cap_trie_t **last, size_t *depth) {

1579 struct cap_trie_t *next, *node = &global.cap_trie;

1580 size_t i, j;

1581 *last = node;

1582 *depth = 0;

1583 for (i = 0; i < nbuf; i++) {

1584 char c = buf[i];

1585 next = NULL;

1586

1587 /* Find c in node.children */

1588 for (j = 0; j < node->nchildren; j++) {

1589 if (node->children[j].c == c) {

1590 next = &node->children[j];

1591 break;

1592 }

1593 }

1594 if (!next) {

1595 /* Not found */

1596 return TB_OK;

1597 }

1598 node = next;

1599 *last = node;

1600 *depth += 1;

1601 if (node->is_leaf && node->nchildren < 1) {

1602 break;

1603 }

1604 }

1605 return TB_OK;

1606 }

1607

1608 static int cap_trie_deinit(struct cap_trie_t *node) {

1609 size_t j;

1610 for (j = 0; j < node->nchildren; j++) {

1611 cap_trie_deinit(&node->children[j]);

1612 }

1613 if (node->children) {

1614 tb_free(node->children);

1615 }

1616 memset(node, 0, sizeof(*node));

1617 return TB_OK;

1618 }

1619

1620 static int init_resize_handler(void) {

1621 struct sigaction sa;

1622 if (pipe(global.resize_pipefd) != 0) {

1623 global.last_errno = errno;

1624 return TB_ERR_RESIZE_PIPE;

1625 }

1626

1627 memset(&sa, 0, sizeof(sa));

1628 sa.sa_handler = handle_resize;

1629 if (sigaction(SIGWINCH, &sa, NULL) != 0) {

1630 global.last_errno = errno;

1631 return TB_ERR_RESIZE_SIGACTION;

1632 }

1633

1634 return TB_OK;

1635 }

1636

1637 static int send_init_escape_codes(void) {

1638 int rv;

1639 if_err_return(rv, bytebuf_puts(&global.out,

1640 global.caps[TB_CAP_ENTER_CA]));

1641 if_err_return(rv, bytebuf_puts(&global.out,

1642 global.caps[TB_CAP_ENTER_KEYPAD]));

1643 if_err_return(rv, bytebuf_puts(&global.out,

1644 global.caps[TB_CAP_HIDE_CURSOR]));

1645 return TB_OK;

1646 }

1647

1648 static int send_clear(void) {

1649 int rv;

1650

1651 if_err_return(rv, send_attr(global.fg, global.bg));

1652 if_err_return(rv, bytebuf_puts(&global.out,

1653 global.caps[TB_CAP_CLEAR_SCREEN]));

1654

1655 if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));

1656 if_err_return(rv, bytebuf_flush(&global.out, global.wfd));

1657

1658 global.last_x = -1;

1659 global.last_y = -1;

1660

1661 return TB_OK;

1662 }

1663

1664 static int update_term_size(void) {

1665 int rv, ioctl_errno;

1666 struct winsize sz;

1667

1668 if (global.ttyfd < 0) {

1669 return TB_OK;

1670 }

1671

1672 memset(&sz, 0, sizeof(sz));

1673

1674 /* Try ioctl TIOCGWINSZ */

1675 if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) {

1676 global.width = sz.ws_col;

1677 global.height = sz.ws_row;

1678 return TB_OK;

1679 }

1680 ioctl_errno = errno;

1681

1682 /* Try >cursor(9999,9999), >u7, <u6 */

1683 if_ok_return(rv, update_term_size_via_esc());

1684

1685 global.last_errno = ioctl_errno;

1686 return TB_ERR_RESIZE_IOCTL;

1687 }

1688

1689 static int update_term_size_via_esc(void) {

1690 #ifndef TB_RESIZE_FALLBACK_MS

1691 #define TB_RESIZE_FALLBACK_MS 1000

1692 #endif

1693

1694 char *move_and_report = "\x1b[9999;9999H\x1b[6n";

1695 int rw, rh;

1696 char buf[TB_OPT_READ_BUF];

1697 ssize_t write_rv, read_rv;

1698 int select_rv;

1699 struct timeval timeout;

1700 fd_set fds;

1701

1702 write_rv = write(global.wfd, move_and_report, strlen(move_and_report));

1703 if (write_rv != (ssize_t)strlen(move_and_report)) {

1704 return TB_ERR_RESIZE_WRITE;

1705 }

1706

1707 FD_ZERO(&fds);

1708 FD_SET(global.rfd, &fds);

1709

1710 timeout.tv_sec = 0;

1711 timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000;

1712

1713 select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout);

1714

1715 if (select_rv != 1) {

1716 global.last_errno = errno;

1717 return TB_ERR_RESIZE_POLL;

1718 }

1719

1720 read_rv = read(global.rfd, buf, sizeof(buf) - 1);

1721 if (read_rv < 1) {

1722 global.last_errno = errno;

1723 return TB_ERR_RESIZE_READ;

1724 }

1725 buf[read_rv] = '\0';

1726

1727 if (sscanf(buf, "\x1b[%d;%dR", &rh, &rw) != 2) {

1728 return TB_ERR_RESIZE_SSCANF;

1729 }

1730

1731 global.width = rw;

1732 global.height = rh;

1733 return TB_OK;

1734 }

1735

1736 static int init_cellbuf(void) {

1737 int rv;

1738 if_err_return(rv, cellbuf_init(&global.back, global.width,

1739 global.height));

1740 if_err_return(rv, cellbuf_init(&global.front, global.width,

1741 global.height));

1742 if_err_return(rv, cellbuf_clear(&global.back));

1743 if_err_return(rv, cellbuf_clear(&global.front));

1744 return TB_OK;

1745 }

1746

1747 static int tb_deinit(void) {

1748

1749 struct sigaction sig = {0};

1750

1751 if (global.caps[0] != NULL && global.wfd >= 0) {

1752 bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);

1753 bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]);

1754 bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]);

1755 bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_CA]);

1756 bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_KEYPAD]);

1757 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);

1758 bytebuf_flush(&global.out, global.wfd);

1759 }

1760 if (global.ttyfd >= 0) {

1761 if (global.has_orig_tios) {

1762 tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios);

1763 }

1764 if (global.ttyfd_open) {

1765 close(global.ttyfd);

1766 global.ttyfd_open = 0;

1767 }

1768 }

1769

1770 sig.sa_handler = SIG_DFL;

1771 sigaction(SIGWINCH, &sig, NULL);

1772 if (global.resize_pipefd[0] >= 0)

1773 close(global.resize_pipefd[0]);

1774 if (global.resize_pipefd[1] >= 0)

1775 close(global.resize_pipefd[1]);

1776

1777 cellbuf_free(&global.back);

1778 cellbuf_free(&global.front);

1779 bytebuf_free(&global.in);

1780 bytebuf_free(&global.out);

1781

1782 if (global.terminfo)

1783 tb_free(global.terminfo);

1784

1785 cap_trie_deinit(&global.cap_trie);

1786

1787 tb_reset();

1788 return TB_OK;

1789 }

1790

1791 static int load_terminfo(void) {

1792 int rv;

1793 char tmp[PATH_MAX];

1794 const char *term, *terminfo, *home, *dirs;

1795

1796 /*

1797 * See terminfo(5) "Fetching Compiled Descriptions" for a description

1798 * of this behavior. Some of these paths are compile-time ncurses

1799 * options, so best guesses are used here.

1800 */

1801 term = getenv("TERM");

1802 if (!term) {

1803 return TB_ERR;

1804 }

1805

1806 /* If TERMINFO is set, try that directory and stop */

1807 terminfo = getenv("TERMINFO");

1808 if (terminfo) {

1809 return load_terminfo_from_path(terminfo, term);

1810 }

1811

1812 /* Next try ~/.terminfo */

1813 home = getenv("HOME");

1814 if (home) {

1815 if_err_return(rv, snprintf_(tmp, sizeof(tmp), "%s/.terminfo",

1816 home));

1817 if_ok_return(rv, load_terminfo_from_path(tmp, term));

1818 }

1819

1820 /*

1821 * Next try TERMINFO_DIRS

1822 *

1823 * Note, empty entries are supposed to be interpretted as the

1824 * "compiled-in default", which is of course system-dependent.

1825 * Previously /etc/terminfo was used here. Let's skip empty entries

1826 * altogether rather than give precedence to a guess, and check common

1827 * paths after this loop.

1828 */

1829 dirs = getenv("TERMINFO_DIRS");

1830 if (dirs) {

1831 char *dir;

1832 if_err_return(rv, snprintf_(tmp, sizeof(tmp), "%s", dirs));

1833 dir = strtok(tmp, ":");

1834 while (dir) {

1835 const char *cdir = dir;

1836 if (*cdir != '\0') {

1837 if_ok_return(rv,

1838 load_terminfo_from_path(cdir, term));

1839 }

1840 dir = strtok(NULL, ":");

1841 }

1842 }

1843

1844 #ifdef TB_TERMINFO_DIR

1845 if_ok_return(rv, load_terminfo_from_path(TB_TERMINFO_DIR, term));

1846 #endif

1847 if_ok_return(rv,

1848 load_terminfo_from_path("/usr/local/etc/terminfo", term));

1849 if_ok_return(rv,

1850 load_terminfo_from_path("/usr/local/share/terminfo", term));

1851 if_ok_return(rv,

1852 load_terminfo_from_path("/usr/local/lib/terminfo", term));

1853 if_ok_return(rv,

1854 load_terminfo_from_path("/etc/terminfo", term));

1855 if_ok_return(rv,

1856 load_terminfo_from_path("/usr/share/terminfo", term));

1857 if_ok_return(rv,

1858 load_terminfo_from_path("/usr/lib/terminfo", term));

1859 if_ok_return(rv,

1860 load_terminfo_from_path("/usr/share/lib/terminfo", term));

1861 if_ok_return(rv,

1862 load_terminfo_from_path("/lib/terminfo", term));

1863

1864 return TB_ERR;

1865 }

1866

1867 static int load_terminfo_from_path(const char *path, const char *term) {

1868 char tmp[PATH_MAX];

1869 int rv;

1870

1871 /* Look for term at this terminfo location, e.g., <terminfo>/x/xterm */

1872 if_err_return(rv, snprintf_(tmp, sizeof(tmp),

1873 "%s/%c/%s", path, term[0], term));

1874 if_ok_return(rv, read_terminfo_path(tmp));

1875

1876 #ifdef __APPLE__

1877 /* Try the Darwin equivalent path, e.g., <terminfo>/78/xterm */

1878 if_err_return(rv, snprintf_(tmp, sizeof(tmp),

1879 "%s/%x/%s", path, term[0], term));

1880 return read_terminfo_path(tmp);

1881 #endif

1882

1883 return TB_ERR;

1884 }

1885

1886 static int read_terminfo_path(const char *path) {

1887 size_t fsize;

1888 char *data;

1889 struct stat st;

1890 FILE *fp = fopen(path, "rb");

1891 if (!fp) {

1892 return TB_ERR;

1893 }

1894

1895 if (fstat(fileno(fp), &st) != 0) {

1896 fclose(fp);

1897 return TB_ERR;

1898 }

1899

1900 fsize = st.st_size;

1901 data = tb_malloc(fsize);

1902 if (!data) {

1903 fclose(fp);

1904 return TB_ERR;

1905 }

1906

1907 if (fread(data, 1, fsize, fp) != fsize) {

1908 fclose(fp);

1909 tb_free(data);

1910 return TB_ERR;

1911 }

1912

1913 global.terminfo = data;

1914 global.nterminfo = fsize;

1915

1916 fclose(fp);

1917 return TB_OK;

1918 }

1919

1920 static int parse_terminfo_caps(void) {

1921 /*

1922 * See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT"

1923 * for a description of this behavior.

1924 */

1925 int16_t *header;

1926 int bytes_per_int, align_offset, pos_str_offsets, pos_str_table, i;

1927

1928 /* Ensure there's at least a header's worth of data */

1929 if (global.nterminfo < 6) {

1930 return TB_ERR;

1931 }

1932

1933 header = (int16_t *)global.terminfo;

1934 /*

1935 * header[0] the magic number (octal 0432 or 01036)

1936 * header[1] the size, in bytes, of the names section

1937 * header[2] the number of bytes in the boolean section

1938 * header[3] the number of short integers in the numbers section

1939 * header[4] the number of offsets in the strings section

1940 * header[5] the size, in bytes, of the string table

1941 */

1942

1943 /* Legacy ints are 16-bit, extended ints are 32-bit */

1944 bytes_per_int = header[0] == 01036 ? 4 /* 32-bit */

1945 : 2; /* 16-bit */

1946

1947 /*

1948 * Between the boolean section and the number section, a null byte

1949 * will be inserted, if necessary, to ensure that the number section

1950 * begins on an even byte

1951 */

1952 align_offset = (header[1] + header[2]) % 2 != 0 ? 1 : 0;

1953

1954 pos_str_offsets =

1955 (6 * sizeof(int16_t)) /* header (12 bytes) */

1956 + header[1] /* length of names section */

1957 + header[2] /* length of boolean section */

1958 + align_offset +

1959 (header[3] * bytes_per_int); /* length of numbers section */

1960

1961 /* length of string offsets table */

1962 pos_str_table = pos_str_offsets + (header[4] * sizeof(int16_t));

1963

1964 /* Load caps */

1965 for (i = 0; i < TB_CAP__COUNT; i++) {

1966 const char *cap = get_terminfo_string(pos_str_offsets,

1967 pos_str_table, header[5],

1968 terminfo_cap_indexes[i]);

1969 if (!cap) {

1970 /* Something is not right */

1971 return TB_ERR;

1972 }

1973 global.caps[i] = cap;

1974 }

1975

1976 return TB_OK;

1977 }

1978

1979 static int load_builtin_caps(void) {

1980 int i, j;

1981 const char *term = getenv("TERM");

1982

1983 if (!term) {

1984 return TB_ERR_NO_TERM;

1985 }

1986

1987 /* Check for exact TERM match */

1988 for (i = 0; builtin_terms[i].name != NULL; i++) {

1989 if (strcmp(term, builtin_terms[i].name) == 0) {

1990 for (j = 0; j < TB_CAP__COUNT; j++) {

1991 global.caps[j] = builtin_terms[i].caps[j];

1992 }

1993 return TB_OK;

1994 }

1995 }

1996

1997 /* Check for partial TERM or alias match */

1998 for (i = 0; builtin_terms[i].name != NULL; i++) {

1999 if (strstr(term, builtin_terms[i].name) != NULL ||

2000 (*(builtin_terms[i].alias) != '\0' &&

2001 strstr(term, builtin_terms[i].alias) != NULL))

2002 {

2003 for (j = 0; j < TB_CAP__COUNT; j++) {

2004 global.caps[j] = builtin_terms[i].caps[j];

2005 }

2006 return TB_OK;

2007 }

2008 }

2009

2010 return TB_ERR_UNSUPPORTED_TERM;

2011 }

2012

2013 static const char *get_terminfo_string(int16_t str_offsets_pos,

2014 int16_t str_table_pos, int16_t str_table_len,

2015 int16_t str_index) {

2016 const int16_t *str_offset =

2017 (int16_t *)(global.terminfo + (int)str_offsets_pos +

2018 ((int)str_index * (int)sizeof(int16_t)));

2019 if (*str_offset < 0) {

2020 /* A negative indicates the cap is absent from this terminal */

2021 return "";

2022 }

2023 if (*str_offset >= str_table_len) {

2024 /* Invalid string offset */

2025 return NULL;

2026 }

2027 if (((size_t)((int)str_table_pos + (int)*str_offset)) >=

2028 global.nterminfo) {

2029 /* Truncated/corrupt terminfo? */

2030 return NULL;

2031 }

2032 return (const char *)(global.terminfo + (int)str_table_pos +

2033 (int)*str_offset);

2034 }

2035

2036 static int wait_event(struct tb_event *event, int timeout) {

2037 int rv;

2038 char buf[TB_OPT_READ_BUF];

2039 fd_set fds;

2040 struct timeval tv;

2041

2042 memset(event, 0, sizeof(*event));

2043 if_ok_return(rv, extract_event(event));

2044

2045 tv.tv_sec = timeout / 1000;

2046 tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;

2047

2048 do {

2049 int maxfd, select_rv, tty_has_events, resize_has_events;

2050 FD_ZERO(&fds);

2051 FD_SET(global.rfd, &fds);

2052 FD_SET(global.resize_pipefd[0], &fds);

2053

2054 maxfd = global.resize_pipefd[0] > global.rfd

2055 ? global.resize_pipefd[0]

2056 : global.rfd;

2057

2058 select_rv = select(maxfd + 1, &fds, NULL, NULL,

2059 (timeout < 0) ? NULL : &tv);

2060

2061 if (select_rv < 0) {

2062 /* Let EINTR/EAGAIN bubble up */

2063 global.last_errno = errno;

2064 return TB_ERR_POLL;

2065 } else if (select_rv == 0) {

2066 return TB_ERR_NO_EVENT;

2067 }

2068

2069 tty_has_events = (FD_ISSET(global.rfd, &fds));

2070 resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds));

2071

2072 if (tty_has_events) {

2073 ssize_t read_rv = read(global.rfd, buf, sizeof(buf));

2074 if (read_rv < 0) {

2075 global.last_errno = errno;

2076 return TB_ERR_READ;

2077 } else if (read_rv > 0) {

2078 bytebuf_nputs(&global.in, buf, read_rv);

2079 }

2080 }

2081

2082 if (resize_has_events) {

2083 int ignore = 0;

2084 read(global.resize_pipefd[0], &ignore, sizeof(ignore));

2085 /* TODO Harden against errors encountered mid-resize */

2086 if_err_return(rv, update_term_size());

2087 if_err_return(rv, resize_cellbufs());

2088 event->type = TB_EVENT_RESIZE;

2089 event->w = global.width;

2090 event->h = global.height;

2091 return TB_OK;

2092 }

2093

2094 memset(event, 0, sizeof(*event));

2095 if_ok_return(rv, extract_event(event));

2096 } while (timeout == -1);

2097

2098 return rv;

2099 }

2100

2101 static int extract_event(struct tb_event *event) {

2102 int rv;

2103 struct bytebuf_t *in = &global.in;

2104

2105 if (in->len == 0) {

2106 return TB_ERR;

2107 }

2108

2109 if (in->buf[0] == '\x1b') {

2110 /* Escape sequence? */

2111 /* In TB_INPUT_ESC,

2112 * skip if the buffer is a single escape char */

2113 if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) {

2114 if_ok_or_need_more_return(rv, extract_esc(event));

2115 }

2116

2117 /* Escape key? */

2118 if (global.input_mode & TB_INPUT_ESC) {

2119 event->type = TB_EVENT_KEY;

2120 event->ch = 0;

2121 event->key = TB_KEY_ESC;

2122 event->mod = 0;

2123 bytebuf_shift(in, 1);

2124 return TB_OK;

2125 }

2126

2127 /* Recurse for alt key */

2128 event->mod |= TB_MOD_ALT;

2129 bytebuf_shift(in, 1);

2130 return extract_event(event);

2131 }

2132

2133 /* ASCII control key? */

2134 if ((uint16_t)in->buf[0] < TB_KEY_SPACE ||

2135 in->buf[0] == TB_KEY_BACKSPACE2)

2136 {

2137 event->type = TB_EVENT_KEY;

2138 event->ch = 0;

2139 event->key = (uint16_t)in->buf[0];

2140 event->mod |= TB_MOD_CTRL;

2141 bytebuf_shift(in, 1);

2142 return TB_OK;

2143 }

2144

2145 /* UTF-8? */

2146 if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) {

2147 event->type = TB_EVENT_KEY;

2148 tb_utf8_char_to_unicode(&event->ch, in->buf);

2149 event->key = 0;

2150 bytebuf_shift(in, tb_utf8_char_length(in->buf[0]));

2151 return TB_OK;

2152 }

2153

2154 /* Need more input */

2155 return TB_ERR;

2156 }

2157

2158 static int extract_esc(struct tb_event *event) {

2159 int rv;

2160 if_ok_or_need_more_return(rv, extract_esc_user(event, 0));

2161 if_ok_or_need_more_return(rv, extract_esc_cap(event));

2162 if_ok_or_need_more_return(rv, extract_esc_mouse(event));

2163 if_ok_or_need_more_return(rv, extract_esc_user(event, 1));

2164 return TB_ERR;

2165 }

2166

2167 static int extract_esc_user(struct tb_event *event, int is_post) {

2168 int rv;

2169 size_t consumed = 0;

2170 struct bytebuf_t *in = &global.in;

2171 int (*fn)(struct tb_event *, size_t *);

2172

2173 fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre;

2174

2175 if (!fn) {

2176 return TB_ERR;

2177 }

2178

2179 rv = fn(event, &consumed);

2180 if (rv == TB_OK) {

2181 bytebuf_shift(in, consumed);

2182 }

2183

2184 if_ok_or_need_more_return(rv, rv);

2185 return TB_ERR;

2186 }

2187

2188 static int extract_esc_cap(struct tb_event *event) {

2189 int rv;

2190 struct bytebuf_t *in = &global.in;

2191 struct cap_trie_t *node;

2192 size_t depth;

2193

2194 if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth));

2195 if (node->is_leaf) {

2196 /* Found a leaf node */

2197 event->type = TB_EVENT_KEY;

2198 event->ch = 0;

2199 event->key = node->key;

2200 event->mod = node->mod;

2201 bytebuf_shift(in, depth);

2202 return TB_OK;

2203 } else if (node->nchildren > 0 && in->len <= depth) {

2204 /* Found a branch node (not enough input) */

2205 return TB_ERR_NEED_MORE;

2206 }

2207

2208 return TB_ERR;

2209 }

2210

2211 static int extract_esc_mouse(struct tb_event *event) {

2212 struct bytebuf_t *in = &global.in;

2213

2214 enum type { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX };

2215

2216 char *cmp[TYPE_MAX];

2217

2218 enum type type = 0;

2219 int ret = TB_ERR;

2220 size_t buf_shift = 0;

2221

2222 /* X10 mouse encoding, the simplest one */

2223 /* \x1b [ M Cb Cx Cy */

2224 cmp[TYPE_VT200] = "\x1b[M";

2225 /* xterm 1006 extended mode or urxvt 1015 extended mode */

2226 /* xterm: \x1b [ < Cb ; Cx ; Cy (M or m) */

2227 cmp[TYPE_1006] = "\x1b[<";

2228 /* urxvt: \x1b [ Cb ; Cx ; Cy M */

2229 cmp[TYPE_1015] = "\x1b[";

2230

2231 /* Unrolled at compile-time (probably) */

2232 for (; type < TYPE_MAX; type++) {

2233 size_t size = strlen(cmp[type]);

2234

2235 if (in->len >= size &&

2236 (strncmp(cmp[type], in->buf, size)) == 0) {

2237 break;

2238 }

2239 }

2240

2241 if (type == TYPE_MAX) {

2242 ret = TB_ERR; /* No match */

2243 return ret;

2244 }

2245

2246 switch (type) {

2247 case TYPE_VT200:

2248 {

2249 int b, fail;

2250 if (in->len < 6) break;

2251

2252 b = in->buf[3] - 0x20;

2253 fail = 0;

2254

2255 switch (b & 3) {

2256 case 0:

2257 event->key = ((b & 64) != 0) ?

2258 TB_KEY_MOUSE_WHEEL_UP :

2259 TB_KEY_MOUSE_LEFT;

2260 break;

2261 case 1:

2262 event->key = ((b & 64) != 0) ?

2263 TB_KEY_MOUSE_WHEEL_DOWN :

2264 TB_KEY_MOUSE_MIDDLE;

2265 break;

2266 case 2:

2267 event->key = TB_KEY_MOUSE_RIGHT;

2268 break;

2269 case 3:

2270 event->key = TB_KEY_MOUSE_RELEASE;

2271 break;

2272 default:

2273 ret = TB_ERR;

2274 fail = 1;

2275 break;

2276 }

2277

2278 if (!fail) {

2279 if ((b & 32) != 0) {

2280 event->mod |= TB_MOD_MOTION;

2281 }

2282

2283 /* the coord is 1,1 for upper left */

2284 event->x = ((uint8_t)in->buf[4]) - 0x21;

2285 event->y = ((uint8_t)in->buf[5]) - 0x21;

2286

2287 ret = TB_OK;

2288 }

2289

2290 buf_shift = 6;

2291 }

2292 break;

2293 case TYPE_1006:

2294 /* fallthrough */

2295 case TYPE_1015:

2296 {

2297 size_t i = 0;

2298

2299 enum {

2300 FIRST_M = 0,

2301 FIRST_SEMICOLON,

2302 LAST_SEMICOLON,

2303 FIRST_LAST_MAX

2304 };

2305

2306 size_t indices[FIRST_LAST_MAX] = {

2307 index_fail,

2308 index_fail,

2309 index_fail

2310 };

2311 int m_is_capital = 0;

2312

2313 for (i = 0; i < in->len; i++) {

2314 if (in->buf[i] != ';') {

2315 if (indices[FIRST_SEMICOLON] == index_fail) {

2316 indices[FIRST_SEMICOLON] = i;

2317 } else {

2318 indices[LAST_SEMICOLON] = i;

2319 }

2320 } else if (indices[FIRST_M] == index_fail) {

2321 if (in->buf[i] == 'm' || in->buf[i] == 'M') {

2322 m_is_capital = (in->buf[i] == 'M');

2323 indices[FIRST_M] = i;

2324 }

2325 }

2326 }

2327

2328 if (indices[FIRST_M] == index_fail ||

2329 indices[FIRST_SEMICOLON] == index_fail ||

2330 indices[LAST_SEMICOLON] == index_fail) {

2331 ret = TB_ERR;

2332 } else {

2333 int start = (type == TYPE_1015 ? 2 : 3);

2334 int fail = 0;

2335

2336 unsigned n1 = strtoul(&in->buf[start], NULL, 10);

2337 unsigned n2 = strtoul(

2338 &in->buf[indices[FIRST_SEMICOLON] + 1],

2339 NULL, 10);

2340 unsigned n3 = strtoul(

2341 &in->buf[indices[LAST_SEMICOLON] + 1],

2342 NULL, 10);

2343

2344 if (type == TYPE_1015) {

2345 n1 -= 0x20;

2346 }

2347

2348

2349 switch (n1 & 3) {

2350 case 0:

2351 event->key = ((n1 & 64) != 0)

2352 ? TB_KEY_MOUSE_WHEEL_UP

2353 : TB_KEY_MOUSE_LEFT;

2354 break;

2355 case 1:

2356 event->key = ((n1 & 64) != 0)

2357 ? TB_KEY_MOUSE_WHEEL_DOWN

2358 : TB_KEY_MOUSE_MIDDLE;

2359 break;

2360 case 2:

2361 event->key = TB_KEY_MOUSE_RIGHT;

2362 break;

2363 case 3:

2364 event->key = TB_KEY_MOUSE_RELEASE;

2365 break;

2366 default:

2367 ret = TB_ERR;

2368 fail = 1;

2369 break;

2370 }

2371

2372 buf_shift = in->len;

2373

2374 if (!fail) {

2375 if (!m_is_capital) {

2376 /* on xterm mouse release is signaled

2377 * by lowercase m */

2378 event->key = TB_KEY_MOUSE_RELEASE;

2379 }

2380

2381 if ((n1 & 32) != 0) {

2382 event->mod |= TB_MOD_MOTION;

2383 }

2384

2385 event->x = ((uint8_t)n2) - 1;

2386 event->y = ((uint8_t)n3) - 1;

2387

2388 ret = TB_OK;

2389 }

2390 }

2391 }

2392 break;

2393 case TYPE_MAX:

2394 ret = TB_ERR;

2395 }

2396

2397 if (buf_shift > 0) {

2398 bytebuf_shift(in, buf_shift);

2399 }

2400

2401 if (ret == TB_OK) {

2402 event->type = TB_EVENT_MOUSE;

2403 }

2404

2405 return ret;

2406 }

2407

2408 static int resize_cellbufs(void) {

2409 int rv;

2410 if_err_return(rv, cellbuf_resize(&global.back, global.width,

2411 global.height));

2412 if_err_return(rv, cellbuf_resize(&global.front, global.width,

2413 global.height));

2414 if_err_return(rv, cellbuf_clear(&global.front));

2415 if_err_return(rv, send_clear());

2416 return TB_OK;

2417 }

2418

2419 static void handle_resize(int sig) {

2420 int errno_copy = errno;

2421 write(global.resize_pipefd[1], &sig, sizeof(sig));

2422 errno = errno_copy;

2423 }

2424

2425 static int send_attr(uintattr_t fg, uintattr_t bg) {

2426 int rv;

2427 uintattr_t attr_bold, attr_blink, attr_italic,

2428 attr_underline, attr_reverse, attr_default;

2429 uintattr_t cfg, cbg;

2430

2431 if (fg == global.last_fg && bg == global.last_bg) {

2432 return TB_OK;

2433 }

2434

2435 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]));

2436

2437 switch (global.output_mode) {

2438 default:

2439 case TB_OUTPUT_NORMAL:

2440 cfg = fg & 0x0f;

2441 cbg = bg & 0x0f;

2442 break;

2443

2444 case TB_OUTPUT_256:

2445 cfg = fg & 0xff;

2446 cbg = bg & 0xff;

2447 break;

2448

2449 case TB_OUTPUT_216:

2450 cfg = fg & 0xff;

2451 cbg = bg & 0xff;

2452 if (cfg > 216)

2453 cfg = 216;

2454 if (cbg > 216)

2455 cbg = 216;

2456 cfg += 0x0f;

2457 cbg += 0x0f;

2458 break;

2459

2460 case TB_OUTPUT_GRAYSCALE:

2461 cfg = fg & 0xff;

2462 cbg = bg & 0xff;

2463 if (cfg > 24)

2464 cfg = 24;

2465 if (cbg > 24)

2466 cbg = 24;

2467 cfg += 0xe7;

2468 cbg += 0xe7;

2469 break;

2470

2471 #ifdef TB_OPT_TRUECOLOR

2472 case TB_OUTPUT_TRUECOLOR:

2473 cfg = fg & 0xffffff;

2474 cbg = bg & 0xffffff;

2475 break;

2476 #endif

2477 }

2478

2479 #ifdef TB_OPT_TRUECOLOR

2480 if (global.output_mode == TB_OUTPUT_TRUECOLOR) {

2481 attr_bold = TB_TRUECOLOR_BOLD;

2482 attr_blink = TB_TRUECOLOR_BLINK;

2483 attr_italic = TB_TRUECOLOR_ITALIC;

2484 attr_underline = TB_TRUECOLOR_UNDERLINE;

2485 attr_reverse = TB_TRUECOLOR_REVERSE;

2486 attr_default = TB_TRUECOLOR_DEFAULT;

2487 } else

2488 #endif

2489 {

2490 attr_bold = TB_BOLD;

2491 attr_blink = TB_BLINK;

2492 attr_italic = TB_ITALIC;

2493 attr_underline = TB_UNDERLINE;

2494 attr_reverse = TB_REVERSE;

2495 attr_default = TB_DEFAULT;

2496 }

2497

2498 /* For convenience (and some back compat), interpret 0 as default in

2499 * some modes */

2500 if (global.output_mode == TB_OUTPUT_NORMAL ||

2501 global.output_mode == TB_OUTPUT_216 ||

2502 global.output_mode == TB_OUTPUT_GRAYSCALE)

2503 {

2504 if ((fg & 0xff) == 0)

2505 fg |= attr_default;

2506 if ((bg & 0xff) == 0)

2507 bg |= attr_default;

2508 }

2509

2510 if (fg & attr_bold)

2511 if_err_return(rv, bytebuf_puts(&global.out,

2512 global.caps[TB_CAP_BOLD]));

2513

2514 if (fg & attr_blink)

2515 if_err_return(rv, bytebuf_puts(&global.out,

2516 global.caps[TB_CAP_BLINK]));

2517

2518 if (fg & attr_underline)

2519 if_err_return(rv, bytebuf_puts(&global.out,

2520 global.caps[TB_CAP_UNDERLINE]));

2521

2522 if (fg & attr_italic)

2523 if_err_return(rv, bytebuf_puts(&global.out,

2524 global.caps[TB_CAP_ITALIC]));

2525

2526 if ((fg & attr_reverse) || (bg & attr_reverse))

2527 if_err_return(rv, bytebuf_puts(&global.out,

2528 global.caps[TB_CAP_REVERSE]));

2529

2530 if_err_return(rv, send_sgr(cfg, cbg, fg & attr_default,

2531 bg & attr_default));

2532

2533 global.last_fg = fg;

2534 global.last_bg = bg;

2535

2536 return TB_OK;

2537 }

2538

2539 static int send_sgr(uintattr_t cfg, uintattr_t cbg, uintattr_t fg_is_default,

2540 uintattr_t bg_is_default) {

2541 int rv;

2542 char nbuf[32];

2543

2544 if (fg_is_default && bg_is_default) {

2545 return TB_OK;

2546 }

2547

2548 switch (global.output_mode) {

2549 default:

2550 case TB_OUTPUT_NORMAL:

2551 send_literal(rv, "\x1b[");

2552 if (!fg_is_default) {

2553 send_literal(rv, "3");

2554 send_num(rv, nbuf, cfg - 1);

2555 if (!bg_is_default) {

2556 send_literal(rv, ";");

2557 }

2558 }

2559 if (!bg_is_default) {

2560 send_literal(rv, "4");

2561 send_num(rv, nbuf, cbg - 1);

2562 }

2563 send_literal(rv, "m");

2564 break;

2565

2566 case TB_OUTPUT_256:

2567 case TB_OUTPUT_216:

2568 case TB_OUTPUT_GRAYSCALE:

2569 send_literal(rv, "\x1b[");

2570 if (!fg_is_default) {

2571 send_literal(rv, "38;5;");

2572 send_num(rv, nbuf, cfg);

2573 if (!bg_is_default) {

2574 send_literal(rv, ";");

2575 }

2576 }

2577 if (!bg_is_default) {

2578 send_literal(rv, "48;5;");

2579 send_num(rv, nbuf, cbg);

2580 }

2581 send_literal(rv, "m");

2582 break;

2583

2584 #ifdef TB_OPT_TRUECOLOR

2585 case TB_OUTPUT_TRUECOLOR:

2586 send_literal(rv, "\x1b[");

2587 if (!fg_is_default) {

2588 send_literal(rv, "38;2;");

2589 send_num(rv, nbuf, (cfg >> 16) & 0xff);

2590 send_literal(rv, ";");

2591 send_num(rv, nbuf, (cfg >> 8) & 0xff);

2592 send_literal(rv, ";");

2593 send_num(rv, nbuf, cfg & 0xff);

2594 if (!bg_is_default) {

2595 send_literal(rv, ";");

2596 }

2597 }

2598 if (!bg_is_default) {

2599 send_literal(rv, "48;2;");

2600 send_num(rv, nbuf, (cbg >> 16) & 0xff);

2601 send_literal(rv, ";");

2602 send_num(rv, nbuf, (cbg >> 8) & 0xff);

2603 send_literal(rv, ";");

2604 send_num(rv, nbuf, cbg & 0xff);

2605 }

2606 send_literal(rv, "m");

2607 break;

2608 #endif

2609 }

2610 return TB_OK;

2611 }

2612

2613 static int send_cursor_if(int x, int y) {

2614 int rv;

2615 char nbuf[32];

2616 if (x < 0 || y < 0) {

2617 return TB_OK;

2618 }

2619 send_literal(rv, "\x1b[");

2620 send_num(rv, nbuf, y + 1);

2621 send_literal(rv, ";");

2622 send_num(rv, nbuf, x + 1);

2623 send_literal(rv, "H");

2624 return TB_OK;

2625 }

2626

2627 static int send_char(int x, int y, uint32_t ch) {

2628 return send_cluster(x, y, &ch, 1);

2629 }

2630

2631 static int send_cluster(int x, int y, uint32_t *ch, size_t nch) {

2632 int rv, i;

2633 char abuf[8];

2634

2635 if (global.last_x != x - 1 || global.last_y != y) {

2636 if_err_return(rv, send_cursor_if(x, y));

2637 }

2638 global.last_x = x;

2639 global.last_y = y;

2640

2641 for (i = 0; i < (int)nch; i++) {

2642 uint32_t ach = *(ch + i);

2643 int aw = tb_utf8_unicode_to_char(abuf, ach);

2644 if (!ach) {

2645 abuf[0] = ' ';

2646 }

2647 if_err_return(rv,

2648 bytebuf_nputs(&global.out, abuf, (size_t)aw));

2649 }

2650

2651 return TB_OK;

2652 }

2653

2654 static int convert_num(uint32_t num, char *buf) {

2655 int i, l = 0;

2656 char ch;

2657 do {

2658 /* '0' = 48; 48 + num%10 < 58 < MAX_8bitCHAR */

2659 buf[l++] = (char)('0' + (num % 10));

2660 num /= 10;

2661 } while (num);

2662 for (i = 0; i < l / 2; i++) {

2663 ch = buf[i];

2664 buf[i] = buf[l - 1 - i];

2665 buf[l - 1 - i] = ch;

2666 }

2667 return l;

2668 }

2669

2670 static int cell_cmp(struct tb_cell *a, struct tb_cell *b) {

2671 if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) {

2672 return 1;

2673 }

2674 #ifdef TB_OPT_EGC

2675 if (a->nech != b->nech) {

2676 return 1;

2677 } else if (a->nech > 0) { /* a->nech == b->nech */

2678 return memcmp(a->ech, b->ech, a->nech);

2679 }

2680 #endif

2681 return 0;

2682 }

2683

2684 static int cell_copy(struct tb_cell *dst, struct tb_cell *src) {

2685 #ifdef TB_OPT_EGC

2686 if (src->nech > 0) {

2687 return cell_set(dst, src->ech, src->nech, src->fg, src->bg);

2688 }

2689 #endif

2690 return cell_set(dst, &src->ch, 1, src->fg, src->bg);

2691 }

2692

2693 static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,

2694 uintattr_t fg, uintattr_t bg) {

2695 cell->ch = ch ? *ch : 0;

2696 cell->fg = fg;

2697 cell->bg = bg;

2698 #ifdef TB_OPT_EGC

2699 if (nch <= 1) {

2700 cell->nech = 0;

2701 } else {

2702 int rv;

2703 if_err_return(rv, cell_reserve_ech(cell, nch + 1));

2704 memcpy(cell->ech, ch, nch);

2705 cell->ech[nch] = '\0';

2706 cell->nech = nch;

2707 }

2708 #else

2709 (void)nch;

2710 (void)cell_reserve_ech;

2711 #endif

2712 return TB_OK;

2713 }

2714

2715 static int cell_reserve_ech(struct tb_cell *cell, size_t n) {

2716 #ifdef TB_OPT_EGC

2717 if (cell->cech >= n) {

2718 return TB_OK;

2719 }

2720 if (!(cell->ech = tb_realloc(cell->ech, n * sizeof(cell->ch)))) {

2721 return TB_ERR_MEM;

2722 }

2723 cell->cech = n;

2724 return TB_OK;

2725 #else

2726 (void)cell;

2727 (void)n;

2728 return TB_ERR;

2729 #endif

2730 }

2731

2732 static int cell_free(struct tb_cell *cell) {

2733 #ifdef TB_OPT_EGC

2734 if (cell->ech) {

2735 tb_free(cell->ech);

2736 }

2737 #endif

2738 memset(cell, 0, sizeof(*cell));

2739 return TB_OK;

2740 }

2741

2742 static int cellbuf_init(struct cellbuf_t *c, int w, int h) {

2743 c->cells = tb_malloc(sizeof(struct tb_cell) * w * h);

2744 if (!c->cells) {

2745 return TB_ERR_MEM;

2746 }

2747 memset(c->cells, 0, sizeof(struct tb_cell) * w * h);

2748 c->width = w;

2749 c->height = h;

2750 return TB_OK;

2751 }

2752

2753 static int cellbuf_free(struct cellbuf_t *c) {

2754 if (c->cells) {

2755 int i;

2756 for (i = 0; i < c->width * c->height; i++) {

2757 cell_free(&c->cells[i]);

2758 }

2759 tb_free(c->cells);

2760 }

2761 memset(c, 0, sizeof(*c));

2762 return TB_OK;

2763 }

2764

2765 static int cellbuf_clear(struct cellbuf_t *c) {

2766 int rv, i;

2767 uint32_t space = (uint32_t)' ';

2768 for (i = 0; i < c->width * c->height; i++) {

2769 if_err_return(rv, cell_set(

2770 &c->cells[i], &space, 1, global.fg, global.bg));

2771

2772 }

2773 return TB_OK;

2774 }

2775

2776 static int cellbuf_get(struct cellbuf_t *c, int x, int y,

2777 struct tb_cell **out) {

2778 if (x < 0 || x >= c->width || y < 0 || y >= c->height) {

2779 *out = NULL;

2780 return TB_ERR_OUT_OF_BOUNDS;

2781 }

2782 *out = &c->cells[(y * c->width) + x];

2783 return TB_OK;

2784 }

2785

2786 static int cellbuf_resize(struct cellbuf_t *c, int w, int h) {

2787 int rv;

2788

2789 int ow = c->width;

2790 int oh = c->height;

2791 int minw, minh, x, y;

2792 struct tb_cell *prev;

2793

2794 if (ow == w && oh == h) {

2795 return TB_OK;

2796 }

2797

2798 w = w < 1 ? 1 : w;

2799 h = h < 1 ? 1 : h;

2800

2801 minw = (w < ow) ? w : ow;

2802 minh = (h < oh) ? h : oh;

2803

2804 prev = c->cells;

2805

2806 if_err_return(rv, cellbuf_init(c, w, h));

2807 if_err_return(rv, cellbuf_clear(c));

2808

2809 x = 0;

2810 while (x < minw) {

2811 y = 0;

2812 while (y < minh) {

2813 struct tb_cell *src, *dst;

2814 src = &prev[(y * ow) + x];

2815 if_err_return(rv, cellbuf_get(c, x, y, &dst));

2816 if_err_return(rv, cell_copy(dst, src));

2817 y++;

2818 }

2819 x++;

2820 }

2821

2822 tb_free(prev);

2823

2824 return TB_OK;

2825 }

2826

2827 static int bytebuf_puts(struct bytebuf_t *b, const char *str) {

2828 return bytebuf_nputs(b, str, (size_t)strlen(str));

2829 }

2830

2831 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr) {

2832 int rv;

2833 if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1));

2834 memcpy(b->buf + b->len, str, nstr);

2835 b->len += nstr;

2836 b->buf[b->len] = '\0';

2837 return TB_OK;

2838 }

2839

2840 static int bytebuf_shift(struct bytebuf_t *b, size_t n) {

2841 size_t nmove;

2842 if (n > b->len) {

2843 n = b->len;

2844 }

2845 nmove = b->len - n;

2846 memmove(b->buf, b->buf + n, nmove);

2847 b->len -= n;

2848 return TB_OK;

2849 }

2850

2851 static int bytebuf_flush(struct bytebuf_t *b, int fd) {

2852 ssize_t write_rv;

2853 if (b->len <= 0) {

2854 return TB_OK;

2855 }

2856 write_rv = write(fd, b->buf, b->len);

2857 if (write_rv < 0 || (size_t)write_rv != b->len) {

2858 /* Note, errno will be 0 on partial write */

2859 global.last_errno = errno;

2860 return TB_ERR;

2861 }

2862 b->len = 0;

2863 return TB_OK;

2864 }

2865

2866 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz) {

2867 char *newbuf;

2868 size_t newcap;

2869

2870 if (b->cap >= sz) {

2871 return TB_OK;

2872 }

2873 newcap = b->cap > 0 ? b->cap : 1;

2874 while (newcap < sz) {

2875 newcap *= 2;

2876 }

2877 if (b->buf) {

2878 newbuf = tb_realloc(b->buf, newcap);

2879 } else {

2880 newbuf = tb_malloc(newcap);

2881 }

2882 if (!newbuf) {

2883 return TB_ERR_MEM;

2884 }

2885 b->buf = newbuf;

2886 b->cap = newcap;

2887 return TB_OK;

2888 }

2889

2890 static int bytebuf_free(struct bytebuf_t *b) {

2891 if (b->buf) {

2892 tb_free(b->buf);

2893 }

2894 memset(b, 0, sizeof(*b));

2895 return TB_OK;

2896 }

2897