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 VK_RMENU = 0xA5;
|
||||||
|
|
||||||
public static int WM_COMMAND = 0x111;
|
public static int WM_COMMAND = 0x111;
|
||||||
|
public static int MN_GETHMENU = 0x01E1;
|
||||||
|
|
||||||
public static int CWP_ALL = 0x0000; // Does not skip any child windows
|
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;
|
package org.synthuse;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ public class SynthuseDlg extends JFrame {
|
|||||||
btnCancel.addActionListener(new ActionListener() {
|
btnCancel.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(ActionEvent arg0) {
|
public void actionPerformed(ActionEvent arg0) {
|
||||||
dialogResult = "";
|
dialogResult = "";
|
||||||
SynthuseDlg.this.dispose();
|
SynthuseDlg.this.disposeWindow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
btnCancel.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_CANCEL_IMG)));
|
btnCancel.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_CANCEL_IMG)));
|
||||||
@@ -383,12 +383,24 @@ public class SynthuseDlg extends JFrame {
|
|||||||
this.addWindowListener(new WindowAdapter() {
|
this.addWindowListener(new WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void windowClosing(WindowEvent arg0) {
|
public void windowClosing(WindowEvent arg0) {
|
||||||
|
KeyboardHook.stopGlobalKeyboardHook(); //stop keyboard hook
|
||||||
config.save();
|
config.save();
|
||||||
SynthuseDlg.this.dispose(); // force app to close
|
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();
|
btnRefresh.doClick();
|
||||||
refreshDatabinding();
|
refreshDatabinding();
|
||||||
}
|
}
|
||||||
@@ -450,4 +462,10 @@ public class SynthuseDlg extends JFrame {
|
|||||||
XpathManager.buildXpathStatementThreaded(hwnd, runtimeId, textPane, xpathEvents);
|
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;
|
package org.synthuse;
|
||||||
|
|
||||||
import com.sun.jna.platform.win32.WinDef.HWND;
|
import com.sun.jna.platform.win32.WinDef.HWND;
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ public class WindowInfo {
|
|||||||
extra = new LinkedHashMap<String, String>();
|
extra = new LinkedHashMap<String, String>();
|
||||||
extra.put("tvCount", tvCount.intValue() + "");
|
extra.put("tvCount", tvCount.intValue() + "");
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if window has a menu
|
//check if window has a menu
|
||||||
HMENU hmenu = Api.User32.instance.GetMenu(hWnd);
|
HMENU hmenu = Api.User32.instance.GetMenu(hWnd);
|
||||||
if (hmenu != null) { //menu item count
|
if (hmenu != null) { //menu item count
|
||||||
@@ -99,6 +100,12 @@ public class WindowInfo {
|
|||||||
this.menus = menuCount;
|
this.menus = menuCount;
|
||||||
this.menu = hmenu;
|
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) {
|
if (isChild) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import java.util.Date;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JTextPane;
|
import javax.swing.JTextPane;
|
||||||
@@ -45,7 +46,7 @@ import com.sun.jna.platform.win32.WinDef.HWND;
|
|||||||
|
|
||||||
public class WindowsEnumeratedXml implements Runnable{
|
public class WindowsEnumeratedXml implements Runnable{
|
||||||
public static Exception lastException = null;
|
public static Exception lastException = null;
|
||||||
|
public static AtomicBoolean enumeratingXmlFlag = new AtomicBoolean(false);
|
||||||
public JTextPane outputPane = null;
|
public JTextPane outputPane = null;
|
||||||
public JLabel lblStatus = null;
|
public JLabel lblStatus = null;
|
||||||
public WindowsEnumeratedXml() {
|
public WindowsEnumeratedXml() {
|
||||||
@@ -64,9 +65,13 @@ public class WindowsEnumeratedXml implements Runnable{
|
|||||||
outputPane.setCaretPosition(0);
|
outputPane.setCaretPosition(0);
|
||||||
double seconds = ((double)(System.nanoTime() - startTime) / 1000000000);
|
double seconds = ((double)(System.nanoTime() - startTime) / 1000000000);
|
||||||
lblStatus.setText("Windows Enumerated Xml loaded in " + new DecimalFormat("#.###").format(seconds) + " seconds");
|
lblStatus.setText("Windows Enumerated Xml loaded in " + new DecimalFormat("#.###").format(seconds) + " seconds");
|
||||||
|
enumeratingXmlFlag.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void getXmlThreaded(JTextPane outputPane, JLabel lblStatus) {
|
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));
|
Thread t = new Thread(new WindowsEnumeratedXml(outputPane, lblStatus));
|
||||||
t.start();
|
t.start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,13 +88,6 @@ public class WinApiTest {
|
|||||||
String menuTxt = api.GetMenuItemText(hmenu, m);
|
String menuTxt = api.GetMenuItemText(hmenu, m);
|
||||||
System.out.println("Menu Text: " + menuTxt);
|
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