Handle global workspaces
This commit is contained in:
parent
69a877894a
commit
4e2ef433fd
7 changed files with 87 additions and 40 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -365,7 +365,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-client-toolkit"
|
name = "cosmic-client-toolkit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/cosmic-protocols?rev=e491d91#e491d91d10cc0a6af725d733bada77ae413e459a"
|
source = "git+https://github.com/pop-os/cosmic-protocols#62986ea6ab3b6517f45395e7912f9bddc8dc94e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cosmic-protocols",
|
"cosmic-protocols",
|
||||||
"smithay-client-toolkit",
|
"smithay-client-toolkit",
|
||||||
|
|
@ -375,7 +375,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-protocols"
|
name = "cosmic-protocols"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/cosmic-protocols?rev=e491d91#e491d91d10cc0a6af725d733bada77ae413e459a"
|
source = "git+https://github.com/pop-os/cosmic-protocols#62986ea6ab3b6517f45395e7912f9bddc8dc94e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
calloop = "0.10.5"
|
calloop = "0.10.5"
|
||||||
cctk = { package = "cosmic-client-toolkit", git = "https://github.com/pop-os/cosmic-protocols", rev = "e491d91" }
|
cctk = { package = "cosmic-client-toolkit", git = "https://github.com/pop-os/cosmic-protocols" }
|
||||||
futures-channel = "0.3.25"
|
futures-channel = "0.3.25"
|
||||||
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = ["tokio", "wayland"] }
|
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = ["tokio", "wayland"] }
|
||||||
tokio = "1.23.0"
|
tokio = "1.23.0"
|
||||||
|
|
|
||||||
63
src/main.rs
63
src/main.rs
|
|
@ -53,9 +53,9 @@ enum Msg {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Workspace {
|
struct Workspace {
|
||||||
name: String,
|
name: String,
|
||||||
img: Option<iced::widget::image::Handle>,
|
img_for_output: HashMap<String, iced::widget::image::Handle>,
|
||||||
handle: zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
handle: zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
||||||
output_name: String,
|
output_names: Vec<String>,
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,6 +63,7 @@ struct Workspace {
|
||||||
struct Toplevel {
|
struct Toplevel {
|
||||||
handle: zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
handle: zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
info: ToplevelInfo,
|
info: ToplevelInfo,
|
||||||
|
output_name: Option<String>,
|
||||||
img: Option<iced::widget::image::Handle>,
|
img: Option<iced::widget::image::Handle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,39 +301,42 @@ impl Application for App {
|
||||||
wayland::Event::Workspaces(workspaces) => {
|
wayland::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 (output_name, workspace) in workspaces {
|
for (output_names, workspace) in workspaces {
|
||||||
let is_active = workspace.state.contains(&WEnum::Value(
|
let is_active = workspace.state.contains(&WEnum::Value(
|
||||||
zcosmic_workspace_handle_v1::State::Active,
|
zcosmic_workspace_handle_v1::State::Active,
|
||||||
));
|
));
|
||||||
|
|
||||||
// XXX efficiency
|
// XXX efficiency
|
||||||
let img = old_workspaces
|
let img_for_output = old_workspaces
|
||||||
.iter()
|
.iter()
|
||||||
.find(|i| i.handle == workspace.handle)
|
.find(|i| i.handle == workspace.handle)
|
||||||
.and_then(|i| i.img.clone());
|
.map(|i| i.img_for_output.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
self.workspaces.push(Workspace {
|
self.workspaces.push(Workspace {
|
||||||
name: workspace.name,
|
name: workspace.name,
|
||||||
handle: workspace.handle,
|
handle: workspace.handle,
|
||||||
output_name,
|
output_names,
|
||||||
img,
|
img_for_output,
|
||||||
is_active,
|
is_active,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.update_capture_filter();
|
self.update_capture_filter();
|
||||||
}
|
}
|
||||||
wayland::Event::NewToplevel(handle, info) => {
|
wayland::Event::NewToplevel(handle, output_name, info) => {
|
||||||
println!("New toplevel: {info:?}");
|
println!("New toplevel: {info:?}");
|
||||||
self.toplevels.push(Toplevel {
|
self.toplevels.push(Toplevel {
|
||||||
handle,
|
handle,
|
||||||
|
output_name,
|
||||||
info,
|
info,
|
||||||
img: None,
|
img: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wayland::Event::UpdateToplevel(handle, info) => {
|
wayland::Event::UpdateToplevel(handle, output_name, 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)
|
||||||
{
|
{
|
||||||
|
toplevel.output_name = output_name;
|
||||||
toplevel.info = info;
|
toplevel.info = info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -341,9 +345,9 @@ impl Application for App {
|
||||||
self.toplevels.remove(idx);
|
self.toplevels.remove(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wayland::Event::WorkspaceCapture(handle, image) => {
|
wayland::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 = Some(image);
|
workspace.img_for_output.insert(output_name, image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wayland::Event::ToplevelCapture(handle, image) => {
|
wayland::Event::ToplevelCapture(handle, image) => {
|
||||||
|
|
@ -435,17 +439,21 @@ fn layer_surface<'a>(app: &'a App, surface: &'a LayerSurface) -> cosmic::Element
|
||||||
workspaces_sidebar(
|
workspaces_sidebar(
|
||||||
app.workspaces
|
app.workspaces
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|i| i.output_name == surface.output_name),
|
.filter(|i| i.output_names.contains(&surface.output_name)),
|
||||||
|
&surface.output_name
|
||||||
),
|
),
|
||||||
toplevel_previews(app.toplevels.iter().filter(|i| {
|
toplevel_previews(app.toplevels.iter().filter(|i| {
|
||||||
|
if i.output_name.as_ref() != Some(&surface.output_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(workspace) = &i.info.workspace {
|
if let Some(workspace) = &i.info.workspace {
|
||||||
app.workspace_for_handle(workspace).map_or(false, |x| {
|
app.workspace_for_handle(workspace)
|
||||||
x.is_active && x.output_name == surface.output_name
|
.map_or(false, |x| x.is_active)
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
})),
|
}))
|
||||||
]
|
]
|
||||||
.spacing(12)
|
.spacing(12)
|
||||||
.height(iced::Length::Fill)
|
.height(iced::Length::Fill)
|
||||||
|
|
@ -460,7 +468,10 @@ fn close_button(on_press: Msg) -> cosmic::Element<'static, Msg> {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn workspace_sidebar_entry(workspace: &Workspace) -> cosmic::Element<Msg> {
|
fn workspace_sidebar_entry<'a>(
|
||||||
|
workspace: &'a Workspace,
|
||||||
|
output_name: &'a str,
|
||||||
|
) -> cosmic::Element<'a, Msg> {
|
||||||
// TODO style
|
// TODO style
|
||||||
let theme = if workspace.is_active {
|
let theme = if workspace.is_active {
|
||||||
cosmic::theme::Button::Primary
|
cosmic::theme::Button::Primary
|
||||||
|
|
@ -472,8 +483,9 @@ fn workspace_sidebar_entry(workspace: &Workspace) -> cosmic::Element<Msg> {
|
||||||
widget::button(widget::column![
|
widget::button(widget::column![
|
||||||
widget::Image::new(
|
widget::Image::new(
|
||||||
workspace
|
workspace
|
||||||
.img
|
.img_for_output
|
||||||
.clone()
|
.get(output_name)
|
||||||
|
.cloned()
|
||||||
.unwrap_or_else(|| widget::image::Handle::from_pixels(
|
.unwrap_or_else(|| widget::image::Handle::from_pixels(
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|
@ -491,11 +503,16 @@ 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>,
|
||||||
|
output_name: &'a str,
|
||||||
) -> cosmic::Element<'a, Msg> {
|
) -> cosmic::Element<'a, Msg> {
|
||||||
widget::column(workspaces.map(workspace_sidebar_entry).collect())
|
widget::column(
|
||||||
.width(iced::Length::Fill)
|
workspaces
|
||||||
.height(iced::Length::Fill)
|
.map(|w| workspace_sidebar_entry(w, output_name))
|
||||||
.into()
|
.collect(),
|
||||||
|
)
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.height(iced::Length::Fill)
|
||||||
|
.into()
|
||||||
|
|
||||||
// New workspace
|
// New workspace
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,17 +57,20 @@ pub enum Event {
|
||||||
ToplevelManager(zcosmic_toplevel_manager_v1::ZcosmicToplevelManagerV1),
|
ToplevelManager(zcosmic_toplevel_manager_v1::ZcosmicToplevelManagerV1),
|
||||||
WorkspaceManager(zcosmic_workspace_manager_v1::ZcosmicWorkspaceManagerV1),
|
WorkspaceManager(zcosmic_workspace_manager_v1::ZcosmicWorkspaceManagerV1),
|
||||||
// XXX Output name rather than `WlOutput`
|
// XXX Output name rather than `WlOutput`
|
||||||
Workspaces(Vec<(String, cctk::workspace::Workspace)>),
|
Workspaces(Vec<(Vec<String>, cctk::workspace::Workspace)>),
|
||||||
WorkspaceCapture(
|
WorkspaceCapture(
|
||||||
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
||||||
|
String,
|
||||||
image::Handle,
|
image::Handle,
|
||||||
),
|
),
|
||||||
NewToplevel(
|
NewToplevel(
|
||||||
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
Option<String>,
|
||||||
ToplevelInfo,
|
ToplevelInfo,
|
||||||
),
|
),
|
||||||
UpdateToplevel(
|
UpdateToplevel(
|
||||||
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
|
Option<String>,
|
||||||
ToplevelInfo,
|
ToplevelInfo,
|
||||||
),
|
),
|
||||||
CloseToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
|
CloseToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols::screencopy::v1::client::zcosmic_screencopy_session_v1,
|
cosmic_protocols::screencopy::v1::client::zcosmic_screencopy_session_v1,
|
||||||
screencopy::{BufferInfo, ScreencopyHandler, ScreencopyState},
|
screencopy::{BufferInfo, ScreencopyHandler, ScreencopyState},
|
||||||
wayland_client::{protocol::wl_shm, Connection, QueueHandle, WEnum},
|
wayland_client::{protocol::wl_shm, Connection, Proxy, QueueHandle, WEnum},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{AppData, Buffer, Capture, CaptureSource, Event};
|
use super::{AppData, Buffer, Capture, CaptureSource, Event};
|
||||||
|
|
@ -71,13 +71,20 @@ impl ScreencopyHandler for AppData {
|
||||||
|
|
||||||
let mut buffer = capture.buffer.lock().unwrap();
|
let mut buffer = capture.buffer.lock().unwrap();
|
||||||
let image = unsafe { buffer.as_mut().unwrap().to_image() };
|
let image = unsafe { buffer.as_mut().unwrap().to_image() };
|
||||||
let event = match &capture.source {
|
match &capture.source {
|
||||||
CaptureSource::Toplevel(toplevel) => Event::ToplevelCapture(toplevel.clone(), image),
|
CaptureSource::Toplevel(toplevel) => {
|
||||||
CaptureSource::Workspace(workspace, _) => {
|
self.send_event(Event::ToplevelCapture(toplevel.clone(), image))
|
||||||
Event::WorkspaceCapture(workspace.clone(), image)
|
}
|
||||||
|
CaptureSource::Workspace(workspace, output) => {
|
||||||
|
if let Some(Some(output_name)) = self.output_names.get(&output.id()) {
|
||||||
|
self.send_event(Event::WorkspaceCapture(
|
||||||
|
workspace.clone(),
|
||||||
|
output_name.clone(),
|
||||||
|
image,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.send_event(event);
|
|
||||||
session.destroy();
|
session.destroy();
|
||||||
|
|
||||||
// Capture again on damage
|
// Capture again on damage
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use cctk::{
|
||||||
},
|
},
|
||||||
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
||||||
toplevel_management::{ToplevelManagerHandler, ToplevelManagerState},
|
toplevel_management::{ToplevelManagerHandler, ToplevelManagerState},
|
||||||
wayland_client::{Connection, QueueHandle, WEnum},
|
wayland_client::{Connection, Proxy, QueueHandle, WEnum},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{AppData, CaptureSource, Event};
|
use super::{AppData, CaptureSource, Event};
|
||||||
|
|
@ -23,7 +23,15 @@ impl ToplevelInfoHandler for AppData {
|
||||||
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
) {
|
) {
|
||||||
let info = self.toplevel_info_state.info(toplevel).unwrap();
|
let info = self.toplevel_info_state.info(toplevel).unwrap();
|
||||||
self.send_event(Event::NewToplevel(toplevel.clone(), info.clone()));
|
let output_name = info
|
||||||
|
.output
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|o| self.output_names.get(&o.id()).cloned()?);
|
||||||
|
self.send_event(Event::NewToplevel(
|
||||||
|
toplevel.clone(),
|
||||||
|
output_name,
|
||||||
|
info.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
self.add_capture_source(CaptureSource::Toplevel(toplevel.clone()));
|
self.add_capture_source(CaptureSource::Toplevel(toplevel.clone()));
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +43,15 @@ impl ToplevelInfoHandler for AppData {
|
||||||
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
) {
|
) {
|
||||||
let info = self.toplevel_info_state.info(toplevel).unwrap();
|
let info = self.toplevel_info_state.info(toplevel).unwrap();
|
||||||
self.send_event(Event::UpdateToplevel(toplevel.clone(), info.clone()));
|
let output_name = info
|
||||||
|
.output
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|o| self.output_names.get(&o.id()).cloned()?);
|
||||||
|
self.send_event(Event::UpdateToplevel(
|
||||||
|
toplevel.clone(),
|
||||||
|
output_name,
|
||||||
|
info.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toplevel_closed(
|
fn toplevel_closed(
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,15 @@ impl WorkspaceHandler for AppData {
|
||||||
|
|
||||||
for group in self.workspace_state.workspace_groups() {
|
for group in self.workspace_state.workspace_groups() {
|
||||||
for workspace in &group.workspaces {
|
for workspace in &group.workspaces {
|
||||||
if let Some(output) = group.output.as_ref() {
|
let output_names: Vec<_> = group
|
||||||
if let Some(output_name) = self.output_names.get(&output.id()).unwrap().clone()
|
.outputs
|
||||||
{
|
.iter()
|
||||||
workspaces.push((output_name, workspace.clone()));
|
.filter_map(|output| self.output_names.get(&output.id()).cloned()?)
|
||||||
|
.collect();
|
||||||
|
if !output_names.is_empty() {
|
||||||
|
workspaces.push((output_names, workspace.clone()));
|
||||||
|
|
||||||
|
for output in &group.outputs {
|
||||||
self.add_capture_source(CaptureSource::Workspace(
|
self.add_capture_source(CaptureSource::Workspace(
|
||||||
workspace.handle.clone(),
|
workspace.handle.clone(),
|
||||||
output.clone(),
|
output.clone(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue