-
Nikola Babic (nab701) authored
# Conflicts: # src/model/OSInterface.java
Nikola Babic (nab701) authored# Conflicts: # src/model/OSInterface.java
OSInterface.java 11.86 KiB
package model;
import com.sun.jna.*;
import com.sun.jna.platform.win32.*;
import model.profiles.commands.saveEverything;
import com.sun.jna.win32.W32APIOptions;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/** Singleton class which interfaces with Windows OS. */
public class OSInterface implements HotkeyDetector, HotkeyRegistration, InputEmulator, OSSettingsWriter,
Runnable {
private static OSInterface instance = null;
private User32 user32 = User32.INSTANCE;
Runtime runtime;
private WinDef.HWND hwnd;
private HashMap<Integer, Hotkey> registeredKeys;
private HashMap<Integer, Integer> pressedKeys;
private WinUser.MSG msg;
/** Signal for the OSInterface to stop it's thread runnable. **/
private boolean stop = false;
/** Stack for hotkeys to be registered. */
private BlockingQueue<Hotkey> hotkeyRegQueue;
private BlockingQueue<Integer> hotkeyUnRegQueue;
/** Stack for keys to be emulated. */
private BlockingQueue<WinUser.INPUT[]> keySendQueue;
private OSInterface() {
registeredKeys = new HashMap<>();
pressedKeys = new HashMap<>();
msg = new WinUser.MSG();
runtime = Runtime.getRuntime();
hotkeyRegQueue = new LinkedBlockingQueue<>();
hotkeyUnRegQueue = new LinkedBlockingQueue<>();
keySendQueue = new LinkedBlockingQueue<>();
}
/**
* Gets the OSInterface instance.
*/
public static OSInterface getInstance() {
if (instance == null)
instance = new OSInterface();
return instance;
}
protected interface User32Extended extends User32 {
User32Extended INSTANCE = (User32Extended)
Native.load("user32", User32Extended.class, W32APIOptions.DEFAULT_OPTIONS);
int SPI_GETMOUSESPEED = 0x0070;
int SPI_SETMOUSESPEED = 0x0071;
boolean SystemParametersInfo(
int uiAction,
int uiParam,
Pointer pvParam,
int fWinIni
);
boolean SystemParametersInfo(
int uiAction,
int uiParam,
int pvParam,
int fWinIni
);
}
/**
* Stops the OSInterface runnable.
* Only should be called when the program is shutting down.
*/
public void stop() throws IOException {
this.stop = true;
saveEverything end = new saveEverything();
end.saveToFile();
}
/**
* Main thread for checking messages and processing requests.
*/
public void run() {
boolean isMsg = user32.PeekMessage(msg, null, 0, 0, 1);
while (msg.message != User32.WM_QUIT && !stop) {
if (isMsg) {
if (msg.message == User32.WM_HOTKEY) {
int keyPressed = msg.wParam.intValue();
if (registeredKeys.containsKey(keyPressed)) {
int keys = pressedKeys.get(keyPressed);
pressedKeys.put(keyPressed, keys + 1);
}
}
}
Queue<Hotkey> toReg = new LinkedList<>();
Queue<Integer> toUnReg = new LinkedList<>();
Queue<WinUser.INPUT[]> toSendKeys = new LinkedList<>();
hotkeyRegQueue.drainTo(toReg);
hotkeyUnRegQueue.drainTo(toUnReg);
keySendQueue.drainTo(toSendKeys);
// Send all keys in the queue.
while (!toSendKeys.isEmpty()) {
WinUser.INPUT[] inputs = toSendKeys.remove();
WinDef.DWORD length = new WinDef.DWORD(inputs.length);
int sizeBytes = inputs[0].size();
WinDef.DWORD sent = user32.SendInput(length, inputs, sizeBytes);
if (sent.intValue() == 0) {
System.err.println("Error sending input!");
}
}
registerQueuedHotkeys(toReg);
unregisterQueuedHotkeys(toUnReg);
isMsg = user32.PeekMessage(msg, null, 0, 0, 1);
try {
Thread.sleep(10);
} catch (Exception e) {
System.err.println("Problem sleeping.");
}
}
// Unregister all hotkeys after program termination.
registeredKeys.forEach((k, v) -> {
if (!unregisterHotkeyThread(k)) {
System.err.println("Error unregistering hotkey!");
}
});
}
/**
* Sets the HWND to use for interfacing with the OS.
* @param hwnd The HWND of a window.
*/
public void setHWND(WinDef.HWND hwnd) {
if (this.hwnd == null)
this.hwnd = hwnd;
}
@Override
public boolean registerHotkey(Hotkey hotkey) {
int key = hotkey.getKeyCode();
if (registeredKeys.containsKey(hotkey.getID()) || key < 1 || key > 254)
return false;
hotkeyRegQueue.add(new Hotkey(key, hotkey.getID(), hotkey.getModifier()));
return true;
}
/**
* The logic for registerHotkey that will run on the thread.
* @param hotkey The hotkey to be registered.
* @return If the hotkey registration was successful.
*/
protected boolean registerHotkeyThread(Hotkey hotkey) {
if (registeredKeys.containsKey(hotkey.getID()))
return false;
boolean success = user32.RegisterHotKey(null, hotkey.getID(), hotkey.getModifier(), hotkey.getKeyCode());
if (success) {
registeredKeys.put(hotkey.getID(), hotkey);
pressedKeys.put(hotkey.getID(), 0);
}
return success;
}
/**
* Registers all the hotkeys currently waiting to be registered with the OS.
*/
protected void registerQueuedHotkeys(Queue<Hotkey> hotkeys) {
while (!hotkeys.isEmpty()) {
Hotkey hotkey = hotkeys.remove();
if (!registerHotkeyThread(hotkey))
System.err.println("Error registering hotkey: " + hotkey.toString());
}
}
/**
* Registers all the hotkeys currently waiting to be registered with the OS.
*/
protected void unregisterQueuedHotkeys(Queue<Integer> ids) {
while (!ids.isEmpty()) {
int id = ids.remove();
if (!unregisterHotkeyThread(id))
System.err.println("Error registering hotkey ID: " + id);
}
}
@Override
public boolean unregisterHotkey(int id) {
if (!registeredKeys.containsKey(id))
return false;
hotkeyUnRegQueue.add(id);
return true;
}
/**
* The logic for unregisterHotkey that will run on the thread.
* @param id The id of the hotkey to be unregistered.
* @return If the hotkey unregistration was successful.
*/
private boolean unregisterHotkeyThread(int id) {
return user32.UnregisterHotKey(null, id);
}
@Override
public boolean setMouseSpeed(int speed) {
if (speed < 1 || speed > 20)
return false;
boolean result = User32Extended.INSTANCE.SystemParametersInfo(
User32Extended.SPI_SETMOUSESPEED,
0,
speed,
0
);
return result;
}
@Override
public boolean wasPressed(int id) {
if (!pressedKeys.containsKey(id))
return false;
int keysLeft = pressedKeys.get(id);
boolean pressed = keysLeft > 0;
// Decrement the pressedKeys value.
if (keysLeft > 0)
pressedKeys.put(id, keysLeft - 1);
return pressed;
}
private String stringProcessNameID(WinDef.DWORD processID) {
return "";
}
public boolean launchApplication(String path) {
try {
runtime.exec(path);
return true;
} catch (Exception e) {
System.err.println("Error launching applications: " + e.getMessage());
return false;
}
}
@Override
public void sendKey(int keyCode, boolean release) {
if (keyCode > 0 && keyCode < 255) {
WinUser.INPUT[] inputs = (WinUser.INPUT[]) new WinUser.INPUT().toArray(2);
final int LEFTDOWN = 0x0002;
final int LEFTUP = 0x0004;
final int RIGHTDOWN = 0x0008;
final int RIGHTUP = 0x0010;
final int MIDDLEDOWN = 0x0020;
final int MIDDLEUP = 0x0040;
final int XDOWN = 0x0080;
final int XUP = 0x0100;
// Mouse input
if (keyCode < 7 && keyCode != 3) {
int mouseDown = 0;
int mouseUp = 0;
int xButton = 0;
switch (keyCode) {
case 1:
mouseDown = LEFTDOWN;
mouseUp = LEFTUP;
break;
case 2:
mouseDown = RIGHTDOWN;
mouseUp = RIGHTUP;
break;
case 4:
mouseDown = MIDDLEDOWN;
mouseUp = MIDDLEUP;
break;
case 5:
mouseDown = XDOWN;
mouseUp = XUP;
xButton = 0x0001;
break;
case 6:
mouseDown = XDOWN;
mouseUp = XUP;
xButton = 0x0002;
default:
System.err.println("Invalid sendKey switch state!");
break;
}
// Mouse button input
inputs[0].type = new WinUser.DWORD(WinUser.INPUT.INPUT_MOUSE);
inputs[0].input.setType("mi");
if (xButton != 0) {
inputs[0].input.mi.mouseData = new WinDef.DWORD(xButton);
}
if (release)
inputs[0].input.mi.dwFlags = new WinDef.DWORD(mouseUp);
else
inputs[0].input.mi.dwFlags = new WinDef.DWORD(mouseDown | mouseUp);
inputs[0].input.mi.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
}
// Keyboard input
else {
if (!release) {
// Key down input
inputs[0].type = new WinUser.DWORD(WinUser.INPUT.INPUT_KEYBOARD);
inputs[0].input.setType("ki");
inputs[0].input.ki.wVk = new WinDef.WORD(keyCode);
inputs[0].input.ki.dwFlags = new WinDef.DWORD(0);
inputs[0].input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
}
// Key up input
inputs[1].type = new WinUser.DWORD(WinUser.INPUT.INPUT_KEYBOARD);
inputs[1].input.setType("ki");
inputs[1].input.ki.wVk = new WinDef.WORD(keyCode);
inputs[1].input.ki.dwFlags = new WinDef.DWORD(WinUser.KEYBDINPUT.KEYEVENTF_KEYUP);
inputs[1].input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
}
keySendQueue.add(inputs);
}
}
/**
* Moves the mouse by the given pixels.
* @param x The pixels to move in the x-axis.
* @param y The pixels to move in the y-axis.
*/
public void moveMouse(int x, int y) {
WinUser.INPUT[] inputs = (WinUser.INPUT[]) new WinUser.INPUT().toArray(1);
final int MOUSEEVENTF_MOVE = 0x0001;
final int MOUSEEVENTF_ABSOLUTE = 0x8000;
inputs[0].type = new WinUser.DWORD(WinUser.INPUT.INPUT_MOUSE);
inputs[0].input.setType("mi");
inputs[0].input.mi.dx = new WinDef.LONG(x);
inputs[0].input.mi.dy = new WinDef.LONG(y);
inputs[0].input.mi.dwFlags = new WinDef.DWORD(MOUSEEVENTF_MOVE);
inputs[0].input.mi.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
keySendQueue.add(inputs);
}
}