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]]
|
||||
name = "cosmic-client-toolkit"
|
||||
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 = [
|
||||
"bitflags 2.8.0",
|
||||
"cosmic-protocols",
|
||||
"libc",
|
||||
"smithay-client-toolkit",
|
||||
|
|
@ -1534,7 +1535,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-protocols"
|
||||
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 = [
|
||||
"bitflags 2.8.0",
|
||||
"wayland-backend",
|
||||
|
|
@ -2148,7 +2149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3946,7 +3947,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5530,7 +5531,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6055,7 +6056,7 @@ dependencies = [
|
|||
"getrandom 0.3.1",
|
||||
"once_cell",
|
||||
"rustix 0.38.44",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6988,7 +6989,7 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -86,5 +86,5 @@ ignored = ["libcosmic"]
|
|||
sctk = { package = "smithay-client-toolkit", version = "=0.19.2" }
|
||||
|
||||
[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", rev = "ed2a481" }
|
||||
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "main" }
|
||||
cosmic-client-toolkit = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "main" }
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ use cctk::{
|
|||
wayland_client::protocol::{
|
||||
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::{
|
||||
applet::{
|
||||
|
|
@ -44,10 +47,7 @@ use cosmic::{
|
|||
Apply, Element, Task,
|
||||
};
|
||||
use cosmic_app_list_config::{AppListConfig, APP_ID};
|
||||
use cosmic_protocols::{
|
||||
toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State,
|
||||
workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
|
||||
};
|
||||
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State;
|
||||
use freedesktop_desktop_entry as fde;
|
||||
use freedesktop_desktop_entry::{get_languages_from_env, DesktopEntry};
|
||||
use futures::future::pending;
|
||||
|
|
@ -327,7 +327,7 @@ struct CosmicAppList {
|
|||
dnd_offer: Option<DndOffer>,
|
||||
is_listening_for_dnd: bool,
|
||||
gpus: Option<Vec<Gpu>>,
|
||||
active_workspaces: Vec<ZcosmicWorkspaceHandleV1>,
|
||||
active_workspaces: Vec<ExtWorkspaceHandleV1>,
|
||||
output_list: HashMap<WlOutput, OutputInfo>,
|
||||
locales: Vec<String>,
|
||||
overflow_favorites_popup: Option<window::Id>,
|
||||
|
|
|
|||
|
|
@ -39,13 +39,15 @@ use cctk::{
|
|||
},
|
||||
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},
|
||||
};
|
||||
use cosmic_protocols::{
|
||||
toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
|
||||
toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
|
||||
workspace::v1::client::zcosmic_workspace_handle_v1::State as WorkspaceUpdateState,
|
||||
};
|
||||
use futures::channel::mpsc::UnboundedSender;
|
||||
use sctk::{
|
||||
|
|
@ -129,12 +131,11 @@ impl WorkspaceHandler for AppData {
|
|||
let active_workspaces = self
|
||||
.workspace_state
|
||||
.workspace_groups()
|
||||
.iter()
|
||||
.filter_map(|x| {
|
||||
x.workspaces.iter().find(|w| {
|
||||
w.state
|
||||
.contains(&WEnum::Value(WorkspaceUpdateState::Active))
|
||||
})
|
||||
x.workspaces
|
||||
.iter()
|
||||
.filter_map(|handle| self.workspace_state.workspace_info(handle))
|
||||
.find(|w| w.state.contains(WorkspaceUpdateState::Active))
|
||||
})
|
||||
.map(|workspace| workspace.handle.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
|
@ -364,7 +365,7 @@ impl CaptureData {
|
|||
pub fn capture_source_shm_fd<Fd: AsFd>(
|
||||
&self,
|
||||
overlay_cursor: bool,
|
||||
source: ZcosmicToplevelHandleV1,
|
||||
source: &ExtForeignToplevelHandleV1,
|
||||
fd: Fd,
|
||||
len: Option<u32>,
|
||||
) -> Option<ShmImage<Fd>> {
|
||||
|
|
@ -379,7 +380,7 @@ impl CaptureData {
|
|||
let capture_session = self
|
||||
.capturer
|
||||
.create_session(
|
||||
&CaptureSource::CosmicToplevel(source),
|
||||
&CaptureSource::Toplevel(source.clone()),
|
||||
CaptureOptions::empty(),
|
||||
&self.qh,
|
||||
SessionData {
|
||||
|
|
@ -494,9 +495,6 @@ impl AppData {
|
|||
wl_shm: self.shm_state.wl_shm().clone(),
|
||||
capturer: self.screencopy_state.capturer().clone(),
|
||||
};
|
||||
let Some(cosmic_toplevel) = self.cosmic_toplevel(&handle) else {
|
||||
return;
|
||||
};
|
||||
std::thread::spawn(move || {
|
||||
use std::ffi::CStr;
|
||||
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?
|
||||
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 {
|
||||
let Ok(img) = img.image() else {
|
||||
tracing::error!("Failed to get RgbaImage");
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@ use cctk::{
|
|||
sctk::{output::OutputInfo, reexports::calloop},
|
||||
toplevel_info::ToplevelInfo,
|
||||
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::{
|
||||
iced::{self, stream, Subscription},
|
||||
iced_core::image::Bytes,
|
||||
};
|
||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1;
|
||||
use image::EncodableLayout;
|
||||
|
||||
use futures::{
|
||||
|
|
@ -110,7 +112,7 @@ pub enum WaylandUpdate {
|
|||
Init(calloop::channel::Sender<WaylandRequest>),
|
||||
Finished,
|
||||
Toplevel(ToplevelUpdate),
|
||||
Workspace(Vec<ZcosmicWorkspaceHandleV1>),
|
||||
Workspace(Vec<ExtWorkspaceHandleV1>),
|
||||
Output(OutputUpdate),
|
||||
ActivationToken {
|
||||
token: Option<String>,
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ impl CaptureData {
|
|||
pub fn capture_source_shm_fd<Fd: AsFd>(
|
||||
&self,
|
||||
overlay_cursor: bool,
|
||||
source: ZcosmicToplevelHandleV1,
|
||||
source: ExtForeignToplevelHandleV1,
|
||||
fd: Fd,
|
||||
len: Option<u32>,
|
||||
) -> Option<ShmImage<Fd>> {
|
||||
|
|
@ -145,7 +145,7 @@ impl CaptureData {
|
|||
let capture_session = self
|
||||
.capturer
|
||||
.create_session(
|
||||
&CaptureSource::CosmicToplevel(source),
|
||||
&CaptureSource::Toplevel(source),
|
||||
CaptureOptions::empty(),
|
||||
&self.qh,
|
||||
SessionData {
|
||||
|
|
@ -310,9 +310,6 @@ impl AppData {
|
|||
wl_shm: self.shm_state.wl_shm().clone(),
|
||||
capturer: self.screencopy_state.capturer().clone(),
|
||||
};
|
||||
let Some(cosmic_toplevel) = self.cosmic_toplevel(&handle) else {
|
||||
return;
|
||||
};
|
||||
std::thread::spawn(move || {
|
||||
use std::ffi::CStr;
|
||||
let name =
|
||||
|
|
@ -323,7 +320,7 @@ impl AppData {
|
|||
};
|
||||
|
||||
// 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 {
|
||||
let Ok(mut img) = img.image() else {
|
||||
tracing::error!("Failed to get RgbaImage");
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@ use cctk::{
|
|||
sctk::{
|
||||
self,
|
||||
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},
|
||||
},
|
||||
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
||||
|
|
@ -15,7 +18,7 @@ use cctk::{
|
|||
workspace::{WorkspaceHandler, WorkspaceState},
|
||||
};
|
||||
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 std::{
|
||||
collections::HashSet,
|
||||
|
|
@ -87,44 +90,41 @@ pub fn spawn_workspaces(tx: mpsc::Sender<TilingState>) -> SyncSender<AppRequest>
|
|||
loop_handle
|
||||
.insert_source(workspaces_rx, |e, _, state| match e {
|
||||
Event::Msg(AppRequest::TilingState(autotile)) => {
|
||||
if let Some(w) =
|
||||
state
|
||||
.workspace_state
|
||||
.workspace_groups()
|
||||
if let Some(w) = state.workspace_state.workspace_groups().find_map(|g| {
|
||||
if let Some(o) = state.expected_output.as_ref() {
|
||||
if !g.outputs.contains(o) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
g.workspaces
|
||||
.iter()
|
||||
.find_map(|g| {
|
||||
if let Some(o) = state.expected_output.as_ref() {
|
||||
if !g.outputs.contains(o) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
g.workspaces.iter().find(|w| {
|
||||
w.state.contains(&WEnum::Value(
|
||||
zcosmic_workspace_handle_v1::State::Active,
|
||||
))
|
||||
})
|
||||
})
|
||||
{
|
||||
w.handle.set_tiling_state(autotile);
|
||||
state
|
||||
.workspace_state
|
||||
.workspace_manager()
|
||||
.get()
|
||||
.unwrap()
|
||||
.commit();
|
||||
.filter_map(|handle| state.workspace_state.workspace_info(handle))
|
||||
.find(|w| w.state.contains(ext_workspace_handle_v1::State::Active))
|
||||
}) {
|
||||
if let Some(cosmic_handle) = &w.cosmic_handle {
|
||||
cosmic_handle.set_tiling_state(autotile);
|
||||
state
|
||||
.workspace_state
|
||||
.workspace_manager()
|
||||
.get()
|
||||
.unwrap()
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Msg(AppRequest::DefaultBehavior(tiling)) => {
|
||||
for w in state
|
||||
.workspace_state
|
||||
.workspace_groups()
|
||||
.iter()
|
||||
.flat_map(|g| g.workspaces.iter())
|
||||
.filter_map(|handle| state.workspace_state.workspace_info(handle))
|
||||
.filter(|w| {
|
||||
!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
|
||||
.workspace_state
|
||||
|
|
@ -167,25 +167,22 @@ pub struct State {
|
|||
registry_state: RegistryState,
|
||||
workspace_state: WorkspaceState,
|
||||
toplevel_info_state: ToplevelInfoState,
|
||||
workspaces_with_previous_toplevel:
|
||||
HashSet<zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1>,
|
||||
workspaces_with_previous_toplevel: HashSet<ext_workspace_handle_v1::ExtWorkspaceHandleV1>,
|
||||
have_workspaces: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn tiling_state(&self) -> Option<TilingState> {
|
||||
self.workspace_state
|
||||
.workspace_groups()
|
||||
.iter()
|
||||
.find_map(|g| {
|
||||
if g.outputs
|
||||
self.workspace_state.workspace_groups().find_map(|g| {
|
||||
if g.outputs
|
||||
.iter()
|
||||
.any(|o| Some(o) == self.expected_output.as_ref())
|
||||
{
|
||||
g.workspaces
|
||||
.iter()
|
||||
.any(|o| Some(o) == self.expected_output.as_ref())
|
||||
{
|
||||
g.workspaces.iter().find_map(|w| {
|
||||
if w.state
|
||||
.contains(&WEnum::Value(zcosmic_workspace_handle_v1::State::Active))
|
||||
{
|
||||
.filter_map(|handle| self.workspace_state.workspace_info(handle))
|
||||
.find_map(|w| {
|
||||
if w.state.contains(ext_workspace_handle_v1::State::Active) {
|
||||
w.tiling.and_then(|e| match e {
|
||||
WEnum::Value(v) => Some(v),
|
||||
_ => {
|
||||
|
|
@ -197,10 +194,10 @@ impl State {
|
|||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use cosmic::iced::{
|
|||
futures::{self, channel::mpsc, SinkExt, StreamExt},
|
||||
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 tokio::sync::Mutex;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use cosmic::{
|
|||
Element,
|
||||
};
|
||||
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 std::{thread, time::Instant};
|
||||
use tracing::error;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// 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::{
|
||||
applet::cosmic_panel_config::PanelAnchor,
|
||||
iced::{
|
||||
|
|
@ -17,17 +25,18 @@ use cosmic::{
|
|||
Element, Task, Theme,
|
||||
};
|
||||
|
||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::{
|
||||
config,
|
||||
wayland::{WorkspaceEvent, WorkspaceList},
|
||||
wayland::WorkspaceEvent,
|
||||
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"));
|
||||
|
||||
|
|
@ -43,9 +52,12 @@ pub enum Layout {
|
|||
|
||||
struct IcedWorkspacesApplet {
|
||||
core: cosmic::app::Core,
|
||||
workspaces: WorkspaceList,
|
||||
workspaces: Vec<Workspace>,
|
||||
workspace_tx: Option<SyncSender<WorkspaceEvent>>,
|
||||
layout: Layout,
|
||||
scroll: f64,
|
||||
next_scroll: Option<Instant>,
|
||||
last_scroll: Instant,
|
||||
}
|
||||
|
||||
impl IcedWorkspacesApplet {
|
||||
|
|
@ -78,7 +90,7 @@ impl IcedWorkspacesApplet {
|
|||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
WorkspaceUpdate(WorkspacesUpdate),
|
||||
WorkspacePressed(ObjectId),
|
||||
WorkspacePressed(ExtWorkspaceHandleV1),
|
||||
WheelScrolled(ScrollDelta),
|
||||
WorkspaceOverview,
|
||||
}
|
||||
|
|
@ -105,6 +117,9 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
core,
|
||||
workspaces: Vec::new(),
|
||||
workspace_tx: Default::default(),
|
||||
scroll: 0.0,
|
||||
next_scroll: None,
|
||||
last_scroll: Instant::now(),
|
||||
},
|
||||
Task::none(),
|
||||
)
|
||||
|
|
@ -125,14 +140,8 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
match message {
|
||||
Message::WorkspaceUpdate(msg) => match msg {
|
||||
WorkspacesUpdate::Workspaces(mut list) => {
|
||||
list.retain(|w| {
|
||||
!matches!(w.1, Some(zcosmic_workspace_handle_v1::State::Hidden))
|
||||
});
|
||||
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,
|
||||
});
|
||||
list.retain(|w| !w.state.contains(ext_workspace_handle_v1::State::Hidden));
|
||||
list.sort_by(|w1, w2| w1.coordinates.cmp(&w2.coordinates));
|
||||
self.workspaces = list;
|
||||
}
|
||||
WorkspacesUpdate::Started(tx) => {
|
||||
|
|
@ -152,8 +161,55 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
ScrollDelta::Lines { x, y } => ((x + y) as f64, false),
|
||||
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 => {
|
||||
|
|
@ -177,11 +233,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
let popup_index = self.popup_index().unwrap_or(self.workspaces.len());
|
||||
|
||||
let buttons = self.workspaces[..popup_index].iter().filter_map(|w| {
|
||||
let content = self
|
||||
.core
|
||||
.applet
|
||||
.text(w.0.clone())
|
||||
.font(cosmic::font::bold());
|
||||
let content = self.core.applet.text(&w.name).font(cosmic::font::bold());
|
||||
|
||||
let (width, height) = if self.core.applet.is_horizontal() {
|
||||
(suggested_total as f32, suggested_window_size.1.get() as f32)
|
||||
|
|
@ -205,18 +257,20 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
} else {
|
||||
[self.core.applet.suggested_padding(true), 0]
|
||||
})
|
||||
.on_press(match w.1 {
|
||||
Some(zcosmic_workspace_handle_v1::State::Active) => Message::WorkspaceOverview,
|
||||
_ => Message::WorkspacePressed(w.2.clone()),
|
||||
})
|
||||
.on_press(
|
||||
if w.state.contains(ext_workspace_handle_v1::State::Active) {
|
||||
Message::WorkspaceOverview
|
||||
} else {
|
||||
Message::WorkspacePressed(w.handle.clone())
|
||||
},
|
||||
)
|
||||
.padding(0);
|
||||
|
||||
Some(
|
||||
btn.class(match w.1 {
|
||||
Some(zcosmic_workspace_handle_v1::State::Active) => {
|
||||
btn.class(
|
||||
if w.state.contains(ext_workspace_handle_v1::State::Active) {
|
||||
cosmic::theme::iced::Button::Primary
|
||||
}
|
||||
Some(zcosmic_workspace_handle_v1::State::Urgent) => {
|
||||
} else if w.state.contains(ext_workspace_handle_v1::State::Urgent) {
|
||||
let appearance = |theme: &Theme| {
|
||||
let cosmic = theme.cosmic();
|
||||
button::Style {
|
||||
|
|
@ -249,8 +303,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
button::Status::Disabled => appearance(theme),
|
||||
}
|
||||
}))
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
let appearance = |theme: &Theme| {
|
||||
let cosmic = theme.cosmic();
|
||||
button::Style {
|
||||
|
|
@ -282,9 +335,8 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,35 +10,29 @@ use cctk::{
|
|||
calloop,
|
||||
calloop_wayland_source::WaylandSource,
|
||||
client::{self as wayland_client},
|
||||
protocols::ext::workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1,
|
||||
},
|
||||
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 std::{
|
||||
os::{
|
||||
fd::{FromRawFd, RawFd},
|
||||
unix::net::UnixStream,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
use std::os::{
|
||||
fd::{FromRawFd, RawFd},
|
||||
unix::net::UnixStream,
|
||||
};
|
||||
use wayland_client::{
|
||||
backend::ObjectId,
|
||||
globals::registry_queue_init,
|
||||
protocol::wl_output::{self, WlOutput},
|
||||
Connection, Proxy, QueueHandle, WEnum,
|
||||
Connection, QueueHandle,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WorkspaceEvent {
|
||||
Activate(ObjectId),
|
||||
Scroll(f64, bool),
|
||||
Activate(ExtWorkspaceHandleV1),
|
||||
}
|
||||
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 socket = std::env::var("X_PRIVILEGED_WAYLAND_SOCKET")
|
||||
|
|
@ -81,100 +75,18 @@ pub fn spawn_workspaces(tx: mpsc::Sender<WorkspaceList>) -> SyncSender<Workspace
|
|||
tx,
|
||||
running: true,
|
||||
have_workspaces: false,
|
||||
scroll: 0.0,
|
||||
next_scroll: None,
|
||||
last_scroll: Instant::now(),
|
||||
};
|
||||
let loop_handle = event_loop.handle();
|
||||
loop_handle
|
||||
.insert_source(workspaces_rx, |e, _, state| match e {
|
||||
Event::Msg(WorkspaceEvent::Activate(id)) => {
|
||||
if let Some(w) = state
|
||||
Event::Msg(WorkspaceEvent::Activate(handle)) => {
|
||||
handle.activate();
|
||||
state
|
||||
.workspace_state
|
||||
.workspace_groups()
|
||||
.iter()
|
||||
.find_map(|g| g.workspaces.iter().find(|w| w.handle.id() == id))
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
.workspace_manager()
|
||||
.get()
|
||||
.unwrap()
|
||||
.commit();
|
||||
}
|
||||
Event::Closed => {
|
||||
if let Ok(workspace_manager) =
|
||||
|
|
@ -203,62 +115,30 @@ pub fn spawn_workspaces(tx: mpsc::Sender<WorkspaceList>) -> SyncSender<Workspace
|
|||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
running: bool,
|
||||
tx: mpsc::Sender<WorkspaceList>,
|
||||
tx: mpsc::Sender<Vec<Workspace>>,
|
||||
configured_output: String,
|
||||
expected_output: Option<WlOutput>,
|
||||
output_state: OutputState,
|
||||
registry_state: RegistryState,
|
||||
workspace_state: WorkspaceState,
|
||||
have_workspaces: bool,
|
||||
scroll: f64,
|
||||
next_scroll: Option<Instant>,
|
||||
last_scroll: Instant,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn workspace_list(
|
||||
&self,
|
||||
) -> Vec<(String, Option<zcosmic_workspace_handle_v1::State>, ObjectId)> {
|
||||
pub fn workspace_list(&self) -> Vec<Workspace> {
|
||||
self.workspace_state
|
||||
.workspace_groups()
|
||||
.iter()
|
||||
.filter_map(|g| {
|
||||
if g.outputs
|
||||
.filter(|g| {
|
||||
g.outputs
|
||||
.iter()
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::wayland::{self, WorkspaceEvent, WorkspaceList};
|
||||
use cctk::sctk::reexports::calloop::channel::SyncSender;
|
||||
use crate::wayland::{self, WorkspaceEvent};
|
||||
use cctk::{sctk::reexports::calloop::channel::SyncSender, workspace::Workspace};
|
||||
use cosmic::iced::{
|
||||
self,
|
||||
futures::{channel::mpsc, SinkExt, StreamExt},
|
||||
|
|
@ -11,12 +11,12 @@ use cosmic::iced::{
|
|||
use once_cell::sync::Lazy;
|
||||
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));
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WorkspacesUpdate {
|
||||
Workspaces(WorkspaceList),
|
||||
Workspaces(Vec<Workspace>),
|
||||
Started(SyncSender<WorkspaceEvent>),
|
||||
Errored,
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ pub enum State {
|
|||
}
|
||||
|
||||
pub struct WorkspacesWatcher {
|
||||
rx: mpsc::Receiver<WorkspaceList>,
|
||||
rx: mpsc::Receiver<Vec<Workspace>>,
|
||||
tx: SyncSender<WorkspaceEvent>,
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue