From 9f956655a89e4160dd1ebb2ab91436ba9d3bdcd8 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Mon, 13 Mar 2023 17:27:32 +0000 Subject: initial v1 impl --- src/helpers/WLClasses.hpp | 3 + src/managers/ProtocolManager.cpp | 1 + src/managers/ProtocolManager.hpp | 2 + src/managers/input/InputMethodRelay.cpp | 65 +++++++--- src/managers/input/InputMethodRelay.hpp | 8 +- src/protocols/TextInputV1.cpp | 203 ++++++++++++++++++++++++++++++++ src/protocols/TextInputV1.hpp | 68 +++++++++++ 7 files changed, 328 insertions(+), 22 deletions(-) create mode 100644 src/protocols/TextInputV1.cpp create mode 100644 src/protocols/TextInputV1.hpp (limited to 'src') diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index 15646c30..8a96c6ff 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 4dd9ed79..6271b0ca 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 66d8ef20..6160176d 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 95f0b6fb..3583ca12 100644 --- a/src/managers/input/InputMethodRelay.cpp +++ b/src/managers/input/InputMethodRelay.cpp @@ -302,10 +302,14 @@ 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, @@ -320,7 +324,7 @@ 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"); @@ -339,7 +343,7 @@ void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { return; } - g_pInputManager->m_sIMERelay.commitIMEState(PINPUT->pWlrInput); + g_pInputManager->m_sIMERelay.commitIMEState(PINPUT); }, PTEXTINPUT, "textInput"); @@ -357,7 +361,7 @@ 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"); @@ -374,7 +378,7 @@ void CInputMethodRelay::createNewTextInput(wlr_text_input_v3* pInput) { if (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 +388,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,6 +434,9 @@ void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) { if (!m_pWLRIME) return; + auto focusedSurface = [](STextInput* pTI) -> wlr_surface* { return pTI->pWlrInput ? pTI->pWlrInput->focused_surface : pTI->pV1Input->focusedSurface; }; + 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) { @@ -424,20 +445,26 @@ void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) { } else if (ti.pWlrInput->focused_surface) { - if (pSurface != ti.pWlrInput->focused_surface) { + if (pSurface != focusedSurface(&ti)) { wlr_input_method_v2_send_deactivate(m_pWLRIME); - commitIMEState(ti.pWlrInput); + commitIMEState(&ti); - wlr_text_input_v3_send_leave(ti.pWlrInput); + if (ti.pWlrInput) + wlr_text_input_v3_send_leave(ti.pWlrInput); + else + zwp_text_input_v1_send_leave(ti.pV1Input->resourceImpl); } 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); } else { setPendingSurface(&ti, pSurface); } diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp index 76746487..e781298c 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,9 @@ class CInputMethodRelay { DYNLISTENER(IMEGrab); DYNLISTENER(IMENewPopup); - void createNewTextInput(wlr_text_input_v3*); + void createNewTextInput(wlr_text_input_v3*, STextInputV1* tiv1 = nullptr); 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 00000000..a1b84662 --- /dev/null +++ b/src/protocols/TextInputV1.cpp @@ -0,0 +1,203 @@ +#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_pFractionalScaleProtocolManager->bindManager(client, data, version, id); +} + +static void handleDisplayDestroy(struct wl_listener* listener, void* data) { + g_pProtocolManager->m_pFractionalScaleProtocolManager->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!"); +} + +// + +void handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleActivate(client, resource, seat, surface); +} + +void handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleDeactivate(client, resource, seat); +} + +void handleShowInputPanel(wl_client* client, wl_resource* resource) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleShowInputPanel(client, resource); +} + +void handleHideInputPanel(wl_client* client, wl_resource* resource) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleHideInputPanel(client, resource); +} + +void handleReset(wl_client* client, wl_resource* resource) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleReset(client, resource); +} + +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); +} + +void handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetContentType(client, resource, hint, purpose); +} + +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); +} + +void handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetPreferredLanguage(client, resource, language); +} + +void handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial) { + g_pProtocolManager->m_pTextInputV1ProtocolManager->handleCommitState(client, resource, serial); +} + +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->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); +} + +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; +} + +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 == ZWP_TEXT_INPUT_V1_CONTENT_HINT_DEFAULT ? ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE : hint, + purpose > 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->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 00000000..3b54fd5f --- /dev/null +++ b/src/protocols/TextInputV1.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "../defines.hpp" +#include "text-input-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; + + 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 -- cgit v1.2.3