diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index 83ff2ea81..bb7d2084d 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ struct roots_desktop { struct wlr_input_inhibit_manager *input_inhibit; struct wlr_linux_dmabuf *linux_dmabuf; struct wlr_layer_shell *layer_shell; + struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; struct wl_listener new_output; struct wl_listener layout_change; @@ -61,6 +63,7 @@ struct roots_desktop { struct wl_listener decoration_new; struct wl_listener input_inhibit_activate; struct wl_listener input_inhibit_deactivate; + struct wl_listener virtual_keyboard_new; #ifdef WLR_HAS_XWAYLAND struct wlr_xwayland *xwayland; diff --git a/include/rootston/virtual_keyboard.h b/include/rootston/virtual_keyboard.h new file mode 100644 index 000000000..613e55959 --- /dev/null +++ b/include/rootston/virtual_keyboard.h @@ -0,0 +1,7 @@ +#ifndef ROOTSTON_VIRTUAL_KEYBOARD_H +#define ROOTSTON_VIRTUAL_KEYBOARD_H + +#include + +void handle_virtual_keyboard(struct wl_listener *listener, void *data); +#endif diff --git a/include/wlr/types/wlr_virtual_keyboard_v1.h b/include/wlr/types/wlr_virtual_keyboard_v1.h new file mode 100644 index 000000000..1236ae249 --- /dev/null +++ b/include/wlr/types/wlr_virtual_keyboard_v1.h @@ -0,0 +1,37 @@ +#ifndef WLR_TYPES_WLR_VIRTUAL_KEYBOARD_V1_H +#define WLR_TYPES_WLR_VIRTUAL_KEYBOARD_V1_H + +#include +#include +#include + +struct wlr_virtual_keyboard_manager_v1 { + struct wl_global *global; + struct wl_list resources; // struct wl_resource* + struct wl_list virtual_keyboards; // struct wlr_virtual_keyboard_v1* + + struct wl_listener display_destroy; + + struct { + struct wl_signal new_virtual_keyboard; // struct wlr_virtual_keyboard_v1* + } events; +}; + +struct wlr_virtual_keyboard_v1 { + struct wl_resource *resource; + struct wlr_input_device input_device; + struct wlr_seat *seat; + + struct wl_list link; + + struct { + struct wl_signal destroy; // struct wlr_virtual_keyboard_v1* + } events; +}; + +struct wlr_virtual_keyboard_manager_v1* wlr_virtual_keyboard_manager_v1_create( + struct wl_display *display); +void wlr_virtual_keyboard_manager_v1_destroy( + struct wlr_virtual_keyboard_manager_v1 *manager); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 4730baf72..8fa64ca92 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -38,6 +38,7 @@ protocols = [ 'idle.xml', 'screenshooter.xml', 'server-decoration.xml', + 'virtual-keyboard-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'wlr-input-inhibitor-unstable-v1.xml', ] diff --git a/protocol/virtual-keyboard-unstable-v1.xml b/protocol/virtual-keyboard-unstable-v1.xml new file mode 100644 index 000000000..5095c91b8 --- /dev/null +++ b/protocol/virtual-keyboard-unstable-v1.xml @@ -0,0 +1,113 @@ + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + + + + + + A virtual keyboard manager allows an application to provide keyboard + input events as if they came from a physical 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. + + + + + + diff --git a/rootston/desktop.c b/rootston/desktop.c index 178a975a1..a6f9e9a07 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -26,6 +26,7 @@ #include "rootston/seat.h" #include "rootston/server.h" #include "rootston/view.h" +#include "rootston/virtual_keyboard.h" #include "rootston/xcursor.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -865,6 +866,12 @@ struct roots_desktop *desktop_create(struct roots_server *server, desktop->linux_dmabuf = wlr_linux_dmabuf_create(server->wl_display, server->renderer); + + desktop->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create( + server->wl_display); + wl_signal_add(&desktop->virtual_keyboard->events.new_virtual_keyboard, + &desktop->virtual_keyboard_new); + desktop->virtual_keyboard_new.notify = handle_virtual_keyboard; return desktop; } diff --git a/rootston/meson.build b/rootston/meson.build index 53a4635de..8ab872b59 100644 --- a/rootston/meson.build +++ b/rootston/meson.build @@ -9,6 +9,7 @@ sources = [ 'main.c', 'output.c', 'seat.c', + 'virtual_keyboard.c', 'wl_shell.c', 'xdg_shell_v6.c', 'xdg_shell.c', diff --git a/rootston/virtual_keyboard.c b/rootston/virtual_keyboard.c new file mode 100644 index 000000000..db47efca0 --- /dev/null +++ b/rootston/virtual_keyboard.c @@ -0,0 +1,21 @@ +#define _POSIX_C_SOURCE 199309L + +#include +#include +#include "rootston/virtual_keyboard.h" +#include "rootston/seat.h" + +void handle_virtual_keyboard(struct wl_listener *listener, void *data) { + struct roots_desktop *desktop = + wl_container_of(listener, desktop, virtual_keyboard_new); + struct wlr_virtual_keyboard_v1 *keyboard = data; + + struct roots_seat *seat = input_seat_from_wlr_seat(desktop->server->input, + keyboard->seat); + if (!seat) { + wlr_log(L_ERROR, "could not find roots seat"); + return; + } + + roots_seat_add_device(seat, &keyboard->input_device); +} diff --git a/types/meson.build b/types/meson.build index 243461607..f9f5b469b 100644 --- a/types/meson.build +++ b/types/meson.build @@ -44,6 +44,7 @@ lib_wlr_types = static_library( 'wlr_tablet_pad.c', 'wlr_tablet_tool.c', 'wlr_touch.c', + 'wlr_virtual_keyboard_v1.c', 'wlr_wl_shell.c', 'wlr_xcursor_manager.c', 'wlr_xdg_output.c', diff --git a/types/wlr_virtual_keyboard_v1.c b/types/wlr_virtual_keyboard_v1.c new file mode 100644 index 000000000..0673f394a --- /dev/null +++ b/types/wlr_virtual_keyboard_v1.c @@ -0,0 +1,236 @@ +#define _POSIX_C_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include "util/signal.h" +#include "virtual-keyboard-unstable-v1-protocol.h" + + +static void keyboard_led_update(struct wlr_keyboard *wlr_kb, uint32_t leds) { + // unsupported by virtual keyboard protocol +} + +static void keyboard_destroy(struct wlr_keyboard *wlr_kb) { + // safe to ignore - keyboard will be destroyed only iff associated virtual + // keyboard is torn down, no need to tear down the keyboard separately +} + +static const struct wlr_keyboard_impl keyboard_impl = { + .destroy = keyboard_destroy, + .led_update = keyboard_led_update +}; + +static void input_device_destroy(struct wlr_input_device *dev) { +} + +static const struct wlr_input_device_impl input_device_impl = { + .destroy = input_device_destroy +}; + +static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_impl; + +static struct wlr_virtual_keyboard_v1 *virtual_keyboard_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwp_virtual_keyboard_v1_interface, &virtual_keyboard_impl)); + return wl_resource_get_user_data(resource); +} + +static void virtual_keyboard_keymap(struct wl_client *client, + struct wl_resource *resource, uint32_t format, int32_t fd, + uint32_t size) { + struct wlr_virtual_keyboard_v1 *keyboard = + virtual_keyboard_from_resource(resource); + + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!context) { + goto context_fail; + } + void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (!data) { + goto fd_fail; + } + struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, data, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(data, size); + if (!keymap) { + goto keymap_fail; + } + wlr_keyboard_set_keymap(keyboard->input_device.keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + return; +keymap_fail: +fd_fail: + xkb_context_unref(context); +context_fail: + wl_client_post_no_memory(client); +} + +static void virtual_keyboard_key(struct wl_client *client, + struct wl_resource *resource, uint32_t time, uint32_t key, + uint32_t state) { + struct wlr_virtual_keyboard_v1 *keyboard = + virtual_keyboard_from_resource(resource); + struct wlr_event_keyboard_key event = { + .time_msec = time, + .keycode = key, + .update_state = false, + .state = state, + }; + wlr_keyboard_notify_key(keyboard->input_device.keyboard, &event); +} + +static void virtual_keyboard_modifiers(struct wl_client *client, + struct wl_resource *resource, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { + struct wlr_virtual_keyboard_v1 *keyboard = + virtual_keyboard_from_resource(resource); + wlr_keyboard_notify_modifiers(keyboard->input_device.keyboard, + mods_depressed, mods_latched, mods_locked, group); +} + +static void virtual_keyboard_destroy_resource(struct wl_resource *resource) { + struct wlr_virtual_keyboard_v1 *keyboard = + virtual_keyboard_from_resource(resource); + wlr_signal_emit_safe(&keyboard->events.destroy, keyboard); + wl_list_remove(&keyboard->link); + wlr_keyboard_destroy(keyboard->input_device.keyboard); + free(keyboard); +} + +static void virtual_keyboard_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_impl = { + .keymap = virtual_keyboard_keymap, + .key = virtual_keyboard_key, + .modifiers = virtual_keyboard_modifiers, + .destroy = virtual_keyboard_destroy, +}; + +static const struct zwp_virtual_keyboard_manager_v1_interface manager_impl; + +static struct wlr_virtual_keyboard_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwp_virtual_keyboard_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void virtual_keyboard_manager_create_virtual_keyboard( + struct wl_client *client, struct wl_resource *resource, + struct wl_resource *seat, uint32_t id) { + struct wlr_virtual_keyboard_manager_v1 *manager = + manager_from_resource(resource); + + struct wlr_virtual_keyboard_v1 *virtual_keyboard = calloc(1, + sizeof(struct wlr_virtual_keyboard_v1)); + if (!virtual_keyboard) { + wl_client_post_no_memory(client); + return; + } + + struct wlr_keyboard* keyboard = calloc(1, sizeof(struct wlr_keyboard)); + if (!keyboard) { + wlr_log(L_ERROR, "Cannot allocate wlr_keyboard"); + free(virtual_keyboard); + wl_client_post_no_memory(client); + return; + } + wlr_keyboard_init(keyboard, &keyboard_impl); + + struct wl_resource *keyboard_resource = wl_resource_create(client, + &zwp_virtual_keyboard_v1_interface, wl_resource_get_version(resource), + id); + if (!keyboard_resource) { + free(keyboard); + free(virtual_keyboard); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(keyboard_resource, &virtual_keyboard_impl, + virtual_keyboard, virtual_keyboard_destroy_resource); + + wlr_input_device_init(&virtual_keyboard->input_device, + WLR_INPUT_DEVICE_KEYBOARD, &input_device_impl, "virtual keyboard", + 0x0, 0x0); + + struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat); + + virtual_keyboard->input_device.keyboard = keyboard; + virtual_keyboard->resource = keyboard_resource; + virtual_keyboard->seat = seat_client->seat; + wl_signal_init(&virtual_keyboard->events.destroy); + + wl_list_insert(&manager->virtual_keyboards, &virtual_keyboard->link); + + wlr_signal_emit_safe(&manager->events.new_virtual_keyboard, + virtual_keyboard); +} + +static const struct zwp_virtual_keyboard_manager_v1_interface manager_impl = { + .create_virtual_keyboard = virtual_keyboard_manager_create_virtual_keyboard, +}; + +static void handle_manager_unbind(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void virtual_keyboard_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_virtual_keyboard_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &zwp_virtual_keyboard_manager_v1_interface, version, id); + + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &manager_impl, manager, + handle_manager_unbind); + wl_list_insert(&manager->resources, wl_resource_get_link(resource)); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_virtual_keyboard_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wlr_virtual_keyboard_manager_v1_destroy(manager); +} + +struct wlr_virtual_keyboard_manager_v1* + wlr_virtual_keyboard_manager_v1_create( + struct wl_display *display) { + struct wlr_virtual_keyboard_manager_v1 *manager = calloc(1, + sizeof(struct wlr_virtual_keyboard_manager_v1)); + if (!manager) { + return NULL; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + wl_list_init(&manager->resources); + wl_list_init(&manager->virtual_keyboards); + + wl_signal_init(&manager->events.new_virtual_keyboard); + manager->global = wl_global_create(display, + &zwp_virtual_keyboard_manager_v1_interface, 1, manager, + virtual_keyboard_manager_bind); + return manager; +} + +void wlr_virtual_keyboard_manager_v1_destroy( + struct wlr_virtual_keyboard_manager_v1 *manager) { + wl_global_destroy(manager->global); + free(manager); +}