Update to workspace v2, based on ext-workspace
In the workspace applet, this now uses `Workspace` in the front-end code instead of a tuple with unnamed fields. Handling of scrolling is also moved to the frontend, which uses less code and seems more natural. It would be good to have a helper in libcosmic for this. It also changes `ObjectId` to `ExtWorkspaceHandleV1`, which is a little simpler and I see no reason here to avoid the more strongly typed object. At some point we may want a shared subscription for workspaces in multiple applets. As well as a higher-level abstraction for screen capture.
This commit is contained in:
parent
f08d80a891
commit
7ba2ed0c53
12 changed files with 196 additions and 269 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
|
@ -1395,8 +1395,9 @@ 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=ed2a481#ed2a48143cd6dc63274aa04461de3d341e50b16b"
|
source = "git+https://github.com/pop-os/cosmic-protocols//?branch=main#6b05c2a157118979cb472a38455ba78ca9729196"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitflags 2.8.0",
|
||||||
"cosmic-protocols",
|
"cosmic-protocols",
|
||||||
"libc",
|
"libc",
|
||||||
"smithay-client-toolkit",
|
"smithay-client-toolkit",
|
||||||
|
|
@ -1534,7 +1535,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=ed2a481#ed2a48143cd6dc63274aa04461de3d341e50b16b"
|
source = "git+https://github.com/pop-os/cosmic-protocols//?branch=main#6b05c2a157118979cb472a38455ba78ca9729196"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
|
|
@ -2148,7 +2149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3946,7 +3947,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5530,7 +5531,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.15",
|
"linux-raw-sys 0.4.15",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -6055,7 +6056,7 @@ dependencies = [
|
||||||
"getrandom 0.3.1",
|
"getrandom 0.3.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 0.38.44",
|
"rustix 0.38.44",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -6988,7 +6989,7 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -86,5 +86,5 @@ ignored = ["libcosmic"]
|
||||||
sctk = { package = "smithay-client-toolkit", version = "=0.19.2" }
|
sctk = { package = "smithay-client-toolkit", version = "=0.19.2" }
|
||||||
|
|
||||||
[patch."https://github.com/pop-os/cosmic-protocols"]
|
[patch."https://github.com/pop-os/cosmic-protocols"]
|
||||||
cctk = { git = "https://github.com/pop-os//cosmic-protocols", package = "cosmic-client-toolkit", rev = "ed2a481" }
|
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "main" }
|
||||||
cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", rev = "ed2a481" }
|
cosmic-client-toolkit = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "main" }
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@ use cctk::{
|
||||||
wayland_client::protocol::{
|
wayland_client::protocol::{
|
||||||
wl_data_device_manager::DndAction, wl_output::WlOutput, wl_seat::WlSeat,
|
wl_data_device_manager::DndAction, wl_output::WlOutput, wl_seat::WlSeat,
|
||||||
},
|
},
|
||||||
wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
wayland_protocols::ext::{
|
||||||
|
foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||||
|
workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
applet::{
|
applet::{
|
||||||
|
|
@ -44,10 +47,7 @@ use cosmic::{
|
||||||
Apply, Element, Task,
|
Apply, Element, Task,
|
||||||
};
|
};
|
||||||
use cosmic_app_list_config::{AppListConfig, APP_ID};
|
use cosmic_app_list_config::{AppListConfig, APP_ID};
|
||||||
use cosmic_protocols::{
|
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State;
|
||||||
toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State,
|
|
||||||
workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
|
||||||
};
|
|
||||||
use freedesktop_desktop_entry as fde;
|
use freedesktop_desktop_entry as fde;
|
||||||
use freedesktop_desktop_entry::{get_languages_from_env, DesktopEntry};
|
use freedesktop_desktop_entry::{get_languages_from_env, DesktopEntry};
|
||||||
use futures::future::pending;
|
use futures::future::pending;
|
||||||
|
|
@ -327,7 +327,7 @@ struct CosmicAppList {
|
||||||
dnd_offer: Option<DndOffer>,
|
dnd_offer: Option<DndOffer>,
|
||||||
is_listening_for_dnd: bool,
|
is_listening_for_dnd: bool,
|
||||||
gpus: Option<Vec<Gpu>>,
|
gpus: Option<Vec<Gpu>>,
|
||||||
active_workspaces: Vec<ZcosmicWorkspaceHandleV1>,
|
active_workspaces: Vec<ExtWorkspaceHandleV1>,
|
||||||
output_list: HashMap<WlOutput, OutputInfo>,
|
output_list: HashMap<WlOutput, OutputInfo>,
|
||||||
locales: Vec<String>,
|
locales: Vec<String>,
|
||||||
overflow_favorites_popup: Option<window::Id>,
|
overflow_favorites_popup: Option<window::Id>,
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,15 @@ use cctk::{
|
||||||
},
|
},
|
||||||
Connection, Dispatch, QueueHandle, WEnum,
|
Connection, Dispatch, QueueHandle, WEnum,
|
||||||
},
|
},
|
||||||
wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
wayland_protocols::ext::{
|
||||||
|
foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||||
|
workspace::v1::client::ext_workspace_handle_v1::State as WorkspaceUpdateState,
|
||||||
|
},
|
||||||
workspace::{WorkspaceHandler, WorkspaceState},
|
workspace::{WorkspaceHandler, WorkspaceState},
|
||||||
};
|
};
|
||||||
use cosmic_protocols::{
|
use cosmic_protocols::{
|
||||||
toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||||
toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
|
toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
|
||||||
workspace::v1::client::zcosmic_workspace_handle_v1::State as WorkspaceUpdateState,
|
|
||||||
};
|
};
|
||||||
use futures::channel::mpsc::UnboundedSender;
|
use futures::channel::mpsc::UnboundedSender;
|
||||||
use sctk::{
|
use sctk::{
|
||||||
|
|
@ -129,12 +131,11 @@ impl WorkspaceHandler for AppData {
|
||||||
let active_workspaces = self
|
let active_workspaces = self
|
||||||
.workspace_state
|
.workspace_state
|
||||||
.workspace_groups()
|
.workspace_groups()
|
||||||
.iter()
|
|
||||||
.filter_map(|x| {
|
.filter_map(|x| {
|
||||||
x.workspaces.iter().find(|w| {
|
x.workspaces
|
||||||
w.state
|
.iter()
|
||||||
.contains(&WEnum::Value(WorkspaceUpdateState::Active))
|
.filter_map(|handle| self.workspace_state.workspace_info(handle))
|
||||||
})
|
.find(|w| w.state.contains(WorkspaceUpdateState::Active))
|
||||||
})
|
})
|
||||||
.map(|workspace| workspace.handle.clone())
|
.map(|workspace| workspace.handle.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
@ -364,7 +365,7 @@ impl CaptureData {
|
||||||
pub fn capture_source_shm_fd<Fd: AsFd>(
|
pub fn capture_source_shm_fd<Fd: AsFd>(
|
||||||
&self,
|
&self,
|
||||||
overlay_cursor: bool,
|
overlay_cursor: bool,
|
||||||
source: ZcosmicToplevelHandleV1,
|
source: &ExtForeignToplevelHandleV1,
|
||||||
fd: Fd,
|
fd: Fd,
|
||||||
len: Option<u32>,
|
len: Option<u32>,
|
||||||
) -> Option<ShmImage<Fd>> {
|
) -> Option<ShmImage<Fd>> {
|
||||||
|
|
@ -379,7 +380,7 @@ impl CaptureData {
|
||||||
let capture_session = self
|
let capture_session = self
|
||||||
.capturer
|
.capturer
|
||||||
.create_session(
|
.create_session(
|
||||||
&CaptureSource::CosmicToplevel(source),
|
&CaptureSource::Toplevel(source.clone()),
|
||||||
CaptureOptions::empty(),
|
CaptureOptions::empty(),
|
||||||
&self.qh,
|
&self.qh,
|
||||||
SessionData {
|
SessionData {
|
||||||
|
|
@ -494,9 +495,6 @@ impl AppData {
|
||||||
wl_shm: self.shm_state.wl_shm().clone(),
|
wl_shm: self.shm_state.wl_shm().clone(),
|
||||||
capturer: self.screencopy_state.capturer().clone(),
|
capturer: self.screencopy_state.capturer().clone(),
|
||||||
};
|
};
|
||||||
let Some(cosmic_toplevel) = self.cosmic_toplevel(&handle) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
let name = unsafe { CStr::from_bytes_with_nul_unchecked(b"app-list-screencopy\0") };
|
let name = unsafe { CStr::from_bytes_with_nul_unchecked(b"app-list-screencopy\0") };
|
||||||
|
|
@ -506,7 +504,7 @@ impl AppData {
|
||||||
};
|
};
|
||||||
|
|
||||||
// XXX is this going to use to much memory?
|
// XXX is this going to use to much memory?
|
||||||
let img = capture_data.capture_source_shm_fd(false, cosmic_toplevel, fd, None);
|
let img = capture_data.capture_source_shm_fd(false, &handle, fd, None);
|
||||||
if let Some(img) = img {
|
if let Some(img) = img {
|
||||||
let Ok(img) = img.image() else {
|
let Ok(img) = img.image() else {
|
||||||
tracing::error!("Failed to get RgbaImage");
|
tracing::error!("Failed to get RgbaImage");
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,15 @@ use cctk::{
|
||||||
sctk::{output::OutputInfo, reexports::calloop},
|
sctk::{output::OutputInfo, reexports::calloop},
|
||||||
toplevel_info::ToplevelInfo,
|
toplevel_info::ToplevelInfo,
|
||||||
wayland_client::protocol::wl_output::WlOutput,
|
wayland_client::protocol::wl_output::WlOutput,
|
||||||
wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
wayland_protocols::ext::{
|
||||||
|
foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||||
|
workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::{self, stream, Subscription},
|
iced::{self, stream, Subscription},
|
||||||
iced_core::image::Bytes,
|
iced_core::image::Bytes,
|
||||||
};
|
};
|
||||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1;
|
|
||||||
use image::EncodableLayout;
|
use image::EncodableLayout;
|
||||||
|
|
||||||
use futures::{
|
use futures::{
|
||||||
|
|
@ -110,7 +112,7 @@ pub enum WaylandUpdate {
|
||||||
Init(calloop::channel::Sender<WaylandRequest>),
|
Init(calloop::channel::Sender<WaylandRequest>),
|
||||||
Finished,
|
Finished,
|
||||||
Toplevel(ToplevelUpdate),
|
Toplevel(ToplevelUpdate),
|
||||||
Workspace(Vec<ZcosmicWorkspaceHandleV1>),
|
Workspace(Vec<ExtWorkspaceHandleV1>),
|
||||||
Output(OutputUpdate),
|
Output(OutputUpdate),
|
||||||
ActivationToken {
|
ActivationToken {
|
||||||
token: Option<String>,
|
token: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ impl CaptureData {
|
||||||
pub fn capture_source_shm_fd<Fd: AsFd>(
|
pub fn capture_source_shm_fd<Fd: AsFd>(
|
||||||
&self,
|
&self,
|
||||||
overlay_cursor: bool,
|
overlay_cursor: bool,
|
||||||
source: ZcosmicToplevelHandleV1,
|
source: ExtForeignToplevelHandleV1,
|
||||||
fd: Fd,
|
fd: Fd,
|
||||||
len: Option<u32>,
|
len: Option<u32>,
|
||||||
) -> Option<ShmImage<Fd>> {
|
) -> Option<ShmImage<Fd>> {
|
||||||
|
|
@ -145,7 +145,7 @@ impl CaptureData {
|
||||||
let capture_session = self
|
let capture_session = self
|
||||||
.capturer
|
.capturer
|
||||||
.create_session(
|
.create_session(
|
||||||
&CaptureSource::CosmicToplevel(source),
|
&CaptureSource::Toplevel(source),
|
||||||
CaptureOptions::empty(),
|
CaptureOptions::empty(),
|
||||||
&self.qh,
|
&self.qh,
|
||||||
SessionData {
|
SessionData {
|
||||||
|
|
@ -310,9 +310,6 @@ impl AppData {
|
||||||
wl_shm: self.shm_state.wl_shm().clone(),
|
wl_shm: self.shm_state.wl_shm().clone(),
|
||||||
capturer: self.screencopy_state.capturer().clone(),
|
capturer: self.screencopy_state.capturer().clone(),
|
||||||
};
|
};
|
||||||
let Some(cosmic_toplevel) = self.cosmic_toplevel(&handle) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
let name =
|
let name =
|
||||||
|
|
@ -323,7 +320,7 @@ impl AppData {
|
||||||
};
|
};
|
||||||
|
|
||||||
// XXX is this going to use to much memory?
|
// XXX is this going to use to much memory?
|
||||||
let img = capure_data.capture_source_shm_fd(false, cosmic_toplevel, fd, None);
|
let img = capure_data.capture_source_shm_fd(false, handle.clone(), fd, None);
|
||||||
if let Some(img) = img {
|
if let Some(img) = img {
|
||||||
let Ok(mut img) = img.image() else {
|
let Ok(mut img) = img.image() else {
|
||||||
tracing::error!("Failed to get RgbaImage");
|
tracing::error!("Failed to get RgbaImage");
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@ use cctk::{
|
||||||
sctk::{
|
sctk::{
|
||||||
self,
|
self,
|
||||||
output::{OutputHandler, OutputState},
|
output::{OutputHandler, OutputState},
|
||||||
reexports::{calloop, calloop_wayland_source::WaylandSource, client as wayland_client},
|
reexports::{
|
||||||
|
calloop, calloop_wayland_source::WaylandSource, client as wayland_client,
|
||||||
|
protocols::ext::workspace::v1::client::ext_workspace_handle_v1,
|
||||||
|
},
|
||||||
registry::{ProvidesRegistryState, RegistryState},
|
registry::{ProvidesRegistryState, RegistryState},
|
||||||
},
|
},
|
||||||
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
||||||
|
|
@ -15,7 +18,7 @@ use cctk::{
|
||||||
workspace::{WorkspaceHandler, WorkspaceState},
|
workspace::{WorkspaceHandler, WorkspaceState},
|
||||||
};
|
};
|
||||||
use cosmic::iced::futures;
|
use cosmic::iced::futures;
|
||||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::{self, TilingState};
|
use cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2::TilingState;
|
||||||
use futures::{channel::mpsc, executor::block_on, SinkExt};
|
use futures::{channel::mpsc, executor::block_on, SinkExt};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
|
|
@ -87,44 +90,41 @@ pub fn spawn_workspaces(tx: mpsc::Sender<TilingState>) -> SyncSender<AppRequest>
|
||||||
loop_handle
|
loop_handle
|
||||||
.insert_source(workspaces_rx, |e, _, state| match e {
|
.insert_source(workspaces_rx, |e, _, state| match e {
|
||||||
Event::Msg(AppRequest::TilingState(autotile)) => {
|
Event::Msg(AppRequest::TilingState(autotile)) => {
|
||||||
if let Some(w) =
|
if let Some(w) = state.workspace_state.workspace_groups().find_map(|g| {
|
||||||
state
|
if let Some(o) = state.expected_output.as_ref() {
|
||||||
.workspace_state
|
if !g.outputs.contains(o) {
|
||||||
.workspace_groups()
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.workspaces
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|g| {
|
.filter_map(|handle| state.workspace_state.workspace_info(handle))
|
||||||
if let Some(o) = state.expected_output.as_ref() {
|
.find(|w| w.state.contains(ext_workspace_handle_v1::State::Active))
|
||||||
if !g.outputs.contains(o) {
|
}) {
|
||||||
return None;
|
if let Some(cosmic_handle) = &w.cosmic_handle {
|
||||||
}
|
cosmic_handle.set_tiling_state(autotile);
|
||||||
}
|
state
|
||||||
g.workspaces.iter().find(|w| {
|
.workspace_state
|
||||||
w.state.contains(&WEnum::Value(
|
.workspace_manager()
|
||||||
zcosmic_workspace_handle_v1::State::Active,
|
.get()
|
||||||
))
|
.unwrap()
|
||||||
})
|
.commit();
|
||||||
})
|
}
|
||||||
{
|
|
||||||
w.handle.set_tiling_state(autotile);
|
|
||||||
state
|
|
||||||
.workspace_state
|
|
||||||
.workspace_manager()
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.commit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Msg(AppRequest::DefaultBehavior(tiling)) => {
|
Event::Msg(AppRequest::DefaultBehavior(tiling)) => {
|
||||||
for w in state
|
for w in state
|
||||||
.workspace_state
|
.workspace_state
|
||||||
.workspace_groups()
|
.workspace_groups()
|
||||||
.iter()
|
|
||||||
.flat_map(|g| g.workspaces.iter())
|
.flat_map(|g| g.workspaces.iter())
|
||||||
|
.filter_map(|handle| state.workspace_state.workspace_info(handle))
|
||||||
.filter(|w| {
|
.filter(|w| {
|
||||||
!state.workspaces_with_previous_toplevel.contains(&w.handle)
|
!state.workspaces_with_previous_toplevel.contains(&w.handle)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
w.handle.set_tiling_state(tiling);
|
if let Some(cosmic_handle) = &w.cosmic_handle {
|
||||||
|
cosmic_handle.set_tiling_state(tiling);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
state
|
state
|
||||||
.workspace_state
|
.workspace_state
|
||||||
|
|
@ -167,25 +167,22 @@ pub struct State {
|
||||||
registry_state: RegistryState,
|
registry_state: RegistryState,
|
||||||
workspace_state: WorkspaceState,
|
workspace_state: WorkspaceState,
|
||||||
toplevel_info_state: ToplevelInfoState,
|
toplevel_info_state: ToplevelInfoState,
|
||||||
workspaces_with_previous_toplevel:
|
workspaces_with_previous_toplevel: HashSet<ext_workspace_handle_v1::ExtWorkspaceHandleV1>,
|
||||||
HashSet<zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1>,
|
|
||||||
have_workspaces: bool,
|
have_workspaces: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn tiling_state(&self) -> Option<TilingState> {
|
pub fn tiling_state(&self) -> Option<TilingState> {
|
||||||
self.workspace_state
|
self.workspace_state.workspace_groups().find_map(|g| {
|
||||||
.workspace_groups()
|
if g.outputs
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|g| {
|
.any(|o| Some(o) == self.expected_output.as_ref())
|
||||||
if g.outputs
|
{
|
||||||
|
g.workspaces
|
||||||
.iter()
|
.iter()
|
||||||
.any(|o| Some(o) == self.expected_output.as_ref())
|
.filter_map(|handle| self.workspace_state.workspace_info(handle))
|
||||||
{
|
.find_map(|w| {
|
||||||
g.workspaces.iter().find_map(|w| {
|
if w.state.contains(ext_workspace_handle_v1::State::Active) {
|
||||||
if w.state
|
|
||||||
.contains(&WEnum::Value(zcosmic_workspace_handle_v1::State::Active))
|
|
||||||
{
|
|
||||||
w.tiling.and_then(|e| match e {
|
w.tiling.and_then(|e| match e {
|
||||||
WEnum::Value(v) => Some(v),
|
WEnum::Value(v) => Some(v),
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -197,10 +194,10 @@ impl State {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use cosmic::iced::{
|
||||||
futures::{self, channel::mpsc, SinkExt, StreamExt},
|
futures::{self, channel::mpsc, SinkExt, StreamExt},
|
||||||
stream, Subscription,
|
stream, Subscription,
|
||||||
};
|
};
|
||||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState;
|
use cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2::TilingState;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ use cosmic::{
|
||||||
Element,
|
Element,
|
||||||
};
|
};
|
||||||
use cosmic_comp_config::{CosmicCompConfig, TileBehavior};
|
use cosmic_comp_config::{CosmicCompConfig, TileBehavior};
|
||||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState;
|
use cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2::TilingState;
|
||||||
use cosmic_time::{anim, chain, id, Timeline};
|
use cosmic_time::{anim, chain, id, Timeline};
|
||||||
use std::{thread, time::Instant};
|
use std::{thread, time::Instant};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
// Copyright 2023 System76 <info@system76.com>
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use cctk::sctk::reexports::{calloop::channel::SyncSender, client::backend::ObjectId};
|
use cctk::{
|
||||||
|
sctk::reexports::{
|
||||||
|
calloop::channel::SyncSender,
|
||||||
|
protocols::ext::workspace::v1::client::ext_workspace_handle_v1::{
|
||||||
|
self, ExtWorkspaceHandleV1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
workspace::Workspace,
|
||||||
|
};
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
applet::cosmic_panel_config::PanelAnchor,
|
applet::cosmic_panel_config::PanelAnchor,
|
||||||
iced::{
|
iced::{
|
||||||
|
|
@ -17,17 +25,18 @@ use cosmic::{
|
||||||
Element, Task, Theme,
|
Element, Task, Theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config,
|
config,
|
||||||
wayland::{WorkspaceEvent, WorkspaceList},
|
wayland::WorkspaceEvent,
|
||||||
wayland_subscription::{workspaces, WorkspacesUpdate},
|
wayland_subscription::{workspaces, WorkspacesUpdate},
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::process::Command as ShellCommand;
|
use std::{
|
||||||
|
process::Command as ShellCommand,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
static AUTOSIZE_MAIN_ID: Lazy<Id> = Lazy::new(|| Id::new("autosize-main"));
|
static AUTOSIZE_MAIN_ID: Lazy<Id> = Lazy::new(|| Id::new("autosize-main"));
|
||||||
|
|
||||||
|
|
@ -43,9 +52,12 @@ pub enum Layout {
|
||||||
|
|
||||||
struct IcedWorkspacesApplet {
|
struct IcedWorkspacesApplet {
|
||||||
core: cosmic::app::Core,
|
core: cosmic::app::Core,
|
||||||
workspaces: WorkspaceList,
|
workspaces: Vec<Workspace>,
|
||||||
workspace_tx: Option<SyncSender<WorkspaceEvent>>,
|
workspace_tx: Option<SyncSender<WorkspaceEvent>>,
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
|
scroll: f64,
|
||||||
|
next_scroll: Option<Instant>,
|
||||||
|
last_scroll: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IcedWorkspacesApplet {
|
impl IcedWorkspacesApplet {
|
||||||
|
|
@ -78,7 +90,7 @@ impl IcedWorkspacesApplet {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Message {
|
enum Message {
|
||||||
WorkspaceUpdate(WorkspacesUpdate),
|
WorkspaceUpdate(WorkspacesUpdate),
|
||||||
WorkspacePressed(ObjectId),
|
WorkspacePressed(ExtWorkspaceHandleV1),
|
||||||
WheelScrolled(ScrollDelta),
|
WheelScrolled(ScrollDelta),
|
||||||
WorkspaceOverview,
|
WorkspaceOverview,
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +117,9 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
||||||
core,
|
core,
|
||||||
workspaces: Vec::new(),
|
workspaces: Vec::new(),
|
||||||
workspace_tx: Default::default(),
|
workspace_tx: Default::default(),
|
||||||
|
scroll: 0.0,
|
||||||
|
next_scroll: None,
|
||||||
|
last_scroll: Instant::now(),
|
||||||
},
|
},
|
||||||
Task::none(),
|
Task::none(),
|
||||||
)
|
)
|
||||||
|
|
@ -125,14 +140,8 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
||||||
match message {
|
match message {
|
||||||
Message::WorkspaceUpdate(msg) => match msg {
|
Message::WorkspaceUpdate(msg) => match msg {
|
||||||
WorkspacesUpdate::Workspaces(mut list) => {
|
WorkspacesUpdate::Workspaces(mut list) => {
|
||||||
list.retain(|w| {
|
list.retain(|w| !w.state.contains(ext_workspace_handle_v1::State::Hidden));
|
||||||
!matches!(w.1, Some(zcosmic_workspace_handle_v1::State::Hidden))
|
list.sort_by(|w1, w2| w1.coordinates.cmp(&w2.coordinates));
|
||||||
});
|
|
||||||
list.sort_by(|a, b| match a.0.len().cmp(&b.0.len()) {
|
|
||||||
Ordering::Equal => a.0.cmp(&b.0),
|
|
||||||
Ordering::Less => Ordering::Less,
|
|
||||||
Ordering::Greater => Ordering::Greater,
|
|
||||||
});
|
|
||||||
self.workspaces = list;
|
self.workspaces = list;
|
||||||
}
|
}
|
||||||
WorkspacesUpdate::Started(tx) => {
|
WorkspacesUpdate::Started(tx) => {
|
||||||
|
|
@ -152,8 +161,55 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
||||||
ScrollDelta::Lines { x, y } => ((x + y) as f64, false),
|
ScrollDelta::Lines { x, y } => ((x + y) as f64, false),
|
||||||
ScrollDelta::Pixels { x, y } => ((x + y) as f64, true),
|
ScrollDelta::Pixels { x, y } => ((x + y) as f64, true),
|
||||||
};
|
};
|
||||||
if let Some(tx) = self.workspace_tx.as_mut() {
|
|
||||||
let _ = tx.try_send(WorkspaceEvent::Scroll(delta, debounce));
|
let dur = if debounce {
|
||||||
|
Duration::from_millis(350)
|
||||||
|
} else {
|
||||||
|
Duration::from_millis(200)
|
||||||
|
};
|
||||||
|
if self.last_scroll.elapsed() > Duration::from_millis(100)
|
||||||
|
|| self.scroll * delta < 0.0
|
||||||
|
{
|
||||||
|
self.next_scroll = None;
|
||||||
|
self.scroll = 0.0;
|
||||||
|
}
|
||||||
|
self.last_scroll = Instant::now();
|
||||||
|
|
||||||
|
self.scroll += delta;
|
||||||
|
if let Some(next) = self.next_scroll {
|
||||||
|
if next > Instant::now() {
|
||||||
|
return cosmic::iced::Task::none();
|
||||||
|
}
|
||||||
|
self.next_scroll = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.scroll.abs() < 1.0 {
|
||||||
|
return cosmic::iced::Task::none();
|
||||||
|
}
|
||||||
|
self.next_scroll = Some(Instant::now() + dur);
|
||||||
|
if let Some(w_i) = self
|
||||||
|
.workspaces
|
||||||
|
.iter()
|
||||||
|
.position(|w| w.state.contains(ext_workspace_handle_v1::State::Active))
|
||||||
|
{
|
||||||
|
let max_w = self.workspaces.len().wrapping_sub(1);
|
||||||
|
let d_i = if self.scroll > 0.0 {
|
||||||
|
if w_i == 0 {
|
||||||
|
max_w
|
||||||
|
} else {
|
||||||
|
w_i.wrapping_sub(1)
|
||||||
|
}
|
||||||
|
} else if w_i == max_w {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
w_i.wrapping_add(1)
|
||||||
|
};
|
||||||
|
self.scroll = 0.0;
|
||||||
|
if let Some(w) = self.workspaces.get(d_i) {
|
||||||
|
if let Some(tx) = self.workspace_tx.as_mut() {
|
||||||
|
let _ = tx.try_send(WorkspaceEvent::Activate(w.handle.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::WorkspaceOverview => {
|
Message::WorkspaceOverview => {
|
||||||
|
|
@ -177,11 +233,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
||||||
let popup_index = self.popup_index().unwrap_or(self.workspaces.len());
|
let popup_index = self.popup_index().unwrap_or(self.workspaces.len());
|
||||||
|
|
||||||
let buttons = self.workspaces[..popup_index].iter().filter_map(|w| {
|
let buttons = self.workspaces[..popup_index].iter().filter_map(|w| {
|
||||||
let content = self
|
let content = self.core.applet.text(&w.name).font(cosmic::font::bold());
|
||||||
.core
|
|
||||||
.applet
|
|
||||||
.text(w.0.clone())
|
|
||||||
.font(cosmic::font::bold());
|
|
||||||
|
|
||||||
let (width, height) = if self.core.applet.is_horizontal() {
|
let (width, height) = if self.core.applet.is_horizontal() {
|
||||||
(suggested_total as f32, suggested_window_size.1.get() as f32)
|
(suggested_total as f32, suggested_window_size.1.get() as f32)
|
||||||
|
|
@ -205,18 +257,20 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
||||||
} else {
|
} else {
|
||||||
[self.core.applet.suggested_padding(true), 0]
|
[self.core.applet.suggested_padding(true), 0]
|
||||||
})
|
})
|
||||||
.on_press(match w.1 {
|
.on_press(
|
||||||
Some(zcosmic_workspace_handle_v1::State::Active) => Message::WorkspaceOverview,
|
if w.state.contains(ext_workspace_handle_v1::State::Active) {
|
||||||
_ => Message::WorkspacePressed(w.2.clone()),
|
Message::WorkspaceOverview
|
||||||
})
|
} else {
|
||||||
|
Message::WorkspacePressed(w.handle.clone())
|
||||||
|
},
|
||||||
|
)
|
||||||
.padding(0);
|
.padding(0);
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
btn.class(match w.1 {
|
btn.class(
|
||||||
Some(zcosmic_workspace_handle_v1::State::Active) => {
|
if w.state.contains(ext_workspace_handle_v1::State::Active) {
|
||||||
cosmic::theme::iced::Button::Primary
|
cosmic::theme::iced::Button::Primary
|
||||||
}
|
} else if w.state.contains(ext_workspace_handle_v1::State::Urgent) {
|
||||||
Some(zcosmic_workspace_handle_v1::State::Urgent) => {
|
|
||||||
let appearance = |theme: &Theme| {
|
let appearance = |theme: &Theme| {
|
||||||
let cosmic = theme.cosmic();
|
let cosmic = theme.cosmic();
|
||||||
button::Style {
|
button::Style {
|
||||||
|
|
@ -249,8 +303,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
||||||
button::Status::Disabled => appearance(theme),
|
button::Status::Disabled => appearance(theme),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
} else {
|
||||||
None => {
|
|
||||||
let appearance = |theme: &Theme| {
|
let appearance = |theme: &Theme| {
|
||||||
let cosmic = theme.cosmic();
|
let cosmic = theme.cosmic();
|
||||||
button::Style {
|
button::Style {
|
||||||
|
|
@ -282,9 +335,8 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
},
|
||||||
_ => return None,
|
)
|
||||||
})
|
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,35 +10,29 @@ use cctk::{
|
||||||
calloop,
|
calloop,
|
||||||
calloop_wayland_source::WaylandSource,
|
calloop_wayland_source::WaylandSource,
|
||||||
client::{self as wayland_client},
|
client::{self as wayland_client},
|
||||||
|
protocols::ext::workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1,
|
||||||
},
|
},
|
||||||
registry::{ProvidesRegistryState, RegistryState},
|
registry::{ProvidesRegistryState, RegistryState},
|
||||||
},
|
},
|
||||||
workspace::{WorkspaceHandler, WorkspaceState},
|
workspace::{Workspace, WorkspaceHandler, WorkspaceState},
|
||||||
};
|
};
|
||||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1;
|
|
||||||
use futures::{channel::mpsc, executor::block_on, SinkExt};
|
use futures::{channel::mpsc, executor::block_on, SinkExt};
|
||||||
use std::{
|
use std::os::{
|
||||||
os::{
|
fd::{FromRawFd, RawFd},
|
||||||
fd::{FromRawFd, RawFd},
|
unix::net::UnixStream,
|
||||||
unix::net::UnixStream,
|
|
||||||
},
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
};
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
backend::ObjectId,
|
|
||||||
globals::registry_queue_init,
|
globals::registry_queue_init,
|
||||||
protocol::wl_output::{self, WlOutput},
|
protocol::wl_output::{self, WlOutput},
|
||||||
Connection, Proxy, QueueHandle, WEnum,
|
Connection, QueueHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum WorkspaceEvent {
|
pub enum WorkspaceEvent {
|
||||||
Activate(ObjectId),
|
Activate(ExtWorkspaceHandleV1),
|
||||||
Scroll(f64, bool),
|
|
||||||
}
|
}
|
||||||
pub type WorkspaceList = Vec<(String, Option<zcosmic_workspace_handle_v1::State>, ObjectId)>;
|
|
||||||
|
|
||||||
pub fn spawn_workspaces(tx: mpsc::Sender<WorkspaceList>) -> SyncSender<WorkspaceEvent> {
|
pub fn spawn_workspaces(tx: mpsc::Sender<Vec<Workspace>>) -> SyncSender<WorkspaceEvent> {
|
||||||
let (workspaces_tx, workspaces_rx) = calloop::channel::sync_channel(100);
|
let (workspaces_tx, workspaces_rx) = calloop::channel::sync_channel(100);
|
||||||
|
|
||||||
let socket = std::env::var("X_PRIVILEGED_WAYLAND_SOCKET")
|
let socket = std::env::var("X_PRIVILEGED_WAYLAND_SOCKET")
|
||||||
|
|
@ -81,100 +75,18 @@ pub fn spawn_workspaces(tx: mpsc::Sender<WorkspaceList>) -> SyncSender<Workspace
|
||||||
tx,
|
tx,
|
||||||
running: true,
|
running: true,
|
||||||
have_workspaces: false,
|
have_workspaces: false,
|
||||||
scroll: 0.0,
|
|
||||||
next_scroll: None,
|
|
||||||
last_scroll: Instant::now(),
|
|
||||||
};
|
};
|
||||||
let loop_handle = event_loop.handle();
|
let loop_handle = event_loop.handle();
|
||||||
loop_handle
|
loop_handle
|
||||||
.insert_source(workspaces_rx, |e, _, state| match e {
|
.insert_source(workspaces_rx, |e, _, state| match e {
|
||||||
Event::Msg(WorkspaceEvent::Activate(id)) => {
|
Event::Msg(WorkspaceEvent::Activate(handle)) => {
|
||||||
if let Some(w) = state
|
handle.activate();
|
||||||
|
state
|
||||||
.workspace_state
|
.workspace_state
|
||||||
.workspace_groups()
|
.workspace_manager()
|
||||||
.iter()
|
.get()
|
||||||
.find_map(|g| g.workspaces.iter().find(|w| w.handle.id() == id))
|
.unwrap()
|
||||||
{
|
.commit();
|
||||||
w.handle.activate();
|
|
||||||
state
|
|
||||||
.workspace_state
|
|
||||||
.workspace_manager()
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Msg(WorkspaceEvent::Scroll(v, debounce)) => {
|
|
||||||
let dur = if debounce {
|
|
||||||
Duration::from_millis(350)
|
|
||||||
} else {
|
|
||||||
Duration::from_millis(200)
|
|
||||||
};
|
|
||||||
if state.last_scroll.elapsed() > Duration::from_millis(100)
|
|
||||||
|| state.scroll * v < 0.0
|
|
||||||
{
|
|
||||||
state.next_scroll = None;
|
|
||||||
state.scroll = 0.0;
|
|
||||||
}
|
|
||||||
state.last_scroll = Instant::now();
|
|
||||||
|
|
||||||
state.scroll += v;
|
|
||||||
if let Some(next) = state.next_scroll {
|
|
||||||
if next > Instant::now() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state.next_scroll = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.scroll.abs() < 1.0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state.next_scroll = Some(Instant::now() + dur);
|
|
||||||
if let Some((w_g, w_i)) = state
|
|
||||||
.workspace_state
|
|
||||||
.workspace_groups()
|
|
||||||
.iter()
|
|
||||||
.find_map(|g| {
|
|
||||||
if !g
|
|
||||||
.outputs
|
|
||||||
.iter()
|
|
||||||
.any(|o| Some(o) == state.expected_output.as_ref())
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
g.workspaces
|
|
||||||
.iter()
|
|
||||||
.position(|w| {
|
|
||||||
w.state.contains(&WEnum::Value(
|
|
||||||
zcosmic_workspace_handle_v1::State::Active,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.map(|w_i| (g, w_i))
|
|
||||||
})
|
|
||||||
{
|
|
||||||
let max_w = w_g.workspaces.len().wrapping_sub(1);
|
|
||||||
let d_i = if state.scroll > 0.0 {
|
|
||||||
if w_i == 0 {
|
|
||||||
max_w
|
|
||||||
} else {
|
|
||||||
w_i.wrapping_sub(1)
|
|
||||||
}
|
|
||||||
} else if w_i == max_w {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
w_i.wrapping_add(1)
|
|
||||||
};
|
|
||||||
state.scroll = 0.0;
|
|
||||||
if let Some(w) = w_g.workspaces.get(d_i) {
|
|
||||||
w.handle.activate();
|
|
||||||
state
|
|
||||||
.workspace_state
|
|
||||||
.workspace_manager()
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Event::Closed => {
|
Event::Closed => {
|
||||||
if let Ok(workspace_manager) =
|
if let Ok(workspace_manager) =
|
||||||
|
|
@ -203,62 +115,30 @@ pub fn spawn_workspaces(tx: mpsc::Sender<WorkspaceList>) -> SyncSender<Workspace
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
running: bool,
|
running: bool,
|
||||||
tx: mpsc::Sender<WorkspaceList>,
|
tx: mpsc::Sender<Vec<Workspace>>,
|
||||||
configured_output: String,
|
configured_output: String,
|
||||||
expected_output: Option<WlOutput>,
|
expected_output: Option<WlOutput>,
|
||||||
output_state: OutputState,
|
output_state: OutputState,
|
||||||
registry_state: RegistryState,
|
registry_state: RegistryState,
|
||||||
workspace_state: WorkspaceState,
|
workspace_state: WorkspaceState,
|
||||||
have_workspaces: bool,
|
have_workspaces: bool,
|
||||||
scroll: f64,
|
|
||||||
next_scroll: Option<Instant>,
|
|
||||||
last_scroll: Instant,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn workspace_list(
|
pub fn workspace_list(&self) -> Vec<Workspace> {
|
||||||
&self,
|
|
||||||
) -> Vec<(String, Option<zcosmic_workspace_handle_v1::State>, ObjectId)> {
|
|
||||||
self.workspace_state
|
self.workspace_state
|
||||||
.workspace_groups()
|
.workspace_groups()
|
||||||
.iter()
|
.filter(|g| {
|
||||||
.filter_map(|g| {
|
g.outputs
|
||||||
if g.outputs
|
|
||||||
.iter()
|
.iter()
|
||||||
.any(|o| Some(o) == self.expected_output.as_ref())
|
.any(|o| Some(o) == self.expected_output.as_ref())
|
||||||
{
|
|
||||||
Some(g.workspaces.iter().map(|w| {
|
|
||||||
(
|
|
||||||
w.name.clone(),
|
|
||||||
match &w.state {
|
|
||||||
x if x.contains(&WEnum::Value(
|
|
||||||
zcosmic_workspace_handle_v1::State::Active,
|
|
||||||
)) =>
|
|
||||||
{
|
|
||||||
Some(zcosmic_workspace_handle_v1::State::Active)
|
|
||||||
}
|
|
||||||
x if x.contains(&WEnum::Value(
|
|
||||||
zcosmic_workspace_handle_v1::State::Urgent,
|
|
||||||
)) =>
|
|
||||||
{
|
|
||||||
Some(zcosmic_workspace_handle_v1::State::Urgent)
|
|
||||||
}
|
|
||||||
x if x.contains(&WEnum::Value(
|
|
||||||
zcosmic_workspace_handle_v1::State::Hidden,
|
|
||||||
)) =>
|
|
||||||
{
|
|
||||||
Some(zcosmic_workspace_handle_v1::State::Hidden)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
w.handle.id(),
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.flatten()
|
.flat_map(|g| {
|
||||||
|
g.workspaces
|
||||||
|
.iter()
|
||||||
|
.filter_map(|handle| self.workspace_state.workspace_info(handle))
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2023 System76 <info@system76.com>
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::wayland::{self, WorkspaceEvent, WorkspaceList};
|
use crate::wayland::{self, WorkspaceEvent};
|
||||||
use cctk::sctk::reexports::calloop::channel::SyncSender;
|
use cctk::{sctk::reexports::calloop::channel::SyncSender, workspace::Workspace};
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
self,
|
self,
|
||||||
futures::{channel::mpsc, SinkExt, StreamExt},
|
futures::{channel::mpsc, SinkExt, StreamExt},
|
||||||
|
|
@ -11,12 +11,12 @@ use cosmic::iced::{
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
pub static WAYLAND_RX: Lazy<Mutex<Option<mpsc::Receiver<WorkspaceList>>>> =
|
pub static WAYLAND_RX: Lazy<Mutex<Option<mpsc::Receiver<Vec<Workspace>>>>> =
|
||||||
Lazy::new(|| Mutex::new(None));
|
Lazy::new(|| Mutex::new(None));
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum WorkspacesUpdate {
|
pub enum WorkspacesUpdate {
|
||||||
Workspaces(WorkspaceList),
|
Workspaces(Vec<Workspace>),
|
||||||
Started(SyncSender<WorkspaceEvent>),
|
Started(SyncSender<WorkspaceEvent>),
|
||||||
Errored,
|
Errored,
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +71,7 @@ pub enum State {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WorkspacesWatcher {
|
pub struct WorkspacesWatcher {
|
||||||
rx: mpsc::Receiver<WorkspaceList>,
|
rx: mpsc::Receiver<Vec<Workspace>>,
|
||||||
tx: SyncSender<WorkspaceEvent>,
|
tx: SyncSender<WorkspaceEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue