Skip to content

Commit

Permalink
(core/platform/lua) support keymap forward
Browse files Browse the repository at this point in the history
In order to deal with top-down and recursive handling of clients
that are stuck with xkb forever (so x11 / wayland) it would help
to not sideband the keymap in at the bridge level but rather get
it through the engine so it would work network transparently.

This patch adds xkb to all platforms (should it be desired and
available) and adds the wiring to extract the current map or the
spec of one from lua via input_remap_translation, and then pass
it onward through open_nonblock(vid, ...).

This match changes done to durden as well as to Xarcan, arcan-wayland
bits missing still.
  • Loading branch information
letoram committed Jun 25, 2023
1 parent 2943111 commit 1d007af
Show file tree
Hide file tree
Showing 21 changed files with 276 additions and 64 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
* convey tui/tpack state in resize events
* target\_anchorhint added for informing clients about positioning and hierarchy
* video\_displaymode expose eotf / coordinates for primaries and contents light levels
* open\_nonblock can now adopt an existing iostream into a target vid
* input\_remap\_translation overloaded form for serializing backing keymap

## Core
* respect border attribute in text rasteriser
* added frame\_id to external events that pairs with shmif-SIGVID signals
* optional tracy build for profiling (-DENABLE\_TRACY)

## Tui
* nbio asynch type confusion fix (function becomes pcall:userdata)
Expand Down
12 changes: 10 additions & 2 deletions doc/input_remap_translation.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
-- input_remap_translation
-- @short: Reset or modify platform level keyboard translation
-- @inargs: number:devid, number:action, string:arg_1, ...
-- @outargs: bool:ok, string:reason
-- @inargs: number:devid, number:action, bool:extract, string:arg_1, ...
-- @outargs: bool:ok, string:reason or nbiotbl
-- @longdescr: For some low level platforms it makes sense modifying input
-- translation before the inputs are processed and forwarded onwards and forego
-- patchin in the scripting layer. The main case where this has caused problems
Expand All @@ -17,12 +18,19 @@
-- The *action* can be one out of:
-- TRANSLATION_CLEAR, TRANSLATION_SET and TRANSLATION_REMAP.
-- TRANSLATION_CLEAR is to revert as close to as the initial state as possible.
-- TRANSLATION_MAP is to set/override a complete map.
-- TRANSLATION_SET is to set/override a complete map.
-- TRANSLATION_REMAP is to add a specific remapping.
--
-- The set of string arguments following the action will be raw-forwarded to
-- the input platform and is thus platform dependent.
--
-- It is also possible to get a readable representation of the platform input
-- map through the *extract* parameter form. With TRANSLATION_SET this will
-- just give you the map based on the spec as an iostream (see
-- ref:open_nonblock). With TRANSLATION_REMAP this will instead provide the
-- current active map. No local modifications will be made with the *extract*
-- form.
--
-- The constant API_ENGINE_BUILD can be used to obtain which input platform
-- is currently in use, which mutates the interpretation and effect of the
-- various actions.
Expand Down
16 changes: 12 additions & 4 deletions doc/open_nonblock.lua
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
-- open_nonblock
-- @short: Open a file in read or write mode for non-blocking I/O.
-- @inargs: string:res
-- @inargs: string:res, bool:write
-- @inargs: vid:res
-- @inargs: vid:res, bool:write
-- @inargs: vid:res, bool:write, string:identifier=stream
-- @inargs: vid:res, bool:write, string:identifier=stream, blocktbl
-- @inargs: string:res
-- @inargs: string:res, bool:write
-- @outargs: blocktbl
-- @longdescr: Create or open the resource indicated by *res* in (default)
-- read-mode or (if *mode* is provided, write mode)
-- If *res* is a vid connected to a frameserver, a streaming fifo session will
-- be set up over the connection along with the corresponding _BCHUNK events.
--
-- If *res* is a vid connected to a frameserver, either a streaming fifo
-- session will be set up over the connection along with the corresponding
-- _BCHUNK events or an pre-existing nonblock-io stream will be redirected to
-- said client and the backing descriptor closed locally.
--
-- The *identifier* argument can then be used to specify some client announced
-- type identifier, or one of the reserved "stdin", "stdout", "stderr".
--
-- If *res* is a string, the initial character determines if it creates a
-- FIFO (<) or a SOCKET (=). Unless a namespace is explicitly set and the
-- namespace is marked as valid for IPC, FIFOs and SOCKETs will be created
-- in the RESOURCE_APPL_TEMP namespace.
--
-- If the string starts with a valid namespace identifier and separator
-- (alphanum:/) the identifier will first be matched to a user defined
-- namespace (see ref:list_namespaces).
--
-- If successful, FIFOs and normal resources return a table wih a close
-- operation (which is activated on garbage collection unless called in
-- beforehand) and a read or write function depending on the mode that
Expand Down
12 changes: 12 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,18 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(FATAL_ERROR "${CL_RED}Unsupported OS(${CMAKE_SYSTEM_NAME}) detected, abandon ship!${CL_RST}")
endif()

# XKB might be necessary to deal with nested clients of all platform types
# and near necessary for wayland and x11 clients.
pkg_check_modules(XKB QUIET xkbcommon)
if (XKB_FOUND)
amsg("${CL_YEL}xkb keyboard: \t${CL_GRN}libxkbcommon${CL_RST}")
list(APPEND ARCAN_DEFINITIONS HAVE_XKBCOMMON)
list(APPEND ARCAN_LIBRARIES ${XKB_LINK_LIBRARIES})
list(APPEND INCLUDE_DIRS ${XKB_INCLUDE_DIRS})
else()
amsg("${CL_YEL}xkb keyboard: \t${CL_RED}no libxkbcommon${CL_RST}")
endif()

if (CLIENT_LIBRARY_BUILD OR BUILD_PRESET STREQUAL "client")
set(AGP_PLATFORM "stub")
else()
Expand Down
36 changes: 26 additions & 10 deletions src/engine/alt/nbio.c
Original file line number Diff line number Diff line change
Expand Up @@ -865,26 +865,42 @@ static int opennonblock_tgt(lua_State* L, bool wr)
if (vobj->feed.state.tag != ARCAN_TAG_FRAMESERV)
arcan_fatal("open_nonblock(tgt), target must be a valid frameserver.");

/* overloaded form:
* open_nonblock(vid, r | w, type, nbio_ud)
*
* This takes an existing userdata, extracts the descriptor and sends to the
* target, while disassociating the descriptor from the argument source.
*/
const char* type = luaL_optstring(L, 3, "stream");
struct arcan_event ev = {
.category = EVENT_TARGET,
.tgt.kind = wr ? TARGET_COMMAND_BCHUNK_IN : TARGET_COMMAND_BCHUNK_OUT
};
snprintf(ev.tgt.message, COUNT_OF(ev.tgt.message), "%s", type);
if (lua_type(L, 4) == LUA_TUSERDATA){
struct nonblock_io** ibb = luaL_checkudata(L, 4, "nonblockIO");
struct nonblock_io* ib = *ibb;

if (ib->fd > 0){
platform_fsrv_pushfd(fsrv, &ev, ib->fd);
close(ib->fd);
ib->fd = -1;
}

return 0;
}

/* WRITE mode = 'INPUT' in the client space */
int outp[2];
if (-1 == pipe(outp)){
arcan_warning("open_nonblock(tgt), pipe-pair creation failed: %d\n", errno);
return 0;
}

const char* type = luaL_optstring(L, 3, "stream");

/* WRITE mode = 'INPUT' in the client space */
int dst = wr ? outp[0] : outp[1];
int src = wr ? outp[1] : outp[0];

/* in any scenario where this would fail, "blocking" behavior is acceptable */
alt_nbio_nonblock_cloexec(src, true);
struct arcan_event ev = {
.category = EVENT_TARGET,
.tgt.kind = wr ? TARGET_COMMAND_BCHUNK_IN : TARGET_COMMAND_BCHUNK_OUT
};
snprintf(ev.tgt.message, COUNT_OF(ev.tgt.message), "%s", type);

