1
0
Fork 0
forked from wry/wry

ifs: init hyprland_focus_grab_manager_v1

This commit is contained in:
kossLAN 2026-04-06 23:42:38 -04:00
parent a75d388e97
commit 2591dc739b
No known key found for this signature in database
9 changed files with 315 additions and 4 deletions

View file

@ -10,6 +10,7 @@ use {
ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1Global,
ext_session_lock_manager_v1::ExtSessionLockManagerV1Global,
head_management::jay_head_manager_v1::JayHeadManagerV1Global,
hyprland_focus_grab_manager_v1::HyprlandFocusGrabManagerV1Global,
ipc::{
data_control::{
ext_data_control_manager_v1::ExtDataControlManagerV1Global,
@ -205,6 +206,7 @@ singletons! {
ZwlrScreencopyManagerV1,
ZwpRelativePointerManagerV1,
ExtSessionLockManagerV1,
HyprlandFocusGrabManagerV1,
WpViewporter,
WpFractionalScaleManagerV1,
ZwpPointerConstraintsV1,

View file

@ -10,6 +10,8 @@ pub mod ext_output_image_capture_source_manager_v1;
pub mod ext_session_lock_manager_v1;
pub mod ext_session_lock_v1;
pub mod head_management;
pub mod hyprland_focus_grab_manager_v1;
pub mod hyprland_focus_grab_v1;
pub mod ipc;
pub mod jay_acceptor_request;
pub mod jay_client_query;

View file

@ -0,0 +1,97 @@
use {
crate::{
client::{Client, ClientError},
globals::{Global, GlobalName},
ifs::hyprland_focus_grab_v1::HyprlandFocusGrabV1,
leaks::Tracker,
object::{Object, Version},
wire::{HyprlandFocusGrabManagerV1Id, hyprland_focus_grab_manager_v1::*},
},
std::rc::Rc,
thiserror::Error,
};
pub struct HyprlandFocusGrabManagerV1Global {
pub name: GlobalName,
}
impl HyprlandFocusGrabManagerV1Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: HyprlandFocusGrabManagerV1Id,
client: &Rc<Client>,
version: Version,
) -> Result<(), HyprlandFocusGrabManagerV1Error> {
let obj = Rc::new(HyprlandFocusGrabManagerV1 {
id,
client: client.clone(),
tracker: Default::default(),
version,
});
track!(client, obj);
client.add_client_obj(&obj)?;
Ok(())
}
}
pub struct HyprlandFocusGrabManagerV1 {
pub id: HyprlandFocusGrabManagerV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl HyprlandFocusGrabManagerV1RequestHandler for HyprlandFocusGrabManagerV1 {
type Error = HyprlandFocusGrabManagerV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
fn create_grab(&self, req: CreateGrab, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let grab = Rc::new(HyprlandFocusGrabV1::new(
req.grab,
&self.client,
self.version,
));
track!(self.client, grab);
self.client.add_client_obj(&grab)?;
Ok(())
}
}
global_base!(
HyprlandFocusGrabManagerV1Global,
HyprlandFocusGrabManagerV1,
HyprlandFocusGrabManagerV1Error
);
impl Global for HyprlandFocusGrabManagerV1Global {
fn version(&self) -> u32 {
1
}
}
simple_add_global!(HyprlandFocusGrabManagerV1Global);
object_base! {
self = HyprlandFocusGrabManagerV1;
version = self.version;
}
impl Object for HyprlandFocusGrabManagerV1 {}
simple_add_obj!(HyprlandFocusGrabManagerV1);
#[derive(Debug, Error)]
pub enum HyprlandFocusGrabManagerV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(HyprlandFocusGrabManagerV1Error, ClientError);

View file

@ -0,0 +1,154 @@
use {
crate::{
client::{Client, ClientError},
ifs::wl_seat::SeatFocusGrab,
ifs::wl_surface::WlSurface,
leaks::Tracker,
object::{Object, Version},
tree::NodeId,
wire::{HyprlandFocusGrabV1Id, hyprland_focus_grab_v1::*},
},
ahash::AHashMap,
std::{
cell::Cell,
rc::Rc,
},
thiserror::Error,
};
pub struct HyprlandFocusGrabV1 {
pub id: HyprlandFocusGrabV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pending: std::cell::RefCell<Option<AHashMap<NodeId, Rc<WlSurface>>>>,
committed: std::cell::RefCell<AHashMap<NodeId, Rc<WlSurface>>>,
active: Cell<bool>,
}
impl HyprlandFocusGrabV1 {
pub fn new(id: HyprlandFocusGrabV1Id, client: &Rc<Client>, version: Version) -> Self {
Self {
id,
client: client.clone(),
tracker: Default::default(),
version,
pending: Default::default(),
committed: Default::default(),
active: Cell::new(false),
}
}
fn send_cleared(&self) {
self.client.event(Cleared { self_id: self.id });
}
fn activate(self: &Rc<Self>) {
let state = &self.client.state;
let seats = state.globals.seats.lock();
let focus_node = self
.committed
.borrow()
.values()
.find_map(|s| s.get_focus_node());
for seat in seats.values() {
if let Some(old) = seat.focus_grab.get() {
old.clear();
}
seat.focus_grab.set(Some(self.clone() as Rc<dyn SeatFocusGrab>));
if let Some(node) = focus_node.clone() {
seat.grab(node);
}
}
self.active.set(true);
}
fn deactivate(&self) {
if !self.active.get() {
return;
}
self.active.set(false);
let state = &self.client.state;
for seat in state.globals.seats.lock().values() {
seat.clear_focus_grab();
}
}
}
impl SeatFocusGrab for HyprlandFocusGrabV1 {
fn contains_node(&self, node_id: NodeId) -> bool {
self.committed.borrow().contains_key(&node_id)
}
fn clear(self: Rc<Self>) {
if !self.active.get() {
return;
}
self.active.set(false);
self.send_cleared();
let state = &self.client.state;
for seat in state.globals.seats.lock().values() {
seat.clear_focus_grab();
}
}
}
impl HyprlandFocusGrabV1RequestHandler for HyprlandFocusGrabV1 {
type Error = HyprlandFocusGrabV1Error;
fn add_surface(&self, req: AddSurface, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let surface = self.client.lookup(req.surface)?;
let node_id: NodeId = surface.node_id.into();
let mut pending = self.pending.borrow_mut();
let pending = pending.get_or_insert_with(|| self.committed.borrow().clone());
pending.insert(node_id, surface);
Ok(())
}
fn remove_surface(&self, req: RemoveSurface, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let surface = self.client.lookup(req.surface)?;
let node_id: NodeId = surface.node_id.into();
let mut pending = self.pending.borrow_mut();
let pending = pending.get_or_insert_with(|| self.committed.borrow().clone());
pending.remove(&node_id);
Ok(())
}
fn commit(&self, _req: Commit, slf: &Rc<Self>) -> Result<(), Self::Error> {
let was_empty = self.committed.borrow().is_empty();
if let Some(new_committed) = self.pending.borrow_mut().take() {
*self.committed.borrow_mut() = new_committed;
}
let is_empty = self.committed.borrow().is_empty();
match (was_empty, is_empty) {
(true, false) => slf.activate(),
(false, true) => self.deactivate(),
_ => {}
}
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.deactivate();
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = HyprlandFocusGrabV1;
version = self.version;
}
impl Object for HyprlandFocusGrabV1 {}
simple_add_obj!(HyprlandFocusGrabV1);
#[derive(Debug, Error)]
pub enum HyprlandFocusGrabV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(HyprlandFocusGrabV1Error, ClientError);

View file

@ -83,8 +83,8 @@ use {
rect::Rect,
state::{DeviceHandlerData, State},
tree::{
ContainerNode, ContainerSplit, Direction, FoundNode, Node, NodeLayer, NodeLayerLink,
NodeLocation, OutputNode, StackedNode, ToplevelNode, WorkspaceNode,
ContainerNode, ContainerSplit, Direction, FoundNode, Node, NodeId, NodeLayer,
NodeLayerLink, NodeLocation, OutputNode, StackedNode, ToplevelNode, WorkspaceNode,
generic_node_visitor, toplevel_create_split, toplevel_parent_container,
toplevel_set_floating, toplevel_set_workspace,
},
@ -164,6 +164,11 @@ impl Drop for DroppedDnd {
}
}
pub trait SeatFocusGrab {
fn contains_node(&self, node_id: NodeId) -> bool;
fn clear(self: Rc<Self>);
}
linear_ids!(PhysicalKeyboardIds, PhysicalKeyboardId, u64);
pub struct PhysicalKeyboard {
@ -258,6 +263,7 @@ pub struct WlSeatGlobal {
warp_mouse_to_focus_scheduled: Cell<bool>,
warp_mouse_to_focus_skip_target_check: Cell<bool>,
mouse_follows_focus: Cell<bool>,
pub focus_grab: CloneCell<Option<Rc<dyn SeatFocusGrab>>>,
}
impl PartialEq for WlSeatGlobal {
@ -403,6 +409,7 @@ impl WlSeatGlobal {
warp_mouse_to_focus_scheduled: Cell::new(false),
warp_mouse_to_focus_skip_target_check: Cell::new(false),
mouse_follows_focus: Cell::new(false),
focus_grab: Default::default(),
});
slf.pointer_cursor.set_owner(slf.clone());
slf.modifiers_listener

View file

@ -1140,6 +1140,11 @@ impl WlSeatGlobal {
self.kb_owner.ungrab(self);
}
pub fn clear_focus_grab(self: &Rc<Self>) {
self.focus_grab.take();
self.ungrab_kb();
}
pub fn grab(self: &Rc<Self>, node: Rc<dyn Node>) {
self.kb_owner.grab(self, node);
}

View file

@ -400,6 +400,7 @@ impl<T: SimplePointerOwnerUsecase> PointerOwner for SimplePointerOwner<T> {
seat.state
.root
.node_find_tree_at(x_int, y_int, &mut found_tree, T::FIND_TREE_USECASE);
self.usecase.filter_found_tree(seat, &mut found_tree);
let mut divergence = found_tree.len().min(stack.len());
for (i, (found, stack)) in found_tree.iter().zip(stack.iter()).enumerate() {
if found.node.node_id() != stack.node_id() {
@ -717,6 +718,11 @@ trait SimplePointerOwnerUsecase: Sized + Clone + 'static {
fn release_grab(&self, seat: &Rc<WlSeatGlobal>);
fn filter_found_tree(&self, seat: &Rc<WlSeatGlobal>, found_tree: &mut Vec<FoundNode>) {
let _ = seat;
let _ = found_tree;
}
fn node_focus(&self, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
let _ = seat;
let _ = node;
@ -829,13 +835,29 @@ impl SimplePointerOwnerUsecase for DefaultPointerUsecase {
fn default_button(
&self,
_spo: &SimplePointerOwner<Self>,
_seat: &Rc<WlSeatGlobal>,
seat: &Rc<WlSeatGlobal>,
_button: u32,
_pn: &Rc<dyn Node>,
pn: &Rc<dyn Node>,
) -> bool {
if let Some(grab) = seat.focus_grab.get() {
if !grab.contains_node(pn.node_id()) {
grab.clear();
return true;
}
}
false
}
fn filter_found_tree(&self, seat: &Rc<WlSeatGlobal>, found_tree: &mut Vec<FoundNode>) {
if let Some(grab) = seat.focus_grab.get() {
if let Some(leaf) = found_tree.last() {
if !grab.contains_node(leaf.node.node_id()) {
found_tree.truncate(1);
}
}
}
}
fn start_drag(
&self,
grab: &SimpleGrabPointerOwner<Self>,

View file

@ -0,0 +1,6 @@
request create_grab {
grab: id(hyprland_focus_grab_v1) (new),
}
request destroy (destructor) {
}

View file

@ -0,0 +1,16 @@
request add_surface {
surface: id(wl_surface),
}
request remove_surface {
surface: id(wl_surface),
}
request commit {
}
request destroy (destructor) {
}
event cleared {
}