Keep window active while being dragged (SDL on Win32)

倖福魔咒の 提交于 2019-12-03 06:05:07

Some workaround that works for me - add event filter for SDL_WINDOWEVENT_SIZE_CHANGED event and do additional SetViewport and draw frame.

int SDLApp::eventFilter(void* pthis, const SDL_Event *event)
{
    if (event->type == SDL_WINDOWEVENT &&
        event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
    {
        SDLApp* app = (SDLApp*)pthis;
        // Note: NULL rectangle is the entire window
        SDL_RenderSetViewport(app->renderer_, NULL);
        app->DrawFrame();
    }
    return 1;
}

...
SDL_SetEventFilter((SDL_EventFilter)SDLApp::eventFilter, this);

This question is old, but the solution I'm using doesn't seem to be mentioned anywhere else, so here it is.

I got my inspiration from this answer, and it doesn't use additional threads.

#include <SDL.h>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <SDL_syswm.h>

#define SIZE_MOVE_TIMER_ID 1

bool sizeMoveTimerRunning = false;

int eventWatch(void*, SDL_Event* event) {
    if (event->type == SDL_SYSWMEVENT) {
        const auto& winMessage = event->syswm.msg->msg.win;
        if (winMessage.msg == WM_ENTERSIZEMOVE) {
            // the user started dragging, so create the timer (with the minimum timeout)
            // if you have vsync enabled, then this shouldn't render unnecessarily
            sizeMoveTimerRunning = SetTimer(GetActiveWindow(), SIZE_MOVE_TIMER_ID, USER_TIMER_MINIMUM, nullptr);
        }
        else if (winMessage.msg == WM_TIMER) {
            if (winMessage.wParam == SIZE_MOVE_TIMER_ID) {
                // call your render function
                render();
            }
        }
    }
    return 0;
}

// rendering function
void render() {
    /* do your rendering here */
}

// event loop - call this function after setting up your window to start the event loop
void eventLoop() {
    SDL_AddEventWatch(eventWatch, nullptr); // register the event watch function
    SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); // we need the native Windows events, so we can listen to WM_ENTERSIZEMOVE and WM_TIMER
    while (true) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (sizeMoveTimerRunning) {
                // modal drag/size loop ended, so kill the timer
                KillTimer(GetActiveWindow(), SIZE_MOVE_TIMER_ID);
                sizeMoveTimerRunning = false;
            }
            /* handle the events here */
        }
        render();
    }
}

Of course, if your rendering function needs to keep additional state (e.g. if you're using OOP), use the void* parameter of eventWatch(void*, SDL_Event*) to pass the state.

I propose you created 2 threads:

  • Thread 1: loops calling SDL_PollEvent() (without rendering anything)
  • Thread 2: does OpenGL rendering (without calling SDL_PollEvent())

This way, your OpenGL context would be manipulated from a single thread. The whole solution has a minimum impact the architecture of your application.

Many windows procedures run a separate message loop until a certain event occurs, so you shouldn't rely on your main loop to do the drawing. If possible, application logic and rendering should always be handled in a separate thread.

Your main thread (that only handles message processing) doesn't need GL context at all, so you wouldn't need to worry about sharing.

I had a similar problem in which it would freeze video playback when the window was dragged or resized. The solution I found was to spawn a separate thread for rendering and use the main thread for input.

Example:

DWORD RenderThread(SDL_Window* window)
{
    //Rendering stuff here...
}

int main()
{
    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_Window* window = SDL_CreateWindow("Title Here",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, h, w, SDL_WINDOW_RESIZABLE);

    HANDLE hRenderThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RenderThread, window, 0, NULL);

    SDL_Event event;

    while (1)
    {
        SDL_PollEvent(&event);

        switch (event.type)
        {
            //Event handling here...
        }
    }
}

Keep in mind that you MUST create the window in the thread that does event handling. If not it won't work. You can create the window in your event handling thread then pass that window pointer to your rendering thread.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!