[BRLTTY] Key combinations not (yet?) handled by the Linux virtual console
Nicolas Pitre
nico at fluxnic.net
Wed Nov 26 22:01:30 UTC 2025
On Wed, 26 Nov 2025, Aura Kelloniemi wrote:
> Or actually, better would be to take a look at the Kitty keyboard protocol
> here: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
>
> It seems that most modern terminal emulators prefer to implement the kitty
> protocol, because it is more advanced and more backwards-compatible than the
> Xterm's implementation of "CSI u". See the above link for all terminal
> emulators which support this protocol.
>
> Kitty protocol has support for very advanced features, like sending
> applications information not only about key presses but also key releases.
>
> I think it would be worthwhile to try to implement this in a TTY application
> which runs in the Linux console, reads raw keyboard input from the evdev
> devices and runs any terminal program (like tmux) in a pseudo-terminal,
> providing this program with the kitty keyboard protocol facilities.
You don't even need to access the evdev interface. In fact as a normal
user you might not have permission to open it.
But the simpler KDSKBMODE ioctl just works on a Linux VT. See attached
source code. This is like the showkey program but it was easier to
reimplement than digging the showkey sources.
> Would anybody be interested in implementing this? I'm actually very
> interested, if there are others who feel the calling to join. The
> implementation language should be C, if we want to pave way for integrating
> this into Linux console in the future. If Linux integration is a low priority
> (or it is clear that this feature is unwanted by Linux developers), we can
> use some other systems language, like Zig, C++ or Rust.
I'm willing to add it to Linux if someone else does the research for the
actual protocol to implement with justifications (seems you are into
that already). Despite Linux console being a low priority, I had no
issues refreshing Unicode support and adding bracketed paste support
last time around.
Nicolas
-------------- next part --------------
#include <linux/kd.h>
#include <linux/input.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
int fd = -1;
long prev_mode;
struct termios orig_termios;
void cleanup(void) {
if (fd >= 0) {
// Restore keyboard mode
ioctl(fd, KDSKBMODE, prev_mode);
// Restore terminal settings
tcsetattr(fd, TCSANOW, &orig_termios);
close(fd);
fd = -1;
}
printf("\nCleaned up and exiting.\n");
}
void signal_handler(int sig) {
cleanup();
exit(0);
}
int main(void) {
fd = open("/dev/tty", O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
// Save original terminal settings
if (tcgetattr(fd, &orig_termios) < 0) {
perror("tcgetattr");
return 1;
}
// Save keyboard mode
if (ioctl(fd, KDGKBMODE, &prev_mode) < 0) {
perror("KDGKBMODE");
return 1;
}
// Set up signal handlers for cleanup AFTER saving states
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
// Register cleanup on normal exit
atexit(cleanup);
// Set terminal to raw mode with timeout
struct termios raw = orig_termios;
raw.c_lflag &= ~(ICANON | ECHO | ISIG);
raw.c_iflag &= ~(IXON | ICRNL);
raw.c_cc[VMIN] = 0; // Non-blocking read
raw.c_cc[VTIME] = 50; // 5 second timeout (in deciseconds)
if (tcsetattr(fd, TCSANOW, &raw) < 0) {
perror("tcsetattr");
return 1;
}
// Set keyboard to K_MEDIUMRAW mode
if (ioctl(fd, KDSKBMODE, K_MEDIUMRAW) < 0) {
perror("KDSKBMODE");
return 1;
}
printf("Listening for keyboard events. Will exit after 5 seconds of inactivity.\n\n");
unsigned char keycode;
int bytes_read;
while ((bytes_read = read(fd, &keycode, 1)) >= 0) {
if (bytes_read == 0) {
// Timeout - no key pressed within timeout period
printf("\nInactivity timeout reached.\n");
break;
}
unsigned char key = keycode & 0x7F;
int released = keycode & 0x80;
if (released) {
printf("Released: ");
} else {
printf("Pressed: ");
}
switch (key) {
case KEY_ESC: printf("ESC\n"); break;
case KEY_ENTER: printf("ENTER\n"); break;
case KEY_UP: printf("UP\n"); break;
case KEY_DOWN: printf("DOWN\n"); break;
case KEY_LEFT: printf("LEFT\n"); break;
case KEY_RIGHT: printf("RIGHT\n"); break;
case KEY_LEFTSHIFT: printf("LEFT SHIFT\n"); break;
case KEY_RIGHTSHIFT: printf("RIGHT SHIFT\n"); break;
case KEY_LEFTCTRL: printf("LEFT CTRL\n"); break;
case KEY_RIGHTCTRL: printf("RIGHT CTRL\n"); break;
case KEY_LEFTALT: printf("LEFT ALT\n"); break;
case KEY_RIGHTALT: printf("RIGHT ALT\n"); break;
case KEY_LEFTMETA: printf("LEFT META\n"); break;
case KEY_RIGHTMETA: printf("RIGHT META\n"); break;
default: printf("keycode %d\n", key); break;
}
}
return 0;
}
More information about the BRLTTY
mailing list