From c92dfdf0e43542480211f2143bcbe3428d6156ed Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sat, 11 May 2024 01:02:57 +0100 Subject: [PATCH] seatmgr: Add a grab class --- src/Compositor.cpp | 5 + src/desktop/LayerSurface.cpp | 2 + src/desktop/Popup.cpp | 20 +-- src/desktop/Popup.hpp | 1 + src/managers/SeatManager.cpp | 73 +++++++++++ src/managers/SeatManager.hpp | 37 ++++++ src/managers/input/InputManager.cpp | 26 ++++ src/managers/input/InputManager.hpp | 6 +- src/protocols/FocusGrab.cpp | 193 ++++------------------------ src/protocols/FocusGrab.hpp | 10 +- src/protocols/XDGShell.cpp | 64 ++++++++- src/protocols/XDGShell.hpp | 20 ++- 12 files changed, 269 insertions(+), 188 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index c56dd2801..4e0a63578 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1025,6 +1025,11 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface)) return; + if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(pSurface)) { + Debug::log(LOG, "surface {:x} won't receive kb focus becuase grab rejected it", (uintptr_t)pSurface); + return; + } + const auto PLASTSURF = m_pLastFocus; // Unfocus last surface if should diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp index cb10079cf..759eb09f5 100644 --- a/src/desktop/LayerSurface.cpp +++ b/src/desktop/LayerSurface.cpp @@ -136,6 +136,8 @@ void CLayerSurface::onMap() { (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()); if (GRABSFOCUS) { + // TODO: use the new superb really very cool grab + g_pSeatManager->setGrab(nullptr); g_pInputManager->releaseAllMouseButtons(); g_pCompositor->focusSurface(surface.wlr()); diff --git a/src/desktop/Popup.cpp b/src/desktop/Popup.cpp index 605dfdfdb..c652d2983 100644 --- a/src/desktop/Popup.cpp +++ b/src/desktop/Popup.cpp @@ -45,6 +45,7 @@ void CPopup::initAllSignals() { listeners.reposition = m_pResource->events.reposition.registerListener([this](std::any d) { this->onReposition(); }); listeners.map = m_pResource->surface->events.map.registerListener([this](std::any d) { this->onMap(); }); listeners.unmap = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); }); + listeners.dismissed = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); }); listeners.destroy = m_pResource->surface->events.destroy.registerListener([this](std::any d) { this->onDestroy(); }); listeners.commit = m_pResource->surface->events.commit.registerListener([this](std::any d) { this->onCommit(); }); listeners.newPopup = m_pResource->surface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); @@ -65,8 +66,9 @@ void CPopup::onDestroy() { } void CPopup::onMap() { - m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; - const auto COORDS = coordsGlobal(); + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; + const auto COORDS = coordsGlobal(); + const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS); CBox box; wlr_surface_get_extends(m_sWLSurface.wlr(), box.pWlr()); @@ -79,8 +81,9 @@ void CPopup::onMap() { m_pSubsurfaceHead = std::make_unique(this); - unconstrain(); + //unconstrain(); sendScale(); + wlr_surface_send_enter(m_pResource->surface->surface, PMONITOR->output); if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP) g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer)); @@ -178,7 +181,6 @@ void CPopup::unconstrain() { CBox box = {PMONITOR->vecPosition.x - COORDS.x, PMONITOR->vecPosition.y - COORDS.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; m_pResource->applyPositioning(box, COORDS - PMONITOR->vecPosition); - wlr_surface_send_enter(m_pResource->surface->surface, PMONITOR->output); } Vector2D CPopup::coordsRelativeToParent() { @@ -190,8 +192,6 @@ Vector2D CPopup::coordsRelativeToParent() { CPopup* current = this; offset -= current->m_pResource->surface->current.geometry.pos(); - offset -= m_pResource->surface->current.geometry.pos(); - while (current->m_pParent && current->m_pResource) { offset += {current->m_sWLSurface.wlr()->current.dx, current->m_sWLSurface.wlr()->current.dy}; @@ -293,11 +293,15 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) { continue; if (!allowsInput) { - const auto BOX = CBox{p->coordsGlobal(), p->size()}; + const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{}; + const Vector2D size = p->m_pResource ? p->m_pResource->geometry.size() : p->size(); + + const auto BOX = CBox{p->coordsGlobal() + offset, size}; if (BOX.containsPoint(globalCoords)) return p; } else { - const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input}.translate(p->coordsGlobal()); + const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{}; + const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input}.translate(p->coordsGlobal() + offset); if (REGION.containsPoint(globalCoords)) return p; } diff --git a/src/desktop/Popup.hpp b/src/desktop/Popup.hpp index e6c35e684..ba6da55a5 100644 --- a/src/desktop/Popup.hpp +++ b/src/desktop/Popup.hpp @@ -68,6 +68,7 @@ class CPopup { CHyprSignalListener map; CHyprSignalListener unmap; CHyprSignalListener commit; + CHyprSignalListener dismissed; CHyprSignalListener reposition; } listeners; diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index f9d394b51..d962dd84b 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -371,6 +371,40 @@ void CSeatManager::sendTouchOrientation(int32_t id, double angle) { } } +void CSeatManager::refocusGrab() { + if (!seatGrab) + return; + + if (seatGrab->surfs.size() > 0) { + // try to find a surf in focus first + const auto MOUSE = g_pInputManager->getMouseCoordsInternal(); + for (auto& s : seatGrab->surfs) { + auto hlSurf = CWLSurface::surfaceFromWlr(s); + if (!hlSurf) + continue; + + auto b = hlSurf->getSurfaceBoxGlobal(); + if (!b.has_value()) + continue; + + if (!b->containsPoint(MOUSE)) + continue; + + if (seatGrab->keyboard) + setKeyboardFocus(s); + if (seatGrab->pointer) + setPointerFocus(s, MOUSE - b->pos()); + return; + } + + wlr_surface* surf = seatGrab->surfs.at(0); + if (seatGrab->keyboard) + setKeyboardFocus(surf); + if (seatGrab->pointer) + setPointerFocus(surf, {}); + } +} + void CSeatManager::onSetCursor(SP seatResource, uint32_t serial, wlr_surface* surf, const Vector2D& hotspot) { if (!state.pointerFocusResource || !seatResource || seatResource->client() != state.pointerFocusResource->client()) { Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the client ain't in focus"); @@ -389,3 +423,42 @@ void CSeatManager::onSetCursor(SP seatResource, uint32_t serial SP CSeatManager::seatResourceForClient(wl_client* client) { return PROTO::seat->seatResourceForClient(client); } + +void CSeatManager::setGrab(SP grab) { + if (seatGrab) { + auto oldGrab = seatGrab; + seatGrab.reset(); + g_pInputManager->refocus(); + if (oldGrab->onEnd) + oldGrab->onEnd(); + } + + if (!grab) + return; + + seatGrab = grab; + + refocusGrab(); +} + +bool CSeatGrab::accepts(wlr_surface* surf) { + return std::find(surfs.begin(), surfs.end(), surf) != surfs.end(); +} + +void CSeatGrab::add(wlr_surface* surf) { + surfs.push_back(surf); +} + +void CSeatGrab::remove(wlr_surface* surf) { + std::erase(surfs, surf); + if ((keyboard && g_pSeatManager->state.keyboardFocus == surf) || (pointer && g_pSeatManager->state.pointerFocus == surf)) + g_pSeatManager->refocusGrab(); +} + +void CSeatGrab::setCallback(std::function onEnd_) { + onEnd = onEnd_; +} + +void CSeatGrab::clear() { + surfs.clear(); +} diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index ef1140246..16f11ffd8 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -14,6 +14,37 @@ class CWLSeatResource; class IPointer; class IKeyboard; +/* + A seat grab defines a restricted set of surfaces that can be focused. + Only one grab can be active at a time + + when a grab is removed, refocus() will happen + + Different from a constraint. + + When first set with setGrab, SeatManager will try to find a surface that is at the mouse pointer to focus, + from first added to last added. If none are, first is focused. +*/ +class CSeatGrab { + public: + bool accepts(wlr_surface* surf); + void add(wlr_surface* surf); + void remove(wlr_surface* surf); + void setCallback(std::function onEnd_); + void clear(); + + bool keyboard = false; + bool pointer = false; + bool touch = false; + + bool removeOnInput = true; // on hard input e.g. click outside, remove + + private: + std::vector surfs; // read-only + std::function onEnd; + friend class CSeatManager; +}; + class CSeatManager { public: CSeatManager(); @@ -76,6 +107,9 @@ class CSeatManager { WP mouse; WP keyboard; + void setGrab(SP grab); // nullptr removes + SP seatGrab; + private: struct SSeatResourceContainer { SSeatResourceContainer(SP); @@ -92,6 +126,8 @@ class CSeatManager { void onNewSeatResource(SP resource); SP containerForResource(SP seatResource); + void refocusGrab(); + struct { CHyprSignalListener newSeatResource; } listeners; @@ -101,6 +137,7 @@ class CSeatManager { DYNLISTENER(touchSurfaceDestroy); friend struct SSeatResourceContainer; + friend class CSeatGrab; }; inline UP g_pSeatManager; diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 50bc0172d..35c715448 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -358,6 +358,26 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (g_pCompositor->m_pLastMonitor->output->software_cursor_locks > 0) g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get()); + // grabs + if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(foundSurface)) { + if (m_bHardInput || refocus) { + g_pSeatManager->setGrab(nullptr); + return; // setGrab will refocus + } else { + // we need to grab the last surface. + foundSurface = g_pSeatManager->state.pointerFocus; + + auto HLSurface = CWLSurface::surfaceFromWlr(foundSurface); + + if (HLSurface) { + const auto BOX = HLSurface->getSurfaceBoxGlobal(); + + if (BOX.has_value()) + surfacePos = BOX->pos(); + } + } + } + if (!foundSurface) { if (!m_bEmptyFocusCursorSet) { if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) { @@ -678,6 +698,12 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { if (const auto PMON = g_pCompositor->getMonitorFromVector(mouseCoords); PMON != g_pCompositor->m_pLastMonitor.get() && PMON) g_pCompositor->setActiveMonitor(PMON); + + if (g_pSeatManager->seatGrab && e.state == WL_POINTER_BUTTON_STATE_PRESSED) { + m_bHardInput = true; + simulateMouseMovement(); + m_bHardInput = false; + } } void CInputManager::processMouseDownKill(const IPointer::SButtonEvent& e) { diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index a1ca07d36..6b30830ce 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -187,10 +187,12 @@ class CInputManager { void releaseAllMouseButtons(); // for some bugs in follow mouse 0 - bool m_bLastFocusOnLS = false; - + bool m_bLastFocusOnLS = false; bool m_bLastFocusOnIMEPopup = false; + // for hard input e.g. clicks + bool m_bHardInput = false; + // for hiding cursor on touch bool m_bLastInputTouch = false; diff --git a/src/protocols/FocusGrab.cpp b/src/protocols/FocusGrab.cpp index 4189ed6e0..5f0771e03 100644 --- a/src/protocols/FocusGrab.cpp +++ b/src/protocols/FocusGrab.cpp @@ -7,119 +7,7 @@ #include #include -// static void focus_grab_pointer_enter(wlr_seat_pointer_grab* grab, wlr_surface* surface, double sx, double sy) { -// if (static_cast(grab->data)->isSurfaceComitted(surface)) -// wlr_seat_pointer_enter(grab->seat, surface, sx, sy); -// else -// wlr_seat_pointer_clear_focus(grab->seat); -// } - -// static void focus_grab_pointer_clear_focus(wlr_seat_pointer_grab* grab) { -// wlr_seat_pointer_clear_focus(grab->seat); -// } - -// static void focus_grab_pointer_motion(wlr_seat_pointer_grab* grab, uint32_t time, double sx, double sy) { -// wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); -// } - -// static uint32_t focus_grab_pointer_button(wlr_seat_pointer_grab* grab, uint32_t time, uint32_t button, wl_pointer_button_state state) { -// uint32_t serial = wlr_seat_pointer_send_button(grab->seat, time, button, state); - -// if (serial) -// return serial; -// else { -// static_cast(grab->data)->finish(true); -// return 0; -// } -// } - -// static void focus_grab_pointer_axis(wlr_seat_pointer_grab* grab, uint32_t time, enum wl_pointer_axis orientation, double value, int32_t value_discrete, -// enum wl_pointer_axis_source source, enum wl_pointer_axis_relative_direction relative_direction) { -// wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, value_discrete, source, relative_direction); -// } - -// static void focus_grab_pointer_frame(wlr_seat_pointer_grab* grab) { -// wlr_seat_pointer_send_frame(grab->seat); -// } - -// static void focus_grab_pointer_cancel(wlr_seat_pointer_grab* grab) { -// static_cast(grab->data)->finish(true); -// } - -// static const wlr_pointer_grab_interface focus_grab_pointer_impl = { -// .enter = focus_grab_pointer_enter, -// .clear_focus = focus_grab_pointer_clear_focus, -// .motion = focus_grab_pointer_motion, -// .button = focus_grab_pointer_button, -// .axis = focus_grab_pointer_axis, -// .frame = focus_grab_pointer_frame, -// .cancel = focus_grab_pointer_cancel, -// }; - -// static void focus_grab_keyboard_enter(wlr_seat_keyboard_grab* grab, wlr_surface* surface, const uint32_t keycodes[], size_t num_keycodes, const wlr_keyboard_modifiers* modifiers) { -// if (static_cast(grab->data)->isSurfaceComitted(surface)) -// wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers); - -// // otherwise the last grabbed window should retain keyboard focus. -// } - -// static void focus_grab_keyboard_clear_focus(wlr_seat_keyboard_grab* grab) { -// static_cast(grab->data)->finish(true); -// } - -// static void focus_grab_keyboard_key(wlr_seat_keyboard_grab* grab, uint32_t time, uint32_t key, uint32_t state) { -// wlr_seat_keyboard_send_key(grab->seat, time, key, state); -// } - -// static void focus_grab_keyboard_modifiers(wlr_seat_keyboard_grab* grab, const wlr_keyboard_modifiers* modifiers) { -// wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); -// } - -// static void focus_grab_keyboard_cancel(wlr_seat_keyboard_grab* grab) { -// static_cast(grab->data)->finish(true); -// } - -// static const wlr_keyboard_grab_interface focus_grab_keyboard_impl = { -// .enter = focus_grab_keyboard_enter, -// .clear_focus = focus_grab_keyboard_clear_focus, -// .key = focus_grab_keyboard_key, -// .modifiers = focus_grab_keyboard_modifiers, -// .cancel = focus_grab_keyboard_cancel, -// }; - -// static uint32_t focus_grab_touch_down(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { -// if (!static_cast(grab->data)->isSurfaceComitted(point->surface)) -// return 0; - -// return wlr_seat_touch_send_down(grab->seat, point->surface, time, point->touch_id, point->sx, point->sy); -// } - -// static void focus_grab_touch_up(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { -// wlr_seat_touch_send_up(grab->seat, time, point->touch_id); -// } - -// static void focus_grab_touch_motion(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { -// wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, point->sy); -// } - -// static void focus_grab_touch_enter(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) {} - -// static void focus_grab_touch_frame(wlr_seat_touch_grab* grab) { -// wlr_seat_touch_send_frame(grab->seat); -// } - -// static void focus_grab_touch_cancel(wlr_seat_touch_grab* grab) { -// static_cast(grab->data)->finish(true); -// } - -// static const wlr_touch_grab_interface focus_grab_touch_impl = { -// .down = focus_grab_touch_down, -// .up = focus_grab_touch_up, -// .motion = focus_grab_touch_motion, -// .enter = focus_grab_touch_enter, -// .frame = focus_grab_touch_frame, -// .cancel = focus_grab_touch_cancel, -// }; +#define LOGM PROTO::focusGrab->protoLog CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, wlr_surface* surface) { hyprListener_surfaceDestroy.initCallback( @@ -134,14 +22,10 @@ CFocusGrab::CFocusGrab(SP resource_) : resource(resource_) if (!resource->resource()) return; - // m_sPointerGrab.interface = &focus_grab_pointer_impl; - // m_sPointerGrab.data = this; - - // m_sKeyboardGrab.interface = &focus_grab_keyboard_impl; - // m_sKeyboardGrab.data = this; - - // m_sTouchGrab.interface = &focus_grab_touch_impl; - // m_sTouchGrab.data = this; + grab = makeShared(); + grab->keyboard = true; + grab->pointer = true; + grab->setCallback([this]() { finish(true); }); resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); @@ -168,21 +52,8 @@ bool CFocusGrab::isSurfaceComitted(wlr_surface* surface) { void CFocusGrab::start() { if (!m_bGrabActive) { - // wlr_seat_pointer_start_grab(g_pCompositor->m_sSeat.seat, &m_sPointerGrab); - // wlr_seat_keyboard_start_grab(g_pCompositor->m_sSeat.seat, &m_sKeyboardGrab); - // wlr_seat_touch_start_grab(g_pCompositor->m_sSeat.seat, &m_sTouchGrab); m_bGrabActive = true; - - // Ensure the grab ends if another grab begins, including from xdg_popup::grab. - - // hyprListener_pointerGrabStarted.initCallback( - // &g_pCompositor->m_sSeat.seat->events.pointer_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); - - // hyprListener_keyboardGrabStarted.initCallback( - // &g_pCompositor->m_sSeat.seat->events.keyboard_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); - - // hyprListener_touchGrabStarted.initCallback( - // &g_pCompositor->m_sSeat.seat->events.touch_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); + g_pSeatManager->setGrab(grab); } // Ensure new surfaces are focused if under the mouse when comitted. @@ -193,59 +64,39 @@ void CFocusGrab::start() { void CFocusGrab::finish(bool sendCleared) { if (m_bGrabActive) { m_bGrabActive = false; - // hyprListener_pointerGrabStarted.removeCallback(); - // hyprListener_keyboardGrabStarted.removeCallback(); - // hyprListener_touchGrabStarted.removeCallback(); - // Only clear grabs that belong to this focus grab. When superseded by another grab - // or xdg_popup grab we might not own the current grab. - - bool hadGrab = false; - // if (g_pCompositor->m_sSeat.seat->pointer_state.grab == &m_sPointerGrab) { - // wlr_seat_pointer_end_grab(g_pCompositor->m_sSeat.seat); - // hadGrab = true; - // } - - // if (g_pCompositor->m_sSeat.seat->keyboard_state.grab == &m_sKeyboardGrab) { - // wlr_seat_keyboard_end_grab(g_pCompositor->m_sSeat.seat); - // hadGrab = true; - // } - - // if (g_pCompositor->m_sSeat.seat->touch_state.grab == &m_sTouchGrab) { - // wlr_seat_touch_end_grab(g_pCompositor->m_sSeat.seat); - // hadGrab = true; - // } + if (g_pSeatManager->seatGrab == grab) { + g_pSeatManager->setGrab(nullptr); + } + grab->clear(); m_mSurfaces.clear(); if (sendCleared) resource->sendCleared(); - - // Ensure surfaces under the mouse when the grab ends get focus. - if (hadGrab) - g_pInputManager->refocus(); } } void CFocusGrab::addSurface(wlr_surface* surface) { auto iter = m_mSurfaces.find(surface); - if (iter == m_mSurfaces.end()) + if (iter == m_mSurfaces.end()) { m_mSurfaces.emplace(surface, std::make_unique(this, surface)); + } } void CFocusGrab::removeSurface(wlr_surface* surface) { auto iter = m_mSurfaces.find(surface); if (iter != m_mSurfaces.end()) { - if (iter->second->state == CFocusGrabSurfaceState::PendingAddition) + if (iter->second->state == CFocusGrabSurfaceState::PendingAddition) { m_mSurfaces.erase(iter); - else + } else iter->second->state = CFocusGrabSurfaceState::PendingRemoval; } } void CFocusGrab::eraseSurface(wlr_surface* surface) { removeSurface(surface); - commit(); + commit(true); } void CFocusGrab::refocusKeyboard() { @@ -264,22 +115,26 @@ void CFocusGrab::refocusKeyboard() { if (surface) g_pCompositor->focusSurface(surface); else - Debug::log(ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen."); + LOGM(ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen."); } -void CFocusGrab::commit() { +void CFocusGrab::commit(bool removeOnly) { auto surfacesChanged = false; auto anyComitted = false; for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) { switch (iter->second->state) { case CFocusGrabSurfaceState::PendingRemoval: + grab->remove(iter->first); iter = m_mSurfaces.erase(iter); surfacesChanged = true; continue; case CFocusGrabSurfaceState::PendingAddition: - iter->second->state = CFocusGrabSurfaceState::Comitted; - surfacesChanged = true; - anyComitted = true; + if (!removeOnly) { + iter->second->state = CFocusGrabSurfaceState::Comitted; + grab->add(iter->first); + surfacesChanged = true; + anyComitted = true; + } break; case CFocusGrabSurfaceState::Comitted: anyComitted = true; break; } diff --git a/src/protocols/FocusGrab.hpp b/src/protocols/FocusGrab.hpp index 3e5d36abe..40922c222 100644 --- a/src/protocols/FocusGrab.hpp +++ b/src/protocols/FocusGrab.hpp @@ -8,6 +8,7 @@ #include class CFocusGrab; +class CSeatGrab; class CFocusGrabSurfaceState { public: @@ -40,14 +41,13 @@ class CFocusGrab { void removeSurface(wlr_surface* surface); void eraseSurface(wlr_surface* surface); void refocusKeyboard(); - void commit(); + void commit(bool removeOnly = false); SP resource; std::unordered_map> m_mSurfaces; - // wlr_seat_pointer_grab m_sPointerGrab; - // wlr_seat_keyboard_grab m_sKeyboardGrab; - // wlr_seat_touch_grab m_sTouchGrab; - bool m_bGrabActive = false; + SP grab; + + bool m_bGrabActive = false; DYNLISTENER(pointerGrabStarted); DYNLISTENER(keyboardGrabStarted); diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index fcb2d63b5..2a13fca1d 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -1,6 +1,8 @@ #include "XDGShell.hpp" #include #include "../Compositor.hpp" +#include "../managers/SeatManager.hpp" +#include "core/Seat.hpp" #define LOGM PROTO::xdgShell->protoLog @@ -14,12 +16,14 @@ CXDGPopupResource::CXDGPopupResource(SP resource_, SPsetDestroy([this](CXdgPopup* r) { if (surface && surface->mapped) surface->events.unmap.emit(); + PROTO::xdgShell->onPopupDestroy(self); events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); resource->setOnDestroy([this](CXdgPopup* r) { if (surface && surface->mapped) surface->events.unmap.emit(); + PROTO::xdgShell->onPopupDestroy(self); events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); @@ -34,11 +38,29 @@ CXDGPopupResource::CXDGPopupResource(SP resource_, SPsetGrab([this](CXdgPopup* r, wl_resource* seat, uint32_t serial) { + LOGM(LOG, "xdg_popup {:x} requests grab", (uintptr_t)this); + listeners.pointerFocus.reset(); + PROTO::xdgShell->addOrStartGrab(self.lock()); + }); + if (parent) taken = true; + + // this will be removed if the popup takes a grab. + // transient (non-grabbing popups) sometimes wouldn't be dismissed by the app + // as a result of either a hl bug or an app bug + // and stay on the screen annoying the user. + listeners.pointerFocus = g_pSeatManager->events.pointerFocusChange.registerListener([this](std::any data) { + if (g_pSeatManager->state.pointerFocusResource && g_pSeatManager->state.pointerFocusResource->client() == resource->client()) + return; + + done(); + }); } CXDGPopupResource::~CXDGPopupResource() { + PROTO::xdgShell->onPopupDestroy(self); events.destroy.emit(); } @@ -85,6 +107,7 @@ void CXDGPopupResource::configure(const CBox& box) { } void CXDGPopupResource::done() { + events.dismissed.emit(); resource->sendPopupDone(); } @@ -659,7 +682,10 @@ wl_client* CXDGWMBase::client() { } CXDGShellProtocol::CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; + grab = makeShared(); + grab->keyboard = true; + grab->pointer = true; + grab->setCallback([this]() { grabbed.clear(); }); } void CXDGShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { @@ -695,3 +721,39 @@ void CXDGShellProtocol::destroyResource(CXDGToplevelResource* resource) { void CXDGShellProtocol::destroyResource(CXDGPopupResource* resource) { std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == resource; }); } + +void CXDGShellProtocol::addOrStartGrab(SP popup) { + if (!grabOwner) { + grabOwner = popup; + grabbed.clear(); + grab->clear(); + grab->add(popup->surface->surface); + if (popup->parent) + grab->add(popup->parent->surface); + g_pSeatManager->setGrab(grab); + grabbed.emplace_back(popup); + return; + } + + grabbed.emplace_back(popup); + + grab->add(popup->surface->surface); + + if (popup->parent) + grab->add(popup->parent->surface); +} + +void CXDGShellProtocol::onPopupDestroy(WP popup) { + if (popup == grabOwner) { + g_pSeatManager->setGrab(nullptr); + for (auto& g : grabbed) { + g->done(); + } + grabbed.clear(); + return; + } + + std::erase(grabbed, popup); + if (popup->surface) + grab->remove(popup->surface->surface); +} diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp index e214b6ac8..4b28a6334 100644 --- a/src/protocols/XDGShell.hpp +++ b/src/protocols/XDGShell.hpp @@ -59,6 +59,7 @@ class CXDGPopupResource { struct { CSignal reposition; + CSignal dismissed; CSignal destroy; // only the role } events; @@ -76,6 +77,11 @@ class CXDGPopupResource { Vector2D accumulateParentOffset(); CXDGPositionerRules positionerRules; + + struct { + // only connected if no grab. + CHyprSignalListener pointerFocus; + } listeners; }; class CXDGToplevelResource { @@ -198,10 +204,10 @@ class CXDGPositionerResource { bool good(); - SXDGPositionerState state; + SXDGPositionerState state; - WP owner; - WP self; + WP owner; + WP self; private: SP resource; @@ -244,6 +250,14 @@ class CXDGShellProtocol : public IWaylandProtocol { std::vector> m_vToplevels; std::vector> m_vPopups; + // current popup grab + WP grabOwner; + SP grab; + std::vector> grabbed; + + void addOrStartGrab(SP popup); + void onPopupDestroy(WP popup); + friend class CXDGWMBase; friend class CXDGPositionerResource; friend class CXDGSurfaceResource;