/* * Copyright 2014, Synthuse.org * Released under the Apache Version 2.0 License. * * last modified by ejakubowski7@gmail.com */ package org.synthuse; //import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.sun.jna.*; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinUser.*; import com.sun.jna.platform.win32.WinDef.*; import com.sun.jna.win32.W32APIOptions; public class KeyboardHook implements Runnable{ // Keyboard event class, interface, and array list public static class TargetKeyPress { int idNumber; int targetKeyCode; boolean withShift, withCtrl, withAlt; public TargetKeyPress (int targetKeyCode) { this.targetKeyCode = targetKeyCode; this.withShift = false; this.withCtrl = false; this.withAlt = false; } public TargetKeyPress (int idNumber, int targetKeyCode, boolean withShift, boolean withCtrl, boolean withAlt) { this.idNumber = idNumber; this.targetKeyCode = targetKeyCode; this.withShift = withShift; this.withCtrl = withCtrl; this.withAlt = withAlt; } public TargetKeyPress (int targetKeyCode, boolean withShift, boolean withCtrl, boolean withAlt) { this.targetKeyCode = targetKeyCode; this.withShift = withShift; this.withCtrl = withCtrl; this.withAlt = withAlt; } } public static List<TargetKeyPress> targetList = Collections.synchronizedList(new ArrayList<TargetKeyPress>());// all keys we want to throw events on public static interface KeyboardEvents { void keyPressed(TargetKeyPress target); } public KeyboardEvents events = new KeyboardEvents() { public void keyPressed(TargetKeyPress target) { //System.out.println("target key pressed: " + target.targetKeyCode); } }; // JNA constants and functions public static final int WH_KEYBOARD_LL = 13; //Modifier key vkCode constants public static final int VK_SHIFT = 0x10; public static final int VK_CONTROL = 0x11; public static final int VK_MENU = 0x12; public static final int VK_CAPITAL = 0x14; public static final int MOD_ALT = 0x0001; public static final int MOD_CONTROL = 0x0002; public static final int MOD_NOREPEAT = 0x4000; public static final int MOD_SHIFT = 0x0004; public static final int MOD_WIN = 0x0008; public static final int QS_HOTKEY = 0x0080; public static final int INFINITE = 0xFFFFFFFF; public static HHOOK hHook = null; public static LowLevelKeyboardProc lpfn; public static volatile boolean quit = false; private static Thread khThread = null; public interface User32Ex extends W32APIOptions { User32Ex instance = (User32Ex) Native.loadLibrary("user32", User32Ex.class, DEFAULT_OPTIONS); LRESULT LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam); HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HMODULE hMod, int dwThreadId); LRESULT CallNextHookEx(HHOOK idHook, int nCode, WPARAM wParam, LPARAM lParam); LRESULT CallNextHookEx(HHOOK idHook, int nCode, WPARAM wParam, Pointer lParam); boolean PeekMessage(MSG lpMsg, HWND hWnd, int wMsgFilterMin, int wMsgFilterMax, int wRemoveMsg); boolean UnhookWindowsHookEx(HHOOK idHook); short GetKeyState(int nVirtKey); short GetAsyncKeyState(int nVirtKey); /* DWORD WINAPI MsgWaitForMultipleObjects( __in DWORD nCount, //The number of object handles in the array pointed to by pHandles. __in const HANDLE *pHandles, //An array of object handles. __in BOOL bWaitAll, //If this parameter is TRUE, the function returns when the states of all objects in the pHandles array have been set to signaled and an input event has been received. __in DWORD dwMilliseconds, //if dwMilliseconds is INFINITE, the function will return only when the specified objects are signaled. __in DWORD dwWakeMask //The input types for which an input event object handle will be added to the array of object handles. );*/ int MsgWaitForMultipleObjects(int nCount, Pointer pHandles, boolean bWaitAll, int dwMilliSeconds, int dwWakeMask); boolean RegisterHotKey(Pointer hWnd, int id, int fsModifiers, int vk); //public static interface HOOKPROC extends StdCallCallback { // LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT lParam); //} } public interface Kernel32 extends W32APIOptions { Kernel32 instance = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class, DEFAULT_OPTIONS); HMODULE GetModuleHandle(String name); } // Create Global Windows Keyboard hook and wait until quit == true public void createGlobalKeyboardHook() { if (hHook != null) return; //hook already running don't add anymore System.out.println("starting global keyboard hook"); HMODULE hMod = Kernel32.instance.GetModuleHandle(null); HOOKPROC lpfn = new HOOKPROC() { @SuppressWarnings("unused") public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT lParam) { //System.out.println("here " + lParam.vkCode); TargetKeyPress target = getTargetKeyPressed(lParam.vkCode); //find if this is a target key pressed if (target != null) events.keyPressed(target); //if (lParam.vkCode == 87) //w // quit = true; return User32.INSTANCE.CallNextHookEx(hHook, nCode, wParam, lParam.getPointer()); } }; hHook = User32.INSTANCE.SetWindowsHookEx(WH_KEYBOARD_LL, lpfn, hMod, 0); if (hHook == null) return; //System.out.println("starting message loop"); MSG msg = new MSG(); try { while (!quit) { User32.INSTANCE.PeekMessage(msg, null, 0, 0, 1); if (msg.message == User32.WM_HOTKEY){ // && msg.wParam.intValue() == 1 //System.out.println("Hot key pressed!"); msg = new MSG(); //must clear msg so it doesn't repeat } Thread.sleep(10); } } catch (Exception e) { e.printStackTrace(); } //System.out.println("message loop stopped"); } // Create HotKeys Windows hook and wait until quit == true public void createHotKeysHook() { registerAllHotKeys(); //User32Ex.instance.MsgWaitForMultipleObjects(0, Pointer.NULL, true, INFINITE, QS_HOTKEY); //System.out.println("starting message loop"); MSG msg = new MSG(); try { while (!quit) { User32.INSTANCE.PeekMessage(msg, null, 0, 0, 1); if (msg.message == User32.WM_HOTKEY){ // && msg.wParam.intValue() == 1 //System.out.println("Hot key pressed " + msg.wParam); TargetKeyPress target = findTargetKeyPressById(msg.wParam.intValue()); if (target != null) events.keyPressed(target); msg = new MSG(); //must clear msg so it doesn't repeat } Thread.sleep(10); } } catch (Exception e) { e.printStackTrace(); } unregisterAllHotKeys(); //System.out.println("message loop stopped"); } //unhook the Global Windows Keyboard hook public void unhook() { if (hHook == null) return; if (!User32.INSTANCE.UnhookWindowsHookEx(hHook)) System.out.println("Failed to unhook"); //System.out.println("Unhooked"); hHook = null; } //stops Keyboard hook and causes the unhook command to be called public static void stopKeyboardHook() { quit = true; if (khThread != null) { try { khThread.join(); } catch (Exception e) { e.printStackTrace(); } } } // search target keyboard event list for a match and return it otherwise return null if no match private TargetKeyPress getTargetKeyPressed(int keyCode) { TargetKeyPress target = null; for (TargetKeyPress tkp : KeyboardHook.targetList) { if (tkp.targetKeyCode != keyCode) continue; if (!tkp.withShift || ((User32Ex.instance.GetKeyState(VK_SHIFT) & 0x8000) != 0)) { if (!tkp.withCtrl || ((User32Ex.instance.GetKeyState(VK_CONTROL) & 0x8000) != 0)) { if (!tkp.withAlt || ((User32Ex.instance.GetKeyState(VK_MENU) & 0x8000) != 0)) { return tkp; } } } } return target; } private TargetKeyPress findTargetKeyPressById(int idNumber) { TargetKeyPress target = null; for (TargetKeyPress tkp : KeyboardHook.targetList) { if (tkp.idNumber == idNumber) return tkp; } return target; } // clear all target keys to watch for public static void clearKeyEvent() { KeyboardHook.targetList.clear(); } // add more target keys to watch for public static void addKeyEvent(int targetKeyCode, boolean withShift, boolean withCtrl, boolean withAlt) { KeyboardHook.targetList.add(new TargetKeyPress(KeyboardHook.targetList.size() + 1 , targetKeyCode, withShift, withCtrl, withAlt)); } // add more target keys to watch for public static void addKeyEvent(int targetKeyCode) { KeyboardHook.targetList.add(new TargetKeyPress(targetKeyCode)); } private void registerAllHotKeys() // must register hot keys in the same thread that is watching for hotkey messages { //System.out.println("registering hotkeys"); for (TargetKeyPress tkp : KeyboardHook.targetList) { //BOOL WINAPI RegisterHotKey(HWND hWnd, int id, UINT fsModifiers, UINT vk); int modifiers = User32.MOD_NOREPEAT; if (tkp.withShift) modifiers = modifiers | User32.MOD_SHIFT; if (tkp.withCtrl) modifiers = modifiers | User32.MOD_CONTROL; if (tkp.withAlt) modifiers = modifiers | User32.MOD_ALT; //System.out.println("RegisterHotKey " + tkp.idNumber + "," + modifiers + ", " + tkp.targetKeyCode); if (!User32.INSTANCE.RegisterHotKey(new WinDef.HWND(Pointer.NULL), tkp.idNumber, modifiers, tkp.targetKeyCode)) { System.out.println("Couldn't register hotkey " + tkp.targetKeyCode); } } } private void unregisterAllHotKeys() // must register hot keys in the same thread that is watching for hotkey messages { //System.out.println("unregistering hotkeys"); for (TargetKeyPress tkp : KeyboardHook.targetList) { if (!User32.INSTANCE.UnregisterHotKey(Pointer.NULL, tkp.idNumber)) { System.out.println("Couldn't unregister hotkey " + tkp.targetKeyCode); } } } @Override public void run() { //createGlobalKeyboardHook(); createHotKeysHook(); //System.out.println("Unhooking Global Keyboard Hook"); unhook();//wait for quit == true then unhook } public KeyboardHook() { } public KeyboardHook(KeyboardEvents events) { this.events = events; } public static void StartKeyboardHookThreaded(KeyboardEvents events) { quit = false; khThread = new Thread(new KeyboardHook(events)); khThread.start(); } /* // testing public static void main(String[] args) throws Exception { //add target keys KeyboardHook.addKeyEvent(KeyEvent.VK_3, true, true, false); KeyboardHook.addKeyEvent(KeyEvent.VK_5, false, true, false); KeyboardHook.addKeyEvent(KeyEvent.VK_Q); //add global hook and event KeyboardHook.StartGlobalKeyboardHookThreaded(new KeyboardHook.KeyboardEvents() { @Override public void keyPressed(KeyboardHook.TargetKeyPress target) { System.out.println("target key pressed " + target.targetKeyCode); if (target.targetKeyCode == KeyEvent.VK_Q){ // if Q was pressed then unhook KeyboardHook.stopGlobalKeyboardHook(); System.out.println("unhooking"); } } }); } */ }