1
0
Fork 0
forked from wry/wry

portal: implement workspace capture

This commit is contained in:
Julian Orth 2024-04-20 13:48:12 +02:00
parent c6864a6d85
commit 33a0a40857
23 changed files with 518 additions and 50 deletions

View file

@ -15,6 +15,7 @@ use {
jay_screenshot::JayScreenshot,
jay_seat_events::JaySeatEvents,
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
jay_select_workspace::{JaySelectWorkspace, JayWorkspaceSelector},
jay_workspace_watcher::JayWorkspaceWatcher,
},
leaks::Tracker,
@ -85,13 +86,14 @@ pub struct Cap;
impl Cap {
pub const NONE: u16 = 0;
pub const WINDOW_CAPTURE: u16 = 1;
pub const SELECT_WORKSPACE: u16 = 2;
}
impl JayCompositor {
fn send_capabilities(&self) {
self.client.event(Capabilities {
self_id: self.id,
cap: &[Cap::NONE, Cap::WINDOW_CAPTURE],
cap: &[Cap::NONE, Cap::WINDOW_CAPTURE, Cap::SELECT_WORKSPACE],
});
}
@ -362,6 +364,24 @@ impl JayCompositorRequestHandler for JayCompositor {
seat.global.select_toplevel(selector);
Ok(())
}
fn select_workspace(&self, req: SelectWorkspace, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let seat = self.client.lookup(req.seat)?;
let obj = Rc::new(JaySelectWorkspace {
id: req.id,
client: self.client.clone(),
tracker: Default::default(),
destroyed: Cell::new(false),
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
let selector = JayWorkspaceSelector {
ws: Default::default(),
jsw: obj.clone(),
};
seat.global.select_workspace(selector);
Ok(())
}
}
object_base! {

View file

@ -0,0 +1,105 @@
use {
crate::{
client::{Client, ClientError},
ifs::{jay_workspace::JayWorkspace, wl_seat::WorkspaceSelector},
leaks::Tracker,
object::{Object, Version},
tree::WorkspaceNode,
utils::clonecell::CloneCell,
wire::{jay_select_workspace::*, JaySelectWorkspaceId, JayWorkspaceId},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
};
pub struct JaySelectWorkspace {
pub id: JaySelectWorkspaceId,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub destroyed: Cell<bool>,
}
pub struct JayWorkspaceSelector {
pub ws: CloneCell<Option<Rc<WorkspaceNode>>>,
pub jsw: Rc<JaySelectWorkspace>,
}
impl WorkspaceSelector for JayWorkspaceSelector {
fn set(&self, ws: Rc<WorkspaceNode>) {
self.ws.set(Some(ws));
}
}
impl Drop for JayWorkspaceSelector {
fn drop(&mut self) {
if self.jsw.destroyed.get() {
return;
}
match self.ws.take() {
None => {
self.jsw.send_cancelled();
}
Some(ws) => {
let id = match self.jsw.client.new_id() {
Ok(id) => id,
Err(e) => {
self.jsw.client.error(e);
return;
}
};
let jw = Rc::new(JayWorkspace {
id,
client: self.jsw.client.clone(),
workspace: CloneCell::new(Some(ws.clone())),
tracker: Default::default(),
});
track!(self.jsw.client, jw);
self.jsw.client.add_server_obj(&jw);
self.jsw
.send_selected(ws.output.get().global.name.raw(), id);
ws.jay_workspaces
.set((self.jsw.client.id, jw.id), jw.clone());
jw.send_initial_properties(&ws);
}
};
let _ = self.jsw.client.remove_obj(&*self.jsw);
}
}
impl JaySelectWorkspace {
fn send_cancelled(&self) {
self.client.event(Cancelled { self_id: self.id });
}
fn send_selected(&self, output: u32, id: JayWorkspaceId) {
self.client.event(Selected {
self_id: self.id,
output,
id,
});
}
}
impl JaySelectWorkspaceRequestHandler for JaySelectWorkspace {
type Error = JaySelectWorkspaceError;
}
object_base! {
self = JaySelectWorkspace;
version = Version(1);
}
impl Object for JaySelectWorkspace {
fn break_loops(&self) {
self.destroyed.set(true);
}
}
simple_add_obj!(JaySelectWorkspace);
#[derive(Debug, Error)]
pub enum JaySelectWorkspaceError {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(JaySelectWorkspaceError, ClientError);

View file

@ -19,6 +19,14 @@ pub struct JayWorkspace {
}
impl JayWorkspace {
pub fn send_initial_properties(&self, workspace: &WorkspaceNode) {
self.send_linear_id(workspace);
self.send_name(workspace);
self.send_output(&workspace.output.get());
self.send_visible(workspace.visible.get());
self.send_done();
}
pub fn send_linear_id(&self, ws: &WorkspaceNode) {
self.client.event(LinearId {
self_id: self.id,

View file

@ -36,11 +36,7 @@ impl JayWorkspaceWatcher {
id: jw.id,
linear_id: workspace.id.raw(),
});
jw.send_linear_id(workspace);
jw.send_name(workspace);
jw.send_output(&workspace.output.get());
jw.send_visible(workspace.visible.get());
jw.send_done();
jw.send_initial_properties(workspace);
Ok(())
}

View file

@ -83,7 +83,10 @@ use {
thiserror::Error,
uapi::OwnedFd,
};
pub use {event_handling::NodeSeatState, pointer_owner::ToplevelSelector};
pub use {
event_handling::NodeSeatState,
pointer_owner::{ToplevelSelector, WorkspaceSelector},
};
pub const POINTER: u32 = 1;
const KEYBOARD: u32 = 2;
@ -1153,10 +1156,13 @@ impl WlSeatGlobal {
self.forward.set(forward);
}
#[allow(dead_code)]
pub fn select_toplevel(self: &Rc<Self>, selector: impl ToplevelSelector) {
self.pointer_owner.select_toplevel(self, selector);
}
pub fn select_workspace(self: &Rc<Self>, selector: impl WorkspaceSelector) {
self.pointer_owner.select_workspace(self, selector);
}
}
global_base!(WlSeatGlobal, WlSeat, WlSeatError);

View file

@ -14,7 +14,7 @@ use {
xdg_toplevel_drag_v1::XdgToplevelDragV1,
},
state::DeviceHandlerData,
tree::{FindTreeUsecase, FoundNode, Node, ToplevelNode},
tree::{FindTreeUsecase, FoundNode, Node, ToplevelNode, WorkspaceNode},
utils::{clonecell::CloneCell, smallmap::SmallMap},
},
std::{
@ -33,6 +33,10 @@ pub trait ToplevelSelector: 'static {
fn set(&self, toplevel: Rc<dyn ToplevelNode>);
}
pub trait WorkspaceSelector: 'static {
fn set(&self, ws: Rc<WorkspaceNode>);
}
impl Default for PointerOwnerHolder {
fn default() -> Self {
let default = Rc::new(SimplePointerOwner {
@ -157,19 +161,32 @@ impl PointerOwnerHolder {
seat.changes.or_assign(CHANGE_CURSOR_MOVED);
}
pub fn select_toplevel(&self, seat: &Rc<WlSeatGlobal>, selector: impl ToplevelSelector) {
fn select_element(&self, seat: &Rc<WlSeatGlobal>, usecase: impl SimplePointerOwnerUsecase) {
self.revert_to_default(seat);
let usecase = Rc::new(SelectToplevelUsecase {
seat: Rc::downgrade(seat),
selector,
latest: Default::default(),
});
if let Some(node) = seat.pointer_stack.borrow().last() {
usecase.node_focus(seat, node);
}
self.owner.set(Rc::new(SimplePointerOwner { usecase }));
seat.trigger_tree_changed();
}
pub fn select_toplevel(&self, seat: &Rc<WlSeatGlobal>, selector: impl ToplevelSelector) {
let usecase = Rc::new(SelectToplevelUsecase {
seat: Rc::downgrade(seat),
selector,
latest: Default::default(),
});
self.select_element(seat, usecase)
}
pub fn select_workspace(&self, seat: &Rc<WlSeatGlobal>, selector: impl WorkspaceSelector) {
let usecase = Rc::new(SelectWorkspaceUsecase {
seat: Rc::downgrade(seat),
selector,
latest: Default::default(),
});
self.select_element(seat, usecase)
}
}
trait PointerOwner {
@ -221,6 +238,12 @@ struct SelectToplevelUsecase<S: ?Sized> {
selector: S,
}
struct SelectWorkspaceUsecase<S: ?Sized> {
seat: Weak<WlSeatGlobal>,
latest: CloneCell<Option<Rc<WorkspaceNode>>>,
selector: S,
}
impl<T: SimplePointerOwnerUsecase> PointerOwner for SimplePointerOwner<T> {
fn button(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, button: u32, state: KeyState) {
if state != KeyState::Pressed {
@ -674,8 +697,22 @@ impl SimplePointerOwnerUsecase for DefaultPointerUsecase {
}
}
impl<S: ToplevelSelector + ?Sized> SimplePointerOwnerUsecase for Rc<SelectToplevelUsecase<S>> {
const FIND_TREE_USECASE: FindTreeUsecase = FindTreeUsecase::SelectToplevel;
trait NodeSelectorUsecase: Sized + 'static {
const FIND_TREE_USECASE: FindTreeUsecase;
fn default_button(
self: &Rc<Self>,
spo: &SimplePointerOwner<Rc<Self>>,
seat: &Rc<WlSeatGlobal>,
button: u32,
pn: &Rc<dyn Node>,
) -> bool;
fn node_focus(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>);
}
impl<U: NodeSelectorUsecase + ?Sized> SimplePointerOwnerUsecase for Rc<U> {
const FIND_TREE_USECASE: FindTreeUsecase = <U as NodeSelectorUsecase>::FIND_TREE_USECASE;
const IS_DEFAULT: bool = false;
fn default_button(
@ -685,17 +722,7 @@ impl<S: ToplevelSelector + ?Sized> SimplePointerOwnerUsecase for Rc<SelectToplev
button: u32,
pn: &Rc<dyn Node>,
) -> bool {
let Some(tl) = pn.clone().node_into_toplevel() else {
return false;
};
let selected_toplevel =
button == BTN_RIGHT || (button == BTN_LEFT && !tl.tl_admits_children());
if !selected_toplevel {
return false;
}
self.selector.set(tl);
spo.revert_to_default(seat);
true
<U as NodeSelectorUsecase>::default_button(self, spo, seat, button, pn)
}
fn start_drag(
@ -721,6 +748,34 @@ impl<S: ToplevelSelector + ?Sized> SimplePointerOwnerUsecase for Rc<SelectToplev
}
fn node_focus(&self, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
<U as NodeSelectorUsecase>::node_focus(self, seat, node)
}
}
impl<S: ToplevelSelector> NodeSelectorUsecase for SelectToplevelUsecase<S> {
const FIND_TREE_USECASE: FindTreeUsecase = FindTreeUsecase::SelectToplevel;
fn default_button(
self: &Rc<Self>,
spo: &SimplePointerOwner<Rc<Self>>,
seat: &Rc<WlSeatGlobal>,
button: u32,
pn: &Rc<dyn Node>,
) -> bool {
let Some(tl) = pn.clone().node_into_toplevel() else {
return false;
};
let selected_toplevel =
button == BTN_RIGHT || (button == BTN_LEFT && !tl.tl_admits_children());
if !selected_toplevel {
return false;
}
self.selector.set(tl);
spo.revert_to_default(seat);
true
}
fn node_focus(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
let mut damage = false;
let tl = node.clone().node_into_toplevel();
if let Some(tl) = &tl {
@ -750,3 +805,50 @@ impl<S: ?Sized> Drop for SelectToplevelUsecase<S> {
}
}
}
impl<S: WorkspaceSelector> NodeSelectorUsecase for SelectWorkspaceUsecase<S> {
const FIND_TREE_USECASE: FindTreeUsecase = FindTreeUsecase::SelectWorkspace;
fn default_button(
self: &Rc<Self>,
spo: &SimplePointerOwner<Rc<Self>>,
seat: &Rc<WlSeatGlobal>,
_button: u32,
pn: &Rc<dyn Node>,
) -> bool {
let Some(ws) = pn.clone().node_into_workspace() else {
return false;
};
self.selector.set(ws);
spo.revert_to_default(seat);
true
}
fn node_focus(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
let mut damage = false;
let ws = node.clone().node_into_workspace();
if let Some(ws) = &ws {
ws.render_highlight.fetch_add(1);
seat.set_known_cursor(KnownCursor::Pointer);
damage = true;
}
if let Some(prev) = self.latest.set(ws) {
prev.render_highlight.fetch_sub(1);
damage = true;
}
if damage {
seat.state.damage();
}
}
}
impl<S: ?Sized> Drop for SelectWorkspaceUsecase<S> {
fn drop(&mut self) {
if let Some(prev) = self.latest.take() {
prev.render_highlight.fetch_sub(1);
if let Some(seat) = self.seat.upgrade() {
seat.state.damage();
}
}
}
}