💾 Archived View for gemini.rmf-dev.com › repo › Vaati › mz › files › 31cbaf5fd07b6393eb6c13c8dc2c760… captured on 2023-01-29 at 17:36:09. Gemini links have been rewritten to link to archived content

View Raw

More Information

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

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 #include "termbox.h"

27

28 void cfmakeraw(struct termios *t) {

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

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

31 t->c_oflag &= ~OPOST;

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

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

34 t->c_cflag |= CS8;

35 }

36

37 #define index_fail ((size_t)-1)

38

39 #define if_err_return(rv, expr) \

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

41 return (rv)

42 #define if_err_break(rv, expr) \

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

44 break

45 #define if_ok_return(rv, expr) \

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

47 return (rv)

48 #define if_ok_or_need_more_return(rv, expr) \

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

50 return (rv)

51

52 #define send_literal(rv, a) \

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

54

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

56 if_err_return((rv), \

57 bytebuf_nputs(&global.out, (nbuf), convert_num((n), (nbuf))))

58

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

60 va_list args;

61 int rv;

62

63 va_start(args, fmt);

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

65 va_end(args);

66

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

68 return TB_ERR;

69 return TB_OK;

70 }

71

72 #define if_not_init_return() \

73 if (!global.initialized) \

74 return TB_ERR_NOT_INIT

75

76 struct bytebuf_t {

77 char *buf;

78 size_t len;

79 size_t cap;

80 };

81

82 struct cellbuf_t {

83 int width;

84 int height;

85 struct tb_cell *cells;

86 };

87

88 struct cap_trie_t {

89 char c;

90 struct cap_trie_t *children;

91 size_t nchildren;

92 int is_leaf;

93 uint16_t key;

94 uint8_t mod;

95 };

96

97 struct tb_global_t {

98 int ttyfd;

99 int rfd;

100 int wfd;

101 int ttyfd_open;

102 int resize_pipefd[2];

103 int width;

104 int height;

105 int cursor_x;

106 int cursor_y;

107 int last_x;

108 int last_y;

109 uintattr_t fg;

110 uintattr_t bg;

111 uintattr_t last_fg;

112 uintattr_t last_bg;

113 int input_mode;

114 int output_mode;

115 char *terminfo;

116 size_t nterminfo;

117 const char *caps[TB_CAP__COUNT];

118 struct cap_trie_t cap_trie;

119 struct bytebuf_t in;

120 struct bytebuf_t out;

121 struct cellbuf_t back;

122 struct cellbuf_t front;

123 struct termios orig_tios;

124 int has_orig_tios;

125 int last_errno;

126 int initialized;

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

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

129 char errbuf[1024];

130 };

131

132 static struct tb_global_t global = {0};

133

134 /* BEGIN codegen c */

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

136

137 static const int16_t terminfo_cap_indexes[] = {

138 66, /* kf1 (TB_CAP_F1) */

139 68, /* kf2 (TB_CAP_F2) */

140 69, /* kf3 (TB_CAP_F3) */

141 70, /* kf4 (TB_CAP_F4) */

142 71, /* kf5 (TB_CAP_F5) */

143 72, /* kf6 (TB_CAP_F6) */

144 73, /* kf7 (TB_CAP_F7) */

145 74, /* kf8 (TB_CAP_F8) */

146 75, /* kf9 (TB_CAP_F9) */

147 67, /* kf10 (TB_CAP_F10) */

148 216, /* kf11 (TB_CAP_F11) */

149 217, /* kf12 (TB_CAP_F12) */

150 77, /* kich1 (TB_CAP_INSERT) */

151 59, /* kdch1 (TB_CAP_DELETE) */

152 76, /* khome (TB_CAP_HOME) */

153 164, /* kend (TB_CAP_END) */

154 82, /* kpp (TB_CAP_PGUP) */

155 81, /* knp (TB_CAP_PGDN) */

156 87, /* kcuu1 (TB_CAP_ARROW_UP) */

157 61, /* kcud1 (TB_CAP_ARROW_DOWN) */

158 79, /* kcub1 (TB_CAP_ARROW_LEFT) */

159 83, /* kcuf1 (TB_CAP_ARROW_RIGHT) */

160 148, /* kcbt (TB_CAP_BACK_TAB) */

161 28, /* smcup (TB_CAP_ENTER_CA) */

162 40, /* rmcup (TB_CAP_EXIT_CA) */

163 16, /* cnorm (TB_CAP_SHOW_CURSOR) */

164 13, /* civis (TB_CAP_HIDE_CURSOR) */

165 5, /* clear (TB_CAP_CLEAR_SCREEN) */

166 39, /* sgr0 (TB_CAP_SGR0) */

167 36, /* smul (TB_CAP_UNDERLINE) */

168 27, /* bold (TB_CAP_BOLD) */

169 26, /* blink (TB_CAP_BLINK) */

170 311, /* sitm (TB_CAP_ITALIC) */

171 34, /* rev (TB_CAP_REVERSE) */

172 89, /* smkx (TB_CAP_ENTER_KEYPAD) */

173 88, /* rmkx (TB_CAP_EXIT_KEYPAD) */

174 };

175

176 /* xterm */

177 static const char *xterm_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

214 };

215

216 /* linux */

217 static const char *linux_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

254 };

255

256 /* screen */

257 static const char *screen_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

294 };

295

296 /* rxvt-256color */

297 static const char *rxvt_256color_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

334 };

335

336 /* rxvt-unicode */

337 static const char *rxvt_unicode_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

374 };

375

376 /* Eterm */

377 static const char *eterm_caps[] = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

414 };

415

416 static struct {

417 const char *name;

418 const char **caps;

419 const char *alias;

420 } builtin_terms[] = {

421 {"xterm", xterm_caps, "" },

422 {"linux", linux_caps, "" },

423 {"screen", screen_caps, "tmux"},

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

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

426 {"Eterm", eterm_caps, "" },

427 {NULL, NULL, NULL },

428 };

429

430 /* END codegen c */

431

432 static struct {

433 const char *cap;

434 const uint16_t key;

435 const uint8_t mod;

436 } builtin_mod_caps[] = {

437 /* xterm arrows */

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

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

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

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

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

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

444 {"\x1b[1;8A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

445

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

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

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

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

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

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

452 {"\x1b[1;8B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

453

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

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

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

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

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

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

460 {"\x1b[1;8C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

461

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

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

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

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

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

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

468 {"\x1b[1;8D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

469

470 /* xterm keys */

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

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

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

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

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

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

477 {"\x1b[1;8H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

478

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

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

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

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

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

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

485 {"\x1b[1;8F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

486

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

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

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

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

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

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

493 {"\x1b[2;8~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

494

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

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

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

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

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

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

501 {"\x1b[3;8~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

502

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

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

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

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

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

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

509 {"\x1b[5;8~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

510

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

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

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

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

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

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

517 {"\x1b[6;8~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

518

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

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

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

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

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

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

525 {"\x1b[1;8P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

526

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

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

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

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

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

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

533 {"\x1b[1;8Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

534

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

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

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

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

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

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

541 {"\x1b[1;8R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

542

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

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

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

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

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

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

549 {"\x1b[1;8S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

550

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

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

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

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

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

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

557 {"\x1b[15;8~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

558

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

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

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

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

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

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

565 {"\x1b[17;8~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

566

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

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

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

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

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

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

573 {"\x1b[18;8~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

574

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

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

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

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

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

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

581 {"\x1b[19;8~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

582

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

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

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

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

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

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

589 {"\x1b[20;8~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

590

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

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

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

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

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

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

597 {"\x1b[21;8~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

598

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

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

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

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

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

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

605 {"\x1b[23;8~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

606

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

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

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

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

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

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

613 {"\x1b[24;8~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},

614

615 /* rxvt arrows */

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

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

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

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

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

621

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

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

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

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

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

627

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

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

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

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

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

633

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

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

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

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

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

639

640 /* rxvt keys */

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

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

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

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

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

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

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

648

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

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

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

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

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

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

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

656

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

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

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

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

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

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

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

664

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

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

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

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

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

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

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

672

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

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

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

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

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

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

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

680

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

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

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

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

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

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

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

688

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

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

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

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

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

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

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

696

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

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

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

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

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

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

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

704

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

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

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

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

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

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

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

712

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

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

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

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

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

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

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

720

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

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

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

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

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

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

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

728

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

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

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

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

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

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

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

736

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

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

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

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

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

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

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

744

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

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

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

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

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

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

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

752

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

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

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

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

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

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

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

760

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

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

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

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

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

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

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

768

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

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

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

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

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

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

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

776

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

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

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

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

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

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

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

784

785 /* linux console/putty arrows */

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

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

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

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

790

791 /* more putty arrows */

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

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

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

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

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

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

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

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

800

801 {NULL, 0, 0 },

802 };

803

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

805 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

806 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

807 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

808 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

809 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

810 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

811 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

812 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

813 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,

814 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1};

815

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

817

818 static int tb_reset(void);

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

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

821 static int init_term_attrs(void);

822 static int init_term_caps(void);

823 static int init_cap_trie(void);

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

825 static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,

826 size_t *depth);

827 static int cap_trie_deinit(struct cap_trie_t *node);

828 static int init_resize_handler(void);

829 static int send_init_escape_codes(void);

830 static int send_clear(void);

831 static int update_term_size(void);

832 static int update_term_size_via_esc(void);

833 static int init_cellbuf(void);

834 static int tb_deinit(void);

835 static int load_terminfo(void);

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

837 static int read_terminfo_path(const char *path);

838 static int parse_terminfo_caps(void);

839 static int load_builtin_caps(void);

840 static const char *get_terminfo_string(int16_t str_offsets_pos,

841 int16_t str_table_pos, int16_t str_table_len, int16_t str_index);

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

843 static int extract_event(struct tb_event *event);

844 static int extract_esc(struct tb_event *event);

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

846 static int extract_esc_cap(struct tb_event *event);

847 static int extract_esc_mouse(struct tb_event *event);

848 static int resize_cellbufs(void);

849 static void handle_resize(int sig);

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

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

852 uintattr_t bg_is_default);

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

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

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

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

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

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

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

860 uintattr_t fg, uintattr_t bg);

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

862 static int cell_free(struct tb_cell *cell);

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

864 static int cellbuf_free(struct cellbuf_t *c);

865 static int cellbuf_clear(struct cellbuf_t *c);

866 static int cellbuf_get(struct cellbuf_t *c, int x, int y, struct tb_cell **out);

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

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

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

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

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

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

873 static int bytebuf_free(struct bytebuf_t *b);

874

875 int tb_init(void) {

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

877 }

878

879 int tb_init_file(const char *path) {

880

881 int ttyfd;

882

883 if (global.initialized) {

884 return TB_ERR_INIT_ALREADY;

885 }

886 ttyfd = open(path, O_RDWR);

887 if (ttyfd < 0) {

888 global.last_errno = errno;

889 return TB_ERR_INIT_OPEN;

890 }

891 global.ttyfd_open = 1;

892 return tb_init_fd(ttyfd);

893 }

894

895 int tb_init_fd(int ttyfd) {

896 return tb_init_rwfd(ttyfd, ttyfd);

897 }

898

899 int tb_init_rwfd(int rfd, int wfd) {

900 int rv;

901

902 tb_reset();

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

904 global.rfd = rfd;

905 global.wfd = wfd;

906

907 do {

908 if_err_break(rv, init_term_attrs());

909 if_err_break(rv, init_term_caps());

910 if_err_break(rv, init_cap_trie());

911 if_err_break(rv, init_resize_handler());

912 if_err_break(rv, send_init_escape_codes());

913 if_err_break(rv, send_clear());

914 if_err_break(rv, update_term_size());

915 if_err_break(rv, init_cellbuf());

916 global.initialized = 1;

917 } while (0);

918

919 if (rv != TB_OK) {

920 tb_deinit();

921 }

922

923 return rv;

924 }

925

926 int tb_shutdown(void) {

927 if_not_init_return();

928 tb_deinit();

929 return TB_OK;

930 }

931

932 int tb_width(void) {

933 if_not_init_return();

934 return global.width;

935 }

936

937 int tb_height(void) {

938 if_not_init_return();

939 return global.height;

940 }

941

942 int tb_clear(void) {

943 if_not_init_return();

944 return cellbuf_clear(&global.back);

945 }

946

947 int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) {

948 if_not_init_return();

949 global.fg = fg;

950 global.bg = bg;

951 return TB_OK;

952 }

953

954 int tb_present(void) {

955

956 int rv, x, y, i;

957

958 if_not_init_return();

959

960 /* TODO Assert global.back.(width,height) == global.front.(width,height) */

961

962 global.last_x = -1;

963 global.last_y = -1;

964

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

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

967 struct tb_cell *back, *front;

968 int w;

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

970 if_err_return(rv, cellbuf_get(&global.front, x, y, &front));

971

972 {

973 #ifdef TB_OPT_EGC

974 if (back->nech > 0)

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

976 else

977 #endif

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

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

980 }

981 if (w < 1) {

982 w = 1;

983 }

984

985 if (cell_cmp(back, front) != 0) {

986 cell_copy(front, back);

987

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

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

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

991 send_char(i, y, ' ');

992 }

993 } else {

994 {

995 #ifdef TB_OPT_EGC

996 if (back->nech > 0)

997 send_cluster(x, y, back->ech, back->nech);

998 else

999 #endif

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

1001 }

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

1003 struct tb_cell *front_wide;

1004 if_err_return(rv,

1005 cellbuf_get(&global.front, x + i, y, &front_wide));

1006 if_err_return(rv,

1007 cell_set(front_wide, 0, 1, back->fg, back->bg));

1008 }

1009 }

1010 }

1011 x += w;

1012 }

1013 }

1014

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

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

1017

1018 return TB_OK;

1019 }

1020

1021 int tb_set_cursor(int cx, int cy) {

1022 int rv;

1023 if_not_init_return();

1024 if (cx < 0)

1025 cx = 0;

1026 if (cy < 0)

1027 cy = 0;

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

1029 if_err_return(rv,

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

1031 }

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

1033 global.cursor_x = cx;

1034 global.cursor_y = cy;

1035 return TB_OK;

1036 }

1037

1038 int tb_hide_cursor(void) {

1039 int rv;

1040 if_not_init_return();

1041 if (global.cursor_x >= 0) {

1042 if_err_return(rv,

1043 bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));

1044 }

1045 global.cursor_x = -1;

1046 global.cursor_y = -1;

1047 return TB_OK;

1048 }

1049

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

1051 if_not_init_return();

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

1053 }

1054

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

1056 uintattr_t bg) {

1057 int rv;

1058 struct tb_cell *cell;

1059 if_not_init_return();

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

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

1062 return TB_OK;

1063 }

1064

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

1066 if_not_init_return();

1067 #ifdef TB_OPT_EGC

1068 int rv;

1069 struct tb_cell *cell;

1070 size_t nech;

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

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

1073 nech = cell->nech + 1;

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

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

1076 } else { /* make new ech */

1077 nech = 2;

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

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

1080 cell->ech[1] = ch;

1081 }

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

1083 cell->nech = nech;

1084 return TB_OK;

1085 #else

1086 (void)x;

1087 (void)y;

1088 (void)ch;

1089 return TB_ERR;

1090 #endif

1091 }

1092

1093 int tb_set_input_mode(int mode) {

1094 if_not_init_return();

1095 if (mode == TB_INPUT_CURRENT) {

1096 return global.input_mode;

1097 }

1098

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

1100 mode |= TB_INPUT_ESC;

1101 }

1102

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

1104 (TB_INPUT_ESC | TB_INPUT_ALT)) {

1105 mode &= ~TB_INPUT_ALT;

1106 }

1107

1108 if (mode & TB_INPUT_MOUSE) {

1109 bytebuf_puts(&global.out, TB_HARDCAP_ENTER_MOUSE);

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

1111 } else {

1112 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);

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

1114 }

1115

1116 global.input_mode = mode;

1117 return TB_OK;

1118 }

1119

1120 int tb_set_output_mode(int mode) {

1121 if_not_init_return();

1122 switch (mode) {

1123 case TB_OUTPUT_CURRENT:

1124 return global.output_mode;

1125 case TB_OUTPUT_NORMAL:

1126 case TB_OUTPUT_256:

1127 case TB_OUTPUT_216:

1128 case TB_OUTPUT_GRAYSCALE:

1129 #ifdef TB_OPT_TRUECOLOR

1130 case TB_OUTPUT_TRUECOLOR:

1131 #endif

1132 global.output_mode = mode;

1133 return TB_OK;

1134 }

1135 return TB_ERR;

1136 }

1137

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

1139 if_not_init_return();

1140 return wait_event(event, timeout_ms);

1141 }

1142

1143 int tb_poll_event(struct tb_event *event) {

1144 if_not_init_return();

1145 return wait_event(event, -1);

1146 }

1147

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

1149 if_not_init_return();

1150

1151 *ttyfd = global.rfd;

1152 *resizefd = global.resize_pipefd[0];

1153

1154 return TB_OK;

1155 }

1156

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

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

1159 }

1160

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

1162 const char *str) {

1163 int rv;

1164 uint32_t uni;

1165 int w, ix = x;

1166 if (out_w) {

1167 *out_w = 0;

1168 }

1169 while (*str) {

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

1171 w = wcwidth((wchar_t)uni);

1172 if (w < 0) {

1173 w = 1;

1174 }

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

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

1177 } else {

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

1179 }

1180 x += w;

1181 if (out_w) {

1182 *out_w += w;

1183 }

1184 }

1185 return TB_OK;

1186 }

1187

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

1189 ...) {

1190 int rv;

1191 va_list vl;

1192 va_start(vl, fmt);

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

1194 va_end(vl);

1195 return rv;

1196 }

1197

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

1199 const char *fmt, ...) {

1200 int rv;

1201 va_list vl;

1202 va_start(vl, fmt);

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

1204 va_end(vl);

1205 return rv;

1206 }

1207

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

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

1210 }

1211

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

1213 int rv;

1214 char buf[TB_OPT_PRINTF_BUF];

1215 va_list vl;

1216 va_start(vl, fmt);

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

1218 va_end(vl);

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

1220 return TB_ERR;

1221 }

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

1223 }

1224

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

1226 switch (fn_type) {

1227 case TB_FUNC_EXTRACT_PRE:

1228 global.fn_extract_esc_pre = fn;

1229 return TB_OK;

1230 case TB_FUNC_EXTRACT_POST:

1231 global.fn_extract_esc_post = fn;

1232 return TB_OK;

1233 }

1234 return TB_ERR;

1235 }

1236

1237 struct tb_cell *tb_cell_buffer(void) {

1238 if (!global.initialized)

1239 return NULL;

1240 return global.back.cells;

1241 }

1242

1243 int tb_utf8_char_length(char c) {

1244 return utf8_length[(unsigned char)c];

1245 }

1246

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

1248

1249 int i;

1250 unsigned char len, mask;

1251 uint32_t result;

1252

1253 if (*c == 0) {

1254 return TB_ERR;

1255 }

1256

1257 len = tb_utf8_char_length(*c);

1258 mask = utf8_mask[len - 1];

1259 result = c[0] & mask;

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

1261 result <<= 6;

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

1263 }

1264

1265 *out = result;

1266 return (int)len;

1267 }

1268

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

1270 int len = 0;

1271 int first;

1272 int i;

1273

1274 if (c < 0x80) {

1275 first = 0;

1276 len = 1;

1277 } else if (c < 0x800) {

1278 first = 0xc0;

1279 len = 2;

1280 } else if (c < 0x10000) {

1281 first = 0xe0;

1282 len = 3;

1283 } else if (c < 0x200000) {

1284 first = 0xf0;

1285 len = 4;

1286 } else if (c < 0x4000000) {

1287 first = 0xf8;

1288 len = 5;

1289 } else {

1290 first = 0xfc;

1291 len = 6;

1292 }

1293

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

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

1296 c >>= 6;

1297 }

1298 out[0] = c | first;

1299

1300 return len;

1301 }

1302

1303 int tb_last_errno(void) {

1304 return global.last_errno;

1305 }

1306

1307 const char *tb_strerror(int err) {

1308 switch (err) {

1309 case TB_OK:

1310 return "Success";

1311 case TB_ERR_NEED_MORE:

1312 return "Not enough input";

1313 case TB_ERR_INIT_ALREADY:

1314 return "Termbox initialized already";

1315 case TB_ERR_MEM:

1316 return "Out of memory";

1317 case TB_ERR_NO_EVENT:

1318 return "No event";

1319 case TB_ERR_NO_TERM:

1320 return "No TERM in environment";

1321 case TB_ERR_NOT_INIT:

1322 return "Termbox not initialized";

1323 case TB_ERR_OUT_OF_BOUNDS:

1324 return "Out of bounds";

1325 case TB_ERR_UNSUPPORTED_TERM:

1326 return "Unsupported terminal";

1327 case TB_ERR_CAP_COLLISION:

1328 return "Termcaps collision";

1329 case TB_ERR_RESIZE_SSCANF:

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

1331 "resize";

1332 case TB_ERR:

1333 case TB_ERR_INIT_OPEN:

1334 case TB_ERR_READ:

1335 case TB_ERR_RESIZE_IOCTL:

1336 case TB_ERR_RESIZE_PIPE:

1337 case TB_ERR_RESIZE_SIGACTION:

1338 case TB_ERR_POLL:

1339 case TB_ERR_TCGETATTR:

1340 case TB_ERR_TCSETATTR:

1341 case TB_ERR_RESIZE_WRITE:

1342 case TB_ERR_RESIZE_POLL:

1343 case TB_ERR_RESIZE_READ:

1344 default:

1345 strerror_r(global.last_errno, global.errbuf,

1346 sizeof(global.errbuf));

1347 return (const char *)global.errbuf;

1348 }

1349 }

1350

1351 int tb_has_truecolor(void) {

1352 #ifdef TB_OPT_TRUECOLOR

1353 return 1;

1354 #else

1355 return 0;

1356 #endif

1357 }

1358

1359 int tb_has_egc(void) {

1360 #ifdef TB_OPT_EGC

1361 return 1;

1362 #else

1363 return 0;

1364 #endif

1365 }

1366

1367 const char *tb_version(void) {

1368 return TB_VERSION_STR;

1369 }

1370

1371 static int tb_reset(void) {

1372 int ttyfd_open = global.ttyfd_open;

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

1374 global.ttyfd = -1;

1375 global.rfd = -1;

1376 global.wfd = -1;

1377 global.ttyfd_open = ttyfd_open;

1378 global.resize_pipefd[0] = -1;

1379 global.resize_pipefd[1] = -1;

1380 global.width = -1;

1381 global.height = -1;

1382 global.cursor_x = -1;

1383 global.cursor_y = -1;

1384 global.last_x = -1;

1385 global.last_y = -1;

1386 global.fg = TB_DEFAULT;

1387 global.bg = TB_DEFAULT;

1388 global.last_fg = ~global.fg;

1389 global.last_bg = ~global.bg;

1390 global.input_mode = TB_INPUT_ESC;

1391 global.output_mode = TB_OUTPUT_NORMAL;

1392 return TB_OK;

1393 }

1394

1395 static int init_term_attrs(void) {

1396

1397 struct termios tios;

1398

1399 if (global.ttyfd < 0) {

1400 return TB_OK;

1401 }

1402

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

1404 global.last_errno = errno;

1405 return TB_ERR_TCGETATTR;

1406 }

1407

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

1409 global.has_orig_tios = 1;

1410

1411 cfmakeraw(&tios);

1412 tios.c_cc[VMIN] = 1;

1413 tios.c_cc[VTIME] = 0;

1414

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

1416 global.last_errno = errno;

1417 return TB_ERR_TCSETATTR;

1418 }

1419

1420 return TB_OK;

1421 }

1422

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

1424 const char *fmt, va_list vl) {

1425 int rv;

1426 char buf[TB_OPT_PRINTF_BUF];

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

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

1429 return TB_ERR;

1430 }

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

1432 }

1433

1434 static int init_term_caps(void) {

1435 if (load_terminfo() == TB_OK) {

1436 return parse_terminfo_caps();

1437 }

1438 return load_builtin_caps();

1439 }

