Skip to content

Commit

Permalink
first public version
Browse files Browse the repository at this point in the history
  • Loading branch information
piater committed Jun 3, 2019
1 parent 16f2f3f commit c6b22f5
Show file tree
Hide file tree
Showing 11 changed files with 555 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.d
*.o
uinputchars
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CC = gcc
CFLAGS = -MMD -MP -Wall

SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
DEP=$(SRC:.c=.d)

uinputchars: $(OBJ)
$(CC) $(LDFLAGS) -o $@ $^

-include $(DEP)

.PHONY: clean
clean:
rm -f $(OBJ) $(DEP) uinputchars
42 changes: 42 additions & 0 deletions NOTES
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
This program reads a character string and translates it into virtual
keystrokes that are entered into the system via /dev/uinput.

The key difficulty (pun intended) is to map characters to keystrokes.
These are the principal bits and pieces involved, along with
highly-simplified descriptions:

- Character Encoding: This defines the representation of a character
by a numeric value (or a sequence of numeric values) in the input
character string. On modern, western systems, a typical character
encoding is UTF-8.

- Kernel Keymap: Each key on the keyboard has an associated Keycode
(or Event Code, defined as KEY_... macros in
linux/input-event-codes.h). The Kernel Keymap maps a Keycode (or a
combination of Keycodes, if modifier or compose keys are pressed) to
an Action Code representing a character (or some other action,
defined as K_... macros in linux/keyboard.h). This mapping is
determined by the active keymap (as loaded by the loadkeys utility).

- The mapping from Action Code to characters is quite complicated and
can be traced in dumpkeys utility. It appears that for ISO-8859-1
characters (0..255), the lower byte of the Action Code equals the
ISO-8859-1 (= UTF-8) character code.

Thus, for ISO-8859-1 characters, this mapping is essentially the
identity mapping. This is all this program implements so far;
therefore, it is currently limited to ISO-8859-1 characters.

Generally, the Action Code of a character appear to be closely
related to their Unicode code point.

To enter a character via /dev/uinput, a corresponding sequence of
Keycodes is written to this device. This can be done via the
following steps:

1. Map the character to its corresponding Action Code.

2. Map the Action Code to a corresponding (sequence of) Keycode(s) via
an inverse keyboard mapping.

3. Write the Keycode(s) to /dev/uinput.
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,37 @@
# uinputchars
A utility to type character strings into /dev/uinput
A Linux utility to type character strings into `/dev/uinput`


## Usage

A typical use case is [entering
passwords](https://github.com/clawoflight/puma). In contrast to
classical password managers, uinputchars can type character strings
into anything - Web forms, e-mail clients, Emacs - without any client
support. In contrast to xdotool, it does not require X; it also works
under Wayland or even on the console.

Generally, uinputchars is run without arguments.

Character strings are read from standard input. There is currently no
option to pass them on the command line because this would make them
appear in the process table.

The current implementation only accepts ISO-8859-1 characters (encoded
as specified by the locale). Depending on the kernel keymap in use,
only a subset of them may be accessible.

The uinputchars utility needs to retrieve the kernel keymap and to
write into `/dev/uinput`; both will generally require root rights. It
is recommended to configure this in `/etc/sudoers`.

Applications receiving events from `/dev/uinput` must be given time to
process them. This is achieved by brief sleeps that can be adjusted
via command-line options.


## Installation

Under Linux, just run (GNU) <kbd>make</kbd>, and move or link the
resulting executable to wherever you want. If this does not work, I
welcome your patches.
25 changes: 25 additions & 0 deletions invkeymap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Copyright 2019 Justus Piater */
/* This file is part of UINPUTCHARS.
UINPUTCHARS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UINPUTCHARS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UINPUTCHARS. If not, see <https://www.gnu.org/licenses/>. */

#ifndef INVKEYMAP_H
#define INVKEYMAP_H

typedef struct {
unsigned char modifiers;
unsigned char principal;
} keyvent;

#endif
162 changes: 162 additions & 0 deletions keymap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/* Copyright 2019 Justus Piater, with one exception documented below */
/* This file is part of UINPUTCHARS.
UINPUTCHARS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UINPUTCHARS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UINPUTCHARS. If not, see <https://www.gnu.org/licenses/>. */

/* Some documentation relevant to this code:
man 4 console_ioctl
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <linux/keyboard.h>
#include <linux/input-event-codes.h>

#include "keymap.h"

#define MAX_KEYVENTS 0x100
#define LINEFEED 0x0a


/* The two functions is_a_console() and getfd() below evolved from
code copied from kbd/src/getfd.c (GPL; Copyright (C) 1994-1999
Andries E. Brouwer) from http://kbd-project.org/.
This reused GPL (v2 or later) code is one reason why all parts of
UINPUTCHARS are released under the GPL. */

static int is_a_console(int fd) {
char arg = 0;
return (isatty(fd) &&
ioctl(fd, KDGKBTYPE, &arg) == 0 &&
((arg == KB_101) || (arg == KB_84)) );
}


static int getfd() {
static char *conspath[] =
{ "/proc/self/fd/0",
"/dev/tty",
"/dev/tty0",
"/dev/vc/0",
"/dev/systty",
"/dev/console",
NULL };

for (int i = 0; conspath[i]; i++) {
/* Permissions don't matter: */
int fd = open(conspath[i], O_RDWR);
if (fd < 0) fd = open(conspath[i], O_WRONLY);
if (fd < 0) fd = open(conspath[i], O_RDONLY);
if (fd >= 0) {
if (is_a_console(fd))
return fd;
close(fd);
}
}

for (int fd = 0; fd < 3; fd++)
if (is_a_console(fd))
return fd;

fprintf(stderr,
"Couldn't get a file descriptor referring to the console\n");
return -1;
}


static void print_keyventry(unsigned int c, unsigned char modifiers,
unsigned char principal) {
printf("[%02x=%c %02x %3u]\n", c, c, modifiers, principal);
}

static void print_keyvent(const keyvent* invkeymap, unsigned char c) {
print_keyventry(c, invkeymap[c].modifiers, invkeymap[c].principal);
}


void print_invkeymap(const keyvent* invkeymap) {
for (int c = 0; c < MAX_KEYVENTS; c++) {
if (invkeymap[c].principal) {
printf("%02x ", c);
print_keyvent(invkeymap, c);
}
}
}


static void get_keys(int fd, keyvent* invkeymap) {
/* This loop was inspired by lk_kernel_keys()
in kbd/src/libkeymap/kernel.c (GPL) from http://kbd-project.org/. */
for (int table = 0; table < MAX_NR_KEYMAPS; table++) {
for (int index = 0; index < NR_KEYS; index++) {
struct kbentry kbe;
kbe.kb_table = table;
kbe.kb_index = index;
kbe.kb_value = 0;

if (ioctl(fd, KDGKBENT, (unsigned long)&kbe))
fprintf(stderr, "KDGKBENT: %s: error at index %d in table %d\n",
strerror(errno), index, table);

if (!index && kbe.kb_value == K_NOSUCHMAP)
break;

if (KTYP(kbe.kb_value) == KT_LATIN ||
KTYP(kbe.kb_value) == KT_LETTER ) {
int charvalue = KVAL(kbe.kb_value);
if (!invkeymap[charvalue].principal) {
/* Prefer lower-valued tables */
invkeymap[charvalue].modifiers = table;
invkeymap[charvalue].principal = index;
}
}
else if (kbe.kb_value == K_ENTER &&
!invkeymap[LINEFEED].principal) {
invkeymap[LINEFEED].modifiers = table;
invkeymap[LINEFEED].principal = index;
}
}
}
}


keyvent* get_invkeymap() {
int fd = getfd();
if (fd < 0)
return NULL;

keyvent* invkeymap = calloc(MAX_KEYVENTS, sizeof(*invkeymap));
if (!invkeymap) {
perror("invkeymap");
return NULL;
}

get_keys(fd, invkeymap);
close(fd);

return invkeymap;
}


void release_invkeymap(keyvent* invkeymap) {
free(invkeymap);
}
21 changes: 21 additions & 0 deletions keymap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* Copyright 2019 Justus Piater */
/* This file is part of UINPUTCHARS.
UINPUTCHARS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UINPUTCHARS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UINPUTCHARS. If not, see <https://www.gnu.org/licenses/>. */

#include "invkeymap.h"

keyvent* get_invkeymap(void);
void release_invkeymap(keyvent* invkeymap);
void print_invkeymap(const keyvent* invkeymap);
Loading

0 comments on commit c6b22f5

Please sign in to comment.