How can I have a window that rejects activation but still receives pointer input?

Date:September 12, 2016 / year-entry #193
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20160912-00/?p=94295
Comments:    14
Summary:The obscure return values of the WM_MOUSEACTIVATE message.

A customer had a dedicated system with two touch screens. One screen was covered by the main app window, and the other was covered by a secondary window. They needed focus to remain on the main app window because reasons.¹

One way of preventing the secondary window from getting focus is to use the WS_EX_NO­ACTIVATE extended style. Another way is to disable it. However, these cause the secondary window to ignore input, but the customer also wanted the user to be able to interact with the secondary window. Can they have their cake and eat it too?

Let's start with the new scratch program and make these changes. The first set of changes is basically the stuff we did in an earlier article to turn the main window into a logging window.

#include <strsafe.h>

class RootWindow : public Window
{
public:
 ...
 void AppendText(LPCTSTR psz)
 {
  ListBox_SetCurSel(m_hwndChild,
      ListBox_AddString(m_hwndChild, psz));
 }

 void LogMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
  TCHAR szMsg[80];
  StringCchPrintf(szMsg, 80, TEXT("%d\t0x%04x\t%p\t%p"),
      GetTickCount(),
      uMsg,
      wParam,
      lParam);
  AppendText(szMsg);
 }
 ...
};

The logging comes from the side window:

class SideWindow : public Window
{
public:
 SideWindow(RootWindow* prw) : m_prw(prw) {}
 virtual LPCTSTR ClassName() { return TEXT("SideWindow"); }
 static SideWindow *Create(RootWindow* prw);
protected:
 LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
 RootWindow* m_prw;
};

LRESULT SideWindow::HandleMessage(
 UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 switch (uMsg) {
 case WM_MOUSEACTIVATE:
  m_prw->LogMessage(uMsg, wParam, lParam);
  return MA_NOACTIVATE;
 case WM_MOUSEMOVE:
 case WM_LBUTTONDOWN:
 case WM_LBUTTONUP:
  m_prw->LogMessage(uMsg, wParam, lParam);
  break;
 }

 return __super::HandleMessage(uMsg, wParam, lParam);
}

SideWindow *SideWindow::Create(RootWindow* prw)
{
 SideWindow *self = new SideWindow(prw);
 if (self && self->WinCreateWindow(0,
     TEXT("SideWindow"), WS_OVERLAPPEDWINDOW,
     CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
     NULL, NULL)) {
     return self;
 }
 delete self;
 return NULL;
}

The side window logs selected mouse messages so we can see what's going on. The interesting thing is that it responds to the WM_MOUSE­ACTIVATE with MA_NO­ACTIVATE, which means, "Thanks for your interest in my window, but I decline your offer to activate me." Another way to decline activation is to return MA_NO­ACTIVATE­AND­EAT, which goes a step further and says, "Throw away the input that caused you to want to activate this window." That's not what we want today, because we want to keep the input; we simply don't want activation.

Let's finish up the program before discussing further.

int PASCAL
WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShowCmd)
{
 ...
  RootWindow *prw = RootWindow::Create();
  if (prw) {
      ShowWindow(prw->GetHWND(), nShowCmd);
      SideWindow *sw = SideWindow::Create(prw);
      ShowWindow(sw->GetHWND(), SW_SHOWNA);
      MSG msg;
      while (GetMessage(&msg, NULL, 0, 0)) {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
      }
  }
  ...
}

Okay, run this program, and it will open two windows. (I didn't bother putting each one on a separate monitor. You can use your imagination.) While focus is on the main window, use your finger or moues to click on the second window. Observe that the second window does not activate, but the logging window shows that it did receive the WM_LBUTTON­DOWN = 0x0201 message. Drag your finger over the window, or drag the mouse, and you'll see the WM_MOUSE­MOVE = 0x0200 messages, and you'll get a WM_LBUTTON­UP = 0x0202 message when the pointer goes up.

So there you have it: A window that rejects activation but stil receives touch and mouse input.


Comments (14)
  1. Zarat says:

    Is the footnote missing? Anyways, the requirement isn’t so obscure when you have mixed input scenarios, you want keyboard input go to the main application so it should keep focus even if you interact with the secondary window via mouse or touch.

  2. Brian_EE says:

    The first paragraph has a footnote reference at the end, but there was no corresponding footnote.

  3. Ray Koopa says:

    Pac-Man would like to receive MA_NO­ACTIVATE­AND­EAT

  4. Kevin Fee says:

    And a minor error in the second to last paragraph: “use your finger or moues”. I’m amazed the spell checker doesn’t pick that up, but apparently a ‘moue’ is a ‘small grimace or expression to convey annoyance’. Learn something new every day.

    1. Joe says:

      Maybe Microsoft is working on a new “Moues” device and Raymond is testing it.

    2. You should but pick up all your nits, not just those you like: “stil” in the last sentence.-P

    3. French Guy says:

      It’s a French word. As for selective nit-picking, I assume Kevin didn’t spot “stil”. I didn’t, either, and I missed “moues” as well.

  5. Piotr Siódmak says:

    https://msdn.microsoft.com/en-us/library/ms997507.aspx
    “If the layered window has the WS_EX_TRANSPARENT extended window style, the shape of the layered window will be ignored and the mouse events will be passed to the other windows underneath the layered window.”
    You can still make the WS_EX_TRANSPARENT window opaque with SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);

    A neat little application called net meter uses this to create a network usage chart that’s always on top and you can click through it.

  6. smf says:

    I can think of many reasons I’d want to do this….
    If the second monitor is purely for clicking on things, while the first has text fields for entry for instance.

    It could also be they are hosting an external component that fails if the window loses focus. There is obviously something odd going on when they managed to get budget for two touch screens.

    1. ender says:

      I wouldn’t be too surprised if this was an ultrasound – the ones I worked with have a large regular monitor mounted above, and a smaller touchscreen built-in to the control panel, and the touchscreen seems to behave this way.

  7. alegr1 says:

    This is what a scroll-bar control is using.

  8. Neil says:

    I wish more apps with floating palettes did this; the flicker as they “restore” activation back to their main window is really annoying.

  9. Yukkuri says:

    The missing footnote is quite the subtle joke, took me a minute to get it.

  10. Corey Stup says:

    I was just needing a way to do this. Thank you!

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index