Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SDL2 Window turns black on resize

I have started to work with SDL2 and am not experienced with it. I am working on a Mac system. Almost everything has been good, but I have a problem that when a resizeable window is resized, while the handle is being dragged, the window turns black, and I can only repaint it after releasing. And I have checked that while the window is being resized, no event is being produced and I have no means to interfere or detect this, as the event loop is just paused. Is there any possible solutions?

Here is the code (Almost replica of a tutorial on handling resize event):

SDL_Event event;

SDL_Rect nativeSize;
SDL_Rect newWindowSize;

float scaleRatioW;//This is to change anything that might rely on something like mouse coords
float scaleRatioH; //(such as a button on screen) over to the new coordinate system scaling would create

SDL_Window * window; //Our beautiful window
SDL_Renderer * renderer; //The renderer for our window
SDL_Texture * backBuffer; //The back buffer that we will be rendering everything to before scaling up

SDL_Texture * ballImage; //A nice picture to demonstrate the scaling;

bool resize;

void InitValues(); //Initialize all the variables needed
void InitSDL();     //Initialize the window, renderer, backBuffer, and image;
bool HandleEvents(); //Handle the window changed size event
void Render();            //Switches the render target back to the window and renders the back buffer, then switches back.
void Resize();      //The important part for stretching. Changes the viewPort, changes the scale ratios

void InitValues()
{
    nativeSize.x = 0;
    nativeSize.y = 0;
    nativeSize.w = 256;
    nativeSize.h = 224; //A GameBoy size window width and height

    scaleRatioW = 1.0f;
    scaleRatioH = 1.0f;

    newWindowSize.x = 0;
    newWindowSize.y = 0;
    newWindowSize.w = nativeSize.w;
    newWindowSize.h = nativeSize.h;

    window = NULL;
    renderer = NULL;
    backBuffer = NULL;
    ballImage = NULL;

    resize = false;
}

void InitSDL()
{
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        //cout << "Failed to initialize SDL" << endl;
        printf("%d\r\n", __LINE__);
    }

    //Set the scaling quality to nearest-pixel
    if(SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0") < 0)
    {
        //cout << "Failed to set Render Scale Quality" << endl;
         printf("%d\r\n", __LINE__);
    }

    //Window needs to be resizable
    window = SDL_CreateWindow("Rescaling Windows!",
                                                    SDL_WINDOWPOS_CENTERED,
                                                    SDL_WINDOWPOS_CENTERED,
                                                    256,
                                                    224,
                                                    SDL_WINDOW_RESIZABLE);

    //You must use the SDL_RENDERER_TARGETTEXTURE flag in order to target the backbuffer
    renderer = SDL_CreateRenderer(window,
                                                      -1,
                                                      SDL_RENDERER_ACCELERATED |
                                                      SDL_RENDERER_TARGETTEXTURE);

    //Set to blue so it's noticeable if it doesn't do right.
    SDL_SetRenderDrawColor(renderer, 0, 0, 200, 255);

    //Similarly, you must use SDL_TEXTUREACCESS_TARGET when you create the texture
    backBuffer = SDL_CreateTexture(renderer,
                                                       SDL_GetWindowPixelFormat(window),
                                                       SDL_TEXTUREACCESS_TARGET,
                                                       nativeSize.w,
                                                       nativeSize.h);

    //IMPORTANT Set the back buffer as the target
    SDL_SetRenderTarget(renderer, backBuffer);

    //Load an image yay
    SDL_Surface * image = SDL_LoadBMP("Ball.bmp");

    ballImage = SDL_CreateTextureFromSurface(renderer, image);

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);

    SDL_FreeSurface(image);
}

