IME: Refactor and fixup popups

This commit is contained in:
Vaxry 2024-03-24 16:08:25 +00:00
parent acf15e5579
commit 5cc4bf699c
12 changed files with 260 additions and 198 deletions

View File

@ -1163,21 +1163,6 @@ wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<
return nullptr;
}
SIMEPopup* CCompositor::vectorToIMEPopup(const Vector2D& pos, std::list<SIMEPopup>& popups) {
for (auto& popup : popups) {
auto surface = popup.pSurface->surface;
CBox box{
popup.realX,
popup.realY,
surface->current.width,
surface->current.height,
};
if (box.containsPoint(pos))
return &popup;
}
return nullptr;
}
CWindow* CCompositor::getWindowFromSurface(wlr_surface* pSurface) {
for (auto& w : m_vWindows) {
if (!w->m_bIsMapped || w->m_bFadingOut)

View File

@ -135,7 +135,6 @@ class CCompositor {
bool monitorExists(CMonitor*);
CWindow* vectorToWindowUnified(const Vector2D&, uint8_t properties, CWindow* pIgnoreWindow = nullptr);
wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector<std::unique_ptr<SLayerSurface>>*, Vector2D*, SLayerSurface**);
SIMEPopup* vectorToIMEPopup(const Vector2D& pos, std::list<SIMEPopup>& popups);
wlr_surface* vectorWindowToSurface(const Vector2D&, CWindow*, Vector2D& sl);
Vector2D vectorToSurfaceLocal(const Vector2D&, CWindow*, wlr_surface*);
CMonitor* getMonitorFromOutput(wlr_output*);

View File

@ -126,12 +126,6 @@ namespace Events {
LISTENER(newTextInput);
LISTENER(newVirtualKeyboard);
// IME Popups
DYNLISTENFUNC(mapInputPopup);
DYNLISTENFUNC(unmapInputPopup);
DYNLISTENFUNC(commitInputPopup);
DYNLISTENFUNC(destroyInputPopup);
// Touch
LISTENER(touchBegin);
LISTENER(touchEnd);

View File

@ -298,26 +298,6 @@ struct SIMEKbGrab {
DYNLISTENER(grabDestroy);
};
struct SIMEPopup {
wlr_input_popup_surface_v2* pSurface = nullptr;
int x, y;
int realX, realY;
bool visible;
CBox lastBox;
DYNLISTENER(mapPopup);
DYNLISTENER(unmapPopup);
DYNLISTENER(destroyPopup);
DYNLISTENER(commitPopup);
DYNLISTENER(focusedSurfaceUnmap);
bool operator==(const SIMEPopup& other) const {
return pSurface == other.pSurface;
}
};
struct STouchDevice {
wlr_input_device* pWlrDevice = nullptr;

View File

@ -215,10 +215,10 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
// also IME popups
if (!foundSurface) {
auto popup = g_pCompositor->vectorToIMEPopup(mouseCoords, m_sIMERelay.m_lIMEPopups);
auto popup = g_pInputManager->m_sIMERelay.popupFromCoords(mouseCoords);
if (popup) {
foundSurface = popup->pSurface->surface;
surfacePos = Vector2D(popup->realX, popup->realY);
foundSurface = popup->getWlrSurface();
surfacePos = popup->globalBox().pos();
}
}

View File

@ -0,0 +1,168 @@
#include "InputMethodPopup.hpp"
CInputPopup::CInputPopup(wlr_input_popup_surface_v2* surf) : pWlr(surf) {
surface.assign(surf->surface);
initCallbacks();
}
static void onCommit(void* owner, void* data) {
const auto PPOPUP = (CInputPopup*)owner;
PPOPUP->onCommit();
}
static void onMap(void* owner, void* data) {
const auto PPOPUP = (CInputPopup*)owner;
PPOPUP->onMap();
}
static void onUnmap(void* owner, void* data) {
const auto PPOPUP = (CInputPopup*)owner;
PPOPUP->onUnmap();
}
static void onDestroy(void* owner, void* data) {
const auto PPOPUP = (CInputPopup*)owner;
PPOPUP->onDestroy();
}
void CInputPopup::initCallbacks() {
hyprListener_commitPopup.initCallback(&pWlr->surface->events.commit, &::onCommit, this, "IME Popup");
hyprListener_mapPopup.initCallback(&pWlr->surface->events.map, &::onMap, this, "IME Popup");
hyprListener_unmapPopup.initCallback(&pWlr->surface->events.unmap, &::onUnmap, this, "IME Popup");
hyprListener_destroyPopup.initCallback(&pWlr->events.destroy, &::onDestroy, this, "IME Popup");
}
CWLSurface* CInputPopup::queryOwner() {
const auto FOCUSED = g_pInputManager->m_sIMERelay.getFocusedTextInput();
if (!FOCUSED)
return nullptr;
return CWLSurface::surfaceFromWlr(FOCUSED->focusedSurface());
}
void CInputPopup::onDestroy() {
hyprListener_commitPopup.removeCallback();
hyprListener_destroyPopup.removeCallback();
hyprListener_mapPopup.removeCallback();
hyprListener_unmapPopup.removeCallback();
g_pInputManager->m_sIMERelay.removePopup(this);
}
void CInputPopup::onMap() {
Debug::log(LOG, "Mapped an IME Popup");
updateBox();
damageEntire();
}
void CInputPopup::onUnmap() {
Debug::log(LOG, "Unmapped an IME Popup");
damageEntire();
}
void CInputPopup::onCommit() {
updateBox();
}
void CInputPopup::damageEntire() {
const auto OWNER = queryOwner();
if (!OWNER) {
Debug::log(ERR, "BUG THIS: No owner in imepopup::damageentire");
return;
}
Vector2D pos = OWNER->getSurfaceBoxGlobal().value_or(CBox{0, 0, 0, 0}).pos();
CBox global = lastBoxLocal.copy().translate(pos);
g_pHyprRenderer->damageBox(&global);
}
void CInputPopup::damageSurface() {
const auto OWNER = queryOwner();
if (!OWNER) {
Debug::log(ERR, "BUG THIS: No owner in imepopup::damagesurface");
return;
}
Vector2D pos = OWNER->getSurfaceBoxGlobal().value_or(CBox{0, 0, 0, 0}).pos() + lastBoxLocal.pos();
g_pHyprRenderer->damageSurface(surface.wlr(), pos.x, pos.y);
}
void CInputPopup::updateBox() {
if (!surface.wlr()->mapped)
return;
const auto OWNER = queryOwner();
const auto PFOCUSEDTI = g_pInputManager->m_sIMERelay.getFocusedTextInput();
if (!PFOCUSEDTI)
return;
bool cursorRect = PFOCUSEDTI->hasCursorRectangle();
CBox cursorBoxLocal = PFOCUSEDTI->cursorBox();
CBox parentBox;
if (!OWNER)
parentBox = {0, 0, 500, 500};
else
parentBox = OWNER->getSurfaceBoxGlobal().value_or(CBox{0, 0, 500, 500});
if (!cursorRect) {
Vector2D coords = OWNER ? OWNER->getSurfaceBoxGlobal().value_or(CBox{0, 0, 500, 500}).pos() : Vector2D{0, 0};
parentBox = {coords, {500, 500}};
cursorBoxLocal = {0, 0, (int)parentBox.w, (int)parentBox.h};
}
if (cursorBoxLocal != lastBoxLocal)
damageEntire();
CMonitor* pMonitor = g_pCompositor->getMonitorFromVector(parentBox.middle());
if (cursorBoxLocal.y + parentBox.y + surface.wlr()->current.height + cursorBoxLocal.height > pMonitor->vecPosition.y + pMonitor->vecSize.y)
cursorBoxLocal.y -= surface.wlr()->current.height + cursorBoxLocal.height;
if (cursorBoxLocal.x + parentBox.x + surface.wlr()->current.width > pMonitor->vecPosition.x + pMonitor->vecSize.x)
cursorBoxLocal.x -= (cursorBoxLocal.x + parentBox.x + surface.wlr()->current.width) - (pMonitor->vecPosition.x + pMonitor->vecSize.x);
lastBoxLocal = cursorBoxLocal;
wlr_input_popup_surface_v2_send_text_input_rectangle(pWlr, cursorBoxLocal.pWlr());
damageSurface();
if (const auto PM = g_pCompositor->getMonitorFromCursor(); PM && PM->ID != lastMonitor) {
const auto PML = g_pCompositor->getMonitorFromID(lastMonitor);
if (PML)
wlr_surface_send_leave(surface.wlr(), PML->output);
wlr_surface_send_enter(surface.wlr(), PM->output);
lastMonitor = PM->ID;
}
}
CBox CInputPopup::globalBox() {
const auto OWNER = queryOwner();
if (!OWNER) {
Debug::log(ERR, "BUG THIS: No owner in imepopup::globalbox");
return {};
}
return lastBoxLocal.copy().translate(OWNER->getSurfaceBoxGlobal().value_or(CBox{0, 0, 0, 0}).pos());
}
bool CInputPopup::isVecInPopup(const Vector2D& point) {
return globalBox().containsPoint(point);
}
wlr_surface* CInputPopup::getWlrSurface() {
return surface.wlr();
}

View File

@ -0,0 +1,41 @@
#pragma once
#include "../../helpers/WLListener.hpp"
#include "../../desktop/WLSurface.hpp"
#include "../../macros.hpp"
#include "../../helpers/Box.hpp"
struct wlr_input_popup_surface_v2;
class CInputPopup {
public:
CInputPopup(wlr_input_popup_surface_v2* surf);
void onDestroy();
void onMap();
void onUnmap();
void onCommit();
void damageEntire();
void damageSurface();
bool isVecInPopup(const Vector2D& point);
CBox globalBox();
wlr_surface* getWlrSurface();
private:
void initCallbacks();
CWLSurface* queryOwner();
void updateBox();
wlr_input_popup_surface_v2* pWlr = nullptr;
CWLSurface surface;
CBox lastBoxLocal;
uint64_t lastMonitor = -1;
DYNLISTENER(mapPopup);
DYNLISTENER(unmapPopup);
DYNLISTENER(destroyPopup);
DYNLISTENER(commitPopup);
};

View File

@ -86,14 +86,7 @@ void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) {
hyprListener_IMENewPopup.initCallback(
&m_pWLRIME->events.new_popup_surface,
[&](void* owner, void* data) {
const auto PNEWPOPUP = &m_lIMEPopups.emplace_back();
PNEWPOPUP->pSurface = (wlr_input_popup_surface_v2*)data;
PNEWPOPUP->hyprListener_commitPopup.initCallback(&PNEWPOPUP->pSurface->surface->events.commit, &Events::listener_commitInputPopup, PNEWPOPUP, "IME Popup");
PNEWPOPUP->hyprListener_mapPopup.initCallback(&PNEWPOPUP->pSurface->surface->events.map, &Events::listener_mapInputPopup, PNEWPOPUP, "IME Popup");
PNEWPOPUP->hyprListener_unmapPopup.initCallback(&PNEWPOPUP->pSurface->surface->events.unmap, &Events::listener_unmapInputPopup, PNEWPOPUP, "IME Popup");
PNEWPOPUP->hyprListener_destroyPopup.initCallback(&PNEWPOPUP->pSurface->events.destroy, &Events::listener_destroyInputPopup, PNEWPOPUP, "IME Popup");
m_vIMEPopups.emplace_back(std::make_unique<CInputPopup>((wlr_input_popup_surface_v2*)data));
Debug::log(LOG, "New input popup");
},
@ -103,132 +96,12 @@ void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) {
PTI->enter(PTI->focusedSurface());
}
void CInputMethodRelay::updateInputPopup(SIMEPopup* pPopup) {
if (!pPopup->pSurface->surface->mapped)
return;
// damage last known pos & size
g_pHyprRenderer->damageBox(&pPopup->lastBox);
const auto PFOCUSEDTI = getFocusedTextInput();
if (!PFOCUSEDTI || !PFOCUSEDTI->focusedSurface())
return;
bool cursorRect = PFOCUSEDTI->hasCursorRectangle();
const auto PFOCUSEDSURFACE = PFOCUSEDTI->focusedSurface();
CBox cursorBox = PFOCUSEDTI->cursorBox();
CBox parentBox;
const auto PSURFACE = CWLSurface::surfaceFromWlr(PFOCUSEDSURFACE);
if (!PSURFACE)
parentBox = {0, 0, 200, 200};
else
parentBox = PSURFACE->getSurfaceBoxGlobal().value_or(CBox{0, 0, 200, 200});
if (!cursorRect)
cursorBox = {0, 0, (int)parentBox.w, (int)parentBox.h};
CMonitor* pMonitor = g_pCompositor->getMonitorFromVector(cursorBox.middle());
if (cursorBox.y + parentBox.y + pPopup->pSurface->surface->current.height + cursorBox.height > pMonitor->vecPosition.y + pMonitor->vecSize.y)
cursorBox.y -= pPopup->pSurface->surface->current.height + cursorBox.height;
if (cursorBox.x + parentBox.x + pPopup->pSurface->surface->current.width > pMonitor->vecPosition.x + pMonitor->vecSize.x)
cursorBox.x -= (cursorBox.x + parentBox.x + pPopup->pSurface->surface->current.width) - (pMonitor->vecPosition.x + pMonitor->vecSize.x);
pPopup->x = cursorBox.x;
pPopup->y = cursorBox.y + cursorBox.height;
pPopup->realX = cursorBox.x + parentBox.x;
pPopup->realY = cursorBox.y + parentBox.y + cursorBox.height;
pPopup->lastBox = cursorBox;
wlr_input_popup_surface_v2_send_text_input_rectangle(pPopup->pSurface, cursorBox.pWlr());
damagePopup(pPopup);
void CInputMethodRelay::setIMEPopupFocus(CInputPopup* pPopup, wlr_surface* pSurface) {
pPopup->onCommit();
}
void CInputMethodRelay::setIMEPopupFocus(SIMEPopup* pPopup, wlr_surface* pSurface) {
updateInputPopup(pPopup);
}
void Events::listener_mapInputPopup(void* owner, void* data) {
const auto PPOPUP = (SIMEPopup*)owner;
Debug::log(LOG, "Mapped an IME Popup");
g_pInputManager->m_sIMERelay.updateInputPopup(PPOPUP);
if (const auto PMONITOR = g_pCompositor->getMonitorFromVector(PPOPUP->lastBox.middle()); PMONITOR)
wlr_surface_send_enter(PPOPUP->pSurface->surface, PMONITOR->output);
}
void Events::listener_unmapInputPopup(void* owner, void* data) {
const auto PPOPUP = (SIMEPopup*)owner;
Debug::log(LOG, "Unmapped an IME Popup");
g_pHyprRenderer->damageBox(&PPOPUP->lastBox);
g_pInputManager->m_sIMERelay.updateInputPopup(PPOPUP);
}
void Events::listener_destroyInputPopup(void* owner, void* data) {
const auto PPOPUP = (SIMEPopup*)owner;
Debug::log(LOG, "Removed an IME Popup");
PPOPUP->hyprListener_commitPopup.removeCallback();
PPOPUP->hyprListener_destroyPopup.removeCallback();
PPOPUP->hyprListener_focusedSurfaceUnmap.removeCallback();
PPOPUP->hyprListener_mapPopup.removeCallback();
PPOPUP->hyprListener_unmapPopup.removeCallback();
g_pInputManager->m_sIMERelay.removePopup(PPOPUP);
}
void Events::listener_commitInputPopup(void* owner, void* data) {
const auto PPOPUP = (SIMEPopup*)owner;
g_pInputManager->m_sIMERelay.updateInputPopup(PPOPUP);
}
void CInputMethodRelay::removePopup(SIMEPopup* pPopup) {
m_lIMEPopups.remove(*pPopup);
}
void CInputMethodRelay::damagePopup(SIMEPopup* pPopup) {
if (!pPopup->pSurface->surface->mapped)
return;
const auto PFOCUSEDTI = getFocusedTextInput();
if (!PFOCUSEDTI || !PFOCUSEDTI->focusedSurface())
return;
Vector2D parentPos;
const auto PFOCUSEDSURFACE = PFOCUSEDTI->focusedSurface();
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));
if (PLS) {
parentPos = Vector2D(PLS->geometry.x, PLS->geometry.y) + g_pCompositor->getMonitorFromID(PLS->monitorID)->vecPosition;
}
} else {
const auto PWINDOW = g_pCompositor->getWindowFromSurface(PFOCUSEDSURFACE);
if (PWINDOW) {
parentPos = PWINDOW->m_vRealPosition.goal();
}
}
g_pHyprRenderer->damageSurface(pPopup->pSurface->surface, parentPos.x + pPopup->x, parentPos.y + pPopup->y);
void CInputMethodRelay::removePopup(CInputPopup* pPopup) {
std::erase_if(m_vIMEPopups, [pPopup](const auto& other) { return other.get() == pPopup; });
}
SIMEKbGrab* CInputMethodRelay::getIMEKeyboardGrab(SKeyboard* pKeyboard) {
@ -268,7 +141,13 @@ void CInputMethodRelay::onNewTextInput(STextInputV1* pTIV1) {
}
void CInputMethodRelay::removeTextInput(CTextInput* pInput) {
m_vTextInputs.remove_if([&](const auto& other) { return other.get() == pInput; });
std::erase_if(m_vTextInputs, [pInput](const auto& other) { return other.get() == pInput; });
}
void CInputMethodRelay::updateAllPopups() {
for (auto& p : m_vIMEPopups) {
p->onCommit();
}
}
void CInputMethodRelay::commitIMEState(CTextInput* pInput) {
@ -304,3 +183,12 @@ void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) {
ti->enter(pSurface);
}
}
CInputPopup* CInputMethodRelay::popupFromCoords(const Vector2D& point) {
for (auto& p : m_vIMEPopups) {
if (p->isVecInPopup(point))
return p.get();
}
return nullptr;
}

View File

@ -4,8 +4,10 @@
#include "../../defines.hpp"
#include "../../helpers/WLClasses.hpp"
#include "TextInput.hpp"
#include "InputMethodPopup.hpp"
class CInputManager;
class CHyprRenderer;
struct STextInputV1;
class CInputMethodRelay {
@ -27,18 +29,20 @@ class CInputMethodRelay {
SIMEKbGrab* getIMEKeyboardGrab(SKeyboard*);
void setIMEPopupFocus(SIMEPopup*, wlr_surface*);
void updateInputPopup(SIMEPopup*);
void damagePopup(SIMEPopup*);
void removePopup(SIMEPopup*);
void setIMEPopupFocus(CInputPopup*, wlr_surface*);
void removePopup(CInputPopup*);
CInputPopup* popupFromCoords(const Vector2D& point);
void updateAllPopups();
private:
std::unique_ptr<SIMEKbGrab> m_pKeyboardGrab;
std::unique_ptr<SIMEKbGrab> m_pKeyboardGrab;
std::list<std::unique_ptr<CTextInput>> m_vTextInputs;
std::list<SIMEPopup> m_lIMEPopups;
std::vector<std::unique_ptr<CTextInput>> m_vTextInputs;
std::vector<std::unique_ptr<CInputPopup>> m_vIMEPopups;
wlr_surface* m_pLastKbFocus = nullptr;
wlr_surface* m_pLastKbFocus = nullptr;
DYNLISTENER(textInputNew);
DYNLISTENER(IMECommit);
@ -50,4 +54,5 @@ class CInputMethodRelay {
friend class CInputManager;
friend class CTextInputV1ProtocolManager;
friend struct CTextInput;
friend class CHyprRenderer;
};

View File

@ -222,9 +222,7 @@ void CTextInput::commitStateToIME(wlr_input_method_v2* ime) {
wlr_input_method_v2_send_content_type(ime, pV1Input->pendingContentType.hint, pV1Input->pendingContentType.purpose);
}
for (auto& p : g_pInputManager->m_sIMERelay.m_lIMEPopups) {
g_pInputManager->m_sIMERelay.updateInputPopup(&p);
}
g_pInputManager->m_sIMERelay.updateAllPopups();
wlr_input_method_v2_send_done(ime);
}

View File

@ -679,16 +679,20 @@ void CHyprRenderer::renderLayer(SLayerSurface* pLayer, CMonitor* pMonitor, times
g_pHyprOpenGL->m_RenderData.clipBox = {};
}
void CHyprRenderer::renderIMEPopup(SIMEPopup* pPopup, CMonitor* pMonitor, timespec* time) {
SRenderData renderdata = {pMonitor, time, pPopup->realX, pPopup->realY};
void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, CMonitor* pMonitor, timespec* time) {
const auto POS = pPopup->globalBox().pos();
SRenderData renderdata = {pMonitor, time, POS.x, POS.y};
const auto SURF = pPopup->getWlrSurface();
renderdata.blur = false;
renderdata.surface = pPopup->pSurface->surface;
renderdata.surface = SURF;
renderdata.decorate = false;
renderdata.w = pPopup->pSurface->surface->current.width;
renderdata.h = pPopup->pSurface->surface->current.height;
renderdata.w = SURF->current.width;
renderdata.h = SURF->current.height;
wlr_surface_for_each_surface(pPopup->pSurface->surface, renderSurface, &renderdata);
wlr_surface_for_each_surface(SURF, renderSurface, &renderdata);
}
void CHyprRenderer::renderSessionLockSurface(SSessionLockSurface* pSurface, CMonitor* pMonitor, timespec* time) {
@ -830,8 +834,8 @@ void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace*
}
// Render IME popups
for (auto& imep : g_pInputManager->m_sIMERelay.m_lIMEPopups) {
renderIMEPopup(&imep, pMonitor, time);
for (auto& imep : g_pInputManager->m_sIMERelay.m_vIMEPopups) {
renderIMEPopup(imep.get(), pMonitor, time);
}
for (auto& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]) {

View File

@ -115,7 +115,7 @@ class CHyprRenderer {
void renderLayer(SLayerSurface*, CMonitor*, timespec*);
void renderSessionLockSurface(SSessionLockSurface*, CMonitor*, timespec*);
void renderDragIcon(CMonitor*, timespec*);
void renderIMEPopup(SIMEPopup*, CMonitor*, timespec*);
void renderIMEPopup(CInputPopup*, CMonitor*, timespec*);
void renderWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const CBox& geometry);
void renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const Vector2D& translate = {0, 0}, const float& scale = 1.f);