2 * This wrapper is Copyright (c) 2020 S. Gilles <sgilles@umd.edu>
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * libtermkey is Copyright (c) 2007-2011 Paul Evans <leonerd@leonerd.org.uk>
25 * Permission is hereby granted, free of charge, to any person obtaining a copy
26 * of this software and associated documentation files (the "Software"), to deal
27 * in the Software without restriction, including without limitation the rights
28 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29 * copies of the Software, and to permit persons to whom the Software is
30 * furnished to do so, subject to the following conditions:
32 * The above copyright notice and this permission notice shall be included in
33 * all copies or substantial portions of the Software.
35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
52 #define STR(s) STR2(s)
55 #define WRAP_TERMKEY_NAME "termkey.TermKey*"
57 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 501
58 static void lua_setuservalue(lua_State
*L
, lua_Integer x
) {
59 luaL_checktype(L
, -1, LUA_TTABLE
);
64 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
65 static void lua_seti(lua_State
*L
, lua_Integer i
, lua_Integer d
) {
67 if (j
< 0 && j
> LUA_REGISTRYINDEX
) {
68 j
+= lua_gettop(L
) + 1;
70 lua_pushinteger(L
, d
);
76 static void table_to_fd_tk_flags(lua_State
*L
, int idx
, int *out_fd
, int *out_flags
) {
81 while (lua_next(L
, idx
)) {
84 sk
= lua_tolstring(L
, -1, &sk_len
);
86 if (!strncmp(sk
, "fd", sk_len
) && out_fd
) {
87 *out_fd
= lua_tointegerx(L
, -2, 0);
88 } else if (lua_toboolean(L
, -2) && out_flags
) {
90 All flags default to false, so we
91 need only set if value is non-false.
93 if (!strncmp(sk
, "nointerpret", sk_len
)) {
94 flags
|= TERMKEY_FLAG_NOINTERPRET
;
95 } else if (!strncmp(sk
, "convertkp", sk_len
)) {
96 flags
|= TERMKEY_FLAG_CONVERTKP
;
97 } else if (!strncmp(sk
, "raw", sk_len
)) {
98 flags
|= TERMKEY_FLAG_RAW
;
99 } else if (!strncmp(sk
, "utf8", sk_len
)) {
100 flags
|= TERMKEY_FLAG_UTF8
;
101 } else if (!strncmp(sk
, "notermios", sk_len
)) {
102 flags
|= TERMKEY_FLAG_NOTERMIOS
;
103 } else if (!strncmp(sk
, "spacesymbol", sk_len
)) {
104 flags
|= TERMKEY_FLAG_SPACESYMBOL
;
105 } else if (!strncmp(sk
, "ctrlc", sk_len
)) {
106 flags
|= TERMKEY_FLAG_CTRLC
;
107 } else if (!strncmp(sk
, "eintr", sk_len
)) {
108 flags
|= TERMKEY_FLAG_EINTR
;
109 } else if (!strncmp(sk
, "nostart", sk_len
)) {
110 flags
|= TERMKEY_FLAG_NOSTART
;
121 static void table_to_canon_flags(lua_State
*L
, int idx
, int *out_flags
) {
126 while (lua_next(L
, idx
)) {
128 lua_pushvalue(L
, -2);
129 sk
= lua_tolstring(L
, -1, &sk_len
);
131 if (lua_toboolean(L
, -2) && out_flags
) {
132 if (!strncmp(sk
, "spacesymbol", sk_len
)) {
133 flags
|= TERMKEY_CANON_SPACESYMBOL
;
134 } else if (!strncmp(sk
, "delbs", sk_len
)) {
135 flags
|= TERMKEY_CANON_DELBS
;
147 int wrap_termkey_start(lua_State
*L
) {
148 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
154 int wrap_termkey_stop(lua_State
*L
) {
155 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
161 int wrap_termkey_is_started(lua_State
*L
) {
162 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
163 lua_pushboolean(L
, termkey_is_started(*tk
));
168 int wrap_termkey_get_fd(lua_State
*L
) {
169 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
170 lua_pushinteger(L
, termkey_get_fd(*tk
));
175 int wrap_termkey_get_flags(lua_State
*L
) {
176 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
177 int flags
= termkey_get_flags(*tk
);
179 lua_createtable(L
, 0, 9);
181 lua_pushboolean(L
, !!(flags
& TERMKEY_FLAG_NOINTERPRET
));
182 lua_setfield(L
, -2, "nointerpret");
184 lua_pushboolean(L
, !!(flags
& TERMKEY_FLAG_CONVERTKP
));
185 lua_setfield(L
, -2, "convertkp");
187 lua_pushboolean(L
, !!(flags
&TERMKEY_FLAG_RAW
));
188 lua_setfield(L
, -2, "raw");
190 lua_pushboolean(L
, !!(flags
&TERMKEY_FLAG_UTF8
));
191 lua_setfield(L
, -2, "utf8");
193 lua_pushboolean(L
, !!(flags
&TERMKEY_FLAG_NOTERMIOS
));
194 lua_setfield(L
, -2, "notermios");
196 lua_pushboolean(L
, !!(flags
&TERMKEY_FLAG_SPACESYMBOL
));
197 lua_setfield(L
, -2, "spacesymbol");
199 lua_pushboolean(L
, !!(flags
&TERMKEY_FLAG_CTRLC
));
200 lua_setfield(L
, -2, "ctrlc");
202 lua_pushboolean(L
, !!(flags
&TERMKEY_FLAG_EINTR
));
203 lua_setfield(L
, -2, "eintr");
205 lua_pushboolean(L
, !!(flags
&TERMKEY_FLAG_NOSTART
));
206 lua_setfield(L
, -2, "nostart");
211 int wrap_termkey_set_flags(lua_State
*L
) {
212 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
215 if (lua_istable(L
, 2)) {
216 table_to_fd_tk_flags(L
, 2, 0, &flags
);
219 termkey_set_flags(*tk
, flags
);
224 int wrap_termkey_get_waittime(lua_State
*L
) {
225 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
226 lua_pushinteger(L
, termkey_get_waittime(*tk
));
231 int wrap_termkey_set_waittime(lua_State
*L
) {
232 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
234 int waittime
= lua_tointegerx(L
, 2, &valid
);
237 termkey_set_waittime(*tk
, waittime
);
243 int wrap_termkey_get_canonflags(lua_State
*L
) {
244 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
245 int flags
= termkey_get_canonflags(*tk
);
247 lua_createtable(L
, 0, 2);
249 lua_pushboolean(L
, !!(flags
& TERMKEY_CANON_SPACESYMBOL
));
250 lua_setfield(L
, -2, "spacesymbol");
252 lua_pushboolean(L
, !!(flags
& TERMKEY_CANON_DELBS
));
253 lua_setfield(L
, -2, "delbs");
258 int wrap_termkey_set_canonflags(lua_State
*L
) {
259 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
262 if (lua_istable(L
, 2)) {
263 table_to_canon_flags(L
, 2, &flags
);
266 termkey_set_canonflags(*tk
, flags
);
271 int wrap_termkey_get_buffer_size(lua_State
*L
) {
272 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
273 lua_pushinteger(L
, termkey_get_buffer_size(*tk
));
278 int wrap_termkey_set_buffer_size(lua_State
*L
) {
279 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
281 size_t buffer_size
= lua_tointegerx(L
, 2, &valid
);
284 termkey_set_buffer_size(*tk
, buffer_size
);
290 int wrap_termkey_get_buffer_remaining(lua_State
*L
) {
291 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
292 lua_pushinteger(L
, termkey_get_buffer_remaining(*tk
));
297 static const char *string_from_sym(TermKeySym sym
) {
299 case TERMKEY_SYM_UNKNOWN
: return "unknown";
300 case TERMKEY_SYM_NONE
: return "none";
301 case TERMKEY_SYM_BACKSPACE
: return "backspace";
302 case TERMKEY_SYM_TAB
: return "tab";
303 case TERMKEY_SYM_ENTER
: return "enter";
304 case TERMKEY_SYM_ESCAPE
: return "escape";
305 case TERMKEY_SYM_SPACE
: return "space";
306 case TERMKEY_SYM_DEL
: return "del";
307 case TERMKEY_SYM_UP
: return "up";
308 case TERMKEY_SYM_DOWN
: return "down";
309 case TERMKEY_SYM_LEFT
: return "left";
310 case TERMKEY_SYM_RIGHT
: return "right";
311 case TERMKEY_SYM_BEGIN
: return "begin";
312 case TERMKEY_SYM_FIND
: return "find";
313 case TERMKEY_SYM_INSERT
: return "insert";
314 case TERMKEY_SYM_DELETE
: return "delete";
315 case TERMKEY_SYM_SELECT
: return "select";
316 case TERMKEY_SYM_PAGEUP
: return "pageup";
317 case TERMKEY_SYM_PAGEDOWN
: return "pagedown";
318 case TERMKEY_SYM_HOME
: return "home";
319 case TERMKEY_SYM_END
: return "end";
320 case TERMKEY_SYM_CANCEL
: return "cancel";
321 case TERMKEY_SYM_CLEAR
: return "clear";
322 case TERMKEY_SYM_CLOSE
: return "close";
323 case TERMKEY_SYM_COMMAND
: return "command";
324 case TERMKEY_SYM_COPY
: return "copy";
325 case TERMKEY_SYM_EXIT
: return "exit";
326 case TERMKEY_SYM_HELP
: return "help";
327 case TERMKEY_SYM_MARK
: return "mark";
328 case TERMKEY_SYM_MESSAGE
: return "message";
329 case TERMKEY_SYM_MOVE
: return "move";
330 case TERMKEY_SYM_OPEN
: return "open";
331 case TERMKEY_SYM_OPTIONS
: return "options";
332 case TERMKEY_SYM_PRINT
: return "print";
333 case TERMKEY_SYM_REDO
: return "redo";
334 case TERMKEY_SYM_REFERENCE
: return "reference";
335 case TERMKEY_SYM_REFRESH
: return "refresh";
336 case TERMKEY_SYM_REPLACE
: return "replace";
337 case TERMKEY_SYM_RESTART
: return "restart";
338 case TERMKEY_SYM_RESUME
: return "resume";
339 case TERMKEY_SYM_SAVE
: return "save";
340 case TERMKEY_SYM_SUSPEND
: return "suspend";
341 case TERMKEY_SYM_UNDO
: return "undo";
342 case TERMKEY_SYM_KP0
: return "kp0";
343 case TERMKEY_SYM_KP1
: return "kp1";
344 case TERMKEY_SYM_KP2
: return "kp2";
345 case TERMKEY_SYM_KP3
: return "kp3";
346 case TERMKEY_SYM_KP4
: return "kp4";
347 case TERMKEY_SYM_KP5
: return "kp5";
348 case TERMKEY_SYM_KP6
: return "kp6";
349 case TERMKEY_SYM_KP7
: return "kp7";
350 case TERMKEY_SYM_KP8
: return "kp8";
351 case TERMKEY_SYM_KP9
: return "kp9";
352 case TERMKEY_SYM_KPENTER
: return "kpenter";
353 case TERMKEY_SYM_KPPLUS
: return "kpplus";
354 case TERMKEY_SYM_KPMINUS
: return "kpminus";
355 case TERMKEY_SYM_KPMULT
: return "kpmult";
356 case TERMKEY_SYM_KPDIV
: return "kpdiv";
357 case TERMKEY_SYM_KPCOMMA
: return "kpcomma";
358 case TERMKEY_SYM_KPPERIOD
: return "kpperiod";
359 case TERMKEY_SYM_KPEQUALS
: return "kpequals";
360 default: return "unknown";
364 static int luaify_getkey_result(lua_State
*L
, TermKey
*tk
, TermKeyKey
*k
, TermKeyResult r
) {
365 TermKeyMouseEvent me
;
366 int button
= 0, line
= 0, col
= 0, initial
= 0, mode
= 0, value
= 0;
367 unsigned long cmd
= 0;
368 long args
[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
373 case TERMKEY_RES_NONE
:
377 case TERMKEY_RES_EOF
:
378 lua_pushliteral(L
, "eof");
381 case TERMKEY_RES_AGAIN
:
382 lua_pushliteral(L
, "again");
385 case TERMKEY_RES_ERROR
:
386 lua_pushliteral(L
, "error");
389 case TERMKEY_RES_KEY
:
393 lua_createtable(L
, 0, 5);
395 lua_pushboolean(L
, !!(k
->modifiers
& TERMKEY_KEYMOD_SHIFT
));
396 lua_setfield(L
, -2, "shift");
397 lua_pushboolean(L
, !!(k
->modifiers
& TERMKEY_KEYMOD_ALT
));
398 lua_setfield(L
, -2, "alt");
399 lua_pushboolean(L
, !!(k
->modifiers
& TERMKEY_KEYMOD_CTRL
));
400 lua_setfield(L
, -2, "ctrl");
403 lua_pushstring(L
, k
->utf8
);
404 lua_setfield(L
, -2, "utf8");
408 case TERMKEY_TYPE_UNICODE
:
409 lua_pushinteger(L
, k
->code
.codepoint
);
410 lua_setfield(L
, -2, "codepoint");
412 case TERMKEY_TYPE_FUNCTION
:
413 lua_pushinteger(L
, k
->code
.number
);
414 lua_setfield(L
, -2, "function_number");
416 case TERMKEY_TYPE_KEYSYM
:
417 lua_pushstring(L
, string_from_sym(k
->code
.sym
));
418 lua_setfield(L
, -2, "sym");
420 case TERMKEY_TYPE_MOUSE
:
421 termkey_interpret_mouse(tk
, k
, &me
, &button
, &line
, &col
);
422 lua_createtable(L
, 0, 4);
424 lua_pushinteger(L
, button
);
425 lua_setfield(L
, -2, "button");
426 lua_pushinteger(L
, line
);
427 lua_setfield(L
, -2, "line");
428 lua_pushinteger(L
, col
);
429 lua_setfield(L
, -2, "col");
432 case TERMKEY_MOUSE_PRESS
:
433 lua_pushliteral(L
, "press");
435 case TERMKEY_MOUSE_DRAG
:
436 lua_pushliteral(L
, "drag");
438 case TERMKEY_MOUSE_RELEASE
:
439 lua_pushliteral(L
, "release");
442 lua_pushliteral(L
, "unknown");
445 lua_setfield(L
, -2, "event");
447 lua_setfield(L
, -2, "mouse");
449 case TERMKEY_TYPE_POSITION
:
450 termkey_interpret_position(tk
, k
, &line
, &col
);
451 lua_createtable(L
, 0, 2);
453 lua_pushinteger(L
, line
);
454 lua_setfield(L
, -2, "line");
455 lua_pushinteger(L
, col
);
456 lua_setfield(L
, -2, "col");
458 lua_setfield(L
, -2, "position");
460 case TERMKEY_TYPE_MODEREPORT
:
461 termkey_interpret_modereport(tk
, k
, &initial
, &mode
, &value
);
462 lua_createtable(L
, 0, 3);
464 lua_pushinteger(L
, initial
);
465 lua_setfield(L
, -2, "initial");
466 lua_pushinteger(L
, mode
);
467 lua_setfield(L
, -2, "mode");
468 lua_pushinteger(L
, value
);
469 lua_setfield(L
, -2, "value");
471 lua_setfield(L
, -2, "modereport");
473 case TERMKEY_TYPE_DCS
: /* fall through */
474 case TERMKEY_TYPE_OSC
:
475 termkey_interpret_string(tk
, k
, &str
);
476 lua_pushstring(L
, str
);
477 lua_setfield(L
, -2, "command_string");
479 case TERMKEY_TYPE_UNKNOWN_CSI
:
480 termkey_interpret_csi(tk
, k
, args
, &nargs
, &cmd
);
481 lua_createtable(L
, 0, 2);
482 lua_createtable(L
, 16, 0);
484 for (size_t j
= 0; j
< nargs
; ++j
) {
485 lua_pushinteger(L
, args
[j
]);
486 lua_seti(L
, -2, j
+ 1);
489 lua_setfield(L
, -2, "args");
491 lua_pushinteger(L
, cmd
);
492 lua_setfield(L
, -2, "cmd");
493 lua_setfield(L
, -2, "csi");
501 int wrap_termkey_getkey(lua_State
*L
) {
502 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
503 TermKeyKey k
= { 0 };
504 TermKeyResult r
= termkey_getkey(*tk
, &k
);
506 return luaify_getkey_result(L
, *tk
, &k
, r
);
509 int wrap_termkey_getkey_force(lua_State
*L
) {
510 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
511 TermKeyKey k
= { 0 };
512 TermKeyResult r
= termkey_getkey_force(*tk
, &k
);
514 return luaify_getkey_result(L
, *tk
, &k
, r
);
517 int wrap_termkey_waitkey(lua_State
*L
) {
518 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
519 TermKeyKey k
= { 0 };
520 TermKeyResult r
= termkey_waitkey(*tk
, &k
);
522 return luaify_getkey_result(L
, *tk
, &k
, r
);
525 int wrap_termkey_gc(lua_State
*L
) {
526 TermKey
**tk
= (TermKey
**) luaL_checkudata(L
, 1, WRAP_TERMKEY_NAME
);
529 termkey_destroy(*tk
);
535 static const struct luaL_Reg tk_mapping
[] = {
536 { "start", wrap_termkey_start
},
537 { "stop", wrap_termkey_stop
},
538 { "is_started", wrap_termkey_is_started
},
539 { "get_fd", wrap_termkey_get_fd
},
540 { "get_flags", wrap_termkey_get_flags
},
541 { "set_flags", wrap_termkey_set_flags
},
542 { "get_waittime", wrap_termkey_get_waittime
},
543 { "set_waittime", wrap_termkey_set_waittime
},
544 { "get_canonflags", wrap_termkey_get_canonflags
},
545 { "set_canonflags", wrap_termkey_set_canonflags
},
546 { "get_buffer_size", wrap_termkey_get_buffer_size
},
547 { "set_buffer_size", wrap_termkey_set_buffer_size
},
548 { "get_buffer_remaining", wrap_termkey_get_buffer_remaining
},
549 { "getkey", wrap_termkey_getkey
},
550 { "getkey_force", wrap_termkey_getkey_force
},
551 { "waitkey", wrap_termkey_waitkey
},
552 { "__gc", wrap_termkey_gc
},
557 static int wrap_new(lua_State
*L
) {
561 TermKey
**tk
= (TermKey
**) lua_newuserdata(L
, sizeof *tk
);
562 luaL_getmetatable(L
, WRAP_TERMKEY_NAME
);
563 lua_pushvalue(L
, -1);
564 lua_setuservalue(L
, -3);
565 lua_setmetatable(L
, -2);
568 luaL_setfuncs(L
, tk_mapping
, 0);
571 if (lua_istable(L
, 1)) {
572 table_to_fd_tk_flags(L
, 1, &fd
, &flags
);
575 *tk
= termkey_new(fd
, flags
);
585 static const struct luaL_Reg mapping
[] = {
590 /* Lua metatable initialization */
591 int luaopen_termkey (lua_State
*L
) {
593 luaL_setfuncs(L
, mapping
, 0);
594 lua_pushvalue(L
, -1);
595 lua_pushliteral(L
, "Copyright (C) 2007-2011 Paul Evans <leonerd@leonerd.org.uk>");
596 lua_setfield(L
, -2, "_COPYRIGHT");
597 lua_pushliteral(L
, "This library allows easy processing of keyboard entry from terminal-based programs.");
598 lua_setfield(L
, -2, "_DESCRIPTION");
599 lua_pushliteral(L
, "lua-termkey " STR(TERMKEY_VERSION_MAJOR
) "." STR(TERMKEY_VERSION_MINOR
));
600 lua_setfield(L
, -2, "_VERSION");
603 Setting the metatable to be its own index allows mixing
604 metatable methods (__gc) and member functions (start) in the
607 luaL_newmetatable(L
, WRAP_TERMKEY_NAME
);
608 lua_pushvalue(L
, -1);
609 lua_setfield(L
, -2, "__index");
610 luaL_setfuncs(L
, tk_mapping
, 0);