2024-05-04 19:14:35 -07:00
|
|
|
#include "FocusGrab.hpp"
|
2024-06-08 10:07:59 +02:00
|
|
|
#include "../Compositor.hpp"
|
2024-05-04 19:14:35 -07:00
|
|
|
#include <hyprland-focus-grab-v1.hpp>
|
2024-05-10 18:27:57 +01:00
|
|
|
#include "../managers/input/InputManager.hpp"
|
|
|
|
|
#include "../managers/SeatManager.hpp"
|
2024-06-08 10:07:59 +02:00
|
|
|
#include "core/Compositor.hpp"
|
2024-05-04 19:14:35 -07:00
|
|
|
#include <cstdint>
|
|
|
|
|
#include <wayland-server.h>
|
|
|
|
|
|
2024-06-08 10:07:59 +02:00
|
|
|
CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, SP<CWLSurfaceResource> surface) {
|
2025-07-08 09:56:40 -07:00
|
|
|
m_listeners.destroy = surface->m_events.destroy.listen([grab, surface] { grab->eraseSurface(surface); });
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
2025-05-04 00:13:29 +02:00
|
|
|
CFocusGrab::CFocusGrab(SP<CHyprlandFocusGrabV1> resource_) : m_resource(resource_) {
|
|
|
|
|
if UNLIKELY (!m_resource->resource())
|
2024-05-04 19:14:35 -07:00
|
|
|
return;
|
|
|
|
|
|
2025-05-04 00:13:29 +02:00
|
|
|
m_grab = makeShared<CSeatGrab>();
|
|
|
|
|
m_grab->m_keyboard = true;
|
|
|
|
|
m_grab->m_pointer = true;
|
|
|
|
|
m_grab->setCallback([this]() { finish(true); });
|
2024-05-04 19:14:35 -07:00
|
|
|
|
2025-05-04 00:13:29 +02:00
|
|
|
m_resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); });
|
|
|
|
|
m_resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); });
|
|
|
|
|
m_resource->setAddSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { addSurface(CWLSurfaceResource::fromResource(surface)); });
|
|
|
|
|
m_resource->setRemoveSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { removeSurface(CWLSurfaceResource::fromResource(surface)); });
|
|
|
|
|
m_resource->setCommit([this](CHyprlandFocusGrabV1* pMgr) { commit(); });
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CFocusGrab::~CFocusGrab() {
|
|
|
|
|
finish(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CFocusGrab::good() {
|
2025-05-04 00:13:29 +02:00
|
|
|
return m_resource->resource();
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-25 15:19:23 +00:00
|
|
|
bool CFocusGrab::isSurfaceCommitted(SP<CWLSurfaceResource> surface) {
|
2025-05-30 18:25:59 +05:00
|
|
|
auto iter = std::ranges::find_if(m_surfaces, [surface](const auto& o) { return o.first == surface; });
|
2025-05-04 00:13:29 +02:00
|
|
|
if (iter == m_surfaces.end())
|
2024-05-04 19:14:35 -07:00
|
|
|
return false;
|
|
|
|
|
|
2025-07-25 15:19:23 +00:00
|
|
|
return iter->second->m_state == CFocusGrabSurfaceState::Committed;
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CFocusGrab::start() {
|
2025-05-04 00:13:29 +02:00
|
|
|
if (!m_grabActive) {
|
|
|
|
|
m_grabActive = true;
|
|
|
|
|
g_pSeatManager->setGrab(m_grab);
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-25 15:19:23 +00:00
|
|
|
// Ensure new surfaces are focused if under the mouse when committed.
|
2024-05-04 19:14:35 -07:00
|
|
|
g_pInputManager->simulateMouseMovement();
|
|
|
|
|
refocusKeyboard();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CFocusGrab::finish(bool sendCleared) {
|
2025-05-04 00:13:29 +02:00
|
|
|
if (m_grabActive) {
|
|
|
|
|
m_grabActive = false;
|
2024-05-04 19:14:35 -07:00
|
|
|
|
2025-05-04 00:13:29 +02:00
|
|
|
if (g_pSeatManager->m_seatGrab == m_grab)
|
2024-05-11 01:02:57 +01:00
|
|
|
g_pSeatManager->setGrab(nullptr);
|
2024-05-04 19:14:35 -07:00
|
|
|
|
2025-05-04 00:13:29 +02:00
|
|
|
m_grab->clear();
|
|
|
|
|
m_surfaces.clear();
|
2024-05-04 19:14:35 -07:00
|
|
|
|
|
|
|
|
if (sendCleared)
|
2025-05-04 00:13:29 +02:00
|
|
|
m_resource->sendCleared();
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 10:07:59 +02:00
|
|
|
void CFocusGrab::addSurface(SP<CWLSurfaceResource> surface) {
|
2025-05-30 18:25:59 +05:00
|
|
|
auto iter = std::ranges::find_if(m_surfaces, [surface](const auto& e) { return e.first == surface; });
|
2025-05-04 00:13:29 +02:00
|
|
|
if (iter == m_surfaces.end())
|
|
|
|
|
m_surfaces.emplace(surface, makeUnique<CFocusGrabSurfaceState>(this, surface));
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-08 10:07:59 +02:00
|
|
|
void CFocusGrab::removeSurface(SP<CWLSurfaceResource> surface) {
|
2025-05-04 00:13:29 +02:00
|
|
|
auto iter = m_surfaces.find(surface);
|
|
|
|
|
if (iter != m_surfaces.end()) {
|
|
|
|
|
if (iter->second->m_state == CFocusGrabSurfaceState::PendingAddition)
|
|
|
|
|
m_surfaces.erase(iter);
|
2025-01-17 18:21:34 +01:00
|
|
|
else
|
2025-05-04 00:13:29 +02:00
|
|
|
iter->second->m_state = CFocusGrabSurfaceState::PendingRemoval;
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 10:07:59 +02:00
|
|
|
void CFocusGrab::eraseSurface(SP<CWLSurfaceResource> surface) {
|
2024-05-04 19:14:35 -07:00
|
|
|
removeSurface(surface);
|
2024-05-11 01:02:57 +01:00
|
|
|
commit(true);
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CFocusGrab::refocusKeyboard() {
|
2025-05-02 17:07:20 +02:00
|
|
|
auto keyboardSurface = g_pSeatManager->m_state.keyboardFocus;
|
2025-07-25 15:19:23 +00:00
|
|
|
if (keyboardSurface && isSurfaceCommitted(keyboardSurface.lock()))
|
2024-05-04 19:14:35 -07:00
|
|
|
return;
|
|
|
|
|
|
2024-06-08 10:07:59 +02:00
|
|
|
SP<CWLSurfaceResource> surface = nullptr;
|
2025-05-04 00:13:29 +02:00
|
|
|
for (auto const& [surf, state] : m_surfaces) {
|
2025-07-25 15:19:23 +00:00
|
|
|
if (state->m_state == CFocusGrabSurfaceState::Committed) {
|
2024-06-08 10:07:59 +02:00
|
|
|
surface = surf.lock();
|
2024-05-04 19:14:35 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (surface)
|
|
|
|
|
g_pCompositor->focusSurface(surface);
|
|
|
|
|
else
|
2024-05-11 01:02:57 +01:00
|
|
|
LOGM(ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen.");
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
2024-05-11 01:02:57 +01:00
|
|
|
void CFocusGrab::commit(bool removeOnly) {
|
2024-05-04 19:14:35 -07:00
|
|
|
auto surfacesChanged = false;
|
2025-07-25 15:19:23 +00:00
|
|
|
auto anyCommitted = false;
|
2025-05-04 00:13:29 +02:00
|
|
|
for (auto iter = m_surfaces.begin(); iter != m_surfaces.end();) {
|
|
|
|
|
switch (iter->second->m_state) {
|
2024-05-04 19:14:35 -07:00
|
|
|
case CFocusGrabSurfaceState::PendingRemoval:
|
2025-05-04 00:13:29 +02:00
|
|
|
m_grab->remove(iter->first.lock());
|
|
|
|
|
iter = m_surfaces.erase(iter);
|
2024-05-04 19:14:35 -07:00
|
|
|
surfacesChanged = true;
|
|
|
|
|
continue;
|
|
|
|
|
case CFocusGrabSurfaceState::PendingAddition:
|
2024-05-11 01:02:57 +01:00
|
|
|
if (!removeOnly) {
|
2025-07-25 15:19:23 +00:00
|
|
|
iter->second->m_state = CFocusGrabSurfaceState::Committed;
|
2025-05-04 00:13:29 +02:00
|
|
|
m_grab->add(iter->first.lock());
|
2024-05-11 01:02:57 +01:00
|
|
|
surfacesChanged = true;
|
2025-07-25 15:19:23 +00:00
|
|
|
anyCommitted = true;
|
2024-05-11 01:02:57 +01:00
|
|
|
}
|
2024-05-04 19:14:35 -07:00
|
|
|
break;
|
2025-07-25 15:19:23 +00:00
|
|
|
case CFocusGrabSurfaceState::Committed: anyCommitted = true; break;
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iter++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (surfacesChanged) {
|
2025-07-25 15:19:23 +00:00
|
|
|
if (anyCommitted)
|
2024-05-04 19:14:35 -07:00
|
|
|
start();
|
|
|
|
|
else
|
|
|
|
|
finish(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CFocusGrabProtocol::CFocusGrabProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CFocusGrabProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
2025-05-04 00:13:29 +02:00
|
|
|
const auto RESOURCE = m_managers.emplace_back(makeUnique<CHyprlandFocusGrabManagerV1>(client, ver, id)).get();
|
2024-05-04 19:14:35 -07:00
|
|
|
RESOURCE->setOnDestroy([this](CHyprlandFocusGrabManagerV1* p) { onManagerResourceDestroy(p->resource()); });
|
|
|
|
|
|
|
|
|
|
RESOURCE->setDestroy([this](CHyprlandFocusGrabManagerV1* p) { onManagerResourceDestroy(p->resource()); });
|
|
|
|
|
RESOURCE->setCreateGrab([this](CHyprlandFocusGrabManagerV1* pMgr, uint32_t id) { onCreateGrab(pMgr, id); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CFocusGrabProtocol::onManagerResourceDestroy(wl_resource* res) {
|
2025-05-04 00:13:29 +02:00
|
|
|
std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; });
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CFocusGrabProtocol::destroyGrab(CFocusGrab* grab) {
|
2025-05-04 00:13:29 +02:00
|
|
|
std::erase_if(m_grabs, [&](const auto& other) { return other.get() == grab; });
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CFocusGrabProtocol::onCreateGrab(CHyprlandFocusGrabManagerV1* pMgr, uint32_t id) {
|
2025-05-04 00:13:29 +02:00
|
|
|
m_grabs.push_back(makeUnique<CFocusGrab>(makeShared<CHyprlandFocusGrabV1>(pMgr->client(), pMgr->version(), id)));
|
|
|
|
|
const auto RESOURCE = m_grabs.back().get();
|
2024-05-04 19:14:35 -07:00
|
|
|
|
2025-01-17 18:21:34 +01:00
|
|
|
if UNLIKELY (!RESOURCE->good()) {
|
2024-05-04 19:14:35 -07:00
|
|
|
pMgr->noMemory();
|
2025-05-04 00:13:29 +02:00
|
|
|
m_grabs.pop_back();
|
2024-05-04 19:14:35 -07:00
|
|
|
}
|
|
|
|
|
}
|