Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ fi

HAVE_VSNPRINTF=`check_func vsnprintf`

# Check for uinput support (Linux only)
if [ "$sys" = "Linux" ]; then
HAVE_UINPUT_H=`check_header linux/uinput.h`
if [ -n "$HAVE_UINPUT_H" ]; then
echo "Found uinput support (keyboard emulation for Wayland)"
fi
fi

# create Makefile
echo 'creating Makefile ...'
echo "PREFIX = $PREFIX" >Makefile
Expand Down Expand Up @@ -282,6 +290,7 @@ echo >>$cfgheader
[ -n "$HAVE_INTTYPES_H" ] && echo $HAVE_INTTYPES_H >>$cfgheader
[ -n "$HAVE_XINPUT2_H" ] && echo $HAVE_XINPUT2_H >>$cfgheader
[ -n "$HAVE_XTEST_H" ] && echo $HAVE_XTEST_H >>$cfgheader
[ -n "$HAVE_UINPUT_H" ] && echo $HAVE_UINPUT_H >>$cfgheader
[ -n "$HAVE_VSNPRINTF" ] && echo $HAVE_VSNPRINTF >>$cfgheader
echo >>$cfgheader

Expand Down
18 changes: 17 additions & 1 deletion src/cfgfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ enum {
CFG_SENS_ROT, CFG_SENS_RX, CFG_SENS_RY, CFG_SENS_RZ,
CFG_INVROT, CFG_INVTRANS, CFG_SWAPYZ,
CFG_AXISMAP_N, CFG_BNMAP_N, CFG_BNACT_N, CFG_KBMAP_N,
CFG_LED, CFG_GRAB,
CFG_LED, CFG_GRAB, CFG_KBMAP_USE_X11,
CFG_SERIAL, CFG_DEVID,

NUM_CFG_OPTIONS
Expand Down Expand Up @@ -96,6 +96,7 @@ void default_cfg(struct cfg *cfg)

cfg->led = LED_ON;
cfg->grab_device = 1;
cfg->kbemu_use_x11 = 0; /* default to uinput when available */

for(i=0; i<6; i++) {
cfg->map_axis[i] = i;
Expand Down Expand Up @@ -426,6 +427,21 @@ int read_cfg(const char *fname, struct cfg *cfg)
}
}

} else if(strcmp(key_str, "kbmap_use_x11") == 0) {
lptr->opt = CFG_KBMAP_USE_X11;
if(isint) {
cfg->kbemu_use_x11 = ival;
} else {
if(strcmp(val_str, "true") == 0 || strcmp(val_str, "on") == 0 || strcmp(val_str, "yes") == 0) {
cfg->kbemu_use_x11 = 1;
} else if(strcmp(val_str, "false") == 0 || strcmp(val_str, "off") == 0 || strcmp(val_str, "no") == 0) {
cfg->kbemu_use_x11 = 0;
} else {
logmsg(LOG_WARNING, "invalid configuration value for %s, expected a boolean value.\n", key_str);
continue;
}
}

} else if(strcmp(key_str, "grab") == 0) {
lptr->opt = CFG_GRAB;
if(isint) {
Expand Down
1 change: 1 addition & 0 deletions src/cfgfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct cfg {
char *kbmap_str[MAX_BUTTONS];
int swapyz;
int led, grab_device;
int kbemu_use_x11;
char serial_dev[PATH_MAX];
int repeat_msec;

Expand Down
197 changes: 132 additions & 65 deletions src/kbemu.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,108 +15,175 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"

#ifdef USE_X11
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "logger.h"
#include "kbemu.h"
#include "cfgfile.h"

enum {
KBEMU_NONE,
KBEMU_X11,
KBEMU_UINPUT
};

#ifdef HAVE_XTEST_H
#include <X11/extensions/XTest.h>
static int use_xtest;
static int backend = KBEMU_NONE;

extern struct cfg cfg;

/* Initialize keyboard emulation based on config, independent of X11 */
void kbemu_init(void)
{
#if defined(__linux__) && defined(HAVE_UINPUT_H)
/* For uinput builds, default to uinput unless kbmap_use_x11 is set */
if(!cfg.kbemu_use_x11) {
if(backend == KBEMU_NONE) {
logmsg(LOG_INFO, "Initializing uinput keyboard emulation backend\n");
if(kbemu_uinput_init() == 0) {
backend = KBEMU_UINPUT;
logmsg(LOG_INFO, "Using uinput keyboard emulation backend\n");
} else {
logmsg(LOG_WARNING, "Failed to initialize uinput backend\n");
}
}
}
#endif
}

static Display *dpy;
#ifdef USE_X11
#include <X11/Xlib.h>

void kbemu_set_display(Display *d)
/* Called by proto_x11.c when X11 connection is established */
void kbemu_set_display(Display *dpy)
{
dpy = d;
if(!dpy) {
if(backend == KBEMU_X11) {
kbemu_x11_cleanup();
backend = KBEMU_NONE;
}
return;
}

/* If we already have a backend (from kbemu_init), don't override */
if(backend != KBEMU_NONE) {
logmsg(LOG_DEBUG, "kbemu backend already initialized\n");
return;
}

if(d) {
#ifdef HAVE_XTEST_H
int tmp;
use_xtest = XTestQueryExtension(dpy, &tmp, &tmp, &tmp, &tmp);
/* Use X11 backend as fallback if uinput wasn't initialized
* (either because HAVE_UINPUT_H not defined, or kbmap_use_x11 was set,
* or uinput initialization failed)
*/
if(kbemu_x11_init(dpy) == 0) {
backend = KBEMU_X11;
logmsg(LOG_INFO, "Using X11 keyboard emulation backend\n");
return;
}

if(use_xtest)
logmsg(LOG_DEBUG, "Using XTEST to send key events\n");
else
#endif
logmsg(LOG_DEBUG, "Using XSendEvent to send key events\n");
logmsg(LOG_WARNING, "Failed to initialize X11 keyboard emulation\n");
}

#else /* !USE_X11 */

/* When X11 is not available, try uinput */
static void kbemu_init_fallback(void)
{
if(backend != KBEMU_NONE) {
return; /* already initialized */
}

#if defined(__linux__) && defined(HAVE_UINPUT_H)
logmsg(LOG_INFO, "X11 not available, trying uinput keyboard emulation\n");
if(kbemu_uinput_init() == 0) {
backend = KBEMU_UINPUT;
logmsg(LOG_INFO, "Using uinput keyboard emulation backend\n");
return;
}
logmsg(LOG_WARNING, "Failed to initialize uinput keyboard emulation\n");
#else
logmsg(LOG_WARNING, "No keyboard emulation backend available\n");
#endif
}

#endif /* USE_X11 */

KeySym kbemu_keysym(const char *str)
{
#ifdef USE_X11
return XStringToKeysym(str);
#else
/* Without X11, we can't parse key names properly.
* This function is mainly used during config parsing. */
logmsg(LOG_WARNING, "kbemu_keysym: X11 not available, cannot parse key name: %s\n", str);
return 0;
#endif
}

const char *kbemu_keyname(KeySym sym)
{
#ifdef USE_X11
return XKeysymToString(sym);
#else
static char buf[32];
snprintf(buf, sizeof buf, "0x%lx", sym);
return buf;
#endif
}

void send_kbevent(KeySym key, int press)
{
XEvent xevent;
Window win;
int rev_state;
KeyCode kc;

if(!dpy) return;

if(!(kc = XKeysymToKeycode(dpy, key))) {
logmsg(LOG_WARNING, "failed to convert keysym %lu to keycode\n", key);
return;
#ifndef USE_X11
/* Auto-initialize if not already done */
if(backend == KBEMU_NONE) {
kbemu_init_fallback();
}
#endif

#ifdef HAVE_XTEST_H
if(use_xtest) {
XTestFakeKeyEvent(dpy, kc, press, 0);
XFlush(dpy);
return;
}
switch(backend) {
#ifdef USE_X11
case KBEMU_X11:
kbemu_x11_send_key(key, press);
break;
#endif

XGetInputFocus(dpy, &win, &rev_state);

xevent.type = press ? KeyPress : KeyRelease;
xevent.xkey.display = dpy;
xevent.xkey.root = DefaultRootWindow(dpy);
xevent.xkey.window = win;
xevent.xkey.subwindow = None;
xevent.xkey.keycode = kc;
xevent.xkey.state = 0;
xevent.xkey.time = CurrentTime;
xevent.xkey.x = xevent.xkey.y = 1;
xevent.xkey.x_root = xevent.xkey.y_root = 1;

XSendEvent(dpy, win, True, press ? KeyPressMask : KeyReleaseMask, &xevent);
XFlush(dpy);
#if defined(__linux__) && defined(HAVE_UINPUT_H)
case KBEMU_UINPUT:
kbemu_uinput_send_key(key, press);
break;
#endif

default:
/* No backend available or not initialized */
break;
}
}

void send_kbevent_combo(KeySym *keys, int count, int press)
{
int i;

if(!dpy || count <= 0) return;

if(press) {
return;
#ifndef USE_X11
/* Auto-initialize if not already done */
if(backend == KBEMU_NONE) {
kbemu_init_fallback();
}
#endif

/* send press events for all keys */
for(i=0; i<count; i++) {
send_kbevent(keys[i], 1);
}
switch(backend) {
#ifdef USE_X11
case KBEMU_X11:
kbemu_x11_send_key_combo(keys, count, press);
break;
#endif

/* send release events in reverse order */
for(i=0; i<count; i++) {
send_kbevent(keys[count - 1 - i], 0);
#if defined(__linux__) && defined(HAVE_UINPUT_H)
case KBEMU_UINPUT:
kbemu_uinput_send_key_combo(keys, count, press);
break;
#endif

default:
/* No backend available or not initialized */
break;
}
}
#else
int spacenavd_kbemu_shut_up_empty_source_warning;
#endif /* USE_X11 */
24 changes: 24 additions & 0 deletions src/kbemu.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,38 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef KBEMU_H_
#define KBEMU_H_

#ifdef USE_X11
#include <X11/Xlib.h>
#include <X11/keysym.h>

void kbemu_set_display(Display *dpy);
#else
/* When X11 is not available, define KeySym for API compatibility */
typedef unsigned long KeySym;
#endif

/* Initialize keyboard emulation based on config */
void kbemu_init(void);

KeySym kbemu_keysym(const char *str);
const char *kbemu_keyname(KeySym sym);

void send_kbevent(KeySym key, int press);
void send_kbevent_combo(KeySym *keys, int count, int press);

/* Backend-specific functions */
#ifdef USE_X11
int kbemu_x11_init(Display *dpy);
void kbemu_x11_cleanup(void);
void kbemu_x11_send_key(KeySym key, int press);
void kbemu_x11_send_key_combo(KeySym *keys, int count, int press);
#endif

#if defined(__linux__) && defined(HAVE_UINPUT_H)
int kbemu_uinput_init(void);
void kbemu_uinput_cleanup(void);
void kbemu_uinput_send_key(KeySym key, int press);
void kbemu_uinput_send_key_combo(KeySym *keys, int count, int press);
#endif

#endif /* KBEMU_H_ */
Loading
Loading