Move wayvr-ipc to our workspace (#324)

This commit is contained in:
Aleksander
2025-12-23 03:50:03 +01:00
committed by GitHub
parent ca5e361f93
commit ccd75c047c
17 changed files with 1570 additions and 20 deletions

496
wayvr-ipc/src/client.rs Normal file
View File

@@ -0,0 +1,496 @@
use bytes::BufMut;
use interprocess::local_socket::{
self,
tokio::{prelude::*, Stream},
GenericNamespaced,
};
use serde::Serialize;
use smallvec::SmallVec;
use std::sync::{Arc, Weak};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
sync::Mutex,
};
use tokio_util::sync::CancellationToken;
use crate::{
gen_id,
ipc::{self, Serial},
packet_client::{self, PacketClient},
packet_server::{self, PacketServer},
util::notifier::Notifier,
};
pub struct QueuedPacket {
notifier: Notifier,
serial: Serial,
packet: Option<PacketServer>,
}
gen_id!(
QueuedPacketVec,
QueuedPacket,
QueuedPacketCell,
QueuedPacketHandle
);
#[derive(Debug, Serialize, Clone)]
pub struct AuthInfo {
pub runtime: String,
}
type SignalFunc = Box<dyn FnMut(&packet_server::PacketServer) -> bool + Send>;
pub struct WayVRClient {
receiver: ReceiverMutex,
sender: SenderMutex,
cancel_token: CancellationToken,
exiting: bool,
queued_packets: QueuedPacketVec,
pub auth: Option<AuthInfo>, // Some if authenticated
pub on_signal: Option<SignalFunc>,
}
pub async fn send_packet(sender: &SenderMutex, data: &[u8]) -> anyhow::Result<()> {
let mut bytes = bytes::BytesMut::new();
// packet size
bytes.put_u32(data.len() as u32);
// packet data
bytes.put_slice(data);
sender.lock().await.write_all(&bytes).await?;
Ok(())
}
pub type WayVRClientMutex = Arc<Mutex<WayVRClient>>;
pub type WayVRClientWeak = Weak<Mutex<WayVRClient>>;
type ReceiverMutex = Arc<Mutex<local_socket::tokio::RecvHalf>>;
type SenderMutex = Arc<Mutex<local_socket::tokio::SendHalf>>;
async fn client_runner(client: WayVRClientMutex) -> anyhow::Result<()> {
loop {
WayVRClient::tick(client.clone()).await?;
}
}
type Payload = SmallVec<[u8; 64]>;
async fn read_payload(
conn: &mut local_socket::tokio::RecvHalf,
size: u32,
) -> anyhow::Result<Payload> {
let mut payload = Payload::new();
payload.resize(size as usize, 0);
conn.read_exact(&mut payload).await?;
Ok(payload)
}
macro_rules! bail_unexpected_response {
() => {
anyhow::bail!("unexpected response");
};
}
// Send and wait for a response from the server
macro_rules! send_and_wait {
($client_mtx:expr, $serial:expr, $packet_to_send:expr, $expected_response_type:ident) => {{
let result =
WayVRClient::queue_wait_packet($client_mtx, $serial, &ipc::data_encode($packet_to_send))
.await?;
if let PacketServer::$expected_response_type(_, res) = result {
res
} else {
bail_unexpected_response!();
}
}};
}
// Send without expecting any response
macro_rules! send_only {
($client_mtx:expr, $packet_to_send:expr) => {{
WayVRClient::send_payload($client_mtx, &ipc::data_encode($packet_to_send)).await?;
}};
}
impl WayVRClient {
pub fn set_signal_handler(&mut self, on_signal: SignalFunc) {
self.on_signal = Some(on_signal);
}
pub async fn new(client_name: &str) -> anyhow::Result<WayVRClientMutex> {
let printname = "/tmp/wayvr_ipc.sock";
let name = printname.to_ns_name::<GenericNamespaced>()?;
let stream = match Stream::connect(name).await {
Ok(c) => c,
Err(e) => {
anyhow::bail!("Failed to connect to the WayVR IPC: {}", e)
}
};
let (receiver, sender) = stream.split();
let receiver = Arc::new(Mutex::new(receiver));
let sender = Arc::new(Mutex::new(sender));
let cancel_token = CancellationToken::new();
let client = Arc::new(Mutex::new(Self {
receiver,
sender: sender.clone(),
exiting: false,
cancel_token: cancel_token.clone(),
queued_packets: QueuedPacketVec::new(),
auth: None,
on_signal: None,
}));
WayVRClient::start_runner(client.clone(), cancel_token);
// Send handshake to the server
send_packet(
&sender,
&ipc::data_encode(&PacketClient::Handshake(packet_client::Handshake {
client_name: String::from(client_name),
magic: String::from(ipc::CONNECTION_MAGIC),
protocol_version: ipc::PROTOCOL_VERSION,
})),
)
.await?;
Ok(client)
}
fn start_runner(client: WayVRClientMutex, cancel_token: CancellationToken) {
tokio::spawn(async move {
tokio::select! {
_ = cancel_token.cancelled() => {
log::info!("Exiting IPC runner gracefully");
}
e = client_runner(client.clone()) => {
log::info!("IPC Runner failed: {:?}", e);
}
}
});
}
async fn tick(client_mtx: WayVRClientMutex) -> anyhow::Result<()> {
let receiver = {
let client = client_mtx.lock().await;
client.receiver.clone()
};
// read packet
let packet = {
let mut receiver = receiver.lock().await;
let packet_size = receiver.read_u32().await?;
log::trace!("packet size {}", packet_size);
if packet_size > 128 * 1024 {
anyhow::bail!("packet size too large (> 128 KiB)");
}
let payload = read_payload(&mut receiver, packet_size).await?;
let packet: PacketServer = ipc::data_decode(&payload)?;
packet
};
{
let mut client = client_mtx.lock().await;
if let PacketServer::HandshakeSuccess(success) = &packet {
if client.auth.is_some() {
anyhow::bail!("Got handshake response twice");
}
client.auth = Some(AuthInfo {
runtime: success.runtime.clone(),
});
log::info!(
"Authenticated. Server runtime name: \"{}\"",
success.runtime
);
}
if let PacketServer::Disconnect(disconnect) = &packet {
anyhow::bail!("Server disconnected us. Reason: {}", disconnect.reason);
}
if client.auth.is_none() {
anyhow::bail!(
"Server tried to send us a packet which is not a HandshakeSuccess or Disconnect"
);
}
if let PacketServer::WvrStateChanged(_) = &packet {
if let Some(on_signal) = &mut client.on_signal {
if (*on_signal)(&packet) {
// Signal consumed
return Ok(());
}
}
}
// queue packet to read if it contains a serial response
if let Some(serial) = packet.serial() {
for qpacket in &mut client.queued_packets.vec {
let Some(qpacket) = qpacket else {
continue;
};
let qpacket = &mut qpacket.obj;
if qpacket.serial != *serial {
continue; //skip
}
// found response serial, fill it and notify the receiver
qpacket.packet = Some(packet);
let notifier = qpacket.notifier.clone();
drop(client);
notifier.notify();
break;
}
}
}
Ok(())
}
// Send packet without feedback
async fn send_payload(client_mtx: WayVRClientMutex, payload: &[u8]) -> anyhow::Result<()> {
let client = client_mtx.lock().await;
let sender = client.sender.clone();
drop(client);
send_packet(&sender, payload).await?;
Ok(())
}
async fn queue_wait_packet(
client_mtx: WayVRClientMutex,
serial: Serial,
payload: &[u8],
) -> anyhow::Result<PacketServer> {
let notifier = Notifier::new();
// Send packet to the server
let queued_packet_handle = {
let mut client = client_mtx.lock().await;
let handle = client.queued_packets.add(QueuedPacket {
notifier: notifier.clone(),
packet: None, // will be filled after notify
serial,
});
let sender = client.sender.clone();
drop(client);
send_packet(&sender, payload).await?;
handle
};
// Wait for response message
notifier.wait().await;
// Fetch response packet
{
let mut client = client_mtx.lock().await;
let cell = client
.queued_packets
.get_mut(&queued_packet_handle)
.ok_or(anyhow::anyhow!(
"missing packet cell, this shouldn't happen"
))?;
let Some(packet) = cell.packet.take() else {
anyhow::bail!("packet is None, this shouldn't happen");
};
client.queued_packets.remove(&queued_packet_handle);
Ok(packet)
}
}
pub async fn fn_wvr_display_list(
client: WayVRClientMutex,
serial: Serial,
) -> anyhow::Result<Vec<packet_server::WvrDisplay>> {
Ok(
send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayList(serial),
WvrDisplayListResponse
)
.list,
)
}
pub async fn fn_wvr_display_get(
client: WayVRClientMutex,
serial: Serial,
handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<Option<packet_server::WvrDisplay>> {
Ok(send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayGet(serial, handle),
WvrDisplayGetResponse
))
}
pub async fn fn_wvr_display_remove(
client: WayVRClientMutex,
serial: Serial,
handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<()> {
send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayRemove(serial, handle),
WvrDisplayRemoveResponse
)
.map_err(|e| anyhow::anyhow!("{}", e))
}
pub async fn fn_wvr_display_create(
client: WayVRClientMutex,
serial: Serial,
params: packet_client::WvrDisplayCreateParams,
) -> anyhow::Result<packet_server::WvrDisplayHandle> {
Ok(send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayCreate(serial, params),
WvrDisplayCreateResponse
))
}
pub async fn fn_wvr_display_set_visible(
client: WayVRClientMutex,
handle: packet_server::WvrDisplayHandle,
visible: bool,
) -> anyhow::Result<()> {
send_only!(client, &PacketClient::WvrDisplaySetVisible(handle, visible));
Ok(())
}
pub async fn fn_wvr_display_set_layout(
client: WayVRClientMutex,
handle: packet_server::WvrDisplayHandle,
layout: packet_server::WvrDisplayWindowLayout,
) -> anyhow::Result<()> {
send_only!(
client,
&PacketClient::WvrDisplaySetWindowLayout(handle, layout)
);
Ok(())
}
pub async fn fn_wvr_display_window_list(
client: WayVRClientMutex,
serial: Serial,
handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<Option<Vec<packet_server::WvrWindow>>> {
Ok(
send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayWindowList(serial, handle),
WvrDisplayWindowListResponse
)
.map(|res| res.list),
)
}
pub async fn fn_wvr_window_set_visible(
client: WayVRClientMutex,
handle: packet_server::WvrWindowHandle,
visible: bool,
) -> anyhow::Result<()> {
send_only!(client, &PacketClient::WvrWindowSetVisible(handle, visible));
Ok(())
}
pub async fn fn_wvr_process_list(
client: WayVRClientMutex,
serial: Serial,
) -> anyhow::Result<Vec<packet_server::WvrProcess>> {
Ok(
send_and_wait!(
client,
serial,
&PacketClient::WvrProcessList(serial),
WvrProcessListResponse
)
.list,
)
}
pub async fn fn_wvr_process_get(
client: WayVRClientMutex,
serial: Serial,
handle: packet_server::WvrProcessHandle,
) -> anyhow::Result<Option<packet_server::WvrProcess>> {
Ok(send_and_wait!(
client,
serial,
&PacketClient::WvrProcessGet(serial, handle),
WvrProcessGetResponse
))
}
pub async fn fn_wvr_process_terminate(
client: WayVRClientMutex,
handle: packet_server::WvrProcessHandle,
) -> anyhow::Result<()> {
send_only!(client, &PacketClient::WvrProcessTerminate(handle));
Ok(())
}
pub async fn fn_wvr_process_launch(
client: WayVRClientMutex,
serial: Serial,
params: packet_client::WvrProcessLaunchParams,
) -> anyhow::Result<packet_server::WvrProcessHandle> {
send_and_wait!(
client,
serial,
&PacketClient::WvrProcessLaunch(serial, params),
WvrProcessLaunchResponse
)
.map_err(|e| anyhow::anyhow!("{}", e))
}
pub async fn fn_wlx_input_state(
client: WayVRClientMutex,
serial: Serial,
) -> anyhow::Result<packet_server::WlxInputState> {
Ok(send_and_wait!(
client,
serial,
&PacketClient::WlxInputState(serial),
WlxInputStateResponse
))
}
pub async fn fn_wlx_haptics(
client: WayVRClientMutex,
params: packet_client::WlxHapticsParams,
) -> anyhow::Result<()> {
send_only!(client, &PacketClient::WlxHaptics(params));
Ok(())
}
}
impl Drop for WayVRClient {
fn drop(&mut self) {
self.exiting = true;
self.cancel_token.cancel();
}
}

