Add a mock backend for testing (including on other compositors)
Should help determine which issues are cosmic-comp bugs.
This commit is contained in:
parent
170e102275
commit
c75a48535a
13 changed files with 397 additions and 184 deletions
|
|
@ -39,6 +39,7 @@ wgpu = ["libcosmic/wgpu"]
|
||||||
# Debugging features
|
# Debugging features
|
||||||
force-shm-screencopy = []
|
force-shm-screencopy = []
|
||||||
no-subsurfaces = ["force-shm-screencopy"]
|
no-subsurfaces = ["force-shm-screencopy"]
|
||||||
|
mock-backend = []
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
# Not usable at opt-level 0, at least with software renderer
|
# Not usable at opt-level 0, at least with software renderer
|
||||||
|
|
|
||||||
216
src/backend/mock.rs
Normal file
216
src/backend/mock.rs
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
// 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_sctk::subsurface_widget::{Shmbuf, SubsurfaceBuffer},
|
||||||
|
};
|
||||||
|
|
||||||
|
use futures_channel::mpsc;
|
||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
fs,
|
||||||
|
io::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 mut file = fs::File::from(utils::create_memfile().unwrap());
|
||||||
|
|
||||||
|
for i in 0..512 * 512 {
|
||||||
|
file.write(&[r, g, b, 255]).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptureImage {
|
||||||
|
width: 512,
|
||||||
|
height: 512,
|
||||||
|
wl_buffer: SubsurfaceBuffer::new(Arc::new(
|
||||||
|
Shmbuf {
|
||||||
|
fd: file.into(),
|
||||||
|
offset: 0,
|
||||||
|
width: 512,
|
||||||
|
height: 512,
|
||||||
|
stride: 512 * 4,
|
||||||
|
format: wl_shm::Format::Abgr8888,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
75
src/backend/mod.rs
Normal file
75
src/backend/mod.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
//! The backend of getting workspace/toplevel information and previews, and
|
||||||
|
//! sending commands to change them.
|
||||||
|
//!
|
||||||
|
//! There are two backends: one that uses cosmic-comp protocols, and a mock
|
||||||
|
//! backend for testing without any special protocols.
|
||||||
|
|
||||||
|
use cosmic::{
|
||||||
|
cctk::wayland_client::protocol::wl_output, iced_sctk::subsurface_widget::SubsurfaceBuffer,
|
||||||
|
};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
// Wayland backend using cosmic-comp specific protocols
|
||||||
|
#[cfg(not(feature = "mock-backend"))]
|
||||||
|
mod wayland;
|
||||||
|
#[cfg(not(feature = "mock-backend"))]
|
||||||
|
pub use cosmic::cctk::{
|
||||||
|
cosmic_protocols::{
|
||||||
|
toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
||||||
|
},
|
||||||
|
toplevel_info::ToplevelInfo,
|
||||||
|
workspace::Workspace,
|
||||||
|
};
|
||||||
|
#[cfg(not(feature = "mock-backend"))]
|
||||||
|
pub use wayland::subscription;
|
||||||
|
|
||||||
|
// Mock backend
|
||||||
|
#[cfg(feature = "mock-backend")]
|
||||||
|
mod mock;
|
||||||
|
#[cfg(feature = "mock-backend")]
|
||||||
|
pub use mock::{
|
||||||
|
subscription, ToplevelInfo, Workspace, ZcosmicToplevelHandleV1, ZcosmicWorkspaceHandleV1,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct CaptureFilter {
|
||||||
|
pub workspaces_on_outputs: Vec<wl_output::WlOutput>,
|
||||||
|
pub toplevels_on_workspaces: Vec<ZcosmicWorkspaceHandleV1>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CaptureImage {
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub wl_buffer: SubsurfaceBuffer,
|
||||||
|
#[cfg(feature = "no-subsurfaces")]
|
||||||
|
pub image: cosmic::widget::image::Handle,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Event {
|
||||||
|
CmdSender(calloop::channel::Sender<Cmd>),
|
||||||
|
Workspaces(Vec<(HashSet<wl_output::WlOutput>, Workspace)>),
|
||||||
|
WorkspaceCapture(ZcosmicWorkspaceHandleV1, wl_output::WlOutput, CaptureImage),
|
||||||
|
NewToplevel(ZcosmicToplevelHandleV1, ToplevelInfo),
|
||||||
|
UpdateToplevel(ZcosmicToplevelHandleV1, ToplevelInfo),
|
||||||
|
CloseToplevel(ZcosmicToplevelHandleV1),
|
||||||
|
ToplevelCapture(ZcosmicToplevelHandleV1, CaptureImage),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Cmd {
|
||||||
|
CaptureFilter(CaptureFilter),
|
||||||
|
ActivateToplevel(ZcosmicToplevelHandleV1),
|
||||||
|
CloseToplevel(ZcosmicToplevelHandleV1),
|
||||||
|
MoveToplevelToWorkspace(
|
||||||
|
ZcosmicToplevelHandleV1,
|
||||||
|
ZcosmicWorkspaceHandleV1,
|
||||||
|
wl_output::WlOutput,
|
||||||
|
),
|
||||||
|
ActivateWorkspace(ZcosmicWorkspaceHandleV1),
|
||||||
|
}
|
||||||
|
|
@ -17,52 +17,7 @@ use std::{
|
||||||
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1;
|
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1;
|
||||||
|
|
||||||
use super::AppData;
|
use super::AppData;
|
||||||
|
use crate::utils;
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
fn create_memfd() -> rustix::io::Result<OwnedFd> {
|
|
||||||
let fd = rustix::io::retry_on_intr(|| {
|
|
||||||
rustix::fs::memfd_create(
|
|
||||||
"cosmic-workspaces-shm",
|
|
||||||
rustix::fs::MemfdFlags::CLOEXEC | rustix::fs::MemfdFlags::ALLOW_SEALING,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let _ = rustix::fs::fcntl_add_seals(
|
|
||||||
&fd,
|
|
||||||
rustix::fs::SealFlags::SHRINK | rustix::fs::SealFlags::SEAL,
|
|
||||||
);
|
|
||||||
Ok(fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_memfile() -> rustix::io::Result<OwnedFd> {
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
if let Ok(fd) = create_memfd() {
|
|
||||||
return Ok(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let flags = ShmOFlags::CREATE | ShmOFlags::EXCL | ShmOFlags::RDWR;
|
|
||||||
|
|
||||||
let time = SystemTime::now();
|
|
||||||
let name = format!(
|
|
||||||
"/cosmic-workspaces-shm-{}",
|
|
||||||
time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
|
|
||||||
);
|
|
||||||
|
|
||||||
match rustix::shm::shm_open(&name, flags, 0600.into()) {
|
|
||||||
Ok(fd) => match rustix::shm::shm_unlink(&name) {
|
|
||||||
Ok(_) => return Ok(fd),
|
|
||||||
Err(errno) => {
|
|
||||||
return Err(errno.into());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
Err(Errno::EXIST | Errno::EXIST) => {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(err) => return Err(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Buffer {
|
pub struct Buffer {
|
||||||
pub backing: Arc<BufferSource>,
|
pub backing: Arc<BufferSource>,
|
||||||
|
|
@ -75,7 +30,7 @@ pub struct Buffer {
|
||||||
|
|
||||||
impl AppData {
|
impl AppData {
|
||||||
fn create_shm_buffer(&self, format: u32, (width, height): (u32, u32)) -> Buffer {
|
fn create_shm_buffer(&self, format: u32, (width, height): (u32, u32)) -> Buffer {
|
||||||
let fd = create_memfile().unwrap(); // XXX?
|
let fd = utils::create_memfile().unwrap(); // XXX?
|
||||||
rustix::fs::ftruncate(&fd, width as u64 * height as u64 * 4).unwrap();
|
rustix::fs::ftruncate(&fd, width as u64 * height as u64 * 4).unwrap();
|
||||||
|
|
||||||
let pool = self.shm_state.wl_shm().create_pool(
|
let pool = self.shm_state.wl_shm().create_pool(
|
||||||
|
|
@ -22,12 +22,6 @@ pub enum CaptureSource {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct CaptureFilter {
|
|
||||||
pub workspaces_on_outputs: Vec<wl_output::WlOutput>,
|
|
||||||
pub toplevels_on_workspaces: Vec<zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Capture {
|
pub struct Capture {
|
||||||
pub source: CaptureSource,
|
pub source: CaptureSource,
|
||||||
pub session: Mutex<Option<ScreencopySession>>,
|
pub session: Mutex<Option<ScreencopySession>>,
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
|
|
||||||
use calloop_wayland_source::WaylandSource;
|
use calloop_wayland_source::WaylandSource;
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols::{
|
|
||||||
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
|
|
||||||
workspace::v1::client::zcosmic_workspace_handle_v1,
|
|
||||||
},
|
|
||||||
screencopy::ScreencopyState,
|
screencopy::ScreencopyState,
|
||||||
sctk::{
|
sctk::{
|
||||||
self,
|
self,
|
||||||
|
|
@ -15,12 +11,10 @@ use cctk::{
|
||||||
seat::{SeatHandler, SeatState},
|
seat::{SeatHandler, SeatState},
|
||||||
shm::{Shm, ShmHandler},
|
shm::{Shm, ShmHandler},
|
||||||
},
|
},
|
||||||
toplevel_info::{ToplevelInfo, ToplevelInfoState},
|
toplevel_info::ToplevelInfoState,
|
||||||
toplevel_management::ToplevelManagerState,
|
toplevel_management::ToplevelManagerState,
|
||||||
wayland_client::{
|
wayland_client::{
|
||||||
globals::registry_queue_init,
|
globals::registry_queue_init, protocol::wl_seat, Connection, Proxy, QueueHandle,
|
||||||
protocol::{wl_output, wl_seat},
|
|
||||||
Connection, Proxy, QueueHandle,
|
|
||||||
},
|
},
|
||||||
workspace::WorkspaceState,
|
workspace::WorkspaceState,
|
||||||
};
|
};
|
||||||
|
|
@ -29,16 +23,8 @@ use cosmic::iced::{
|
||||||
self,
|
self,
|
||||||
futures::{executor::block_on, FutureExt, SinkExt},
|
futures::{executor::block_on, FutureExt, SinkExt},
|
||||||
};
|
};
|
||||||
use cosmic::iced_sctk::subsurface_widget::SubsurfaceBuffer;
|
|
||||||
use futures_channel::mpsc;
|
use futures_channel::mpsc;
|
||||||
use std::{
|
use std::{cell::RefCell, collections::HashMap, fs, path::PathBuf, sync::Arc, thread};
|
||||||
cell::RefCell,
|
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
fs,
|
|
||||||
path::PathBuf,
|
|
||||||
sync::Arc,
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
use buffer::Buffer;
|
use buffer::Buffer;
|
||||||
|
|
@ -50,60 +36,12 @@ use screencopy::{ScreencopySession, SessionData};
|
||||||
mod toplevel;
|
mod toplevel;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
|
||||||
pub use capture::CaptureFilter;
|
use super::{CaptureFilter, CaptureImage, Cmd, Event};
|
||||||
|
|
||||||
// TODO define subscription for a particular output/workspace/toplevel (but we want to rate limit?)
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Event {
|
|
||||||
CmdSender(calloop::channel::Sender<Cmd>),
|
|
||||||
Workspaces(Vec<(HashSet<wl_output::WlOutput>, cctk::workspace::Workspace)>),
|
|
||||||
WorkspaceCapture(
|
|
||||||
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
|
||||||
wl_output::WlOutput,
|
|
||||||
CaptureImage,
|
|
||||||
),
|
|
||||||
NewToplevel(
|
|
||||||
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
|
||||||
ToplevelInfo,
|
|
||||||
),
|
|
||||||
UpdateToplevel(
|
|
||||||
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
|
||||||
ToplevelInfo,
|
|
||||||
),
|
|
||||||
CloseToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
|
|
||||||
ToplevelCapture(
|
|
||||||
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
|
||||||
CaptureImage,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CaptureImage {
|
|
||||||
pub width: u32,
|
|
||||||
pub height: u32,
|
|
||||||
pub wl_buffer: SubsurfaceBuffer,
|
|
||||||
#[cfg(feature = "no-subsurfaces")]
|
|
||||||
pub image: cosmic::widget::image::Handle,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscription(conn: Connection) -> iced::Subscription<Event> {
|
pub fn subscription(conn: Connection) -> iced::Subscription<Event> {
|
||||||
iced::subscription::run_with_id("wayland-sub", async { start(conn) }.flatten_stream())
|
iced::subscription::run_with_id("wayland-sub", async { start(conn) }.flatten_stream())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Cmd {
|
|
||||||
CaptureFilter(CaptureFilter),
|
|
||||||
ActivateToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
|
|
||||||
CloseToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
|
|
||||||
MoveToplevelToWorkspace(
|
|
||||||
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
|
||||||
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
|
||||||
wl_output::WlOutput,
|
|
||||||
),
|
|
||||||
ActivateWorkspace(zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppData {
|
pub struct AppData {
|
||||||
qh: QueueHandle<Self>,
|
qh: QueueHandle<Self>,
|
||||||
dmabuf_state: DmabufState,
|
dmabuf_state: DmabufState,
|
||||||
99
src/main.rs
99
src/main.rs
|
|
@ -4,14 +4,9 @@
|
||||||
#![allow(clippy::single_match)]
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols::{
|
cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1,
|
||||||
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
|
|
||||||
workspace::v1::client::zcosmic_workspace_handle_v1,
|
|
||||||
},
|
|
||||||
sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer},
|
sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer},
|
||||||
toplevel_info::ToplevelInfo,
|
|
||||||
wayland_client::{
|
wayland_client::{
|
||||||
backend::ObjectId,
|
|
||||||
protocol::{wl_data_device_manager::DndAction, wl_output},
|
protocol::{wl_data_device_manager::DndAction, wl_output},
|
||||||
Connection, Proxy, WEnum,
|
Connection, Proxy, WEnum,
|
||||||
},
|
},
|
||||||
|
|
@ -52,8 +47,10 @@ use std::{
|
||||||
mod desktop_info;
|
mod desktop_info;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod localize;
|
mod localize;
|
||||||
|
mod backend;
|
||||||
mod view;
|
mod view;
|
||||||
mod wayland;
|
use backend::{ToplevelInfo, ZcosmicToplevelHandleV1, ZcosmicWorkspaceHandleV1};
|
||||||
|
mod utils;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, CosmicConfigEntry)]
|
#[derive(Clone, Debug, Default, PartialEq, CosmicConfigEntry)]
|
||||||
|
|
@ -94,14 +91,13 @@ impl CosmicFlags for Args {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WlDndId {
|
struct WlDndId {
|
||||||
id: ObjectId,
|
|
||||||
mime_type: &'static str,
|
mime_type: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataFromMimeType for WlDndId {
|
impl DataFromMimeType for WlDndId {
|
||||||
fn from_mime_type(&self, mime_type: &str) -> Option<Vec<u8>> {
|
fn from_mime_type(&self, mime_type: &str) -> Option<Vec<u8>> {
|
||||||
if mime_type == self.mime_type {
|
if mime_type == self.mime_type {
|
||||||
Some(self.id.protocol_id().to_string().into_bytes())
|
Some(Vec::new())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -111,16 +107,16 @@ impl DataFromMimeType for WlDndId {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Msg {
|
enum Msg {
|
||||||
WaylandEvent(WaylandEvent),
|
WaylandEvent(WaylandEvent),
|
||||||
Wayland(wayland::Event),
|
Wayland(backend::Event),
|
||||||
Close,
|
Close,
|
||||||
ActivateWorkspace(zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1),
|
ActivateWorkspace(ZcosmicWorkspaceHandleV1),
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
CloseWorkspace(zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1),
|
CloseWorkspace(ZcosmicWorkspaceHandleV1),
|
||||||
ActivateToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
|
ActivateToplevel(ZcosmicToplevelHandleV1),
|
||||||
CloseToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
|
CloseToplevel(ZcosmicToplevelHandleV1),
|
||||||
StartDrag(Size, DragSurface),
|
StartDrag(Size, DragSurface),
|
||||||
DndWorkspaceEnter(
|
DndWorkspaceEnter(
|
||||||
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
ZcosmicWorkspaceHandleV1,
|
||||||
wl_output::WlOutput,
|
wl_output::WlOutput,
|
||||||
DndAction,
|
DndAction,
|
||||||
Vec<String>,
|
Vec<String>,
|
||||||
|
|
@ -139,17 +135,17 @@ enum Msg {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Workspace {
|
struct Workspace {
|
||||||
name: String,
|
name: String,
|
||||||
img_for_output: HashMap<wl_output::WlOutput, wayland::CaptureImage>,
|
img_for_output: HashMap<wl_output::WlOutput, backend::CaptureImage>,
|
||||||
handle: zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
handle: ZcosmicWorkspaceHandleV1,
|
||||||
outputs: HashSet<wl_output::WlOutput>,
|
outputs: HashSet<wl_output::WlOutput>,
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Toplevel {
|
struct Toplevel {
|
||||||
handle: zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
handle: ZcosmicToplevelHandleV1,
|
||||||
info: ToplevelInfo,
|
info: ToplevelInfo,
|
||||||
img: Option<wayland::CaptureImage>,
|
img: Option<backend::CaptureImage>,
|
||||||
icon: Option<PathBuf>,
|
icon: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,11 +167,11 @@ struct LayerSurface {
|
||||||
enum DragSurface {
|
enum DragSurface {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
Workspace {
|
Workspace {
|
||||||
handle: zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
handle: ZcosmicWorkspaceHandleV1,
|
||||||
output: wl_output::WlOutput,
|
output: wl_output::WlOutput,
|
||||||
},
|
},
|
||||||
Toplevel {
|
Toplevel {
|
||||||
handle: zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
handle: ZcosmicToplevelHandleV1,
|
||||||
output: wl_output::WlOutput,
|
output: wl_output::WlOutput,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -202,34 +198,28 @@ struct App {
|
||||||
toplevels: Vec<Toplevel>,
|
toplevels: Vec<Toplevel>,
|
||||||
conn: Option<Connection>,
|
conn: Option<Connection>,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
wayland_cmd_sender: Option<calloop::channel::Sender<wayland::Cmd>>,
|
wayland_cmd_sender: Option<calloop::channel::Sender<backend::Cmd>>,
|
||||||
drag_surface: Option<(SurfaceId, DragSurface, Size)>,
|
drag_surface: Option<(SurfaceId, DragSurface, Size)>,
|
||||||
conf: Conf,
|
conf: Conf,
|
||||||
core: cosmic::app::Core,
|
core: cosmic::app::Core,
|
||||||
drop_target: Option<(
|
drop_target: Option<(ZcosmicWorkspaceHandleV1, wl_output::WlOutput)>,
|
||||||
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
|
||||||
wl_output::WlOutput,
|
|
||||||
)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
fn workspace_for_handle(
|
fn workspace_for_handle(&self, handle: &ZcosmicWorkspaceHandleV1) -> Option<&Workspace> {
|
||||||
&self,
|
|
||||||
handle: &zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
|
||||||
) -> Option<&Workspace> {
|
|
||||||
self.workspaces.iter().find(|i| &i.handle == handle)
|
self.workspaces.iter().find(|i| &i.handle == handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn workspace_for_handle_mut(
|
fn workspace_for_handle_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
handle: &ZcosmicWorkspaceHandleV1,
|
||||||
) -> Option<&mut Workspace> {
|
) -> Option<&mut Workspace> {
|
||||||
self.workspaces.iter_mut().find(|i| &i.handle == handle)
|
self.workspaces.iter_mut().find(|i| &i.handle == handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toplevel_for_handle_mut(
|
fn toplevel_for_handle_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
handle: &ZcosmicToplevelHandleV1,
|
||||||
) -> Option<&mut Toplevel> {
|
) -> Option<&mut Toplevel> {
|
||||||
self.toplevels.iter_mut().find(|i| &i.handle == handle)
|
self.toplevels.iter_mut().find(|i| &i.handle == handle)
|
||||||
}
|
}
|
||||||
|
|
@ -311,14 +301,14 @@ impl App {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_wayland_cmd(&self, cmd: wayland::Cmd) {
|
fn send_wayland_cmd(&self, cmd: backend::Cmd) {
|
||||||
if let Some(sender) = self.wayland_cmd_sender.as_ref() {
|
if let Some(sender) = self.wayland_cmd_sender.as_ref() {
|
||||||
sender.send(cmd).unwrap();
|
sender.send(cmd).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_capture_filter(&self) {
|
fn update_capture_filter(&self) {
|
||||||
let mut capture_filter = wayland::CaptureFilter::default();
|
let mut capture_filter = backend::CaptureFilter::default();
|
||||||
if self.visible {
|
if self.visible {
|
||||||
capture_filter.workspaces_on_outputs =
|
capture_filter.workspaces_on_outputs =
|
||||||
self.outputs.iter().map(|x| x.handle.clone()).collect();
|
self.outputs.iter().map(|x| x.handle.clone()).collect();
|
||||||
|
|
@ -329,7 +319,7 @@ impl App {
|
||||||
.map(|x| x.handle.clone())
|
.map(|x| x.handle.clone())
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
self.send_wayland_cmd(wayland::Cmd::CaptureFilter(capture_filter));
|
self.send_wayland_cmd(backend::Cmd::CaptureFilter(capture_filter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,10 +403,10 @@ impl Application for App {
|
||||||
},
|
},
|
||||||
Msg::Wayland(evt) => {
|
Msg::Wayland(evt) => {
|
||||||
match evt {
|
match evt {
|
||||||
wayland::Event::CmdSender(sender) => {
|
backend::Event::CmdSender(sender) => {
|
||||||
self.wayland_cmd_sender = Some(sender);
|
self.wayland_cmd_sender = Some(sender);
|
||||||
}
|
}
|
||||||
wayland::Event::Workspaces(workspaces) => {
|
backend::Event::Workspaces(workspaces) => {
|
||||||
let old_workspaces = mem::take(&mut self.workspaces);
|
let old_workspaces = mem::take(&mut self.workspaces);
|
||||||
self.workspaces = Vec::new();
|
self.workspaces = Vec::new();
|
||||||
for (outputs, workspace) in workspaces {
|
for (outputs, workspace) in workspaces {
|
||||||
|
|
@ -441,7 +431,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
self.update_capture_filter();
|
self.update_capture_filter();
|
||||||
}
|
}
|
||||||
wayland::Event::NewToplevel(handle, info) => {
|
backend::Event::NewToplevel(handle, info) => {
|
||||||
log::debug!("New toplevel: {info:?}");
|
log::debug!("New toplevel: {info:?}");
|
||||||
self.toplevels.push(Toplevel {
|
self.toplevels.push(Toplevel {
|
||||||
icon: desktop_info::icon_for_app_id(info.app_id.clone()),
|
icon: desktop_info::icon_for_app_id(info.app_id.clone()),
|
||||||
|
|
@ -450,7 +440,7 @@ impl Application for App {
|
||||||
img: None,
|
img: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wayland::Event::UpdateToplevel(handle, info) => {
|
backend::Event::UpdateToplevel(handle, info) => {
|
||||||
if let Some(toplevel) =
|
if let Some(toplevel) =
|
||||||
self.toplevels.iter_mut().find(|x| x.handle == handle)
|
self.toplevels.iter_mut().find(|x| x.handle == handle)
|
||||||
{
|
{
|
||||||
|
|
@ -458,17 +448,17 @@ impl Application for App {
|
||||||
toplevel.info = info;
|
toplevel.info = info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wayland::Event::CloseToplevel(handle) => {
|
backend::Event::CloseToplevel(handle) => {
|
||||||
if let Some(idx) = self.toplevels.iter().position(|x| x.handle == handle) {
|
if let Some(idx) = self.toplevels.iter().position(|x| x.handle == handle) {
|
||||||
self.toplevels.remove(idx);
|
self.toplevels.remove(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wayland::Event::WorkspaceCapture(handle, output_name, image) => {
|
backend::Event::WorkspaceCapture(handle, output_name, image) => {
|
||||||
if let Some(workspace) = self.workspace_for_handle_mut(&handle) {
|
if let Some(workspace) = self.workspace_for_handle_mut(&handle) {
|
||||||
workspace.img_for_output.insert(output_name, image);
|
workspace.img_for_output.insert(output_name, image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wayland::Event::ToplevelCapture(handle, image) => {
|
backend::Event::ToplevelCapture(handle, image) => {
|
||||||
if let Some(toplevel) = self.toplevel_for_handle_mut(&handle) {
|
if let Some(toplevel) = self.toplevel_for_handle_mut(&handle) {
|
||||||
//println!("Got toplevel image!");
|
//println!("Got toplevel image!");
|
||||||
toplevel.img = Some(image);
|
toplevel.img = Some(image);
|
||||||
|
|
@ -480,10 +470,10 @@ impl Application for App {
|
||||||
return self.hide();
|
return self.hide();
|
||||||
}
|
}
|
||||||
Msg::ActivateWorkspace(workspace_handle) => {
|
Msg::ActivateWorkspace(workspace_handle) => {
|
||||||
self.send_wayland_cmd(wayland::Cmd::ActivateWorkspace(workspace_handle));
|
self.send_wayland_cmd(backend::Cmd::ActivateWorkspace(workspace_handle));
|
||||||
}
|
}
|
||||||
Msg::ActivateToplevel(toplevel_handle) => {
|
Msg::ActivateToplevel(toplevel_handle) => {
|
||||||
self.send_wayland_cmd(wayland::Cmd::ActivateToplevel(toplevel_handle));
|
self.send_wayland_cmd(backend::Cmd::ActivateToplevel(toplevel_handle));
|
||||||
return self.hide();
|
return self.hide();
|
||||||
}
|
}
|
||||||
Msg::CloseWorkspace(_workspace_handle) => {
|
Msg::CloseWorkspace(_workspace_handle) => {
|
||||||
|
|
@ -502,16 +492,12 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
Msg::CloseToplevel(toplevel_handle) => {
|
Msg::CloseToplevel(toplevel_handle) => {
|
||||||
// TODO confirmation?
|
// TODO confirmation?
|
||||||
self.send_wayland_cmd(wayland::Cmd::CloseToplevel(toplevel_handle));
|
self.send_wayland_cmd(backend::Cmd::CloseToplevel(toplevel_handle));
|
||||||
}
|
}
|
||||||
Msg::StartDrag(size, drag_surface) => {
|
Msg::StartDrag(size, drag_surface) => {
|
||||||
let (wl_id, output, mime_type) = match &drag_surface {
|
let (output, mime_type) = match &drag_surface {
|
||||||
DragSurface::Workspace { handle, output } => {
|
DragSurface::Workspace { handle, output } => (output, &*WORKSPACE_MIME),
|
||||||
(handle.clone().id(), output, &*WORKSPACE_MIME)
|
DragSurface::Toplevel { handle, output } => (output, &*TOPLEVEL_MIME),
|
||||||
}
|
|
||||||
DragSurface::Toplevel { handle, output } => {
|
|
||||||
(handle.clone().id(), output, &*TOPLEVEL_MIME)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let id = SurfaceId::unique();
|
let id = SurfaceId::unique();
|
||||||
if let Some((parent_id, _)) = self
|
if let Some((parent_id, _)) = self
|
||||||
|
|
@ -525,10 +511,7 @@ impl Application for App {
|
||||||
DndAction::Move,
|
DndAction::Move,
|
||||||
*parent_id,
|
*parent_id,
|
||||||
Some(DndIcon::Custom(id)),
|
Some(DndIcon::Custom(id)),
|
||||||
Box::new(WlDndId {
|
Box::new(WlDndId { mime_type }),
|
||||||
id: wl_id,
|
|
||||||
mime_type,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -559,7 +542,7 @@ impl Application for App {
|
||||||
.and_then(|s| u32::from_str(s).ok());
|
.and_then(|s| u32::from_str(s).ok());
|
||||||
if let Some((_, DragSurface::Toplevel { handle, .. }, _)) = &self.drag_surface {
|
if let Some((_, DragSurface::Toplevel { handle, .. }, _)) = &self.drag_surface {
|
||||||
if let Some(drop_target) = &self.drop_target {
|
if let Some(drop_target) = &self.drop_target {
|
||||||
self.send_wayland_cmd(wayland::Cmd::MoveToplevelToWorkspace(
|
self.send_wayland_cmd(backend::Cmd::MoveToplevelToWorkspace(
|
||||||
handle.clone(),
|
handle.clone(),
|
||||||
drop_target.0.clone(),
|
drop_target.0.clone(),
|
||||||
drop_target.1.clone(),
|
drop_target.1.clone(),
|
||||||
|
|
@ -640,7 +623,7 @@ impl Application for App {
|
||||||
});
|
});
|
||||||
let mut subscriptions = vec![events, config_subscription, comp_config_subscription];
|
let mut subscriptions = vec![events, config_subscription, comp_config_subscription];
|
||||||
if let Some(conn) = self.conn.clone() {
|
if let Some(conn) = self.conn.clone() {
|
||||||
subscriptions.push(wayland::subscription(conn).map(Msg::Wayland));
|
subscriptions.push(backend::subscription(conn).map(Msg::Wayland));
|
||||||
}
|
}
|
||||||
iced::Subscription::batch(subscriptions)
|
iced::Subscription::batch(subscriptions)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
51
src/utils.rs
Normal file
51
src/utils.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use rustix::{io::Errno, shm::ShmOFlags};
|
||||||
|
use std::{
|
||||||
|
os::fd::OwnedFd,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn create_memfd() -> rustix::io::Result<OwnedFd> {
|
||||||
|
let fd = rustix::io::retry_on_intr(|| {
|
||||||
|
rustix::fs::memfd_create(
|
||||||
|
"cosmic-workspaces-shm",
|
||||||
|
rustix::fs::MemfdFlags::CLOEXEC | rustix::fs::MemfdFlags::ALLOW_SEALING,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let _ = rustix::fs::fcntl_add_seals(
|
||||||
|
&fd,
|
||||||
|
rustix::fs::SealFlags::SHRINK | rustix::fs::SealFlags::SEAL,
|
||||||
|
);
|
||||||
|
Ok(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_memfile() -> rustix::io::Result<OwnedFd> {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
if let Ok(fd) = create_memfd() {
|
||||||
|
return Ok(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let flags = ShmOFlags::CREATE | ShmOFlags::EXCL | ShmOFlags::RDWR;
|
||||||
|
|
||||||
|
let time = SystemTime::now();
|
||||||
|
let name = format!(
|
||||||
|
"/cosmic-workspaces-shm-{}",
|
||||||
|
time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
|
||||||
|
);
|
||||||
|
|
||||||
|
match rustix::shm::shm_open(&name, flags, 0600.into()) {
|
||||||
|
Ok(fd) => match rustix::shm::shm_unlink(&name) {
|
||||||
|
Ok(_) => return Ok(fd),
|
||||||
|
Err(errno) => {
|
||||||
|
return Err(errno.into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
Err(Errno::EXIST | Errno::EXIST) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols::{
|
cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
|
||||||
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
|
|
||||||
workspace::v1::client::zcosmic_workspace_handle_v1,
|
|
||||||
},
|
|
||||||
wayland_client::protocol::wl_output,
|
wayland_client::protocol::wl_output,
|
||||||
};
|
};
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
|
@ -19,7 +16,10 @@ use cosmic::{
|
||||||
};
|
};
|
||||||
use cosmic_comp_config::workspace::WorkspaceLayout;
|
use cosmic_comp_config::workspace::WorkspaceLayout;
|
||||||
|
|
||||||
use crate::{wayland::CaptureImage, App, DragSurface, LayerSurface, Msg, Toplevel, Workspace};
|
use crate::{
|
||||||
|
backend::{self, CaptureImage},
|
||||||
|
App, DragSurface, LayerSurface, Msg, Toplevel, Workspace,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) fn layer_surface<'a>(
|
pub(crate) fn layer_surface<'a>(
|
||||||
app: &'a App,
|
app: &'a App,
|
||||||
|
|
@ -148,7 +148,7 @@ fn workspaces_sidebar<'a>(
|
||||||
workspaces: impl Iterator<Item = &'a Workspace>,
|
workspaces: impl Iterator<Item = &'a Workspace>,
|
||||||
output: &'a wl_output::WlOutput,
|
output: &'a wl_output::WlOutput,
|
||||||
layout: WorkspaceLayout,
|
layout: WorkspaceLayout,
|
||||||
drop_target: Option<&zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1>,
|
drop_target: Option<&backend::ZcosmicWorkspaceHandleV1>,
|
||||||
) -> cosmic::Element<'a, Msg> {
|
) -> cosmic::Element<'a, Msg> {
|
||||||
let sidebar_entries = workspaces
|
let sidebar_entries = workspaces
|
||||||
.map(|w| workspace_sidebar_entry(w, output, drop_target == Some(&w.handle)))
|
.map(|w| workspace_sidebar_entry(w, output, drop_target == Some(&w.handle)))
|
||||||
|
|
@ -272,7 +272,7 @@ fn toplevel_previews<'a>(
|
||||||
toplevels: impl Iterator<Item = &'a Toplevel>,
|
toplevels: impl Iterator<Item = &'a Toplevel>,
|
||||||
output: &'a wl_output::WlOutput,
|
output: &'a wl_output::WlOutput,
|
||||||
layout: WorkspaceLayout,
|
layout: WorkspaceLayout,
|
||||||
drag_toplevel: Option<&'a zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1>,
|
drag_toplevel: Option<&'a backend::ZcosmicToplevelHandleV1>,
|
||||||
) -> cosmic::Element<'a, Msg> {
|
) -> cosmic::Element<'a, Msg> {
|
||||||
let (width, height) = match layout {
|
let (width, height) = match layout {
|
||||||
WorkspaceLayout::Vertical => (iced::Length::FillPortion(4), iced::Length::Fill),
|
WorkspaceLayout::Vertical => (iced::Length::FillPortion(4), iced::Length::Fill),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue