diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e420570..dfda4d41 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -274,6 +274,7 @@ protocolNew("staging/alpha-modifier/alpha-modifier-v1.xml" "alpha-modifier-v1" f protocolNew("staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml" "ext-foreign-toplevel-list-v1" false) protocolNew("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml" "pointer-gestures-unstable-v1" false) protocolNew("unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml" "keyboard-shortcuts-inhibit-unstable-v1" false) +protocolNew("unstable/text-input/text-input-unstable-v3.xml" "text-input-unstable-v3" false) # tools add_subdirectory(hyprctl) diff --git a/protocols/meson.build b/protocols/meson.build index de6bbcce..7556fffc 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -51,6 +51,7 @@ new_protocols = [ [wl_protocol_dir, 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml'], [wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'], [wl_protocol_dir, 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/text-input/text-input-unstable-v3.xml'], ] wl_protos_src = [] diff --git a/src/Compositor.cpp b/src/Compositor.cpp index d7d2936e..3d441a95 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -230,8 +230,6 @@ void CCompositor::initServer() { wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry); wlr_xdg_foreign_v2_create(m_sWLDisplay, m_sWLRForeignRegistry); - m_sWLRTextInputMgr = wlr_text_input_manager_v3_create(m_sWLDisplay); - m_sWLRIMEMgr = wlr_input_method_manager_v2_create(m_sWLDisplay); m_sWLRActivation = wlr_xdg_activation_v1_create(m_sWLDisplay); @@ -289,7 +287,6 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer"); 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"); addWLSignal(&m_sWLRActivation->events.request_activate, &Events::listen_activateXDG, m_sWLRActivation, "ActivationV1"); addWLSignal(&m_sWLRSessionLockMgr->events.new_lock, &Events::listen_newSessionLock, m_sWLRSessionLockMgr, "SessionLockMgr"); @@ -337,7 +334,6 @@ void CCompositor::removeAllSignals() { removeWLSignal(&Events::listen_RendererDestroy); removeWLSignal(&Events::listen_powerMgrSetMode); removeWLSignal(&Events::listen_newIME); - removeWLSignal(&Events::listen_newTextInput); removeWLSignal(&Events::listen_activateXDG); removeWLSignal(&Events::listen_newSessionLock); diff --git a/src/Compositor.hpp b/src/Compositor.hpp index a2c8abba..c0845664 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -69,7 +69,6 @@ class CCompositor { wlr_xdg_foreign_registry* m_sWLRForeignRegistry; wlr_output_power_manager_v1* m_sWLROutputPowerMgr; wlr_input_method_manager_v2* m_sWLRIMEMgr; - wlr_text_input_manager_v3* m_sWLRTextInputMgr; wlr_xdg_activation_v1* m_sWLRActivation; wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf; wlr_backend* m_sWLRHeadlessBackend; diff --git a/src/events/Events.hpp b/src/events/Events.hpp index a49eb92e..69ca82d0 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -118,7 +118,6 @@ namespace Events { // IME LISTENER(newIME); - LISTENER(newTextInput); LISTENER(newVirtualKeyboard); // Touch diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index bd65adcd..93c3b66f 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -210,12 +210,6 @@ void Events::listener_newIME(wl_listener* listener, void* data) { 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); -} - void Events::listener_newSessionLock(wl_listener* listener, void* data) { Debug::log(LOG, "New session lock!"); diff --git a/src/includes.hpp b/src/includes.hpp index e78a1880..1935a268 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -87,7 +87,6 @@ extern "C" { #include #include #include -#include #include #include #include diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index a10032a0..7d93d46a 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -13,6 +13,7 @@ #include "../protocols/PointerGestures.hpp" #include "../protocols/ForeignToplevelWlr.hpp" #include "../protocols/ShortcutsInhibit.hpp" +#include "../protocols/TextInputV3.hpp" #include "tearing-control-v1.hpp" #include "fractional-scale-v1.hpp" @@ -27,6 +28,7 @@ #include "pointer-gestures-unstable-v1.hpp" #include "wlr-foreign-toplevel-management-unstable-v1.hpp" #include "keyboard-shortcuts-inhibit-unstable-v1.hpp" +#include "text-input-unstable-v3.hpp" CProtocolManager::CProtocolManager() { @@ -43,6 +45,7 @@ CProtocolManager::CProtocolManager() { PROTO::pointerGestures = std::make_unique(&zwp_pointer_gestures_v1_interface, 3, "PointerGestures"); PROTO::foreignToplevelWlr = std::make_unique(&zwlr_foreign_toplevel_manager_v1_interface, 3, "ForeignToplevelWlr"); PROTO::shortcutsInhibit = std::make_unique(&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1, "ShortcutsInhibit"); + PROTO::textInputV3 = std::make_unique(&zwp_text_input_manager_v3_interface, 1, "TextInputV3"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/managers/input/InputMethodRelay.cpp b/src/managers/input/InputMethodRelay.cpp index 17da87ea..4f1cd278 100644 --- a/src/managers/input/InputMethodRelay.cpp +++ b/src/managers/input/InputMethodRelay.cpp @@ -1,9 +1,12 @@ #include "InputMethodRelay.hpp" #include "InputManager.hpp" #include "../../Compositor.hpp" +#include "../../protocols/TextInputV3.hpp" CInputMethodRelay::CInputMethodRelay() { static auto P = g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast(param)); }); + + listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(ti); }); } void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) { @@ -142,8 +145,8 @@ CTextInput* CInputMethodRelay::getFocusedTextInput() { return nullptr; } -void CInputMethodRelay::onNewTextInput(wlr_text_input_v3* pInput) { - m_vTextInputs.emplace_back(std::make_unique(pInput)); +void CInputMethodRelay::onNewTextInput(std::any tiv3) { + m_vTextInputs.emplace_back(std::make_unique(std::any_cast>(tiv3))); } void CInputMethodRelay::onNewTextInput(STextInputV1* pTIV1) { diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp index 01d36675..1b2cb07a 100644 --- a/src/managers/input/InputMethodRelay.hpp +++ b/src/managers/input/InputMethodRelay.hpp @@ -3,8 +3,10 @@ #include #include "../../defines.hpp" #include "../../helpers/WLClasses.hpp" +#include "../../helpers/signal/Listener.hpp" #include "TextInput.hpp" #include "InputMethodPopup.hpp" +#include class CInputManager; class CHyprRenderer; @@ -15,7 +17,7 @@ class CInputMethodRelay { CInputMethodRelay(); void onNewIME(wlr_input_method_v2*); - void onNewTextInput(wlr_text_input_v3*); + void onNewTextInput(std::any tiv3); void onNewTextInput(STextInputV1* pTIV1); wlr_input_method_v2* m_pWLRIME = nullptr; @@ -47,6 +49,10 @@ class CInputMethodRelay { wlr_surface* m_pLastKbFocus = nullptr; + struct { + CHyprSignalListener newTIV3; + } listeners; + DYNLISTENER(textInputNew); DYNLISTENER(IMECommit); DYNLISTENER(IMEDestroy); diff --git a/src/managers/input/TextInput.cpp b/src/managers/input/TextInput.cpp index be26d39c..12f13971 100644 --- a/src/managers/input/TextInput.cpp +++ b/src/managers/input/TextInput.cpp @@ -3,13 +3,14 @@ #include "InputManager.hpp" #include "../../protocols/TextInputV1.hpp" #include "../../Compositor.hpp" +#include "../../protocols/TextInputV3.hpp" CTextInput::CTextInput(STextInputV1* ti) : pV1Input(ti) { ti->pTextInput = this; initCallbacks(); } -CTextInput::CTextInput(wlr_text_input_v3* ti) : pWlrInput(ti) { +CTextInput::CTextInput(std::weak_ptr ti) : pV3Input(ti) { initCallbacks(); } @@ -25,31 +26,42 @@ void CTextInput::tiV1Destroyed() { } void CTextInput::initCallbacks() { - hyprListener_textInputEnable.initCallback( - isV3() ? &pWlrInput->events.enable : &pV1Input->sEnable, [this](void* owner, void* data) { onEnabled(); }, this, "textInput"); + if (isV3()) { + const auto INPUT = pV3Input.lock(); - hyprListener_textInputCommit.initCallback( - isV3() ? &pWlrInput->events.commit : &pV1Input->sCommit, [this](void* owner, void* data) { onCommit(); }, this, "textInput"); - - hyprListener_textInputDisable.initCallback( - isV3() ? &pWlrInput->events.disable : &pV1Input->sDisable, [this](void* owner, void* data) { onDisabled(); }, this, "textInput"); - - hyprListener_textInputDestroy.initCallback( - isV3() ? &pWlrInput->events.destroy : &pV1Input->sDestroy, - [this](void* owner, void* data) { - if (pWlrInput && pWlrInput->current_enabled && focusedSurface()) + listeners.enable = INPUT->events.enable.registerListener([this](std::any p) { onEnabled(); }); + listeners.disable = INPUT->events.disable.registerListener([this](std::any p) { onDisabled(); }); + listeners.commit = INPUT->events.onCommit.registerListener([this](std::any p) { onCommit(); }); + listeners.destroy = INPUT->events.destroy.registerListener([this](std::any p) { + const auto INPUT = pV3Input.lock(); + if (INPUT && INPUT->current.enabled && focusedSurface()) g_pInputManager->m_sIMERelay.deactivateIME(this); - - hyprListener_textInputCommit.removeCallback(); - hyprListener_textInputDestroy.removeCallback(); - hyprListener_textInputDisable.removeCallback(); - hyprListener_textInputEnable.removeCallback(); - hyprListener_surfaceDestroyed.removeCallback(); - hyprListener_surfaceUnmapped.removeCallback(); - g_pInputManager->m_sIMERelay.removeTextInput(this); - }, - this, "textInput"); + }); + } else { + hyprListener_textInputEnable.initCallback( + &pV1Input->sEnable, [this](void* owner, void* data) { onEnabled(); }, this, "textInput"); + + hyprListener_textInputCommit.initCallback( + &pV1Input->sCommit, [this](void* owner, void* data) { onCommit(); }, this, "textInput"); + + hyprListener_textInputDisable.initCallback( + &pV1Input->sDisable, [this](void* owner, void* data) { onDisabled(); }, this, "textInput"); + + hyprListener_textInputDestroy.initCallback( + &pV1Input->sDestroy, + [this](void* owner, void* data) { + hyprListener_textInputCommit.removeCallback(); + hyprListener_textInputDestroy.removeCallback(); + hyprListener_textInputDisable.removeCallback(); + hyprListener_textInputEnable.removeCallback(); + hyprListener_surfaceDestroyed.removeCallback(); + hyprListener_surfaceUnmapped.removeCallback(); + + g_pInputManager->m_sIMERelay.removeTextInput(this); + }, + this, "textInput"); + } } void CTextInput::onEnabled(wlr_surface* surfV1) { @@ -96,7 +108,7 @@ void CTextInput::onCommit() { return; } - if (!(pWlrInput ? pWlrInput->current_enabled : pV1Input->active)) { + if (!(isV3() ? pV3Input.lock()->current.enabled : pV1Input->active)) { Debug::log(WARN, "Disabled TextInput commit?"); return; } @@ -144,7 +156,7 @@ void CTextInput::setFocusedSurface(wlr_surface* pSurface) { } bool CTextInput::isV3() { - return pWlrInput; + return !pV1Input; } void CTextInput::enter(wlr_surface* pSurface) { @@ -166,8 +178,8 @@ void CTextInput::enter(wlr_surface* pSurface) { enterLocks = 1; } - if (pWlrInput) - wlr_text_input_v3_send_enter(pWlrInput, pSurface); + if (isV3()) + pV3Input.lock()->enter(pSurface); else { zwp_text_input_v1_send_enter(pV1Input->resourceImpl, pSurface->resource); pV1Input->active = true; @@ -186,8 +198,8 @@ void CTextInput::leave() { enterLocks = 0; } - if (pWlrInput && pWlrInput->focused_surface) - wlr_text_input_v3_send_leave(pWlrInput); + if (isV3() && focusedSurface()) + pV3Input.lock()->leave(focusedSurface()); else if (focusedSurface() && pV1Input) { zwp_text_input_v1_send_leave(pV1Input->resourceImpl); pV1Input->active = false; @@ -199,22 +211,24 @@ void CTextInput::leave() { } wlr_surface* CTextInput::focusedSurface() { - return pWlrInput ? pWlrInput->focused_surface : pFocusedSurface; + return pFocusedSurface; } wl_client* CTextInput::client() { - return pWlrInput ? wl_resource_get_client(pWlrInput->resource) : pV1Input->client; + return isV3() ? pV3Input.lock()->client() : pV1Input->client; } void CTextInput::commitStateToIME(wlr_input_method_v2* ime) { if (isV3()) { - if (pWlrInput->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) - wlr_input_method_v2_send_surrounding_text(ime, pWlrInput->current.surrounding.text, pWlrInput->current.surrounding.cursor, pWlrInput->current.surrounding.anchor); + const auto INPUT = pV3Input.lock(); - wlr_input_method_v2_send_text_change_cause(ime, pWlrInput->current.text_change_cause); + if (INPUT->current.surrounding.updated) + wlr_input_method_v2_send_surrounding_text(ime, INPUT->current.surrounding.text.c_str(), INPUT->current.surrounding.cursor, INPUT->current.surrounding.anchor); - if (pWlrInput->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) - wlr_input_method_v2_send_content_type(ime, pWlrInput->current.content_type.hint, pWlrInput->current.content_type.purpose); + wlr_input_method_v2_send_text_change_cause(ime, INPUT->current.cause); + + if (INPUT->current.contentType.updated) + wlr_input_method_v2_send_content_type(ime, INPUT->current.contentType.hint, INPUT->current.contentType.purpose); } else { if (pV1Input->pendingSurrounding.isPending) wlr_input_method_v2_send_surrounding_text(ime, pV1Input->pendingSurrounding.text.c_str(), pV1Input->pendingSurrounding.cursor, pV1Input->pendingSurrounding.anchor); @@ -232,19 +246,18 @@ void CTextInput::commitStateToIME(wlr_input_method_v2* ime) { void CTextInput::updateIMEState(wlr_input_method_v2* ime) { if (isV3()) { - if (ime->current.preedit.text) { - wlr_text_input_v3_send_preedit_string(pWlrInput, ime->current.preedit.text, ime->current.preedit.cursor_begin, ime->current.preedit.cursor_end); - } + const auto INPUT = pV3Input.lock(); - if (ime->current.commit_text) { - wlr_text_input_v3_send_commit_string(pWlrInput, ime->current.commit_text); - } + if (ime->current.preedit.text) + INPUT->preeditString(ime->current.preedit.text, ime->current.preedit.cursor_begin, ime->current.preedit.cursor_end); - if (ime->current.delete_.before_length || ime->current.delete_.after_length) { - wlr_text_input_v3_send_delete_surrounding_text(pWlrInput, ime->current.delete_.before_length, ime->current.delete_.after_length); - } + if (ime->current.commit_text) + INPUT->commitString(ime->current.commit_text); - wlr_text_input_v3_send_done(pWlrInput); + if (ime->current.delete_.before_length || ime->current.delete_.after_length) + INPUT->deleteSurroundingText(ime->current.delete_.before_length, ime->current.delete_.after_length); + + INPUT->sendDone(); } else { if (ime->current.preedit.text) { zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preedit.cursor_begin); @@ -271,9 +284,9 @@ void CTextInput::updateIMEState(wlr_input_method_v2* ime) { } bool CTextInput::hasCursorRectangle() { - return !isV3() || pWlrInput->current.features & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; + return !isV3() || pV3Input.lock()->current.box.updated; } CBox CTextInput::cursorBox() { - return CBox{isV3() ? pWlrInput->current.cursor_rectangle : pV1Input->cursorRectangle}; + return CBox{isV3() ? pV3Input.lock()->current.box.cursorBox : pV1Input->cursorRectangle}; } \ No newline at end of file diff --git a/src/managers/input/TextInput.hpp b/src/managers/input/TextInput.hpp index 4acfb960..6d7e9c59 100644 --- a/src/managers/input/TextInput.hpp +++ b/src/managers/input/TextInput.hpp @@ -3,17 +3,19 @@ #include "../../helpers/WLListener.hpp" #include "../../macros.hpp" #include "../../helpers/Box.hpp" +#include "../../helpers/signal/Listener.hpp" +#include -struct wlr_text_input_v3; struct wlr_surface; struct wl_client; struct STextInputV1; +class CTextInputV3; class CTextInput { public: + CTextInput(std::weak_ptr ti); CTextInput(STextInputV1* ti); - CTextInput(wlr_text_input_v3* ti); ~CTextInput(); bool isV3(); @@ -34,13 +36,13 @@ class CTextInput { wlr_surface* focusedSurface(); private: - void setFocusedSurface(wlr_surface* pSurface); - void initCallbacks(); + void setFocusedSurface(wlr_surface* pSurface); + void initCallbacks(); - wlr_surface* pFocusedSurface = nullptr; - int enterLocks = 0; - wlr_text_input_v3* pWlrInput = nullptr; - STextInputV1* pV1Input = nullptr; + wlr_surface* pFocusedSurface = nullptr; + int enterLocks = 0; + std::weak_ptr pV3Input; + STextInputV1* pV1Input = nullptr; DYNLISTENER(textInputEnable); DYNLISTENER(textInputDisable); @@ -48,4 +50,11 @@ class CTextInput { DYNLISTENER(textInputDestroy); DYNLISTENER(surfaceUnmapped); DYNLISTENER(surfaceDestroyed); + + struct { + CHyprSignalListener enable; + CHyprSignalListener disable; + CHyprSignalListener commit; + CHyprSignalListener destroy; + } listeners; }; \ No newline at end of file diff --git a/src/protocols/TextInputV3.cpp b/src/protocols/TextInputV3.cpp new file mode 100644 index 00000000..fbb5c3ae --- /dev/null +++ b/src/protocols/TextInputV3.cpp @@ -0,0 +1,129 @@ +#include "TextInputV3.hpp" +#include + +#define LOGM PROTO::textInputV3->protoLog + +void CTextInputV3::SState::reset() { + cause = ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD; + surrounding.updated = false; + contentType.updated = false; + box.updated = false; + enabled = false; +} + +CTextInputV3::CTextInputV3(SP resource_) : resource(resource_) { + if (!resource->resource()) + return; + + LOGM(LOG, "New tiv3 at {:016x}", (uintptr_t)this); + + resource->setDestroy([this](CZwpTextInputV3* r) { PROTO::textInputV3->destroyTextInput(this); }); + resource->setOnDestroy([this](CZwpTextInputV3* r) { PROTO::textInputV3->destroyTextInput(this); }); + + resource->setCommit([this](CZwpTextInputV3* r) { + current = pending; + events.onCommit.emit(); + serial++; + }); + + resource->setSetSurroundingText([this](CZwpTextInputV3* r, const char* text, int32_t cursor, int32_t anchor) { + pending.surrounding.updated = true; + pending.surrounding.anchor = anchor; + pending.surrounding.cursor = cursor; + pending.surrounding.text = text; + }); + + resource->setSetTextChangeCause([this](CZwpTextInputV3* r, zwpTextInputV3ChangeCause cause) { pending.cause = cause; }); + + resource->setSetContentType([this](CZwpTextInputV3* r, zwpTextInputV3ContentHint hint, zwpTextInputV3ContentPurpose purpose) { + pending.contentType.updated = true; + pending.contentType.hint = hint; + pending.contentType.purpose = purpose; + }); + + resource->setSetCursorRectangle([this](CZwpTextInputV3* r, int32_t x, int32_t y, int32_t w, int32_t h) { + pending.box.updated = true; + pending.box.cursorBox = {x, y, w, h}; + }); + + resource->setEnable([this](CZwpTextInputV3* r) { + events.enable.emit(); + pending.enabled = true; + }); + + resource->setDisable([this](CZwpTextInputV3* r) { + events.disable.emit(); + pending.enabled = false; + pending.reset(); + }); +} + +CTextInputV3::~CTextInputV3() { + events.destroy.emit(); +} + +void CTextInputV3::enter(wlr_surface* surf) { + resource->sendEnter(surf->resource); +} + +void CTextInputV3::leave(wlr_surface* surf) { + resource->sendLeave(surf->resource); +} + +void CTextInputV3::preeditString(const std::string& text, int32_t cursorBegin, int32_t cursorEnd) { + resource->sendPreeditString(text.c_str(), cursorBegin, cursorEnd); +} + +void CTextInputV3::commitString(const std::string& text) { + resource->sendCommitString(text.c_str()); +} + +void CTextInputV3::deleteSurroundingText(uint32_t beforeLength, uint32_t afterLength) { + resource->sendDeleteSurroundingText(beforeLength, afterLength); +} + +void CTextInputV3::sendDone() { + resource->sendDone(serial); +} + +bool CTextInputV3::good() { + return resource->resource(); +} + +wl_client* CTextInputV3::client() { + return wl_resource_get_client(resource->resource()); +} + +CTextInputV3Protocol::CTextInputV3Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CTextInputV3Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(std::make_unique(client, ver, id)).get(); + RESOURCE->setOnDestroy([this](CZwpTextInputManagerV3* p) { this->onManagerResourceDestroy(p->resource()); }); + + RESOURCE->setDestroy([this](CZwpTextInputManagerV3* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); + RESOURCE->setGetTextInput([this](CZwpTextInputManagerV3* pMgr, uint32_t id, wl_resource* seat) { this->onGetTextInput(pMgr, id, seat); }); +} + +void CTextInputV3Protocol::onManagerResourceDestroy(wl_resource* res) { + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); +} + +void CTextInputV3Protocol::destroyTextInput(CTextInputV3* input) { + std::erase_if(m_vTextInputs, [&](const auto& other) { return other.get() == input; }); +} + +void CTextInputV3Protocol::onGetTextInput(CZwpTextInputManagerV3* pMgr, uint32_t id, wl_resource* seat) { + const auto CLIENT = wl_resource_get_client(pMgr->resource()); + const auto RESOURCE = m_vTextInputs.emplace_back(std::make_shared(std::make_shared(CLIENT, wl_resource_get_version(pMgr->resource()), id))); + + if (!RESOURCE->good()) { + wl_resource_post_no_memory(pMgr->resource()); + m_vTextInputs.pop_back(); + LOGM(ERR, "Failed to create a tiv3 resource"); + return; + } + + events.newTextInput.emit(std::weak_ptr(RESOURCE)); +} \ No newline at end of file diff --git a/src/protocols/TextInputV3.hpp b/src/protocols/TextInputV3.hpp new file mode 100644 index 00000000..6d4f3d54 --- /dev/null +++ b/src/protocols/TextInputV3.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "text-input-unstable-v3.hpp" +#include "../helpers/signal/Signal.hpp" +#include "../helpers/Box.hpp" + +class CTextInputV3 { + public: + CTextInputV3(SP resource_); + ~CTextInputV3(); + + void enter(wlr_surface* surf); + void leave(wlr_surface* surf); + void preeditString(const std::string& text, int32_t cursorBegin, int32_t cursorEnd); + void commitString(const std::string& text); + void deleteSurroundingText(uint32_t beforeLength, uint32_t afterLength); + void sendDone(); + + bool good(); + + wl_client* client(); + + struct { + CSignal onCommit; + CSignal enable; + CSignal disable; + CSignal destroy; + } events; + + struct SState { + struct { + bool updated = false; + std::string text = ""; + uint32_t cursor = 0; + uint32_t anchor = 0; + } surrounding; + + struct { + bool updated = false; + zwpTextInputV3ContentHint hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE; + zwpTextInputV3ContentPurpose purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; + } contentType; + + struct { + bool updated = false; + CBox cursorBox; + } box; + + bool enabled = false; + + zwpTextInputV3ChangeCause cause = ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD; + + void reset(); + }; + SState pending, current; + + private: + SP resource; + + int serial = 0; +}; + +class CTextInputV3Protocol : public IWaylandProtocol { + public: + CTextInputV3Protocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + struct { + CSignal newTextInput; // WP + } events; + + private: + void onManagerResourceDestroy(wl_resource* res); + void destroyTextInput(CTextInputV3* input); + void onGetTextInput(CZwpTextInputManagerV3* pMgr, uint32_t id, wl_resource* seat); + + // + std::vector> m_vManagers; + std::vector> m_vTextInputs; + + friend class CTextInputV3; +}; + +namespace PROTO { + inline UP textInputV3; +}; \ No newline at end of file