Drawing content at a fixed screen position, regardless of window position


Today's Little Program draws content at a fixed screen position. The idea is that the window is really a viewport into some magical world. Unfortunately, our magical world just has a sign that says "Booga booga." Creating a more interesting magical world is left as an exercise.

Start with our scratch program and make these changes:

void OnMove(HWND hwnd, int x, int y)
{
 InvalidateRect(hwnd, 0, TRUE);
}

void
PaintContent(HWND hwnd, PAINTSTRUCT *pps)
{
 POINT ptOrigin = { 0, 0 };
 ClientToScreen(hwnd, &ptOrigin);
 POINT ptOrg;
 SetWindowOrgEx(pps->hdc, ptOrigin.x, ptOrigin.y, &ptOrg);
 TextOut(pps->hdc, 200, 200, TEXT("Booga booga"), 11);
 SetWindowOrgEx(pps->hdc, ptOrg.x, ptOrg.y, nullptr);
}

    HANDLE_MSG(hwnd, WM_MOVE, OnMove);

Run this program and drag the window across the screen. When it reaches the "magic place", you will see the words "Booga booga". (You can resize the window to be smaller in order to make finding the magic place more of a challenge.)

Comments (10)
  1. You can save three lines of code. If you rewrite the TextOut line this way:

    TextOut(pps->hdc, 200 – ptOrigin.x, 200 – ptOrigin.y, TEXT("Booga booga"), 11);

    both calls to SetWindowOrgEx and the ptOrg declaration can be removed – a 50% saving inside PaintContent!

    [Your solution does not generalize well. -Raymond]
  2. Simon Buchan says:

    I think you're probably the only blogger out there that can make me continuously giggle through a post demonstrating OS APIs. SetWindowOrgEx()'s documentation is a little shaky on actual semantics, but it looks like SetWindowOrgEx(x, y) ~= SetViewportOrg(-x, -y) ~= subtract x and y from all coordinates?

  3. Dan Bugglin says:

    @Antonio for this program yes but I think the point here is to showcase how SetWindowOrgEx can save you a bit of math.

    In this case it seems to be more trouble than its worth, but if you start painting more stuff it quickly becomes a bit complicated and harder to read if you do the math by hand.

  4. This is the first time I've ever compiled one of these Little Programs.  It's pretty nifty, but (on my system at least) I can see the text flicker when I move around.  I assume this is because GDI/USER is automatically blitting the window image to try and reduce flicker, but in this case, it has the opposite effect. Is there a property or something that can be set on the window in order to simplify this behavior?

    [Standard techniques for reducing flicker apply here, like this one from 2003. -Raymond]
  5. Myria says:

    My GDI-Fu is not good…  On pre-DWM Windows versions, would this have drawn to the screen regardless of whether your window's client area was on top of it?  Or does the HDC clip?

    [Not sure how DWM factors into the matter. Paint DCs have always been clipped to the window dirty region. Asking this question suggest that you aren't really all that familiar with the Windows painting model; you might want to read up on that first. -Raymond]
  6. anyfoo says:

    While most of your articles are interesting (and I'm really thankful for the hours of informative entertainment you've given us), I do miss the tales of past times a little. Granted, 16bit Windows input queues were mentioned a few times…

  7. [Your solution does not generalize well. -Raymond]

    Right. It depends on the complexity of painting and how you calculate the coordinates of the objects. If you have to draw several items at fixed positions, SetWindowOrgEx() is the way to go. If the drawing is simple (as in this sample), or if you calculate coordinates on the fly, subtracting ptOrigin may be better.

  8. Neil says:

    Last time I looked (may have been Windows 3.1) InvalidateRect(hWnd, 0, TRUE); causes the non-client area to be redrawn too. Better to get the client rect and just invalidate that.

    [I'm pretty sure that's never been true. I just wrote a test app to confirm. Invalidating with Invalidate­Rect(hWnd, 0, TRUE) does not generate a WM_NC­PAINT. Maybe you're doing something else to force a nonclient paint, like changing activation. -Raymond]
  9. icabod says:

    I have a similar query to Simon Buchan.

    With the caveat that the internals are likely to change, are SetViewportOrgEx and SetWindowOrgEx essentially implemented the same way?  Or is there some other internal that's different?

  10. Should the scratch program (unmodified) fail on the line:

         if (SUCCEEDED(CoInitialize(NULL))) {/* In case we use COM */

    and not show the window? Just wondering if this is an artifact of using Express version of VS 2010. Everything else compiles/links/etc and runs.

Comments are closed.