在拖动时保持窗口活动(Win32上的SDL)

时间:2011-08-18 11:20:52

标签: c++ winapi opengl sdl

首先,我的代码设置了SDL环境,然后继续更新OpenGL上下文,而不执行任何SDL_Event处理。这会导致窗口在打开时对Windows显示无响应。窗口闪烁了一下。标题栏将附加“(无响应)”,并且在窗口内单击时,它将变为灰色,因为Windows在非响应窗口上默认执行此操作。然而,在这种状态下(即使在它变灰之后),OpenGL显示继续更新和动画,这里是踢球者,它甚至在拖动窗口的时候 。显然,在这种情况下,应用程序不会正确处理来自窗口的事件,导致窗口认为它处于挂起状态。但有明显的证据表明,opengl仍在继续呈现。

现在我对代码进行一次修改,这三行放在循环内的适当位置(也可以进行OpenGL绘制):

SDL_Event event;
if (SDL_PollEvent(&event) && event.type == SDL_QUIT)
    break;

所有这一切都是使用SDL刷新消息队列。

现在的行为是Windows不再认为它是“无响应”并且它不会变灰。没有闪烁。一切似乎都在游泳。但是,一旦我单击并拖动标题栏拖动窗口,渲染就会被阻止。我没有调试它以确定,但我怀疑SDL_PollEvent阻止窗口拖动的持续时间。

有解决方法吗?这很有趣,因为未能处理事件所表现出的部分行为证明了我理想的可能性。

更新:我找到了这个主题:http://www.gamedev.net/topic/488074-win32-message-pump-and-opengl---rendering-pauses-while-draggingresizing/

判决似乎是微软为我们做出的某些选择......它基本上被卡在DefWindowProc()中,直到鼠标被释放。破解修复程序会变得非常混乱,我可能可以通过在另一个线程中渲染来解决问题。但我甚至不想开始考虑从多个线程中处理OpenGL上下文,如果这甚至是可能的话。

5 个答案:

答案 0 :(得分:3)

适用于我的一些解决方法 - 为SDL_WINDOWEVENT_SIZE_CHANGED事件添加事件过滤器,并执行其他SetViewport和绘制框架。

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);

答案 1 :(得分:2)

这个问题很旧,但是我正在使用的解决方案似乎在其他任何地方都没有提及,所以在这里。

我从this的答案中得到了启发,它不使用其他线程。

#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();
    }
}

当然,如果渲染功能需要保持其他状态(例如,如果您使用的是OOP),请使用void*的{​​{1}}参数传递状态。

答案 2 :(得分:0)

我建议你创建2个帖子:

  • 线程1:循环调用SDL_PollEvent()(不呈现任何内容)
  • 线程2:执行OpenGL渲染(不调用SDL_PollEvent())

这样,您的OpenGL上下文将从单个线程进行操作。整个解决方案对应用程序的体系结构影响最小。

答案 3 :(得分:0)

许多Windows程序运行单独的消息循环,直到发生某个事件,因此您不应该依赖主循环来进行绘制。如果可能,应始终在单独的线程中处理应用程序逻辑和呈现。

您的主线程(仅处理消息处理)根本不需要GL上下文,因此您无需担心共享。

答案 4 :(得分:0)

我遇到了类似的问题,当拖动或调整窗口大小时,它会冻结视频播放。我发现的解决方案是生成一个单独的线程进行渲染,并使用主线程进行输入。

示例:

{{1}}

请记住,您必须在执行事件处理的线程中创建窗口。如果不是它将无法正常工作。您可以在事件处理线程中创建窗口,然后将该窗口指针传递给渲染线程。