cosmic-applets/cosmic-applet-workspaces/src/wayland.rs

279 lines
10 KiB
Rust
Raw Normal View History

2022-07-19 23:39:19 -04:00
use calloop::channel::*;
use cosmic_client_toolkit::{
sctk::{
self,
output::{OutputHandler, OutputState},
reexports::client::WaylandSource,
registry::{ProvidesRegistryState, RegistryState},
},
workspace::{WorkspaceHandler, WorkspaceState},
2022-07-19 23:39:19 -04:00
};
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1;
2022-11-29 16:52:31 -05:00
use futures::{channel::mpsc, executor::block_on, SinkExt};
use std::{env, os::unix::net::UnixStream, path::PathBuf, time::Duration};
2022-11-29 16:52:31 -05:00
use wayland_backend::client::ObjectId;
2022-06-16 10:48:15 -04:00
use wayland_client::{
globals::registry_queue_init,
protocol::wl_output::{self, WlOutput},
2022-11-29 16:52:31 -05:00
ConnectError, Proxy,
2022-06-16 10:48:15 -04:00
};
use wayland_client::{Connection, QueueHandle, WEnum};
2022-06-16 11:55:31 -04:00
2022-11-29 16:52:31 -05:00
#[derive(Debug, Clone)]
pub enum WorkspaceEvent {
Activate(ObjectId),
Scroll(f64),
}
pub type WorkspaceList = Vec<(String, Option<zcosmic_workspace_handle_v1::State>, ObjectId)>;
pub fn spawn_workspaces(tx: mpsc::Sender<WorkspaceList>) -> SyncSender<WorkspaceEvent> {
2022-07-19 11:33:19 -04:00
let (workspaces_tx, workspaces_rx) = calloop::channel::sync_channel(100);
2022-06-20 16:15:19 -04:00
2022-07-19 11:33:19 -04:00
if let Ok(Ok(conn)) = std::env::var("WAYLAND_DISPLAY")
.map_err(anyhow::Error::msg)
2022-06-16 12:23:25 -04:00
.map(|display_str| {
2022-06-16 10:48:15 -04:00
let mut socket_path = env::var_os("XDG_RUNTIME_DIR")
.map(Into::<PathBuf>::into)
.ok_or(ConnectError::NoCompositor)?;
2022-06-16 12:23:25 -04:00
socket_path.push(display_str);
2022-06-16 10:48:15 -04:00
Ok(UnixStream::connect(socket_path).map_err(|_| ConnectError::NoCompositor)?)
})
.and_then(|s| s.map(|s| Connection::from_socket(s).map_err(anyhow::Error::msg)))
{
std::thread::spawn(move || {
let configured_output = std::env::var("COSMIC_PANEL_OUTPUT")
2022-08-16 14:36:31 -04:00
.ok()
.unwrap_or_default();
2022-06-20 13:15:12 -04:00
let mut event_loop = calloop::EventLoop::<State>::try_new().unwrap();
let loop_handle = event_loop.handle();
let (globals, event_queue) = registry_queue_init(&conn).unwrap();
2022-06-16 10:48:15 -04:00
let qhandle = event_queue.handle();
2022-06-20 15:48:27 -04:00
WaylandSource::new(event_queue)
.expect("Failed to create wayland source")
.insert(loop_handle)
.unwrap();
2022-06-20 13:15:12 -04:00
let registry_state = RegistryState::new(&globals);
2022-06-16 10:48:15 -04:00
let mut state = State {
// Must be before `WorkspaceState`
output_state: OutputState::new(&globals, &qhandle),
configured_output,
workspace_state: WorkspaceState::new(&registry_state, &qhandle),
registry_state,
expected_output: None,
2022-06-16 10:48:15 -04:00
tx,
running: true,
have_workspaces: false,
2022-06-16 10:48:15 -04:00
};
2022-06-23 12:15:08 -04:00
let loop_handle = event_loop.handle();
loop_handle
.insert_source(workspaces_rx, |e, _, state| match e {
2022-06-23 12:15:08 -04:00
Event::Msg(WorkspaceEvent::Activate(id)) => {
if let Some(w) = 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();
2022-06-20 14:42:12 -04:00
}
2022-06-23 12:15:08 -04:00
}
Event::Msg(WorkspaceEvent::Scroll(v)) => {
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))
})
{
2022-06-23 12:15:08 -04:00
let max_w = w_g.workspaces.len().wrapping_sub(1);
let d_i = if v > 0.0 {
if w_i == max_w {
0
2022-06-20 15:48:27 -04:00
} else {
2022-06-23 12:15:08 -04:00
w_i.wrapping_add(1)
2022-06-20 14:42:12 -04:00
}
2023-01-05 10:09:10 -08:00
} else if w_i == 0 {
max_w
2022-06-23 12:15:08 -04:00
} else {
2023-01-05 10:09:10 -08:00
w_i.wrapping_sub(1)
2022-06-23 12:15:08 -04:00
};
if let Some(w) = w_g.workspaces.get(d_i) {
w.handle.activate();
state
.workspace_state
.workspace_manager()
.get()
.unwrap()
.commit();
2022-06-20 14:42:12 -04:00
}
}
2022-06-16 14:33:26 -04:00
}
Event::Closed => {
if let Ok(workspace_manager) =
state.workspace_state.workspace_manager().get()
{
for g in state.workspace_state.workspace_groups() {
g.handle.destroy();
}
workspace_manager.stop();
2022-06-23 12:15:08 -04:00
}
}
})
.unwrap();
2022-06-23 12:15:08 -04:00
while state.running {
2022-06-20 15:48:27 -04:00
event_loop
.dispatch(Duration::from_millis(16), &mut state)
.unwrap();
}
});
} else {
2022-07-19 11:33:19 -04:00
eprintln!("ENV variable WAYLAND_DISPLAY is missing. Exiting...");
std::process::exit(1);
}
workspaces_tx
}
2022-06-15 16:51:08 -04:00
#[derive(Debug)]
2022-06-16 12:08:31 -04:00
pub struct State {
2022-06-16 10:48:15 -04:00
running: bool,
2022-11-29 16:52:31 -05:00
tx: mpsc::Sender<WorkspaceList>,
configured_output: String,
expected_output: Option<WlOutput>,
output_state: OutputState,
registry_state: RegistryState,
workspace_state: WorkspaceState,
have_workspaces: bool,
2022-06-16 11:55:31 -04:00
}
impl State {
2022-11-29 16:52:31 -05:00
pub fn workspace_list(
&self,
) -> Vec<(String, Option<zcosmic_workspace_handle_v1::State>, ObjectId)> {
self.workspace_state
.workspace_groups()
2022-06-20 15:48:27 -04:00
.iter()
.filter_map(|g| {
if g.outputs
.iter()
.any(|o| Some(o) == self.expected_output.as_ref())
{
2022-07-19 23:39:19 -04:00
Some(g.workspaces.iter().map(|w| {
(
w.name.clone(),
match &w.state {
x if x.contains(&WEnum::Value(
zcosmic_workspace_handle_v1::State::Active,
)) =>
{
2022-11-29 16:52:31 -05:00
Some(zcosmic_workspace_handle_v1::State::Active)
}
x if x.contains(&WEnum::Value(
zcosmic_workspace_handle_v1::State::Urgent,
)) =>
{
2022-11-29 16:52:31 -05:00
Some(zcosmic_workspace_handle_v1::State::Urgent)
}
x if x.contains(&WEnum::Value(
zcosmic_workspace_handle_v1::State::Hidden,
)) =>
{
2022-11-29 16:52:31 -05:00
Some(zcosmic_workspace_handle_v1::State::Hidden)
}
_ => None,
2022-07-19 23:39:19 -04:00
},
w.handle.id(),
2022-07-19 23:39:19 -04:00
)
}))
} else {
None
}
})
2022-06-20 15:48:27 -04:00
.flatten()
2022-11-29 16:52:31 -05:00
.collect()
}
}
impl ProvidesRegistryState for State {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
sctk::registry_handlers![OutputState,];
2022-06-16 11:55:31 -04:00
}
impl OutputHandler for State {
fn output_state(&mut self) -> &mut OutputState {
&mut self.output_state
}
2022-06-15 16:51:08 -04:00
fn new_output(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
output: wl_output::WlOutput,
2022-06-16 10:48:15 -04:00
) {
let info = self.output_state.info(&output).unwrap();
if info.name.as_deref() == Some(&self.configured_output) {
self.expected_output = Some(output);
if self.have_workspaces {
let _ = block_on(self.tx.send(self.workspace_list()));
2022-06-16 10:48:15 -04:00
}
}
}
2022-06-15 16:51:08 -04:00
fn update_output(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
2022-06-16 10:48:15 -04:00
) {
}
fn output_destroyed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
2022-06-16 11:55:31 -04:00
) {
}
}
impl WorkspaceHandler for State {
fn workspace_state(&mut self) -> &mut WorkspaceState {
&mut self.workspace_state
2022-06-16 11:55:31 -04:00
}
2022-06-16 12:23:25 -04:00
fn done(&mut self) {
self.have_workspaces = true;
let _ = block_on(self.tx.send(self.workspace_list()));
2022-06-20 15:48:27 -04:00
}
2022-11-29 16:52:31 -05:00
}
cosmic_client_toolkit::delegate_workspace!(State);
sctk::delegate_output!(State);
sctk::delegate_registry!(State);