First, let's talk about event delivery and UI threading as it is important that you understand the context switching that is going behind the scenes with the threads and the memory being shared between them.
Java like just about every other application ever designed uses a single thread for all UI events. The reason for this has been well documented all over the internet. So if you are using AWT/Swing or Java FX, your events are dispatched on the AWT/Swing or Java FX platform thread respectively and no other threads should be interacting or expecting these events.
JNativeHook uses its own dispatch thread that is completely independent of Java's UI. This was primarily done for compatibility reasons, as I did not want to force a particular UI API on the implementing user. Because of this, if you would like to use AWT/Swing, Java FX or something else, you must dispatch the native event on the thread context that is responsible for your UI. There is a spring dispatcher built into the library, and a Java FX dispatcher I have posted here that allow for very simple context switching via the default dispatch method by registering the dispatcher with the GlobalScreen.
You maybe asking yourself, "what if I want to modify the event before it is dispatched?" This should also be possible, but requires a little more effort that registering a dispatch class. You should be able to extend the GlobalScreen class and its anonymous inner classes to accomplish anything else you need. For example, if you want to dispatch Swing InputEvents instead of NativeInputEvents you could do the following:
public class CustomScreen extends GlobalScreen {
// Static blocks are run when the class is loaded.
static {
// Add adaptors to the listener to convert the events.
addNativeKeyListener(new KeyAdapter());
addNativeMouseListener(new MouseAdapter());
setEventDispatcher(new SwingDispatchService());
}
private static class KeyAdapter extends SwingKeyAdapter {
public void keyTyped(KeyEvent keyEvent) {
KeyListener[] listeners = eventListeners.getListeners(KeyListener.class);
for (int i = 0; i < listeners.length; i++) {
listeners[i].keyTyped(keyEvent);
}
}
public void keyPressed(KeyEvent keyEvent) {
KeyListener[] listeners = eventListeners.getListeners(KeyListener.class);
for (int i = 0; i < listeners.length; i++) {
listeners[i].keyPressed(keyEvent);
}
}
public void keyReleased(KeyEvent keyEvent) {
KeyListener[] listeners = eventListeners.getListeners(KeyListener.class);
for (int i = 0; i < listeners.length; i++) {
listeners[i].keyReleased(keyEvent);
}
}
}
private static class MouseAdapter extends SwingMouseAdapter {
public void mouseClicked(MouseEvent mouseEvent) {
MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);
for (int i = 0; i < listeners.length; i++) {
listeners[i].mouseClicked(mouseEvent);
}
}
public void mousePressed(MouseEvent mouseEvent) {
MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);
for (int i = 0; i < listeners.length; i++) {
listeners[i].mousePressed(mouseEvent);
}
}
public void mouseReleased(MouseEvent mouseEvent) {
MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);
for (int i = 0; i < listeners.length; i++) {
listeners[i].mouseReleased(mouseEvent);
}
}
public void mouseEntered(MouseEvent mouseEvent) {
MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);
for (int i = 0; i < listeners.length; i++) {
listeners[i].mouseEntered(mouseEvent);
}
}
public void mouseExited(MouseEvent mouseEvent) {
MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);
for (int i = 0; i < listeners.length; i++) {
listeners[i].mouseExited(mouseEvent);
}
}
}
public static void addSwingKeyListener(KeyListener listener) {
if (listener != null) {
eventListeners.add(KeyListener.class, listener);
}
}
public static void removeKeyListener(KeyListener listener) {
if (listener != null) {
eventListeners.remove(KeyListener.class, listener);
}
}
public static void addMouseListener(MouseListener listener) {
if (listener != null) {
eventListeners.add(MouseListener.class, listener);
}
}
public static void removeMouseListener(MouseListener listener) {
if (listener != null) {
eventListeners.remove(MouseListener.class, listener);
}
}
}
All this does is register a couple adaptor classes included with the library with the parent global screen and dispatches to the swing listeners in the adaptors. There are other ways to do this that may be more powerful like extending the GlobalScreen.NativeHookThread anonymous inner class, but that will take significantly more code on your part.
Dealing with Java FX is very similar to swing, you can find an example dispatcher at my previous post: https://stackoverflow.com/a/45423466/773849
As far as the .consume()
goes, yes that is unsupported but still possible on some platforms. If you are using Windows or OS X, you can stop event propagation with some clever code as outlined in the wiki. This will require that you override the aforementioned NativeHookThread inner class and you must be fast with your events as the NativeHookThread is not really a not a thread in this case and will block the operating system. This method is not available on Linux, because linux has no way to perform this function at this point in time.