if (ARCAN_OK != platform_fsrv_pushfd(fsrv, &ev, dst)){
close(dst);
close(src);
Expand Down
37 changes: 30 additions & 7 deletions src/engine/arcan_lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -6414,6 +6414,14 @@ static int inputremaptranslation(lua_State* ctx)
LUA_TRACE("input_remap_translation")
int devid = luaL_checknumber(ctx, 1);
int act = luaL_checknumber(ctx, 2);
bool getmap = false;
int ofs = 2;

/* set if we want an iostream to the current (remap) or a desired one */
if (lua_type(ctx, 3) == LUA_TBOOLEAN || lua_type(ctx, 3) == LUA_TNUMBER){
getmap = luaL_checkbnumber(ctx, 3);
ofs++;
}

if (
act != EVENT_TRANSLATION_CLEAR &&
Expand All @@ -6423,14 +6431,30 @@ static int inputremaptranslation(lua_State* ctx)
}

int ttop = lua_gettop(ctx);
const char* arr[ttop-2];
for (size_t i = 0; i < ttop - 2; i++){
arr[i] = luaL_checkstring(ctx, i+3);
const char* arr[ttop];

if (ttop - ofs > 0){
for (size_t i = 0; i < ttop - ofs; i++){
arr[i] = luaL_checkstring(ctx, i+ofs+1);
}
arr[ttop-ofs] = NULL;
}

arr[ttop-2] = NULL;
const char* err = "";
bool res = platform_event_translation(devid, act, arr, &err);

if (getmap){
int mode = EVENT_TRANSLATION_SERIALIZE_CURRENT;
if (act == EVENT_TRANSLATION_SET)
mode = EVENT_TRANSLATION_SERIALIZE_SPEC;

int fd = platform_event_translation(devid, mode, arr, &err);
struct nonblock_io* dst;
alt_nbio_import(ctx, fd, mode, &dst, NULL);
lua_pushstring(ctx, err);
LUA_ETRACE("input_remap_translation", NULL, 2);
}

int res = platform_event_translation(devid, act, arr, &err);
lua_pushboolean(ctx, res);
lua_pushstring(ctx, err);

Expand Down Expand Up @@ -7275,8 +7299,7 @@ static int arcantargethint(lua_State* ctx)
if (lua_type(ctx, tblind) != LUA_TTABLE)
luaL_typerror(ctx, tblind, "expected argument table");

/* LABELHINT (labelhint: label, initial, descr, vsym, subv) */
if (strcmp(msg, "input_label") == 0){
else if (strcmp(msg, "input_label") == 0){
struct arcan_event ev = {
.category = EVENT_EXTERNAL,
.ext = EVENT_EXTERNAL_LABELHINT,
Expand Down
108 changes: 103 additions & 5 deletions src/platform/arcan/video.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <math.h>
#include <stdatomic.h>
#include <lua.h>
#include <errno.h>

extern jmp_buf arcanmain_recover_state;

Expand Down Expand Up @@ -75,7 +76,6 @@ extern jmp_buf arcanmain_recover_state;
#include "../egl-dri/egl_gbm_helper.h"

static struct egl_env agp_eglenv;

#endif

#ifdef _DEBUG
Expand All @@ -84,6 +84,12 @@ static struct egl_env agp_eglenv;
#define DEBUG 0
#endif

#ifdef HAVE_XKBCOMMON
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon-compose.h>
#endif

#define debug_print(fmt, ...) \
do { if (DEBUG) arcan_warning("%lld:%s:%d:%s(): " fmt "\n",\
arcan_timemillis(), "platform-arcan:", __LINE__, __func__,##__VA_ARGS__); } while (0)
Expand Down Expand Up @@ -132,6 +138,7 @@ struct display {
struct agp_vstore* vstore;
float ppcm;
int id;
map_region keymap;

/* only used for first display */
uint8_t subseg_alloc;
Expand Down Expand Up @@ -420,11 +427,92 @@ int platform_video_cardhandle(int cardn, int* method, size_t* msz, uint8_t** dbu
return -1;
}

bool platform_event_translation(int devid,
static char* names_to_keymap_str(const char** arg, const char** err)
{
#ifdef HAVE_XKBCOMMON
/* setup / fill out pref. */
struct xkb_context* xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
struct xkb_rule_names names = {0};
names.layout = arg[0];
if (names.layout)
names.model = arg[1];
if (names.model)
names.variant = arg[2];
if (names.variant)
names.options = arg[3];

/* compile / convert */
struct xkb_keymap* kmap =
xkb_keymap_new_from_names(xkb_context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!kmap){
*err = "couldn't compile map";
xkb_context_unref(xkb_context);
return NULL;
}
char* map = xkb_map_get_as_string(kmap);
if (!map){
*err = "export failed";
xkb_keymap_unref(kmap);
xkb_context_unref(xkb_context);
return NULL;
}

/* export / cleanup */
char* res = strdup(map);
if (!res){
*err = "map copy failed";
}
else
*err = "";

xkb_keymap_unref(kmap);
xkb_context_unref(xkb_context);
return res;
#else
*err = "no xkb support";
return NULL;
#endif
}

int platform_event_translation(int devid,
int action, const char** names, const char** err)
{
/*
* The serialize and remap can have meaning here as we might have native
* wayland / x11 clients that need the map client side and we are running
* inside another arcan that has it.
*
* If we get a BCHUNK event with 'xkb' type, the shared / cached 'current'
* is forwarded, and if not we build and set according to the shared helper
*/
if ((devid == -1 || devid == 0) && disp[0].keymap.ptr){
switch (action){
case EVENT_TRANSLATION_SET:{
char* newmap = names_to_keymap_str(names, err);
if (!newmap){
return -1;
}
arcan_release_map(disp[0].keymap);
disp[0].keymap.ptr = newmap;
disp[0].keymap.sz = strlen(newmap);
return 0;
}
break;
case EVENT_TRANSLATION_SERIALIZE_SPEC:{
char* newmap = names_to_keymap_str(names, err);
int fd = arcan_strbuf_tempfile(newmap, strlen(newmap), err);
free(newmap);
return fd;
}
case EVENT_TRANSLATION_SERIALIZE_CURRENT:{
return arcan_strbuf_tempfile(disp[0].keymap.ptr, disp[0].keymap.sz, err);
}
break;
}
}

*err = "Not Supported";
return false;
return -1;
}

int platform_event_device_request(int space, const char* path)
Expand Down Expand Up @@ -1193,7 +1281,7 @@ static bool scan_subseg(arcan_tgtevent* ev, bool ok)
* return true if the segment has expired
*/
extern struct arcan_luactx* main_lua_context;
static bool event_process_disp(arcan_evctx* ctx, struct display* d, size_t i)
static bool event_process_disp(arcan_evctx* ctx, struct display* d, size_t did)
{
if (!d->conn.addr)
return true;
Expand Down Expand Up @@ -1367,8 +1455,18 @@ static bool event_process_disp(arcan_evctx* ctx, struct display* d, size_t i)
return true; /* it's not safe here */
break;

case TARGET_COMMAND_BCHUNK_IN:
if (strcmp(ev.tgt.message, "xkb") == 0){
data_source ds = {.fd = ev.tgt.ioevs[0].iv};
arcan_release_map(d->keymap);
/* setting to write means that the fd won't be cached and the whole resource
* will be read in one go and copied, so no need to dup and keep a fd */
d->keymap = arcan_map_resource(&ds, true);
}

/* fallthrough to default is intended */
default:
if (i == 0){
if (did == 0){
arcan_lwa_subseg_ev(main_lua_context, ARCAN_VIDEO_WORLDID, 0, &ev);
}
break;
Expand Down
1 change: 1 addition & 0 deletions src/platform/cmake/CMakeLists.BSD
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ list(APPEND ARCAN_PLATFORM
${PLATFORM_PATH}/launch.c
${PLATFORM_PATH}/config.c
${PLATFORM_PATH}/random.c
${PLATFORM_PATH}/tempfile.c
${PLATFORM_PATH}/prodthrd.c
)
set(LWA_PLATFORM ${ARCAN_PLATFORM})
Expand Down
1 change: 1 addition & 0 deletions src/platform/cmake/CMakeLists.Darwin
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ set(ARCAN_PLATFORM
${PLATFORM_PATH}/launch.c
${PLATFORM_PATH}/config.c
${PLATFORM_PATH}/random.c
${PLATFORM_PATH}/tempfile.c
${PLATFORM_PATH}/fsrv_guard.c
)

Expand Down
1 change: 1 addition & 0 deletions src/platform/cmake/CMakeLists.Linux
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ set(ARCAN_PLATFORM
${PLATFORM_PATH}/launch.c
${PLATFORM_PATH}/config.c
${PLATFORM_PATH}/random.c
${PLATFORM_PATH}/tempfile.c
${PLATFORM_PATH}/../stub/setproctitle.c
${PLATFORM_PATH}/prodthrd.c
)
Expand Down

0 comments on commit 1d007af

Please sign in to comment.