summaryrefslogtreecommitdiff
path: root/zen/scroll_window_under_cursor.cpp
blob: f201bb04e0d6f2d0052ca88e540ed0ec40cfc847 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html       *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
// **************************************************************************

//redirect mouse wheel events directly to window under cursor rather than window having input focus
//implementing new Windows Vista UI guidelines: http://msdn.microsoft.com/en-us/library/bb545459.aspx#wheel
//this is confirmed to be required for at least Windows 2000 to Windows 8
//on Ubuntu Linux, this is already the default behavior

//Usage: just include this file into a Windows project

#include <cassert>
#include "win.h"     //includes "windows.h"
#include "Windowsx.h" //WM_MOUSEWHEEL


namespace
{
#ifndef WM_MOUSEHWHEEL //MinGW is clueless...
#define WM_MOUSEHWHEEL                  0x020E
#endif

LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam)
{
    //"if nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function
    //without further processing and should return the value returned by CallNextHookEx"
    if (nCode == HC_ACTION) //the only valid value for this hook type
    {
        MSG& msgInfo = *reinterpret_cast<MSG*>(lParam);

        if (msgInfo.message == WM_MOUSEWHEEL ||
            msgInfo.message == WM_MOUSEHWHEEL)
        {
            POINT pt = {};
            pt.x = GET_X_LPARAM(msgInfo.lParam); //yes, there's also msgInfo.pt, but let's not take chances
            pt.y = GET_Y_LPARAM(msgInfo.lParam); //

            //visible child window directly under cursor; attention: not necessarily from our process!
            if (HWND hWin = ::WindowFromPoint(pt)) //http://blogs.msdn.com/b/oldnewthing/archive/2010/12/30/10110077.aspx
                if (msgInfo.hwnd != hWin && ::GetCapture() == nullptr)
                {
                    DWORD winProcessId = 0;
                    ::GetWindowThreadProcessId( //no-fail!
                        hWin,           //_In_       HWND hWnd,
                        &winProcessId); //_Out_opt_  LPDWORD lpdwProcessId
                    if (winProcessId == ::GetCurrentProcessId()) //no-fail!
                        msgInfo.hwnd = hWin; //it would be a bug to set handle from another process here
                }
        }
    }
    return ::CallNextHookEx(nullptr, nCode, wParam, lParam);
}

struct Dummy
{
    Dummy()
    {
        hHook = ::SetWindowsHookEx(WH_GETMESSAGE,  //__in  int idHook,
                                   mouseInputHook, //__in  HOOKPROC lpfn,
                                   nullptr,        //__in  HINSTANCE hMod,
                                   ::GetCurrentThreadId()); //__in  DWORD dwThreadId
        assert(hHook);
    }

    ~Dummy()
    {
        if (hHook)
            ::UnhookWindowsHookEx(hHook);
    }

private:
    HHOOK hHook;
} dummy;
}
bgstack15