From 4f7010d6c321c02f42f9bf4c8ca309fbfc8ae100 Mon Sep 17 00:00:00 2001
From: John Nunley <dev@notgull.net>
Date: Sun, 10 Nov 2024 20:04:40 -0800
Subject: [PATCH] feat: Add waker() method to AndroidAppWaker

This commit adds a "waker()" method to AndroidAppWaker. It converts it
into an `std::task::Waker`, which is the type of waker used by
asynchronous tasks for scheduling. The goal is to allow AndroidAppWaker
to be easily used to set up an asynchronous context.

The implementation is very efficient, owing to the "static pointer"
style of coding already used. The "wake" function just calls
ALooper_wake, and cloning/dropping the waker is just a copy.

Signed-off-by: John Nunley <dev@notgull.net>
---
 .gitignore                                  |  1 +
 android-activity/src/game_activity/mod.rs   | 22 +++++++++++++++++++++
 android-activity/src/native_activity/mod.rs | 22 +++++++++++++++++++++
 3 files changed, 45 insertions(+)

diff --git a/.gitignore b/.gitignore
index eb5a316..a9d37c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 target
+Cargo.lock
diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs
index 7cf3894..333de52 100644
--- a/android-activity/src/game_activity/mod.rs
+++ b/android-activity/src/game_activity/mod.rs
@@ -8,6 +8,7 @@ use std::ptr;
 use std::ptr::NonNull;
 use std::sync::Weak;
 use std::sync::{Arc, Mutex, RwLock};
+use std::task::{RawWaker, RawWakerVTable, Waker};
 use std::time::Duration;
 
 use libc::c_void;
@@ -118,6 +119,27 @@ impl AndroidAppWaker {
             ALooper_wake(self.looper.as_ptr());
         }
     }
+
+    /// Creates a [`Waker`] that wakes up the [`AndroidApp`].
+    ///
+    /// This is useful for using this crate in `async` environments.
+    ///
+    /// [`Waker`]: std::task::Waker
+    pub fn waker(self) -> Waker {
+        const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
+
+        unsafe fn clone(data: *const ()) -> RawWaker {
+            RawWaker::new(data, &VTABLE)
+        }
+
+        unsafe fn wake(data: *const ()) {
+            ndk_sys::ALooper_wake(data as *const _ as *mut _)
+        }
+
+        unsafe fn drop(_: *const ()) {}
+
+        unsafe { Waker::from_raw(RawWaker::new(self.looper.as_ptr() as *const (), &VTABLE)) }
+    }
 }
 
 impl AndroidApp {
diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs
index 2adcd52..d6e0b2b 100644
--- a/android-activity/src/native_activity/mod.rs
+++ b/android-activity/src/native_activity/mod.rs
@@ -6,6 +6,7 @@ use std::panic::AssertUnwindSafe;
 use std::ptr;
 use std::ptr::NonNull;
 use std::sync::{Arc, Mutex, RwLock, Weak};
+use std::task::{RawWaker, RawWakerVTable, Waker};
 use std::time::Duration;
 
 use libc::c_void;
@@ -83,6 +84,27 @@ impl AndroidAppWaker {
             ndk_sys::ALooper_wake(self.looper.as_ptr());
         }
     }
+
+    /// Creates a [`Waker`] that wakes up the [`AndroidApp`].
+    ///
+    /// This is useful for using this crate in `async` environments.
+    ///
+    /// [`Waker`]: std::task::Waker
+    pub fn waker(self) -> Waker {
+        const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
+
+        unsafe fn clone(data: *const ()) -> RawWaker {
+            RawWaker::new(data, &VTABLE)
+        }
+
+        unsafe fn wake(data: *const ()) {
+            ndk_sys::ALooper_wake(data as *const _ as *mut _)
+        }
+
+        unsafe fn drop(_: *const ()) {}
+
+        unsafe { Waker::from_raw(RawWaker::new(self.looper.as_ptr() as *const (), &VTABLE)) }
+    }
 }
 
 impl AndroidApp {