💾 Archived View for gemini.rmf-dev.com › repo › Vaati › mz › files › 2d21324ddbd7d1d5a8ba35d199387ec… captured on 2023-05-24 at 18:40:21. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-04-19)

➡️ 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 #ifndef PATH_MAX

69 #define PATH_MAX 1024

70 #endif

71

72 void cfmakeraw(struct termios *t) {

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

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

75 t->c_oflag &= ~OPOST;

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

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

78 t->c_cflag |= CS8;

79 }

80

81 #define index_fail ((size_t)-1)

82

83 #define if_err_return(rv, expr) \

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

85 return (rv)

86 #define if_err_break(rv, expr) \

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

88 break

89 #define if_ok_return(rv, expr) \

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

91 return (rv)

92 #define if_ok_or_need_more_return(rv, expr) \

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

94 return (rv)

95

96 #define send_literal(rv, a) \

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

98

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

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

101 convert_num((n), (nbuf))))

102

103

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

105 va_list args;

106 int rv;

107

108 va_start(args, fmt);

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

110 va_end(args);

111

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

113 return TB_ERR;

114 return TB_OK;

115 }

116

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

118

119 struct bytebuf_t {

120 char *buf;

121 size_t len;

122 size_t cap;

123 };

124

125 struct cellbuf_t {

126 int width;

127 int height;

128 struct tb_cell *cells;

129 };

130

131 struct cap_trie_t {

132 char c;

133 struct cap_trie_t *children;

134 size_t nchildren;

135 int is_leaf;

136 uint16_t key;

137 uint8_t mod;

138 };

139

140 struct tb_global_t {

141 int ttyfd;

142 int rfd;

143 int wfd;

144 int ttyfd_open;

145 int resize_pipefd[2];

146 int width;

147 int height;

148 int cursor_x;

149 int cursor_y;

150 int last_x;

151 int last_y;

152 uintattr_t fg;

153 uintattr_t bg;

154 uintattr_t last_fg;

155 uintattr_t last_bg;

156 int input_mode;

157 int output_mode;

158 char *terminfo;

159 size_t nterminfo;

160 const char *caps[TB_CAP__COUNT];

161 struct cap_trie_t cap_trie;

162 struct bytebuf_t in;

163 struct bytebuf_t out;

164 struct cellbuf_t back;

165 struct cellbuf_t front;

166 struct termios orig_tios;

167 int has_orig_tios;

168 int last_errno;

169 int initialized;

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

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

172 char errbuf[1024];

173 };

174

175 static struct tb_global_t global = {0};

176

177 /* BEGIN codegen c */

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

179

180 static const int16_t terminfo_cap_indexes[] = {

181 66, /* kf1 (TB_CAP_F1) */

182 68, /* kf2 (TB_CAP_F2) */

183 69, /* kf3 (TB_CAP_F3) */

184 70, /* kf4 (TB_CAP_F4) */

185 71, /* kf5 (TB_CAP_F5) */

186 72, /* kf6 (TB_CAP_F6) */

187 73, /* kf7 (TB_CAP_F7) */

188 74, /* kf8 (TB_CAP_F8) */

189 75, /* kf9 (TB_CAP_F9) */

190 67, /* kf10 (TB_CAP_F10) */

191 216, /* kf11 (TB_CAP_F11) */

192 217, /* kf12 (TB_CAP_F12) */

193 77, /* kich1 (TB_CAP_INSERT) */

194 59, /* kdch1 (TB_CAP_DELETE) */

195 76, /* khome (TB_CAP_HOME) */

196 164, /* kend (TB_CAP_END) */

197 82, /* kpp (TB_CAP_PGUP) */

198 81, /* knp (TB_CAP_PGDN) */

199 87, /* kcuu1 (TB_CAP_ARROW_UP) */

200 61, /* kcud1 (TB_CAP_ARROW_DOWN) */

201 79, /* kcub1 (TB_CAP_ARROW_LEFT) */

202 83, /* kcuf1 (TB_CAP_ARROW_RIGHT) */

203 148, /* kcbt (TB_CAP_BACK_TAB) */

204 28, /* smcup (TB_CAP_ENTER_CA) */

205 40, /* rmcup (TB_CAP_EXIT_CA) */

206 16, /* cnorm (TB_CAP_SHOW_CURSOR) */

207 13, /* civis (TB_CAP_HIDE_CURSOR) */

208 5, /* clear (TB_CAP_CLEAR_SCREEN) */

209 39, /* sgr0 (TB_CAP_SGR0) */

210 36, /* smul (TB_CAP_UNDERLINE) */

211 27, /* bold (TB_CAP_BOLD) */

212 26, /* blink (TB_CAP_BLINK) */

213 311, /* sitm (TB_CAP_ITALIC) */

214 34, /* rev (TB_CAP_REVERSE) */

215 89, /* smkx (TB_CAP_ENTER_KEYPAD) */

216 88, /* rmkx (TB_CAP_EXIT_KEYPAD) */

217 };

218

219 /* xterm */

220 static const char *xterm_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

257 };

258

259 /* linux */

260 static const char *linux_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

297 };

298

299 /* screen */

300 static const char *screen_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

337 };

338

339 /* rxvt-256color */

340 static const char *rxvt_256color_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

377 };

378

379 /* rxvt-unicode */

380 static const char *rxvt_unicode_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

417 };

418

419 /* Eterm */

420 static const char *eterm_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

457 };

458

459 static struct {

460 const char *name;

461 const char **caps;

462 const char *alias;

463 } builtin_terms[] = {

464 {"xterm", xterm_caps, "" },

465 {"linux", linux_caps, "" },

466 {"screen", screen_caps, "tmux"},

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

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

469 {"Eterm", eterm_caps, "" },

470 {NULL, NULL, NULL },

471 };

472

473 /* END codegen c */

474 #define TB_MOD_ALL TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT

475

476 static struct {

477 const char *cap;

478 const uint16_t key;

479 const uint8_t mod;

480 } builtin_mod_caps[] = {

481 /* xterm arrows */

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

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

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

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

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

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

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

489

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

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

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

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

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

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

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

497

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

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

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

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

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

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

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

505

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

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

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

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

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

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

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

513

514 /* xterm keys */

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

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

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

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

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

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

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

522

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

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

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

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

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

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

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

530

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

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

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

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

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

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

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

538

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

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

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

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

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

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

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

546

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

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

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

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

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

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

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

554

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

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

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

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

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

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

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

562

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

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

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

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

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

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

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

570

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

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

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

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

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

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

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

578

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

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

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

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

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

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

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

586

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

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

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

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

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

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

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

594

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

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

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

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

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

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

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

602

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

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

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

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

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

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

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

610

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

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

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

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

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

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

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

618

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

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

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

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

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

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

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

626

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

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

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

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

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

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

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

634

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

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

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

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

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

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

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

642

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

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

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

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

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

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

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

650

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

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

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

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

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

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

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

658

659 /* rxvt arrows */

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

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

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

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

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

665

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

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

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

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

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

671

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

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

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

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

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

677

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

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

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

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

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

683

684 /* rxvt keys */

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

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

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

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

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

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

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

692

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

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

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

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

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

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

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

700

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

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

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

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

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

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

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

708

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

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

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

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

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

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

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

716

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

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

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

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

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

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

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

724

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

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

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

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

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

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

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

732

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

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

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

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

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

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

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

740

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

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

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

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

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

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

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

748

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

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

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

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

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

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

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

756

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

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

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

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

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

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

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

764

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

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

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

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

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

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

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

772

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

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

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

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

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

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

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

780

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

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

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

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

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

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

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

788

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

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

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

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

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

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

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

796

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

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

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

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

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

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

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

804

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

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

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

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

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

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

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

812

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

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

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

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

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

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

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

820

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

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

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

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

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

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

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

828

829 /* linux console/putty arrows */

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

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

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

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

834

835 /* more putty arrows */

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

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

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

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

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

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

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

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

844

845 {NULL, 0, 0 },

846 };

847

848 static const unsigned char utf8_length[256] = {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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

853 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

854 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

855 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

856 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

857 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,

858 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5,

859 5, 6, 6, 1, 1};

860

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

862

863 static int tb_reset(void);

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

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

