diff --git a/rust/Cargo.lock b/rust/Cargo.lock index d1f3231..fd84da8 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -151,6 +151,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.6" @@ -610,7 +625,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" name = "rust_lib_flight" version = "0.1.0" dependencies = [ + "crossbeam-channel", "flutter_rust_bridge", + "libc", "serde", "serde_json", "wayland-client", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 9da6ce3..aa00f70 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -13,3 +13,5 @@ wayland-client = "0.31" serde = { version = "1", features = ["derive"] } serde_json = "1" wayland-protocols = { version = "0.32.12", features = ["client", "staging"] } +libc = "0.2.185" +crossbeam-channel = "0.5.15" diff --git a/rust/src/api/workspace_api.rs b/rust/src/api/workspace_api.rs index 4ddf2f7..d5dfee4 100644 --- a/rust/src/api/workspace_api.rs +++ b/rust/src/api/workspace_api.rs @@ -1,29 +1,34 @@ use crate::{frb_generated::StreamSink, internal::wayland::AppState}; -use flutter_rust_bridge::RustAutoOpaqueNom; use serde_json::Result; use std::collections::HashMap; use wayland_client::Connection; -use wayland_protocols::ext::workspace::v1::client::{ - ext_workspace_handle_v1::ExtWorkspaceHandleV1, ext_workspace_manager_v1::ExtWorkspaceManagerV1, -}; +use std::sync::{OnceLock, mpsc::Sender}; +use std::os::fd::AsRawFd; + +static CONTROLLER: OnceLock> = OnceLock::new(); #[derive(Clone)] pub struct Workspace { pub activated: bool, pub id: u32, pub coords: Option<(u8, u8)>, - pub handle: RustAutoOpaqueNom, - pub manager_handle: RustAutoOpaqueNom, } impl Workspace { pub fn activate(&self) { - self.handle.try_read().unwrap().clone().activate(); - self.manager_handle.try_read().unwrap().clone().commit(); + if let Some(tx) = CONTROLLER.get() { + tx.send(self.id).expect("Sending activate command failed"); + } } } pub fn listen_workspaces(sink: StreamSink>>) -> Result<()> { + use std::sync::mpsc; + + let (tx, rx) = mpsc::channel::(); + + CONTROLLER.set(tx).unwrap(); + std::thread::spawn(move || { let conn = Connection::connect_to_env().expect("Failed to connect to Wayland"); @@ -32,17 +37,44 @@ pub fn listen_workspaces(sink: StreamSink>>) -> Result<()> { let mut state = AppState { workspaces: HashMap::new(), - workspace_group: HashMap::new(), + workspace_groups: HashMap::new(), + workspace_handles: HashMap::new(), sink, }; conn.display().get_registry(&qh, ()); - loop { - event_queue - .blocking_dispatch(&mut state) - .expect("Wayland dispatch failed"); + +loop { + event_queue.flush().unwrap(); + + let read_guard = event_queue.prepare_read().unwrap(); + let fd = read_guard.connection_fd().as_raw_fd(); + + let mut fds = [libc::pollfd { + fd, + events: libc::POLLIN, + revents: 0, + }]; + + let timeout = 10; + + let res = unsafe { libc::poll(fds.as_mut_ptr(), 1, timeout) }; + + if res > 0 && (fds[0].revents & libc::POLLIN) != 0 { + read_guard.read().unwrap(); + event_queue.dispatch_pending(&mut state).unwrap(); + } else { + drop(read_guard); + } + + while let Ok(id) = rx.try_recv() { + if let Some(ws) = state.workspace_handles.get(&id) { + ws.handle.activate(); + ws.manager_handle.commit(); } + } +} }); Ok(()) diff --git a/rust/src/internal/wayland.rs b/rust/src/internal/wayland.rs index 3871180..4b83601 100644 --- a/rust/src/internal/wayland.rs +++ b/rust/src/internal/wayland.rs @@ -1,7 +1,6 @@ use crate::{frb_generated::StreamSink, workspace_api::Workspace}; use std::collections::HashMap; -use flutter_rust_bridge::RustAutoOpaqueNom; use wayland_client::{ self, event_created_child, protocol::wl_registry, Connection, Dispatch, Proxy, QueueHandle, }; @@ -10,9 +9,15 @@ use wayland_protocols::ext::workspace::v1::client::{ ext_workspace_group_handle_v1, ext_workspace_handle_v1, ext_workspace_manager_v1, }; +pub struct WorkspaceHandles { + pub handle: ext_workspace_handle_v1::ExtWorkspaceHandleV1, + pub manager_handle: ext_workspace_manager_v1::ExtWorkspaceManagerV1, +} + pub struct AppState { pub workspaces: HashMap, - pub workspace_group: HashMap, + pub workspace_groups: HashMap, + pub workspace_handles: HashMap, pub sink: StreamSink>>, } @@ -21,7 +26,7 @@ impl AppState { let mut groups: HashMap> = HashMap::new(); for (ws_id, ws) in &self.workspaces { - if let Some(group_id) = self.workspace_group.get(ws_id) { + if let Some(group_id) = self.workspace_groups.get(ws_id) { groups .entry(group_id.clone()) .or_insert_with(Vec::new) @@ -85,15 +90,21 @@ impl Dispatch for AppState match event { ext_workspace_manager_v1::Event::Workspace { workspace } => { state.workspaces.insert( - workspace.id().protocol_id().clone(), + workspace.id().protocol_id(), Workspace { activated: false, id: workspace.id().protocol_id(), - handle: RustAutoOpaqueNom::new(workspace.clone()), - manager_handle: RustAutoOpaqueNom::new(proxy.clone()), coords: None, }, ); + + state.workspace_handles.insert( + workspace.id().protocol_id(), + WorkspaceHandles { + handle: workspace, + manager_handle: proxy.clone(), + } + ); } _ => {} } @@ -156,7 +167,8 @@ impl Dispatch for AppState { ext_workspace_handle_v1::Event::Removed => { state.workspaces.remove(&id.protocol_id()); - state.workspace_group.remove(&id.protocol_id()); + state.workspace_groups.remove(&id.protocol_id()); + state.workspace_handles.remove(&id.protocol_id()); state.emit(); } @@ -179,13 +191,13 @@ impl Dispatch for match event { ext_workspace_group_handle_v1::Event::WorkspaceEnter { workspace } => { state - .workspace_group + .workspace_groups .insert(workspace.id().protocol_id(), group_id.protocol_id()); state.emit(); } ext_workspace_group_handle_v1::Event::WorkspaceLeave { workspace } => { - state.workspace_group.remove(&workspace.id().protocol_id()); + state.workspace_groups.remove(&workspace.id().protocol_id()); state.emit(); }