diff options
-rw-r--r-- | examples/meson.build | 5 | ||||
-rw-r--r-- | examples/virtual-pointer.c | 138 | ||||
-rw-r--r-- | include/wlr/types/wlr_virtual_pointer_v1.h | 53 | ||||
-rw-r--r-- | protocol/meson.build | 1 | ||||
-rw-r--r-- | protocol/wlr-virtual-pointer-unstable-v1.xml | 139 | ||||
-rw-r--r-- | types/meson.build | 1 | ||||
-rw-r--r-- | types/wlr_virtual_pointer_v1.c | 353 |
7 files changed, 690 insertions, 0 deletions
diff --git a/examples/meson.build b/examples/meson.build index cfc4df86..90ac7611 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -143,6 +143,11 @@ clients = { 'dep': [wlroots], 'proto': ['wlr-foreign-toplevel-management-unstable-v1'], }, + 'virtual-pointer': { + 'src': 'virtual-pointer.c', + 'dep': wlroots, + 'proto': ['wlr-virtual-pointer-unstable-v1'], + }, } foreach name, info : compositors diff --git a/examples/virtual-pointer.c b/examples/virtual-pointer.c new file mode 100644 index 00000000..84af7ffd --- /dev/null +++ b/examples/virtual-pointer.c @@ -0,0 +1,138 @@ +/* + * 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. + */ + +#define _POSIX_C_SOURCE 200112L +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wayland-client-protocol.h> +#include "wlr-virtual-pointer-unstable-v1-client-protocol.h" + +static struct wl_seat *seat = NULL; +static struct zwlr_virtual_pointer_manager_v1 *pointer_manager = NULL; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + if (strcmp(interface, wl_seat_interface.name) == 0) { + seat = wl_registry_bind(registry, name, + &wl_seat_interface, version); + } else if (strcmp(interface, + zwlr_virtual_pointer_manager_v1_interface.name) == 0) { + pointer_manager = wl_registry_bind(registry, name, + &zwlr_virtual_pointer_manager_v1_interface, 1); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // Who cares? +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: ./virtual-pointer <subcommand>\n"); + return EXIT_FAILURE; + } + struct wl_display * display = wl_display_connect(NULL); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return EXIT_FAILURE; + } + + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + wl_display_dispatch(display); + wl_display_roundtrip(display); + + if (pointer_manager == NULL) { + fprintf(stderr, "compositor does not support wlr-virtual-pointer-unstable-v1\n"); + return EXIT_FAILURE; + } + + struct zwlr_virtual_pointer_v1 *pointer = + zwlr_virtual_pointer_manager_v1_create_virtual_pointer( + pointer_manager, seat); + + const char *cmd = argv[1]; + if (strcmp(cmd, "motion") == 0) { + if (argc < 4) { + fprintf(stderr, "Usage: ./virtual-pointer motion <dx> <dy>\n"); + return EXIT_FAILURE; + } + wl_fixed_t dx = wl_fixed_from_double(atof(argv[2])); + wl_fixed_t dy = wl_fixed_from_double(atof(argv[3])); + zwlr_virtual_pointer_v1_motion(pointer, 0, dx, dy); + } else if (strcmp(cmd, "absolute") == 0) { + if (argc < 6) { + fprintf(stderr, "Usage: ./virtual-pointer absolute <x> <y> <x_extent> <y_extent>\n"); + return EXIT_FAILURE; + } + uint32_t x = atoi(argv[2]); + uint32_t y = atoi(argv[3]); + uint32_t x_extent = atoi(argv[4]); + uint32_t y_extent = atoi(argv[5]); + zwlr_virtual_pointer_v1_motion_absolute(pointer, 0, x, y, x_extent, y_extent); + } else if (strcmp(cmd, "button") == 0) { + if (argc < 4) { + fprintf(stderr, "Usage: ./virtual-pointer button <button> press|release\n"); + return EXIT_FAILURE; + } + uint32_t button = atoi(argv[2]); + bool press = !!strcmp(argv[3], "release"); + zwlr_virtual_pointer_v1_button(pointer, 0, button, press); + } else if (strcmp(cmd, "axis") == 0) { + if (argc < 4) { + fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value>\n"); + return EXIT_FAILURE; + } + uint32_t axis = atoi(argv[2]); + wl_fixed_t value = wl_fixed_from_double(atof(argv[3])); + zwlr_virtual_pointer_v1_axis(pointer, 0, axis, value); + zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis); + } else if (strcmp(cmd, "axis_discrete") == 0) { + if (argc < 5) { + fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value> <value_discrete>\n"); + return EXIT_FAILURE; + } + uint32_t axis = atoi(argv[2]); + wl_fixed_t value = wl_fixed_from_double(atof(argv[3])); + uint32_t discrete = atoi(argv[4]); + zwlr_virtual_pointer_v1_axis_discrete(pointer, 0, axis, value, discrete); + zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis); + } else { + fprintf(stderr, "Invalid subcommand\n"); + return EXIT_FAILURE; + } + + zwlr_virtual_pointer_v1_frame(pointer); + zwlr_virtual_pointer_v1_destroy(pointer); + wl_display_dispatch(display); + + return EXIT_SUCCESS; +} diff --git a/include/wlr/types/wlr_virtual_pointer_v1.h b/include/wlr/types/wlr_virtual_pointer_v1.h new file mode 100644 index 00000000..7d2d80d9 --- /dev/null +++ b/include/wlr/types/wlr_virtual_pointer_v1.h @@ -0,0 +1,53 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_VIRTUAL_POINTER_V1_H +#define WLR_TYPES_WLR_VIRTUAL_POINTER_V1_H + +#include <wayland-server-core.h> +#include <wayland-server-protocol.h> +#include <wlr/interfaces/wlr_input_device.h> +#include <wlr/interfaces/wlr_pointer.h> + +struct wlr_virtual_pointer_manager_v1 { + struct wl_global *global; + struct wl_list virtual_pointers; // struct wlr_virtual_pointer_v1* + + struct wl_listener display_destroy; + + struct { + struct wl_signal new_virtual_pointer; // struct wlr_virtual_pointer_v1_new_pointer_event* + struct wl_signal destroy; + } events; +}; + +struct wlr_virtual_pointer_v1 { + struct wlr_input_device input_device; + struct wl_resource *resource; + /* Vertical and horizontal */ + struct wlr_event_pointer_axis axis_event[2]; + enum wl_pointer_axis axis; + bool axis_valid[2]; + + struct wl_list link; + + struct { + struct wl_signal destroy; // struct wlr_virtual_pointer_v1* + } events; +}; + +struct wlr_virtual_pointer_v1_new_pointer_event { + struct wlr_virtual_pointer_v1 *new_pointer; + /** Suggested by client; may be NULL. */ + struct wlr_seat *suggested_seat; +}; + +struct wlr_virtual_pointer_manager_v1* wlr_virtual_pointer_manager_v1_create( + struct wl_display *display); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index c39be751..b9aa49c7 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -41,6 +41,7 @@ protocols = { 'wlr-layer-shell-unstable-v1': 'wlr-layer-shell-unstable-v1.xml', 'wlr-output-management-unstable-v1': 'wlr-output-management-unstable-v1.xml', 'wlr-screencopy-unstable-v1': 'wlr-screencopy-unstable-v1.xml', + 'wlr-virtual-pointer-unstable-v1': 'wlr-virtual-pointer-unstable-v1.xml', } foreach name, path : protocols diff --git a/protocol/wlr-virtual-pointer-unstable-v1.xml b/protocol/wlr-virtual-pointer-unstable-v1.xml new file mode 100644 index 00000000..e21b4d71 --- /dev/null +++ b/protocol/wlr-virtual-pointer-unstable-v1.xml @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="wlr_virtual_pointer_unstable_v1"> + <copyright> + 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. + </copyright> + + <interface name="zwlr_virtual_pointer_v1" version="1"> + <description summary="virtual pointer"> + This protocol allows clients to emulate a physical pointer device. The + requests are mostly mirror opposites of those specified in wl_pointer. + </description> + + <enum name="error"> + <entry name="invalid_axis" value="0" + summary="client sent invalid axis enumeration value" /> + <entry name="invalid_axis_source" value="1" + summary="client sent invalid axis source enumeration value" /> + </enum> + + <request name="motion"> + <description summary="pointer relative motion event"> + The pointer has moved by a relative amount to the previous request. + + Values are in the global compositor space. + </description> + <arg name="time" type="uint" summary="timestamp with millisecond granularity"/> + <arg name="dx" type="fixed" summary="displacement on the x-axis"/> + <arg name="dy" type="fixed" summary="displacement on the y-axis"/> + </request> + + <request name="motion_absolute"> + <description summary="pointer absolute motion event"> + 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. + </description> + <arg name="time" type="uint" summary="timestamp with millisecond granularity"/> + <arg name="x" type="uint" summary="position on the x-axis"/> + <arg name="y" type="uint" summary="position on the y-axis"/> + <arg name="x_extent" type="uint" summary="extent of the x-axis"/> + <arg name="y_extent" type="uint" summary="extent of the y-axis"/> + </request> + + <request name="button"> + <description summary="button event"> + A button was pressed or released. + </description> + <arg name="time" type="uint" summary="timestamp with millisecond granularity"/> + <arg name="button" type="uint" summary="button that produced the event"/> + <arg name="state" type="uint" enum="wl_pointer.button_state" summary="physical state of the button"/> + </request> + + <request name="axis"> + <description summary="axis event"> + Scroll and other axis requests. + </description> + <arg name="time" type="uint" summary="timestamp with millisecond granularity"/> + <arg name="axis" type="uint" enum="wl_pointer.axis" summary="axis type"/> + <arg name="value" type="fixed" summary="length of vector in touchpad coordinates"/> + </request> + + <request name="frame"> + <description summary="end of a pointer event sequence"> + Indicates the set of events that logically belong together. + </description> + </request> + + <request name="axis_source"> + <description summary="axis source event"> + Source information for scroll and other axis. + </description> + <arg name="axis_source" type="uint" enum="wl_pointer.axis_source" summary="source of the axis event"/> + </request> + + <request name="axis_stop"> + <description summary="axis stop event"> + Stop notification for scroll and other axes. + </description> + <arg name="time" type="uint" summary="timestamp with millisecond granularity"/> + <arg name="axis" type="uint" enum="wl_pointer.axis" summary="the axis stopped with this event"/> + </request> + + <request name="axis_discrete"> + <description summary="axis click event"> + 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. + </description> + <arg name="time" type="uint" summary="timestamp with millisecond granularity"/> + <arg name="axis" type="uint" enum="wl_pointer.axis" summary="axis type"/> + <arg name="value" type="fixed" summary="length of vector in touchpad coordinates"/> + <arg name="discrete" type="int" summary="number of steps"/> + </request> + + <request name="destroy" type="destructor" since="1"> + <description summary="destroy the virtual pointer object"/> + </request> + </interface> + + <interface name="zwlr_virtual_pointer_manager_v1" version="1"> + <description summary="virtual pointer manager"> + This object allows clients to create individual virtual pointer objects. + </description> + + <request name="create_virtual_pointer"> + <description summary="Create a new virtual pointer"> + Creates a new virtual pointer. The optional seat is a suggestion to the + compositor. + </description> + <arg name="seat" type="object" interface="wl_seat" allow-null="true"/> + <arg name="id" type="new_id" interface="zwlr_virtual_pointer_v1"/> + </request> + + <request name="destroy" type="destructor" since="1"> + <description summary="destroy the virtual pointer manager"/> + </request> + </interface> +</protocol> diff --git a/types/meson.build b/types/meson.build index 4e141fb3..fb8562c3 100644 --- a/types/meson.build +++ b/types/meson.build @@ -63,6 +63,7 @@ wlr_files += files( 'wlr_text_input_v3.c', 'wlr_touch.c', 'wlr_virtual_keyboard_v1.c', + 'wlr_virtual_pointer_v1.c', 'wlr_xcursor_manager.c', 'wlr_xdg_decoration_v1.c', 'wlr_xdg_output_v1.c', diff --git a/types/wlr_virtual_pointer_v1.c b/types/wlr_virtual_pointer_v1.c new file mode 100644 index 00000000..91318735 --- /dev/null +++ b/types/wlr_virtual_pointer_v1.c @@ -0,0 +1,353 @@ +#include <assert.h> +#include <stdlib.h> +#include <wlr/types/wlr_seat.h> +#include <wlr/types/wlr_virtual_pointer_v1.h> +#include <wlr/types/wlr_pointer.h> +#include <wlr/util/log.h> +#include "util/signal.h" +#include "wlr-virtual-pointer-unstable-v1-protocol.h" + +static void input_device_destroy(struct wlr_input_device *dev) { + struct wlr_virtual_pointer_v1 *pointer = + (struct wlr_virtual_pointer_v1 *)dev; + wl_resource_set_user_data(pointer->resource, NULL); + wlr_signal_emit_safe(&pointer->events.destroy, pointer); + wl_list_remove(&pointer->link); + free(pointer); +} + +static const struct wlr_input_device_impl input_device_impl = { + .destroy = input_device_destroy +}; + +static const struct zwlr_virtual_pointer_v1_interface virtual_pointer_impl; + +static struct wlr_virtual_pointer_v1 *virtual_pointer_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_virtual_pointer_v1_interface, &virtual_pointer_impl)); + return wl_resource_get_user_data(resource); +} + +static void virtual_pointer_motion(struct wl_client *client, + struct wl_resource *resource, uint32_t time, + wl_fixed_t dx, wl_fixed_t dy) { + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer == NULL) { + return; + } + struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_event_pointer_motion event = { + .device = wlr_dev, + .time_msec = time, + .delta_x = wl_fixed_to_double(dx), + .delta_y = wl_fixed_to_double(dy), + .unaccel_dx = wl_fixed_to_double(dx), + .unaccel_dy = wl_fixed_to_double(dy), + }; + wlr_signal_emit_safe(&wlr_dev->pointer->events.motion, &event); +} + +static void virtual_pointer_motion_absolute(struct wl_client *client, + struct wl_resource *resource, uint32_t time, uint32_t x, uint32_t y, + uint32_t x_extent, uint32_t y_extent) { + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer == NULL) { + return; + } + if (x_extent == 0 || y_extent == 0) { + return; + } + struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_event_pointer_motion_absolute event = { + .device = wlr_dev, + .time_msec = time, + .x = (double)x / x_extent, + .y = (double)y / y_extent, + }; + wlr_signal_emit_safe(&wlr_dev->pointer->events.motion_absolute, &event); +} + +static void virtual_pointer_button(struct wl_client *client, + struct wl_resource *resource, uint32_t time, uint32_t button, + uint32_t state) { + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer == NULL) { + return; + } + struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_event_pointer_button event = { + .device = wlr_dev, + .time_msec = time, + .button = button, + .state = state ? WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED + }; + wlr_signal_emit_safe(&wlr_dev->pointer->events.button, &event); +} + +static void virtual_pointer_axis(struct wl_client *client, + struct wl_resource *resource, uint32_t time, uint32_t axis, + wl_fixed_t value) { + if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + wl_resource_post_error(resource, + ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, + "Invalid enumeration value %d", axis); + return; + } + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer == NULL) { + return; + } + struct wlr_input_device *wlr_dev = &pointer->input_device; + pointer->axis_valid[axis] = true; + pointer->axis_event[pointer->axis].device = wlr_dev; + pointer->axis_event[pointer->axis].time_msec = time; + pointer->axis_event[pointer->axis].orientation = axis; + pointer->axis_event[pointer->axis].delta = wl_fixed_to_double(value); +} + +static void virtual_pointer_frame(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer == NULL) { + return; + } + struct wlr_input_device *wlr_dev = &pointer->input_device; + + for (size_t i = 0; + i < sizeof(pointer->axis_valid) / sizeof(pointer->axis_valid[0]); + ++i) { + if (pointer->axis_valid[i]) { + /* Deliver pending axis event */ + wlr_signal_emit_safe(&wlr_dev->pointer->events.axis, + &pointer->axis_event[i]); + memset(&pointer->axis_event[i], 0, sizeof(pointer->axis_event[i])); + pointer->axis_valid[i] = false; + } + } + + wlr_signal_emit_safe(&wlr_dev->pointer->events.frame, wlr_dev->pointer); +} + +static void virtual_pointer_axis_source(struct wl_client *client, + struct wl_resource *resource, uint32_t source) { + if (source > WL_POINTER_AXIS_SOURCE_WHEEL_TILT) { + wl_resource_post_error(resource, + ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS_SOURCE, + "Invalid enumeration value %d", source); + return; + } + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer == NULL) { + return; + } + struct wlr_input_device *wlr_dev = &pointer->input_device; + pointer->axis_event[pointer->axis].device = wlr_dev; + pointer->axis_event[pointer->axis].source = source; +} + +static void virtual_pointer_axis_stop(struct wl_client *client, + struct wl_resource *resource, uint32_t time, uint32_t axis) { + if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + wl_resource_post_error(resource, + ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, + "Invalid enumeration value %d", axis); + return; + } + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer == NULL) { + return; + } + struct wlr_input_device *wlr_dev = &pointer->input_device; + pointer->axis = axis; + pointer->axis_valid[pointer->axis] = true; + pointer->axis_event[pointer->axis].device = wlr_dev; + pointer->axis_event[pointer->axis].time_msec = time; + pointer->axis_event[pointer->axis].orientation = axis; + pointer->axis_event[pointer->axis].delta = 0; + pointer->axis_event[pointer->axis].delta_discrete = 0; +} + +static void virtual_pointer_axis_discrete(struct wl_client *client, + struct wl_resource *resource, uint32_t time, uint32_t axis, + wl_fixed_t value, int32_t discrete) { + if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + wl_resource_post_error(resource, + ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, + "Invalid enumeration value %d", axis); + return; + } + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer == NULL) { + return; + } + struct wlr_input_device *wlr_dev = &pointer->input_device; + pointer->axis = axis; + pointer->axis_valid[pointer->axis] = true; + pointer->axis_event[pointer->axis].device = wlr_dev; + pointer->axis_event[pointer->axis].time_msec = time; + pointer->axis_event[pointer->axis].orientation = axis; + pointer->axis_event[pointer->axis].delta_discrete = discrete; +} + +static void virtual_pointer_destroy_resource(struct wl_resource *resource) { + struct wlr_virtual_pointer_v1 *pointer = + virtual_pointer_from_resource(resource); + if (pointer != NULL) { + wlr_input_device_destroy(&pointer->input_device); + } +} + +static void virtual_pointer_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwlr_virtual_pointer_v1_interface virtual_pointer_impl = { + .motion = virtual_pointer_motion, + .motion_absolute = virtual_pointer_motion_absolute, + .button = virtual_pointer_button, + .axis = virtual_pointer_axis, + .frame = virtual_pointer_frame, + .axis_source = virtual_pointer_axis_source, + .axis_stop = virtual_pointer_axis_stop, + .axis_discrete = virtual_pointer_axis_discrete, + .destroy = virtual_pointer_destroy, +}; + +static const struct zwlr_virtual_pointer_manager_v1_interface manager_impl; + +static struct wlr_virtual_pointer_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_virtual_pointer_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void virtual_pointer_manager_create_virtual_pointer( + struct wl_client *client, struct wl_resource *resource, + struct wl_resource *seat, uint32_t id) { + struct wlr_virtual_pointer_manager_v1 *manager = manager_from_resource(resource); + + struct wlr_virtual_pointer_v1 *virtual_pointer = calloc(1, + sizeof(struct wlr_virtual_pointer_v1)); + if (!virtual_pointer) { + wl_client_post_no_memory(client); + return; + } + + struct wlr_pointer *pointer = calloc(1, sizeof(struct wlr_pointer)); + if (!pointer) { + wlr_log(WLR_ERROR, "Cannot allocate wlr_pointer"); + free(virtual_pointer); + wl_client_post_no_memory(client); + return; + } + wlr_pointer_init(pointer, NULL); + + struct wl_resource *pointer_resource = wl_resource_create(client, + &zwlr_virtual_pointer_v1_interface, wl_resource_get_version(resource), + id); + if (!pointer_resource) { + free(pointer); + free(virtual_pointer); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(pointer_resource, &virtual_pointer_impl, + virtual_pointer, virtual_pointer_destroy_resource); + + wlr_input_device_init(&virtual_pointer->input_device, + WLR_INPUT_DEVICE_POINTER, &input_device_impl, "virtual pointer", + 0x0, 0x0); + + struct wlr_virtual_pointer_v1_new_pointer_event event = { + .new_pointer = virtual_pointer, + }; + + if (seat) { + struct wlr_seat_client *seat_client = + wlr_seat_client_from_resource(seat); + event.suggested_seat = seat_client->seat; + } + + virtual_pointer->input_device.pointer = pointer; + virtual_pointer->resource = pointer_resource; + wl_signal_init(&virtual_pointer->events.destroy); + + wl_list_insert(&manager->virtual_pointers, &virtual_pointer->link); + wlr_signal_emit_safe(&manager->events.new_virtual_pointer, &event); +} + +static void virtual_pointer_manager_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwlr_virtual_pointer_manager_v1_interface manager_impl = { + .create_virtual_pointer = virtual_pointer_manager_create_virtual_pointer, + .destroy = virtual_pointer_manager_destroy, +}; + +static void virtual_pointer_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_virtual_pointer_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &zwlr_virtual_pointer_manager_v1_interface, version, id); + + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &manager_impl, manager, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_virtual_pointer_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wlr_signal_emit_safe(&manager->events.destroy, manager); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + struct wlr_virtual_pointer_v1 *pointer, *pointer_tmp; + wl_list_for_each_safe(pointer, pointer_tmp, + &manager->virtual_pointers, link) { + wl_resource_destroy(pointer->resource); + } + free(manager); +} + +struct wlr_virtual_pointer_manager_v1* wlr_virtual_pointer_manager_v1_create( + struct wl_display *display) { + struct wlr_virtual_pointer_manager_v1 *manager = calloc(1, + sizeof(struct wlr_virtual_pointer_manager_v1)); + if (!manager) { + return NULL; + } + + wl_list_init(&manager->virtual_pointers); + + wl_signal_init(&manager->events.new_virtual_pointer); + wl_signal_init(&manager->events.destroy); + manager->global = wl_global_create(display, + &zwlr_virtual_pointer_manager_v1_interface, 1, manager, + virtual_pointer_manager_bind); + if (!manager->global) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + return manager; +} |