866 static int init_term_attrs(void);

867 static int init_term_caps(void);

868 static int init_cap_trie(void);

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

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

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

872 static int cap_trie_deinit(struct cap_trie_t *node);

873 static int init_resize_handler(void);

874 static int send_init_escape_codes(void);

875 static int send_clear(void);

876 static int update_term_size(void);

877 static int update_term_size_via_esc(void);

878 static int init_cellbuf(void);

879 static int tb_deinit(void);

880 static int load_terminfo(void);

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

882 static int read_terminfo_path(const char *path);

883 static int parse_terminfo_caps(void);

884 static int load_builtin_caps(void);

885 static const char *get_terminfo_string(int16_t str_offsets_pos,

886 int16_t str_table_pos, int16_t str_table_len,

887 int16_t str_index);

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

889 static int extract_event(struct tb_event *event);

890 static int extract_esc(struct tb_event *event);

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

892 static int extract_esc_cap(struct tb_event *event);

893 static int extract_esc_mouse(struct tb_event *event);

894 static int resize_cellbufs(void);

895 static void handle_resize(int sig);

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

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

898 uintattr_t bg_is_default);

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

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

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

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

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

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

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

906 uintattr_t fg, uintattr_t bg);

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

908 static int cell_free(struct tb_cell *cell);

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

910 static int cellbuf_free(struct cellbuf_t *c);

911 static int cellbuf_clear(struct cellbuf_t *c);

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

913 struct tb_cell **out);

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

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

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

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

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

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

920 static int bytebuf_free(struct bytebuf_t *b);

921

922 int tb_init(void) {

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

924 }

925

926 int tb_init_file(const char *path) {

927

928 int ttyfd;

929

930 if (global.initialized) {

931 return TB_ERR_INIT_ALREADY;

932 }

933 ttyfd = open(path, O_RDWR);

934 if (ttyfd < 0) {

935 global.last_errno = errno;

936 return TB_ERR_INIT_OPEN;

937 }

938 global.ttyfd_open = 1;

939 return tb_init_fd(ttyfd);

940 }

941

942 int tb_init_fd(int ttyfd) {

943 return tb_init_rwfd(ttyfd, ttyfd);

944 }

945

946 int tb_init_rwfd(int rfd, int wfd) {

947 int rv;

948 const char *term;

949 const char *retry_with = "xterm";

950

951 tb_reset();

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

953 global.rfd = rfd;

954 global.wfd = wfd;

955

956 term = getenv("TERM");

957 if (!term) {

958 term = retry_with;

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

960 }

961

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

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

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

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

966 }

967

968 do {

969 retry:

970 if_err_break(rv, init_term_attrs());

971 if_err_break(rv, init_term_caps());

972 if_err_break(rv, init_cap_trie());

973 if_err_break(rv, init_resize_handler());

974 if_err_break(rv, send_init_escape_codes());

975 if_err_break(rv, send_clear());

976 if_err_break(rv, update_term_size());

977 if_err_break(rv, init_cellbuf());

978 global.initialized = 1;

979 } while (0);

980

981 if (rv != TB_OK) {

982 if (strcmp(term, retry_with)) {

983 setenv("TERM", retry_with, 1);

984 term = retry_with;

985 goto retry;

986 }

987 tb_deinit();

988 }

989

990 return rv;

991 }

992

993 int tb_shutdown(void) {

994 if_not_init_return();

995 tb_deinit();

996 return TB_OK;

997 }

998

999 int tb_width(void) {

1000 if_not_init_return();

1001 return global.width;

1002 }

1003

1004 int tb_height(void) {

1005 if_not_init_return();

1006 return global.height;

1007 }

1008

1009 int tb_clear(void) {

1010 if_not_init_return();

1011 return cellbuf_clear(&global.back);

1012 }

1013

1014 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) {

1015 if_not_init_return();

1016 global.fg = fg;

1017 global.bg = bg;

1018 return TB_OK;

1019 }

1020

1021 int tb_present(void) {

1022

1023 int rv, x, y, i;

1024

1025 if_not_init_return();

1026

1027 global.last_x = -1;

1028 global.last_y = -1;

1029

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

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

1032 struct tb_cell *back, *front;

1033 int w;

1034 if_err_return(rv, cellbuf_get(

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

1036 if_err_return(rv, cellbuf_get(

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

1038

1039 {

1040 #ifdef TB_OPT_EGC

1041 if (back->nech > 0)

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

1043 else

1044 #endif

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

1046 * wchar_t */

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

1048 }

1049 if (w < 1) {

1050 w = 1;

1051 }

1052

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

1054 x += w;

1055 continue;

1056 }

1057 cell_copy(front, back);

1058

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

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

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

1062 send_char(i, y, ' ');

1063 }

1064 } else {

1065 {

1066 #ifdef TB_OPT_EGC

1067 if (back->nech > 0)

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

1069 back->nech);

1070 else

1071 #endif

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

1073 }

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

1075 struct tb_cell *front_wide;

1076 if_err_return(rv, cellbuf_get(

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

1078 &front_wide));

1079 if_err_return(rv, cell_set(front_wide,

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

1081

1082 }

1083 }

1084 x += w;

1085 }

1086 }

1087

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

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

1090

1091 return TB_OK;

1092 }

1093

1094 int tb_set_cursor(int cx, int cy) {

1095 int rv;

1096 if_not_init_return();

1097 if (cx < 0)

1098 cx = 0;

1099 if (cy < 0)

1100 cy = 0;

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

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

1103 global.caps[TB_CAP_SHOW_CURSOR]));

1104 }

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

1106 global.cursor_x = cx;

1107 global.cursor_y = cy;

1108 return TB_OK;

1109 }

1110

1111 int tb_hide_cursor(void) {

1112 int rv;

1113 if_not_init_return();

1114 if (global.cursor_x >= 0) {

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

1116 global.caps[TB_CAP_HIDE_CURSOR]));

1117 }

1118 global.cursor_x = -1;

1119 global.cursor_y = -1;

1120 return TB_OK;

1121 }

1122

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

1124 if_not_init_return();

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

1126 }

1127

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

1129 uintattr_t bg) {

1130 int rv;

1131 struct tb_cell *cell;

1132 if_not_init_return();

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

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

1135 return TB_OK;

1136 }

1137

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

1139 if_not_init_return();

1140 #ifdef TB_OPT_EGC

1141 int rv;

1142 struct tb_cell *cell;

1143 size_t nech;

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

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

1146 nech = cell->nech + 1;

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

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

1149 } else { /* make new ech */

1150 nech = 2;

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

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

1153 cell->ech[1] = ch;

1154 }

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

1156 cell->nech = nech;

1157 return TB_OK;

1158 #else

1159 (void)x;

1160 (void)y;

1161 (void)ch;

1162 return TB_ERR;

1163 #endif

1164 }

1165

1166 int tb_set_input_mode(int mode) {

1167 if_not_init_return();

1168 if (mode == TB_INPUT_CURRENT) {

1169 return global.input_mode;

1170 }

1171

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

1173 mode |= TB_INPUT_ESC;

1174 }

1175

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

1177 (TB_INPUT_ESC | TB_INPUT_ALT)) {

1178 mode &= ~TB_INPUT_ALT;

1179 }

1180

1181 if (mode & TB_INPUT_MOUSE) {

1182 bytebuf_puts(&global.out, TB_HARDCAP_ENTER_MOUSE);

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

1184 } else {

1185 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);

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

1187 }

1188

1189 global.input_mode = mode;

1190 return TB_OK;

1191 }

1192

1193 int tb_set_output_mode(int mode) {

1194 if_not_init_return();

1195 switch (mode) {

1196 case TB_OUTPUT_CURRENT:

1197 return global.output_mode;

1198 case TB_OUTPUT_NORMAL:

1199 case TB_OUTPUT_256:

1200 case TB_OUTPUT_216:

1201 case TB_OUTPUT_GRAYSCALE:

1202 #ifdef TB_OPT_TRUECOLOR

1203 case TB_OUTPUT_TRUECOLOR:

1204 #endif

1205 global.output_mode = mode;

1206 return TB_OK;

1207 }

1208 return TB_ERR;

1209 }

1210

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