1440

1441 static int init_cap_trie(void) {

1442 int rv, i;

1443

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

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

1446 if_err_return(rv, cap_trie_add(global.caps[i], tb_key_i(i), 0));

1447 }

1448

1449 /* Add built-in mod caps */

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

1451 rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key,

1452 builtin_mod_caps[i].mod);

1453 /*

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

1455 * builtin_mod_caps. It is desirable to give precedence to global.caps

1456 * here.

1457 */

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

1459 return rv;

1460 }

1461 }

1462

1463 return TB_OK;

1464 }

1465

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

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

1468 size_t i, j;

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

1470 char c = cap[i];

1471 next = NULL;

1472

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

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

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

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

1477 break;

1478 }

1479 }

1480 if (!next) {

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

1482 node->nchildren += 1;

1483 node->children =

1484 tb_realloc(node->children, sizeof(*node) * node->nchildren);

1485 if (!node->children) {

1486 return TB_ERR_MEM;

1487 }

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

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

1490 next->c = c;

1491 }

1492

1493 /* Continue */

1494 node = next;

1495 }

1496

1497 if (node->is_leaf) {

1498 /* Already a leaf here */

1499 return TB_ERR_CAP_COLLISION;

1500 }

1501

1502 node->is_leaf = 1;

1503 node->key = key;

1504 node->mod = mod;

1505 return TB_OK;

1506 }

1507

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

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

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

1511 size_t i, j;

1512 *last = node;

1513 *depth = 0;

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

1515 char c = buf[i];

1516 next = NULL;

1517

1518 /* Find c in node.children */

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

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

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

1522 break;

1523 }

1524 }

1525 if (!next) {

1526 /* Not found */

1527 return TB_OK;

1528 }

1529 node = next;

1530 *last = node;

1531 *depth += 1;

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

1533 break;

1534 }

1535 }

1536 return TB_OK;

1537 }

1538

1539 static int cap_trie_deinit(struct cap_trie_t *node) {

1540 size_t j;

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

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

1543 }

1544 if (node->children) {

1545 tb_free(node->children);

1546 }

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

1548 return TB_OK;

1549 }

1550

1551 static int init_resize_handler(void) {

1552 struct sigaction sa;

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

1554 global.last_errno = errno;

1555 return TB_ERR_RESIZE_PIPE;

1556 }

1557

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

1559 sa.sa_handler = handle_resize;

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

1561 global.last_errno = errno;

1562 return TB_ERR_RESIZE_SIGACTION;

1563 }

1564

1565 return TB_OK;

1566 }

1567

1568 static int send_init_escape_codes(void) {

1569 int rv;

1570 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA]));

1571 if_err_return(rv,

1572 bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_KEYPAD]));

1573 if_err_return(rv,

1574 bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));

1575 return TB_OK;

1576 }

1577

1578 static int send_clear(void) {

1579 int rv;

1580

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

1582 if_err_return(rv,

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

1584

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

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

1587

1588 global.last_x = -1;

1589 global.last_y = -1;

1590

1591 return TB_OK;

1592 }

1593

1594 static int update_term_size(void) {

1595 int rv, ioctl_errno;

1596 struct winsize sz;

1597

1598 if (global.ttyfd < 0) {

1599 return TB_OK;

1600 }

1601

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

1603

1604 /* Try ioctl TIOCGWINSZ */

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

1606 global.width = sz.ws_col;

1607 global.height = sz.ws_row;

1608 return TB_OK;

1609 }

1610 ioctl_errno = errno;

1611

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

1613 if_ok_return(rv, update_term_size_via_esc());

1614

1615 global.last_errno = ioctl_errno;

1616 return TB_ERR_RESIZE_IOCTL;

1617 }

1618

1619 static int update_term_size_via_esc(void) {

1620 #ifndef TB_RESIZE_FALLBACK_MS

1621 #define TB_RESIZE_FALLBACK_MS 1000

1622 #endif

1623

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

1625 int rw, rh;

1626 char buf[TB_OPT_READ_BUF];

1627 ssize_t write_rv, read_rv;

1628 int select_rv;

1629 struct timeval timeout;

1630 fd_set fds;

1631

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

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

1634 return TB_ERR_RESIZE_WRITE;

1635 }

1636

1637 FD_ZERO(&fds);

1638 FD_SET(global.rfd, &fds);

1639

1640 timeout.tv_sec = 0;

1641 timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000;

1642

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

1644

1645 if (select_rv != 1) {

1646 global.last_errno = errno;

1647 return TB_ERR_RESIZE_POLL;

1648 }

1649

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

1651 if (read_rv < 1) {

1652 global.last_errno = errno;

1653 return TB_ERR_RESIZE_READ;

1654 }

1655 buf[read_rv] = '\0';

1656

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

1658 return TB_ERR_RESIZE_SSCANF;

1659 }

1660

1661 global.width = rw;

1662 global.height = rh;

1663 return TB_OK;

1664 }

1665

1666 static int init_cellbuf(void) {

1667 int rv;

1668 if_err_return(rv, cellbuf_init(&global.back, global.width, global.height));

1669 if_err_return(rv, cellbuf_init(&global.front,

1670 global.width, global.height));

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

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

1673 return TB_OK;

1674 }

1675

1676 static int tb_deinit(void) {

1677

1678 struct sigaction sig = {0};

1679

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

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

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

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

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

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

1686 bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);

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

1688 }

1689 if (global.ttyfd >= 0) {

1690 if (global.has_orig_tios) {

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

1692 }

1693 if (global.ttyfd_open) {

1694 close(global.ttyfd);

1695 global.ttyfd_open = 0;

1696 }

1697 }

1698

1699 sig.sa_handler = SIG_DFL;

1700 sigaction(SIGWINCH, &sig, NULL);

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

1702 close(global.resize_pipefd[0]);

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

1704 close(global.resize_pipefd[1]);

1705

1706 cellbuf_free(&global.back);

1707 cellbuf_free(&global.front);

1708 bytebuf_free(&global.in);

1709 bytebuf_free(&global.out);

1710

1711 if (global.terminfo)

1712 tb_free(global.terminfo);

1713

1714 cap_trie_deinit(&global.cap_trie);

1715

1716 tb_reset();

1717 return TB_OK;

1718 }

1719

1720 static int load_terminfo(void) {

1721 int rv;

1722 char tmp[PATH_MAX];

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

1724

1725 /*

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

1727 * this behavior. Some of these paths are compile-time ncurses options, so

1728 * best guesses are used here.

1729 */

1730 term = getenv("TERM");

1731 if (!term) {

1732 return TB_ERR;

1733 }

1734

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

1736 terminfo = getenv("TERMINFO");

1737 if (terminfo) {

1738 return load_terminfo_from_path(terminfo, term);

1739 }

1740

1741 /* Next try ~/.terminfo */

1742 home = getenv("HOME");

1743 if (home) {

1744 if_err_return(rv, snprintf_(tmp, sizeof(tmp), "%s/.terminfo", home));

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

1746 }

1747

1748 /*

1749 * Next try TERMINFO_DIRS

1750 *

1751 * Note, empty entries are supposed to be interpretted as the "compiled-in

1752 * default", which is of course system-dependent. Previously /etc/terminfo

1753 * was used here. Let's skip empty entries altogether rather than give

1754 * precedence to a guess, and check common paths after this loop.

1755 */

1756 dirs = getenv("TERMINFO_DIRS");

1757 if (dirs) {

1758 char *dir;

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

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

1761 while (dir) {

1762 const char *cdir = dir;

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

1764 if_ok_return(rv, load_terminfo_from_path(cdir, term));

1765 }

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

1767 }

1768 }

1769

1770 #ifdef TB_TERMINFO_DIR

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

1772 #endif

1773 if_ok_return(rv, load_terminfo_from_path("/usr/local/etc/terminfo", term));