42
wayvr-ipc/src/ipc.rs Normal file
View File

@@ -0,0 +1,42 @@
use std::sync::{Arc, Mutex as SyncMutex};
pub type Serial = u64;
#[derive(Clone, Default)]
pub struct SerialGenerator {
serial: Arc<SyncMutex<u64>>,
}
impl SerialGenerator {
pub fn new() -> SerialGenerator {
Self {
serial: Arc::new(SyncMutex::new(0)),
}
}
pub fn increment_get(&self) -> Serial {
let mut serial = self.serial.lock().unwrap();
let cur = *serial;
*serial += 1;
cur
}
}
pub const PROTOCOL_VERSION: u32 = 3;
pub const CONNECTION_MAGIC: &str = "wayvr_ipc";
pub fn data_encode<T>(data: &T) -> Vec<u8>
where
T: serde::Serialize,
{
let str = serde_json::to_string(&data).unwrap();
log::debug!("serialized data: {}", str);
str.into_bytes()
}
pub fn data_decode<T>(data: &[u8]) -> anyhow::Result<T>
where
T: for<'a> serde::Deserialize<'a>,
{
Ok(serde_json::from_str::<T>(std::str::from_utf8(data)?)?)
}

7
wayvr-ipc/src/lib.rs Normal file
View File

