portal: implement workspace capture
This commit is contained in:
parent
c6864a6d85
commit
33a0a40857
23 changed files with 518 additions and 50 deletions
|
|
@ -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! {
|
||||
|
|
|
|||
105
src/ifs/jay_select_workspace.rs
Normal file
105
src/ifs/jay_select_workspace.rs
Normal 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);
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue