B4J Library jNativeHookB4J for intercepting system input events

Generally speaking, a hook is a method of intercepting program events without a dedicated API. You can read more about it here: https://en.wikipedia.org/wiki/Hooking

This library wraps the JNativeHook library, allowing you to intercept global keyboard events and mouse events. That is, your B4J application can now recieve these events even if they occur outside your application or your application is not in focus or has no GUI. In order to use this library, download the JNativeHook-<version>.zip file from this address: https://github.com/kwhat/jnativehook/releases . The B4J library was written for version 2.0.2. Unzip the archive and place the JNativeHook.jar file in your B4J external libraries folder. Then download the jNativeHookB4J.zip file attached to this post and extract the jNativeHookB4J.jar and jNativeHookB4J.xml files to your B4J external libraries folder.

The following code shows an example of its usage:
B4X:
Sub Process_Globals
    Dim NH as NativeHook
    Dim t as Timer
End Sub

Sub AppStart(Args() as String)
    NH.Initialize("NH", Me)        'Initializes and registers the NativeHook
    NH.startNativeKeyListener    'starts a NativeKeyListener
    NH.enableKillCode   'Allows you to manually unregister by pressing Windows_Escape
    NH.EnableEventConsumption  'Allows you to consume input events
    t.Initialize("t", 60*1000)
    t.Enabled = True            'Timer will unregister the NativeHook in 1 minute
    StartMessageLoop
End Sub

'Event fired whenever a key is pressed, regardless of what application is in focus
Sub NH_NativeKeyPressed(nke as NativeKeyEvent) As Boolean
    If NH.isCharacter(nke.keyCode) Then
        Log(nke.keyText & " Pressed")
        If nke.keyText=="F" Then Return True    'Consumes the event if the user pressed the "F" key
    End If
    Return False
End Sub

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

Sub t_Tick
    t.Enabled = False
    NH.unregisterNativeHook
End Sub

Note how in the above example, all the NH_ events appear in the Main module. This doesn't mean, however, that they will execute in the Main thread. They will execute in the JNativeHook delegate thread. This is a general advantage however it means that if you want to access UI elements from within your event subs, you will have to use Thread.RunOnGuiThread() to do so. Also note that unregistering the NativeHook will end the NativeHook event loop. If this was the only active event loop in your program (no UI event loop, no server event loop, etc...), ending that event loop will end your program. That is why an _Unregistered event is provided.

Make sure to unregister the NativeHook when your program is done using it. If too many NativeHooks are left registered but unused, this will interfere with your ability to create new instances of NativeHook and for other programs that respond to these system events to do so. If this happens, reboot your computer and it should clear all those dangling NativeHooks from memory. However, if you properly unregister your NativeHooks, you shouldn't have any problems.

My preliminary tests with this library indicate that it performs very well and consumes very little computing resources. On a few years old i7 system, a program that I wrote with this library stayed at around 0.03% CPU load and very rarely spiked to 0.5% even with heavy mouse and keyboard usage and all native listeners running. The B4J library is pretty heavily commented and the comments sort of lead you through its usage. The JNativeHook.jar file that you download from github has packed into it all the native files necessary to work on Windows, Linux and Darwin-based Mac systems. I've only tested it on Windows, however.

EDIT (4JUL2015): jNativeHookB4J.zip was compiled against jdk 8u40. I've attached another .zip called jNativeHookB4JAncient.zip that was compiled against jdk 6u26 which should work for users and customers still using Javas 6 or 7 (I've only tested Ancient on jdk 7u71, though). jNativeHookB4jAncient works in exactly the same way except the NativeHook object is called NativeHookAncient. If you're using Java 5 or below, well, you're on your own...

EDIT (5JUL2015): Updated both libraries to version 1.1 to enable event consumption. See post #18 for details.
 

Attachments

  • jNativeHookB4J1.1.zip
    8.6 KB · Views: 1,270
  • jNativeHookB4JAncient1.1.zip
    8.7 KB · Views: 1,031
Last edited:

JakeBullet70

Well-Known Member
Licensed User
Longtime User
Take a look here.

https://www.b4x.com/android/forum/t...-and-try-catch-to-your-methods-hot-key.71897/

There is a download of the library's you need. I had trouble finding it and then I had to rename the library to get it to work.

In b4j menu, go to TOOLS, Configure Paths. In the picture below you can see where I have my additional libraries path. Your will be different.


upload_2016-10-16_9-56-12.png
 

ThRuST

Well-Known Member
Licensed User
Longtime User
NativeHook v2.1.0 beta is just showing a white empty pane with a waiting icon, and needs to be closed.
This library needs to be updated. It does not work on Windows 10 x64 with the latest Java JRE 1.8.0_111.
 

stevel05

Expert
Licensed User
Longtime User
There seems to be a problem with the NativeMouseClicked Method when I consume an event, it's almost as if the released phase is not being processed. Mouse control is not consistent after the first captured event.

It seems to work as expected if I use the MousePressed and MouseReleased methods instead. Modified demo attached.

Win10 JDK 1.8.0_71
 

Attachments

  • NHUI.zip
    1.3 KB · Views: 496

ThRuST

