//======================================================================== // GLFW 3.5 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2024 kunitoki // Copyright (c) 2017 Curi0 // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include "internal.h" #include #include #include #include #include #include #include static float lastCursorPosX = 0.0f; static float lastCursorPosY = 0.0f; static void moveNativeWindowToBackground(ANativeActivity* nativeActivity) { JNIEnv* env = NULL; (*nativeActivity->vm)->AttachCurrentThread(nativeActivity->vm, &env, NULL); jmethodID moveTaskToBackMethod = (*env)->GetMethodID(env, nativeActivity->clazz, "moveTaskToBack", "(Z)Z"); if (moveTaskToBackMethod == NULL) return; (*env)->CallBooleanMethod(env, nativeActivity->clazz, moveTaskToBackMethod, JNI_TRUE); } static int32_t handleInput(struct android_app* app, AInputEvent* event) { if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { size_t pointerCount = AMotionEvent_getPointerCount(event); for (size_t i = 0; i < pointerCount; ++i) { lastCursorPosX = AMotionEvent_getX(event, i); lastCursorPosY = AMotionEvent_getY(event, i); int32_t action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK; // Map Android touch events to GLFW touch events switch (action) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_POINTER_DOWN: _glfwInputMouseClick(_glfw.windowListHead, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, 0); break; case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_POINTER_UP: _glfwInputMouseClick(_glfw.windowListHead, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, 0); break; case AMOTION_EVENT_ACTION_MOVE: _glfwInputCursorPos(_glfw.windowListHead, lastCursorPosX, lastCursorPosY); break; case AMOTION_EVENT_ACTION_CANCEL: // Handle cancel if necessary break; } } return 1; } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { _glfwInputKey(_glfw.windowListHead, 0 , AKeyEvent_getKeyCode(event), GLFW_PRESS, 0); return 1; } return 0; } static void handleEvents(int timeout) { ALooper_pollOnce(timeout, NULL, NULL, (void**)&_glfw.gstate.source); if (_glfw.gstate.source != NULL) _glfw.gstate.source->process(_glfw.gstate.app, _glfw.gstate.source); } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// GLFWbool _glfwCreateWindowAndroid(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { // wait for window to become ready while (_glfw.gstate.app->window == NULL) handleEvents(-1); // hmmm maybe should be ANative_Window only? window->android = _glfw.gstate.app; window->android->onInputEvent = handleInput; if (ctxconfig->client != GLFW_NO_API) { if ((ctxconfig->source == GLFW_NATIVE_CONTEXT_API) | (ctxconfig->source == GLFW_EGL_CONTEXT_API)) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { if (!_glfwInitOSMesa()) return GLFW_FALSE; if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } if (!_glfwRefreshContextAttribs(window, ctxconfig)) return GLFW_FALSE; } return GLFW_TRUE; } void _glfwDestroyWindowAndroid(_GLFWwindow* window) { if (window->context.destroy) window->context.destroy(window); ANativeActivity_finish(window->android->activity); } void _glfwSetWindowTitleAndroid(_GLFWwindow* window, const char* title) { } void _glfwSetWindowIconAndroid(_GLFWwindow* window, int count, const GLFWimage* images) { } void _glfwSetWindowMonitorAndroid(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate) { } void _glfwGetWindowPosAndroid(_GLFWwindow* window, int* xpos, int* ypos) { if (xpos) *xpos = 0; if (ypos) *ypos = 0; } void _glfwSetWindowPosAndroid(_GLFWwindow* window, int xpos, int ypos) { } void _glfwGetWindowSizeAndroid(_GLFWwindow* window, int* width, int* height) { if (width) { *width = (window->android->window != NULL) ? ANativeWindow_getWidth(window->android->window) : 0; } if (height) { *height = (window->android->window != NULL) ? ANativeWindow_getHeight(window->android->window) : 0; } } void _glfwSetWindowSizeAndroid(_GLFWwindow* window, int width, int height) { } void _glfwSetWindowSizeLimitsAndroid(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { } void _glfwSetWindowAspectRatioAndroid(_GLFWwindow* window, int n, int d) { } void _glfwGetFramebufferSizeAndroid(_GLFWwindow* window, int* width, int* height) { // the underlying buffer geometry is currently being initialized from the window width and height... // so high resolution displays are currently not supported...so it is safe to just call _glfwGetWindowSizeAndroid() for now _glfwGetWindowSizeAndroid(window, width, height); } void _glfwGetWindowFrameSizeAndroid(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { if (left) *left = window->android->contentRect.left; if (top) *top = window->android->contentRect.top; if (right) { int windowWidth = (window->android->window != NULL) ? ANativeWindow_getWidth(window->android->window) : 0; int rightFrame = windowWidth - window->android->contentRect.right; if (rightFrame < 0) rightFrame = 0; *right = rightFrame; } if (bottom) { int windowHeight = (window->android->window != NULL) ? ANativeWindow_getHeight(window->android->window) : 0; int bottomFrame = windowHeight - window->android->contentRect.bottom; if (bottomFrame < 0) bottomFrame = 0; *bottom = bottomFrame; } } void _glfwGetWindowContentScaleAndroid(_GLFWwindow* window, float* xscale, float* yscale) { if (xscale) { int32_t widthDensity = AConfiguration_getScreenWidthDp(window->android->config); if (widthDensity == ACONFIGURATION_SCREEN_WIDTH_DP_ANY) { *xscale = 1.0f; } else { int32_t widthPixels = ANativeWindow_getWidth(window->android->window); *xscale = (float)widthPixels / (float)widthDensity; } } if (yscale) { int32_t heightDensity = AConfiguration_getScreenHeightDp(window->android->config); if (heightDensity == ACONFIGURATION_SCREEN_HEIGHT_DP_ANY) { *yscale = 1.0f; } else { int32_t heightPixels = ANativeWindow_getHeight(window->android->window); *yscale = (float)heightPixels / (float)heightDensity; } } } void _glfwIconifyWindowAndroid(_GLFWwindow* window) { moveNativeWindowToBackground(window->android->activity); } void _glfwRestoreWindowAndroid(_GLFWwindow* window) { } void _glfwMaximizeWindowAndroid(_GLFWwindow* window) { } GLFWbool _glfwWindowMaximizedAndroid(_GLFWwindow* window) { return GLFW_TRUE; } GLFWbool _glfwWindowHoveredAndroid(_GLFWwindow* window) { return GLFW_FALSE; } GLFWbool _glfwFramebufferTransparentAndroid(_GLFWwindow* window) { return GLFW_FALSE; } void _glfwSetWindowResizableAndroid(_GLFWwindow* window, GLFWbool enabled) { } void _glfwSetWindowDecoratedAndroid(_GLFWwindow* window, GLFWbool enabled) { } void _glfwSetWindowFloatingAndroid(_GLFWwindow* window, GLFWbool enabled) { } void _glfwSetWindowMousePassthroughAndroid(_GLFWwindow* window, GLFWbool enabled) { } float _glfwGetWindowOpacityAndroid(_GLFWwindow* window) { return 1.0f; } void _glfwSetWindowOpacityAndroid(_GLFWwindow* window, float opacity) { } void _glfwSetRawMouseMotionAndroid(_GLFWwindow *window, GLFWbool enabled) { } GLFWbool _glfwRawMouseMotionSupportedAndroid(void) { return GLFW_FALSE; } void _glfwShowWindowAndroid(_GLFWwindow* window) { } void _glfwRequestWindowAttentionAndroid(_GLFWwindow* window) { } void _glfwHideWindowAndroid(_GLFWwindow* window) { } void _glfwFocusWindowAndroid(_GLFWwindow* window) { } GLFWbool _glfwWindowFocusedAndroid(_GLFWwindow* window) { return GLFW_FALSE; } GLFWbool _glfwWindowIconifiedAndroid(_GLFWwindow* window) { return GLFW_FALSE; } GLFWbool _glfwWindowVisibleAndroid(_GLFWwindow* window) { return GLFW_FALSE; } void _glfwPollEventsAndroid(void) { handleEvents(0); } void _glfwWaitEventsAndroid(void) { handleEvents(-1); } void _glfwWaitEventsTimeoutAndroid(double timeout) { handleEvents(timeout * 1e3); } void _glfwPostEmptyEventAndroid(void) { } void _glfwGetCursorPosAndroid(_GLFWwindow* window, double* xpos, double* ypos) { if (xpos) *xpos = (double)lastCursorPosX; if (ypos) *ypos = (double)lastCursorPosY; } void _glfwSetCursorPosAndroid(_GLFWwindow* window, double x, double y) { } void _glfwSetCursorModeAndroid(_GLFWwindow* window, int mode) { } GLFWbool _glfwCreateCursorAndroid(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { return GLFW_TRUE; } GLFWbool _glfwCreateStandardCursorAndroid(_GLFWcursor* cursor, int shape) { return GLFW_TRUE; } void _glfwDestroyCursorAndroid(_GLFWcursor* cursor) { } void _glfwSetCursorAndroid(_GLFWwindow* window, _GLFWcursor* cursor) { } void _glfwSetClipboardStringAndroid(const char* string) { } const char* _glfwGetClipboardStringAndroid(void) { return NULL; } const char* _glfwGetScancodeNameAndroid(int scancode) { return ""; } int _glfwGetKeyScancodeAndroid(int key) { return -1; } EGLenum _glfwGetEGLPlatformAndroid(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) { int type = 0; if (_glfw.egl.ANGLE_platform_angle_opengl) { if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL) type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE; else if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGLES) type = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE; } if (_glfw.egl.ANGLE_platform_angle_vulkan) { if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN) type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; } if (type) { *attribs = _glfw_calloc(3, sizeof(EGLint)); (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE; (*attribs)[1] = type; (*attribs)[2] = EGL_NONE; return EGL_PLATFORM_ANGLE_ANGLE; } } return 0; } EGLNativeDisplayType _glfwGetEGLNativeDisplayAndroid(void) { return EGL_DEFAULT_DISPLAY; } EGLNativeWindowType _glfwGetEGLNativeWindowAndroid(_GLFWwindow* window) { return ((EGLNativeWindowType) window->android->window); } void _glfwGetRequiredInstanceExtensionsAndroid(char** extensions) { if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_android_surface) return; extensions[0] = "VK_KHR_surface"; extensions[1] = "VK_KHR_android_surface"; } GLFWbool _glfwGetPhysicalDevicePresentationSupportAndroid(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { return GLFW_TRUE; } VkResult _glfwCreateWindowSurfaceAndroid(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { VkResult err; VkAndroidSurfaceCreateInfoKHR sci; PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR; vkCreateAndroidSurfaceKHR = (PFN_vkCreateAndroidSurfaceKHR)vkGetInstanceProcAddr(instance, "vkCreateAndroidSurfaceKHR"); if (!vkCreateAndroidSurfaceKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "Android: Vulkan instance missing VK_KHR_android_surface extension"); return VK_ERROR_EXTENSION_NOT_PRESENT; } memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; sci.window = window->android->window; err = vkCreateAndroidSurfaceKHR(instance, &sci, allocator, surface); if (err) { _glfwInputError(GLFW_PLATFORM_ERROR, "Android: Failed to create Vulkan surface: %s", _glfwGetVulkanResultString(err)); } return err; } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI struct android_app* glfwGetAndroidApp(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return _glfw.gstate.app; }