aboutsummaryrefslogtreecommitdiff
path: root/termbox2/termbox2.ha
blob: f8476ee9323bcd33a2073872a3bcce8614cb75b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
use encoding::utf8;
use fmt;
use types::c;

// Type for key values, see [[termbox2::KEY]].
export type key = u16;

// ASCII constants, see [[termbox2::key_event]].key.
export def KEY = struct {
	CTRL_TILDE: key = 0x00,
	CTRL_2: key = 0x00, // clash with CTRL_TILDE
	CTRL_A: key = 0x01,
	CTRL_B: key = 0x02,
	CTRL_C: key = 0x03,
	CTRL_D: key = 0x04,
	CTRL_E: key = 0x05,
	CTRL_F: key = 0x06,
	CTRL_G: key = 0x07,
	BACKSPACE: key = 0x08,
	CTRL_H: key = 0x08, // clash with CTRL_BACKSPACE
	TAB: key = 0x09,
	CTRL_I: key = 0x09, // clash with TAB
	CTRL_J: key = 0x0a,
	CTRL_K: key = 0x0b,
	CTRL_L: key = 0x0c,
	ENTER: key = 0x0d,
	CTRL_M: key = 0x0d, // clash with ENTER
	CTRL_N: key = 0x0e,
	CTRL_O: key = 0x0f,
	CTRL_P: key = 0x10,
	CTRL_Q: key = 0x11,
	CTRL_R: key = 0x12,
	CTRL_S: key = 0x13,
	CTRL_T: key = 0x14,
	CTRL_U: key = 0x15,
	CTRL_V: key = 0x16,
	CTRL_W: key = 0x17,
	CTRL_X: key = 0x18,
	CTRL_Y: key = 0x19,
	CTRL_Z: key = 0x1a,
	ESC: key = 0x1b,
	CTRL_LSQ_BRACKET: key = 0x1b, // clash with 'ESC'
	CTRL_3: key = 0x1b, // clash with 'ESC'
	CTRL_4: key = 0x1c,
	CTRL_BACKSLASH: key = 0x1c, // clash with 'CTRL_4'
	CTRL_5: key = 0x1d,
	CTRL_RSQ_BRACKET: key = 0x1d, // clash with 'CTRL_5'
	CTRL_6: key = 0x1e,
	CTRL_7: key = 0x1f,
	CTRL_SLASH: key = 0x1f, // clash with 'CTRL_7'
	CTRL_UNDERSCORE: key = 0x1f, // clash with 'CTRL_7'
	SPACE: key = 0x20,
	BACKSPACE2: key = 0x7f,
	CTRL_8: key = 0x7f, // clash with 'BACKSPACE2'
	F1: key = (0xffff - 0),
	F2: key = (0xffff - 1),
	F3: key = (0xffff - 2),
	F4: key = (0xffff - 3),
	F5: key = (0xffff - 4),
	F6: key = (0xffff - 5),
	F7: key = (0xffff - 6),
	F8: key = (0xffff - 7),
	F9: key = (0xffff - 8),
	F10: key = (0xffff - 9),
	F11: key = (0xffff - 10),
	F12: key = (0xffff - 11),
	INSERT: key = (0xffff - 12),
	DELETE: key = (0xffff - 13),
	HOME: key = (0xffff - 14),
	END: key = (0xffff - 15),
	PGUP: key = (0xffff - 16),
	PGDN: key = (0xffff - 17),
	ARROW_UP: key = (0xffff - 18),
	ARROW_DOWN: key = (0xffff - 19),
	ARROW_LEFT: key = (0xffff - 20),
	ARROW_RIGHT: key = (0xffff - 21),
	BACK_TAB: key = (0xffff - 22),
	MOUSE_LEFT: key = (0xffff - 23),
	MOUSE_RIGHT: key = (0xffff - 24),
	MOUSE_MIDDLE: key = (0xffff - 25),
	MOUSE_RELEASE: key = (0xffff - 26),
	MOUSE_WHEEL_UP: key = (0xffff - 27),
	MOUSE_WHEEL_DOWN: key = (0xffff - 28),
};

// Type for key modifiers, see [[termbox2::MOD]].
export type mod = u8;

