mdparker/Derelict3

SDL2 window problem

Closed this issue · 11 comments

Hello. I've found that when using the SDL2 binding, minimizing then restoring a window created with SDL causes the window to resize to an odd, small size. Below is the simplest code that reproduces this problem for me every time.

I'm currently using 64-bit Windows 8 with the latest version of dmd (2.063.2), and right now I do not have access to other systems to test this. I believe that this is somehow caused by D using the C library instead of being a SDL specific error. When I compile and run the equivalent code in C++, using the same exact version of SDL2, everything works as expected.

import derelict.sdl2.sdl;

void main()
{
    DerelictSDL2.load();
    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window *window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
    bool isRunning = true;

    while(isRunning)
    {
        SDL_Event event;
        while(SDL_PollEvent(&event))
        {
            if(event.type == SDL_QUIT)
            {
                isRunning = false;
            }
        }
    }

    SDL_DestroyWindow(window);
    SDL_Quit();
}

I would like to add that a friend tested this on 32-bit Ubuntu 13.04 with the latest version of Derelict3 and dmd. Everything works as expected for him.

Edit: He also tested on 32-bit Windows 7 and reproduced my original problem. I conclude it's Windows specific.

In the future, when you aren't sure if something is a Derelict bug, I ask that you post in the Derelict forum for help. Please use the issue tracker here for reporting confirmed bugs.

EDIT: I missed that you tried a C++ version that worked.
What makes you think it's a problem specific to D? Did you try the same code in C?

I'm not at home at the moment, but when I get to my PC in a few hours I'll see if I can reproduce this on my system.

I can confirm that this is happening in all of my current D projects using SDL2 on Windows, but in none of my C projects. Now comes the hard part. I have no idea where to start looking for the cause.

I have a small hunch about what may be causing this - I literally just woke up with the idea. 😄 -, but I could be entirely wrong because I have very little knowledge of the internals of these kind of things.

Basically, "SDL provides a version of WinMain() which performs some SDL initialization before calling your main code" (from the Windows FAQ on the SDL wiki, http://wiki.libsdl.org/moin.fcg/FAQWindows). This is the only thing I can think of that is Windows specific, and I think there's a possibility this initialization isn't happening with the binding.

I'll look into this more myself to see what I can find out as well.

I don't believe that has anything to do with it. SDL's WinMain is not necessary and doesn't do anything that could affect any windows you create. It's only there as a convenience to allow a single main function across multiple platforms for those who want to use it.

My initial thought was that there might be a problem somewhere in the binding with the size of an event object, or a function parameter. That sort of mistake can result in some freaky behavior, but I've been over all of the event and window related stuff and can't see anything that stands out.

I realize that my lack of sleep did result in me being quite wrong. Like I said, I had just woken with the idea. lol

Now that I'm on the right track, I added outputs of the window events being received by SDL_PollEvent as a simple test. When the window is minimized, the SDL_WINDOWEVENT_RESIZED event is returned before SDL_WINDOWEVENT_MINIMIZED. SDL_WINDOWEVENT_RESTORED is solely returned when expected. Using the same code in C++ again, SDL_WINDOWEVENT_RESIZED is never returned on a simple window minimize.

Pretty well odd. I don't expect Derelict to effect something like that if it's simply just calling the same C API.

I've just learned three new things about this.

  1. It's not just the SDL_WINDOWEVENT_RESIZED that's being sent before the minimize, but also SDL_WINDOWEVENT_SIZE_CHANGED.

  2. Calling SDL_SetWindowSize when handling the SDL_WINDOWEVENT_MINIMIZED event will cause the resize to happen again just before SDL_WINDOWEVENT_RESTORED is sent.

  3. For me, the new window size in both cases (before minimize and restore) is 160x24.

I'll bang my head against this for a little while longer today to see if I can get a breakthrough. I've been poring through the SDL source and reading all I can about Win32 window events for clues, but nothing major jumped out at me there.

For now, as a workaround:

  1. On SDL_WINDOWEVENT_SIZE_CHANGED, save the current size of the window somewhere like oldSize. This requires that you keep track of the current window size yourself. Calling SDL_GetWindowSize while handling this event will give you the new size rather than the old one.

  2. On SDL_WINDOWEVENT_MINIMIZED, set a flag like wasMinimized to true.

  3. On SDL_WINDOWEVENT_RESTORED, if wasMinimized is true call SDL_SetWindowSize with oldSize and set wasMinimized to false.

Each step should likely happen in a version( Windows) block. It only hides the problem, but until someone can figure out what's happening, it gets the job done.

Some new info.

  1. When the window is created with SDL_WINDOW_RESIZABLE, the size is set to 160x24 on minimize, but the original size is automatically restored on SDL_WINDOW_RESTORED.

  2. Another difference between D and C. In C, when the window is created only with the flag SDL_WINDOW_SHOWN, the window has a border. In D with the same flag, there is no border. The border only appears when SDL_WINDOW_RESIZABLE is added to the flags.

OK, looks like I've got it. Compile your D executable as a Windows subsystem exe or use WinMain instead of main (I assume, as WinMain makes the app Windows subsystem automatically), then the problem goes away. With DMD, you can pass -L/SUBSYSTEM:WINDOWS:5.01 on the command line for a 32-bit app (5.02 for 64-bit). That will allow you to still use main, but eliminates the console window that pops up without that flag. Furthermore, the window has a border when it's created and the resize on minimize doesn't happen anymore, just as was happening in C.

For me, the C app was automatically being compiled as Windows subsystem by premake. I guess since in your case your C++ code was relying on SDL's WinMain, you were automatically getting the Windows subsystem, too. So your hunch about WinMain turned out to be a pretty good one! It had nothing to do with any initialization SDL was doing, but everything to do with how Windows handles it internally.

Case closed :-)