Initial toplevel capture
This commit is contained in:
parent
7e20f7f4b2
commit
4abedacdfa
2 changed files with 119 additions and 52 deletions
103
src/main.rs
103
src/main.rs
|
|
@ -1,13 +1,17 @@
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1,
|
cosmic_protocols::{
|
||||||
|
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
|
||||||
|
workspace::v1::client::zcosmic_workspace_handle_v1,
|
||||||
|
},
|
||||||
sctk::shell::layer::{Anchor, KeyboardInteractivity, Layer},
|
sctk::shell::layer::{Anchor, KeyboardInteractivity, Layer},
|
||||||
|
toplevel_info::ToplevelInfo,
|
||||||
wayland_client::protocol::wl_output,
|
wayland_client::protocol::wl_output,
|
||||||
};
|
};
|
||||||
use iced::{
|
use iced::{
|
||||||
event::wayland::{Event as WaylandEvent, OutputEvent},
|
event::wayland::{Event as WaylandEvent, OutputEvent},
|
||||||
keyboard::KeyCode,
|
keyboard::KeyCode,
|
||||||
sctk_settings::InitialSurface,
|
sctk_settings::InitialSurface,
|
||||||
Application, Command, Element, Subscription,
|
widget, Application, Command, Element, Subscription,
|
||||||
};
|
};
|
||||||
use iced_native::{
|
use iced_native::{
|
||||||
command::platform_specific::wayland::layer_surface::{IcedOutput, SctkLayerSurfaceSettings},
|
command::platform_specific::wayland::layer_surface::{IcedOutput, SctkLayerSurfaceSettings},
|
||||||
|
|
@ -21,12 +25,13 @@ use std::{collections::HashMap, process};
|
||||||
|
|
||||||
mod wayland;
|
mod wayland;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Msg {
|
enum Msg {
|
||||||
WaylandEvent(WaylandEvent),
|
WaylandEvent(WaylandEvent),
|
||||||
Wayland(wayland::Event),
|
Wayland(wayland::Event),
|
||||||
Close,
|
Close,
|
||||||
Closed(SurfaceIdWrapper),
|
Closed(SurfaceIdWrapper),
|
||||||
|
ActivateWorkspace(zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -37,6 +42,13 @@ struct Workspace {
|
||||||
output_name: Option<String>,
|
output_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Toplevel {
|
||||||
|
handle: zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
info: ToplevelInfo,
|
||||||
|
img: Option<iced::widget::image::Handle>,
|
||||||
|
}
|
||||||
|
|
||||||
struct LayerSurface {
|
struct LayerSurface {
|
||||||
output: wl_output::WlOutput,
|
output: wl_output::WlOutput,
|
||||||
output_name: Option<String>,
|
output_name: Option<String>,
|
||||||
|
|
@ -50,6 +62,7 @@ struct App {
|
||||||
max_surface_id: usize,
|
max_surface_id: usize,
|
||||||
layer_surfaces: HashMap<SurfaceId, LayerSurface>,
|
layer_surfaces: HashMap<SurfaceId, LayerSurface>,
|
||||||
workspaces: Vec<Workspace>,
|
workspaces: Vec<Workspace>,
|
||||||
|
toplevels: Vec<Toplevel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
@ -115,7 +128,6 @@ impl Application for App {
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Msg::Wayland(evt) => {
|
Msg::Wayland(evt) => {
|
||||||
println!("{:?}", evt);
|
|
||||||
match evt {
|
match evt {
|
||||||
wayland::Event::Workspaces(workspaces) => {
|
wayland::Event::Workspaces(workspaces) => {
|
||||||
// XXX efficiency
|
// XXX efficiency
|
||||||
|
|
@ -128,9 +140,16 @@ impl Application for App {
|
||||||
output_name,
|
output_name,
|
||||||
img: None,
|
img: None,
|
||||||
});
|
});
|
||||||
println!("add workspace");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wayland::Event::NewToplevel(handle, info) => {
|
||||||
|
println!("New toplevel");
|
||||||
|
self.toplevels.push(Toplevel {
|
||||||
|
handle,
|
||||||
|
info,
|
||||||
|
img: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
wayland::Event::WorkspaceCapture(workspace, image) => {
|
wayland::Event::WorkspaceCapture(workspace, image) => {
|
||||||
// XXX performance
|
// XXX performance
|
||||||
for i in &mut self.workspaces {
|
for i in &mut self.workspaces {
|
||||||
|
|
@ -139,12 +158,23 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wayland::Event::ToplevelCapture(toplevel, image) => {
|
||||||
|
println!("Toplevel capture");
|
||||||
|
for i in &mut self.toplevels {
|
||||||
|
if &i.handle == &toplevel {
|
||||||
|
i.img = Some(image.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Msg::Close => {
|
Msg::Close => {
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
Msg::Closed(_) => {}
|
Msg::Closed(_) => {}
|
||||||
|
Msg::ActivateWorkspace(workspace) => {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::none()
|
Command::none()
|
||||||
|
|
@ -152,7 +182,6 @@ impl Application for App {
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Msg> {
|
fn subscription(&self) -> Subscription<Msg> {
|
||||||
let events = iced::subscription::events_with(|evt, _| {
|
let events = iced::subscription::events_with(|evt, _| {
|
||||||
//println!("{:?}", evt);
|
|
||||||
if let iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(evt)) = evt
|
if let iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(evt)) = evt
|
||||||
{
|
{
|
||||||
Some(Msg::WaylandEvent(evt))
|
Some(Msg::WaylandEvent(evt))
|
||||||
|
|
@ -185,30 +214,28 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layer_surface<'a>(app: &'a App, surface: &'a LayerSurface) -> cosmic::Element<'a, Msg> {
|
fn layer_surface<'a>(app: &'a App, surface: &'a LayerSurface) -> cosmic::Element<'a, Msg> {
|
||||||
workspaces_sidebar(
|
widget::column![
|
||||||
app.workspaces
|
workspaces_sidebar(
|
||||||
.iter()
|
app.workspaces
|
||||||
.filter(|i| &i.output_name == &surface.output_name),
|
.iter()
|
||||||
)
|
.filter(|i| &i.output_name == &surface.output_name),
|
||||||
|
),
|
||||||
|
toplevel_previews(app.toplevels.iter()) // XXX
|
||||||
|
]
|
||||||
|
.height(iced::Length::Fill)
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn workspace_sidebar_entry(workspace: &Workspace) -> cosmic::Element<Msg> {
|
fn workspace_sidebar_entry(workspace: &Workspace) -> cosmic::Element<Msg> {
|
||||||
// x to close
|
// Indicate active workspace?
|
||||||
// captured preview
|
widget::column![
|
||||||
// number name
|
widget::button(widget::text("X")), // TODO close button
|
||||||
// - selectable
|
widget::button(widget::Image::new(workspace.img.clone().unwrap_or_else(
|
||||||
iced::widget::column![
|
|| widget::image::Handle::from_pixels(0, 0, vec![0, 0, 0, 255])
|
||||||
iced::widget::Image::new(
|
)))
|
||||||
workspace
|
.on_press(Msg::ActivateWorkspace(workspace.handle.clone())),
|
||||||
.img
|
widget::text(&workspace.name)
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| iced::widget::image::Handle::from_pixels(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
vec![0, 0, 0, 255]
|
|
||||||
))
|
|
||||||
),
|
|
||||||
iced::widget::text(&workspace.name)
|
|
||||||
]
|
]
|
||||||
.height(iced::Length::Fill)
|
.height(iced::Length::Fill)
|
||||||
.width(iced::Length::Fill)
|
.width(iced::Length::Fill)
|
||||||
|
|
@ -218,21 +245,25 @@ fn workspace_sidebar_entry(workspace: &Workspace) -> cosmic::Element<Msg> {
|
||||||
fn workspaces_sidebar<'a>(
|
fn workspaces_sidebar<'a>(
|
||||||
workspaces: impl Iterator<Item = &'a Workspace>,
|
workspaces: impl Iterator<Item = &'a Workspace>,
|
||||||
) -> cosmic::Element<'a, Msg> {
|
) -> cosmic::Element<'a, Msg> {
|
||||||
iced::widget::column(workspaces.map(workspace_sidebar_entry).collect()).into()
|
widget::column(workspaces.map(workspace_sidebar_entry).collect()).into()
|
||||||
// New workspace
|
// New workspace
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
fn toplevel_preview<'a>(toplevel: &'a Toplevel) -> cosmic::Element<'a, Msg> {
|
||||||
fn window_preview(&Window) -> cosmic::Element<Msg> {
|
// capture of window
|
||||||
// capture of window
|
// - selectable
|
||||||
// - selectable
|
// name of window
|
||||||
// name of window
|
widget::button(widget::Image::new(toplevel.img.clone().unwrap_or_else(
|
||||||
|
|| widget::image::Handle::from_pixels(0, 0, vec![0, 0, 0, 255]),
|
||||||
|
)))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_previews(windows: &[Window]) -> cosmic::Element<Msg> {
|
fn toplevel_previews<'a>(
|
||||||
iced::widgets::row(windows.iter().map(window_preview).collect())
|
toplevels: impl Iterator<Item = &'a Toplevel>,
|
||||||
|
) -> cosmic::Element<'a, Msg> {
|
||||||
|
widget::row(toplevels.map(toplevel_preview).collect()).into()
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
App::run(iced::Settings {
|
App::run(iced::Settings {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
// shown on one.
|
// shown on one.
|
||||||
// * Need output name to compare?
|
// * Need output name to compare?
|
||||||
|
|
||||||
|
// TODO: Way to activate workspace, toplevel? Close? Move?
|
||||||
|
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols::{
|
cosmic_protocols::{
|
||||||
screencopy::v1::client::{zcosmic_screencopy_manager_v1, zcosmic_screencopy_session_v1},
|
screencopy::v1::client::{zcosmic_screencopy_manager_v1, zcosmic_screencopy_session_v1},
|
||||||
|
|
@ -17,7 +19,7 @@ use cctk::{
|
||||||
registry::{ProvidesRegistryState, RegistryState},
|
registry::{ProvidesRegistryState, RegistryState},
|
||||||
shm::{raw::RawPool, ShmHandler, ShmState},
|
shm::{raw::RawPool, ShmHandler, ShmState},
|
||||||
},
|
},
|
||||||
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
toplevel_info::{ToplevelInfo, ToplevelInfoHandler, ToplevelInfoState},
|
||||||
wayland_client::{
|
wayland_client::{
|
||||||
backend::ObjectId,
|
backend::ObjectId,
|
||||||
globals::registry_queue_init,
|
globals::registry_queue_init,
|
||||||
|
|
@ -35,7 +37,7 @@ use std::{collections::HashMap, thread};
|
||||||
|
|
||||||
// TODO define subscription for a particular output/workspace/toplevel (but we want to rate limit?)
|
// TODO define subscription for a particular output/workspace/toplevel (but we want to rate limit?)
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
// XXX Output name rather than `WlOutput`
|
// XXX Output name rather than `WlOutput`
|
||||||
Workspaces(Vec<(Option<String>, cctk::workspace::Workspace)>),
|
Workspaces(Vec<(Option<String>, cctk::workspace::Workspace)>),
|
||||||
|
|
@ -43,6 +45,14 @@ pub enum Event {
|
||||||
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
||||||
image::Handle,
|
image::Handle,
|
||||||
),
|
),
|
||||||
|
NewToplevel(
|
||||||
|
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
ToplevelInfo,
|
||||||
|
),
|
||||||
|
ToplevelCapture(
|
||||||
|
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
image::Handle,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscription() -> iced::Subscription<Event> {
|
pub fn subscription() -> iced::Subscription<Event> {
|
||||||
|
|
@ -50,6 +60,7 @@ pub fn subscription() -> iced::Subscription<Event> {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CaptureSource {
|
enum CaptureSource {
|
||||||
|
Toplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
|
||||||
Workspace(zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1),
|
Workspace(zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,6 +83,12 @@ struct AppData {
|
||||||
output_names: HashMap<ObjectId, Option<String>>,
|
output_names: HashMap<ObjectId, Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AppData {
|
||||||
|
fn send_event(&mut self, event: Event) {
|
||||||
|
let _ = block_on(self.sender.send(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ProvidesRegistryState for AppData {
|
impl ProvidesRegistryState for AppData {
|
||||||
fn registry(&mut self) -> &mut RegistryState {
|
fn registry(&mut self) -> &mut RegistryState {
|
||||||
&mut self.registry_state
|
&mut self.registry_state
|
||||||
|
|
@ -132,6 +149,27 @@ impl ToplevelInfoHandler for AppData {
|
||||||
_qh: &QueueHandle<Self>,
|
_qh: &QueueHandle<Self>,
|
||||||
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
) {
|
) {
|
||||||
|
let info = self.toplevel_info_state.info(&toplevel).unwrap();
|
||||||
|
self.send_event(Event::NewToplevel(
|
||||||
|
toplevel.clone(),
|
||||||
|
info.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let frame = self.screencopy_state.screencopy_manager.capture_toplevel(
|
||||||
|
toplevel,
|
||||||
|
zcosmic_screencopy_manager_v1::CursorMode::Hidden,
|
||||||
|
&self.qh,
|
||||||
|
Default::default(), // TODO
|
||||||
|
);
|
||||||
|
// XXX first_frame
|
||||||
|
self.frames.insert(
|
||||||
|
frame.id(),
|
||||||
|
Frame {
|
||||||
|
buffer: None,
|
||||||
|
source: CaptureSource::Toplevel(toplevel.clone()),
|
||||||
|
first_frame: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_toplevel(
|
fn update_toplevel(
|
||||||
|
|
@ -140,6 +178,7 @@ impl ToplevelInfoHandler for AppData {
|
||||||
_qh: &QueueHandle<Self>,
|
_qh: &QueueHandle<Self>,
|
||||||
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
) {
|
) {
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toplevel_closed(
|
fn toplevel_closed(
|
||||||
|
|
@ -148,6 +187,7 @@ impl ToplevelInfoHandler for AppData {
|
||||||
_qh: &QueueHandle<Self>,
|
_qh: &QueueHandle<Self>,
|
||||||
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
) {
|
) {
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +205,6 @@ impl WorkspaceHandler for AppData {
|
||||||
let output_name = self.output_names.get(&output.id()).unwrap().clone();
|
let output_name = self.output_names.get(&output.id()).unwrap().clone();
|
||||||
workspaces.push((output_name, workspace.clone()));
|
workspaces.push((output_name, workspace.clone()));
|
||||||
|
|
||||||
//println!("capture workspace");
|
|
||||||
let frame = self.screencopy_state.screencopy_manager.capture_workspace(
|
let frame = self.screencopy_state.screencopy_manager.capture_workspace(
|
||||||
&workspace.handle,
|
&workspace.handle,
|
||||||
output,
|
output,
|
||||||
|
|
@ -179,14 +218,14 @@ impl WorkspaceHandler for AppData {
|
||||||
Frame {
|
Frame {
|
||||||
buffer: None,
|
buffer: None,
|
||||||
source: CaptureSource::Workspace(workspace.handle.clone()),
|
source: CaptureSource::Workspace(workspace.handle.clone()),
|
||||||
first_frame: false,
|
first_frame: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = block_on(self.sender.send(Event::Workspaces(workspaces)));
|
self.send_event(Event::Workspaces(workspaces));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,19 +282,15 @@ impl ScreencopyHandler for AppData {
|
||||||
) {
|
) {
|
||||||
let frame = self.frames.get_mut(&session.id()).unwrap();
|
let frame = self.frames.get_mut(&session.id()).unwrap();
|
||||||
let (mut pool, buffer, buffer_info) = frame.buffer.take().unwrap();
|
let (mut pool, buffer, buffer_info) = frame.buffer.take().unwrap();
|
||||||
match &frame.source {
|
let image =
|
||||||
|
image::Handle::from_pixels(buffer_info.width, buffer_info.height, pool.mmap().to_vec());
|
||||||
|
let event = match &frame.source {
|
||||||
|
CaptureSource::Toplevel(toplevel) => Event::ToplevelCapture(toplevel.clone(), image),
|
||||||
CaptureSource::Workspace(workspace) => {
|
CaptureSource::Workspace(workspace) => {
|
||||||
let image = image::Handle::from_pixels(
|
Event::WorkspaceCapture(workspace.clone(), image)
|
||||||
buffer_info.width,
|
|
||||||
buffer_info.height,
|
|
||||||
pool.mmap().to_vec(),
|
|
||||||
); // XXX
|
|
||||||
let _ = block_on(
|
|
||||||
self.sender
|
|
||||||
.send(Event::WorkspaceCapture(workspace.clone(), image)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
self.send_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn failed(
|
fn failed(
|
||||||
|
|
@ -266,6 +301,7 @@ impl ScreencopyHandler for AppData {
|
||||||
reason: WEnum<zcosmic_screencopy_session_v1::FailureReason>,
|
reason: WEnum<zcosmic_screencopy_session_v1::FailureReason>,
|
||||||
) {
|
) {
|
||||||
// TODO
|
// TODO
|
||||||
|
println!("Failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue