blob: c294db680571aff1fac5c432e4bdcdad6c30f5b3 [file] [log] [blame] [edit]
/*
* libbrlapi - A library providing access to braille terminals for applications.
*
* Copyright (C) 2006-2023 by Dave Mielke <dave@mielke.cc>
*
* libbrlapi comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#define BRLAPI_NO_SINGLE_SESSION 1
#include "prologue.h"
#include <brlapi.h>
#include <brl_cmds.h>
#include <errno.h>
#include <lua.h>
#include <lauxlib.h>
static const char *handle_t = "brlapi";
#define checkhandle(L, arg) (brlapi_handle_t *)luaL_checkudata(L, (arg), handle_t)
static void error(lua_State *L) {
lua_pushstring(L, brlapi_strerror(&brlapi_error));
lua_error(L);
}
static int openConnection(lua_State *L) {
const brlapi_connectionSettings_t desiredSettings = {
.host = luaL_optstring(L, 1, NULL), .auth = luaL_optstring(L, 2, NULL)
};
brlapi_connectionSettings_t actualSettings;
brlapi_handle_t *const handle = (brlapi_handle_t *)lua_newuserdatauv(
L, brlapi_getHandleSize(), 0
);
luaL_setmetatable(L, handle_t);
if (brlapi__openConnection(handle, &desiredSettings, &actualSettings) == -1)
error(L);
lua_pushstring(L, actualSettings.host);
lua_pushstring(L, actualSettings.auth);
return 3;
}
static int getFileDescriptor(lua_State *L) {
const int fileDescriptor = brlapi__getFileDescriptor(checkhandle(L, 1));
if (fileDescriptor == BRLAPI_INVALID_FILE_DESCRIPTOR) error(L);
lua_pushinteger(L, fileDescriptor);
return 1;
}
static int closeConnection(lua_State *L) {
brlapi__closeConnection(checkhandle(L, 1));
return 0;
}
static int getDriverName(lua_State *L) {
char name[BRLAPI_MAXNAMELENGTH + 1];
if (brlapi__getDriverName(checkhandle(L, 1), name, sizeof(name)) == -1)
error(L);
lua_pushstring(L, name);
return 1;
}
static int getModelIdentifier(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
char ident[BRLAPI_MAXNAMELENGTH + 1];
if (brlapi__getModelIdentifier(handle, ident, sizeof(ident)) == -1) error(L);
lua_pushstring(L, ident);
return 1;
}
static int getDisplaySize(lua_State *L) {
unsigned int x, y;
if (brlapi__getDisplaySize(checkhandle(L, 1), &x, &y) == -1) error(L);
lua_pushinteger(L, x), lua_pushinteger(L, y);
return 2;
}
static int enterTtyMode(lua_State *L) {
const int result = brlapi__enterTtyMode(checkhandle(L, 1),
luaL_optinteger(L, 2, BRLAPI_TTY_DEFAULT), luaL_optstring(L, 3, NULL)
);
if (result == -1) error(L);
lua_pushinteger(L, result);
return 1;
}
static int leaveTtyMode(lua_State *L) {
if (brlapi__leaveTtyMode(checkhandle(L, 1)) == -1) error(L);
return 0;
}
static int setFocus(lua_State *L) {
if (brlapi__setFocus(checkhandle(L, 1), luaL_checkinteger(L, 2)) == -1)
error(L);
return 0;
}
static int writeText(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
const int cursor = luaL_checkinteger(L, 2);
const char *const text = luaL_checkstring(L, 3);
if (brlapi__writeText(handle, cursor, text) == -1) error(L);
return 0;
}
static int writeDots(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
const unsigned char *const dots = (const unsigned char *)luaL_checkstring(L, 2);
if (brlapi__writeDots(handle, dots) == -1) error(L);
return 0;
}
static int readKey(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
const int wait = lua_toboolean(L, 2);
brlapi_keyCode_t keyCode;
int result;
do {
result = brlapi__readKey(handle, wait, &keyCode);
} while (result == -1 &&
brlapi_errno == BRLAPI_ERROR_LIBCERR && brlapi_libcerrno == EINTR);
switch (result) {
case -1:
error(L);
break; /* never reached */
case 0:
break;
case 1:
lua_pushinteger(L, (lua_Integer)keyCode);
return 1;
}
return 0;
}
static int readKeyWithTimeout(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
const int timeout_ms = luaL_checkinteger(L, 2);
brlapi_keyCode_t keyCode;
int result;
do {
result = brlapi__readKeyWithTimeout(handle, timeout_ms, &keyCode);
} while (result == -1 &&
brlapi_errno == BRLAPI_ERROR_LIBCERR && brlapi_libcerrno == EINTR);
switch (result) {
case -1:
error(L);
break; /* never reached */
case 0:
break;
case 1:
lua_pushinteger(L, (lua_Integer)keyCode);
return 1;
}
return 0;
}
static int expandKeyCode(lua_State *L) {
brlapi_keyCode_t keyCode = (brlapi_keyCode_t)luaL_checkinteger(L, 1);
brlapi_expandedKeyCode_t expansion;
brlapi_expandKeyCode(keyCode, &expansion);
lua_pushinteger(L, expansion.type),
lua_pushinteger(L, expansion.command),
lua_pushinteger(L, expansion.argument),
lua_pushinteger(L, expansion.flags);
return 4;
}
static int describeKeyCode(lua_State *L) {
brlapi_keyCode_t keyCode = (brlapi_keyCode_t)luaL_checkinteger(L, 1);
brlapi_describedKeyCode_t description;
brlapi_describeKeyCode(keyCode, &description);
lua_pushstring(L, description.type),
lua_pushstring(L, description.command),
lua_pushinteger(L, description.argument);
lua_createtable(L, description.flags, 0);
for (size_t i = 0; i < description.flags; i += 1) {
lua_pushstring(L, description.flag[i]);
lua_rawseti(L, -2, i + 1);
}
return 4;
}
static int enterRawMode(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
const char *const driverName = luaL_checkstring(L, 2);
if (brlapi__enterRawMode(handle, driverName) == -1) error(L);
return 0;
}
static int leaveRawMode(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
if (brlapi__leaveRawMode(handle) == -1) error(L);
return 0;
}
static int changeKeys(
lua_State *L,
int BRLAPI_STDCALL (*change) (brlapi_handle_t *, brlapi_rangeType_t, const brlapi_keyCode_t[], unsigned int)
) {
brlapi_handle_t *const handle = checkhandle(L, 1);
static const char *rangeNames[] = {
"all", "code", "command", "key", "type", NULL
};
static const brlapi_rangeType_t rangeTypes[] = {
brlapi_rangeType_all,
brlapi_rangeType_code,
brlapi_rangeType_command,
brlapi_rangeType_key,
brlapi_rangeType_type
};
const brlapi_rangeType_t range = rangeTypes[
luaL_checkoption(L, 2, NULL, rangeNames)
];
if (range == brlapi_rangeType_all) {
if (change(handle, range, NULL, 0) == -1) error(L);
return 0;
}
luaL_checktype(L, 3, LUA_TTABLE);
lua_len(L, 3);
{
const size_t count = lua_tointeger(L, -1);
brlapi_keyCode_t keys[count];
lua_pop(L, 1);
for (size_t i = 0; i < count; i += 1) {
lua_geti(L, 3, i + 1);
keys[i] = (brlapi_keyCode_t)luaL_checkinteger(L, -1);
lua_pop(L, 1);
}
if (change(handle, range, keys, count) == -1) error(L);
}
return 0;
}
static int acceptKeys(lua_State *L) {
return changeKeys(L, brlapi__acceptKeys);
}
static int ignoreKeys(lua_State *L) {
return changeKeys(L, brlapi__ignoreKeys);
}
static int acceptAllKeys(lua_State *L) {
if (brlapi__acceptAllKeys(checkhandle(L, 1)) == -1) error(L);
return 0;
}
static int ignoreAllKeys(lua_State *L) {
if (brlapi__ignoreAllKeys(checkhandle(L, 1)) == -1) error(L);
return 0;
}
static int suspendDriver(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
const char *const driverName = luaL_checkstring(L, 2);
if (brlapi__suspendDriver(handle, driverName) == -1) error(L);
return 0;
}
static int resumeDriver(lua_State *L) {
if (brlapi__resumeDriver(checkhandle(L, 1)) == -1) error(L);
return 0;
}
static int pause_(lua_State *L) {
brlapi_handle_t *const handle = checkhandle(L, 1);
const int timeout_ms = luaL_checkinteger(L, 2);
int result;
do {
result = brlapi__pause(handle, timeout_ms);
} while (timeout_ms == -1 && result == -1 &&
brlapi_errno == BRLAPI_ERROR_LIBCERR && brlapi_libcerrno == EINTR);
if (result == -1) error(L);
return 0;
}
static int getBooleanParameter(lua_State *L, brlapi_param_t param) {
brlapi_param_bool_t value;
const int result = brlapi__getParameter(checkhandle(L, 1),
param, 0, BRLAPI_PARAMF_GLOBAL, &value, sizeof(value)
);
if (result == -1) error(L);
lua_pushboolean(L, value);
return 1;
}
static int setBooleanParameter(lua_State *L, brlapi_param_t param) {
brlapi_handle_t *const handle = checkhandle(L, 1);
const brlapi_param_flags_t flags = BRLAPI_PARAMF_GLOBAL;
const brlapi_param_bool_t value = lua_toboolean(L, 2);
if (brlapi__setParameter(handle, param, 0, flags, &value, sizeof(value)) == -1)
error(L);
return 0;
}
static int getStringParameter(
lua_State *L,
brlapi_param_t param, brlapi_param_subparam_t subparam
) {
size_t count;
char *string = brlapi__getParameterAlloc(checkhandle(L, 1),
param, subparam, BRLAPI_PARAMF_GLOBAL, &count
);
if (string == NULL) error(L);
lua_pushlstring(L, string, count);
free(string);
return 1;
}
static int setStringParameter(
lua_State *L,
brlapi_param_t param, brlapi_param_subparam_t subparam
) {
brlapi_handle_t *const handle = checkhandle(L, 1);
size_t length;
const brlapi_param_flags_t flags = BRLAPI_PARAMF_GLOBAL;
const char *string = luaL_checklstring(L, 2, &length);
if (brlapi__setParameter(handle, param, subparam, flags, string, length) == -1)
error(L);
return 0;
}
static int getDriverCode(lua_State *L) {
return getStringParameter(L, BRLAPI_PARAM_DRIVER_CODE, 0);
}
static int getDriverVersion(lua_State *L) {
return getStringParameter(L, BRLAPI_PARAM_DRIVER_VERSION, 0);
}
static int getDeviceOnline(lua_State *L) {
return getBooleanParameter(L, BRLAPI_PARAM_DEVICE_ONLINE);
}
static int getAudibleAlerts(lua_State *L) {
return getBooleanParameter(L, BRLAPI_PARAM_AUDIBLE_ALERTS);
}
static int setAudibleAlerts(lua_State *L) {
return setBooleanParameter(L, BRLAPI_PARAM_AUDIBLE_ALERTS);
}
static int getClipboardContent(lua_State *L) {
return getStringParameter(L, BRLAPI_PARAM_CLIPBOARD_CONTENT, 0);
}
static int setClipboardContent(lua_State *L) {
return setStringParameter(L, BRLAPI_PARAM_CLIPBOARD_CONTENT, 0);
}
static int getComputerBrailleTable(lua_State *L) {
return getStringParameter(L, BRLAPI_PARAM_COMPUTER_BRAILLE_TABLE, 0);
}
static int setComputerBrailleTable(lua_State *L) {
return setStringParameter(L, BRLAPI_PARAM_COMPUTER_BRAILLE_TABLE, 0);
}
static int getLiteraryBrailleTable(lua_State *L) {
return getStringParameter(L, BRLAPI_PARAM_LITERARY_BRAILLE_TABLE, 0);
}
static int setLiteraryBrailleTable(lua_State *L) {
return setStringParameter(L, BRLAPI_PARAM_LITERARY_BRAILLE_TABLE, 0);
}
static int getMessageLocale(lua_State *L) {
return getStringParameter(L, BRLAPI_PARAM_MESSAGE_LOCALE, 0);
}
static int setMessageLocale(lua_State *L) {
return setStringParameter(L, BRLAPI_PARAM_MESSAGE_LOCALE, 0);
}
static int getBoundCommandKeycodes(lua_State *L) {
size_t count;
brlapi_keyCode_t *codes = brlapi__getParameterAlloc(checkhandle(L, 1),
BRLAPI_PARAM_BOUND_COMMAND_KEYCODES, 0, BRLAPI_PARAMF_GLOBAL, &count
);
if (codes == NULL) error(L);
count /= sizeof(brlapi_keyCode_t);
lua_newtable(L);
for (size_t i = 0; i < count; i += 1) {
lua_pushinteger(L, (lua_Integer)codes[i]);
lua_rawseti(L, -2, i + 1);
}
free(codes);
return 1;
}
static int getCommandKeycodeName(lua_State *L) {
const brlapi_keyCode_t keyCode = (brlapi_keyCode_t)luaL_checkinteger(L, 2);
return getStringParameter(L, BRLAPI_PARAM_COMMAND_KEYCODE_NAME, keyCode);
}
static int getCommandKeycodeSummary(lua_State *L) {
const brlapi_keyCode_t keyCode = (brlapi_keyCode_t)luaL_checkinteger(L, 2);
return getStringParameter(L, BRLAPI_PARAM_COMMAND_KEYCODE_SUMMARY, keyCode);
}
static int getDefinedDriverKeycodes(lua_State *L) {
size_t count;
brlapi_keyCode_t *codes = brlapi__getParameterAlloc(checkhandle(L, 1),
BRLAPI_PARAM_DEFINED_DRIVER_KEYCODES, 0, BRLAPI_PARAMF_GLOBAL, &count
);
if (codes == NULL) error(L);
count /= sizeof(brlapi_keyCode_t);
lua_newtable(L);
for (size_t i = 0; i < count; i += 1) {
lua_pushinteger(L, (lua_Integer)codes[i]);
lua_rawseti(L, -2, i + 1);
}
free(codes);
return 1;
}
static int getDriverKeycodeName(lua_State *L) {
const brlapi_keyCode_t keyCode = (brlapi_keyCode_t)luaL_checkinteger(L, 2);
return getStringParameter(L, BRLAPI_PARAM_DRIVER_KEYCODE_NAME, keyCode);
}
static int getDriverKeycodeSummary(lua_State *L) {
const brlapi_keyCode_t keyCode = (brlapi_keyCode_t)luaL_checkinteger(L, 2);
return getStringParameter(L, BRLAPI_PARAM_DRIVER_KEYCODE_SUMMARY, keyCode);
}
static inline int versionGetField(lua_State *L, const char *name) {
if (lua_getfield(L, 1, name) == LUA_TNUMBER) {
if (lua_getfield(L, 2, name) == LUA_TNUMBER) {
return 1;
}
}
return 0;
}
static int versionCompare(lua_State *L, int op) {
int result = 0;
if (versionGetField(L, "major")) {
if (lua_compare(L, -2, -1, LUA_OPLT)) {
result = 1;
} else if (lua_compare(L, -2, -1, LUA_OPEQ)) {
if (versionGetField(L, "minor")) {
if (lua_compare(L, -2, -1, LUA_OPLT)) {
result = 1;
} else if (lua_compare(L, -2, -1, LUA_OPEQ)) {
if (versionGetField(L, "revision")) {
if (lua_compare(L, -2, -1, op)) {
result = 1;
}
}
}
}
}
}
lua_pushboolean(L, result);
return 1;
}
static int versionLT(lua_State *L) {
return versionCompare(L, LUA_OPLT);
}
static int versionLE(lua_State *L) {
return versionCompare(L, LUA_OPLE);
}
static int versionString(lua_State *L) {
static const char *separator = ".";
lua_getfield(L, 1, "major");
lua_pushstring(L, separator);
lua_getfield(L, 1, "minor");
lua_pushstring(L, separator);
lua_getfield(L, 1, "revision");
lua_concat(L, 5);
return 1;
}
static const luaL_Reg version_meta[] = {
{ "__lt", versionLT },
{ "__le", versionLE },
{ "__tostring", versionString },
{ NULL, NULL }
};
static const luaL_Reg meta[] = {
{ "__close", closeConnection },
{ NULL, NULL }
};
static const luaL_Reg funcs[] = {
{ "openConnection", openConnection },
{ "getFileDescriptor", getFileDescriptor },
{ "closeConnection", closeConnection },
{ "getDriverName", getDriverName },
{ "getModelIdentifier", getModelIdentifier },
{ "getDisplaySize", getDisplaySize },
{ "enterTtyMode", enterTtyMode },
{ "leaveTtyMode", leaveTtyMode },
{ "setFocus", setFocus },
{ "writeText", writeText },
{ "writeDots", writeDots },
{ "readKey", readKey },
{ "readKeyWithTimeout", readKeyWithTimeout },
{ "expandKeyCode", expandKeyCode },
{ "describeKeyCode", describeKeyCode },
{ "enterRawMode", enterRawMode },
{ "leaveRawMode", leaveRawMode },
{ "acceptKeys", acceptKeys },
{ "ignoreKeys", ignoreKeys },
{ "acceptAllKeys", acceptAllKeys },
{ "ignoreAllKeys", ignoreAllKeys },
{ "suspendDriver", suspendDriver },
{ "resumeDriver", resumeDriver },
{ "pause", pause_ },
{ "getDriverCode", getDriverCode },
{ "getDriverVersion", getDriverVersion },
{ "getDeviceOnline", getDeviceOnline },
{ "getAudibleAlerts", getAudibleAlerts },
{ "setAudibleAlerts", setAudibleAlerts },
{ "getClipboardContent", getClipboardContent },
{ "setClipboardContent", setClipboardContent },
{ "getComputerBrailleTable", getComputerBrailleTable },
{ "setComputerBrailleTable", setComputerBrailleTable },
{ "getLiteraryBrailleTable", getLiteraryBrailleTable },
{ "setLiteraryBrailleTable", setLiteraryBrailleTable },
{ "getMessageLocale", getMessageLocale },
{ "setMessageLocale", setMessageLocale },
{ "getBoundCommandKeycodes", getBoundCommandKeycodes },
{ "getCommandKeycodeName", getCommandKeycodeName },
{ "getCommandKeycodeSummary", getCommandKeycodeSummary },
{ "getDefinedDriverKeycodes", getDefinedDriverKeycodes },
{ "getDriverKeycodeName", getDriverKeycodeName },
{ "getDriverKeycodeSummary", getDriverKeycodeSummary },
{ NULL, NULL }
};
static void createcmdtable(lua_State *L) {
lua_newtable(L);
#define CMD(name, value) lua_pushinteger(L, (lua_Integer)value), lua_setfield(L, -2, STRINGIFY(name));
#include "cmd.auto.h"
#undef CMD
}
int luaopen_brlapi(lua_State *L) {
luaL_newmetatable(L, handle_t);
luaL_setfuncs(L, meta, 0);
luaL_newlib(L, funcs);
{
int major, minor, revision;
brlapi_getLibraryVersion(&major, &minor, &revision);
lua_createtable(L, 0, 3);
lua_pushinteger(L, major), lua_setfield(L, -2, "major");
lua_pushinteger(L, minor), lua_setfield(L, -2, "minor");
lua_pushinteger(L, revision), lua_setfield(L, -2, "revision");
luaL_newmetatable(L, "version");
luaL_setfuncs(L, version_meta, 0);
lua_setmetatable(L, -2);
lua_setfield(L, -2, "version");
}
createcmdtable(L);
lua_setfield(L, -2, "CMD");
/* Set function table as metatable __index */
lua_pushvalue(L, -1), lua_setfield(L, -3, "__index");
return 1;
}