// Key modifiers (bitwise), see [[termbox2::event]].mod.
export def MOD = struct {
	ALT: mod = 1,
	CTRL: mod = 2,
	SHIFT: mod = 4,
	MOTION: mod = 8,
};

// Type for input modes, see [[termbox2::INPUT]].
export type input_mode = int;

// Input modes (bitwise), see [[termbox2::set_input_mode]].
export def INPUT = struct {
	CURRENT: input_mode = 0,
	ESC: input_mode = 1,
	ALT: input_mode = 2,
	MOUSE: input_mode = 4,
};

// Type for output modes, see [[termbox2::OUTPUT]].
export type output_mode = int;

// Output modes, see [[termbox2::set_output_mode]].
export def OUTPUT = struct {
	CURRENT: output_mode = 0,
	NORMAL: output_mode = 1,
	_256: output_mode = 2,
	_216: output_mode = 3,
	GRAYSCALE: output_mode = 4,
	TRUECOLOR: output_mode = 5, // only available if OPT_ATTR_W >= 32
};

// Type for error return values, see [[termbox2::ERR]].
export type err = !int;

// Common error return values.
//
// Library behavior is undefined after receiving ERR.MEM.
// Callers may attempt reinitializing by freeing memory, invoking
// [[termbox2::shutdown]], then [[termbox2::init]].
export def ERR = struct {
	ERR: err = -1,
	NEED_MORE: err = -2,
	INIT_ALREADY: err = -3,
	INIT_OPEN: err = -4,
	MEM: err = -5,
	NO_EVENT: err = -6,
	NO_TERM: err = -7,
	NOT_INIT: err = -8,
	OUT_OF_BOUNDS: err = -9,
	READ: err = -10,
	RESIZE_IOCTL: err = -11,
	RESIZE_PIPE: err = -12,
	RESIZE_SIGACTION: err = -13,
	POLL: err = -14,
	SELECT: err = -14,
	TCGETATTR: err = -15,
	TCSETATTR: err = -16,
	UNSUPPORTED_TERM: err = -17,
	RESIZE_WRITE: err = -18,
	RESIZE_POLL: err = -19,
	RESIZE_SELECT: err = -19,
	RESIZE_READ: err = -20,
	RESIZE_SSCANF: err = -21,
	CAP_COLLISION: err = -22,
	HARE_BUILD: err = -9990, // Additional error type
	HARE_INVALID_UTF8: err = -9991, // Additional error type
	HARE_OTHER: err = -9992, // Additional error type
};

// An incoming key press event. Note that there is overlap between
// MOD.CTRL and KEY.CTRL_* keys. MOD.CTRL and MOD.SHIFT are only set
// as modifiers to KEY.ARROW_*.
export type key_event = struct {
	mod: mod, // Modifier
	key_or_ch: (key | rune), // A [[termbox2::KEY]] or a Unicode code point
};

// An incoming resize event.
export type resize_event = struct {
	w: i32, // resize width
	h: i32, // resize height
};

// An incoming mouse event.
export type mouse_event = struct {
	key: key, // Mouse button, e.g. termbox2::KEY.MOUSE_LEFT
	x: i32,
	y: i32,
};

// The C data structure for an event, for compatibility.
type tb_event = struct {
	t: u8,
	mod: u8,
	key: u16,
	ch: u32,
	w: i32,
	h: i32,
	x: i32,
	y: i32,
};

def TB_EVENT_KEY: u8 = 1;
def TB_EVENT_RESIZE: u8 = 2;
def TB_EVENT_MOUSE: u8 = 3;
fn tb_event_to_event(e: tb_event) (event | err) = switch (e.t) {
	case TB_EVENT_KEY => yield key_event {
		mod = e.mod,
		key_or_ch = if (e.key != 0) e.key: key else e.ch: rune,
	};
	case TB_EVENT_RESIZE => yield resize_event {
		w = e.w,
		h = e.h,
	};
	case TB_EVENT_MOUSE => yield mouse_event {
		key = e.key,
		x = e.x,
		y = e.y,
	};
	case => yield ERR.HARE_OTHER;
	};

// Any incoming event from the tty.
export type event = (key_event | resize_event | mouse_event);

fn build_opts_match() bool =
	(has_egc() == OPT_EGC) &&
	(attr_width() == OPT_ATTR_W) &&
	(has_truecolor() == (OPT_ATTR_W == 32 || OPT_ATTR_W == 64));

