From 55fc0bb1a650a531b1a2064359f6bcb8d6fdfc8b Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Fri, 26 Apr 2024 22:16:57 -0600 Subject: [PATCH] feat: Add support for mouse buttons Using the virtual-pointer-unstable-v1 protocol. Fixes: #24 --- main.c | 95 +++++++++++++- protocol/meson.build | 1 + protocol/virtual-pointer-unstable-v1.xml | 152 +++++++++++++++++++++++ 3 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 protocol/virtual-pointer-unstable-v1.xml diff --git a/main.c b/main.c index dc5bd19..397a9b2 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,5 @@ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include "virtual-keyboard-unstable-v1-client-protocol.h" +#include "virtual-pointer-unstable-v1-client-protocol.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) @@ -24,7 +26,10 @@ enum wtype_command_type { WTYPE_COMMAND_KEY_PRESS = 3, WTYPE_COMMAND_KEY_RELEASE = 4, WTYPE_COMMAND_SLEEP = 5, - WTYPE_COMMAND_TEXT_STDIN = 6 + WTYPE_COMMAND_TEXT_STDIN = 6, + WTYPE_COMMAND_BUTTON_PRESS = 7, + WTYPE_COMMAND_BUTTON_RELEASE = 8, + WTYPE_COMMAND_BUTTON_CLICK = 9, }; @@ -38,6 +43,19 @@ enum wtype_mod { WTYPE_MOD_ALTGR = 128 }; +// These are taken from linux/input-event-codes.h +enum wtype_btn { + WTYPE_BTN_NONE = 0, + WTYPE_BTN_LEFT = 0x110, + WTYPE_BTN_RIGHT = 0x111, + WTYPE_BTN_MIDDLE = 0x112, + WTYPE_BTN_SIDE = 0x113, + WTYPE_BTN_EXTRA = 0x114, + WTYPE_BTN_FORWARD = 0x115, + WTYPE_BTN_BACK = 0x116, + WTYPE_BTN_TASK = 0x117 +}; + struct wtype_command { enum wtype_command_type type; @@ -49,6 +67,7 @@ struct wtype_command { }; unsigned int single_key_code; enum wtype_mod mod; + uint32_t button; unsigned int sleep_ms; }; }; @@ -66,6 +85,8 @@ struct wtype { struct wl_seat *seat; struct zwp_virtual_keyboard_manager_v1 *manager; struct zwp_virtual_keyboard_v1 *keyboard; + struct zwlr_virtual_pointer_manager_v1 *pointer_manager; + struct zwlr_virtual_pointer_v1 *pointer; // Stores a keycode -> (xkb_keysym_t, wchar_t) mapping // Beware that this is one-indexed (as zero-keycodes are sometimes @@ -110,6 +131,10 @@ static void handle_wl_event(void *data, struct wl_registry *registry, wtype->manager = wl_registry_bind( registry, name, &zwp_virtual_keyboard_manager_v1_interface, 1 ); + } else if (!strcmp(interface, zwlr_virtual_pointer_manager_v1_interface.name)) { + wtype->pointer_manager = wl_registry_bind( + registry, name, &zwlr_virtual_pointer_manager_v1_interface, 1 + ); } } @@ -135,6 +160,17 @@ static const struct { const char *name; enum wtype_mod mod; } mod_names[] = { {"altgr", WTYPE_MOD_ALTGR}, }; +static const struct { const char *name; uint32_t button; } button_names[] = { + {"left", WTYPE_BTN_LEFT}, + {"right", WTYPE_BTN_RIGHT}, + {"middle", WTYPE_BTN_MIDDLE}, + {"forward", WTYPE_BTN_FORWARD}, + {"back", WTYPE_BTN_BACK}, + {"side", WTYPE_BTN_SIDE}, + {"extra", WTYPE_BTN_EXTRA}, + {"task" , WTYPE_BTN_TASK}, +}; + enum wtype_mod name_to_mod(const char *name) { @@ -146,6 +182,20 @@ enum wtype_mod name_to_mod(const char *name) return WTYPE_MOD_NONE; } +uint32_t button_code_from_name(const char *name) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(button_names); i++) { + if (!strcasecmp(button_names[i].name, name)) { + return button_names[i].button; + } + } + // If we got a digit, convert to a numeric button event + if (strlen(name) == 1 && isdigit(*name)) { + return 0x100 | ((uint32_t) (*name - '0')); + } + return WTYPE_BTN_NONE; +} + static unsigned int append_keymap_entry(struct wtype *wtype, wchar_t ch, xkb_keysym_t xkb) { @@ -270,6 +320,18 @@ static void parse_args(struct wtype *wtype, int argc, const char *argv[]) } cmd->type = argv[i][1] == 'P' ? WTYPE_COMMAND_KEY_PRESS : WTYPE_COMMAND_KEY_RELEASE; cmd->single_key_code = get_key_code_by_xkb(wtype, ks); + } else if (!strcmp("-c", argv[i])) { + cmd->type = WTYPE_COMMAND_BUTTON_CLICK; + cmd->button = button_code_from_name(argv[i + 1]); + if (cmd->button == WTYPE_BTN_NONE) { + fail("Unknown button '%s'", argv[i + 1]); + } + } else if (!strcmp("-B", argv[i]) || !strcmp("-b", argv[i])) { + cmd->type = argv[i][1] == 'B' ? WTYPE_COMMAND_BUTTON_PRESS : WTYPE_COMMAND_BUTTON_RELEASE; + cmd->button = button_code_from_name(argv[i + 1]); + if (cmd->button == WTYPE_BTN_NONE) { + fail("Unknown button '%s'", argv[i + 1]); + } } else { fail("Unknown parameter %s", argv[i]); } @@ -418,7 +480,29 @@ static void run_text_stdin(struct wtype *wtype, struct wtype_command *cmd) free(cmd->key_codes); } +static void run_button(struct wtype *wtype, struct wtype_command *cmd) { + zwlr_virtual_pointer_v1_button( + wtype->pointer, 0, cmd->single_key_code, + cmd->type == WTYPE_COMMAND_BUTTON_PRESS ? + WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED); + zwlr_virtual_pointer_v1_frame(wtype->pointer); + wl_display_roundtrip(wtype->display); +} +static void run_click(struct wtype *wtype, struct wtype_command *cmd) { + zwlr_virtual_pointer_v1_button( + wtype->pointer, 0, cmd->single_key_code, + WL_POINTER_BUTTON_STATE_PRESSED); + wl_display_roundtrip(wtype->display); + usleep(2000); + zwlr_virtual_pointer_v1_button( + wtype->pointer, 0, cmd->single_key_code, + WL_POINTER_BUTTON_STATE_RELEASED); + zwlr_virtual_pointer_v1_frame(wtype->pointer); + wl_display_roundtrip(wtype->display); + usleep(2000); +} static void run_commands(struct wtype *wtype) + { void (*handlers[])(struct wtype *, struct wtype_command *) = { [WTYPE_COMMAND_SLEEP] = run_sleep, @@ -428,6 +512,9 @@ static void run_commands(struct wtype *wtype) [WTYPE_COMMAND_KEY_RELEASE] = run_key, [WTYPE_COMMAND_TEXT] = run_text, [WTYPE_COMMAND_TEXT_STDIN] = run_text_stdin, + [WTYPE_COMMAND_BUTTON_PRESS] = run_button, + [WTYPE_COMMAND_BUTTON_RELEASE] = run_button, + [WTYPE_COMMAND_BUTTON_CLICK] = run_click, }; for (unsigned int i = 0; i < wtype->command_count; i++) { handlers[wtype->commands[i].type](wtype, &wtype->commands[i]); @@ -530,6 +617,10 @@ int main(int argc, const char *argv[]) wtype.keyboard = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard( wtype.manager, wtype.seat ); + // TODO: make this optional + wtype.pointer = zwlr_virtual_pointer_manager_v1_create_virtual_pointer( + wtype.pointer_manager, wtype.seat + ); upload_keymap(&wtype); run_commands(&wtype); @@ -543,6 +634,8 @@ int main(int argc, const char *argv[]) free(wtype.keymap); zwp_virtual_keyboard_v1_destroy(wtype.keyboard); zwp_virtual_keyboard_manager_v1_destroy(wtype.manager); + zwlr_virtual_pointer_v1_destroy(wtype.pointer); + zwlr_virtual_pointer_manager_v1_destroy(wtype.pointer_manager); wl_registry_destroy(wtype.registry); wl_display_disconnect(wtype.display); } diff --git a/protocol/meson.build b/protocol/meson.build index d3faaf0..b6edbb9 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -17,6 +17,7 @@ wayland_scanner_client = generator( client_protocols = [ ['virtual-keyboard-unstable-v1.xml'], + ['virtual-pointer-unstable-v1.xml'], ] client_protos_src = [] client_protos_headers = [] diff --git a/protocol/virtual-pointer-unstable-v1.xml b/protocol/virtual-pointer-unstable-v1.xml new file mode 100644 index 0000000..ea243e7 --- /dev/null +++ b/protocol/virtual-pointer-unstable-v1.xml @@ -0,0 +1,152 @@ + + + + Copyright © 2019 Josef Gajdusek + + 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. + + + + + This protocol allows clients to emulate a physical pointer device. The + requests are mostly mirror opposites of those specified in wl_pointer. + + + + + + + + + + The pointer has moved by a relative amount to the previous request. + + Values are in the global compositor space. + + + + + + + + + The pointer has moved in an absolute coordinate frame. + + Value of x can range from 0 to x_extent, value of y can range from 0 + to y_extent. + + + + + + + + + + + A button was pressed or released. + + + + + + + + + Scroll and other axis requests. + + + + + + + + + Indicates the set of events that logically belong together. + + + + + + Source information for scroll and other axis. + + + + + + + Stop notification for scroll and other axes. + + + + + + + + Discrete step information for scroll and other axes. + + This event allows the client to extend data normally sent using the axis + event with discrete value. + + + + + + + + + + + + + + + This object allows clients to create individual virtual pointer objects. + + + + + Creates a new virtual pointer. The optional seat is a suggestion to the + compositor. + + + + + + + + + + + + + Creates a new virtual pointer. The seat and the output arguments are + optional. If the seat argument is set, the compositor should assign the + input device to the requested seat. If the output argument is set, the + compositor should map the input device to the requested output. + + + + + + +