Merge pull request #170 from mahkoh/jorth/workspace-capture
portal: implement workspace capture
This commit is contained in:
commit
1566873c25
29 changed files with 709 additions and 195 deletions
|
|
@ -72,6 +72,12 @@ You can change this GPU at runtime.
|
||||||
## Screen Sharing
|
## Screen Sharing
|
||||||
|
|
||||||
Jay supports screen sharing via xdg-desktop-portal.
|
Jay supports screen sharing via xdg-desktop-portal.
|
||||||
|
There are three supported modes:
|
||||||
|
|
||||||
|
- Window capture
|
||||||
|
- Output capture
|
||||||
|
- Workspace capture which is like output capture except that only one workspace will be
|
||||||
|
shown.
|
||||||
|
|
||||||
## Screen Locking
|
## Screen Locking
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
- Screencasts now support window capture.
|
- Screencasts now support window capture.
|
||||||
|
- Screencasts now support workspace capture.
|
||||||
- Add support for wp-alpha-modifier.
|
- Add support for wp-alpha-modifier.
|
||||||
- Add support for per-device keymaps.
|
- Add support for per-device keymaps.
|
||||||
- Add support for virtual-keyboard-unstable-v1.
|
- Add support for virtual-keyboard-unstable-v1.
|
||||||
|
|
|
||||||
|
|
@ -451,6 +451,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
update_render_data_scheduled: Cell::new(false),
|
update_render_data_scheduled: Cell::new(false),
|
||||||
screencasts: Default::default(),
|
screencasts: Default::default(),
|
||||||
hardware_cursor_needs_render: Cell::new(false),
|
hardware_cursor_needs_render: Cell::new(false),
|
||||||
|
screencopies: Default::default(),
|
||||||
});
|
});
|
||||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||||
id: state.node_ids.next(),
|
id: state.node_ids.next(),
|
||||||
|
|
@ -467,9 +468,11 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
visible_on_desired_output: Default::default(),
|
visible_on_desired_output: Default::default(),
|
||||||
desired_output: CloneCell::new(dummy_output.global.output_id.clone()),
|
desired_output: CloneCell::new(dummy_output.global.output_id.clone()),
|
||||||
jay_workspaces: Default::default(),
|
jay_workspaces: Default::default(),
|
||||||
capture: Cell::new(false),
|
may_capture: Cell::new(false),
|
||||||
|
has_capture: Cell::new(false),
|
||||||
title_texture: Cell::new(None),
|
title_texture: Cell::new(None),
|
||||||
attention_requests: Default::default(),
|
attention_requests: Default::default(),
|
||||||
|
render_highlight: Default::default(),
|
||||||
});
|
});
|
||||||
*dummy_workspace.output_link.borrow_mut() =
|
*dummy_workspace.output_link.borrow_mut() =
|
||||||
Some(dummy_output.workspaces.add_last(dummy_workspace.clone()));
|
Some(dummy_output.workspaces.add_last(dummy_workspace.clone()));
|
||||||
|
|
|
||||||
|
|
@ -655,7 +655,7 @@ impl ConfigProxyHandler {
|
||||||
fn handle_get_workspace_capture(&self, workspace: Workspace) -> Result<(), CphError> {
|
fn handle_get_workspace_capture(&self, workspace: Workspace) -> Result<(), CphError> {
|
||||||
let name = self.get_workspace(workspace)?;
|
let name = self.get_workspace(workspace)?;
|
||||||
let capture = match self.state.workspaces.get(name.as_str()) {
|
let capture = match self.state.workspaces.get(name.as_str()) {
|
||||||
Some(ws) => ws.capture.get(),
|
Some(ws) => ws.may_capture.get(),
|
||||||
None => self.state.default_workspace_capture.get(),
|
None => self.state.default_workspace_capture.get(),
|
||||||
};
|
};
|
||||||
self.respond(Response::GetWorkspaceCapture { capture });
|
self.respond(Response::GetWorkspaceCapture { capture });
|
||||||
|
|
@ -669,7 +669,8 @@ impl ConfigProxyHandler {
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
let name = self.get_workspace(workspace)?;
|
let name = self.get_workspace(workspace)?;
|
||||||
if let Some(ws) = self.state.workspaces.get(name.as_str()) {
|
if let Some(ws) = self.state.workspaces.get(name.as_str()) {
|
||||||
ws.capture.set(capture);
|
ws.may_capture.set(capture);
|
||||||
|
ws.update_has_captures();
|
||||||
ws.output.get().schedule_update_render_data();
|
ws.output.get().schedule_update_render_data();
|
||||||
self.state.damage();
|
self.state.damage();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ pub mod jay_screencast;
|
||||||
pub mod jay_screenshot;
|
pub mod jay_screenshot;
|
||||||
pub mod jay_seat_events;
|
pub mod jay_seat_events;
|
||||||
pub mod jay_select_toplevel;
|
pub mod jay_select_toplevel;
|
||||||
|
pub mod jay_select_workspace;
|
||||||
pub mod jay_toplevel;
|
pub mod jay_toplevel;
|
||||||
pub mod jay_workspace;
|
pub mod jay_workspace;
|
||||||
pub mod jay_workspace_watcher;
|
pub mod jay_workspace_watcher;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use {
|
||||||
jay_screenshot::JayScreenshot,
|
jay_screenshot::JayScreenshot,
|
||||||
jay_seat_events::JaySeatEvents,
|
jay_seat_events::JaySeatEvents,
|
||||||
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
|
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
|
||||||
|
jay_select_workspace::{JaySelectWorkspace, JayWorkspaceSelector},
|
||||||
jay_workspace_watcher::JayWorkspaceWatcher,
|
jay_workspace_watcher::JayWorkspaceWatcher,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
|
|
@ -85,13 +86,14 @@ pub struct Cap;
|
||||||
impl Cap {
|
impl Cap {
|
||||||
pub const NONE: u16 = 0;
|
pub const NONE: u16 = 0;
|
||||||
pub const WINDOW_CAPTURE: u16 = 1;
|
pub const WINDOW_CAPTURE: u16 = 1;
|
||||||
|
pub const SELECT_WORKSPACE: u16 = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JayCompositor {
|
impl JayCompositor {
|
||||||
fn send_capabilities(&self) {
|
fn send_capabilities(&self) {
|
||||||
self.client.event(Capabilities {
|
self.client.event(Capabilities {
|
||||||
self_id: self.id,
|
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);
|
seat.global.select_toplevel(selector);
|
||||||
Ok(())
|
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! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use {
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
tree::{OutputNode, ToplevelNode, WorkspaceNodeId},
|
tree::{OutputNode, ToplevelNode, WorkspaceNode, WorkspaceNodeId},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||||
errorfmt::ErrorFmt,
|
errorfmt::ErrorFmt,
|
||||||
|
|
@ -106,6 +106,18 @@ struct ScreencastBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JayScreencast {
|
impl JayScreencast {
|
||||||
|
pub fn shows_ws(&self, ws: &WorkspaceNode) -> bool {
|
||||||
|
if self.show_all.get() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for &id in &*self.show_workspaces.borrow() {
|
||||||
|
if id == ws.id {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(id: JayScreencastId, client: &Rc<Client>) -> Self {
|
pub fn new(id: JayScreencastId, client: &Rc<Client>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
|
@ -309,10 +321,7 @@ impl JayScreencast {
|
||||||
if let Some(target) = self.target.take() {
|
if let Some(target) = self.target.take() {
|
||||||
match target {
|
match target {
|
||||||
Target::Output(output) => {
|
Target::Output(output) => {
|
||||||
output.screencasts.remove(&(self.client.id, self.id));
|
output.remove_screencast(self);
|
||||||
if output.screencasts.is_empty() {
|
|
||||||
output.state.damage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Target::Toplevel(tl) => {
|
Target::Toplevel(tl) => {
|
||||||
let data = tl.tl_data();
|
let data = tl.tl_data();
|
||||||
|
|
@ -514,10 +523,7 @@ impl JayScreencastRequestHandler for JayScreencast {
|
||||||
self.do_destroy();
|
self.do_destroy();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
if o.screencasts.is_empty() {
|
o.add_screencast(slf);
|
||||||
o.state.damage();
|
|
||||||
}
|
|
||||||
o.screencasts.set((self.client.id, self.id), slf.clone());
|
|
||||||
new_target = Some(Target::Output(o));
|
new_target = Some(Target::Output(o));
|
||||||
}
|
}
|
||||||
PendingTarget::Toplevel(t) => {
|
PendingTarget::Toplevel(t) => {
|
||||||
|
|
@ -546,11 +552,14 @@ impl JayScreencastRequestHandler for JayScreencast {
|
||||||
need_realloc = true;
|
need_realloc = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut capture_rules_changed = false;
|
||||||
if let Some(show_all) = self.pending.show_all.take() {
|
if let Some(show_all) = self.pending.show_all.take() {
|
||||||
self.show_all.set(show_all);
|
self.show_all.set(show_all);
|
||||||
|
capture_rules_changed = true;
|
||||||
}
|
}
|
||||||
if let Some(new_workspaces) = self.pending.show_workspaces.borrow_mut().take() {
|
if let Some(new_workspaces) = self.pending.show_workspaces.borrow_mut().take() {
|
||||||
*self.show_workspaces.borrow_mut() = new_workspaces;
|
*self.show_workspaces.borrow_mut() = new_workspaces;
|
||||||
|
capture_rules_changed = true;
|
||||||
}
|
}
|
||||||
if let Some(running) = self.pending.running.take() {
|
if let Some(running) = self.pending.running.take() {
|
||||||
self.running.set(running);
|
self.running.set(running);
|
||||||
|
|
@ -560,6 +569,12 @@ impl JayScreencastRequestHandler for JayScreencast {
|
||||||
slf.schedule_realloc();
|
slf.schedule_realloc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if capture_rules_changed {
|
||||||
|
if let Some(Target::Output(o)) = self.target.get() {
|
||||||
|
o.screencast_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
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 {
|
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) {
|
pub fn send_linear_id(&self, ws: &WorkspaceNode) {
|
||||||
self.client.event(LinearId {
|
self.client.event(LinearId {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,7 @@ impl JayWorkspaceWatcher {
|
||||||
id: jw.id,
|
id: jw.id,
|
||||||
linear_id: workspace.id.raw(),
|
linear_id: workspace.id.raw(),
|
||||||
});
|
});
|
||||||
jw.send_linear_id(workspace);
|
jw.send_initial_properties(workspace);
|
||||||
jw.send_name(workspace);
|
|
||||||
jw.send_output(&workspace.output.get());
|
|
||||||
jw.send_visible(workspace.visible.get());
|
|
||||||
jw.send_done();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,14 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
backend,
|
backend,
|
||||||
client::{Client, ClientError, ClientId},
|
client::{Client, ClientError, ClientId},
|
||||||
gfx_api::GfxTexture,
|
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
ifs::{
|
ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1},
|
||||||
wl_buffer::WlBufferStorage, wl_surface::WlSurface,
|
|
||||||
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, zxdg_output_v1::ZxdgOutputV1,
|
|
||||||
},
|
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::{ConnectorData, State},
|
state::{ConnectorData, State},
|
||||||
time::Time,
|
|
||||||
tree::{calculate_logical_size, OutputNode},
|
tree::{calculate_logical_size, OutputNode},
|
||||||
utils::{
|
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, transform_ext::TransformExt},
|
||||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
|
||||||
linkedlist::LinkedList, transform_ext::TransformExt,
|
|
||||||
},
|
|
||||||
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -25,7 +17,6 @@ use {
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::hash_map::Entry,
|
collections::hash_map::Entry,
|
||||||
ops::Deref,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
|
@ -68,8 +59,6 @@ pub struct WlOutputGlobal {
|
||||||
pub width_mm: i32,
|
pub width_mm: i32,
|
||||||
pub height_mm: i32,
|
pub height_mm: i32,
|
||||||
pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>,
|
pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>,
|
||||||
pub unused_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
|
|
||||||
pub pending_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
|
|
||||||
pub destroyed: Cell<bool>,
|
pub destroyed: Cell<bool>,
|
||||||
pub legacy_scale: Cell<u32>,
|
pub legacy_scale: Cell<u32>,
|
||||||
pub persistent: Rc<PersistentOutputState>,
|
pub persistent: Rc<PersistentOutputState>,
|
||||||
|
|
@ -125,8 +114,6 @@ impl WlOutputGlobal {
|
||||||
width_mm,
|
width_mm,
|
||||||
height_mm,
|
height_mm,
|
||||||
bindings: Default::default(),
|
bindings: Default::default(),
|
||||||
unused_captures: Default::default(),
|
|
||||||
pending_captures: Default::default(),
|
|
||||||
destroyed: Cell::new(false),
|
destroyed: Cell::new(false),
|
||||||
legacy_scale: Cell::new(scale.round_up()),
|
legacy_scale: Cell::new(scale.round_up()),
|
||||||
persistent: persistent_state.clone(),
|
persistent: persistent_state.clone(),
|
||||||
|
|
@ -210,88 +197,6 @@ impl WlOutputGlobal {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform_screencopies(
|
|
||||||
&self,
|
|
||||||
tex: &Rc<dyn GfxTexture>,
|
|
||||||
render_hardware_cursors: bool,
|
|
||||||
x_off: i32,
|
|
||||||
y_off: i32,
|
|
||||||
size: Option<(i32, i32)>,
|
|
||||||
) {
|
|
||||||
if self.pending_captures.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let now = Time::now().unwrap();
|
|
||||||
let mut captures = vec![];
|
|
||||||
for capture in self.pending_captures.iter() {
|
|
||||||
captures.push(capture.deref().clone());
|
|
||||||
let wl_buffer = match capture.buffer.take() {
|
|
||||||
Some(b) => b,
|
|
||||||
_ => {
|
|
||||||
log::warn!("Capture frame is pending but has no buffer attached");
|
|
||||||
capture.send_failed();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if wl_buffer.destroyed() {
|
|
||||||
capture.send_failed();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some(WlBufferStorage::Shm { mem, stride }) =
|
|
||||||
wl_buffer.storage.borrow_mut().deref()
|
|
||||||
{
|
|
||||||
let res = self.state.perform_shm_screencopy(
|
|
||||||
tex,
|
|
||||||
self.pos.get(),
|
|
||||||
x_off,
|
|
||||||
y_off,
|
|
||||||
size,
|
|
||||||
&capture,
|
|
||||||
mem,
|
|
||||||
*stride,
|
|
||||||
wl_buffer.format,
|
|
||||||
Transform::None,
|
|
||||||
);
|
|
||||||
if let Err(e) = res {
|
|
||||||
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
|
|
||||||
capture.send_failed();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let fb = match wl_buffer.famebuffer.get() {
|
|
||||||
Some(fb) => fb,
|
|
||||||
_ => {
|
|
||||||
log::warn!("Capture buffer has no framebuffer");
|
|
||||||
capture.send_failed();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let res = self.state.perform_screencopy(
|
|
||||||
tex,
|
|
||||||
&fb,
|
|
||||||
self.pos.get(),
|
|
||||||
render_hardware_cursors,
|
|
||||||
x_off - capture.rect.x1(),
|
|
||||||
y_off - capture.rect.y1(),
|
|
||||||
size,
|
|
||||||
Transform::None,
|
|
||||||
);
|
|
||||||
if let Err(e) = res {
|
|
||||||
log::warn!("Could not perform screencopy: {}", ErrorFmt(e));
|
|
||||||
capture.send_failed();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if capture.with_damage.get() {
|
|
||||||
capture.send_damage();
|
|
||||||
}
|
|
||||||
capture.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _);
|
|
||||||
}
|
|
||||||
for capture in captures {
|
|
||||||
capture.output_link.take();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pixel_size(&self) -> (i32, i32) {
|
pub fn pixel_size(&self) -> (i32, i32) {
|
||||||
let mode = self.mode.get();
|
let mode = self.mode.get();
|
||||||
self.persistent
|
self.persistent
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,10 @@ use {
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::OwnedFd,
|
uapi::OwnedFd,
|
||||||
};
|
};
|
||||||
pub use {event_handling::NodeSeatState, pointer_owner::ToplevelSelector};
|
pub use {
|
||||||
|
event_handling::NodeSeatState,
|
||||||
|
pointer_owner::{ToplevelSelector, WorkspaceSelector},
|
||||||
|
};
|
||||||
|
|
||||||
pub const POINTER: u32 = 1;
|
pub const POINTER: u32 = 1;
|
||||||
const KEYBOARD: u32 = 2;
|
const KEYBOARD: u32 = 2;
|
||||||
|
|
@ -1153,10 +1156,13 @@ impl WlSeatGlobal {
|
||||||
self.forward.set(forward);
|
self.forward.set(forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn select_toplevel(self: &Rc<Self>, selector: impl ToplevelSelector) {
|
pub fn select_toplevel(self: &Rc<Self>, selector: impl ToplevelSelector) {
|
||||||
self.pointer_owner.select_toplevel(self, selector);
|
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);
|
global_base!(WlSeatGlobal, WlSeat, WlSeatError);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use {
|
||||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||||
},
|
},
|
||||||
state::DeviceHandlerData,
|
state::DeviceHandlerData,
|
||||||
tree::{FindTreeUsecase, FoundNode, Node, ToplevelNode},
|
tree::{FindTreeUsecase, FoundNode, Node, ToplevelNode, WorkspaceNode},
|
||||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -33,6 +33,10 @@ pub trait ToplevelSelector: 'static {
|
||||||
fn set(&self, toplevel: Rc<dyn ToplevelNode>);
|
fn set(&self, toplevel: Rc<dyn ToplevelNode>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait WorkspaceSelector: 'static {
|
||||||
|
fn set(&self, ws: Rc<WorkspaceNode>);
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for PointerOwnerHolder {
|
impl Default for PointerOwnerHolder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let default = Rc::new(SimplePointerOwner {
|
let default = Rc::new(SimplePointerOwner {
|
||||||
|
|
@ -157,19 +161,32 @@ impl PointerOwnerHolder {
|
||||||
seat.changes.or_assign(CHANGE_CURSOR_MOVED);
|
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);
|
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() {
|
if let Some(node) = seat.pointer_stack.borrow().last() {
|
||||||
usecase.node_focus(seat, node);
|
usecase.node_focus(seat, node);
|
||||||
}
|
}
|
||||||
self.owner.set(Rc::new(SimplePointerOwner { usecase }));
|
self.owner.set(Rc::new(SimplePointerOwner { usecase }));
|
||||||
seat.trigger_tree_changed();
|
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 {
|
trait PointerOwner {
|
||||||
|
|
@ -221,6 +238,12 @@ struct SelectToplevelUsecase<S: ?Sized> {
|
||||||
selector: S,
|
selector: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SelectWorkspaceUsecase<S: ?Sized> {
|
||||||
|
seat: Weak<WlSeatGlobal>,
|
||||||
|
latest: CloneCell<Option<Rc<WorkspaceNode>>>,
|
||||||
|
selector: S,
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: SimplePointerOwnerUsecase> PointerOwner for SimplePointerOwner<T> {
|
impl<T: SimplePointerOwnerUsecase> PointerOwner for SimplePointerOwner<T> {
|
||||||
fn button(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, button: u32, state: KeyState) {
|
fn button(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, button: u32, state: KeyState) {
|
||||||
if state != KeyState::Pressed {
|
if state != KeyState::Pressed {
|
||||||
|
|
@ -674,8 +697,22 @@ impl SimplePointerOwnerUsecase for DefaultPointerUsecase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: ToplevelSelector + ?Sized> SimplePointerOwnerUsecase for Rc<SelectToplevelUsecase<S>> {
|
trait NodeSelectorUsecase: Sized + 'static {
|
||||||
const FIND_TREE_USECASE: FindTreeUsecase = FindTreeUsecase::SelectToplevel;
|
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;
|
const IS_DEFAULT: bool = false;
|
||||||
|
|
||||||
fn default_button(
|
fn default_button(
|
||||||
|
|
@ -685,17 +722,7 @@ impl<S: ToplevelSelector + ?Sized> SimplePointerOwnerUsecase for Rc<SelectToplev
|
||||||
button: u32,
|
button: u32,
|
||||||
pn: &Rc<dyn Node>,
|
pn: &Rc<dyn Node>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let Some(tl) = pn.clone().node_into_toplevel() else {
|
<U as NodeSelectorUsecase>::default_button(self, spo, seat, button, pn)
|
||||||
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 start_drag(
|
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>) {
|
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 mut damage = false;
|
||||||
let tl = node.clone().node_into_toplevel();
|
let tl = node.clone().node_into_toplevel();
|
||||||
if let Some(tl) = &tl {
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use {
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
utils::linkedlist::LinkedNode,
|
|
||||||
wire::{zwlr_screencopy_frame_v1::*, WlBufferId, ZwlrScreencopyFrameV1Id},
|
wire::{zwlr_screencopy_frame_v1::*, WlBufferId, ZwlrScreencopyFrameV1Id},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, ops::Deref, rc::Rc},
|
std::{cell::Cell, ops::Deref, rc::Rc},
|
||||||
|
|
@ -28,7 +27,6 @@ pub struct ZwlrScreencopyFrameV1 {
|
||||||
pub overlay_cursor: bool,
|
pub overlay_cursor: bool,
|
||||||
pub used: Cell<bool>,
|
pub used: Cell<bool>,
|
||||||
pub with_damage: Cell<bool>,
|
pub with_damage: Cell<bool>,
|
||||||
pub output_link: Cell<Option<LinkedNode<Rc<Self>>>>,
|
|
||||||
pub buffer: Cell<Option<Rc<WlBuffer>>>,
|
pub buffer: Cell<Option<Rc<WlBuffer>>>,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
@ -90,19 +88,16 @@ impl ZwlrScreencopyFrameV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_copy(
|
fn do_copy(
|
||||||
&self,
|
self: &Rc<Self>,
|
||||||
buffer_id: WlBufferId,
|
buffer_id: WlBufferId,
|
||||||
with_damage: bool,
|
with_damage: bool,
|
||||||
) -> Result<(), ZwlrScreencopyFrameV1Error> {
|
) -> Result<(), ZwlrScreencopyFrameV1Error> {
|
||||||
if self.used.replace(true) {
|
if self.used.replace(true) {
|
||||||
return Err(ZwlrScreencopyFrameV1Error::AlreadyUsed);
|
return Err(ZwlrScreencopyFrameV1Error::AlreadyUsed);
|
||||||
}
|
}
|
||||||
let link = match self.output_link.take() {
|
let Some(node) = self.output.node.get() else {
|
||||||
Some(l) => l,
|
self.send_failed();
|
||||||
_ => {
|
return Ok(());
|
||||||
self.send_failed();
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let buffer = self.client.lookup(buffer_id)?;
|
let buffer = self.client.lookup(buffer_id)?;
|
||||||
if (buffer.rect.width(), buffer.rect.height()) != (self.rect.width(), self.rect.height()) {
|
if (buffer.rect.width(), buffer.rect.height()) != (self.rect.width(), self.rect.height()) {
|
||||||
|
|
@ -122,27 +117,35 @@ impl ZwlrScreencopyFrameV1 {
|
||||||
self.output.connector.connector.damage();
|
self.output.connector.connector.damage();
|
||||||
}
|
}
|
||||||
self.with_damage.set(with_damage);
|
self.with_damage.set(with_damage);
|
||||||
self.output.pending_captures.add_last_existing(&link);
|
node.screencopies
|
||||||
self.output_link.set(Some(link));
|
.set((self.client.id, self.id), self.clone());
|
||||||
|
node.screencast_changed();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn detach(&self) {
|
||||||
|
if let Some(node) = self.output.node.get() {
|
||||||
|
node.screencopies.remove(&(self.client.id, self.id));
|
||||||
|
node.screencast_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZwlrScreencopyFrameV1RequestHandler for ZwlrScreencopyFrameV1 {
|
impl ZwlrScreencopyFrameV1RequestHandler for ZwlrScreencopyFrameV1 {
|
||||||
type Error = ZwlrScreencopyFrameV1Error;
|
type Error = ZwlrScreencopyFrameV1Error;
|
||||||
|
|
||||||
fn copy(&self, req: Copy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn copy(&self, req: Copy, slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.do_copy(req.buffer, false)
|
slf.do_copy(req.buffer, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.detach();
|
||||||
self.client.remove_obj(self)?;
|
self.client.remove_obj(self)?;
|
||||||
self.output_link.take();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_with_damage(&self, req: CopyWithDamage, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn copy_with_damage(&self, req: CopyWithDamage, slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.do_copy(req.buffer, true)
|
slf.do_copy(req.buffer, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,7 +158,7 @@ simple_add_obj!(ZwlrScreencopyFrameV1);
|
||||||
|
|
||||||
impl Object for ZwlrScreencopyFrameV1 {
|
impl Object for ZwlrScreencopyFrameV1 {
|
||||||
fn break_loops(&self) {
|
fn break_loops(&self) {
|
||||||
self.output_link.take();
|
self.detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,6 @@ impl ZwlrScreencopyManagerV1 {
|
||||||
overlay_cursor,
|
overlay_cursor,
|
||||||
used: Cell::new(false),
|
used: Cell::new(false),
|
||||||
with_damage: Cell::new(false),
|
with_damage: Cell::new(false),
|
||||||
output_link: Cell::new(None),
|
|
||||||
buffer: Cell::new(None),
|
buffer: Cell::new(None),
|
||||||
version: self.version,
|
version: self.version,
|
||||||
});
|
});
|
||||||
|
|
@ -136,9 +135,6 @@ impl ZwlrScreencopyManagerV1 {
|
||||||
frame.send_linux_dmabuf();
|
frame.send_linux_dmabuf();
|
||||||
frame.send_buffer_done();
|
frame.send_buffer_done();
|
||||||
}
|
}
|
||||||
frame
|
|
||||||
.output_link
|
|
||||||
.set(Some(output.global.unused_captures.add_last(frame.clone())));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -297,7 +297,7 @@ fn finish_display_connect(dpy: Rc<PortalDisplayPrelude>) {
|
||||||
id: dpy.con.id(),
|
id: dpy.con.id(),
|
||||||
con: dpy.con.clone(),
|
con: dpy.con.clone(),
|
||||||
owner: Default::default(),
|
owner: Default::default(),
|
||||||
window_capture: Cell::new(false),
|
caps: Default::default(),
|
||||||
});
|
});
|
||||||
dpy.con.add_object(jc.clone());
|
dpy.con.add_object(jc.clone());
|
||||||
dpy.registry.request_bind(name, version, jc.deref());
|
dpy.registry.request_bind(name, version, jc.deref());
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ use {
|
||||||
wl_usr::usr_ifs::{
|
wl_usr::usr_ifs::{
|
||||||
usr_jay_screencast::{UsrJayScreencast, UsrJayScreencastOwner},
|
usr_jay_screencast::{UsrJayScreencast, UsrJayScreencastOwner},
|
||||||
usr_jay_select_toplevel::UsrJaySelectToplevel,
|
usr_jay_select_toplevel::UsrJaySelectToplevel,
|
||||||
|
usr_jay_select_workspace::UsrJaySelectWorkspace,
|
||||||
usr_jay_toplevel::UsrJayToplevel,
|
usr_jay_toplevel::UsrJayToplevel,
|
||||||
|
usr_jay_workspace::UsrJayWorkspace,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -64,6 +66,7 @@ pub enum ScreencastPhase {
|
||||||
SourcesSelected,
|
SourcesSelected,
|
||||||
Selecting(Rc<SelectingScreencast>),
|
Selecting(Rc<SelectingScreencast>),
|
||||||
SelectingWindow(Rc<SelectingWindowScreencast>),
|
SelectingWindow(Rc<SelectingWindowScreencast>),
|
||||||
|
SelectingWorkspace(Rc<SelectingWorkspaceScreencast>),
|
||||||
Starting(Rc<StartingScreencast>),
|
Starting(Rc<StartingScreencast>),
|
||||||
Started(Rc<StartedScreencast>),
|
Started(Rc<StartedScreencast>),
|
||||||
Terminated,
|
Terminated,
|
||||||
|
|
@ -89,6 +92,12 @@ pub struct SelectingWindowScreencast {
|
||||||
pub selector: Rc<UsrJaySelectToplevel>,
|
pub selector: Rc<UsrJaySelectToplevel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SelectingWorkspaceScreencast {
|
||||||
|
pub core: SelectingScreencastCore,
|
||||||
|
pub dpy: Rc<PortalDisplay>,
|
||||||
|
pub selector: Rc<UsrJaySelectWorkspace>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct StartingScreencast {
|
pub struct StartingScreencast {
|
||||||
pub session: Rc<ScreencastSession>,
|
pub session: Rc<ScreencastSession>,
|
||||||
pub request_obj: Rc<DbusObject>,
|
pub request_obj: Rc<DbusObject>,
|
||||||
|
|
@ -100,6 +109,7 @@ pub struct StartingScreencast {
|
||||||
|
|
||||||
pub enum ScreencastTarget {
|
pub enum ScreencastTarget {
|
||||||
Output(Rc<PortalOutput>),
|
Output(Rc<PortalOutput>),
|
||||||
|
Workspace(Rc<PortalOutput>, Rc<UsrJayWorkspace>),
|
||||||
Toplevel(Rc<UsrJayToplevel>),
|
Toplevel(Rc<UsrJayToplevel>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,14 +166,26 @@ impl PwClientNodeOwner for StartingScreencast {
|
||||||
port.supported_metas.set(SUPPORTED_META_VIDEO_CROP);
|
port.supported_metas.set(SUPPORTED_META_VIDEO_CROP);
|
||||||
let jsc = self.dpy.jc.create_screencast();
|
let jsc = self.dpy.jc.create_screencast();
|
||||||
match &self.target {
|
match &self.target {
|
||||||
ScreencastTarget::Output(o) => jsc.set_output(&o.jay),
|
ScreencastTarget::Output(o) => {
|
||||||
|
jsc.set_output(&o.jay);
|
||||||
|
jsc.set_allow_all_workspaces(true);
|
||||||
|
}
|
||||||
|
ScreencastTarget::Workspace(o, ws) => {
|
||||||
|
jsc.set_output(&o.jay);
|
||||||
|
jsc.allow_workspace(ws);
|
||||||
|
}
|
||||||
ScreencastTarget::Toplevel(t) => jsc.set_toplevel(t),
|
ScreencastTarget::Toplevel(t) => jsc.set_toplevel(t),
|
||||||
}
|
}
|
||||||
jsc.set_use_linear_buffers(true);
|
jsc.set_use_linear_buffers(true);
|
||||||
jsc.set_allow_all_workspaces(true);
|
|
||||||
jsc.configure();
|
jsc.configure();
|
||||||
if let ScreencastTarget::Toplevel(t) = &self.target {
|
match &self.target {
|
||||||
self.dpy.con.remove_obj(&**t);
|
ScreencastTarget::Output(_) => {}
|
||||||
|
ScreencastTarget::Workspace(_, w) => {
|
||||||
|
self.dpy.con.remove_obj(&**w);
|
||||||
|
}
|
||||||
|
ScreencastTarget::Toplevel(t) => {
|
||||||
|
self.dpy.con.remove_obj(&**t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let started = Rc::new(StartedScreencast {
|
let started = Rc::new(StartedScreencast {
|
||||||
session: self.session.clone(),
|
session: self.session.clone(),
|
||||||
|
|
@ -253,12 +275,22 @@ impl ScreencastSession {
|
||||||
s.dpy.con.remove_obj(&*s.selector);
|
s.dpy.con.remove_obj(&*s.selector);
|
||||||
s.core.reply.err("Session has been terminated");
|
s.core.reply.err("Session has been terminated");
|
||||||
}
|
}
|
||||||
|
ScreencastPhase::SelectingWorkspace(s) => {
|
||||||
|
s.dpy.con.remove_obj(&*s.selector);
|
||||||
|
s.core.reply.err("Session has been terminated");
|
||||||
|
}
|
||||||
ScreencastPhase::Starting(s) => {
|
ScreencastPhase::Starting(s) => {
|
||||||
s.reply.err("Session has been terminated");
|
s.reply.err("Session has been terminated");
|
||||||
s.node.con.destroy_obj(s.node.deref());
|
s.node.con.destroy_obj(s.node.deref());
|
||||||
s.dpy.screencasts.remove(self.session_obj.path());
|
s.dpy.screencasts.remove(self.session_obj.path());
|
||||||
if let ScreencastTarget::Toplevel(t) = &s.target {
|
match &s.target {
|
||||||
s.dpy.con.remove_obj(&**t);
|
ScreencastTarget::Output(_) => {}
|
||||||
|
ScreencastTarget::Workspace(_, w) => {
|
||||||
|
s.dpy.con.remove_obj(&**w);
|
||||||
|
}
|
||||||
|
ScreencastTarget::Toplevel(t) => {
|
||||||
|
s.dpy.con.remove_obj(&**t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ScreencastPhase::Started(s) => {
|
ScreencastPhase::Started(s) => {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use {
|
||||||
ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
||||||
ptl_screencast::{
|
ptl_screencast::{
|
||||||
ScreencastPhase, ScreencastSession, ScreencastTarget, SelectingWindowScreencast,
|
ScreencastPhase, ScreencastSession, ScreencastTarget, SelectingWindowScreencast,
|
||||||
|
SelectingWorkspaceScreencast,
|
||||||
},
|
},
|
||||||
ptr_gui::{
|
ptr_gui::{
|
||||||
Align, Button, ButtonOwner, Flow, GuiElement, Label, Orientation, OverlayWindow,
|
Align, Button, ButtonOwner, Flow, GuiElement, Label, Orientation, OverlayWindow,
|
||||||
|
|
@ -14,7 +15,9 @@ use {
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::copyhashmap::CopyHashMap,
|
utils::copyhashmap::CopyHashMap,
|
||||||
wl_usr::usr_ifs::{
|
wl_usr::usr_ifs::{
|
||||||
usr_jay_select_toplevel::UsrJaySelectToplevelOwner, usr_jay_toplevel::UsrJayToplevel,
|
usr_jay_select_toplevel::UsrJaySelectToplevelOwner,
|
||||||
|
usr_jay_select_workspace::UsrJaySelectWorkspaceOwner, usr_jay_toplevel::UsrJayToplevel,
|
||||||
|
usr_jay_workspace::UsrJayWorkspace,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
|
|
@ -43,7 +46,8 @@ struct StaticButton {
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
enum ButtonRole {
|
enum ButtonRole {
|
||||||
Accept,
|
Accept,
|
||||||
Window,
|
SelectWorkspace,
|
||||||
|
SelectWindow,
|
||||||
Reject,
|
Reject,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,14 +75,20 @@ fn create_accept_gui(surface: &Rc<SelectionGuiSurface>) -> Rc<dyn GuiElement> {
|
||||||
let label = Rc::new(Label::default());
|
let label = Rc::new(Label::default());
|
||||||
*label.text.borrow_mut() = text;
|
*label.text.borrow_mut() = text;
|
||||||
let accept_button = static_button(surface, ButtonRole::Accept, "Share This Output");
|
let accept_button = static_button(surface, ButtonRole::Accept, "Share This Output");
|
||||||
let window_button = static_button(surface, ButtonRole::Window, "Share A Window");
|
let workspace_button = static_button(surface, ButtonRole::SelectWorkspace, "Share A Workspcae");
|
||||||
|
let window_button = static_button(surface, ButtonRole::SelectWindow, "Share A Window");
|
||||||
let reject_button = static_button(surface, ButtonRole::Reject, "Reject");
|
let reject_button = static_button(surface, ButtonRole::Reject, "Reject");
|
||||||
for button in [&accept_button, &window_button, &reject_button] {
|
for button in [
|
||||||
|
&accept_button,
|
||||||
|
&workspace_button,
|
||||||
|
&window_button,
|
||||||
|
&reject_button,
|
||||||
|
] {
|
||||||
button.border_color.set(Color::from_gray(100));
|
button.border_color.set(Color::from_gray(100));
|
||||||
button.border.set(2.0);
|
button.border.set(2.0);
|
||||||
button.padding.set(5.0);
|
button.padding.set(5.0);
|
||||||
}
|
}
|
||||||
for button in [&accept_button, &window_button] {
|
for button in [&accept_button, &workspace_button, &window_button] {
|
||||||
button.bg_color.set(Color::from_rgb(170, 200, 170));
|
button.bg_color.set(Color::from_rgb(170, 200, 170));
|
||||||
button.bg_hover_color.set(Color::from_rgb(170, 255, 170));
|
button.bg_hover_color.set(Color::from_rgb(170, 255, 170));
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +102,10 @@ fn create_accept_gui(surface: &Rc<SelectionGuiSurface>) -> Rc<dyn GuiElement> {
|
||||||
flow.in_margin.set(V_MARGIN);
|
flow.in_margin.set(V_MARGIN);
|
||||||
flow.cross_margin.set(H_MARGIN);
|
flow.cross_margin.set(H_MARGIN);
|
||||||
let mut elements: Vec<Rc<dyn GuiElement>> = vec![label, accept_button];
|
let mut elements: Vec<Rc<dyn GuiElement>> = vec![label, accept_button];
|
||||||
if surface.gui.dpy.jc.window_capture.get() {
|
if surface.gui.dpy.jc.caps.select_workspace.get() {
|
||||||
|
elements.push(workspace_button);
|
||||||
|
}
|
||||||
|
if surface.gui.dpy.jc.caps.window_capture.get() {
|
||||||
elements.push(window_button);
|
elements.push(window_button);
|
||||||
}
|
}
|
||||||
elements.push(reject_button);
|
elements.push(reject_button);
|
||||||
|
|
@ -140,7 +153,7 @@ impl ButtonOwner for StaticButton {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match self.role {
|
match self.role {
|
||||||
ButtonRole::Accept | ButtonRole::Window => {
|
ButtonRole::Accept | ButtonRole::SelectWorkspace | ButtonRole::SelectWindow => {
|
||||||
log::info!("User has accepted the request");
|
log::info!("User has accepted the request");
|
||||||
let selecting = match self.surface.gui.screencast_session.phase.get() {
|
let selecting = match self.surface.gui.screencast_session.phase.get() {
|
||||||
ScreencastPhase::Selecting(selecting) => selecting,
|
ScreencastPhase::Selecting(selecting) => selecting,
|
||||||
|
|
@ -154,6 +167,19 @@ impl ButtonOwner for StaticButton {
|
||||||
selecting
|
selecting
|
||||||
.core
|
.core
|
||||||
.starting(dpy, ScreencastTarget::Output(self.surface.output.clone()));
|
.starting(dpy, ScreencastTarget::Output(self.surface.output.clone()));
|
||||||
|
} else if self.role == ButtonRole::SelectWorkspace {
|
||||||
|
let selector = dpy.jc.select_workspace(&seat.wl);
|
||||||
|
let selecting = Rc::new(SelectingWorkspaceScreencast {
|
||||||
|
core: selecting.core.clone(),
|
||||||
|
dpy: dpy.clone(),
|
||||||
|
selector: selector.clone(),
|
||||||
|
});
|
||||||
|
selector.owner.set(Some(selecting.clone()));
|
||||||
|
self.surface
|
||||||
|
.gui
|
||||||
|
.screencast_session
|
||||||
|
.phase
|
||||||
|
.set(ScreencastPhase::SelectingWorkspace(selecting));
|
||||||
} else {
|
} else {
|
||||||
let selector = dpy.jc.select_toplevel(&seat.wl);
|
let selector = dpy.jc.select_toplevel(&seat.wl);
|
||||||
let selecting = Rc::new(SelectingWindowScreencast {
|
let selecting = Rc::new(SelectingWindowScreencast {
|
||||||
|
|
@ -199,6 +225,37 @@ impl UsrJaySelectToplevelOwner for SelectingWindowScreencast {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UsrJaySelectWorkspaceOwner for SelectingWorkspaceScreencast {
|
||||||
|
fn done(&self, output: u32, ws: Option<Rc<UsrJayWorkspace>>) {
|
||||||
|
let Some(ws) = ws else {
|
||||||
|
log::info!("User has aborted the selection");
|
||||||
|
self.core.session.kill();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
match self.core.session.phase.get() {
|
||||||
|
ScreencastPhase::SelectingWorkspace(s) => {
|
||||||
|
self.dpy.con.remove_obj(&*s.selector);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.dpy.con.remove_obj(&*ws);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("User has selected a workspace");
|
||||||
|
let output = match self.dpy.outputs.get(&output) {
|
||||||
|
Some(o) => o,
|
||||||
|
_ => {
|
||||||
|
log::warn!("Workspace does not belong to any known output");
|
||||||
|
self.dpy.con.remove_obj(&*ws);
|
||||||
|
self.core.session.kill();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.core
|
||||||
|
.starting(&self.dpy, ScreencastTarget::Workspace(output, ws));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn static_button(surface: &Rc<SelectionGuiSurface>, role: ButtonRole, text: &str) -> Rc<Button> {
|
fn static_button(surface: &Rc<SelectionGuiSurface>, role: ButtonRole, text: &str) -> Rc<Button> {
|
||||||
let button = Rc::new(Button::default());
|
let button = Rc::new(Button::default());
|
||||||
let slf = Rc::new(StaticButton {
|
let slf = Rc::new(StaticButton {
|
||||||
|
|
|
||||||
|
|
@ -117,13 +117,13 @@ impl Renderer<'_> {
|
||||||
if let Some(ws) = output.workspace.get() {
|
if let Some(ws) = output.workspace.get() {
|
||||||
fullscreen = ws.fullscreen.get();
|
fullscreen = ws.fullscreen.get();
|
||||||
}
|
}
|
||||||
|
let theme = &self.state.theme;
|
||||||
|
let th = theme.sizes.title_height.get();
|
||||||
if let Some(fs) = fullscreen {
|
if let Some(fs) = fullscreen {
|
||||||
fs.tl_as_node().node_render(self, x, y, None);
|
fs.tl_as_node().node_render(self, x, y, None);
|
||||||
} else {
|
} else {
|
||||||
render_layer!(output.layers[0]);
|
render_layer!(output.layers[0]);
|
||||||
render_layer!(output.layers[1]);
|
render_layer!(output.layers[1]);
|
||||||
let theme = &self.state.theme;
|
|
||||||
let th = theme.sizes.title_height.get();
|
|
||||||
{
|
{
|
||||||
let c = theme.colors.bar_background.get();
|
let c = theme.colors.bar_background.get();
|
||||||
self.base.fill_boxes2(
|
self.base.fill_boxes2(
|
||||||
|
|
@ -132,11 +132,9 @@ impl Renderer<'_> {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
);
|
);
|
||||||
let has_captures =
|
|
||||||
!output.screencasts.is_empty() || !output.global.pending_captures.is_empty();
|
|
||||||
let rd = output.render_data.borrow_mut();
|
let rd = output.render_data.borrow_mut();
|
||||||
if let Some(aw) = &rd.active_workspace {
|
if let Some(aw) = &rd.active_workspace {
|
||||||
let c = match has_captures && aw.captured {
|
let c = match aw.captured {
|
||||||
true => theme.colors.captured_focused_title_background.get(),
|
true => theme.colors.captured_focused_title_background.get(),
|
||||||
false => theme.colors.focused_title_background.get(),
|
false => theme.colors.focused_title_background.get(),
|
||||||
};
|
};
|
||||||
|
|
@ -147,10 +145,7 @@ impl Renderer<'_> {
|
||||||
.fill_boxes2(slice::from_ref(&rd.underline), &c, x, y);
|
.fill_boxes2(slice::from_ref(&rd.underline), &c, x, y);
|
||||||
let c = theme.colors.unfocused_title_background.get();
|
let c = theme.colors.unfocused_title_background.get();
|
||||||
self.base.fill_boxes2(&rd.inactive_workspaces, &c, x, y);
|
self.base.fill_boxes2(&rd.inactive_workspaces, &c, x, y);
|
||||||
let c = match has_captures {
|
let c = theme.colors.captured_unfocused_title_background.get();
|
||||||
true => theme.colors.captured_unfocused_title_background.get(),
|
|
||||||
false => theme.colors.unfocused_title_background.get(),
|
|
||||||
};
|
|
||||||
self.base
|
self.base
|
||||||
.fill_boxes2(&rd.captured_inactive_workspaces, &c, x, y);
|
.fill_boxes2(&rd.captured_inactive_workspaces, &c, x, y);
|
||||||
let c = theme.colors.attention_requested_background.get();
|
let c = theme.colors.attention_requested_background.get();
|
||||||
|
|
@ -206,6 +201,13 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
render_layer!(output.layers[2]);
|
render_layer!(output.layers[2]);
|
||||||
render_layer!(output.layers[3]);
|
render_layer!(output.layers[3]);
|
||||||
|
if let Some(ws) = output.workspace.get() {
|
||||||
|
if ws.render_highlight.get() > 0 {
|
||||||
|
let color = self.state.theme.colors.highlight.get();
|
||||||
|
let bounds = ws.position.get().at_point(x, y + th + 1);
|
||||||
|
self.base.fill_boxes(&[bounds], &color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_workspace(&mut self, workspace: &WorkspaceNode, x: i32, y: i32) {
|
pub fn render_workspace(&mut self, workspace: &WorkspaceNode, x: i32, y: i32) {
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@ impl ConnectorHandler {
|
||||||
screencasts: Default::default(),
|
screencasts: Default::default(),
|
||||||
update_render_data_scheduled: Cell::new(false),
|
update_render_data_scheduled: Cell::new(false),
|
||||||
hardware_cursor_needs_render: Cell::new(false),
|
hardware_cursor_needs_render: Cell::new(false),
|
||||||
|
screencopies: Default::default(),
|
||||||
});
|
});
|
||||||
self.state
|
self.state
|
||||||
.add_output_scale(on.global.persistent.scale.get());
|
.add_output_scale(on.global.persistent.scale.get());
|
||||||
|
|
@ -235,6 +236,9 @@ impl ConnectorHandler {
|
||||||
for sc in screencasts {
|
for sc in screencasts {
|
||||||
sc.do_destroy();
|
sc.do_destroy();
|
||||||
}
|
}
|
||||||
|
for (_, sc) in on.screencopies.lock().drain() {
|
||||||
|
sc.send_failed();
|
||||||
|
}
|
||||||
global.destroyed.set(true);
|
global.destroyed.set(true);
|
||||||
self.state.root.outputs.remove(&self.id);
|
self.state.root.outputs.remove(&self.id);
|
||||||
self.state.root.update_extents();
|
self.state.root.update_extents();
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ impl FindTreeResult {
|
||||||
pub enum FindTreeUsecase {
|
pub enum FindTreeUsecase {
|
||||||
None,
|
None,
|
||||||
SelectToplevel,
|
SelectToplevel,
|
||||||
|
SelectWorkspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Node: 'static {
|
pub trait Node: 'static {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use {
|
||||||
ifs::{
|
ifs::{
|
||||||
jay_output::JayOutput,
|
jay_output::JayOutput,
|
||||||
jay_screencast::JayScreencast,
|
jay_screencast::JayScreencast,
|
||||||
|
wl_buffer::WlBufferStorage,
|
||||||
wl_output::WlOutputGlobal,
|
wl_output::WlOutputGlobal,
|
||||||
wl_seat::{
|
wl_seat::{
|
||||||
collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, WlSeatGlobal,
|
collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, WlSeatGlobal,
|
||||||
|
|
@ -19,12 +20,14 @@ use {
|
||||||
SurfaceSendPreferredTransformVisitor,
|
SurfaceSendPreferredTransformVisitor,
|
||||||
},
|
},
|
||||||
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
|
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
|
||||||
|
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
text::{self, TextTexture},
|
text::{self, TextTexture},
|
||||||
|
time::Time,
|
||||||
tree::{
|
tree::{
|
||||||
walker::NodeVisitor, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node,
|
walker::NodeVisitor, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node,
|
||||||
NodeId, WorkspaceNode,
|
NodeId, WorkspaceNode,
|
||||||
|
|
@ -33,7 +36,7 @@ use {
|
||||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||||
linkedlist::LinkedList, scroller::Scroller, transform_ext::TransformExt,
|
linkedlist::LinkedList, scroller::Scroller, transform_ext::TransformExt,
|
||||||
},
|
},
|
||||||
wire::{JayOutputId, JayScreencastId},
|
wire::{JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
jay_config::video::Transform,
|
jay_config::video::Transform,
|
||||||
|
|
@ -66,6 +69,7 @@ pub struct OutputNode {
|
||||||
pub hardware_cursor_needs_render: Cell<bool>,
|
pub hardware_cursor_needs_render: Cell<bool>,
|
||||||
pub update_render_data_scheduled: Cell<bool>,
|
pub update_render_data_scheduled: Cell<bool>,
|
||||||
pub screencasts: CopyHashMap<(ClientId, JayScreencastId), Rc<JayScreencast>>,
|
pub screencasts: CopyHashMap<(ClientId, JayScreencastId), Rc<JayScreencast>>,
|
||||||
|
pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc<ZwlrScreencopyFrameV1>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn output_render_data(state: Rc<State>) {
|
pub async fn output_render_data(state: Rc<State>) {
|
||||||
|
|
@ -81,6 +85,22 @@ pub async fn output_render_data(state: Rc<State>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputNode {
|
impl OutputNode {
|
||||||
|
pub fn add_screencast(&self, sc: &Rc<JayScreencast>) {
|
||||||
|
self.screencasts.set((sc.client.id, sc.id), sc.clone());
|
||||||
|
self.screencast_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_screencast(&self, sc: &JayScreencast) {
|
||||||
|
self.screencasts.remove(&(sc.client.id, sc.id));
|
||||||
|
self.screencast_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn screencast_changed(&self) {
|
||||||
|
for ws in self.workspaces.iter() {
|
||||||
|
ws.update_has_captures();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn perform_screencopies(
|
pub fn perform_screencopies(
|
||||||
&self,
|
&self,
|
||||||
tex: &Rc<dyn GfxTexture>,
|
tex: &Rc<dyn GfxTexture>,
|
||||||
|
|
@ -90,17 +110,94 @@ impl OutputNode {
|
||||||
size: Option<(i32, i32)>,
|
size: Option<(i32, i32)>,
|
||||||
) {
|
) {
|
||||||
if let Some(workspace) = self.workspace.get() {
|
if let Some(workspace) = self.workspace.get() {
|
||||||
if !workspace.capture.get() {
|
if !workspace.may_capture.get() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.global
|
self.perform_wlr_screencopies(tex, render_hardware_cursor, x_off, y_off, size);
|
||||||
.perform_screencopies(tex, render_hardware_cursor, x_off, y_off, size);
|
|
||||||
for sc in self.screencasts.lock().values() {
|
for sc in self.screencasts.lock().values() {
|
||||||
sc.copy_texture(self, tex, render_hardware_cursor, x_off, y_off, size);
|
sc.copy_texture(self, tex, render_hardware_cursor, x_off, y_off, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn perform_wlr_screencopies(
|
||||||
|
&self,
|
||||||
|
tex: &Rc<dyn GfxTexture>,
|
||||||
|
render_hardware_cursors: bool,
|
||||||
|
x_off: i32,
|
||||||
|
y_off: i32,
|
||||||
|
size: Option<(i32, i32)>,
|
||||||
|
) {
|
||||||
|
if self.screencopies.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let now = Time::now().unwrap();
|
||||||
|
for (_, capture) in self.screencopies.lock().drain() {
|
||||||
|
let wl_buffer = match capture.buffer.take() {
|
||||||
|
Some(b) => b,
|
||||||
|
_ => {
|
||||||
|
log::warn!("Capture frame is pending but has no buffer attached");
|
||||||
|
capture.send_failed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if wl_buffer.destroyed() {
|
||||||
|
capture.send_failed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(WlBufferStorage::Shm { mem, stride }) =
|
||||||
|
wl_buffer.storage.borrow_mut().deref()
|
||||||
|
{
|
||||||
|
let res = self.state.perform_shm_screencopy(
|
||||||
|
tex,
|
||||||
|
self.global.pos.get(),
|
||||||
|
x_off,
|
||||||
|
y_off,
|
||||||
|
size,
|
||||||
|
&capture,
|
||||||
|
mem,
|
||||||
|
*stride,
|
||||||
|
wl_buffer.format,
|
||||||
|
Transform::None,
|
||||||
|
);
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
|
||||||
|
capture.send_failed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let fb = match wl_buffer.famebuffer.get() {
|
||||||
|
Some(fb) => fb,
|
||||||
|
_ => {
|
||||||
|
log::warn!("Capture buffer has no framebuffer");
|
||||||
|
capture.send_failed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let res = self.state.perform_screencopy(
|
||||||
|
tex,
|
||||||
|
&fb,
|
||||||
|
self.global.pos.get(),
|
||||||
|
render_hardware_cursors,
|
||||||
|
x_off - capture.rect.x1(),
|
||||||
|
y_off - capture.rect.y1(),
|
||||||
|
size,
|
||||||
|
Transform::None,
|
||||||
|
);
|
||||||
|
if let Err(e) = res {
|
||||||
|
log::warn!("Could not perform screencopy: {}", ErrorFmt(e));
|
||||||
|
capture.send_failed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if capture.with_damage.get() {
|
||||||
|
capture.send_damage();
|
||||||
|
}
|
||||||
|
capture.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _);
|
||||||
|
}
|
||||||
|
self.screencast_changed();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
self.global.clear();
|
self.global.clear();
|
||||||
self.workspace.set(None);
|
self.workspace.set(None);
|
||||||
|
|
@ -111,6 +208,8 @@ impl OutputNode {
|
||||||
self.render_data.borrow_mut().titles.clear();
|
self.render_data.borrow_mut().titles.clear();
|
||||||
self.lock_surface.take();
|
self.lock_surface.take();
|
||||||
self.jay_outputs.clear();
|
self.jay_outputs.clear();
|
||||||
|
self.screencasts.clear();
|
||||||
|
self.screencopies.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_spaces_changed(self: &Rc<Self>) {
|
pub fn on_spaces_changed(self: &Rc<Self>) {
|
||||||
|
|
@ -228,13 +327,13 @@ impl OutputNode {
|
||||||
if Some(ws.id) == active_id {
|
if Some(ws.id) == active_id {
|
||||||
rd.active_workspace = Some(OutputWorkspaceRenderData {
|
rd.active_workspace = Some(OutputWorkspaceRenderData {
|
||||||
rect,
|
rect,
|
||||||
captured: ws.capture.get(),
|
captured: ws.has_capture.get(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if ws.attention_requests.active() {
|
if ws.attention_requests.active() {
|
||||||
rd.attention_requested_workspaces.push(rect);
|
rd.attention_requested_workspaces.push(rect);
|
||||||
}
|
}
|
||||||
if ws.capture.get() {
|
if ws.has_capture.get() {
|
||||||
rd.captured_inactive_workspaces.push(rect);
|
rd.captured_inactive_workspaces.push(rect);
|
||||||
} else {
|
} else {
|
||||||
rd.inactive_workspaces.push(rect);
|
rd.inactive_workspaces.push(rect);
|
||||||
|
|
@ -345,10 +444,13 @@ impl OutputNode {
|
||||||
visible_on_desired_output: Cell::new(false),
|
visible_on_desired_output: Cell::new(false),
|
||||||
desired_output: CloneCell::new(self.global.output_id.clone()),
|
desired_output: CloneCell::new(self.global.output_id.clone()),
|
||||||
jay_workspaces: Default::default(),
|
jay_workspaces: Default::default(),
|
||||||
capture: self.state.default_workspace_capture.clone(),
|
may_capture: self.state.default_workspace_capture.clone(),
|
||||||
|
has_capture: Cell::new(false),
|
||||||
title_texture: Default::default(),
|
title_texture: Default::default(),
|
||||||
attention_requests: Default::default(),
|
attention_requests: Default::default(),
|
||||||
|
render_highlight: Default::default(),
|
||||||
});
|
});
|
||||||
|
ws.update_has_captures();
|
||||||
*ws.output_link.borrow_mut() = Some(self.workspaces.add_last(ws.clone()));
|
*ws.output_link.borrow_mut() = Some(self.workspaces.add_last(ws.clone()));
|
||||||
self.state.workspaces.set(name.to_string(), ws.clone());
|
self.state.workspaces.set(name.to_string(), ws.clone());
|
||||||
if self.workspace.is_none() {
|
if self.workspace.is_none() {
|
||||||
|
|
@ -642,6 +744,20 @@ impl Node for OutputNode {
|
||||||
}
|
}
|
||||||
return FindTreeResult::AcceptsInput;
|
return FindTreeResult::AcceptsInput;
|
||||||
}
|
}
|
||||||
|
let bar_height = self.state.theme.sizes.title_height.get() + 1;
|
||||||
|
if usecase == FindTreeUsecase::SelectWorkspace {
|
||||||
|
if y >= bar_height {
|
||||||
|
y -= bar_height;
|
||||||
|
if let Some(ws) = self.workspace.get() {
|
||||||
|
tree.push(FoundNode {
|
||||||
|
node: ws.clone(),
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
return FindTreeResult::AcceptsInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
let res = self.find_layer_surface_at(x, y, &[OVERLAY, TOP], tree, usecase);
|
let res = self.find_layer_surface_at(x, y, &[OVERLAY, TOP], tree, usecase);
|
||||||
if res.accepts_input() {
|
if res.accepts_input() {
|
||||||
|
|
@ -690,7 +806,6 @@ impl Node for OutputNode {
|
||||||
});
|
});
|
||||||
fs.tl_as_node().node_find_tree_at(x, y, tree, usecase)
|
fs.tl_as_node().node_find_tree_at(x, y, tree, usecase)
|
||||||
} else {
|
} else {
|
||||||
let bar_height = self.state.theme.sizes.title_height.get() + 1;
|
|
||||||
if y >= bar_height {
|
if y >= bar_height {
|
||||||
y -= bar_height;
|
y -= bar_height;
|
||||||
let len = tree.len();
|
let len = tree.len();
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use {
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||||
|
numcell::NumCell,
|
||||||
threshold_counter::ThresholdCounter,
|
threshold_counter::ThresholdCounter,
|
||||||
},
|
},
|
||||||
wire::JayWorkspaceId,
|
wire::JayWorkspaceId,
|
||||||
|
|
@ -49,9 +50,11 @@ pub struct WorkspaceNode {
|
||||||
pub visible_on_desired_output: Cell<bool>,
|
pub visible_on_desired_output: Cell<bool>,
|
||||||
pub desired_output: CloneCell<Rc<OutputId>>,
|
pub desired_output: CloneCell<Rc<OutputId>>,
|
||||||
pub jay_workspaces: CopyHashMap<(ClientId, JayWorkspaceId), Rc<JayWorkspace>>,
|
pub jay_workspaces: CopyHashMap<(ClientId, JayWorkspaceId), Rc<JayWorkspace>>,
|
||||||
pub capture: Cell<bool>,
|
pub may_capture: Cell<bool>,
|
||||||
|
pub has_capture: Cell<bool>,
|
||||||
pub title_texture: Cell<Option<TextTexture>>,
|
pub title_texture: Cell<Option<TextTexture>>,
|
||||||
pub attention_requests: ThresholdCounter,
|
pub attention_requests: ThresholdCounter,
|
||||||
|
pub render_highlight: NumCell<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkspaceNode {
|
impl WorkspaceNode {
|
||||||
|
|
@ -62,11 +65,34 @@ impl WorkspaceNode {
|
||||||
self.jay_workspaces.clear();
|
self.jay_workspaces.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_has_captures(&self) {
|
||||||
|
let mut has_capture = false;
|
||||||
|
let output = self.output.get();
|
||||||
|
'update: {
|
||||||
|
if !self.may_capture.get() {
|
||||||
|
break 'update;
|
||||||
|
}
|
||||||
|
for sc in output.screencasts.lock().values() {
|
||||||
|
if sc.shows_ws(self) {
|
||||||
|
has_capture = true;
|
||||||
|
break 'update;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if output.screencopies.is_not_empty() {
|
||||||
|
has_capture = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.has_capture.replace(has_capture) != has_capture {
|
||||||
|
output.schedule_update_render_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
self.output.set(output.clone());
|
self.output.set(output.clone());
|
||||||
for jw in self.jay_workspaces.lock().values() {
|
for jw in self.jay_workspaces.lock().values() {
|
||||||
jw.send_output(output);
|
jw.send_output(output);
|
||||||
}
|
}
|
||||||
|
self.update_has_captures();
|
||||||
struct OutputSetter<'a>(&'a Rc<OutputNode>);
|
struct OutputSetter<'a>(&'a Rc<OutputNode>);
|
||||||
impl NodeVisitorBase for OutputSetter<'_> {
|
impl NodeVisitorBase for OutputSetter<'_> {
|
||||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ pub mod usr_jay_pointer;
|
||||||
pub mod usr_jay_render_ctx;
|
pub mod usr_jay_render_ctx;
|
||||||
pub mod usr_jay_screencast;
|
pub mod usr_jay_screencast;
|
||||||
pub mod usr_jay_select_toplevel;
|
pub mod usr_jay_select_toplevel;
|
||||||
|
pub mod usr_jay_select_workspace;
|
||||||
pub mod usr_jay_toplevel;
|
pub mod usr_jay_toplevel;
|
||||||
pub mod usr_jay_workspace;
|
pub mod usr_jay_workspace;
|
||||||
pub mod usr_jay_workspace_watcher;
|
pub mod usr_jay_workspace_watcher;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use {
|
||||||
usr_jay_output::UsrJayOutput, usr_jay_pointer::UsrJayPointer,
|
usr_jay_output::UsrJayOutput, usr_jay_pointer::UsrJayPointer,
|
||||||
usr_jay_render_ctx::UsrJayRenderCtx, usr_jay_screencast::UsrJayScreencast,
|
usr_jay_render_ctx::UsrJayRenderCtx, usr_jay_screencast::UsrJayScreencast,
|
||||||
usr_jay_select_toplevel::UsrJaySelectToplevel,
|
usr_jay_select_toplevel::UsrJaySelectToplevel,
|
||||||
|
usr_jay_select_workspace::UsrJaySelectWorkspace,
|
||||||
usr_jay_workspace_watcher::UsrJayWorkspaceWatcher, usr_wl_output::UsrWlOutput,
|
usr_jay_workspace_watcher::UsrJayWorkspaceWatcher, usr_wl_output::UsrWlOutput,
|
||||||
usr_wl_seat::UsrWlSeat,
|
usr_wl_seat::UsrWlSeat,
|
||||||
},
|
},
|
||||||
|
|
@ -25,7 +26,13 @@ pub struct UsrJayCompositor {
|
||||||
pub id: JayCompositorId,
|
pub id: JayCompositorId,
|
||||||
pub con: Rc<UsrCon>,
|
pub con: Rc<UsrCon>,
|
||||||
pub owner: CloneCell<Option<Rc<dyn UsrJayCompositorOwner>>>,
|
pub owner: CloneCell<Option<Rc<dyn UsrJayCompositorOwner>>>,
|
||||||
|
pub caps: UsrJayCompositorCaps,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct UsrJayCompositorCaps {
|
||||||
pub window_capture: Cell<bool>,
|
pub window_capture: Cell<bool>,
|
||||||
|
pub select_workspace: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait UsrJayCompositorOwner {
|
pub trait UsrJayCompositorOwner {
|
||||||
|
|
@ -129,6 +136,21 @@ impl UsrJayCompositor {
|
||||||
sc
|
sc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select_workspace(&self, seat: &UsrWlSeat) -> Rc<UsrJaySelectWorkspace> {
|
||||||
|
let sc = Rc::new(UsrJaySelectWorkspace {
|
||||||
|
id: self.con.id(),
|
||||||
|
con: self.con.clone(),
|
||||||
|
owner: Default::default(),
|
||||||
|
});
|
||||||
|
self.con.request(SelectWorkspace {
|
||||||
|
self_id: self.id,
|
||||||
|
id: sc.id,
|
||||||
|
seat: seat.id,
|
||||||
|
});
|
||||||
|
self.con.add_object(sc.clone());
|
||||||
|
sc
|
||||||
|
}
|
||||||
|
|
||||||
fn client_id(&self, parser: MsgParser<'_, '_>) -> Result<(), MsgParserError> {
|
fn client_id(&self, parser: MsgParser<'_, '_>) -> Result<(), MsgParserError> {
|
||||||
let ev: ClientId = self.con.parse(self, parser)?;
|
let ev: ClientId = self.con.parse(self, parser)?;
|
||||||
if let Some(owner) = self.owner.get() {
|
if let Some(owner) = self.owner.get() {
|
||||||
|
|
@ -150,7 +172,8 @@ impl UsrJayCompositor {
|
||||||
for &cap in ev.cap {
|
for &cap in ev.cap {
|
||||||
match cap {
|
match cap {
|
||||||
Cap::NONE => {}
|
Cap::NONE => {}
|
||||||
Cap::WINDOW_CAPTURE => self.window_capture.set(true),
|
Cap::WINDOW_CAPTURE => self.caps.window_capture.set(true),
|
||||||
|
Cap::SELECT_WORKSPACE => self.caps.select_workspace.set(true),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::formats,
|
format::formats,
|
||||||
ifs::jay_workspace::JayWorkspace,
|
|
||||||
utils::{
|
utils::{
|
||||||
buffd::{MsgParser, MsgParserError},
|
buffd::{MsgParser, MsgParserError},
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
|
|
@ -9,7 +8,10 @@ use {
|
||||||
video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
||||||
wire::{jay_screencast::*, JayScreencastId},
|
wire::{jay_screencast::*, JayScreencastId},
|
||||||
wl_usr::{
|
wl_usr::{
|
||||||
usr_ifs::{usr_jay_output::UsrJayOutput, usr_jay_toplevel::UsrJayToplevel},
|
usr_ifs::{
|
||||||
|
usr_jay_output::UsrJayOutput, usr_jay_toplevel::UsrJayToplevel,
|
||||||
|
usr_jay_workspace::UsrJayWorkspace,
|
||||||
|
},
|
||||||
usr_object::UsrObject,
|
usr_object::UsrObject,
|
||||||
UsrCon,
|
UsrCon,
|
||||||
},
|
},
|
||||||
|
|
@ -78,8 +80,7 @@ impl UsrJayScreencast {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn allow_workspace(&self, ws: &UsrJayWorkspace) {
|
||||||
pub fn allow_workspace(&self, ws: &JayWorkspace) {
|
|
||||||
self.con.request(AllowWorkspace {
|
self.con.request(AllowWorkspace {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
workspace: ws.id,
|
workspace: ws.id,
|
||||||
|
|
|
||||||
72
src/wl_usr/usr_ifs/usr_jay_select_workspace.rs
Normal file
72
src/wl_usr/usr_ifs/usr_jay_select_workspace.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
utils::{
|
||||||
|
buffd::{MsgParser, MsgParserError},
|
||||||
|
clonecell::CloneCell,
|
||||||
|
},
|
||||||
|
wire::{jay_select_workspace::*, JaySelectWorkspaceId},
|
||||||
|
wl_usr::{usr_ifs::usr_jay_workspace::UsrJayWorkspace, usr_object::UsrObject, UsrCon},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct UsrJaySelectWorkspace {
|
||||||
|
pub id: JaySelectWorkspaceId,
|
||||||
|
pub con: Rc<UsrCon>,
|
||||||
|
pub owner: CloneCell<Option<Rc<dyn UsrJaySelectWorkspaceOwner>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UsrJaySelectWorkspaceOwner {
|
||||||
|
fn done(&self, output: u32, ws: Option<Rc<UsrJayWorkspace>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UsrJaySelectWorkspace {
|
||||||
|
fn cancelled(&self, parser: MsgParser<'_, '_>) -> Result<(), UsrJaySelectWorkspaceError> {
|
||||||
|
let _ev: Cancelled = self.con.parse(self, parser)?;
|
||||||
|
if let Some(owner) = self.owner.get() {
|
||||||
|
owner.done(0, None);
|
||||||
|
}
|
||||||
|
self.con.remove_obj(self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected(&self, parser: MsgParser<'_, '_>) -> Result<(), UsrJaySelectWorkspaceError> {
|
||||||
|
let ev: Selected = self.con.parse(self, parser)?;
|
||||||
|
let tl = Rc::new(UsrJayWorkspace {
|
||||||
|
id: ev.id,
|
||||||
|
con: self.con.clone(),
|
||||||
|
owner: Default::default(),
|
||||||
|
});
|
||||||
|
self.con.add_object(tl.clone());
|
||||||
|
match self.owner.get() {
|
||||||
|
Some(owner) => owner.done(ev.output, Some(tl)),
|
||||||
|
_ => self.con.remove_obj(&*tl),
|
||||||
|
}
|
||||||
|
self.con.remove_obj(self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usr_object_base! {
|
||||||
|
UsrJaySelectWorkspace, JaySelectWorkspace;
|
||||||
|
|
||||||
|
CANCELLED => cancelled,
|
||||||
|
SELECTED => selected,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UsrObject for UsrJaySelectWorkspace {
|
||||||
|
fn destroy(&self) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
fn break_loops(&self) {
|
||||||
|
self.owner.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum UsrJaySelectWorkspaceError {
|
||||||
|
#[error("Parsing failed")]
|
||||||
|
MsgParserError(#[from] MsgParserError),
|
||||||
|
}
|
||||||
|
|
@ -83,6 +83,11 @@ request select_toplevel {
|
||||||
seat: id(wl_seat),
|
seat: id(wl_seat),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request select_workspace {
|
||||||
|
id: id(jay_select_workspace),
|
||||||
|
seat: id(wl_seat),
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event client_id {
|
event client_id {
|
||||||
|
|
|
||||||
7
wire/jay_select_workspace.txt
Normal file
7
wire/jay_select_workspace.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
event cancelled {
|
||||||
|
}
|
||||||
|
|
||||||
|
event selected {
|
||||||
|
output: u32,
|
||||||
|
id: id(jay_workspace),
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue