Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android black screen on resume using OpenGL ES 2.0 #4041

Closed
SDLBugzilla opened this issue Feb 11, 2021 · 47 comments
Closed

Android black screen on resume using OpenGL ES 2.0 #4041

SDLBugzilla opened this issue Feb 11, 2021 · 47 comments

Comments

@SDLBugzilla
Copy link
Collaborator

This bug report was migrated from our old Bugzilla tracker.

Reported in version: 2.0.14
Reported for operating system, platform: Android (All), ARM

Comments on the original bug report:

On 2021-02-02 22:01:29 +0000, Vitaly Novichkov wrote:

Hello!

I found that the bug against the black screen on resuming got back, it happens on various devices:

  • Open the game
  • Switch to another application, try to do anything
  • Try to switch the game back
  • You'll get the black screen
  • However, the game itself will resume its normal work, but a black screen
  • No matter what to draw: textures or color-filled shapes
  • I do render into texture, then into the screen. Also, I draw some shapes on the screen directly.
    This happens on Android 4.1, 5.1, Android 10, also on Android 8 the bug confirmed.

At the logger I get the next spamming:

02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/libEGL: eglMakeCurrent:777 error 3002 (EGL_BAD_ACCESS)
02-03 00:55:02.482 3666-3690/ru.wohlsoft.thextech.debug E/EGL_emulation: tid 3690: eglMakeCurrent(1500): error 0x3002 (EGL_BAD_ACCESS)


If needed, I can try to compose a simple unit test that reproduces this bug

On 2021-02-03 07:21:14 +0000, Sylvain wrote:

Did you make sure not to call any rendering function between the two events
SDL_WILL_ENTER_BACKGROUND and SDL_DID_ENTER_FOREGROUND

see docs/README-android.md

On 2021-02-03 21:50:38 +0000, Vitaly Novichkov wrote:

Oh, interesting, how I messed this up? Gonna try this out... Thanks for the notice!

On 2021-02-03 22:19:12 +0000, Vitaly Novichkov wrote:

btw, does that also counting the rendering into the texture too? Okay, I'll check.

On 2021-02-03 23:16:12 +0000, Vitaly Novichkov wrote:

Nope, that didn't help completely, even I made the strict avoiding of any render functions being called, I still can get the black screen and the EGL_BAD_ACCESS error after I switched application back

On 2021-02-04 07:24:08 +0000, Sylvain wrote:

that really really is a reason,
are you depleting the event loop ?
no multi-threading ?

really stop using renderer after reading WILL_ENTER_BG, and start again using DID_ENTER_FG.

are you using the SDL_Renderer or your own context ?

On 2021-02-04 08:02:26 +0000, Vitaly Novichkov wrote:

are you depleting the event loop ?

No, I do call the event loop sequentially

no multi-threading ?

No, single threading

really stop using renderer after reading WILL_ENTER_BG, and start again using DID_ENTER_FG.

Oh, I did a mistake - I used the WILL_ENTER_FG instead of WILL_ENTER_BG event, I should re-try this experiment again.

are you using the SDL_Renderer or your own context ?

I use SDL_Renderer

On 2021-02-04 08:32:58 +0000, Vitaly Novichkov wrote:

Okay, I really made sure no render will be called between those events, I even added asserts to crash if any of the render calls will be executed after SDL_APP_WILLENTERBACKGROUND event, and allow them to be called after SDL_APP_DIDENTERFOREGROUND event that unlocks the render.

On 2021-02-04 08:35:03 +0000, Vitaly Novichkov wrote:

The result is the same, on Android 10:

  • open the game
  • switch another application (my log will print the fact "SDL_APP_WILLENTERBACKGROUND" was coming, I made sure those messages be printed to see the result)
  • All big render calls got blocked by a boolean
  • All small routine render calls got blocked by assert
  • Switch the game back (my log will print the fact of "SDL_APP_DIDENTERFOREGROUND")
  • Black screen

On 2021-02-04 08:41:00 +0000, Vitaly Novichkov wrote:

I also checked the fact of any attempts to call the render calls, just by spamming of the same message into the log, the spamming is off completely when entering background, and getting on back when entered background.

On 2021-02-04 08:48:27 +0000, Sylvain wrote:

are you depleting the event loop ?
No, I do call the event loop sequentially

before each frame, and also periodically, you must poll all the events.

Just to make sure, all functions using the SDL_renderer includes: RenderCopy, RenderPresent, CreateTexture, UpdateTexture, ..

On 2021-02-04 08:55:37 +0000, Vitaly Novichkov wrote:

before each frame, and also periodically, you must poll all the events.

Yes, that what I already do, I poll all events in every loop cycle

Just to make sure, all functions using the SDL_renderer includes:

CreateTexture, UpdateTexture
I don't call them during the loop except for the lazy-decompression algorithm that gets executed when calling the big render call, I blocked by the boolean

RenderCopy, RenderPresent
Both blocked by the boolean I do set

On 2021-02-04 09:15:34 +0000, Sylvain wrote:

This look strange ...

you have recent SDL2 lib + sync'ed java file ?

you can add trace here:
http://hg.libsdl.org/SDL/file/1cde3dd0f44d/src/video/android/SDL_androidevents.c
to backup and restore context

maybe log the event: SDL_RENDER_DEVICE_RESET

with those print do you have a full adb log to check ?

try with a simpler test case ?

On 2021-02-04 09:47:04 +0000, Vitaly Novichkov wrote:

Closing to the evening (UTC+3) I'll try to compose a simple test program that simulates my and verify the work.

On 2021-02-04 09:52:02 +0000, Vitaly Novichkov wrote:

Anyway, without of SDL2 patching, I did the tracking of SDL_RENDER_DEVICE_RESET event, and I got next:

02-04 12:50:08.444 2791-2814/ru.wohlsoft.thextech.debug D/TRACKERS: Android: Entering background
02-04 12:50:16.879 2791-2814/ru.wohlsoft.thextech.debug D/TRACKERS: Android: Resumed foreground
02-04 12:50:19.591 2791-2814/ru.wohlsoft.thextech.debug D/TRACKERS: Android: Entering background
02-04 12:50:23.807 2791-2814/ru.wohlsoft.thextech.debug D/TRACKERS: Android: Resumed foreground
02-04 12:50:27.021 2791-2814/ru.wohlsoft.thextech.debug D/TRACKERS: Android: Entering background
02-04 12:50:32.787 2791-2814/ru.wohlsoft.thextech.debug D/TRACKERS: Android: Resumed foreground
02-04 12:50:32.787 2791-2814/ru.wohlsoft.thextech.debug D/TRACKERS: Android: Render Device Reset

I had multiple times to go into the background, and then, resume back, but the thing got blacked once SDL_RENDER_DEVICE_RESET was happened at my log

On 2021-02-04 09:54:51 +0000, Vitaly Novichkov wrote:

So, seems once this event came:


SDL_RENDER_TARGETS_RESET

the render targets have been reset and their contents need to be updated (>= SDL 2.0.2)

SDL_RENDER_DEVICE_RESET

the device has been reset and all textures need to be recreated (>= SDL 2.0.4)

I should reload all the textures again, right?

On 2021-02-04 09:55:33 +0000, Vitaly Novichkov wrote:

I'll try some in the evening after my workday will finish.

On 2021-02-04 12:26:04 +0000, Sylvain wrote:

