summary refs log tree commit diff stats
path: root/rust/qmk-hid-com/src_c
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-12-30 18:22:41 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-12-30 18:22:41 +0100
commitba9f12810f7dc4969ac175f6e959d5fe6407747d (patch)
treef4039e46985d56bac471bd8568f96b159a92b2ae /rust/qmk-hid-com/src_c
parentfeat(src): Make usable (diff)
downloadqmk_layout-ba9f12810f7dc4969ac175f6e959d5fe6407747d.zip
feat(treewide): Migrate the Unicode handling to a custom c program, that works via rawhid
Diffstat (limited to '')
-rwxr-xr-xrust/qmk-hid-com/src_c/build.sh18
-rw-r--r--rust/qmk-hid-com/src_c/meson.build51
-rw-r--r--rust/qmk-hid-com/src_c/protocol/meson.build37
-rw-r--r--rust/qmk-hid-com/src_c/protocol/virtual-keyboard-unstable-v1.xml113
-rw-r--r--rust/qmk-hid-com/src_c/src/error.c20
-rw-r--r--rust/qmk-hid-com/src_c/src/error.h1
-rw-r--r--rust/qmk-hid-com/src_c/src/hid/hid.c83
-rw-r--r--rust/qmk-hid-com/src_c/src/hid/hid.h5
-rw-r--r--rust/qmk-hid-com/src_c/src/keyboard/keyboard.c135
-rw-r--r--rust/qmk-hid-com/src_c/src/keyboard/keyboard.h17
-rw-r--r--rust/qmk-hid-com/src_c/src/keyboard/virtual-keyboard-unstable-v1-client-protocol.h280
-rw-r--r--rust/qmk-hid-com/src_c/src/main.c28
12 files changed, 788 insertions, 0 deletions
diff --git a/rust/qmk-hid-com/src_c/build.sh b/rust/qmk-hid-com/src_c/build.sh
new file mode 100755
index 0000000..de615f8
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/build.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env sh
+
+# Source: https://mesonbuild.com/Builtin-options.html
+# Use gcc's wrappers to support lto
+CC="gcc" AR="gcc-ar" RANLIB="gcc-ranlib" meson setup build_release \
+    -Dbuildtype=release \
+    -Ddebug=false \
+    -Doptimization=3 \
+    -Dstrip=true \
+    -Dwarning_level=everything \
+    -Dwerror=true \
+    -Db_lundef=true \
+    -Db_lto=true \
+    -Db_ndebug=false \
+    -Db_pgo=generate \
+    -Db_staticpic=true \
+    -Db_pie=false \
+    -Dc_std=gnu2x
diff --git a/rust/qmk-hid-com/src_c/meson.build b/rust/qmk-hid-com/src_c/meson.build
new file mode 100644
index 0000000..9e8357f
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/meson.build
@@ -0,0 +1,51 @@
+# This is heavily based on `wtype`
+
+project(
+    'qmk-unicode-type', 'c',
+    version: '0.1',
+    license: 'GPL-3.0-or-later',
+    default_options : [
+        'buildtype=release',
+        'default_library=static'
+    ],
+)
+
+git = find_program('git', native: true, required: false)
+
+if not git.found()
+    add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'c')
+else
+    git_commit_hash = run_command([git.full_path(), 'describe', '--always', '--tags'], check: true).stdout().strip()
+    git_branch = run_command([git.full_path(), 'rev-parse', '--abbrev-ref', 'HEAD'], check: true).stdout().strip()
+    version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch)
+    add_project_arguments('-DVERSION=@0@'.format(version), language: 'c')
+endif
+cc = meson.get_compiler('c')
+
+
+wayland_client = dependency('wayland-client')
+libhid = dependency('hidapi-hidraw')
+rt = cc.find_library('rt')
+
+
+
+src_files = files(
+  './src/error.c',
+  './src/hid/hid.c',
+  './src/keyboard/keyboard.c',
+  './src/main.c',
+)
+
+subdir('protocol')
+
+executable(
+    meson.project_name(),
+    src_files,
+    dependencies: [
+        client_protos,
+        wayland_client,
+        libhid,
+        rt,
+    ],
+)
+
diff --git a/rust/qmk-hid-com/src_c/protocol/meson.build b/rust/qmk-hid-com/src_c/protocol/meson.build
new file mode 100644
index 0000000..1bfe9e0
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/protocol/meson.build
@@ -0,0 +1,37 @@
+wayland_scanner = find_program('wayland-scanner')
+
+wayland_scanner_code = generator(
+	wayland_scanner,
+	output: '@BASENAME@-protocol.c',
+	arguments: ['private-code', '@INPUT@', '@OUTPUT@'],
+)
+
+wayland_scanner_client = generator(
+	wayland_scanner,
+	output: '@BASENAME@-client-protocol.h',
+	arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
+)
+
+
+client_protocols = [
+	['virtual-keyboard-unstable-v1.xml'],
+]
+client_protos_src = []
+client_protos_headers = []
+
+
+foreach p : client_protocols
+	xml = join_paths(p)
+	client_protos_src += wayland_scanner_code.process(xml)
+	client_protos_headers += wayland_scanner_client.process(xml)
+endforeach
+lib_client_protos = static_library(
+	'client_protos',
+	client_protos_src + client_protos_headers,
+	dependencies: [wayland_client]
+)
+
+client_protos = declare_dependency(
+	link_with: lib_client_protos,
+	sources: client_protos_headers,
+)
diff --git a/rust/qmk-hid-com/src_c/protocol/virtual-keyboard-unstable-v1.xml b/rust/qmk-hid-com/src_c/protocol/virtual-keyboard-unstable-v1.xml
new file mode 100644
index 0000000..5095c91
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/protocol/virtual-keyboard-unstable-v1.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="virtual_keyboard_unstable_v1">
+  <copyright>
+    Copyright © 2008-2011  Kristian Høgsberg
+    Copyright © 2010-2013  Intel Corporation
+    Copyright © 2012-2013  Collabora, Ltd.
+    Copyright © 2018       Purism SPC
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="zwp_virtual_keyboard_v1" version="1">
+    <description summary="virtual keyboard">
+      The virtual keyboard provides an application with requests which emulate
+      the behaviour of a physical keyboard.
+
+      This interface can be used by clients on its own to provide raw input
+      events, or it can accompany the input method protocol.
+    </description>
+
+    <request name="keymap">
+      <description summary="keyboard mapping">
+        Provide a file descriptor to the compositor which can be
+        memory-mapped to provide a keyboard mapping description.
+
+        Format carries a value from the keymap_format enumeration.
+      </description>
+      <arg name="format" type="uint" summary="keymap format"/>
+      <arg name="fd" type="fd" summary="keymap file descriptor"/>
+      <arg name="size" type="uint" summary="keymap size, in bytes"/>
+    </request>
+
+    <enum name="error">
+      <entry name="no_keymap" value="0" summary="No keymap was set"/>
+    </enum>
+
+    <request name="key">
+      <description summary="key event">
+        A key was pressed or released.
+        The time argument is a timestamp with millisecond granularity, with an
+        undefined base. All requests regarding a single object must share the
+        same clock.
+
+        Keymap must be set before issuing this request.
+
+        State carries a value from the key_state enumeration.
+      </description>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="key" type="uint" summary="key that produced the event"/>
+      <arg name="state" type="uint" summary="physical state of the key"/>
+    </request>
+
+    <request name="modifiers">
+      <description summary="modifier and group state">
+        Notifies the compositor that the modifier and/or group state has
+        changed, and it should update state.
+
+        The client should use wl_keyboard.modifiers event to synchronize its
+        internal state with seat state.
+
+        Keymap must be set before issuing this request.
+      </description>
+      <arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
+      <arg name="mods_latched" type="uint" summary="latched modifiers"/>
+      <arg name="mods_locked" type="uint" summary="locked modifiers"/>
+      <arg name="group" type="uint" summary="keyboard layout"/>
+    </request>
+
+    <request name="destroy" type="destructor" since="1">
+      <description summary="destroy the virtual keyboard keyboard object"/>
+    </request>
+  </interface>
+
+  <interface name="zwp_virtual_keyboard_manager_v1" version="1">
+    <description summary="virtual keyboard manager">
+      A virtual keyboard manager allows an application to provide keyboard
+      input events as if they came from a physical keyboard.
+    </description>
+
+    <enum name="error">
+      <entry name="unauthorized" value="0" summary="client not authorized to use the interface"/>
+    </enum>
+
+    <request name="create_virtual_keyboard">
+      <description summary="Create a new virtual keyboard">
+        Creates a new virtual keyboard associated to a seat.
+
+        If the compositor enables a keyboard to perform arbitrary actions, it
+        should present an error when an untrusted client requests a new
+        keyboard.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat"/>
+      <arg name="id" type="new_id" interface="zwp_virtual_keyboard_v1"/>
+    </request>
+  </interface>
+</protocol>
diff --git a/rust/qmk-hid-com/src_c/src/error.c b/rust/qmk-hid-com/src_c/src/error.c
new file mode 100644
index 0000000..f970cfc
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/error.c
@@ -0,0 +1,20 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wayland-client.h>
+
+#include "error.h"
+
+__attribute__((noreturn)) __attribute__((format(printf, 1, 2))) void
+fail(const char *format, ...) {
+  va_list vas;
+
+  va_start(vas, format);
+  fprintf(stderr, "Error: ");
+  vfprintf(stderr, format, vas);
+  va_end(vas);
+
+  fprintf(stderr, "\n");
+
+  exit(EXIT_FAILURE);
+}
diff --git a/rust/qmk-hid-com/src_c/src/error.h b/rust/qmk-hid-com/src_c/src/error.h
new file mode 100644
index 0000000..e0d6aaf
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/error.h
@@ -0,0 +1 @@
+void fail(const char *format, ...);
diff --git a/rust/qmk-hid-com/src_c/src/hid/hid.c b/rust/qmk-hid-com/src_c/src/hid/hid.c
new file mode 100644
index 0000000..d468ba8
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/hid/hid.c
@@ -0,0 +1,83 @@
+#include <assert.h>
+#include <hidapi/hidapi.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "../error.h"
+#include "hid.h"
+
+static hid_device *find_device_by_usage(uint32_t expected_usage,
+                                        uint32_t expected_usage_page) {
+  struct hid_device_info *devs;
+  devs = hid_enumerate(0x0, 0x0);
+
+  hid_device *found_device = NULL;
+  bool found = false;
+
+  struct hid_device_info *cur_dev = devs;
+  for (; cur_dev; cur_dev = cur_dev->next) {
+    // printf("Checking device: %ls, %d (%d:%d) at %s ..\n",
+    //        cur_dev->product_string, cur_dev->product_id, cur_dev->usage,
+    //        cur_dev->usage_page, cur_dev->path);
+
+    if (cur_dev->usage == expected_usage &&
+        cur_dev->usage_page == expected_usage_page) {
+      found_device = hid_open_path(cur_dev->path);
+      found = true;
+
+      goto output;
+    }
+  }
+
+output:
+  hid_free_enumeration(devs);
+  if (!found_device) {
+    if (found) {
+      fail("Failed to open device usage (%d) and usage page (%d): %ls",
+           expected_usage, expected_usage_page, hid_error(found_device));
+    } else {
+      fail("Can't find device with usage (%d) and usage page (%d): %ls",
+           expected_usage, expected_usage_page, hid_error(found_device));
+    }
+    hid_exit();
+
+    return NULL;
+  } else {
+    return found_device;
+  }
+}
+
+uint32_t read_next(uint32_t usage, uint32_t usage_page) {
+  hid_device *handle;
+
+  if (hid_init()) {
+    fail("Failed to init hid");
+  }
+
+  handle = find_device_by_usage(usage, usage_page);
+
+  uint8_t buf[4];
+  int res = 0;
+  res = hid_read(handle, buf, sizeof(buf));
+  if (res < 0) {
+    fail("Failed to read 4 bytes from hid device: %ls", hid_error(handle));
+  } else if (res == 0) {
+    fail("Device did not send anything!");
+  }
+
+  hid_close(handle);
+  if (hid_exit()) {
+    fail("Failed to exit hid");
+  }
+
+  // Buf should only contain positive values.
+  assert(buf[0] >= 0 && buf[1] >= 0 && buf[2] >= 0 && buf[3] >= 0);
+
+  // Only lower Endian supported, TODO add the other one
+  uint32_t key_code =
+      (uint32_t)(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24));
+
+  return key_code;
+}
diff --git a/rust/qmk-hid-com/src_c/src/hid/hid.h b/rust/qmk-hid-com/src_c/src/hid/hid.h
new file mode 100644
index 0000000..9ffb9d3
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/hid/hid.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <stdint.h>
+
+uint32_t read_next(uint32_t usage, uint32_t usage_page);
diff --git a/rust/qmk-hid-com/src_c/src/keyboard/keyboard.c b/rust/qmk-hid-com/src_c/src/keyboard/keyboard.c
new file mode 100644
index 0000000..4949085
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/keyboard/keyboard.c
@@ -0,0 +1,135 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "virtual-keyboard-unstable-v1-client-protocol.h"
+
+#include "../error.h"
+#include "keyboard.h"
+
+
+static void handle_wl_event(void *data, struct wl_registry *registry,
+                            uint32_t name, const char *interface,
+                            uint32_t version);
+static void handle_wl_event_remove(void *data, struct wl_registry *registry,
+                                   uint32_t name);
+
+const struct wl_registry_listener registry_listener = {
+    .global = handle_wl_event,
+    .global_remove = handle_wl_event_remove,
+};
+
+struct qmk_unicode_type init_qmk_unicode() {
+  struct qmk_unicode_type qmk_unicode_type;
+
+  memset(&qmk_unicode_type, 0, sizeof(qmk_unicode_type));
+  qmk_unicode_type.display = wl_display_connect(NULL);
+
+  if (qmk_unicode_type.display == NULL) {
+    fail("Wayland connection failed");
+  }
+
+  qmk_unicode_type.registry = wl_display_get_registry(qmk_unicode_type.display);
+
+  wl_registry_add_listener(qmk_unicode_type.registry, &registry_listener,
+                           &qmk_unicode_type);
+  wl_display_dispatch(qmk_unicode_type.display);
+  wl_display_roundtrip(qmk_unicode_type.display);
+
+  if (qmk_unicode_type.manager == NULL) {
+    fail("Compositor does not support the virtual keyboard protocol");
+  }
+  if (qmk_unicode_type.seat == NULL) {
+    fail("No seat found");
+  }
+
+  qmk_unicode_type.keyboard =
+      zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
+          qmk_unicode_type.manager, qmk_unicode_type.seat);
+
+  return qmk_unicode_type;
+}
+void destroy_qmk_unicode(struct qmk_unicode_type qmk_unicode_type) {
+  zwp_virtual_keyboard_v1_destroy(qmk_unicode_type.keyboard);
+  zwp_virtual_keyboard_manager_v1_destroy(qmk_unicode_type.manager);
+  wl_registry_destroy(qmk_unicode_type.registry);
+  wl_display_disconnect(qmk_unicode_type.display);
+}
+
+static void upload_keymap(struct qmk_unicode_type *qmk_unicode_type,
+                          uint32_t key_code);
+
+void type_keycode(struct qmk_unicode_type *qmk_unicode_type,
+                  uint32_t key_code) {
+  upload_keymap(qmk_unicode_type, key_code);
+
+  zwp_virtual_keyboard_v1_key(qmk_unicode_type->keyboard, 0, 1,
+                              WL_KEYBOARD_KEY_STATE_PRESSED);
+  wl_display_roundtrip(qmk_unicode_type->display);
+  usleep(2000);
+  zwp_virtual_keyboard_v1_key(qmk_unicode_type->keyboard, 0, 1,
+                              WL_KEYBOARD_KEY_STATE_RELEASED);
+  wl_display_roundtrip(qmk_unicode_type->display);
+  usleep(2000);
+}
+
+static void handle_wl_event(void *data, struct wl_registry *registry,
+                            uint32_t name, const char *interface,
+                            uint32_t version) {
+  struct qmk_unicode_type *qmk_unicode_type = (struct qmk_unicode_type *)data;
+
+  if (!strcmp(interface, wl_seat_interface.name)) {
+    qmk_unicode_type->seat = (struct wl_seat *)wl_registry_bind(
+        registry, name, &wl_seat_interface, version <= 7 ? version : 7);
+  } else if (!strcmp(interface,
+                     zwp_virtual_keyboard_manager_v1_interface.name)) {
+    qmk_unicode_type->manager =
+        (struct zwp_virtual_keyboard_manager_v1 *)wl_registry_bind(
+            registry, name, &zwp_virtual_keyboard_manager_v1_interface, 1);
+  }
+}
+
+static void
+handle_wl_event_remove(__attribute__((unused)) void *data,
+                       __attribute__((unused)) struct wl_registry *_registry,
+                       __attribute__((unused)) uint32_t _name) {}
+
+static void upload_keymap(struct qmk_unicode_type *qmk_unicode_type,
+                          uint32_t key_code) {
+  char filename[] = "/tmp/qmk_unicode_type-XXXXXX";
+  int fd = mkstemp(filename);
+  if (fd < 0) {
+    fail("Failed to create the temporary keymap file");
+  }
+  unlink(filename);
+  FILE *f = fdopen(fd, "w");
+
+  fprintf(f,
+          "xkb_keymap {\n"
+          "  xkb_keycodes \"(unnamed)\" {\n"
+          "      minimum = 8;\n"
+          "      maximum = 10;\n"
+          "      <K1> = 9;\n"
+          "  };\n"
+          // TODO: Is including "complete" here really a good idea?
+          "  xkb_types \"(unnamed)\" { include \"complete\" };\n"
+          "  xkb_compatibility \"(unnamed)\" { include \"complete\" };\n"
+          "  xkb_symbols \"(unnamed)\" {\n"
+          "      key <K1> {[U%X]};\n"
+          "  };\n"
+          "};\n",
+          key_code);
+
+  fputc('\0', f);
+  fflush(f);
+
+  uint32_t keymap_size = (uint32_t)ftell(f);
+  zwp_virtual_keyboard_v1_keymap(qmk_unicode_type->keyboard,
+                                 WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fileno(f),
+                                 keymap_size);
+
+  wl_display_roundtrip(qmk_unicode_type->display);
+  fclose(f);
+}
diff --git a/rust/qmk-hid-com/src_c/src/keyboard/keyboard.h b/rust/qmk-hid-com/src_c/src/keyboard/keyboard.h
new file mode 100644
index 0000000..ac5d7bd
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/keyboard/keyboard.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <stdint.h>
+
+struct qmk_unicode_type init_qmk_unicode();
+void destroy_qmk_unicode(struct qmk_unicode_type qmk_unicode_type);
+
+void type_keycode(struct qmk_unicode_type *qmk_unicode_type, uint32_t key_code);
+
+// Private
+struct qmk_unicode_type {
+  struct wl_display *display;
+  struct wl_registry *registry;
+  struct wl_seat *seat;
+  struct zwp_virtual_keyboard_manager_v1 *manager;
+  struct zwp_virtual_keyboard_v1 *keyboard;
+};
diff --git a/rust/qmk-hid-com/src_c/src/keyboard/virtual-keyboard-unstable-v1-client-protocol.h b/rust/qmk-hid-com/src_c/src/keyboard/virtual-keyboard-unstable-v1-client-protocol.h
new file mode 100644
index 0000000..81cac0b
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/keyboard/virtual-keyboard-unstable-v1-client-protocol.h
@@ -0,0 +1,280 @@
+/* Generated by wayland-scanner 1.23.1 */
+
+#ifndef VIRTUAL_KEYBOARD_UNSTABLE_V1_CLIENT_PROTOCOL_H
+#define VIRTUAL_KEYBOARD_UNSTABLE_V1_CLIENT_PROTOCOL_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include "wayland-client.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page page_virtual_keyboard_unstable_v1 The virtual_keyboard_unstable_v1 protocol
+ * @section page_ifaces_virtual_keyboard_unstable_v1 Interfaces
+ * - @subpage page_iface_zwp_virtual_keyboard_v1 - virtual keyboard
+ * - @subpage page_iface_zwp_virtual_keyboard_manager_v1 - virtual keyboard manager
+ * @section page_copyright_virtual_keyboard_unstable_v1 Copyright
+ * <pre>
+ *
+ * Copyright © 2008-2011  Kristian Høgsberg
+ * Copyright © 2010-2013  Intel Corporation
+ * Copyright © 2012-2013  Collabora, Ltd.
+ * Copyright © 2018       Purism SPC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * </pre>
+ */
+struct wl_seat;
+struct zwp_virtual_keyboard_manager_v1;
+struct zwp_virtual_keyboard_v1;
+
+#ifndef ZWP_VIRTUAL_KEYBOARD_V1_INTERFACE
+#define ZWP_VIRTUAL_KEYBOARD_V1_INTERFACE
+/**
+ * @page page_iface_zwp_virtual_keyboard_v1 zwp_virtual_keyboard_v1
+ * @section page_iface_zwp_virtual_keyboard_v1_desc Description
+ *
+ * The virtual keyboard provides an application with requests which emulate
+ * the behaviour of a physical keyboard.
+ *
+ * This interface can be used by clients on its own to provide raw input
+ * events, or it can accompany the input method protocol.
+ * @section page_iface_zwp_virtual_keyboard_v1_api API
+ * See @ref iface_zwp_virtual_keyboard_v1.
+ */
+/**
+ * @defgroup iface_zwp_virtual_keyboard_v1 The zwp_virtual_keyboard_v1 interface
+ *
+ * The virtual keyboard provides an application with requests which emulate
+ * the behaviour of a physical keyboard.
+ *
+ * This interface can be used by clients on its own to provide raw input
+ * events, or it can accompany the input method protocol.
+ */
+extern const struct wl_interface zwp_virtual_keyboard_v1_interface;
+#endif
+#ifndef ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_INTERFACE
+#define ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_INTERFACE
+/**
+ * @page page_iface_zwp_virtual_keyboard_manager_v1 zwp_virtual_keyboard_manager_v1
+ * @section page_iface_zwp_virtual_keyboard_manager_v1_desc Description
+ *
+ * A virtual keyboard manager allows an application to provide keyboard
+ * input events as if they came from a physical keyboard.
+ * @section page_iface_zwp_virtual_keyboard_manager_v1_api API
+ * See @ref iface_zwp_virtual_keyboard_manager_v1.
+ */
+/**
+ * @defgroup iface_zwp_virtual_keyboard_manager_v1 The zwp_virtual_keyboard_manager_v1 interface
+ *
+ * A virtual keyboard manager allows an application to provide keyboard
+ * input events as if they came from a physical keyboard.
+ */
+extern const struct wl_interface zwp_virtual_keyboard_manager_v1_interface;
+#endif
+
+#ifndef ZWP_VIRTUAL_KEYBOARD_V1_ERROR_ENUM
+#define ZWP_VIRTUAL_KEYBOARD_V1_ERROR_ENUM
+enum zwp_virtual_keyboard_v1_error {
+	/**
+	 * No keymap was set
+	 */
+	ZWP_VIRTUAL_KEYBOARD_V1_ERROR_NO_KEYMAP = 0,
+};
+#endif /* ZWP_VIRTUAL_KEYBOARD_V1_ERROR_ENUM */
+
+#define ZWP_VIRTUAL_KEYBOARD_V1_KEYMAP 0
+#define ZWP_VIRTUAL_KEYBOARD_V1_KEY 1
+#define ZWP_VIRTUAL_KEYBOARD_V1_MODIFIERS 2
+#define ZWP_VIRTUAL_KEYBOARD_V1_DESTROY 3
+
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_V1_KEYMAP_SINCE_VERSION 1
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_V1_KEY_SINCE_VERSION 1
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_V1_MODIFIERS_SINCE_VERSION 1
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_V1_DESTROY_SINCE_VERSION 1
+
+/** @ingroup iface_zwp_virtual_keyboard_v1 */
+static inline void
+zwp_virtual_keyboard_v1_set_user_data(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, void *user_data)
+{
+	wl_proxy_set_user_data((struct wl_proxy *) zwp_virtual_keyboard_v1, user_data);
+}
+
+/** @ingroup iface_zwp_virtual_keyboard_v1 */
+static inline void *
+zwp_virtual_keyboard_v1_get_user_data(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1)
+{
+	return wl_proxy_get_user_data((struct wl_proxy *) zwp_virtual_keyboard_v1);
+}
+
+static inline uint32_t
+zwp_virtual_keyboard_v1_get_version(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1)
+{
+	return wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ *
+ * Provide a file descriptor to the compositor which can be
+ * memory-mapped to provide a keyboard mapping description.
+ *
+ * Format carries a value from the keymap_format enumeration.
+ */
+static inline void
+zwp_virtual_keyboard_v1_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t format, int32_t fd, uint32_t size)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_v1,
+			 ZWP_VIRTUAL_KEYBOARD_V1_KEYMAP, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1), 0, format, fd, size);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ *
+ * A key was pressed or released.
+ * The time argument is a timestamp with millisecond granularity, with an
+ * undefined base. All requests regarding a single object must share the
+ * same clock.
+ *
+ * Keymap must be set before issuing this request.
+ *
+ * State carries a value from the key_state enumeration.
+ */
+static inline void
+zwp_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t time, uint32_t key, uint32_t state)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_v1,
+			 ZWP_VIRTUAL_KEYBOARD_V1_KEY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1), 0, time, key, state);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ *
+ * Notifies the compositor that the modifier and/or group state has
+ * changed, and it should update state.
+ *
+ * The client should use wl_keyboard.modifiers event to synchronize its
+ * internal state with seat state.
+ *
+ * Keymap must be set before issuing this request.
+ */
+static inline void
+zwp_virtual_keyboard_v1_modifiers(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_v1,
+			 ZWP_VIRTUAL_KEYBOARD_V1_MODIFIERS, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1), 0, mods_depressed, mods_latched, mods_locked, group);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_v1
+ */
+static inline void
+zwp_virtual_keyboard_v1_destroy(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_v1,
+			 ZWP_VIRTUAL_KEYBOARD_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_v1), WL_MARSHAL_FLAG_DESTROY);
+}
+
+#ifndef ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_ERROR_ENUM
+#define ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_ERROR_ENUM
+enum zwp_virtual_keyboard_manager_v1_error {
+	/**
+	 * client not authorized to use the interface
+	 */
+	ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_ERROR_UNAUTHORIZED = 0,
+};
+#endif /* ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_ERROR_ENUM */
+
+#define ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_CREATE_VIRTUAL_KEYBOARD 0
+
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_manager_v1
+ */
+#define ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_CREATE_VIRTUAL_KEYBOARD_SINCE_VERSION 1
+
+/** @ingroup iface_zwp_virtual_keyboard_manager_v1 */
+static inline void
+zwp_virtual_keyboard_manager_v1_set_user_data(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1, void *user_data)
+{
+	wl_proxy_set_user_data((struct wl_proxy *) zwp_virtual_keyboard_manager_v1, user_data);
+}
+
+/** @ingroup iface_zwp_virtual_keyboard_manager_v1 */
+static inline void *
+zwp_virtual_keyboard_manager_v1_get_user_data(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1)
+{
+	return wl_proxy_get_user_data((struct wl_proxy *) zwp_virtual_keyboard_manager_v1);
+}
+
+static inline uint32_t
+zwp_virtual_keyboard_manager_v1_get_version(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1)
+{
+	return wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_manager_v1);
+}
+
+/** @ingroup iface_zwp_virtual_keyboard_manager_v1 */
+static inline void
+zwp_virtual_keyboard_manager_v1_destroy(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1)
+{
+	wl_proxy_destroy((struct wl_proxy *) zwp_virtual_keyboard_manager_v1);
+}
+
+/**
+ * @ingroup iface_zwp_virtual_keyboard_manager_v1
+ *
+ * Creates a new virtual keyboard associated to a seat.
+ *
+ * If the compositor enables a keyboard to perform arbitrary actions, it
+ * should present an error when an untrusted client requests a new
+ * keyboard.
+ */
+static inline struct zwp_virtual_keyboard_v1 *
+zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1, struct wl_seat *seat)
+{
+	struct wl_proxy *id;
+
+	id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_virtual_keyboard_manager_v1,
+			 ZWP_VIRTUAL_KEYBOARD_MANAGER_V1_CREATE_VIRTUAL_KEYBOARD, &zwp_virtual_keyboard_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_virtual_keyboard_manager_v1), 0, seat, NULL);
+
+	return (struct zwp_virtual_keyboard_v1 *) id;
+}
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/rust/qmk-hid-com/src_c/src/main.c b/rust/qmk-hid-com/src_c/src/main.c
new file mode 100644
index 0000000..48d490e
--- /dev/null
+++ b/rust/qmk-hid-com/src_c/src/main.c
@@ -0,0 +1,28 @@
+#include <stdlib.h>
+
+#include "error.h"
+#include "hid/hid.h"
+#include "keyboard/keyboard.h"
+
+int main(int argc, const char *argv[]) {
+  if (argc != 3) {
+    fail("Usage: %s <usage> <usage_page>", argv[0]);
+  }
+
+  char *ep;
+  uint32_t usage = (uint32_t)strtoul(argv[1], &ep, 0);
+  if (*ep != 0) {
+    fail("Invalid usage: %x", usage);
+  }
+
+  uint32_t usage_page = (uint32_t)strtoul(argv[2], &ep, 0);
+  if (*ep != 0) {
+    fail("Invalid usage: %x", usage_page);
+  }
+
+  uint32_t key_value = read_next(usage, usage_page);
+
+  struct qmk_unicode_type qmk_unicode_type = init_qmk_unicode();
+  type_keycode(&qmk_unicode_type, key_value);
+  destroy_qmk_unicode(qmk_unicode_type);
+}