220 lines
7.1 KiB
Rust
220 lines
7.1 KiB
Rust
// Copyright 2024 System76 <info@system76.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
use cosmic::{
|
|
cctk::{
|
|
cosmic_protocols::{
|
|
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
|
|
workspace::v1::client::zcosmic_workspace_handle_v1,
|
|
},
|
|
wayland_client::{
|
|
protocol::{wl_output, wl_shm},
|
|
Connection, WEnum,
|
|
},
|
|
},
|
|
iced::{
|
|
self,
|
|
futures::{executor::block_on, FutureExt, SinkExt},
|
|
},
|
|
iced_winit::platform_specific::wayland::subsurface_widget::{Shmbuf, SubsurfaceBuffer},
|
|
};
|
|
|
|
use futures_channel::mpsc;
|
|
use std::{
|
|
collections::HashSet,
|
|
fs,
|
|
io::{self, Write},
|
|
sync::{
|
|
atomic::{AtomicUsize, Ordering},
|
|
Arc,
|
|
},
|
|
thread,
|
|
};
|
|
|
|
use super::{CaptureImage, Cmd, Event};
|
|
use crate::utils;
|
|
|
|
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
|
|
struct MockObjectId(usize);
|
|
|
|
fn create_solid_capture_image(r: u8, g: u8, b: u8) -> CaptureImage {
|
|
let file = fs::File::from(utils::create_memfile().unwrap());
|
|
let mut file = io::BufWriter::new(file);
|
|
|
|
for i in 0..512 * 512 {
|
|
file.write(&[b, g, r, 255]).unwrap();
|
|
}
|
|
|
|
CaptureImage {
|
|
width: 512,
|
|
height: 512,
|
|
wl_buffer: SubsurfaceBuffer::new(Arc::new(
|
|
Shmbuf {
|
|
fd: file.into_inner().unwrap().into(),
|
|
offset: 0,
|
|
width: 512,
|
|
height: 512,
|
|
stride: 512 * 4,
|
|
format: wl_shm::Format::Argb8888,
|
|
}
|
|
.into(),
|
|
))
|
|
.0,
|
|
transform: wl_output::Transform::Normal,
|
|
#[cfg(feature = "no-subsurfaces")]
|
|
image: cosmic::widget::image::Handle::from_rgba(512, 512, [r, g, b, 255].repeat(512 * 512)),
|
|
}
|
|
}
|
|
|
|
impl MockObjectId {
|
|
fn new() -> Self {
|
|
static NEXT_MOCK_ID: AtomicUsize = AtomicUsize::new(0);
|
|
Self(NEXT_MOCK_ID.fetch_add(1, Ordering::SeqCst))
|
|
}
|
|
}
|
|
|
|
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
|
|
pub struct ZcosmicWorkspaceHandleV1(MockObjectId);
|
|
|
|
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
|
|
pub struct ZcosmicToplevelHandleV1(MockObjectId);
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
pub struct CaptureFilter {
|
|
pub workspaces_on_outputs: Vec<wl_output::WlOutput>,
|
|
pub toplevels_on_workspaces: Vec<ZcosmicWorkspaceHandleV1>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
pub struct ToplevelInfo {
|
|
pub title: String,
|
|
pub app_id: String,
|
|
pub state: HashSet<zcosmic_toplevel_handle_v1::State>,
|
|
pub output: HashSet<wl_output::WlOutput>,
|
|
pub workspace: HashSet<ZcosmicWorkspaceHandleV1>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Workspace {
|
|
pub handle: ZcosmicWorkspaceHandleV1,
|
|
pub name: String,
|
|
// pub coordinates: Vec<u32>,
|
|
pub state: Vec<WEnum<zcosmic_workspace_handle_v1::State>>,
|
|
// pub capabilities: Vec<WEnum<zcosmic_workspace_handle_v1::ZcosmicWorkspaceCapabilitiesV1>>,
|
|
// pub tiling: Option<WEnum<zcosmic_workspace_handle_v1::TilingState>>,
|
|
}
|
|
|
|
pub fn subscription(conn: Connection) -> iced::Subscription<Event> {
|
|
iced::Subscription::run_with_id("wayland-mock-sub", async { start(conn) }.flatten_stream())
|
|
}
|
|
|
|
struct AppData {
|
|
sender: mpsc::Sender<Event>,
|
|
outputs: Vec<wl_output::WlOutput>,
|
|
workspaces: Vec<(HashSet<wl_output::WlOutput>, Workspace)>,
|
|
}
|
|
|
|
impl AppData {
|
|
fn send_event(&mut self, event: Event) {
|
|
let _ = block_on(self.sender.send(event));
|
|
}
|
|
|
|
fn add_output(&mut self, output: &wl_output::WlOutput) {
|
|
// Add four workspaces for each output
|
|
let mut new_workspaces = Vec::new();
|
|
for i in 0..=4 {
|
|
let workspace_handle = ZcosmicWorkspaceHandleV1(MockObjectId::new());
|
|
let workspace = Workspace {
|
|
handle: workspace_handle.clone(),
|
|
name: format!("Workspace {i}"),
|
|
state: if i == 0 {
|
|
vec![WEnum::Value(zcosmic_workspace_handle_v1::State::Active)]
|
|
} else {
|
|
Vec::new()
|
|
},
|
|
};
|
|
// Add three toplevels for each workspace
|
|
for j in 0..=3 {
|
|
let toplevel_handle = ZcosmicToplevelHandleV1(MockObjectId::new());
|
|
let toplevel_info = ToplevelInfo {
|
|
title: format!("App {}", j),
|
|
app_id: "com.example.app".to_string(),
|
|
state: if i == 0 {
|
|
HashSet::from([zcosmic_toplevel_handle_v1::State::Activated])
|
|
} else {
|
|
HashSet::new()
|
|
},
|
|
output: HashSet::from([output.clone()]),
|
|
workspace: HashSet::from([workspace_handle.clone()]),
|
|
};
|
|
self.send_event(Event::NewToplevel(toplevel_handle.clone(), toplevel_info));
|
|
self.send_event(Event::ToplevelCapture(
|
|
toplevel_handle,
|
|
create_solid_capture_image(255, 0, 0),
|
|
));
|
|
}
|
|
self.workspaces
|
|
.push((HashSet::from([output.clone()]), workspace));
|
|
new_workspaces.push(workspace_handle);
|
|
}
|
|
self.send_event(Event::Workspaces(self.workspaces.clone()));
|
|
for workspace_handle in new_workspaces {
|
|
self.send_event(Event::WorkspaceCapture(
|
|
workspace_handle,
|
|
output.clone(),
|
|
create_solid_capture_image(0, 255, 0),
|
|
));
|
|
}
|
|
self.outputs.push(output.clone());
|
|
}
|
|
|
|
fn handle_cmd(&mut self, cmd: Cmd) {
|
|
match cmd {
|
|
Cmd::CaptureFilter(filter) => {
|
|
for output in &filter.workspaces_on_outputs {
|
|
if !self.outputs.contains(output) {
|
|
self.add_output(output);
|
|
}
|
|
}
|
|
}
|
|
Cmd::ActivateToplevel(toplevel_handle) => {
|
|
println!("Activate {:?}", toplevel_handle);
|
|
}
|
|
Cmd::CloseToplevel(toplevel_handle) => {
|
|
println!("Close {:?}", toplevel_handle);
|
|
}
|
|
Cmd::MoveToplevelToWorkspace(toplevel_handle, workspace_handle, output) => {}
|
|
Cmd::ActivateWorkspace(workspace_handle) => {
|
|
println!("Activate {:?}", workspace_handle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn start(_conn: Connection) -> mpsc::Receiver<Event> {
|
|
let (sender, receiver) = mpsc::channel(20);
|
|
thread::spawn(move || {
|
|
let mut event_loop = calloop::EventLoop::try_new().unwrap();
|
|
let (cmd_sender, cmd_channel) = calloop::channel::channel();
|
|
event_loop
|
|
.handle()
|
|
.insert_source(cmd_channel, |cmd, (), app_data: &mut AppData| match cmd {
|
|
calloop::channel::Event::Msg(cmd) => app_data.handle_cmd(cmd),
|
|
calloop::channel::Event::Closed => {}
|
|
})
|
|
.unwrap();
|
|
|
|
let mut app_data = AppData {
|
|
sender,
|
|
outputs: Vec::new(),
|
|
workspaces: Vec::new(),
|
|
};
|
|
app_data.send_event(Event::CmdSender(cmd_sender));
|
|
loop {
|
|
event_loop.dispatch(None, &mut app_data).unwrap();
|
|
}
|
|
});
|
|
receiver
|
|
}
|
|
|
|
// TODO WorkspaceCapture, ToplevelCapture, NewToplevel, Workspaces
|