B4J Question Nativehook MouseMove event

ThRuST

Well-Known Member
Licensed User
Longtime User
Does Nativehook support MouseMove? I want to detect mouse cursor position allover the screen with B4j NativeHook library. I tried this Sub but that did not work. Please help.

B4X:
Sub nh_NativeMouseMove(nme As NativeMouseEvent) As Boolean
    Log($"nh_NativeMouseMoved(X:${nme.X},Y:${nme.Y},ClickCount:${nme.ClickCount},Button${nme.MouseButton})"$)
    Return False
End Sub
 

Roycefer

Well-Known Member
Licensed User
Longtime User
Did you properly initialize the NativeHook? Did you properly enable the relevant listeners? Have you properly unregistered old NativeHooks (if not, reboot your computer to clear the old ones from memory)?
 
Upvote 0

ThRuST

Well-Known Member
Licensed User
Longtime User
Very relevant indeed, I should have been more clear with initializiation and closing. Since I use Debug mode on a transparent form I just run the code and stop code from running from the official Stop application button in B4J IDE. To be honest I am not sure if this is ok to free up the hooks each time the program is running?

Parts of my code that is relevant to NativeHook:

Process_Globals

B4X:
Dim NH As NativeHook

Appstart

B4X:
   NH.Initialize("NH", Me) 'Initializes and registers the NativeHook
   NH.startNativeMouseWheelListener
   NH.enableKillCode 'Allows you to manually unregister by pressing Windows_Escape
   'NH.startNativeKeyListener  'starts a NativeKeyListener
   NH.startNativeMouseInputListener

B4X:
Sub nh_NativeMouseMove(nme As NativeMouseEvent) As Boolean
    Log($"nh_NativeMouseMoved(X:${nme.X},Y:${nme.Y},ClickCount:${nme.ClickCount},Button${nme.MouseButton})"$)
    Return False     'Not consuming the event.
End Sub

B4X:
Sub NH_NativeMouseClicked(nme As NativeMouseEvent) As Boolean
   
    Log($"nh_NativeMouseClicked(X:${nme.X},Y:${nme.Y},ClickCount:${nme.ClickCount},Button${nme.MouseButton})"$)
   
    'Thread.RunOnGuiThread(True)
   
    If nme.MouseButton == 2 Then
        Log("Right mouse button was pressed.")
        fx.Msgbox(MainForm, "hello world","")
        'x = AwtRobot.RobotMouseCurrentLocation(x)
        'y = AwtRobot.RobotMouseCurrentLocation(y)   
    End If

    Return False
   
End Sub

'Event fired when the NativeHook is unregistered
Sub NH_Unregistered
   
    NH.endNativeMouseInputListener
    NH.unregisterNativeHook
   
    Log("NativeHook unregistered")
    StopMessageLoop    'No more event loops means this program ends here
   
End Sub

Observations
* No message was displayed that the nativehook stops when the application is closed from the Stop button in the B4J IDE.
* I want to display a GUI MessageBox when the mouse button is clicked. Failed to run on the GUI thread. Dunno how that works yet.
* Not sure if Start/StopMessageLoop is needed.
* I have not rebooted my computer between the tries.
 
Upvote 0

ThRuST

Well-Known Member
Licensed User
Longtime User
Forgot to add that I want to add mouse cursor movement in a label that displays on the form, which is useful during development

B4X:
Lbl_PanelTopPosition.Text = x & " " & y

So even if mousemovement is tracked it might not update automatically without usage of a timer?

Note I am talking about NativeHook and Full screen realtime tracking of the mouse cursor position.
 
Upvote 0

Roycefer

Well-Known Member
Licensed User
Longtime User
I strongly recommend against using the jNativeHook library in debug mode. Everything runs in the Main thread in debug mode and this screws up the library's threading model.

Remember that the event subs that the library raises will NOT be running in the Main thread. If you want to access UI elements from those subs, you have to use Thread.RunOnGuiThread (from the Threading library). The reason for this is that the library would not be able to consume input events if those event subs ran in the Main thread. They need to run in the NativeHook delegate thread to offer event consumption.

Also, killing your application does NOT unregister the NativeHook. It will leave the NativeHook "dangling". Too many dangling NativeHooks in memory will interfere with the function of future NativeHooks and other applications. Reboot your computer to clear them.

Proper way to unregister a NativeHook:
B4X:
NH.unregisterNativeHook   'this will cause the NH_Unregistered event to be raised

Keep in mind that the NativeHook library will raise an event for every single tiny microscopic mouse movement. The library can provide some filtering but you will probably want to add your own time-based filtering so that you don't spam the Main thread's event queue with requests to update the reported mouse position. I think 10x/sec is probably as fast as you need. An alternative might be to use the jAWTRobot library and a timer to accomplish this. The AWTRobot does not need to be unregistered or anything. You can just kill the program and it will die with the program.
 
Upvote 0

ThRuST

Well-Known Member
Licensed User
Longtime User
@Roycefer Great advice to run in Release mode when working with hooks, and also stop the application from a button to properly execute the code when closing the application, thanks. I use a combo with mousemove by Erel and your excellent NativeHook. The AWTRobot library is indded awesome too. A complete documenation would be great for multi-function libraries because it's time consuming to search for solutions for all kinds of things but as usual we're used to that and it's part of the game :cool:
You explain well and that clears out alot questions. But perhaps I am not the only one who wonder how to pass and read the nme.x and y values globally and how to read the cursor position from the MouseMove event in realtime. Might be possible with a timer but then we need to read the current mouse location, tadaa x and y values :) Anyway I use Erels mousemove solution but it would be great to sort this out with your great library.
 
Upvote 0

Roycefer

Well-Known Member
Licensed User
Longtime User
The following code stores the current mouse position in a global variable using the NativeHook library:
B4X:
Sub Process_Globals
    Public nh As NativeHook
    Public currentMouseX As Int
    Public currentMouseY As Int
End Sub

Sub AppStart (Args() As String)
    nh.Initialize("nh", Me)
    nh.startNativeMouseInputListener
    StartMessageLoop
End Sub

Sub nh_NativeMouseMoved(nme As NativeMouseEvent) As Boolean
    currentMouseX = nme.X
    currentMouseY = nme.y
End Sub

The following code uses jAWTRobot and a Timer to retrieve the mouse position and store it in a global variable:
B4X:
Sub Process_Globals
    Public currentMouseX As Int
    Public currentMouseY As Int
    Public lore As AWTRobot
    Public t As Timer
End Sub

Sub AppStart (Args() As String)
    t.Initialize("t", 100)
    t.Enabled = True
    StartMessageLoop
End Sub

Sub t_Tick
    Dim m() As Int = lore.RobotMouseCurrentLocation
    currentMouseX = m(0)
    currentMouseY = m(1)
End Sub
 
Last edited:
Upvote 0

ThRuST

Well-Known Member
Licensed User
Longtime User
@Roycefer Oh god damnit this was simply the best reply ever in this forum. Clean professional nice and tidy code, man you rock. Hat off for the allmighty Roycefer :)
 
Upvote 0

ThRuST

Well-Known Member
Licensed User
Longtime User
It would be great to know also how to run a subroutine in the GUI thread from the MouseClicked event in Nativehook and AWTRobot Library, users will love us both I am sure :D
 
Upvote 0

ThRuST

Well-Known Member
Licensed User
Longtime User
I know mr.Cableguy has the answer to this one, I just want him to share it with the rest of the world. I've become emotional ;)
 
Upvote 0

Cableguy

Expert
Licensed User
Longtime User
in the above code the mouse positions are stored inside a global, so you just need to run a timer to update a label if you want visual representation
 
Upvote 0

ThRuST

Well-Known Member
Licensed User
Longtime User
@Cableguy I see, because the thread gets busy like a river since every waterdrop causes the river to be flooded so the timer acts like a watermill. You're a genious too :rolleyes:
 
Upvote 0
Top