1774 if_ok_return(rv,

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

1776 if_ok_return(rv, load_terminfo_from_path("/usr/local/lib/terminfo", term));

1777 if_ok_return(rv, load_terminfo_from_path("/etc/terminfo", term));

1778 if_ok_return(rv, load_terminfo_from_path("/usr/share/terminfo", term));

1779 if_ok_return(rv, load_terminfo_from_path("/usr/lib/terminfo", term));

1780 if_ok_return(rv, load_terminfo_from_path("/usr/share/lib/terminfo", term));

1781 if_ok_return(rv, load_terminfo_from_path("/lib/terminfo", term));

1782

1783 return TB_ERR;

1784 }

1785

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

1787 char tmp[PATH_MAX];

1788 int rv;

1789

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

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

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

1793 if_ok_return(rv, read_terminfo_path(tmp));

1794

1795 #ifdef __APPLE__

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

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

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

1799 return read_terminfo_path(tmp);

1800 #endif

1801

1802 return TB_ERR;

1803 }

1804

1805 static int read_terminfo_path(const char *path) {

1806 size_t fsize;

1807 char *data;

1808 struct stat st;

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

1810 if (!fp) {

1811 return TB_ERR;

1812 }

1813

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

1815 fclose(fp);

1816 return TB_ERR;

1817 }

1818

1819 fsize = st.st_size;

1820 data = tb_malloc(fsize);

1821 if (!data) {

1822 fclose(fp);

1823 return TB_ERR;

1824 }

1825

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

1827 fclose(fp);

1828 tb_free(data);

1829 return TB_ERR;

1830 }

1831

1832 global.terminfo = data;

1833 global.nterminfo = fsize;

1834

1835 fclose(fp);

1836 return TB_OK;

1837 }

1838

1839 static int parse_terminfo_caps(void) {

1840 /*

1841 * See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a

1842 * description of this behavior.

1843 */

1844 int16_t *header;

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

1846

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

1848 if (global.nterminfo < 6) {

1849 return TB_ERR;

1850 }

1851

1852 header = (int16_t *)global.terminfo;

1853 /*

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

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

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

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

1858 * header[4] the number of offsets (short integers) in the strings section

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

1860 */

1861

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

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

1864 : 2; /* 16-bit */

1865

1866 /*

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

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

1869 * begins on an even byte

1870 */

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

1872

1873 pos_str_offsets =

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

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

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

1877 + align_offset +

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

1879

1880 pos_str_table =

1881 pos_str_offsets +

1882 (header[4] * sizeof(int16_t)); /* length of string offsets table */

1883

1884 /* Load caps */

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

1886 const char *cap = get_terminfo_string(pos_str_offsets, pos_str_table,

1887 header[5], terminfo_cap_indexes[i]);

1888 if (!cap) {

1889 /* Something is not right */

1890 return TB_ERR;

1891 }

1892 global.caps[i] = cap;

1893 }

1894

1895 return TB_OK;

1896 }

1897

1898 static int load_builtin_caps(void) {

1899 int i, j;

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

1901

1902 if (!term) {

1903 return TB_ERR_NO_TERM;

1904 }

1905

1906 /* Check for exact TERM match */

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

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

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

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

1911 }

1912 return TB_OK;

1913 }

1914 }

1915

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

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

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

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

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

1921 {

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

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

1924 }

1925 return TB_OK;

1926 }

1927 }

1928

1929 return TB_ERR_UNSUPPORTED_TERM;

1930 }

1931

1932 static const char *get_terminfo_string(int16_t str_offsets_pos,

1933 int16_t str_table_pos, int16_t str_table_len, int16_t str_index) {

1934 const int16_t *str_offset =

1935 (int16_t *)(global.terminfo + (int)str_offsets_pos +

1936 ((int)str_index * (int)sizeof(int16_t)));

1937 if (*str_offset < 0) {

1938 /* A negative indicates the cap is absent from this terminal */

1939 return "";

1940 }

1941 if (*str_offset >= str_table_len) {

1942 /* Invalid string offset */

1943 return NULL;

1944 }

1945 if (((size_t)((int)str_table_pos + (int)*str_offset)) >=

1946 global.nterminfo) {

1947 /* Truncated/corrupt terminfo? */

1948 return NULL;

1949 }

1950 return (

1951 const char *)(global.terminfo + (int)str_table_pos + (int)*str_offset);

1952 }

1953

1954 static int wait_event(struct tb_event *event, int timeout) {

1955 int rv;

1956 char buf[TB_OPT_READ_BUF];

1957 fd_set fds;

1958 struct timeval tv;

1959

1960 memset(event, 0, sizeof(*event));

1961 if_ok_return(rv, extract_event(event));

1962

1963 tv.tv_sec = timeout / 1000;

1964 tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;

1965

1966 do {

1967 int maxfd, select_rv, tty_has_events, resize_has_events;

1968 FD_ZERO(&fds);

1969 FD_SET(global.rfd, &fds);

1970 FD_SET(global.resize_pipefd[0], &fds);

1971

1972 maxfd = global.resize_pipefd[0] > global.rfd

1973 ? global.resize_pipefd[0]

1974 : global.rfd;

1975

1976 select_rv =

1977 select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv);

1978

1979 if (select_rv < 0) {

1980 /* Let EINTR/EAGAIN bubble up */

1981 global.last_errno = errno;

1982 return TB_ERR_POLL;

1983 } else if (select_rv == 0) {

1984 return TB_ERR_NO_EVENT;

1985 }

1986

1987 tty_has_events = (FD_ISSET(global.rfd, &fds));

1988 resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds));

1989

1990 if (tty_has_events) {

1991 ssize_t read_rv = read(global.rfd, buf, sizeof(buf));

1992 if (read_rv < 0) {

1993 global.last_errno = errno;

1994 return TB_ERR_READ;

1995 } else if (read_rv > 0) {

1996 bytebuf_nputs(&global.in, buf, read_rv);

1997 }

1998 }

1999

2000 if (resize_has_events) {

2001 int ignore = 0;

2002 read(global.resize_pipefd[0], &ignore, sizeof(ignore));

2003 /* TODO Harden against errors encountered mid-resize */

2004 if_err_return(rv, update_term_size());

2005 if_err_return(rv, resize_cellbufs());

2006 event->type = TB_EVENT_RESIZE;

2007 event->w = global.width;

2008 event->h = global.height;

2009 return TB_OK;

2010 }

2011

2012 memset(event, 0, sizeof(*event));

2013 if_ok_return(rv, extract_event(event));

2014 } while (timeout == -1);

2015

2016 return rv;

2017 }

2018

2019 static int extract_event(struct tb_event *event) {

2020 int rv;

2021 struct bytebuf_t *in = &global.in;

2022

2023 if (in->len == 0) {

2024 return TB_ERR;

2025 }

2026

2027 if (in->buf[0] == '\x1b') {

2028 /* Escape sequence? */

2029 /* In TB_INPUT_ESC, skip if the buffer is a single escape char */

2030 if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) {

2031 if_ok_or_need_more_return(rv, extract_esc(event));

2032 }

2033

2034 /* Escape key? */

2035 if (global.input_mode & TB_INPUT_ESC) {

2036 event->type = TB_EVENT_KEY;

2037 event->ch = 0;

2038 event->key = TB_KEY_ESC;

2039 event->mod = 0;

2040 bytebuf_shift(in, 1);

2041 return TB_OK;

2042 }

2043

2044 /* Recurse for alt key */

2045 event->mod |= TB_MOD_ALT;

2046 bytebuf_shift(in, 1);

2047 return extract_event(event);

2048 }

2049

2050 /* ASCII control key? */

2051 if ((uint16_t)in->buf[0] < TB_KEY_SPACE || in->buf[0] == TB_KEY_BACKSPACE2)

2052 {

2053 event->type = TB_EVENT_KEY;

2054 event->ch = 0;

2055 event->key = (uint16_t)in->buf[0];

2056 event->mod |= TB_MOD_CTRL;

2057 bytebuf_shift(in, 1);

2058 return TB_OK;

2059 }

2060

2061 /* UTF-8? */

2062 if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) {

2063 event->type = TB_EVENT_KEY;

2064 tb_utf8_char_to_unicode(&event->ch, in->buf);

2065 event->key = 0;

2066 bytebuf_shift(in, tb_utf8_char_length(in->buf[0]));

2067 return TB_OK;

2068 }

2069

2070 /* Need more input */

2071 return TB_ERR;

2072 }

2073

2074 static int extract_esc(struct tb_event *event) {

2075 int rv;

2076 if_ok_or_need_more_return(rv, extract_esc_user(event, 0));

2077 if_ok_or_need_more_return(rv, extract_esc_cap(event));

2078 if_ok_or_need_more_return(rv, extract_esc_mouse(event));

2079 if_ok_or_need_more_return(rv, extract_esc_user(event, 1));

2080 return TB_ERR;

2081 }

2082

2083 static int extract_esc_user(struct tb_event *event, int is_post) {

2084 int rv;

2085 size_t consumed = 0;

2086 struct bytebuf_t *in = &global.in;

2087 int (*fn)(struct tb_event *, size_t *);

2088

2089 fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre;

2090

2091 if (!fn) {

2092 return TB_ERR;

2093 }

2094

2095 rv = fn(event, &consumed);

2096 if (rv == TB_OK) {

2097 bytebuf_shift(in, consumed);

2098 }

2099

2100 if_ok_or_need_more_return(rv, rv);

2101 return TB_ERR;

2102 }

2103

2104 static int extract_esc_cap(struct tb_event *event) {

2105 int rv;

2106 struct bytebuf_t *in = &global.in;

2107 struct cap_trie_t *node;

2108 size_t depth;

2109

2110 if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth));

2111 if (node->is_leaf) {

2112 /* Found a leaf node */

2113 event->type = TB_EVENT_KEY;

2114 event->ch = 0;

2115 event->key = node->key;

2116 event->mod = node->mod;

2117 bytebuf_shift(in, depth);

2118 return TB_OK;

2119 } else if (node->nchildren > 0 && in->len <= depth) {

2120 /* Found a branch node (not enough input) */

2121 return TB_ERR_NEED_MORE;

2122 }

2123

2124 return TB_ERR;

2125 }

2126

2127 static int extract_esc_mouse(struct tb_event *event) {

2128 struct bytebuf_t *in = &global.in;

2129

2130 enum type { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX };

2131

2132 char *cmp[TYPE_MAX];

2133

2134 enum type type = 0;

2135 int ret = TB_ERR;

2136 size_t buf_shift = 0;

2137

2138 /* X10 mouse encoding, the simplest one */

2139 /* \x1b [ M Cb Cx Cy */

2140 cmp[TYPE_VT200] = "\x1b[M";

2141 /* xterm 1006 extended mode or urxvt 1015 extended mode */

2142 /* xterm: \x1b [ < Cb ; Cx ; Cy (M or m) */

2143 cmp[TYPE_1006] = "\x1b[<";

2144 /* urxvt: \x1b [ Cb ; Cx ; Cy M */

2145 cmp[TYPE_1015] = "\x1b[";

2146

2147 /* Unrolled at compile-time (probably) */

2148 for (; type < TYPE_MAX; type++) {

2149 size_t size = strlen(cmp[type]);

2150

2151 if (in->len >= size &&

2152 (strncmp(cmp[type], in->buf, size)) == 0) {

2153 break;

2154 }

2155 }

2156

2157 if (type == TYPE_MAX) {

2158 ret = TB_ERR; /* No match */

2159 return ret;

2160 }

2161

2162 switch (type) {

2163 case TYPE_VT200:

2164 {

2165 int b, fail;

2166 if (in->len < 6) break;

2167

2168 b = in->buf[3] - 0x20;

2169 fail = 0;

2170

2171 switch (b & 3) {

2172 case 0:

2173 event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP

2174 : TB_KEY_MOUSE_LEFT;

2175 break;

2176 case 1:

2177 event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN

2178 : TB_KEY_MOUSE_MIDDLE;

2179 break;

2180 case 2:

2181 event->key = TB_KEY_MOUSE_RIGHT;

2182 break;

2183 case 3:

2184 event->key = TB_KEY_MOUSE_RELEASE;

2185 break;

2186 default:

2187 ret = TB_ERR;

2188 fail = 1;

2189 break;

2190 }

2191

2192 if (!fail) {

2193 if ((b & 32) != 0) {

2194 event->mod |= TB_MOD_MOTION;

2195 }

2196

2197 /* the coord is 1,1 for upper left */

2198 event->x = ((uint8_t)in->buf[4]) - 0x21;

2199 event->y = ((uint8_t)in->buf[5]) - 0x21;

2200

2201 ret = TB_OK;

2202 }

2203

2204 buf_shift = 6;

2205 }

2206 break;

2207 case TYPE_1006:

2208 /* fallthrough */

2209 case TYPE_1015:

2210 {

2211 size_t i = 0;

2212

2213 enum {

2214 FIRST_M = 0,

2215 FIRST_SEMICOLON,

2216 LAST_SEMICOLON,

2217 FIRST_LAST_MAX

2218 };

2219

2220 size_t indices[FIRST_LAST_MAX] = {

2221 index_fail,

2222 index_fail,

2223 index_fail

2224 };

2225 int m_is_capital = 0;

2226

2227 for (i = 0; i < in->len; i++) {

2228 if (in->buf[i] != ';') {

2229 if (indices[FIRST_SEMICOLON] == index_fail) {

2230 indices[FIRST_SEMICOLON] = i;

2231 } else {

2232 indices[LAST_SEMICOLON] = i;

2233 }

2234 } else if (indices[FIRST_M] == index_fail) {

2235 if (in->buf[i] == 'm' || in->buf[i] == 'M') {

2236 m_is_capital = (in->buf[i] == 'M');

2237 indices[FIRST_M] = i;

2238 }

2239 }

2240 }

2241

2242 if (indices[FIRST_M] == index_fail ||

2243 indices[FIRST_SEMICOLON] == index_fail ||

2244 indices[LAST_SEMICOLON] == index_fail) {

2245 ret = TB_ERR;

2246 } else {

2247 int start = (type == TYPE_1015 ? 2 : 3);

2248 int fail = 0;

2249

2250 unsigned n1 = strtoul(&in->buf[start], NULL, 10);

2251 unsigned n2 = strtoul(

2252 &in->buf[indices[FIRST_SEMICOLON] + 1],

2253 NULL, 10);

2254 unsigned n3 = strtoul(

2255 &in->buf[indices[LAST_SEMICOLON] + 1],

2256 NULL, 10);

2257

2258 if (type == TYPE_1015) {

2259 n1 -= 0x20;

2260 }

2261

2262

2263 switch (n1 & 3) {

2264 case 0:

2265 event->key = ((n1 & 64) != 0)

2266 ? TB_KEY_MOUSE_WHEEL_UP

2267 : TB_KEY_MOUSE_LEFT;

2268 break;

2269 case 1:

2270 event->key = ((n1 & 64) != 0)

2271 ? TB_KEY_MOUSE_WHEEL_DOWN

2272 : TB_KEY_MOUSE_MIDDLE;

2273 break;

2274 case 2:

2275 event->key = TB_KEY_MOUSE_RIGHT;

2276 break;

2277 case 3:

2278 event->key = TB_KEY_MOUSE_RELEASE;

2279 break;

2280 default:

2281 ret = TB_ERR;

2282 fail = 1;

2283 break;

2284 }

2285

2286 buf_shift = in->len;

2287

2288 if (!fail) {

2289 if (!m_is_capital) {

2290 /* on xterm mouse release is signaled by lowercase m */

2291 event->key = TB_KEY_MOUSE_RELEASE;

2292 }

2293

2294 if ((n1 & 32) != 0) {

2295 event->mod |= TB_MOD_MOTION;

2296 }

2297

2298 event->x = ((uint8_t)n2) - 1;

2299 event->y = ((uint8_t)n3) - 1;

2300

2301 ret = TB_OK;

2302 }

2303 }

2304 }

2305 break;

2306 case TYPE_MAX:

2307 ret = TB_ERR;

2308 }

2309

2310 if (buf_shift > 0) {

2311 bytebuf_shift(in, buf_shift);

2312 }

2313

2314 if (ret == TB_OK) {

2315 event->type = TB_EVENT_MOUSE;

2316 }

2317

2318 return ret;

2319 }

2320

2321 static int resize_cellbufs(void) {

2322 int rv;

2323 if_err_return(rv,

2324 cellbuf_resize(&global.back, global.width, global.height));

2325 if_err_return(rv,

2326 cellbuf_resize(&global.front, global.width, global.height));

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

2328 if_err_return(rv, send_clear());

2329 return TB_OK;

2330 }

2331

2332 static void handle_resize(int sig) {

2333 int errno_copy = errno;

2334 write(global.resize_pipefd[1], &sig, sizeof(sig));

2335 errno = errno_copy;

2336 }

2337

2338 static int send_attr(uintattr_t fg, uintattr_t bg) {

2339 int rv;

2340 uintattr_t attr_bold, attr_blink, attr_italic,

2341 attr_underline, attr_reverse, attr_default;

2342 uintattr_t cfg, cbg;

2343

2344 if (fg == global.last_fg && bg == global.last_bg) {

2345 return TB_OK;

2346 }

2347

2348 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]));

2349

2350 switch (global.output_mode) {

2351 default:

2352 case TB_OUTPUT_NORMAL:

2353 cfg = fg & 0x0f;

2354 cbg = bg & 0x0f;

2355 break;

2356

2357 case TB_OUTPUT_256:

2358 cfg = fg & 0xff;

2359 cbg = bg & 0xff;

2360 break;

2361

2362 case TB_OUTPUT_216:

2363 cfg = fg & 0xff;

2364 cbg = bg & 0xff;

2365 if (cfg > 216)

2366 cfg = 216;

2367 if (cbg > 216)

2368 cbg = 216;

2369 cfg += 0x0f;

2370 cbg += 0x0f;

2371 break;

2372

2373 case TB_OUTPUT_GRAYSCALE:

2374 cfg = fg & 0xff;

2375 cbg = bg & 0xff;

2376 if (cfg > 24)

2377 cfg = 24;

2378 if (cbg > 24)

2379 cbg = 24;

2380 cfg += 0xe7;

2381 cbg += 0xe7;

2382 break;

2383

2384 #ifdef TB_OPT_TRUECOLOR

2385 case TB_OUTPUT_TRUECOLOR:

2386 cfg = fg & 0xffffff;

2387 cbg = bg & 0xffffff;

2388 break;

2389 #endif

2390 }

2391

2392 #ifdef TB_OPT_TRUECOLOR

2393 if (global.output_mode == TB_OUTPUT_TRUECOLOR) {

2394 attr_bold = TB_TRUECOLOR_BOLD;

2395 attr_blink = TB_TRUECOLOR_BLINK;

2396 attr_italic = TB_TRUECOLOR_ITALIC;

2397 attr_underline = TB_TRUECOLOR_UNDERLINE;

2398 attr_reverse = TB_TRUECOLOR_REVERSE;

2399 attr_default = TB_TRUECOLOR_DEFAULT;

2400 } else

2401 #endif

2402 {

2403 attr_bold = TB_BOLD;

2404 attr_blink = TB_BLINK;

2405 attr_italic = TB_ITALIC;

2406 attr_underline = TB_UNDERLINE;

2407 attr_reverse = TB_REVERSE;

2408 attr_default = TB_DEFAULT;

2409 }

2410

2411 /* For convenience (and some back compat), interpret 0 as default in some

2412 * modes */

2413 if (global.output_mode == TB_OUTPUT_NORMAL ||

2414 global.output_mode == TB_OUTPUT_216 ||

2415 global.output_mode == TB_OUTPUT_GRAYSCALE)

2416 {

2417 if ((fg & 0xff) == 0)

2418 fg |= attr_default;

2419 if ((bg & 0xff) == 0)

2420 bg |= attr_default;

2421 }

2422

2423 if (fg & attr_bold)

2424 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD]));

2425

2426 if (fg & attr_blink)

2427 if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK]));

2428

2429 if (fg & attr_underline)

2430 if_err_return(rv,

2431 bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE]));

2432

2433 if (fg & attr_italic)

2434 if_err_return(rv,

2435 bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC]));

2436

2437 if ((fg & attr_reverse) || (bg & attr_reverse))

2438 if_err_return(rv,

2439 bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE]));

2440

2441 if_err_return(rv, send_sgr(cfg, cbg, fg & attr_default, bg & attr_default));

2442

2443 global.last_fg = fg;

2444 global.last_bg = bg;

2445

2446 return TB_OK;

2447 }

2448

2449 static int send_sgr(uintattr_t cfg, uintattr_t cbg, uintattr_t fg_is_default,

2450 uintattr_t bg_is_default) {

2451 int rv;

2452 char nbuf[32];

2453

2454 if (fg_is_default && bg_is_default) {

2455 return TB_OK;

2456 }

2457

2458 switch (global.output_mode) {

2459 default:

2460 case TB_OUTPUT_NORMAL:

2461 send_literal(rv, "\x1b[");

2462 if (!fg_is_default) {

2463 send_literal(rv, "3");

2464 send_num(rv, nbuf, cfg - 1);

2465 if (!bg_is_default) {

2466 send_literal(rv, ";");

2467 }

2468 }

2469 if (!bg_is_default) {

2470 send_literal(rv, "4");

2471 send_num(rv, nbuf, cbg - 1);

2472 }

2473 send_literal(rv, "m");

2474 break;

2475

2476 case TB_OUTPUT_256:

2477 case TB_OUTPUT_216:

2478 case TB_OUTPUT_GRAYSCALE:

2479 send_literal(rv, "\x1b[");

2480 if (!fg_is_default) {

2481 send_literal(rv, "38;5;");

2482 send_num(rv, nbuf, cfg);

2483 if (!bg_is_default) {

2484 send_literal(rv, ";");

2485 }

2486 }

2487 if (!bg_is_default) {

2488 send_literal(rv, "48;5;");

2489 send_num(rv, nbuf, cbg);

2490 }

2491 send_literal(rv, "m");

2492 break;

2493

2494 #ifdef TB_OPT_TRUECOLOR

2495 case TB_OUTPUT_TRUECOLOR:

2496 send_literal(rv, "\x1b[");

2497 if (!fg_is_default) {

2498 send_literal(rv, "38;2;");

2499 send_num(rv, nbuf, (cfg >> 16) & 0xff);

2500 send_literal(rv, ";");

2501 send_num(rv, nbuf, (cfg >> 8) & 0xff);

2502 send_literal(rv, ";");

2503 send_num(rv, nbuf, cfg & 0xff);

2504 if (!bg_is_default) {

2505 send_literal(rv, ";");

2506 }

2507 }

2508 if (!bg_is_default) {

2509 send_literal(rv, "48;2;");

2510 send_num(rv, nbuf, (cbg >> 16) & 0xff);

2511 send_literal(rv, ";");

2512 send_num(rv, nbuf, (cbg >> 8) & 0xff);

2513 send_literal(rv, ";");

2514 send_num(rv, nbuf, cbg & 0xff);

2515 }

2516 send_literal(rv, "m");

2517 break;

2518 #endif

2519 }

2520 return TB_OK;

2521 }

2522

2523 static int send_cursor_if(int x, int y) {

2524 int rv;

2525 char nbuf[32];

2526 if (x < 0 || y < 0) {

2527 return TB_OK;

2528 }

2529 send_literal(rv, "\x1b[");

2530 send_num(rv, nbuf, y + 1);

2531 send_literal(rv, ";");

2532 send_num(rv, nbuf, x + 1);

2533 send_literal(rv, "H");

2534 return TB_OK;

2535 }

2536

2537 static int send_char(int x, int y, uint32_t ch) {

2538 return send_cluster(x, y, &ch, 1);

2539 }

2540

2541 static int send_cluster(int x, int y, uint32_t *ch, size_t nch) {

2542 int rv, i;

2543 char abuf[8];

2544

2545 if (global.last_x != x - 1 || global.last_y != y) {

2546 if_err_return(rv, send_cursor_if(x, y));

2547 }

2548 global.last_x = x;

2549 global.last_y = y;

2550

2551 for (i = 0; i < (int)nch; i++) {

2552 uint32_t ach = *(ch + i);

2553 int aw = tb_utf8_unicode_to_char(abuf, ach);

2554 if (!ach) {

2555 abuf[0] = ' ';

2556 }

2557 if_err_return(rv,

2558 bytebuf_nputs(&global.out, abuf, (size_t)aw));

2559 }

2560

2561 return TB_OK;

2562 }

2563

2564 static int convert_num(uint32_t num, char *buf) {

2565 int i, l = 0;

2566 char ch;

2567 do {

2568 /* '0' = 48; 48 + num%10 < 58 < MAX_8bitCHAR */

2569 buf[l++] = (char)('0' + (num % 10));

2570 num /= 10;

2571 } while (num);

2572 for (i = 0; i < l / 2; i++) {

2573 ch = buf[i];

2574 buf[i] = buf[l - 1 - i];

2575 buf[l - 1 - i] = ch;

2576 }

2577 return l;

2578 }

2579

2580 static int cell_cmp(struct tb_cell *a, struct tb_cell *b) {

2581 if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) {

2582 return 1;

2583 }

2584 #ifdef TB_OPT_EGC

2585 if (a->nech != b->nech) {

2586 return 1;

2587 } else if (a->nech > 0) { /* a->nech == b->nech */

2588 return memcmp(a->ech, b->ech, a->nech);

2589 }

2590 #endif

2591 return 0;

2592 }

2593

2594 static int cell_copy(struct tb_cell *dst, struct tb_cell *src) {

2595 #ifdef TB_OPT_EGC

2596 if (src->nech > 0) {

2597 return cell_set(dst, src->ech, src->nech, src->fg, src->bg);

2598 }

2599 #endif

2600 return cell_set(dst, &src->ch, 1, src->fg, src->bg);

2601 }

2602

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

2604 uintattr_t fg, uintattr_t bg) {

2605 cell->ch = ch ? *ch : 0;

2606 cell->fg = fg;

2607 cell->bg = bg;

2608 #ifdef TB_OPT_EGC

2609 if (nch <= 1) {

2610 cell->nech = 0;

2611 } else {

2612 int rv;

2613 if_err_return(rv, cell_reserve_ech(cell, nch + 1));

2614 memcpy(cell->ech, ch, nch);

2615 cell->ech[nch] = '\0';

2616 cell->nech = nch;

2617 }

2618 #else

2619 (void)nch;

2620 (void)cell_reserve_ech;

2621 #endif

2622 return TB_OK;

2623 }

2624

2625 static int cell_reserve_ech(struct tb_cell *cell, size_t n) {

2626 #ifdef TB_OPT_EGC

2627 if (cell->cech >= n) {

2628 return TB_OK;

2629 }

2630 if (!(cell->ech = tb_realloc(cell->ech, n * sizeof(cell->ch)))) {

2631 return TB_ERR_MEM;

2632 }

2633 cell->cech = n;

2634 return TB_OK;

2635 #else

2636 (void)cell;

2637 (void)n;

2638 return TB_ERR;

2639 #endif

2640 }

2641

2642 static int cell_free(struct tb_cell *cell) {

2643 #ifdef TB_OPT_EGC

2644 if (cell->ech) {

2645 tb_free(cell->ech);

2646 }

2647 #endif

2648 memset(cell, 0, sizeof(*cell));

2649 return TB_OK;

2650 }

2651

2652 static int cellbuf_init(struct cellbuf_t *c, int w, int h) {

2653 c->cells = tb_malloc(sizeof(struct tb_cell) * w * h);

2654 if (!c->cells) {

2655 return TB_ERR_MEM;

2656 }

2657 memset(c->cells, 0, sizeof(struct tb_cell) * w * h);

2658 c->width = w;

2659 c->height = h;

2660 return TB_OK;

2661 }

2662

2663 static int cellbuf_free(struct cellbuf_t *c) {

2664 if (c->cells) {

2665 int i;

2666 for (i = 0; i < c->width * c->height; i++) {

2667 cell_free(&c->cells[i]);

2668 }

2669 tb_free(c->cells);

2670 }

2671 memset(c, 0, sizeof(*c));

2672 return TB_OK;

2673 }

2674

2675 static int cellbuf_clear(struct cellbuf_t *c) {

2676 int rv, i;

2677 uint32_t space = (uint32_t)' ';

2678 for (i = 0; i < c->width * c->height; i++) {

2679 if_err_return(rv,

2680 cell_set(&c->cells[i], &space, 1, global.fg, global.bg));

2681 }

2682 return TB_OK;

2683 }

2684

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

2686 struct tb_cell **out) {

2687 if (x < 0 || x >= c->width || y < 0 || y >= c->height) {

2688 *out = NULL;

2689 return TB_ERR_OUT_OF_BOUNDS;

2690 }

2691 *out = &c->cells[(y * c->width) + x];

2692 return TB_OK;

2693 }

2694

2695 static int cellbuf_resize(struct cellbuf_t *c, int w, int h) {

2696 int rv;

2697

2698 int ow = c->width;

2699 int oh = c->height;

2700 int minw, minh, x, y;

2701 struct tb_cell *prev;

2702

2703 if (ow == w && oh == h) {

2704 return TB_OK;

2705 }

2706

2707 w = w < 1 ? 1 : w;

2708 h = h < 1 ? 1 : h;

2709

2710 minw = (w < ow) ? w : ow;

2711 minh = (h < oh) ? h : oh;

2712

2713 prev = c->cells;

2714

2715 if_err_return(rv, cellbuf_init(c, w, h));

2716 if_err_return(rv, cellbuf_clear(c));

2717

2718 x = 0;

2719 while (x < minw) {

2720 y = 0;

2721 while (y < minh) {

2722 struct tb_cell *src, *dst;

2723 src = &prev[(y * ow) + x];

2724 if_err_return(rv, cellbuf_get(c, x, y, &dst));

2725 if_err_return(rv, cell_copy(dst, src));

2726 y++;

2727 }

2728 x++;

2729 }

2730

2731 tb_free(prev);

2732

2733 return TB_OK;

2734 }

2735

2736 static int bytebuf_puts(struct bytebuf_t *b, const char *str) {

2737 return bytebuf_nputs(b, str, (size_t)strlen(str));

2738 }

2739

2740 static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr) {

2741 int rv;

2742 if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1));

2743 memcpy(b->buf + b->len, str, nstr);

2744 b->len += nstr;

2745 b->buf[b->len] = '\0';

2746 return TB_OK;

2747 }

2748

2749 static int bytebuf_shift(struct bytebuf_t *b, size_t n) {

2750 size_t nmove;

2751 if (n > b->len) {

2752 n = b->len;

2753 }

2754 nmove = b->len - n;

2755 memmove(b->buf, b->buf + n, nmove);

2756 b->len -= n;

2757 return TB_OK;

2758 }

2759

2760 static int bytebuf_flush(struct bytebuf_t *b, int fd) {

2761 ssize_t write_rv;

2762 if (b->len <= 0) {

2763 return TB_OK;

2764 }

2765 write_rv = write(fd, b->buf, b->len);

2766 if (write_rv < 0 || (size_t)write_rv != b->len) {

2767 /* Note, errno will be 0 on partial write */

2768 global.last_errno = errno;

2769 return TB_ERR;

2770 }

2771 b->len = 0;

2772 return TB_OK;

2773 }

2774

2775 static int bytebuf_reserve(struct bytebuf_t *b, size_t sz) {

2776 char *newbuf;

2777 size_t newcap;

2778

2779 if (b->cap >= sz) {

2780 return TB_OK;

2781 }

2782 newcap = b->cap > 0 ? b->cap : 1;

2783 while (newcap < sz) {

2784 newcap *= 2;

2785 }

2786 if (b->buf) {

2787 newbuf = tb_realloc(b->buf, newcap);

2788 } else {

2789 newbuf = tb_malloc(newcap);

2790 }

2791 if (!newbuf) {

2792 return TB_ERR_MEM;

2793 }

2794 b->buf = newbuf;

2795 b->cap = newcap;

2796 return TB_OK;

2797 }

2798

2799 static int bytebuf_free(struct bytebuf_t *b) {

2800 if (b->buf) {

2801 tb_free(b->buf);

2802 }

2803 memset(b, 0, sizeof(*b));

2804 return TB_OK;

2805 }

2806