rb_termbox 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,536 @@
1
+ #include <assert.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <errno.h>
5
+ #include <fcntl.h>
6
+ #include <signal.h>
7
+ #include <stdio.h>
8
+ #include <sys/ioctl.h>
9
+ #include <sys/time.h>
10
+ #include <sys/stat.h>
11
+ #include <termios.h>
12
+ #include <unistd.h>
13
+
14
+ #include "termbox.h"
15
+
16
+ #include "bytebuffer.inl"
17
+ #include "term.inl"
18
+ #include "input.inl"
19
+
20
+ struct cellbuf {
21
+ int width;
22
+ int height;
23
+ struct tb_cell *cells;
24
+ };
25
+
26
+ #define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]
27
+ #define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)
28
+ #define LAST_COORD_INIT -1
29
+
30
+ static struct termios orig_tios;
31
+
32
+ static struct cellbuf back_buffer;
33
+ static struct cellbuf front_buffer;
34
+ static struct bytebuffer output_buffer;
35
+ static struct bytebuffer input_buffer;
36
+
37
+ static int termw;
38
+ static int termh;
39
+
40
+ static int inputmode = TB_INPUT_ESC;
41
+
42
+ static int inout;
43
+ static int winch_fds[2];
44
+
45
+ static int lastx = LAST_COORD_INIT;
46
+ static int lasty = LAST_COORD_INIT;
47
+ static int cursor_x = -1;
48
+ static int cursor_y = -1;
49
+
50
+ static uint16_t background = TB_DEFAULT;
51
+ static uint16_t foreground = TB_DEFAULT;
52
+
53
+ static void write_cursor(int x, int y);
54
+ static void write_sgr_fg(uint16_t fg);
55
+ static void write_sgr_bg(uint16_t bg);
56
+ static void write_sgr(uint16_t fg, uint16_t bg);
57
+
58
+ static void cellbuf_init(struct cellbuf *buf, int width, int height);
59
+ static void cellbuf_resize(struct cellbuf *buf, int width, int height);
60
+ static void cellbuf_clear(struct cellbuf *buf);
61
+ static void cellbuf_free(struct cellbuf *buf);
62
+
63
+ static void update_size(void);
64
+ static void update_term_size(void);
65
+ static void send_attr(uint16_t fg, uint16_t bg);
66
+ static void send_char(int x, int y, uint32_t c);
67
+ static void send_clear(void);
68
+ static void sigwinch_handler(int xxx);
69
+ static int wait_fill_event(struct tb_event *event, struct timeval *timeout);
70
+
71
+ /* may happen in a different thread */
72
+ static volatile int buffer_size_change_request;
73
+
74
+ /* -------------------------------------------------------- */
75
+
76
+ int tb_init(void)
77
+ {
78
+ inout = open("/dev/tty", O_RDWR);
79
+ if (inout == -1) {
80
+ return TB_EFAILED_TO_OPEN_TTY;
81
+ }
82
+
83
+ if (init_term() < 0) {
84
+ close(inout);
85
+ return TB_EUNSUPPORTED_TERMINAL;
86
+ }
87
+
88
+ if (pipe(winch_fds) < 0) {
89
+ close(inout);
90
+ return TB_EPIPE_TRAP_ERROR;
91
+ }
92
+
93
+ struct sigaction sa;
94
+ sa.sa_handler = sigwinch_handler;
95
+ sa.sa_flags = 0;
96
+ sigaction(SIGWINCH, &sa, 0);
97
+
98
+ tcgetattr(inout, &orig_tios);
99
+
100
+ struct termios tios;
101
+ memcpy(&tios, &orig_tios, sizeof(tios));
102
+
103
+ tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
104
+ | INLCR | IGNCR | ICRNL | IXON);
105
+ tios.c_oflag &= ~OPOST;
106
+ tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
107
+ tios.c_cflag &= ~(CSIZE | PARENB);
108
+ tios.c_cflag |= CS8;
109
+ tios.c_cc[VMIN] = 0;
110
+ tios.c_cc[VTIME] = 0;
111
+ tcsetattr(inout, TCSAFLUSH, &tios);
112
+
113
+ bytebuffer_init(&input_buffer, 128);
114
+ bytebuffer_init(&output_buffer, 32 * 1024);
115
+
116
+ bytebuffer_puts(&output_buffer, funcs[T_ENTER_CA]);
117
+ bytebuffer_puts(&output_buffer, funcs[T_ENTER_KEYPAD]);
118
+ bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);
119
+ send_clear();
120
+
121
+ update_term_size();
122
+ cellbuf_init(&back_buffer, termw, termh);
123
+ cellbuf_init(&front_buffer, termw, termh);
124
+ cellbuf_clear(&back_buffer);
125
+ cellbuf_clear(&front_buffer);
126
+
127
+ return 0;
128
+ }
129
+
130
+ void tb_shutdown(void)
131
+ {
132
+ bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);
133
+ bytebuffer_puts(&output_buffer, funcs[T_SGR0]);
134
+ bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]);
135
+ bytebuffer_puts(&output_buffer, funcs[T_EXIT_CA]);
136
+ bytebuffer_puts(&output_buffer, funcs[T_EXIT_KEYPAD]);
137
+ bytebuffer_flush(&output_buffer, inout);
138
+ tcsetattr(inout, TCSAFLUSH, &orig_tios);
139
+
140
+ shutdown_term();
141
+ close(inout);
142
+ close(winch_fds[0]);
143
+ close(winch_fds[1]);
144
+
145
+ cellbuf_free(&back_buffer);
146
+ cellbuf_free(&front_buffer);
147
+ bytebuffer_free(&output_buffer);
148
+ bytebuffer_free(&input_buffer);
149
+ }
150
+
151
+ void tb_present(void)
152
+ {
153
+ int x,y;
154
+ struct tb_cell *back, *front;
155
+
156
+ /* invalidate cursor position */
157
+ lastx = LAST_COORD_INIT;
158
+ lasty = LAST_COORD_INIT;
159
+
160
+ if (buffer_size_change_request) {
161
+ update_size();
162
+ buffer_size_change_request = 0;
163
+ }
164
+
165
+ for (y = 0; y < front_buffer.height; ++y) {
166
+ for (x = 0; x < front_buffer.width; ++x) {
167
+ back = &CELL(&back_buffer, x, y);
168
+ front = &CELL(&front_buffer, x, y);
169
+ if (memcmp(back, front, sizeof(struct tb_cell)) == 0)
170
+ continue;
171
+ send_attr(back->fg, back->bg);
172
+ send_char(x, y, back->ch);
173
+ memcpy(front, back, sizeof(struct tb_cell));
174
+ }
175
+ }
176
+ if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
177
+ write_cursor(cursor_x, cursor_y);
178
+ bytebuffer_flush(&output_buffer, inout);
179
+ }
180
+
181
+ void tb_set_cursor(int cx, int cy)
182
+ {
183
+ if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))
184
+ bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);
185
+
186
+ if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))
187
+ bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);
188
+
189
+ cursor_x = cx;
190
+ cursor_y = cy;
191
+ if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
192
+ write_cursor(cursor_x, cursor_y);
193
+ }
194
+
195
+ void tb_put_cell(int x, int y, const struct tb_cell *cell)
196
+ {
197
+ if (x >= back_buffer.width || y >= back_buffer.height)
198
+ return;
199
+ CELL(&back_buffer, x, y) = *cell;
200
+ }
201
+
202
+ void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg)
203
+ {
204
+ struct tb_cell c = {ch, fg, bg};
205
+ tb_put_cell(x, y, &c);
206
+ }
207
+
208
+ void tb_blit(int x, int y, int w, int h, const struct tb_cell *cells)
209
+ {
210
+ if (x+w > back_buffer.width || y+h > back_buffer.height)
211
+ return;
212
+
213
+ int sy;
214
+ struct tb_cell *dst = &CELL(&back_buffer, x, y);
215
+ size_t size = sizeof(struct tb_cell) * w;
216
+
217
+ for (sy = 0; sy < h; ++sy) {
218
+ memcpy(dst, cells, size);
219
+ dst += back_buffer.width;
220
+ cells += w;
221
+ }
222
+ }
223
+
224
+ int tb_poll_event(struct tb_event *event)
225
+ {
226
+ return wait_fill_event(event, 0);
227
+ }
228
+
229
+ int tb_peek_event(struct tb_event *event, int timeout)
230
+ {
231
+ struct timeval tv;
232
+ tv.tv_sec = timeout / 1000;
233
+ tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
234
+ return wait_fill_event(event, &tv);
235
+ }
236
+
237
+ int tb_width(void)
238
+ {
239
+ return termw;
240
+ }
241
+
242
+ int tb_height(void)
243
+ {
244
+ return termh;
245
+ }
246
+
247
+ void tb_clear(void)
248
+ {
249
+ if (buffer_size_change_request) {
250
+ update_size();
251
+ buffer_size_change_request = 0;
252
+ }
253
+ cellbuf_clear(&back_buffer);
254
+ }
255
+
256
+ int tb_select_input_mode(int mode)
257
+ {
258
+ if (mode)
259
+ inputmode = mode;
260
+ return inputmode;
261
+ }
262
+
263
+ void tb_set_clear_attributes(uint16_t fg, uint16_t bg)
264
+ {
265
+ foreground = fg;
266
+ background = bg;
267
+ }
268
+
269
+ /* -------------------------------------------------------- */
270
+
271
+ static int convertnum(uint32_t num, char* buf) {
272
+ int i, l = 0;
273
+ int ch;
274
+ do {
275
+ buf[l++] = '0' + (num % 10);
276
+ num /= 10;
277
+ } while (num);
278
+ for(i = 0; i < l / 2; i++) {
279
+ ch = buf[i];
280
+ buf[i] = buf[l - 1 - i];
281
+ buf[l - 1 - i] = ch;
282
+ }
283
+ return l;
284
+ }
285
+
286
+ #define WRITE_LITERAL(X) bytebuffer_append(&output_buffer, (X), sizeof(X)-1)
287
+ #define WRITE_INT(X) bytebuffer_append(&output_buffer, buf, convertnum((X), buf))
288
+
289
+ static void write_cursor(int x, int y) {
290
+ char buf[32];
291
+ WRITE_LITERAL("\033[");
292
+ WRITE_INT(y+1);
293
+ WRITE_LITERAL(";");
294
+ WRITE_INT(x+1);
295
+ WRITE_LITERAL("H");
296
+ }
297
+
298
+ static void write_sgr_fg(uint16_t fg) {
299
+ char buf[32];
300
+ WRITE_LITERAL("\033[3");
301
+ WRITE_INT(fg-1);
302
+ WRITE_LITERAL("m");
303
+ }
304
+
305
+ static void write_sgr_bg(uint16_t bg) {
306
+ char buf[32];
307
+ WRITE_LITERAL("\033[4");
308
+ WRITE_INT(bg-1);
309
+ WRITE_LITERAL("m");
310
+ }
311
+
312
+ static void write_sgr(uint16_t fg, uint16_t bg) {
313
+ char buf[32];
314
+ WRITE_LITERAL("\033[3");
315
+ WRITE_INT(fg-1);
316
+ WRITE_LITERAL(";4");
317
+ WRITE_INT(bg-1);
318
+ WRITE_LITERAL("m");
319
+ }
320
+
321
+ static void cellbuf_init(struct cellbuf *buf, int width, int height)
322
+ {
323
+ buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height);
324
+ assert(buf->cells);
325
+ buf->width = width;
326
+ buf->height = height;
327
+ }
328
+
329
+ static void cellbuf_resize(struct cellbuf *buf, int width, int height)
330
+ {
331
+ if (buf->width == width && buf->height == height)
332
+ return;
333
+
334
+ int oldw = buf->width;
335
+ int oldh = buf->height;
336
+ struct tb_cell *oldcells = buf->cells;
337
+
338
+ cellbuf_init(buf, width, height);
339
+ cellbuf_clear(buf);
340
+
341
+ int minw = (width < oldw) ? width : oldw;
342
+ int minh = (height < oldh) ? height : oldh;
343
+ int i;
344
+
345
+ for (i = 0; i < minh; ++i) {
346
+ struct tb_cell *csrc = oldcells + (i * oldw);
347
+ struct tb_cell *cdst = buf->cells + (i * width);
348
+ memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);
349
+ }
350
+
351
+ free(oldcells);
352
+ }
353
+
354
+ static void cellbuf_clear(struct cellbuf *buf)
355
+ {
356
+ int i;
357
+ int ncells = buf->width * buf->height;
358
+
359
+ for (i = 0; i < ncells; ++i) {
360
+ buf->cells[i].ch = ' ';
361
+ buf->cells[i].fg = foreground;
362
+ buf->cells[i].bg = background;
363
+ }
364
+ }
365
+
366
+ static void cellbuf_free(struct cellbuf *buf)
367
+ {
368
+ free(buf->cells);
369
+ }
370
+
371
+ static void get_term_size(int *w, int *h)
372
+ {
373
+ struct winsize sz;
374
+ memset(&sz, 0, sizeof(sz));
375
+
376
+ ioctl(inout, TIOCGWINSZ, &sz);
377
+
378
+ if (w) *w = sz.ws_col;
379
+ if (h) *h = sz.ws_row;
380
+ }
381
+
382
+ static void update_term_size(void)
383
+ {
384
+ struct winsize sz;
385
+ memset(&sz, 0, sizeof(sz));
386
+
387
+ ioctl(inout, TIOCGWINSZ, &sz);
388
+
389
+ termw = sz.ws_col;
390
+ termh = sz.ws_row;
391
+ }
392
+
393
+ static void send_attr(uint16_t fg, uint16_t bg)
394
+ {
395
+ #define LAST_ATTR_INIT 0xFFFF
396
+ static uint16_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT;
397
+ if (fg != lastfg || bg != lastbg) {
398
+ bytebuffer_puts(&output_buffer, funcs[T_SGR0]);
399
+ uint16_t fgcol = fg & 0x0F;
400
+ uint16_t bgcol = bg & 0x0F;
401
+ if (fgcol != TB_DEFAULT) {
402
+ if (bgcol != TB_DEFAULT)
403
+ write_sgr(fgcol, bgcol);
404
+ else
405
+ write_sgr_fg(fgcol);
406
+ } else if (bgcol != TB_DEFAULT) {
407
+ write_sgr_bg(bgcol);
408
+ }
409
+
410
+ if (fg & TB_BOLD)
411
+ bytebuffer_puts(&output_buffer, funcs[T_BOLD]);
412
+ if (bg & TB_BOLD)
413
+ bytebuffer_puts(&output_buffer, funcs[T_BLINK]);
414
+ if (fg & TB_UNDERLINE)
415
+ bytebuffer_puts(&output_buffer, funcs[T_UNDERLINE]);
416
+ if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
417
+ bytebuffer_puts(&output_buffer, funcs[T_REVERSE]);
418
+
419
+ lastfg = fg;
420
+ lastbg = bg;
421
+ }
422
+ }
423
+
424
+ static void send_char(int x, int y, uint32_t c)
425
+ {
426
+ char buf[7];
427
+ int bw = tb_utf8_unicode_to_char(buf, c);
428
+ buf[bw] = '\0';
429
+ if (x-1 != lastx || y != lasty)
430
+ write_cursor(x, y);
431
+ lastx = x; lasty = y;
432
+ if(!c) buf[0] = ' '; // replace 0 with whitespace
433
+ bytebuffer_puts(&output_buffer, buf);
434
+ }
435
+
436
+ static void send_clear(void)
437
+ {
438
+ send_attr(foreground, background);
439
+ bytebuffer_puts(&output_buffer, funcs[T_CLEAR_SCREEN]);
440
+ if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
441
+ write_cursor(cursor_x, cursor_y);
442
+ bytebuffer_flush(&output_buffer, inout);
443
+
444
+ /* we need to invalidate cursor position too and these two vars are
445
+ * used only for simple cursor positioning optimization, cursor
446
+ * actually may be in the correct place, but we simply discard
447
+ * optimization once and it gives us simple solution for the case when
448
+ * cursor moved */
449
+ lastx = LAST_COORD_INIT;
450
+ lasty = LAST_COORD_INIT;
451
+ }
452
+
453
+ static void sigwinch_handler(int xxx)
454
+ {
455
+ (void) xxx;
456
+ const int zzz = 1;
457
+ write(winch_fds[1], &zzz, sizeof(int));
458
+ }
459
+
460
+ static void update_size(void)
461
+ {
462
+ update_term_size();
463
+ cellbuf_resize(&back_buffer, termw, termh);
464
+ cellbuf_resize(&front_buffer, termw, termh);
465
+ cellbuf_clear(&front_buffer);
466
+ send_clear();
467
+ }
468
+
469
+ static int wait_fill_event(struct tb_event *event, struct timeval *timeout)
470
+ {
471
+ // ;-)
472
+ #define ENOUGH_DATA_FOR_PARSING 64
473
+ fd_set events;
474
+ memset(event, 0, sizeof(struct tb_event));
475
+
476
+ // try to extract event from input buffer, return on success
477
+ event->type = TB_EVENT_KEY;
478
+ if (extract_event(event, &input_buffer, inputmode))
479
+ return TB_EVENT_KEY;
480
+
481
+ // it looks like input buffer is incomplete, let's try the short path,
482
+ // but first make sure there is enough space
483
+ int prevlen = input_buffer.len;
484
+ bytebuffer_resize(&input_buffer, prevlen + ENOUGH_DATA_FOR_PARSING);
485
+ ssize_t r = read(inout, input_buffer.buf + prevlen,
486
+ ENOUGH_DATA_FOR_PARSING);
487
+ if (r < 0) {
488
+ // EAGAIN / EWOULDBLOCK shouldn't occur here
489
+ assert(errno != EAGAIN && errno != EWOULDBLOCK);
490
+ return -1;
491
+ } else if (r > 0) {
492
+ bytebuffer_resize(&input_buffer, prevlen + r);
493
+ if (extract_event(event, &input_buffer, inputmode))
494
+ return TB_EVENT_KEY;
495
+ } else {
496
+ bytebuffer_resize(&input_buffer, prevlen);
497
+ }
498
+
499
+ // r == 0, or not enough data, let's go to select
500
+ while (1) {
501
+ FD_ZERO(&events);
502
+ FD_SET(inout, &events);
503
+ FD_SET(winch_fds[0], &events);
504
+ int maxfd = (winch_fds[0] > inout) ? winch_fds[0] : inout;
505
+ int result = select(maxfd+1, &events, 0, 0, timeout);
506
+ if (!result)
507
+ return 0;
508
+
509
+ if (FD_ISSET(inout, &events)) {
510
+ event->type = TB_EVENT_KEY;
511
+ prevlen = input_buffer.len;
512
+ bytebuffer_resize(&input_buffer,
513
+ prevlen + ENOUGH_DATA_FOR_PARSING);
514
+ r = read(inout, input_buffer.buf + prevlen,
515
+ ENOUGH_DATA_FOR_PARSING);
516
+ if (r < 0) {
517
+ // EAGAIN / EWOULDBLOCK shouldn't occur here
518
+ assert(errno != EAGAIN && errno != EWOULDBLOCK);
519
+ return -1;
520
+ }
521
+ bytebuffer_resize(&input_buffer, prevlen + r);
522
+ if (r == 0)
523
+ continue;
524
+ if (extract_event(event, &input_buffer, inputmode))
525
+ return TB_EVENT_KEY;
526
+ }
527
+ if (FD_ISSET(winch_fds[0], &events)) {
528
+ event->type = TB_EVENT_RESIZE;
529
+ int zzz = 0;
530
+ read(winch_fds[0], &zzz, sizeof(int));
531
+ buffer_size_change_request = 1;
532
+ get_term_size(&event->w, &event->h);
533
+ return TB_EVENT_RESIZE;
534
+ }
535
+ }
536
+ }