diff --git a/CMakeLists.txt b/CMakeLists.txt index a5ea06a68d3..9b3259505d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,5 +140,6 @@ target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.o ${CMAKE_SOURCE_DIR}/hyprland-toplevel-export-v1-protocol.o ${CMAKE_SOURCE_DIR}/fractional-scale-v1-protocol.o + ${CMAKE_SOURCE_DIR}/text-input-unstable-v1-protocol.o ${CMAKE_SOURCE_DIR}/subprojects/udis86/build/libudis86/liblibudis86.a ) diff --git a/Makefile b/Makefile index 12613b273d0..4aaed5e9daa 100644 --- a/Makefile +++ b/Makefile @@ -131,6 +131,16 @@ fractional-scale-v1-protocol.c: fractional-scale-v1-protocol.o: fractional-scale-v1-protocol.h +text-input-unstable-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/unstable/text-input/text-input-unstable-v1.xml $@ + +text-input-unstable-v1-protocol.c: + $(WAYLAND_SCANNER) private-code \ + $(WAYLAND_PROTOCOLS)/unstable/text-input/text-input-unstable-v1.xml $@ + +text-input-unstable-v1-protocol.o: text-input-unstable-v1-protocol.h + legacyrenderer: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DLEGACY_RENDERER:STRING=true -H./ -B./build -G Ninja cmake --build ./build --config Release --target all -j$(shell nproc) @@ -196,7 +206,7 @@ uninstall: rm -f ${PREFIX}/share/man/man1/Hyprland.1 rm -f ${PREFIX}/share/man/man1/hyprctl.1 -protocols: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o wlr-screencopy-unstable-v1-protocol.o idle-protocol.o ext-workspace-unstable-v1-protocol.o pointer-constraints-unstable-v1-protocol.o tablet-unstable-v2-protocol.o wlr-output-power-management-unstable-v1-protocol.o linux-dmabuf-unstable-v1-protocol.o hyprland-toplevel-export-v1-protocol.o wlr-foreign-toplevel-management-unstable-v1-protocol.o fractional-scale-v1-protocol.o +protocols: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o wlr-screencopy-unstable-v1-protocol.o idle-protocol.o ext-workspace-unstable-v1-protocol.o pointer-constraints-unstable-v1-protocol.o tablet-unstable-v2-protocol.o wlr-output-power-management-unstable-v1-protocol.o linux-dmabuf-unstable-v1-protocol.o hyprland-toplevel-export-v1-protocol.o wlr-foreign-toplevel-management-unstable-v1-protocol.o fractional-scale-v1-protocol.o text-input-unstable-v1-protocol.o fixwlr: sed -i -E 's/(soversion = 12)([^032]|$$)/soversion = 12032/g' subprojects/wlroots/meson.build diff --git a/protocols/meson.build b/protocols/meson.build index 9d47e0a6041..47b2dcaeb8e 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -21,6 +21,7 @@ wayland_scanner = find_program( protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'], [wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index 15646c30ef4..8a96c6ff865 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -297,8 +297,11 @@ struct SSwipeGesture { CMonitor* pMonitor = nullptr; }; +struct STextInputV1; + struct STextInput { wlr_text_input_v3* pWlrInput = nullptr; + STextInputV1* pV1Input = nullptr; wlr_surface* pPendingSurface = nullptr; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 4dd9ed79a5c..6271b0ca363 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -3,4 +3,5 @@ CProtocolManager::CProtocolManager() { m_pToplevelExportProtocolManager = std::make_unique(); m_pFractionalScaleProtocolManager = std::make_unique(); + m_pTextInputV1ProtocolManager = std::make_unique(); } \ No newline at end of file diff --git a/src/managers/ProtocolManager.hpp b/src/managers/ProtocolManager.hpp index 66d8ef2045b..6160176d881 100644 --- a/src/managers/ProtocolManager.hpp +++ b/src/managers/ProtocolManager.hpp @@ -3,6 +3,7 @@ #include "../defines.hpp" #include "../protocols/ToplevelExport.hpp" #include "../protocols/FractionalScale.hpp" +#include "../protocols/TextInputV1.hpp" class CProtocolManager { public: @@ -10,6 +11,7 @@ class CProtocolManager { std::unique_ptr m_pToplevelExportProtocolManager; std::unique_ptr m_pFractionalScaleProtocolManager; + std::unique_ptr m_pTextInputV1ProtocolManager; }; inline std::unique_ptr g_pProtocolManager; diff --git a/src/managers/input/InputMethodRelay.cpp b/src/managers/input/InputMethodRelay.cpp index 95f0b6fbcac..f8fa6b0943e 100644 --- a/src/managers/input/InputMethodRelay.cpp +++ b/src/managers/input/InputMethodRelay.cpp @@ -28,20 +28,41 @@ void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) { return; } - 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 (PTI->pWlrInput) { + 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.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); - } + 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); + wlr_text_input_v3_send_done(PTI->pWlrInput); + } else { + if (PIMR->m_pWLRIME->current.preedit.text) { + zwp_text_input_v1_send_preedit_cursor(PTI->pV1Input->resourceImpl, 0); + zwp_text_input_v1_send_preedit_styling(PTI->pV1Input->resourceImpl, 0, std::string(PIMR->m_pWLRIME->current.preedit.text).length() - 1, + ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_ACTIVE); + zwp_text_input_v1_send_preedit_string(PTI->pV1Input->resourceImpl, PTI->pV1Input->serial, PIMR->m_pWLRIME->current.preedit.text, ""); + } + + if (PIMR->m_pWLRIME->current.commit_text) { + zwp_text_input_v1_send_commit_string(PTI->pV1Input->resourceImpl, PTI->pV1Input->serial, PIMR->m_pWLRIME->current.commit_text); + } + + if (PIMR->m_pWLRIME->current.delete_.before_length || PIMR->m_pWLRIME->current.delete_.after_length) { + zwp_text_input_v1_send_delete_surrounding_text(PTI->pV1Input->resourceImpl, PIMR->m_pWLRIME->current.delete_.before_length, + PIMR->m_pWLRIME->current.delete_.after_length); + + if (PIMR->m_pWLRIME->current.preedit.text) + zwp_text_input_v1_send_commit_string(PTI->pV1Input->resourceImpl, PTI->pV1Input->serial, PIMR->m_pWLRIME->current.preedit.text); + } + } }, this, "IMERelay"); @@ -62,9 +83,12 @@ void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) { Debug::log(LOG, "IME Destroy"); if (PTI) { - setPendingSurface(PTI, PTI->pWlrInput->focused_surface); + setPendingSurface(PTI, focusedSurface(PTI)); - wlr_text_input_v3_send_leave(PTI->pWlrInput); + if (PTI->pWlrInput) + wlr_text_input_v3_send_leave(PTI->pWlrInput); + else + zwp_text_input_v1_send_leave(PTI->pV1Input->resourceImpl); } }, this, "IMERelay"); @@ -122,11 +146,19 @@ void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) { const auto PTI = getFocusableTextInput(); if (PTI) { - wlr_text_input_v3_send_enter(PTI->pWlrInput, PTI->pPendingSurface); + if (PTI->pWlrInput) + wlr_text_input_v3_send_enter(PTI->pWlrInput, PTI->pPendingSurface); + else + zwp_text_input_v1_send_enter(PTI->pV1Input->resourceImpl, PTI->pPendingSurface->resource); + setPendingSurface(PTI, nullptr); } } +wlr_surface* CInputMethodRelay::focusedSurface(STextInput* pTI) { + return pTI->pWlrInput ? pTI->pWlrInput->focused_surface : pTI->pV1Input->focusedSurface; +} + void CInputMethodRelay::updateInputPopup(SIMEPopup* pPopup) { if (!pPopup->pSurface->mapped) return; @@ -136,12 +168,12 @@ void CInputMethodRelay::updateInputPopup(SIMEPopup* pPopup) { const auto PFOCUSEDTI = getFocusedTextInput(); - if (!PFOCUSEDTI || !PFOCUSEDTI->pWlrInput->focused_surface) + if (!PFOCUSEDTI || !focusedSurface(PFOCUSEDTI)) return; - bool cursorRect = PFOCUSEDTI->pWlrInput->current.features & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; - const auto PFOCUSEDSURFACE = PFOCUSEDTI->pWlrInput->focused_surface; - auto cursorBox = PFOCUSEDTI->pWlrInput->current.cursor_rectangle; + bool cursorRect = PFOCUSEDTI->pWlrInput ? PFOCUSEDTI->pWlrInput->current.features & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE : true; + const auto PFOCUSEDSURFACE = focusedSurface(PFOCUSEDTI); + auto cursorBox = PFOCUSEDTI->pWlrInput ? PFOCUSEDTI->pWlrInput->current.cursor_rectangle : PFOCUSEDTI->pV1Input->cursorRectangle; Vector2D parentPos; Vector2D parentSize; @@ -238,12 +270,12 @@ void CInputMethodRelay::damagePopup(SIMEPopup* pPopup) { const auto PFOCUSEDTI = getFocusedTextInput(); - if (!PFOCUSEDTI || !PFOCUSEDTI->pWlrInput->focused_surface) + if (!PFOCUSEDTI || !focusedSurface(PFOCUSEDTI)) return; Vector2D parentPos; - const auto PFOCUSEDSURFACE = PFOCUSEDTI->pWlrInput->focused_surface; + const auto PFOCUSEDSURFACE = focusedSurface(PFOCUSEDTI); if (wlr_layer_surface_v1_try_from_wlr_surface(PFOCUSEDSURFACE)) { const auto PLS = g_pCompositor->getLayerSurfaceFromWlr(wlr_layer_surface_v1_try_from_wlr_surface(PFOCUSEDSURFACE)); @@ -279,8 +311,9 @@ SIMEKbGrab* CInputMethodRelay::getIMEKeyboardGrab(SKeyboard* pKeyboard) { } STextInput* CInputMethodRelay::getFocusedTextInput() { + for (auto& ti : m_lTextInputs) { - if (ti.pWlrInput->focused_surface) { + if (focusedSurface(&ti)) { return &ti; } } @@ -302,13 +335,17 @@ void CInputMethodRelay::onNewTextInput(wlr_text_input_v3* pInput) { createNewTextInput(pInput); } -void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { +void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput, STextInputV1* pTIV1) { const auto PTEXTINPUT = &m_lTextInputs.emplace_back(); PTEXTINPUT->pWlrInput = pInput; + PTEXTINPUT->pV1Input = pTIV1; + + if (pTIV1) + pTIV1->pTextInput = PTEXTINPUT; PTEXTINPUT->hyprListener_textInputEnable.initCallback( - &pInput->events.enable, + pInput ? &pInput->events.enable : &pTIV1->sEnable, [](void* owner, void* data) { const auto PINPUT = (STextInput*)owner; @@ -320,12 +357,12 @@ void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { Debug::log(LOG, "Enable TextInput"); wlr_input_method_v2_send_activate(g_pInputManager->m_sIMERelay.m_pWLRIME); - g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT); }, PTEXTINPUT, "textInput"); PTEXTINPUT->hyprListener_textInputCommit.initCallback( - &pInput->events.commit, + pInput ? &pInput->events.commit : &pTIV1->sCommit, [](void* owner, void* data) { const auto PINPUT = (STextInput*)owner; @@ -339,12 +376,12 @@ void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { return; } - g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT); }, PTEXTINPUT, "textInput"); PTEXTINPUT->hyprListener_textInputDisable.initCallback( - &pInput->events.disable, + pInput ? &pInput->events.disable : &pTIV1->sDisable, [](void* owner, void* data) { const auto PINPUT = (STextInput*)owner; @@ -357,12 +394,12 @@ void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { wlr_input_method_v2_send_deactivate(g_pInputManager->m_sIMERelay.m_pWLRIME); - g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT); }, PTEXTINPUT, "textInput"); PTEXTINPUT->hyprListener_textInputDestroy.initCallback( - &pInput->events.destroy, + pInput ? &pInput->events.destroy : &pTIV1->sDestroy, [](void* owner, void* data) { const auto PINPUT = (STextInput*)owner; @@ -371,10 +408,10 @@ void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { return; } - if (PINPUT->pWlrInput->current_enabled) { + if (PINPUT->pWlrInput && PINPUT->pWlrInput->current_enabled) { wlr_input_method_v2_send_deactivate(g_pInputManager->m_sIMERelay.m_pWLRIME); - g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT); } g_pInputManager->m_sIMERelay.setPendingSurface(PINPUT, nullptr); @@ -384,26 +421,40 @@ void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { PINPUT->hyprListener_textInputDisable.removeCallback(); PINPUT->hyprListener_textInputEnable.removeCallback(); - g_pInputManager->m_sIMERelay.removeTextInput(PINPUT->pWlrInput); + g_pInputManager->m_sIMERelay.removeTextInput(PINPUT); }, PTEXTINPUT, "textInput"); } -void CInputMethodRelay::removeTextInput(wlr_text_input_v3* pInput) { - m_lTextInputs.remove_if([&](const auto& other) { return other.pWlrInput == pInput; }); +void CInputMethodRelay::removeTextInput(STextInput* pInput) { + m_lTextInputs.remove_if([&](const auto& other) { return other.pWlrInput == pInput->pWlrInput && other.pV1Input == pInput->pV1Input; }); } -void CInputMethodRelay::commitIMEState(wlr_text_input_v3* pInput) { +void CInputMethodRelay::commitIMEState(STextInput* pInput) { if (!m_pWLRIME) return; - if (pInput->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) - wlr_input_method_v2_send_surrounding_text(m_pWLRIME, pInput->current.surrounding.text, pInput->current.surrounding.cursor, pInput->current.surrounding.anchor); + if (pInput->pWlrInput) { + // V3 + if (pInput->pWlrInput->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) + wlr_input_method_v2_send_surrounding_text(m_pWLRIME, pInput->pWlrInput->current.surrounding.text, pInput->pWlrInput->current.surrounding.cursor, + pInput->pWlrInput->current.surrounding.anchor); + + wlr_input_method_v2_send_text_change_cause(m_pWLRIME, pInput->pWlrInput->current.text_change_cause); + + if (pInput->pWlrInput->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) + wlr_input_method_v2_send_content_type(m_pWLRIME, pInput->pWlrInput->current.content_type.hint, pInput->pWlrInput->current.content_type.purpose); + } else { + // V1 + if (pInput->pV1Input->pendingSurrounding.isPending) + wlr_input_method_v2_send_surrounding_text(m_pWLRIME, pInput->pV1Input->pendingSurrounding.text.c_str(), pInput->pV1Input->pendingSurrounding.cursor, + pInput->pV1Input->pendingSurrounding.anchor); - wlr_input_method_v2_send_text_change_cause(m_pWLRIME, pInput->current.text_change_cause); + wlr_input_method_v2_send_text_change_cause(m_pWLRIME, 0); - if (pInput->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) - wlr_input_method_v2_send_content_type(m_pWLRIME, pInput->current.content_type.hint, pInput->current.content_type.purpose); + if (pInput->pV1Input->pendingContentType.isPending) + wlr_input_method_v2_send_content_type(m_pWLRIME, pInput->pV1Input->pendingContentType.hint, pInput->pV1Input->pendingContentType.purpose); + } for (auto& p : m_lIMEPopups) { updateInputPopup(&p); @@ -416,28 +467,40 @@ void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) { if (!m_pWLRIME) return; + auto client = [](STextInput* pTI) -> wl_client* { return pTI->pWlrInput ? wl_resource_get_client(pTI->pWlrInput->resource) : pTI->pV1Input->client; }; + for (auto& ti : m_lTextInputs) { if (ti.pPendingSurface) { if (pSurface != ti.pPendingSurface) setPendingSurface(&ti, nullptr); - } else if (ti.pWlrInput->focused_surface) { + } else if (focusedSurface(&ti)) { - if (pSurface != ti.pWlrInput->focused_surface) { + if (pSurface != focusedSurface(&ti)) { wlr_input_method_v2_send_deactivate(m_pWLRIME); - commitIMEState(ti.pWlrInput); - - wlr_text_input_v3_send_leave(ti.pWlrInput); + commitIMEState(&ti); + + if (ti.pWlrInput) + wlr_text_input_v3_send_leave(ti.pWlrInput); + else { + zwp_text_input_v1_send_leave(ti.pV1Input->resourceImpl); + ti.pV1Input->focusedSurface = nullptr; + } } else { continue; } } - if (pSurface && wl_resource_get_client(ti.pWlrInput->resource) == wl_resource_get_client(pSurface->resource)) { + if (pSurface && client(&ti) == wl_resource_get_client(pSurface->resource)) { if (m_pWLRIME) { - wlr_text_input_v3_send_enter(ti.pWlrInput, pSurface); + if (ti.pWlrInput) + wlr_text_input_v3_send_enter(ti.pWlrInput, pSurface); + else { + zwp_text_input_v1_send_enter(ti.pV1Input->resourceImpl, pSurface->resource); + ti.pV1Input->focusedSurface = pSurface; + } } else { setPendingSurface(&ti, pSurface); } diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp index 767464875d5..83332285291 100644 --- a/src/managers/input/InputMethodRelay.hpp +++ b/src/managers/input/InputMethodRelay.hpp @@ -4,6 +4,7 @@ #include "../../helpers/WLClasses.hpp" class CInputManager; +struct STextInputV1; class CInputMethodRelay { public: @@ -14,8 +15,8 @@ class CInputMethodRelay { wlr_input_method_v2* m_pWLRIME = nullptr; - void commitIMEState(wlr_text_input_v3*); - void removeTextInput(wlr_text_input_v3*); + void commitIMEState(STextInput* pInput); + void removeTextInput(STextInput* pInput); void onKeyboardFocus(wlr_surface*); @@ -43,8 +44,10 @@ class CInputMethodRelay { DYNLISTENER(IMEGrab); DYNLISTENER(IMENewPopup); - void createNewTextInput(wlr_text_input_v3*); + void createNewTextInput(wlr_text_input_v3*, STextInputV1* tiv1 = nullptr); + wlr_surface* focusedSurface(STextInput* pInput); friend class CHyprRenderer; friend class CInputManager; + friend class CTextInputV1ProtocolManager; }; \ No newline at end of file diff --git a/src/protocols/TextInputV1.cpp b/src/protocols/TextInputV1.cpp new file mode 100644 index 00000000000..f50f3298042 --- /dev/null +++ b/src/protocols/TextInputV1.cpp @@ -0,0 +1,216 @@ +#include "TextInputV1.hpp" + +#include "../Compositor.hpp" + +#define TEXT_INPUT_VERSION 1 + +static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->bindManager(client, data, version, id); +} + +static void handleDisplayDestroy(struct wl_listener* listener, void* data) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->displayDestroy(); +} + +void CTextInputV1ProtocolManager::displayDestroy() { + wl_global_destroy(m_pGlobal); +} + +CTextInputV1ProtocolManager::CTextInputV1ProtocolManager() { + m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &zwp_text_input_manager_v1_interface, TEXT_INPUT_VERSION, this, bindManagerInt); + + if (!m_pGlobal) { + Debug::log(ERR, "TextInputV1Manager could not start!"); + return; + } + + m_liDisplayDestroy.notify = handleDisplayDestroy; + wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy); + + Debug::log(LOG, "TextInputV1Manager started successfully!"); +} + +static void createTI(wl_client* client, wl_resource* resource, uint32_t id) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->createTI(client, resource, id); +} + +static const struct zwp_text_input_manager_v1_interface textInputManagerImpl = { + .create_text_input = createTI, +}; + +void CTextInputV1ProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) { + const auto RESOURCE = wl_resource_create(client, &zwp_text_input_manager_v1_interface, version, id); + wl_resource_set_implementation(RESOURCE, &textInputManagerImpl, this, nullptr); + + Debug::log(LOG, "TextInputV1Manager bound successfully!"); +} + +// + +static void handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleActivate(client, resource, seat, surface); +} + +static void handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleDeactivate(client, resource, seat); +} + +static void handleShowInputPanel(wl_client* client, wl_resource* resource) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleShowInputPanel(client, resource); +} + +static void handleHideInputPanel(wl_client* client, wl_resource* resource) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleHideInputPanel(client, resource); +} + +static void handleReset(wl_client* client, wl_resource* resource) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleReset(client, resource); +} + +static void handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetSurroundingText(client, resource, text, cursor, anchor); +} + +static void handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetContentType(client, resource, hint, purpose); +} + +static void handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetCursorRectangle(client, resource, x, y, width, height); +} + +static void handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetPreferredLanguage(client, resource, language); +} + +static void handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleCommitState(client, resource, serial); +} + +static void handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleInvokeAction(client, resource, button, index); +} + +static const struct zwp_text_input_v1_interface textInputImpl = { + .activate = handleActivate, + .deactivate = handleDeactivate, + .show_input_panel = handleShowInputPanel, + .hide_input_panel = handleHideInputPanel, + .reset = handleReset, + .set_surrounding_text = handleSetSurroundingText, + .set_content_type = handleSetContentType, + .set_cursor_rectangle = handleSetCursorRectangle, + .set_preferred_language = handleSetPreferredLanguage, + .commit_state = handleCommitState, + .invoke_action = handleInvokeAction, +}; + +void CTextInputV1ProtocolManager::removeTI(STextInputV1* pTI) { + const auto TI = std::find_if(m_pClients.begin(), m_pClients.end(), [&](const auto& other) { return other->resourceCaller == pTI->resourceCaller; }); + if (TI == m_pClients.end()) + return; + + // if ((*TI)->resourceImpl) + // wl_resource_destroy((*TI)->resourceImpl); + + std::erase_if(m_pClients, [&](const auto& other) { return other.get() == pTI; }); +} + +STextInputV1* tiFromResource(wl_resource* resource) { + ASSERT(wl_resource_instance_of(resource, &zwp_text_input_v1_interface, &textInputImpl)); + return (STextInputV1*)wl_resource_get_user_data(resource); +} + +static void destroyTI(wl_resource* resource) { + const auto TI = tiFromResource(resource); + + if (!TI) + return; + + if (TI->resourceImpl) { + wl_resource_set_user_data(resource, nullptr); + } + + TI->pTextInput->hyprListener_textInputDestroy.emit(nullptr); + + g_pProtocolManager->m_pTextInputV1ProtocolManager->removeTI(TI); +} + +void CTextInputV1ProtocolManager::createTI(wl_client* client, wl_resource* resource, uint32_t id) { + const auto PTI = m_pClients.emplace_back(std::make_unique()).get(); + Debug::log(LOG, "New TI V1 at %x", PTI); + + PTI->client = client; + PTI->resourceCaller = resource; + PTI->resourceImpl = wl_resource_create(client, &zwp_text_input_v1_interface, TEXT_INPUT_VERSION, id); + + if (!PTI->resourceImpl) { + Debug::log(ERR, "Could not alloc wl_resource for TIV1"); + removeTI(PTI); + return; + } + + wl_resource_set_implementation(PTI->resourceImpl, &textInputImpl, PTI, &destroyTI); + wl_resource_set_user_data(PTI->resourceImpl, PTI); + + wl_signal_init(&PTI->sEnable); + wl_signal_init(&PTI->sDisable); + wl_signal_init(&PTI->sDestroy); + wl_signal_init(&PTI->sCommit); + + g_pInputManager->m_sIMERelay.createNewTextInput(nullptr, PTI); +} + +void CTextInputV1ProtocolManager::handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface) { + const auto PTI = tiFromResource(resource); + PTI->pTextInput->hyprListener_textInputEnable.emit(nullptr); +} + +void CTextInputV1ProtocolManager::handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat) { + const auto PTI = tiFromResource(resource); + PTI->pTextInput->hyprListener_textInputDisable.emit(nullptr); +} + +void CTextInputV1ProtocolManager::handleShowInputPanel(wl_client* client, wl_resource* resource) { + ; +} + +void CTextInputV1ProtocolManager::handleHideInputPanel(wl_client* client, wl_resource* resource) { + ; +} + +void CTextInputV1ProtocolManager::handleReset(wl_client* client, wl_resource* resource) { + const auto PTI = tiFromResource(resource); + PTI->pendingSurrounding.isPending = false; + PTI->pendingContentType.isPending = false; +} + +void CTextInputV1ProtocolManager::handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor) { + const auto PTI = tiFromResource(resource); + PTI->pendingSurrounding = {true, std::string(text), cursor, anchor}; +} + +void CTextInputV1ProtocolManager::handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose) { + const auto PTI = tiFromResource(resource); + PTI->pendingContentType = {true, hint == (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_HINT_DEFAULT ? (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE : hint, + purpose > (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD ? hint + 1 : hint}; +} + +void CTextInputV1ProtocolManager::handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) { + const auto PTI = tiFromResource(resource); + PTI->cursorRectangle = wlr_box{x, y, width, height}; +} + +void CTextInputV1ProtocolManager::handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language) { + ; +} + +void CTextInputV1ProtocolManager::handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial) { + const auto PTI = tiFromResource(resource); + PTI->serial = serial; + PTI->pTextInput->hyprListener_textInputCommit.emit(nullptr); +} + +void CTextInputV1ProtocolManager::handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index) { + ; +} \ No newline at end of file diff --git a/src/protocols/TextInputV1.hpp b/src/protocols/TextInputV1.hpp new file mode 100644 index 00000000000..9057e13267a --- /dev/null +++ b/src/protocols/TextInputV1.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "../defines.hpp" +#include "text-input-unstable-v1-protocol.h" + +#include + +struct STextInput; + +struct STextInputV1 { + wl_client* client = nullptr; + wl_resource* resourceCaller = nullptr; + + wl_resource* resourceImpl = nullptr; + + wlr_surface* focusedSurface = nullptr; + + STextInput* pTextInput = nullptr; + + wl_signal sEnable; + wl_signal sDisable; + wl_signal sCommit; + wl_signal sDestroy; + + uint32_t serial = 0; + + struct SPendingSurr { + bool isPending = false; + std::string text = ""; + uint32_t cursor = 0; + uint32_t anchor = 0; + } pendingSurrounding; + + struct SPendingCT { + bool isPending = false; + uint32_t hint = 0; + uint32_t purpose = 0; + } pendingContentType; + + wlr_box cursorRectangle = {0, 0, 0, 0}; + + bool operator==(const STextInputV1& other) { + return other.client == client && other.resourceCaller == resourceCaller && other.resourceImpl == resourceImpl; + } +}; + +class CTextInputV1ProtocolManager { + public: + CTextInputV1ProtocolManager(); + + void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id); + void createTI(wl_client* client, wl_resource* resource, uint32_t id); + void removeTI(STextInputV1* pTI); + + void displayDestroy(); + + // handlers for tiv1 + void handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface); + void handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat); + void handleShowInputPanel(wl_client* client, wl_resource* resource); + void handleHideInputPanel(wl_client* client, wl_resource* resource); + void handleReset(wl_client* client, wl_resource* resource); + void handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor); + void handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose); + void handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height); + void handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language); + void handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial); + void handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index); + + private: + wl_global* m_pGlobal = nullptr; + wl_listener m_liDisplayDestroy; + + std::vector> m_pClients; +}; \ No newline at end of file