Well-Known Member
Licensed User
Longtime User
Steve, do you know how to use mouse buttons with jNativeHookB4J? I would like to see a simple example to get started.
Yesterday I bought a Logitech G300S gaming mouse and want to add support for it in my project. I recommend you do as well as 9 programmable buttons will open for new ways to interact with windows applications. Some buttons can use keyboard and combinations and be programmed into the mouse internal memory by Logitech gaming application that stays in the taskbar. You can use any of their SDKs to access the leds and stuff and to change the keyboard char/combinations. The mouse driver exists for both Windows and Mac (all versions), so there will be plenty of opportunities to write sophisticated applications with gaming mouse support.
A simple source code example how to access the mouse buttons would be great, so that I can implement mouse button support in my project.
The thing is that I want to access the mouse buttons from outside the form, so that I can associate it with contextmenus and useful stuff.
Make sure you buy the Logitech G300S gaming mouse it's the perfect mouse not only for gaming, my idea is that it will be awesome in applications.
The potential is endless so check it out and then tell me how thanful you are that I recommended it so you can be a master a jnativehookb4j :)
 

stevel05

Expert
Licensed User
Longtime User
All I know so far is in the app attached to the post above. Unfortunately there is not going to be a 'simple' example as dealing with this is going to be quite complex by it's nature. There are a number of things that will have to be done in an unconventional manner to get it to be useful for what you are after. if I get it working reliably I'll share the code.
 

ThRuST

Well-Known Member
Licensed User
Longtime User
Is it difficult to write a 'simple mouse library' that access the native Java mouse mouseclick event and allow something to do with it?
What is the complexity all about by accessing multiple mouse buttons in java? I am thinking outside the native hook by asking this.
 

ThRuST

Well-Known Member
Licensed User
Longtime User
I'm not sure, but if this might be of any help to put together a mouse library for mouseclicks outside the form, I am willing to donate money to the one who can help me. because it's vital that I progress with my B4J project. Here goes
You can add an event filter to the scene with addEventFilter(). This will be called before the event is consumed by any child controls. Here's what the code for the event filter looks like.


Native Java
B4X:
scene.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent mouseEvent) {
        System.out.println("mouse click detected! " + mouseEvent.getSource());
    }
});

Good luck.
 

ThRuST

Well-Known Member
Licensed User
Longtime User
Steve, It depends on threading library I don't have it, but I have the others. Do you mind posting that library here?
 

ThRuST

Well-Known Member
Licensed User
Longtime User
Steve, it seems to works very well. Did you put this together by the time I asked the question? :) Guess if I am happy to see this, words cannot express how thankful I am this means alot to me. You will be the first to beta test my project once I have come further with my work. Even in that case I only can access one mouse button that will be good enough to make it extremely useful. And in that case I might be able to support all of the buttons, WOW. Go buy the Logitech G300S gaming mouse all of you, and especially you Steve ;) I was about to start a new WPF project in Visual studio 2015, but there's no need for that now. I should implement your nice text colour routine as well. Since you helped me to learn to use the tableview control with SQLite I will be able to implement that as well as mouse support.
The last thing I need to ask for is that you strip out the printing routine from the text colouring example and leave out the very core for text highlighting so I can add it to a view in my project. After that I will let you see my project and I will donate money for your help and code. I think you deserve some appreciation from your work. Can I reach you somewhere else than from this forum? I had a bad day at the business course today, but man did you make up for that. Thanks for your help I really appreciate it. Now I can go back to work on my B4J Mindcraft project. I have tested in on my Macbook and it runs perfectly. I'll let you know if your mouse support works on my Mac. I cannot see why it shouldn't, but I look forward to try that.

mousehook1.PNG

mousehook2.PNG

Steve made it happen :rolleyes:
 

Cableguy

Expert
Licensed User
Longtime User
Hi, trying to implement this using the latest release JNativeHook v2.1.0 but it seems to not be 100% compatible with your lib.

Using it on Main module works just fine, but I can't get it to work on Class Modules.
 

ThRuST

Well-Known Member
Licensed User
Longtime User
Just to make it easier for the code monsters to help you :)
Which version of JDK are you using? This seems more and more relevant the more time that passes..
 

Cableguy

Expert
Licensed User
Longtime User
I'm using OpenJDK 11 but the error message clearly states that it couldn't find the class I initialized it in.
 

ThRuST

Well-Known Member
Licensed User
Longtime User
Sorry we need to be more precise of version. Open-Java v11.0.1 I assume? Darn I should add this option to Athena document templates. It's extremely relevant to JavaFX.
 

Roycefer

Well-Known Member
Licensed User
Longtime User
The first post in this thread says that the B4J library is built for version 2.0.2 of the jNativeHook library. You shouldn't assume that newer versions of the jNativeHook library will work.
 

Cableguy

Expert
Licensed User
Longtime User
My post was more in the sense of "asking" for a possible update of this lib since it's (almost) impossible to find the original version of this.
Also, using a 3 years old wrapper, when the last lib update is less that a few months old, is like using a pixel 3 with android 4!
 

Roycefer

Well-Known Member
Licensed User
Longtime User
It's not almost impossible to find the version 2.0.2 of the JNativeHook library. It's exactly as easy as it was when the jNativeHookB4J library was first released. The library is at the link provided in the first post.

Is there some new feature in version 2.1.0 that you need that isn't in version 2.0.2?
 
Top