/*
* code protected with a GNU affero GPLv3 license
* copyright (C) 2020 Sylvain BERTRAND
*/
/*
* usage:
* "x11keyautorepeat" alone will turn off x11 global key repeat
* "x11keyautorepeat WHATEVER" will turn on x11 global key repeat
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
/*
* ABBREVIATIONS:
* addr : ADDRess
* auth : AUTHentication
* fd : File Descriptor
* fmt(s) : ForMaT(S)
* img(s) : IMaGe(S)
* max : MAXimum
* min : MINimum
* n : couNt
* num(S) : NUMber(S)
* recv : RECeiVe
* req(s) : REQuest(S)
* scr(s) : SCReen(S)
* so : SOcket
* sz : SiZe (usually a count of bytes)
* w(s) : Word(S) (32 bits)
*/
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define loop for(;;)
#define FATAL(fmt, ...) ({fprintf(stderr, fmt, ##__VA_ARGS__); exit(EXIT_FAILURE);})
#define POUT(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
#define PERR(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
static u8 *so_pathname = "/tmp/.X11-unix/X0";
static int so_fd;
#ifdef __GNUC__
#define PACKED __attribute__((packed))
#else
#error "missing C extension for packed structure declaration"
#endif
struct x11_setup {
u8 endian;
u8 unused0;
u16 major;
u16 minor;
u16 auth_name_sz;
u16 auth_data_sz;
u16 unused1;
} PACKED;
struct x11_setup_status_common {
u8 code;
u8 unused_or_reason_sz;
u16 major;
u16 minor;
u16 additional_data_ws_n;
} PACKED;
struct x11_change_keyboard_control_req {
u8 opcode;
u8 unused;
u16 req_ws_n;
u32 mask;
u8 auto_repeat_mode;
u8 pad[3];
} PACKED;
/* handle short write */
static void x11_write(void *data, u16 sz)
{
u8 *p;
size_t sent_bytes_n;
if (sz == 0)
return;
sent_bytes_n = 0;
p = data;
loop {
ssize_t r;
errno = 0;
r = write(so_fd, p, (size_t)sz - sent_bytes_n);
if (r == -1)
FATAL("error while sending %u bytes to the x11 server:%s\n", (int)((size_t)sz - sent_bytes_n), strerror(errno));
sent_bytes_n += (size_t)r;
if (sent_bytes_n == (size_t)sz)
break;
p += r;
}
}
/* handle short read */
static u16 x11_read(void *buf, u16 max_sz)
{
u8 *p;
size_t recv_bytes_n;
if (max_sz == 0)
return 0;
p = buf;
recv_bytes_n = 0;
loop {
ssize_t r;
errno = 0;
r = read(so_fd, p, (size_t)max_sz - recv_bytes_n);
if (r == -1)
FATAL("error while receiving %u bytes from the x11 server:%s\n", (int)((size_t)max_sz - recv_bytes_n), strerror(errno));
if (r == 0) /* no more data: 0-sized datagram, connection properly closed, end of file... */
break;
recv_bytes_n += (size_t)r;
if (recv_bytes_n == (size_t)max_sz)
break;
p += r;
}
return (u16)recv_bytes_n;
}
int main(int argc, u8 **argv)
{
int ri;
u16 r16;
struct sockaddr_un addr;
struct x11_setup x11_setup;
struct x11_setup_status_common x11_status;
u8 *additional_data;
struct x11_change_keyboard_control_req req;
close(0);
additional_data = 0;
errno = 0;
/* xserver expects a SOCK_STREAM socket */
ri = socket(AF_UNIX, SOCK_STREAM, 0);
if (ri == -1)
FATAL("unable to create a socket:%s\n", strerror(errno));
so_fd = ri;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, so_pathname, sizeof(addr.sun_path));
ri = connect(so_fd, (struct sockaddr*)&addr, sizeof(addr));
if (ri == -1)
FATAL("unable to connect the socket %d to address '%s':%s\n", so_fd, so_pathname, strerror(errno));
POUT("connected to unix socket '%s'\n", so_pathname);
/*--------------------------------------------------------------------*/
/* x11 setup */
memset(&x11_setup, 0, sizeof(x11_setup));
x11_setup.endian = 'l'; /* l-ittle endian or 'B'-ig endian */
x11_setup.major = 11; /* wayland is x12 */
x11_write(&x11_setup, sizeof(x11_setup));
POUT("x11 connection setup sent\n");
POUT("receiving x11 setup status common data...\n");
r16 = x11_read(&x11_status, sizeof(x11_status));
if (r16 != sizeof(x11_status))
FATAL("unable to get x11 setup status common data\n");
if (x11_status.additional_data_ws_n != 0) {
additional_data = realloc(additional_data, x11_status.additional_data_ws_n * 4);
if (additional_data == 0)
FATAL("unable to allocate memory to x11 setup status additional data\n");
POUT("receiving x11 setup status additional data, %u bytes...\n", x11_status.additional_data_ws_n * 4);
r16 = x11_read(additional_data, x11_status.additional_data_ws_n * 4);
if (r16 != (x11_status.additional_data_ws_n * 4))
FATAL("incomplete x11 setup status additional data\n");
}
if (x11_status.code == 0) {
POUT("x11 setup: failure\n");
if (x11_status.additional_data_ws_n != 0)
/* don't expect any string to end with '\0' */
FATAL("reason:%.*s\n", (int)x11_status.unused_or_reason_sz, additional_data);
FATAL("no failure reason provided\n");
}
free(additional_data);
additional_data = 0;
if (x11_status.code == 2)
FATAL("x11 setup: authentication is not supported\n");
if (x11_status.code != 1)
FATAL("x11 setup: unknown status code (0x%02x)\n", x11_status.code);
/* x11_status.code == 1 */
POUT("x11 setup success\n");
/*--------------------------------------------------------------------*/
/* change_keyboard_control req */
memset(&req, 0, sizeof(req));
req.opcode = 102;
req.req_ws_n = sizeof(req) / 4;
req.mask = 0x80; /* auto-repeat-mode */
if (argc > 1)
req.auto_repeat_mode = 1; /* 1 = on, 0 = off, 2 = default */
POUT("sending x11 change_keyboard_control request\n");
x11_write(&req, sizeof(req));
POUT("done\n");
exit(EXIT_SUCCESS);
}