fn void_or_err(r: int) (void | err) = if (r < 0) r: err;
fn int_or_err(r: int) (int | err) = if (r < 0) r: err else r;
fn size_or_err(r: int) (size | err) = if (r < 0) r: err else r: size;
fn str_or_err(cstr: *c::char) (str | err) = match(c::tostr(cstr)) {
	case utf8::invalid => yield ERR.HARE_INVALID_UTF8;
	case let s: str => yield s;
};

// Initialize the termbox library. This function should be called before
// any other functions. After successful initialization, the library must
// be finalized using [[termbox2::shutdown]].
export fn init() (void | err) =
	if (!build_opts_match())
		ERR.HARE_BUILD
	else
		void_or_err(tb_init());
@symbol("tb_init") fn tb_init() int;

// Same as [[termbox2::init]], but allows to specify a file path in place
// of the default /dev/tty.
export fn init_file(path: str) (void | err) = {
	let c_path = c::fromstr(path)!;
	defer free(c_path);
	return if (!build_opts_match())
		ERR.HARE_BUILD
	else
		void_or_err(tb_init_file(c_path));
};
@symbol("tb_init_file") fn tb_init_file(cstr: *c::char) int;

// Same as [[termbox2::init]], but allows to specify an output
// file descriptor.
export fn init_fd(ttyfd: int) (void | err) =
	if (!build_opts_match())
		ERR.HARE_BUILD
	else
		void_or_err(tb_init_fd(ttyfd));
@symbol("tb_init_fd") fn tb_init_fd(fd: int) int;

// Same as [[termbox2::init]], but allows to specify input and output
// file descriptors.
export fn init_rwfd(rfd: int, wfd: int) (void | err) =
	if (!build_opts_match())
		ERR.HARE_BUILD
	else
		void_or_err(tb_init_rwfd(rfd, wfd));
@symbol("tb_init_rwfd") fn tb_init_rwfd(rfd: int, wfd: int) int;

// Finalizes the library.
export fn shutdown() (void | err) = void_or_err(tb_shutdown());
@symbol("tb_shutdown") fn tb_shutdown() int;

// Return the width of the internal back buffer (which is the same as the
// terminal's window width in columns). The internal buffer can be resized
// after [[termbox2::clear]] or [[termbox2::present]] calls. Returns ERR.ERR
// when called before [[termbox2::init]] or after [[termbox2::shutdown]].
export fn width() (size | err) = size_or_err(tb_width());
@symbol("tb_width") fn tb_width() int;

// Return the height of the internal back buffer (which is the same as the
// terminal's window width in rows). The internal buffer can be resized
// after [[termbox2::clear]] or [[termbox2::present]] calls. Returns ERR.ERR
// when called before [[termbox2::init]] or after [[termbox2::shutdown]].
export fn height() (size | err) = size_or_err(tb_height());
@symbol("tb_height") fn tb_height() int;

// Clear the internal back buffer using [[termbox2::ATTRIBUTE]].DEFAULT.
export fn clear() (void | err) = void_or_err(tb_clear());
@symbol("tb_clear") fn tb_clear() int;

// Clear the internal back buffer using the specified attributes.
export fn set_clear_attrs(fg: uattr, bg: uattr) (void | err) =
	void_or_err(tb_set_clear_attrs(fg, bg));
@symbol("tb_set_clear_attrs") fn tb_set_clear_attrs(fg: uattr, bg: uattr) int;

// Synchronize the internal back buffer with the terminal by
// writing to tty.
export fn present() (void | err) = void_or_err(tb_present());
@symbol("tb_present") fn tb_present() int;

// Clear the internal front buffer effectively forcing a complete re-render
// of the back buffer to the tty. It is not necessary to call this under
// normal circumstances.
export fn invalidate() (void | err) = void_or_err(tb_invalidate());
@symbol("tb_invalidate") fn tb_invalidate() int;

// Set the position of the cursor. Upper-left cell is (0, 0).
export fn set_cursor(cx: int, cy: int) (void | err) =
	void_or_err(tb_set_cursor(cx, cy));
@symbol("tb_set_cursor") fn tb_set_cursor(x: int, y: int) int;

// Hide the cursor.
export fn hide_cursor() (void | err) = void_or_err(tb_hide_cursor());
@symbol("tb_hide_cursor") fn tb_hide_cursor() int;

// Set cell contents in the internal back buffer at the specified position.
// Non-printable runes are replaced with U+FFFD at render time.
export fn set_cell(
	x: int,
	y: int,
	ch: rune,
	fg: uattr,
	bg: uattr,
) (void | err) = void_or_err(tb_set_cell(x, y, ch: u32, fg, bg));
@symbol("tb_set_cell") fn tb_set_cell(
	x: int,
	y: int,
	ch: u32,
	fg: uattr,
	bg: uattr,
) int;

// Set extended cell contents. Only available when OPT_ATTR_W >= 32.
// See also [[termbox2::set_cell]] and [[termbox2::cell]].
export fn set_cell_ex(
	x: int,
	y: int,
	ch: []rune,
	fg: uattr,
	bg: uattr,
) (void | err) =
	void_or_err(tb_set_cell_ex(x, y, &ch[0]: *u32, len(ch), fg, bg));
@symbol("tb_set_cell_ex") fn tb_set_cell_ex(
	x: int,
	y: int,
	ch: *u32,
	n: size,
	fg: uattr,
	bg: uattr,
) int;

// Add one character to the cell's ech. Only available when OPT_ATTR_W >= 32.
// See also [[termbox2::set_cell_ex]] and [[termbox2::cell]].
export fn extend_cell(x: int, y: int, ch: rune) (void | err) =
	void_or_err(tb_extend_cell(x, y, ch: u32));
@symbol("tb_extend_cell") fn tb_extend_cell(x: int, y: int, ch: u32) int;

// Set the input mode, see [[termbox2::INPUT]].
//
// Termbox has two input modes:
// - INPUT.ESC When escape (\x1b) is in the buffer and there's no match
// for an escape sequence, a key event for [[termbox2::KEY]].ESC is returned.
// - INPUT.ALT When escape (\x1b) is in the buffer and there's no match
// for an an escape sequence, the next keyboard event is returned with a
// [[termbox2::MOD]].ALT modifier.
//
// You can also apply termbox2::INPUT.MOUSE via bitwise OR operation to either
// of the modes (e.g., INPUT.ALT | INPUT.MOUSE) to receive
// [[termbox2::mouse_event]] events. If none of the main two modes were
// set, but the mouse mode was, INPUT.ESC is used. If for some reason you've
// decided to use INPUT.ESC | INPUT.ALT, it will behave as if only INPUT.ESC
// was selected.
//
// If mode is INPUT.CURRENT, return the current input mode.
//
// The default input mode is INPUT.ESC.
export fn set_input_mode(mode: input_mode) (int | err) =
	int_or_err(tb_set_input_mode(mode: int));
@symbol("tb_set_input_mode") fn tb_set_input_mode(mode: int) int;

