From a5bfdeb0d8847f2674a2f86eecc03cdf858a3303 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sun, 24 Nov 2024 04:53:37 +0100 Subject: [PATCH] [wip] implement cosmic-keymap-layout protocol Test implementation of https://github.com/pop-os/cosmic-protocols/pull/28 WARNING: Do not use this outside of protocol evaluation. The protocol might change which will cause the wayland connection to disconnect clients due to protocol mismatch. --- include/protocols/cosmic-keymap-layout.h | 25 ++ protocols/cosmic-keymap-unstable-v1.xml | 75 ++++++ protocols/meson.build | 1 + .../cosmic-keymap-layout.c | 255 ++++++++++++++++++ .../cosmic-keymap-layout/meson.build | 3 + src/protocols/meson.build | 1 + 6 files changed, 360 insertions(+) create mode 100644 include/protocols/cosmic-keymap-layout.h create mode 100644 protocols/cosmic-keymap-unstable-v1.xml create mode 100644 src/protocols/cosmic-keymap-layout/cosmic-keymap-layout.c create mode 100644 src/protocols/cosmic-keymap-layout/meson.build diff --git a/include/protocols/cosmic-keymap-layout.h b/include/protocols/cosmic-keymap-layout.h new file mode 100644 index 000000000..39546b2a6 --- /dev/null +++ b/include/protocols/cosmic-keymap-layout.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_PROTOCOLS_COSMIC_KEYMAP_LAYOUT_H +#define LABWC_PROTOCOLS_COSMIC_KEYMAP_LAYOUT_H + +#include + +struct lab_cosmic_keymap_layout_manager { + struct { + struct wl_signal request_layout; + } events; + + /* Private */ + struct wl_global *global; + struct { + struct wl_listener display_destroy; + } on; + struct wl_list keymap_listeners; + struct wl_list manager_resources; +}; + +struct lab_cosmic_keymap_layout_manager *lab_cosmic_keymap_layout_manager_create( + struct wl_display *display, uint32_t version); + +#endif /* LABWC_PROTOCOLS_COSMIC_KEYMAP_LAYOUT_H */ + diff --git a/protocols/cosmic-keymap-unstable-v1.xml b/protocols/cosmic-keymap-unstable-v1.xml new file mode 100644 index 000000000..b1885d207 --- /dev/null +++ b/protocols/cosmic-keymap-unstable-v1.xml @@ -0,0 +1,75 @@ + + + + Copyright © 2024 System76, Inc + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + TODO + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/protocols/meson.build b/protocols/meson.build index 711a9ec1a..d9b917446 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -21,6 +21,7 @@ server_protocols = [ wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', + 'cosmic-keymap-unstable-v1.xml', 'cosmic-workspace-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'wlr-input-inhibitor-unstable-v1.xml', diff --git a/src/protocols/cosmic-keymap-layout/cosmic-keymap-layout.c b/src/protocols/cosmic-keymap-layout/cosmic-keymap-layout.c new file mode 100644 index 000000000..2ba6e4f1d --- /dev/null +++ b/src/protocols/cosmic-keymap-layout/cosmic-keymap-layout.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include "common/mem.h" +#include "common/list.h" +#include "protocols/cosmic-keymap-layout.h" +#include "cosmic-keymap-unstable-v1-protocol.h" + +#define LAB_COSMIC_KEYMAP_LAYOUT_VERSION 1 + +struct keymap_listener { + struct lab_cosmic_keymap_layout_manager *manager; + struct wlr_keyboard *kb; + uint32_t group; + struct { + struct wl_listener modifiers; + struct wl_listener destroy; + } on_kb; + struct wl_list link; + struct wl_list keymap_resources; +}; + +/* Keymap listeners */ +static void +keymap_listener_destroy(struct keymap_listener *listener) +{ + wl_list_remove(&listener->on_kb.modifiers.link); + wl_list_remove(&listener->on_kb.destroy.link); + wl_list_remove(&listener->link); + + struct wl_resource *res, *tmp; + wl_resource_for_each_safe(res, tmp, &listener->keymap_resources) { + wl_resource_set_user_data(res, NULL); + wl_resource_destroy(res); + } + free(listener); +} + +static void +kb_handle_destroy(struct wl_listener *listener, void *data) +{ + struct keymap_listener *keymap_listener = + wl_container_of(listener, keymap_listener, on_kb.destroy); + + keymap_listener_destroy(keymap_listener); +} + +static void +kb_handle_modifier(struct wl_listener *listener, void *data) +{ + struct keymap_listener *keymap_listener = + wl_container_of(listener, keymap_listener, on_kb.modifiers); + uint32_t group = keymap_listener->kb->modifiers.group; + + //FIXME: something weird going on here, on a non-group modifier + // press modifiers.group is reset to 0 for whatever reason + // likely related to running nested and the external wayland + // modifier events just getting passed through. + + if (keymap_listener->group == group) { + return; + } + keymap_listener->group = group; + + struct wl_resource *res; + wl_resource_for_each(res, &keymap_listener->keymap_resources) { + zcosmic_keymap_v1_send_group(res, group); + } +} + +static struct keymap_listener * +keymap_listener_create(struct lab_cosmic_keymap_layout_manager *manager, struct wlr_keyboard *kb) +{ + struct keymap_listener *listener = znew(*listener); + listener->kb = kb; + listener->manager = manager; + listener->group = kb->modifiers.group; + wl_list_init(&listener->keymap_resources); + + listener->on_kb.modifiers.notify = kb_handle_modifier; + wl_signal_add(&kb->events.modifiers, &listener->on_kb.modifiers); + + listener->on_kb.destroy.notify = kb_handle_destroy; + wl_signal_add(&kb->base.events.destroy, &listener->on_kb.destroy); + + wl_list_append(&manager->keymap_listeners, &listener->link); + + return listener; +} + + +/* Keymap handlers */ +static void +keymap_handle_set_group(struct wl_client *client, struct wl_resource *resource, uint32_t group) +{ + struct keymap_listener *listener = wl_resource_get_user_data(resource); + assert(listener); + + wl_signal_emit_mutable(&listener->manager->events.request_layout, &group); +} + +static void +keymap_handle_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zcosmic_keymap_v1_interface keymap_impl = { + .set_group = keymap_handle_set_group, + .destroy = keymap_handle_destroy, +}; + +static void +keymap_instance_resource_destroy(struct wl_resource *resource) +{ + wl_list_remove(wl_resource_get_link(resource)); + + struct keymap_listener *listener = wl_resource_get_user_data(resource); + if (listener && wl_list_empty(&listener->keymap_resources)) { + keymap_listener_destroy(listener); + } +} + +/* Manager handlers */ +static void +manager_handle_get_keymap(struct wl_client *client, struct wl_resource *resource, + uint32_t keymap, struct wl_resource *keyboard) +{ + struct lab_cosmic_keymap_layout_manager *manager = + wl_resource_get_user_data(resource); + assert(manager); + + struct wlr_seat_client *seat_client = wl_resource_get_user_data(keyboard); + if (!seat_client) { + wlr_log(WLR_ERROR, "failed to find seat_client"); + return; + } + struct wlr_keyboard *kb = seat_client->seat->keyboard_state.keyboard; + if (!kb) { + wlr_log(WLR_ERROR, "failed to find keyboard"); + return; + } + uint32_t version = wl_resource_get_version(resource); + struct wl_resource *keymap_resource = + wl_resource_create(client, &zcosmic_keymap_v1_interface, version, keymap); + if (!resource) { + wlr_log(WLR_ERROR, "failed to create keymap resource"); + wl_client_post_no_memory(client); + return; + } + + struct keymap_listener *listener = NULL; + + struct keymap_listener *kl; + wl_list_for_each(kl, &manager->keymap_listeners, link) { + if (kl->kb == kb) { + listener = kl; + break; + } + } + if (!listener) { + listener = keymap_listener_create(manager, kb); + } + + wl_resource_set_implementation(keymap_resource, &keymap_impl, listener, + keymap_instance_resource_destroy); + + wl_list_insert(&listener->keymap_resources, wl_resource_get_link(keymap_resource)); + + zcosmic_keymap_v1_send_group(keymap_resource, listener->group); +} + +static void +manager_handle_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zcosmic_keymap_manager_v1_interface manager_impl = { + .get_keymap = manager_handle_get_keymap, + .destroy = manager_handle_destroy, +}; + +static void +manager_instance_resource_destroy(struct wl_resource *resource) +{ + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +manager_handle_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + struct lab_cosmic_keymap_layout_manager *manager = data; + struct wl_resource *resource = wl_resource_create(client, + &zcosmic_keymap_manager_v1_interface, + version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &manager_impl, + manager, manager_instance_resource_destroy); + + wl_list_insert(&manager->manager_resources, wl_resource_get_link(resource)); +} + +static void +manager_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct lab_cosmic_keymap_layout_manager *manager = + wl_container_of(listener, manager, on.display_destroy); + + wl_global_destroy(manager->global); + + struct keymap_listener *kl, *kl_tmp; + wl_list_for_each_safe(kl, kl_tmp, &manager->keymap_listeners, link) { + keymap_listener_destroy(kl); + } + + struct wl_resource *resource, *res_tmp; + wl_resource_for_each_safe(resource, res_tmp, &manager->manager_resources) { + wl_resource_destroy(resource); + } + + wl_list_remove(&manager->on.display_destroy.link); + free(manager); +} + +/* Public API */ +struct lab_cosmic_keymap_layout_manager * +lab_cosmic_keymap_layout_manager_create(struct wl_display *display, uint32_t version) +{ + assert(version <= LAB_COSMIC_KEYMAP_LAYOUT_VERSION); + struct lab_cosmic_keymap_layout_manager *manager = znew(*manager); + + manager->global = wl_global_create(display, + &zcosmic_keymap_manager_v1_interface, + version, manager, manager_handle_bind); + + if (!manager->global) { + free(manager); + return NULL; + } + + manager->on.display_destroy.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->on.display_destroy); + + wl_signal_init(&manager->events.request_layout); + wl_list_init(&manager->manager_resources); + wl_list_init(&manager->keymap_listeners); + return manager; +} diff --git a/src/protocols/cosmic-keymap-layout/meson.build b/src/protocols/cosmic-keymap-layout/meson.build new file mode 100644 index 000000000..0b8ac4b75 --- /dev/null +++ b/src/protocols/cosmic-keymap-layout/meson.build @@ -0,0 +1,3 @@ +labwc_sources += files( + 'cosmic-keymap-layout.c', +) diff --git a/src/protocols/meson.build b/src/protocols/meson.build index d4412119b..4a54185aa 100644 --- a/src/protocols/meson.build +++ b/src/protocols/meson.build @@ -1 +1,2 @@ subdir('cosmic_workspaces') +subdir('cosmic-keymap-layout')