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

Ste*_* Lu 11 c++ opengl winapi sdl

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

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

SDL_Event event;
if (SDL_PollEvent(&event) && event.type == SDL_QUIT)
    break;
Run Code Online (Sandbox Code Playgroud)

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

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

有没有解决的办法?这很有趣,因为未能处理事件所表现出的部分行为证明我理想的是可能的.

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

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

inf*_*fra 7

一些对我有用的解决方法 - 为 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);
Run Code Online (Sandbox Code Playgroud)


Ber*_*ard 6

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

我从这个答案中得到了灵感,它不使用额外的线程。

#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();
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,如果您的渲染函数需要保留额外的状态(例如,如果您使用 OOP),请使用void*参数eventWatch(void*, SDL_Event*)来传递状态。


use*_*136 0

我建议你创建 2 个线程:

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

这样,您的 OpenGL 上下文就可以通过单个线程进行操作。整个解决方案对应用程序架构的影响最小。

  • 这不是一个好的解决方案,因为渲染取决于事件处理的结果。拥有两个不可并行任务的线程没有帮助。 (2认同)