// Set the output mode, see [[termbox2::OUTPUT]].
//
// Termbox has multiple output modes:
//
// 1. OUTPUT.NORMAL => [0..8]
// 
// This mode provides 8 different colors (see [[termbox2::COLOR]]), plus
// COLOR.DEFAULT which skips sending a color code (i.e., uses the terminal's
// default color). Colors may be bitwise OR'd with style attributes, see
// [[termbox2::ATTRIBUTE]]. Extra attributes are available if OPT_ATTR_W == 64.
//
// Some notes: ATTRIBUTE.REVERSE and ATTRIBUTE.BRIGHT can be applied as either
// fg or bg attributes for the same effect. The rest of the attributes apply to
// fg only and are ignored as bg attributes.
//
// Example usage:
// 
// 	let fg = termbox2::COLOR.BLACK | termbox2::ATTRIBUTE.BOLD;
//	let bg = termbox2::COLOR.RED;
// 	termbox2::set_cell(x, y, '@', fg, bg);
//
// 2. OUTPUT._256 => [0..255] + ATTRIBUTE.HI_BLACK
//
// In this mode you get 256 distinct colors (plus default):
// - 0x00 (1): ATTRIBUTE.DEFAULT
// - ATTRIBUTE.HI_BLACK (1): COLOR.BLACK in OUTPUT.NORMAL
// - 0x01..0x07 (7): the next 7 colors as in OUTPUT.NORMAL
// - 0x08..0x0f (8): bright versions of the above
// - 0x10..0xe7 (216): 216 different colors
// - 0xe8..0xff (24): 24 different shades of gray
//
// All style attributes except ATTRIBUTE.BRIGHT may be bitwise OR'd as in
// OUTPUT.NORMAL.
//
// Note COLOR.BLACK must be used for black, as 0x00 represents default.
//
// 3. OUTPUT._216 => [0..216]
//
// This mode supports the 216-color range of OUTPUT._256 only, but you
// don't need to provide an offset:
// - 0x00 (1): COLOR.DEFAULT
// - 0x01..0xd8 (216): 216 different colors
//
// 4. OUTPUT.GRAYSCALE => [0..24]
//
// This mode supports the 24-color range of OUTPUT._256 only, but you
// don't need to provide an offset:
// - 0x00 (1): COLOR.DEFAULT
// - 0x01..0x18 (24): 24 different shades of gray
//
// 5. OUTPUT.TRUECOLOR => [0x000000..0xffffff] + ATTRIBUTE.HI_BLACK
//
// This mode provides 24-bit color on supported terminals.
// The format is 0xRRGGBB.
//
// All style attributes except ATTRIBUTE.BRIGHT may be bitwise OR'd as in
// OUTPUT.NORMAL.
//
// Note ATTRIBUTE.HI_BLACK must be used for black, as 0x000000 represents
// default.
//
// To use the terminal default color (i.e., to not send an escape code), pass
// COLOR.DEFAULT. For convenience, the value 0 is interpreted as COLOR.DEFAULT
// in all modes.
//
// Note, cell attributes persist after switching output modes. Any translation
// between, for example, OUTPUT.NORMAL's COLOR.RED and OUTPUT.TRUECOLOR's
// 0xff0000 must be performed by the caller. Also note that cells previously
// rendered in one mode may persist unchanged until the front buffer is cleared
// (such as after a resize event) at which point it will be re-interpreted and
// flushed according to the current mode. Callers may invoke
// [[termbox2::invalidate]] if it is desirable to immediately re-interpret
// and flush the entire screen according to the current mode.
//
// Note, not all terminals support all output modes, especially beyond
// OUTPUT.NORMAL. There is also no very reliable way to determine color
// support dynamically. If portability is desired, callers are recommended to
// use OUTPUT.NORMAL or make output mode end-user configurable. The same
// advice applies to style attributes.
//
// If mode is OUTPUT.CURRENT, return the current output mode.
//
// The default output mode is OUTPUT.NORMAL.
export fn set_output_mode(mode: output_mode) (int | err) =
	int_or_err(tb_set_output_mode(mode: int));
@symbol("tb_set_output_mode") fn tb_set_output_mode(mode: int) int;

// Wait for an event up to timeout_ms milliseconds and return it. If no event
// is available within the timeout period, ERR.NO_EVENT is returned. On a resize
// event, the underlying select(2) call may be interrupted, yielding a return
// code of ERR.POLL.
export fn peek_event(timeout_ms: size) (event | err) = {
	let e = tb_event {
		t = 0,
		mod = 0,
		key = 0,
		ch = 0,
		w = 0,
		h = 0,
		x = 0,
		y = 0,
	};
	let r = tb_peek_event(&e, timeout_ms: int);
	return if (r < 0) r: err else tb_event_to_event(e);
};
@symbol("tb_peek_event") fn tb_peek_event(ev: *tb_event, t: int) int;

// Same as [[termbox2::peek_event]] except no timeout.
export fn poll_event() (event | err) = {
	let e = tb_event {
		t = 0,
		mod = 0,
		key = 0,
		ch = 0,
		w = 0,
		h = 0,
		x = 0,
		y = 0,
	};
	let r = tb_poll_event(&e);
	return if (r < 0) r: err else tb_event_to_event(e);
};
@symbol("tb_poll_event") fn tb_poll_event(ev: *tb_event) int;

// Internal termbox fds that can be used with poll(2), select(2), etc.
// externally. Callers must invoke [[termbox2::poll_event]] or
// [[termbox2::peek_event]] if fds become readable.
export fn get_fds() ((int, int) | err) = {
	let ttyfd: int = 0;
	let resizefd: int = 0;
	let r = tb_get_fds(&ttyfd, &resizefd);
	return if (r < 0) r: err else (ttyfd, resizefd);
};
@symbol("tb_get_fds") fn tb_get_fds(ttyfd: *int, resizefd: *int) int;

// Print function. On success, it returns the width of the printed string.
// Strings are interpreted as UTF-8.
//
// No attempt is made to do proper grapheme cluster parsing. With
// [[termbox2::OPT_EGC]] enabled, as a very coarse approximation, codepoints
// of width 0 are tacked on to the previous codepoint via
// [[termbox2::extend_cell]].
//
// Non-printable characters (iswprint(3)) and truncated UTF-8 byte sequences
// are replaced with U+FFFD.
//
// Newlines (\n) are supported with the caveat that the return value will be
// the width of the string as if it were on a single line.
//
// If the starting coordinate is out of bounds, ERR.OUT_OF_BOUNDS is returned.
// If the starting coordinates is in bounds, but goes out of bounds, then the
// out-of-bounds portions of the string are ignored.
//
// For finer control, use [[termbox2::set_cell]].
export fn print(
	x: int,
	y: int,
	fg: uattr,
	bg: uattr,
	fmt_str: str,
	args: fmt::field...
) (size | err) = {
	let s = fmt::asprintf(fmt_str, args...)!;
	defer free(s);
	let cstr = c::fromstr(s)!;
	defer free(cstr);
	let outw: size = 0;
	let e = tb_print_ex(x, y, fg, bg, &outw, cstr);
	return if (e < 0) e: err else outw;
};
@symbol("tb_print_ex") fn tb_print_ex(
	x: int,
	y: int,
	fg: uattr,
	bg: uattr,
	out_w: *size,
	cstr: const *c::char,
) int;

// Send raw bytes to the terminal.
export fn send(buf: []u8) (void | err) =
	void_or_err(tb_send(&buf[0]: *c::char, len(buf)));
@symbol("tb_send") fn tb_send(buf: const *c::char, nbuf: size) int;

// Returns a string explaining the given error code. The caller must free
// the return value.
export fn strerror(e: err) str = switch (e) {
	case ERR.HARE_BUILD => 
		yield fmt::asprintf(
			"Cannot initialize Termbox because of a mismatch "
			"between the C and Hare build options.\n"
			"Hare OPT_EGC is {} and C TB_OPT_EGC is {}\n"
			"Hare OPT_ATTR_W {} and  C TB_OPT_ATTR_W is {}",
			OPT_EGC, has_egc(), OPT_ATTR_W, attr_width()
		)!;
	case ERR.HARE_INVALID_UTF8 => 
		yield fmt::asprintf("Invalid UTF8 sequence recieved from tb")!;
	case ERR.HARE_OTHER =>
		yield fmt::asprintf("Unknown error in the Hare bindings")!;
	case => yield match (str_or_err(tb_strerror(e: int))) {
		case err => yield fmt::asprintf(
			"Unexpected error while interpreting previous error"
		)!;
		case let estr: str => yield estr;
	};
};
@symbol("tb_strerror") fn tb_strerror(e: int) const *c::char;

// Returns true if the the C termbox2 library was compiled with truecolor
// support (i.e., if [[termbox2::OPT_ATTR_W]] is 32 or 64).
export fn has_truecolor() bool = tb_has_truecolor() != 0;
@symbol("tb_has_truecolor") fn tb_has_truecolor() int;

// Returns true if the the C termbox2 library was compiled with EGC
// support (i.e., if [[termbox2::OPT_EGC]] is true).
export fn has_egc() bool = tb_has_egc() != 0;
@symbol("tb_has_egc") fn tb_has_egc() int;

// Returns the value of the TB_OPT_ATTR_W compile time option of the
// C termbox2 library.
export fn attr_width() size = tb_attr_width(): size;
@symbol("tb_attr_width") fn tb_attr_width() int;

// Returns the string version of the C termbox2 library.
export fn version() (str | err) = str_or_err(tb_version());
@symbol("tb_version") fn tb_version() const *c::char;

Generated with cgit - Back to sebastiano.tronto.net