yep, re-create texture once you get the device reset.
(you don't need to recreate the SDL renderer)

On 2021-02-04 12:53:31 +0000, Sylvain wrote:

Updated the android README file because this is a recurrent issue
http://hg.libsdl.org/SDL/rev/8609e27b3eed

feel free to improve it!

@Wohlstand
Copy link
Contributor

Gonna resume the work on this at me. To confirm the solution of this I must implement the textures reloading at me. However, I still have some questions on this:

  • Should I destroy all old SDL_Texture* objects or they already got self-destroyed and I will have the set of the dead pointers?
  • Forgot to tell you that this bug also affects the drawing of texture-less shapes, probably that because the render settings also got been reset too, right?

@Wohlstand
Copy link
Contributor

Wohlstand commented Feb 22, 2021

Okay, I did an attempt to handle the SDL_RENDER_DEVICE_RESET (it goes after SDL_APP_DIDENTERFOREGROUND) and reload some of the textures, however, that didn't help:

  • Attempt to use SDL_CreateTexture() caused me E/libEGL: eglMakeCurrentImpl:1321 error 3002 (EGL_BAD_ACCESS) error
  • When I trying to remove an old texture by SDL_DestroyTexture(), I get the same.
  • Attempt to call SDL_SetRenderTarget(m_gRenderer, NULL); have caused the same error

Now I need to try what will happen if I'll re-create the renderer context 🤔

@ell1e
Copy link
Contributor

ell1e commented Feb 23, 2021

I hope this is possibly helpful:

Usually how this works from my own experience a long while back is that you're basically supposed to not touch the SDL2 graphics API any more in any way once SDL_WILL_ENTER_BACKGROUND reached you. This includes destroying textures, or querying them in any way, or doing anything to the renderer (like destroying it). If you have mechanisms that may garbage collect textures while in background you need to queue up the deletions instead and wait to do them until the app returns into foreground. If you still see EGL_BAD_ACCESS, it usually means you forgot some code accessing any texture, any renderer, any OGL context which violated this rule. If you leave everything alone until the app is back it should usually magically "just work" with same textures, same context, same everything unchanged. From your description, it seems like you maybe didn't follow this approach so it looks a little like it might be a bug in your code then, not an SDL2 bug.

@Wohlstand
Copy link
Contributor

I guess that the whole issue should have the "help wanted" tag rather than "bug".

@samgubernick
Copy link

I assume the issue I'm having is the same. SDL is apparently not getting the OpenGL context when the app comes back into focus. I posted more about this here: https://discourse.libsdl.org/t/sdl-2-0-14-error-after-reentering-android-activity/28701

I'm not sure how to set break points in Android Studio for SDL, but from what I'm observing, either android_egl_context_restore(SDL_Window *window) or android_egl_context_backup(SDL_Window *window) isn't working correctly. Those are in video/android/SDL_androidevents.c. SDL still has a lock on the context so I can't call SDL_GL_CreateContext() or SDL_GL_DeleteContext(). GetCurrentContext() returns nullptr without an error code, and CreateContext() fails because there's an existing context.

@Wohlstand
Copy link
Contributor

@samgubernick, interesting... Initially, I got the guide that I don't need to re-create the context:

yep, re-create texture once you get the device reset.
(you don't need to recreate the SDL renderer)

, but that didn't help: re-created textures won't work, and there are even texture-less primitives that won't be drawn.

@Wohlstand
Copy link
Contributor

I'm not sure how to set break points in Android Studio for SDL

You can do that if you build the SDL2 library on your own, simply browse actual source files, open them, and set, them should work. Files must be located at the places where they have placed while the build process.

@ell1e
Copy link
Contributor

ell1e commented Feb 24, 2021

From when I last tried this you definitely don't need to recreate anything, not the textures either, unless something major changed in the few last SDL2 releases. But you sure would be required to not touch anything at all in any way about the textures, the context(!), the renderer, the windows for things like size, or anything which could access the GL context. @samgubernick you also really shouldn't need to modify the SDL2 code to make anything a weak reference. Any specific reason why you did so? Just getting a black screen after resume can also happen due to a lot of reasons, so you may not be having the same issue at all.

Also, if BLOCK_ON_PAUSE is 1 (which used to be the default) then as soon as the app pauses your event loop should freeze anyway. My deepest apologies if that should have changed too, I tried all of this about two years back. But I wouldn't have heard of any changes to this at least. So one easy alternative to trying to fix your code not to touch anything would be to just use BLOCK_ON_PAUSE.

@samgubernick
Copy link

Thanks for all that info. Yeah, I tried adding the weak reference because of the suggestion in Android Studio about the memory leak from the static Context, but I took that out and just used the original one from SDL.

So, I tried a few different things to isolate the issue. I haven't had a chance to use breakpoints in SDL but I replaced the entire run() loop with just a basic while(true) and SDL_PollEvent() and I'm still getting an error about the context.

while(true)
{
SDL_PollEvent();
if (SDL_GL_GetCurrentContext() != nullptr) // this evaluates to true
{
glCheckFramebufferStatus(GL_FRAMEBUFFER); // this is returning GL_FRAMEBUFFER_UNDEFINED
glClear(GL_COLOR_BUFFER_BIT); // This is getting an "Invalid framebuffer operation" error from OpenGL
}
}

When that's first running, everything's fine. After switching tasks and then reopening it on my Pixel 3, I start getting errors. For just testing this now, there's no other task running, so nothing should be touching OpenGL after the SDL_APP_WILLENTERBACKGROUND event since the program's just in that above loop. If I try to reinitialize OpenGL after glCheckFramebufferStatus(), I'll get a segmentation fault of some variety on the depth check:

    if (SDL_GL_GetCurrentContext() != nullptr)
{
	if constexpr (LOG) { msg << "Context is valid--deleting..."; msg.out(LOG_DATA, log::Color::openGl); }
	SDL_GL_DeleteContext(context);
} // this is running

initializeAttributes();
if constexpr (LOG) { msg << "Creating GL context"; msg.out(LOG_DATA, log::Color::openGl); }
context = SDL_GL_CreateContext(window);
if (context == nullptr)
{
	if constexpr (LOG) { msg << "Failed to create GL context: " << SDL_GetError(); msg.err(LOG_DATA); }
}
else
{
	if constexpr (LOG) { msg << "Successfully created GL context"; msg.out(LOG_DATA, log::Color::openGl); }
} // this is running

if constexpr (LOG) { msg << "Finished creating GL context"; msg.out(LOG_DATA, log::Color::openGl); }
{
	auto majorVersion = int{ 0 };
	auto minorVersion = int{ 0 };
	SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &majorVersion);
	SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minorVersion);

	msg << "OpenGL version: " << majorVersion << "." << minorVersion; msg.out(LOG_DATA, log::Color::openGl);

	msg << "Getting profile mask"; msg.out(LOG_DATA, log::Color::openGl);
	auto profileMask = int{ 0 };
	SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profileMask);

	msg << "Getting depth size"; msg.out(LOG_DATA, log::Color::openGl);
	auto depthSize = int{ 0 };
	if (SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthSize) == 0) // this is causing a segmentation error in SDL_video.c on line 3660: glGetIntegervFunc(attrib, (GLint *) value);
	{
		msg << "Success"; msg.out(LOG_DATA, log::Color::openGl);
	}
}

I don't want to hijack the original bug report if you don't think this is related to it. Thanks again for all info you posted! It's really helpful to me. I'll see what else I can do to track this problem down.

@ell1e
Copy link
Contributor

ell1e commented Feb 26, 2021

Are you running SDL_GL_GetCurrentContext() after you got SDL_APP_WILLENTERBACKGROUND? AFAIK you shouldn't do that either, instead use a global variable like some custom am_i_in_background where after getting SDL_APP_WILLENTERBACKGROUND in SDL_PollEvent you set it to 1, and then you skip any OpenGL check (even just to check for a context) entirely until you get SDL_DID_ENTER_FOREGROUND. The context never disappears, neither do window, textures, ... but any access while in background will (permanently!) break things.

Like this:

int am_i_in_background = 0;

...

    while (true) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_APP_WILLENTERBACKGROUND)
                am_i_in_background = 1;
            elseif (event.type == SDL_DID_ENTER_FOREGROUND)
                am_i_in_background = 0;
        }
        if (!am_i_in_background && SDL_GL_GetCurrentContext() != nullptr) { // this evaluates to true
            glCheckFramebufferStatus(GL_FRAMEBUFFER); // this is returning GL_FRAMEBUFFER_UNDEFINED
            glClear(GL_COLOR_BUFFER_BIT); // This is getting an "Invalid framebuffer operation" error from OpenGL
        }
    }

@samgubernick
Copy link

samgubernick commented Feb 26, 2021

Yeah, I replaced the contents of those events with return, so there's nothing touching OpenGL there, either. I'm confident I'm not touching OpenGL after the program starts to enter the background or before it completely comes back. Output from logging that loop:

   loop start
   loop end
   ...
   loop start
   sdlEventAppWillEnterBackground called
   sdlEventAppDidEnterBackground called
   <no output because it's in the background, and then I get this after reopening app>
   Adreno: OverrideNativeWindowFormat: Unable to find a format in sRGB colorspace for EGL format: 518
   Adreno: Create: Initialize failed
   sdlEventAppWillEnterForeground called
   sdlEventAppDidEnterForeground called
   GL_FRAMEBUFFER_UNDEFINED
   glClear: Invalid framebuffer operation

Do you have a simple OpenGL window program that I can test here? If not, I'll try to separate out my window code and the rest of it into a small repository so you can see exact what's going on. I'd like to contribute to fixing this if that's where the problem is, but I don't know the inside of SDL at all.

Edit: replacing glClear() with SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthSize) still causes the segmentation fault.

  while(true)
  {
     output("Loop start");

     while (SDL_PollEvent(&events.sdlEvent) == 1)
     {
     }

     if (SDL_GL_GetCurrentContext() != nullptr) // this evaluates to true
     {
        glCheckFramebufferStatus(GL_FRAMEBUFFER); // this is returning GL_FRAMEBUFFER_UNDEFINED
        auto depthSize = int{ 0 };
        SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthSize); // sigsegv_fault
     }

     output("Loop end");
  }

@ell1e
Copy link
Contributor

ell1e commented Mar 3, 2021

I don't understand, your loop still does not seem to be checking SDL_PollEvent as I suggested and avoiding any access to the context while in background. As far as I know you can't just poll events and ignore them, you need to track the foreground/background state always.

@samgubernick
Copy link

samgubernick commented Mar 3, 2021

Sorry, I oversimplified what I wrote. Yes, while(true) should've been a global called shouldRender in that example, and it only changes on WILL_ENTER_BACKGROUND and DID_ENTER_FOREGROUND. In terms of ignoring the polled events, how would that affect this particular case? After creating the window and the GL context, as long as I'm not touching OpenGL again, is there something I actually need to handle apart from the filtered events? Plugging it back into my event handler, the only window events I'm seeing are focus lost, minimized, restored, and focus gained, and I'm not seeing any display-related events at all. I'm returning 0 on the filtered events so I wouldn't expect to see those in my event handler (I haven't confirmed that those aren't there).

I've stripped even more out to the point where all that loop is doing is outputting that it's running. No OpenGL checks or anything else. When the app goes into the background, I get log messages from the filtered events (WILL/DID_ENTER_BACKGROUND) and then complete silence. When I reopen it, I get the two Adreno errors, which I would hazard are related to my problem:

  I/Adreno: OverrideNativeWindowFormat: Unable to find a format in sRGB colorspace for EGL format: 518
  I/Adreno: Create: Initialize failed

followed by my log messages from WILL/DID_ENTER_FOREGROUND. Adding SDL_Delay(200) inside the loop (and after SDL_PollEvent()) to increment a variable, comparing that variable to 30, and then touching OpenGL, I'm still getting the previous error about the invalid framebuffer.

I have to see why I can't get breakpoints in SDL to work in Android Studio. That would help me find what's going on in SDL since I can only follow it abstractly right now.

Thanks again for your help.

@ell1e
Copy link
Contributor

ell1e commented Mar 3, 2021

In terms of ignoring the polled events, how would that affect this particular case?

That can affect everything, because that takes one SDL event off the queue and that could be an SDL_APP_WILLENTERBACKGROUND. Every event must be checked for this, or you risk getting into an undefined state. So I'm not sure what exactly you're trying to do, but from looking at your code it seems kind of obvious to me that it won't work due to that alone.

is there something I actually need to handle apart from the filtered events?

You need to track the background/foreground state which your above code doesn't seem to do. (Or am I missing something?) Basically once you get SDL_PollEvent with a change and then you call SDL_PollEvent again, SDL2 will assume you have now informed all your threads or whatever and your context is now unsafe to access. This can happen with any single call of SDL_PollEvent as far as I'm aware.

After creating the window and the GL context, as long as I'm not touching OpenGL again, is there something I actually need to handle apart from the filtered events?

But you are touching OpenGL, with SDL_GL_GetCurrentContext.

@samgubernick
Copy link

samgubernick commented Mar 3, 2021

I understand what you're saying, but I think the confusion is coming from the unclear code that I posted above.

  namespace
     {
        bool shouldRender = false;

        void sdlEventFunctionAppDid...AppWill... // set shouldRender to true or false
        ...
        void customFilter()
        {
        ... // sends the events to the appropriate app event handler
        }
     }

   StartHere::now()
   {
      createWindow(); // this will set shouldRender to true
      SDL_SetEventFilter(customFilter, ptrToResources);
      ...
     auto t =  int{ 0 };
     while (true)
     {
         while (shouldRender)
         {
            logOut("loop start");
            events.checkEventsNoResources(); // handles SDL events, but while (SDL_PollEvent() == 1) would work, too, because of the filter
            
            SDL_Delay(200);
            ++t; // this won't increment while the app is minimized
            
            if (t == 30) // minimize the app before t reaches 30
            {
               glBindFramebuffer(GL_FRAMEBUFFER, 0);
               switch (glCheckFramebufferStatus(GL_FRAMEBUFFER))
               {
               // invalid framebuffer
               }
            }
            logOut("loop end");
         }
     }
   }

I added the SDL app events to the event handler and I can confirm that the filter is correctly not sending them to the handler since they're each returning 0. As a note, I'm using the Mercurial build from a week or so ago, but I also had this problem in 2.0.14, which is why I tried upgrading to the development build. I'll see if the problem goes away if I can downgrade to 2.0.12 or even earlier.

When I have some time again, I'll try to get Android Studio to use breakpoints in SDL but I've gotten frustrated when I've tried this before; it just says it isn't part of my project. "This file does not belong to any project target, code insight features might not work properly," Even more frustrating is that I can't get the Android Studio emulator to actually use GLES 3.0. It seems perpetually stuck on 2.0 even after adding the dynamic line to the .ini file. It could be my laptop (Aero 15x) but that doesn't make much sense to me since it has a 1070 Max-Q. I might be able to temporarily change some of my code to ignore the incompatibility since it'd just be to see if it's the Adreno GPU or not, though I don't remember how much from 3.0 I'm using.

@ell1e
Copy link
Contributor

ell1e commented Mar 3, 2021

You still seem to be doing glBindFramebuffer unconditionally. Why aren't you checking shouldRender before doing that? Right, I guess I see it maybe now, but couldn't it be that 1. events.checkEventsNoResources calls SDL_PollEvent() multiple times, 2. now you're in the background but you run glBindFramebuffer right after if t has an unlucky value in the same loop with no in between shouldRender check? I think your checks are still logically in possibly wrong order. Even just one wrong access can blow things up. You need a possible bail out after any event being processed, and I don't see that guaranteed in how your loop is set up right now.

@samgubernick
Copy link

samgubernick commented Mar 3, 2021

Yeah, I completely get where you're coming from. The SDL documentation is a life saver for that--that was the only way I knew about the app events and the custom filter.

For this test on my phone, it's exactly what I posted apart from a few simplified sections that definitely are not using OpenGL. That was one of the reasons I replaced my own event handler with the dummy one that just pumps through the SDL events without doing anything. The thing with t was my idea to make sure that nothing in that loop was touching OpenGL till well after SDL's sent DIDENTERFOREGROUND and my program handled it. I could post that one file/part of that file to GitHub if it'd help to confirm that I'm not calling any OpenGL function.

@ell1e
Copy link
Contributor

ell1e commented Mar 3, 2021

I'm not sure I was 100% clear, so just in case I was not: events.checkEventsNoResources() could mean you're now in the background. Then you'll still unconditionally call glBindFramebuffer right after. (The loop condition shouldRender is after all not being checked in the middle of your loop.) It just seems like your loop isn't set up right, even with the filter.

@samgubernick
Copy link

Thanks for the clarification. I copied/pasted this from the main loop:

int t = 0;
while (true) // the program should stay in this loop even when minimized
{
	if constexpr (LOG) { msg << "A start"; msg.out(LOG_DATA); }
	if (!shouldRender)
	{
		//if constexpr (LOG) { msg << "Polling events..."; msg.out(LOG_DATA); }
		while (SDL_PollEvent(&events.sdlEvent) == 1)
		{

		}
		//if constexpr (LOG) { msg << "Finished polling events..."; msg.out(LOG_DATA); }
	}
	while (shouldRender)
	{
		//if constexpr (LOG) { msg << "B start"; msg.out(LOG_DATA); }

		//if constexpr (LOG) { msg << "Polling events..."; msg.out(LOG_DATA); }
		while (SDL_PollEvent(&events.sdlEvent) == 1)
		{

		}
		//if constexpr (LOG) { msg << "Finished polling events..."; msg.out(LOG_DATA); }

        ++t;
        SDL_Delay(200);
        if constexpr (LOG) { msg << "Run count (" << t << ")"; msg.out(LOG_DATA); }

    if (t == 30 && shouldRender)
    {
            // bind the framebuffer here
        }
        else if (false)
		{
		    if constexpr (LOG) { msg << "Shouldn't render"; msg.out(LOG_DATA); }
			auto a = 2;
			auto b = a;
		}
		//if constexpr (LOG) { msg << "B end"; msg.out(LOG_DATA); }
	}
	if constexpr (LOG) { msg << "A end"; msg.out(LOG_DATA); }
}

@ell1e
Copy link
Contributor

ell1e commented Mar 3, 2021

Yes, that one looks better (if you keep any GL stuff to the bind the framebuffer here section). Does that help?

@samgubernick
Copy link

It's possible the cause of this came from when I first created the context:

if (SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, SDL_ENABLE) != SUCCESS)
{ // use srgb framebuffer for gamma correction
	if constexpr (LOG) { msg << "SDL error: " << SDL_GetError(); msg.err(LOG_DATA); }
}

That works fine when I create the context the first time, but when I minimize it and then reenter it, the Adreno drivers fail. Commenting that line out has eliminated all of the Adreno errors in the log from my test code--I'll try reloading the whole game to see if it works correctly.

@1bsyl
Copy link
Contributor

1bsyl commented Mar 9, 2021

Maybe you're receiving the event: SDL_RENDER_DEVICE_RESET
see: https://github.com/libsdl-org/SDL/blob/main/docs/README-android.md

@Wohlstand
Copy link
Contributor

Wohlstand commented Mar 9, 2021

In my case, I do receive the SDL_RENDER_DEVICE_RESET event prior to the case when I can't render at all. However, an attempt to re-create the context caused me the "context already exists" error, even I'll destroy the previous context 🤔 Later I'll retry to reproduce this again to confirm.

@1bsyl
Copy link
Contributor

1bsyl commented Mar 9, 2021

This is what happens when the event is set:
https://github.com/libsdl-org/SDL/blob/main/src/video/android/SDL_androidevents.c#L68
the device is re-created internally, so you probably only need to get it again.

@Wohlstand
Copy link
Contributor

Wohlstand commented Mar 9, 2021

need to get it again.

Stupid question: how?

In my case, I do use SDL_Renderer API, not the direct OpenGL use

@1bsyl
Copy link
Contributor

1bsyl commented Mar 9, 2021

SDL_GL_GetCurrentContext

@Wohlstand
Copy link
Contributor

SDL_GL_GetCurrentContext

That for the case when I do use OpenGL directly, I do use the SDL_Renderer context created by the SDL_CreateRenderer() call with the SDL_RENDERER_ACCELERATED flag. I asked how to revive the context at the SDL_Renderer's backend that I use at my side.

@Wohlstand
Copy link
Contributor

Oh, should the SDL_GetRenderer() work?

@1bsyl
Copy link
Contributor

1bsyl commented Mar 9, 2021

if you use SDL_Renderer then you only need to delete, then re-create all the texture you use

@Wohlstand
Copy link
Contributor

I did, but the EGL_BAD_ACCESS error had occurred in just an attempt to create the texture, and there are even non-textured primitives (color-filled rectangles) that won't render after the RESET event came.

@1bsyl
Copy link
Contributor

1bsyl commented Mar 9, 2021

Then you've something wrong. Maybe when calling function while in background. I'd suggest to re-start with a plain hello world to figure out.

@Wohlstand
Copy link
Contributor

Then you've something wrong. Maybe when calling function while in background. I'd suggest to re-start with a plain hello world to figure out.

I did the wide check, and the application never calls any render functions while in the background, even I made the protections from off them to avoid any possible render call done in the background. But you right, I should compose the simple application and verify the work of it. In another case, the SDL activity isn't the main activity: it gets open by the separated launcher Java activity (I made it to let the user setup the game before starting the actual game). I'll try to make a similar case but in a form of a dummy application that will be as simple as possible.

@1bsyl
Copy link
Contributor

1bsyl commented Mar 10, 2021

Just wondering:
which android device do you use ? does this happen with any device ?
do you use latest SDL2 source ?

If you are not changing the BLOCK_ON_PAUSE feature, you shouldn't even care about using or not the renderer, because the pollEvent call is blocking when the app is in background.

Another thing: can you try to comment out in SDL_renderer, the is a call to "glFinish()" (src/render/opengles2/SDL_render_gles2.c), which look suspicious because it's called from another thread than the rendering thread.

@Wohlstand
Copy link
Contributor

Wohlstand commented Mar 10, 2021

which android device do you use ? does this happen with any device ?

I have multiple different devices:

  • My SGN9 with Android 10
  • My Archos 70c tablet with Android 5.1
  • My friend's Retroid Pocket thing that using the Android 8
  • My friend's Huawei MediaPad M6 that using the Android 9

All those devices have the same behavior

do you use latest SDL2 source ?

I do use the 2.0.14 release yet, do you suggest to me try the latest dev version to verify the work?

BLOCK_ON_PAUSE

I never touched this hint, it works at me in its default value.

Another thing: can you try to comment out in SDL_renderer, the is a call to "glFinish()" (src/render/opengles2/SDL_render_gles2.c), which look suspicious because it's called from another thread than the rendering thread.

I'll need to try that...

EDIT: Added 4'th phone

@1bsyl
Copy link
Contributor

1bsyl commented Mar 10, 2021

Since there is a recent device in the list, I think this ok.
2.0.14 .. is also recent and should be fine.

  • you can try the glFinish, but I don't think this is that, otherwise I would also have such black screen.
  • there is a "debug_enabled" that you can set to SDL_TRUE in opengles2 renderer ...
  • maybe this helps, before creating the window:
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
  • reading the back-log, just to make sure:
    in your main look, where you check the events, you must poll all the events. I mean to deplete the event queue.

@samgubernick
Copy link

