News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

How to receive mouse messages for the entire screen?

Started by NoCforMe, March 01, 2014, 02:35:15 PM

Previous topic - Next topic

NoCforMe

Well, I'm back. (Apparently I stayed away long enough for my account to be canceled.)

My question this time is this: how can I tap into mouse messages for the entire Windows screen?

What I'm thinking of are programs that can use the mouse outside of their own windows/client areas. One example that I use is my paint program, Paint Shop Pro, which has a screen-capture function that allows me to marquee-select any area of any window on the desktop. Dunno how they do this, but it works.

I thought of trying to subclass the desktop window:

; Subclass the desktop:
CALL GetDesktopWindow
MOV DesktopHandle, EAX
INVOKE SetWindowLong, DesktopHandle, GWL_WNDPROC, ADDR DesktopProc
MOV DesktopProcPtr, EAX


but then I read more about SetWindowLong(): if you want to change the address of the window procedure for a window, "You cannot change this attribute if the window does not belong to the same process as the calling thread." D'oh!

So how could I do this? What I'm trying to do is pretty simple: I'd just like to be able to maquee-select an area of the screen, then display the dimensions in pixels of this area. Seems like that should be pretty simple, no?

Edit: By "marquee select" what I mean is to click anywhere on the screen and drag an outline, then measure the rectangle, possibly while being dragged, or after the mouse button is let go.

Thanks in advance for any help!

(Oh, and I did try traditional mouse capture (via SetCapture() ); couldn't get that to work either.)
Assembly language programming should be fun. That's why I do it.

dedndave

i don't think they let you subclass the desktop window
but, you might be able capture the mouse (problem - if the OS or some other program captures, you lose capture)
maybe a hook on the mouse ?
the WindowFromPoint function might also be useful

NoCforMe

Quote from: dedndave on March 01, 2014, 02:58:39 PM
the WindowFromPoint function might also be useful

Hmm; not sure how knowing which window the mouse is over could be useful. All I know is that PSP lets you click and drag an outline anywhere on the screen (as well as capture what's inside the rectangle).
Assembly language programming should be fun. That's why I do it.

MichaelW

One method is to capture the mouse.

;==============================================================================
; Build as a console app.
;==============================================================================
include \masm32\include\masm32rt.inc
;==============================================================================
    .data
    .code
;==============================================================================
DlgProc proc hwndDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    SWITCH uMsg
        CASE WM_INITDIALOG
        CASE WM_LBUTTONDOWN
            invoke SetCapture, hwndDlg
        CASE WM_LBUTTONUP
            invoke ReleaseCapture
        CASE WM_MOUSEMOVE
            loc 0,0
            ;----------------------------------------------------------
            ; The 16-bit cursor coordinates returned in lParam must be
            ; sign-extended to 32 bits because client coordinates to
            ; the left of and/or above the client area are negative.
            ;----------------------------------------------------------
            mov eax, lParam
            mov edx, eax
            movsx eax, ax
            shr edx, 16
            movsx edx, dx
            printf("%d     %d     ",eax,edx)
        CASE WM_COMMAND
            SWITCH wParam
                CASE IDCANCEL
                    invoke EndDialog, hwndDlg, NULL
            ENDSW
        CASE WM_CLOSE
            invoke EndDialog, hwndDlg, NULL
    ENDSW
    return 0
DlgProc endp
;==============================================================================
start:
;==============================================================================
    Dialog "Test", \
           "MS Sans Serif",10, \
           WS_OVERLAPPED or WS_SYSMENU or DS_CENTER, \
           0,0,0,80,60,1024
    CallModalDialog NULL,0,DlgProc,NULL
    exit
;==============================================================================
end start


I have used this technique to capture a window to a bitmap file, but I have never tried dragging a selection rectangle, or anything similar.


Well Microsoft, here's another nice mess you've gotten us into.

hutch--

Have a look at a tool in the "tools" directory of MASM32 called "Getcolor". It shows how to use GetCapture".

NoCforMe

I don't seem to have that program in my tools folder. I got MASM32 more than a year ago; has it been added since then?. Any chance you could post a zip of it here? (Don't want to reload the package just yet.)

MichaelW, I don't see how that could work for me. Wouldn't the mouse click (resulting in WM_LBUTTONDOWN being received) have to be in one of MY windows? Otherwise, I won't get this message and won't be able to capture the mouse. Or am I missing something there? Remember, I want to be able to click anywhere on the screen, not necessarily in any of my program's areas.
Assembly language programming should be fun. That's why I do it.

dedndave

when a window has mouse capture, all mouse messages (movement, clicks) are sent to the window that has capture

the only trick to mouse capture is understanding the relationship with WM_CAPTURECHANGED
i have played with it quite a bit (movable marker lines, sizable windows, etc)
rather than executing "release" code, directly....
you just call ReleaseCapture
then put the "release" code in the WM_CAPTURECHANGED handler
that way, if the system releases capture (for whatever reason) - or another window grabs capture, your release code is executed

i mentioned WindowFromPoint because of the following scenerio....
let's say you have capture - all the mouse messages come to your window
in some cases, you may want to pass those messages to the window that would have received them if you didn't have capture
so, you could get WindowFromPoint, then SendMessage to "forward" messages

i really think that a mouse hook is more in order for this, though - you'll have to experiment

http://msdn.microsoft.com/en-us/library/windows/desktop/ms632589%28v=vs.85%29.aspx

the problem is - the Desktop uses the drag-selection outline to select a group of icons
that's very different from selecting a graphic region
it might require some sort of Shell Extension Handler

http://msdn.microsoft.com/en-us/library/windows/desktop/cc144067%28v=vs.85%29.aspx

hopefully, that gives you some ideas   :biggrin:


dedndave

if you find that mouse capture is workable....

here is a simple example that demonstrates the use of WM_CAPTURECHANGED
it uses mouse capture to create movable dividers

qWord

Quote from: NoCforMe on March 01, 2014, 02:35:15 PMEdit: By "marquee select" what I mean is to click anywhere on the screen and drag an outline, then measure the rectangle, possibly while being dragged, or after the mouse button is let go.
A simple solution would be to create a snapshot of the current desktop, which is present in a popup window that covers the desktop. The actual selection/measurement is then done by the popup window.
If the content of the Desktop does change while "measurement", layered windows may be a solution (e.g. a semi-transparent overlay).
MREAL macros - when you need floating point arithmetic while assembling!

farrier

For mouse capture info, I got a lot of mileage from Iczelion's tutorial 24.  It had everything and more than what I needed.

hth,

farrier
For the code is dark, and full of errors!
It's a good day to code!
Don't Bogart that code, my friend!

jj2007

Quote from: qWord on March 02, 2014, 11:57:52 AMA simple solution would be to create a snapshot of the current desktop, which is present in a popup window that covers the desktop. The actual selection/measurement is then done by the popup window.

Similar: A transparent window that draws rectangles using GetWindowDC. I attach a draft version in plain Masm32 - no time to finish it, but you'll get the idea.

TouEnMasm

Fa is a musical note to play with CL

qWord

In the attachment an example using layered windows. The trick is to create an semi-transparent overlay whereas the alpha value is nonzero - due this all mouse messages are send to the overlay window.

include \masm32\include\masm32rt.inc
.686
.mmx
.xmm

IDBTN_SELECT        EQU 100
IDCTL_SELECTION     EQU 101
COLOR_KEY           EQU 0ff00ffh
CONST_ALPHA         EQU 40          ; for 32bpp: 1+, 16bpp: 5+
COLOR_OVERLAY       EQU 0333333h
DELTA_WAIT          EQU 100
FRAME_WIDTH         EQU 3
FRAME_COLOR         EQU 0ffh
WM_NEW_SELECTION    EQU WM_USER+100

WndProc proto hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
OverlayProc proto hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
FrameProc proto hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

.const
    szMainClass     db "Win32Class",0
    szOverlayClass  db "OverlayClass32",0
    szFrameClass    db "FrameClass32",0
.data?
    g_hInstance     HINSTANCE ?

    ;/* gloabl variables for simplification :-D */
    g_hMainWnd      HWND ?
    g_hOverlayWnd   HWND ?
    g_hStaticImg    HWND ?
    g_hStaticTxt    HWND ?
    g_hWndFrame     HWND ?
    g_rcFrame       RECT <>

.code
main proc
LOCAL wcex:WNDCLASSEX
LOCAL msg:MSG

    mov g_hInstance,rv(GetModuleHandle,0)
    mov wcex.hInstance,eax
    mov wcex.cbSize,SIZEOF WNDCLASSEX
    mov wcex.style, CS_HREDRAW or CS_VREDRAW
    mov wcex.lpfnWndProc, OFFSET WndProc
    mov wcex.cbClsExtra,NULL
    mov wcex.cbWndExtra,0
    mov wcex.hbrBackground,rv(GetStockObject,WHITE_BRUSH)
    mov wcex.lpszMenuName,NULL
    mov wcex.lpszClassName,OFFSET szMainClass
    mov wcex.hIcon,rv(LoadIcon,NULL,IDI_APPLICATION)
    mov wcex.hIconSm,eax
    mov wcex.hCursor,rv(LoadCursor,NULL,IDC_ARROW)
    invoke RegisterClassEx, ADDR wcex
    mov wcex.lpfnWndProc, OFFSET OverlayProc
    mov wcex.hbrBackground,0
    mov wcex.lpszClassName,OFFSET szOverlayClass
    invoke RegisterClassEx, ADDR wcex
    mov wcex.lpfnWndProc, OFFSET FrameProc
    mov wcex.lpszClassName,OFFSET szFrameClass
    invoke RegisterClassEx, ADDR wcex
    fn CreateWindowEx,0,OFFSET szMainClass,"select region",WS_VISIBLE or WS_SYSMENU or WS_SIZEBOX or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_CLIPCHILDREN,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,g_hInstance,0

    .while 1
        invoke GetMessage,ADDR msg,NULL,0,0
        .break .if !eax || eax == -1
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .endw

    invoke ExitProcess,msg.wParam

main endp

;/* make sure that left <= right and top <= bottom */
checkRect proc pRect: ptr RECT

    mov eax,pRect
    mov ecx,[eax].RECT.left
    mov edx,[eax].RECT.right
    .if ecx > edx
        mov [eax].RECT.left,edx
        mov [eax].RECT.right,ecx
    .endif
    mov ecx,[eax].RECT.top
    mov edx,[eax].RECT.bottom
    .if ecx > edx
        mov [eax].RECT.top,edx
        mov [eax].RECT.bottom,ecx
    .endif
    ret

checkRect endp

;/* main window */
WndProc proc uses ebx esi edi hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL sz[128]:CHAR

    .if uMsg == WM_CLOSE
        invoke DestroyWindow,hWnd
    .elseif uMsg == WM_DESTROY
        .if rv(SendMessage,g_hStaticImg,STM_GETIMAGE,IMAGE_BITMAP,0)
            fn DeleteObject,eax
        .endif
        fn PostQuitMessage,NULL
    .elseif uMsg == WM_CREATE
    mrm g_hMainWnd,hWnd
        fn CreateWindowEx,0,"button","Select",WS_VISIBLE or WS_CHILD,0,0,100,30,hWnd,IDBTN_SELECT,g_hInstance,0
        fnx g_hStaticImg = CreateWindowEx,0,"static",0,WS_CHILD or WS_VISIBLE or SS_BITMAP or WS_BORDER,5,35,100,100,hWnd,0,g_hInstance,0
        fnx g_hStaticTxt = CreateWindowEx,0,"static",0,WS_CHILD or WS_VISIBLE,103,0,200,30,hWnd,0,g_hInstance,0
        fn SendMessage,g_hStaticImg,STM_SETIMAGE,IMAGE_BITMAP,rv(CreateCompatibleBitmap,rv(GetDC,0),10,10)
    .elseif uMsg == WM_COMMAND && wParam == BN_CLICKED SHL 16 OR IDBTN_SELECT
        .if !g_hOverlayWnd
            fnx esi = GetSystemMetrics,SM_CXSCREEN
            fnx edi = GetSystemMetrics,SM_CYSCREEN

            ;/* hide this window and create screen-overlay */
            invoke ShowWindow,hWnd,SW_MINIMIZE
            fnx g_hOverlayWnd = CreateWindowEx,WS_EX_LAYERED OR WS_EX_TOPMOST,OFFSET szOverlayClass,0,WS_VISIBLE OR WS_POPUP,0,0,esi,edi,0,0,g_hInstance,0
        .endif
    .elseif uMsg == WM_NEW_SELECTION
        mov g_hOverlayWnd,0
        mov eax,wParam
        .if eax
            ;/* process new selection */
            mov ecx,[eax].RECT.right
            mov edx,[eax].RECT.bottom
            sub ecx,[eax].RECT.left
            sub edx,[eax].RECT.top
            fn wsprintf,&sz,"x: %i, y: %i, w: %i, h: %i",[eax].RECT.left,[eax].RECT.top,ecx,edx
            fn SendMessage,g_hStaticTxt,WM_SETTEXT,0,&sz

            push rv(SendMessage,g_hStaticImg,STM_GETIMAGE,IMAGE_BITMAP,0)
            fn SendMessage,g_hStaticImg,STM_SETIMAGE,IMAGE_BITMAP,lParam
            pop eax
            .if eax
                invoke DeleteObject,eax
            .endif
        .endif
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif

    xor eax,eax
    ret
WndProc endp

;/**
; * This window is transparent and occupies the whole screen.
; * Actual the window is draw with an low alpha value to make sure that
; * mouse messages are received.
; * When dragging starts, a layered window (hit-transparent) is created that
; * shows the selection-frame.
; */
OverlayProc proc uses ebx esi edi hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL ps:PAINTSTRUCT
LOCAL hBr:HBRUSH
LOCAL rect:RECT,w:SDWORD,h:SDWORD

    .if uMsg == WM_CLOSE || (uMsg == WM_KEYDOWN && wParam == VK_ESCAPE)
        fn ShowWindow,g_hMainWnd,SW_RESTORE
        fn SendMessage,g_hMainWnd,WM_NEW_SELECTION,0,0
        fn DestroyWindow,hWnd
    .elseif uMsg == WM_DESTROY
    .elseif uMsg == WM_CREATE
        ;/**
        ; *  Draw window with alpha value > 0 thus
        ; *  mouse messages are send to this window.
        ; */
        invoke SetLayeredWindowAttributes,hWnd,0,CONST_ALPHA,LWA_ALPHA
        invoke SetForegroundWindow,hWnd
        mov g_hWndFrame,0
    .elseif uMsg == WM_LBUTTONDOWN
        .if !g_hWndFrame
            ;/* show frame */
            fnx esi = GetSystemMetrics,SM_CXSCREEN
            fnx edi = GetSystemMetrics,SM_CYSCREEN
            fnx g_hWndFrame = CreateWindowEx,WS_EX_LAYERED or WS_EX_TRANSPARENT or WS_EX_TOPMOST,OFFSET szFrameClass,0,WS_VISIBLE OR WS_POPUP,0,0,1,1,hWnd,0,g_hInstance,0
            fn GetCursorPos,&g_rcFrame
        .endif
    .elseif uMsg == WM_LBUTTONUP
        .if g_hWndFrame
            mov g_hWndFrame,0

            ;/* destroy this window (and g_hWndFrame) */
            invoke DestroyWindow,hWnd
            invoke Sleep,DELTA_WAIT

            ;/* get image from region */
            fn RtlMoveMemory,&rect,&g_rcFrame,RECT
            fn checkRect,&rect
            mov ecx,rect.right
            mov edx,rect.bottom
            sub ecx,rect.left
            sub edx,rect.top
            mov w,ecx
            mov h,edx
            fnx esi = GetDC,0
            fnx ebx = CreateCompatibleDC,esi
            fnx edi = SelectObject,ebx,rv(CreateCompatibleBitmap,esi,w,h)
            invoke BitBlt,ebx,0,0,w,h,esi,rect.left,rect.top,SRCCOPY
            fnx edi = SelectObject,ebx,edi
            fn DeleteDC,ebx

            ;/* send result */
            fn SendMessage,g_hMainWnd,WM_NEW_SELECTION,&rect,edi

            ;/* show main-window */
            invoke ShowWindow,g_hMainWnd,SW_RESTORE
        .endif
    .elseif uMsg == WM_MOUSEMOVE
        .if g_hWndFrame
            ;/* update window position (frame) */
            fn GetCursorPos,&g_rcFrame.right
            fn RtlMoveMemory,&rect,&g_rcFrame,RECT
            fn checkRect,&rect
            mov ecx,rect.right
            mov edx,rect.bottom
            sub ecx,rect.left
            sub edx,rect.top
            invoke SetWindowPos,g_hWndFrame,0,rect.left,rect.top,ecx,edx,SWP_NOZORDER
        .endif
    .elseif uMsg == WM_PAINT
        invoke BeginPaint,hWnd,ADDR ps
        mov ecx,ps.rcPaint.right
        mov edx,ps.rcPaint.bottom
        sub ecx,ps.rcPaint.left
        sub edx,ps.rcPaint.top
        mov rect.right,ecx
        mov rect.bottom,edx

        fnx ebx = CreateCompatibleDC,ps.hdc
        fnx edi = SelectObject,ebx,rv(CreateCompatibleBitmap,ps.hdc,rect.right,rect.bottom)

        fnx hBr = SelectObject,ebx,rv(CreateSolidBrush,COLOR_OVERLAY)
        invoke BitBlt,ebx,0,0,rect.right,rect.bottom,ebx,0,0,PATCOPY
        fn DeleteObject,rv(SelectObject,ebx,hBr)
       
        fn BitBlt,ps.hdc,ps.rcPaint.left,ps.rcPaint.top,rect.right,rect.bottom,ebx,0,0,SRCCOPY
        fn DeleteObject,rv(SelectObject,ebx,edi)
        fn DeleteDC,ebx

        invoke EndPaint,hWnd,ADDR ps
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif

    xor eax,eax
    ret

OverlayProc endp

;/* This window is hit-transparent and shows the selection-frame */
FrameProc proc uses ebx esi edi hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL ps:PAINTSTRUCT
LOCAL hBr:HBRUSH,hPen:HPEN
LOCAL rect:RECT

    .if uMsg == WM_DESTROY
    .elseif uMsg == WM_CREATE
        invoke SetLayeredWindowAttributes,hWnd,COLOR_KEY,128,LWA_COLORKEY OR LWA_ALPHA
    .elseif uMsg == WM_PAINT
        fn GetClientRect,hWnd,&rect
        invoke BeginPaint,hWnd,ADDR ps
        fnx ebx = CreateCompatibleDC,ps.hdc
        fnx edi = SelectObject,ebx,rv(CreateCompatibleBitmap,ps.hdc,rect.right,rect.bottom)

        fnx hBr = SelectObject,ebx,rv(CreateSolidBrush,COLOR_KEY)
        invoke BitBlt,ebx,0,0,rect.right,rect.bottom,ebx,0,0,PATCOPY
        fnx hPen = SelectObject,ebx,rv(CreatePen,PS_SOLID,FRAME_WIDTH,FRAME_COLOR)

        invoke Rectangle,ebx,rect.left,rect.top,rect.right,rect.bottom
        fn DeleteObject,rv(SelectObject,ebx,hPen)
        fn DeleteObject,rv(SelectObject,ebx,hBr)

        fn BitBlt,ps.hdc,0,0,rect.right,rect.bottom,ebx,0,0,SRCCOPY
        fn DeleteObject,rv(SelectObject,ebx,edi)
        fn DeleteDC,ebx

        invoke EndPaint,hWnd,ADDR ps
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
FrameProc endp
end main


Quote from: jj2007 on March 02, 2014, 01:03:17 PMSimilar: A transparent window that draws rectangles using GetWindowDC.
you might describe that more detailed.
MREAL macros - when you need floating point arithmetic while assembling!

jj2007

#13
Quote from: qWord on March 03, 2014, 05:43:33 AM
Quote from: jj2007 on March 02, 2014, 01:03:17 PMSimilar: A transparent window that draws rectangles using GetWindowDC.
you might describe that more detailed.

Basically, you paint rectangles on a window that doesn't paint its background.

Simple example attached, in plain Masm32 (exe, asm and asc for the proud RichMasm user ;)). Actually, I needed this app for something completely unrelated to this thread, so thanks to the OP for pushing me a little bit :t

EDIT: Version 2 leaves W=nnn, H=nnn on top right corner of each rectangle.

qWord

Quote from: jj2007 on March 03, 2014, 07:00:06 PMBasically, you paint rectangles on a window that doesn't paint its background.
That method does no longer work due to the desktop window manager (vista+), which compose the final image from buffers - the applications no longer draw directly to the screen DC.
MREAL macros - when you need floating point arithmetic while assembling!