From c342690e61cf433767e8c170c311574fd085318a Mon Sep 17 00:00:00 2001 From: Maksim Belov Date: Tue, 14 Jul 2026 00:18:43 +0300 Subject: [PATCH 01/12] Move previously dynamically loaded pojavexec logic into mojoexec --- android-gradle/src/main/cpp/CMakeLists.txt | 16 ------------ {android-gradle => jni_bindings}/.gitignore | 0 {android-gradle => jni_bindings}/build.gradle | 16 ++---------- .../consumer-rules.pro | 0 .../proguard-rules.pro | 0 .../src/main/AndroidManifest.xml | 0 .../glfw/AndroidClipboardProvider.java | 0 .../dnbootstrap/glfw/ClipboardProvider.java | 0 .../dnbootstrap/glfw/CursorImplementor.java | 0 .../glfw/FallbackCursorDrawable.java | 0 .../git/artdeell/dnbootstrap/glfw/GLFW.java | 0 .../artdeell/dnbootstrap/glfw/GLFWCursor.java | 0 .../dnbootstrap/glfw/GLFWCursorView.java | 0 .../glfw/GamepadEnableHandler.java | 0 .../dnbootstrap/glfw/GamepadKeycodes.java | 0 .../dnbootstrap/glfw/GrabListener.java | 0 .../artdeell/dnbootstrap/glfw/KeyCodes.java | 0 .../artdeell/dnbootstrap/glfw/MouseCodes.java | 0 .../git/artdeell/dnbootstrap/utils/Utils.java | 0 .../src/main/res/values/attributes.xml | 0 src/CMakeLists.txt | 5 +++- src/android_init.c | 25 +++---------------- src/android_monitor.c | 7 +++--- src/android_platform.h | 15 ----------- src/android_window.c | 5 ++-- 25 files changed, 16 insertions(+), 73 deletions(-) delete mode 100644 android-gradle/src/main/cpp/CMakeLists.txt rename {android-gradle => jni_bindings}/.gitignore (100%) rename {android-gradle => jni_bindings}/build.gradle (59%) rename {android-gradle => jni_bindings}/consumer-rules.pro (100%) rename {android-gradle => jni_bindings}/proguard-rules.pro (100%) rename {android-gradle => jni_bindings}/src/main/AndroidManifest.xml (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/ClipboardProvider.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/FallbackCursorDrawable.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadEnableHandler.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadKeycodes.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/GrabListener.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/KeyCodes.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/glfw/MouseCodes.java (100%) rename {android-gradle => jni_bindings}/src/main/java/git/artdeell/dnbootstrap/utils/Utils.java (100%) rename {android-gradle => jni_bindings}/src/main/res/values/attributes.xml (100%) diff --git a/android-gradle/src/main/cpp/CMakeLists.txt b/android-gradle/src/main/cpp/CMakeLists.txt deleted file mode 100644 index 58aec2db..00000000 --- a/android-gradle/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.22.1) - -project("dnbglfw") - -#################################################### -# GLFW -#################################################### -set(GLFW_BUILD_EXAMPLES OFF CACHE STRING "" FORCE) -set(GLFW_BUILD_TESTS OFF CACHE STRING "" FORCE) -set(GLFW_INSTALL OFF CACHE STRING "" FORCE) -set(BUILD_SHARED_LIBS ON CACHE STRING "" FORCE) -set(GLFW_BUILD_X11 OFF CACHE STRING "" FORCE) -set(GLFW_BUILD_WAYLAND OFF CACHE STRING "" FORCE) -set(GLFW_BUILD_ANDROID ON CACHE STRING "" FORCE) - -add_subdirectory(../../../../) diff --git a/android-gradle/.gitignore b/jni_bindings/.gitignore similarity index 100% rename from android-gradle/.gitignore rename to jni_bindings/.gitignore diff --git a/android-gradle/build.gradle b/jni_bindings/build.gradle similarity index 59% rename from android-gradle/build.gradle rename to jni_bindings/build.gradle index b9467acb..9d059ef5 100644 --- a/android-gradle/build.gradle +++ b/jni_bindings/build.gradle @@ -8,13 +8,6 @@ android { defaultConfig { minSdk 21 - externalNativeBuild { - cmake { - arguments "-DGLFW_BUILD_WAYLAND=OFF", "-DGLFW_BUILD_X11=OFF", "-DGLFW_BUILD_ANDROID=ON", - "-DGLFW_BUILD_EXAMPLES=OFF", "-DGLFW_BUILD_TESTS=OFF", "-DGLFW_INSTALL=OFF", "-DBUILD_SHARED_LIBS=ON" - cFlags "-Wno-nullability-completeness" - } - } } buildTypes { @@ -37,14 +30,9 @@ android { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } - - externalNativeBuild { - cmake { - path file('../CMakeLists.txt') - } - } } dependencies { - implementation 'androidx.annotation:annotation-jvm:1.9.1' + implementation 'androidx.annotation:annotation-jvm:1.10.0' + api project(":mojoexec:jni_bindings") } \ No newline at end of file diff --git a/android-gradle/consumer-rules.pro b/jni_bindings/consumer-rules.pro similarity index 100% rename from android-gradle/consumer-rules.pro rename to jni_bindings/consumer-rules.pro diff --git a/android-gradle/proguard-rules.pro b/jni_bindings/proguard-rules.pro similarity index 100% rename from android-gradle/proguard-rules.pro rename to jni_bindings/proguard-rules.pro diff --git a/android-gradle/src/main/AndroidManifest.xml b/jni_bindings/src/main/AndroidManifest.xml similarity index 100% rename from android-gradle/src/main/AndroidManifest.xml rename to jni_bindings/src/main/AndroidManifest.xml diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/ClipboardProvider.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/ClipboardProvider.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/ClipboardProvider.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/ClipboardProvider.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/FallbackCursorDrawable.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/FallbackCursorDrawable.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/FallbackCursorDrawable.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/FallbackCursorDrawable.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadEnableHandler.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadEnableHandler.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadEnableHandler.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadEnableHandler.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadKeycodes.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadKeycodes.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadKeycodes.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GamepadKeycodes.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GrabListener.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GrabListener.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/GrabListener.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GrabListener.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/KeyCodes.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/KeyCodes.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/KeyCodes.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/KeyCodes.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/MouseCodes.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/MouseCodes.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/glfw/MouseCodes.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/MouseCodes.java diff --git a/android-gradle/src/main/java/git/artdeell/dnbootstrap/utils/Utils.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/utils/Utils.java similarity index 100% rename from android-gradle/src/main/java/git/artdeell/dnbootstrap/utils/Utils.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/utils/Utils.java diff --git a/android-gradle/src/main/res/values/attributes.xml b/jni_bindings/src/main/res/values/attributes.xml similarity index 100% rename from android-gradle/src/main/res/values/attributes.xml rename to jni_bindings/src/main/res/values/attributes.xml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e1ccbdc..04bfd660 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -181,7 +181,10 @@ if (GLFW_BUILD_WAYLAND) endif() if (GLFW_BUILD_ANDROID) - target_link_libraries(glfw PRIVATE android log) + if (NOT TARGET mojoexec) + message(FATAL_ERROR "MojoExec is a required dependency! Make sure you have added it beforeincluding this") + endif () + target_link_libraries(glfw PRIVATE android log mojoexec) endif () if (GLFW_BUILD_X11) diff --git a/src/android_init.c b/src/android_init.c index ac2cf20e..83d62638 100644 --- a/src/android_init.c +++ b/src/android_init.c @@ -29,6 +29,7 @@ #include #include +#include ////////////////////////////////////////////////////////////////////////// @@ -126,34 +127,15 @@ extern void android_destroy_window(void); void* _glfwLoadVulkanDriverAndroid(void) { - if(_glfw.android.pojavexec_handle == NULL) return NULL; - typedef void* (*loadVulkanDriver_t)(void); - loadVulkanDriver_t loadVulkanDriver = (loadVulkanDriver_t) - _glfwPlatformGetModuleSymbol(_glfw.android.pojavexec_handle, "pojavexec_loadVulkanDriver"); - void* vkHandle = loadVulkanDriver(); - return vkHandle; + return mojoexec_acq_vulkan_handle(); } void* _glfwLoadEglAndroid(void) { - return _glfw.android.renderspec->egl_acquire(_glfw.android.renderspec->egl_path); + return mojoexec_acq_egl_handle(); } int _glfwInitAndroid(void) { - void* pojavexec_handle = _glfwPlatformLoadModule("libpojavexec.so"); - if(!pojavexec_handle) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to load platform support library"); - return GLFW_FALSE; - } - - _glfw.android.pojavexec_handle = pojavexec_handle; - - typedef const pojavexec_renderspec_t* (*getRenderspec_t)(void); - getRenderspec_t getRenderspec = (getRenderspec_t) - _glfwPlatformGetModuleSymbol(_glfw.android.pojavexec_handle, "pojavexec_getRenderSpec"); - - _glfw.android.renderspec = getRenderspec(); - _glfwPollMonitorsAndroid(); return android_init_window(); @@ -163,7 +145,6 @@ void _glfwTerminateAndroid(void) { android_destroy_window(); free(_glfw.null.clipboardString); - _glfwPlatformFreeModule(_glfw.android.pojavexec_handle); _glfwTerminateOSMesa(); _glfwTerminateEGL(); } diff --git a/src/android_monitor.c b/src/android_monitor.c index 05beb2b3..5fef2e93 100644 --- a/src/android_monitor.c +++ b/src/android_monitor.c @@ -30,18 +30,19 @@ #include #include #include +#include // The the sole (fake) video mode of our (sole) fake monitor // static GLFWvidmode getVideoMode(void) { GLFWvidmode mode; - mode.width = _glfw.android.renderspec->disp_width; - mode.height = _glfw.android.renderspec->disp_height; + mode.width = mojoexec_renderspec.disp_width; + mode.height = mojoexec_renderspec.disp_height; mode.redBits = 8; mode.greenBits = 8; mode.blueBits = 8; - mode.refreshRate = _glfw.android.renderspec->disp_hz; + mode.refreshRate = (int) mojoexec_renderspec.disp_hz; return mode; } diff --git a/src/android_platform.h b/src/android_platform.h index 8212cfc8..72e5a1d5 100644 --- a/src/android_platform.h +++ b/src/android_platform.h @@ -77,19 +77,6 @@ typedef void (APIENTRY *PFN_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysical typedef int32_t (*ANativeWindow_setBuffersTransform_t)(struct ANativeWindow *_Nonnull window,int32_t transform); -typedef void* (*acquire_egl_handle_t)(const char*); - -typedef struct { - acquire_egl_handle_t egl_acquire; - const char* egl_path; - int force_gles_context; - int override_major_version; - bool force_recreate_on_resize; - int disp_width; - int disp_height; - int disp_hz; -} pojavexec_renderspec_t; - // Android-specific per-window data // @@ -125,8 +112,6 @@ typedef struct _GLFWlibraryAndroid double xcursor; double ycursor; _GLFWwindow* focusedWindow; - void* pojavexec_handle; - const pojavexec_renderspec_t* renderspec; } _GLFWlibraryAndroid; typedef struct _GLFWcursorAndroid diff --git a/src/android_window.c b/src/android_window.c index d424bfe8..e35aba8b 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -42,6 +42,7 @@ #include #include #include +#include #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "android_window", __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "android_window", __VA_ARGS__) @@ -399,9 +400,9 @@ static void android_reconfigure_context(const _GLFWctxconfig *ctxconfig, _GLFWct memcpy(target, ctxconfig, sizeof (_GLFWctxconfig)); if(target->client == GLFW_NO_API) return; - if(_glfw.android.renderspec->force_gles_context) { + if(mojoexec_renderspec.force_gles_context) { target->client = GLFW_OPENGL_ES_API; - target->major = _glfw.android.renderspec->override_major_version; + target->major = mojoexec_renderspec.override_major_version; target->minor = 0; } } From 9786f1666d8df2cb588ee058bfc8ee82a72106e1 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Sat, 18 Jul 2026 01:54:57 +0400 Subject: [PATCH 02/12] Android: notify JNI about glfwInit --- .../main/java/git/artdeell/dnbootstrap/glfw/GLFW.java | 10 ++++++++++ src/android_init.c | 4 +++- src/android_window.c | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java index 10872802..2e25c54e 100644 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java +++ b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java @@ -16,6 +16,7 @@ public class GLFW { private static final Set grabListeners = Collections.newSetFromMap(new WeakHashMap<>()); + private static Runnable onInitCallback; private static WeakReference cursorImpl; private static WeakReference clipboardImpl; private static WeakReference gamepadEnable; @@ -30,6 +31,8 @@ public class GLFW { GLFW.initialize(); } + + public static void setCursorImpl(CursorImplementor cursorImpl) { GLFW.cursorImpl = new WeakReference<>(cursorImpl); addGrabListener(cursorImpl); @@ -140,6 +143,13 @@ public static void sendKeyEvent(int glfwCode, boolean state, int mods) { sendKeyEvent(glfwCode, state ? 1 : 0, mods); } + public static void receiveInit() { + onInitCallback.run(); + } + public static void setInitCallback(Runnable callback){ + onInitCallback = callback; + } + public static native void initialize(); private static native void sendMousePosition0(double x, double y); public static native void sendKeyEvent(int glfwCode, int state, int mods); diff --git a/src/android_init.c b/src/android_init.c index 83d62638..077e353c 100644 --- a/src/android_init.c +++ b/src/android_init.c @@ -124,6 +124,7 @@ GLFWbool _glfwConnectAndroid(int platformID, _GLFWplatform* platform) extern GLFWbool android_init_window(void); extern void android_destroy_window(void); +extern void android_notify_init(); void* _glfwLoadVulkanDriverAndroid(void) { @@ -136,8 +137,9 @@ void* _glfwLoadEglAndroid(void) { int _glfwInitAndroid(void) { - _glfwPollMonitorsAndroid(); + _glfwPollMonitorsAndroid(); + android_notify_init(); return android_init_window(); } diff --git a/src/android_window.c b/src/android_window.c index e35aba8b..99239a81 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -91,6 +91,7 @@ static struct { jmethodID method_getClipboardString; jmethodID method_setClipboardString; jmethodID method_enableDirectGamepad; + jmethodID method_receiveInit; } jni; static _Thread_local struct { @@ -1300,6 +1301,11 @@ VkResult _glfwCreateWindowSurfaceAndroid(VkInstance instance, return err; } +void android_notify_init(){ + ensure_comm_connected(); + (*jni_tl.env)->CallStaticVoidMethod(jni_tl.env, jni.glfw_class, jni.method_receiveInit); +} + JNIEXPORT void JNICALL Java_git_artdeell_dnbootstrap_glfw_GLFW_nativeSurfaceCreated(JNIEnv *env, jclass clazz, @@ -1361,6 +1367,7 @@ Java_git_artdeell_dnbootstrap_glfw_GLFW_initialize(JNIEnv *env, jclass clazz) { jni.method_getClipboardString = (*env)->GetStaticMethodID(env, clazz, "getClipboardString", "()Ljava/lang/String;"); jni.method_setClipboardString = (*env)->GetStaticMethodID(env, clazz, "setClipboardString", "(Ljava/lang/String;)V"); jni.method_enableDirectGamepad = (*env)->GetStaticMethodID(env, clazz, "enableDirectGamepad", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)V"); + jni.method_receiveInit = (*env)->GetStaticMethodID(env, clazz, "receiveInit", "()V"); } JNIEXPORT void JNICALL From 6a3120f51aaf430cde11e73738889ec66e1b565e Mon Sep 17 00:00:00 2001 From: Artyom Dokuchaev <66699673+whitebelyash@users.noreply.github.com> Date: Mon, 20 Jul 2026 13:00:23 +0400 Subject: [PATCH 03/12] Android: notify JNI about glfwInit (#7) --- .../main/java/git/artdeell/dnbootstrap/glfw/GLFW.java | 10 ++++++++++ src/android_init.c | 4 +++- src/android_window.c | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java index 10872802..2e25c54e 100644 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java +++ b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java @@ -16,6 +16,7 @@ public class GLFW { private static final Set grabListeners = Collections.newSetFromMap(new WeakHashMap<>()); + private static Runnable onInitCallback; private static WeakReference cursorImpl; private static WeakReference clipboardImpl; private static WeakReference gamepadEnable; @@ -30,6 +31,8 @@ public class GLFW { GLFW.initialize(); } + + public static void setCursorImpl(CursorImplementor cursorImpl) { GLFW.cursorImpl = new WeakReference<>(cursorImpl); addGrabListener(cursorImpl); @@ -140,6 +143,13 @@ public static void sendKeyEvent(int glfwCode, boolean state, int mods) { sendKeyEvent(glfwCode, state ? 1 : 0, mods); } + public static void receiveInit() { + onInitCallback.run(); + } + public static void setInitCallback(Runnable callback){ + onInitCallback = callback; + } + public static native void initialize(); private static native void sendMousePosition0(double x, double y); public static native void sendKeyEvent(int glfwCode, int state, int mods); diff --git a/src/android_init.c b/src/android_init.c index 83d62638..077e353c 100644 --- a/src/android_init.c +++ b/src/android_init.c @@ -124,6 +124,7 @@ GLFWbool _glfwConnectAndroid(int platformID, _GLFWplatform* platform) extern GLFWbool android_init_window(void); extern void android_destroy_window(void); +extern void android_notify_init(); void* _glfwLoadVulkanDriverAndroid(void) { @@ -136,8 +137,9 @@ void* _glfwLoadEglAndroid(void) { int _glfwInitAndroid(void) { - _glfwPollMonitorsAndroid(); + _glfwPollMonitorsAndroid(); + android_notify_init(); return android_init_window(); } diff --git a/src/android_window.c b/src/android_window.c index e35aba8b..99239a81 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -91,6 +91,7 @@ static struct { jmethodID method_getClipboardString; jmethodID method_setClipboardString; jmethodID method_enableDirectGamepad; + jmethodID method_receiveInit; } jni; static _Thread_local struct { @@ -1300,6 +1301,11 @@ VkResult _glfwCreateWindowSurfaceAndroid(VkInstance instance, return err; } +void android_notify_init(){ + ensure_comm_connected(); + (*jni_tl.env)->CallStaticVoidMethod(jni_tl.env, jni.glfw_class, jni.method_receiveInit); +} + JNIEXPORT void JNICALL Java_git_artdeell_dnbootstrap_glfw_GLFW_nativeSurfaceCreated(JNIEnv *env, jclass clazz, @@ -1361,6 +1367,7 @@ Java_git_artdeell_dnbootstrap_glfw_GLFW_initialize(JNIEnv *env, jclass clazz) { jni.method_getClipboardString = (*env)->GetStaticMethodID(env, clazz, "getClipboardString", "()Ljava/lang/String;"); jni.method_setClipboardString = (*env)->GetStaticMethodID(env, clazz, "setClipboardString", "(Ljava/lang/String;)V"); jni.method_enableDirectGamepad = (*env)->GetStaticMethodID(env, clazz, "enableDirectGamepad", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)V"); + jni.method_receiveInit = (*env)->GetStaticMethodID(env, clazz, "receiveInit", "()V"); } JNIEXPORT void JNICALL From 0fa326f20d070fcfcecf2abee62d5ddef236d173 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Sat, 1 Aug 2026 14:23:17 +0400 Subject: [PATCH 04/12] jni-bindings: remove unused stuff --- .../glfw/AndroidClipboardProvider.java | 28 ----- .../dnbootstrap/glfw/CursorImplementor.java | 6 - .../git/artdeell/dnbootstrap/glfw/GLFW.java | 105 ++++++------------ ...pboardProvider.java => GLFWClipboard.java} | 2 +- .../artdeell/dnbootstrap/glfw/GLFWCursor.java | 24 +++- .../dnbootstrap/glfw/GLFWCursorView.java | 77 ------------- 6 files changed, 50 insertions(+), 192 deletions(-) delete mode 100644 jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java delete mode 100644 jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java rename jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/{ClipboardProvider.java => GLFWClipboard.java} (75%) delete mode 100644 jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java deleted file mode 100644 index 92e8ebb6..00000000 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/AndroidClipboardProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -package git.artdeell.dnbootstrap.glfw; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; - -public class AndroidClipboardProvider implements ClipboardProvider { - private final ClipboardManager clipboardManager; - public AndroidClipboardProvider(Context context) { - clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - } - - @Override - public String getClipboardString() { - if(!clipboardManager.hasPrimaryClip()) return null; - ClipData clipData = clipboardManager.getPrimaryClip(); - if(clipData == null) return null; - if(clipData.getItemCount() < 1) return null; - CharSequence text = clipData.getItemAt(0).getText(); - if(text == null) return null; - return text.toString(); - } - - @Override - public void setClipboardString(String str) { - clipboardManager.setPrimaryClip(ClipData.newPlainText("GLFW Paste", str)); - } -} diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java deleted file mode 100644 index f0284290..00000000 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/CursorImplementor.java +++ /dev/null @@ -1,6 +0,0 @@ -package git.artdeell.dnbootstrap.glfw; - -public interface CursorImplementor extends GrabListener { - void onCursorPosition(); - void onCursorChanged(); -} diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java index 2e25c54e..7784909e 100644 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java +++ b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java @@ -8,21 +8,22 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; -import java.util.Collections; -import java.util.Set; -import java.util.WeakHashMap; import git.artdeell.dnbootstrap.utils.Utils; public class GLFW { - private static final Set grabListeners = Collections.newSetFromMap(new WeakHashMap<>()); + public interface PosCallback { + void receive(double x, double y); + } + public interface CursorCallback { + void onCursorUse(GLFWCursor cursor); + } + private static GrabListener mGrabListener; private static Runnable onInitCallback; - private static WeakReference cursorImpl; - private static WeakReference clipboardImpl; - private static WeakReference gamepadEnable; - private static boolean grabbing = false; - private static GLFWCursor cursor; - public static double cursorX = 0.5, cursorY = 0.5; + private static PosCallback mPositionCallback; + private static CursorCallback mCursorCallback; + private static GamepadEnableHandler gamepadEnabler; + private static GLFWClipboard mClipboardImpl; public static ByteBuffer gamepadButtonBuffer; public static FloatBuffer gamepadAxisBuffer; @@ -31,65 +32,35 @@ public class GLFW { GLFW.initialize(); } - - - public static void setCursorImpl(CursorImplementor cursorImpl) { - GLFW.cursorImpl = new WeakReference<>(cursorImpl); - addGrabListener(cursorImpl); - } - - public static void setClipboardImpl(ClipboardProvider clipboardImpl) { - GLFW.clipboardImpl = new WeakReference<>(clipboardImpl); - } - public static void setGamepadEnableHandler(GamepadEnableHandler handler) { - GLFW.gamepadEnable = new WeakReference<>(handler); + GLFW.gamepadEnabler = handler; } - public static void addGrabListener(GrabListener grabListener) { - grabListeners.add(grabListener); + public static void setGrabListener(GrabListener grabListener) { + mGrabListener = grabListener; } - public static boolean isGrabbing() { - return grabbing; + public static void setClipboardImpl(GLFWClipboard mClipboardImpl) { + GLFW.mClipboardImpl = mClipboardImpl; } - - public static GLFWCursor getCursor() { - return cursor; + public static void setPositionCallback(PosCallback callback){ + mPositionCallback = callback; } - - public static void sendMousePos() { - if(!grabbing) { - if(cursorX < 0) cursorX = 0; - else if(cursorX > 1) cursorX = 1; - if(cursorY < 0) cursorY = 0; - else if(cursorY > 1) cursorY = 1; - } - CursorImplementor cursor = Utils.getWeakReference(GLFW.cursorImpl); - if(cursor != null) cursor.onCursorPosition(); - sendMousePosition0(cursorX, cursorY); + public static void setCursorCallback(CursorCallback callback){ + mCursorCallback = callback; + } + public static void setInitCallback(Runnable callback){ + onInitCallback = callback; } @SuppressWarnings("unused") // Used from native private static void receiveGrabState(boolean isGrabbing) { - boolean wasGrabbing = GLFW.grabbing; - GLFW.grabbing = isGrabbing; - Utils.runOnUiThread(() -> { - for(GrabListener grabListener : grabListeners) grabListener.onGrabState(isGrabbing); - }); - if(!isGrabbing && wasGrabbing) { - cursorX = cursorY = 0.5; - sendMousePos(); - } - + mGrabListener.onGrabState(isGrabbing); } @SuppressWarnings("unused") // Used from native private static void receiveCursorPos(double x, double y) { - cursorX = x; - cursorY = y; - CursorImplementor cursor = Utils.getWeakReference(GLFW.cursorImpl); - if(cursor != null) cursor.onCursorPosition(); + mPositionCallback.receive(x, y); } @SuppressWarnings("unused") // Used from native @@ -106,23 +77,17 @@ private static GLFWCursor loadCursor(ByteBuffer imageBytes, int width, int heigh @SuppressWarnings("unused") // Used from native private static void useCursor(GLFWCursor glfwCursor) { - GLFW.cursor = glfwCursor; - CursorImplementor cursor = Utils.getWeakReference(GLFW.cursorImpl); - if(cursor != null) cursor.onCursorChanged(); + mCursorCallback.onCursorUse(glfwCursor); } @SuppressWarnings("unused") // Used from native private static String getClipboardString() { - ClipboardProvider clipboardProvider = Utils.getWeakReference(clipboardImpl); - if(clipboardProvider == null) return null; - return clipboardProvider.getClipboardString(); + return mClipboardImpl.getClipboardString(); } @SuppressWarnings("unused") // Used from native private static void setClipboardString(String str) { - ClipboardProvider clipboardProvider = Utils.getWeakReference(clipboardImpl); - if(clipboardProvider == null) return; - clipboardProvider.setClipboardString(str); + mClipboardImpl.setClipboardString(str); } @SuppressWarnings("unused") // Used from native @@ -135,23 +100,15 @@ private static void enableDirectGamepad(ByteBuffer buttonBuffer, ByteBuffer axis } gamepadAxisBuffer = axisFloatBuffer; gamepadButtonBuffer = buttonBuffer; - GamepadEnableHandler enableHandler = Utils.getWeakReference(gamepadEnable); - if(enableHandler != null) enableHandler.onEnableGamepad(); - } - - public static void sendKeyEvent(int glfwCode, boolean state, int mods) { - sendKeyEvent(glfwCode, state ? 1 : 0, mods); + gamepadEnabler.onEnableGamepad(); } + @SuppressWarnings("unused") // Used from native public static void receiveInit() { onInitCallback.run(); } - public static void setInitCallback(Runnable callback){ - onInitCallback = callback; - } - public static native void initialize(); - private static native void sendMousePosition0(double x, double y); + public static native void sendMousePosition0(double x, double y); public static native void sendKeyEvent(int glfwCode, int state, int mods); public static native void sendRawKeyEvent(int androidCode, int state, int mods, char codepoint); public static native void sendMouseEvent(int glfwMouseKey, int state, int mods); diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/ClipboardProvider.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWClipboard.java similarity index 75% rename from jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/ClipboardProvider.java rename to jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWClipboard.java index cf6445b2..c198746d 100644 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/ClipboardProvider.java +++ b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWClipboard.java @@ -1,6 +1,6 @@ package git.artdeell.dnbootstrap.glfw; -public interface ClipboardProvider { +public interface GLFWClipboard { String getClipboardString(); void setClipboardString(String str); } diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java index 63bfc917..0faa365a 100644 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java +++ b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursor.java @@ -3,12 +3,24 @@ import android.graphics.Bitmap; public class GLFWCursor { - public final Bitmap bitmap; - public final int hotX, hotY; + private final int xhot; + private final int yhot; + private final Bitmap bitmap; + public GLFWCursor(Bitmap cursorBitmap, int xhot, int yhot) { + this.xhot = xhot; + this.yhot = yhot; + this.bitmap = cursorBitmap; + } + + public int getXhot() { + return xhot; + } + + public int getYhot() { + return yhot; + } - public GLFWCursor(Bitmap bitmap, int hotX, int hotY) { - this.bitmap = bitmap; - this.hotX = hotX; - this.hotY = hotY; + public Bitmap getBitmap() { + return bitmap; } } diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java deleted file mode 100644 index b575df8b..00000000 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFWCursorView.java +++ /dev/null @@ -1,77 +0,0 @@ -package git.artdeell.dnbootstrap.glfw; - -import androidx.annotation.NonNull; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.View; - -import git.artdeell.dnbglfw.R; - - -public class GLFWCursorView extends View implements CursorImplementor { - private Drawable cursorDrawable; - private final Paint customCursorPaint = new Paint(); - private boolean noDraw = false; - private float mouseScale = 1f; - - public GLFWCursorView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public GLFWCursorView(Context context) { - this(context, null); - } - - public GLFWCursorView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public GLFWCursorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - GLFW.setCursorImpl(this); - if(attrs != null) { - try(TypedArray arr = context.obtainStyledAttributes(attrs,R.styleable.GLFWCursorView)) { - cursorDrawable = arr.getDrawable(R.styleable.GLFWCursorView_defaultCursorDrawable); - } - } - if(cursorDrawable == null) cursorDrawable = new FallbackCursorDrawable(); - cursorDrawable.setBounds(0, 0, 36, 54); - } - - @Override - protected void onDraw(@NonNull Canvas canvas) { - if(noDraw) return; - canvas.translate((int)(GLFW.cursorX * getWidth()), (int)(GLFW.cursorY * getHeight())); - GLFWCursor cursor = GLFW.getCursor(); - canvas.scale(mouseScale, mouseScale); - if(cursor == null) { - cursorDrawable.draw(canvas); - }else { - canvas.drawBitmap(cursor.bitmap, -cursor.hotX, -cursor.hotY, customCursorPaint); - } - } - - @Override - public void onCursorPosition() { - if(!noDraw) post(this::invalidate); - } - - @Override - public void onCursorChanged() { - post(this::invalidate); - } - - @Override - public void onGrabState(boolean isGrabbing) { - noDraw = isGrabbing; - invalidate(); - } - - public void setCursorScale(float scale){ - this.mouseScale = scale; - } -} From 7df29cd249aca628367e943ebb08d8260fa907b4 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Sat, 1 Aug 2026 21:23:41 +0400 Subject: [PATCH 05/12] Android: switch input to absolute coordinates --- src/android_window.c | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/src/android_window.c b/src/android_window.c index 99239a81..4094614b 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -77,10 +77,6 @@ static pthread_cond_t nw_egl_cond; static pthread_mutex_t nw_vulkan_mutex; static pthread_cond_t nw_vulkan_cond; -static struct { - double x, y; -} cursor_unscaled; - static struct { JavaVM *vm; jclass glfw_class; @@ -270,16 +266,8 @@ static inline void android_send_event(input_event_t *ev) { _input_queue_push(&input_queue, ev); } -static void computeCursorPos() { - int width = surfaceOwner->android.width; - int height = surfaceOwner->android.height; - _glfw.android.xcursor = cursor_unscaled.x * width; - _glfw.android.ycursor = cursor_unscaled.y * height; -} - static void push_flag_events() { if((update_flags & FLAG_MOUSE_POS) != 0) { - computeCursorPos(); _glfwInputCursorPos(surfaceOwner, _glfw.android.xcursor, _glfw.android.ycursor); } update_flags = 0; @@ -877,20 +865,11 @@ void _glfwGetCursorPosAndroid(_GLFWwindow* window, double* xpos, double* ypos) void _glfwSetCursorPosAndroid(_GLFWwindow* window, double x, double y) { ensure_comm_connected(); - double scaled_cursor_x = x / window->android.width; - double scaled_cursor_y = y / window->android.height; (*jni_tl.env)->CallStaticVoidMethod(jni_tl.env, jni.glfw_class, jni.method_receiveCursorPos, - scaled_cursor_x, scaled_cursor_y); - - cursor_unscaled.x = scaled_cursor_x; - cursor_unscaled.y = scaled_cursor_y; - if(window == surfaceOwner) { - _glfw.android.xcursor = x; - _glfw.android.ycursor = y; - } else { - computeCursorPos(); - } + x, y); + _glfw.android.xcursor = x; + _glfw.android.ycursor = y; } void _glfwSetCursorModeAndroid(_GLFWwindow* window, int mode) @@ -1349,9 +1328,8 @@ Java_git_artdeell_dnbootstrap_glfw_GLFW_nativeSurfaceDestroyed(JNIEnv *env, JNIEXPORT void JNICALL Java_git_artdeell_dnbootstrap_glfw_GLFW_sendMousePosition0__DD(JNIEnv *env, jclass clazz, jdouble v1, jdouble v2) { - if(cursor_unscaled.x == v1 && cursor_unscaled.y == v2) return; - cursor_unscaled.x = v1; - cursor_unscaled.y = v2; + _glfw.android.xcursor = v1; + _glfw.android.ycursor = v2; update_flags |= FLAG_MOUSE_POS; _input_queue_wait_unlock(&input_queue); } From 73cb0feacf1cc0e05451bcc67c5f6d36f6bdd398 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Mon, 3 Aug 2026 01:58:42 +0400 Subject: [PATCH 06/12] EGL: fallback to MojoExec for client library Fixes MobileGL --- src/egl_context.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/egl_context.c b/src/egl_context.c index a97e104b..2088e850 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -34,6 +34,7 @@ #if defined(_GLFW_ANDROID) #include "android_egl_context_hook.h" +#include "mojoexec.h" #endif // Return a description of the specified EGL error @@ -883,6 +884,15 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, break; } +#if defined(_GLFW_ANDROID) + // As a last resort try getting client handle from MojoExec + // It might appear as a real client library + if(!window->context.egl.client){ + window->context.egl.client = mojoexec_acq_egl_handle(); + fprintf(stderr, "acquired client handle through mojoexec : %p\n", window->context.egl.client); + } +#endif + if (!window->context.egl.client) { _glfwInputError(GLFW_API_UNAVAILABLE, From bc9744711b3a2b7e6682b2e2a2a36ac61898c37d Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Fri, 7 Aug 2026 14:53:51 +0400 Subject: [PATCH 07/12] Android: support setting window attributes --- .../src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java | 1 + src/android_window.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java index 7784909e..9531e9d7 100644 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java +++ b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java @@ -118,4 +118,5 @@ public static void receiveInit() { public static native void nativeSurfaceDestroyed(); public static native void nativeSurfaceUpdated(); public static native void nativeNotifyGamepadConnected(); + public static native void nativeSetWindowAttribs(int attrib, boolean value); } diff --git a/src/android_window.c b/src/android_window.c index 4094614b..b916892c 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -1422,4 +1422,11 @@ void _glfwSendJoystickConnectEvent(void) { .type = GLFW_ANDROID_EVENT_TYPE_JOYSTICK_STATE }; android_send_event(&event); +} + +JNIEXPORT void JNICALL +Java_git_artdeell_dnbootstrap_glfw_GLFW_nativeSetWindowAttribs(JNIEnv *env, jclass clazz, + jint attrib, jboolean value) { + if(!surfaceOwner) return; + glfwSetWindowAttrib((GLFWwindow *) surfaceOwner, attrib, value ? GLFW_TRUE : GLFW_FALSE); } \ No newline at end of file From 7efaa2c5229eef38bb8f62dde03d3daaca8d2a89 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Fri, 7 Aug 2026 15:11:54 +0400 Subject: [PATCH 08/12] jni-bindings: add some GLFW attributes into bindings --- .../src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java index 9531e9d7..43e8d857 100644 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java +++ b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java @@ -27,6 +27,9 @@ public interface CursorCallback { public static ByteBuffer gamepadButtonBuffer; public static FloatBuffer gamepadAxisBuffer; + public static final int GLFW_VISIBLE = 0x00020004; + public static final int GLFW_HOVERED = 0x0002000B; + static { System.loadLibrary("glfw"); GLFW.initialize(); From 8c1caf24d1c10e8aba9059f7077c67703b582bd0 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Fri, 7 Aug 2026 17:32:31 +0400 Subject: [PATCH 09/12] Android: correctly set hover/visible attributes --- src/android_platform.h | 1 + src/android_window.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/android_platform.h b/src/android_platform.h index 72e5a1d5..e35628e4 100644 --- a/src/android_platform.h +++ b/src/android_platform.h @@ -87,6 +87,7 @@ typedef struct _GLFWwindowAndroid int width; int height; GLFWbool visible; + GLFWbool hovered; GLFWbool iconified; GLFWbool maximized; GLFWbool resizable; diff --git a/src/android_window.c b/src/android_window.c index b916892c..dbc85b35 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -352,6 +352,7 @@ static int createNativeWindow(_GLFWwindow* window, window->android.maximized = wndconfig->maximized; window->android.floating = wndconfig->floating; window->android.transparent = fbconfig->transparent; + window->android.hovered = GLFW_TRUE; window->android.opacity = 1.f; return GLFW_TRUE; @@ -679,7 +680,7 @@ GLFWbool _glfwWindowMaximizedAndroid(_GLFWwindow* window) GLFWbool _glfwWindowHoveredAndroid(_GLFWwindow* window) { - return GLFW_TRUE; + return window->android.hovered; } GLFWbool _glfwFramebufferTransparentAndroid(_GLFWwindow* window) @@ -1428,5 +1429,16 @@ JNIEXPORT void JNICALL Java_git_artdeell_dnbootstrap_glfw_GLFW_nativeSetWindowAttribs(JNIEnv *env, jclass clazz, jint attrib, jboolean value) { if(!surfaceOwner) return; - glfwSetWindowAttrib((GLFWwindow *) surfaceOwner, attrib, value ? GLFW_TRUE : GLFW_FALSE); + switch(attrib){ + case GLFW_HOVERED: + surfaceOwner->android.hovered = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_VISIBLE: + if(value) _glfwShowWindowAndroid(surfaceOwner); + else _glfwHideWindowAndroid(surfaceOwner); + break; + default: + _glfwInputError(GLFW_INVALID_ENUM, "Unsupported window attribute"); + break; + } } \ No newline at end of file From 6ef99d261580dc550812fd4f43d63469f8122da9 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Fri, 7 Aug 2026 17:43:24 +0400 Subject: [PATCH 10/12] Android: also apply focus on show window --- src/android_window.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/android_window.c b/src/android_window.c index dbc85b35..22eb3e79 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -728,6 +728,12 @@ GLFWbool _glfwRawMouseMotionSupportedAndroid(void) void _glfwShowWindowAndroid(_GLFWwindow* window) { + if(_glfw.android.focusedWindow == NULL) + { + _glfw.android.focusedWindow = window; + _glfwInputWindowFocus(window, GLFW_TRUE); + } + window->android.visible = GLFW_TRUE; } From 920ad84d2b81f40c139f71ca16e7b1aa5ca2c668 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Fri, 7 Aug 2026 22:40:26 +0400 Subject: [PATCH 11/12] Android: make keyboard events return boolean --- .../src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java | 2 +- src/android_window.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java index 43e8d857..2b8ee6cc 100644 --- a/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java +++ b/jni_bindings/src/main/java/git/artdeell/dnbootstrap/glfw/GLFW.java @@ -113,7 +113,7 @@ public static void receiveInit() { public static native void initialize(); public static native void sendMousePosition0(double x, double y); public static native void sendKeyEvent(int glfwCode, int state, int mods); - public static native void sendRawKeyEvent(int androidCode, int state, int mods, char codepoint); + public static native boolean sendRawKeyEvent(int androidCode, int state, int mods, char codepoint); public static native void sendMouseEvent(int glfwMouseKey, int state, int mods); public static native void sendBulkUnicodeEvent(String input, int mods); public static native void sendScrollEvent(double xoffset, double yoffset); diff --git a/src/android_window.c b/src/android_window.c index 22eb3e79..043cbad6 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -1369,11 +1369,11 @@ Java_git_artdeell_dnbootstrap_glfw_GLFW_sendKeyEvent(JNIEnv *env, jclass clazz, android_send_event(&event); } -JNIEXPORT void JNICALL +JNIEXPORT jboolean JNICALL Java_git_artdeell_dnbootstrap_glfw_GLFW_sendRawKeyEvent(JNIEnv *env, jclass clazz, jint android_code, jint state, jint mods, jchar codepoint) { int glfw_key = translate_android_key(android_code); - if(glfw_key == -1) return; + if(glfw_key == -1) return false; input_event_t event = { .type = GLFW_ANDROID_EVENT_TYPE_KEYBOARD_KEY, .k.glfw_code = glfw_key, From 62734b0e134d77fcee63f1dbc4a963e5278ce4d7 Mon Sep 17 00:00:00 2001 From: whitebelyash Date: Fri, 7 Aug 2026 23:03:04 +0400 Subject: [PATCH 12/12] Android: add forgotten return --- src/android_window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/android_window.c b/src/android_window.c index 043cbad6..ebc2e85f 100644 --- a/src/android_window.c +++ b/src/android_window.c @@ -1383,6 +1383,7 @@ Java_git_artdeell_dnbootstrap_glfw_GLFW_sendRawKeyEvent(JNIEnv *env, jclass claz .k.codepoint = codepoint }; android_send_event(&event); + return true; } JNIEXPORT void JNICALL