bool HandleEvents()
{
    while(SDL_PollEvent(&event) )
    {
       printf("%d\r\n", __LINE__);
       if(event.type == SDL_QUIT)
       {
             printf("%d\r\n", __LINE__);
            return true;
       }
        else if(event.type == SDL_WINDOWEVENT)
        {
            if(event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
            {
                resize = true;
                printf("%d\r\n", __LINE__);
            }
        }

        return false;
    }
    return false;
}

void Render()
{
    SDL_RenderCopy(renderer, ballImage, NULL, NULL); //Render the entire ballImage to the backBuffer at (0, 0)
    printf("%d\r\n", __LINE__);

    SDL_SetRenderTarget(renderer, NULL); //Set the target back to the window

    if(resize)
    {
        Resize();
        resize = false;
    }
    printf("%d\r\n", __LINE__);

    SDL_RenderCopy(renderer, backBuffer, &nativeSize, &newWindowSize); //Render the backBuffer onto the screen at (0,0)
    SDL_RenderPresent(renderer);
    SDL_RenderClear(renderer); //Clear the window buffer

    SDL_SetRenderTarget(renderer, backBuffer); //Set the target back to the back buffer
    SDL_RenderClear(renderer); //Clear the back buffer
    printf("%d\r\n", __LINE__);

}

void Resize()
{
    int w, h;
    printf("%d\r\n", __LINE__);

    SDL_GetWindowSize(window, &w, &h);

    scaleRatioW = w / nativeSize.w;
    scaleRatioH = h / nativeSize.h;  //The ratio from the native size to the new size

    newWindowSize.w = w;
    newWindowSize.h = h;

    //In order to do a resize, you must destroy the back buffer. Try without it, it doesn't work
    SDL_DestroyTexture(backBuffer);
    backBuffer = SDL_CreateTexture(renderer,
                                   SDL_GetWindowPixelFormat(window),
                                   SDL_TEXTUREACCESS_TARGET, //Again, must be created using this
                                   nativeSize.w,
                                   nativeSize.h);

    SDL_Rect viewPort;
    SDL_RenderGetViewport(renderer, &viewPort);

    if(viewPort.w != newWindowSize.w || viewPort.h != newWindowSize.h)
    {
        //VERY IMPORTANT - Change the viewport over to the new size. It doesn't do this for you.
        SDL_RenderSetViewport(renderer, &newWindowSize);
    }
}

int main(int argc, char * argv[])
{
    InitValues();
    InitSDL();

    bool quit = false;
    printf("%d\r\n", __LINE__);

    while(!quit)
    {
        printf("%d\r\n", __LINE__);
        quit = HandleEvents();
        Render();
    }

    return 0;
}
like image 254
Aozi Avatar asked Dec 25 '22 10:12

Aozi


1 Answers

Ok, after fighting a bit with SDL2, I got it working with macOS 10.12.

Here is the problem:

  1. SDL2 catches resize events and resends only the last 3 when you poll events using SDL_PollEvent(&event) for example.
  2. During this time (you pressed left mouse click on resize area and hold the mouse) the SDL_PollEvent is blocking.

Here is the workaround:

Luckily, you can hook into the event handler using SDL_SetEventFilter. This will fire every time a event is being received. So for all resize events as they occur.

So what you can do is to register your own event filter callback that basically allows every event (by returning 1), listens on resize events, and sends them to your draw loop.

Example:

//register this somewhere
int filterEvent(void *userdata, SDL_Event * event) {
    if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) {
        //convert userdata pointer to yours and trigger your own draw function
        //this is called very often now
        //IMPORTANT: Might be called from a different thread, see SDL_SetEventFilter docs
        ((MyApplicationClass *)userdata)->myDrawFunction(); 

        //return 0 if you don't want to handle this event twice
        return 0;
    }

    //important to allow all events, or your SDL_PollEvent doesn't get any event
    return 1;
}


///after SDL_Init
SDL_SetEventFilter(filterEvent, this) //this is instance of MyApplicationClass for example

Important: Do not call SDL_PollEvent within your filterEvent callback, as this will result in weird behaviour of stuck events. (resize will not stop sometimes for example)

like image 83
Marc J. Schmidt Avatar answered Jan 06 '23 00:01

Marc J. Schmidt