From 8f538472afe5da4bebe2cbc51c63615f2c790fa0 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Tue, 31 Oct 2023 16:01:55 +0800 Subject: [PATCH 01/20] implement input method keyboard grab fixes #1160. --- src/core/seat/input-method-relay.cpp | 69 +++++++++++++++++++++++++++- src/core/seat/input-method-relay.hpp | 8 +++- src/core/seat/keyboard.cpp | 19 ++++++-- src/core/seat/keyboard.hpp | 2 +- 4 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index a4561403d..1b16a8c2d 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -26,9 +26,11 @@ wf::input_method_relay::input_method_relay() return; } + LOGI("new input method connected"); input_method = new_input_method; on_input_method_commit.connect(&input_method->events.commit); on_input_method_destroy.connect(&input_method->events.destroy); + on_grab_keyboard.connect(&input_method->events.grab_keyboard); auto *text_input = find_focusable_text_input(); if (text_input) @@ -83,7 +85,10 @@ wf::input_method_relay::input_method_relay() on_input_method_commit.disconnect(); on_input_method_destroy.disconnect(); - input_method = nullptr; + on_grab_keyboard.disconnect(); + on_grab_keyboard_destroy.disconnect(); + input_method = nullptr; + keyboard_grab = nullptr; auto *text_input = find_focused_text_input(); if (text_input != nullptr) @@ -96,6 +101,24 @@ wf::input_method_relay::input_method_relay() } }); + on_grab_keyboard.set_callback([&] (void *data) + { + if (keyboard_grab != nullptr) + { + LOGI("Attempted to grab input method keyboard twice"); + return; + } + + keyboard_grab = static_cast(data); + on_grab_keyboard_destroy.connect(&keyboard_grab->events.destroy); + }); + + on_grab_keyboard_destroy.set_callback([&] (void *data) + { + on_grab_keyboard_destroy.disconnect(); + keyboard_grab = nullptr; + }); + on_text_input_new.connect(&wf::get_core().protocols.text_input->events.text_input); on_input_method_new.connect(&wf::get_core().protocols.input_method->events.input_method); wf::get_core().connect(&keyboard_focus_changed); @@ -141,6 +164,50 @@ void wf::input_method_relay::remove_text_input(wlr_text_input_v3 *input) text_inputs.erase(it, text_inputs.end()); } +bool wf::input_method_relay::should_grab(wlr_keyboard *kbd) +{ + if (keyboard_grab == nullptr) + { + return false; + } + + // input method sends key via a virtual keyboard + struct wlr_virtual_keyboard_v1 *virtual_keyboard = wlr_input_device_get_virtual_keyboard(&kbd->base); + if (virtual_keyboard && + (wl_resource_get_client(virtual_keyboard->resource) == + wl_resource_get_client(input_method->keyboard_grab->resource))) + { + return false; + } + + return true; +} + +bool wf::input_method_relay::handle_key(struct wlr_keyboard *kbd, uint32_t time, uint32_t key, + uint32_t state) +{ + if (!should_grab(kbd)) + { + return false; + } + + wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, kbd); + wlr_input_method_keyboard_grab_v2_send_key(keyboard_grab, time, key, state); + return true; +} + +bool wf::input_method_relay::handle_modifier(struct wlr_keyboard *kbd) +{ + if (!should_grab(kbd)) + { + return false; + } + + wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, kbd); + wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, &kbd->modifiers); + return true; +} + wf::text_input*wf::input_method_relay::find_focusable_text_input() { auto it = std::find_if(text_inputs.begin(), text_inputs.end(), diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 07d948185..02f3def4a 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -17,7 +17,9 @@ class input_method_relay private: wf::wl_listener_wrapper on_text_input_new, - on_input_method_new, on_input_method_commit, on_input_method_destroy; + on_input_method_new, on_input_method_commit, on_input_method_destroy, + on_grab_keyboard, on_grab_keyboard_destroy; + wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; text_input *find_focusable_text_input(); text_input *find_focused_text_input(); void set_focus(wlr_surface*); @@ -34,6 +36,8 @@ class input_method_relay } }; + bool should_grab(wlr_keyboard*); + public: wlr_input_method_v2 *input_method = nullptr; @@ -43,6 +47,8 @@ class input_method_relay void send_im_state(wlr_text_input_v3*); void disable_text_input(wlr_text_input_v3*); void remove_text_input(wlr_text_input_v3*); + bool handle_key(struct wlr_keyboard*, uint32_t, uint32_t, uint32_t); + bool handle_modifier(struct wlr_keyboard*); ~input_method_relay(); }; diff --git a/src/core/seat/keyboard.cpp b/src/core/seat/keyboard.cpp index 5f91ea3bd..fd4f7d951 100644 --- a/src/core/seat/keyboard.cpp +++ b/src/core/seat/keyboard.cpp @@ -12,6 +12,7 @@ #include "cursor.hpp" #include "touch.hpp" #include "input-manager.hpp" +#include "input-method-relay.hpp" #include "wayfire/compositor-view.hpp" #include "wayfire/signal-definitions.hpp" @@ -37,7 +38,8 @@ void wf::keyboard_t::setup_listeners() } seat->priv->set_keyboard(this); - if (!handle_keyboard_key(ev->keycode, ev->state) && (mode == input_event_processing_mode_t::FULL)) + if (!handle_keyboard_key(ev->time_msec, ev->keycode, + ev->state) && (mode == input_event_processing_mode_t::FULL)) { if (ev->state == WL_KEYBOARD_KEY_STATE_PRESSED) { @@ -71,8 +73,12 @@ void wf::keyboard_t::setup_listeners() auto kbd = static_cast(data); auto seat = wf::get_core().get_current_seat(); - wlr_seat_set_keyboard(seat, kbd); - wlr_seat_keyboard_send_modifiers(seat, &kbd->modifiers); + if (!wf::get_core_impl().im_relay->handle_modifier(kbd)) + { + wlr_seat_set_keyboard(seat, kbd); + wlr_seat_keyboard_send_modifiers(seat, &kbd->modifiers); + } + wf::get_core().seat->notify_activity(); }); @@ -281,7 +287,7 @@ bool wf::keyboard_t::has_only_modifiers() return true; } -bool wf::keyboard_t::handle_keyboard_key(uint32_t key, uint32_t state) +bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t state) { using namespace std::chrono; @@ -335,5 +341,10 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t key, uint32_t state) mod_binding_key = 0; } + if (!handled_in_plugin) + { + handled_in_plugin |= wf::get_core_impl().im_relay->handle_key(handle, time, key, state); + } + return handled_in_plugin; } diff --git a/src/core/seat/keyboard.hpp b/src/core/seat/keyboard.hpp index 9e4c4fda1..e982148b0 100644 --- a/src/core/seat/keyboard.hpp +++ b/src/core/seat/keyboard.hpp @@ -50,7 +50,7 @@ class keyboard_t std::chrono::steady_clock::time_point mod_binding_start; - bool handle_keyboard_key(uint32_t key, uint32_t state); + bool handle_keyboard_key(uint32_t time, uint32_t key, uint32_t state); /** Get the current locked mods */ uint32_t get_locked_mods(); From e5699cb0080e2f69d11a1cdda3d28abe8ce67152 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Sun, 19 Nov 2023 01:52:54 +0800 Subject: [PATCH 02/20] support input method popups --- src/core/seat/input-method-relay.cpp | 178 +++++++++++++++++++++++++++ src/core/seat/input-method-relay.hpp | 34 ++++- 2 files changed, 210 insertions(+), 2 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 1b16a8c2d..1c0abad92 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -2,6 +2,7 @@ #include "input-method-relay.hpp" #include "../core-impl.hpp" #include "../../output/output-impl.hpp" +#include "wayfire/scene-operations.hpp" #include @@ -31,6 +32,7 @@ wf::input_method_relay::input_method_relay() on_input_method_commit.connect(&input_method->events.commit); on_input_method_destroy.connect(&input_method->events.destroy); on_grab_keyboard.connect(&input_method->events.grab_keyboard); + on_new_popup_surface.connect(&input_method->events.new_popup_surface); auto *text_input = find_focusable_text_input(); if (text_input) @@ -87,6 +89,7 @@ wf::input_method_relay::input_method_relay() on_input_method_destroy.disconnect(); on_grab_keyboard.disconnect(); on_grab_keyboard_destroy.disconnect(); + on_new_popup_surface.disconnect(); input_method = nullptr; keyboard_grab = nullptr; @@ -119,6 +122,12 @@ wf::input_method_relay::input_method_relay() keyboard_grab = nullptr; }); + on_new_popup_surface.set_callback([&] (void *data) + { + auto popup = static_cast(data); + popup_surfaces.push_back(wf::popup_surface::create(this, popup)); + }); + on_text_input_new.connect(&wf::get_core().protocols.text_input->events.text_input); on_input_method_new.connect(&wf::get_core().protocols.input_method->events.input_method); wf::get_core().connect(&keyboard_focus_changed); @@ -164,6 +173,17 @@ void wf::input_method_relay::remove_text_input(wlr_text_input_v3 *input) text_inputs.erase(it, text_inputs.end()); } +void wf::input_method_relay::remove_popup_surface(wf::popup_surface *popup) +{ + auto it = std::remove_if(popup_surfaces.begin(), + popup_surfaces.end(), + [&] (const auto & suf) + { + return suf.get() == popup; + }); + popup_surfaces.erase(it, popup_surfaces.end()); +} + bool wf::input_method_relay::should_grab(wlr_keyboard *kbd) { if (keyboard_grab == nullptr) @@ -318,6 +338,11 @@ wf::text_input::text_input(wf::input_method_relay *rel, wlr_text_input_v3 *in) : return; } + for (auto popup : relay->popup_surfaces) + { + popup->update_geometry(); + } + relay->send_im_state(input); }); @@ -378,3 +403,156 @@ void wf::text_input::set_pending_focused_surface(wlr_surface *surface) wf::text_input::~text_input() {} + +wf::popup_surface::popup_surface(wf::input_method_relay *rel, wlr_input_popup_surface_v2 *in) : + relay(rel), surface(in) +{ + main_surface = std::make_shared(in->surface, true); + + on_destroy.set_callback([&] (void*) + { + on_map.disconnect(); + on_unmap.disconnect(); + on_destroy.disconnect(); + + relay->remove_popup_surface(this); + }); + + on_map.set_callback([&] (void*) { map(); }); + on_unmap.set_callback([&] (void*) { unmap(); }); + on_commit.set_callback([&] (void*) { update_geometry(); }); + + on_map.connect(&surface->surface->events.map); + on_unmap.connect(&surface->surface->events.unmap); + on_destroy.connect(&surface->events.destroy); +} + +std::shared_ptr wf::popup_surface::create( + wf::input_method_relay *rel, wlr_input_popup_surface_v2 *in) +{ + auto self = view_interface_t::create(rel, in); + auto translation_node = std::make_shared(); + translation_node->set_children_list({std::make_unique(in->surface, + false)}); + self->surface_root_node = translation_node; + self->set_surface_root_node(translation_node); + self->set_role(VIEW_ROLE_DESKTOP_ENVIRONMENT); + return self; +} + +void wf::popup_surface::map() +{ + auto text_input = this->relay->find_focused_text_input(); + if (!text_input) + { + LOGE("trying to map IM popup surface without text input."); + return; + } + + auto view = wf::wl_surface_to_wayfire_view(text_input->input->focused_surface->resource); + auto output = view->get_output(); + set_output(output); + + auto target_layer = wf::scene::layer::UNMANAGED; + wf::scene::readd_front(get_output()->node_for_layer(target_layer), get_root_node()); + + priv->set_mapped_surface_contents(main_surface); + priv->set_mapped(true); + on_commit.connect(&surface->surface->events.commit); + + update_geometry(); + + damage(); + emit_view_map(); +} + +void wf::popup_surface::unmap() +{ + damage(); + + priv->unset_mapped_surface_contents(); + + emit_view_unmap(); + priv->set_mapped(false); + on_commit.disconnect(); +} + +void wf::popup_surface::update_geometry() +{ + auto text_input = this->relay->find_focused_text_input(); + if (!text_input) + { + LOGI("no focused text input"); + return; + } + + if (!is_mapped()) + { + LOGI("input method window not mapped"); + return; + } + + bool cursor_rect = text_input->input->current.features & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; + auto cursor = text_input->input->current.cursor_rectangle; + int x = 0, y = 0; + if (cursor_rect) + { + x = cursor.x; + y = cursor.y + cursor.height; + } + + auto wlr_surface = text_input->input->focused_surface; + auto view = wf::wl_surface_to_wayfire_view(wlr_surface->resource); + auto toplevel = toplevel_cast(view); + auto g = toplevel->get_geometry(); + auto margins = toplevel->toplevel()->current().margins; + + auto xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); + if (xdg_surface) + { + // substract shadows etc; test app: d-feet + x -= xdg_surface->current.geometry.x; + y -= xdg_surface->current.geometry.y; + } + + damage(); + x += g.x + margins.left; + y += g.y + margins.top; + auto width = surface->surface->current.width; + auto height = surface->surface->current.height; + + auto output = view->get_output(); + auto g_output = output->get_layout_geometry(); + x = std::max(0, std::min(x, g_output.width - width)); + if (y + height > g_output.height) + { + y -= height; + if (cursor_rect) + { + y -= cursor.height; + } + } + + y = std::max(0, y); + + surface_root_node->set_offset({x, y}); + geometry.x = x; + geometry.y = y; + geometry.width = width; + geometry.height = height; + damage(); + wf::scene::update(get_surface_root_node(), wf::scene::update_flag::GEOMETRY); +} + +bool wf::popup_surface::is_mapped() const +{ + return priv->wsurface != nullptr; +} + +wf::geometry_t wf::popup_surface::get_geometry() +{ + return geometry; +} + +wf::popup_surface::~popup_surface() +{} diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 02f3def4a..99e771113 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -4,6 +4,7 @@ #include "wayfire/signal-definitions.hpp" #include "wayfire/view.hpp" #include +#include #include #include @@ -11,6 +12,7 @@ namespace wf { struct text_input; +struct popup_surface; class input_method_relay { @@ -18,10 +20,9 @@ class input_method_relay wf::wl_listener_wrapper on_text_input_new, on_input_method_new, on_input_method_commit, on_input_method_destroy, - on_grab_keyboard, on_grab_keyboard_destroy; + on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; text_input *find_focusable_text_input(); - text_input *find_focused_text_input(); void set_focus(wlr_surface*); wf::signal::connection_t keyboard_focus_changed = @@ -42,11 +43,14 @@ class input_method_relay wlr_input_method_v2 *input_method = nullptr; std::vector> text_inputs; + std::vector> popup_surfaces; input_method_relay(); void send_im_state(wlr_text_input_v3*); + text_input *find_focused_text_input(); void disable_text_input(wlr_text_input_v3*); void remove_text_input(wlr_text_input_v3*); + void remove_popup_surface(popup_surface*); bool handle_key(struct wlr_keyboard*, uint32_t, uint32_t, uint32_t); bool handle_modifier(struct wlr_keyboard*); ~input_method_relay(); @@ -67,4 +71,30 @@ struct text_input void set_pending_focused_surface(wlr_surface*); ~text_input(); }; + +struct popup_surface : public wf::view_interface_t +{ + input_method_relay *relay = nullptr; + wlr_input_popup_surface_v2 *surface = nullptr; + wf::wl_listener_wrapper on_destroy, on_map, on_unmap, on_commit; + + popup_surface(input_method_relay*, wlr_input_popup_surface_v2*); + static std::shared_ptr create(input_method_relay*, wlr_input_popup_surface_v2*); + bool is_mapped() const; + wf::geometry_t get_geometry(); + void map(); + void unmap(); + void update_geometry(); + ~popup_surface(); + + private: + wf::geometry_t geometry{0, 0, 0, 0}; + std::shared_ptr main_surface; + std::shared_ptr surface_root_node; + + virtual wlr_surface *get_keyboard_focus_surface() override + { + return nullptr; + } +}; } From eb4760ad8320828af33a4e799ce0131257ead105 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Fri, 24 Nov 2023 15:48:12 +0800 Subject: [PATCH 03/20] input method: avoid disabling a just-enabled text input on focus change --- src/core/seat/input-method-relay.cpp | 9 +++++++++ src/core/seat/input-method-relay.hpp | 1 + 2 files changed, 10 insertions(+) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 1c0abad92..81d3e6c2f 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -158,8 +158,17 @@ void wf::input_method_relay::disable_text_input(wlr_text_input_v3 *input) return; } + // on focus change, we disable on one text input and enable on another + // however, the former may also send us a disable request AFTER we've done the above + // let's just ignore repeated disable reuqests. + if (input == already_disabled_input) + { + return; + } + wlr_input_method_v2_send_deactivate(input_method); send_im_state(input); + already_disabled_input = input; } void wf::input_method_relay::remove_text_input(wlr_text_input_v3 *input) diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 99e771113..92ca4c41f 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -22,6 +22,7 @@ class input_method_relay on_input_method_new, on_input_method_commit, on_input_method_destroy, on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; + wlr_text_input_v3 *already_disabled_input = nullptr; text_input *find_focusable_text_input(); void set_focus(wlr_surface*); From 316955b60800b6b44a3a313ff982ba0bc87be5e6 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Fri, 24 Nov 2023 13:15:52 +0800 Subject: [PATCH 04/20] workaround focus change while preediting --- src/core/seat/input-method-relay.cpp | 8 ++++++++ src/core/seat/input-method-relay.hpp | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 81d3e6c2f..80dff3dc0 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -49,6 +49,14 @@ wf::input_method_relay::input_method_relay() auto evt_input_method = static_cast(data); assert(evt_input_method == input_method); + // FIXME: workaround focus change while preediting + if (focus_just_changed) + { + LOGI("focus_just_changed, ignore input method commit"); + focus_just_changed = false; + return; + } + auto *text_input = find_focused_text_input(); if (text_input == nullptr) { diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 92ca4c41f..a005b6033 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -23,6 +23,7 @@ class input_method_relay on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; wlr_text_input_v3 *already_disabled_input = nullptr; + bool focus_just_changed = false; text_input *find_focusable_text_input(); void set_focus(wlr_surface*); @@ -36,6 +37,8 @@ class input_method_relay { set_focus(nullptr); } + + focus_just_changed = true; }; bool should_grab(wlr_keyboard*); From 74443a556d67d7d905c0d0603aea40b94c728d56 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Tue, 5 Dec 2023 17:52:42 +0800 Subject: [PATCH 05/20] input method: don't grab keyboard if no text input is focused --- src/core/seat/input-method-relay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 80dff3dc0..7b7212356 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -203,7 +203,7 @@ void wf::input_method_relay::remove_popup_surface(wf::popup_surface *popup) bool wf::input_method_relay::should_grab(wlr_keyboard *kbd) { - if (keyboard_grab == nullptr) + if ((keyboard_grab == nullptr) || !find_focused_text_input()) { return false; } From 371600a76970e6c2283fb06d89ca86c1c59a9c88 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Tue, 5 Dec 2023 17:06:58 +0800 Subject: [PATCH 06/20] input method: fix conflicts with compositor shortcut keys We pass all key events to input method after plugins, but don't pass input method generated ones to plugins since we should have already done that. This conflicts with onscreen keyboards, but unfortunately no better way in sight. --- src/core/seat/input-method-relay.cpp | 16 +++++++++++----- src/core/seat/input-method-relay.hpp | 2 ++ src/core/seat/keyboard.cpp | 27 ++++++++++++++++++--------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 7b7212356..42ac0b828 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -100,6 +100,7 @@ wf::input_method_relay::input_method_relay() on_new_popup_surface.disconnect(); input_method = nullptr; keyboard_grab = nullptr; + last_keyboard_resource = nullptr; auto *text_input = find_focused_text_input(); if (text_input != nullptr) @@ -127,6 +128,7 @@ wf::input_method_relay::input_method_relay() on_grab_keyboard_destroy.set_callback([&] (void *data) { on_grab_keyboard_destroy.disconnect(); + last_keyboard_resource = keyboard_grab->resource; keyboard_grab = nullptr; }); @@ -208,16 +210,20 @@ bool wf::input_method_relay::should_grab(wlr_keyboard *kbd) return false; } - // input method sends key via a virtual keyboard + return !is_im_sent(kbd); +} + +bool wf::input_method_relay::is_im_sent(wlr_keyboard *kbd) +{ struct wlr_virtual_keyboard_v1 *virtual_keyboard = wlr_input_device_get_virtual_keyboard(&kbd->base); - if (virtual_keyboard && - (wl_resource_get_client(virtual_keyboard->resource) == - wl_resource_get_client(input_method->keyboard_grab->resource))) + if (!virtual_keyboard) { return false; } - return true; + auto resource = + input_method->keyboard_grab ? input_method->keyboard_grab->resource : last_keyboard_resource; + return wl_resource_get_client(virtual_keyboard->resource) == wl_resource_get_client(resource); } bool wf::input_method_relay::handle_key(struct wlr_keyboard *kbd, uint32_t time, uint32_t key, diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index a005b6033..5dc35a48f 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -24,6 +24,7 @@ class input_method_relay wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; wlr_text_input_v3 *already_disabled_input = nullptr; bool focus_just_changed = false; + wl_resource *last_keyboard_resource = nullptr; text_input *find_focusable_text_input(); void set_focus(wlr_surface*); @@ -57,6 +58,7 @@ class input_method_relay void remove_popup_surface(popup_surface*); bool handle_key(struct wlr_keyboard*, uint32_t, uint32_t, uint32_t); bool handle_modifier(struct wlr_keyboard*); + bool is_im_sent(struct wlr_keyboard*); ~input_method_relay(); }; diff --git a/src/core/seat/keyboard.cpp b/src/core/seat/keyboard.cpp index fd4f7d951..a914196a4 100644 --- a/src/core/seat/keyboard.cpp +++ b/src/core/seat/keyboard.cpp @@ -295,7 +295,8 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s auto& seat = wf::get_core_impl().seat; bool handled_in_plugin = false; - auto mod = mod_from_key(key); + bool im_sent = wf::get_core_impl().im_relay->is_im_sent(handle); + auto mod = mod_from_key(key); input->locked_mods = this->get_locked_mods(); if (state == WLR_KEY_PRESSED) @@ -320,11 +321,19 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s mod_binding_key = 0; } - handled_in_plugin |= wf::get_core().bindings->handle_key( - wf::keybinding_t{get_modifiers(), key}, mod_binding_key); + if (!im_sent) + { + handled_in_plugin |= wf::get_core().bindings->handle_key( + wf::keybinding_t{get_modifiers(), key}, mod_binding_key); + } + + if (!handled_in_plugin) + { + handled_in_plugin |= wf::get_core_impl().im_relay->handle_key(handle, time, key, state); + } } else { - if (mod_binding_key != 0) + if (!im_sent && (mod_binding_key != 0)) { int timeout = wf::option_wrapper_t( "input/modifier_binding_timeout"); @@ -338,12 +347,12 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s } } - mod_binding_key = 0; - } + if (!handled_in_plugin) + { + handled_in_plugin |= wf::get_core_impl().im_relay->handle_key(handle, time, key, state); + } - if (!handled_in_plugin) - { - handled_in_plugin |= wf::get_core_impl().im_relay->handle_key(handle, time, key, state); + mod_binding_key = 0; } return handled_in_plugin; From e0fce8a67364b9388bbb33bb32bd4a8a49e0d8c9 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Fri, 8 Dec 2023 15:47:11 +0800 Subject: [PATCH 07/20] input method: address reviews --- src/core/seat/input-method-relay.cpp | 32 +++++++++++++++++++++++++--- src/core/seat/input-method-relay.hpp | 7 ++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 42ac0b828..77db45405 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -27,7 +27,7 @@ wf::input_method_relay::input_method_relay() return; } - LOGI("new input method connected"); + LOGD("new input method connected"); input_method = new_input_method; on_input_method_commit.connect(&input_method->events.commit); on_input_method_destroy.connect(&input_method->events.destroy); @@ -117,7 +117,7 @@ wf::input_method_relay::input_method_relay() { if (keyboard_grab != nullptr) { - LOGI("Attempted to grab input method keyboard twice"); + LOGW("Attempted to grab input method keyboard twice"); return; } @@ -474,6 +474,12 @@ void wf::popup_surface::map() auto view = wf::wl_surface_to_wayfire_view(text_input->input->focused_surface->resource); auto output = view->get_output(); + if (!output) + { + LOGD("trying to map input method popup with a view not on an output."); + return; + } + set_output(output); auto target_layer = wf::scene::layer::UNMANAGED; @@ -481,6 +487,7 @@ void wf::popup_surface::map() priv->set_mapped_surface_contents(main_surface); priv->set_mapped(true); + _is_mapped = true; on_commit.connect(&surface->surface->events.commit); update_geometry(); @@ -491,15 +498,30 @@ void wf::popup_surface::map() void wf::popup_surface::unmap() { + if (!is_mapped()) + { + return; + } damage(); priv->unset_mapped_surface_contents(); emit_view_unmap(); priv->set_mapped(false); + _is_mapped = false; on_commit.disconnect(); } +std::string wf::popup_surface::get_app_id() +{ + return "input-method-popup"; +} + +std::string wf::popup_surface::get_title() +{ + return "input-method-popup"; +} + void wf::popup_surface::update_geometry() { auto text_input = this->relay->find_focused_text_input(); @@ -546,7 +568,10 @@ void wf::popup_surface::update_geometry() auto output = view->get_output(); auto g_output = output->get_layout_geometry(); + // make sure right edge is on screen, sliding to the left when needed, + // but keep left edge on screen nonetheless. x = std::max(0, std::min(x, g_output.width - width)); + // down edge is going to be out of screen; flip upwards if (y + height > g_output.height) { y -= height; @@ -556,6 +581,7 @@ void wf::popup_surface::update_geometry() } } + // make sure top edge is on screen, sliding down and sacrificing down edge if unavoidable y = std::max(0, y); surface_root_node->set_offset({x, y}); @@ -569,7 +595,7 @@ void wf::popup_surface::update_geometry() bool wf::popup_surface::is_mapped() const { - return priv->wsurface != nullptr; + return priv->wsurface != nullptr && _is_mapped; } wf::geometry_t wf::popup_surface::get_geometry() diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 5dc35a48f..3f012bf7a 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -56,7 +56,7 @@ class input_method_relay void disable_text_input(wlr_text_input_v3*); void remove_text_input(wlr_text_input_v3*); void remove_popup_surface(popup_surface*); - bool handle_key(struct wlr_keyboard*, uint32_t, uint32_t, uint32_t); + bool handle_key(struct wlr_keyboard*, uint32_t time, uint32_t key, uint32_t state); bool handle_modifier(struct wlr_keyboard*); bool is_im_sent(struct wlr_keyboard*); ~input_method_relay(); @@ -86,7 +86,9 @@ struct popup_surface : public wf::view_interface_t popup_surface(input_method_relay*, wlr_input_popup_surface_v2*); static std::shared_ptr create(input_method_relay*, wlr_input_popup_surface_v2*); - bool is_mapped() const; + bool is_mapped() const override; + std::string get_app_id() override; + std::string get_title() override; wf::geometry_t get_geometry(); void map(); void unmap(); @@ -97,6 +99,7 @@ struct popup_surface : public wf::view_interface_t wf::geometry_t geometry{0, 0, 0, 0}; std::shared_ptr main_surface; std::shared_ptr surface_root_node; + bool _is_mapped = false; virtual wlr_surface *get_keyboard_focus_surface() override { From 880bb885d888f16b19656cc515b85fcba8ac6eba Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Fri, 8 Dec 2023 17:00:49 +0800 Subject: [PATCH 08/20] use at place_popup_at function to convert from relative coordinates to onscreen ones --- src/api/wayfire/view.hpp | 2 ++ src/core/seat/input-method-relay.cpp | 18 ++++++-------- src/view/view.cpp | 35 ++++++++++++++++++++++++++++ src/view/xdg-shell.cpp | 27 ++++----------------- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/api/wayfire/view.hpp b/src/api/wayfire/view.hpp index 8f9ad5726..ca3422c04 100644 --- a/src/api/wayfire/view.hpp +++ b/src/api/wayfire/view.hpp @@ -258,6 +258,8 @@ class view_node_tag_t private: wayfire_view view; }; + +pointf_t place_popup_at(wlr_surface *parent, wlr_surface *popup, wf::pointf_t relative); } #endif diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 77db45405..4006d93fd 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -548,21 +548,17 @@ void wf::popup_surface::update_geometry() auto wlr_surface = text_input->input->focused_surface; auto view = wf::wl_surface_to_wayfire_view(wlr_surface->resource); - auto toplevel = toplevel_cast(view); - auto g = toplevel->get_geometry(); - auto margins = toplevel->toplevel()->current().margins; - - auto xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); - if (xdg_surface) + if (!view) { - // substract shadows etc; test app: d-feet - x -= xdg_surface->current.geometry.x; - y -= xdg_surface->current.geometry.y; + return; } damage(); - x += g.x + margins.left; - y += g.y + margins.top; + + wf::pointf_t popup_offset = wf::place_popup_at(wlr_surface, surface->surface, {x * 1.0, y * 1.0}); + x = popup_offset.x; + y = popup_offset.y; + auto width = surface->surface->current.width; auto height = surface->surface->current.height; diff --git a/src/view/view.cpp b/src/view/view.cpp index fb83d66c3..3c5246955 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -249,3 +249,38 @@ wlr_surface*wf::view_interface_t::get_wlr_surface() { return priv->wsurface; } + +// Offset relative to the parent surface +wf::pointf_t wf::place_popup_at(wlr_surface *parent, wlr_surface *popup, wf::pointf_t relative) +{ + auto popup_parent = wf::wl_surface_to_wayfire_view(parent->resource).get(); + + wf::pointf_t popup_offset = relative; + if (wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(parent)) + { + wlr_box box; + wlr_xdg_surface_get_geometry(xdg_surface, &box); + popup_offset.x += box.x; + popup_offset.y += box.y; + } + + // Get the {0, 0} of the parent view in output coordinates + popup_offset += popup_parent->get_surface_root_node()->to_global({0, 0}); + + auto xdg_surface = wlr_xdg_surface_try_from_wlr_surface(parent); + if (xdg_surface) + { + // substract shadows etc; test app: d-feet + popup_offset.x -= xdg_surface->current.geometry.x; + popup_offset.y -= xdg_surface->current.geometry.y; + } + + // Apply transformers to the popup position + auto node = popup_parent->get_surface_root_node()->parent(); + while (node != popup_parent->get_transformed_node().get()) + { + popup_offset = node->to_global(popup_offset); + node = node->parent(); + } + return popup_offset; +} diff --git a/src/view/xdg-shell.cpp b/src/view/xdg-shell.cpp index f7a658a1d..fb6fb5971 100644 --- a/src/view/xdg-shell.cpp +++ b/src/view/xdg-shell.cpp @@ -165,29 +165,10 @@ void wayfire_xdg_popup::update_position() } // Offset relative to the parent surface - wf::pointf_t popup_offset = {1.0 * popup->current.geometry.x, 1.0 * popup->current.geometry.y}; - if (wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(popup->parent)) - { - wlr_box box; - wlr_xdg_surface_get_geometry(xdg_surface, &box); - popup_offset.x += box.x; - popup_offset.y += box.y; - } - - // Get the {0, 0} of the parent view in output coordinates - popup_offset += popup_parent->get_surface_root_node()->to_global({0, 0}); - - // Subtract shadows, etc. - popup_offset.x -= popup->base->current.geometry.x; - popup_offset.y -= popup->base->current.geometry.y; - - // Apply transformers to the popup position - auto node = popup_parent->get_surface_root_node()->parent(); - while (node != popup_parent->get_transformed_node().get()) - { - popup_offset = node->to_global(popup_offset); - node = node->parent(); - } + wf::pointf_t popup_offset = wf::place_popup_at(popup->parent, popup->base->surface, { + popup->current.geometry.x, + popup->current.geometry.y, + }); this->move(popup_offset.x, popup_offset.y); } From d60b7bbf122ff35672c90a614f909c6037c3efb9 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Fri, 8 Dec 2023 17:11:50 +0800 Subject: [PATCH 09/20] input method: simplify key handling code --- src/core/seat/keyboard.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/core/seat/keyboard.cpp b/src/core/seat/keyboard.cpp index a914196a4..6f49fe21f 100644 --- a/src/core/seat/keyboard.cpp +++ b/src/core/seat/keyboard.cpp @@ -294,8 +294,13 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s auto& input = wf::get_core_impl().input; auto& seat = wf::get_core_impl().seat; + if (wf::get_core_impl().im_relay->is_im_sent(handle)) + { + mod_binding_key = 0; + return false; + } + bool handled_in_plugin = false; - bool im_sent = wf::get_core_impl().im_relay->is_im_sent(handle); auto mod = mod_from_key(key); input->locked_mods = this->get_locked_mods(); @@ -321,11 +326,8 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s mod_binding_key = 0; } - if (!im_sent) - { - handled_in_plugin |= wf::get_core().bindings->handle_key( - wf::keybinding_t{get_modifiers(), key}, mod_binding_key); - } + handled_in_plugin |= wf::get_core().bindings->handle_key( + wf::keybinding_t{get_modifiers(), key}, mod_binding_key); if (!handled_in_plugin) { @@ -333,7 +335,7 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s } } else { - if (!im_sent && (mod_binding_key != 0)) + if (mod_binding_key != 0) { int timeout = wf::option_wrapper_t( "input/modifier_binding_timeout"); From a09a5606bf7873aa093e5356a75673f63a133705 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Fri, 8 Dec 2023 18:18:42 +0800 Subject: [PATCH 10/20] fix style --- src/core/seat/input-method-relay.cpp | 5 +++-- src/core/seat/keyboard.cpp | 2 +- src/view/view.cpp | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 4006d93fd..3d804496b 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -502,6 +502,7 @@ void wf::popup_surface::unmap() { return; } + damage(); priv->unset_mapped_surface_contents(); @@ -547,7 +548,7 @@ void wf::popup_surface::update_geometry() } auto wlr_surface = text_input->input->focused_surface; - auto view = wf::wl_surface_to_wayfire_view(wlr_surface->resource); + auto view = wf::wl_surface_to_wayfire_view(wlr_surface->resource); if (!view) { return; @@ -555,7 +556,7 @@ void wf::popup_surface::update_geometry() damage(); - wf::pointf_t popup_offset = wf::place_popup_at(wlr_surface, surface->surface, {x * 1.0, y * 1.0}); + wf::pointf_t popup_offset = wf::place_popup_at(wlr_surface, surface->surface, {x* 1.0, y * 1.0}); x = popup_offset.x; y = popup_offset.y; diff --git a/src/core/seat/keyboard.cpp b/src/core/seat/keyboard.cpp index 6f49fe21f..449082c79 100644 --- a/src/core/seat/keyboard.cpp +++ b/src/core/seat/keyboard.cpp @@ -301,7 +301,7 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s } bool handled_in_plugin = false; - auto mod = mod_from_key(key); + auto mod = mod_from_key(key); input->locked_mods = this->get_locked_mods(); if (state == WLR_KEY_PRESSED) diff --git a/src/view/view.cpp b/src/view/view.cpp index 3c5246955..9ac601b87 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -282,5 +282,6 @@ wf::pointf_t wf::place_popup_at(wlr_surface *parent, wlr_surface *popup, wf::poi popup_offset = node->to_global(popup_offset); node = node->parent(); } + return popup_offset; } From 6ce370de8c77c6573a69a92d3086673493d45f6b Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Fri, 8 Dec 2023 18:19:52 +0800 Subject: [PATCH 11/20] convert coordinates type --- src/view/xdg-shell.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/view/xdg-shell.cpp b/src/view/xdg-shell.cpp index fb6fb5971..a8fc39210 100644 --- a/src/view/xdg-shell.cpp +++ b/src/view/xdg-shell.cpp @@ -166,8 +166,8 @@ void wayfire_xdg_popup::update_position() // Offset relative to the parent surface wf::pointf_t popup_offset = wf::place_popup_at(popup->parent, popup->base->surface, { - popup->current.geometry.x, - popup->current.geometry.y, + popup->current.geometry.x * 1.0, + popup->current.geometry.y * 1.0, }); this->move(popup_offset.x, popup_offset.y); From 1682ced9af489293c7ed936ca0969827b0ee78a5 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Tue, 26 Dec 2023 11:32:47 +0800 Subject: [PATCH 12/20] input method: use input_method->active instead of already_disabled_input --- src/core/seat/input-method-relay.cpp | 3 +-- src/core/seat/input-method-relay.hpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 3d804496b..3632a1d36 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -171,14 +171,13 @@ void wf::input_method_relay::disable_text_input(wlr_text_input_v3 *input) // on focus change, we disable on one text input and enable on another // however, the former may also send us a disable request AFTER we've done the above // let's just ignore repeated disable reuqests. - if (input == already_disabled_input) + if (!input_method->active) { return; } wlr_input_method_v2_send_deactivate(input_method); send_im_state(input); - already_disabled_input = input; } void wf::input_method_relay::remove_text_input(wlr_text_input_v3 *input) diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 3f012bf7a..1d53ef33e 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -22,7 +22,6 @@ class input_method_relay on_input_method_new, on_input_method_commit, on_input_method_destroy, on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; - wlr_text_input_v3 *already_disabled_input = nullptr; bool focus_just_changed = false; wl_resource *last_keyboard_resource = nullptr; text_input *find_focusable_text_input(); From 302838f619846d28aacbffa9b39a34167acbcd5a Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Wed, 27 Dec 2023 15:36:14 +0800 Subject: [PATCH 13/20] move place_popup_at to view-impl --- src/api/wayfire/view.hpp | 2 -- src/view/view-impl.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/view/view-impl.hpp | 2 ++ src/view/view.cpp | 36 ------------------------------------ 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/api/wayfire/view.hpp b/src/api/wayfire/view.hpp index ca3422c04..8f9ad5726 100644 --- a/src/api/wayfire/view.hpp +++ b/src/api/wayfire/view.hpp @@ -258,8 +258,6 @@ class view_node_tag_t private: wayfire_view view; }; - -pointf_t place_popup_at(wlr_surface *parent, wlr_surface *popup, wf::pointf_t relative); } #endif diff --git a/src/view/view-impl.cpp b/src/view/view-impl.cpp index 42fa35a30..71d3114db 100644 --- a/src/view/view-impl.cpp +++ b/src/view/view-impl.cpp @@ -375,3 +375,39 @@ wayfire_toplevel_view wf::find_topmost_parent(wayfire_toplevel_view v) return v; } + +// Offset relative to the parent surface +wf::pointf_t wf::place_popup_at(wlr_surface *parent, wlr_surface *popup, wf::pointf_t relative) +{ + auto popup_parent = wf::wl_surface_to_wayfire_view(parent->resource).get(); + + wf::pointf_t popup_offset = relative; + if (wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(parent)) + { + wlr_box box; + wlr_xdg_surface_get_geometry(xdg_surface, &box); + popup_offset.x += box.x; + popup_offset.y += box.y; + } + + // Get the {0, 0} of the parent view in output coordinates + popup_offset += popup_parent->get_surface_root_node()->to_global({0, 0}); + + auto xdg_surface = wlr_xdg_surface_try_from_wlr_surface(parent); + if (xdg_surface) + { + // substract shadows etc; test app: d-feet + popup_offset.x -= xdg_surface->current.geometry.x; + popup_offset.y -= xdg_surface->current.geometry.y; + } + + // Apply transformers to the popup position + auto node = popup_parent->get_surface_root_node()->parent(); + while (node != popup_parent->get_transformed_node().get()) + { + popup_offset = node->to_global(popup_offset); + node = node->parent(); + } + + return popup_offset; +} diff --git a/src/view/view-impl.hpp b/src/view/view-impl.hpp index b9bf84eca..fa6574042 100644 --- a/src/view/view-impl.hpp +++ b/src/view/view-impl.hpp @@ -73,6 +73,8 @@ int xwayland_get_pid(); void init_desktop_apis(); void init_xdg_decoration_handlers(); + +pointf_t place_popup_at(wlr_surface *parent, wlr_surface *popup, wf::pointf_t relative); } #endif /* end of include guard: VIEW_IMPL_HPP */ diff --git a/src/view/view.cpp b/src/view/view.cpp index 9ac601b87..fb83d66c3 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -249,39 +249,3 @@ wlr_surface*wf::view_interface_t::get_wlr_surface() { return priv->wsurface; } - -// Offset relative to the parent surface -wf::pointf_t wf::place_popup_at(wlr_surface *parent, wlr_surface *popup, wf::pointf_t relative) -{ - auto popup_parent = wf::wl_surface_to_wayfire_view(parent->resource).get(); - - wf::pointf_t popup_offset = relative; - if (wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(parent)) - { - wlr_box box; - wlr_xdg_surface_get_geometry(xdg_surface, &box); - popup_offset.x += box.x; - popup_offset.y += box.y; - } - - // Get the {0, 0} of the parent view in output coordinates - popup_offset += popup_parent->get_surface_root_node()->to_global({0, 0}); - - auto xdg_surface = wlr_xdg_surface_try_from_wlr_surface(parent); - if (xdg_surface) - { - // substract shadows etc; test app: d-feet - popup_offset.x -= xdg_surface->current.geometry.x; - popup_offset.y -= xdg_surface->current.geometry.y; - } - - // Apply transformers to the popup position - auto node = popup_parent->get_surface_root_node()->parent(); - while (node != popup_parent->get_transformed_node().get()) - { - popup_offset = node->to_global(popup_offset); - node = node->parent(); - } - - return popup_offset; -} From cda1913fc63d0fc319d68279be94bac377463550 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Wed, 27 Dec 2023 15:44:23 +0800 Subject: [PATCH 14/20] input method: explain the focus change workaround in detail --- src/core/seat/input-method-relay.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 3632a1d36..4c0df2bfe 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -50,6 +50,15 @@ wf::input_method_relay::input_method_relay() assert(evt_input_method == input_method); // FIXME: workaround focus change while preediting + // + // With input method v2, we have no way to notify the input method that + // input focus has changed. The input method maintains its state, and + // will bring it to the new window, i.e. a half-finished preedit string + // from the old window will be brought to the new one. This is undesired. + // + // We ignore such commit requests so it doesn't have any affect on the + // new window. Even when the previous window isn't preediting when + // switching focus, it doesn't have any bad effect to the new window anyway. if (focus_just_changed) { LOGI("focus_just_changed, ignore input method commit"); From 7940111f316cbe21154caa134d74e8cbffcbde64 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Tue, 26 Dec 2023 17:56:44 +0800 Subject: [PATCH 15/20] input method: use pid to identify the input method sent key events because last_keyboard_resource may be invalid when we need to use it. --- src/core/seat/input-method-relay.cpp | 12 +++++++----- src/core/seat/input-method-relay.hpp | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 4c0df2bfe..9bdba9b61 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -18,6 +18,8 @@ wf::input_method_relay::input_method_relay() on_input_method_new.set_callback([&] (void *data) { auto new_input_method = static_cast(data); + auto client = wl_resource_get_client(new_input_method->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); if (input_method != nullptr) { @@ -109,7 +111,7 @@ wf::input_method_relay::input_method_relay() on_new_popup_surface.disconnect(); input_method = nullptr; keyboard_grab = nullptr; - last_keyboard_resource = nullptr; + pid = 0; auto *text_input = find_focused_text_input(); if (text_input != nullptr) @@ -137,7 +139,6 @@ wf::input_method_relay::input_method_relay() on_grab_keyboard_destroy.set_callback([&] (void *data) { on_grab_keyboard_destroy.disconnect(); - last_keyboard_resource = keyboard_grab->resource; keyboard_grab = nullptr; }); @@ -229,9 +230,10 @@ bool wf::input_method_relay::is_im_sent(wlr_keyboard *kbd) return false; } - auto resource = - input_method->keyboard_grab ? input_method->keyboard_grab->resource : last_keyboard_resource; - return wl_resource_get_client(virtual_keyboard->resource) == wl_resource_get_client(resource); + auto client = wl_resource_get_client(virtual_keyboard->resource); + pid_t vkbd_pid; + wl_client_get_credentials(client, &vkbd_pid, NULL, NULL); + return vkbd_pid == pid; } bool wf::input_method_relay::handle_key(struct wlr_keyboard *kbd, uint32_t time, uint32_t key, diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 1d53ef33e..a2b2bcdda 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -23,7 +23,7 @@ class input_method_relay on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; bool focus_just_changed = false; - wl_resource *last_keyboard_resource = nullptr; + pid_t pid = 0; text_input *find_focusable_text_input(); void set_focus(wlr_surface*); From c302c353a7dd1ff3f88310723ecf0847e67bb8e4 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Wed, 27 Dec 2023 16:04:01 +0800 Subject: [PATCH 16/20] fix headers --- src/core/seat/input-method-relay.cpp | 2 +- src/core/seat/input-method-relay.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index 9bdba9b61..b1a7d445e 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -1,7 +1,7 @@ #include #include "input-method-relay.hpp" #include "../core-impl.hpp" -#include "../../output/output-impl.hpp" +#include "../../view/view-impl.hpp" #include "wayfire/scene-operations.hpp" #include diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index a2b2bcdda..af6b6fb5d 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -1,10 +1,10 @@ #pragma once -#include "seat-impl.hpp" #include "wayfire/util.hpp" #include "wayfire/signal-definitions.hpp" #include "wayfire/view.hpp" #include #include +#include #include #include From 5f0ce1feff1c697f0f2e12f439350178c5076c25 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Sat, 30 Dec 2023 15:44:41 +0800 Subject: [PATCH 17/20] input method: better ways to identify IM sent keys --- src/core/seat/input-method-relay.cpp | 32 +++++++++++++++++++++------- src/core/seat/input-method-relay.hpp | 1 - src/core/seat/seat-impl.hpp | 5 +++-- src/core/seat/seat.cpp | 9 +++++--- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index b1a7d445e..bd55e3c1b 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -2,9 +2,11 @@ #include "input-method-relay.hpp" #include "../core-impl.hpp" #include "../../view/view-impl.hpp" +#include "core/seat/seat-impl.hpp" #include "wayfire/scene-operations.hpp" #include +#include wf::input_method_relay::input_method_relay() { @@ -18,9 +20,6 @@ wf::input_method_relay::input_method_relay() on_input_method_new.set_callback([&] (void *data) { auto new_input_method = static_cast(data); - auto client = wl_resource_get_client(new_input_method->resource); - wl_client_get_credentials(client, &pid, NULL, NULL); - if (input_method != nullptr) { LOGI("Attempted to connect second input method"); @@ -111,7 +110,6 @@ wf::input_method_relay::input_method_relay() on_new_popup_surface.disconnect(); input_method = nullptr; keyboard_grab = nullptr; - pid = 0; auto *text_input = find_focused_text_input(); if (text_input != nullptr) @@ -230,10 +228,28 @@ bool wf::input_method_relay::is_im_sent(wlr_keyboard *kbd) return false; } - auto client = wl_resource_get_client(virtual_keyboard->resource); - pid_t vkbd_pid; - wl_client_get_credentials(client, &vkbd_pid, NULL, NULL); - return vkbd_pid == pid; + // We have already identified the device as IM-based device + auto device_impl = (wf::input_device_impl_t*)kbd->base.data; + if (device_impl->is_im_keyboard) + { + return true; + } + + if (this->input_method) + { + // This is a workaround because we do not have sufficient information to know which virtual keyboards + // are connected to IMs + auto im_client = wl_resource_get_client(input_method->resource); + auto vkbd_client = wl_resource_get_client(virtual_keyboard->resource); + + if (im_client == vkbd_client) + { + device_impl->is_im_keyboard = true; + return true; + } + } + + return false; } bool wf::input_method_relay::handle_key(struct wlr_keyboard *kbd, uint32_t time, uint32_t key, diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index af6b6fb5d..1d6864755 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -23,7 +23,6 @@ class input_method_relay on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; bool focus_just_changed = false; - pid_t pid = 0; text_input *find_focusable_text_input(); void set_focus(wlr_surface*); diff --git a/src/core/seat/seat-impl.hpp b/src/core/seat/seat-impl.hpp index ad19ba1f3..2c00c250a 100644 --- a/src/core/seat/seat-impl.hpp +++ b/src/core/seat/seat-impl.hpp @@ -5,7 +5,6 @@ #include #include -#include "../../view/surface-impl.hpp" #include "wayfire/output.hpp" #include "wayfire/input-device.hpp" #include "wayfire/scene-input.hpp" @@ -23,11 +22,13 @@ class input_device_impl_t : public wf::input_device_t { public: input_device_impl_t(wlr_input_device *dev); - virtual ~input_device_impl_t() = default; + virtual ~input_device_impl_t(); wf::wl_listener_wrapper on_destroy; virtual void update_options() {} + + bool is_im_keyboard = false; }; class pointer_t; diff --git a/src/core/seat/seat.cpp b/src/core/seat/seat.cpp index c04f448d6..40ee3ad64 100644 --- a/src/core/seat/seat.cpp +++ b/src/core/seat/seat.cpp @@ -1,15 +1,12 @@ #include "seat-impl.hpp" #include "cursor.hpp" -#include "wayfire/compositor-view.hpp" #include "wayfire/geometry.hpp" -#include "wayfire/opengl.hpp" #include "../core-impl.hpp" #include "../view/view-impl.hpp" #include "keyboard.hpp" #include "pointer.hpp" #include "touch.hpp" #include "input-manager.hpp" -#include "wayfire/render-manager.hpp" #include "wayfire/output-layout.hpp" #include #include "wayfire/scene-input.hpp" @@ -593,6 +590,12 @@ wf::input_device_impl_t::input_device_impl_t(wlr_input_device *dev) : wf::get_core_impl().input->handle_input_destroyed(this->get_wlr_handle()); }); on_destroy.connect(&dev->events.destroy); + this->handle->data = this; +} + +wf::input_device_impl_t::~input_device_impl_t() +{ + this->handle->data = NULL; } static wf::pointf_t to_local_recursive(wf::scene::node_t *node, wf::pointf_t point) From 8a9cf9649f5e565dad6d060a66a507386569817c Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Sat, 30 Dec 2023 15:45:52 +0800 Subject: [PATCH 18/20] Revert "input method: use input_method->active instead of already_disabled_input" This reverts commit 1682ced9af489293c7ed936ca0969827b0ee78a5. input_method->active doesn't work for this purpose (see comments) --- src/core/seat/input-method-relay.cpp | 43 +++++++++++++++++++++++++++- src/core/seat/input-method-relay.hpp | 1 + 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index bd55e3c1b..a597e84a0 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -179,13 +179,54 @@ void wf::input_method_relay::disable_text_input(wlr_text_input_v3 *input) // on focus change, we disable on one text input and enable on another // however, the former may also send us a disable request AFTER we've done the above // let's just ignore repeated disable reuqests. - if (!input_method->active) + // + // input_method->active doesn't work for this purpose e.g. in one case: + // + // * start Alacritty and activate IM + // * start another Alacritty and activate IM + // * close the new Alacritty with shortcut key + // + // The log: + // + // II 30-12-23 16:13:29.232 - [build/wayfire/src/view/xdg-shell/xdg-toplevel-view.cpp:29] new xdg_shell_stable surface: Alacritty app-id: Alacritty + // II 30-12-23 16:13:29.278 - [build/wayfire/src/core/seat/input-method-relay.cpp:365] disable_text_input on set_focus 0x55b8a3cd1900 + // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:179] 0x55b8a2ed7310 disable IM for 0x55b8a3cd1900 + // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:180] 0x55b8a2ed7310 in disable_text_input: input_method->active true + // DD 30-12-23 16:13:29.279 - [types/wlr_text_input_v3.c:185] Text input commit received without focus + // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:444] on_text_input_disable 0x55b8a3cd1900 + // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:179] 0x55b8a2ed7310 disable IM for 0x55b8a3cd1900 + // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:180] 0x55b8a2ed7310 in disable_text_input: input_method->active false + // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:221] 0x55b8a2ed7310 input_method->active should be false: false + // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:407] 0x6918e1c4586d3200 enable IM for 0x55b8a3d8c210 + // DD 30-12-23 16:13:29.986 - [types/wlr_compositor.c:683] New wlr_surface 0x55b8a3de27f0 (res 0x55b8a3e931c0) + // DD 30-12-23 16:13:31.011 - [build/wayfire/src/output/output.cpp:293] output Virtual-1: activate plugin builtin-close-view + // DD 30-12-23 16:13:31.011 - [build/wayfire/src/output/output.cpp:310] output Virtual-1: deactivate plugin builtin-close-view + // II 30-12-23 16:13:31.029 - [build/wayfire/src/core/seat/input-method-relay.cpp:365] disable_text_input on set_focus 0x55b8a3d8c210 + // II 30-12-23 16:13:31.029 - [build/wayfire/src/core/seat/input-method-relay.cpp:179] 0x55b8a2ed7310 disable IM for 0x55b8a3d8c210 + // II 30-12-23 16:13:31.029 - [build/wayfire/src/core/seat/input-method-relay.cpp:180] 0x55b8a2ed7310 in disable_text_input: input_method->active true + // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:407] 0x55b8a3a10060 enable IM for 0x55b8a3cd1900 + // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:455] destroying 0x55b8a3d8c210 + // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:179] 0x55b8a2ed7310 disable IM for 0x55b8a3d8c210 + // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:180] 0x55b8a2ed7310 in disable_text_input: input_method->active true + // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:221] 0x55b8a2ed7310 input_method->active should be false: true + // + // * (enable for A; not in log) + // * disable for A (set_focus) + // * disable for A (on_text_input_disable) + // * enable for B + // * close B + // * disable for B (set_focus) + // * enable for A + // * disable for B (destruction) <-- this actually disables IM for A as A is in focus now + + if (input == already_disabled_input) { return; } wlr_input_method_v2_send_deactivate(input_method); send_im_state(input); + already_disabled_input = input; } void wf::input_method_relay::remove_text_input(wlr_text_input_v3 *input) diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 1d6864755..1ca47d592 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -22,6 +22,7 @@ class input_method_relay on_input_method_new, on_input_method_commit, on_input_method_destroy, on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; + wlr_text_input_v3 *already_disabled_input = nullptr; bool focus_just_changed = false; text_input *find_focusable_text_input(); void set_focus(wlr_surface*); From 91e8f06e3b081a4c29f9663f6d00062ab055d363 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Sat, 30 Dec 2023 20:02:05 +0800 Subject: [PATCH 19/20] input method: don't send deactivate command if the input is not the currently focused input --- src/core/seat/input-method-relay.cpp | 50 +++------------------------- src/core/seat/input-method-relay.hpp | 1 - 2 files changed, 5 insertions(+), 46 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index a597e84a0..a9d276f9d 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -176,57 +176,17 @@ void wf::input_method_relay::disable_text_input(wlr_text_input_v3 *input) return; } - // on focus change, we disable on one text input and enable on another - // however, the former may also send us a disable request AFTER we've done the above - // let's just ignore repeated disable reuqests. - // - // input_method->active doesn't work for this purpose e.g. in one case: - // - // * start Alacritty and activate IM - // * start another Alacritty and activate IM - // * close the new Alacritty with shortcut key - // - // The log: - // - // II 30-12-23 16:13:29.232 - [build/wayfire/src/view/xdg-shell/xdg-toplevel-view.cpp:29] new xdg_shell_stable surface: Alacritty app-id: Alacritty - // II 30-12-23 16:13:29.278 - [build/wayfire/src/core/seat/input-method-relay.cpp:365] disable_text_input on set_focus 0x55b8a3cd1900 - // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:179] 0x55b8a2ed7310 disable IM for 0x55b8a3cd1900 - // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:180] 0x55b8a2ed7310 in disable_text_input: input_method->active true - // DD 30-12-23 16:13:29.279 - [types/wlr_text_input_v3.c:185] Text input commit received without focus - // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:444] on_text_input_disable 0x55b8a3cd1900 - // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:179] 0x55b8a2ed7310 disable IM for 0x55b8a3cd1900 - // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:180] 0x55b8a2ed7310 in disable_text_input: input_method->active false - // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:221] 0x55b8a2ed7310 input_method->active should be false: false - // II 30-12-23 16:13:29.279 - [build/wayfire/src/core/seat/input-method-relay.cpp:407] 0x6918e1c4586d3200 enable IM for 0x55b8a3d8c210 - // DD 30-12-23 16:13:29.986 - [types/wlr_compositor.c:683] New wlr_surface 0x55b8a3de27f0 (res 0x55b8a3e931c0) - // DD 30-12-23 16:13:31.011 - [build/wayfire/src/output/output.cpp:293] output Virtual-1: activate plugin builtin-close-view - // DD 30-12-23 16:13:31.011 - [build/wayfire/src/output/output.cpp:310] output Virtual-1: deactivate plugin builtin-close-view - // II 30-12-23 16:13:31.029 - [build/wayfire/src/core/seat/input-method-relay.cpp:365] disable_text_input on set_focus 0x55b8a3d8c210 - // II 30-12-23 16:13:31.029 - [build/wayfire/src/core/seat/input-method-relay.cpp:179] 0x55b8a2ed7310 disable IM for 0x55b8a3d8c210 - // II 30-12-23 16:13:31.029 - [build/wayfire/src/core/seat/input-method-relay.cpp:180] 0x55b8a2ed7310 in disable_text_input: input_method->active true - // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:407] 0x55b8a3a10060 enable IM for 0x55b8a3cd1900 - // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:455] destroying 0x55b8a3d8c210 - // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:179] 0x55b8a2ed7310 disable IM for 0x55b8a3d8c210 - // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:180] 0x55b8a2ed7310 in disable_text_input: input_method->active true - // II 30-12-23 16:13:31.038 - [build/wayfire/src/core/seat/input-method-relay.cpp:221] 0x55b8a2ed7310 input_method->active should be false: true - // - // * (enable for A; not in log) - // * disable for A (set_focus) - // * disable for A (on_text_input_disable) - // * enable for B - // * close B - // * disable for B (set_focus) - // * enable for A - // * disable for B (destruction) <-- this actually disables IM for A as A is in focus now - - if (input == already_disabled_input) + // Don't deactivate input method if the text input isn't in focus. + // We may get several and posibly interwined enable/disable calls while + // switching focus / closing windows; don't deactivate for the wrong one. + auto focused_input = find_focused_text_input(); + if (!focused_input || input != focused_input->input) { return; } wlr_input_method_v2_send_deactivate(input_method); send_im_state(input); - already_disabled_input = input; } void wf::input_method_relay::remove_text_input(wlr_text_input_v3 *input) diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 1ca47d592..1d6864755 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -22,7 +22,6 @@ class input_method_relay on_input_method_new, on_input_method_commit, on_input_method_destroy, on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; - wlr_text_input_v3 *already_disabled_input = nullptr; bool focus_just_changed = false; text_input *find_focusable_text_input(); void set_focus(wlr_surface*); From bfaef7a7d606cf6bc694b74f71d95faae3b5e6ba Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Sat, 30 Dec 2023 20:08:57 +0800 Subject: [PATCH 20/20] fix code style --- src/core/seat/input-method-relay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index a9d276f9d..a341ed376 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -180,7 +180,7 @@ void wf::input_method_relay::disable_text_input(wlr_text_input_v3 *input) // We may get several and posibly interwined enable/disable calls while // switching focus / closing windows; don't deactivate for the wrong one. auto focused_input = find_focused_text_input(); - if (!focused_input || input != focused_input->input) + if (!focused_input || (input != focused_input->input)) { return; } @@ -240,7 +240,7 @@ bool wf::input_method_relay::is_im_sent(wlr_keyboard *kbd) { // This is a workaround because we do not have sufficient information to know which virtual keyboards // are connected to IMs - auto im_client = wl_resource_get_client(input_method->resource); + auto im_client = wl_resource_get_client(input_method->resource); auto vkbd_client = wl_resource_get_client(virtual_keyboard->resource); if (im_client == vkbd_client)