A quick update: SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, SDL_ENABLE) was the exact cause of my problem. After removing that (even though SDL's able to create the initial context correctly), I'm able to load data back into the new GL context and run the shader programs. Any chance you're doing something similar?

@Wohlstand
Copy link
Contributor

I should try the same...

@Wohlstand
Copy link
Contributor

Nope, with the case of SDL Renderer, that didn't help. I have another idea to try. On the side, I have another SDL2-game that doesn't have this bug, I gonna upgrade the SDL2 on it and try to see its behavior... The one difference that I build the SDL2 with the ANDROID.mk via the overlay made at CMake, at this game I do build SDL2 with the CMake build directly.

@Wohlstand
Copy link
Contributor

Okay, just now I made the experiment on building the SDL2 through "Android.mk" file, and the result is still be same faulty... However, the difference:

  • at my other game I do start the SDL2 activity directly
  • at this application I do start the Java-side hand-made activity called "launcher", and then I do start the SDK Activity by the next code:
        Intent myIntent = new Intent(Launcher.this, thextechActivity.class);
        myIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        Launcher.this.startActivity(myIntent);
        Launcher.this.finish();

and, when I trying to resume that activity, I get the fault

@Wohlstand
Copy link
Contributor

Probably I'll try to compose the simple unit test application that draws simple primitives to verify the thing at all...

@Wohlstand
Copy link
Contributor

Okay, just now I made a simple application and I placed it here:
https://github.com/Wohlstand/SDL2-Android-Example

so, I had to implement the same rendering sequence as my game, I did next experiments:

  • Simply render color squares: no bug
  • Try to draw the same squares on a texture: BUG APPEARS HERE!
  • Okay, I also did render of the third square, but drawn on the screen itself, not on the textured surface: BUG STILL APPEARS HERE, and there were no textures loaded, except one created from the scratch for the target rendering.

HOWEVER:

After some tweaks, I finally found the reason for the mess, look:

At my game I did the next:

  • After the game loading and initialization of everything, I set SDL_SetRenderTarget(m_gRenderer, m_tBuffer);
  • Then I draw all stuff on the texture attached
  • Then, I do SDL_SetRenderTarget(m_gRenderer, nullptr); to draw the texture and on-screen buttons.
  • Before doing the SDL_RenderPresent(m_gRenderer);, I did SDL_SetRenderTarget(m_gRenderer, m_tBuffer);
  • Then, wait, and go for the next loop

So, when I accidentally did the experiment, and I had to change the way of texture target:

  • After the game loading and initialization of everything, I set SDL_SetRenderTarget(m_gRenderer, m_tBuffer);, then, after I'll clean the surface, I do SDL_SetRenderTarget(m_gRenderer, nullptr); at final
  • At begin of render function, I call SDL_SetRenderTarget(m_gRenderer, m_tBuffer);
  • Then I draw all stuff on the texture attached
  • Then, I do SDL_SetRenderTarget(m_gRenderer, nullptr); to draw the texture and on-screen buttons.
  • Then, I do the SDL_RenderPresent(m_gRenderer);
  • Then, wait, and go for the next loop

The way I made had fixed the bug at my demo application, so, gonna try the game itself!

Conclusion: this was a VERY WEIRD bug, and it was MINE, because I messed up with the texture target, and didn't guess that until I built the simple demo application by myself... If the game will get this bug solved, I'll say, please add the note about the rendering into the texture: unplug any texture targets until to do the SDL_RenderPresent(m_gRenderer); and never keep the texture target be say forever, plug the texture target in only condition when it's really needed!

@Wohlstand
Copy link
Contributor

P.S. To replay the invalid behavior, please comment the #define TEXTURE_TARGET_ORDER_VALID line at main.cpp, and the buggy behavior will be, otherwise keep it to see the proper work.

@Wohlstand
Copy link
Contributor

Okay, so, I refactored my game side to make sure the screen target will be always kept before and after repainting and do set the texture target in the only condition when the in-game world gets rendered.

@baidwwy
Copy link

baidwwy commented Jan 8, 2022

me too
I'm not using textures, just fillrect
SDL_RenderClear returns not 0
GetError
Unable to make EGL context current (call to eglMakeCurrent failed, reporting an error of EGL_BAD_ACCESS)

SDL 2.0.18
android 7.1.1

sorry i don't speak english, this is from google translate

@baidwwy
Copy link

baidwwy commented Jan 8, 2022

SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1");
after
looks like normal

@crudelios
Copy link
Contributor

After two days scratching my head I found this issue and I can confirm that removing the render target does fix the problem.

Note that instead of constantly swapping the render target during normal texture drawing, an alternative to prevent the black screen is simply to set the render target to NULL on the WILL_ENTER_BACKGROUND event, then restore the render target on the DID_ENTER_FOREGROUND event.

@1bsyl
Copy link
Contributor

1bsyl commented Mar 14, 2022

Thanks, I've added a comment,
let's say this fix the issue ..

@1bsyl 1bsyl closed this as completed Mar 14, 2022
madebr pushed a commit to madebr/SDL that referenced this issue Mar 19, 2023
* Make Win32 fullscreen and borderless windows minimizable

* reduced a few ifdefs, fixed an unused warning if built w/o SDL_HAVE_YUV.

* rawinput: Fix double detection of gamepads on some 3rd party X360 wireless receivers

The name that the Raw Input joystick driver pulls from the HID stack comes
from USB string descriptors contained on the device. For official wireless
receivers, this always contains "Xbox 360 Wireless Receiver for Windows"
which matches the friendly name that WGI provides.

3rd party Xbox 360 wireless receivers may have different strings in their
USB string descriptors (one uses "XBOX 360 For Windows" instead). This
fails to match WGI's name and causes Raw Input and WGI to both report the
same gamepad.

Since wireless Xbox 360 controllers seem to have a consistent VID/PID
regardless of the adapter enumerating them, we can also match on that to
catch these.

The duplicate case reported to me was:
Controller (XBOX 360 For Windows) - 030000005e040000a102000000007200
Xbox 360 Wireless Receiver for Windows - 030000005e0400000000000000007701

* Makefile.os2: fixed setting OS2DEBUG in CFLAGS.

* minor os/2 clean-ups.

* emscripten: Don't prevent default on filtered key events

* IME Composition Truncation + SDL_IsTextInputShown + SDL_ClearComposition (libsdl-org#5398)

* Fixes for IME Composition Truncation + Addition of SDL_ClearComposition, SDL_IsTextInputShown

* Fixed: Documentation and code style issues raised during code review.

* Sync wiki -> header

* SDL_windowskeyboard.c (IME_IsTextInputShown): remove unused local vars.

* cmake: Make test code use proper C main functions.

Fixes libsdl-org#5021.

* SDL_blit_N.c: removed duplicated const (fixes bug libsdl-org#5401)

* SDL_endian.h: use endian predefs from newer gcc and clang versions.

Closes: libsdl-org#5403

* Android: add comment to set render target to NULL when going to background (bug libsdl-org#4041)

* Make SDL_VIDEO_OPENGL_EGL optional on Android

* Fixed: Incorrect assumption that mouse button is released when window is allocated

* SDL_audiocvt.c: Don't byteswap 8-bit streams

Otherwise, this results an assert on big endian machines when attenpting to use SDL_LoadWAV_RW function to load 8-bit WAV files.

* METAL: clip rect w/h must be <= render pass

* Fix name of macOS platform and link to README file

* Fixed bug libsdl-org#2199: make SDL_blit_slow handles SDL_PIXELFORMAT_ARGB2101010, storing as RGBA

* Fixed bug libsdl-org#2691 - re-enable surface_testCompleteSurfaceConversion for ARGB2101010

* INSTALL.txt: Say "macOS" instead of "Mac OS X" in modern times.

Reference Issue libsdl-org#5407.

* SDL_windowskeyboard.c: fix build with SDL_DISABLE_WINDOWS_IME defined.

Fixes libsdl-org#5408

* minor whitespace cleanup.

* SDL_audiocvt.c: minor cleanup.

* Adjust gradle dependencies to enable the build to position assets for the APK

* emscripten: obey enable-misc/SDL_MISC settings

* minor optimization (SDL_audiocvt.c)

* imported two libtool mainstream commits 28fbcb6a and b55b1cc8

* Remove 'reserved identifier' warning

* SDL_blit_slow: remove one nested 'if()' because of ARGB2101010 handling

* SDL_triangle_blit_slow: sync code with SDL_blit_slow to handle ARGB2101010

* SDL_GetBasePath() fixes for OS/2

* use _Static_assert for SDL_COMPILE_TIME_ASSERT(), when available

* testplatform.c: move static asserts out of TestTypes().

* simplify SetDSerror
- no need to keep the error in a static variable
- always print the error code
- reduce the required stack-size
- reduce the number of snprintf calls (and code size)

* Fixed build when events are disabled

Fixes libsdl-org#5413

* Removed problematic call to ISensor_SetEventSink()

Fixes libsdl-org#5288

* Fixed warnings when building with cygwin

Fixes libsdl-org#5025

* SDL_cocoawindow.m: update fullscreen toggle when SDL_SetWindowResizable called

* Ignore unknown WM_SIZE types.

According to MSDN, we can also get SIZE_MAXHIDE and SIZE_MAXSHOW,
based on state changes to other windows. It's not clear under
what circumstances this will happen (I saw some docs indicating
it may require multiple application windows), but it doesn't seem
right to treat them as RESTORED.

* Fix relative mouse input for Unvanquished (unvanquished.net)

Here's an IRC dump that hopefully explains the issue this fixes:

> I'm debugging something odd where, for a libre game,
  unvanquished.net (a FPS), relative mouse input in fullscreen is
  buggy
> it's like, working mostly ok, but it has a weird
  performance/cleanup bug
> after some time in relative mouse input mode, some time as low
  as 15s, usually more, the SDL sends A LOT of relative mouse
  input per frame
> almost all of which have xrel==0 && yrel==0
> by A LOT, I mean that after ~1min, it's usually in the
  thousands per frame
> each frame, a while ( SDL_PollEvent( &e)) loop reads the
  inputs, but it seems SDL is not clearing the list.
> one way to clear the list is to open the in-game console or
  menu, which switches the input mode to absolute, then close it
  which gets a working relative input mode (for some time at least)
> I've shown the issue to be present with SDL2.0.20 but not with
  2.0.14 on my system
> some other players on Arch Linux (SDL2.0.20) report a possibly
  related issue, where some keys seem to be pressed at random
> I've did some bisection on SDL master, and I've found that
  there are actually two commits involved, one breaking it
  totally (no input at all), and one fixing it partially (with
  the problem described above)

First related commit that breaks it totally:

	commit 82793ac
	Author: Sam Lantinga <slouken@libsdl.org>
	Date:   Thu Oct 14 14:26:21 2021 -0700

	    Fixed mouse warping while in relative mode

	    We should get a mouse event with an absolute position and no relative motion and shouldn't change the OS cursor position at all

Second related commit, that halfway fixes it:

	commit 31f8c3e
	Author: Sam Lantinga <slouken@libsdl.org>
	Date:   Thu Jan 6 11:27:44 2022 -0800

	    Fixed event pump starvation if the application frequently pushes its own events

Reverting the first commit did fix the issue for me, but would
probably reintroduce the bug it was fixing(?). This patch should
fix it for everyone hopefully.

DaemonEngine/Daemon#600 is the upstream
bug, and contains some early investigation.

* Don't warn if anyone peeps for events after quitting the event subsystem

Fixes libsdl-org#5013

* video: Add a hint to allow Vulkan surfaces on foreign windows

* Fixed compile warning and comment typo

* Added a hint to mark a foreign window as usable with OpenGL

Fixes libsdl-org#2942

* When updating grab state, only activate windows that are grabbed, fullscreen, and shown.

Fixes libsdl-org#5371

* Try not forcing activation when grabbing the mouse in fullscreen windows

* Workaround for bug in Microsoft WGI support

Fixes libsdl-org#5270

* Use SDL_Log instead of printf

* Added a hint to capture the mouse when mouse buttons are pressed, defaulting on

Fixes libsdl-org#5301

* Fixed memory leak in WIN_CreateBlankCursor()

* Fixed freeing the Windows blank cursor

* Start rumbling once a raw input controller has been correlated

Fixes libsdl-org#5351

* Modern CMake doesn't need "LANGUAGE C" for Objective-C

CMake 3.19 fails to compile Objective-C with that property set

Fixes libsdl-org#5418

* Added the hint SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, controlling whether the mouse should be constrained to the center of the window or the whole window in relative mode.

For further info about the pros and cons, check out the discussion in libsdl-org#5271

* Added a note that SDL_RenderReadPixels() should be called before SDL_RenderPresent()

* Sync wiki -> header

* Fix "SDL_TRUE is not defined" runtime error for emscripten.

* SDL_UnionRect: If both rects are empty, zero out the result struct.

* SDL_Rect: minor code cleanups.

* SDL_Rect: Added floating point versions of all the rectangle APIs.

Fixes libsdl-org#5110.

* Fix "SDL_FALSE is not defined" runtime error for emscripten.

* Fixed Android crash when SDL_HIDAPI_DISABLED set to 1

The Java code needs the native functions to be implemented, even if they're not surfaced via the C API.

Fixes libsdl-org#5326

* Relative mouse mode is tied to the window with keyboard focus

This isn't obvious, but makes sense when thinking about how games actually use it. This is also in line with how Windows mouse relative mode is implemented.

Fixes libsdl-org#5340

* Fixed parameter operation ordering for ease of reading

* audio: Set error message on dsp init failure.

if SDL_EnumUnixAudioDevices() fails to find any devices,
set an error message on the exit path. Without this,
SDL_Init() could fail without any message available
in SDL_GetError().

* Sync wiki -> headers.

* Fixed bug libsdl-org#2426 - SDL_RenderReadPixels result is unspecified and fails testautomation
Call SDL_RenderPresent after calling SDL_RenderReadPixels.
From "include/SDL_render.h":
"If you're using this on the main rendering target, it should be called after rendering and before SDL_RenderPresent()."

* Fixed bug libsdl-org#2962 - when SDL_RenderReadPixels format = 0, used format of the target texture
include/SDL_render.h, format:
"0 to use the format of the rendering target "

* blit-auto optimizations

* regenerated SDL_blit_auto.c.

* Don't try to hide foreign windows when destroying the SDL window representation

Fixes libsdl-org#5432

* hints: Added SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE.

Fixes libsdl-org#2349.

* SDL_render.c: remove unsused case, since renderer target has been forced to NULL previously (see bug libsdl-org#4213)

* Add mapping for Logitech Precision Gamepad

* Also fix Android crash when SDL_HIDAPI_DISABLED set to 1

The Java code needs the native functions to be implemented, even if
they're not surfaced via the C API, therefore, a stub version of
functions were made only to the purpose of "fill the gaps" when
SDL_HIDAPI_DISABLED set to 1.

* use != '\0' comparison instead of SDL_strlen in the functions of SDL_getenv

* make SDL_strcasecmp standard compliant

* optimize the SDL_str(case)cmp functions

* Avoid trying to use texture framebuffers on emscripten

* stdlib: just cast iconv()'s 2nd arg to void *.

This makes the compiler happy (enough) regardless of whether the C runtime
headers think this argument should be const or not.

Fixes libsdl-org#4966.

* Fixed freeing a constant string (thanks @gnrlwart)

* Android: add SetWindowResizable() implementation
- which also enable/disable the orientation lock status.

This is only provided when the window is not SDL_WINDOW_FULLSCREEN (see SDL_video.c).
Final orientation also depends on SDL_HINT_ORIENTATIONS.

* Video: do not use hardcoded VIVANTE and VDK libraries

* Add SDL_RenderGetWindow() API to get the window associated with a renderer (libsdl-org#5440)

Add SDL_RenderGetWindow() API to get the window associated with a renderer

* Sync wiki -> header

* wayland: Bump minimum requirement to wayland-client 1.18 or newer

* wayland: The rest of the wayland-client 1.18 requirement... Git, please

* wayland: Basic support for zwp_tablet_*v2 protocol

* video: wayland: Use xdg-output for retrieving the desktop dimensions

Using wl-output to get the desktop display dimensions and dividing by the integer scale factor will not return the correct result when using a desktop with fractional scaling (e.g. a 3840x2160 display at 150% will incorrectly report the scaled desktop area as 1920x1080 instead of 2560x1440).  Use the xdg-output protocol, if available, to retrieve the correct desktop dimensions and offset.

Versions 1 through 3 of the protocol are supported.

* Vita: support audio format fallback

* Vita: add native YUV textures support.

* Fail if texture init fails.
* Refactor and cleanup.

* static analysis: Fixed several complaints from codechecker.

There are still some pending Objective-C specific issues.

Reference issue libsdl-org#4600.

* add some Thrustmaster wheels

* Fixed bug libsdl-org#1650: X11 doesn't set KMOD_NUM and KMOD_CAPS to system state

* x11: Minor cleanups and corrections in X11_ShowMessageBox.

Reference issue libsdl-org#3254.

* Avoid depending on libwayland 1.20 unnecessarily

When using shared linking (linking in the normal way with
-lwayland-client) rather than loading Wayland libraries dynamically at
runtime, listing symbols that don't exist in the current version results
in a build failure. We don't actually call wl_proxy_marshal_flags() or
wl_proxy_marshal_array_flags() directly; the reason we need them is
that they're called by the code generated by wayland-scanner >= 1.20.

If we're building against an older Wayland library, then we'll have its
corresponding version of wayland-scanner (mismatched versions are not
supported), so we won't need those two symbols, and can avoid generating
a dependency on them.

Conversely, if we're building against a newer Wayland library, the
generated code will call them unconditionally, so we cannot treat them as
optional and gracefully fall back: that would result in a crash. Instead,
treat them as a mandatory part of the Wayland library, so that if they
are not found at runtime, we can fall back to X11 without crashing.

libwayland 1.18 is in several LTS distributions (Ubuntu 20.04,
Debian 11, RHEL 8) so avoiding a hard dependency on 1.20 is quite
useful.

Signed-off-by: Simon McVittie <smcv@collabora.com>
Resolves: libsdl-org#5376

* wayland: Don't overwrite error message from SDL_EGL_CreateSurface

SDL_EGL_CreateSurface sets a more specific error message, so overwriting
it would lose information.

Signed-off-by: Simon McVittie <smcv@collabora.com>

* wayland: Minor fixes for old compilers

* wayland: Remove some now-redundant casts

* iOS >= 10.0 silence GLes deprecation warnings

* wayland: Add an xkb_keysym_t->SDL_Keycode mapping for backspace

* wayland: Enforce text capitalization manually, for remapped keymods

* wayland: Use xkb_keymap_mod to set mod state

* YUV: fix invalid read on last line when converting from SDL_PIXELFORMAT_YUY2 (see bug libsdl-org#4841)

* Vita: Fix NULL-pointer dereference

* wayland: Optimize keyboard_handle_modifiers.

1. Mod index values are (mostly) constant, so can be done with xkb_state_new
2. Mods can change without the group changing, avoid remap events if possible

Lastly, as a bonus, I added braces to the locale check, because I was nearby.

* Initialize compile status variable and check also program link status

* testshader: use SDL_malloc instead of SDL_stack_alloc.

* README-windows: Convert to actual Markdown text.

* README-windows: Wordwrap text file.

This makes for easier reading outside of a formal Markdown viewer.

* README-windows: Notes on building with Visual Studio/LLVM.

Fixes libsdl-org#5186.

* wayland: Work around a GNOME xdg_output scaling issue

* wayland: Avoid overwriting xdg_output position with wl_output position

* wayland: Relax the check for mismatching output scales

* configure: improvements to libdecor discovery :

use PKG_CHECK_MODULES, and use DECOR_LIB with find_lib.
Closes: libsdl-org#5460

Co-authored-by: Trigan2025 <trigan2025@hotmail.fr>

* direct3d: Implement missing blend operations.

This is only for Direct3D 9; Direct3D 11 already had this implemented.

Fixes libsdl-org#5375.

* Remove URLs from markdown headers in README-visualc.md

* Clean up formatting in README-directfb.md

* testgles2: Call correct function to get shader info log and add link status checking

* Sync wiki -> header

* Sync wiki -> header

* Vita: Use preallocated memory pool for textures

* Sync wiki -> header

* Correct default structure packing on Windows ARM64

See issue libsdl-org#5454 for details

* Fix potential memory leak in QueueCmdFillRects

* Vita: add hint to select which touchpad generates mouse events

* emscriptenaudio: proxy calls to main thread

* video: wayland: Use wp-viewporter for fullscreen with non-native resolutions

Wayland doesn't support mode switching, however Wayland compositors can support the wp_viewporter protocol, which allows for the mapping of arbitrarily sized buffer regions to output surfaces.  Use this functionality, when available, for fullscreen output when using non-native display modes and/or when dealing with scaled desktops, which can incur significant overdraw without this extension.

This also allows for the exposure of arbitrarily sized, emulated display modes, which can be useful for legacy compatability.

* wayland: Minor fix for old compilers

* video: wayland: Expose more resolutions for mode emulation

Expose as many emulated display modes as possible.  They will currently display stretched to the display's native desktop aspect, but if an application requires a hardcoded resolution, it will work at minimum.

Aside from the change in the emulated display mode list, the Wayland event handling code had to be updated to support separate scaling for the x and y axes, as square pixels are no longer guaranteed.

* video: wayland: Use viewports for non-fullscreen windows with fractional scaling

Use viewports for non-fullscreen windows when the desktop uses fractional scaling and the window is flagged as DPI-aware to provide a backbuffer mapped as close to 1:1 output as possible.  In the cases of odd window sizes the backbuffer may be a pixel off of scaling perfectly into the window size due to its scaled size being rounded off, but a minute amount of scaling during output is likely preferable to the large amounts of overdraw needed with integer scaled buffers.

* Vita: add audio capture support

* Vita: add SDL_GetPreferredLocales support

* stdinc: SDL_COMPILE_TIME_ASSERT defines shouldn't have a semicolon.

* x11: Catch X11 errors in X11_SetWindowPosition and X11_SetWindowSize.

The functions can go south if other operations are in progress, like
X11_SetWindowBordered, which might be doing something traumatic behind the
scenes of the window manager.

We can't make these tasks totally synchronous, which would fix the problem,
because not only can the window manager block however long it wants, it might
also decide to deny our requests without any notification, so we'd be waiting
forever for a window change that isn't coming.  :(

Fixes libsdl-org#5274.

* joystick: Fix rumble issues on PS5 HIDAPI controllers

We were returning the report size from HIDAPI_DriverPS5_RumbleJoystick() rather
than 0 upon success, causing SDL_JoystickRumble() (and callers) to think that
rumbling failed.

This didn't cause major problems until 1868c5b, when it started preventing
rumble state from being persisted in the joystick core, even though it was
successfully sent to the hardware.

This led to all sorts of strangeness, including broken rumble duration and
attempts to stop rumble being discarded.

* Desktop OpenGL 1.X/2.X PSVita Support

* Update README-vita.md

* Cleanup Spaces

* Vita: PVROGL: fix indentation and ifdef guards

* Vita: fix readme

* x11: Ignore BadValue for extremely small XRRSetScreenSize resolutions.

Reference Issue libsdl-org#4840.

* SDL2 thread proxying fixes

This PR uses new APIs added in [emscripten-core/emscripten#9336](emscripten-core/emscripten#9336)
to improve compatibility with USE_PTHREADS=1.

Original PR: emscripten-ports/SDL2#127
By: @jakogut
Reviewed by: Daft-Freak

* emscriptenmouse: remove old extra `_INT`

* fix formatting and cast warnings

Co-authored-by: Charlie Birks <charlie@daftgames.net>

* emscriptenmouse: remove useless return statement

* emscriptenframebuffer: fix formatting

* emscripten: Proxy SDL_GetUsableDisplayBounds to the main thread.

* Fixed loading 32-bit BMP files

* Move SDL_List functions to SDL_list.c to avoid more merge with eventual PR

* Add SDL_list.c/h

* Add SDL_list to macosx xcode

* Use RoInitialize/RoUninitialize for Windows.Gaming.Input

Thanks @walbourn!

Fixes libsdl-org#5270

* fix build against older SDKs after commit 8ebef12.

* check for HAVE_ROAPI_H in cmake and autotools, and

update SDL_config_windows.h and SDL_config_winrt.h

* attempt to fix uwp build

* render: Fix setting the scale mode for non-native textures

* Visualise scroll wheel events in testmouse

* Minor cleanup

* x11: Don't unload libGL.so to prevent a crash in XCloseDisplay()

libGL.so may register callbacks that can be invoked upon XCloseDisplay().
If XCloseDisplay() is called after libGL.so is unloaded, the callback pointer
will point at freed memory and invoking it will crash.

The texture framebuffer check optimized out in f37e4a9 was causing libGL.so to
never be unloaded as a side-effect. Skipping it exposed this bug by allowing
libGL.so to actually unload.

* x11: Wait a bit to see if window pos changes when changing fullscreen.

Helps prevent window from moving to 0,0 when leaving fullscreen.

Fixes libsdl-org#4749.

* testgles2: Fix buffer object sizes

* Added a define VERBOSE_MOTION_EVENTS to show mouse and finger motion events

* Handle interaction between auto capture and the SDL_CaptureMouse() API

Fixes libsdl-org#5457

* Minor cleanup

* IBus should use ev keycode instead of X keycode

See: https://github.com/ibus/ibus/blob/5a455b1ead5d72483952356ddfe25b9e3b637e6f/client/gtk2/ibusimcontext.c#L468

* Ignore focus change messages that contradict GetForegroundWindow.

On Wine, when a window is programmatically minimized in response
to losing focus, we receive a WM_ACTIVATE for the deactivation,
but GetForegroundWindow still indicates that our window is focused.
This causes an incorrect SDL_WINDOWEVENT_FOCUS_GAINED.

This is probably a Wine bug, but it may take a while to fix and
then for the fix to make its way to users.

* Don't resize fullscreen windows when hiding or minimizing them (thanks @madewokherd!)

This has the benefit of window previews (mousing over the icon) having the correct size and contents.

Fixes libsdl-org#5320

* x11: when waiting on fullscreen changes, not window position _and_ size.

This makes sure the window doesn't have outdated values if you try to access
them (or call something that does, like SDL_SetWindowMinimumSize).

Fixes libsdl-org#5233.

* Send key release event to input method. (libsdl-org#5281)

Co-authored-by: Ethan Lee <flibitijibibo@gmail.com>

* Cache the fact that a device didn't look like a joystick

Fixes libsdl-org#5211

* x11: Try to keep SDL_WINDOW_FULLSCREEN* in sync with window manager.

So if Gnome/KDE/etc have a keyboard shortcut or titlebar decoration to
make any window go fullscreen (with the _NET_WM_FULLSCREEN flag on the
_NET_WM_STATE property), we update the SDL window flag.

Fixes libsdl-org#5390.

* Sort controllers by the js* index on Linux

Also fixed the initial scan to directly scan devices instead of using
udev so they can be sorted, as intended.

Fixes libsdl-org#4688

* wayland: Evaluate WINDOWPOS_CENTERED_DISPLAY for move events

Partially fixes the mouse cursor in UE5 editor. Imperfect because UE5 uses window position and global mouse state to get position, but of course we don't have global mouse and this is just to get the right display index so this still fails overall. We really need to make global mouse support a feature query...

* wayland: Try to detach at the beginning of ShowWindow, just in case.

It's possible that an external component (probably a GL/VK context) committed, so we need to cover our bases and detach in both HideWindow and ShowWindow.

Fixes a crash in UE5 editor's pop-ups.

* wayland: Add a bug link for the detach FIXME

* wayland: Pin the fake window position at (0, 0).

I kind of thought it'd be nice to have it in the center, but this is an issue
for applications that still assume global mouse and window positions are
accessible. For example, this fixes cursor offset issues in UE5.

* Makefile.in: added missing CXX variable.

* Compile with recursive mutexes for emscripten

Emscripten actually does support recursive mutexes, so no need to use SDL's fake recursive code.

Background: libsdl-org#5428, libsdl-org#5479

* x11: Treat WM setting the window "fullscreen" like FULLSCREEN_DESKTOP.

Fixes libsdl-org#5390.

* Only update modifier state for keys that are pressed in another application

Fixes libsdl-org#4432

* minor adjustment to os/2 watcom makefile

* Make sure the UIKit message box is being handled on the main thread

Potentially fixes libsdl-org#4865

* render: Update the size/scale/viewport on moves, in addition to resizes.

For OpenGL this means resetting the viewport state shadowing flag too.

Fixes libsdl-org#1504

* direct3d11: Set the swapchain target immediately after creating it.

Fixes libsdl-org#4782

* video: wayland: Set the surface damage region when using fullscreen viewports

When using emulated display modes, the output size is often larger than the drawable buffer.  As the surface damage region is automatically calculated from the smaller drawable buffer size, the damage region needs to be manually set to cover the entire viewport region or visual repaint artifacts can result.

* Fixed typo

* Updated the patch notes with API changes for 2.0.22

* Update version to 2.0.22 for release

* audio: SDL_ConvertStereoToMono_SSE3 missed an unaligned load.

* UWP: Require Windows 10 16299 or newer.

This is required to build with WGI support. Thanks for @FrozenChameleon for the fix!

Fixes libsdl-org#5504

* Vita: fix VIDEO_VITA_PVR flag

* fix os2 timer in fallback mode

* testevdev: Recognise touchpads as such

At the time I contributed this unit test, SDL didn't understand Linux
touchpads, but now it does.

Fixes: 373216a "Added support for touchpads in the Linux evdev code"
Signed-off-by: Simon McVittie <smcv@collabora.com>

* testevdev: Adapt to a broader definition of keyboards

At the time I contributed this unit test, SDL had a relatively narrow
definition of what is a keyboard, approximately matching udev
ID_INPUT_KEYBOARD. Now it uses the equivalent of udev ID_INPUT_KEY,
which matches anything with keyboard keys, and not just reasonably
complete alphanumeric keyboards.

Fixes: 040bd7a "Fix udev not detecting ID_INPUT_KEY devices when udev is not running"
Signed-off-by: Simon McVittie <smcv@collabora.com>

* test: Copy utf8.txt to build directory

testiconv wants this.

Signed-off-by: Simon McVittie <smcv@collabora.com>

* Initialise scandir argument

'scandir' does not initialise 'entries' on error

* Wayland: Add SDL_HINT_VIDEO_WAYLAND_PREFER_LIBDECOR

This hint allows libdecor to be used even when xdg-decoration is
available. It's mostly useful for debugging libdecor, but could in
theory be used by applications which want to (for example) bundle their
own libdecor plugins.

* WhatsNew.txt: Added SDL_HINT_VIDEO_WAYLAND_PREFER_LIBDECOR

* WhatsNew.txt: Document new dependency on libwayland-client 1.18.0

Signed-off-by: Simon McVittie <smcv@collabora.com>

* CI: update os2.yml to use open-watcom/setup-watcom

also remove os2-buildbot.sh -- not needed anymore.

* avoid NullPointer in SDL_GL_MakeCurrent

* avoid NullPointer in SDL_GetRenderTarget

* video: Wayland: Clamp fullscreen window dimensions to desktop

A scaled fullscreen window may exceed the bounds of the desktop.  Clamp the window size to the desktop dimensions in fullscreen mode.

* video: Wayland: Always round scaled pointer coordinates down

Rounding up can cause the pointer coordinates to exceed the window boundaries at the right and bottom edges.

* x11: Update the display when the WM changes a window's fullscreen state.

Fixes libsdl-org#5390.

* Added CMake option to disable the installer

* cmake: Fixed indenting and some oldschool `endif(TEXT)` things.

* hints: Make SDL_VIDEODRIVER and SDL_AUDIODRIVER formal hints.

They were just environment variables before.

Fixes libsdl-org#5528.

* Revert "video: Prefer Wayland over X11"

This reverts commit 8ceba27.

SDL Wayland support is stable, but there are a number of issues with third-party software (NVIDIA drivers, libwayland event overflow, libdecor not handling plugin load failures, Steam overlay not working with Wayland, etc.) that make it better to default to X11 at this time.

Games which would like to prefer wayland when available can use the following code before SDL_Init():
	SDL_SetHint(SDL_HINT_VIDEODRIVER, "wayland,x11");

Fixes libsdl-org#5527

* Certain audio drivers, like the RME "Pro" Audio driver, have resampling quality issues when using WASAPI.

We'll use SDL's resampling algorithm so we have consistent quality between platforms and drivers.

Fixes libsdl-org#5538

* wayland: Add support for TOOLTIP/POPUP_MENU

* video: Don't minimize fullscreen windows on focus loss by default when mode switching is disabled

When mode switching is disabled in a video backend, fullscreen windows are basically just fullscreen desktop windows with different internal scaling.  As no mode switching occurs, there's no need to minimize them on focus loss by default.  This can still be overridden by explicitly setting the internal hint for minimizing on focus loss.

This has the side effect of fixing a bug on GNOME, where, when a fullscreen Wayland window has it's focus lost and restored via alt+tab followed by switching back to windowed mode, the top portion of the window won't end up being obstructed by GNOME's top bar.

* Added controller mapping for the Thrustmaster Dual Analog 3.2 on Linux

* Enable evdev-based gamecontrollerdb on FreeBSD

* simplify SDL_GetBasePath on windows
- use GetModuleFileName directly (as recommended)

* use explicit GetModuleFileNameW

* Remove HWND_TOPMOST for fullscreen windows

Fixes libsdl-org#5509

* SDL_render.c: internally change viewport/cliprect type from SDL_FRect to SDL_DRect (double precision). (see bug libsdl-org#5547)

* Don't call scandir() inside of scandir()

This works around a crash in address sanitizer

* Fixed compile warnings

* x11: Revert "Fix keymap updating for X11 backend"

This reverts commit de6d290.

This patch had multiple issues, discussed in libsdl-org#5520.

* Fixed build

* Fix recent changes in VitaSDK

* SceKernelMemBlockType to unsigned int

* Fix a -Wshadow warning

* Remove an unused function prototype

* WhatsNew.txt: Add SDL_FRectEqualsEpsilon()

* Add SDL_FLT_EPSILON.

* SDL_Rect: Use a default epsilon in SDL_FRectEquals()

Add SDL_FRectEqualsEpsilon() for when more control over
equality test is required.

* test: Basic tests for SDL_FRectEquals

Based on the integer version. These tests mostly check that input
isn't mangled and that invalid input gives the expected negative
result.

* hidapi: Wrap CopyHIDDeviceInfo in define checks.

The purpose of this is to silence a 'defined but not used'
warning.

* os2: SDL_DestroyMutex should ignore NULL mutexes.

Every other backend does this, so this should match, now.
It's possible this was harmless, but we can avoid the system call
and the (likely?) debug message when it fails, though!

* windows: Fix calling convention for RoInitialize/RoUninitialize

Fixes libsdl-org#5563

* windows: Fix RoInitialize() failure after a CoInitializeEx() call using apartment threading

This mirrors the same codepath in WIN_CoInitialize() which handles STA and MTA.

* WGI: Keep a reference to the MTA to avoid crashing on COM teardown

Fixes libsdl-org#5552
Fixes libsdl-org#5270

* WGI: Only call RoUninitialize() if RoInitialize() succeeded

* Handle potential out of memory condition when working with hints

* SDL_Rect: Add \sa documentation block to SDL_FRect

* Return a correlation error when trigger rumble is attempted without correlation

* Fixed build

* SDL: sometimes the PS5 controller doesn't report having to power even when connected over USB. Possibly related to being completely charged? Either way we already know that it's USB or BT so let's use the driver's knowledge instead.

* Added support for the Backbone One controller on iOS

* Revert "Fix relative mouse input for Unvanquished (unvanquished.net)"

This reverts commit 3318590.

Fixes libsdl-org#5569

* hidapi, libusb: import mainstream commit 536bad201e

* Fixed logical size synchronization issue on macOS

https://discourse.libsdl.org/t/sdl-2-0-22-prerelease/35306/6

* x11: revert checks for _NET_WM_STATE_FULLSCREEN changes.

This reverts commit 8597735.
This reverts commit 0249df9.

Fixes libsdl-org#5572.
Reopens libsdl-org#5390.

Co-authored-by: Jo Bates <jo@valvesoftware.com>
Co-authored-by: Ozkan Sezer <sezeroz@gmail.com>
Co-authored-by: Cameron Gutman <aicommander@gmail.com>
Co-authored-by: Charlie Birks <charlie@daftgames.net>
Co-authored-by: Zach Reedy <86971250+zreedy@users.noreply.github.com>
Co-authored-by: SDL Wiki Bot <icculus-sdlwikibot@icculus.org>
Co-authored-by: Ryan C. Gordon <icculus@icculus.org>
Co-authored-by: Sylvain <sylvain.becker@gmail.com>
Co-authored-by: Guus Waals <_@guusw.nl>
Co-authored-by: Zach Reedy <zreedy@opera.com>
Co-authored-by: Wohlstand <admin@wohlnet.ru>
Co-authored-by: Sam Lantinga <slouken@libsdl.org>
Co-authored-by: staphen <staphen@gmail.com>
Co-authored-by: pionere <pionere@freemail.hu>
Co-authored-by: uyjulian <uyjulian@gmail.com>
Co-authored-by: Esme Povirk <esme@codeweavers.com>
Co-authored-by: Antoine Fontaine <antoine.fontaine@epfl.ch>
Co-authored-by: Ethan Lee <flibitijibibo@gmail.com>
Co-authored-by: Christian Kündig <kuendig@scandit.com>
Co-authored-by: Eddy Jansson <eddy@klopper.net>
Co-authored-by: Egor <egor@opensrc.club>
Co-authored-by: Jonatha Gabriel <jonathagabrielns@gmail.com>
Co-authored-by: Joao Paulo Magalhaes <dev@jpmag.me>
Co-authored-by: Florian "sp1rit"​ <sp1ritCS@protonmail.com>
Co-authored-by: Frank Praznik <frank.praznik@gmail.com>
Co-authored-by: Ivan Epifanov <isage.dna@gmail.com>
Co-authored-by: Kimplul <kimi.h.kuparinen@gmail.com>
Co-authored-by: Simon McVittie <smcv@collabora.com>
Co-authored-by: DominusExult <domiman+github@gmail.com>
Co-authored-by: capehill <juha.niemimaki@gmail.com>
Co-authored-by: Trigan2025 <trigan2025@hotmail.fr>
Co-authored-by: Cameron Cawley <ccawley2011@gmail.com>
Co-authored-by: Mathieu Eyraud <70028899+meyraud705@users.noreply.github.com>
Co-authored-by: Connor Clark <cjamcl@gmail.com>
Co-authored-by: Jaylon Gowie <jaylongowie46@gmail.com>
Co-authored-by: Reinhold Gschweicher <pyro4hell@gmail.com>
Co-authored-by: Christoph Reichenbach <creichen@gmail.com>
Co-authored-by: Weng Xuetian <wengxt@gmail.com>
Co-authored-by: David Gow <david@ingeniumdigital.com>
Co-authored-by: Semphris <semphris@protonmail.com>
Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants