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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 987138e46b00b9460893570cc75437b50c569fa1
4
+ data.tar.gz: 80c60904af6281c8b8a90b9a021774b6b3402476
5
+ SHA512:
6
+ metadata.gz: 1eb154f3f71cb796a8e7d0ff30e16d8e3ba6db432cb45e1b15edd2d536e23cc529e5ec6417a888b99c3bed0681693be4eb5f8d6ad916d971efdb7bda69100c04
7
+ data.tar.gz: fb941fb5601b298a72f858c19b177cd2b322768e7245ad3ef9603a15c822eae50dc78cc6efae80576f9a74c8716de472a90c8c85b5274abc029d867a8a4f9f39
data/.gitignore CHANGED
@@ -1,2 +1,4 @@
1
1
  vendor/**
2
2
  .bundle/**
3
+ *.o
4
+ *.so
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
3
  gemspec
@@ -5,20 +5,18 @@ PATH
5
5
  ffi
6
6
 
7
7
  GEM
8
- remote: http://rubygems.org/
8
+ remote: https://rubygems.org/
9
9
  specs:
10
- diff-lcs (1.1.2)
11
- ffi (1.0.7)
12
- rake (>= 0.8.7)
13
- rake (0.8.7)
14
- rspec (2.5.0)
15
- rspec-core (~> 2.5.0)
16
- rspec-expectations (~> 2.5.0)
17
- rspec-mocks (~> 2.5.0)
18
- rspec-core (2.5.1)
19
- rspec-expectations (2.5.0)
20
- diff-lcs (~> 1.1.2)
21
- rspec-mocks (2.5.0)
10
+ diff-lcs (1.2.5)
11
+ ffi (1.9.3)
12
+ rspec (2.14.1)
13
+ rspec-core (~> 2.14.0)
14
+ rspec-expectations (~> 2.14.0)
15
+ rspec-mocks (~> 2.14.0)
16
+ rspec-core (2.14.7)
17
+ rspec-expectations (2.14.4)
18
+ diff-lcs (>= 1.1.3, < 2.0)
19
+ rspec-mocks (2.14.4)
22
20
 
23
21
  PLATFORMS
24
22
  ruby
@@ -0,0 +1,14 @@
1
+ rb_termbox - [Termbox](https://github.com/nsf/termbox) binding
2
+ ====================
3
+
4
+ ### Development Notes ###
5
+ - Run ```ruby ext/termbox/extconf.rb && make```
6
+ - Install bundler and ````bundle install````
7
+ - Run ruby sample/keyboard.rb to get a feel for what you can do. It's a partial port of termbox's own sample keyboard application. (Ctrl + Q to quit)
8
+
9
+ WIP Keyboard example:
10
+ <img src="http://i.imgur.com/aslMxFi.png"/>
11
+
12
+ ### Contributors ###
13
+ James Cook
14
+ Paul Schuegraf [@pscheugr](https://github.com/pschuegr)
@@ -0,0 +1,78 @@
1
+ struct bytebuffer {
2
+ char *buf;
3
+ int len;
4
+ int cap;
5
+ };
6
+
7
+ static void bytebuffer_reserve(struct bytebuffer *b, int cap) {
8
+ if (b->cap >= cap) {
9
+ return;
10
+ }
11
+
12
+ // prefer doubling capacity
13
+ if (b->cap * 2 >= cap) {
14
+ cap = b->cap * 2;
15
+ }
16
+
17
+ char *newbuf = malloc(cap);
18
+ if (b->len > 0) {
19
+ // copy what was there, b->len > 0 assumes b->buf != null
20
+ memcpy(newbuf, b->buf, b->len);
21
+ }
22
+ if (b->buf) {
23
+ // in case there was an allocated buffer, free it
24
+ free(b->buf);
25
+ }
26
+ b->buf = newbuf;
27
+ b->cap = cap;
28
+ }
29
+
30
+ static void bytebuffer_init(struct bytebuffer *b, int cap) {
31
+ b->cap = 0;
32
+ b->len = 0;
33
+ b->buf = 0;
34
+
35
+ if (cap > 0) {
36
+ b->cap = cap;
37
+ b->buf = malloc(cap); // just assume malloc works always
38
+ }
39
+ }
40
+
41
+ static void bytebuffer_free(struct bytebuffer *b) {
42
+ if (b->buf)
43
+ free(b->buf);
44
+ }
45
+
46
+ static void bytebuffer_clear(struct bytebuffer *b) {
47
+ b->len = 0;
48
+ }
49
+
50
+ static void bytebuffer_append(struct bytebuffer *b, const char *data, int len) {
51
+ bytebuffer_reserve(b, b->len + len);
52
+ memcpy(b->buf + b->len, data, len);
53
+ b->len += len;
54
+ }
55
+
56
+ static void bytebuffer_puts(struct bytebuffer *b, const char *str) {
57
+ bytebuffer_append(b, str, strlen(str));
58
+ }
59
+
60
+ static void bytebuffer_resize(struct bytebuffer *b, int len) {
61
+ bytebuffer_reserve(b, len);
62
+ b->len = len;
63
+ }
64
+
65
+ static void bytebuffer_flush(struct bytebuffer *b, int fd) {
66
+ write(fd, b->buf, b->len);
67
+ bytebuffer_clear(b);
68
+ }
69
+
70
+ static void bytebuffer_truncate(struct bytebuffer *b, int n) {
71
+ if (n <= 0)
72
+ return;
73
+ if (n > b->len)
74
+ n = b->len;
75
+ const int nmove = b->len - n;
76
+ memmove(b->buf, b->buf+n, nmove);
77
+ b->len -= n;
78
+ }
@@ -0,0 +1,3 @@
1
+ require "mkmf"
2
+ $CFLAGS = "-fPIC -O0 -g -std=gnu99 -D_GNU_SOURCE"
3
+ create_makefile("libtermbox")
@@ -0,0 +1,96 @@
1
+ // if s1 starts with s2 returns true, else false
2
+ // len is the length of s1
3
+ // s2 should be null-terminated
4
+ static bool starts_with(const char *s1, int len, const char *s2)
5
+ {
6
+ int n = 0;
7
+ while (*s2 && n < len) {
8
+ if (*s1++ != *s2++)
9
+ return false;
10
+ n++;
11
+ }
12
+ return *s2 == 0;
13
+ }
14
+
15
+ // convert escape sequence to event, and return consumed bytes on success (failure == 0)
16
+ static int parse_escape_seq(struct tb_event *event, const char *buf, int len)
17
+ {
18
+ // it's pretty simple here, find 'starts_with' match and return
19
+ // success, else return failure
20
+ int i;
21
+ for (i = 0; keys[i]; i++) {
22
+ if (starts_with(buf, len, keys[i])) {
23
+ event->ch = 0;
24
+ event->key = 0xFFFF-i;
25
+ return strlen(keys[i]);
26
+ }
27
+ }
28
+ return 0;
29
+ }
30
+
31
+ static bool extract_event(struct tb_event *event, struct bytebuffer *inbuf, int inputmode)
32
+ {
33
+ const char *buf = inbuf->buf;
34
+ const int len = inbuf->len;
35
+ if (len == 0)
36
+ return false;
37
+
38
+ if (buf[0] == '\033') {
39
+ int n = parse_escape_seq(event, buf, len);
40
+ if (n) {
41
+ bytebuffer_truncate(inbuf, n);
42
+ return true;
43
+ } else {
44
+ // it's not escape sequence, then it's ALT or ESC,
45
+ // check inputmode
46
+ switch (inputmode) {
47
+ case TB_INPUT_ESC:
48
+ // if we're in escape mode, fill ESC event, pop
49
+ // buffer, return success
50
+ event->ch = 0;
51
+ event->key = TB_KEY_ESC;
52
+ event->mod = 0;
53
+ bytebuffer_truncate(inbuf, 1);
54
+ return true;
55
+ case TB_INPUT_ALT:
56
+ // if we're in alt mode, set ALT modifier to
57
+ // event and redo parsing
58
+ event->mod = TB_MOD_ALT;
59
+ bytebuffer_truncate(inbuf, 1);
60
+ return extract_event(event, inbuf, inputmode);
61
+ default:
62
+ assert(!"never got here");
63
+ break;
64
+ }
65
+ }
66
+ }
67
+
68
+ // if we're here, this is not an escape sequence and not an alt sequence
69
+ // so, it's a FUNCTIONAL KEY or a UNICODE character
70
+
71
+ // first of all check if it's a functional key
72
+ if ((unsigned char)buf[0] <= TB_KEY_SPACE ||
73
+ (unsigned char)buf[0] == TB_KEY_BACKSPACE2)
74
+ {
75
+ // fill event, pop buffer, return success */
76
+ event->ch = 0;
77
+ event->key = (uint16_t)buf[0];
78
+ bytebuffer_truncate(inbuf, 1);
79
+ return true;
80
+ }
81
+
82
+ // feh... we got utf8 here
83
+
84
+ // check if there is all bytes
85
+ if (len >= tb_utf8_char_length(buf[0])) {
86
+ /* everything ok, fill event, pop buffer, return success */
87
+ tb_utf8_char_to_unicode(&event->ch, buf);
88
+ event->key = 0;
89
+ bytebuffer_truncate(inbuf, tb_utf8_char_length(buf[0]));
90
+ return true;
91
+ }
92
+
93
+ // event isn't recognized, perhaps there is not enough bytes in utf8
94
+ // sequence
95
+ return false;
96
+ }
@@ -0,0 +1,291 @@
1
+ enum {
2
+ T_ENTER_CA,
3
+ T_EXIT_CA,
4
+ T_SHOW_CURSOR,
5
+ T_HIDE_CURSOR,
6
+ T_CLEAR_SCREEN,
7
+ T_SGR0,
8
+ T_UNDERLINE,
9
+ T_BOLD,
10
+ T_BLINK,
11
+ T_REVERSE,
12
+ T_ENTER_KEYPAD,
13
+ T_EXIT_KEYPAD,
14
+ T_FUNCS_NUM,
15
+ };
16
+
17
+ #define EUNSUPPORTED_TERM -1
18
+
19
+ // rxvt-256color
20
+ static const char *rxvt_256color_keys[] = {
21
+ "\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
22
+ };
23
+ static const char *rxvt_256color_funcs[] = {
24
+ "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>",
25
+ };
26
+
27
+ // Eterm
28
+ static const char *eterm_keys[] = {
29
+ "\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
30
+ };
31
+ static const char *eterm_funcs[] = {
32
+ "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "",
33
+ };
34
+
35
+ // screen
36
+ static const char *screen_keys[] = {
37
+ "\033OP","\033OQ","\033OR","\033OS","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[1~","\033[4~","\033[5~","\033[6~","\033OA","\033OB","\033OD","\033OC", 0
38
+ };
39
+ static const char *screen_funcs[] = {
40
+ "\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l", "\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>",
41
+ };
42
+
43
+ // rxvt-unicode
44
+ static const char *rxvt_unicode_keys[] = {
45
+ "\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
46
+ };
47
+ static const char *rxvt_unicode_funcs[] = {
48
+ "\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>",
49
+ };
50
+
51
+ // linux
52
+ static const char *linux_keys[] = {
53
+ "\033[[A","\033[[B","\033[[C","\033[[D","\033[[E","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[1~","\033[4~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
54
+ };
55
+ static const char *linux_funcs[] = {
56
+ "", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "",
57
+ };
58
+
59
+ // xterm
60
+ static const char *xterm_keys[] = {
61
+ "\033OP","\033OQ","\033OR","\033OS","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033OH","\033OF","\033[5~","\033[6~","\033OA","\033OB","\033OD","\033OC", 0
62
+ };
63
+ static const char *xterm_funcs[] = {
64
+ "\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l", "\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>",
65
+ };
66
+
67
+ static struct term {
68
+ const char *name;
69
+ const char **keys;
70
+ const char **funcs;
71
+ } terms[] = {
72
+ {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
73
+ {"Eterm", eterm_keys, eterm_funcs},
74
+ {"screen", screen_keys, screen_funcs},
75
+ {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
76
+ {"linux", linux_keys, linux_funcs},
77
+ {"xterm", xterm_keys, xterm_funcs},
78
+ {0, 0, 0},
79
+ };
80
+
81
+ static bool init_from_terminfo = false;
82
+ static const char **keys;
83
+ static const char **funcs;
84
+
85
+ static int try_compatible(const char *term, const char *name,
86
+ const char **tkeys, const char **tfuncs)
87
+ {
88
+ if (strstr(term, name)) {
89
+ keys = tkeys;
90
+ funcs = tfuncs;
91
+ return 0;
92
+ }
93
+
94
+ return EUNSUPPORTED_TERM;
95
+ }
96
+
97
+ static int init_term_builtin(void)
98
+ {
99
+ int i;
100
+ const char *term = getenv("TERM");
101
+
102
+ if (term) {
103
+ for (i = 0; terms[i].name; i++) {
104
+ if (!strcmp(terms[i].name, term)) {
105
+ keys = terms[i].keys;
106
+ funcs = terms[i].funcs;
107
+ return 0;
108
+ }
109
+ }
110
+
111
+ /* let's do some heuristic, maybe it's a compatible terminal */
112
+ if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)
113
+ return 0;
114
+ if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0)
115
+ return 0;
116
+ if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)
117
+ return 0;
118
+ if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0)
119
+ return 0;
120
+ if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0)
121
+ return 0;
122
+ /* let's assume that 'cygwin' is xterm compatible */
123
+ if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0)
124
+ return 0;
125
+ }
126
+
127
+ return EUNSUPPORTED_TERM;
128
+ }
129
+
130
+ //----------------------------------------------------------------------
131
+ // terminfo
132
+ //----------------------------------------------------------------------
133
+
134
+ static char *read_file(const char *file) {
135
+ FILE *f = fopen(file, "rb");
136
+ if (!f)
137
+ return 0;
138
+
139
+ struct stat st;
140
+ if (fstat(fileno(f), &st) != 0) {
141
+ fclose(f);
142
+ return 0;
143
+ }
144
+
145
+ char *data = malloc(st.st_size);
146
+ if (!data) {
147
+ fclose(f);
148
+ return 0;
149
+ }
150
+
151
+ if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) {
152
+ fclose(f);
153
+ free(data);
154
+ return 0;
155
+ }
156
+
157
+ fclose(f);
158
+ return data;
159
+ }
160
+
161
+ static char *terminfo_try_path(const char *path, const char *term) {
162
+ char tmp[4096];
163
+ snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
164
+ tmp[sizeof(tmp)-1] = '\0';
165
+ char *data = read_file(tmp);
166
+ if (data) {
167
+ return data;
168
+ }
169
+
170
+ // fallback to darwin specific dirs structure
171
+ snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
172
+ tmp[sizeof(tmp)-1] = '\0';
173
+ return read_file(tmp);
174
+ }
175
+
176
+ static char *load_terminfo(void) {
177
+ char tmp[4096];
178
+ const char *term = getenv("TERM");
179
+ if (!term) {
180
+ return 0;
181
+ }
182
+
183
+ // if TERMINFO is set, no other directory should be searched
184
+ const char *terminfo = getenv("TERMINFO");
185
+ if (terminfo) {
186
+ return terminfo_try_path(terminfo, term);
187
+ }
188
+
189
+ // next, consider ~/.terminfo
190
+ const char *home = getenv("HOME");
191
+ if (home) {
192
+ snprintf(tmp, sizeof(tmp), "%s/.terminfo", home);
193
+ tmp[sizeof(tmp)-1] = '\0';
194
+ char *data = terminfo_try_path(tmp, term);
195
+ if (data)
196
+ return data;
197
+ }
198
+
199
+ // next, TERMINFO_DIRS
200
+ const char *dirs = getenv("TERMINFO_DIRS");
201
+ if (dirs) {
202
+ snprintf(tmp, sizeof(tmp), "%s", dirs);
203
+ tmp[sizeof(tmp)-1] = '\0';
204
+ char *dir = strtok(tmp, ":");
205
+ while (dir) {
206
+ const char *cdir = dir;
207
+ if (strcmp(cdir, "") == 0) {
208
+ cdir = "/usr/share/terminfo";
209
+ }
210
+ char *data = terminfo_try_path(cdir, term);
211
+ if (data)
212
+ return data;
213
+ dir = strtok(0, ":");
214
+ }
215
+ }
216
+
217
+ // fallback to /usr/share/terminfo
218
+ return terminfo_try_path("/usr/share/terminfo", term);
219
+ }
220
+
221
+ #define TI_MAGIC 0432
222
+ #define TI_HEADER_LENGTH 12
223
+ #define TB_KEYS_NUM 22
224
+
225
+ static const char *terminfo_copy_string(char *data, int str, int table) {
226
+ const int16_t off = *(int16_t*)(data + str);
227
+ const char *src = data + table + off;
228
+ int len = strlen(src);
229
+ char *dst = malloc(len+1);
230
+ strcpy(dst, src);
231
+ return dst;
232
+ }
233
+
234
+ static const int16_t ti_funcs[] = {
235
+ 28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,
236
+ };
237
+
238
+ static const int16_t ti_keys[] = {
239
+ 66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69,
240
+ 70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61,
241
+ 79, 83,
242
+ };
243
+
244
+ static int init_term(void) {
245
+ int i;
246
+ char *data = load_terminfo();
247
+ if (!data) {
248
+ init_from_terminfo = false;
249
+ return init_term_builtin();
250
+ }
251
+
252
+ int16_t *header = (int16_t*)data;
253
+ if ((header[1] + header[2]) % 2) {
254
+ // old quirk to align everything on word boundaries
255
+ header[2] += 1;
256
+ }
257
+
258
+ const int str_offset = TI_HEADER_LENGTH +
259
+ header[1] + header[2] + 2 * header[3];
260
+ const int table_offset = str_offset + 2 * header[4];
261
+
262
+ keys = malloc(sizeof(const char*) * (TB_KEYS_NUM+1));
263
+ for (i = 0; i < TB_KEYS_NUM; i++) {
264
+ keys[i] = terminfo_copy_string(data,
265
+ str_offset + 2 * ti_keys[i], table_offset);
266
+ }
267
+ keys[TB_KEYS_NUM] = 0;
268
+
269
+ funcs = malloc(sizeof(const char*) * T_FUNCS_NUM);
270
+ for (i = 0; i < T_FUNCS_NUM; i++) {
271
+ funcs[i] = terminfo_copy_string(data,
272
+ str_offset + 2 * ti_funcs[i], table_offset);
273
+ }
274
+
275
+ init_from_terminfo = true;
276
+ return 0;
277
+ }
278
+
279
+ static void shutdown_term(void) {
280
+ if (init_from_terminfo) {
281
+ int i;
282
+ for (i = 0; i < TB_KEYS_NUM; i++) {
283
+ free((void*)keys[i]);
284
+ }
285
+ for (i = 0; i < T_FUNCS_NUM; i++) {
286
+ free((void*)funcs[i]);
287
+ }
288
+ free(keys);
289
+ free(funcs);
290
+ }
291
+ }