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", "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]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -610,7 +625,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
name = "rust_lib_flight" name = "rust_lib_flight"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"crossbeam-channel",
"flutter_rust_bridge", "flutter_rust_bridge",
"libc",
"serde", "serde",
"serde_json", "serde_json",
"wayland-client", "wayland-client",

View file

@ -13,3 +13,5 @@ wayland-client = "0.31"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
wayland-protocols = { version = "0.32.12", features = ["client", "staging"] } 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 crate::{frb_generated::StreamSink, internal::wayland::AppState};
use flutter_rust_bridge::RustAutoOpaqueNom;
use serde_json::Result; use serde_json::Result;
use std::collections::HashMap; use std::collections::HashMap;
use wayland_client::Connection; use wayland_client::Connection;
use wayland_protocols::ext::workspace::v1::client::{ use std::sync::{OnceLock, mpsc::Sender};
ext_workspace_handle_v1::ExtWorkspaceHandleV1, ext_workspace_manager_v1::ExtWorkspaceManagerV1, use std::os::fd::AsRawFd;
};
static CONTROLLER: OnceLock<Sender<u32>> = OnceLock::new();
#[derive(Clone)] #[derive(Clone)]
pub struct Workspace { pub struct Workspace {
pub activated: bool, pub activated: bool,
pub id: u32, pub id: u32,
pub coords: Option<(u8, u8)>, pub coords: Option<(u8, u8)>,
pub handle: RustAutoOpaqueNom<ExtWorkspaceHandleV1>,
pub manager_handle: RustAutoOpaqueNom<ExtWorkspaceManagerV1>,
} }
impl Workspace { impl Workspace {
pub fn activate(&self) { pub fn activate(&self) {
self.handle.try_read().unwrap().clone().activate(); if let Some(tx) = CONTROLLER.get() {
self.manager_handle.try_read().unwrap().clone().commit(); tx.send(self.id).expect("Sending activate command failed");
}
} }
} }
pub fn listen_workspaces(sink: StreamSink<Vec<Vec<Workspace>>>) -> Result<()> { 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 || { std::thread::spawn(move || {
let conn = Connection::connect_to_env().expect("Failed to connect to Wayland"); let conn = Connection::connect_to_env().expect("Failed to connect to Wayland");
@ -32,16 +37,43 @@ pub fn listen_workspaces(sink: StreamSink<Vec<Vec<Workspace>>>) -> Result<()> {
let mut state = AppState { let mut state = AppState {
workspaces: HashMap::new(), workspaces: HashMap::new(),
workspace_group: HashMap::new(), workspace_groups: HashMap::new(),
workspace_handles: HashMap::new(),
sink, sink,
}; };
conn.display().get_registry(&qh, ()); conn.display().get_registry(&qh, ());
loop { loop {
event_queue event_queue.flush().unwrap();
.blocking_dispatch(&mut state)
.expect("Wayland dispatch failed"); 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();
}
}
} }
}); });

View file

@ -1,7 +1,6 @@
use crate::{frb_generated::StreamSink, workspace_api::Workspace}; use crate::{frb_generated::StreamSink, workspace_api::Workspace};
use std::collections::HashMap; use std::collections::HashMap;
use flutter_rust_bridge::RustAutoOpaqueNom;
use wayland_client::{ use wayland_client::{
self, event_created_child, protocol::wl_registry, Connection, Dispatch, Proxy, QueueHandle, 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, 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 struct AppState {
pub workspaces: HashMap<u32, Workspace>, 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>>>, pub sink: StreamSink<Vec<Vec<Workspace>>>,
} }
@ -21,7 +26,7 @@ impl AppState {
let mut groups: HashMap<u32, Vec<(&u32, &Workspace)>> = HashMap::new(); let mut groups: HashMap<u32, Vec<(&u32, &Workspace)>> = HashMap::new();
for (ws_id, ws) in &self.workspaces { 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 groups
.entry(group_id.clone()) .entry(group_id.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
@ -85,15 +90,21 @@ impl Dispatch<ext_workspace_manager_v1::ExtWorkspaceManagerV1, ()> for AppState
match event { match event {
ext_workspace_manager_v1::Event::Workspace { workspace } => { ext_workspace_manager_v1::Event::Workspace { workspace } => {
state.workspaces.insert( state.workspaces.insert(
workspace.id().protocol_id().clone(), workspace.id().protocol_id(),
Workspace { Workspace {
activated: false, activated: false,
id: workspace.id().protocol_id(), id: workspace.id().protocol_id(),
handle: RustAutoOpaqueNom::new(workspace.clone()),
manager_handle: RustAutoOpaqueNom::new(proxy.clone()),
coords: None, 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 => { ext_workspace_handle_v1::Event::Removed => {
state.workspaces.remove(&id.protocol_id()); 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(); state.emit();
} }
@ -179,13 +191,13 @@ impl Dispatch<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> for
match event { match event {
ext_workspace_group_handle_v1::Event::WorkspaceEnter { workspace } => { ext_workspace_group_handle_v1::Event::WorkspaceEnter { workspace } => {
state state
.workspace_group .workspace_groups
.insert(workspace.id().protocol_id(), group_id.protocol_id()); .insert(workspace.id().protocol_id(), group_id.protocol_id());
state.emit(); state.emit();
} }
ext_workspace_group_handle_v1::Event::WorkspaceLeave { workspace } => { 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(); state.emit();
} }