mirror of
https://github.com/hyprwm/Hyprland
synced 2024-12-22 19:29:49 +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;
|
||||
wl_list link;
|
||||
std::unique_ptr<CPopup> popupHead;
|
||||
|
||||
bool keyboardExclusive = false;
|
||||
|
||||
|
@ -69,8 +70,6 @@ class CLayerSurface {
|
|||
void onCommit();
|
||||
|
||||
private:
|
||||
std::unique_ptr<CPopup> popupHead;
|
||||
|
||||
DYNLISTENER(destroyLayerSurface);
|
||||
DYNLISTENER(mapLayerSurface);
|
||||
DYNLISTENER(unmapLayerSurface);
|
||||
|
@ -82,4 +81,4 @@ class CLayerSurface {
|
|||
bool operator==(const CLayerSurface& rhs) const {
|
||||
return layerSurface == rhs.layerSurface && monitorID == rhs.monitorID;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -36,6 +36,10 @@ CPopup::~CPopup() {
|
|||
hyprListener_destroyPopup.removeCallback();
|
||||
}
|
||||
|
||||
wlr_xdg_popup* CPopup::wlr() {
|
||||
return this->m_pWLR;
|
||||
}
|
||||
|
||||
static void onNewPopup(void* owner, void* data) {
|
||||
const auto POPUP = (CPopup*)owner;
|
||||
POPUP->onNewPopup((wlr_xdg_popup*)data);
|
||||
|
|
|
@ -15,21 +15,23 @@ class CPopup {
|
|||
|
||||
~CPopup();
|
||||
|
||||
Vector2D coordsRelativeToParent();
|
||||
Vector2D coordsGlobal();
|
||||
wlr_xdg_popup* wlr();
|
||||
Vector2D coordsRelativeToParent();
|
||||
Vector2D coordsGlobal();
|
||||
|
||||
Vector2D size();
|
||||
Vector2D size();
|
||||
|
||||
void onNewPopup(wlr_xdg_popup* popup);
|
||||
void onDestroy();
|
||||
void onMap();
|
||||
void onUnmap();
|
||||
void onCommit(bool ignoreSiblings = false);
|
||||
void onReposition();
|
||||
void onNewPopup(wlr_xdg_popup* popup);
|
||||
void onDestroy();
|
||||
void onMap();
|
||||
void onUnmap();
|
||||
void onCommit(bool ignoreSiblings = false);
|
||||
void onReposition();
|
||||
|
||||
void recheckTree();
|
||||
void recheckTree();
|
||||
|
||||
CWLSurface m_sWLSurface;
|
||||
CWLSurface m_sWLSurface;
|
||||
std::vector<std::unique_ptr<CPopup>> m_vChildren;
|
||||
|
||||
private:
|
||||
// T1 owners, each popup has to have one of these
|
||||
|
@ -37,20 +39,18 @@ class CPopup {
|
|||
PHLLS m_pLayerOwner;
|
||||
|
||||
// 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_vLastPos = {};
|
||||
Vector2D m_vLastSize = {};
|
||||
Vector2D m_vLastPos = {};
|
||||
|
||||
bool m_bRequestedReposition = false;
|
||||
bool m_bRequestedReposition = false;
|
||||
|
||||
bool m_bInert = false;
|
||||
bool m_bInert = false;
|
||||
|
||||
//
|
||||
std::vector<std::unique_ptr<CPopup>> m_vChildren;
|
||||
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
|
||||
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
|
||||
|
||||
// signals
|
||||
DYNLISTENER(newPopup);
|
||||
|
@ -67,4 +67,4 @@ class CPopup {
|
|||
|
||||
Vector2D localToGlobal(const Vector2D& rel);
|
||||
Vector2D t1ParentCoords();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
#include "FocusGrab.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 <managers/input/InputManager.hpp>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#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) {
|
||||
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))
|
||||
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_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) {
|
||||
|
@ -182,16 +190,11 @@ void CFocusGrab::start() {
|
|||
|
||||
hyprListener_touchGrabStarted.initCallback(
|
||||
&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.
|
||||
g_pInputManager->refocus();
|
||||
refocusKeyboard();
|
||||
}
|
||||
|
||||
void CFocusGrab::finish(bool sendCleared) {
|
||||
|
@ -253,6 +256,172 @@ void CFocusGrab::eraseSurface(wlr_surface* surface) {
|
|||
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() {
|
||||
auto surfacesChanged = false;
|
||||
for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "WaylandProtocol.hpp"
|
||||
#include "desktop/Popup.hpp"
|
||||
#include "hyprland-focus-grab-v1.hpp"
|
||||
#include "macros.hpp"
|
||||
#include <cstdint>
|
||||
|
@ -39,6 +40,9 @@ class CFocusGrab {
|
|||
void addSurface(wlr_surface* surface);
|
||||
void removeSurface(wlr_surface* surface);
|
||||
void eraseSurface(wlr_surface* surface);
|
||||
void refocusKeyboard();
|
||||
bool refocusKeyboardTestSurface(wlr_surface* surface);
|
||||
bool refocusKeyboardTestPopupTree(UP<CPopup>& popup);
|
||||
void commit();
|
||||
|
||||
SP<CHyprlandFocusGrabV1> resource;
|
||||
|
|
Loading…
Reference in a new issue