File Coverage

lib/Termbox.pm
Criterion Covered Total %
statement 45 45 100.0
branch n/a
condition n/a
subroutine 15 15 100.0
pod n/a
total 60 60 100.0


line stmt bran cond sub pod time code
1             package Termbox {
2 2     2   109327 use 5.020;
  2         16  
3 2     2   9 use strict;
  2         4  
  2         37  
4 2     2   9 use warnings;
  2         2  
  2         124  
5             our $VERSION = "2.01";
6             #
7 2     2   1119 use File::ShareDir qw[dist_dir];
  2         56629  
  2         116  
8 2     2   872 use File::Spec::Functions qw[catdir canonpath];
  2         1681  
  2         122  
9             #
10 2     2   1068 use FFI::CheckLib;
  2         15030  
  2         160  
11 2     2   1555 use FFI::Platypus 2;
  2         14706  
  2         75  
12 2     2   938 use FFI::Platypus::Memory qw( malloc free );
  2         27386  
  2         244  
13             $ENV{FFI_PLATYPUS_DLERROR} = 1;
14             my $ffi = FFI::Platypus->new(
15             api => 1,
16             lang => 'C',
17             lib => find_lib_or_exit(
18             lib => 'termbox2',
19             recursive => 1,
20             libpath => [ qw[ . ./share/lib], canonpath( catdir( dist_dir(__PACKAGE__), 'lib' ) ) ]
21             )
22             );
23             #
24 2     2   15 use base qw[Exporter];
  2         3  
  2         161  
25 2     2   12 use vars qw[@EXPORT_OK @EXPORT %EXPORT_TAGS];
  2         3  
  2         375  
26              
27             # Utility functions that should be at the end but we need it here
28             $ffi->attach( tb_has_truecolor => ['void'] => 'int' );
29             $ffi->attach( tb_has_egc => ['void'] => 'int' );
30             #
31             our $TRUECOLOR = tb_has_truecolor();
32             our $uintattr_t = $TRUECOLOR ? 'uint32_t' : 'uint16_t';
33             #
34             $EXPORT_TAGS{api} = [
35             qw[
36             tb_init tb_init_file tb_init_fd tb_init_rwfd tb_shutdown
37             tb_width tb_height
38             tb_clear tb_set_clear_attrs
39             tb_present
40             tb_invalidate
41             tb_set_cursor tb_hide_cursor
42             tb_set_cell tb_set_cell_ex tb_extend_cell
43             tb_set_input_mode
44             tb_set_output_mode
45             tb_peek_event
46             tb_poll_event
47             tb_get_fds
48             tb_print
49             tb_send
50             tb_set_func
51             tb_utf8_char_length tb_utf8_char_to_unicode tb_utf8_unicode_to_char
52             tb_last_errno tb_strerror
53             tb_cell_buffer
54             tb_has_truecolor tb_has_egc
55             tb_version
56             ]
57             ];
58             #
59             sub _export ($$) {
60 12     12   19 my ( $tag, $values ) = @_;
61 12         14 push @{ $EXPORT_TAGS{$tag} }, keys %$values;
  12         89  
62 2     2   13 no strict 'refs';
  2         3  
  2         996  
63 12         52 for my $key ( keys %$values ) {
64 264     4   773 *{ __PACKAGE__ . '::' . $key } = sub () { $values->{$key} }
  4         23  
65 264         619 }
66             }
67             #
68             _export keys => {
69              
70             # Terminal-dependent key constants (tb_event.key) and terminfo capabilities
71             TB_KEY_F1 => ( 0xFFFF - 0 ),
72             TB_KEY_F2 => ( 0xFFFF - 1 ),
73             TB_KEY_F3 => ( 0xFFFF - 2 ),
74             TB_KEY_F4 => ( 0xFFFF - 3 ),
75             TB_KEY_F5 => ( 0xFFFF - 4 ),
76             TB_KEY_F6 => ( 0xFFFF - 5 ),
77             TB_KEY_F7 => ( 0xFFFF - 6 ),
78             TB_KEY_F8 => ( 0xFFFF - 7 ),
79             TB_KEY_F9 => ( 0xFFFF - 8 ),
80             TB_KEY_F10 => ( 0xFFFF - 9 ),
81             TB_KEY_F11 => ( 0xFFFF - 10 ),
82             TB_KEY_F12 => ( 0xFFFF - 11 ),
83             TB_KEY_INSERT => ( 0xFFFF - 12 ),
84             TB_KEY_DELETE => ( 0xFFFF - 13 ),
85             TB_KEY_HOME => ( 0xFFFF - 14 ),
86             TB_KEY_END => ( 0xFFFF - 15 ),
87             TB_KEY_PGUP => ( 0xFFFF - 16 ),
88             TB_KEY_PGDN => ( 0xFFFF - 17 ),
89             TB_KEY_ARROW_UP => ( 0xFFFF - 18 ),
90             TB_KEY_ARROW_DOWN => ( 0xFFFF - 19 ),
91             TB_KEY_ARROW_LEFT => ( 0xFFFF - 20 ),
92             TB_KEY_ARROW_RIGHT => ( 0xFFFF - 21 ),
93             TB_KEY_BACK_TAB => ( 0xffff - 22 ),
94             TB_KEY_MOUSE_LEFT => ( 0xffff - 23 ),
95             TB_KEY_MOUSE_RIGHT => ( 0xffff - 24 ),
96             TB_KEY_MOUSE_MIDDLE => ( 0xffff - 25 ),
97             TB_KEY_MOUSE_RELEASE => ( 0xffff - 26 ),
98             TB_KEY_MOUSE_WHEEL_UP => ( 0xffff - 27 ),
99             TB_KEY_MOUSE_WHEEL_DOWN => ( 0xffff - 28 ),
100             #
101             TB_CAP_F1 => 0,
102             TB_CAP_F2 => 1,
103             TB_CAP_F3 => 2,
104             TB_CAP_F4 => 3,
105             TB_CAP_F5 => 4,
106             TB_CAP_F6 => 5,
107             TB_CAP_F7 => 6,
108             TB_CAP_F8 => 7,
109             TB_CAP_F9 => 8,
110             TB_CAP_F10 => 9,
111             TB_CAP_F11 => 10,
112             TB_CAP_F12 => 11,
113             TB_CAP_INSERT => 12,
114             TB_CAP_DELETE => 13,
115             TB_CAP_HOME => 14,
116             TB_CAP_END => 15,
117             TB_CAP_PGUP => 16,
118             TB_CAP_PGDN => 17,
119             TB_CAP_ARROW_UP => 18,
120             TB_CAP_ARROW_DOWN => 19,
121             TB_CAP_ARROW_LEFT => 20,
122             TB_CAP_ARROW_RIGHT => 21,
123             TB_CAP_BACK_TAB => 22,
124             TB_CAP__COUNT_KEYS => 23,
125             TB_CAP_ENTER_CA => 23,
126             TB_CAP_EXIT_CA => 24,
127             TB_CAP_SHOW_CURSOR => 25,
128             TB_CAP_HIDE_CURSOR => 26,
129             TB_CAP_CLEAR_SCREEN => 27,
130             TB_CAP_SGR0 => 28,
131             TB_CAP_UNDERLINE => 29,
132             TB_CAP_BOLD => 30,
133             TB_CAP_BLINK => 31,
134             TB_CAP_ITALIC => 32,
135             TB_CAP_REVERSE => 33,
136             TB_CAP_ENTER_KEYPAD => 34,
137             TB_CAP_EXIT_KEYPAD => 35,
138             TB_CAP__COUNT => 36
139             };
140             _export colors => {
141             TB_DEFAULT => 0x0000,
142             TB_BLACK => 0x0001,
143             TB_RED => 0x0002,
144             TB_GREEN => 0x0003,
145             TB_YELLOW => 0x0004,
146             TB_BLUE => 0x0005,
147             TB_MAGENTA => 0x0006,
148             TB_CYAN => 0x0007,
149             TB_WHITE => 0x0008,
150             TB_BOLD => 0x0100,
151             TB_UNDERLINE => 0x0200,
152             TB_REVERSE => 0x0400,
153             TB_ITALIC => 0x0800,
154             TB_BLINK => 0x1000,
155             TB_256_BLACK => 0x2000, (
156             $TRUECOLOR ? (
157             TB_TRUECOLOR_BOLD => 0x01000000,
158             TB_TRUECOLOR_UNDERLINE => 0x02000000,
159             TB_TRUECOLOR_REVERSE => 0x04000000,
160             TB_TRUECOLOR_ITALIC => 0x08000000,
161             TB_TRUECOLOR_BLINK => 0x10000000,
162             TB_TRUECOLOR_BLACK => 0x20000000,
163             ) :
164             ()
165             )
166             };
167             _export event => {
168              
169             #~ Event types (tb_event.type)
170             TB_EVENT_KEY => 1,
171             TB_EVENT_RESIZE => 2,
172             TB_EVENT_MOUSE => 3,
173              
174             #~ Key modifiers (bitwise) (tb_event.mod)
175             TB_MOD_ALT => 1,
176             TB_MOD_CTRL => 2,
177             TB_MOD_SHIFT => 4,
178             TB_MOD_MOTION => 8,
179              
180             #~ Input modes (bitwise) (tb_set_input_mode)
181             TB_INPUT_CURRENT => 0,
182             TB_INPUT_ESC => 1,
183             TB_INPUT_ALT => 2,
184             TB_INPUT_MOUSE => 4,
185              
186             #~ Output modes (tb_set_output_mode)
187             TB_OUTPUT_CURRENT => 0,
188             TB_OUTPUT_NORMAL => 1,
189             TB_OUTPUT_256 => 2,
190             TB_OUTPUT_216 => 3,
191             TB_OUTPUT_GRAYSCALE => 4,
192             ( $TRUECOLOR ? ( TB_OUTPUT_TRUECOLOR => 5 ) : () )
193             };
194             _export return => {
195             TB_OK => 0,
196             TB_ERR => -1,
197             TB_ERR_NEED_MORE => -2,
198             TB_ERR_INIT_ALREADY => -3,
199             TB_ERR_INIT_OPEN => -4,
200             TB_ERR_MEM => -5,
201             TB_ERR_NO_EVENT => -6,
202             TB_ERR_NO_TERM => -7,
203             TB_ERR_NOT_INIT => -8,
204             TB_ERR_OUT_OF_BOUNDS => -9,
205             TB_ERR_READ => -10,
206             TB_ERR_RESIZE_IOCTL => -11,
207             TB_ERR_RESIZE_PIPE => -12,
208             TB_ERR_RESIZE_SIGACTION => -13,
209             TB_ERR_POLL => -14,
210             TB_ERR_TCGETATTR => -15,
211             TB_ERR_TCSETATTR => -16,
212             TB_ERR_UNSUPPORTED_TERM => -17,
213             TB_ERR_RESIZE_WRITE => -18,
214             TB_ERR_RESIZE_POLL => -19,
215             TB_ERR_RESIZE_READ => -20,
216             TB_ERR_RESIZE_SSCANF => -21,
217             TB_ERR_CAP_COLLISION => -22
218             };
219             _export return =>
220             { TB_ERR_SELECT => TB_ERR_POLL(), TB_ERR_RESIZE_SELECT => TB_ERR_RESIZE_POLL() };
221             _export func => { TB_FUNC_EXTRACT_PRE => 0, TB_FUNC_EXTRACT_POST => 1 };
222             #
223             @EXPORT_OK = sort map { @$_ = sort @$_; @$_ } values %EXPORT_TAGS;
224             $EXPORT_TAGS{'all'} = \@EXPORT_OK; # When you want to import everything
225              
226             #
227             package #
228             Termbox::Cell {
229 2     2   994 use FFI::Platypus::Record;
  2         3182  
  2         264  
230             record_layout_1(
231             uint32_t => 'ch',
232             $Termbox::uintattr_t => 'fg',
233             $Termbox::uintattr_t => 'bg', (
234             Termbox::tb_has_egc() ? ( 'opaque' => 'ech', size_t => 'nech', size_t => 'cech' ) :
235             ()
236             )
237             );
238             };
239             $ffi->type('record(Termbox::Cell)');
240             #
241             package #
242             Termbox::Event {
243 2     2   15 use FFI::Platypus::Record;
  2         5  
  2         1412  
244             record_layout_1(
245             qw[
246             uint8_t type
247             uint8_t mod
248             uint16_t key
249             uint32_t ch
250             int32_t w
251             int32_t h
252             int32_t x
253             int32_t y
254             ]
255             );
256             };
257             #
258             $ffi->type('record(Termbox::Event)');
259             #
260             $ffi->attach( tb_init => ['void'] => 'int' );
261             $ffi->attach( tb_init_file => ['string'] => 'int' );
262             $ffi->attach( tb_init_fd => ['int'] => 'int' );
263             $ffi->attach( tb_init_rwfd => [ 'int', 'int' ] => 'int' );
264             $ffi->attach( tb_shutdown => ['void'] => 'void' );
265             #
266             $ffi->attach( tb_width => ['void'] => 'int' );
267             $ffi->attach( tb_height => ['void'] => 'int' );
268             #
269             $ffi->attach( tb_clear => ['void'] => 'void' );
270             $ffi->attach( tb_set_clear_attrs => [ $uintattr_t, $uintattr_t ] => 'void' );
271             #
272             $ffi->attach( tb_present => ['void'] => 'void' );
273             #
274             $ffi->attach( tb_invalidate => ['void'] => 'void' );
275             #
276             $ffi->attach( tb_set_cursor => [ 'int', 'int' ] => 'void' );
277             $ffi->attach( tb_hide_cursor => ['void'] => 'void' );
278             #
279             $ffi->attach(
280             tb_set_cell => [ 'int', 'int', 'uint32_t', $uintattr_t, $uintattr_t ] => 'int',
281             sub {
282             my ( $xsub, $x, $y, $ch, $fg, $bg ) = @_;
283             $xsub->( $x, $y, ord $ch, $fg, $bg );
284             }
285             );
286             $ffi->attach(
287             tb_set_cell_ex => [ 'int', 'int', 'uint32_t', 'size_t', $uintattr_t, $uintattr_t ] => 'int',
288             sub {
289             my ( $xsub, $x, $y, $ch, $nch, $fg, $bg ) = @_;
290             $xsub->( $x, $y, ord $ch, $nch, $fg, $bg );
291             }
292             );
293             $ffi->attach(
294             tb_extend_cell => [ 'int', 'int', 'uint32_t' ] => 'int',
295             sub {
296             my ( $xsub, $x, $y, $ch ) = @_;
297             $xsub->( $x, $y, ord $ch );
298             }
299             );
300             #
301             $ffi->attach( tb_set_input_mode => ['int'] => 'int' );
302             #
303             $ffi->attach( tb_set_output_mode => ['int'] => 'int' );
304             #
305             $ffi->attach( tb_peek_event => [ 'record(Termbox::Event)*', 'int' ] => 'int' );
306             #
307             $ffi->attach( tb_poll_event => ['record(Termbox::Event)*'] => 'int' );
308             #
309             $ffi->attach( tb_get_fds => [ 'int*', 'int*' ] => 'int' );
310             #
311             $ffi->attach( tb_print => [ 'int', 'int', $uintattr_t, $uintattr_t, 'string' ] => 'int' );
312              
313             #~ int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, ...);
314             #~ int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, const char *str);
315             #~ int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w, const char *fmt, ...);
316             #
317             $ffi->attach( tb_send => [ 'string', 'size_t' ] => 'int' );
318             #
319             $ffi->type( '(opaque, opaque)->int' => 'closure_t' ); # int (*fn)(struct tb_event *, size_t *)
320             $ffi->attach(
321             tb_set_func => [ 'int', 'closure_t' ] => 'int',
322             sub {
323             CORE::state $cache;
324             my ( $xsub, $fn_type, $func ) = @_;
325             $cache->{$fn_type}->unsticky if $cache->{$fn_type};
326             my $closure;
327             if ($func) {
328             $closure = $ffi->closure(
329             sub {
330             my ( $event, $size ) = @_;
331             $func->(
332             $ffi->cast( 'opaque', 'record(Termbox::Event)*', $event ),
333             $ffi->cast( 'opaque', 'size_t*', $size )
334             );
335             }
336             );
337             $closure->sticky;
338             }
339             $cache->{$fn_type} = $closure;
340             $xsub->( $fn_type, $closure );
341             }
342             );
343             #
344             $ffi->attach( tb_utf8_char_length => ['char'] => 'int' );
345             $ffi->attach( tb_utf8_char_to_unicode => [ 'uint32_t*', 'string*' ] => 'int' );
346             $ffi->attach( tb_utf8_unicode_to_char => [ 'string', 'uint32_t' ] => 'int' );
347             $ffi->attach( tb_last_errno => ['void'] => 'int' );
348             $ffi->attach( tb_strerror => ['int'] => 'string' );
349             $ffi->attach( tb_cell_buffer => ['void'] => 'record(Termbox::Cell)*' );
350              
351             # tb_has_truecolor and tbs_has_egc are defined near the top
352             $ffi->attach( tb_version => ['void'] => 'string' );
353             }
354             1;
355             __END__