ime-v2: move to new impl

This commit is contained in:
Vaxry 2024-05-01 16:41:17 +01:00
parent 4ed6b69b68
commit 8bcccf9f0f
22 changed files with 1208 additions and 265 deletions

View file

@ -262,8 +262,6 @@ void CCompositor::initServer() {
wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry);
wlr_xdg_foreign_v2_create(m_sWLDisplay, m_sWLRForeignRegistry);
m_sWLRIMEMgr = wlr_input_method_manager_v2_create(m_sWLDisplay);
m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop);
if (!m_sWLRHeadlessBackend) {
@ -312,7 +310,6 @@ void CCompositor::initAllSignals() {
addWLSignal(&m_sWLRVirtPtrMgr->events.new_virtual_pointer, &Events::listen_newVirtPtr, m_sWLRVirtPtrMgr, "VirtPtrMgr");
addWLSignal(&m_sWLRVKeyboardMgr->events.new_virtual_keyboard, &Events::listen_newVirtualKeyboard, m_sWLRVKeyboardMgr, "VKeyboardMgr");
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
addWLSignal(&m_sWLRIMEMgr->events.input_method, &Events::listen_newIME, m_sWLRIMEMgr, "IMEMgr");
if (m_sWRLDRMLeaseMgr)
addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM");
@ -355,7 +352,6 @@ void CCompositor::removeAllSignals() {
removeWLSignal(&Events::listen_newVirtPtr);
removeWLSignal(&Events::listen_newVirtualKeyboard);
removeWLSignal(&Events::listen_RendererDestroy);
removeWLSignal(&Events::listen_newIME);
if (m_sWRLDRMLeaseMgr)
removeWLSignal(&Events::listen_leaseRequest);

View file

@ -64,7 +64,6 @@ class CCompositor {
wlr_virtual_pointer_manager_v1* m_sWLRVirtPtrMgr;
wlr_tablet_manager_v2* m_sWLRTabletManager;
wlr_xdg_foreign_registry* m_sWLRForeignRegistry;
wlr_input_method_manager_v2* m_sWLRIMEMgr;
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
wlr_backend* m_sWLRHeadlessBackend;
// ------------------------------------------------- //

View file

@ -107,7 +107,6 @@ namespace Events {
LISTENER(pinchEnd);
// IME
LISTENER(newIME);
LISTENER(newVirtualKeyboard);
// Touch

View file

@ -187,9 +187,3 @@ void Events::listener_sessionActive(wl_listener* listener, void* data) {
g_pConfigManager->m_bWantsMonitorReload = true;
}
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);
}

View file

@ -2,9 +2,12 @@
#include "../defines.hpp"
#include <algorithm>
#include "../Compositor.hpp"
#include "../managers/TokenManager.hpp"
#include <optional>
#include <set>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <iomanip>
#include <sstream>
#ifdef HAS_EXECINFO
@ -834,3 +837,35 @@ bool envEnabled(const std::string& env) {
return false;
return std::string(ENV) == "1";
}
std::pair<int, std::string> openExclusiveShm() {
std::string name = g_pTokenManager->getRandomUUID();
for (size_t i = 0; i < 69; ++i) {
int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0)
return {fd, name};
}
return {-1, ""};
}
int allocateSHMFile(size_t len) {
auto [fd, name] = openExclusiveShm();
if (fd < 0)
return -1;
shm_unlink(name.c_str());
int ret;
do {
ret = ftruncate(fd, len);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
}
return fd;
}

View file

@ -38,6 +38,7 @@ void throwError(const std::string& err);
uint32_t drmFormatToGL(uint32_t drm);
uint32_t glFormatToType(uint32_t gl);
bool envEnabled(const std::string& env);
int allocateSHMFile(size_t len);
template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {

View file

@ -218,14 +218,6 @@ struct SSwipeGesture {
CMonitor* pMonitor = nullptr;
};
struct SIMEKbGrab {
wlr_input_method_keyboard_grab_v2* pWlrKbGrab = nullptr;
wlr_keyboard* pKeyboard = nullptr;
DYNLISTENER(grabDestroy);
};
struct STouchDevice {
wlr_input_device* pWlrDevice = nullptr;

View file

@ -83,7 +83,6 @@ extern "C" {
#include <wlr/types/wlr_xdg_foreign_registry.h>
#include <wlr/types/wlr_xdg_foreign_v1.h>
#include <wlr/types/wlr_xdg_foreign_v2.h>
#include <wlr/types/wlr_input_method_v2.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_switch.h>
#include <wlr/config.h>

View file

@ -324,9 +324,6 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard
return true;
}
if (pKeyboard->isVirtual && g_pInputManager->shouldIgnoreVirtualKeyboard(pKeyboard))
return true;
if (!m_pXKBTranslationState) {
Debug::log(ERR, "BUG THIS: m_pXKBTranslationState NULL!");
updateXKBTranslationState();

View file

@ -19,26 +19,7 @@
#include "../protocols/XDGActivation.hpp"
#include "../protocols/IdleNotify.hpp"
#include "../protocols/SessionLock.hpp"
#include "tearing-control-v1.hpp"
#include "fractional-scale-v1.hpp"
#include "xdg-output-unstable-v1.hpp"
#include "cursor-shape-v1.hpp"
#include "idle-inhibit-unstable-v1.hpp"
#include "relative-pointer-unstable-v1.hpp"
#include "xdg-decoration-unstable-v1.hpp"
#include "alpha-modifier-v1.hpp"
#include "wlr-gamma-control-unstable-v1.hpp"
#include "ext-foreign-toplevel-list-v1.hpp"
#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"
#include "pointer-constraints-unstable-v1.hpp"
#include "wlr-output-power-management-unstable-v1.hpp"
#include "xdg-activation-v1.hpp"
#include "ext-idle-notify-v1.hpp"
#include "ext-session-lock-v1.hpp"
#include "../protocols/InputMethodV2.hpp"
CProtocolManager::CProtocolManager() {
@ -61,6 +42,7 @@ CProtocolManager::CProtocolManager() {
PROTO::activation = std::make_unique<CXDGActivationProtocol>(&xdg_activation_v1_interface, 1, "XDGActivation");
PROTO::idle = std::make_unique<CIdleNotifyProtocol>(&ext_idle_notifier_v1_interface, 1, "IdleNotify");
PROTO::sessionLock = std::make_unique<CSessionLockProtocol>(&ext_session_lock_manager_v1_interface, 1, "SessionLock");
PROTO::ime = std::make_unique<CInputMethodV2Protocol>(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
// Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner.

View file

@ -10,6 +10,7 @@
#include "../../protocols/PointerConstraints.hpp"
#include "../../protocols/IdleNotify.hpp"
#include "../../protocols/SessionLock.hpp"
#include "../../protocols/InputMethodV2.hpp"
CInputManager::CInputManager() {
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
@ -1207,6 +1208,8 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar
if (!pKeyboard->enabled)
return;
const bool DISALLOWACTION = pKeyboard->isVirtual && shouldIgnoreVirtualKeyboard(pKeyboard);
const auto EMAP = std::unordered_map<std::string, std::any>{{"keyboard", pKeyboard}, {"event", e}};
EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP);
@ -1216,17 +1219,16 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar
g_pKeybindManager->dpms("on");
}
bool passEvent = g_pKeybindManager->onKeyEvent(e, pKeyboard);
bool passEvent = DISALLOWACTION || g_pKeybindManager->onKeyEvent(e, pKeyboard);
PROTO::idle->onActivity();
if (passEvent) {
const auto IME = m_sIMERelay.m_pIME.lock();
const auto PIMEGRAB = m_sIMERelay.getIMEKeyboardGrab(pKeyboard);
if (PIMEGRAB && PIMEGRAB->pWlrKbGrab && PIMEGRAB->pWlrKbGrab->input_method) {
wlr_input_method_keyboard_grab_v2_set_keyboard(PIMEGRAB->pWlrKbGrab, wlr_keyboard_from_input_device(pKeyboard->keyboard));
wlr_input_method_keyboard_grab_v2_send_key(PIMEGRAB->pWlrKbGrab, e->time_msec, e->keycode, e->state);
if (IME && IME->hasGrab() && !DISALLOWACTION) {
IME->setKeyboard(wlr_keyboard_from_input_device(pKeyboard->keyboard));
IME->sendKey(e->time_msec, e->keycode, e->state);
} else {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(pKeyboard->keyboard));
wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, e->time_msec, e->keycode, e->state);
@ -1240,16 +1242,18 @@ void CInputManager::onKeyboardMod(void* data, SKeyboard* pKeyboard) {
if (!pKeyboard->enabled)
return;
const auto PIMEGRAB = m_sIMERelay.getIMEKeyboardGrab(pKeyboard);
const bool DISALLOWACTION = pKeyboard->isVirtual && shouldIgnoreVirtualKeyboard(pKeyboard);
const auto ALLMODS = accumulateModsFromAllKBs();
auto MODS = wlr_keyboard_from_input_device(pKeyboard->keyboard)->modifiers;
MODS.depressed = ALLMODS;
if (PIMEGRAB && PIMEGRAB->pWlrKbGrab && PIMEGRAB->pWlrKbGrab->input_method) {
wlr_input_method_keyboard_grab_v2_set_keyboard(PIMEGRAB->pWlrKbGrab, wlr_keyboard_from_input_device(pKeyboard->keyboard));
wlr_input_method_keyboard_grab_v2_send_modifiers(PIMEGRAB->pWlrKbGrab, &MODS);
const auto IME = m_sIMERelay.m_pIME.lock();
if (IME && IME->hasGrab() && !DISALLOWACTION) {
IME->setKeyboard(wlr_keyboard_from_input_device(pKeyboard->keyboard));
IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
} else {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(pKeyboard->keyboard));
wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &MODS);
@ -1273,8 +1277,7 @@ void CInputManager::onKeyboardMod(void* data, SKeyboard* pKeyboard) {
bool CInputManager::shouldIgnoreVirtualKeyboard(SKeyboard* pKeyboard) {
return !pKeyboard ||
(m_sIMERelay.m_pKeyboardGrab &&
wl_resource_get_client(m_sIMERelay.m_pKeyboardGrab->pWlrKbGrab->resource) == wl_resource_get_client(wlr_input_device_get_virtual_keyboard(pKeyboard->keyboard)->resource));
(!m_sIMERelay.m_pIME.expired() && m_sIMERelay.m_pIME.lock()->grabClient() == wl_resource_get_client(wlr_input_device_get_virtual_keyboard(pKeyboard->keyboard)->resource));
}
void CInputManager::refocus() {

View file

@ -2,37 +2,14 @@
#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/FractionalScale.hpp"
#include "../../protocols/InputMethodV2.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");
CInputPopup::CInputPopup(SP<CInputMethodPopupV2> popup_) : popup(popup_) {
listeners.commit = popup_->events.commit.registerListener([this](std::any d) { onCommit(); });
listeners.map = popup_->events.map.registerListener([this](std::any d) { onMap(); });
listeners.unmap = popup_->events.unmap.registerListener([this](std::any d) { onUnmap(); });
listeners.destroy = popup_->events.destroy.registerListener([this](std::any d) { onDestroy(); });
surface.assign(popup_->surface());
}
CWLSurface* CInputPopup::queryOwner() {
@ -45,11 +22,6 @@ CWLSurface* CInputPopup::queryOwner() {
}
void CInputPopup::onDestroy() {
hyprListener_commitPopup.removeCallback();
hyprListener_destroyPopup.removeCallback();
hyprListener_mapPopup.removeCallback();
hyprListener_unmapPopup.removeCallback();
g_pInputManager->m_sIMERelay.removePopup(this);
}
@ -101,7 +73,7 @@ void CInputPopup::damageSurface() {
}
void CInputPopup::updateBox() {
if (!surface.wlr()->mapped)
if (!popup.lock()->mapped)
return;
const auto OWNER = queryOwner();
@ -142,7 +114,7 @@ void CInputPopup::updateBox() {
popupOffset.x -= popupOverflow;
CBox cursorBoxLocal({-popupOffset.x, -popupOffset.y}, cursorBoxParent.size());
wlr_input_popup_surface_v2_send_text_input_rectangle(pWlr, cursorBoxLocal.pWlr());
popup.lock()->sendInputRectangle(cursorBoxLocal);
CBox popupBoxParent(cursorBoxParent.pos() + popupOffset, currentPopupSize);
if (popupBoxParent != lastBoxLocal) {

View file

@ -4,17 +4,13 @@
#include "../../desktop/WLSurface.hpp"
#include "../../macros.hpp"
#include "../../helpers/Box.hpp"
#include "../../helpers/signal/Listener.hpp"
struct wlr_input_popup_surface_v2;
class CInputMethodPopupV2;
class CInputPopup {
public:
CInputPopup(wlr_input_popup_surface_v2* surf);
void onDestroy();
void onMap();
void onUnmap();
void onCommit();
CInputPopup(SP<CInputMethodPopupV2> popup);
void damageEntire();
void damageSurface();
@ -24,18 +20,25 @@ class CInputPopup {
CBox globalBox();
wlr_surface* getWlrSurface();
void onCommit();
private:
void initCallbacks();
CWLSurface* queryOwner();
void updateBox();
CWLSurface* queryOwner();
void updateBox();
wlr_input_popup_surface_v2* pWlr = nullptr;
CWLSurface surface;
CBox lastBoxLocal;
uint64_t lastMonitor = -1;
void onDestroy();
void onMap();
void onUnmap();
DYNLISTENER(mapPopup);
DYNLISTENER(unmapPopup);
DYNLISTENER(destroyPopup);
DYNLISTENER(commitPopup);
WP<CInputMethodPopupV2> popup;
CWLSurface surface;
CBox lastBoxLocal;
uint64_t lastMonitor = -1;
struct {
CHyprSignalListener map;
CHyprSignalListener unmap;
CHyprSignalListener destroy;
CHyprSignalListener commit;
} listeners;
};

View file

@ -2,98 +2,53 @@
#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/TextInputV3.hpp"
#include "../../protocols/InputMethodV2.hpp"
CInputMethodRelay::CInputMethodRelay() {
static auto P = g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast<wlr_surface*>(param)); });
listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(ti); });
listeners.newIME = PROTO::ime->events.newIME.registerListener([this](std::any ime) { onNewIME(std::any_cast<SP<CInputMethodV2>>(ime)); });
}
void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) {
if (m_pWLRIME) {
void CInputMethodRelay::onNewIME(SP<CInputMethodV2> pIME) {
if (!m_pIME.expired()) {
Debug::log(ERR, "Cannot register 2 IMEs at once!");
wlr_input_method_v2_send_unavailable(pIME);
pIME->unavailable();
return;
}
m_pWLRIME = pIME;
m_pIME = pIME;
hyprListener_IMECommit.initCallback(
&m_pWLRIME->events.commit,
[&](void* owner, void* data) {
const auto PTI = getFocusedTextInput();
const auto PIMR = (CInputMethodRelay*)owner;
listeners.commitIME = pIME->events.onCommit.registerListener([this](std::any d) {
const auto PTI = getFocusedTextInput();
if (!PTI) {
Debug::log(LOG, "No focused TextInput on IME Commit");
return;
}
if (!PTI) {
Debug::log(LOG, "No focused TextInput on IME Commit");
return;
}
PTI->updateIMEState(PIMR->m_pWLRIME);
},
this, "IMERelay");
PTI->updateIMEState(m_pIME.lock());
});
hyprListener_IMEDestroy.initCallback(
&m_pWLRIME->events.destroy,
[&](void* owner, void* data) {
m_pWLRIME = nullptr;
listeners.destroyIME = pIME->events.destroy.registerListener([this](std::any d) {
const auto PTI = getFocusedTextInput();
hyprListener_IMEDestroy.removeCallback();
hyprListener_IMECommit.removeCallback();
hyprListener_IMEGrab.removeCallback();
hyprListener_IMENewPopup.removeCallback();
Debug::log(LOG, "IME Destroy");
m_pKeyboardGrab.reset(nullptr);
if (PTI)
PTI->leave();
const auto PTI = getFocusedTextInput();
m_pIME.reset();
});
Debug::log(LOG, "IME Destroy");
listeners.newPopup = pIME->events.newPopup.registerListener([this](std::any d) {
m_vIMEPopups.emplace_back(std::make_unique<CInputPopup>(std::any_cast<SP<CInputMethodPopupV2>>(d)));
if (PTI)
PTI->leave();
},
this, "IMERelay");
hyprListener_IMEGrab.initCallback(
&m_pWLRIME->events.grab_keyboard,
[&](void* owner, void* data) {
Debug::log(LOG, "IME TextInput Keyboard Grab new");
m_pKeyboardGrab.reset(nullptr);
m_pKeyboardGrab = std::make_unique<SIMEKbGrab>();
m_pKeyboardGrab->pKeyboard = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat);
const auto PKBGRAB = (wlr_input_method_keyboard_grab_v2*)data;
m_pKeyboardGrab->pWlrKbGrab = PKBGRAB;
wlr_input_method_keyboard_grab_v2_set_keyboard(m_pKeyboardGrab->pWlrKbGrab, m_pKeyboardGrab->pKeyboard);
m_pKeyboardGrab->hyprListener_grabDestroy.initCallback(
&PKBGRAB->events.destroy,
[&](void* owner, void* data) {
m_pKeyboardGrab->hyprListener_grabDestroy.removeCallback();
Debug::log(LOG, "IME TextInput Keyboard Grab destroy");
m_pKeyboardGrab.reset(nullptr);
},
m_pKeyboardGrab.get(), "IME Keyboard Grab");
},
this, "IMERelay");
hyprListener_IMENewPopup.initCallback(
&m_pWLRIME->events.new_popup_surface,
[&](void* owner, void* data) {
m_vIMEPopups.emplace_back(std::make_unique<CInputPopup>((wlr_input_popup_surface_v2*)data));
Debug::log(LOG, "New input popup");
},
this, "IMERelay");
Debug::log(LOG, "New input popup");
});
if (!g_pCompositor->m_pLastFocus)
return;
@ -117,22 +72,6 @@ void CInputMethodRelay::removePopup(CInputPopup* pPopup) {
std::erase_if(m_vIMEPopups, [pPopup](const auto& other) { return other.get() == pPopup; });
}
SIMEKbGrab* CInputMethodRelay::getIMEKeyboardGrab(SKeyboard* pKeyboard) {
if (!m_pWLRIME)
return nullptr;
if (!m_pKeyboardGrab.get())
return nullptr;
const auto VIRTKB = wlr_input_device_get_virtual_keyboard(pKeyboard->keyboard);
if (VIRTKB && (wl_resource_get_client(VIRTKB->resource) == wl_resource_get_client(m_pKeyboardGrab->pWlrKbGrab->resource)))
return nullptr;
return m_pKeyboardGrab.get();
}
CTextInput* CInputMethodRelay::getFocusedTextInput() {
if (!g_pCompositor->m_pLastFocus)
return nullptr;
@ -164,33 +103,30 @@ void CInputMethodRelay::updateAllPopups() {
}
void CInputMethodRelay::activateIME(CTextInput* pInput) {
if (!m_pWLRIME)
if (m_pIME.expired())
return;
wlr_input_method_v2_send_activate(g_pInputManager->m_sIMERelay.m_pWLRIME);
m_pIME.lock()->activate();
commitIMEState(pInput);
}
void CInputMethodRelay::deactivateIME(CTextInput* pInput) {
if (!m_pWLRIME)
if (m_pIME.expired())
return;
if (!m_pWLRIME->active)
return;
wlr_input_method_v2_send_deactivate(g_pInputManager->m_sIMERelay.m_pWLRIME);
m_pIME.lock()->deactivate();
commitIMEState(pInput);
}
void CInputMethodRelay::commitIMEState(CTextInput* pInput) {
if (!m_pWLRIME)
if (m_pIME.expired())
return;
pInput->commitStateToIME(m_pWLRIME);
pInput->commitStateToIME(m_pIME.lock());
}
void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) {
if (!m_pWLRIME)
if (m_pIME.expired())
return;
if (pSurface == m_pLastKbFocus)

View file

@ -11,39 +11,36 @@
class CInputManager;
class CHyprRenderer;
struct STextInputV1;
class CInputMethodV2;
class CInputMethodRelay {
public:
CInputMethodRelay();
void onNewIME(wlr_input_method_v2*);
void onNewTextInput(std::any tiv3);
void onNewTextInput(STextInputV1* pTIV1);
void onNewIME(SP<CInputMethodV2>);
void onNewTextInput(std::any tiv3);
void onNewTextInput(STextInputV1* pTIV1);
wlr_input_method_v2* m_pWLRIME = nullptr;
void activateIME(CTextInput* pInput);
void deactivateIME(CTextInput* pInput);
void commitIMEState(CTextInput* pInput);
void removeTextInput(CTextInput* pInput);
void activateIME(CTextInput* pInput);
void deactivateIME(CTextInput* pInput);
void commitIMEState(CTextInput* pInput);
void removeTextInput(CTextInput* pInput);
void onKeyboardFocus(wlr_surface*);
void onKeyboardFocus(wlr_surface*);
CTextInput* getFocusedTextInput();
CTextInput* getFocusedTextInput();
void setIMEPopupFocus(CInputPopup*, wlr_surface*);
void removePopup(CInputPopup*);
SIMEKbGrab* getIMEKeyboardGrab(SKeyboard*);
CInputPopup* popupFromCoords(const Vector2D& point);
CInputPopup* popupFromSurface(const wlr_surface* surface);
void setIMEPopupFocus(CInputPopup*, wlr_surface*);
void removePopup(CInputPopup*);
void updateAllPopups();
CInputPopup* popupFromCoords(const Vector2D& point);
CInputPopup* popupFromSurface(const wlr_surface* surface);
void updateAllPopups();
WP<CInputMethodV2> m_pIME;
private:
std::unique_ptr<SIMEKbGrab> m_pKeyboardGrab;
std::vector<std::unique_ptr<CTextInput>> m_vTextInputs;
std::vector<std::unique_ptr<CInputPopup>> m_vIMEPopups;
@ -51,14 +48,12 @@ class CInputMethodRelay {
struct {
CHyprSignalListener newTIV3;
CHyprSignalListener newIME;
CHyprSignalListener commitIME;
CHyprSignalListener destroyIME;
CHyprSignalListener newPopup;
} listeners;
DYNLISTENER(textInputNew);
DYNLISTENER(IMECommit);
DYNLISTENER(IMEDestroy);
DYNLISTENER(IMEGrab);
DYNLISTENER(IMENewPopup);
friend class CHyprRenderer;
friend class CInputManager;
friend class CTextInputV1ProtocolManager;

View file

@ -4,6 +4,7 @@
#include "../../protocols/TextInputV1.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/TextInputV3.hpp"
#include "../../protocols/InputMethodV2.hpp"
CTextInput::CTextInput(STextInputV1* ti) : pV1Input(ti) {
ti->pTextInput = this;
@ -67,7 +68,7 @@ void CTextInput::initCallbacks() {
void CTextInput::onEnabled(wlr_surface* surfV1) {
Debug::log(LOG, "TI ENABLE");
if (!g_pInputManager->m_sIMERelay.m_pWLRIME) {
if (g_pInputManager->m_sIMERelay.m_pIME.expired()) {
// Debug::log(WARN, "Enabling TextInput on no IME!");
return;
}
@ -85,7 +86,7 @@ void CTextInput::onEnabled(wlr_surface* surfV1) {
}
void CTextInput::onDisabled() {
if (!g_pInputManager->m_sIMERelay.m_pWLRIME) {
if (g_pInputManager->m_sIMERelay.m_pIME.expired()) {
// Debug::log(WARN, "Disabling TextInput on no IME!");
return;
}
@ -103,7 +104,7 @@ void CTextInput::onDisabled() {
}
void CTextInput::onCommit() {
if (!g_pInputManager->m_sIMERelay.m_pWLRIME) {
if (g_pInputManager->m_sIMERelay.m_pIME.expired()) {
// Debug::log(WARN, "Committing TextInput on no IME!");
return;
}
@ -218,67 +219,66 @@ wl_client* CTextInput::client() {
return isV3() ? pV3Input.lock()->client() : pV1Input->client;
}
void CTextInput::commitStateToIME(wlr_input_method_v2* ime) {
void CTextInput::commitStateToIME(SP<CInputMethodV2> ime) {
if (isV3()) {
const auto INPUT = pV3Input.lock();
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);
ime->surroundingText(INPUT->current.surrounding.text, INPUT->current.surrounding.cursor, INPUT->current.surrounding.anchor);
wlr_input_method_v2_send_text_change_cause(ime, INPUT->current.cause);
ime->textChangeCause(INPUT->current.cause);
if (INPUT->current.contentType.updated)
wlr_input_method_v2_send_content_type(ime, INPUT->current.contentType.hint, INPUT->current.contentType.purpose);
ime->textContentType(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);
ime->surroundingText(pV1Input->pendingSurrounding.text, pV1Input->pendingSurrounding.cursor, pV1Input->pendingSurrounding.anchor);
wlr_input_method_v2_send_text_change_cause(ime, 0);
ime->textChangeCause(ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD);
if (pV1Input->pendingContentType.isPending)
wlr_input_method_v2_send_content_type(ime, pV1Input->pendingContentType.hint, pV1Input->pendingContentType.purpose);
ime->textContentType((zwpTextInputV3ContentHint)pV1Input->pendingContentType.hint, (zwpTextInputV3ContentPurpose)pV1Input->pendingContentType.purpose);
}
g_pInputManager->m_sIMERelay.updateAllPopups();
wlr_input_method_v2_send_done(ime);
ime->done();
}
void CTextInput::updateIMEState(wlr_input_method_v2* ime) {
void CTextInput::updateIMEState(SP<CInputMethodV2> ime) {
if (isV3()) {
const auto INPUT = pV3Input.lock();
if (ime->current.preedit.text)
INPUT->preeditString(ime->current.preedit.text, ime->current.preedit.cursor_begin, ime->current.preedit.cursor_end);
if (ime->current.preeditString.committed)
INPUT->preeditString(ime->current.preeditString.string, ime->current.preeditString.begin, ime->current.preeditString.end);
if (ime->current.commit_text)
INPUT->commitString(ime->current.commit_text);
if (ime->current.committedString.committed)
INPUT->commitString(ime->current.committedString.string);
if (ime->current.delete_.before_length || ime->current.delete_.after_length)
INPUT->deleteSurroundingText(ime->current.delete_.before_length, ime->current.delete_.after_length);
if (ime->current.deleteSurrounding.committed)
INPUT->deleteSurroundingText(ime->current.deleteSurrounding.before, ime->current.deleteSurrounding.after);
INPUT->sendDone();
} else {
if (ime->current.preedit.text) {
zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preedit.cursor_begin);
zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, std::string(ime->current.preedit.text).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preedit.text, "");
if (ime->current.preeditString.committed) {
zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preeditString.begin);
zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, std::string(ime->current.preeditString.string).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str(), "");
} else {
zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preedit.cursor_begin);
zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preeditString.begin);
zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, "", "");
}
if (ime->current.commit_text) {
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.commit_text);
}
if (ime->current.committedString.committed)
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.committedString.string.c_str());
if (ime->current.delete_.before_length || ime->current.delete_.after_length) {
zwp_text_input_v1_send_delete_surrounding_text(pV1Input->resourceImpl, std::string(ime->current.preedit.text).length() - ime->current.delete_.before_length,
ime->current.delete_.after_length + ime->current.delete_.before_length);
if (ime->current.deleteSurrounding.committed) {
zwp_text_input_v1_send_delete_surrounding_text(pV1Input->resourceImpl, std::string(ime->current.preeditString.string).length() - ime->current.deleteSurrounding.before,
ime->current.deleteSurrounding.after + ime->current.deleteSurrounding.before);
if (ime->current.preedit.text)
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preedit.text);
if (ime->current.preeditString.committed)
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str());
}
}
}

View file

@ -11,6 +11,7 @@ struct wl_client;
struct STextInputV1;
class CTextInputV3;
class CInputMethodV2;
class CTextInput {
public:
@ -23,8 +24,8 @@ class CTextInput {
void leave();
void tiV1Destroyed();
wl_client* client();
void commitStateToIME(wlr_input_method_v2* ime);
void updateIMEState(wlr_input_method_v2* ime);
void commitStateToIME(SP<CInputMethodV2> ime);
void updateIMEState(SP<CInputMethodV2> ime);
void onEnabled(wlr_surface* surfV1 = nullptr);
void onDisabled();

View file

@ -0,0 +1,383 @@
#include "InputMethodV2.hpp"
#include "../Compositor.hpp"
#include <sys/mman.h>
#define LOGM PROTO::ime->protoLog
CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP<CZwpInputMethodKeyboardGrabV2> resource_, SP<CInputMethodV2> owner_) : resource(resource_), owner(owner_) {
if (!resource->resource())
return;
resource->setRelease([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); });
resource->setOnDestroy([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); });
const auto PKEYBOARD = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat);
if (!PKEYBOARD) {
LOGM(ERR, "IME called but no active keyboard???");
return;
}
sendKeyboardData(PKEYBOARD);
}
CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
if (!owner.expired())
std::erase_if(owner.lock()->grabs, [](const auto& g) { return g.expired(); });
}
void CInputMethodKeyboardGrabV2::sendKeyboardData(wlr_keyboard* keyboard) {
if (keyboard == pLastKeyboard)
return;
pLastKeyboard = keyboard;
int keymapFD = allocateSHMFile(keyboard->keymap_size);
if (keymapFD < 0) {
LOGM(ERR, "Failed to create a keymap file for keyboard grab");
return;
}
void* data = mmap(nullptr, keyboard->keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD, 0);
if (data == MAP_FAILED) {
LOGM(ERR, "Failed to mmap a keymap file for keyboard grab");
close(keymapFD);
return;
}
memcpy(data, keyboard->keymap_string, keyboard->keymap_size);
munmap(data, keyboard->keymap_size);
resource->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymapFD, keyboard->keymap_size);
close(keymapFD);
sendMods(0, 0, 0, 0);
resource->sendRepeatInfo(keyboard->repeat_info.rate, keyboard->repeat_info.delay);
}
void CInputMethodKeyboardGrabV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) {
const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, wl_resource_get_client(resource->resource())));
resource->sendKey(SERIAL, time, key, (uint32_t)state);
}
void CInputMethodKeyboardGrabV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, wl_resource_get_client(resource->resource())));
resource->sendModifiers(SERIAL, depressed, latched, locked, group);
}
bool CInputMethodKeyboardGrabV2::good() {
return resource->resource();
}
SP<CInputMethodV2> CInputMethodKeyboardGrabV2::getOwner() {
return owner.lock();
}
wl_client* CInputMethodKeyboardGrabV2::client() {
return wl_resource_get_client(resource->resource());
}
CInputMethodPopupV2::CInputMethodPopupV2(SP<CZwpInputPopupSurfaceV2> resource_, SP<CInputMethodV2> owner_, wlr_surface* wlrSurface) : resource(resource_), owner(owner_) {
if (!resource->resource())
return;
resource->setDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); });
resource->setOnDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); });
pSurface = wlrSurface;
hyprListener_destroySurface.initCallback(
&wlrSurface->events.destroy,
[this](void* owner, void* data) {
if (mapped)
events.unmap.emit();
hyprListener_commitSurface.removeCallback();
hyprListener_destroySurface.removeCallback();
if (g_pCompositor->m_pLastFocus == pSurface)
g_pCompositor->m_pLastFocus = nullptr;
pSurface = nullptr;
},
this, "IMEPopup");
hyprListener_commitSurface.initCallback(
&wlrSurface->events.commit,
[this](void* owner, void* data) {
if (pSurface->pending.buffer_width > 0 && pSurface->pending.buffer_height > 0 && !mapped) {
mapped = true;
wlr_surface_map(pSurface);
events.map.emit();
return;
}
if (pSurface->pending.buffer_width <= 0 && pSurface->pending.buffer_height <= 0 && mapped) {
mapped = false;
wlr_surface_unmap(pSurface);
events.unmap.emit();
return;
}
events.commit.emit();
},
this, "IMEPopup");
}
CInputMethodPopupV2::~CInputMethodPopupV2() {
if (!owner.expired())
std::erase_if(owner.lock()->popups, [](const auto& p) { return p.expired(); });
events.destroy.emit();
}
bool CInputMethodPopupV2::good() {
return resource->resource();
}
void CInputMethodPopupV2::sendInputRectangle(const CBox& box) {
resource->sendTextInputRectangle(box.x, box.y, box.w, box.h);
}
wlr_surface* CInputMethodPopupV2::surface() {
return pSurface;
}
void CInputMethodV2::SState::reset() {
committedString.committed = false;
deleteSurrounding.committed = false;
preeditString.committed = false;
}
CInputMethodV2::CInputMethodV2(SP<CZwpInputMethodV2> resource_) : resource(resource_) {
if (!resource->resource())
return;
resource->setDestroy([this](CZwpInputMethodV2* r) {
events.destroy.emit();
PROTO::ime->destroyResource(this);
});
resource->setOnDestroy([this](CZwpInputMethodV2* r) {
events.destroy.emit();
PROTO::ime->destroyResource(this);
});
resource->setCommitString([this](CZwpInputMethodV2* r, const char* str) {
pending.committedString.string = str;
pending.committedString.committed = true;
});
resource->setDeleteSurroundingText([this](CZwpInputMethodV2* r, uint32_t before, uint32_t after) {
pending.deleteSurrounding.before = before;
pending.deleteSurrounding.after = after;
pending.deleteSurrounding.committed = true;
});
resource->setSetPreeditString([this](CZwpInputMethodV2* r, const char* str, int32_t begin, int32_t end) {
pending.preeditString.string = str;
pending.preeditString.begin = begin;
pending.preeditString.end = end;
pending.preeditString.committed = true;
});
resource->setCommit([this](CZwpInputMethodV2* r, uint32_t serial) {
current = pending;
pending.reset();
events.onCommit.emit();
});
resource->setGetInputPopupSurface([this](CZwpInputMethodV2* r, uint32_t id, wl_resource* surface) {
const auto CLIENT = wl_resource_get_client(r->resource());
const auto RESOURCE = PROTO::ime->m_vPopups.emplace_back(std::make_shared<CInputMethodPopupV2>(
std::make_shared<CZwpInputPopupSurfaceV2>(CLIENT, wl_resource_get_version(r->resource()), id), self.lock(), wlr_surface_from_resource(surface)));
if (!RESOURCE->good()) {
wl_resource_post_no_memory(r->resource());
PROTO::ime->m_vPopups.pop_back();
return;
}
LOGM(LOG, "New IME Popup with resource id {}", id);
popups.emplace_back(RESOURCE);
events.newPopup.emit(RESOURCE);
});
resource->setGrabKeyboard([this](CZwpInputMethodV2* r, uint32_t id) {
const auto CLIENT = wl_resource_get_client(r->resource());
const auto RESOURCE = PROTO::ime->m_vGrabs.emplace_back(
std::make_shared<CInputMethodKeyboardGrabV2>(std::make_shared<CZwpInputMethodKeyboardGrabV2>(CLIENT, wl_resource_get_version(r->resource()), id), self.lock()));
if (!RESOURCE->good()) {
wl_resource_post_no_memory(r->resource());
PROTO::ime->m_vGrabs.pop_back();
return;
}
LOGM(LOG, "New IME Grab with resource id {}", id);
grabs.emplace_back(RESOURCE);
});
}
CInputMethodV2::~CInputMethodV2() {
events.destroy.emit();
}
bool CInputMethodV2::good() {
return resource->resource();
}
void CInputMethodV2::activate() {
if (active)
return;
resource->sendActivate();
active = true;
}
void CInputMethodV2::deactivate() {
if (!active)
return;
resource->sendDeactivate();
active = false;
}
void CInputMethodV2::surroundingText(const std::string& text, uint32_t cursor, uint32_t anchor) {
resource->sendSurroundingText(text.c_str(), cursor, anchor);
}
void CInputMethodV2::textChangeCause(zwpTextInputV3ChangeCause changeCause) {
resource->sendTextChangeCause((uint32_t)changeCause);
}
void CInputMethodV2::textContentType(zwpTextInputV3ContentHint hint, zwpTextInputV3ContentPurpose purpose) {
resource->sendContentType((uint32_t)hint, (uint32_t)purpose);
}
void CInputMethodV2::done() {
resource->sendDone();
}
void CInputMethodV2::unavailable() {
resource->sendUnavailable();
}
bool CInputMethodV2::hasGrab() {
return !grabs.empty();
}
wl_client* CInputMethodV2::grabClient() {
if (grabs.empty())
return nullptr;
for (auto& gw : grabs) {
auto g = gw.lock();
if (!g)
continue;
return g->client();
}
return nullptr;
}
void CInputMethodV2::sendInputRectangle(const CBox& box) {
inputRectangle = box;
for (auto& wp : popups) {
auto p = wp.lock();
if (!p)
continue;
p->sendInputRectangle(inputRectangle);
}
}
void CInputMethodV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) {
for (auto& gw : grabs) {
auto g = gw.lock();
if (!g)
continue;
g->sendKey(time, key, state);
}
}
void CInputMethodV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
for (auto& gw : grabs) {
auto g = gw.lock();
if (!g)
continue;
g->sendMods(depressed, latched, locked, group);
}
}
void CInputMethodV2::setKeyboard(wlr_keyboard* keyboard) {
for (auto& gw : grabs) {
auto g = gw.lock();
if (!g)
continue;
g->sendKeyboardData(keyboard);
}
}
wl_client* CInputMethodV2::client() {
return wl_resource_get_client(resource->resource());
}
CInputMethodV2Protocol::CInputMethodV2Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CInputMethodV2Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(std::make_unique<CZwpInputMethodManagerV2>(client, ver, id)).get();
RESOURCE->setOnDestroy([this](CZwpInputMethodManagerV2* p) { this->onManagerResourceDestroy(p->resource()); });
RESOURCE->setDestroy([this](CZwpInputMethodManagerV2* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); });
RESOURCE->setGetInputMethod([this](CZwpInputMethodManagerV2* pMgr, wl_resource* seat, uint32_t id) { this->onGetIME(pMgr, seat, id); });
}
void CInputMethodV2Protocol::onManagerResourceDestroy(wl_resource* res) {
std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; });
}
void CInputMethodV2Protocol::destroyResource(CInputMethodPopupV2* popup) {
std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == popup; });
}
void CInputMethodV2Protocol::destroyResource(CInputMethodKeyboardGrabV2* grab) {
std::erase_if(m_vGrabs, [&](const auto& other) { return other.get() == grab; });
}
void CInputMethodV2Protocol::destroyResource(CInputMethodV2* ime) {
std::erase_if(m_vIMEs, [&](const auto& other) { return other.get() == ime; });
}
void CInputMethodV2Protocol::onGetIME(CZwpInputMethodManagerV2* mgr, wl_resource* seat, uint32_t id) {
const auto CLIENT = wl_resource_get_client(mgr->resource());
const auto RESOURCE = m_vIMEs.emplace_back(std::make_shared<CInputMethodV2>(std::make_shared<CZwpInputMethodV2>(CLIENT, wl_resource_get_version(mgr->resource()), id)));
if (!RESOURCE->good()) {
wl_resource_post_no_memory(mgr->resource());
m_vIMEs.pop_back();
return;
}
RESOURCE->self = RESOURCE;
LOGM(LOG, "New IME with resource id {}", id);
events.newIME.emit(RESOURCE);
}

View file

@ -0,0 +1,160 @@
#pragma once
#include <memory>
#include <vector>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "input-method-unstable-v2.hpp"
#include "text-input-unstable-v3.hpp"
#include "../helpers/signal/Signal.hpp"
#include "../desktop/WLSurface.hpp"
class CInputMethodKeyboardGrabV2;
class CInputMethodPopupV2;
class CInputMethodV2 {
public:
CInputMethodV2(SP<CZwpInputMethodV2> resource_);
~CInputMethodV2();
struct {
CSignal onCommit;
CSignal destroy;
CSignal newPopup;
} events;
struct SState {
void reset();
struct {
std::string string;
bool committed = false;
} committedString;
struct {
std::string string;
int32_t begin = 0, end = 0;
bool committed = false;
} preeditString;
struct {
uint32_t before = 0, after = 0;
bool committed = false;
} deleteSurrounding;
};
SState pending, current;
bool good();
void activate();
void deactivate();
void surroundingText(const std::string& text, uint32_t cursor, uint32_t anchor);
void textChangeCause(zwpTextInputV3ChangeCause changeCause);
void textContentType(zwpTextInputV3ContentHint hint, zwpTextInputV3ContentPurpose purpose);
void done();
void unavailable();
void sendInputRectangle(const CBox& box);
bool hasGrab();
void sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state);
void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
void setKeyboard(wlr_keyboard* keyboard);
wl_client* client();
wl_client* grabClient();
private:
SP<CZwpInputMethodV2> resource;
std::vector<WP<CInputMethodKeyboardGrabV2>> grabs;
std::vector<WP<CInputMethodPopupV2>> popups;
WP<CInputMethodV2> self;
bool active = false;
CBox inputRectangle;
friend class CInputMethodPopupV2;
friend class CInputMethodKeyboardGrabV2;
friend class CInputMethodV2Protocol;
};
class CInputMethodKeyboardGrabV2 {
public:
CInputMethodKeyboardGrabV2(SP<CZwpInputMethodKeyboardGrabV2> resource_, SP<CInputMethodV2> owner_);
~CInputMethodKeyboardGrabV2();
bool good();
SP<CInputMethodV2> getOwner();
wl_client* client();
void sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state);
void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
void sendKeyboardData(wlr_keyboard* keyboard);
private:
SP<CZwpInputMethodKeyboardGrabV2> resource;
WP<CInputMethodV2> owner;
wlr_keyboard* pLastKeyboard = nullptr; // READ-ONLY
};
class CInputMethodPopupV2 {
public:
CInputMethodPopupV2(SP<CZwpInputPopupSurfaceV2> resource_, SP<CInputMethodV2> owner_, wlr_surface* surface);
~CInputMethodPopupV2();
bool good();
void sendInputRectangle(const CBox& box);
wlr_surface* surface();
struct {
CSignal map;
CSignal unmap;
CSignal commit;
CSignal destroy;
} events;
bool mapped = false;
private:
SP<CZwpInputPopupSurfaceV2> resource;
WP<CInputMethodV2> owner;
wlr_surface* pSurface = nullptr;
DYNLISTENER(commitSurface);
DYNLISTENER(destroySurface);
};
class CInputMethodV2Protocol : public IWaylandProtocol {
public:
CInputMethodV2Protocol(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 newIME; // SP<CInputMethodV2>
} events;
private:
void onManagerResourceDestroy(wl_resource* res);
void destroyResource(CInputMethodPopupV2* popup);
void destroyResource(CInputMethodKeyboardGrabV2* grab);
void destroyResource(CInputMethodV2* ime);
void onGetIME(CZwpInputMethodManagerV2* mgr, wl_resource* seat, uint32_t id);
//
std::vector<UP<CZwpInputMethodManagerV2>> m_vManagers;
std::vector<SP<CInputMethodV2>> m_vIMEs;
std::vector<SP<CInputMethodKeyboardGrabV2>> m_vGrabs;
std::vector<SP<CInputMethodPopupV2>> m_vPopups;
friend class CInputMethodPopupV2;
friend class CInputMethodKeyboardGrabV2;
friend class CInputMethodV2;
};
namespace PROTO {
inline UP<CInputMethodV2Protocol> ime;
};