From 1c4d0e8c18ca0e269dd2019d50a2f1bb7deffd82 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Fri, 5 Aug 2022 13:03:37 +0200 Subject: [PATCH] added IME protocol support --- src/Compositor.cpp | 10 ++ src/Compositor.hpp | 2 + src/events/Events.hpp | 4 + src/events/Misc.cpp | 12 ++ src/helpers/WLClasses.hpp | 11 ++ src/includes.hpp | 4 + src/managers/input/InputManager.hpp | 3 + src/managers/input/InputMethodRelay.cpp | 224 ++++++++++++++++++++++++ src/managers/input/InputMethodRelay.hpp | 32 ++++ 9 files changed, 302 insertions(+) create mode 100644 src/managers/input/InputMethodRelay.cpp create mode 100644 src/managers/input/InputMethodRelay.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index e32d4caa..b3b7657b 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -153,6 +153,10 @@ CCompositor::CCompositor() { m_sWLRPointerGestures = wlr_pointer_gestures_v1_create(m_sWLDisplay); m_sWLRSession = wlr_backend_get_session(m_sWLRBackend); + + m_sWLRTextInputMgr = wlr_text_input_manager_v3_create(m_sWLDisplay); + + m_sWLRIMEMgr = wlr_input_method_manager_v2_create(m_sWLDisplay); } CCompositor::~CCompositor() { @@ -200,6 +204,9 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer"); addWLSignal(&m_sWLRIdleInhibitMgr->events.new_inhibitor, &Events::listen_newIdleInhibitor, m_sWLRIdleInhibitMgr, "WLRIdleInhibitMgr"); addWLSignal(&m_sWLROutputPowerMgr->events.set_mode, &Events::listen_powerMgrSetMode, m_sWLROutputPowerMgr, "PowerMgr"); + addWLSignal(&m_sWLRIMEMgr->events.input_method, &Events::listen_newIME, m_sWLRIMEMgr, "IMEMgr"); + addWLSignal(&m_sWLRTextInputMgr->events.text_input, &Events::listen_newTextInput, m_sWLRTextInputMgr, "TextInputMgr"); + if (m_sWLRSession) addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session"); } @@ -665,6 +672,7 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) { if (!pSurface) { wlr_seat_keyboard_clear_focus(m_sSeat.seat); g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); // unfocused + g_pInputManager->m_sIMERelay.onKeyboardFocus(nullptr); return; } @@ -676,6 +684,8 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) { wlr_seat_keyboard_notify_enter(m_sSeat.seat, pSurface, KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers); + g_pInputManager->m_sIMERelay.onKeyboardFocus(pSurface); + wlr_seat_keyboard_focus_change_event event = { .seat = m_sSeat.seat, .old_surface = m_pLastFocus, diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 5d46141c..35cb76e9 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -66,6 +66,8 @@ public: wlr_idle_inhibit_manager_v1* m_sWLRIdleInhibitMgr; wlr_pointer_gestures_v1* m_sWLRPointerGestures; wlr_output_power_manager_v1* m_sWLROutputPowerMgr; + wlr_input_method_manager_v2* m_sWLRIMEMgr; + wlr_text_input_manager_v3* m_sWLRTextInputMgr; // ------------------------------------------------- // diff --git a/src/events/Events.hpp b/src/events/Events.hpp index db15eceb..8d0f58cd 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -133,4 +133,8 @@ namespace Events { // Power LISTENER(powerMgrSetMode); + + // IME + LISTENER(newIME); + LISTENER(newTextInput); }; \ No newline at end of file diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index 5caa8984..38219e57 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -174,4 +174,16 @@ void Events::listener_powerMgrSetMode(wl_listener* listener, void* data) { if (!wlr_output_commit(EVENT->output)) Debug::log(ERR, "Couldn't set power mode"); +} + +void Events::listener_newIME(wl_listener* listener, void* data) { + Debug::log(LOG, "New IME added!"); + + g_pInputManager->m_sIMERelay.onNewIME((wlr_input_method_v2*)data); +} + +void Events::listener_newTextInput(wl_listener* listener, void* data) { + Debug::log(LOG, "New TextInput added!"); + + g_pInputManager->m_sIMERelay.onNewTextInput((wlr_text_input_v3*)data); } \ No newline at end of file diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index 6d2edfaa..60283dc0 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -272,4 +272,15 @@ struct SSwipeGesture { int speedPoints = 0; CMonitor* pMonitor = nullptr; +}; + +struct STextInput { + wlr_text_input_v3* pWlrInput = nullptr; + + wlr_surface* pPendingSurface = nullptr; + + DYNLISTENER(textInputEnable); + DYNLISTENER(textInputDisable); + DYNLISTENER(textInputCommit); + DYNLISTENER(textInputDestroy); }; \ No newline at end of file diff --git a/src/includes.hpp b/src/includes.hpp index 8b350611..2cb531bd 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -32,6 +32,7 @@ #define class _class #define namespace _namespace #define static +#define delete delete_ extern "C" { #include @@ -94,8 +95,11 @@ extern "C" { #include #include #include +#include +#include } +#undef delete #undef class #undef namespace #undef static diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index da821dfc..362591a1 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -5,6 +5,7 @@ #include "../../helpers/WLClasses.hpp" #include "../../Window.hpp" #include "../../helpers/Timer.hpp" +#include "InputMethodRelay.hpp" enum eClickBehaviorMode { CLICKMODE_DEFAULT = 0, @@ -77,6 +78,8 @@ public: CTimer m_tmrLastCursorMovement; + CInputMethodRelay m_sIMERelay; + // for shared mods uint32_t accumulateModsFromAllKBs(); diff --git a/src/managers/input/InputMethodRelay.cpp b/src/managers/input/InputMethodRelay.cpp new file mode 100644 index 00000000..406e76d0 --- /dev/null +++ b/src/managers/input/InputMethodRelay.cpp @@ -0,0 +1,224 @@ +#include "InputMethodRelay.hpp" +#include "InputManager.hpp" + +CInputMethodRelay::CInputMethodRelay() { + +} + +void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) { + if (m_pWLRIME) { + Debug::log(ERR, "Cannot register 2 IMEs at once!"); + + wlr_input_method_v2_send_unavailable(pIME); + + return; + } + + m_pWLRIME = pIME; + + hyprListener_IMECommit.initCallback(&m_pWLRIME->events.commit, [&](void* owner, void* data) { + + const auto PTI = getFocusedTextInput(); + const auto PIMR = (CInputMethodRelay*)owner; + + if (!PTI) { + Debug::log(LOG, "No focused TextInput on IME Commit"); + return; + } + + Debug::log(LOG, "IME Commit"); + + if (PIMR->m_pWLRIME->current.preedit.text) { + wlr_text_input_v3_send_preedit_string(PTI->pWlrInput, PIMR->m_pWLRIME->current.preedit.text, PIMR->m_pWLRIME->current.preedit.cursor_begin, PIMR->m_pWLRIME->current.preedit.cursor_end); + } + + if (PIMR->m_pWLRIME->current.commit_text) { + wlr_text_input_v3_send_commit_string(PTI->pWlrInput, PIMR->m_pWLRIME->current.commit_text); + } + + if (PIMR->m_pWLRIME->current.delete_.before_length || + PIMR->m_pWLRIME->current.delete_.after_length) { + wlr_text_input_v3_send_delete_surrounding_text(PTI->pWlrInput, PIMR->m_pWLRIME->current.delete_.before_length, PIMR->m_pWLRIME->current.delete_.after_length); + } + + wlr_text_input_v3_send_done(PTI->pWlrInput); + + }, this, "IMERelay"); + + hyprListener_IMEDestroy.initCallback(&m_pWLRIME->events.destroy, [&](void* owner, void* data) { + + m_pWLRIME = nullptr; + + hyprListener_IMEDestroy.removeCallback(); + hyprListener_IMECommit.removeCallback(); + + const auto PTI = getFocusedTextInput(); + + Debug::log(LOG, "IME Destroy"); + + if (PTI) { + PTI->pPendingSurface = PTI->pWlrInput->focused_surface; + + wlr_text_input_v3_send_leave(PTI->pWlrInput); + } + + }, this, "IMERelay"); + + const auto PTI = getFocusableTextInput(); + + if (PTI) { + wlr_text_input_v3_send_enter(PTI->pWlrInput, PTI->pPendingSurface); + PTI->pPendingSurface = nullptr; + } +} + +STextInput* CInputMethodRelay::getFocusedTextInput() { + for (auto& ti : m_lTextInputs) { + if (ti.pWlrInput->focused_surface) { + return &ti; + } + } + + return nullptr; +} + +STextInput* CInputMethodRelay::getFocusableTextInput() { + for (auto& ti : m_lTextInputs) { + if (ti.pPendingSurface) { + return &ti; + } + } + + return nullptr; +} + +void CInputMethodRelay::onNewTextInput(wlr_text_input_v3* pInput) { + createNewTextInput(pInput); +} + +void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { + const auto PTEXTINPUT = &m_lTextInputs.emplace_back(); + + PTEXTINPUT->pWlrInput = pInput; + + PTEXTINPUT->hyprListener_textInputEnable.initCallback(&pInput->events.enable, [](void* owner, void* data) { + + const auto PINPUT = (STextInput*)owner; + + if (!g_pInputManager->m_sIMERelay.m_pWLRIME) { + Debug::log(ERR, "Enabling TextInput on no IME!"); + return; + } + + Debug::log(LOG, "Enable TextInput"); + + wlr_input_method_v2_send_activate(g_pInputManager->m_sIMERelay.m_pWLRIME); + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + + }, PTEXTINPUT, "textInput"); + + PTEXTINPUT->hyprListener_textInputCommit.initCallback(&pInput->events.commit, [](void* owner, void* data) { + + const auto PINPUT = (STextInput*)owner; + + if (!g_pInputManager->m_sIMERelay.m_pWLRIME) { + Debug::log(ERR, "Committing TextInput on no IME!"); + return; + } + + if (!PINPUT->pWlrInput->current_enabled) { + Debug::log(ERR, "Disabled TextInput commit?"); + return; + } + + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + + }, PTEXTINPUT, "textInput"); + + PTEXTINPUT->hyprListener_textInputDisable.initCallback(&pInput->events.disable, [](void* owner, void* data) { + + const auto PINPUT = (STextInput*)owner; + + if (!g_pInputManager->m_sIMERelay.m_pWLRIME) { + Debug::log(ERR, "Disabling TextInput on no IME!"); + return; + } + + Debug::log(LOG, "Disable TextInput"); + + wlr_input_method_v2_send_deactivate(g_pInputManager->m_sIMERelay.m_pWLRIME); + + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + + }, PTEXTINPUT, "textInput"); + + PTEXTINPUT->hyprListener_textInputDestroy.initCallback(&pInput->events.destroy, [](void* owner, void* data) { + + const auto PINPUT = (STextInput*)owner; + + if (!g_pInputManager->m_sIMERelay.m_pWLRIME) { + Debug::log(ERR, "Disabling TextInput on no IME!"); + return; + } + + if (PINPUT->pWlrInput->current_enabled) { + wlr_input_method_v2_send_deactivate(g_pInputManager->m_sIMERelay.m_pWLRIME); + + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + } + + PINPUT->hyprListener_textInputCommit.removeCallback(); + PINPUT->hyprListener_textInputDestroy.removeCallback(); + PINPUT->hyprListener_textInputDisable.removeCallback(); + PINPUT->hyprListener_textInputEnable.removeCallback(); + + g_pInputManager->m_sIMERelay.removeTextInput(PINPUT->pWlrInput); + + }, PTEXTINPUT, "textInput"); +} + +void CInputMethodRelay::removeTextInput(wlr_text_input_v3* pInput) { + m_lTextInputs.remove_if([&](const auto& other) { return other.pWlrInput == pInput; }); +} + +void CInputMethodRelay::commitIMEState(wlr_text_input_v3* pInput) { + wlr_input_method_v2_send_surrounding_text(m_pWLRIME, pInput->current.surrounding.text, pInput->current.surrounding.cursor, pInput->current.surrounding.anchor); + wlr_input_method_v2_send_text_change_cause(m_pWLRIME, pInput->current.text_change_cause); + wlr_input_method_v2_send_content_type(m_pWLRIME, pInput->current.content_type.hint, pInput->current.content_type.purpose); + wlr_input_method_v2_send_done(m_pWLRIME); +} + +void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) { + if (!m_pWLRIME) + return; + + for (auto& ti : m_lTextInputs) { + if (ti.pPendingSurface) { + + if (pSurface != ti.pPendingSurface) + ti.pPendingSurface = nullptr; + + } else if (ti.pWlrInput->focused_surface) { + + if (pSurface != ti.pWlrInput->focused_surface) { + wlr_input_method_v2_send_deactivate(m_pWLRIME); + commitIMEState(ti.pWlrInput); + + wlr_text_input_v3_send_leave(ti.pWlrInput); + } else { + continue; + } + + } + + if (pSurface && wl_resource_get_client(ti.pWlrInput->resource) == wl_resource_get_client(pSurface->resource)) { + + if (m_pWLRIME) { + wlr_text_input_v3_send_enter(ti.pWlrInput, pSurface); + } else { + ti.pPendingSurface = pSurface; + } + + } + } +} \ No newline at end of file diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp new file mode 100644 index 00000000..4a9ea7b1 --- /dev/null +++ b/src/managers/input/InputMethodRelay.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "../../defines.hpp" +#include "../../helpers/WLClasses.hpp" + +class CInputMethodRelay { +public: + CInputMethodRelay(); + + void onNewIME(wlr_input_method_v2*); + void onNewTextInput(wlr_text_input_v3*); + + wlr_input_method_v2* m_pWLRIME = nullptr; + + void commitIMEState(wlr_text_input_v3*); + void removeTextInput(wlr_text_input_v3*); + + void onKeyboardFocus(wlr_surface*); + + STextInput* getFocusedTextInput(); + STextInput* getFocusableTextInput(); + +private: + + std::list m_lTextInputs; + + DYNLISTENER(textInputNew); + DYNLISTENER(IMECommit); + DYNLISTENER(IMEDestroy); + + void createNewTextInput(wlr_text_input_v3*); +}; \ No newline at end of file