3030
3131Runtime *runtime = nullptr ;
3232
33+ // Pipe file descriptors: g_backPipe[0] is read-end, g_backPipe[1] is write-end
34+ static int g_backPipe[2 ] = {-1 , -1 };
35+
3336// the sensorTypes corresponding to _sensors[] positions
3437constexpr int SENSOR_TYPES[MAX_SENSORS] = {
3538 ASENSOR_TYPE_ACCELEROMETER,
@@ -106,6 +109,48 @@ void handleCommand(android_app *app, int32_t cmd) {
106109 }
107110}
108111
112+ static void pushBackEvent () {
113+ auto *maEvent = new MAEvent ();
114+ maEvent->nativeKey = AKEYCODE_BACK;
115+ maEvent->type = EVENT_TYPE_KEY_PRESSED;
116+ runtime->pushEvent (maEvent);
117+ }
118+
119+ //
120+ // Callback registered with ALooper that is triggered when the pipe receives data.
121+ // This is what wakes the blocked ALooper_pollOnce() and lets us run pushBackEvent().
122+ //
123+ static int pipeCallback (int fd, int events, void *data) {
124+ // clear the byte that woke the pipe, then return 1 to stay registered
125+ logEntered ();
126+ char buf[1 ];
127+ read (fd, buf, 1 );
128+ pushBackEvent ();
129+ return 1 ;
130+ }
131+
132+ //
133+ // Set up the pipe and register its read-end (g_backPipe[0]) with the ALooper.
134+ // This allows us to wake the looper from Java code by writing to the pipe.
135+ //
136+ static void setupBackWakePipe (ALooper *looper) {
137+ if (pipe (g_backPipe) == 0 ) {
138+ // Make read-end non-blocking to avoid stalling the loop
139+ fcntl (g_backPipe[0 ], F_SETFL, O_NONBLOCK);
140+
141+ // Register the pipe with the looper so it wakes up when there's input
142+ ALooper_addFd (looper,
143+ g_backPipe[0 ], // fd to watch
144+ 0 , // arbitrary/unused identifier
145+ ALOOPER_EVENT_INPUT, // watch for input readiness
146+ pipeCallback, // callback to run on wake
147+ nullptr ); // no additional data
148+ trace (" Back pipe registered with looper" );
149+ } else {
150+ trace (" Failed to create back pipe" );
151+ }
152+ }
153+
109154// see http://stackoverflow.com/questions/15913080
110155static void process_input (android_app *app, android_poll_source *source) {
111156 AInputEvent* event = nullptr ;
@@ -114,12 +159,8 @@ static void process_input(android_app *app, android_poll_source *source) {
114159 AKeyEvent_getKeyCode (event) == AKEYCODE_BACK) {
115160 // prevent AInputQueue_preDispatchEvent from attempting to close
116161 // the keypad here to avoid a crash in android 4.2 + 4.3.
117- if (AKeyEvent_getAction (event) == AKEY_EVENT_ACTION_DOWN &&
118- runtime->isActive ()) {
119- auto *maEvent = new MAEvent ();
120- maEvent->nativeKey = AKEYCODE_BACK;
121- maEvent->type = EVENT_TYPE_KEY_PRESSED;
122- runtime->pushEvent (maEvent);
162+ if (AKeyEvent_getAction (event) == AKEY_EVENT_ACTION_DOWN && runtime->isActive ()) {
163+ pushBackEvent ();
123164 }
124165 AInputQueue_finishEvent (app->inputQueue , event, true );
125166 } else if (!AInputQueue_preDispatchEvent (app->inputQueue , event)) {
@@ -128,6 +169,18 @@ static void process_input(android_app *app, android_poll_source *source) {
128169 }
129170}
130171
172+ extern " C" JNIEXPORT void JNICALL Java_net_sourceforge_smallbasic_MainActivity_onBack
173+ (JNIEnv *env, jclass clazz) {
174+ if (runtime != nullptr ) {
175+ logEntered ();
176+ if (g_backPipe[1 ] >= 0 ) {
177+ // write a placeholder byte to trigger the read and wake ALooper_pollOnce
178+ char buf = ' x' ;
179+ write (g_backPipe[1 ], &buf, 1 );
180+ }
181+ }
182+ }
183+
131184// callbacks from MainActivity.java
132185extern " C" JNIEXPORT void JNICALL Java_net_sourceforge_smallbasic_MainActivity_onActivityPaused
133186 (JNIEnv *env, jclass jclazz, jboolean paused) {
@@ -234,6 +287,7 @@ Runtime::Runtime(android_app *app) :
234287 _looper = ALooper_forThread ();
235288 _sensorManager = ASensorManager_getInstance ();
236289 memset (&_sensors, 0 , sizeof (_sensors));
290+ setupBackWakePipe (_looper);
237291}
238292
239293Runtime::~Runtime () {
@@ -1075,4 +1129,3 @@ void osd_beep(void) {
10751129 osd_sound (1000 , 30 , 100 , 0 );
10761130 osd_sound (500 , 30 , 100 , 0 );
10771131}
1078-
0 commit comments