WM_PAINT patch for foreign windows

I just spent the last several hours digging through Delphi’s message-handling system, trying to find out why my SDL wrapper component wouldn’t display properly under certain circumstances. Eventually I tracked it to the fact that it was never receiving the WM_PAINT message. Turns out SDL was swallowing it to handle internally.

That’s perfectly acceptable when you have a window that SDL created and is managing, but when it’s a foreign window created by SDL_CreateWindowFrom, someone else owns the window handle and needs to be able to respond to paint requests in its own fashion.

This patch fixes it. It’s Windows only, of course. Not sure if the same problem exists on other platforms, but it wouldn’t surprise me. If so, they’ll need platform-specific fixes.

Index: SDL_win32events.c===================================================================
— SDL_win32events.c (revision 4599)
+++ SDL_win32events.c (working copy)
@@ -587,15 +587,18 @@

     /* We were occluded, refresh our display */
 case WM_PAINT:
  •    {
    
  •        RECT rect;
    
  •        if (GetUpdateRect(hwnd, &rect, FALSE)) {
    
  •            ValidateRect(hwnd, &rect);
    
  •            SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_EXPOSED,
    
  •                                0, 0);
    
  •        }
    
  •    }
    
  •    return (0);
    
  •    {
    
  •        if (!(SDL_GetWindowFlags(data->windowID) & SDL_WINDOW_FOREIGN)) {
    
  •            RECT rect;
    
  •            if (GetUpdateRect(hwnd, &rect, FALSE)) {
    
  •                ValidateRect(hwnd, &rect);
    
  •                SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_EXPOSED,
    
  •                                    0, 0);
    
  •            }
    
  •            return (0);
    
  •        }
    
  •        else break;
    
  •    }
    
       /* If this isn't our window, we don't need to repaint the frame.
          This fixes a reentrancy issue that can cause stack overflows with foreign windows.

This is a little tricky in that right now SDL assumes that for foreign
windows it owns the graphics but the application owns the input.

Can you tell a little more about how you’re trying to use it?On Mon, Jul 6, 2009 at 10:47 AM, Mason Wheeler wrote:

I just spent the last several hours digging through Delphi’s
message-handling system, trying to find out why my SDL wrapper component
wouldn’t display properly under certain circumstances. Eventually I tracked
it to the fact that it was never receiving the WM_PAINT message. Turns out
SDL was swallowing it to handle internally.

That’s perfectly acceptable when you have a window that SDL created and is
managing, but when it’s a foreign window created by SDL_CreateWindowFrom,
someone else owns the window handle and needs to be able to respond to paint
requests in its own fashion.

This patch fixes it. It’s Windows only, of course. Not sure if the same
problem exists on other platforms, but it wouldn’t surprise me. If so,
they’ll need platform-specific fixes.

Index: SDL_win32events.c

— SDL_win32events.c (revision 4599)
+++ SDL_win32events.c (working copy)
@@ -587,15 +587,18 @@

     /* We were occluded, refresh our display */
 case WM_PAINT:
  •    {
    
  •        RECT rect;
    
  •        if (GetUpdateRect(hwnd, &rect, FALSE)) {
    
  •            ValidateRect(hwnd, &rect);
    
  •            SDL_SendWindowEvent(data->windowID,
    

SDL_WINDOWEVENT_EXPOSED,

  •                                0, 0);
    
  •        }
    
  •    }
    
  •    return (0);
    
  •    {
    
  •        if (!(SDL_GetWindowFlags(data->windowID) &
    

SDL_WINDOW_FOREIGN)) {

  •            RECT rect;
    
  •            if (GetUpdateRect(hwnd, &rect, FALSE)) {
    
  •                ValidateRect(hwnd, &rect);
    
  •                SDL_SendWindowEvent(data->windowID,
    

SDL_WINDOWEVENT_EXPOSED,

  •                                    0, 0);
    
  •            }
    
  •            return (0);
    
  •        }
    
  •        else break;
    
  •    }
    
       /* If this isn't our window, we don't need to repaint the frame.
          This fixes a reentrancy issue that can cause stack overflows
    

with foreign windows.


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


-Sam Lantinga, Founder and President, Galaxy Gameworks LLC

It’s pretty simple. I built a control that wraps an SDL_Window and embeds
it on a Delphi form. In Delphi, forms are constructed in one pass, and then
shown after construction is complete. Sometimes quite a bit after; forms
can be constructed at startup time and cached in memory until they’re
needed.

The TSDLFrame constructor calls the inherited constructors (in Delphi,
construction order is from the actual object type down to the base class,
not from the base up like in C++) to set up a generic rectangle with a
HWND handle, then passes that handle to SDL_CreateWindowFrom.
Unfortunately, that’s as far as it can go, since it’s quite possible at this
point for the component to be offscreen. If I try to call SDL_CreateRenderer
while the window’s offscreen, the call will fail. I need to delay that call
until the moment when the window is displayed onscreen.

Fortunately, Windows makes it very easy to know when something is
displayed onscreen: It sends a WM_PAINT message. But if I never
receive it because SDL_win32events.c is swallowing it internally, I
can’t finish setting up the control, and it becomes useless.

Other external systems are likely to have the same problem. If SDL
really needs to do that SDL_SendWindowEvent call, then go ahead
and do it, but don’t Return(0) at the end unless you know there’s no
one else who needs that message.

This is a little tricky in that right now SDL assumes that for foreign windows it owns the graphics but the application owns the input.

Can you tell a little more about how you’re trying to use it?

I just spent the last several hours digging through Delphi’s message-handling system, trying to find out why my SDL wrapper component wouldn’t display properly under certain circumstances. Eventually I tracked it to the fact that it was never receiving the WM_PAINT message. Turns out SDL was swallowing it to handle internally.From: slouken@libsdl.org (slouken)
Subject: Re: [SDL] WM_PAINT patch for foreign windows
On Mon, Jul 6, 2009 at 10:47 AM, Mason Wheeler <@Mason_Wheeler> wrote:

That’s perfectly acceptable when you have a window that SDL created and is managing, but when it’s a foreign window created by SDL_CreateWindowFrom, someone else owns the window handle and needs to be able to respond to paint requests in its own fashion.

This patch fixes it. It’s Windows only, of course. Not sure if the same problem exists on other platforms, but it wouldn’t surprise me. If so, they’ll need platform-specific
fixes.

Index: SDL_win32events.c

— SDL_win32events.c (revision 4599)
+++ SDL_win32events.c (working copy)
@@ -587,15 +587,18 @@

    /* We were occluded, refresh our display */
case WM_PAINT:
  •    {
    
  •        RECT rect;
    
  •        if (GetUpdateRect(hwnd, &rect, FALSE)) {
    
  •            ValidateRect(hwnd, &rect);
    
  •            SDL_SendWindowEvent(data->windowID,
    

SDL_WINDOWEVENT_EXPOSED,

  •                                0, 0);
    
  •        }
    
  •    }
    
  •    return (0);
    
  •    {
    
  •        if (!(SDL_GetWindowFlags(data->windowID) & SDL_WINDOW_FOREIGN)) {
    
  •            RECT rect;
    
  •            if (GetUpdateRect(hwnd, &rect, FALSE)) {
    
  •                ValidateRect(hwnd,
    

&rect);

  •                SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_EXPOSED,
    
  •                                    0, 0);
    
  •            }
    
  •            return (0);
    
  •        }
    
  •        else break;
    
  •    }
    
      /* If this isn't our window, we don't need to repaint the frame.
         This fixes a reentrancy issue that can
    

cause stack overflows with foreign windows.

That sounds like a reasonable compromise. I’ll look into that.On Fri, Aug 7, 2009 at 5:59 AM, Mason Wheeler wrote:

It’s pretty simple. I built a control that wraps an SDL_Window and embeds
it on a Delphi form. In Delphi, forms are constructed in one pass, and
then
shown after construction is complete. Sometimes quite a bit after; forms
can be constructed at startup time and cached in memory until they’re
needed.

The TSDLFrame constructor calls the inherited constructors (in Delphi,
construction order is from the actual object type down to the base class,
not from the base up like in C++) to set up a generic rectangle with a
HWND handle, then passes that handle to SDL_CreateWindowFrom.
Unfortunately, that’s as far as it can go, since it’s quite possible at
this
point for the component to be offscreen. If I try to call
SDL_CreateRenderer
while the window’s offscreen, the call will fail. I need to delay that
call
until the moment when the window is displayed onscreen.

Fortunately, Windows makes it very easy to know when something is
displayed onscreen: It sends a WM_PAINT message. But if I never
receive it because SDL_win32events.c is swallowing it internally, I
can’t finish setting up the control, and it becomes useless.

Other external systems are likely to have the same problem. If SDL
really needs to do that SDL_SendWindowEvent call, then go ahead
and do it, but don’t Return(0) at the end unless you know there’s no
one else who needs that message.

From: Sam Lantinga <@slouken>
**Subject: Re: [SDL] WM_PAINT patch for foreign windows

This is a little tricky in that right now SDL assumes that for foreign
windows it owns the graphics but the application owns the input.

Can you tell a little more about how you’re trying to use it?

On Mon, Jul 6, 2009 at 10:47 AM, Mason Wheeler wrote:

I just spent the last several hours digging through Delphi’s
message-handling system, trying to find out why my SDL wrapper component
wouldn’t display properly under certain circumstances. Eventually I tracked
it to the fact that it was never receiving the WM_PAINT message. Turns out
SDL was swallowing it to handle internally.

That’s perfectly acceptable when you have a window that SDL created and is
managing, but when it’s a foreign window created by SDL_CreateWindowFrom,
someone else owns the window handle and needs to be able to respond to paint
requests in its own fashion.

This patch fixes it. It’s Windows only, of course. Not sure if the same
problem exists on other platforms, but it wouldn’t surprise me. If so,
they’ll need platform-specific fixes.

Index: SDL_win32events.c

— SDL_win32events.c (revision 4599)
+++ SDL_win32events.c (working copy)
@@ -587,15 +587,18 @@

     /* We were occluded, refresh our display */
 case WM_PAINT:
  •    {
    
  •        RECT rect;
    
  •        if (GetUpdateRect(hwnd, &rect, FALSE)) {
    
  •            ValidateRect(hwnd, &rect);
    
  •            SDL_SendWindowEvent(data->windowID,
    

SDL_WINDOWEVENT_EXPOSED,

  •                                0, 0);
    
  •        }
    
  •    }
    
  •    return (0);
    
  •    {
    
  •        if (!(SDL_GetWindowFlags(data->windowID) &
    

SDL_WINDOW_FOREIGN)) {

  •            RECT rect;
    
  •            if (GetUpdateRect(hwnd, &rect, FALSE)) {
    
  •                ValidateRect(hwnd, &rect);
    
  •                SDL_SendWindowEvent(data->windowID,
    

SDL_WINDOWEVENT_EXPOSED,

  •                                    0, 0);
    
  •            }
    
  •            return (0);
    
  •        }
    
  •        else break;
    
  •    }
    
       /* If this isn't our window, we don't need to repaint the frame.
          This fixes a reentrancy issue that can cause stack overflows
    

with foreign windows.


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


-Sam Lantinga, Founder and President, Galaxy Gameworks LLC

2009/8/7 Sam Lantinga :

That sounds like a reasonable compromise.? I’ll look into that.

Other external systems are likely to have the same problem.? If SDL
really needs to do that SDL_SendWindowEvent call, then go ahead
and do it, but don’t Return(0) at the end unless you know there’s no
one else who needs that message.

what about sending a paint event in SDL’s event queue instead?

event.type = SDL_WINDOWEVENT;
event.window.event = SDL_WINDOWEVENT_PAINT;
etc.> On Fri, Aug 7, 2009 at 5:59 AM, Mason Wheeler wrote: