diff options
Diffstat (limited to '')
| -rw-r--r-- | kwin-type.c | 267 |
1 files changed, 99 insertions, 168 deletions
diff --git a/kwin-type.c b/kwin-type.c index 00f2369..4120f84 100644 --- a/kwin-type.c +++ b/kwin-type.c @@ -4,185 +4,116 @@ */ #define _GNU_SOURCE +#include <dbus/dbus.h> #include <err.h> -#include <locale.h> +#include <libei.h> +#include <poll.h> #include <signal.h> -#include <stdint.h> #include <stdio.h> #include <string.h> -#include <time.h> -#include <wchar.h> -#include <wayland-client.h> -#include <xkbcommon/xkbcommon.h> - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -/* https://invent.kde.org/libraries/plasma-wayland-protocols/-/blob/master/src/protocols/fake-input.xml */ -static const struct wl_message input_methods[] = { - { "authenticate", "ss", NULL }, - { "pointer_motion", "ff", NULL }, - { "button", "uu", NULL }, - { "axis", "uf", NULL }, - { "touch_down", "2uff", NULL }, - { "touch_motion", "2uff", NULL }, - { "touch_up", "2u", NULL }, - { "touch_cancel", "2", NULL }, - { "touch_frame", "2", NULL }, - { "pointer_motion_absolute", "3ff", NULL }, - { "keyboard_key", "4uu", NULL }, - { "destroy", "5", NULL }, - { "keyboard_keysym", "6uu", NULL }, -}; -static const struct wl_interface input_interface = { "org_kde_kwin_fake_input", 6, ARRAY_SIZE(input_methods), input_methods, 0, NULL }; - -/* https://invent.kde.org/libraries/plasma-wayland-protocols/-/blob/master/src/protocols/keystate.xml */ -static const struct wl_message state_methods[] = { { "fetchStates", "", NULL } }; -static const struct wl_message state_events[] = { { "stateChanged", "uu", NULL } }; -static const struct wl_interface state_interface = { "org_kde_kwin_keystate", 1, ARRAY_SIZE(state_methods), state_methods, ARRAY_SIZE(state_events), state_events }; -enum { AUTHENTICATE = 0, KEY = 10, DESTROY = 11, KEYSYM = 12 }; -enum { RELEASED = 0, PRESSED = 1, CAPS_LOCK_KEY = 58 }; - -static volatile sig_atomic_t interrupted; - -static void on_signal(int signo) -{ - interrupted = signo; -} - -static void state_changed(void *data, struct wl_proxy *, uint32_t key, uint32_t state) -{ - bool **caps = data; - if (key == 0) { - *caps[0] = true; - *caps[1] = state != 0; - } -} - -static void registry_global(void *data, struct wl_registry *, uint32_t name, const char *interface, uint32_t version) -{ - uint32_t **names = data; - if (!strcmp(interface, input_interface.name) && version >= (uint32_t)input_interface.version) - *names[0] = name; - else if (!strcmp(interface, state_interface.name)) - *names[1] = name; -} - -static void registry_global_remove(void *, struct wl_registry *, uint32_t) -{ -} - -static void type_key(struct wl_proxy *input, uint32_t request, uint32_t key) -{ - wl_proxy_marshal_flags(input, request, NULL, input_interface.version, 0, key, PRESSED); - wl_proxy_marshal_flags(input, request, NULL, input_interface.version, 0, key, RELEASED); -} +#include <unistd.h> int main(int argc, char **argv) { - bool caps_known = false, caps_locked = false, restore_caps = false, ret = false; - uint32_t input_name = 0, keystate_name = 0; - struct wl_proxy *input, *keystate; - struct wl_registry *registry; - struct wl_display *display; - xkb_keysym_t keysym; - wint_t cp; + dbus_int32_t capabilities = 1, cookie; + DBusError error = DBUS_ERROR_INIT; + DBusMessage *request, *reply; + DBusConnection *bus; + struct ei_ping *ping = NULL; + struct ei_device *device; + struct ei_event *event; + struct ei_seat *seat; + struct ei *ei; + size_t length = 0, chunk; + bool done = false, valid; + char text[255], saved; + int fd, ret; if (argc != 1) errx(1, "usage: %s < text.txt", argv[0]); - if (!setlocale(LC_CTYPE, "C.UTF-8")) - errx(1, "cannot select C.UTF-8 locale"); - if (sigaction(SIGINT, &(struct sigaction){ .sa_handler = on_signal }, NULL) < 0 || - sigaction(SIGTERM, &(struct sigaction){ .sa_handler = on_signal }, NULL) < 0 || - sigaction(SIGHUP, &(struct sigaction){ .sa_handler = on_signal }, NULL) < 0) { - warn("installing signal handlers"); - goto err; - } - display = wl_display_connect(NULL); - if (!display) { - warn("cannot connect to display"); - goto err; - } - registry = wl_display_get_registry(display); - if (!registry) { - warn("getting registry"); - goto err_display; - } - wl_registry_add_listener(registry, - &(const struct wl_registry_listener){ registry_global, registry_global_remove }, - (uint32_t *[]){ &input_name, &keystate_name }); - if (wl_display_roundtrip(display) < 0) { - warn("synchronizing display"); - goto err_registry; - } - if (!input_name || !keystate_name) { - warnx("%s-v%d and %s-v%d protocols are required", - input_interface.name, input_interface.version, state_interface.name, state_interface.version); - goto err_registry; - } - keystate = wl_registry_bind(registry, keystate_name, &state_interface, state_interface.version); - if (!keystate) { - warn("binding key-state interface"); - goto err_registry; - } - wl_proxy_add_listener(keystate, - (void (*[])(void)){ (void (*)(void))state_changed }, - (bool *[]){ &caps_known, &caps_locked }); - input = wl_registry_bind(registry, input_name, &input_interface, input_interface.version); - if (!input) { - warn("binding fake-input interface"); - goto err_keystate; - } - wl_proxy_marshal_flags(input, AUTHENTICATE, NULL, input_interface.version, 0, "kwin-type", "Type text from stdin"); - wl_proxy_marshal_flags(keystate, 0, NULL, state_interface.version, 0); - if (wl_display_roundtrip(display) < 0) { - warn("synchronizing display"); - goto err_input; - } - if (!caps_known) { - warnx("cannot read caps lock state"); - goto err_input; - } - - /* Chrome has some complicated keyboard refreshing logic. We could patch upstream kwin to - * hack around it, or fix Chrome itself, but it's probably easier to just delay briefly - * here. Probably other things will benefit from this hack too. Oh well. */ - nanosleep(&(struct timespec){ .tv_nsec = 100000000 }, NULL); - - if (interrupted) - goto err_input; - if (caps_locked) { - restore_caps = true; - type_key(input, KEY, CAPS_LOCK_KEY); - } - - while (!interrupted && (cp = fgetwc(stdin)) != WEOF) { - if (interrupted) - break; - keysym = xkb_utf32_to_keysym(cp); - if (keysym == XKB_KEY_Linefeed) - keysym = XKB_KEY_Return; - type_key(input, KEYSYM, keysym); - } - if (!interrupted && ferror(stdin)) - warn("reading stdin"); - else - ret = true; + bus = dbus_bus_get_private(DBUS_BUS_SESSION, &error); + if (!bus) + errx(1, "connecting to session bus: %s", error.message); + request = dbus_message_new_method_call("org.kde.KWin", "/org/kde/KWin/EIS/RemoteDesktop", + "org.kde.KWin.EIS.RemoteDesktop", "connectToEIS"); + if (!request || !dbus_message_append_args(request, DBUS_TYPE_INT32, &capabilities, DBUS_TYPE_INVALID)) + errx(1, "allocating D-Bus request"); + reply = dbus_connection_send_with_reply_and_block(bus, request, -1, &error); + dbus_message_unref(request); + if (!reply) + errx(1, "connecting to KWin EIS: %s", error.message); + if (!dbus_message_get_args(reply, &error, DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_INT32, &cookie, DBUS_TYPE_INVALID)) + errx(1, "reading KWin EIS reply: %s", error.message); + dbus_message_unref(reply); -err_input: - if (restore_caps) - type_key(input, KEY, CAPS_LOCK_KEY); - wl_proxy_marshal_flags(input, DESTROY, NULL, input_interface.version, WL_MARSHAL_FLAG_DESTROY); - if (wl_display_roundtrip(display) < 0) { - warn("synchronizing display"); - ret = false; + ei = ei_new_sender(NULL); + if (!ei) + errx(1, "creating libei context"); + ei_configure_name(ei, "kwin-type"); + ret = ei_setup_backend_fd(ei, fd); + if (ret < 0) + errx(1, "connecting to EIS: %s", strerror(-ret)); + while (!done) { + ei_dispatch(ei); + while ((event = ei_get_event(ei))) { + switch (ei_event_get_type(event)) { + case EI_EVENT_SEAT_ADDED: + seat = ei_event_get_seat(event); + if (!ei_seat_has_capability(seat, EI_DEVICE_CAP_TEXT)) + errx(1, "KWin EIS does not support text input"); + ei_seat_bind_capabilities(seat, EI_DEVICE_CAP_TEXT, NULL); + break; + case EI_EVENT_DEVICE_RESUMED: + device = ei_event_get_device(event); + if (ping || !ei_device_has_capability(device, EI_DEVICE_CAP_TEXT)) + break; + ei_device_start_emulating(device, 1); + while (!feof(stdin)) { + length += fread(text + length, 1, sizeof(text) - 1 - length, stdin); + if (ferror(stdin)) + err(1, "reading stdin"); + if (!length) + break; + if (memchr(text, '\0', length)) + errx(1, "stdin must not contain NUL bytes"); + text[length] = '\0'; + for (chunk = length; ; --chunk) { + saved = text[chunk]; + text[chunk] = '\0'; + valid = dbus_validate_utf8(text, NULL); + text[chunk] = saved; + if (valid) + break; + if (feof(stdin) || length - chunk == 3) + errx(1, "stdin must be valid UTF-8"); + } + ei_device_text_utf8_with_length(device, text, chunk); + ei_device_frame(device, ei_now(ei)); + length -= chunk; + memmove(text, text + chunk, length); + } + ei_device_stop_emulating(device); + ping = ei_new_ping(ei); + ei_ping(ping); + break; + case EI_EVENT_PONG: + done = true; + break; + case EI_EVENT_DISCONNECT: + errx(1, "disconnected from KWin EIS"); + default: + break; + } + ei_event_unref(event); + } + if (!done && poll(&(struct pollfd){ .fd = ei_get_fd(ei), .events = POLLIN }, 1, -1) <= 0) + err(1, "waiting for KWin EIS"); } -err_keystate: - wl_proxy_destroy(keystate); -err_registry: - wl_registry_destroy(registry); -err_display: - wl_display_disconnect(display); -err: - return interrupted ? (128 + interrupted) : (ret ? 0 : 1); + ei_ping_unref(ping); + ei_unref(ei); + dbus_connection_close(bus); + dbus_connection_unref(bus); + return 0; } |
