aboutsummaryrefslogtreecommitdiff
path: root/raylib/src/platforms/rcore_android.c
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-04-21 11:09:56 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2025-04-21 11:09:56 +0200
commit123144c93bfc77883c8fb517828b47bbe13b8671 (patch)
tree762739afedb5f3f168051367515cb9b9a06ec68d /raylib/src/platforms/rcore_android.c
downloadminesweeper-master.tar.gz
minesweeper-master.zip
Initial commitHEADmaster
Diffstat (limited to 'raylib/src/platforms/rcore_android.c')
-rw-r--r--raylib/src/platforms/rcore_android.c1339
1 files changed, 1339 insertions, 0 deletions
diff --git a/raylib/src/platforms/rcore_android.c b/raylib/src/platforms/rcore_android.c
new file mode 100644
index 0000000..85ce82a
--- /dev/null
+++ b/raylib/src/platforms/rcore_android.c
@@ -0,0 +1,1339 @@
1/**********************************************************************************************
2*
3* rcore_android - Functions to manage window, graphics device and inputs
4*
5* PLATFORM: ANDROID
6* - Android (ARM, ARM64)
7*
8* LIMITATIONS:
9* - Limitation 01
10* - Limitation 02
11*
12* POSSIBLE IMPROVEMENTS:
13* - Improvement 01
14* - Improvement 02
15*
16* ADDITIONAL NOTES:
17* - TRACELOG() function is located in raylib [utils] module
18*
19* CONFIGURATION:
20* #define RCORE_PLATFORM_CUSTOM_FLAG
21* Custom flag for rcore on target platform -not used-
22*
23* DEPENDENCIES:
24* - Android NDK: Provides C API to access Android functionality
25* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
26*
27*
28* LICENSE: zlib/libpng
29*
30* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
31*
32* This software is provided "as-is", without any express or implied warranty. In no event
33* will the authors be held liable for any damages arising from the use of this software.
34*
35* Permission is granted to anyone to use this software for any purpose, including commercial
36* applications, and to alter it and redistribute it freely, subject to the following restrictions:
37*
38* 1. The origin of this software must not be misrepresented; you must not claim that you
39* wrote the original software. If you use this software in a product, an acknowledgment
40* in the product documentation would be appreciated but is not required.
41*
42* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
43* as being the original software.
44*
45* 3. This notice may not be removed or altered from any source distribution.
46*
47**********************************************************************************************/
48
49#include <android_native_app_glue.h> // Required for: android_app struct and activity management
50#include <android/window.h> // Required for: AWINDOW_FLAG_FULLSCREEN definition and others
51//#include <android/sensor.h> // Required for: Android sensors functions (accelerometer, gyroscope, light...)
52#include <jni.h> // Required for: JNIEnv and JavaVM [Used in OpenURL()]
53
54#include <EGL/egl.h> // Native platform windowing system interface
55
56//----------------------------------------------------------------------------------
57// Types and Structures Definition
58//----------------------------------------------------------------------------------
59typedef struct {
60 // Application data
61 struct android_app *app; // Android activity
62 struct android_poll_source *source; // Android events polling source
63 bool appEnabled; // Flag to detect if app is active ** = true
64 bool contextRebindRequired; // Used to know context rebind required
65
66 // Display data
67 EGLDisplay device; // Native display device (physical screen connection)
68 EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
69 EGLContext context; // Graphic context, mode in which drawing can be done
70 EGLConfig config; // Graphic config
71} PlatformData;
72
73//----------------------------------------------------------------------------------
74// Global Variables Definition
75//----------------------------------------------------------------------------------
76extern CoreData CORE; // Global CORE state context
77extern bool isGpuReady; // Flag to note GPU has been initialized successfully
78static PlatformData platform = { 0 }; // Platform specific data
79
80//----------------------------------------------------------------------------------
81// Local Variables Definition
82//----------------------------------------------------------------------------------
83#define KEYCODE_MAP_SIZE 162
84static const KeyboardKey mapKeycode[KEYCODE_MAP_SIZE] = {
85 KEY_NULL, // AKEYCODE_UNKNOWN
86 0, // AKEYCODE_SOFT_LEFT
87 0, // AKEYCODE_SOFT_RIGHT
88 0, // AKEYCODE_HOME
89 KEY_BACK, // AKEYCODE_BACK
90 0, // AKEYCODE_CALL
91 0, // AKEYCODE_ENDCALL
92 KEY_ZERO, // AKEYCODE_0
93 KEY_ONE, // AKEYCODE_1
94 KEY_TWO, // AKEYCODE_2
95 KEY_THREE, // AKEYCODE_3
96 KEY_FOUR, // AKEYCODE_4
97 KEY_FIVE, // AKEYCODE_5
98 KEY_SIX, // AKEYCODE_6
99 KEY_SEVEN, // AKEYCODE_7
100 KEY_EIGHT, // AKEYCODE_8
101 KEY_NINE, // AKEYCODE_9
102 0, // AKEYCODE_STAR
103 0, // AKEYCODE_POUND
104 KEY_UP, // AKEYCODE_DPAD_UP
105 KEY_DOWN, // AKEYCODE_DPAD_DOWN
106 KEY_LEFT, // AKEYCODE_DPAD_LEFT
107 KEY_RIGHT, // AKEYCODE_DPAD_RIGHT
108 0, // AKEYCODE_DPAD_CENTER
109 KEY_VOLUME_UP, // AKEYCODE_VOLUME_UP
110 KEY_VOLUME_DOWN, // AKEYCODE_VOLUME_DOWN
111 0, // AKEYCODE_POWER
112 0, // AKEYCODE_CAMERA
113 0, // AKEYCODE_CLEAR
114 KEY_A, // AKEYCODE_A
115 KEY_B, // AKEYCODE_B
116 KEY_C, // AKEYCODE_C
117 KEY_D, // AKEYCODE_D
118 KEY_E, // AKEYCODE_E
119 KEY_F, // AKEYCODE_F
120 KEY_G, // AKEYCODE_G
121 KEY_H, // AKEYCODE_H
122 KEY_I, // AKEYCODE_I
123 KEY_J, // AKEYCODE_J
124 KEY_K, // AKEYCODE_K
125 KEY_L, // AKEYCODE_L
126 KEY_M, // AKEYCODE_M
127 KEY_N, // AKEYCODE_N
128 KEY_O, // AKEYCODE_O
129 KEY_P, // AKEYCODE_P
130 KEY_Q, // AKEYCODE_Q
131 KEY_R, // AKEYCODE_R
132 KEY_S, // AKEYCODE_S
133 KEY_T, // AKEYCODE_T
134 KEY_U, // AKEYCODE_U
135 KEY_V, // AKEYCODE_V
136 KEY_W, // AKEYCODE_W
137 KEY_X, // AKEYCODE_X
138 KEY_Y, // AKEYCODE_Y
139 KEY_Z, // AKEYCODE_Z
140 KEY_COMMA, // AKEYCODE_COMMA
141 KEY_PERIOD, // AKEYCODE_PERIOD
142 KEY_LEFT_ALT, // AKEYCODE_ALT_LEFT
143 KEY_RIGHT_ALT, // AKEYCODE_ALT_RIGHT
144 KEY_LEFT_SHIFT, // AKEYCODE_SHIFT_LEFT
145 KEY_RIGHT_SHIFT, // AKEYCODE_SHIFT_RIGHT
146 KEY_TAB, // AKEYCODE_TAB
147 KEY_SPACE, // AKEYCODE_SPACE
148 0, // AKEYCODE_SYM
149 0, // AKEYCODE_EXPLORER
150 0, // AKEYCODE_ENVELOPE
151 KEY_ENTER, // AKEYCODE_ENTER
152 KEY_BACKSPACE, // AKEYCODE_DEL
153 KEY_GRAVE, // AKEYCODE_GRAVE
154 KEY_MINUS, // AKEYCODE_MINUS
155 KEY_EQUAL, // AKEYCODE_EQUALS
156 KEY_LEFT_BRACKET, // AKEYCODE_LEFT_BRACKET
157 KEY_RIGHT_BRACKET, // AKEYCODE_RIGHT_BRACKET
158 KEY_BACKSLASH, // AKEYCODE_BACKSLASH
159 KEY_SEMICOLON, // AKEYCODE_SEMICOLON
160 KEY_APOSTROPHE, // AKEYCODE_APOSTROPHE
161 KEY_SLASH, // AKEYCODE_SLASH
162 0, // AKEYCODE_AT
163 0, // AKEYCODE_NUM
164 0, // AKEYCODE_HEADSETHOOK
165 0, // AKEYCODE_FOCUS
166 0, // AKEYCODE_PLUS
167 KEY_MENU, // AKEYCODE_MENU
168 0, // AKEYCODE_NOTIFICATION
169 0, // AKEYCODE_SEARCH
170 0, // AKEYCODE_MEDIA_PLAY_PAUSE
171 0, // AKEYCODE_MEDIA_STOP
172 0, // AKEYCODE_MEDIA_NEXT
173 0, // AKEYCODE_MEDIA_PREVIOUS
174 0, // AKEYCODE_MEDIA_REWIND
175 0, // AKEYCODE_MEDIA_FAST_FORWARD
176 0, // AKEYCODE_MUTE
177 KEY_PAGE_UP, // AKEYCODE_PAGE_UP
178 KEY_PAGE_DOWN, // AKEYCODE_PAGE_DOWN
179 0, // AKEYCODE_PICTSYMBOLS
180 0, // AKEYCODE_SWITCH_CHARSET
181 0, // AKEYCODE_BUTTON_A
182 0, // AKEYCODE_BUTTON_B
183 0, // AKEYCODE_BUTTON_C
184 0, // AKEYCODE_BUTTON_X
185 0, // AKEYCODE_BUTTON_Y
186 0, // AKEYCODE_BUTTON_Z
187 0, // AKEYCODE_BUTTON_L1
188 0, // AKEYCODE_BUTTON_R1
189 0, // AKEYCODE_BUTTON_L2
190 0, // AKEYCODE_BUTTON_R2
191 0, // AKEYCODE_BUTTON_THUMBL
192 0, // AKEYCODE_BUTTON_THUMBR
193 0, // AKEYCODE_BUTTON_START
194 0, // AKEYCODE_BUTTON_SELECT
195 0, // AKEYCODE_BUTTON_MODE
196 KEY_ESCAPE, // AKEYCODE_ESCAPE
197 KEY_DELETE, // AKEYCODE_FORWARD_DELL
198 KEY_LEFT_CONTROL, // AKEYCODE_CTRL_LEFT
199 KEY_RIGHT_CONTROL, // AKEYCODE_CTRL_RIGHT
200 KEY_CAPS_LOCK, // AKEYCODE_CAPS_LOCK
201 KEY_SCROLL_LOCK, // AKEYCODE_SCROLL_LOCK
202 KEY_LEFT_SUPER, // AKEYCODE_META_LEFT
203 KEY_RIGHT_SUPER, // AKEYCODE_META_RIGHT
204 0, // AKEYCODE_FUNCTION
205 KEY_PRINT_SCREEN, // AKEYCODE_SYSRQ
206 KEY_PAUSE, // AKEYCODE_BREAK
207 KEY_HOME, // AKEYCODE_MOVE_HOME
208 KEY_END, // AKEYCODE_MOVE_END
209 KEY_INSERT, // AKEYCODE_INSERT
210 0, // AKEYCODE_FORWARD
211 0, // AKEYCODE_MEDIA_PLAY
212 0, // AKEYCODE_MEDIA_PAUSE
213 0, // AKEYCODE_MEDIA_CLOSE
214 0, // AKEYCODE_MEDIA_EJECT
215 0, // AKEYCODE_MEDIA_RECORD
216 KEY_F1, // AKEYCODE_F1
217 KEY_F2, // AKEYCODE_F2
218 KEY_F3, // AKEYCODE_F3
219 KEY_F4, // AKEYCODE_F4
220 KEY_F5, // AKEYCODE_F5
221 KEY_F6, // AKEYCODE_F6
222 KEY_F7, // AKEYCODE_F7
223 KEY_F8, // AKEYCODE_F8
224 KEY_F9, // AKEYCODE_F9
225 KEY_F10, // AKEYCODE_F10
226 KEY_F11, // AKEYCODE_F11
227 KEY_F12, // AKEYCODE_F12
228 KEY_NUM_LOCK, // AKEYCODE_NUM_LOCK
229 KEY_KP_0, // AKEYCODE_NUMPAD_0
230 KEY_KP_1, // AKEYCODE_NUMPAD_1
231 KEY_KP_2, // AKEYCODE_NUMPAD_2
232 KEY_KP_3, // AKEYCODE_NUMPAD_3
233 KEY_KP_4, // AKEYCODE_NUMPAD_4
234 KEY_KP_5, // AKEYCODE_NUMPAD_5
235 KEY_KP_6, // AKEYCODE_NUMPAD_6
236 KEY_KP_7, // AKEYCODE_NUMPAD_7
237 KEY_KP_8, // AKEYCODE_NUMPAD_8
238 KEY_KP_9, // AKEYCODE_NUMPAD_9
239 KEY_KP_DIVIDE, // AKEYCODE_NUMPAD_DIVIDE
240 KEY_KP_MULTIPLY, // AKEYCODE_NUMPAD_MULTIPLY
241 KEY_KP_SUBTRACT, // AKEYCODE_NUMPAD_SUBTRACT
242 KEY_KP_ADD, // AKEYCODE_NUMPAD_ADD
243 KEY_KP_DECIMAL, // AKEYCODE_NUMPAD_DOT
244 0, // AKEYCODE_NUMPAD_COMMA
245 KEY_KP_ENTER, // AKEYCODE_NUMPAD_ENTER
246 KEY_KP_EQUAL // AKEYCODE_NUMPAD_EQUALS
247};
248
249//----------------------------------------------------------------------------------
250// Module Internal Functions Declaration
251//----------------------------------------------------------------------------------
252int InitPlatform(void); // Initialize platform (graphics, inputs and more)
253void ClosePlatform(void); // Close platform
254
255static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands
256static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs
257static GamepadButton AndroidTranslateGamepadButton(int button); // Map Android gamepad button to raylib gamepad button
258
259//----------------------------------------------------------------------------------
260// Module Functions Declaration
261//----------------------------------------------------------------------------------
262// NOTE: Functions declaration is provided by raylib.h
263
264//----------------------------------------------------------------------------------
265// Module Functions Definition: Application
266//----------------------------------------------------------------------------------
267
268// To allow easier porting to android, we allow the user to define a
269// main function which we call from android_main, defined by ourselves
270extern int main(int argc, char *argv[]);
271
272// Android main function
273void android_main(struct android_app *app)
274{
275 char arg0[] = "raylib"; // NOTE: argv[] are mutable
276 platform.app = app;
277
278 // NOTE: Return from main is ignored
279 (void)main(1, (char *[]) { arg0, NULL });
280
281 // Request to end the native activity
282 ANativeActivity_finish(app->activity);
283
284 // Android ALooper_pollOnce() variables
285 int pollResult = 0;
286 int pollEvents = 0;
287
288 // Waiting for application events before complete finishing
289 while (!app->destroyRequested)
290 {
291 // Poll all events until we reach return value TIMEOUT, meaning no events left to process
292 while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void **)&platform.source)) > ALOOPER_POLL_TIMEOUT)
293 {
294 if (platform.source != NULL) platform.source->process(app, platform.source);
295 }
296 }
297}
298
299// NOTE: Add this to header (if apps really need it)
300struct android_app *GetAndroidApp(void)
301{
302 return platform.app;
303}
304
305//----------------------------------------------------------------------------------
306// Module Functions Definition: Window and Graphics Device
307//----------------------------------------------------------------------------------
308
309// Check if application should close
310bool WindowShouldClose(void)
311{
312 if (CORE.Window.ready) return CORE.Window.shouldClose;
313 else return true;
314}
315
316// Toggle fullscreen mode
317void ToggleFullscreen(void)
318{
319 TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
320}
321
322// Toggle borderless windowed mode
323void ToggleBorderlessWindowed(void)
324{
325 TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
326}
327
328// Set window state: maximized, if resizable
329void MaximizeWindow(void)
330{
331 TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
332}
333
334// Set window state: minimized
335void MinimizeWindow(void)
336{
337 TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
338}
339
340// Set window state: not minimized/maximized
341void RestoreWindow(void)
342{
343 TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
344}
345
346// Set window configuration state using flags
347void SetWindowState(unsigned int flags)
348{
349 TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
350}
351
352// Clear window configuration state flags
353void ClearWindowState(unsigned int flags)
354{
355 TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
356}
357
358// Set icon for window
359void SetWindowIcon(Image image)
360{
361 TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
362}
363
364// Set icon for window
365void SetWindowIcons(Image *images, int count)
366{
367 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
368}
369
370// Set title for window
371void SetWindowTitle(const char *title)
372{
373 CORE.Window.title = title;
374}
375
376// Set window position on screen (windowed mode)
377void SetWindowPosition(int x, int y)
378{
379 TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
380}
381
382// Set monitor for the current window
383void SetWindowMonitor(int monitor)
384{
385 TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
386}
387
388// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
389void SetWindowMinSize(int width, int height)
390{
391 CORE.Window.screenMin.width = width;
392 CORE.Window.screenMin.height = height;
393}
394
395// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
396void SetWindowMaxSize(int width, int height)
397{
398 CORE.Window.screenMax.width = width;
399 CORE.Window.screenMax.height = height;
400}
401
402// Set window dimensions
403void SetWindowSize(int width, int height)
404{
405 TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
406}
407
408// Set window opacity, value opacity is between 0.0 and 1.0
409void SetWindowOpacity(float opacity)
410{
411 TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
412}
413
414// Set window focused
415void SetWindowFocused(void)
416{
417 TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
418}
419
420// Get native window handle
421void *GetWindowHandle(void)
422{
423 TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
424 return NULL;
425}
426
427// Get number of monitors
428int GetMonitorCount(void)
429{
430 TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
431 return 1;
432}
433
434// Get number of monitors
435int GetCurrentMonitor(void)
436{
437 TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
438 return 0;
439}
440
441// Get selected monitor position
442Vector2 GetMonitorPosition(int monitor)
443{
444 TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
445 return (Vector2){ 0, 0 };
446}
447
448// Get selected monitor width (currently used by monitor)
449int GetMonitorWidth(int monitor)
450{
451 TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
452 return 0;
453}
454
455// Get selected monitor height (currently used by monitor)
456int GetMonitorHeight(int monitor)
457{
458 TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
459 return 0;
460}
461
462// Get selected monitor physical width in millimetres
463int GetMonitorPhysicalWidth(int monitor)
464{
465 TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
466 return 0;
467}
468
469// Get selected monitor physical height in millimetres
470int GetMonitorPhysicalHeight(int monitor)
471{
472 TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
473 return 0;
474}
475
476// Get selected monitor refresh rate
477int GetMonitorRefreshRate(int monitor)
478{
479 TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
480 return 0;
481}
482
483// Get the human-readable, UTF-8 encoded name of the selected monitor
484const char *GetMonitorName(int monitor)
485{
486 TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
487 return "";
488}
489
490// Get window position XY on monitor
491Vector2 GetWindowPosition(void)
492{
493 TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
494 return (Vector2){ 0, 0 };
495}
496
497// Get window scale DPI factor for current monitor
498Vector2 GetWindowScaleDPI(void)
499{
500 TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
501 return (Vector2){ 1.0f, 1.0f };
502}
503
504// Set clipboard text content
505void SetClipboardText(const char *text)
506{
507 TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
508}
509
510// Get clipboard text content
511// NOTE: returned string is allocated and freed by GLFW
512const char *GetClipboardText(void)
513{
514 TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
515 return NULL;
516}
517
518// Show mouse cursor
519void ShowCursor(void)
520{
521 CORE.Input.Mouse.cursorHidden = false;
522}
523
524// Hides mouse cursor
525void HideCursor(void)
526{
527 CORE.Input.Mouse.cursorHidden = true;
528}
529
530// Enables cursor (unlock cursor)
531void EnableCursor(void)
532{
533 // Set cursor position in the middle
534 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
535
536 CORE.Input.Mouse.cursorHidden = false;
537}
538
539// Disables cursor (lock cursor)
540void DisableCursor(void)
541{
542 // Set cursor position in the middle
543 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
544
545 CORE.Input.Mouse.cursorHidden = true;
546}
547
548// Swap back buffer with front buffer (screen drawing)
549void SwapScreenBuffer(void)
550{
551 eglSwapBuffers(platform.device, platform.surface);
552}
553
554//----------------------------------------------------------------------------------
555// Module Functions Definition: Misc
556//----------------------------------------------------------------------------------
557
558// Get elapsed time measure in seconds since InitTimer()
559double GetTime(void)
560{
561 double time = 0.0;
562 struct timespec ts = { 0 };
563 clock_gettime(CLOCK_MONOTONIC, &ts);
564 unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
565
566 time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
567
568 return time;
569}
570
571// Open URL with default system browser (if available)
572// NOTE: This function is only safe to use if you control the URL given.
573// A user could craft a malicious string performing another action.
574// Only call this function yourself not with user input or make sure to check the string yourself.
575// Ref: https://github.com/raysan5/raylib/issues/686
576void OpenURL(const char *url)
577{
578 // Security check to (partially) avoid malicious code
579 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
580 else
581 {
582 JNIEnv *env = NULL;
583 JavaVM *vm = platform.app->activity->vm;
584 (*vm)->AttachCurrentThread(vm, &env, NULL);
585
586 jstring urlString = (*env)->NewStringUTF(env, url);
587 jclass uriClass = (*env)->FindClass(env, "android/net/Uri");
588 jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
589 jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString);
590
591 jclass intentClass = (*env)->FindClass(env, "android/content/Intent");
592 jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;");
593 jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId);
594 jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V");
595 jobject intent = (*env)->AllocObject(env, intentClass);
596
597 (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri);
598 jclass activityClass = (*env)->FindClass(env, "android/app/Activity");
599 jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V");
600 (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent);
601
602 (*vm)->DetachCurrentThread(vm);
603 }
604}
605
606//----------------------------------------------------------------------------------
607// Module Functions Definition: Inputs
608//----------------------------------------------------------------------------------
609
610// Set internal gamepad mappings
611int SetGamepadMappings(const char *mappings)
612{
613 TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
614 return 0;
615}
616
617// Set gamepad vibration
618void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
619{
620 TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform");
621}
622
623// Set mouse position XY
624void SetMousePosition(int x, int y)
625{
626 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
627 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
628}
629
630// Set mouse cursor
631void SetMouseCursor(int cursor)
632{
633 TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
634}
635
636// Get physical key name.
637const char *GetKeyName(int key)
638{
639 TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
640 return "";
641}
642
643// Register all input events
644void PollInputEvents(void)
645{
646#if defined(SUPPORT_GESTURES_SYSTEM)
647 // NOTE: Gestures update must be called every frame to reset gestures correctly
648 // because ProcessGestureEvent() is just called on an event, not every frame
649 UpdateGestures();
650#endif
651
652 // Reset keys/chars pressed registered
653 CORE.Input.Keyboard.keyPressedQueueCount = 0;
654 CORE.Input.Keyboard.charPressedQueueCount = 0;
655 // Reset key repeats
656 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
657
658 // Reset last gamepad button/axis registered state
659 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
660 //CORE.Input.Gamepad.axisCount = 0;
661
662 for (int i = 0; i < MAX_GAMEPADS; i++)
663 {
664 if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available
665 {
666 // Register previous gamepad states
667 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
668 CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
669 }
670 }
671
672 // Register previous touch states
673 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
674
675 // Reset touch positions
676 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
677
678 // Register previous keys states
679 // NOTE: Android supports up to 260 keys
680 for (int i = 0; i < 260; i++)
681 {
682 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
683 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
684 }
685
686 // Android ALooper_pollOnce() variables
687 int pollResult = 0;
688 int pollEvents = 0;
689
690 // Poll Events (registered events) until we reach TIMEOUT which indicates there are no events left to poll
691 // NOTE: Activity is paused if not enabled (platform.appEnabled)
692 while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
693 {
694 // Process this event
695 if (platform.source != NULL) platform.source->process(platform.app, platform.source);
696
697 // NOTE: Allow closing the window in case a configuration change happened.
698 // The android_main function should be allowed to return to its caller in order for the
699 // Android OS to relaunch the activity.
700 if (platform.app->destroyRequested != 0)
701 {
702 CORE.Window.shouldClose = true;
703 }
704 }
705}
706
707//----------------------------------------------------------------------------------
708// Module Internal Functions Definition
709//----------------------------------------------------------------------------------
710
711// Initialize platform: graphics, inputs and more
712int InitPlatform(void)
713{
714 // Initialize display basic configuration
715 //----------------------------------------------------------------------------
716 CORE.Window.currentFbo.width = CORE.Window.screen.width;
717 CORE.Window.currentFbo.height = CORE.Window.screen.height;
718
719 // Set desired windows flags before initializing anything
720 ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
721
722 int orientation = AConfiguration_getOrientation(platform.app->config);
723
724 if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait");
725 else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape");
726
727 // TODO: Automatic orientation doesn't seem to work
728 if (CORE.Window.screen.width <= CORE.Window.screen.height)
729 {
730 AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT);
731 TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
732 }
733 else
734 {
735 AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND);
736 TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
737 }
738
739 //AConfiguration_getDensity(platform.app->config);
740 //AConfiguration_getKeyboard(platform.app->config);
741 //AConfiguration_getScreenSize(platform.app->config);
742 //AConfiguration_getScreenLong(platform.app->config);
743
744 // Set some default window flags
745 CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false
746 CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false
747 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true
748 CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false
749 //----------------------------------------------------------------------------
750
751 // Initialize App command system
752 // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()...
753 //----------------------------------------------------------------------------
754 platform.app->onAppCmd = AndroidCommandCallback;
755 //----------------------------------------------------------------------------
756
757 // Initialize input events system
758 //----------------------------------------------------------------------------
759 platform.app->onInputEvent = AndroidInputCallback;
760 //----------------------------------------------------------------------------
761
762 // Initialize storage system
763 //----------------------------------------------------------------------------
764 InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize assets manager
765
766 CORE.Storage.basePath = platform.app->activity->internalDataPath; // Define base path for storage
767 //----------------------------------------------------------------------------
768
769 TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Initialized successfully");
770
771 // Android ALooper_pollOnce() variables
772 int pollResult = 0;
773 int pollEvents = 0;
774
775 // Wait for window to be initialized (display and context)
776 while (!CORE.Window.ready)
777 {
778 // Process events until we reach TIMEOUT, which indicates no more events queued.
779 while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
780 {
781 // Process this event
782 if (platform.source != NULL) platform.source->process(platform.app, platform.source);
783
784 // NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle.
785 //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
786 }
787 }
788
789 return 0;
790}
791
792// Close platform
793void ClosePlatform(void)
794{
795 // Close surface, context and display
796 if (platform.device != EGL_NO_DISPLAY)
797 {
798 eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
799
800 if (platform.surface != EGL_NO_SURFACE)
801 {
802 eglDestroySurface(platform.device, platform.surface);
803 platform.surface = EGL_NO_SURFACE;
804 }
805
806 if (platform.context != EGL_NO_CONTEXT)
807 {
808 eglDestroyContext(platform.device, platform.context);
809 platform.context = EGL_NO_CONTEXT;
810 }
811
812 eglTerminate(platform.device);
813 platform.device = EGL_NO_DISPLAY;
814 }
815
816 // NOTE: Reset global state in case the activity is being relaunched.
817 if (platform.app->destroyRequested != 0) {
818 CORE = (CoreData){0};
819 platform = (PlatformData){0};
820 }
821}
822
823// Initialize display device and framebuffer
824// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
825// If width or height are 0, default display size will be used for framebuffer size
826// NOTE: returns false in case graphic device could not be created
827static int InitGraphicsDevice(void)
828{
829 CORE.Window.fullscreen = true;
830 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
831
832 EGLint samples = 0;
833 EGLint sampleBuffer = 0;
834 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
835 {
836 samples = 4;
837 sampleBuffer = 1;
838 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
839 }
840
841 const EGLint framebufferAttribs[] =
842 {
843 EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support
844 EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
845 EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
846 EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
847 //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
848 EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
849 //EGL_STENCIL_SIZE, 8, // Stencil buffer size
850 EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
851 EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
852 EGL_NONE
853 };
854
855 const EGLint contextAttribs[] =
856 {
857 EGL_CONTEXT_CLIENT_VERSION, 2,
858 EGL_NONE
859 };
860
861 EGLint numConfigs = 0;
862
863 // Get an EGL device connection
864 platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
865 if (platform.device == EGL_NO_DISPLAY)
866 {
867 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
868 return -1;
869 }
870
871 // Initialize the EGL device connection
872 if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
873 {
874 // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
875 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
876 return -1;
877 }
878
879 // Get an appropriate EGL framebuffer configuration
880 eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs);
881
882 // Set rendering API
883 eglBindAPI(EGL_OPENGL_ES_API);
884
885 // Create an EGL rendering context
886 platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
887 if (platform.context == EGL_NO_CONTEXT)
888 {
889 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
890 return -1;
891 }
892
893 // Create an EGL window surface
894 //---------------------------------------------------------------------------------
895 EGLint displayFormat = 0;
896
897 // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
898 // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
899 eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
900
901 // At this point we need to manage render size vs screen size
902 // NOTE: This function use and modify global module variables:
903 // -> CORE.Window.screen.width/CORE.Window.screen.height
904 // -> CORE.Window.render.width/CORE.Window.render.height
905 // -> CORE.Window.screenScale
906 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
907
908 ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
909 //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size
910
911 platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL);
912
913 // There must be at least one frame displayed before the buffers are swapped
914 //eglSwapInterval(platform.device, 1);
915
916 if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE)
917 {
918 TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface");
919 return -1;
920 }
921 else
922 {
923 CORE.Window.render.width = CORE.Window.screen.width;
924 CORE.Window.render.height = CORE.Window.screen.height;
925 CORE.Window.currentFbo.width = CORE.Window.render.width;
926 CORE.Window.currentFbo.height = CORE.Window.render.height;
927
928 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
929 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
930 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
931 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
932 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
933 }
934
935 // Load OpenGL extensions
936 // NOTE: GL procedures address loader is required to load extensions
937 rlLoadExtensions(eglGetProcAddress);
938
939 CORE.Window.ready = true;
940
941 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
942
943 return 0;
944}
945
946// ANDROID: Process activity lifecycle commands
947static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
948{
949 switch (cmd)
950 {
951 case APP_CMD_START:
952 {
953 //rendering = true;
954 } break;
955 case APP_CMD_RESUME: break;
956 case APP_CMD_INIT_WINDOW:
957 {
958 if (app->window != NULL)
959 {
960 if (platform.contextRebindRequired)
961 {
962 // Reset screen scaling to full display size
963 EGLint displayFormat = 0;
964 eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
965
966 // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the
967 // context rebinding if the screen is scaled unless offsets are added. There's probably a more
968 // appropriate way to fix this
969 ANativeWindow_setBuffersGeometry(app->window,
970 CORE.Window.render.width + CORE.Window.renderOffset.x,
971 CORE.Window.render.height + CORE.Window.renderOffset.y,
972 displayFormat);
973
974 // Recreate display surface and re-attach OpenGL context
975 platform.surface = eglCreateWindowSurface(platform.device, platform.config, app->window, NULL);
976 eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
977
978 platform.contextRebindRequired = false;
979 }
980 else
981 {
982 CORE.Window.display.width = ANativeWindow_getWidth(platform.app->window);
983 CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window);
984
985 // Initialize graphics device (display device and OpenGL context)
986 InitGraphicsDevice();
987
988 // Initialize OpenGL context (states and resources)
989 // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
990 rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
991 isGpuReady = true;
992
993 // Setup default viewport
994 // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
995 SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
996
997 // Initialize hi-res timer
998 InitTimer();
999
1000 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
1001 // Load default font
1002 // WARNING: External function: Module required: rtext
1003 LoadFontDefault();
1004 #if defined(SUPPORT_MODULE_RSHAPES)
1005 // Set font white rectangle for shapes drawing, so shapes and text can be batched together
1006 // WARNING: rshapes module is required, if not available, default internal white rectangle is used
1007 Rectangle rec = GetFontDefault().recs[95];
1008 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
1009 {
1010 // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
1011 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
1012 }
1013 else
1014 {
1015 // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
1016 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
1017 }
1018 #endif
1019 #else
1020 #if defined(SUPPORT_MODULE_RSHAPES)
1021 // Set default texture and rectangle to be used for shapes drawing
1022 // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
1023 Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
1024 SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes
1025 #endif
1026 #endif
1027
1028 // Initialize random seed
1029 SetRandomSeed((unsigned int)time(NULL));
1030
1031 // TODO: GPU assets reload in case of lost focus (lost context)
1032 // NOTE: This problem has been solved just unbinding and rebinding context from display
1033 /*
1034 if (assetsReloadRequired)
1035 {
1036 for (int i = 0; i < assetCount; i++)
1037 {
1038 // TODO: Unload old asset if required
1039
1040 // Load texture again to pointed texture
1041 (*textureAsset + i) = LoadTexture(assetPath[i]);
1042 }
1043 }
1044 */
1045 }
1046 }
1047 } break;
1048 case APP_CMD_GAINED_FOCUS:
1049 {
1050 platform.appEnabled = true;
1051 CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
1052 //ResumeMusicStream();
1053 } break;
1054 case APP_CMD_PAUSE: break;
1055 case APP_CMD_LOST_FOCUS:
1056 {
1057 platform.appEnabled = false;
1058 CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
1059 //PauseMusicStream();
1060 } break;
1061 case APP_CMD_TERM_WINDOW:
1062 {
1063 // Detach OpenGL context and destroy display surface
1064 // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming.
1065 // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
1066 // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :(
1067 if (platform.device != EGL_NO_DISPLAY)
1068 {
1069 eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1070
1071 if (platform.surface != EGL_NO_SURFACE)
1072 {
1073 eglDestroySurface(platform.device, platform.surface);
1074 platform.surface = EGL_NO_SURFACE;
1075 }
1076
1077 platform.contextRebindRequired = true;
1078 }
1079 // If 'platform.device' is already set to 'EGL_NO_DISPLAY'
1080 // this means that the user has already called 'CloseWindow()'
1081
1082 } break;
1083 case APP_CMD_SAVE_STATE: break;
1084 case APP_CMD_STOP: break;
1085 case APP_CMD_DESTROY: break;
1086 case APP_CMD_CONFIG_CHANGED:
1087 {
1088 //AConfiguration_fromAssetManager(platform.app->config, platform.app->activity->assetManager);
1089 //print_cur_config(platform.app);
1090
1091 // Check screen orientation here!
1092 } break;
1093 default: break;
1094 }
1095}
1096
1097// ANDROID: Map Android gamepad button to raylib gamepad button
1098static GamepadButton AndroidTranslateGamepadButton(int button)
1099{
1100 switch (button)
1101 {
1102 case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN;
1103 case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT;
1104 case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT;
1105 case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP;
1106 case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1;
1107 case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1;
1108 case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2;
1109 case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
1110 case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB;
1111 case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB;
1112 case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT;
1113 case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT;
1114 case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE;
1115 // On some (most?) gamepads dpad events are reported as axis motion instead
1116 case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN;
1117 case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT;
1118 case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT;
1119 case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP;
1120 default: return GAMEPAD_BUTTON_UNKNOWN;
1121 }
1122}
1123
1124// ANDROID: Get input events
1125static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
1126{
1127 // If additional inputs are required check:
1128 // https://developer.android.com/ndk/reference/group/input
1129 // https://developer.android.com/training/game-controllers/controller-input
1130
1131 int type = AInputEvent_getType(event);
1132 int source = AInputEvent_getSource(event);
1133
1134 if (type == AINPUT_EVENT_TYPE_MOTION)
1135 {
1136 if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
1137 ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
1138 {
1139 // For now we'll assume a single gamepad which we "detect" on its input event
1140 CORE.Input.Gamepad.ready[0] = true;
1141
1142 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue(
1143 event, AMOTION_EVENT_AXIS_X, 0);
1144 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue(
1145 event, AMOTION_EVENT_AXIS_Y, 0);
1146 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue(
1147 event, AMOTION_EVENT_AXIS_Z, 0);
1148 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue(
1149 event, AMOTION_EVENT_AXIS_RZ, 0);
1150 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue(
1151 event, AMOTION_EVENT_AXIS_BRAKE, 0)*2.0f - 1.0f;
1152 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue(
1153 event, AMOTION_EVENT_AXIS_GAS, 0)*2.0f - 1.0f;
1154
1155 // dpad is reported as an axis on android
1156 float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0);
1157 float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0);
1158
1159 if (dpadX == 1.0f)
1160 {
1161 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1;
1162 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0;
1163 }
1164 else if (dpadX == -1.0f)
1165 {
1166 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0;
1167 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1;
1168 }
1169 else
1170 {
1171 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0;
1172 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0;
1173 }
1174
1175 if (dpadY == 1.0f)
1176 {
1177 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1;
1178 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0;
1179 }
1180 else if (dpadY == -1.0f)
1181 {
1182 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0;
1183 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1;
1184 }
1185 else
1186 {
1187 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0;
1188 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0;
1189 }
1190
1191 return 1; // Handled gamepad axis motion
1192 }
1193 }
1194 else if (type == AINPUT_EVENT_TYPE_KEY)
1195 {
1196 int32_t keycode = AKeyEvent_getKeyCode(event);
1197 //int32_t AKeyEvent_getMetaState(event);
1198
1199 // Handle gamepad button presses and releases
1200 if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
1201 ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
1202 {
1203 // For now we'll assume a single gamepad which we "detect" on its input event
1204 CORE.Input.Gamepad.ready[0] = true;
1205
1206 GamepadButton button = AndroidTranslateGamepadButton(keycode);
1207
1208 if (button == GAMEPAD_BUTTON_UNKNOWN) return 1;
1209
1210 if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
1211 {
1212 CORE.Input.Gamepad.currentButtonState[0][button] = 1;
1213 }
1214 else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up
1215
1216 return 1; // Handled gamepad button
1217 }
1218
1219 KeyboardKey key = (keycode > 0 && keycode < KEYCODE_MAP_SIZE)? mapKeycode[keycode] : KEY_NULL;
1220 if (key != KEY_NULL)
1221 {
1222 // Save current key and its state
1223 // NOTE: Android key action is 0 for down and 1 for up
1224 if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
1225 {
1226 CORE.Input.Keyboard.currentKeyState[key] = 1; // Key down
1227
1228 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
1229 CORE.Input.Keyboard.keyPressedQueueCount++;
1230 }
1231 else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
1232 else CORE.Input.Keyboard.currentKeyState[key] = 0; // Key up
1233 }
1234
1235 if (keycode == AKEYCODE_POWER)
1236 {
1237 // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS
1238 // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS
1239 // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected.
1240 // NOTE: AndroidManifest.xml must have <activity android:configChanges="orientation|keyboardHidden|screenSize" >
1241 // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
1242 return 0;
1243 }
1244 else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU))
1245 {
1246 // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS!
1247 return 1;
1248 }
1249 else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN))
1250 {
1251 // Set default OS behaviour
1252 return 0;
1253 }
1254
1255 return 0;
1256 }
1257
1258 // Register touch points count
1259 CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event);
1260
1261 for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
1262 {
1263 // Register touch points id
1264 CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i);
1265
1266 // Register touch points position
1267 CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
1268
1269 // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height
1270 float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x)/(float)CORE.Window.display.width;
1271 float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y)/(float)CORE.Window.display.height;
1272 CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2;
1273 CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2;
1274 }
1275
1276 int32_t action = AMotionEvent_getAction(event);
1277 unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
1278
1279#if defined(SUPPORT_GESTURES_SYSTEM)
1280 GestureEvent gestureEvent = { 0 };
1281
1282 gestureEvent.pointCount = CORE.Input.Touch.pointCount;
1283
1284 // Register touch actions
1285 if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
1286 else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP;
1287 else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
1288 else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
1289
1290 for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
1291 {
1292 gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
1293 gestureEvent.position[i] = CORE.Input.Touch.position[i];
1294 gestureEvent.position[i].x /= (float)GetScreenWidth();
1295 gestureEvent.position[i].y /= (float)GetScreenHeight();
1296 }
1297
1298 // Gesture data is sent to gestures system for processing
1299 ProcessGestureEvent(gestureEvent);
1300#endif
1301
1302 int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
1303
1304 if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP)
1305 {
1306 // One of the touchpoints is released, remove it from touch point arrays
1307 for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++)
1308 {
1309 CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1];
1310 CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1];
1311 }
1312
1313 CORE.Input.Touch.pointCount--;
1314 }
1315
1316 // When all touchpoints are tapped and released really quickly, this event is generated
1317 if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0;
1318
1319 if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
1320 else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
1321
1322 // Stores the previous position of touch[0] only while it's active to calculate the delta.
1323 if (flags == AMOTION_EVENT_ACTION_MOVE)
1324 {
1325 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
1326 }
1327 else
1328 {
1329 CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
1330 }
1331
1332 // Map touch[0] as mouse input for convenience
1333 CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0];
1334 CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
1335
1336 return 0;
1337}
1338
1339// EOF

Generated with cgit - Back to sebastiano.tronto.net