@@ -0,0 +1,7 @@
pub mod ipc;
pub mod packet_client;
pub mod packet_server;
mod util;
#[cfg(feature = "client")]
pub mod client;

View File

@@ -0,0 +1,71 @@
// Contents of this file should be the same as on wlx-overlay-s.
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::{ipc::Serial, packet_server};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Handshake {
pub protocol_version: u32, // always set to PROTOCOL_VERSION
pub magic: String, // always set to CONNECTION_MAGIC
pub client_name: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum AttachTo {
None,
HandLeft,
HandRight,
Head,
Stage,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WvrProcessLaunchParams {
pub name: String,
pub exec: String,
pub target_display: packet_server::WvrDisplayHandle,
pub env: Vec<String>,
pub args: String,
pub userdata: HashMap<String, String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WvrDisplayCreateParams {
pub width: u16,
pub height: u16,
pub name: String,
pub scale: Option<f32>,
pub attach_to: AttachTo,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WlxHapticsParams {
pub intensity: f32,
pub duration: f32,
pub frequency: f32,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum PacketClient {
Handshake(Handshake),
WvrDisplayCreate(Serial, WvrDisplayCreateParams),
WvrDisplayGet(Serial, packet_server::WvrDisplayHandle),
WvrDisplayList(Serial),
WvrDisplayRemove(Serial, packet_server::WvrDisplayHandle),
WvrDisplaySetVisible(packet_server::WvrDisplayHandle, bool),
WvrDisplayWindowList(Serial, packet_server::WvrDisplayHandle),
WvrDisplaySetWindowLayout(
packet_server::WvrDisplayHandle,
packet_server::WvrDisplayWindowLayout,
),
WvrWindowSetVisible(packet_server::WvrWindowHandle, bool),
WvrProcessGet(Serial, packet_server::WvrProcessHandle),
WvrProcessLaunch(Serial, WvrProcessLaunchParams),
WvrProcessList(Serial),
WvrProcessTerminate(packet_server::WvrProcessHandle),
WlxHaptics(WlxHapticsParams),
WlxInputState(Serial),
}

View File

@@ -0,0 +1,163 @@
// Contents of this file should be the same as on wlx-overlay-s.
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::ipc::Serial;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HandshakeSuccess {
pub runtime: String, // Runtime name, for example "wlx-overlay-s"
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Disconnect {
pub reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct WvrDisplayHandle {
pub idx: u32,
pub generation: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct WvrProcessHandle {
pub idx: u32,
pub generation: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct WvrWindowHandle {
pub idx: u32,
pub generation: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WvrDisplay {
pub width: u16,
pub height: u16,
pub name: String,
pub visible: bool,
pub handle: WvrDisplayHandle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WvrWindow {
pub pos_x: i32,
pub pos_y: i32,
pub size_x: u32,
pub size_y: u32,
pub visible: bool,
pub handle: WvrWindowHandle,
pub process_handle: WvrProcessHandle,
pub display_handle: WvrDisplayHandle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WvrDisplayList {
pub list: Vec<WvrDisplay>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WvrWindowList {
pub list: Vec<WvrWindow>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WvrProcess {
pub name: String,
pub display_handle: WvrDisplayHandle,
pub handle: WvrProcessHandle,
pub userdata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WvrProcessList {
pub list: Vec<WvrProcess>,
}
#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Margins {
pub left: u16,
pub right: u16,
pub top: u16,
pub bottom: u16,
}
#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct StackingOptions {
pub margins_first: Margins,
pub margins_rest: Margins,
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum WvrDisplayWindowLayout {
Tiling,
Stacking(StackingOptions),
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum WvrStateChanged {
DisplayCreated,
DisplayRemoved,
ProcessCreated,
ProcessRemoved,
WindowCreated,
WindowRemoved,
DashboardShown,
DashboardHidden,
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct WlxInputStatePointer {
pub pos: [f32; 3],
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct WlxInputState {
pub hmd_pos: [f32; 3],
pub left: WlxInputStatePointer,
pub right: WlxInputStatePointer,
}
// "Wvr" prefixes are WayVR-specific
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PacketServer {
Disconnect(Disconnect),
HandshakeSuccess(HandshakeSuccess),
WlxInputStateResponse(Serial, WlxInputState),
WvrDisplayCreateResponse(Serial, WvrDisplayHandle),
WvrDisplayGetResponse(Serial, Option<WvrDisplay>),
WvrDisplayListResponse(Serial, WvrDisplayList),
WvrDisplayRemoveResponse(Serial, Result<(), String>),
WvrDisplayWindowListResponse(Serial, Option<WvrWindowList>),
WvrProcessGetResponse(Serial, Option<WvrProcess>),
WvrProcessLaunchResponse(Serial, Result<WvrProcessHandle, String>),
WvrProcessListResponse(Serial, WvrProcessList),
WvrStateChanged(WvrStateChanged),
}
impl PacketServer {
pub fn serial(&self) -> Option<&Serial> {
match self {
PacketServer::Disconnect(_) => None,
PacketServer::HandshakeSuccess(_) => None,
PacketServer::WlxInputStateResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayCreateResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayGetResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayListResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayRemoveResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayWindowListResponse(serial, _) => Some(serial),
PacketServer::WvrProcessGetResponse(serial, _) => Some(serial),
PacketServer::WvrProcessLaunchResponse(serial, _) => Some(serial),
PacketServer::WvrProcessListResponse(serial, _) => Some(serial),
PacketServer::WvrStateChanged(_) => None,
}
}
}

View File

@@ -0,0 +1,173 @@
#[macro_export]
macro_rules! gen_id {
(
$container_name:ident,
$instance_name:ident,
$cell_name:ident,
$handle_name:ident) => {
//ThingCell
pub struct $cell_name {
pub obj: $instance_name,
pub generation: u64,
}
//ThingVec
pub struct $container_name {
// Vec<Option<ThingCell>>
pub vec: Vec<Option<$cell_name>>,
cur_generation: u64,
}
//ThingHandle
#[derive(Default, Clone, Copy, PartialEq, Hash, Eq)]
pub struct $handle_name {
idx: u32,
generation: u64,
}
#[allow(dead_code)]
impl $handle_name {
pub fn reset(&mut self) {
self.generation = 0;
}
pub fn is_set(&self) -> bool {
self.generation > 0
}
pub fn id(&self) -> u32 {
self.idx
}
pub fn new(idx: u32, generation: u64) -> Self {
Self { idx, generation }
}
}
//ThingVec
#[allow(dead_code)]
impl $container_name {
pub fn new() -> Self {
Self {
vec: Vec::new(),
cur_generation: 0,
}
}
pub fn iter(&self, callback: &dyn Fn($handle_name, &$instance_name)) {
for (idx, opt_cell) in self.vec.iter().enumerate() {
if let Some(cell) = opt_cell {
let handle = $container_name::get_handle(&cell, idx);
callback(handle, &cell.obj);
}
}
}
pub fn iter_mut(
&mut self,
callback: &mut dyn FnMut($handle_name, &mut $instance_name),
) {
for (idx, opt_cell) in self.vec.iter_mut().enumerate() {
if let Some(cell) = opt_cell {
let handle = $container_name::get_handle(&cell, idx);
callback(handle, &mut cell.obj);
}
}
}
pub fn get_handle(cell: &$cell_name, idx: usize) -> $handle_name {
$handle_name {
idx: idx as u32,
generation: cell.generation,
}
}
fn find_unused_idx(&mut self) -> Option<u32> {
for (num, obj) in self.vec.iter().enumerate() {
if obj.is_none() {
return Some(num as u32);
}
}
None
}
pub fn add(&mut self, obj: $instance_name) -> $handle_name {
self.cur_generation += 1;
let generation = self.cur_generation;
let unused_idx = self.find_unused_idx();
let idx = if let Some(idx) = unused_idx {
idx
} else {
self.vec.len() as u32
};
let handle = $handle_name { idx, generation };
let cell = $cell_name { obj, generation };
if let Some(idx) = unused_idx {
self.vec[idx as usize] = Some(cell);
} else {
self.vec.push(Some(cell))
}
handle
}
pub fn remove(&mut self, handle: &$handle_name) {
// Out of bounds, ignore
if handle.idx as usize >= self.vec.len() {
return;
}
// Remove only if the generation matches
if let Some(cell) = &self.vec[handle.idx as usize] {
if cell.generation == handle.generation {
self.vec[handle.idx as usize] = None;
}
}
}
pub fn get(&self, handle: &$handle_name) -> Option<&$instance_name> {
// Out of bounds, ignore
if handle.idx as usize >= self.vec.len() {
return None;
}
if let Some(cell) = &self.vec[handle.idx as usize] {
if cell.generation == handle.generation {
return Some(&cell.obj);
}
}
None
}
pub fn get_mut(&mut self, handle: &$handle_name) -> Option<&mut $instance_name> {
// Out of bounds, ignore
if handle.idx as usize >= self.vec.len() {
return None;
}
if let Some(cell) = &mut self.vec[handle.idx as usize] {
if cell.generation == handle.generation {
return Some(&mut cell.obj);
}
}
None
}
}
};
}
/* Example usage:
gen_id!(ThingVec, ThingInstance, ThingCell, ThingHandle);
struct ThingInstance {}
impl ThingInstance {}
*/

View File

@@ -0,0 +1,4 @@
pub mod handle;
#[cfg(feature = "client")]
pub mod notifier;

View File

@@ -0,0 +1,25 @@
use std::sync::Arc;
use tokio::sync::Notify;
// Copyable wrapped Notify struct for easier usage
#[derive(Default, Clone)]
pub struct Notifier {
notifier: Arc<Notify>,
}
impl Notifier {
pub fn new() -> Self {
Self {
notifier: Arc::new(Notify::new()),
}
}
pub fn notify(&self) {
self.notifier.notify_waiters();
}
pub async fn wait(&self) {
self.notifier.notified().await;
}
}