Initial commit
This commit is contained in:
commit
970885ec45
3 changed files with 618 additions and 0 deletions
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "cosmic-workspaces"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cctk = { package = "cosmic-client-toolkit", git = "https://github.com/pop-os/cosmic-protocols" }
|
||||||
|
futures-channel = "0.3.25"
|
||||||
|
iced = { git = "https://github.com/pop-os/libcosmic", features = ["tokio"] }
|
||||||
|
iced_native = { git = "https://github.com/pop-os/libcosmic" }
|
||||||
|
iced_sctk = { git = "https://github.com/pop-os/libcosmic" }
|
||||||
|
libcosmic = { git = "https://github.com/pop-os/libcosmic" }
|
||||||
|
tokio = "1.23.0"
|
||||||
261
src/main.rs
Normal file
261
src/main.rs
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
use cctk::{
|
||||||
|
cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1,
|
||||||
|
sctk::shell::layer::{Anchor, KeyboardInteractivity, Layer},
|
||||||
|
wayland_client::protocol::wl_output,
|
||||||
|
};
|
||||||
|
use iced::{
|
||||||
|
event::wayland::{Event as WaylandEvent, OutputEvent},
|
||||||
|
keyboard::KeyCode,
|
||||||
|
sctk_settings::InitialSurface,
|
||||||
|
Application, Command, Element, Subscription,
|
||||||
|
};
|
||||||
|
use iced_native::{
|
||||||
|
command::platform_specific::wayland::layer_surface::{IcedOutput, SctkLayerSurfaceSettings},
|
||||||
|
window::Id as SurfaceId,
|
||||||
|
};
|
||||||
|
use iced_sctk::{
|
||||||
|
application::SurfaceIdWrapper,
|
||||||
|
commands::layer_surface::{destroy_layer_surface, get_layer_surface},
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, process};
|
||||||
|
|
||||||
|
mod wayland;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Msg {
|
||||||
|
WaylandEvent(WaylandEvent),
|
||||||
|
Wayland(wayland::Event),
|
||||||
|
Close,
|
||||||
|
Closed(SurfaceIdWrapper),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Workspace {
|
||||||
|
name: String,
|
||||||
|
img: Option<iced::widget::image::Handle>,
|
||||||
|
handle: zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
||||||
|
output: wl_output::WlOutput,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LayerSurface {
|
||||||
|
output: wl_output::WlOutput,
|
||||||
|
//workspaces: Vec<Workspace>,
|
||||||
|
// Active workspace
|
||||||
|
// windows in workspace
|
||||||
|
// - for transitions, would need windows in more than one workspace
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct App {
|
||||||
|
max_surface_id: usize,
|
||||||
|
layer_surfaces: HashMap<SurfaceId, LayerSurface>,
|
||||||
|
workspaces: Vec<Workspace>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
fn next_surface_id(&mut self) -> SurfaceId {
|
||||||
|
self.max_surface_id += 1;
|
||||||
|
SurfaceId::new(self.max_surface_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Application for App {
|
||||||
|
type Message = Msg;
|
||||||
|
type Theme = cosmic::Theme;
|
||||||
|
type Executor = iced::executor::Default;
|
||||||
|
type Flags = ();
|
||||||
|
|
||||||
|
fn new(_flags: ()) -> (Self, Command<Msg>) {
|
||||||
|
//(Self::default(), destroy_layer_surface(SurfaceId::new(0)))
|
||||||
|
(Self::default(), Command::none())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title(&self) -> String {
|
||||||
|
String::from("cosmic-workspaces")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, message: Msg) -> Command<Msg> {
|
||||||
|
match message {
|
||||||
|
Msg::WaylandEvent(evt) => match evt {
|
||||||
|
WaylandEvent::Output(evt, output) => match evt {
|
||||||
|
OutputEvent::Created(Some(info)) => {
|
||||||
|
//println!("Create: {:?}", output);
|
||||||
|
if let Some((width, height)) = info.logical_size {
|
||||||
|
let id = self.next_surface_id();
|
||||||
|
self.layer_surfaces.insert(
|
||||||
|
id.clone(),
|
||||||
|
LayerSurface {
|
||||||
|
output: output.clone(),
|
||||||
|
//workspaces: Vec::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// /*
|
||||||
|
return get_layer_surface(SctkLayerSurfaceSettings {
|
||||||
|
id,
|
||||||
|
keyboard_interactivity: KeyboardInteractivity::Exclusive,
|
||||||
|
//keyboard_interactivity: KeyboardInteractivity::None,
|
||||||
|
namespace: "workspaces".into(),
|
||||||
|
layer: Layer::Overlay,
|
||||||
|
size: Some((Some(width as _), Some(height as _))),
|
||||||
|
output: IcedOutput::Output(output),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
// */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutputEvent::Removed => {
|
||||||
|
if let Some((id, _)) = self
|
||||||
|
.layer_surfaces
|
||||||
|
.iter()
|
||||||
|
.find(|(_id, surface)| &surface.output == &output)
|
||||||
|
{
|
||||||
|
let id = *id;
|
||||||
|
self.layer_surfaces.remove(&id).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO handle update/remove
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Msg::Wayland(evt) => {
|
||||||
|
println!("{:?}", evt);
|
||||||
|
match evt {
|
||||||
|
wayland::Event::Workspaces(workspaces) => {
|
||||||
|
// XXX efficiency
|
||||||
|
// XXX removal
|
||||||
|
self.workspaces = Vec::new();
|
||||||
|
for (output, workspace) in workspaces {
|
||||||
|
/*
|
||||||
|
if output != &surface.output {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
self.workspaces.push(Workspace {
|
||||||
|
name: workspace.name,
|
||||||
|
handle: workspace.handle,
|
||||||
|
output,
|
||||||
|
img: None,
|
||||||
|
});
|
||||||
|
println!("add workspace");
|
||||||
|
// Oh, set workspaces before surfaces created?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wayland::Event::WorkspaceCapture(workspace, image) => {
|
||||||
|
// XXX performanc
|
||||||
|
for i in &mut self.workspaces {
|
||||||
|
if &i.handle == &workspace {
|
||||||
|
i.img = Some(image.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Msg::Close => {
|
||||||
|
//println!("Close");
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
Msg::Closed(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subscription(&self) -> Subscription<Msg> {
|
||||||
|
let events = iced::subscription::events_with(|evt, _| {
|
||||||
|
//println!("{:?}", evt);
|
||||||
|
if let iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(evt)) = evt
|
||||||
|
{
|
||||||
|
Some(Msg::WaylandEvent(evt))
|
||||||
|
} else if let iced::Event::Keyboard(iced::keyboard::Event::KeyReleased {
|
||||||
|
key_code: KeyCode::Escape,
|
||||||
|
modifiers: _,
|
||||||
|
}) = evt
|
||||||
|
{
|
||||||
|
Some(Msg::Close)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
iced::Subscription::batch(vec![events, wayland::subscription().map(Msg::Wayland)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self, id: SurfaceIdWrapper) -> cosmic::Element<Msg> {
|
||||||
|
use iced::widget::*;
|
||||||
|
if let SurfaceIdWrapper::LayerSurface(id) = id {
|
||||||
|
if let Some(surface) = self.layer_surfaces.get(&id) {
|
||||||
|
return layer_surface(self, surface);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
text("workspaces").into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_requested(&self, id: SurfaceIdWrapper) -> Msg {
|
||||||
|
Msg::Closed(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layer_surface<'a>(app: &'a App, surface: &'a LayerSurface) -> cosmic::Element<'a, Msg> {
|
||||||
|
//workspaces_sidebar(app.workspaces.iter().filter(|i| &i.output == &surface.output))
|
||||||
|
workspaces_sidebar(app.workspaces.iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workspace_sidebar_entry(workspace: &Workspace) -> cosmic::Element<Msg> {
|
||||||
|
// x to close
|
||||||
|
// captured preview
|
||||||
|
// number name
|
||||||
|
// - selectable
|
||||||
|
iced::widget::column![
|
||||||
|
iced::widget::Image::new(
|
||||||
|
workspace
|
||||||
|
.img
|
||||||
|
.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)
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workspaces_sidebar<'a>(
|
||||||
|
workspaces: impl Iterator<Item = &'a Workspace>,
|
||||||
|
) -> cosmic::Element<'a, Msg> {
|
||||||
|
//println!("{:?}", workspaces);
|
||||||
|
iced::widget::column(workspaces.map(workspace_sidebar_entry).collect()).into()
|
||||||
|
// New workspace
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn window_preview(&Window) -> cosmic::Element<Msg> {
|
||||||
|
// capture of window
|
||||||
|
// - selectable
|
||||||
|
// name of window
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_previews(windows: &[Window]) -> cosmic::Element<Msg> {
|
||||||
|
iced::widgets::row(windows.iter().map(window_preview).collect())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO create one surface per monitor?
|
||||||
|
// TODO how to get monitor size?
|
||||||
|
pub fn main() -> iced::Result {
|
||||||
|
App::run(iced::Settings {
|
||||||
|
antialiasing: true,
|
||||||
|
exit_on_close_request: false,
|
||||||
|
initial_surface: InitialSurface::LayerSurface(SctkLayerSurfaceSettings {
|
||||||
|
keyboard_interactivity: KeyboardInteractivity::None,
|
||||||
|
namespace: "ignore".into(),
|
||||||
|
size: Some((Some(1), Some(1))),
|
||||||
|
layer: Layer::Background,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..iced::Settings::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
344
src/wayland.rs
Normal file
344
src/wayland.rs
Normal file
|
|
@ -0,0 +1,344 @@
|
||||||
|
// Workspaces Info, Toplevel Info
|
||||||
|
// Capture
|
||||||
|
// - subscribe to all workspaces, to start with? All that are associated with an output should be
|
||||||
|
// shown on one.
|
||||||
|
// * Need output name to compare?
|
||||||
|
|
||||||
|
use cctk::{
|
||||||
|
cosmic_protocols::{
|
||||||
|
screencopy::v1::client::{zcosmic_screencopy_manager_v1, zcosmic_screencopy_session_v1},
|
||||||
|
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
|
||||||
|
workspace::v1::client::zcosmic_workspace_handle_v1,
|
||||||
|
},
|
||||||
|
screencopy::{BufferInfo, ScreencopyHandler, ScreencopyState},
|
||||||
|
sctk::{
|
||||||
|
self,
|
||||||
|
output::{OutputHandler, OutputState},
|
||||||
|
registry::{ProvidesRegistryState, RegistryState},
|
||||||
|
shm::{raw::RawPool, ShmHandler, ShmState},
|
||||||
|
},
|
||||||
|
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
||||||
|
wayland_client::{
|
||||||
|
backend::ObjectId,
|
||||||
|
globals::registry_queue_init,
|
||||||
|
protocol::{wl_buffer, wl_output, wl_shm},
|
||||||
|
Connection, Dispatch, Proxy, QueueHandle, WEnum,
|
||||||
|
},
|
||||||
|
workspace::{WorkspaceHandler, WorkspaceState},
|
||||||
|
};
|
||||||
|
use futures_channel::mpsc;
|
||||||
|
use iced::{
|
||||||
|
futures::{executor::block_on, FutureExt, SinkExt},
|
||||||
|
widget::image,
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, thread};
|
||||||
|
|
||||||
|
// TODO define subscription for a particular output/workspace/toplevel (but we want to rate limit?)
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Event {
|
||||||
|
Workspaces(Vec<(wl_output::WlOutput, cctk::workspace::Workspace)>),
|
||||||
|
WorkspaceCapture(
|
||||||
|
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
||||||
|
image::Handle,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscription() -> iced::Subscription<Event> {
|
||||||
|
iced::subscription::run("wayland-sub", async { start() }.flatten_stream())
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CaptureSource {
|
||||||
|
Workspace(zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
buffer: Option<(RawPool, wl_buffer::WlBuffer, BufferInfo)>,
|
||||||
|
source: CaptureSource,
|
||||||
|
first_frame: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AppData {
|
||||||
|
qh: QueueHandle<Self>,
|
||||||
|
output_state: OutputState,
|
||||||
|
registry_state: RegistryState,
|
||||||
|
toplevel_info_state: ToplevelInfoState,
|
||||||
|
workspace_state: WorkspaceState,
|
||||||
|
screencopy_state: ScreencopyState,
|
||||||
|
shm_state: ShmState,
|
||||||
|
sender: mpsc::Sender<Event>,
|
||||||
|
frames: HashMap<ObjectId, Frame>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProvidesRegistryState for AppData {
|
||||||
|
fn registry(&mut self) -> &mut RegistryState {
|
||||||
|
&mut self.registry_state
|
||||||
|
}
|
||||||
|
|
||||||
|
sctk::registry_handlers!(OutputState,);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShmHandler for AppData {
|
||||||
|
fn shm_state(&mut self) -> &mut ShmState {
|
||||||
|
&mut self.shm_state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: don't need this if we use same connection with same IDs? Or?
|
||||||
|
impl OutputHandler for AppData {
|
||||||
|
fn output_state(&mut self) -> &mut OutputState {
|
||||||
|
&mut self.output_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_output(
|
||||||
|
&mut self,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
_output: wl_output::WlOutput,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_output(
|
||||||
|
&mut self,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
_output: wl_output::WlOutput,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_destroyed(
|
||||||
|
&mut self,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
_output: wl_output::WlOutput,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO any indication when we have all toplevels?
|
||||||
|
impl ToplevelInfoHandler for AppData {
|
||||||
|
fn toplevel_info_state(&mut self) -> &mut ToplevelInfoState {
|
||||||
|
&mut self.toplevel_info_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_toplevel(
|
||||||
|
&mut self,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
println!(
|
||||||
|
"New toplevel: {:?}",
|
||||||
|
self.toplevel_info_state.info(toplevel).unwrap()
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_toplevel(
|
||||||
|
&mut self,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
println!(
|
||||||
|
"Update toplevel: {:?}",
|
||||||
|
self.toplevel_info_state.info(toplevel).unwrap()
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toplevel_closed(
|
||||||
|
&mut self,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
println!(
|
||||||
|
"Closed toplevel: {:?}",
|
||||||
|
self.toplevel_info_state.info(toplevel).unwrap()
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkspaceHandler for AppData {
|
||||||
|
fn workspace_state(&mut self) -> &mut WorkspaceState {
|
||||||
|
&mut self.workspace_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn done(&mut self) {
|
||||||
|
let mut workspaces = Vec::new();
|
||||||
|
|
||||||
|
for group in self.workspace_state.workspace_groups() {
|
||||||
|
/*
|
||||||
|
println!(
|
||||||
|
"Group: capabilities: {:?}, output: {:?}",
|
||||||
|
&group.capabilities, &group.output
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
for workspace in &group.workspaces {
|
||||||
|
//println!("{:?}", &workspace);
|
||||||
|
|
||||||
|
if let Some(output) = group.output.as_ref() {
|
||||||
|
workspaces.push((output.clone(), workspace.clone()));
|
||||||
|
|
||||||
|
//println!("capture workspace");
|
||||||
|
let frame = self.screencopy_state.screencopy_manager.capture_workspace(
|
||||||
|
&workspace.handle,
|
||||||
|
output,
|
||||||
|
zcosmic_screencopy_manager_v1::CursorMode::Hidden,
|
||||||
|
&self.qh,
|
||||||
|
Default::default(), // TODO
|
||||||
|
);
|
||||||
|
// XXX first_frame
|
||||||
|
self.frames.insert(
|
||||||
|
frame.id(),
|
||||||
|
Frame {
|
||||||
|
buffer: None,
|
||||||
|
source: CaptureSource::Workspace(workspace.handle.clone()),
|
||||||
|
first_frame: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = block_on(self.sender.send(Event::Workspaces(workspaces)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScreencopyHandler for AppData {
|
||||||
|
fn screencopy_state(&mut self) -> &mut ScreencopyState {
|
||||||
|
&mut self.screencopy_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_done(
|
||||||
|
&mut self,
|
||||||
|
conn: &Connection,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1,
|
||||||
|
buffer_infos: &[BufferInfo],
|
||||||
|
) {
|
||||||
|
//println!("init_done");
|
||||||
|
// TODO BIND
|
||||||
|
|
||||||
|
// XXX
|
||||||
|
let buffer_info = buffer_infos
|
||||||
|
.iter()
|
||||||
|
.find(|x| {
|
||||||
|
x.type_ == WEnum::Value(zcosmic_screencopy_session_v1::BufferType::WlShm)
|
||||||
|
&& x.format == wl_shm::Format::Abgr8888.into()
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let buf_len = buffer_info.stride * buffer_info.height;
|
||||||
|
|
||||||
|
let mut pool = RawPool::new(buf_len as usize, &self.shm_state).unwrap();
|
||||||
|
let buffer = pool.create_buffer(
|
||||||
|
0,
|
||||||
|
buffer_info.width as i32,
|
||||||
|
buffer_info.height as i32,
|
||||||
|
buffer_info.stride as i32,
|
||||||
|
wl_shm::Format::Abgr8888,
|
||||||
|
(),
|
||||||
|
qh,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut frame = self.frames.get_mut(&session.id()).unwrap();
|
||||||
|
|
||||||
|
session.attach_buffer(&buffer, None, 0); // XXX age?
|
||||||
|
if frame.first_frame {
|
||||||
|
session.commit(zcosmic_screencopy_session_v1::Options::empty());
|
||||||
|
} else {
|
||||||
|
session.commit(zcosmic_screencopy_session_v1::Options::OnDamage);
|
||||||
|
}
|
||||||
|
conn.flush().unwrap();
|
||||||
|
|
||||||
|
frame.buffer = Some((pool, buffer, buffer_info.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ready(
|
||||||
|
&mut self,
|
||||||
|
conn: &Connection,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1,
|
||||||
|
) {
|
||||||
|
let frame = self.frames.get_mut(&session.id()).unwrap();
|
||||||
|
let (mut pool, buffer, buffer_info) = frame.buffer.take().unwrap();
|
||||||
|
match &frame.source {
|
||||||
|
CaptureSource::Workspace(workspace) => {
|
||||||
|
let image = image::Handle::from_pixels(
|
||||||
|
buffer_info.width,
|
||||||
|
buffer_info.height,
|
||||||
|
pool.mmap().to_vec(),
|
||||||
|
); // XXX
|
||||||
|
let _ = block_on(
|
||||||
|
self.sender
|
||||||
|
.send(Event::WorkspaceCapture(workspace.clone(), image)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//println!("ready");
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
fn failed(
|
||||||
|
&mut self,
|
||||||
|
conn: &Connection,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1,
|
||||||
|
reason: WEnum<zcosmic_screencopy_session_v1::FailureReason>,
|
||||||
|
) {
|
||||||
|
//println!("failed");
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<wl_buffer::WlBuffer, ()> for AppData {
|
||||||
|
fn event(
|
||||||
|
_app_data: &mut Self,
|
||||||
|
buffer: &wl_buffer::WlBuffer,
|
||||||
|
event: wl_buffer::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start() -> mpsc::Receiver<Event> {
|
||||||
|
let (sender, receiver) = mpsc::channel(20);
|
||||||
|
|
||||||
|
// TODO share connection? Can't use same `WlOutput` with seperate connection
|
||||||
|
let conn = Connection::connect_to_env().unwrap();
|
||||||
|
let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
|
||||||
|
let qh = event_queue.handle();
|
||||||
|
|
||||||
|
let registry_state = RegistryState::new(&globals);
|
||||||
|
let mut app_data = AppData {
|
||||||
|
qh: qh.clone(),
|
||||||
|
output_state: OutputState::new(&globals, &qh),
|
||||||
|
workspace_state: WorkspaceState::new(®istry_state, &qh), // Create before toplevel info state
|
||||||
|
toplevel_info_state: ToplevelInfoState::new(®istry_state, &qh),
|
||||||
|
screencopy_state: ScreencopyState::new(&globals, &qh),
|
||||||
|
registry_state,
|
||||||
|
shm_state: ShmState::bind(&globals, &qh).unwrap(),
|
||||||
|
sender,
|
||||||
|
frames: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
thread::spawn(move || loop {
|
||||||
|
event_queue.blocking_dispatch(&mut app_data).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
sctk::delegate_output!(AppData);
|
||||||
|
sctk::delegate_registry!(AppData);
|
||||||
|
sctk::delegate_shm!(AppData);
|
||||||
|
cctk::delegate_toplevel_info!(AppData);
|
||||||
|
cctk::delegate_workspace!(AppData);
|
||||||
|
cctk::delegate_screencopy!(AppData);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue