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,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
+ }