Hyprland/src/protocols/core/Seat.cpp

565 lines
18 KiB
C++
Raw Normal View History

2024-05-10 18:27:57 +01:00
#include "Seat.hpp"
#include "Compositor.hpp"
#include "DataDevice.hpp"
2024-05-10 18:27:57 +01:00
#include "../../devices/IKeyboard.hpp"
#include "../../devices/IHID.hpp"
2024-05-10 18:27:57 +01:00
#include "../../managers/SeatManager.hpp"
#include "../../config/ConfigValue.hpp"
#include <algorithm>
#include <fcntl.h>
CWLTouchResource::CWLTouchResource(SP<CWlTouch> resource_, SP<CWLSeatResource> owner_) : owner(owner_), resource(resource_) {
if UNLIKELY (!good())
2024-05-10 18:27:57 +01:00
return;
resource->setRelease([this](CWlTouch* r) { PROTO::seat->destroyResource(this); });
resource->setOnDestroy([this](CWlTouch* r) { PROTO::seat->destroyResource(this); });
}
bool CWLTouchResource::good() {
return resource->resource();
}
void CWLTouchResource::sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs, int32_t id, const Vector2D& local) {
2024-05-10 18:27:57 +01:00
if (!owner)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
return;
ASSERT(surface->client() == owner->client());
2024-05-10 18:27:57 +01:00
currentSurface = surface;
listeners.destroySurface = surface->events.destroy.registerListener([this, timeMs, id](std::any d) { sendUp(timeMs + 10 /* hack */, id); });
2024-05-10 18:27:57 +01:00
resource->sendDown(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->getResource().get(), id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
fingers++;
2024-05-10 18:27:57 +01:00
}
void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) {
if (!owner)
2024-05-10 18:27:57 +01:00
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
return;
2024-05-10 18:27:57 +01:00
resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id);
fingers--;
if (fingers <= 0) {
currentSurface.reset();
listeners.destroySurface.reset();
fingers = 0;
}
2024-05-10 18:27:57 +01:00
}
void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner)
2024-05-10 18:27:57 +01:00
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
return;
2024-05-10 18:27:57 +01:00
resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
}
void CWLTouchResource::sendFrame() {
if (!owner)
2024-05-10 18:27:57 +01:00
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
return;
2024-05-10 18:27:57 +01:00
resource->sendFrame();
}
void CWLTouchResource::sendCancel() {
if (!owner || !currentSurface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
return;
2024-05-10 18:27:57 +01:00
resource->sendCancel();
}
void CWLTouchResource::sendShape(int32_t id, const Vector2D& shape) {
if (!owner || !currentSurface || resource->version() < 6)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
return;
2024-05-10 18:27:57 +01:00
resource->sendShape(id, wl_fixed_from_double(shape.x), wl_fixed_from_double(shape.y));
}
void CWLTouchResource::sendOrientation(int32_t id, double angle) {
if (!owner || !currentSurface || resource->version() < 6)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
return;
2024-05-10 18:27:57 +01:00
resource->sendOrientation(id, wl_fixed_from_double(angle));
}
CWLPointerResource::CWLPointerResource(SP<CWlPointer> resource_, SP<CWLSeatResource> owner_) : owner(owner_), resource(resource_) {
if UNLIKELY (!good())
2024-05-10 18:27:57 +01:00
return;
resource->setRelease([this](CWlPointer* r) { PROTO::seat->destroyResource(this); });
resource->setOnDestroy([this](CWlPointer* r) { PROTO::seat->destroyResource(this); });
resource->setSetCursor([this](CWlPointer* r, uint32_t serial, wl_resource* surf, int32_t hotX, int32_t hotY) {
if (!owner) {
LOGM(ERR, "Client bug: setCursor when seatClient is already dead");
return;
}
auto surfResource = surf ? CWLSurfaceResource::fromResource(surf) : nullptr;
if (surfResource && surfResource->role->role() != SURFACE_ROLE_CURSOR && surfResource->role->role() != SURFACE_ROLE_UNASSIGNED) {
r->error(-1, "Cursor surface already has a different role");
return;
}
if (surfResource) {
surfResource->role = makeShared<CCursorSurfaceRole>();
surfResource->updateCursorShm();
}
g_pSeatManager->onSetCursor(owner.lock(), serial, surfResource, {hotX, hotY});
2024-05-10 18:27:57 +01:00
});
if (g_pSeatManager->state.pointerFocus && g_pSeatManager->state.pointerFocus->client() == resource->client())
sendEnter(g_pSeatManager->state.pointerFocus.lock(), {-1, -1} /* Coords don't really matter that much, they will be updated next move */);
2024-05-10 18:27:57 +01:00
}
int CWLPointerResource::version() {
return resource->version();
}
2024-05-10 18:27:57 +01:00
bool CWLPointerResource::good() {
return resource->resource();
}
void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2D& local) {
2024-05-10 18:27:57 +01:00
if (!owner || currentSurface == surface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
if (currentSurface) {
LOGM(WARN, "requested CWLPointerResource::sendEnter without sendLeave first.");
sendLeave();
}
ASSERT(surface->client() == owner->client());
2024-05-10 18:27:57 +01:00
currentSurface = surface;
listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { sendLeave(); });
2024-05-10 18:27:57 +01:00
resource->sendEnter(g_pSeatManager->nextSerial(owner.lock()), surface->getResource().get(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
2024-05-10 18:27:57 +01:00
}
void CWLPointerResource::sendLeave() {
if (!owner || !currentSurface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
// release all buttons unless we have a dnd going on in which case
// the events shall be lost.
if (!PROTO::data->dndActive()) {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
for (auto const& b : pressedButtons) {
sendButton(now.tv_sec * 1000 + now.tv_nsec / 1000000, b, WL_POINTER_BUTTON_STATE_RELEASED);
}
}
pressedButtons.clear();
resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get());
currentSurface.reset();
listeners.destroySurface.reset();
2024-05-10 18:27:57 +01:00
}
void CWLPointerResource::sendMotion(uint32_t timeMs, const Vector2D& local) {
if (!owner || !currentSurface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
}
void CWLPointerResource::sendButton(uint32_t timeMs, uint32_t button, wl_pointer_button_state state) {
if (!owner || !currentSurface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
if (state == WL_POINTER_BUTTON_STATE_RELEASED && std::find(pressedButtons.begin(), pressedButtons.end(), button) == pressedButtons.end()) {
LOGM(ERR, "sendButton release on a non-pressed button");
return;
} else if (state == WL_POINTER_BUTTON_STATE_PRESSED && std::find(pressedButtons.begin(), pressedButtons.end(), button) != pressedButtons.end()) {
LOGM(ERR, "sendButton press on a non-pressed button");
return;
}
if (state == WL_POINTER_BUTTON_STATE_RELEASED)
std::erase(pressedButtons, button);
else if (state == WL_POINTER_BUTTON_STATE_PRESSED)
pressedButtons.emplace_back(button);
2024-05-10 18:27:57 +01:00
resource->sendButton(g_pSeatManager->nextSerial(owner.lock()), timeMs, button, state);
}
void CWLPointerResource::sendAxis(uint32_t timeMs, wl_pointer_axis axis, double value) {
if (!owner || !currentSurface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
resource->sendAxis(timeMs, axis, wl_fixed_from_double(value));
}
void CWLPointerResource::sendFrame() {
if (!owner || resource->version() < 5)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
resource->sendFrame();
}
void CWLPointerResource::sendAxisSource(wl_pointer_axis_source source) {
if (!owner || !currentSurface || resource->version() < 5)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
resource->sendAxisSource(source);
}
void CWLPointerResource::sendAxisStop(uint32_t timeMs, wl_pointer_axis axis) {
if (!owner || !currentSurface || resource->version() < 5)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
resource->sendAxisStop(timeMs, axis);
}
void CWLPointerResource::sendAxisDiscrete(wl_pointer_axis axis, int32_t discrete) {
if (!owner || !currentSurface || resource->version() < 5)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
resource->sendAxisDiscrete(axis, discrete);
}
void CWLPointerResource::sendAxisValue120(wl_pointer_axis axis, int32_t value120) {
if (!owner || !currentSurface || resource->version() < 8)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
resource->sendAxisValue120(axis, value120);
}
void CWLPointerResource::sendAxisRelativeDirection(wl_pointer_axis axis, wl_pointer_axis_relative_direction direction) {
if (!owner || !currentSurface || resource->version() < 9)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
return;
2024-05-10 18:27:57 +01:00
resource->sendAxisRelativeDirection(axis, direction);
}
CWLKeyboardResource::CWLKeyboardResource(SP<CWlKeyboard> resource_, SP<CWLSeatResource> owner_) : owner(owner_), resource(resource_) {
if UNLIKELY (!good())
2024-05-10 18:27:57 +01:00
return;
resource->setRelease([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); });
resource->setOnDestroy([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); });
if (!g_pSeatManager->keyboard) {
LOGM(ERR, "No keyboard on bound wl_keyboard??");
return;
}
2024-05-10 18:27:57 +01:00
sendKeymap(g_pSeatManager->keyboard.lock());
repeatInfo(g_pSeatManager->keyboard->repeatRate, g_pSeatManager->keyboard->repeatDelay);
if (g_pSeatManager->state.keyboardFocus && g_pSeatManager->state.keyboardFocus->client() == resource->client())
sendEnter(g_pSeatManager->state.keyboardFocus.lock());
2024-05-10 18:27:57 +01:00
}
bool CWLKeyboardResource::good() {
return resource->resource();
}
void CWLKeyboardResource::sendKeymap(SP<IKeyboard> keyboard) {
if (!keyboard)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
return;
core: begin using CFileDescriptor from hyprutils (#9122) * config: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * hyprctl: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * ikeyboard: make fd use CFileDescriptor make use of the new CFileDescriptor instead of manual FD handling, also in sendKeymap remove dead code, it already early returns if keyboard isnt valid, and dont try to close the FD that ikeyboard owns. * core: make SHMFile functions use CFileDescriptor make SHMFile misc functions use CFileDescriptor and its associated usage in dmabuf and keyboard. * core: make explicit sync use CFileDescriptor begin using CFileDescriptor in explicit sync and its timelines and eglsync usage in opengl, there is still a bit left with manual handling that requires future aquamarine change aswell. * eventmgr: make fd and sockets use CFileDescriptor make use of the hyprutils CFileDescriptor instead of manual FD and socket handling and closing. * eventloopmgr: make timerfd use CFileDescriptor make the timerfd use CFileDescriptor instead of manual fd handling * opengl: make gbm fd use CFileDescriptor make the gbm rendernode fd use CFileDescriptor instead of manual fd handling * core: make selection source/offer use CFileDescriptor make data selection source and offers use CFileDescriptor and its associated use in xwm and protocols * protocols: convert protocols fd to CFileDescriptor make most fd handling use CFileDescriptor in protocols * shm: make SHMPool use CfileDescriptor make SHMPool use CFileDescriptor instead of manual fd handling. * opengl: duplicate fd with CFileDescriptor duplicate fenceFD with CFileDescriptor duplicate instead. * xwayland: make sockets and fds use CFileDescriptor instead of manual opening/closing make sockets and fds use CFileDescriptor * keybindmgr: make sockets and fds use CFileDescriptor make sockets and fds use CFileDescriptor instead of manual handling.
2025-01-30 12:30:12 +01:00
std::string_view keymap = keyboard->xkbKeymapString;
Hyprutils::OS::CFileDescriptor& fd = keyboard->xkbKeymapFD;
uint32_t size = keyboard->xkbKeymapString.length() + 1;
2024-05-10 18:27:57 +01:00
core: begin using CFileDescriptor from hyprutils (#9122) * config: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * hyprctl: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * ikeyboard: make fd use CFileDescriptor make use of the new CFileDescriptor instead of manual FD handling, also in sendKeymap remove dead code, it already early returns if keyboard isnt valid, and dont try to close the FD that ikeyboard owns. * core: make SHMFile functions use CFileDescriptor make SHMFile misc functions use CFileDescriptor and its associated usage in dmabuf and keyboard. * core: make explicit sync use CFileDescriptor begin using CFileDescriptor in explicit sync and its timelines and eglsync usage in opengl, there is still a bit left with manual handling that requires future aquamarine change aswell. * eventmgr: make fd and sockets use CFileDescriptor make use of the hyprutils CFileDescriptor instead of manual FD and socket handling and closing. * eventloopmgr: make timerfd use CFileDescriptor make the timerfd use CFileDescriptor instead of manual fd handling * opengl: make gbm fd use CFileDescriptor make the gbm rendernode fd use CFileDescriptor instead of manual fd handling * core: make selection source/offer use CFileDescriptor make data selection source and offers use CFileDescriptor and its associated use in xwm and protocols * protocols: convert protocols fd to CFileDescriptor make most fd handling use CFileDescriptor in protocols * shm: make SHMPool use CfileDescriptor make SHMPool use CFileDescriptor instead of manual fd handling. * opengl: duplicate fd with CFileDescriptor duplicate fenceFD with CFileDescriptor duplicate instead. * xwayland: make sockets and fds use CFileDescriptor instead of manual opening/closing make sockets and fds use CFileDescriptor * keybindmgr: make sockets and fds use CFileDescriptor make sockets and fds use CFileDescriptor instead of manual handling.
2025-01-30 12:30:12 +01:00
if (keymap == lastKeymap)
seat: avoid sending pointless 'keymap' and 'repeat_info' events (#8276) #### Describe your PR, what does it fix/add? Fix lag spikes when pressing more than 6 keys at the same time. #### Is there anything you want to mention? (unchecked code, possible bugs, found problems, breaking compatibility, etc.) Debugging process: <details> This is triggered by typing some applications, like CopyQ or XWayland. Typing in Firefox doesn't lead to lag, however it itself does lag handling these events. Profiling CopyQ shows that paths leading to `QtWaylandClient::QWaylandInputDevice::Keyboard::keyboard` take over 80% of processing time of an otherwise idle program. Looking at output of 'wev' even when it's not focused shows same events received over and over again. ``` [14: wl_keyboard] repeat_info: rate: 25 keys/sec; delay: 300 ms [14: wl_keyboard] keymap: format: 1 (xkb v1), size: 64754 ``` Looking at what passes through CInputManager::onKeyboardKey() -> CSeatManager::setKeyboard() shows Hyprland 'switching' between endpoints of the same keyboard, one of them being named like the other but with '-1' suffix. </details> Tested changing layouts in Fcitx5 and with following config. ``` input:kb_layout = us,cz input:kb_variant = ,qwerty input:kb_options = grp:alt_shift_toggle ``` Also tested changing 'input:repeat_delay' while running. Curiously, now these events appear in the output of 'wev' only once. Changing layouts still seems to work fine though. #### Is it ready for merging, or does it need work? Ready for merging.
2024-10-28 22:25:27 +03:00
return;
core: begin using CFileDescriptor from hyprutils (#9122) * config: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * hyprctl: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * ikeyboard: make fd use CFileDescriptor make use of the new CFileDescriptor instead of manual FD handling, also in sendKeymap remove dead code, it already early returns if keyboard isnt valid, and dont try to close the FD that ikeyboard owns. * core: make SHMFile functions use CFileDescriptor make SHMFile misc functions use CFileDescriptor and its associated usage in dmabuf and keyboard. * core: make explicit sync use CFileDescriptor begin using CFileDescriptor in explicit sync and its timelines and eglsync usage in opengl, there is still a bit left with manual handling that requires future aquamarine change aswell. * eventmgr: make fd and sockets use CFileDescriptor make use of the hyprutils CFileDescriptor instead of manual FD and socket handling and closing. * eventloopmgr: make timerfd use CFileDescriptor make the timerfd use CFileDescriptor instead of manual fd handling * opengl: make gbm fd use CFileDescriptor make the gbm rendernode fd use CFileDescriptor instead of manual fd handling * core: make selection source/offer use CFileDescriptor make data selection source and offers use CFileDescriptor and its associated use in xwm and protocols * protocols: convert protocols fd to CFileDescriptor make most fd handling use CFileDescriptor in protocols * shm: make SHMPool use CfileDescriptor make SHMPool use CFileDescriptor instead of manual fd handling. * opengl: duplicate fd with CFileDescriptor duplicate fenceFD with CFileDescriptor duplicate instead. * xwayland: make sockets and fds use CFileDescriptor instead of manual opening/closing make sockets and fds use CFileDescriptor * keybindmgr: make sockets and fds use CFileDescriptor make sockets and fds use CFileDescriptor instead of manual handling.
2025-01-30 12:30:12 +01:00
seat: avoid sending pointless 'keymap' and 'repeat_info' events (#8276) #### Describe your PR, what does it fix/add? Fix lag spikes when pressing more than 6 keys at the same time. #### Is there anything you want to mention? (unchecked code, possible bugs, found problems, breaking compatibility, etc.) Debugging process: <details> This is triggered by typing some applications, like CopyQ or XWayland. Typing in Firefox doesn't lead to lag, however it itself does lag handling these events. Profiling CopyQ shows that paths leading to `QtWaylandClient::QWaylandInputDevice::Keyboard::keyboard` take over 80% of processing time of an otherwise idle program. Looking at output of 'wev' even when it's not focused shows same events received over and over again. ``` [14: wl_keyboard] repeat_info: rate: 25 keys/sec; delay: 300 ms [14: wl_keyboard] keymap: format: 1 (xkb v1), size: 64754 ``` Looking at what passes through CInputManager::onKeyboardKey() -> CSeatManager::setKeyboard() shows Hyprland 'switching' between endpoints of the same keyboard, one of them being named like the other but with '-1' suffix. </details> Tested changing layouts in Fcitx5 and with following config. ``` input:kb_layout = us,cz input:kb_variant = ,qwerty input:kb_options = grp:alt_shift_toggle ``` Also tested changing 'input:repeat_delay' while running. Curiously, now these events appear in the output of 'wev' only once. Changing layouts still seems to work fine though. #### Is it ready for merging, or does it need work? Ready for merging.
2024-10-28 22:25:27 +03:00
lastKeymap = keymap;
const wl_keyboard_keymap_format format = keyboard ? WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 : WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP;
core: begin using CFileDescriptor from hyprutils (#9122) * config: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * hyprctl: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * ikeyboard: make fd use CFileDescriptor make use of the new CFileDescriptor instead of manual FD handling, also in sendKeymap remove dead code, it already early returns if keyboard isnt valid, and dont try to close the FD that ikeyboard owns. * core: make SHMFile functions use CFileDescriptor make SHMFile misc functions use CFileDescriptor and its associated usage in dmabuf and keyboard. * core: make explicit sync use CFileDescriptor begin using CFileDescriptor in explicit sync and its timelines and eglsync usage in opengl, there is still a bit left with manual handling that requires future aquamarine change aswell. * eventmgr: make fd and sockets use CFileDescriptor make use of the hyprutils CFileDescriptor instead of manual FD and socket handling and closing. * eventloopmgr: make timerfd use CFileDescriptor make the timerfd use CFileDescriptor instead of manual fd handling * opengl: make gbm fd use CFileDescriptor make the gbm rendernode fd use CFileDescriptor instead of manual fd handling * core: make selection source/offer use CFileDescriptor make data selection source and offers use CFileDescriptor and its associated use in xwm and protocols * protocols: convert protocols fd to CFileDescriptor make most fd handling use CFileDescriptor in protocols * shm: make SHMPool use CfileDescriptor make SHMPool use CFileDescriptor instead of manual fd handling. * opengl: duplicate fd with CFileDescriptor duplicate fenceFD with CFileDescriptor duplicate instead. * xwayland: make sockets and fds use CFileDescriptor instead of manual opening/closing make sockets and fds use CFileDescriptor * keybindmgr: make sockets and fds use CFileDescriptor make sockets and fds use CFileDescriptor instead of manual handling.
2025-01-30 12:30:12 +01:00
resource->sendKeymap(format, fd.get(), size);
2024-05-10 18:27:57 +01:00
}
void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
2024-05-10 18:27:57 +01:00
if (!owner || currentSurface == surface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
return;
2024-05-10 18:27:57 +01:00
if (currentSurface) {
LOGM(WARN, "requested CWLKeyboardResource::sendEnter without sendLeave first.");
sendLeave();
}
ASSERT(surface->client() == owner->client());
2024-05-10 18:27:57 +01:00
currentSurface = surface;
listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { sendLeave(); });
2024-05-10 18:27:57 +01:00
wl_array arr;
wl_array_init(&arr);
resource->sendEnter(g_pSeatManager->nextSerial(owner.lock()), surface->getResource().get(), &arr);
2024-05-10 18:27:57 +01:00
wl_array_release(&arr);
}
void CWLKeyboardResource::sendLeave() {
if (!owner || !currentSurface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
return;
resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get());
currentSurface.reset();
listeners.destroySurface.reset();
2024-05-10 18:27:57 +01:00
}
void CWLKeyboardResource::sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state) {
if (!owner || !currentSurface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
return;
2024-05-10 18:27:57 +01:00
resource->sendKey(g_pSeatManager->nextSerial(owner.lock()), timeMs, key, state);
}
void CWLKeyboardResource::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
if (!owner || !currentSurface)
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
return;
2024-05-10 18:27:57 +01:00
resource->sendModifiers(g_pSeatManager->nextSerial(owner.lock()), depressed, latched, locked, group);
}
void CWLKeyboardResource::repeatInfo(uint32_t rate, uint32_t delayMs) {
seat: avoid sending pointless 'keymap' and 'repeat_info' events (#8276) #### Describe your PR, what does it fix/add? Fix lag spikes when pressing more than 6 keys at the same time. #### Is there anything you want to mention? (unchecked code, possible bugs, found problems, breaking compatibility, etc.) Debugging process: <details> This is triggered by typing some applications, like CopyQ or XWayland. Typing in Firefox doesn't lead to lag, however it itself does lag handling these events. Profiling CopyQ shows that paths leading to `QtWaylandClient::QWaylandInputDevice::Keyboard::keyboard` take over 80% of processing time of an otherwise idle program. Looking at output of 'wev' even when it's not focused shows same events received over and over again. ``` [14: wl_keyboard] repeat_info: rate: 25 keys/sec; delay: 300 ms [14: wl_keyboard] keymap: format: 1 (xkb v1), size: 64754 ``` Looking at what passes through CInputManager::onKeyboardKey() -> CSeatManager::setKeyboard() shows Hyprland 'switching' between endpoints of the same keyboard, one of them being named like the other but with '-1' suffix. </details> Tested changing layouts in Fcitx5 and with following config. ``` input:kb_layout = us,cz input:kb_variant = ,qwerty input:kb_options = grp:alt_shift_toggle ``` Also tested changing 'input:repeat_delay' while running. Curiously, now these events appear in the output of 'wev' only once. Changing layouts still seems to work fine though. #### Is it ready for merging, or does it need work? Ready for merging.
2024-10-28 22:25:27 +03:00
if (!owner || resource->version() < 4 || (rate == lastRate && delayMs == lastDelayMs))
2024-05-10 18:27:57 +01:00
return;
seat: avoid sending pointless 'keymap' and 'repeat_info' events (#8276) #### Describe your PR, what does it fix/add? Fix lag spikes when pressing more than 6 keys at the same time. #### Is there anything you want to mention? (unchecked code, possible bugs, found problems, breaking compatibility, etc.) Debugging process: <details> This is triggered by typing some applications, like CopyQ or XWayland. Typing in Firefox doesn't lead to lag, however it itself does lag handling these events. Profiling CopyQ shows that paths leading to `QtWaylandClient::QWaylandInputDevice::Keyboard::keyboard` take over 80% of processing time of an otherwise idle program. Looking at output of 'wev' even when it's not focused shows same events received over and over again. ``` [14: wl_keyboard] repeat_info: rate: 25 keys/sec; delay: 300 ms [14: wl_keyboard] keymap: format: 1 (xkb v1), size: 64754 ``` Looking at what passes through CInputManager::onKeyboardKey() -> CSeatManager::setKeyboard() shows Hyprland 'switching' between endpoints of the same keyboard, one of them being named like the other but with '-1' suffix. </details> Tested changing layouts in Fcitx5 and with following config. ``` input:kb_layout = us,cz input:kb_variant = ,qwerty input:kb_options = grp:alt_shift_toggle ``` Also tested changing 'input:repeat_delay' while running. Curiously, now these events appear in the output of 'wev' only once. Changing layouts still seems to work fine though. #### Is it ready for merging, or does it need work? Ready for merging.
2024-10-28 22:25:27 +03:00
lastRate = rate;
lastDelayMs = delayMs;
2024-05-10 18:27:57 +01:00
resource->sendRepeatInfo(rate, delayMs);
}
CWLSeatResource::CWLSeatResource(SP<CWlSeat> resource_) : resource(resource_) {
if UNLIKELY (!good())
2024-05-10 18:27:57 +01:00
return;
resource->setOnDestroy([this](CWlSeat* r) {
events.destroy.emit();
PROTO::seat->destroyResource(this);
});
resource->setRelease([this](CWlSeat* r) {
events.destroy.emit();
PROTO::seat->destroyResource(this);
});
pClient = resource->client();
resource->setGetKeyboard([this](CWlSeat* r, uint32_t id) {
const auto RESOURCE = PROTO::seat->m_vKeyboards.emplace_back(makeShared<CWLKeyboardResource>(makeShared<CWlKeyboard>(r->client(), r->version(), id), self.lock()));
if UNLIKELY (!RESOURCE->good()) {
2024-05-10 18:27:57 +01:00
r->noMemory();
PROTO::seat->m_vKeyboards.pop_back();
return;
}
keyboards.emplace_back(RESOURCE);
2024-05-10 18:27:57 +01:00
});
resource->setGetPointer([this](CWlSeat* r, uint32_t id) {
const auto RESOURCE = PROTO::seat->m_vPointers.emplace_back(makeShared<CWLPointerResource>(makeShared<CWlPointer>(r->client(), r->version(), id), self.lock()));
if UNLIKELY (!RESOURCE->good()) {
2024-05-10 18:27:57 +01:00
r->noMemory();
PROTO::seat->m_vPointers.pop_back();
return;
}
pointers.emplace_back(RESOURCE);
2024-05-10 18:27:57 +01:00
});
resource->setGetTouch([this](CWlSeat* r, uint32_t id) {
const auto RESOURCE = PROTO::seat->m_vTouches.emplace_back(makeShared<CWLTouchResource>(makeShared<CWlTouch>(r->client(), r->version(), id), self.lock()));
if UNLIKELY (!RESOURCE->good()) {
2024-05-10 18:27:57 +01:00
r->noMemory();
PROTO::seat->m_vTouches.pop_back();
return;
}
touches.emplace_back(RESOURCE);
2024-05-10 18:27:57 +01:00
});
if (resource->version() >= 2)
resource->sendName(HL_SEAT_NAME);
sendCapabilities(PROTO::seat->currentCaps);
}
CWLSeatResource::~CWLSeatResource() {
events.destroy.emit();
}
void CWLSeatResource::sendCapabilities(uint32_t caps) {
uint32_t wlCaps = 0;
if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)
wlCaps |= WL_SEAT_CAPABILITY_KEYBOARD;
if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)
wlCaps |= WL_SEAT_CAPABILITY_POINTER;
if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)
wlCaps |= WL_SEAT_CAPABILITY_TOUCH;
resource->sendCapabilities((wl_seat_capability)wlCaps);
}
bool CWLSeatResource::good() {
return resource->resource();
}
wl_client* CWLSeatResource::client() {
return pClient;
}
CWLSeatProtocol::CWLSeatProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CWLSeatProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vSeatResources.emplace_back(makeShared<CWLSeatResource>(makeShared<CWlSeat>(client, ver, id)));
if UNLIKELY (!RESOURCE->good()) {
2024-05-10 18:27:57 +01:00
wl_client_post_no_memory(client);
m_vSeatResources.pop_back();
return;
}
RESOURCE->self = RESOURCE;
LOGM(LOG, "New seat resource bound at {:x}", (uintptr_t)RESOURCE.get());
events.newSeatResource.emit(RESOURCE);
}
void CWLSeatProtocol::destroyResource(CWLSeatResource* seat) {
std::erase_if(m_vSeatResources, [&](const auto& other) { return other.get() == seat; });
}
void CWLSeatProtocol::destroyResource(CWLKeyboardResource* resource) {
std::erase_if(m_vKeyboards, [&](const auto& other) { return other.get() == resource; });
}
void CWLSeatProtocol::destroyResource(CWLPointerResource* resource) {
std::erase_if(m_vPointers, [&](const auto& other) { return other.get() == resource; });
}
void CWLSeatProtocol::destroyResource(CWLTouchResource* resource) {
std::erase_if(m_vTouches, [&](const auto& other) { return other.get() == resource; });
}
void CWLSeatProtocol::updateCapabilities(uint32_t caps) {
if (caps == currentCaps)
return;
currentCaps = caps;
for (auto const& s : m_vSeatResources) {
2024-05-10 18:27:57 +01:00
s->sendCapabilities(caps);
}
}
void CWLSeatProtocol::updateKeymap() {
if (!(currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
return;
for (auto const& k : m_vKeyboards) {
2024-05-10 18:27:57 +01:00
k->sendKeymap(g_pSeatManager->keyboard.lock());
}
}
void CWLSeatProtocol::updateRepeatInfo(uint32_t rate, uint32_t delayMs) {
if (!(currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
return;
for (auto const& k : m_vKeyboards) {
2024-05-10 18:27:57 +01:00
k->repeatInfo(rate, delayMs);
}
}
SP<CWLSeatResource> CWLSeatProtocol::seatResourceForClient(wl_client* client) {
for (auto const& r : m_vSeatResources) {
2024-05-10 18:27:57 +01:00
if (r->client() == client)
return r;
}
return nullptr;
}
std::vector<uint8_t>& CCursorSurfaceRole::cursorPixelData(SP<CWLSurfaceResource> surface) {
RASSERT(surface->role->role() == SURFACE_ROLE_CURSOR, "cursorPixelData called on a non-cursor surface");
auto role = (CCursorSurfaceRole*)surface->role.get();
return role->cursorShmPixelData;
}