Dan Ginsburg
I've seen this on several devices including Moto Z running Android 7 and a Snapdragon 845 running Android 9.
What happens is as follows:
SDLActivity.onWindowFocusChanged(true) happens pretty early on, but it's before we've done SDL_CreateWindow and so Android_Window is 0x0 thus this message does not get sent:
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
JNIEnv *env, jclass cls, jboolean hasFocus)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window) {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged()");
SDL_SendWindowEvent(Android_Window, (hasFocus ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST), 0, 0);
}
SDL_UnlockMutex(Android_ActivityMutex);
}
When the window does get created, in Android_CreateWindow it does this:
window->flags &= ~SDL_WINDOW_RESIZABLE; /* window is NEVER resizeable */
window->flags &= ~SDL_WINDOW_HIDDEN;
window->flags |= SDL_WINDOW_SHOWN; /* only one window on Android */
window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */
/* One window, it always has focus */
SDL_SetMouseFocus(window);
SDL_SetKeyboardFocus(window);
The SDL_SetKeyboardFocus does send an SDL_WINDOWEVENT_FOCUS_GAINED message, but it gets eaten in SDL_SendWindowEvent because we've forced SDL_WINDOW_INPUT_FOCUS beforehand:
case SDL_WINDOWEVENT_FOCUS_GAINED:
if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
return 0;
}
window->flags |= SDL_WINDOW_INPUT_FOCUS;
SDL_OnWindowFocusGained(window);
break;
I can fix the problem if I comment out this line from Android_CreateWindow:
window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */
I would propose that as a fix unless there is a reason not to.
java layer runs as if separate mouse and touch was 1,
Use SDL_HINT_MOUSE_TOUCH_EVENTS and SDL_HINT_TOUCH_MOUSE_EVENTS
for generating synthetic touch/mouse events
by checking Android_Window validity
- SDLThread: user application is exiting:
SDL_VideoQuit() and clearing SDL_GetVideoDevice()
- ActivityThread is changing orientation/size
surfaceChanged() > Android_SetScreenResolution() > SDL_GetVideoDevice()
- Separate function into Android_SetScreenResolution() and Android_SendResize(),
formating, and mark Android_DeviceWidth/Heigh as static
- If you call onPause() before CreateWindow(), SDLThread will run in infinite loop in background.
- If you call onPause() between a DestroyWindow() and a new CreateWindow(), semaphores are invalids.
SDLActivity.java: the first resume() starts the SDLThread, don't call
nativeResume() as it would post ResumeSem. And the first pause would
automatically be resumed.
First error could happen if Android_SetWindowFullscreen somehow gets
called between SurfaceDestroyed() and SurfaceCreated()
Second error should not happen has native_window validity is guaranteed.
(It would happens previously with error -19)
It accesses data->native_window, which can be changed by onNativeSurfacedChanged().
Currently, Android_SetWindowFullscreen() may access data->native_window after it
has been released, and before a new reference is acquired.
(can be reproduced by adding some SDL_Delay() in onNativeSurfacedChanged and
Android_SetWindowFullscreen() ).
It may happen to have the sequence pause()/resume()/pause(), before polling
any events.
Before, it ends in 'resumed' state because as the check is greedy.
Now, always increase the Pause semaphore, and stop at each pause.
It ends in 'paused' state.
Related to bug 3250: set up a reconfiguration of SurfaceView holder.
Turn the screen off manually before the app starts
(repro rate is not 100%..)
"Pause" transition will add events:
SDL_WINDOWEVENT_ENTER
SDL_WINDOWEVENT_FOCUS_LOST
SDL_WINDOWEVENT_MINIMIZED
SDL_APP_WILL ENTER BACKGROUND
SDL_APP_DID ENTER BACKGROUND
"Resume" transition will add events:
SDL_APP_WILL ENTER FOREGROUND
SDL_APP_DID ENTER FOREGROUND
SDL_WINDOWEVENT_FOCUS_GAINED
SDL_WINDOWEVENT_RESTORED
If Android application doesn't empty the event loop in between, it enters in
"paused" state when SDL_WINDOWEVENT_RESTORED is fetched.
See bug 3250 for pratical case.
Occurs when application goes to background:
- Java activity is destroying SurfaceView holder and "egl_surface" (in onNativeSurfaceDestroyed())
- While native thread is in Android_GLES_SwapWindow(), prepared to call SDL_EGL_SwapBuffers()
The error is "call to eglSwapBuffers failed, reporting an error of EGL_BAD_SURFACE"
It an be reproduced easily by adding a SDL_Delay(100) at the begining of SDL_EGL_SwapBuffers(),
and putting the application into background.
Use a semaphore to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
(Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
Touch device types include SDL_TOUCH_DEVICE_DIRECT (a touch screen with window-relative coordinates for touches), SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE (a trackpad-style device with absolute device coordinates), and SDL_TOUCH_DEVICE_INDIRECT_RELATIVE (a trackpad-style device with screen cursor-relative coordinates).
Phone screens are an example of a direct device type. Mac trackpads are the indirect-absolute touch device type. The Apple TV remote is an indirect-relative touch device type.
Expand SDLActivity::SDLSurface::surfaceChanged() callback to grab the panel width and height at the same time and pass that along to the native code. Only works on API 17+. Duplicates surface dimensions whenever it fails.
Add Android_DeviceWidth/Android_DeviceHeight globals to native code.
Disambiguate Android_ScreenWidth/Android_ScreenHeight -> Android_SurfaceWidth/Android_SurfaceHeight
Use device width/height for all display mode settings.
This is commented out in SDLActivity.java, with the note #CURSORIMPLEENTATION because it requires API 24, which is higher than the minimum required SDK
Manuel Sabogal
If SDL is compiled with the Audio subsystem disabled there are some undefined references to the functions ANDROIDAUDIO_ResumeDevices and ANDROIDAUDIO_PauseDevices in the file src/video/android/SDL_androidevents.c.