From 244b360c780e5cafb8f316f09239eaca66ecfef8 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Sun, 24 Nov 2024 22:12:31 +0100 Subject: [PATCH] Allow `start` to be called multiple time Create one `NSEventContext` per call to `start`, effectively allowing multiple, independenct callback to be registered at once. --- .../nseventforwarder-rs/lib.rs | 77 ++++++++----------- .../packages/nseventforwarder/src/index.cts | 4 +- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs b/desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs index e3d8deb9a8d4..09c7271e5a46 100644 --- a/desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs +++ b/desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs @@ -8,10 +8,8 @@ use std::thread::JoinHandle; use block2::RcBlock; use neon::prelude::{ - Context, FunctionContext, Handle, JsFunction, JsNull, JsResult, JsUndefined, ModuleContext, - NeonResult, Object, Root, + Context, FunctionContext, JsFunction, JsNull, JsResult, ModuleContext, NeonResult, Object, Root, }; -use neon::result::Throw; use objc2_app_kit::{NSEvent, NSEventMask}; #[neon::main] @@ -20,31 +18,11 @@ fn main(mut cx: ModuleContext<'_>) -> NeonResult<()> { Ok(()) } -/// NSEventForwarder instance. It must be initialized by `start` and cleaned up by the callback -/// function returned from `start`. -static NSEVENTFORWARDER: Mutex> = Mutex::new(None); - -struct NSEventForwarder { - /// The thread listening for incoming [NSEvent]s. - thread: JoinHandle<()>, - /// Signal for the current execution context to stop. - stop: mpsc::Sender<()>, -} - -impl NSEventForwarder { - fn stop(self) { - // Tell the thread to stop running - let _ = self.stop.send(()); - // Wait for the thread to shutdown - self.thread - .join() - .expect("Couldn't join the NSEventForwarder thread"); - } -} - /// Register a callback to fire every time a [NSEventMask::LeftMouseDown] or [NSEventMask::RightMouseDown] event occur. /// -/// Returns a stop function to call when the original callback shouldn't be called anymore. +/// Returns a stop function to call when the original callback shouldn't be called anymore. This +/// stop function returns a `true` value when called the first time and the callback is +/// deregistered. If it were to be called yet again, it will keep returning `false`. fn start(mut cx: FunctionContext<'_>) -> JsResult<'_, JsFunction> { // Set up neon stuff. // These will be moved into the spawned thread @@ -88,29 +66,42 @@ fn start(mut cx: FunctionContext<'_>) -> JsResult<'_, JsFunction> { // The thread's execution will stop when this function returns }); - let new_context = NSEventForwarder { + // NSEventForwarder instance. It must be cleaned up by the callback + // function returned from `start` (aka `stop`). We use an Option here + // because we can not enforce the Nodejs caller to only call `stop` once. + let nseventforwarder = Mutex::new(Some(NSEventForwarder { thread: join_handle, stop: stop_tx, - }; - - // Update the global NSEventForwarder state - let mut nseventmonitor_context = NSEVENTFORWARDER.lock().unwrap(); - // Stop any old NSEventForwarder - if let Some(context) = nseventmonitor_context.take() { - context.stop(); - } - let _ = nseventmonitor_context.insert(new_context); - drop(nseventmonitor_context); + })); // Return a stop function to be invoked from the node runtime to deregister the NSEvent // callback. - JsFunction::new(&mut cx, stop) + JsFunction::new(&mut cx, move |mut cx: FunctionContext<'_>| { + // Stop this NSEventForwarder + // Returns whether NSEventForwarder was stopped on this invocation of the stop function + let mut stopped = false; + if let Some(context) = nseventforwarder.lock().unwrap().take() { + context.stop(); + stopped = true; + } + Ok(cx.boolean(stopped)) + }) } -fn stop(mut cx: FunctionContext<'_>) -> Result, Throw> { - if let Some(context) = NSEVENTFORWARDER.lock().unwrap().take() { - context.stop(); - } +struct NSEventForwarder { + /// The thread listening for incoming [NSEvent]s. + thread: JoinHandle<()>, + /// Signal for the current execution context to stop. + stop: mpsc::Sender<()>, +} - Ok(JsUndefined::new(&mut cx)) +impl NSEventForwarder { + fn stop(self) { + // Tell the thread to stop running + let _ = self.stop.send(()); + // Wait for the thread to shutdown + self.thread + .join() + .expect("Couldn't join the NSEventForwarder thread"); + } } diff --git a/desktop/packages/nseventforwarder/src/index.cts b/desktop/packages/nseventforwarder/src/index.cts index bee20624aeba..0d480d15add3 100644 --- a/desktop/packages/nseventforwarder/src/index.cts +++ b/desktop/packages/nseventforwarder/src/index.cts @@ -6,9 +6,9 @@ import * as addon from './load.cjs'; // Use this declaration to assign types to the addon's exports, // which otherwise by default are `any`. declare module './load.cjs' { - function start(cb: () => void): () => void; + function start(cb: () => void): () => boolean; } -export function start(cb: () => void): () => void { +export function start(cb: () => void): () => boolean { return addon.start(cb); }