rb_termbox 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }