SDL_android.c 103 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_PLATFORM_ANDROID
  20. #include "SDL_android.h"
  21. #include "../../events/SDL_events_c.h"
  22. #include "../../video/android/SDL_androidkeyboard.h"
  23. #include "../../video/android/SDL_androidmouse.h"
  24. #include "../../video/android/SDL_androidtouch.h"
  25. #include "../../video/android/SDL_androidpen.h"
  26. #include "../../video/android/SDL_androidvideo.h"
  27. #include "../../video/android/SDL_androidwindow.h"
  28. #include "../../joystick/android/SDL_sysjoystick_c.h"
  29. #include "../../haptic/android/SDL_syshaptic_c.h"
  30. #include "../../hidapi/android/hid.h"
  31. #include "../../SDL_hints_c.h"
  32. #include <android/log.h>
  33. #include <android/configuration.h>
  34. #include <android/asset_manager_jni.h>
  35. #include <sys/system_properties.h>
  36. #include <pthread.h>
  37. #include <sys/types.h>
  38. #include <unistd.h>
  39. #include <dlfcn.h>
  40. #define SDL_JAVA_PREFIX org_libsdl_app
  41. #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
  42. #define CONCAT2(prefix, class, function) Java_##prefix##_##class##_##function
  43. #define SDL_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)
  44. #define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
  45. #define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
  46. #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
  47. // Audio encoding definitions
  48. #define ENCODING_PCM_8BIT 3
  49. #define ENCODING_PCM_16BIT 2
  50. #define ENCODING_PCM_FLOAT 4
  51. // Java class SDLActivity
  52. JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetVersion)(
  53. JNIEnv *env, jclass cls);
  54. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
  55. JNIEnv *env, jclass cls);
  56. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeInitMainThread)(
  57. JNIEnv *env, jclass cls);
  58. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeCleanupMainThread)(
  59. JNIEnv *env, jclass cls);
  60. JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(
  61. JNIEnv *env, jclass cls,
  62. jstring library, jstring function, jobject array);
  63. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
  64. JNIEnv *env, jclass jcls,
  65. jstring filename);
  66. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
  67. JNIEnv *env, jclass jcls,
  68. jint surfaceWidth, jint surfaceHeight,
  69. jint deviceWidth, jint deviceHeight, jfloat density, jfloat rate);
  70. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
  71. JNIEnv *env, jclass cls);
  72. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(
  73. JNIEnv *env, jclass jcls);
  74. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
  75. JNIEnv *env, jclass jcls);
  76. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(
  77. JNIEnv *env, jclass jcls);
  78. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeScreenKeyboardShown)(
  79. JNIEnv *env, jclass jcls);
  80. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeScreenKeyboardHidden)(
  81. JNIEnv *env, jclass jcls);
  82. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
  83. JNIEnv *env, jclass jcls,
  84. jint keycode);
  85. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
  86. JNIEnv *env, jclass jcls,
  87. jint keycode);
  88. JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)(
  89. JNIEnv *env, jclass jcls);
  90. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
  91. JNIEnv *env, jclass jcls);
  92. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
  93. JNIEnv *env, jclass jcls,
  94. jint touch_device_id_in, jint pointer_finger_id_in,
  95. jint action, jfloat x, jfloat y, jfloat p);
  96. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchStart)(
  97. JNIEnv *env, jclass jcls);
  98. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchUpdate)(
  99. JNIEnv *env, jclass jcls,
  100. jfloat scale);
  101. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchEnd)(
  102. JNIEnv *env, jclass jcls);
  103. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
  104. JNIEnv *env, jclass jcls,
  105. jint button, jint action, jfloat x, jfloat y, jboolean relative);
  106. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePen)(
  107. JNIEnv *env, jclass jcls,
  108. jint pen_id_in, jint device_type, jint button, jint action, jfloat x, jfloat y, jfloat p);
  109. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
  110. JNIEnv *env, jclass jcls,
  111. jfloat x, jfloat y, jfloat z);
  112. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
  113. JNIEnv *env, jclass jcls);
  114. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
  115. JNIEnv *env, jclass cls);
  116. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeLocaleChanged)(
  117. JNIEnv *env, jclass cls);
  118. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDarkModeChanged)(
  119. JNIEnv *env, jclass cls, jboolean enabled);
  120. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
  121. JNIEnv *env, jclass cls);
  122. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
  123. JNIEnv *env, jclass cls);
  124. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
  125. JNIEnv *env, jclass cls);
  126. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
  127. JNIEnv *env, jclass cls);
  128. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
  129. JNIEnv *env, jclass cls, jboolean hasFocus);
  130. JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
  131. JNIEnv *env, jclass cls,
  132. jstring name);
  133. JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeGetHintBoolean)(
  134. JNIEnv *env, jclass cls,
  135. jstring name, jboolean default_value);
  136. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
  137. JNIEnv *env, jclass cls,
  138. jstring name, jstring value);
  139. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetNaturalOrientation)(
  140. JNIEnv *env, jclass cls,
  141. jint orientation);
  142. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeRotationChanged)(
  143. JNIEnv *env, jclass cls,
  144. jint rotation);
  145. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeInsetsChanged)(
  146. JNIEnv *env, jclass cls,
  147. jint left, jint right, jint top, jint bottom);
  148. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
  149. JNIEnv *env, jclass cls,
  150. jint touchId, jstring name);
  151. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
  152. JNIEnv *env, jclass cls,
  153. jint requestCode, jboolean result);
  154. JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeAllowRecreateActivity)(
  155. JNIEnv *env, jclass jcls);
  156. JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter)(
  157. JNIEnv *env, jclass jcls);
  158. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeFileDialog)(
  159. JNIEnv *env, jclass jcls,
  160. jint requestCode, jobjectArray fileList, jint filter);
  161. static JNINativeMethod SDLActivity_tab[] = {
  162. { "nativeGetVersion", "()Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetVersion) },
  163. { "nativeSetupJNI", "()V", SDL_JAVA_INTERFACE(nativeSetupJNI) },
  164. { "nativeInitMainThread", "()V", SDL_JAVA_INTERFACE(nativeInitMainThread) },
  165. { "nativeCleanupMainThread", "()V", SDL_JAVA_INTERFACE(nativeCleanupMainThread) },
  166. { "nativeRunMain", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I", SDL_JAVA_INTERFACE(nativeRunMain) },
  167. { "onNativeDropFile", "(Ljava/lang/String;)V", SDL_JAVA_INTERFACE(onNativeDropFile) },
  168. { "nativeSetScreenResolution", "(IIIIFF)V", SDL_JAVA_INTERFACE(nativeSetScreenResolution) },
  169. { "onNativeResize", "()V", SDL_JAVA_INTERFACE(onNativeResize) },
  170. { "onNativeSurfaceCreated", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceCreated) },
  171. { "onNativeSurfaceChanged", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceChanged) },
  172. { "onNativeSurfaceDestroyed", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed) },
  173. { "onNativeScreenKeyboardShown", "()V", SDL_JAVA_INTERFACE(onNativeScreenKeyboardShown) },
  174. { "onNativeScreenKeyboardHidden", "()V", SDL_JAVA_INTERFACE(onNativeScreenKeyboardHidden) },
  175. { "onNativeKeyDown", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyDown) },
  176. { "onNativeKeyUp", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyUp) },
  177. { "onNativeSoftReturnKey", "()Z", SDL_JAVA_INTERFACE(onNativeSoftReturnKey) },
  178. { "onNativeKeyboardFocusLost", "()V", SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost) },
  179. { "onNativeTouch", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativeTouch) },
  180. { "onNativePinchStart", "()V", SDL_JAVA_INTERFACE(onNativePinchStart) },
  181. { "onNativePinchUpdate", "(F)V", SDL_JAVA_INTERFACE(onNativePinchUpdate) },
  182. { "onNativePinchEnd", "()V", SDL_JAVA_INTERFACE(onNativePinchEnd) },
  183. { "onNativeMouse", "(IIFFZ)V", SDL_JAVA_INTERFACE(onNativeMouse) },
  184. { "onNativePen", "(IIIIFFF)V", SDL_JAVA_INTERFACE(onNativePen) },
  185. { "onNativeAccel", "(FFF)V", SDL_JAVA_INTERFACE(onNativeAccel) },
  186. { "onNativeClipboardChanged", "()V", SDL_JAVA_INTERFACE(onNativeClipboardChanged) },
  187. { "nativeLowMemory", "()V", SDL_JAVA_INTERFACE(nativeLowMemory) },
  188. { "onNativeLocaleChanged", "()V", SDL_JAVA_INTERFACE(onNativeLocaleChanged) },
  189. { "onNativeDarkModeChanged", "(Z)V", SDL_JAVA_INTERFACE(onNativeDarkModeChanged) },
  190. { "nativeSendQuit", "()V", SDL_JAVA_INTERFACE(nativeSendQuit) },
  191. { "nativeQuit", "()V", SDL_JAVA_INTERFACE(nativeQuit) },
  192. { "nativePause", "()V", SDL_JAVA_INTERFACE(nativePause) },
  193. { "nativeResume", "()V", SDL_JAVA_INTERFACE(nativeResume) },
  194. { "nativeFocusChanged", "(Z)V", SDL_JAVA_INTERFACE(nativeFocusChanged) },
  195. { "nativeGetHint", "(Ljava/lang/String;)Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetHint) },
  196. { "nativeGetHintBoolean", "(Ljava/lang/String;Z)Z", SDL_JAVA_INTERFACE(nativeGetHintBoolean) },
  197. { "nativeSetenv", "(Ljava/lang/String;Ljava/lang/String;)V", SDL_JAVA_INTERFACE(nativeSetenv) },
  198. { "nativeSetNaturalOrientation", "(I)V", SDL_JAVA_INTERFACE(nativeSetNaturalOrientation) },
  199. { "onNativeRotationChanged", "(I)V", SDL_JAVA_INTERFACE(onNativeRotationChanged) },
  200. { "onNativeInsetsChanged", "(IIII)V", SDL_JAVA_INTERFACE(onNativeInsetsChanged) },
  201. { "nativeAddTouch", "(ILjava/lang/String;)V", SDL_JAVA_INTERFACE(nativeAddTouch) },
  202. { "nativePermissionResult", "(IZ)V", SDL_JAVA_INTERFACE(nativePermissionResult) },
  203. { "nativeAllowRecreateActivity", "()Z", SDL_JAVA_INTERFACE(nativeAllowRecreateActivity) },
  204. { "nativeCheckSDLThreadCounter", "()I", SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter) },
  205. { "onNativeFileDialog", "(I[Ljava/lang/String;I)V", SDL_JAVA_INTERFACE(onNativeFileDialog) }
  206. };
  207. // Java class SDLInputConnection
  208. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
  209. JNIEnv *env, jclass cls,
  210. jstring text, jint newCursorPosition);
  211. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
  212. JNIEnv *env, jclass cls,
  213. jchar chUnicode);
  214. static JNINativeMethod SDLInputConnection_tab[] = {
  215. { "nativeCommitText", "(Ljava/lang/String;I)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText) },
  216. { "nativeGenerateScancodeForUnichar", "(C)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar) }
  217. };
  218. // Java class SDLAudioManager
  219. JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
  220. JNIEnv *env, jclass jcls);
  221. JNIEXPORT void JNICALL
  222. SDL_JAVA_AUDIO_INTERFACE(nativeAddAudioDevice)(JNIEnv *env, jclass jcls, jboolean recording, jstring name,
  223. jint device_id);
  224. JNIEXPORT void JNICALL
  225. SDL_JAVA_AUDIO_INTERFACE(nativeRemoveAudioDevice)(JNIEnv *env, jclass jcls, jboolean recording,
  226. jint device_id);
  227. static JNINativeMethod SDLAudioManager_tab[] = {
  228. { "nativeSetupJNI", "()V", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) },
  229. { "nativeAddAudioDevice", "(ZLjava/lang/String;I)V", SDL_JAVA_AUDIO_INTERFACE(nativeAddAudioDevice) },
  230. { "nativeRemoveAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(nativeRemoveAudioDevice) }
  231. };
  232. // Java class SDLControllerManager
  233. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
  234. JNIEnv *env, jclass jcls);
  235. JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
  236. JNIEnv *env, jclass jcls,
  237. jint device_id, jint keycode);
  238. JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
  239. JNIEnv *env, jclass jcls,
  240. jint device_id, jint keycode);
  241. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
  242. JNIEnv *env, jclass jcls,
  243. jint device_id, jint axis, jfloat value);
  244. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
  245. JNIEnv *env, jclass jcls,
  246. jint device_id, jint hat_id, jint x, jint y);
  247. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
  248. JNIEnv *env, jclass jcls,
  249. jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
  250. jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led);
  251. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
  252. JNIEnv *env, jclass jcls,
  253. jint device_id);
  254. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
  255. JNIEnv *env, jclass jcls,
  256. jint device_id, jstring device_name);
  257. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
  258. JNIEnv *env, jclass jcls,
  259. jint device_id);
  260. static JNINativeMethod SDLControllerManager_tab[] = {
  261. { "nativeSetupJNI", "()V", SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI) },
  262. { "onNativePadDown", "(II)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown) },
  263. { "onNativePadUp", "(II)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) },
  264. { "onNativeJoy", "(IIF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy) },
  265. { "onNativeHat", "(IIII)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat) },
  266. { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) },
  267. { "nativeRemoveJoystick", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick) },
  268. { "nativeAddHaptic", "(ILjava/lang/String;)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic) },
  269. { "nativeRemoveHaptic", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic) }
  270. };
  271. // Uncomment this to log messages entering and exiting methods in this file
  272. // #define DEBUG_JNI
  273. static void checkJNIReady(void);
  274. /*******************************************************************************
  275. This file links the Java side of Android with libsdl
  276. *******************************************************************************/
  277. #include <jni.h>
  278. /*******************************************************************************
  279. Globals
  280. *******************************************************************************/
  281. static pthread_key_t mThreadKey;
  282. static pthread_once_t key_once = PTHREAD_ONCE_INIT;
  283. static JavaVM *mJavaVM = NULL;
  284. // Main activity
  285. static jclass mActivityClass;
  286. // method signatures
  287. static jmethodID midClipboardGetText;
  288. static jmethodID midClipboardHasText;
  289. static jmethodID midClipboardSetText;
  290. static jmethodID midCreateCustomCursor;
  291. static jmethodID midDestroyCustomCursor;
  292. static jmethodID midGetContext;
  293. static jmethodID midGetManifestEnvironmentVariables;
  294. static jmethodID midGetNativeSurface;
  295. static jmethodID midInitTouch;
  296. static jmethodID midIsAndroidTV;
  297. static jmethodID midIsChromebook;
  298. static jmethodID midIsDeXMode;
  299. static jmethodID midIsTablet;
  300. static jmethodID midManualBackButton;
  301. static jmethodID midMinimizeWindow;
  302. static jmethodID midOpenURL;
  303. static jmethodID midRequestPermission;
  304. static jmethodID midShowToast;
  305. static jmethodID midSendMessage;
  306. static jmethodID midSetActivityTitle;
  307. static jmethodID midSetCustomCursor;
  308. static jmethodID midSetOrientation;
  309. static jmethodID midSetRelativeMouseEnabled;
  310. static jmethodID midSetSystemCursor;
  311. static jmethodID midSetWindowStyle;
  312. static jmethodID midShouldMinimizeOnFocusLoss;
  313. static jmethodID midShowTextInput;
  314. static jmethodID midSupportsRelativeMouse;
  315. static jmethodID midOpenFileDescriptor;
  316. static jmethodID midShowFileDialog;
  317. static jmethodID midGetPreferredLocales;
  318. // audio manager
  319. static jclass mAudioManagerClass;
  320. // method signatures
  321. static jmethodID midRegisterAudioDeviceCallback;
  322. static jmethodID midUnregisterAudioDeviceCallback;
  323. static jmethodID midAudioSetThreadPriority;
  324. // controller manager
  325. static jclass mControllerManagerClass;
  326. // method signatures
  327. static jmethodID midPollInputDevices;
  328. static jmethodID midJoystickSetLED;
  329. static jmethodID midPollHapticDevices;
  330. static jmethodID midHapticRun;
  331. static jmethodID midHapticRumble;
  332. static jmethodID midHapticStop;
  333. // Accelerometer data storage
  334. static SDL_DisplayOrientation displayNaturalOrientation;
  335. static SDL_DisplayOrientation displayCurrentOrientation;
  336. static float fLastAccelerometer[3];
  337. static bool bHasNewData;
  338. static bool bHasEnvironmentVariables;
  339. // Android AssetManager
  340. static void Internal_Android_Create_AssetManager(void);
  341. static void Internal_Android_Destroy_AssetManager(void);
  342. static AAssetManager *asset_manager = NULL;
  343. static jobject javaAssetManagerRef = 0;
  344. static SDL_Mutex *Android_ActivityMutex = NULL;
  345. static SDL_Mutex *Android_LifecycleMutex = NULL;
  346. static SDL_Semaphore *Android_LifecycleEventSem = NULL;
  347. static SDL_AndroidLifecycleEvent Android_LifecycleEvents[SDL_NUM_ANDROID_LIFECYCLE_EVENTS];
  348. static int Android_NumLifecycleEvents;
  349. /*******************************************************************************
  350. Functions called by JNI
  351. *******************************************************************************/
  352. /* From http://developer.android.com/guide/practices/jni.html
  353. * All threads are Linux threads, scheduled by the kernel.
  354. * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
  355. * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
  356. * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
  357. * and cannot make JNI calls.
  358. * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
  359. * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
  360. * is a no-op.
  361. * Note: You can call this function any number of times for the same thread, there's no harm in it
  362. */
  363. /* From http://developer.android.com/guide/practices/jni.html
  364. * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
  365. * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
  366. * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
  367. * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
  368. * Note: The destructor is not called unless the stored value is != NULL
  369. * Note: You can call this function any number of times for the same thread, there's no harm in it
  370. * (except for some lost CPU cycles)
  371. */
  372. // Set local storage value
  373. static bool Android_JNI_SetEnv(JNIEnv *env)
  374. {
  375. int status = pthread_setspecific(mThreadKey, env);
  376. if (status < 0) {
  377. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed pthread_setspecific() in Android_JNI_SetEnv() (err=%d)", status);
  378. return false;
  379. }
  380. return true;
  381. }
  382. // Get local storage value
  383. JNIEnv *Android_JNI_GetEnv(void)
  384. {
  385. // Get JNIEnv from the Thread local storage
  386. JNIEnv *env = pthread_getspecific(mThreadKey);
  387. if (!env) {
  388. // If it fails, try to attach ! (e.g the thread isn't created with SDL_CreateThread()
  389. int status;
  390. // There should be a JVM
  391. if (!mJavaVM) {
  392. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM");
  393. return NULL;
  394. }
  395. /* Attach the current thread to the JVM and get a JNIEnv.
  396. * It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */
  397. status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
  398. if (status < 0) {
  399. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status);
  400. return NULL;
  401. }
  402. // Save JNIEnv into the Thread local storage
  403. if (!Android_JNI_SetEnv(env)) {
  404. return NULL;
  405. }
  406. }
  407. return env;
  408. }
  409. // Set up an external thread for using JNI with Android_JNI_GetEnv()
  410. bool Android_JNI_SetupThread(void)
  411. {
  412. JNIEnv *env;
  413. int status;
  414. // There should be a JVM
  415. if (!mJavaVM) {
  416. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM");
  417. return false;
  418. }
  419. /* Attach the current thread to the JVM and get a JNIEnv.
  420. * It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */
  421. status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
  422. if (status < 0) {
  423. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status);
  424. return false;
  425. }
  426. // Save JNIEnv into the Thread local storage
  427. if (!Android_JNI_SetEnv(env)) {
  428. return false;
  429. }
  430. return true;
  431. }
  432. // Destructor called for each thread where mThreadKey is not NULL
  433. static void Android_JNI_ThreadDestroyed(void *value)
  434. {
  435. // The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required
  436. JNIEnv *env = (JNIEnv *)value;
  437. if (env) {
  438. (*mJavaVM)->DetachCurrentThread(mJavaVM);
  439. Android_JNI_SetEnv(NULL);
  440. }
  441. }
  442. // Creation of local storage mThreadKey
  443. static void Android_JNI_CreateKey(void)
  444. {
  445. int status = pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed);
  446. if (status < 0) {
  447. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_key_create() (err=%d)", status);
  448. }
  449. }
  450. static void Android_JNI_CreateKey_once(void)
  451. {
  452. int status = pthread_once(&key_once, Android_JNI_CreateKey);
  453. if (status < 0) {
  454. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_once() (err=%d)", status);
  455. }
  456. }
  457. static void register_methods(JNIEnv *env, const char *classname, JNINativeMethod *methods, int nb)
  458. {
  459. jclass clazz = (*env)->FindClass(env, classname);
  460. if (!clazz || (*env)->RegisterNatives(env, clazz, methods, nb) < 0) {
  461. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to register methods of %s", classname);
  462. return;
  463. }
  464. }
  465. // Library init
  466. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
  467. {
  468. JNIEnv *env = NULL;
  469. mJavaVM = vm;
  470. if ((*mJavaVM)->GetEnv(mJavaVM, (void **)&env, JNI_VERSION_1_4) != JNI_OK) {
  471. __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to get JNI Env");
  472. return JNI_VERSION_1_4;
  473. }
  474. register_methods(env, "org/libsdl/app/SDLActivity", SDLActivity_tab, SDL_arraysize(SDLActivity_tab));
  475. register_methods(env, "org/libsdl/app/SDLInputConnection", SDLInputConnection_tab, SDL_arraysize(SDLInputConnection_tab));
  476. register_methods(env, "org/libsdl/app/SDLAudioManager", SDLAudioManager_tab, SDL_arraysize(SDLAudioManager_tab));
  477. register_methods(env, "org/libsdl/app/SDLControllerManager", SDLControllerManager_tab, SDL_arraysize(SDLControllerManager_tab));
  478. register_methods(env, "org/libsdl/app/HIDDeviceManager", HIDDeviceManager_tab, SDL_arraysize(HIDDeviceManager_tab));
  479. return JNI_VERSION_1_4;
  480. }
  481. void checkJNIReady(void)
  482. {
  483. if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
  484. // We aren't fully initialized, let's just return.
  485. return;
  486. }
  487. SDL_SetMainReady();
  488. }
  489. // Get SDL version -- called before SDL_main() to verify JNI bindings
  490. JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetVersion)(JNIEnv *env, jclass cls)
  491. {
  492. char version[128];
  493. SDL_snprintf(version, sizeof(version), "%d.%d.%d", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION);
  494. return (*env)->NewStringUTF(env, version);
  495. }
  496. // Activity initialization -- called before SDL_main() to initialize JNI bindings
  497. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
  498. {
  499. __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
  500. // Start with a clean slate
  501. SDL_ClearError();
  502. /*
  503. * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
  504. * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
  505. */
  506. Android_JNI_CreateKey_once();
  507. // Save JNIEnv of SDLActivity
  508. Android_JNI_SetEnv(env);
  509. if (!mJavaVM) {
  510. __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to found a JavaVM");
  511. }
  512. /* Use a mutex to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
  513. * (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
  514. */
  515. if (!Android_ActivityMutex) {
  516. Android_ActivityMutex = SDL_CreateMutex(); // Could this be created twice if onCreate() is called a second time ?
  517. }
  518. if (!Android_ActivityMutex) {
  519. __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ActivityMutex mutex");
  520. }
  521. Android_LifecycleMutex = SDL_CreateMutex();
  522. if (!Android_LifecycleMutex) {
  523. __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_LifecycleMutex mutex");
  524. }
  525. Android_LifecycleEventSem = SDL_CreateSemaphore(0);
  526. if (!Android_LifecycleEventSem) {
  527. __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_LifecycleEventSem semaphore");
  528. }
  529. mActivityClass = (jclass)((*env)->NewGlobalRef(env, cls));
  530. midClipboardGetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardGetText", "()Ljava/lang/String;");
  531. midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardHasText", "()Z");
  532. midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardSetText", "(Ljava/lang/String;)V");
  533. midCreateCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "createCustomCursor", "([IIIII)I");
  534. midDestroyCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "destroyCustomCursor", "(I)V");
  535. midGetContext = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/app/Activity;");
  536. midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass, "getManifestEnvironmentVariables", "()Z");
  537. midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass, "getNativeSurface", "()Landroid/view/Surface;");
  538. midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass, "initTouch", "()V");
  539. midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass, "isAndroidTV", "()Z");
  540. midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass, "isChromebook", "()Z");
  541. midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass, "isDeXMode", "()Z");
  542. midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass, "isTablet", "()Z");
  543. midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass, "manualBackButton", "()V");
  544. midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass, "minimizeWindow", "()V");
  545. midOpenURL = (*env)->GetStaticMethodID(env, mActivityClass, "openURL", "(Ljava/lang/String;)Z");
  546. midRequestPermission = (*env)->GetStaticMethodID(env, mActivityClass, "requestPermission", "(Ljava/lang/String;I)V");
  547. midShowToast = (*env)->GetStaticMethodID(env, mActivityClass, "showToast", "(Ljava/lang/String;IIII)Z");
  548. midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
  549. midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle", "(Ljava/lang/String;)Z");
  550. midSetCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setCustomCursor", "(I)Z");
  551. midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass, "setOrientation", "(IIZLjava/lang/String;)V");
  552. midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
  553. midSetSystemCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setSystemCursor", "(I)Z");
  554. midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass, "setWindowStyle", "(Z)V");
  555. midShouldMinimizeOnFocusLoss = (*env)->GetStaticMethodID(env, mActivityClass, "shouldMinimizeOnFocusLoss", "()Z");
  556. midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIIII)Z");
  557. midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
  558. midOpenFileDescriptor = (*env)->GetStaticMethodID(env, mActivityClass, "openFileDescriptor", "(Ljava/lang/String;Ljava/lang/String;)I");
  559. midShowFileDialog = (*env)->GetStaticMethodID(env, mActivityClass, "showFileDialog", "([Ljava/lang/String;ZZI)Z");
  560. midGetPreferredLocales = (*env)->GetStaticMethodID(env, mActivityClass, "getPreferredLocales", "()Ljava/lang/String;");
  561. if (!midClipboardGetText ||
  562. !midClipboardHasText ||
  563. !midClipboardSetText ||
  564. !midCreateCustomCursor ||
  565. !midDestroyCustomCursor ||
  566. !midGetContext ||
  567. !midGetManifestEnvironmentVariables ||
  568. !midGetNativeSurface ||
  569. !midInitTouch ||
  570. !midIsAndroidTV ||
  571. !midIsChromebook ||
  572. !midIsDeXMode ||
  573. !midIsTablet ||
  574. !midManualBackButton ||
  575. !midMinimizeWindow ||
  576. !midOpenURL ||
  577. !midRequestPermission ||
  578. !midShowToast ||
  579. !midSendMessage ||
  580. !midSetActivityTitle ||
  581. !midSetCustomCursor ||
  582. !midSetOrientation ||
  583. !midSetRelativeMouseEnabled ||
  584. !midSetSystemCursor ||
  585. !midSetWindowStyle ||
  586. !midShouldMinimizeOnFocusLoss ||
  587. !midShowTextInput ||
  588. !midSupportsRelativeMouse ||
  589. !midOpenFileDescriptor ||
  590. !midShowFileDialog ||
  591. !midGetPreferredLocales) {
  592. __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
  593. }
  594. checkJNIReady();
  595. }
  596. // Audio initialization -- called before SDL_main() to initialize JNI bindings
  597. JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
  598. {
  599. __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
  600. mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
  601. midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
  602. "registerAudioDeviceCallback",
  603. "()V");
  604. midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
  605. "unregisterAudioDeviceCallback",
  606. "()V");
  607. midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
  608. "audioSetThreadPriority", "(ZI)V");
  609. if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioSetThreadPriority) {
  610. __android_log_print(ANDROID_LOG_WARN, "SDL",
  611. "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
  612. }
  613. checkJNIReady();
  614. }
  615. // Controller initialization -- called before SDL_main() to initialize JNI bindings
  616. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
  617. {
  618. __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
  619. mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
  620. midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
  621. "pollInputDevices", "()V");
  622. midJoystickSetLED = (*env)->GetStaticMethodID(env, mControllerManagerClass,
  623. "joystickSetLED", "(IIII)V");
  624. midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
  625. "pollHapticDevices", "()V");
  626. midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass,
  627. "hapticRun", "(IFI)V");
  628. midHapticRumble = (*env)->GetStaticMethodID(env, mControllerManagerClass,
  629. "hapticRumble", "(IFFI)V");
  630. midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass,
  631. "hapticStop", "(I)V");
  632. if (!midPollInputDevices || !midJoystickSetLED || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) {
  633. __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
  634. }
  635. checkJNIReady();
  636. }
  637. static int run_count = 0;
  638. static bool allow_recreate_activity;
  639. static bool allow_recreate_activity_set;
  640. JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter)(
  641. JNIEnv *env, jclass jcls)
  642. {
  643. int tmp = run_count;
  644. run_count += 1;
  645. return tmp;
  646. }
  647. void Android_SetAllowRecreateActivity(bool enabled)
  648. {
  649. allow_recreate_activity = enabled;
  650. allow_recreate_activity_set = true;
  651. }
  652. JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeAllowRecreateActivity)(
  653. JNIEnv *env, jclass jcls)
  654. {
  655. return allow_recreate_activity;
  656. }
  657. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeInitMainThread)(
  658. JNIEnv *env, jclass jcls)
  659. {
  660. __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeInitSDLThread() %d time", run_count);
  661. run_count += 1;
  662. // Save JNIEnv of SDLThread
  663. Android_JNI_SetEnv(env);
  664. }
  665. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeCleanupMainThread)(
  666. JNIEnv *env, jclass jcls)
  667. {
  668. /* This is a Java thread, it doesn't need to be Detached from the JVM.
  669. * Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */
  670. Android_JNI_SetEnv(NULL);
  671. }
  672. // Start up the SDL app
  673. JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array)
  674. {
  675. int status = -1;
  676. const char *library_file;
  677. void *library_handle;
  678. library_file = (*env)->GetStringUTFChars(env, library, NULL);
  679. library_handle = dlopen(library_file, RTLD_GLOBAL);
  680. if (library_handle == NULL) {
  681. /* When deploying android app bundle format uncompressed native libs may not extract from apk to filesystem.
  682. In this case we should use lib name without path. https://bugzilla.libsdl.org/show_bug.cgi?id=4739 */
  683. const char *library_name = SDL_strrchr(library_file, '/');
  684. if (library_name && *library_name) {
  685. library_name += 1;
  686. library_handle = dlopen(library_name, RTLD_GLOBAL);
  687. }
  688. }
  689. if (library_handle) {
  690. const char *function_name;
  691. SDL_main_func SDL_main;
  692. function_name = (*env)->GetStringUTFChars(env, function, NULL);
  693. SDL_main = (SDL_main_func)dlsym(library_handle, function_name);
  694. if (SDL_main) {
  695. // Use the name "app_process" for argv[0] so PHYSFS_platformCalcBaseDir() works.
  696. // https://github.com/love2d/love-android/issues/24
  697. // (note that PhysicsFS hasn't used argv on Android in a long time, but we'll keep this for compat at least for SDL3's lifetime. --ryan.)
  698. const char *argv0 = "app_process";
  699. const int len = (*env)->GetArrayLength(env, array); // argv elements, not counting argv[0].
  700. size_t total_alloc_len = (SDL_strlen(argv0) + 1) + ((len + 2) * sizeof (char *)); // len+2 to allocate an array that also holds argv0 and a NULL terminator.
  701. for (int i = 0; i < len; ++i) {
  702. total_alloc_len++; // null terminator.
  703. jstring string = (*env)->GetObjectArrayElement(env, array, i);
  704. if (string) {
  705. const char *utf = (*env)->GetStringUTFChars(env, string, 0);
  706. if (utf) {
  707. total_alloc_len += SDL_strlen(utf) + 1;
  708. (*env)->ReleaseStringUTFChars(env, string, utf);
  709. }
  710. (*env)->DeleteLocalRef(env, string);
  711. }
  712. }
  713. void *args = malloc(total_alloc_len); // This should NOT be SDL_malloc()
  714. if (!args) { // uhoh.
  715. __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Out of memory parsing command line!");
  716. } else {
  717. size_t remain = total_alloc_len - (sizeof (char *) * (len + 2));
  718. int argc = 0;
  719. char **argv = (char **) args;
  720. char *ptr = (char *) &argv[len + 2];
  721. size_t cpy = SDL_strlcpy(ptr, argv0, remain) + 1;
  722. argv[argc++] = ptr;
  723. SDL_assert(cpy <= remain); remain -= cpy; ptr += cpy;
  724. for (int i = 0; i < len; ++i) {
  725. jstring string = (*env)->GetObjectArrayElement(env, array, i);
  726. const char *utf = string ? (*env)->GetStringUTFChars(env, string, 0) : NULL;
  727. cpy = SDL_strlcpy(ptr, utf ? utf : "", remain) + 1;
  728. if (cpy < remain) {
  729. argv[argc++] = ptr;
  730. remain -= cpy;
  731. ptr += cpy;
  732. }
  733. if (utf) {
  734. (*env)->ReleaseStringUTFChars(env, string, utf);
  735. }
  736. if (string) {
  737. (*env)->DeleteLocalRef(env, string);
  738. }
  739. }
  740. argv[argc] = NULL;
  741. // Run the application.
  742. status = SDL_RunApp(argc, argv, SDL_main, NULL);
  743. // Release the arguments.
  744. free(args); // This should NOT be SDL_free()
  745. }
  746. } else {
  747. __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);
  748. }
  749. (*env)->ReleaseStringUTFChars(env, function, function_name);
  750. dlclose(library_handle);
  751. } else {
  752. __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't load library %s", library_file);
  753. }
  754. (*env)->ReleaseStringUTFChars(env, library, library_file);
  755. // Do not issue an exit or the whole application will terminate instead of just the SDL thread
  756. // exit(status);
  757. return status;
  758. }
  759. static int FindLifecycleEvent(SDL_AndroidLifecycleEvent event)
  760. {
  761. for (int index = 0; index < Android_NumLifecycleEvents; ++index) {
  762. if (Android_LifecycleEvents[index] == event) {
  763. return index;
  764. }
  765. }
  766. return -1;
  767. }
  768. static void RemoveLifecycleEvent(int index)
  769. {
  770. if (index < Android_NumLifecycleEvents - 1) {
  771. SDL_memmove(&Android_LifecycleEvents[index], &Android_LifecycleEvents[index+1], (Android_NumLifecycleEvents - index - 1) * sizeof(Android_LifecycleEvents[index]));
  772. }
  773. --Android_NumLifecycleEvents;
  774. }
  775. void Android_SendLifecycleEvent(SDL_AndroidLifecycleEvent event)
  776. {
  777. SDL_LockMutex(Android_LifecycleMutex);
  778. {
  779. int index;
  780. bool add_event = true;
  781. switch (event) {
  782. case SDL_ANDROID_LIFECYCLE_WAKE:
  783. // We don't need more than one wake queued
  784. index = FindLifecycleEvent(SDL_ANDROID_LIFECYCLE_WAKE);
  785. if (index >= 0) {
  786. add_event = false;
  787. }
  788. break;
  789. case SDL_ANDROID_LIFECYCLE_PAUSE:
  790. // If we have a resume queued, just stay in the paused state
  791. index = FindLifecycleEvent(SDL_ANDROID_LIFECYCLE_RESUME);
  792. if (index >= 0) {
  793. RemoveLifecycleEvent(index);
  794. add_event = false;
  795. }
  796. break;
  797. case SDL_ANDROID_LIFECYCLE_RESUME:
  798. // If we have a pause queued, just stay in the resumed state
  799. index = FindLifecycleEvent(SDL_ANDROID_LIFECYCLE_PAUSE);
  800. if (index >= 0) {
  801. RemoveLifecycleEvent(index);
  802. add_event = false;
  803. }
  804. break;
  805. case SDL_ANDROID_LIFECYCLE_LOWMEMORY:
  806. // We don't need more than one low memory event queued
  807. index = FindLifecycleEvent(SDL_ANDROID_LIFECYCLE_LOWMEMORY);
  808. if (index >= 0) {
  809. add_event = false;
  810. }
  811. break;
  812. case SDL_ANDROID_LIFECYCLE_DESTROY:
  813. // Remove all other events, we're done!
  814. while (Android_NumLifecycleEvents > 0) {
  815. RemoveLifecycleEvent(0);
  816. }
  817. break;
  818. default:
  819. SDL_assert(!"Sending unexpected lifecycle event");
  820. add_event = false;
  821. break;
  822. }
  823. if (add_event) {
  824. SDL_assert(Android_NumLifecycleEvents < SDL_arraysize(Android_LifecycleEvents));
  825. Android_LifecycleEvents[Android_NumLifecycleEvents++] = event;
  826. SDL_SignalSemaphore(Android_LifecycleEventSem);
  827. }
  828. }
  829. SDL_UnlockMutex(Android_LifecycleMutex);
  830. }
  831. bool Android_WaitLifecycleEvent(SDL_AndroidLifecycleEvent *event, Sint64 timeoutNS)
  832. {
  833. bool got_event = false;
  834. while (!got_event && SDL_WaitSemaphoreTimeoutNS(Android_LifecycleEventSem, timeoutNS)) {
  835. SDL_LockMutex(Android_LifecycleMutex);
  836. {
  837. if (Android_NumLifecycleEvents > 0) {
  838. *event = Android_LifecycleEvents[0];
  839. RemoveLifecycleEvent(0);
  840. got_event = true;
  841. }
  842. }
  843. SDL_UnlockMutex(Android_LifecycleMutex);
  844. }
  845. return got_event;
  846. }
  847. void Android_LockActivityMutex(void)
  848. {
  849. SDL_LockMutex(Android_ActivityMutex);
  850. }
  851. void Android_UnlockActivityMutex(void)
  852. {
  853. SDL_UnlockMutex(Android_ActivityMutex);
  854. }
  855. // Drop file
  856. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
  857. JNIEnv *env, jclass jcls,
  858. jstring filename)
  859. {
  860. const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
  861. SDL_SendDropFile(NULL, NULL, path);
  862. (*env)->ReleaseStringUTFChars(env, filename, path);
  863. SDL_SendDropComplete(NULL);
  864. }
  865. // Set screen resolution
  866. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
  867. JNIEnv *env, jclass jcls,
  868. jint surfaceWidth, jint surfaceHeight,
  869. jint deviceWidth, jint deviceHeight, jfloat density, jfloat rate)
  870. {
  871. SDL_LockMutex(Android_ActivityMutex);
  872. Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, density, rate);
  873. SDL_UnlockMutex(Android_ActivityMutex);
  874. }
  875. // Resize
  876. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
  877. JNIEnv *env, jclass jcls)
  878. {
  879. SDL_LockMutex(Android_ActivityMutex);
  880. if (Android_Window) {
  881. Android_SendResize(Android_Window);
  882. }
  883. SDL_UnlockMutex(Android_ActivityMutex);
  884. }
  885. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetNaturalOrientation)(
  886. JNIEnv *env, jclass jcls,
  887. jint orientation)
  888. {
  889. displayNaturalOrientation = (SDL_DisplayOrientation)orientation;
  890. }
  891. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeRotationChanged)(
  892. JNIEnv *env, jclass jcls,
  893. jint rotation)
  894. {
  895. SDL_LockMutex(Android_ActivityMutex);
  896. if (displayNaturalOrientation == SDL_ORIENTATION_LANDSCAPE) {
  897. rotation += 90;
  898. }
  899. switch (rotation % 360) {
  900. case 0:
  901. displayCurrentOrientation = SDL_ORIENTATION_PORTRAIT;
  902. break;
  903. case 90:
  904. displayCurrentOrientation = SDL_ORIENTATION_LANDSCAPE;
  905. break;
  906. case 180:
  907. displayCurrentOrientation = SDL_ORIENTATION_PORTRAIT_FLIPPED;
  908. break;
  909. case 270:
  910. displayCurrentOrientation = SDL_ORIENTATION_LANDSCAPE_FLIPPED;
  911. break;
  912. default:
  913. displayCurrentOrientation = SDL_ORIENTATION_UNKNOWN;
  914. break;
  915. }
  916. Android_SetOrientation(displayCurrentOrientation);
  917. SDL_UnlockMutex(Android_ActivityMutex);
  918. }
  919. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeInsetsChanged)(
  920. JNIEnv *env, jclass jcls,
  921. jint left, jint right, jint top, jint bottom)
  922. {
  923. SDL_LockMutex(Android_ActivityMutex);
  924. Android_SetWindowSafeAreaInsets(left, right, top, bottom);
  925. SDL_UnlockMutex(Android_ActivityMutex);
  926. }
  927. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
  928. JNIEnv *env, jclass cls,
  929. jint touchId, jstring name)
  930. {
  931. const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
  932. SDL_AddTouch(Android_ConvertJavaTouchID(touchId),
  933. SDL_TOUCH_DEVICE_DIRECT, utfname);
  934. (*env)->ReleaseStringUTFChars(env, name, utfname);
  935. }
  936. JNIEXPORT void JNICALL
  937. SDL_JAVA_AUDIO_INTERFACE(nativeAddAudioDevice)(JNIEnv *env, jclass jcls, jboolean recording,
  938. jstring name, jint device_id)
  939. {
  940. #if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
  941. if (SDL_GetCurrentAudioDriver() != NULL) {
  942. void *handle = (void *)((size_t)device_id);
  943. if (!SDL_FindPhysicalAudioDeviceByHandle(handle)) {
  944. const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL);
  945. SDL_AddAudioDevice(recording, SDL_strdup(utf8name), NULL, handle);
  946. (*env)->ReleaseStringUTFChars(env, name, utf8name);
  947. }
  948. }
  949. #endif
  950. }
  951. JNIEXPORT void JNICALL
  952. SDL_JAVA_AUDIO_INTERFACE(nativeRemoveAudioDevice)(JNIEnv *env, jclass jcls, jboolean recording,
  953. jint device_id)
  954. {
  955. #if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
  956. if (SDL_GetCurrentAudioDriver() != NULL) {
  957. SDL_Log("Removing device with handle %d, recording %d", device_id, recording);
  958. SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id)));
  959. }
  960. #endif
  961. }
  962. // Paddown
  963. JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
  964. JNIEnv *env, jclass jcls,
  965. jint device_id, jint keycode)
  966. {
  967. #ifdef SDL_JOYSTICK_ANDROID
  968. return Android_OnPadDown(device_id, keycode);
  969. #else
  970. return false;
  971. #endif // SDL_JOYSTICK_ANDROID
  972. }
  973. // Padup
  974. JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
  975. JNIEnv *env, jclass jcls,
  976. jint device_id, jint keycode)
  977. {
  978. #ifdef SDL_JOYSTICK_ANDROID
  979. return Android_OnPadUp(device_id, keycode);
  980. #else
  981. return false;
  982. #endif // SDL_JOYSTICK_ANDROID
  983. }
  984. // Joy
  985. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
  986. JNIEnv *env, jclass jcls,
  987. jint device_id, jint axis, jfloat value)
  988. {
  989. #ifdef SDL_JOYSTICK_ANDROID
  990. Android_OnJoy(device_id, axis, value);
  991. #endif // SDL_JOYSTICK_ANDROID
  992. }
  993. // POV Hat
  994. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
  995. JNIEnv *env, jclass jcls,
  996. jint device_id, jint hat_id, jint x, jint y)
  997. {
  998. #ifdef SDL_JOYSTICK_ANDROID
  999. Android_OnHat(device_id, hat_id, x, y);
  1000. #endif // SDL_JOYSTICK_ANDROID
  1001. }
  1002. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
  1003. JNIEnv *env, jclass jcls,
  1004. jint device_id, jstring device_name, jstring device_desc,
  1005. jint vendor_id, jint product_id,
  1006. jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led)
  1007. {
  1008. #ifdef SDL_JOYSTICK_ANDROID
  1009. const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
  1010. const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
  1011. Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats, can_rumble, has_rgb_led);
  1012. (*env)->ReleaseStringUTFChars(env, device_name, name);
  1013. (*env)->ReleaseStringUTFChars(env, device_desc, desc);
  1014. #endif // SDL_JOYSTICK_ANDROID
  1015. }
  1016. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
  1017. JNIEnv *env, jclass jcls,
  1018. jint device_id)
  1019. {
  1020. #ifdef SDL_JOYSTICK_ANDROID
  1021. Android_RemoveJoystick(device_id);
  1022. #endif // SDL_JOYSTICK_ANDROID
  1023. }
  1024. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
  1025. JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
  1026. {
  1027. #ifdef SDL_HAPTIC_ANDROID
  1028. const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
  1029. Android_AddHaptic(device_id, name);
  1030. (*env)->ReleaseStringUTFChars(env, device_name, name);
  1031. #endif // SDL_HAPTIC_ANDROID
  1032. }
  1033. JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
  1034. JNIEnv *env, jclass jcls, jint device_id)
  1035. {
  1036. #ifdef SDL_HAPTIC_ANDROID
  1037. Android_RemoveHaptic(device_id);
  1038. #endif
  1039. }
  1040. // Called from surfaceCreated()
  1041. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls)
  1042. {
  1043. SDL_LockMutex(Android_ActivityMutex);
  1044. if (Android_Window) {
  1045. SDL_WindowData *data = Android_Window->internal;
  1046. data->native_window = Android_JNI_GetNativeWindow();
  1047. SDL_SetPointerProperty(SDL_GetWindowProperties(Android_Window), SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, data->native_window);
  1048. if (data->native_window == NULL) {
  1049. SDL_SetError("Could not fetch native window from UI thread");
  1050. }
  1051. }
  1052. SDL_UnlockMutex(Android_ActivityMutex);
  1053. }
  1054. // Called from surfaceChanged()
  1055. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
  1056. {
  1057. SDL_LockMutex(Android_ActivityMutex);
  1058. #ifdef SDL_VIDEO_OPENGL_EGL
  1059. if (Android_Window && (Android_Window->flags & SDL_WINDOW_OPENGL)) {
  1060. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  1061. SDL_WindowData *data = Android_Window->internal;
  1062. // If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here
  1063. if (data->egl_surface == EGL_NO_SURFACE) {
  1064. data->egl_surface = SDL_EGL_CreateSurface(_this, Android_Window, (NativeWindowType)data->native_window);
  1065. SDL_SetPointerProperty(SDL_GetWindowProperties(Android_Window), SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER, data->egl_surface);
  1066. }
  1067. // GL Context handling is done in the event loop because this function is run from the Java thread
  1068. }
  1069. #endif
  1070. if (Android_Window) {
  1071. Android_RestoreScreenKeyboard(SDL_GetVideoDevice(), Android_Window);
  1072. }
  1073. SDL_UnlockMutex(Android_ActivityMutex);
  1074. }
  1075. // Called from surfaceDestroyed()
  1076. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
  1077. {
  1078. int nb_attempt = 50;
  1079. retry:
  1080. SDL_LockMutex(Android_ActivityMutex);
  1081. if (Android_Window) {
  1082. SDL_WindowData *data = Android_Window->internal;
  1083. // Wait for Main thread being paused and context un-activated to release 'egl_surface'
  1084. if ((Android_Window->flags & SDL_WINDOW_OPENGL) && !data->backup_done) {
  1085. nb_attempt -= 1;
  1086. if (nb_attempt == 0) {
  1087. SDL_SetError("Try to release egl_surface with context probably still active");
  1088. } else {
  1089. SDL_UnlockMutex(Android_ActivityMutex);
  1090. SDL_Delay(10);
  1091. goto retry;
  1092. }
  1093. }
  1094. #ifdef SDL_VIDEO_OPENGL_EGL
  1095. if (data->egl_surface != EGL_NO_SURFACE) {
  1096. SDL_EGL_DestroySurface(SDL_GetVideoDevice(), data->egl_surface);
  1097. data->egl_surface = EGL_NO_SURFACE;
  1098. }
  1099. #endif
  1100. if (data->native_window) {
  1101. ANativeWindow_release(data->native_window);
  1102. data->native_window = NULL;
  1103. }
  1104. // GL Context handling is done in the event loop because this function is run from the Java thread
  1105. }
  1106. SDL_UnlockMutex(Android_ActivityMutex);
  1107. }
  1108. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeScreenKeyboardShown)(JNIEnv *env, jclass jcls)
  1109. {
  1110. SDL_SendScreenKeyboardShown();
  1111. }
  1112. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeScreenKeyboardHidden)(JNIEnv *env, jclass jcls)
  1113. {
  1114. SDL_SendScreenKeyboardHidden();
  1115. }
  1116. // Keydown
  1117. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
  1118. JNIEnv *env, jclass jcls,
  1119. jint keycode)
  1120. {
  1121. SDL_LockMutex(Android_ActivityMutex);
  1122. if (Android_Window) {
  1123. Android_OnKeyDown(keycode);
  1124. }
  1125. SDL_UnlockMutex(Android_ActivityMutex);
  1126. }
  1127. // Keyup
  1128. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
  1129. JNIEnv *env, jclass jcls,
  1130. jint keycode)
  1131. {
  1132. SDL_LockMutex(Android_ActivityMutex);
  1133. if (Android_Window) {
  1134. Android_OnKeyUp(keycode);
  1135. }
  1136. SDL_UnlockMutex(Android_ActivityMutex);
  1137. }
  1138. // Virtual keyboard return key might stop text input
  1139. JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)(
  1140. JNIEnv *env, jclass jcls)
  1141. {
  1142. if (SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, false)) {
  1143. SDL_StopTextInput(Android_Window);
  1144. return JNI_TRUE;
  1145. }
  1146. return JNI_FALSE;
  1147. }
  1148. // Keyboard Focus Lost
  1149. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
  1150. JNIEnv *env, jclass jcls)
  1151. {
  1152. // Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget
  1153. SDL_StopTextInput(Android_Window);
  1154. }
  1155. // Touch
  1156. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
  1157. JNIEnv *env, jclass jcls,
  1158. jint touch_device_id_in, jint pointer_finger_id_in,
  1159. jint action, jfloat x, jfloat y, jfloat p)
  1160. {
  1161. SDL_LockMutex(Android_ActivityMutex);
  1162. Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
  1163. SDL_UnlockMutex(Android_ActivityMutex);
  1164. }
  1165. // Pinch
  1166. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchStart)(
  1167. JNIEnv *env, jclass jcls)
  1168. {
  1169. SDL_LockMutex(Android_ActivityMutex);
  1170. if (Android_Window) {
  1171. SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, 0, Android_Window, 0);
  1172. }
  1173. SDL_UnlockMutex(Android_ActivityMutex);
  1174. }
  1175. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchUpdate)(
  1176. JNIEnv *env, jclass jcls, jfloat scale)
  1177. {
  1178. SDL_LockMutex(Android_ActivityMutex);
  1179. if (Android_Window) {
  1180. SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, 0, Android_Window, scale);
  1181. }
  1182. SDL_UnlockMutex(Android_ActivityMutex);
  1183. }
  1184. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchEnd)(
  1185. JNIEnv *env, jclass jcls)
  1186. {
  1187. SDL_LockMutex(Android_ActivityMutex);
  1188. if (Android_Window) {
  1189. SDL_SendPinch(SDL_EVENT_PINCH_END, 0, Android_Window, 0);
  1190. }
  1191. SDL_UnlockMutex(Android_ActivityMutex);
  1192. }
  1193. // Mouse
  1194. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
  1195. JNIEnv *env, jclass jcls,
  1196. jint button, jint action, jfloat x, jfloat y, jboolean relative)
  1197. {
  1198. SDL_LockMutex(Android_ActivityMutex);
  1199. Android_OnMouse(Android_Window, button, action, x, y, relative);
  1200. SDL_UnlockMutex(Android_ActivityMutex);
  1201. }
  1202. // Pen
  1203. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePen)(
  1204. JNIEnv *env, jclass jcls,
  1205. jint pen_id_in, jint device_type, jint button, jint action, jfloat x, jfloat y, jfloat p)
  1206. {
  1207. SDL_LockMutex(Android_ActivityMutex);
  1208. Android_OnPen(Android_Window, pen_id_in, device_type, button, action, x, y, p);
  1209. SDL_UnlockMutex(Android_ActivityMutex);
  1210. }
  1211. // Accelerometer
  1212. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
  1213. JNIEnv *env, jclass jcls,
  1214. jfloat x, jfloat y, jfloat z)
  1215. {
  1216. fLastAccelerometer[0] = x;
  1217. fLastAccelerometer[1] = y;
  1218. fLastAccelerometer[2] = z;
  1219. bHasNewData = true;
  1220. }
  1221. // Clipboard
  1222. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
  1223. JNIEnv *env, jclass jcls)
  1224. {
  1225. // TODO: compute new mime types
  1226. SDL_SendClipboardUpdate(false, NULL, 0);
  1227. }
  1228. // Low memory
  1229. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
  1230. JNIEnv *env, jclass cls)
  1231. {
  1232. Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_LOWMEMORY);
  1233. }
  1234. /* Locale
  1235. * requires android:configChanges="layoutDirection|locale" in AndroidManifest.xml */
  1236. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeLocaleChanged)(
  1237. JNIEnv *env, jclass cls)
  1238. {
  1239. SDL_SendAppEvent(SDL_EVENT_LOCALE_CHANGED);
  1240. }
  1241. // Dark mode
  1242. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDarkModeChanged)(
  1243. JNIEnv *env, jclass cls, jboolean enabled)
  1244. {
  1245. Android_SetDarkMode(enabled);
  1246. }
  1247. // Send Quit event to "SDLThread" thread
  1248. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
  1249. JNIEnv *env, jclass cls)
  1250. {
  1251. Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_DESTROY);
  1252. }
  1253. // Activity ends
  1254. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
  1255. JNIEnv *env, jclass cls)
  1256. {
  1257. const char *str;
  1258. if (Android_ActivityMutex) {
  1259. SDL_DestroyMutex(Android_ActivityMutex);
  1260. Android_ActivityMutex = NULL;
  1261. }
  1262. if (Android_LifecycleMutex) {
  1263. SDL_DestroyMutex(Android_LifecycleMutex);
  1264. Android_LifecycleMutex = NULL;
  1265. }
  1266. if (Android_LifecycleEventSem) {
  1267. SDL_DestroySemaphore(Android_LifecycleEventSem);
  1268. Android_LifecycleEventSem = NULL;
  1269. }
  1270. Android_NumLifecycleEvents = 0;
  1271. Internal_Android_Destroy_AssetManager();
  1272. str = SDL_GetError();
  1273. if (str && str[0]) {
  1274. __android_log_print(ANDROID_LOG_ERROR, "SDL", "SDLActivity thread ends (error=%s)", str);
  1275. } else {
  1276. __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDLActivity thread ends");
  1277. }
  1278. }
  1279. // Pause
  1280. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
  1281. JNIEnv *env, jclass cls)
  1282. {
  1283. __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
  1284. Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_PAUSE);
  1285. }
  1286. // Resume
  1287. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
  1288. JNIEnv *env, jclass cls)
  1289. {
  1290. __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
  1291. Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_RESUME);
  1292. }
  1293. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
  1294. JNIEnv *env, jclass cls, jboolean hasFocus)
  1295. {
  1296. SDL_LockMutex(Android_ActivityMutex);
  1297. if (Android_Window) {
  1298. __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged()");
  1299. SDL_SendWindowEvent(Android_Window, (hasFocus ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST), 0, 0);
  1300. }
  1301. SDL_UnlockMutex(Android_ActivityMutex);
  1302. }
  1303. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
  1304. JNIEnv *env, jclass cls,
  1305. jstring text, jint newCursorPosition)
  1306. {
  1307. const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
  1308. SDL_SendKeyboardText(utftext);
  1309. (*env)->ReleaseStringUTFChars(env, text, utftext);
  1310. }
  1311. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
  1312. JNIEnv *env, jclass cls,
  1313. jchar chUnicode)
  1314. {
  1315. SDL_SendKeyboardUnicodeKey(0, chUnicode);
  1316. }
  1317. JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
  1318. JNIEnv *env, jclass cls,
  1319. jstring name)
  1320. {
  1321. const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
  1322. const char *hint = SDL_GetHint(utfname);
  1323. jstring result = (*env)->NewStringUTF(env, hint);
  1324. (*env)->ReleaseStringUTFChars(env, name, utfname);
  1325. return result;
  1326. }
  1327. JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeGetHintBoolean)(
  1328. JNIEnv *env, jclass cls,
  1329. jstring name, jboolean default_value)
  1330. {
  1331. jboolean result;
  1332. const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
  1333. result = SDL_GetHintBoolean(utfname, default_value);
  1334. (*env)->ReleaseStringUTFChars(env, name, utfname);
  1335. return result;
  1336. }
  1337. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
  1338. JNIEnv *env, jclass cls,
  1339. jstring name, jstring value)
  1340. {
  1341. const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
  1342. const char *utfvalue = (*env)->GetStringUTFChars(env, value, NULL);
  1343. // This is only called at startup, to initialize the environment
  1344. // Note that we call setenv() directly to avoid affecting SDL environments
  1345. setenv(utfname, utfvalue, 1); // This should NOT be SDL_setenv()
  1346. if (SDL_strcmp(utfname, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
  1347. // Special handling for this hint, which needs to persist outside the normal application flow
  1348. // Only set this the first time we run, in case it's been set by the application via SDL_SetHint()
  1349. if (!allow_recreate_activity_set) {
  1350. Android_SetAllowRecreateActivity(SDL_GetStringBoolean(utfvalue, false));
  1351. }
  1352. }
  1353. (*env)->ReleaseStringUTFChars(env, name, utfname);
  1354. (*env)->ReleaseStringUTFChars(env, value, utfvalue);
  1355. }
  1356. /*******************************************************************************
  1357. Functions called by SDL into Java
  1358. *******************************************************************************/
  1359. static SDL_AtomicInt s_active;
  1360. struct LocalReferenceHolder
  1361. {
  1362. JNIEnv *m_env;
  1363. const char *m_func;
  1364. };
  1365. static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func)
  1366. {
  1367. struct LocalReferenceHolder refholder;
  1368. refholder.m_env = NULL;
  1369. refholder.m_func = func;
  1370. #ifdef DEBUG_JNI
  1371. SDL_Log("Entering function %s", func);
  1372. #endif
  1373. return refholder;
  1374. }
  1375. static bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env)
  1376. {
  1377. const int capacity = 16;
  1378. if ((*env)->PushLocalFrame(env, capacity) < 0) {
  1379. SDL_SetError("Failed to allocate enough JVM local references");
  1380. return false;
  1381. }
  1382. SDL_AtomicIncRef(&s_active);
  1383. refholder->m_env = env;
  1384. return true;
  1385. }
  1386. static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder)
  1387. {
  1388. #ifdef DEBUG_JNI
  1389. SDL_Log("Leaving function %s", refholder->m_func);
  1390. #endif
  1391. if (refholder->m_env) {
  1392. JNIEnv *env = refholder->m_env;
  1393. (*env)->PopLocalFrame(env, NULL);
  1394. SDL_AtomicDecRef(&s_active);
  1395. }
  1396. }
  1397. ANativeWindow *Android_JNI_GetNativeWindow(void)
  1398. {
  1399. ANativeWindow *anw = NULL;
  1400. jobject s;
  1401. JNIEnv *env = Android_JNI_GetEnv();
  1402. s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
  1403. if (s) {
  1404. anw = ANativeWindow_fromSurface(env, s);
  1405. (*env)->DeleteLocalRef(env, s);
  1406. }
  1407. return anw;
  1408. }
  1409. void Android_JNI_SetActivityTitle(const char *title)
  1410. {
  1411. JNIEnv *env = Android_JNI_GetEnv();
  1412. jstring jtitle = (*env)->NewStringUTF(env, title);
  1413. (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetActivityTitle, jtitle);
  1414. (*env)->DeleteLocalRef(env, jtitle);
  1415. }
  1416. void Android_JNI_SetWindowStyle(bool fullscreen)
  1417. {
  1418. JNIEnv *env = Android_JNI_GetEnv();
  1419. (*env)->CallStaticVoidMethod(env, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0);
  1420. }
  1421. void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint)
  1422. {
  1423. JNIEnv *env = Android_JNI_GetEnv();
  1424. jstring jhint = (*env)->NewStringUTF(env, (hint ? hint : ""));
  1425. (*env)->CallStaticVoidMethod(env, mActivityClass, midSetOrientation, w, h, (resizable ? 1 : 0), jhint);
  1426. (*env)->DeleteLocalRef(env, jhint);
  1427. }
  1428. SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void)
  1429. {
  1430. return displayNaturalOrientation;
  1431. }
  1432. SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void)
  1433. {
  1434. return displayCurrentOrientation;
  1435. }
  1436. void Android_JNI_MinimizeWindow(void)
  1437. {
  1438. JNIEnv *env = Android_JNI_GetEnv();
  1439. (*env)->CallStaticVoidMethod(env, mActivityClass, midMinimizeWindow);
  1440. }
  1441. bool Android_JNI_ShouldMinimizeOnFocusLoss(void)
  1442. {
  1443. JNIEnv *env = Android_JNI_GetEnv();
  1444. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midShouldMinimizeOnFocusLoss);
  1445. }
  1446. bool Android_JNI_GetAccelerometerValues(float values[3])
  1447. {
  1448. bool result = false;
  1449. if (bHasNewData) {
  1450. int i;
  1451. for (i = 0; i < 3; ++i) {
  1452. values[i] = fLastAccelerometer[i];
  1453. }
  1454. bHasNewData = false;
  1455. result = true;
  1456. }
  1457. return result;
  1458. }
  1459. /*
  1460. * Audio support
  1461. */
  1462. void Android_StartAudioHotplug(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording)
  1463. {
  1464. JNIEnv *env = Android_JNI_GetEnv();
  1465. // this will fire the callback for each existing device right away (which will eventually SDL_AddAudioDevice), and again later when things change.
  1466. (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midRegisterAudioDeviceCallback);
  1467. *default_playback = *default_recording = NULL; // !!! FIXME: how do you decide the default device id?
  1468. }
  1469. void Android_StopAudioHotplug(void)
  1470. {
  1471. JNIEnv *env = Android_JNI_GetEnv();
  1472. (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback);
  1473. }
  1474. static void Android_JNI_AudioSetThreadPriority(int recording, int device_id)
  1475. {
  1476. JNIEnv *env = Android_JNI_GetEnv();
  1477. (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, recording, device_id);
  1478. }
  1479. void Android_AudioThreadInit(SDL_AudioDevice *device)
  1480. {
  1481. Android_JNI_AudioSetThreadPriority((int) device->recording, (int)device->instance_id);
  1482. }
  1483. // Test for an exception and call SDL_SetError with its detail if one occurs
  1484. // If the parameter silent is truthy then SDL_SetError() will not be called.
  1485. static bool Android_JNI_ExceptionOccurred(bool silent)
  1486. {
  1487. JNIEnv *env = Android_JNI_GetEnv();
  1488. jthrowable exception;
  1489. // Detect mismatch LocalReferenceHolder_Init/Cleanup
  1490. SDL_assert(SDL_GetAtomicInt(&s_active) > 0);
  1491. exception = (*env)->ExceptionOccurred(env);
  1492. if (exception != NULL) {
  1493. jmethodID mid;
  1494. // Until this happens most JNI operations have undefined behaviour
  1495. (*env)->ExceptionClear(env);
  1496. if (!silent) {
  1497. jclass exceptionClass = (*env)->GetObjectClass(env, exception);
  1498. jclass classClass = (*env)->FindClass(env, "java/lang/Class");
  1499. jstring exceptionName;
  1500. const char *exceptionNameUTF8;
  1501. jstring exceptionMessage;
  1502. mid = (*env)->GetMethodID(env, classClass, "getName", "()Ljava/lang/String;");
  1503. exceptionName = (jstring)(*env)->CallObjectMethod(env, exceptionClass, mid);
  1504. exceptionNameUTF8 = (*env)->GetStringUTFChars(env, exceptionName, 0);
  1505. (*env)->DeleteLocalRef(env, classClass);
  1506. mid = (*env)->GetMethodID(env, exceptionClass, "getMessage", "()Ljava/lang/String;");
  1507. exceptionMessage = (jstring)(*env)->CallObjectMethod(env, exception, mid);
  1508. (*env)->DeleteLocalRef(env, exceptionClass);
  1509. if (exceptionMessage != NULL) {
  1510. const char *exceptionMessageUTF8 = (*env)->GetStringUTFChars(env, exceptionMessage, 0);
  1511. SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
  1512. (*env)->ReleaseStringUTFChars(env, exceptionMessage, exceptionMessageUTF8);
  1513. (*env)->DeleteLocalRef(env, exceptionMessage);
  1514. } else {
  1515. SDL_SetError("%s", exceptionNameUTF8);
  1516. }
  1517. (*env)->ReleaseStringUTFChars(env, exceptionName, exceptionNameUTF8);
  1518. (*env)->DeleteLocalRef(env, exceptionName);
  1519. }
  1520. return true;
  1521. }
  1522. return false;
  1523. }
  1524. static void Internal_Android_Create_AssetManager(void)
  1525. {
  1526. struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1527. JNIEnv *env = Android_JNI_GetEnv();
  1528. jmethodID mid;
  1529. jobject context;
  1530. jobject javaAssetManager;
  1531. if (!LocalReferenceHolder_Init(&refs, env)) {
  1532. LocalReferenceHolder_Cleanup(&refs);
  1533. return;
  1534. }
  1535. // context = SDLActivity.getContext();
  1536. context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  1537. // javaAssetManager = context.getAssets();
  1538. mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
  1539. "getAssets", "()Landroid/content/res/AssetManager;");
  1540. javaAssetManager = (*env)->CallObjectMethod(env, context, mid);
  1541. /**
  1542. * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
  1543. * object. Note that the caller is responsible for obtaining and holding a VM reference
  1544. * to the jobject to prevent its being garbage collected while the native object is
  1545. * in use.
  1546. */
  1547. javaAssetManagerRef = (*env)->NewGlobalRef(env, javaAssetManager);
  1548. asset_manager = AAssetManager_fromJava(env, javaAssetManagerRef);
  1549. if (!asset_manager) {
  1550. (*env)->DeleteGlobalRef(env, javaAssetManagerRef);
  1551. Android_JNI_ExceptionOccurred(true);
  1552. }
  1553. LocalReferenceHolder_Cleanup(&refs);
  1554. }
  1555. static void Internal_Android_Destroy_AssetManager(void)
  1556. {
  1557. JNIEnv *env = Android_JNI_GetEnv();
  1558. if (asset_manager) {
  1559. (*env)->DeleteGlobalRef(env, javaAssetManagerRef);
  1560. asset_manager = NULL;
  1561. }
  1562. }
  1563. static const char *GetAssetPath(const char *path)
  1564. {
  1565. if (path && path[0] == '.' && path[1] == '/') {
  1566. path += 2;
  1567. while (*path == '/') {
  1568. ++path;
  1569. }
  1570. }
  1571. return path;
  1572. }
  1573. bool Android_JNI_FileOpen(void **puserdata, const char *fileName, const char *mode)
  1574. {
  1575. SDL_assert(puserdata != NULL);
  1576. AAsset *asset = NULL;
  1577. *puserdata = NULL;
  1578. if (!asset_manager) {
  1579. Internal_Android_Create_AssetManager();
  1580. if (!asset_manager) {
  1581. return SDL_SetError("Couldn't create asset manager");
  1582. }
  1583. }
  1584. fileName = GetAssetPath(fileName);
  1585. asset = AAssetManager_open(asset_manager, fileName, AASSET_MODE_UNKNOWN);
  1586. if (!asset) {
  1587. return SDL_SetError("Couldn't open asset '%s'", fileName);
  1588. }
  1589. *puserdata = (void *)asset;
  1590. return true;
  1591. }
  1592. size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOStatus *status)
  1593. {
  1594. const int bytes = AAsset_read((AAsset *)userdata, buffer, size);
  1595. if (bytes < 0) {
  1596. SDL_SetError("AAsset_read() failed");
  1597. *status = SDL_IO_STATUS_ERROR;
  1598. return 0;
  1599. } else if (bytes < size) {
  1600. *status = SDL_IO_STATUS_EOF;
  1601. }
  1602. return (size_t)bytes;
  1603. }
  1604. size_t Android_JNI_FileWrite(void *userdata, const void *buffer, size_t size, SDL_IOStatus *status)
  1605. {
  1606. SDL_SetError("Cannot write to Android package filesystem");
  1607. *status = SDL_IO_STATUS_ERROR;
  1608. return 0;
  1609. }
  1610. Sint64 Android_JNI_FileSize(void *userdata)
  1611. {
  1612. return (Sint64) AAsset_getLength64((AAsset *)userdata);
  1613. }
  1614. Sint64 Android_JNI_FileSeek(void *userdata, Sint64 offset, SDL_IOWhence whence)
  1615. {
  1616. return (Sint64) AAsset_seek64((AAsset *)userdata, offset, (int)whence);
  1617. }
  1618. bool Android_JNI_FileClose(void *userdata)
  1619. {
  1620. AAsset_close((AAsset *)userdata);
  1621. return true;
  1622. }
  1623. bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata)
  1624. {
  1625. SDL_assert(path != NULL);
  1626. if (!asset_manager) {
  1627. Internal_Android_Create_AssetManager();
  1628. if (!asset_manager) {
  1629. return SDL_SetError("Couldn't create asset manager");
  1630. }
  1631. }
  1632. path = GetAssetPath(path);
  1633. AAssetDir *adir = AAssetManager_openDir(asset_manager, path);
  1634. if (!adir) {
  1635. return SDL_SetError("AAssetManager_openDir failed");
  1636. }
  1637. SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
  1638. const char *ent;
  1639. while ((result == SDL_ENUM_CONTINUE) && ((ent = AAssetDir_getNextFileName(adir)) != NULL)) {
  1640. result = cb(userdata, path, ent);
  1641. }
  1642. AAssetDir_close(adir);
  1643. return (result != SDL_ENUM_FAILURE);
  1644. }
  1645. bool Android_JNI_GetAssetPathInfo(const char *path, SDL_PathInfo *info)
  1646. {
  1647. if (!asset_manager) {
  1648. Internal_Android_Create_AssetManager();
  1649. if (!asset_manager) {
  1650. return SDL_SetError("Couldn't create asset manager");
  1651. }
  1652. }
  1653. path = GetAssetPath(path);
  1654. // this is sort of messy, but there isn't a stat()-like interface to the Assets.
  1655. AAsset *aasset = AAssetManager_open(asset_manager, path, AASSET_MODE_UNKNOWN);
  1656. if (aasset) { // it's a file!
  1657. info->type = SDL_PATHTYPE_FILE;
  1658. info->size = (Uint64) AAsset_getLength64(aasset);
  1659. AAsset_close(aasset);
  1660. return true;
  1661. }
  1662. AAssetDir *adir = AAssetManager_openDir(asset_manager, path);
  1663. if (adir) { // This does _not_ return NULL for a missing directory! Treat empty directories as missing. Better than nothing. :/
  1664. const bool contains_something = (AAssetDir_getNextFileName(adir) != NULL); // if not NULL, there are files in this directory, so it's _definitely_ a directory.
  1665. AAssetDir_close(adir);
  1666. if (contains_something) {
  1667. info->type = SDL_PATHTYPE_DIRECTORY;
  1668. info->size = 0;
  1669. return true;
  1670. }
  1671. }
  1672. return SDL_SetError("Couldn't open asset '%s'", path);
  1673. }
  1674. bool Android_JNI_SetClipboardText(const char *text)
  1675. {
  1676. JNIEnv *env = Android_JNI_GetEnv();
  1677. jstring string = (*env)->NewStringUTF(env, text);
  1678. (*env)->CallStaticVoidMethod(env, mActivityClass, midClipboardSetText, string);
  1679. (*env)->DeleteLocalRef(env, string);
  1680. return true;
  1681. }
  1682. char *Android_JNI_GetClipboardText(void)
  1683. {
  1684. JNIEnv *env = Android_JNI_GetEnv();
  1685. char *text = NULL;
  1686. jstring string;
  1687. string = (*env)->CallStaticObjectMethod(env, mActivityClass, midClipboardGetText);
  1688. if (string) {
  1689. const char *utf = (*env)->GetStringUTFChars(env, string, 0);
  1690. if (utf) {
  1691. text = SDL_strdup(utf);
  1692. (*env)->ReleaseStringUTFChars(env, string, utf);
  1693. }
  1694. (*env)->DeleteLocalRef(env, string);
  1695. }
  1696. return (!text) ? SDL_strdup("") : text;
  1697. }
  1698. bool Android_JNI_HasClipboardText(void)
  1699. {
  1700. JNIEnv *env = Android_JNI_GetEnv();
  1701. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midClipboardHasText);
  1702. }
  1703. /* returns 0 on success or -1 on error (others undefined then)
  1704. * returns truthy or falsy value in plugged, charged and battery
  1705. * returns the value in seconds and percent or -1 if not available
  1706. */
  1707. int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent)
  1708. {
  1709. struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1710. JNIEnv *env = Android_JNI_GetEnv();
  1711. jmethodID mid;
  1712. jobject context;
  1713. jstring action;
  1714. jclass cls;
  1715. jobject filter;
  1716. jobject intent;
  1717. jstring iname;
  1718. jmethodID imid;
  1719. jstring bname;
  1720. jmethodID bmid;
  1721. if (!LocalReferenceHolder_Init(&refs, env)) {
  1722. LocalReferenceHolder_Cleanup(&refs);
  1723. return -1;
  1724. }
  1725. // context = SDLActivity.getContext();
  1726. context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  1727. action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
  1728. cls = (*env)->FindClass(env, "android/content/IntentFilter");
  1729. mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
  1730. filter = (*env)->NewObject(env, cls, mid, action);
  1731. (*env)->DeleteLocalRef(env, action);
  1732. mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
  1733. intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
  1734. (*env)->DeleteLocalRef(env, filter);
  1735. cls = (*env)->GetObjectClass(env, intent);
  1736. imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
  1737. // Watch out for C89 scoping rules because of the macro
  1738. #define GET_INT_EXTRA(var, key) \
  1739. int var; \
  1740. iname = (*env)->NewStringUTF(env, key); \
  1741. (var) = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
  1742. (*env)->DeleteLocalRef(env, iname);
  1743. bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
  1744. // Watch out for C89 scoping rules because of the macro
  1745. #define GET_BOOL_EXTRA(var, key) \
  1746. int var; \
  1747. bname = (*env)->NewStringUTF(env, key); \
  1748. (var) = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
  1749. (*env)->DeleteLocalRef(env, bname);
  1750. if (plugged) {
  1751. // Watch out for C89 scoping rules because of the macro
  1752. GET_INT_EXTRA(plug, "plugged") // == BatteryManager.EXTRA_PLUGGED (API 5)
  1753. if (plug == -1) {
  1754. LocalReferenceHolder_Cleanup(&refs);
  1755. return -1;
  1756. }
  1757. // 1 == BatteryManager.BATTERY_PLUGGED_AC
  1758. // 2 == BatteryManager.BATTERY_PLUGGED_USB
  1759. *plugged = (0 < plug) ? 1 : 0;
  1760. }
  1761. if (charged) {
  1762. // Watch out for C89 scoping rules because of the macro
  1763. GET_INT_EXTRA(status, "status") // == BatteryManager.EXTRA_STATUS (API 5)
  1764. if (status == -1) {
  1765. LocalReferenceHolder_Cleanup(&refs);
  1766. return -1;
  1767. }
  1768. // 5 == BatteryManager.BATTERY_STATUS_FULL
  1769. *charged = (status == 5) ? 1 : 0;
  1770. }
  1771. if (battery) {
  1772. GET_BOOL_EXTRA(present, "present") // == BatteryManager.EXTRA_PRESENT (API 5)
  1773. *battery = present ? 1 : 0;
  1774. }
  1775. if (seconds) {
  1776. *seconds = -1; // not possible
  1777. }
  1778. if (percent) {
  1779. int level;
  1780. int scale;
  1781. // Watch out for C89 scoping rules because of the macro
  1782. {
  1783. GET_INT_EXTRA(level_temp, "level") // == BatteryManager.EXTRA_LEVEL (API 5)
  1784. level = level_temp;
  1785. }
  1786. // Watch out for C89 scoping rules because of the macro
  1787. {
  1788. GET_INT_EXTRA(scale_temp, "scale") // == BatteryManager.EXTRA_SCALE (API 5)
  1789. scale = scale_temp;
  1790. }
  1791. if ((level == -1) || (scale == -1)) {
  1792. LocalReferenceHolder_Cleanup(&refs);
  1793. return -1;
  1794. }
  1795. *percent = level * 100 / scale;
  1796. }
  1797. (*env)->DeleteLocalRef(env, intent);
  1798. LocalReferenceHolder_Cleanup(&refs);
  1799. return 0;
  1800. }
  1801. // Add all touch devices
  1802. void Android_JNI_InitTouch(void)
  1803. {
  1804. JNIEnv *env = Android_JNI_GetEnv();
  1805. (*env)->CallStaticVoidMethod(env, mActivityClass, midInitTouch);
  1806. }
  1807. void Android_JNI_PollInputDevices(void)
  1808. {
  1809. JNIEnv *env = Android_JNI_GetEnv();
  1810. (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
  1811. }
  1812. void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue)
  1813. {
  1814. JNIEnv *env = Android_JNI_GetEnv();
  1815. (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midJoystickSetLED, device_id, red, green, blue);
  1816. }
  1817. void Android_JNI_PollHapticDevices(void)
  1818. {
  1819. JNIEnv *env = Android_JNI_GetEnv();
  1820. (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
  1821. }
  1822. void Android_JNI_HapticRun(int device_id, float intensity, int length)
  1823. {
  1824. JNIEnv *env = Android_JNI_GetEnv();
  1825. (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, intensity, length);
  1826. }
  1827. void Android_JNI_HapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length)
  1828. {
  1829. JNIEnv *env = Android_JNI_GetEnv();
  1830. (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRumble, device_id, low_frequency_intensity, high_frequency_intensity, length);
  1831. }
  1832. void Android_JNI_HapticStop(int device_id)
  1833. {
  1834. JNIEnv *env = Android_JNI_GetEnv();
  1835. (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticStop, device_id);
  1836. }
  1837. // See SDLActivity.java for constants.
  1838. #define COMMAND_SET_KEEP_SCREEN_ON 5
  1839. bool SDL_SendAndroidMessage(Uint32 command, int param)
  1840. {
  1841. CHECK_PARAM(command < 0x8000) {
  1842. return SDL_InvalidParamError("command");
  1843. }
  1844. return Android_JNI_SendMessage(command, param);
  1845. }
  1846. // sends message to be handled on the UI event dispatch thread
  1847. bool Android_JNI_SendMessage(int command, int param)
  1848. {
  1849. JNIEnv *env = Android_JNI_GetEnv();
  1850. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSendMessage, command, param);
  1851. }
  1852. bool Android_JNI_SuspendScreenSaver(bool suspend)
  1853. {
  1854. return Android_JNI_SendMessage(COMMAND_SET_KEEP_SCREEN_ON, (suspend == false) ? 0 : 1);
  1855. }
  1856. void Android_JNI_ShowScreenKeyboard(int input_type, SDL_Rect *inputRect)
  1857. {
  1858. JNIEnv *env = Android_JNI_GetEnv();
  1859. (*env)->CallStaticBooleanMethod(env, mActivityClass, midShowTextInput,
  1860. input_type,
  1861. inputRect->x,
  1862. inputRect->y,
  1863. inputRect->w,
  1864. inputRect->h);
  1865. }
  1866. void Android_JNI_HideScreenKeyboard(void)
  1867. {
  1868. // has to match Activity constant
  1869. const int COMMAND_TEXTEDIT_HIDE = 3;
  1870. Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
  1871. }
  1872. bool Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
  1873. {
  1874. JNIEnv *env;
  1875. jclass clazz;
  1876. jmethodID mid;
  1877. jobject context;
  1878. jstring title;
  1879. jstring message;
  1880. jintArray button_flags;
  1881. jintArray button_ids;
  1882. jobjectArray button_texts;
  1883. jintArray colors;
  1884. jobject text;
  1885. jint temp;
  1886. int i;
  1887. env = Android_JNI_GetEnv();
  1888. // convert parameters
  1889. clazz = (*env)->FindClass(env, "java/lang/String");
  1890. title = (*env)->NewStringUTF(env, messageboxdata->title);
  1891. message = (*env)->NewStringUTF(env, messageboxdata->message);
  1892. button_flags = (*env)->NewIntArray(env, messageboxdata->numbuttons);
  1893. button_ids = (*env)->NewIntArray(env, messageboxdata->numbuttons);
  1894. button_texts = (*env)->NewObjectArray(env, messageboxdata->numbuttons,
  1895. clazz, NULL);
  1896. for (i = 0; i < messageboxdata->numbuttons; ++i) {
  1897. const SDL_MessageBoxButtonData *sdlButton;
  1898. if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
  1899. sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i];
  1900. } else {
  1901. sdlButton = &messageboxdata->buttons[i];
  1902. }
  1903. temp = sdlButton->flags;
  1904. (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp);
  1905. temp = sdlButton->buttonID;
  1906. (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp);
  1907. text = (*env)->NewStringUTF(env, sdlButton->text);
  1908. (*env)->SetObjectArrayElement(env, button_texts, i, text);
  1909. (*env)->DeleteLocalRef(env, text);
  1910. }
  1911. if (messageboxdata->colorScheme) {
  1912. colors = (*env)->NewIntArray(env, SDL_MESSAGEBOX_COLOR_COUNT);
  1913. for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; ++i) {
  1914. temp = (0xFFU << 24) |
  1915. (messageboxdata->colorScheme->colors[i].r << 16) |
  1916. (messageboxdata->colorScheme->colors[i].g << 8) |
  1917. (messageboxdata->colorScheme->colors[i].b << 0);
  1918. (*env)->SetIntArrayRegion(env, colors, i, 1, &temp);
  1919. }
  1920. } else {
  1921. colors = NULL;
  1922. }
  1923. (*env)->DeleteLocalRef(env, clazz);
  1924. // context = SDLActivity.getContext();
  1925. context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  1926. clazz = (*env)->GetObjectClass(env, context);
  1927. mid = (*env)->GetMethodID(env, clazz,
  1928. "messageboxShowMessageBox", "(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I");
  1929. *buttonID = (*env)->CallIntMethod(env, context, mid,
  1930. (jint)messageboxdata->flags,
  1931. title,
  1932. message,
  1933. button_flags,
  1934. button_ids,
  1935. button_texts,
  1936. colors);
  1937. (*env)->DeleteLocalRef(env, context);
  1938. (*env)->DeleteLocalRef(env, clazz);
  1939. // delete parameters
  1940. (*env)->DeleteLocalRef(env, title);
  1941. (*env)->DeleteLocalRef(env, message);
  1942. (*env)->DeleteLocalRef(env, button_flags);
  1943. (*env)->DeleteLocalRef(env, button_ids);
  1944. (*env)->DeleteLocalRef(env, button_texts);
  1945. (*env)->DeleteLocalRef(env, colors);
  1946. return true;
  1947. }
  1948. /*
  1949. //////////////////////////////////////////////////////////////////////////////
  1950. //
  1951. // Functions exposed to SDL applications in SDL_system.h
  1952. //////////////////////////////////////////////////////////////////////////////
  1953. */
  1954. void *SDL_GetAndroidJNIEnv(void)
  1955. {
  1956. return Android_JNI_GetEnv();
  1957. }
  1958. void *SDL_GetAndroidActivity(void)
  1959. {
  1960. // See SDL_system.h for caveats on using this function.
  1961. JNIEnv *env = Android_JNI_GetEnv();
  1962. if (!env) {
  1963. return NULL;
  1964. }
  1965. // return SDLActivity.getContext();
  1966. return (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  1967. }
  1968. int SDL_GetAndroidSDKVersion(void)
  1969. {
  1970. static int sdk_version;
  1971. if (!sdk_version) {
  1972. char sdk[PROP_VALUE_MAX] = { 0 };
  1973. if (__system_property_get("ro.build.version.sdk", sdk) != 0) {
  1974. sdk_version = SDL_atoi(sdk);
  1975. }
  1976. }
  1977. return sdk_version;
  1978. }
  1979. bool SDL_IsAndroidTablet(void)
  1980. {
  1981. JNIEnv *env = Android_JNI_GetEnv();
  1982. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsTablet);
  1983. }
  1984. bool SDL_IsAndroidTV(void)
  1985. {
  1986. JNIEnv *env = Android_JNI_GetEnv();
  1987. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsAndroidTV);
  1988. }
  1989. bool SDL_IsChromebook(void)
  1990. {
  1991. JNIEnv *env = Android_JNI_GetEnv();
  1992. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsChromebook);
  1993. }
  1994. bool SDL_IsDeXMode(void)
  1995. {
  1996. JNIEnv *env = Android_JNI_GetEnv();
  1997. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsDeXMode);
  1998. }
  1999. void SDL_SendAndroidBackButton(void)
  2000. {
  2001. JNIEnv *env = Android_JNI_GetEnv();
  2002. (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton);
  2003. }
  2004. const char *SDL_GetAndroidInternalStoragePath(void)
  2005. {
  2006. static char *s_AndroidInternalFilesPath = NULL;
  2007. if (!s_AndroidInternalFilesPath) {
  2008. struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  2009. jmethodID mid;
  2010. jobject context;
  2011. jobject fileObject;
  2012. jstring pathString;
  2013. const char *path;
  2014. JNIEnv *env = Android_JNI_GetEnv();
  2015. if (!LocalReferenceHolder_Init(&refs, env)) {
  2016. LocalReferenceHolder_Cleanup(&refs);
  2017. return NULL;
  2018. }
  2019. // context = SDLActivity.getContext();
  2020. context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  2021. if (!context) {
  2022. SDL_SetError("Couldn't get Android context!");
  2023. LocalReferenceHolder_Cleanup(&refs);
  2024. return NULL;
  2025. }
  2026. // fileObj = context.getFilesDir();
  2027. mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
  2028. "getFilesDir", "()Ljava/io/File;");
  2029. fileObject = (*env)->CallObjectMethod(env, context, mid);
  2030. if (!fileObject) {
  2031. SDL_SetError("Couldn't get internal directory");
  2032. LocalReferenceHolder_Cleanup(&refs);
  2033. return NULL;
  2034. }
  2035. // path = fileObject.getCanonicalPath();
  2036. mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
  2037. "getCanonicalPath", "()Ljava/lang/String;");
  2038. pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
  2039. if (Android_JNI_ExceptionOccurred(false)) {
  2040. LocalReferenceHolder_Cleanup(&refs);
  2041. return NULL;
  2042. }
  2043. path = (*env)->GetStringUTFChars(env, pathString, NULL);
  2044. s_AndroidInternalFilesPath = SDL_strdup(path);
  2045. (*env)->ReleaseStringUTFChars(env, pathString, path);
  2046. LocalReferenceHolder_Cleanup(&refs);
  2047. }
  2048. return s_AndroidInternalFilesPath;
  2049. }
  2050. Uint32 SDL_GetAndroidExternalStorageState(void)
  2051. {
  2052. struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  2053. jmethodID mid;
  2054. jclass cls;
  2055. jstring stateString;
  2056. const char *state_string;
  2057. Uint32 stateFlags;
  2058. JNIEnv *env = Android_JNI_GetEnv();
  2059. if (!LocalReferenceHolder_Init(&refs, env)) {
  2060. LocalReferenceHolder_Cleanup(&refs);
  2061. return 0;
  2062. }
  2063. cls = (*env)->FindClass(env, "android/os/Environment");
  2064. mid = (*env)->GetStaticMethodID(env, cls,
  2065. "getExternalStorageState", "()Ljava/lang/String;");
  2066. stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
  2067. state_string = (*env)->GetStringUTFChars(env, stateString, NULL);
  2068. // Print an info message so people debugging know the storage state
  2069. __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state_string);
  2070. if (SDL_strcmp(state_string, "mounted") == 0) {
  2071. stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
  2072. SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
  2073. } else if (SDL_strcmp(state_string, "mounted_ro") == 0) {
  2074. stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
  2075. } else {
  2076. stateFlags = 0;
  2077. }
  2078. (*env)->ReleaseStringUTFChars(env, stateString, state_string);
  2079. LocalReferenceHolder_Cleanup(&refs);
  2080. return stateFlags;
  2081. }
  2082. const char *SDL_GetAndroidExternalStoragePath(void)
  2083. {
  2084. static char *s_AndroidExternalFilesPath = NULL;
  2085. if (!s_AndroidExternalFilesPath) {
  2086. struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  2087. jmethodID mid;
  2088. jobject context;
  2089. jobject fileObject;
  2090. jstring pathString;
  2091. const char *path;
  2092. JNIEnv *env = Android_JNI_GetEnv();
  2093. if (!LocalReferenceHolder_Init(&refs, env)) {
  2094. LocalReferenceHolder_Cleanup(&refs);
  2095. return NULL;
  2096. }
  2097. // context = SDLActivity.getContext();
  2098. context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  2099. // fileObj = context.getExternalFilesDir();
  2100. mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
  2101. "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
  2102. fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
  2103. if (!fileObject) {
  2104. SDL_SetError("Couldn't get external directory");
  2105. LocalReferenceHolder_Cleanup(&refs);
  2106. return NULL;
  2107. }
  2108. // path = fileObject.getAbsolutePath();
  2109. mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
  2110. "getAbsolutePath", "()Ljava/lang/String;");
  2111. pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
  2112. path = (*env)->GetStringUTFChars(env, pathString, NULL);
  2113. s_AndroidExternalFilesPath = SDL_strdup(path);
  2114. (*env)->ReleaseStringUTFChars(env, pathString, path);
  2115. LocalReferenceHolder_Cleanup(&refs);
  2116. }
  2117. return s_AndroidExternalFilesPath;
  2118. }
  2119. const char *SDL_GetAndroidCachePath(void)
  2120. {
  2121. // !!! FIXME: lots of duplication with SDL_GetAndroidExternalStoragePath and SDL_GetAndroidInternalStoragePath; consolidate these functions!
  2122. static char *s_AndroidCachePath = NULL;
  2123. if (!s_AndroidCachePath) {
  2124. struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  2125. jmethodID mid;
  2126. jobject context;
  2127. jobject fileObject;
  2128. jstring pathString;
  2129. const char *path;
  2130. JNIEnv *env = Android_JNI_GetEnv();
  2131. if (!LocalReferenceHolder_Init(&refs, env)) {
  2132. LocalReferenceHolder_Cleanup(&refs);
  2133. return NULL;
  2134. }
  2135. // context = SDLActivity.getContext();
  2136. context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  2137. // fileObj = context.getExternalFilesDir();
  2138. mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
  2139. "getCacheDir", "()Ljava/io/File;");
  2140. fileObject = (*env)->CallObjectMethod(env, context, mid);
  2141. if (!fileObject) {
  2142. SDL_SetError("Couldn't get cache directory");
  2143. LocalReferenceHolder_Cleanup(&refs);
  2144. return NULL;
  2145. }
  2146. // path = fileObject.getAbsolutePath();
  2147. mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
  2148. "getAbsolutePath", "()Ljava/lang/String;");
  2149. pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
  2150. path = (*env)->GetStringUTFChars(env, pathString, NULL);
  2151. s_AndroidCachePath = SDL_strdup(path);
  2152. (*env)->ReleaseStringUTFChars(env, pathString, path);
  2153. LocalReferenceHolder_Cleanup(&refs);
  2154. }
  2155. return s_AndroidCachePath;
  2156. }
  2157. bool SDL_ShowAndroidToast(const char *message, int duration, int gravity, int xOffset, int yOffset)
  2158. {
  2159. return Android_JNI_ShowToast(message, duration, gravity, xOffset, yOffset);
  2160. }
  2161. void Android_JNI_GetManifestEnvironmentVariables(void)
  2162. {
  2163. if (!mActivityClass || !midGetManifestEnvironmentVariables) {
  2164. __android_log_print(ANDROID_LOG_WARN, "SDL", "Request to get environment variables before JNI is ready");
  2165. return;
  2166. }
  2167. if (!bHasEnvironmentVariables) {
  2168. JNIEnv *env = Android_JNI_GetEnv();
  2169. bool ret = (*env)->CallStaticBooleanMethod(env, mActivityClass, midGetManifestEnvironmentVariables);
  2170. if (ret) {
  2171. bHasEnvironmentVariables = true;
  2172. }
  2173. }
  2174. }
  2175. int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y)
  2176. {
  2177. JNIEnv *env = Android_JNI_GetEnv();
  2178. int custom_cursor = 0;
  2179. jintArray pixels;
  2180. pixels = (*env)->NewIntArray(env, surface->w * surface->h);
  2181. if (pixels) {
  2182. (*env)->SetIntArrayRegion(env, pixels, 0, surface->w * surface->h, (int *)surface->pixels);
  2183. custom_cursor = (*env)->CallStaticIntMethod(env, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y);
  2184. (*env)->DeleteLocalRef(env, pixels);
  2185. } else {
  2186. SDL_OutOfMemory();
  2187. }
  2188. return custom_cursor;
  2189. }
  2190. void Android_JNI_DestroyCustomCursor(int cursorID)
  2191. {
  2192. JNIEnv *env = Android_JNI_GetEnv();
  2193. (*env)->CallStaticVoidMethod(env, mActivityClass, midDestroyCustomCursor, cursorID);
  2194. }
  2195. bool Android_JNI_SetCustomCursor(int cursorID)
  2196. {
  2197. JNIEnv *env = Android_JNI_GetEnv();
  2198. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetCustomCursor, cursorID);
  2199. }
  2200. bool Android_JNI_SetSystemCursor(int cursorID)
  2201. {
  2202. JNIEnv *env = Android_JNI_GetEnv();
  2203. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetSystemCursor, cursorID);
  2204. }
  2205. bool Android_JNI_SupportsRelativeMouse(void)
  2206. {
  2207. JNIEnv *env = Android_JNI_GetEnv();
  2208. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSupportsRelativeMouse);
  2209. }
  2210. bool Android_JNI_SetRelativeMouseEnabled(bool enabled)
  2211. {
  2212. JNIEnv *env = Android_JNI_GetEnv();
  2213. return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetRelativeMouseEnabled, (enabled == 1));
  2214. }
  2215. typedef struct NativePermissionRequestInfo
  2216. {
  2217. int request_code;
  2218. char *permission;
  2219. SDL_RequestAndroidPermissionCallback callback;
  2220. void *userdata;
  2221. struct NativePermissionRequestInfo *next;
  2222. } NativePermissionRequestInfo;
  2223. static NativePermissionRequestInfo pending_permissions;
  2224. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
  2225. JNIEnv *env, jclass cls,
  2226. jint requestCode, jboolean result)
  2227. {
  2228. SDL_LockMutex(Android_ActivityMutex);
  2229. NativePermissionRequestInfo *prev = &pending_permissions;
  2230. for (NativePermissionRequestInfo *info = prev->next; info != NULL; info = info->next) {
  2231. if (info->request_code == (int) requestCode) {
  2232. prev->next = info->next;
  2233. SDL_UnlockMutex(Android_ActivityMutex);
  2234. info->callback(info->userdata, info->permission, result ? true : false);
  2235. SDL_free(info->permission);
  2236. SDL_free(info);
  2237. return;
  2238. }
  2239. prev = info;
  2240. }
  2241. SDL_UnlockMutex(Android_ActivityMutex);
  2242. }
  2243. bool SDL_RequestAndroidPermission(const char *permission, SDL_RequestAndroidPermissionCallback cb, void *userdata)
  2244. {
  2245. if (!permission) {
  2246. return SDL_InvalidParamError("permission");
  2247. } else if (!cb) {
  2248. return SDL_InvalidParamError("cb");
  2249. }
  2250. NativePermissionRequestInfo *info = (NativePermissionRequestInfo *) SDL_calloc(1, sizeof (NativePermissionRequestInfo));
  2251. if (!info) {
  2252. return false;
  2253. }
  2254. info->permission = SDL_strdup(permission);
  2255. if (!info->permission) {
  2256. SDL_free(info);
  2257. return false;
  2258. }
  2259. static SDL_AtomicInt next_request_code;
  2260. info->request_code = SDL_AddAtomicInt(&next_request_code, 1);
  2261. info->callback = cb;
  2262. info->userdata = userdata;
  2263. SDL_LockMutex(Android_ActivityMutex);
  2264. info->next = pending_permissions.next;
  2265. pending_permissions.next = info;
  2266. SDL_UnlockMutex(Android_ActivityMutex);
  2267. JNIEnv *env = Android_JNI_GetEnv();
  2268. jstring jpermission = (*env)->NewStringUTF(env, permission);
  2269. (*env)->CallStaticVoidMethod(env, mActivityClass, midRequestPermission, jpermission, info->request_code);
  2270. (*env)->DeleteLocalRef(env, jpermission);
  2271. return true;
  2272. }
  2273. // Show toast notification
  2274. bool Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset)
  2275. {
  2276. bool result;
  2277. JNIEnv *env = Android_JNI_GetEnv();
  2278. jstring jmessage = (*env)->NewStringUTF(env, message);
  2279. result = (*env)->CallStaticBooleanMethod(env, mActivityClass, midShowToast, jmessage, duration, gravity, xOffset, yOffset);
  2280. (*env)->DeleteLocalRef(env, jmessage);
  2281. return result;
  2282. }
  2283. bool Android_JNI_GetLocale(char *buf, size_t buflen)
  2284. {
  2285. bool result = false;
  2286. if (buf && buflen > 0) {
  2287. *buf = '\0';
  2288. JNIEnv *env = Android_JNI_GetEnv();
  2289. jstring string = (jstring)(*env)->CallStaticObjectMethod(env, mActivityClass, midGetPreferredLocales);
  2290. if (string) {
  2291. const char *utf8string = (*env)->GetStringUTFChars(env, string, NULL);
  2292. if (utf8string) {
  2293. result = true;
  2294. SDL_strlcpy(buf, utf8string, buflen);
  2295. (*env)->ReleaseStringUTFChars(env, string, utf8string);
  2296. }
  2297. (*env)->DeleteLocalRef(env, string);
  2298. }
  2299. }
  2300. return result;
  2301. }
  2302. bool Android_JNI_OpenURL(const char *url)
  2303. {
  2304. bool result;
  2305. JNIEnv *env = Android_JNI_GetEnv();
  2306. jstring jurl = (*env)->NewStringUTF(env, url);
  2307. result = (*env)->CallStaticBooleanMethod(env, mActivityClass, midOpenURL, jurl);
  2308. (*env)->DeleteLocalRef(env, jurl);
  2309. return result;
  2310. }
  2311. int Android_JNI_OpenFileDescriptor(const char *uri, const char *mode)
  2312. {
  2313. // Get fopen-style modes
  2314. int moderead = 0, modewrite = 0, modeappend = 0, modeupdate = 0;
  2315. for (const char *cmode = mode; *cmode; cmode++) {
  2316. switch (*cmode) {
  2317. case 'a':
  2318. modeappend = 1;
  2319. break;
  2320. case 'r':
  2321. moderead = 1;
  2322. break;
  2323. case 'w':
  2324. modewrite = 1;
  2325. break;
  2326. case '+':
  2327. modeupdate = 1;
  2328. break;
  2329. default:
  2330. break;
  2331. }
  2332. }
  2333. // Translate fopen-style modes to ContentResolver modes.
  2334. // Android only allows "r", "w", "wt", "wa", "rw" or "rwt".
  2335. const char *contentResolverMode = "r";
  2336. if (moderead) {
  2337. if (modewrite) {
  2338. contentResolverMode = "rwt";
  2339. } else {
  2340. contentResolverMode = modeupdate ? "rw" : "r";
  2341. }
  2342. } else if (modewrite) {
  2343. contentResolverMode = modeupdate ? "rwt" : "wt";
  2344. } else if (modeappend) {
  2345. contentResolverMode = modeupdate ? "rw" : "wa";
  2346. }
  2347. JNIEnv *env = Android_JNI_GetEnv();
  2348. jstring jstringUri = (*env)->NewStringUTF(env, uri);
  2349. jstring jstringMode = (*env)->NewStringUTF(env, contentResolverMode);
  2350. jint fd = (*env)->CallStaticIntMethod(env, mActivityClass, midOpenFileDescriptor, jstringUri, jstringMode);
  2351. (*env)->DeleteLocalRef(env, jstringUri);
  2352. (*env)->DeleteLocalRef(env, jstringMode);
  2353. if (fd == -1) {
  2354. SDL_SetError("Unspecified error in JNI");
  2355. }
  2356. return fd;
  2357. }
  2358. static struct AndroidFileDialog
  2359. {
  2360. int request_code;
  2361. SDL_DialogFileCallback callback;
  2362. void *userdata;
  2363. } mAndroidFileDialogData;
  2364. JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeFileDialog)(
  2365. JNIEnv *env, jclass jcls,
  2366. jint requestCode, jobjectArray fileList, jint filter)
  2367. {
  2368. if (mAndroidFileDialogData.callback != NULL && mAndroidFileDialogData.request_code == requestCode) {
  2369. if (fileList == NULL) {
  2370. SDL_SetError("Unspecified error in JNI");
  2371. mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1);
  2372. mAndroidFileDialogData.callback = NULL;
  2373. return;
  2374. }
  2375. // Convert fileList to string
  2376. size_t count = (*env)->GetArrayLength(env, fileList);
  2377. char **charFileList = SDL_calloc(count + 1, sizeof(char *));
  2378. if (charFileList == NULL) {
  2379. mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1);
  2380. mAndroidFileDialogData.callback = NULL;
  2381. return;
  2382. }
  2383. // Convert to UTF-8
  2384. // TODO: Fix modified UTF-8 to classic UTF-8
  2385. for (int i = 0; i < count; i++) {
  2386. jstring string = (*env)->GetObjectArrayElement(env, fileList, i);
  2387. if (!string) {
  2388. continue;
  2389. }
  2390. const char *utf8string = (*env)->GetStringUTFChars(env, string, NULL);
  2391. if (!utf8string) {
  2392. (*env)->DeleteLocalRef(env, string);
  2393. continue;
  2394. }
  2395. char *newFile = SDL_strdup(utf8string);
  2396. if (!newFile) {
  2397. (*env)->ReleaseStringUTFChars(env, string, utf8string);
  2398. (*env)->DeleteLocalRef(env, string);
  2399. mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1);
  2400. mAndroidFileDialogData.callback = NULL;
  2401. // Cleanup memory
  2402. for (int j = 0; j < i; j++) {
  2403. SDL_free(charFileList[j]);
  2404. }
  2405. SDL_free(charFileList);
  2406. return;
  2407. }
  2408. charFileList[i] = newFile;
  2409. (*env)->ReleaseStringUTFChars(env, string, utf8string);
  2410. (*env)->DeleteLocalRef(env, string);
  2411. }
  2412. // Call user-provided callback
  2413. SDL_ClearError();
  2414. mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, (const char *const *) charFileList, filter);
  2415. mAndroidFileDialogData.callback = NULL;
  2416. // Cleanup memory
  2417. for (int i = 0; i < count; i++) {
  2418. SDL_free(charFileList[i]);
  2419. }
  2420. SDL_free(charFileList);
  2421. }
  2422. }
  2423. bool Android_JNI_OpenFileDialog(
  2424. SDL_DialogFileCallback callback, void *userdata,
  2425. const SDL_DialogFileFilter *filters, int nfilters, bool forwrite,
  2426. bool multiple)
  2427. {
  2428. if (mAndroidFileDialogData.callback != NULL) {
  2429. SDL_SetError("Only one file dialog can be run at a time.");
  2430. return false;
  2431. }
  2432. if (forwrite) {
  2433. multiple = false;
  2434. }
  2435. JNIEnv *env = Android_JNI_GetEnv();
  2436. // Setup filters
  2437. jobjectArray filtersArray = NULL;
  2438. if (filters) {
  2439. jclass stringClass = (*env)->FindClass(env, "java/lang/String");
  2440. filtersArray = (*env)->NewObjectArray(env, nfilters, stringClass, NULL);
  2441. (*env)->DeleteLocalRef(env, stringClass);
  2442. // Convert to string
  2443. for (int i = 0; i < nfilters; i++) {
  2444. jstring str = (*env)->NewStringUTF(env, filters[i].pattern);
  2445. (*env)->SetObjectArrayElement(env, filtersArray, i, str);
  2446. (*env)->DeleteLocalRef(env, str);
  2447. }
  2448. }
  2449. // Setup data
  2450. static SDL_AtomicInt next_request_code;
  2451. mAndroidFileDialogData.request_code = SDL_AddAtomicInt(&next_request_code, 1);
  2452. mAndroidFileDialogData.userdata = userdata;
  2453. mAndroidFileDialogData.callback = callback;
  2454. // Invoke JNI
  2455. jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass,
  2456. midShowFileDialog, filtersArray, (jboolean) multiple, (jboolean) forwrite, mAndroidFileDialogData.request_code);
  2457. (*env)->DeleteLocalRef(env, filtersArray);
  2458. if (!success) {
  2459. mAndroidFileDialogData.callback = NULL;
  2460. SDL_AddAtomicInt(&next_request_code, -1);
  2461. SDL_SetError("Unspecified error in JNI");
  2462. return false;
  2463. }
  2464. return true;
  2465. }
  2466. #endif // SDL_PLATFORM_ANDROID