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

@ -297,7 +297,7 @@ fn finish_display_connect(dpy: Rc<PortalDisplayPrelude>) {
id: dpy.con.id(),
con: dpy.con.clone(),
owner: Default::default(),
window_capture: Cell::new(false),
caps: Default::default(),
});
dpy.con.add_object(jc.clone());
dpy.registry.request_bind(name, version, jc.deref());

View file

@ -37,7 +37,9 @@ use {
wl_usr::usr_ifs::{
usr_jay_screencast::{UsrJayScreencast, UsrJayScreencastOwner},
usr_jay_select_toplevel::UsrJaySelectToplevel,
usr_jay_select_workspace::UsrJaySelectWorkspace,
usr_jay_toplevel::UsrJayToplevel,
usr_jay_workspace::UsrJayWorkspace,
},
},
std::{
@ -64,6 +66,7 @@ pub enum ScreencastPhase {
SourcesSelected,
Selecting(Rc<SelectingScreencast>),
SelectingWindow(Rc<SelectingWindowScreencast>),
SelectingWorkspace(Rc<SelectingWorkspaceScreencast>),
Starting(Rc<StartingScreencast>),
Started(Rc<StartedScreencast>),
Terminated,
@ -89,6 +92,12 @@ pub struct SelectingWindowScreencast {
pub selector: Rc<UsrJaySelectToplevel>,
}
pub struct SelectingWorkspaceScreencast {
pub core: SelectingScreencastCore,
pub dpy: Rc<PortalDisplay>,
pub selector: Rc<UsrJaySelectWorkspace>,
}
pub struct StartingScreencast {
pub session: Rc<ScreencastSession>,
pub request_obj: Rc<DbusObject>,
@ -100,6 +109,7 @@ pub struct StartingScreencast {
pub enum ScreencastTarget {
Output(Rc<PortalOutput>),
Workspace(Rc<PortalOutput>, Rc<UsrJayWorkspace>),
Toplevel(Rc<UsrJayToplevel>),
}
@ -156,14 +166,26 @@ impl PwClientNodeOwner for StartingScreencast {
port.supported_metas.set(SUPPORTED_META_VIDEO_CROP);
let jsc = self.dpy.jc.create_screencast();
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),
}
jsc.set_use_linear_buffers(true);
jsc.set_allow_all_workspaces(true);
jsc.configure();
if let ScreencastTarget::Toplevel(t) = &self.target {
self.dpy.con.remove_obj(&**t);
match &self.target {
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 {
session: self.session.clone(),
@ -253,12 +275,22 @@ impl ScreencastSession {
s.dpy.con.remove_obj(&*s.selector);
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) => {
s.reply.err("Session has been terminated");
s.node.con.destroy_obj(s.node.deref());
s.dpy.screencasts.remove(self.session_obj.path());
if let ScreencastTarget::Toplevel(t) = &s.target {
s.dpy.con.remove_obj(&**t);
match &s.target {
ScreencastTarget::Output(_) => {}
ScreencastTarget::Workspace(_, w) => {
s.dpy.con.remove_obj(&**w);
}
ScreencastTarget::Toplevel(t) => {
s.dpy.con.remove_obj(&**t);
}
}
}
ScreencastPhase::Started(s) => {

View file

@ -5,6 +5,7 @@ use {
ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
ptl_screencast::{
ScreencastPhase, ScreencastSession, ScreencastTarget, SelectingWindowScreencast,
SelectingWorkspaceScreencast,
},
ptr_gui::{
Align, Button, ButtonOwner, Flow, GuiElement, Label, Orientation, OverlayWindow,
@ -14,7 +15,9 @@ use {
theme::Color,
utils::copyhashmap::CopyHashMap,
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,
@ -43,7 +46,8 @@ struct StaticButton {
#[derive(Copy, Clone, Eq, PartialEq)]
enum ButtonRole {
Accept,
Window,
SelectWorkspace,
SelectWindow,
Reject,
}
@ -71,14 +75,20 @@ fn create_accept_gui(surface: &Rc<SelectionGuiSurface>) -> Rc<dyn GuiElement> {
let label = Rc::new(Label::default());
*label.text.borrow_mut() = text;
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");
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.set(2.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_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.cross_margin.set(H_MARGIN);
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(reject_button);
@ -140,7 +153,7 @@ impl ButtonOwner for StaticButton {
return;
}
match self.role {
ButtonRole::Accept | ButtonRole::Window => {
ButtonRole::Accept | ButtonRole::SelectWorkspace | ButtonRole::SelectWindow => {
log::info!("User has accepted the request");
let selecting = match self.surface.gui.screencast_session.phase.get() {
ScreencastPhase::Selecting(selecting) => selecting,
@ -154,6 +167,19 @@ impl ButtonOwner for StaticButton {
selecting
.core
.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 {
let selector = dpy.jc.select_toplevel(&seat.wl);
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> {
let button = Rc::new(Button::default());
let slf = Rc::new(StaticButton {