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


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.

Skip to main content