Skip to content

win32 video: add hint for set WS_EX_NOREDIRECTIONBITMAP#15776

Open
7aGiven wants to merge 6 commits into
libsdl-org:mainfrom
7aGiven:main
Open

win32 video: add hint for set WS_EX_NOREDIRECTIONBITMAP#15776
7aGiven wants to merge 6 commits into
libsdl-org:mainfrom
7aGiven:main

Conversation

@7aGiven

@7aGiven 7aGiven commented Jun 7, 2026

Copy link
Copy Markdown
Contributor
  • I confirm that I am the author of this code and release it to the SDL project under the Zlib license. This contribution does not contain code from other sources, including code generated by a Large Language Model ("AI").

Description

When modal loop, SDL_EVENT_WINDOW_EXPOSED have sent by WM_TIMER, cancel send it at WM_PAINT.

Existing Issue

fix #15773

Please ignore #15775. The branch do force push by my bad operation, so I can't commit to that PR, I create this PR.

@7aGiven 7aGiven changed the title Don't send SDL_EVENT_WINDOW_EXPOSED(data1=0) when modal on Windows Don't send SDL_EVENT_WINDOW_EXPOSED(data1=0) when modal loop on Windows Jun 7, 2026
@slouken

slouken commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

What's the issue with sending SDL_EVENT_WINDOW_EXPOSED in the modal loop?

@slouken slouken added this to the 3.6.0 milestone Jun 7, 2026
@7aGiven

7aGiven commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

I do a test case to reproduce the issue.

After the bug is trigger by below code, The normal code with direct3d11 also trigger the bug when resize window until reboot system to fix the bug.

#include <SDL3/SDL_init.h>
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_render.h>

#pragma comment(lib, "SDL3.lib")

static SDL_Window* window;
static SDL_Renderer* renderer;
static char buf[256];

static bool EventWatch(void* userdata, SDL_Event* evt) {
	if (evt->type == SDL_EVENT_MOUSE_MOTION)
		return true;
	SDL_GetEventDescription(evt, buf, sizeof buf);
	SDL_Log(buf);
	if (evt->type == SDL_EVENT_WINDOW_EXPOSED)
	{
		int w, h;
		SDL_GetWindowSize(window, &w, &h);
		SDL_SetWindowSize(window, w & 0x7FFFFFFE, h & 0x7FFFFFFE);
		SDL_RenderClear(renderer);
		SDL_RenderPresent(renderer);
	}
	return true;
}
void main() {
	SDL_Init(SDL_INIT_VIDEO);
	window = SDL_CreateWindow("SDL3App", 800, 480, SDL_WINDOW_RESIZABLE);
	renderer = SDL_CreateRenderer(window, "direct3d11");
	SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);

	SDL_AddEventWatch(EventWatch, 0);
	SDL_Event evt;
	while (true)
		if (SDL_PollEvent(&evt))
			if (evt.type == SDL_EVENT_QUIT)
				break;
}
06 08 085425

@7aGiven

7aGiven commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

run the test case. resize window will cause that black bar bottom.

@7aGiven

7aGiven commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

When should send SDL_EVENT_WINDOW_EXPOSED(data1=0)?
When should send SDL_EVENT_WINDOW_EXPOSED(data1=1)?

SDL_EVENT_WINDOW_EXPOSED now sets data1 to true if it is sent during live resizing

Is it mean not send SDL_EVENT_WINDOW_EXPOSED(data1=0) during live resizing?

@slouken

@7aGiven

7aGiven commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

I confirm that the reason is that SetWindowPos change window size during WM_PAINT with Direct3D flip mode.

SetWindowPos trigger WM_NCPAINT cause draw origin from client area left top to window left top. (The issue not occur when WM_NCPAINT return 0;)

@7aGiven

7aGiven commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

The another way is that set WS_EX_NOREDIRECTIONBITMAP with flip mode can fix the issue.

@slouken

slouken commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

@flibitijibibo, thoughts?

@flibitijibibo

Copy link
Copy Markdown
Collaborator

Nothing from me... this supposedly makes the FNA window more like XNA's but if this has an adverse effect on general windowing behavior on Windows we can make it optional or something.

@7aGiven

7aGiven commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Nothing from me... this supposedly makes the FNA window more like XNA's but if this has an adverse effect on general windowing behavior on Windows we can make it optional or something.

This not makes the FNA window more like XNA's.

XNA only draw when WM_PAINT during live reisze.

SDL draw when WM_PAINT and WM_TIMER (by SetTimer with 100Hz at WM_ENTERSIZEMOVE) during live size.

When live resize window and stop mouse but no exit resize. WM_PAINT won't continue trigger until continue move mouse to resize.

So during live resize in XNA. When you stop move mouse. The game screen will freeze completely.

My PR not cause the game freeze. It will draw with 100Hz during live resize.

@7aGiven

7aGiven commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

I think there is a 100Hz source that trigger SDL_EVENT_WINDOW_EXPOSED during live resize. Not need send SDL_EVENT_WINDOW_EXPOSED when WM_PAINT during live resize.

