views:

333

answers:

2

Hello, I created a window with the following flags to overlay a d3d application: WS_EX_TOPMOST | WS_EX_COMPOSITED | WS_EX_TRANSPARENT | WS_EX_LAYERED I proceeded with colorkeying the window for transperacy and all worked well. However once I began drawing on it using GDI an unforeseen problem occurred:

For some reason the mouse events (especially movement) are not passed correctly through the window when WM_PAINT is in progress, and so it appears as though the mouse and the keyboard for that matter lag. the FPS is fine, this is some API problem, I suspect that for some reason the keyboard/mouse messages are not handled as they should while the WM_PAINT is in progress, because the slower the timer is set to the less jerking there is.

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

 switch(msg)
 {
  case WM_DESTROY:
  {
          KillTimer(hwnd, ID_TIMER);
          PostQuitMessage(0);
          break;
         }
  case WM_CREATE:
  {
   SetTimer(hwnd, ID_TIMER, 10, NULL);
   break;
  }
  case WM_TIMER:
  {
   InvalidateRect(hwnd, 0, 1);
   break;
         }
  case WM_PAINT:
  {
   paint(hwnd);
   break;
  }
 }
 return DefWindowProc(hwnd, msg, wParam, lParam);
}

and

void paint (HWND hwnd)
{
 PAINTSTRUCT Ps;
 HDC hdc = BeginPaint(hwnd, &Ps);

 SetBkColor(hdc, RGB(0,0,0));
 SetBkMode(hdc, TRANSPARENT);



 LOGBRUSH log_brush;
 log_brush.lbStyle = BS_NULL;
 HBRUSH handle_brush = CreateBrushIndirect(&log_brush);
 SelectObject(hdc, handle_brush);


..........................................


 DeleteObject(font);
 DeleteObject(pen);
 DeleteObject(handle_brush);

 EndPaint(hwnd, &Ps);
}

Thank you for any help you may be able to give.

A: 

WndProc() is not always re-entrant. I believe with the main message pump, the mouse and keyboard messages are queued up and waiting for you to finish the prior WM_PAINT message. Conversely, if you were to call SendMessage() from WndProc(), then you are looking at re-entrace. The other case is PostMessage() which would add the message to the queue. Maybe look at using DirectInput for mouse and keyboard input if this is an issue. Otherwise, look for ways to speed up your drawing.

Jeff
You also should change your SelectObject() call to DeleteObject(SelectObject(hdc, handle_brush));
Jeff
@ John's answer - his timer calls InvalidateRect(), so WM_PAINT would result from WM_TIMER. Correct?
Jeff
+1  A: 

WM_PAINT messages are never delivered to your window unless someone calls UpdateWindow or there are no keyboard or mouse messages in your input queue.

Once you begin processing WM_PAINT, if a keyboard or mouse message arrives, it just sits in your queue until you are done with WM_PAINT. So What you are describing isn't possible.

If your WM_PAINT code takes a long time to execute, that could cause jerkiness, but you say that's not a problem so perhaps it's your handling of WM_ERASEBKGND? I don't see that code, but I do see that when you InvalidateRect, you are passing TRUE as the last parameter which means that you want the background to be erased.

If you don't handle WM_ERASEBKGND, then DefWindowProc will do it for you erasing your entire window with the brush from from your window class. This could result in windows thinking that no part of your window is transparent.

If you want mouse messages to pass through your window, a more reliable way is to handle the WM_NCHITTEST message and return HTTRANSPARENT where you want the mouse to pass through.
This is basically what how the WS_EX_TRANSPARENT style works. like this

case WM_NCHITTEST:
   {
   lRet = DefWindowProc(hwnd, uMsg, wParam, lParam);
   if (HTCLIENT == lRet)
      lRet = HTTRANSPARENT;
   }

If your window has no non-client area, then you can skip the call to DefWindowProc.

John Knoeller
By changing the InvalidateRect third parameter to FALSE the jerkyness has gone, however previous frames are not erased anymore, does that tell you anything? This windows API is confusing. Thanks.
Nick
Tried using Fillrect instead to erase previous frame and that did help reduce the jerkyness by a lot.
Nick
@Nick: You don't show what you actually do in your paint code, so I can't see what you would need to do instead of erasebkgnd, sorry. Fillrect sounds like it might do the trick. or your can use PatBlt to fill a rectangular area with sold color.
John Knoeller
Well it would seem that filling in several megapixels with a solid color creates a slight delay in processing and that delay is the problem because during that delay the mouse events are not passed down to the d3d application through the overlay window causing that mouse lag or rather jerking.What should I do? Thanks for your help.
Nick
@Nick: don't do that ;) Just erase the smallest rectangle you can get away with. You could try using ExtTextOut(.. ETO_OPAQUE, ...) with an empty string, at one time this was the fastest way to draw solid color rectangles, but nothing beats just _not_ erasing where you don't need to erase
John Knoeller
would having a separate thread for WM_PAINT fix the issue?
Nick
@Nick: you cant put paint on a separate thread.
John Knoeller
PAINT seems to be the problem, it delay the message handling, would having double buffering with one buffer in another thread help?
Nick
double buffering might help, but not as much as simply painting fewer pixels. You havn't even begun to optimize your paint code. There is no need to create a brush on each paint message, etc.
John Knoeller
Well I get what you are saying, and I could try to find the difference between two frames and just blt the difference but it will still not solve the nagging problem that the message loop has to wait on WM_PAINT isnt there a way to make sure WM_PAINT doesn't interfere with the message processing? Thanks.
Nick
Sorry, the only possible solution is to make your WM_PAINT code faster. Windows won't let you handle messages for a single window on more than 1 thread.
John Knoeller
What if I use opengl instead? Would it fix the issue? Thanks for your help.
Nick
Sorry, Nick. I have no idea if that would help. somehow you have to make your painting go faster, but I have no idea what your painting _does_ and I'm not really an opengl expert anyway.
John Knoeller
Is there a way to ignore the message pump altogether? And just draw on the created window? I have had some success but it doesn't go smoothly.
Nick
You can draw on any message you want to, but if you don't make your drawing faster than all you will do is move the problem. You will never be allowed to handle more than one message at a time. you have to make your paint code _faster_. as I've said multiple times now. If you need help figuring out how to make it faster than post more questions showing what you are trying to do and what you have tried, You'll get more eyes and advice than just mine that way.
John Knoeller
Faster? I can not even use double buffering with bitblt because when i do it the time it takes to bitblt is enough for the mouse to start jerking again. I will try to use opengl or d3d though.
Nick