asdf-overlay-node 2.0.3

Asdf Overlay Node Addon
pub mod ime;
pub mod input;
pub mod surface;
pub mod tracing;

use anyhow::Context;
use asdf_overlay_client::client::IpcClientEventStream;
use asdf_overlay_client::common::event::surface::SurfaceEvent;
use asdf_overlay_client::common::event::tracing::TracingEvent;
use asdf_overlay_client::common::event::{OverlayEvent, window::WindowEvent};
use napi::{
    bindgen_prelude::{FnArgs, Function, JsObjectValue, JsValuesTupleIntoVec, Object},
    threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue},
};

use crate::event::input::InputEvent;
use crate::event::surface::SurfaceInfo;
use crate::event::tracing::TracingMetadata;

pub(crate) struct VarArgs(
    Box<dyn FnOnce(napi::sys::napi_env) -> napi::Result<Vec<napi::sys::napi_value>>>,
);

impl VarArgs {
    fn of<T>(args: T) -> Self
    where
        T: 'static,
        FnArgs<T>: JsValuesTupleIntoVec,
    {
        Self(Box::new(|env| FnArgs::from(args).into_vec(env)))
    }
}

impl JsValuesTupleIntoVec for VarArgs {
    fn into_vec(self, env: napi::sys::napi_env) -> napi::Result<Vec<napi::sys::napi_value>> {
        self.0(env)
    }
}

pub(crate) type EmitTsFn =
    ThreadsafeFunction<VarArgs, UnknownReturnValue, VarArgs, napi::Status, false>;

pub(crate) fn create_emit_tsfn<'env>(emitter: &Object<'env>) -> anyhow::Result<EmitTsFn> {
    let emit_fn = emitter
        .get_named_property::<Function<VarArgs, UnknownReturnValue>>("emit")
        .context("cannot find emit function of EventEmitter")?;
    let emit_fn = emit_fn.bind(emitter)?;

    emit_fn
        .build_threadsafe_function()
        .build()
        .context("failed to build threadsafe function")
}

pub(crate) async fn event_task(mut stream: IpcClientEventStream, emit_tsfn: EmitTsFn) {
    struct Emitter(EmitTsFn);
    impl Emitter {
        fn emit<Args>(&self, args: Args)
        where
            FnArgs<Args>: JsValuesTupleIntoVec,
            Args: 'static,
        {
            self.0
                .call(VarArgs::of(args), ThreadsafeFunctionCallMode::Blocking);
        }
    }

    let emitter = Emitter(emit_tsfn);
    while let Some(event) = stream.recv().await {
        match event {
            OverlayEvent::Window { id, event } => match event {
                WindowEvent::Added { width, height } => {
                    emitter.emit(("window_added", id, width, height));
                }

                WindowEvent::Resized { width, height } => {
                    emitter.emit(("window_resized", id, width, height));
                }
                WindowEvent::Input(input) => match InputEvent::from(input) {
                    InputEvent::Cursor { event } => {
                        emitter.emit(("window_cursor_input", id, event));
                    }
                    InputEvent::Keyboard { event } => {
                        emitter.emit(("window_keyboard_input", id, event));
                    }
                },

                WindowEvent::Destroyed => {
                    emitter.emit(("window_destroyed", id));
                }
            },

            OverlayEvent::InputBlockingEnded => {
                emitter.emit(("input_blocking_ended",));
            }

            OverlayEvent::Surface { id, event } => match event {
                SurfaceEvent::Added {
                    width,
                    height,
                    info,
                } => {
                    emitter.emit(("surface_added", id, width, height, SurfaceInfo::from(info)));
                }

                SurfaceEvent::Resized { width, height } => {
                    emitter.emit(("surface_resized", id, width, height));
                }

                SurfaceEvent::Destroyed => {
                    emitter.emit(("surface_destroyed", id));
                }
            },

            OverlayEvent::Tracing(event) => match event {
                TracingEvent::Enter(metadata) => {
                    emitter.emit(("tracing_enter", TracingMetadata::from(metadata)));
                }
                TracingEvent::Event { metadata, message } => {
                    emitter.emit(("tracing_event", TracingMetadata::from(metadata), message));
                }
                TracingEvent::Exit => {
                    emitter.emit(("tracing_exit",));
                }
            },
        }
    }

    emitter.emit(("disconnected",));
}