mouse pointer hit box via fb, winapi and sdl2

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
thrive4
Posts: 70
Joined: Jun 25, 2021 15:32

mouse pointer hit box via fb, winapi and sdl2

Post by thrive4 »

Well 'hit box' is a bit ambiguous but what is meant
a boundary where the current coordinates of the mouse
pointer resides.

I have added three code examples to illustrate the
dare I say 'pro and cons' of these implementations.
Should also note there are also other routes as always...

Before setting up a 'hit box' we'll need to
get the mouse pointer coordinates this is where
the implementations differ.

a) fb basically GetMouse (see manual for more information)
- multi platform
- no additional libs required
- only works in focused window?
- only returns relative coordinates not absolute

a) winapi GetCursorPos
https://docs.microsoft.com/en-us/window ... tcursorpos
- not multi platform
- needs windows.bi
- works on whole desktop regardless of focused window
- only returns absolute coordinates not relative
however can be mitigated by ScreenToClient
https://docs.microsoft.com/en-us/window ... entoclient
- of course there are many more mouse related calls

c) sld2 SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN etc
https://wiki.libsdl.org/CategoryMouse
- multi platform
- needs sdl2/sdl.bi
- only works in focused window
- can return absolute and relative coordinates (bit funky though)
- native support of SDL_PointInRect (surprise that's where I got it from..)

Ok so much for mouse pointers and buttons.
All three implementations utilize more or
less the same method of detecting whether or
not the mouse is in a specific area and of course
can be combined by the mouse button press.

Sdl's rect, possibly, could be overloaded with
say .visible .color etc the freebasic user-defined type
allows that flexibility quite elegantly.

FB implementation

Code: Select all

' based on code contained in fb manual for getmouse
' added example hitbox detection by thrive4 2022

' init screen
dim screenwidth As integer   = 640
dim screenheight As integer  = 480

' init hit box and cordinates
Type rect
    x As integer
    y As integer
    w As integer
    h As integer
End Type

Dim boxa As rect
dim boxb as rect
dim boxc as rect

boxa.x = 100
boxa.y = 100
boxa.w = 100
boxa.h = 100

boxb.x = 300
boxb.y = 100
boxb.w = 100
boxb.h = 100

' example proportional placing hit box
boxc.x = (screenwidth * 0.5) - ((screenwidth * 0.35f) * 0.5f)
boxc.y = screenheight - screenheight * 0.15f
boxc.w = screenwidth * 0.35f
boxc.h = screenheight * 0.15f

' mouse pointer used for pointinrect
dim mouse as rect
mouse.x = 0
mouse.y = 0
mouse.w = 0
mouse.h = 0

' lifted from line 104 https://github.com/libsdl-org/SDL/blob/2b3c16eb5b3af0829bf5b18966a31dd16dbecad2/include/SDL_rect.h
function hitbox(p as rect, r as rect) as boolean
    if ((p.x >= r.x) and (p.x < (r.x + r.w)) and (p.y >= r.y) and (p.y < (r.y + r.h))) then 
        return true
    else
        return false
    end if
end function

' init mouse feedback
Dim As Integer x, y, buttons, res 

' Set video mode and enter loop
ScreenRes screenwidth, screenheight, 8

' draw hit boxes
Line (boxa.x, boxa.y) - (boxa.x + boxa.w, boxa.y + boxa.h), 15, b
Line (boxb.x, boxb.y) - (boxb.x + boxb.w, boxb.y + boxb.h), 15, b
Line (boxc.x, boxc.y) - (boxc.x + boxc.w, boxc.y + boxc.h), 15, b

' get mouse x, y and buttons. discard wheel position.
Do
    res = GetMouse (x, y, , buttons)
    Locate 1, 1
    If res <> 0 Then '' Failure

#ifdef __FB_DOS__
    Print "Mouse or mouse driver not available"
#else
    Print "Mouse not available or not on window"
#endif

    Else
        Print Using "mouse position: ###:###  buttons: "; x; y;
        If buttons And 1 Then Print "l";
        If buttons And 2 Then Print "r";
        If buttons And 4 Then Print "m";
        ' method 1
        'if x > boxa.x and x < boxa.x + boxa.w and y > boxa.y and y < boxa.y + boxa.h then print "boxa";
        'if x > boxb.x and x < boxb.x + boxb.w and y > boxb.y and y < boxb.y + boxb.h then print "boxb";
        'if x > boxc.x and x < boxc.x + boxc.w and y > boxc.y and y < boxc.y + boxc.h then print "boxc";
        ' method 2 emulate SDL_PointInRect
        mouse.x = x
        mouse.y = y
        select case true
            case hitbox(mouse, boxa)
                print "boxa"
            case hitbox(mouse, boxb)
                print "boxb"
            case hitbox(mouse, boxc)
                print "boxc"
            case else
        end select
        Print "   "
    End If

    ' use sleep to keep cpu usage low
    sleep(60)

Loop While Inkey = ""

End
winapi implementation

Code: Select all

' based on code contained in fb manual for getmouse
' modified for win api
' added example hitbox detection by thrive4 2022

' windows mouse
#Include "windows.bi"
Dim vpoint as Point
' needed to get handle with FindWindowA
' or use appname = mid(command(0), instrrev(command(0), "\") + 1)
dim title as string = "mouse_winapi_tut"
Windowtitle title

' init screen
dim screenwidth As integer   = 640
dim screenheight As integer  = 480

' init hit box and cordinates
Type rectA
    x As integer
    y As integer
    w As integer
    h As integer
End Type

Dim boxa As rectA
dim boxb as rectA
dim boxc as rectA

boxa.x = 100
boxa.y = 100
boxa.w = 100
boxa.h = 100

boxb.x = 300
boxb.y = 100
boxb.w = 100
boxb.h = 100

' example proportional placing hit box
boxc.x = (screenwidth * 0.5) - ((screenwidth * 0.35f) * 0.5f)
boxc.y = screenheight - screenheight * 0.15f
boxc.w = screenwidth * 0.35f
boxc.h = screenheight * 0.15f

' mouse pointer used for pointinrect
dim mouse as rectA
mouse.x = 0
mouse.y = 0
mouse.w = 0
mouse.h = 0

' lifted from line 104 https://github.com/libsdl-org/SDL/blob/2b3c16eb5b3af0829bf5b18966a31dd16dbecad2/include/SDL_rect.h
function hitbox(p as rectA, r as rectA) as boolean
    if ((p.x >= r.x) and (p.x < (r.x + r.w)) and (p.y >= r.y) and (p.y < (r.y + r.h))) then 
        return true
    else
        return false
    end if
end function

' Set video mode and enter loop
ScreenRes screenwidth, screenheight, 8

' draw hit boxes
Line (boxa.x, boxa.y) - (boxa.x + boxa.w, boxa.y + boxa.h), 15, b
Line (boxb.x, boxb.y) - (boxb.x + boxb.w, boxb.y + boxb.h), 15, b
Line (boxc.x, boxc.y) - (boxc.x + boxc.w, boxc.y + boxc.h), 15, b

' get mouse x, y and buttons. discard wheel position.
Do
    GetCursorPos(@vpoint)
    ' used for relative mouse pointer position 
    ScreenToClient(FindWindowA(NULL, title), @vpoint)
    Locate 1, 1

    Print Using "mouse position: ###:###  buttons: "; vpoint.x; vpoint.y;
    If GetAsyncKeyState(VK_LBUTTON) < 0 Then Print "l";
    If GetAsyncKeyState(VK_RBUTTON) < 0 Then Print "r";
    If GetAsyncKeyState(VK_MBUTTON) < 0 Then Print "m";
    ' method 1   
    'if vpoint.x > boxa.x and vpoint.x < boxa.x + boxa.w and vpoint.y > boxa.y and vpoint.y < boxa.y + boxa.h then print "boxa";
    'if vpoint.x > boxb.x and vpoint.x < boxb.x + boxb.w and vpoint.y > boxb.y and vpoint.y < boxb.y + boxb.h then print "boxb";
    'if vpoint.x > boxc.x and vpoint.x < boxc.x + boxc.w and vpoint.y > boxc.y and vpoint.y < boxc.y + boxc.h then print "boxc";
    ' method 2 emulate SDL_PointInRect would be more practical to use vpoint directly but ok
    mouse.x = vpoint.x
    mouse.y = vpoint.y
    select case true
        case hitbox(mouse, boxa)
            print "boxa"
        case hitbox(mouse, boxb)
            print "boxb"
        case hitbox(mouse, boxc)
            print "boxc"
        case else
    end select
    Print "   "

    ' use sleep to keep cpu usage low
    sleep(60)
Loop While Inkey = ""


End

sdl2 implementation
compile and run with sdl2.dll in same location as compiled exe
sdl2 dll can be found here:
https://www.libsdl.org/download-2.0.php

Code: Select all

' sdl 2 example hitbox detection with mouse by thrive4 2022

#include once "SDL2/SDL.bi"

' setup sdl
dim event as SDL_Event
dim running as boolean = True
dim mousebutton as string
dim screenwidth As integer   = 640
dim screenheight As integer  = 480
Dim As SDL_Color backgrondcolor = (75, 85, 95, 0)
Dim As SDL_Color boxcolor = (255, 255, 255, 0)
Dim As SDL_Texture Ptr texture
Dim As SDL_Window Ptr glass = SDL_CreateWindow("mouse_sdl2_tut", 100, 100, screenwidth, screenheight, SDL_WINDOW_RESIZABLE)
' used for method 2 hitbox detection
dim mousepos as SDL_Point 
SDL_ShowCursor(SDL_ENABLE)

' init hit box and cordinates
dim boxa as SDL_Rect
boxa.x = 100
boxa.y = 100
boxa.w = 100
boxa.h = 100

dim boxb as SDL_Rect
boxb.x = 300
boxb.y = 100
boxb.w = 100
boxb.h = 100

' example proportional placing hit box
dim boxc as SDL_Rect
boxc.x = (screenwidth * 0.5) - ((screenwidth * 0.35f) * 0.5f)
boxc.y = screenheight - screenheight * 0.15f
boxc.w = screenwidth * 0.35f
boxc.h = screenheight * 0.15f

' renderer
Dim As SDL_Renderer Ptr renderer = SDL_CreateRenderer(glass, -1, SDL_RENDERER_ACCELERATED Or SDL_RENDERER_PRESENTVSYNC)
if (renderer = NULL) Then
    print "sdl2 could not create render"
    SDL_Quit()
End If

' get mouse x, y and buttons
while running
    while SDL_PollEvent(@event) <> 0
        ' basic window interaction 
        select case event.type
            case SDL_KEYDOWN and event.key.keysym.sym = SDLK_ESCAPE
                running = False
                exit while
            case SDL_WINDOWEVENT and event.window.event = SDL_WINDOWEVENT_CLOSE
                running = False
                exit while
            case SDL_MOUSEMOTION
                mousebutton = ""
                ' method 1 note motion used to be mouse
                'if (event.motion.x > boxa.x and event.motion.x < boxa.x + boxa.w and _
                '    event.motion.y > boxa.y and event.motion.y < boxa.y + boxa.h)  then
                '    mousebutton = "mouse in zone boxa"
                'end if
                'if (event.motion.x > boxb.x and event.motion.x < boxb.x + boxb.w and _
                '    event.motion.y > boxb.y and event.motion.y < boxb.y + boxb.h)  then
                '    mousebutton = "mouse in zone boxb"
                'end if
                ' method 2 note booelan true needs to be 1 to evaluate
                mousepos.x = event.motion.x 
                mousepos.y = event.motion.y
                select case 1
                    case SDL_PointInRect(@mousepos, @boxa)
                        mousebutton = "mouse in zone boxa"
                    case SDL_PointInRect(@mousepos, @boxb)
                        mousebutton = "mouse in zone boxb"
                    case SDL_PointInRect(@mousepos, @boxc)
                        mousebutton = "mouse in zone boxc"
                    case else
                        ' nop
                end select

            case SDL_MOUSEBUTTONDOWN
                ' button
                select case event.button.button
                    case SDL_BUTTON_LEFT
                        mousebutton = "left"
                    case SDL_BUTTON_MIDDLE
                        mousebutton = "middle"
                    case SDL_BUTTON_RIGHT
                        mousebutton = "right"
                    case else
                        ' nop
                end select
        end select
    wend
    ' background
    SDL_SetRenderDrawColor(renderer, backgrondcolor.r, backgrondcolor.g, backgrondcolor.b, backgrondcolor.a )

    SDL_RenderClear(renderer)

    ' draw hit boxes
    SDL_SetRenderDrawColor(renderer, boxcolor.r, boxcolor.g, boxcolor.b, boxcolor.a)
    SDL_RenderDrawRect(renderer, @boxa)
    SDL_RenderDrawRect(renderer, @boxb)
    SDL_RenderDrawRect(renderer, @boxc)

    SDL_RenderPresent(renderer)
    SDL_SetWindowTitle(glass, "mouse_sdl2_tut | coord: " &  event.motion.x & ":" & event.motion.y & " | button: " + mousebutton)

    ' use sdl_delay to keep cpu usage low
    SDL_Delay(30)

wend

'clean up sdl
SDL_DestroyTexture(texture)
SDL_DestroyRenderer(renderer)
SDL_DestroyWindow(glass)
SDL_Quit()

End

coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: mouse pointer hit box via fb, winapi and sdl2

Post by coderJeff »

Very nice examples and write up. Good job!

For the windows API example, the HWND of the fb graphics window is exposed through ScreenControl and the following could be used if FindWindowA() could be unreliable due to duplicate window titles:

Code: Select all

#include "fbgfx.bi"

function getFBGfxHWND() as HWND
	dim as integer gfxHWND
	ScreenControl fb.GET_WINDOW_HANDLE, gfxHWND
	return cast( HWND, gfxHWND )
end function
Then later:

Code: Select all

ScreenToClient(getFBGfxHWND(), @vpoint)
thrive4
Posts: 70
Joined: Jun 25, 2021 15:32

Re: mouse pointer hit box via fb, winapi and sdl2

Post by thrive4 »

@coderJeff

A somewhat belated but still sincere thanks for the
feedback and the tip with ScreenControl fb.GET_WINDOW_HANDLE, gfxHWND

I have expanded the code a little here:
viewtopic.php?t=31726

Just wanted to do an extra 'many thanks' for your code
on rounded boxes and the mouse up trick.

(Tried ScreenEvent / EVENT_MOUSE_BUTTON_RELEASE
but somehow could not get it to work so this came
as a useful workaround)
Lothar Schirm
Posts: 436
Joined: Sep 28, 2013 15:08
Location: Germany

Re: mouse pointer hit box via fb, winapi and sdl2

Post by Lothar Schirm »

coderJeff wrote: Apr 03, 2022 14:54 Very nice examples and write up. Good job!

For the windows API example, the HWND of the fb graphics window is exposed through ScreenControl and the following could be used if FindWindowA() could be unreliable due to duplicate window titles:

Code: Select all

#include "fbgfx.bi"

function getFBGfxHWND() as HWND
	dim as integer gfxHWND
	ScreenControl fb.GET_WINDOW_HANDLE, gfxHWND
	return cast( HWND, gfxHWND )
end function
Then later:

Code: Select all

ScreenToClient(getFBGfxHWND(), @vpoint)
Could this code be included as an example into the documentation for ScreenControl? I always used "GetForegroundWindow" (Windows API) in order to evaluate the HWND of the fb graphics window, because I was confused that ScreenControl fb.GET_WINDOW_HANDLE ... delivers an integer value, not a real windows handle.
Post Reply