protocols: add hyprland_focus_grab_v1 implementation

This commit is contained in:
outfoxxed 2024-05-03 00:20:15 -07:00
parent 387127b12a
commit a2905b9e16
No known key found for this signature in database
GPG key ID: 4C88A185FB89301E
6 changed files with 388 additions and 1 deletions

View file

@ -280,6 +280,7 @@ protocolNew("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" "
protocolNew("staging/xdg-activation/xdg-activation-v1.xml" "xdg-activation-v1" false)
protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false)
protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false)
protocolNew("subprojects/hyprland-protocols/protocols/hyprland-focus-grab-v1.xml" "hyprland-focus-grab-v1" true)
# tools
add_subdirectory(hyprctl)

View file

@ -57,6 +57,7 @@ new_protocols = [
[wl_protocol_dir, 'staging/xdg-activation/xdg-activation-v1.xml'],
[wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'],
[wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'],
[hl_protocol_dir, 'protocols/hyprland-focus-grab-v1.xml'],
]
wl_protos_src = []

View file

@ -22,6 +22,7 @@
#include "../protocols/InputMethodV2.hpp"
#include "../protocols/VirtualKeyboard.hpp"
#include "../protocols/VirtualPointer.hpp"
#include "../protocols/FocusGrab.hpp"
CProtocolManager::CProtocolManager() {
@ -47,6 +48,7 @@ CProtocolManager::CProtocolManager() {
PROTO::ime = std::make_unique<CInputMethodV2Protocol>(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
PROTO::virtualKeyboard = std::make_unique<CVirtualKeyboardProtocol>(&zwp_virtual_keyboard_manager_v1_interface, 1, "VirtualKeyboard");
PROTO::virtualPointer = std::make_unique<CVirtualPointerProtocol>(&zwlr_virtual_pointer_manager_v1_interface, 2, "VirtualPointer");
PROTO::focusGrab = std::make_unique<CFocusGrabProtocol>(&hyprland_focus_grab_manager_v1_interface, 1, "FocusGrab");
// Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner.

307
src/protocols/FocusGrab.cpp Normal file
View file

@ -0,0 +1,307 @@
#include "FocusGrab.hpp"
#include "Compositor.hpp"
#include <hyprland-focus-grab-v1.hpp>
#include <managers/input/InputManager.hpp>
#include <cstdint>
#include <memory>
#include <wayland-server.h>
static void focus_grab_pointer_enter(wlr_seat_pointer_grab* grab, wlr_surface* surface, double sx, double sy) {
if (static_cast<CFocusGrab*>(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<CFocusGrab*>(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<CFocusGrab*>(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<CFocusGrab*>(grab->data)->isSurfaceComitted(surface)) {
wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers);
} else {
// the last grabbed window should retain keybaord focus.
}
}
static void focus_grab_keyboard_clear_focus(wlr_seat_keyboard_grab* grab) {
static_cast<CFocusGrab*>(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<CFocusGrab*>(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<CFocusGrab*>(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<CFocusGrab*>(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};
CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, wlr_surface* surface) {
hyprListener_surfaceDestroy.initCallback(
&surface->events.destroy, [=](void*, void*) { grab->eraseSurface(surface); }, this, "CFocusGrab");
}
CFocusGrabSurfaceState::~CFocusGrabSurfaceState() {
hyprListener_surfaceDestroy.removeCallback();
}
CFocusGrab::CFocusGrab(SP<CHyprlandFocusGrabV1> 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;
resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); });
resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); });
resource->setAddSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { this->addSurface(wlr_surface_from_resource(surface)); });
resource->setRemoveSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { this->removeSurface(wlr_surface_from_resource(surface)); });
resource->setCommit([this](CHyprlandFocusGrabV1* pMgr) { this->commit(); });
}
CFocusGrab::~CFocusGrab() {
finish(false);
}
bool CFocusGrab::good() {
return resource->resource();
}
bool CFocusGrab::isSurfaceComitted(wlr_surface* surface) {
auto iter = m_mSurfaces.find(surface);
if (iter == m_mSurfaces.end())
return false;
return iter->second->state == CFocusGrabSurfaceState::Comitted;
}
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*) { this->finish(true); }, this, "CFocusGrab");
hyprListener_keyboardGrabStarted.initCallback(
&g_pCompositor->m_sSeat.seat->events.keyboard_grab_begin, [this](void*, void*) { this->finish(true); }, this, "CFocusGrab");
hyprListener_touchGrabStarted.initCallback(
&g_pCompositor->m_sSeat.seat->events.touch_grab_begin, [this](void*, void*) { this->finish(true); }, this, "CFocusGrab");
}
// Ensure new surfaces are focused if under the mouse when comitted.
g_pInputManager->refocus();
}
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 == &this->m_sPointerGrab) {
wlr_seat_pointer_end_grab(g_pCompositor->m_sSeat.seat);
hadGrab = true;
}
if (g_pCompositor->m_sSeat.seat->keyboard_state.grab == &this->m_sKeyboardGrab) {
wlr_seat_keyboard_end_grab(g_pCompositor->m_sSeat.seat);
hadGrab = true;
}
if (g_pCompositor->m_sSeat.seat->touch_state.grab == &this->m_sTouchGrab) {
wlr_seat_touch_end_grab(g_pCompositor->m_sSeat.seat);
hadGrab = true;
}
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()) {
m_mSurfaces.emplace(surface, std::make_unique<CFocusGrabSurfaceState>(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) {
m_mSurfaces.erase(iter);
} else {
iter->second->state = CFocusGrabSurfaceState::PendingRemoval;
}
}
}
void CFocusGrab::eraseSurface(wlr_surface* surface) {
removeSurface(surface);
commit();
}
void CFocusGrab::commit() {
auto surfacesChanged = false;
for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) {
switch (iter->second->state) {
case CFocusGrabSurfaceState::PendingRemoval:
iter = m_mSurfaces.erase(iter);
surfacesChanged = true;
continue;
case CFocusGrabSurfaceState::PendingAddition:
iter->second->state = CFocusGrabSurfaceState::Comitted;
surfacesChanged = true;
break;
default: break;
}
iter++;
}
if (surfacesChanged) {
if (!m_mSurfaces.empty()) {
start();
} else {
finish(false);
}
}
}
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) {
const auto RESOURCE = m_vManagers.emplace_back(std::make_unique<CHyprlandFocusGrabManagerV1>(client, ver, id)).get();
RESOURCE->setOnDestroy([this](CHyprlandFocusGrabManagerV1* p) { this->onManagerResourceDestroy(p->resource()); });
RESOURCE->setCreateGrab([this](CHyprlandFocusGrabManagerV1* pMgr, uint32_t id) { this->onCreateGrab(pMgr, id); });
}
void CFocusGrabProtocol::onManagerResourceDestroy(wl_resource* res) {
std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; });
}
void CFocusGrabProtocol::destroyGrab(CFocusGrab* grab) {
std::erase_if(m_vGrabs, [&](const auto& other) { return other.get() == grab; });
}
void CFocusGrabProtocol::onCreateGrab(CHyprlandFocusGrabManagerV1* pMgr, uint32_t id) {
m_vGrabs.push_back(std::make_unique<CFocusGrab>(std::make_shared<CHyprlandFocusGrabV1>(pMgr->client(), pMgr->version(), id)));
const auto RESOURCE = m_vGrabs.back().get();
if (!RESOURCE->good()) {
pMgr->noMemory();
m_vGrabs.pop_back();
}
}

View file

@ -0,0 +1,76 @@
#pragma once
#include "WaylandProtocol.hpp"
#include "hyprland-focus-grab-v1.hpp"
#include "macros.hpp"
#include <cstdint>
#include <unordered_map>
#include <vector>
class CFocusGrab;
class CFocusGrabSurfaceState {
public:
CFocusGrabSurfaceState(CFocusGrab* grab, wlr_surface* surface);
~CFocusGrabSurfaceState();
enum State {
PendingAddition,
PendingRemoval,
Comitted,
} state = PendingAddition;
private:
DYNLISTENER(surfaceDestroy);
};
class CFocusGrab {
public:
CFocusGrab(SP<CHyprlandFocusGrabV1> resource_);
~CFocusGrab();
bool good();
bool isSurfaceComitted(wlr_surface* surface);
void start();
void finish(bool sendCleared);
private:
void addSurface(wlr_surface* surface);
void removeSurface(wlr_surface* surface);
void eraseSurface(wlr_surface* surface);
void commit();
SP<CHyprlandFocusGrabV1> resource;
std::unordered_map<wlr_surface*, UP<CFocusGrabSurfaceState>> m_mSurfaces;
wlr_seat_pointer_grab m_sPointerGrab;
wlr_seat_keyboard_grab m_sKeyboardGrab;
wlr_seat_touch_grab m_sTouchGrab;
bool m_bGrabActive = false;
DYNLISTENER(pointerGrabStarted);
DYNLISTENER(keyboardGrabStarted);
DYNLISTENER(touchGrabStarted);
friend class CFocusGrabSurfaceState;
};
class CFocusGrabProtocol : public IWaylandProtocol {
public:
CFocusGrabProtocol(const wl_interface* iface, const int& var, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
private:
void onManagerResourceDestroy(wl_resource* res);
void destroyGrab(CFocusGrab* grab);
void onCreateGrab(CHyprlandFocusGrabManagerV1* pMgr, uint32_t id);
std::vector<UP<CHyprlandFocusGrabManagerV1>> m_vManagers;
std::vector<UP<CFocusGrab>> m_vGrabs;
friend class CFocusGrab;
};
namespace PROTO {
inline UP<CFocusGrabProtocol> focusGrab;
}

@ -1 +1 @@
Subproject commit 0c2ce70625cb30aef199cb388f99e19a61a6ce03
Subproject commit 8af1ce2df94fbb58120a03f9ea1a659cfa115f61