mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-22 19:26:00 +01:00
protocols/focus_grab: try to pick surface for keyboard focus
This commit is contained in:
parent
3da58270c6
commit
090358d0d1
5 changed files with 208 additions and 32 deletions
|
@ -32,6 +32,7 @@ class CLayerSurface {
|
||||||
|
|
||||||
wlr_layer_surface_v1* layerSurface;
|
wlr_layer_surface_v1* layerSurface;
|
||||||
wl_list link;
|
wl_list link;
|
||||||
|
std::unique_ptr<CPopup> popupHead;
|
||||||
|
|
||||||
bool keyboardExclusive = false;
|
bool keyboardExclusive = false;
|
||||||
|
|
||||||
|
@ -69,8 +70,6 @@ class CLayerSurface {
|
||||||
void onCommit();
|
void onCommit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<CPopup> popupHead;
|
|
||||||
|
|
||||||
DYNLISTENER(destroyLayerSurface);
|
DYNLISTENER(destroyLayerSurface);
|
||||||
DYNLISTENER(mapLayerSurface);
|
DYNLISTENER(mapLayerSurface);
|
||||||
DYNLISTENER(unmapLayerSurface);
|
DYNLISTENER(unmapLayerSurface);
|
||||||
|
@ -82,4 +81,4 @@ class CLayerSurface {
|
||||||
bool operator==(const CLayerSurface& rhs) const {
|
bool operator==(const CLayerSurface& rhs) const {
|
||||||
return layerSurface == rhs.layerSurface && monitorID == rhs.monitorID;
|
return layerSurface == rhs.layerSurface && monitorID == rhs.monitorID;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,10 @@ CPopup::~CPopup() {
|
||||||
hyprListener_destroyPopup.removeCallback();
|
hyprListener_destroyPopup.removeCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_xdg_popup* CPopup::wlr() {
|
||||||
|
return this->m_pWLR;
|
||||||
|
}
|
||||||
|
|
||||||
static void onNewPopup(void* owner, void* data) {
|
static void onNewPopup(void* owner, void* data) {
|
||||||
const auto POPUP = (CPopup*)owner;
|
const auto POPUP = (CPopup*)owner;
|
||||||
POPUP->onNewPopup((wlr_xdg_popup*)data);
|
POPUP->onNewPopup((wlr_xdg_popup*)data);
|
||||||
|
|
|
@ -15,21 +15,23 @@ class CPopup {
|
||||||
|
|
||||||
~CPopup();
|
~CPopup();
|
||||||
|
|
||||||
Vector2D coordsRelativeToParent();
|
wlr_xdg_popup* wlr();
|
||||||
Vector2D coordsGlobal();
|
Vector2D coordsRelativeToParent();
|
||||||
|
Vector2D coordsGlobal();
|
||||||
|
|
||||||
Vector2D size();
|
Vector2D size();
|
||||||
|
|
||||||
void onNewPopup(wlr_xdg_popup* popup);
|
void onNewPopup(wlr_xdg_popup* popup);
|
||||||
void onDestroy();
|
void onDestroy();
|
||||||
void onMap();
|
void onMap();
|
||||||
void onUnmap();
|
void onUnmap();
|
||||||
void onCommit(bool ignoreSiblings = false);
|
void onCommit(bool ignoreSiblings = false);
|
||||||
void onReposition();
|
void onReposition();
|
||||||
|
|
||||||
void recheckTree();
|
void recheckTree();
|
||||||
|
|
||||||
CWLSurface m_sWLSurface;
|
CWLSurface m_sWLSurface;
|
||||||
|
std::vector<std::unique_ptr<CPopup>> m_vChildren;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// T1 owners, each popup has to have one of these
|
// T1 owners, each popup has to have one of these
|
||||||
|
@ -37,20 +39,18 @@ class CPopup {
|
||||||
PHLLS m_pLayerOwner;
|
PHLLS m_pLayerOwner;
|
||||||
|
|
||||||
// T2 owners
|
// T2 owners
|
||||||
CPopup* m_pParent = nullptr;
|
CPopup* m_pParent = nullptr;
|
||||||
|
|
||||||
wlr_xdg_popup* m_pWLR = nullptr;
|
wlr_xdg_popup* m_pWLR = nullptr;
|
||||||
|
|
||||||
Vector2D m_vLastSize = {};
|
Vector2D m_vLastSize = {};
|
||||||
Vector2D m_vLastPos = {};
|
Vector2D m_vLastPos = {};
|
||||||
|
|
||||||
bool m_bRequestedReposition = false;
|
bool m_bRequestedReposition = false;
|
||||||
|
|
||||||
bool m_bInert = false;
|
bool m_bInert = false;
|
||||||
|
|
||||||
//
|
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
|
||||||
std::vector<std::unique_ptr<CPopup>> m_vChildren;
|
|
||||||
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
|
|
||||||
|
|
||||||
// signals
|
// signals
|
||||||
DYNLISTENER(newPopup);
|
DYNLISTENER(newPopup);
|
||||||
|
@ -67,4 +67,4 @@ class CPopup {
|
||||||
|
|
||||||
Vector2D localToGlobal(const Vector2D& rel);
|
Vector2D localToGlobal(const Vector2D& rel);
|
||||||
Vector2D t1ParentCoords();
|
Vector2D t1ParentCoords();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
#include "FocusGrab.hpp"
|
#include "FocusGrab.hpp"
|
||||||
#include "Compositor.hpp"
|
#include "Compositor.hpp"
|
||||||
|
#include "desktop/LayerSurface.hpp"
|
||||||
|
#include "desktop/WLSurface.hpp"
|
||||||
|
#include "desktop/Workspace.hpp"
|
||||||
|
#include "render/decorations/CHyprGroupBarDecoration.hpp"
|
||||||
|
#include "wlr-layer-shell-unstable-v1-protocol.h"
|
||||||
|
#include <functional>
|
||||||
#include <hyprland-focus-grab-v1.hpp>
|
#include <hyprland-focus-grab-v1.hpp>
|
||||||
#include <managers/input/InputManager.hpp>
|
#include <managers/input/InputManager.hpp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
|
#include <wayland-util.h>
|
||||||
|
|
||||||
static void focus_grab_pointer_enter(wlr_seat_pointer_grab* grab, wlr_surface* surface, double sx, double sy) {
|
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))
|
if (static_cast<CFocusGrab*>(grab->data)->isSurfaceComitted(surface))
|
||||||
|
@ -59,11 +66,12 @@ static void focus_grab_keyboard_enter(wlr_seat_keyboard_grab* grab, wlr_surface*
|
||||||
if (static_cast<CFocusGrab*>(grab->data)->isSurfaceComitted(surface))
|
if (static_cast<CFocusGrab*>(grab->data)->isSurfaceComitted(surface))
|
||||||
wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers);
|
wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers);
|
||||||
|
|
||||||
// otherwise the last grabbed window should retain keyboard focus.
|
// otherwise the last grabbed window should retain keyboard focus.
|
||||||
}
|
}
|
||||||
|
|
||||||
static void focus_grab_keyboard_clear_focus(wlr_seat_keyboard_grab* grab) {
|
static void focus_grab_keyboard_clear_focus(wlr_seat_keyboard_grab* grab) {
|
||||||
static_cast<CFocusGrab*>(grab->data)->finish(true);
|
// focus will be cleared from non whitelisted surfaces if no
|
||||||
|
// whitelisted surfaces will accept it, so this should not reset the grab.
|
||||||
}
|
}
|
||||||
|
|
||||||
static void focus_grab_keyboard_key(wlr_seat_keyboard_grab* grab, uint32_t time, uint32_t key, uint32_t state) {
|
static void focus_grab_keyboard_key(wlr_seat_keyboard_grab* grab, uint32_t time, uint32_t key, uint32_t state) {
|
||||||
|
@ -182,16 +190,11 @@ void CFocusGrab::start() {
|
||||||
|
|
||||||
hyprListener_touchGrabStarted.initCallback(
|
hyprListener_touchGrabStarted.initCallback(
|
||||||
&g_pCompositor->m_sSeat.seat->events.touch_grab_begin, [this](void*, void*) { this->finish(true); }, this, "CFocusGrab");
|
&g_pCompositor->m_sSeat.seat->events.touch_grab_begin, [this](void*, void*) { this->finish(true); }, this, "CFocusGrab");
|
||||||
|
|
||||||
// If the current keyboard focus surface isn't whitelisted, kick it out.
|
|
||||||
auto focusedSurface = g_pCompositor->m_sSeat.seat->keyboard_state.focused_surface;
|
|
||||||
if (focusedSurface != nullptr && !isSurfaceComitted(focusedSurface)) {
|
|
||||||
wlr_seat_keyboard_clear_focus(g_pCompositor->m_sSeat.seat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure new surfaces are focused if under the mouse when comitted.
|
// Ensure new surfaces are focused if under the mouse when comitted.
|
||||||
g_pInputManager->refocus();
|
g_pInputManager->refocus();
|
||||||
|
refocusKeyboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFocusGrab::finish(bool sendCleared) {
|
void CFocusGrab::finish(bool sendCleared) {
|
||||||
|
@ -253,6 +256,172 @@ void CFocusGrab::eraseSurface(wlr_surface* surface) {
|
||||||
commit();
|
commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CFocusGrab::refocusKeyboardTestSurface(wlr_surface* surface) {
|
||||||
|
if (!isSurfaceComitted(surface))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto KEYBOARD = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat);
|
||||||
|
uint32_t keycodes[32] = {0};
|
||||||
|
|
||||||
|
wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, surface, keycodes, 0, &KEYBOARD->modifiers);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFocusGrab::refocusKeyboardTestPopupTree(UP<CPopup>& popup) {
|
||||||
|
for (auto& popup : popup->m_vChildren) {
|
||||||
|
if (refocusKeyboardTestPopupTree(popup))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wlrPopup = popup->wlr();
|
||||||
|
if (!wlrPopup)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return refocusKeyboardTestSurface(wlrPopup->base->surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFocusGrab::refocusKeyboard() {
|
||||||
|
// If there is no kb focused surface or the current kb focused surface is not whitelisted, replace it.
|
||||||
|
auto keyboardSurface = g_pCompositor->m_sSeat.seat->keyboard_state.focused_surface;
|
||||||
|
if (keyboardSurface != nullptr && isSurfaceComitted(keyboardSurface))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto testLayers = [&](std::vector<PHLLS>& lsl, std::function<bool(PHLLS&)> f) {
|
||||||
|
for (auto& ls : lsl | std::views::reverse) {
|
||||||
|
if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->surface->mapped) || ls->alpha.value() == 0.f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ls->layerSurface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (f(ls))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto testWindow = [&](PHLWINDOW& window) {
|
||||||
|
if (refocusKeyboardTestPopupTree(window->m_pPopupHead))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (refocusKeyboardTestSurface(window->m_pWLSurface.wlr()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto testWindows = [&](std::function<bool(PHLWINDOW&)> f) {
|
||||||
|
for (auto& window : g_pCompositor->m_vWindows | std::views::reverse) {
|
||||||
|
if (!window->m_bIsMapped || window->isHidden() || window->m_sAdditionalConfigData.noFocus)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (f(window) && refocusKeyboardTestPopupTree(window->m_pPopupHead))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& window : g_pCompositor->m_vWindows | std::views::reverse) {
|
||||||
|
if (!window->m_bIsMapped || window->isHidden() || window->m_sAdditionalConfigData.noFocus)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (f(window) && refocusKeyboardTestSurface(window->m_pWLSurface.wlr()))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto testMonitor = [&](CMonitor* monitor) {
|
||||||
|
// layer popups
|
||||||
|
for (auto& lsl : monitor->m_aLayerSurfaceLayers | std::views::reverse) {
|
||||||
|
auto ret = testLayers(lsl, [&](auto& layer) { return refocusKeyboardTestPopupTree(layer->popupHead); });
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// overlay and top layers
|
||||||
|
auto layerCallback = [&](auto& layer) { return refocusKeyboardTestSurface(layer->layerSurface->surface); };
|
||||||
|
|
||||||
|
if (testLayers(monitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], layerCallback))
|
||||||
|
return true;
|
||||||
|
if (testLayers(monitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], layerCallback))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto workspace = monitor->activeWorkspace;
|
||||||
|
|
||||||
|
// fullscreen windows
|
||||||
|
if (workspace->m_bHasFullscreenWindow && workspace->m_efFullscreenMode == FULLSCREEN_FULL) {
|
||||||
|
auto window = g_pCompositor->getFullscreenWindowOnWorkspace(workspace->m_iID);
|
||||||
|
if (window) {
|
||||||
|
if (testWindow(window))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pinned windows
|
||||||
|
if (testWindows([&](auto& window) { return window->m_bPinned; }))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// windows on the active special workspace
|
||||||
|
if (monitor->activeSpecialWorkspace) {
|
||||||
|
auto ret = testWindows([&](auto& window) { return window->m_pWorkspace == monitor->activeSpecialWorkspace; });
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspace->m_bHasFullscreenWindow) {
|
||||||
|
// windows created over a fullscreen window
|
||||||
|
auto ret = testWindows([&](auto& window) { return window->m_pWorkspace == workspace && window->m_bCreatedOverFullscreen; });
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// the fullscreen window
|
||||||
|
auto window = g_pCompositor->getFullscreenWindowOnWorkspace(workspace->m_iID);
|
||||||
|
|
||||||
|
if (testWindow(window))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating windows
|
||||||
|
bool ret = testWindows([&](auto& window) { return window->m_pWorkspace == workspace && window->m_bIsFloating; });
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// tiled windows
|
||||||
|
ret = testWindows([&](auto& window) { return window->m_pWorkspace == workspace; });
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// bottom and background layers
|
||||||
|
if (testLayers(monitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], layerCallback))
|
||||||
|
return true;
|
||||||
|
if (testLayers(monitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], layerCallback))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto activeMonitor = g_pCompositor->getMonitorFromCursor();
|
||||||
|
if (testMonitor(activeMonitor))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto& monitor : g_pCompositor->m_vMonitors) {
|
||||||
|
if (monitor.get() == activeMonitor)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (testMonitor(monitor.get()))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no applicable surfaces found, at least remove focus from any non whitelisted surfaces.
|
||||||
|
wlr_seat_keyboard_clear_focus(g_pCompositor->m_sSeat.seat);
|
||||||
|
}
|
||||||
|
|
||||||
void CFocusGrab::commit() {
|
void CFocusGrab::commit() {
|
||||||
auto surfacesChanged = false;
|
auto surfacesChanged = false;
|
||||||
for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) {
|
for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "WaylandProtocol.hpp"
|
#include "WaylandProtocol.hpp"
|
||||||
|
#include "desktop/Popup.hpp"
|
||||||
#include "hyprland-focus-grab-v1.hpp"
|
#include "hyprland-focus-grab-v1.hpp"
|
||||||
#include "macros.hpp"
|
#include "macros.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
@ -39,6 +40,9 @@ class CFocusGrab {
|
||||||
void addSurface(wlr_surface* surface);
|
void addSurface(wlr_surface* surface);
|
||||||
void removeSurface(wlr_surface* surface);
|
void removeSurface(wlr_surface* surface);
|
||||||
void eraseSurface(wlr_surface* surface);
|
void eraseSurface(wlr_surface* surface);
|
||||||
|
void refocusKeyboard();
|
||||||
|
bool refocusKeyboardTestSurface(wlr_surface* surface);
|
||||||
|
bool refocusKeyboardTestPopupTree(UP<CPopup>& popup);
|
||||||
void commit();
|
void commit();
|
||||||
|
|
||||||
SP<CHyprlandFocusGrabV1> resource;
|
SP<CHyprlandFocusGrabV1> resource;
|
||||||
|
|
Loading…
Reference in a new issue