1212 if_not_init_return();

1213 return wait_event(event, timeout_ms);

1214 }

1215

1216 int tb_poll_event(struct tb_event *event) {

1217 if_not_init_return();

1218 return wait_event(event, -1);

1219 }

1220

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

1222 if_not_init_return();

1223

1224 *ttyfd = global.rfd;

1225 *resizefd = global.resize_pipefd[0];

1226

1227 return TB_OK;

1228 }

1229

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

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

1232 }

1233

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

1235 const char *str) {

1236 int rv;

1237 uint32_t uni;

1238 int w, ix = x;

1239 if (out_w) {

1240 *out_w = 0;

1241 }

1242 while (*str) {

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

1244 w = wcwidth((wchar_t)uni);

1245 if (w <= 0) {

1246 w = 1;

1247 }

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

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

1250 } else {

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

1252 }

1253 x += w;

1254 if (out_w) {

1255 *out_w += w;

1256 }

1257 }

1258 return TB_OK;

1259 }

1260

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

1262 ...) {

1263 int rv;

1264 va_list vl;

1265 va_start(vl, fmt);

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

1267 va_end(vl);

1268 return rv;

1269 }

1270

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

1272 const char *fmt, ...) {

1273 int rv;

1274 va_list vl;

1275 va_start(vl, fmt);

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

1277 va_end(vl);

1278 return rv;

1279 }

1280

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

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

1283 }

1284

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

1286 int rv;

1287 char buf[TB_OPT_PRINTF_BUF];

1288 va_list vl;

1289 va_start(vl, fmt);

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

1291 va_end(vl);

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

1293 return TB_ERR;

1294 }

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

1296 }

1297

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

1299 switch (fn_type) {

1300 case TB_FUNC_EXTRACT_PRE:

1301 global.fn_extract_esc_pre = fn;

1302 return TB_OK;

1303 case TB_FUNC_EXTRACT_POST:

1304 global.fn_extract_esc_post = fn;

1305 return TB_OK;

1306 }

1307 return TB_ERR;

1308 }

1309

1310 struct tb_cell *tb_cell_buffer(void) {

1311 if (!global.initialized)

1312 return NULL;

1313 return global.back.cells;

1314 }

1315

1316 int tb_utf8_char_length(char c) {

1317 return utf8_length[(unsigned char)c];

1318 }

1319

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

1321

1322 int i;

1323 unsigned char len, mask;

1324 uint32_t result;

1325

1326 if (*c == 0) {

1327 return TB_ERR;

1328 }

1329

1330 len = tb_utf8_char_length(*c);

1331 mask = utf8_mask[len - 1];

1332 result = c[0] & mask;

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

1334 result <<= 6;

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

1336 }

1337

1338 *out = result;

1339 return (int)len;

1340 }

1341

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

1343 int len = 0;

1344 int first;

1345 int i;

1346

1347 if (c < 0x80) {

1348 first = 0;

1349 len = 1;

1350 } else if (c < 0x800) {

1351 first = 0xc0;

1352 len = 2;

1353 } else if (c < 0x10000) {

1354 first = 0xe0;

1355 len = 3;

1356 } else if (c < 0x200000) {

1357 first = 0xf0;

1358 len = 4;

1359 } else if (c < 0x4000000) {

1360 first = 0xf8;

1361 len = 5;

1362 } else {

1363 first = 0xfc;

1364 len = 6;

1365 }

1366

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

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

1369 c >>= 6;

1370 }

1371 out[0] = c | first;

1372

1373 return len;

1374 }

1375

1376 int tb_last_errno(void) {

1377 return global.last_errno;

1378 }

1379

1380 const char *tb_strerror(int err) {

1381 switch (err) {

1382 case TB_OK:

1383 return "Success";

1384 case TB_ERR_NEED_MORE:

1385 return "Not enough input";

1386 case TB_ERR_INIT_ALREADY:

1387 return "Termbox initialized already";

1388 case TB_ERR_MEM:

1389 return "Out of memory";

1390 case TB_ERR_NO_EVENT:

1391 return "No event";

1392 case TB_ERR_NO_TERM:

1393 return "No TERM in environment";

1394 case TB_ERR_NOT_INIT:

1395 return "Termbox not initialized";

1396 case TB_ERR_OUT_OF_BOUNDS:

1397 return "Out of bounds";

1398 case TB_ERR_UNSUPPORTED_TERM:

1399 return "Unsupported terminal";

1400 case TB_ERR_CAP_COLLISION:

1401 return "Termcaps collision";

1402 case TB_ERR_RESIZE_SSCANF:

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

1404 " after resize";

1405 case TB_ERR:

1406 case TB_ERR_INIT_OPEN:

1407 case TB_ERR_READ:

1408 case TB_ERR_RESIZE_IOCTL:

1409 case TB_ERR_RESIZE_PIPE:

1410 case TB_ERR_RESIZE_SIGACTION:

1411 case TB_ERR_POLL:

1412 case TB_ERR_TCGETATTR:

1413 case TB_ERR_TCSETATTR:

1414 case TB_ERR_RESIZE_WRITE:

1415 case TB_ERR_RESIZE_POLL:

1416 case TB_ERR_RESIZE_READ:

1417 default:

1418 strerror_r(global.last_errno, global.errbuf,

1419 sizeof(global.errbuf));

1420 return (const char *)global.errbuf;

1421 }

1422 }

1423

1424 int tb_has_truecolor(void) {

1425 #ifdef TB_OPT_TRUECOLOR

1426 return 1;

1427 #else

1428 return 0;

1429 #endif

1430 }

1431

1432 int tb_has_egc(void) {

1433 #ifdef TB_OPT_EGC

1434 return 1;

1435 #else

1436 return 0;

1437 #endif

1438 }

1439

1440 const char *tb_version(void) {

1441 return TB_VERSION_STR;

1442 }

1443

1444 static int tb_reset(void) {

1445 int ttyfd_open = global.ttyfd_open;

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

1447 global.ttyfd = -1;

1448 global.rfd = -1;

1449 global.wfd = -1;

1450 global.ttyfd_open = ttyfd_open;

1451 global.resize_pipefd[0] = -1;

1452 global.resize_pipefd[1] = -1;

1453 global.width = -1;

1454 global.height = -1;

1455 global.cursor_x = -1;

1456 global.cursor_y = -1;

1457 global.last_x = -1;

1458 global.last_y = -1;

1459 global.fg = TB_DEFAULT;

1460 global.bg = TB_DEFAULT;

1461 global.last_fg = ~global.fg;

1462 global.last_bg = ~global.bg;

1463 global.input_mode = TB_INPUT_ESC;

1464 global.output_mode = TB_OUTPUT_NORMAL;

1465 return TB_OK;

1466 }

1467

1468 static int init_term_attrs(void) {

1469

1470 struct termios tios;

1471

1472 if (global.ttyfd < 0) {

1473 return TB_OK;

1474 }

1475

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

1477 global.last_errno = errno;

1478 return TB_ERR_TCGETATTR;

1479 }

1480

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

1482 global.has_orig_tios = 1;

1483

1484 cfmakeraw(&tios);

1485 tios.c_cc[VMIN] = 1;

1486 tios.c_cc[VTIME] = 0;

1487

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

1489 global.last_errno = errno;

1490 return TB_ERR_TCSETATTR;

1491 }

1492

1493 return TB_OK;

1494 }

1495

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

1497 const char *fmt, va_list vl) {

1498 int rv;

1499 char buf[TB_OPT_PRINTF_BUF];

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

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

1502 return TB_ERR;

1503 }

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

1505 }

1506

1507 static int init_term_caps(void) {

1508 if (load_terminfo() == TB_OK) {

1509 return parse_terminfo_caps();

1510 }

1511 return load_builtin_caps();

1512 }

1513

1514 static int init_cap_trie(void) {

1515 int rv, i;

1516

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

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

1519 if_err_return(rv,

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

1521 }

1522

1523 /* Add built-in mod caps */

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

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

1526 builtin_mod_caps[i].key,

1527 builtin_mod_caps[i].mod);

1528 /*

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

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

1531 * global.caps here.

1532 */

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

1534 return rv;

1535 }

1536 }

1537

1538 return TB_OK;