May affect some code that use if (event->window.data1 == 0) draw(); for avoid to draw too fast at live resize.

@7aGiven

7aGiven commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

There is another way to fix issue. Disable WIN_SetWindowSize between WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE.

@7aGiven

7aGiven commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Acording my test. Reason may call SetWindowPos to change size ten times during modal loop.

Maybe not during whole modal loop. I'm not sure.

After run reproduce code with not flip mode. Run a normal flip mode app will also occur the issue.

@7aGiven 7aGiven changed the title Don't send SDL_EVENT_WINDOW_EXPOSED(data1=0) when modal loop on Windows Video: bug when SetWindowPos multi times during modal loop Jun 11, 2026
@7aGiven 7aGiven changed the title Video: bug when SetWindowPos multi times during modal loop win32 video: bug when SetWindowPos multi times during modal loop Jun 11, 2026
@7aGiven 7aGiven changed the title win32 video: bug when SetWindowPos multi times during modal loop win32 video: bug when SetWindowPos 10 times during modal loop Jun 11, 2026
@7aGiven

7aGiven commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

A test case that without Direct3D and SDL. Run test case and drag left border of window to x < 200. Then close test case and run any app with Direct3D flip mode. App Direct3D flip mode will black bar bottom during live resize.

#include <stdio.h>
#include <windows.h>

static int windowX;
static unsigned short nSetWindowPos = 0;

static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg != 0) {
        WINDOWPOS* pos;
        switch (msg) {
        case WM_WINDOWPOSCHANGED:
            pos = lParam;
            printf("WM_WINDOWPOSCHANGED %d %d %d %d %X\n", pos->x, pos->y, pos->cx, pos->cy, pos->flags);
            windowX = pos->x;
            return 0;
        case WM_ENTERSIZEMOVE:
            printf("WM_ENTERSIZEMOVE\n");
            return 0;
        case WM_EXITSIZEMOVE:
            printf("WM_EXITSIZEMOVE\n");
            return 0;
        case WM_PAINT:
            printf("WM_PAINT %d\n", windowX);
            PAINTSTRUCT ps;
            BeginPaint(hwnd, &ps);
            if (windowX < 200) {
                printf("---------------SetWindowPos Enter %d\n", nSetWindowPos);
                for (int i = 0; i < 10; i++) {
                    SetWindowPos(hwnd, 0, 400 + i, 400 + i, 350 + i, 350 + i, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSENDCHANGING);
                }
                printf("---------------SetWindowPos Leave %d\n", nSetWindowPos);
                nSetWindowPos++;
            }
            EndPaint(hwnd, &ps);
            return 0;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        default:
            //printf("WindowProc %X---%llX\n", msg, wParam);
            break;
        }
    }
    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

static int WINAPI _WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    // Register the window class.
    const wchar_t CLASS_NAME[] = L"Sample Window Class";

    WNDCLASS wc;
    ZeroMemory(&wc, sizeof(WNDCLASS));

    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.hbrBackground = COLOR_BACKGROUND;

    RegisterClassW(&wc);

    // Create the window.

    HWND hwnd = CreateWindowExW(
        0,                                          // Optional window styles.
        CLASS_NAME,                     // Window class
        L"Learn to Program Windows",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style

        // Size and position
        250, 250, 500, 500,

        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );

    if (hwnd == NULL)
    {
        return 0;
    }
    ShowWindow(hwnd, nCmdShow);

    MSG msg;
    while (GetMessageW(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    return 0;
}

int main() {
    return _WinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), SW_SHOW);
}

@7aGiven 7aGiven changed the title win32 video: bug when SetWindowPos 10 times during modal loop win32 video: bug when SetWindowPos 10 times during between WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE Jun 11, 2026
@7aGiven 7aGiven changed the title win32 video: bug when SetWindowPos 10 times during between WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE win32 video: bug when call SetWindowPos 10 times during between WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE Jun 11, 2026
@7aGiven 7aGiven closed this Jun 12, 2026
@7aGiven 7aGiven reopened this Jun 12, 2026
@7aGiven

7aGiven commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

The another way is that set WS_EX_NOREDIRECTIONBITMAP with flip mode can fix the issue.

I think this way is a good idea. Add a flag or property for SDL_CreateWindowWithProperties to allow CreateWindowEx with ExStyle WS_EX_NOREDIRECTIONBITMAP.

How do you think? @slouken

@7aGiven

7aGiven commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

WS_EX_NOREDIRECTIONBITMAP not available on Windows 7. CreateWindowEx return ERROR_INVALID_PARAMETER.

It available from Windows 8.

@7aGiven

7aGiven commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL and DXGI_SWAP_EFFECT_FLIP_DISCARD not available on Windows 7. CreateSwapChainForHwnd return DXGI_ERROR_INVALID_CALL.

It available from Windows 8.

@7aGiven 7aGiven changed the title win32 video: bug when call SetWindowPos 10 times during between WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE win32 video: add hint for set WS_EX_NOREDIRECTIONBITMAP Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Video: Bottom black bar. Call SetWindowSize when SDL_EVENT_WINDOW_EXPOSED, live resize window on Windows

3 participants