This commit is contained in:
Henry Hiles 2026-04-20 16:37:15 -04:00
commit bf24020a7a
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
4 changed files with 85 additions and 22 deletions

17
rust/Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -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<Sender<u32>> = OnceLock::new();
#[derive(Clone)]
pub struct Workspace {
pub activated: bool,
pub id: u32,
pub coords: Option<(u8, u8)>,
pub handle: RustAutoOpaqueNom<ExtWorkspaceHandleV1>,
pub manager_handle: RustAutoOpaqueNom<ExtWorkspaceManagerV1>,
}
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<Vec<Vec<Workspace>>>) -> Result<()> {
use std::sync::mpsc;
let (tx, rx) = mpsc::channel::<u32>();
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<Vec<Vec<Workspace>>>) -> 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(())

View file

@ -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<u32, Workspace>,
pub workspace_group: HashMap<u32, u32>,
pub workspace_groups: HashMap<u32, u32>,
pub workspace_handles: HashMap<u32, WorkspaceHandles>,
pub sink: StreamSink<Vec<Vec<Workspace>>>,
}
@ -21,7 +26,7 @@ impl AppState {
let mut groups: HashMap<u32, Vec<(&u32, &Workspace)>> = 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<ext_workspace_manager_v1::ExtWorkspaceManagerV1, ()> 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<ext_workspace_handle_v1::ExtWorkspaceHandleV1, ()> 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<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> 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();
}