1539 }

1540

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

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

1543 size_t i, j;

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

1545 char c = cap[i];

1546 next = NULL;

1547

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

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

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

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

1552 break;

1553 }

1554 }

1555 if (!next) {

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

1557 node->nchildren += 1;

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

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

1560 if (!node->children) {

1561 return TB_ERR_MEM;

1562 }

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

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

1565 next->c = c;

1566 }

1567

1568 /* Continue */

1569 node = next;

1570 }

1571

1572 if (node->is_leaf) {

1573 /* Already a leaf here */

1574 return TB_ERR_CAP_COLLISION;

1575 }

1576

1577 node->is_leaf = 1;

1578 node->key = key;

1579 node->mod = mod;

1580 return TB_OK;

1581 }

1582

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

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

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

1586 size_t i, j;

1587 *last = node;

1588 *depth = 0;

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

1590 char c = buf[i];

1591 next = NULL;

1592

1593 /* Find c in node.children */

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

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

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

1597 break;

1598 }

1599 }

1600 if (!next) {

1601 /* Not found */

1602 return TB_OK;

1603 }

1604 node = next;

1605 *last = node;

1606 *depth += 1;

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

1608 break;

1609 }

1610 }

1611 return TB_OK;

1612 }

1613

1614 static int cap_trie_deinit(struct cap_trie_t *node) {

1615 size_t j;

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

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

1618 }

1619 if (node->children) {

1620 tb_free(node->children);

1621 }

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

1623 return TB_OK;

1624 }

1625

1626 static int init_resize_handler(void) {

1627 struct sigaction sa;

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

1629 global.last_errno = errno;

1630 return TB_ERR_RESIZE_PIPE;

1631 }

1632

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

1634 sa.sa_handler = handle_resize;

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

1636 global.last_errno = errno;

1637 return TB_ERR_RESIZE_SIGACTION;

1638 }

1639

1640 return TB_OK;

1641 }

1642

1643 static int send_init_escape_codes(void) {

1644 int rv;

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

1646 global.caps[TB_CAP_ENTER_CA]));

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

1648 global.caps[TB_CAP_ENTER_KEYPAD]));

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

1650 global.caps[TB_CAP_HIDE_CURSOR]));

1651 return TB_OK;

1652 }

1653

1654 static int send_clear(void) {

1655 int rv;

1656

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

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

1659 global.caps[TB_CAP_CLEAR_SCREEN]));

1660

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

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

1663

1664 global.last_x = -1;

1665 global.last_y = -1;

1666

1667 return TB_OK;

1668 }

1669

1670 static int update_term_size(void) {

1671 int rv, ioctl_errno;

1672 struct winsize sz;

1673

1674 if (global.ttyfd < 0) {

1675 return TB_OK;

1676 }

1677

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

1679

1680 /* Try ioctl TIOCGWINSZ */

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

1682 global.width = sz.ws_col;

1683 global.height = sz.ws_row;

1684 return TB_OK;

1685 }

1686 ioctl_errno = errno;

1687

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

1689 if_ok_return(rv, update_term_size_via_esc());

1690

1691 global.last_errno = ioctl_errno;

1692 return TB_ERR_RESIZE_IOCTL;

1693 }

1694

1695 static int update_term_size_via_esc(void) {

1696 #ifndef TB_RESIZE_FALLBACK_MS

1697 #define TB_RESIZE_FALLBACK_MS 1000

1698 #endif

1699

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

1701 int rw, rh;

1702 char buf[TB_OPT_READ_BUF];

1703 ssize_t write_rv, read_rv;

1704 int select_rv;

1705 struct timeval timeout;

1706 fd_set fds;

1707

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

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

1710 return TB_ERR_RESIZE_WRITE;

1711 }

1712

1713 FD_ZERO(&fds);

1714 FD_SET(global.rfd, &fds);

1715

1716 timeout.tv_sec = 0;

1717 timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000;

1718

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

1720

1721 if (select_rv != 1) {

1722 global.last_errno = errno;

1723 return TB_ERR_RESIZE_POLL;

1724 }

1725

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

1727 if (read_rv < 1) {

1728 global.last_errno = errno;

1729 return TB_ERR_RESIZE_READ;

1730 }

1731 buf[read_rv] = '\0';

1732

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

1734 return TB_ERR_RESIZE_SSCANF;

1735 }

1736

1737 global.width = rw;

1738 global.height = rh;

1739 return TB_OK;

1740 }

1741

1742 static int init_cellbuf(void) {

1743 int rv;

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

1745 global.height));

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

1747 global.height));

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

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

1750 return TB_OK;

1751 }

1752

1753 static int tb_deinit(void) {

1754

1755 struct sigaction sig = {0};

1756

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

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

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

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

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

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

1763 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);

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

1765 }

1766 if (global.ttyfd >= 0) {

1767 if (global.has_orig_tios) {

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

1769 }

1770 if (global.ttyfd_open) {

1771 close(global.ttyfd);

1772 global.ttyfd_open = 0;

1773 }

1774 }

1775

1776 sig.sa_handler = SIG_DFL;

1777 sigaction(SIGWINCH, &sig, NULL);

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

1779 close(global.resize_pipefd[0]);

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

1781 close(global.resize_pipefd[1]);

1782

1783 cellbuf_free(&global.back);

1784 cellbuf_free(&global.front);

1785 bytebuf_free(&global.in);

1786 bytebuf_free(&global.out);

1787

1788 if (global.terminfo)

1789 tb_free(global.terminfo);

1790

1791 cap_trie_deinit(&global.cap_trie);

1792

1793 tb_reset();

1794 return TB_OK;

1795 }

1796

1797 static int load_terminfo(void) {

1798 int rv;

1799 char tmp[PATH_MAX];

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

1801

1802 /*

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

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

1805 * options, so best guesses are used here.

1806 */

1807 term = getenv("TERM");

1808 if (!term) {

1809 return TB_ERR;

1810 }

1811

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

1813 terminfo = getenv("TERMINFO");

1814 if (terminfo) {

1815 return load_terminfo_from_path(terminfo, term);

1816 }

1817

1818 /* Next try ~/.terminfo */

1819 home = getenv("HOME");

1820 if (home) {

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

1822 home));

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

1824 }

1825

1826 /*

1827 * Next try TERMINFO_DIRS

1828 *

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

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

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

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

1833 * paths after this loop.

1834 */

1835 dirs = getenv("TERMINFO_DIRS");

1836 if (dirs) {

1837 char *dir;

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

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

1840 while (dir) {

1841 const char *cdir = dir;

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

1843 if_ok_return(rv,

1844 load_terminfo_from_path(cdir, term));

1845 }

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

1847 }

1848 }

1849

1850 #ifdef TB_TERMINFO_DIR

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

1852 #endif

1853 if_ok_return(rv,

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

1855 if_ok_return(rv,

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

1857 if_ok_return(rv,

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

1859 if_ok_return(rv,

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

1861 if_ok_return(rv,

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

1863 if_ok_return(rv,

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

1865 if_ok_return(rv,

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

1867 if_ok_return(rv,

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

1869

1870 return TB_ERR;

1871 }

1872

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

1874 char tmp[PATH_MAX];

1875 int rv;

1876

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

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

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

1880 if_ok_return(rv, read_terminfo_path(tmp));

1881

1882 #ifdef __APPLE__

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

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

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

1886 return read_terminfo_path(tmp);

1887 #endif

1888

1889 return TB_ERR;

1890 }

1891

1892 static int read_terminfo_path(const char *path) {

1893 size_t fsize;

1894 char *data;

1895 struct stat st;

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

1897 if (!fp) {

1898 return TB_ERR;

1899 }

1900

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

1902 fclose(fp);

1903 return TB_ERR;

1904 }

1905

1906 fsize = st.st_size;

1907 data = tb_malloc(fsize);

1908 if (!data) {

1909 fclose(fp);

1910 return TB_ERR;

1911 }

1912

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

1914 fclose(fp);

1915 tb_free(data);

1916 return TB_ERR;

1917 }

1918

1919 global.terminfo = data;

1920 global.nterminfo = fsize;

1921

1922 fclose(fp);

1923 return TB_OK;

1924 }

1925

1926 static int parse_terminfo_caps(void) {

1927 /*

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

1929 * for a description of this behavior.

1930 */

1931 int16_t *header;

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

1933

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

1935 if (global.nterminfo < 6) {

1936 return TB_ERR;

1937 }

1938

1939 header = (int16_t *)global.terminfo;

1940 /*

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

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

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

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

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

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

1947 */

1948

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

1950 bytes_per_int = header[0] == 01036 ? 4 /* 32-bit */

1951 : 2; /* 16-bit */

1952

1953 /*

1954 * Between the boolean section and the number section, a null byte

1955 * will be inserted, if necessary, to ensure that the number section

1956 * begins on an even byte

1957 */

1958 align_offset = (header[1] + header[2]) % 2 != 0 ? 1 : 0;

1959

1960 pos_str_offsets =

1961 (6 * sizeof(int16_t)) /* header (12 bytes) */

1962 + header[1] /* length of names section */

1963 + header[2] /* length of boolean section */

1964 + align_offset +

1965 (header[3] * bytes_per_int); /* length of numbers section */

1966

1967 /* length of string offsets table */

1968 pos_str_table = pos_str_offsets + (header[4] * sizeof(int16_t));

1969

1970 /* Load caps */

1971 for (i = 0; i < TB_CAP__COUNT; i++) {

1972 const char *cap = get_terminfo_string(pos_str_offsets,

1973 pos_str_table, header[5],

1974 terminfo_cap_indexes[i]);

1975 if (!cap) {

1976 /* Something is not right */

1977 return TB_ERR;

1978 }

1979 global.caps[i] = cap;

1980 }

1981

1982 return TB_OK;

1983 }

1984

1985 static int load_builtin_caps(void) {

1986 int i, j;

1987 const char *term = getenv("TERM");

1988

1989 if (!term) {

1990 return TB_ERR_NO_TERM;

1991 }

1992

1993 /* Check for exact TERM match */

1994 for (i = 0; builtin_terms[i].name != NULL; i++) {

1995 if (strcmp(term, builtin_terms[i].name) == 0) {

1996 for (j = 0; j < TB_CAP__COUNT; j++) {

1997 global.caps[j] = builtin_terms[i].caps[j];

1998 }

1999 return TB_OK;

2000 }

2001 }

2002

2003 /* Check for partial TERM or alias match */

2004 for (i = 0; builtin_terms[i].name != NULL; i++) {

2005 if (strstr(term, builtin_terms[i].name) != NULL ||

2006 (*(builtin_terms[i].alias) != '\0' &&

2007 strstr(term, builtin_terms[i].alias) != NULL))

2008 {

2009 for (j = 0; j < TB_CAP__COUNT; j++) {

2010 global.caps[j] = builtin_terms[i].caps[j];

2011 }

2012 return TB_OK;

2013 }

2014 }

2015

2016 return TB_ERR_UNSUPPORTED_TERM;

2017 }

2018

2019 static const char *get_terminfo_string(int16_t str_offsets_pos,

2020 int16_t str_table_pos, int16_t str_table_len,

2021 int16_t str_index) {

2022 const int16_t *str_offset =

2023 (int16_t *)(global.terminfo + (int)str_offsets_pos +

2024 ((int)str_index * (int)sizeof(int16_t)));

2025 if (*str_offset < 0) {

2026 /* A negative indicates the cap is absent from this terminal */

2027 return "";

2028 }

2029 if (*str_offset >= str_table_len) {

2030 /* Invalid string offset */

2031 return NULL;

2032 }

2033 if (((size_t)((int)str_table_pos + (int)*str_offset)) >=

2034 global.nterminfo) {

2035 /* Truncated/corrupt terminfo? */

2036 return NULL;

2037 }

2038 return (const char *)(global.terminfo + (int)str_table_pos +

2039 (int)*str_offset);

2040 }

2041

2042 static int wait_event(struct tb_event *event, int timeout) {

2043 int rv;

2044 char buf[TB_OPT_READ_BUF];

2045 fd_set fds;

2046 struct timeval tv;

2047

2048 memset(event, 0, sizeof(*event));

2049 if_ok_return(rv, extract_event(event));

2050

2051 tv.tv_sec = timeout / 1000;

2052 tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;

2053

2054 do {

2055 int maxfd, select_rv, tty_has_events, resize_has_events;

2056 FD_ZERO(&fds);

2057 FD_SET(global.rfd, &fds);

2058 FD_SET(global.resize_pipefd[0], &fds);

2059

2060 maxfd = global.resize_pipefd[0] > global.rfd

2061 ? global.resize_pipefd[0]

2062 : global.rfd;

2063

2064 select_rv = select(maxfd + 1, &fds, NULL, NULL,

2065 (timeout < 0) ? NULL : &tv);

2066

2067 if (select_rv < 0) {

2068 /* Let EINTR/EAGAIN bubble up */

2069 global.last_errno = errno;

2070 return TB_ERR_POLL;

2071 } else if (select_rv == 0) {

2072 return TB_ERR_NO_EVENT;

2073 }

2074

2075 tty_has_events = (FD_ISSET(global.rfd, &fds));

2076 resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds));

2077

2078 if (tty_has_events) {

2079 ssize_t read_rv = read(global.rfd, buf, sizeof(buf));

2080 if (read_rv < 0) {

2081 global.last_errno = errno;

2082 return TB_ERR_READ;

2083 } else if (read_rv > 0) {

2084 bytebuf_nputs(&global.in, buf, read_rv);

2085 }

2086 }

2087

2088 if (resize_has_events) {

2089 int ignore = 0;

2090 read(global.resize_pipefd[0], &ignore, sizeof(ignore));

2091 /* TODO Harden against errors encountered mid-resize */

2092 if_err_return(rv, update_term_size());

2093 if_err_return(rv, resize_cellbufs());

2094 event->type = TB_EVENT_RESIZE;

2095 event->w = global.width;

2096 event->h = global.height;

2097 return TB_OK;

2098 }

2099

2100 memset(event, 0, sizeof(*event));

2101 if_ok_return(rv, extract_event(event));

2102 } while (timeout == -1);

2103

2104 return rv;

2105 }

2106

2107 static int extract_event(struct tb_event *event) {

2108 int rv;

2109 struct bytebuf_t *in = &global.in;

2110

2111 if (in->len == 0) {

2112 return TB_ERR;

2113 }

2114

2115 if (in->buf[0] == '\x1b') {

2116 /* Escape sequence? */

2117 /* In TB_INPUT_ESC,

2118 * skip if the buffer is a single escape char */

2119 if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) {

2120 if_ok_or_need_more_return(rv, extract_esc(event));

2121 }

2122

2123 /* Escape key? */

2124 if (global.input_mode & TB_INPUT_ESC) {

2125 event->type = TB_EVENT_KEY;

2126 event->ch = 0;

2127 event->key = TB_KEY_ESC;

2128 event->mod = 0;

2129 bytebuf_shift(in, 1);

2130 return TB_OK;

2131 }

2132

2133 /* Recurse for alt key */

2134 event->mod |= TB_MOD_ALT;

2135 bytebuf_shift(in, 1);

2136 return extract_event(event);

2137 }

2138

2139 /* ASCII control key? */

2140 if ((uint16_t)in->buf[0] < TB_KEY_SPACE ||

2141 in->buf[0] == TB_KEY_BACKSPACE2)

2142 {

2143 event->type = TB_EVENT_KEY;

2144 event->ch = 0;

2145 event->key = (uint16_t)in->buf[0];

2146 event->mod |= TB_MOD_CTRL;

2147 bytebuf_shift(in, 1);

2148 return TB_OK;

2149 }

2150

2151 /* UTF-8? */

2152 if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) {

2153 event->type = TB_EVENT_KEY;

2154 tb_utf8_char_to_unicode(&event->ch, in->buf);

2155 event->key = 0;

2156 bytebuf_shift(in, tb_utf8_char_length(in->buf[0]));

2157 return TB_OK;

2158 }

2159

2160 /* Need more input */

2161 return TB_ERR;

2162 }

2163

2164 static int extract_esc(struct tb_event *event) {

2165 int rv;

2166 if_ok_or_need_more_return(rv, extract_esc_user(event, 0));

2167 if_ok_or_need_more_return(rv, extract_esc_cap(event));

2168 if_ok_or_need_more_return(rv, extract_esc_mouse(event));

2169 if_ok_or_need_more_return(rv, extract_esc_user(event, 1));

2170 return TB_ERR;

2171 }

2172

2173 static int extract_esc_user(struct tb_event *event, int is_post) {

2174 int rv;

2175 size_t consumed = 0;

2176 struct bytebuf_t *in = &global.in;

2177 int (*fn)(struct tb_event *, size_t *);

2178

2179 fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre;

2180

2181 if (!fn) {

2182 return TB_ERR;

2183 }

2184

2185 rv = fn(event, &consumed);

2186 if (rv == TB_OK) {

2187 bytebuf_shift(in, consumed);

2188 }

2189

2190 if_ok_or_need_more_return(rv, rv);

2191 return TB_ERR;

2192 }

2193

2194 static int extract_esc_cap(struct tb_event *event) {

2195 int rv;

2196 struct bytebuf_t *in = &global.in;

2197 struct cap_trie_t *node;

2198 size_t depth;

2199

2200 if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth));

2201 if (node->is_leaf) {

2202 /* Found a leaf node */

2203 event->type = TB_EVENT_KEY;

2204 event->ch = 0;

2205 event->key = node->key;

2206 event->mod = node->mod;

2207 bytebuf_shift(in, depth);

2208 return TB_OK;

2209 } else if (node->nchildren > 0 && in->len <= depth) {

2210 /* Found a branch node (not enough input) */

2211 return TB_ERR_NEED_MORE;

2212 }

2213

2214 return TB_ERR;

2215 }

2216

2217 static int extract_esc_mouse(struct tb_event *event) {

2218 struct bytebuf_t *in = &global.in;

2219

2220 enum type { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX };

2221

2222 char *cmp[TYPE_MAX];

2223

2224 enum type type = 0;

2225 int ret = TB_ERR;

2226 size_t buf_shift = 0;

2227

2228 /* X10 mouse encoding, the simplest one */

2229 /* \x1b [ M Cb Cx Cy */

2230 cmp[TYPE_VT200] = "\x1b[M";

2231 /* xterm 1006 extended mode or urxvt 1015 extended mode */

2232 /* xterm: \x1b [ < Cb ; Cx ; Cy (M or m) */

2233 cmp[TYPE_1006] = "\x1b[<";

2234 /* urxvt: \x1b [ Cb ; Cx ; Cy M */

2235 cmp[TYPE_1015] = "\x1b[";

2236

2237 /* Unrolled at compile-time (probably) */

2238 for (; type < TYPE_MAX; type++) {

2239 size_t size = strlen(cmp[type]);

2240

2241 if (in->len >= size &&

2242 (strncmp(cmp[type], in->buf, size)) == 0) {

2243 break;

2244 }

2245 }

2246

2247 if (type == TYPE_MAX) {

2248 ret = TB_ERR; /* No match */

2249 return ret;

2250 }

2251

2252 switch (type) {

2253 case TYPE_VT200:

2254 {

2255 int b, fail;

2256 if (in->len < 6) break;

2257

2258 b = in->buf[3] - 0x20;

2259 fail = 0;

2260

2261 switch (b & 3) {

2262 case 0:

2263 event->key = ((b & 64) != 0) ?

2264 TB_KEY_MOUSE_WHEEL_UP :

2265 TB_KEY_MOUSE_LEFT;

2266 break;

2267 case 1:

2268 event->key = ((b & 64) != 0) ?

2269 TB_KEY_MOUSE_WHEEL_DOWN :

2270 TB_KEY_MOUSE_MIDDLE;

2271 break;

2272 case 2:

2273 event->key = TB_KEY_MOUSE_RIGHT;

2274 break;

2275 case 3:

2276 event->key = TB_KEY_MOUSE_RELEASE;

2277 break;

2278 default:

2279 ret = TB_ERR;

2280 fail = 1;

2281 break;

2282 }

2283

2284 if (!fail) {

2285 if ((b & 32) != 0) {

2286 event->mod |= TB_MOD_MOTION;

2287 }

2288

2289 /* the coord is 1,1 for upper left */

2290 event->x = ((uint8_t)in->buf[4]) - 0x21;

2291 event->y = ((uint8_t)in->buf[5]) - 0x21;

2292

2293 ret = TB_OK;

2294 }

2295

2296 buf_shift = 6;

2297 }

2298 break;

2299 case TYPE_1006:

2300 /* fallthrough */

2301 case TYPE_1015:

2302 {

2303 size_t i = 0;

2304

2305 enum {

2306 FIRST_M = 0,

2307 FIRST_SEMICOLON,

2308 LAST_SEMICOLON,

2309 FIRST_LAST_MAX

2310 };

2311

2312 size_t indices[FIRST_LAST_MAX] = {

2313 index_fail,

2314 index_fail,

2315 index_fail

2316 };

2317 int m_is_capital = 0;

2318

2319 for (i = 0; i < in->len; i++) {

2320 if (in->buf[i] != ';') {

2321 if (indices[FIRST_SEMICOLON] == index_fail) {

2322 indices[FIRST_SEMICOLON] = i;

2323 } else {

2324 indices[LAST_SEMICOLON] = i;

2325 }

2326 } else if (indices[FIRST_M] == index_fail) {

2327 if (in->buf[i] == 'm' || in->buf[i] == 'M') {

2328 m_is_capital = (in->buf[i] == 'M');

2329 indices[FIRST_M] = i;

2330 }

2331 }

2332 }

2333

2334 if (indices[FIRST_M] == index_fail ||

2335 indices[FIRST_SEMICOLON] == index_fail ||

2336 indices[LAST_SEMICOLON] == index_fail) {

2337 ret = TB_ERR;

2338 } else {

2339 int start = (type == TYPE_1015 ? 2 : 3);

2340 int fail = 0;

2341

2342 unsigned n1 = strtoul(&in->buf[start], NULL, 10);

2343 unsigned n2 = strtoul(

2344 &in->buf[indices[FIRST_SEMICOLON] + 1],

2345 NULL, 10);

2346 unsigned n3 = strtoul(

2347 &in->buf[indices[LAST_SEMICOLON] + 1],

2348 NULL, 10);

2349

2350 if (type == TYPE_1015) {

2351 n1 -= 0x20;

2352 }

2353

2354

2355 switch (n1 & 3) {

2356 case 0:

2357 event->key = ((n1 & 64) != 0)

2358 ? TB_KEY_MOUSE_WHEEL_UP

2359 : TB_KEY_MOUSE_LEFT;

2360 break;

2361 case 1:

2362 event->key = ((n1 & 64) != 0)

2363 ? TB_KEY_MOUSE_WHEEL_DOWN

2364 : TB_KEY_MOUSE_MIDDLE;

2365 break;

2366 case 2:

2367 event->key = TB_KEY_MOUSE_RIGHT;

2368 break;

2369 case 3:

2370 event->key = TB_KEY_MOUSE_RELEASE;

2371 break;

2372 default:

2373 ret = TB_ERR;

2374 fail = 1;

2375 break;

2376 }

2377

2378 buf_shift = in->len;

2379

2380 if (!fail) {

2381 if (!m_is_capital) {

2382 /* on xterm mouse release is signaled

2383 * by lowercase m */

2384 event->key = TB_KEY_MOUSE_RELEASE;

2385 }

2386

2387 if ((n1 & 32) != 0) {

2388 event->mod |= TB_MOD_MOTION;

2389 }

2390

2391 event->x = ((uint8_t)n2) - 1;

2392 event->y = ((uint8_t)n3) - 1;

2393

2394 ret = TB_OK;

2395 }

2396 }

2397 }

2398 break;

2399 case TYPE_MAX:

2400 ret = TB_ERR;

2401 }

2402

2403 if (buf_shift > 0) {

2404 bytebuf_shift(in, buf_shift);

2405 }

2406

2407 if (ret == TB_OK) {

2408 event->type = TB_EVENT_MOUSE;

2409 }

2410

2411 return ret;

2412 }

2413

2414 static int resize_cellbufs(void) {

2415 int rv;

2416 if_err_return(rv, cellbuf_resize(&global.back, global.width,

2417 global.height));

2418 if_err_return(rv, cellbuf_resize(&global.front, global.width,

2419 global.height));

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

2421 if_err_return(rv, send_clear());

2422 return TB_OK;

2423 }

2424

2425 static void handle_resize(int sig) {

2426 int errno_copy = errno;

2427 write(global.resize_pipefd[1], &sig, sizeof(sig));

2428 errno = errno_copy;

2429 }

2430

2431 static int send_attr(uintattr_t fg, uintattr_t bg) {

2432 int rv;

2433 uintattr_t attr_bold, attr_blink, attr_italic,

2434 attr_underline, attr_reverse, attr_default;

2435 uintattr_t cfg, cbg;

2436

2437 if (fg == global.last_fg && bg == global.last_bg) {

2438 return TB_OK;

2439 }

2440

2441 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]));

2442

2443 switch (global.output_mode) {

2444 default:

2445 case TB_OUTPUT_NORMAL:

2446 cfg = fg & 0x0f;

2447 cbg = bg & 0x0f;

2448 break;

2449

2450 case TB_OUTPUT_256:

2451 cfg = fg & 0xff;

2452 cbg = bg & 0xff;

2453 break;

2454

2455 case TB_OUTPUT_216:

2456 cfg = fg & 0xff;

2457 cbg = bg & 0xff;

2458 if (cfg > 216)

2459 cfg = 216;

2460 if (cbg > 216)

2461 cbg = 216;

2462 cfg += 0x0f;

2463 cbg += 0x0f;

2464 break;

2465

2466 case TB_OUTPUT_GRAYSCALE:

2467 cfg = fg & 0xff;

2468 cbg = bg & 0xff;

2469 if (cfg > 24)

2470 cfg = 24;

2471 if (cbg > 24)

2472 cbg = 24;

2473 cfg += 0xe7;

2474 cbg += 0xe7;

2475 break;

2476

2477 #ifdef TB_OPT_TRUECOLOR

2478 case TB_OUTPUT_TRUECOLOR:

2479 cfg = fg & 0xffffff;

2480 cbg = bg & 0xffffff;

2481 break;

2482 #endif

2483 }

2484

2485 #ifdef TB_OPT_TRUECOLOR

2486 if (global.output_mode == TB_OUTPUT_TRUECOLOR) {

2487 attr_bold = TB_TRUECOLOR_BOLD;

2488 attr_blink = TB_TRUECOLOR_BLINK;

2489 attr_italic = TB_TRUECOLOR_ITALIC;

2490 attr_underline = TB_TRUECOLOR_UNDERLINE;

2491 attr_reverse = TB_TRUECOLOR_REVERSE;

2492 attr_default = TB_TRUECOLOR_DEFAULT;

2493 } else

2494 #endif

2495 {

2496 attr_bold = TB_BOLD;

2497 attr_blink = TB_BLINK;

2498 attr_italic = TB_ITALIC;

2499 attr_underline = TB_UNDERLINE;

2500 attr_reverse = TB_REVERSE;

2501 attr_default = TB_DEFAULT;

2502 }

2503

2504 /* For convenience (and some back compat), interpret 0 as default in

2505 * some modes */

2506 if (global.output_mode == TB_OUTPUT_NORMAL ||

2507 global.output_mode == TB_OUTPUT_216 ||

2508 global.output_mode == TB_OUTPUT_GRAYSCALE)

2509 {

2510 if ((fg & 0xff) == 0)

2511 fg |= attr_default;

2512 if ((bg & 0xff) == 0)

2513 bg |= attr_default;

2514 }

2515

2516 if (fg & attr_bold)

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

2518 global.caps[TB_CAP_BOLD]));

2519

2520 if (fg & attr_blink)

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

2522 global.caps[TB_CAP_BLINK]));

2523

2524 if (fg & attr_underline)

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

2526 global.caps[TB_CAP_UNDERLINE]));

2527

2528 if (fg & attr_italic)

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

2530 global.caps[TB_CAP_ITALIC]));

2531

2532 if ((fg & attr_reverse) || (bg & attr_reverse))

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

2534 global.caps[TB_CAP_REVERSE]));

2535

2536 if_err_return(rv, send_sgr(cfg, cbg, fg & attr_default,

2537 bg & attr_default));

2538

2539 global.last_fg = fg;

2540 global.last_bg = bg;

2541

2542 return TB_OK;

2543 }

2544

2545 static int send_sgr(uintattr_t cfg, uintattr_t cbg, uintattr_t fg_is_default,

2546 uintattr_t bg_is_default) {

2547 int rv;

2548 char nbuf[32];

2549

2550 if (fg_is_default && bg_is_default) {

2551 return TB_OK;

2552 }

2553

2554 switch (global.output_mode) {

2555 default:

2556 case TB_OUTPUT_NORMAL:

2557 send_literal(rv, "\x1b[");

2558 if (!fg_is_default) {

2559 send_literal(rv, "3");

2560 send_num(rv, nbuf, cfg - 1);

2561 if (!bg_is_default) {

2562 send_literal(rv, ";");

2563 }

2564 }

2565 if (!bg_is_default) {

2566 send_literal(rv, "4");

2567 send_num(rv, nbuf, cbg - 1);

2568 }

2569 send_literal(rv, "m");

2570 break;

2571

2572 case TB_OUTPUT_256:

2573 case TB_OUTPUT_216:

2574 case TB_OUTPUT_GRAYSCALE:

2575 send_literal(rv, "\x1b[");

2576 if (!fg_is_default) {

2577 send_literal(rv, "38;5;");

2578 send_num(rv, nbuf, cfg);

2579 if (!bg_is_default) {

2580 send_literal(rv, ";");

2581 }

2582 }

2583 if (!bg_is_default) {

2584 send_literal(rv, "48;5;");

2585 send_num(rv, nbuf, cbg);

2586 }

2587 send_literal(rv, "m");

2588 break;

2589

2590 #ifdef TB_OPT_TRUECOLOR

2591 case TB_OUTPUT_TRUECOLOR:

2592 send_literal(rv, "\x1b[");

2593 if (!fg_is_default) {

2594 send_literal(rv, "38;2;");

2595 send_num(rv, nbuf, (cfg >> 16) & 0xff);

2596 send_literal(rv, ";");

2597 send_num(rv, nbuf, (cfg >> 8) & 0xff);

2598 send_literal(rv, ";");

2599 send_num(rv, nbuf, cfg & 0xff);

2600 if (!bg_is_default) {

2601 send_literal(rv, ";");

2602 }

2603 }

2604 if (!bg_is_default) {

2605 send_literal(rv, "48;2;");

2606 send_num(rv, nbuf, (cbg >> 16) & 0xff);

2607 send_literal(rv, ";");

2608 send_num(rv, nbuf, (cbg >> 8) & 0xff);

2609 send_literal(rv, ";");

2610 send_num(rv, nbuf, cbg & 0xff);

2611 }

2612 send_literal(rv, "m");

2613 break;

2614 #endif

2615 }

2616 return TB_OK;

2617 }

2618

2619 static int send_cursor_if(int x, int y) {

2620 int rv;

2621 char nbuf[32];

2622 if (x < 0 || y < 0) {

2623 return TB_OK;

2624 }

2625 send_literal(rv, "\x1b[");

2626 send_num(rv, nbuf, y + 1);

2627 send_literal(rv, ";");

2628 send_num(rv, nbuf, x + 1);

2629 send_literal(rv, "H");

2630 return TB_OK;

2631 }

2632

2633 static int send_char(int x, int y, uint32_t ch) {

2634 return send_cluster(x, y, &ch, 1);

2635 }

2636

2637 static int send_cluster(int x, int y, uint32_t *ch, size_t nch) {

2638 int rv, i;

2639 char abuf[8];

2640

2641 if (global.last_x != x - 1 || global.last_y != y) {

2642 if_err_return(rv, send_cursor_if(x, y));

2643 }

2644 global.last_x = x;

2645 global.last_y = y;

2646

2647 for (i = 0; i < (int)nch; i++) {

2648 uint32_t ach = *(ch + i);

2649 int aw = tb_utf8_unicode_to_char(abuf, ach);

2650 if (!ach) {

2651 abuf[0] = ' ';

2652 }

2653 if_err_return(rv,

2654 bytebuf_nputs(&global.out, abuf, (size_t)aw));

2655 }

2656

2657 return TB_OK;

2658 }

2659

2660 static int convert_num(uint32_t num, char *buf) {

2661 int i, l = 0;

2662 char ch;

2663 do {

2664 /* '0' = 48; 48 + num%10 < 58 < MAX_8bitCHAR */

2665 buf[l++] = (char)('0' + (num % 10));

2666 num /= 10;

2667 } while (num);

2668 for (i = 0; i < l / 2; i++) {

2669 ch = buf[i];

2670 buf[i] = buf[l - 1 - i];

2671 buf[l - 1 - i] = ch;

2672 }

2673 return l;

2674 }

2675

2676 static int cell_cmp(struct tb_cell *a, struct tb_cell *b) {

2677 if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) {

2678 return 1;

2679 }

2680 #ifdef TB_OPT_EGC

2681 if (a->nech != b->nech) {

2682 return 1;

2683 } else if (a->nech > 0) { /* a->nech == b->nech */

2684 return memcmp(a->ech, b->ech, a->nech);

2685 }

2686 #endif

2687 return 0;

2688 }

2689

2690 static int cell_copy(struct tb_cell *dst, struct tb_cell *src) {

2691 #ifdef TB_OPT_EGC

2692 if (src->nech > 0) {

2693 return cell_set(dst, src->ech, src->nech, src->fg, src->bg);

2694 }

2695 #endif

2696 return cell_set(dst, &src->ch, 1, src->fg, src->bg);

2697 }

2698

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

2700 uintattr_t fg, uintattr_t bg) {

2701 cell->ch = ch ? *ch : 0;

2702 cell->fg = fg;

2703 cell->bg = bg;

2704 #ifdef TB_OPT_EGC

2705 if (nch <= 1) {

2706 cell->nech = 0;

2707 } else {

2708 int rv;

2709 if_err_return(rv, cell_reserve_ech(cell, nch + 1));

2710 memcpy(cell->ech, ch, nch);

2711 cell->ech[nch] = '\0';

2712 cell->nech = nch;

2713 }

2714 #else

2715 (void)nch;

2716 (void)cell_reserve_ech;

2717 #endif

2718 return TB_OK;

2719 }

2720

2721 static int cell_reserve_ech(struct tb_cell *cell, size_t n) {

2722 #ifdef TB_OPT_EGC

2723 if (cell->cech >= n) {

2724 return TB_OK;

2725 }

2726 if (!(cell->ech = tb_realloc(cell->ech, n * sizeof(cell->ch)))) {

2727 return TB_ERR_MEM;

2728 }

2729 cell->cech = n;

2730 return TB_OK;

2731 #else

2732 (void)cell;

2733 (void)n;

2734 return TB_ERR;

2735 #endif

2736 }

2737

2738 static int cell_free(struct tb_cell *cell) {

2739 #ifdef TB_OPT_EGC

2740 if (cell->ech) {

2741 tb_free(cell->ech);

2742 }

2743 #endif

2744 memset(cell, 0, sizeof(*cell));

2745 return TB_OK;

2746 }

2747

2748 static int cellbuf_init(struct cellbuf_t *c, int w, int h) {

2749 c->cells = tb_malloc(sizeof(struct tb_cell) * w * h);

2750 if (!c->cells) {

2751 return TB_ERR_MEM;

2752 }

2753 memset(c->cells, 0, sizeof(struct tb_cell) * w * h);

2754 c->width = w;

2755 c->height = h;

2756 return TB_OK;

2757 }

2758

2759 static int cellbuf_free(struct cellbuf_t *c) {

2760 if (c->cells) {

2761 int i;

2762 for (i = 0; i < c->width * c->height; i++) {

2763 cell_free(&c->cells[i]);

2764 }

2765 tb_free(c->cells);

2766 }

2767 memset(c, 0, sizeof(*c));

2768 return TB_OK;

2769 }

2770

2771 static int cellbuf_clear(struct cellbuf_t *c) {

2772 int rv, i;

2773 uint32_t space = (uint32_t)' ';

2774 for (i = 0; i < c->width * c->height; i++) {

2775 if_err_return(rv, cell_set(

2776 &c->cells[i], &space, 1, global.fg, global.bg));

2777

2778 }

2779 return TB_OK;

2780 }

2781

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

2783 struct tb_cell **out) {

2784 if (x < 0 || x >= c->width || y < 0 || y >= c->height) {

2785 *out = NULL;

2786 return TB_ERR_OUT_OF_BOUNDS;

2787 }

2788 *out = &c->cells[(y * c->width) + x];

2789 return TB_OK;

2790 }

2791

2792 static int cellbuf_resize(struct cellbuf_t *c, int w, int h) {

2793 int rv;

2794

2795 int ow = c->width;

2796 int oh = c->height;

2797 int minw, minh, x, y;

2798 struct tb_cell *prev;

2799

2800 if (ow == w && oh == h) {

2801 return TB_OK;

2802 }

2803

2804 w = w < 1 ? 1 : w;

2805 h = h < 1 ? 1 : h;

2806

2807 minw = (w < ow) ? w : ow;

2808 minh = (h < oh) ? h : oh;

2809

2810 prev = c->cells;

2811

2812 if_err_return(rv, cellbuf_init(c, w, h));

2813 if_err_return(rv, cellbuf_clear(c));

2814

2815 x = 0;

2816 while (x < minw) {

2817 y = 0;

2818 while (y < minh) {

2819 struct tb_cell *src, *dst;

2820 src = &prev[(y * ow) + x];

2821 if_err_return(rv, cellbuf_get(c, x, y, &dst));

2822 if_err_return(rv, cell_copy(dst, src));

2823 y++;

2824 }

2825 x++;

2826 }

2827

2828 tb_free(prev);

2829

2830 return TB_OK;

2831 }

2832

2833 static int bytebuf_puts(struct bytebuf_t *b, const char *str) {

2834 return bytebuf_nputs(b, str, (size_t)strlen(str));

2835 }

2836

2837 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr) {

2838 int rv;

2839 if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1));

2840 memcpy(b->buf + b->len, str, nstr);

2841 b->len += nstr;

2842 b->buf[b->len] = '\0';

2843 return TB_OK;

2844 }

2845

2846 static int bytebuf_shift(struct bytebuf_t *b, size_t n) {

2847 size_t nmove;

2848 if (n > b->len) {

2849 n = b->len;

2850 }

2851 nmove = b->len - n;

2852 memmove(b->buf, b->buf + n, nmove);

2853 b->len -= n;

2854 return TB_OK;

2855 }

2856

2857 static int bytebuf_flush(struct bytebuf_t *b, int fd) {

2858 ssize_t write_rv;

2859 if (b->len <= 0) {

2860 return TB_OK;

2861 }

2862 write_rv = write(fd, b->buf, b->len);

2863 if (write_rv < 0 || (size_t)write_rv != b->len) {

2864 /* Note, errno will be 0 on partial write */

2865 global.last_errno = errno;

2866 return TB_ERR;

2867 }

2868 b->len = 0;

2869 return TB_OK;

2870 }

2871

2872 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz) {

2873 char *newbuf;

2874 size_t newcap;

2875

2876 if (b->cap >= sz) {

2877 return TB_OK;

2878 }

2879 newcap = b->cap > 0 ? b->cap : 1;

2880 while (newcap < sz) {

2881 newcap *= 2;

2882 }

2883 if (b->buf) {

2884 newbuf = tb_realloc(b->buf, newcap);

2885 } else {

2886 newbuf = tb_malloc(newcap);

2887 }

2888 if (!newbuf) {

2889 return TB_ERR_MEM;

2890 }

2891 b->buf = newbuf;

2892 b->cap = newcap;

2893 return TB_OK;

2894 }

2895

2896 static int bytebuf_free(struct bytebuf_t *b) {

2897 if (b->buf) {

2898 tb_free(b->buf);

2899 }

2900 memset(b, 0, sizeof(*b));

2901 return TB_OK;

2902 }

2903