1

I am trying to implement Pointer Input Messages to replace regular mouse message in a window. I'm doing this to have a better support of stylus input in my program. Everything works fine except for double-click.

I didn't process pointer message before, so these pointer messages posted by stylus driver were just passed to DefWindowProc and DefWindowProc just generated mouse input like WM_LBUTTONDBLCLK.

Unlike mouse message WM_LBUTTONDBLCLK, there is no pointer message that would explicitly tell you it's a double click. I understand their intention of designing a concise group of messages and make everything else in a clean single structure. POINTER_PEN_INFO is that struct which contains all information associated with the current message. I thought I could find anything there, maybe some flags to indicate that a WM_POINTERDOWN message should be treated as a double click, but nothing is there as well.

Is there anything I missed? If not, what else could I do to detect a double-click? I could only find some antiquated documents that was written for Window XP on MSDN. I'm programming on Windows 10, Win32 API programming with C++.

Thank you!

Asesh
  • 3,186
  • 2
  • 21
  • 31
Jacob
  • 43
  • 8
  • 1
    Every time you get `WM_POINTERDOWN` compare the timestamp (`GetMessageTime()`) against the previous one. If it's less than `GetDoubleClickTime()` treat it as a double-click, otherwise save the timestamp for comparison next time. – Jonathan Potter Apr 27 '18 at 06:56
  • Yes, that's good work around @Jonathan Potter ! But manually detection is the last option I'd like to choose. Not only the time, but also the distance between two taps should be considered. The restriction of this maximum distance varies depending on resolution and monitor. I have to transform the pixel distance to unified physical distance. Besides, it's better not to define this time and distance limitation in code. These setting should comply with system. – Jacob Apr 27 '18 at 07:25
  • `GetSystemMetrics(SM_CXDOUBLECLICK)` and `GetSystemMetrics(SM_CYDOUBLECLICK)` are the values the OS uses. – Jonathan Potter Apr 27 '18 at 07:38
  • @JonathanPotter Thank you! You are right. I thought windows would do more advanced calculations. I will do it in this way. Please post an answer for me to vote if you don't mind. – Jacob Apr 27 '18 at 07:52
  • Do you also need to isolate single-clicks? If so, the logic is a bit more complex. You'd need to set up a timer for that. – IInspectable Apr 27 '18 at 08:25
  • Note you may be better off using the Windows Touch interface: https://msdn.microsoft.com/en-us/library/windows/desktop/dd940543(v=vs.85).aspx – Jonathan Potter Apr 27 '18 at 10:09
  • The gesture interface may appear to provide the functionality you are after. I would still advise against using it (unless things have considerably changed since Windows 7). From my experience (with Windows 7), the gestures did work reliably, yet produced a really poor user experience. Response times were always beyond acceptable levels, making the UI appear to be unresponsive and generally sluggish. And there is no way for you to improve the situation (e.g. by disabling palm rejection). It's also exclusive: Either `WM_GESTURE` or `WM_TOUCH`. I don't know whether it interferes with... – IInspectable Apr 27 '18 at 10:24
  • `WM_POINTER`, or whether the gesture interface is still supported in Windows 10. – IInspectable Apr 27 '18 at 10:24

1 Answers1

1

You can do this by tracking clicks and comparing each click to the last in the same way that Windows does.

Pseudo-code:

POINT ptLastClickPos;
DWORD dwLastClickTime;

if (uMsg == WM_POINTERDOWN)
{
    DWORD dwClickTime = GetMessageTime();
    POINT ptClickPos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };

    if (dwLastClickTime + GetDoubleClickTime() > dwClickTime
    &&  abs(ptLastClickPos.x - ptClickPos.x) < GetSystemMetrics(SM_CXDOUBLECLK)
    &&  abs(ptLastClickPos.y - ptClickPos.y) < GetSystemMetrics(SM_CYDOUBLECLK))
    {
        // double-click!
    }
    else
    {
        dwLastClickTime = dwClickTime;
        ptLastClickPos = ptClickPos;
    }
}
Community
  • 1
  • 1
Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • The `xxLastXxx` variables need static storage duration, or least outlive the input handling code. I believe the double-tap rectangle is larger than the double-click rectangle. I am not aware of an API that returns the double-tap rectangle, although that may well exist. I am not sure, whether the double-tap timeout is identical to the double-click timeout, so that may need to be addressed as well. – IInspectable Apr 27 '18 at 08:18
  • @IInspectable I agree. I tried to change double-tap setting for pen and double-click setting for mouse in control panel. Both `GetSystemMetrics(SM_CXDOUBLECLK)` and `GetDoubleClickTime()` return the value of mouse not pen. The default spatial tolerance I get from `GetSystemMetrics(SM_CXDOUBLECLK)`( for mouse double-click) is 4 pixels, double-tap rectangle should be much larger than that, no one could easily create a double-tap with 4 pixels tolerance using pen. – Jacob Apr 27 '18 at 09:28
  • As an aside, the timing code is sensitive to timer rollover effects. A simple fix has been published by Raymond Chen in his blog entry [Implementing higher-order clicks](https://blogs.msdn.microsoft.com/oldnewthing/20041018-00/?p=37543). I believe it is based on the fact, that conversion from a signed integer to an unsigned integer is well defined in C++, whereas an integer overflow is not. – IInspectable Apr 27 '18 at 10:04
  • @IInspectable "psuedo-code" means... psuedo-code :) – Jonathan Potter Apr 27 '18 at 10:05
  • I understand. But the pseudo part should really only apply to the implementation. As posted, we have no way of telling, which part is to be taken literally, and which part isn't, even more so, as it mostly isn't pseudo code. – IInspectable Apr 27 '18 at 10:13
  • The comparison should be inclusive, `>=` and `<=`, according to my checks. – Paul Apr 22 '23 at 12:21