Added Global Keyboard Hook for refreshing using Ctrl+Shift+3
Created a Jna Global Keyboard Hook class and added hot key for refreshing the windows xml (Ctrl + Shift + 3). Fixed cancel button to dispose window properly.
This commit is contained in:
@@ -115,6 +115,7 @@ public class Api {
|
||||
public static int VK_RMENU = 0xA5;
|
||||
|
||||
public static int WM_COMMAND = 0x111;
|
||||
public static int MN_GETHMENU = 0x01E1;
|
||||
|
||||
public static int CWP_ALL = 0x0000; // Does not skip any child windows
|
||||
|
||||
|
||||
199
src/org/synthuse/KeyboardHook.java
Normal file
199
src/org/synthuse/KeyboardHook.java
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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.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 targetKeyCode;
|
||||
boolean withShift, withCtrl, withAlt;
|
||||
public TargetKeyPress (int targetKeyCode) {
|
||||
this.targetKeyCode = targetKeyCode;
|
||||
this.withShift = false;
|
||||
this.withCtrl = false;
|
||||
this.withAlt = false;
|
||||
}
|
||||
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 HHOOK hHook = null;
|
||||
public static LowLevelKeyboardProc lpfn;
|
||||
public static volatile boolean quit = false;
|
||||
|
||||
public interface User32 extends W32APIOptions {
|
||||
User32 instance = (User32) Native.loadLibrary("user32", User32.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);
|
||||
|
||||
//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
|
||||
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;
|
||||
MSG msg = new MSG();
|
||||
try {
|
||||
while (!quit) {
|
||||
User32.instance.PeekMessage(msg, null, 0, 0, 0);
|
||||
Thread.sleep(10);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//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 stopGlobalKeyboardHook() {
|
||||
quit = true;
|
||||
}
|
||||
|
||||
// search target keyboard event list for a match and return it otherwise return null if no match
|
||||
public TargetKeyPress getTargetKeyPressed(int keyCode) {
|
||||
TargetKeyPress target = null;
|
||||
for (TargetKeyPress tkp : KeyboardHook.targetList) {
|
||||
if (tkp.targetKeyCode != keyCode)
|
||||
continue;
|
||||
if (!tkp.withShift || ((User32.instance.GetKeyState(VK_SHIFT) & 0x8000) != 0)) {
|
||||
if (!tkp.withCtrl || ((User32.instance.GetKeyState(VK_CONTROL) & 0x8000) != 0)) {
|
||||
if (!tkp.withAlt || ((User32.instance.GetKeyState(VK_MENU) & 0x8000) != 0)) {
|
||||
return tkp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
// add more target keys to watch for
|
||||
public static void addKeyEvent(int targetKeyCode, boolean withShift, boolean withCtrl, boolean withAlt) {
|
||||
KeyboardHook.targetList.add(new TargetKeyPress(targetKeyCode, withShift, withCtrl, withAlt));
|
||||
}
|
||||
// add more target keys to watch for
|
||||
public static void addKeyEvent(int targetKeyCode) {
|
||||
KeyboardHook.targetList.add(new TargetKeyPress(targetKeyCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
createGlobalKeyboardHook();
|
||||
unhook();//wait for quit == true then unhook
|
||||
}
|
||||
|
||||
public KeyboardHook() {
|
||||
}
|
||||
|
||||
public KeyboardHook(KeyboardEvents events) {
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
public static void StartGlobalKeyboardHookThreaded(KeyboardEvents events) {
|
||||
Thread t = new Thread(new KeyboardHook(events));
|
||||
t.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");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright 2014, Synthuse.org
|
||||
* Released under the Apache Version 2.0 License.
|
||||
*
|
||||
* last modified by ejakubowski7@gmail.com
|
||||
*/
|
||||
|
||||
package org.synthuse;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
@@ -321,7 +321,7 @@ public class SynthuseDlg extends JFrame {
|
||||
btnCancel.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
dialogResult = "";
|
||||
SynthuseDlg.this.dispose();
|
||||
SynthuseDlg.this.disposeWindow();
|
||||
}
|
||||
});
|
||||
btnCancel.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_CANCEL_IMG)));
|
||||
@@ -383,12 +383,24 @@ public class SynthuseDlg extends JFrame {
|
||||
this.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent arg0) {
|
||||
|
||||
KeyboardHook.stopGlobalKeyboardHook(); //stop keyboard hook
|
||||
config.save();
|
||||
SynthuseDlg.this.dispose(); // force app to close
|
||||
}
|
||||
});
|
||||
|
||||
KeyboardHook.addKeyEvent(KeyEvent.VK_3, true, true, false);// refresh xml when CTRL+SHIFT+3 is pressed
|
||||
//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_3){
|
||||
btnRefresh.doClick();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
btnRefresh.doClick();
|
||||
refreshDatabinding();
|
||||
}
|
||||
@@ -450,4 +462,10 @@ public class SynthuseDlg extends JFrame {
|
||||
XpathManager.buildXpathStatementThreaded(hwnd, runtimeId, textPane, xpathEvents);
|
||||
}
|
||||
}
|
||||
|
||||
public void disposeWindow()
|
||||
{
|
||||
WindowEvent closingEvent = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
|
||||
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(closingEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Copyright 2014, Synthuse.org
|
||||
* Released under the Apache Version 2.0 License.
|
||||
*
|
||||
* last modified by ejakubowski7@gmail.com
|
||||
*/
|
||||
|
||||
package org.synthuse;
|
||||
|
||||
import com.sun.jna.platform.win32.WinDef.HWND;
|
||||
|
||||
@@ -91,6 +91,7 @@ public class WindowInfo {
|
||||
extra = new LinkedHashMap<String, String>();
|
||||
extra.put("tvCount", tvCount.intValue() + "");
|
||||
}
|
||||
|
||||
//check if window has a menu
|
||||
HMENU hmenu = Api.User32.instance.GetMenu(hWnd);
|
||||
if (hmenu != null) { //menu item count
|
||||
@@ -99,6 +100,12 @@ public class WindowInfo {
|
||||
this.menus = menuCount;
|
||||
this.menu = hmenu;
|
||||
}
|
||||
else
|
||||
{
|
||||
LRESULT result = Api.User32.instance.PostMessage(hWnd, Api.MN_GETHMENU, new WPARAM(0), new LPARAM());
|
||||
if (result.longValue() != 1)
|
||||
System.out.println("MN_GETHMENU: " + result.longValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (isChild) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JTextPane;
|
||||
@@ -45,7 +46,7 @@ import com.sun.jna.platform.win32.WinDef.HWND;
|
||||
|
||||
public class WindowsEnumeratedXml implements Runnable{
|
||||
public static Exception lastException = null;
|
||||
|
||||
public static AtomicBoolean enumeratingXmlFlag = new AtomicBoolean(false);
|
||||
public JTextPane outputPane = null;
|
||||
public JLabel lblStatus = null;
|
||||
public WindowsEnumeratedXml() {
|
||||
@@ -64,9 +65,13 @@ public class WindowsEnumeratedXml implements Runnable{
|
||||
outputPane.setCaretPosition(0);
|
||||
double seconds = ((double)(System.nanoTime() - startTime) / 1000000000);
|
||||
lblStatus.setText("Windows Enumerated Xml loaded in " + new DecimalFormat("#.###").format(seconds) + " seconds");
|
||||
enumeratingXmlFlag.set(false);
|
||||
}
|
||||
|
||||
public static void getXmlThreaded(JTextPane outputPane, JLabel lblStatus) {
|
||||
if (enumeratingXmlFlag.get())
|
||||
return; //something is already running
|
||||
enumeratingXmlFlag.set(true); //if we don't do this the multiple xml's could get combined on the textpane
|
||||
Thread t = new Thread(new WindowsEnumeratedXml(outputPane, lblStatus));
|
||||
t.start();
|
||||
}
|
||||
|
||||
@@ -88,13 +88,6 @@ public class WinApiTest {
|
||||
String menuTxt = api.GetMenuItemText(hmenu, m);
|
||||
System.out.println("Menu Text: " + menuTxt);
|
||||
}
|
||||
/*
|
||||
if (menuCount == 5) {
|
||||
HMENU smenu = Api.User32.instance.GetSubMenu(hmenu, 0);
|
||||
boolean result = Api.User32.instance.TrackPopupMenu(smenu, 0, 1, 1, 0, hWnd, 0);
|
||||
System.out.println("TrackPopupMenu: " + result);
|
||||
System.out.println("last error: " + Api.Kernel32.instance.GetLastError());
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user