diff --git a/pom.xml b/pom.xml
index 279193ec..ba1864dd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
21
0.9.28
mousemaster
- mousemaster.MousemasterApplication
+ mousemaster.platform.windows.WindowsMain
5.13.0
diff --git a/src/main/java/mousemaster/ApplicationOptions.java b/src/main/java/mousemaster/ApplicationOptions.java
new file mode 100644
index 00000000..f0517ef5
--- /dev/null
+++ b/src/main/java/mousemaster/ApplicationOptions.java
@@ -0,0 +1,50 @@
+package mousemaster;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+public record ApplicationOptions(String tempDirectory,
+ String logLevel,
+ boolean logToFile,
+ boolean pauseOnError,
+ boolean showVersion,
+ Path configurationPath,
+ boolean multipleInstancesAllowed,
+ boolean keyRegurgitationEnabled,
+ boolean graalvmAgentRun) {
+
+ public static ApplicationOptions parse(String[] args) {
+ return new ApplicationOptions(
+ stringArg(args, "--temp-directory=", null),
+ stringArg(args, "--log-level=", null),
+ booleanArg(args, "--log-to-file=", false),
+ booleanArg(args, "--pause-on-error=", true),
+ Stream.of(args).anyMatch(Predicate.isEqual("--version")),
+ Paths.get(stringArg(args, "--configuration-file=",
+ "mousemaster.properties")),
+ booleanArg(args, "--multiple-instances-allowed=", false),
+ booleanArg(args, "--key-regurgitation-enabled=", true),
+ Stream.of(args).anyMatch(Predicate.isEqual("--graalvm-agent-run"))
+ );
+ }
+
+ private static String stringArg(String[] args, String prefix, String defaultValue) {
+ return Stream.of(args)
+ .filter(arg -> arg.startsWith(prefix))
+ .map(arg -> arg.substring(prefix.length()))
+ .findFirst()
+ .orElse(defaultValue);
+ }
+
+ private static boolean booleanArg(String[] args, String prefix, boolean defaultValue) {
+ return Stream.of(args)
+ .filter(arg -> arg.startsWith(prefix))
+ .map(arg -> arg.substring(prefix.length()))
+ .findFirst()
+ .map(Boolean::parseBoolean)
+ .orElse(defaultValue);
+ }
+
+}
diff --git a/src/main/java/mousemaster/ComboWatcher.java b/src/main/java/mousemaster/ComboWatcher.java
index 37546c0c..075cc135 100644
--- a/src/main/java/mousemaster/ComboWatcher.java
+++ b/src/main/java/mousemaster/ComboWatcher.java
@@ -2,6 +2,7 @@
import mousemaster.ComboMove.WaitComboMove;
import mousemaster.ResolvedKeyComboMove.ResolvedPressComboMove;
+import mousemaster.platform.ActiveAppFinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/mousemaster/GridManager.java b/src/main/java/mousemaster/GridManager.java
index d8a9eea6..8b5d41c3 100644
--- a/src/main/java/mousemaster/GridManager.java
+++ b/src/main/java/mousemaster/GridManager.java
@@ -1,6 +1,7 @@
package mousemaster;
import mousemaster.Grid.GridBuilder;
+import mousemaster.platform.Overlay;
/**
* Displays the grid and handles grid commands.
@@ -9,13 +10,16 @@ public class GridManager implements MousePositionListener, ModeListener {
private final ScreenManager screenManager;
private final MouseController mouseController;
+ private final Overlay overlay;
private Grid grid;
private int mouseX, mouseY;
private Mode currentMode;
- public GridManager(ScreenManager screenManager, MouseController mouseController) {
+ public GridManager(ScreenManager screenManager, MouseController mouseController,
+ Overlay overlay) {
this.screenManager = screenManager;
this.mouseController = mouseController;
+ this.overlay = overlay;
}
public void shrinkGridUp() {
@@ -294,7 +298,7 @@ public void modeChanged(Mode newMode) {
.height(gridHeight);
}
case GridArea.ActiveWindowGridArea activeWindowGridArea -> {
- Rectangle activeWindowRectangle = WindowsOverlay.activeWindowRectangle(
+ Rectangle activeWindowRectangle = overlay.activeWindowRectangle(
activeWindowGridArea.widthPercent(),
activeWindowGridArea.heightPercent(), scaledTopInset,
scaledBottomInset, scaledLeftInset, scaledRightInset);
@@ -325,9 +329,9 @@ private void gridChanged() {
private void setOverlay() {
if (grid.lineVisible())
- WindowsOverlay.setGrid(grid);
+ overlay.setGrid(grid);
else
- WindowsOverlay.hideGrid();
+ overlay.hideGrid();
}
@Override
@@ -336,4 +340,3 @@ public void modeTimedOut() {
}
}
-
diff --git a/src/main/java/mousemaster/Hint.java b/src/main/java/mousemaster/Hint.java
index 9a578294..a856688e 100644
--- a/src/main/java/mousemaster/Hint.java
+++ b/src/main/java/mousemaster/Hint.java
@@ -5,7 +5,7 @@
public record Hint(double centerX, double centerY, double cellWidth, double cellHeight,
List keySequence) {
- boolean startsWith(List selectedHintKeySequence) {
+ public boolean startsWith(List selectedHintKeySequence) {
if (selectedHintKeySequence.isEmpty())
return true;
return keySequence.subList(0, selectedHintKeySequence.size())
diff --git a/src/main/java/mousemaster/HintManager.java b/src/main/java/mousemaster/HintManager.java
index 0d131145..0fd479af 100644
--- a/src/main/java/mousemaster/HintManager.java
+++ b/src/main/java/mousemaster/HintManager.java
@@ -4,6 +4,9 @@
import mousemaster.HintGridArea.ActiveWindowHintGridArea;
import mousemaster.HintGridArea.AllScreensHintGridArea;
import mousemaster.HintMesh.HintMeshBuilder;
+import mousemaster.platform.Overlay;
+import mousemaster.platform.UiAutomation;
+import mousemaster.platform.UiAutomation.UiElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -18,6 +21,8 @@ public class HintManager implements ModeListener, MousePositionListener {
private final ScreenManager screenManager;
private final MouseController mouseController;
+ private final Overlay overlay;
+ private final UiAutomation uiAutomation;
private ModeController modeController;
private HintMesh hintMesh;
private ViewportFilter screenFilter;
@@ -40,14 +45,14 @@ public class HintManager implements ModeListener, MousePositionListener {
private boolean lastHintCommandSupercedesOtherCommands;
private record PendingUiHintQuery(
- Future> future,
+ Future> future,
HintMeshConfiguration hintMeshConfiguration,
Zoom zoom,
ViewportFilter screenFilter) {
}
private PendingUiHintQuery pendingUiHintQuery;
- private List lastUiElements;
+ private List lastUiElements;
/**
* It would be better to have an instance of Zoom instead of ZoomConfiguration
@@ -69,10 +74,13 @@ private record HintMeshState(HintMesh hintMesh,
}
public HintManager(int maxPositionHistorySize, ScreenManager screenManager,
- MouseController mouseController) {
+ MouseController mouseController, Overlay overlay,
+ UiAutomation uiAutomation) {
this.maxPositionHistorySize = maxPositionHistorySize;
this.screenManager = screenManager;
this.mouseController = mouseController;
+ this.overlay = overlay;
+ this.uiAutomation = uiAutomation;
}
public void setModeController(ModeController modeController) {
@@ -158,7 +166,7 @@ else if (hintMeshConfiguration.type() instanceof HintMeshType.HintGrid hintGrid
currentMode = newMode;
hintMeshStates.clear();
hintMesh = null;
- WindowsOverlay.hideHintMesh();
+ overlay.hideHintMesh();
return;
}
if (!hintMeshConfiguration.visible()) {
@@ -166,7 +174,7 @@ else if (hintMeshConfiguration.type() instanceof HintMeshType.HintGrid hintGrid
// An alternative would be a setting like hint.reset-selected-key-sequence-history-after-selection=true.
hintMeshStates.clear();
hintMesh = null;
- WindowsOverlay.hideHintMesh();
+ overlay.hideHintMesh();
}
Point zoomCenterPoint = newMode.zoom().center().centerPoint(
screenManager.activeScreen().rectangle(), mouseX, mouseY,
@@ -180,7 +188,7 @@ else if (hintMeshConfiguration.type() instanceof HintMeshType.HintGrid hintGrid
if (currentMode == null ||
!(currentMode.hintMesh().type() instanceof HintMeshType.UiHintMesh)) {
pendingUiHintQuery = new PendingUiHintQuery(
- WindowsUiAutomation.startFindInteractiveUiElements(),
+ uiAutomation.startFindInteractiveUiElements(),
hintMeshConfiguration, newZoom, newScreenFilter);
currentMode = newMode;
currentZoom = newZoom;
@@ -229,7 +237,7 @@ private void activateHintMesh(Mode newMode, HintMesh newHintMesh,
newMode.zoom()),
new HintMeshState(newHintMesh, lastSelectedHintPoint));
hintMesh = newHintMesh;
- WindowsOverlay.setHintMesh(hintMesh, newZoom);
+ overlay.setHintMesh(hintMesh, newZoom);
if (hintMeshConfiguration.mouseMovement() ==
HintMouseMovement.MOUSE_FOLLOWS_HINT_GRID_CENTER) {
moveMouse(hintMeshCenter(hintMesh.hints(), hintMesh.selectedKeySequence()));
@@ -287,7 +295,7 @@ public void completePendingUiHintQuery() {
return;
PendingUiHintQuery pending = pendingUiHintQuery;
pendingUiHintQuery = null;
- List uiElements;
+ List uiElements;
try {
uiElements = pending.future().get();
}
@@ -324,7 +332,7 @@ private ViewportFilter screenFilter(HintMeshConfiguration hintMeshConfiguration)
}
case ActiveWindowHintGridArea activeWindowHintGridArea -> {
Rectangle activeWindowRectangle =
- WindowsOverlay.activeWindowRectangle(1, 1, 0, 0, 0, 0);
+ overlay.activeWindowRectangle(1, 1, 0, 0, 0, 0);
Point gridCenter = activeWindowRectangle.center();
Screen screen =
screenManager.nearestScreenContaining(gridCenter.x(),
@@ -335,7 +343,7 @@ private ViewportFilter screenFilter(HintMeshConfiguration hintMeshConfiguration)
}
else if (type instanceof HintMeshType.UiHintMesh) {
Rectangle activeWindowRectangle =
- WindowsOverlay.activeWindowRectangle(1, 1, 0, 0, 0, 0);
+ overlay.activeWindowRectangle(1, 1, 0, 0, 0, 0);
Point center = activeWindowRectangle.center();
Screen screen = screenManager.nearestScreenContaining(center.x(), center.y());
return ViewportFilter.of(screen);
@@ -352,7 +360,7 @@ private HintMesh buildHintMesh(
HintMeshConfiguration hintMeshConfiguration,
ZoomConfiguration zoomConfiguration, Zoom zoom,
ViewportFilter screenFilter,
- List uiElements) {
+ List uiElements) {
HintMeshBuilder hintMesh = new HintMeshBuilder();
hintMesh.visible(hintMeshConfiguration.visible())
.styleByFilter(hintMeshConfiguration.styleByFilter());
@@ -403,7 +411,7 @@ else if (hintGrid.area() instanceof AllScreensHintGridArea allScreensHintGridAre
}
else if (hintGrid.area() instanceof ActiveWindowHintGridArea activeWindowHintGridArea) {
Rectangle activeWindowRectangle =
- WindowsOverlay.activeWindowRectangle(1, 1, 0, 0, 0, 0);
+ overlay.activeWindowRectangle(1, 1, 0, 0, 0, 0);
Point gridCenter = activeWindowRectangle.center();
Screen screen =
screenManager.nearestScreenContaining(gridCenter.x(), gridCenter.y());
@@ -468,7 +476,7 @@ else if (type instanceof HintMeshType.UiHintMesh) {
.prefixLength(prefixLengths.size() == 1 ?
prefixLengths.iterator().next() : -1)
.backgroundArea(
- WindowsOverlay.activeWindowRectangle(1, 1, 0, 0, 0, 0));
+ overlay.activeWindowRectangle(1, 1, 0, 0, 0, 0));
}
else {
int hintCount = positionHistory.size();
@@ -544,14 +552,14 @@ else if (type instanceof HintMeshType.UiHintMesh) {
private void buildUiHints(HintMeshConfiguration hintMeshConfiguration,
ViewportFilter screenFilter,
- List uiElements,
+ List uiElements,
Set prefixLengths, List hints) {
HintMeshKeys hintMeshKeys =
hintMeshConfiguration.keysByFilter().get(screenFilter);
List selectionKeys = hintMeshKeys.selectionKeys();
int rowKeyOffset = hintMeshKeys.rowKeyOffset();
for (int i = 0; i < uiElements.size(); i++) {
- WindowsUiAutomation.UiElement element = uiElements.get(i);
+ UiElement element = uiElements.get(i);
List keySequence = hintKeySequence(
selectionKeys, rowKeyOffset, uiElements.size(),
0, -1, i,
@@ -911,7 +919,7 @@ public void unselectHintKey() {
hintMeshStates.get(hintMeshKey).previousModeSelectedHintPoint
)
);
- WindowsOverlay.setHintMesh(hintMesh, currentZoom);
+ overlay.setHintMesh(hintMesh, currentZoom);
if (hintMeshConfiguration.mouseMovement() == HintMouseMovement.MOUSE_FOLLOWS_HINT_GRID_CENTER) {
moveMouse(hintMeshCenter(hintMesh.hints(),
hintMesh.selectedKeySequence()));
@@ -987,7 +995,7 @@ public void selectHintKey(Key key) {
hintMesh,
hintMeshStates.get(hintMeshKey).previousModeSelectedHintPoint
));
- WindowsOverlay.setHintMesh(hintMesh, currentZoom);
+ overlay.setHintMesh(hintMesh, currentZoom);
if (hintMeshConfiguration.mouseMovement() == HintMouseMovement.MOUSE_FOLLOWS_HINT_GRID_CENTER) {
moveMouse(hintMeshCenter(hintMesh.hints(), newSelectedKeySequence));
}
@@ -1031,7 +1039,7 @@ private void finalizeHintSelection(Hint hint, List newSelectedKeySequence)
.map(Key::name)
.toList() +
" selected");
- WindowsOverlay.animateHintMatch(hint);
+ overlay.animateHintMatch(hint);
hintMesh =
hintMesh.builder()
.selectedKeySequence(newSelectedKeySequence)
diff --git a/src/main/java/mousemaster/IndicatorManager.java b/src/main/java/mousemaster/IndicatorManager.java
index d7e1fd5e..444ffa25 100644
--- a/src/main/java/mousemaster/IndicatorManager.java
+++ b/src/main/java/mousemaster/IndicatorManager.java
@@ -1,12 +1,17 @@
package mousemaster;
+import mousemaster.platform.Overlay;
+
public class IndicatorManager implements ModeListener {
+ private final Overlay overlay;
private final MouseState mouseState;
private final KeyboardState keyboardState;
private Mode currentMode;
- public IndicatorManager(MouseState mouseState, KeyboardState keyboardState) {
+ public IndicatorManager(Overlay overlay, MouseState mouseState,
+ KeyboardState keyboardState) {
+ this.overlay = overlay;
this.mouseState = mouseState;
this.keyboardState = keyboardState;
}
@@ -28,15 +33,15 @@ private void updateIndicator(boolean allowFade) {
if (currentMode.indicator().enabled()) {
Indicator indicator = activeIndicator();
if (indicator.hexColor() == null)
- WindowsOverlay.hideIndicator(allowFade);
+ overlay.hideIndicator(allowFade);
else
- WindowsOverlay.setIndicator(indicator,
+ overlay.setIndicator(indicator,
currentMode.indicator().fadeAnimationEnabled(),
currentMode.indicator().fadeAnimationDuration(),
allowFade);
}
else
- WindowsOverlay.hideIndicator(allowFade);
+ overlay.hideIndicator(allowFade);
}
@Override
diff --git a/src/main/java/mousemaster/KeyboardLayout.java b/src/main/java/mousemaster/KeyboardLayout.java
index 0a2a640e..1dd91ac4 100644
--- a/src/main/java/mousemaster/KeyboardLayout.java
+++ b/src/main/java/mousemaster/KeyboardLayout.java
@@ -29,6 +29,7 @@ public final class KeyboardLayout {
Type listType = new TypeToken>() {}.getType();
Gson gson = new GsonBuilder()
.registerTypeAdapter(Key.class, new KeyDeserializer())
+ .registerTypeAdapter(KeyboardLayoutKey.class, new KeyboardLayoutKeyDeserializer())
.create();
long before = System.nanoTime();
List keyboardLayouts = gson.fromJson(reader, listType);
@@ -57,11 +58,27 @@ public Key deserialize(JsonElement json, Type typeOfT, JsonDeserializationContex
}
}
- public record KeyboardLayoutKey(int scanCode, WindowsVirtualKey virtualKey, Key key,
+ public record KeyboardLayoutKey(int scanCode, String virtualKeyName, Key key,
String text, String name) {
}
+ public static class KeyboardLayoutKeyDeserializer implements JsonDeserializer {
+
+ @Override
+ public KeyboardLayoutKey deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context)
+ throws JsonParseException {
+ JsonObject obj = json.getAsJsonObject();
+ int scanCode = obj.get("scanCode").getAsInt();
+ String virtualKeyName = obj.get("virtualKey").getAsString();
+ Key key = context.deserialize(obj.get("key"), Key.class);
+ String text = obj.has("text") ? obj.get("text").getAsString() : null;
+ String name = obj.has("name") ? obj.get("name").getAsString() : null;
+ return new KeyboardLayoutKey(scanCode, virtualKeyName, key, text, name);
+ }
+ }
+
private final String identifier;
private final String displayName;
private final String driverName;
@@ -110,9 +127,9 @@ public Key keyFromScanCode(int scanCode) {
return null;
}
- public Key keyFromVirtualKey(WindowsVirtualKey virtualKey) {
+ public Key keyFromVirtualKeyName(String virtualKeyName) {
for (KeyboardLayoutKey keyboardLayoutKey : keys) {
- if (keyboardLayoutKey.virtualKey == virtualKey)
+ if (virtualKeyName.equals(keyboardLayoutKey.virtualKeyName()))
return keyboardLayoutKey.key();
}
return null;
@@ -126,10 +143,10 @@ public int scanCode(Key key) {
return -1;
}
- public WindowsVirtualKey virtualKey(Key key) {
+ public String virtualKeyName(Key key) {
for (KeyboardLayoutKey keyboardLayoutKey : keys) {
if (keyboardLayoutKey.key.equals(key))
- return keyboardLayoutKey.virtualKey();
+ return keyboardLayoutKey.virtualKeyName();
}
return null;
}
diff --git a/src/main/java/mousemaster/KeyboardManager.java b/src/main/java/mousemaster/KeyboardManager.java
index 1d061f86..61a184a4 100644
--- a/src/main/java/mousemaster/KeyboardManager.java
+++ b/src/main/java/mousemaster/KeyboardManager.java
@@ -1,6 +1,7 @@
package mousemaster;
import mousemaster.ComboWatcher.ComboWatcherUpdateResult;
+import mousemaster.platform.KeyRegurgitator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/mousemaster/MacroPlayer.java b/src/main/java/mousemaster/MacroPlayer.java
index ab82520d..901387b5 100644
--- a/src/main/java/mousemaster/MacroPlayer.java
+++ b/src/main/java/mousemaster/MacroPlayer.java
@@ -2,6 +2,7 @@
import mousemaster.KeyEvent.PressKeyEvent;
import mousemaster.KeyEvent.ReleaseKeyEvent;
+import mousemaster.platform.Keyboard;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,6 +18,7 @@ public class MacroPlayer {
private final PlatformClock clock;
private final ComboWatcher comboWatcher;
private final KeyboardManager keyboardManager;
+ private final Keyboard keyboard;
private final List macrosToExecute = new ArrayList<>();
private MacroInProgress macroInProgress;
private final Set keysPressedByMacro = new HashSet<>();
@@ -51,10 +53,11 @@ public class MacroPlayer {
private final Set deferredUserReleases = new HashSet<>();
public MacroPlayer(PlatformClock clock, ComboWatcher comboWatcher,
- KeyboardManager keyboardManager) {
+ KeyboardManager keyboardManager, Keyboard keyboard) {
this.clock = clock;
this.comboWatcher = comboWatcher;
this.keyboardManager = keyboardManager;
+ this.keyboard = keyboard;
}
private static class MacroInProgress {
@@ -113,7 +116,7 @@ public void breakMacro() {
List releases = new ArrayList<>();
for (Key key : keysPressedByMacro)
releases.add(new ResolvedKeyMacroMove(key, false, MacroMoveDestination.OS));
- WindowsKeyboard.sendInputMoves(releases, false);
+ keyboard.sendInputMoves(releases, false);
}
reset();
}
@@ -130,7 +133,7 @@ public void reset() {
public void keyPressedNotEaten(Key key) {
macroReleasedKeys.remove(key);
- WindowsKeyboard.keyPressedNotEaten(key);
+ keyboard.keyPressedNotEaten(key);
}
public void keyReleasedNotEaten(Key key) {
@@ -139,7 +142,7 @@ public void keyReleasedNotEaten(Key key) {
macroReleasedKeys.remove(key);
// Scenario where user-press-eaten, then macro-press (uneats the key), then user-release (not eaten as per rule 1).
// The macro-press is a repeating SendInput that should be stopped when user releases the key.
- WindowsKeyboard.keyReleasedNotEaten(key);
+ keyboard.keyReleasedNotEaten(key);
}
public void newKeyEvent() {
@@ -184,12 +187,12 @@ private void processDeferredReleases() {
deferredUserReleases.clear();
if (!releases.isEmpty()) {
logger.debug("Processing deferred user releases: " + releases);
- WindowsKeyboard.sendInputMoves(releases, false);
+ keyboard.sendInputMoves(releases, false);
}
}
public void recordEarlyRelease(Key key) {
- WindowsKeyboard.recordEarlyReleaseForQueuedPress(key);
+ keyboard.recordEarlyReleaseForQueuedPress(key);
if (macroInProgress == null)
return;
for (int i = macroInProgress.currentIndex + 1;
@@ -214,7 +217,7 @@ public void recordEarlyRelease(Key key) {
*/
public void clearEarlyRelease(Key key) {
earlyReleasedKeys.remove(key);
- WindowsKeyboard.clearEarlyReleaseForQueuedPress(key);
+ keyboard.clearEarlyReleaseForQueuedPress(key);
}
public void update(double delta) {
@@ -315,7 +318,7 @@ private void executeParallel(ResolvedMacroParallel parallel) {
else {
// Flush any pending OS moves before sending to ComboWatcher.
if (!osMoves.isEmpty()) {
- WindowsKeyboard.sendInputMoves(osMoves, true);
+ keyboard.sendInputMoves(osMoves, true);
osMoves.clear();
}
KeyEvent event = keyMove.press()
@@ -328,7 +331,7 @@ private void executeParallel(ResolvedMacroParallel parallel) {
}
// Flush remaining OS moves.
if (!osMoves.isEmpty()) {
- WindowsKeyboard.sendInputMoves(osMoves, true);
+ keyboard.sendInputMoves(osMoves, true);
}
}
diff --git a/src/main/java/mousemaster/MouseController.java b/src/main/java/mousemaster/MouseController.java
index 0a6c3610..6a295f11 100644
--- a/src/main/java/mousemaster/MouseController.java
+++ b/src/main/java/mousemaster/MouseController.java
@@ -1,5 +1,7 @@
package mousemaster;
+import mousemaster.platform.PlatformMouse;
+
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
@@ -7,6 +9,7 @@
public class MouseController implements ModeListener, MousePositionListener {
private final ScreenManager screenManager;
+ private final PlatformMouse platformMouse;
private Mouse mouse;
private Wheel wheel;
private double moveDuration;
@@ -43,8 +46,10 @@ public class MouseController implements ModeListener, MousePositionListener {
private int jumpBeginX, jumpBeginY;
private int jumpEndX, jumpEndY;
- public MouseController(ScreenManager screenManager) {
+ public MouseController(ScreenManager screenManager,
+ PlatformMouse platformMouse) {
this.screenManager = screenManager;
+ this.platformMouse = platformMouse;
}
public void reset() {
@@ -104,7 +109,7 @@ private boolean activelyWheeling() {
public void update(double delta) {
if (activelyMoving()) {
- WindowsMouse.beginMove();
+ platformMouse.beginMove();
moveDuration += delta;
double moveVelocity = moveVelocity();
// Save direction for potential deceleration.
@@ -131,7 +136,7 @@ else if (!xMoveForwardStack.isEmpty()) {
deltaBigEnough = deltaDistanceY >= 1;
}
if (deltaBigEnough && !jumping) {
- WindowsMouse.moveBy(
+ platformMouse.moveBy(
!xMoveForwardStack.isEmpty() && xMoveForwardStack.peek(),
deltaDistanceX,
!yMoveForwardStack.isEmpty() && yMoveForwardStack.peek(),
@@ -140,7 +145,7 @@ else if (!xMoveForwardStack.isEmpty()) {
}
}
else if (decelerating) {
- WindowsMouse.beginMove();
+ platformMouse.beginMove();
decelerationDuration += delta;
double velocity = decelerationVelocity(mouse.velocity(),
velocityAtDecelerationStart, decelerationDuration);
@@ -166,7 +171,7 @@ else if (decelerateXActive) {
deltaBigEnough = deltaDistanceY >= 1;
}
if (deltaBigEnough && !jumping) {
- WindowsMouse.moveBy(
+ platformMouse.moveBy(
decelerateXActive && decelerateXForward,
deltaDistanceX,
decelerateYActive && decelerateYForward,
@@ -176,7 +181,7 @@ else if (decelerateXActive) {
}
}
else if (!jumping)
- WindowsMouse.endMove();
+ platformMouse.endMove();
if (jumping) {
jumpDuration += delta;
double jumpVelocity = mouse.smoothJumpVelocity(); // Pixels per second.
@@ -205,7 +210,7 @@ else if (!jumping)
}
}
if (nextJumpX != jumpX || nextJumpY != jumpY) {
- WindowsMouse.synchronousMoveTo(nextJumpX, nextJumpY);
+ platformMouse.synchronousMoveTo(nextJumpX, nextJumpY);
jumpX = nextJumpX;
jumpY = nextJumpY;
}
@@ -222,9 +227,9 @@ else if (!jumping)
if (lastWheelYActive)
lastWheelYForward = yWheelForwardStack.peek();
if (!xWheelForwardStack.isEmpty())
- WindowsMouse.wheelHorizontallyBy(xWheelForwardStack.peek(), deltaDistance);
+ platformMouse.wheelHorizontallyBy(xWheelForwardStack.peek(), deltaDistance);
if (!yWheelForwardStack.isEmpty())
- WindowsMouse.wheelVerticallyBy(yWheelForwardStack.peek(), deltaDistance);
+ platformMouse.wheelVerticallyBy(yWheelForwardStack.peek(), deltaDistance);
}
else if (wheelDecelerating) {
wheelDecelerationDuration += delta;
@@ -236,9 +241,9 @@ else if (wheelDecelerating) {
}
else {
if (wheelDecelerateXActive)
- WindowsMouse.wheelHorizontallyBy(wheelDecelerateXForward, deltaDistance);
+ platformMouse.wheelHorizontallyBy(wheelDecelerateXForward, deltaDistance);
if (wheelDecelerateYActive)
- WindowsMouse.wheelVerticallyBy(wheelDecelerateYForward, deltaDistance);
+ platformMouse.wheelVerticallyBy(wheelDecelerateYForward, deltaDistance);
}
}
}
@@ -464,20 +469,20 @@ public void stopMoveRight() {
public void clickLeft() {
if (!leftPressing)
- WindowsMouse.pressLeft();
- WindowsMouse.releaseLeft();
+ platformMouse.pressLeft();
+ platformMouse.releaseLeft();
}
public void clickMiddle() {
if (!middlePressing)
- WindowsMouse.pressMiddle();
- WindowsMouse.releaseMiddle();
+ platformMouse.pressMiddle();
+ platformMouse.releaseMiddle();
}
public void clickRight() {
if (!rightPressing)
- WindowsMouse.pressRight();
- WindowsMouse.releaseRight();
+ platformMouse.pressRight();
+ platformMouse.releaseRight();
}
public void pressLeft() {
@@ -485,7 +490,7 @@ public void pressLeft() {
return;
releaseAll();
leftPressing = true;
- WindowsMouse.pressLeft();
+ platformMouse.pressLeft();
}
public void pressMiddle() {
@@ -493,7 +498,7 @@ public void pressMiddle() {
return;
releaseAll();
middlePressing = true;
- WindowsMouse.pressMiddle();
+ platformMouse.pressMiddle();
}
public void pressRight() {
@@ -501,24 +506,24 @@ public void pressRight() {
return;
releaseAll();
rightPressing = true;
- WindowsMouse.pressRight();
+ platformMouse.pressRight();
}
public void releaseLeft() {
if (leftPressing)
- WindowsMouse.releaseLeft();
+ platformMouse.releaseLeft();
leftPressing = false;
}
public void releaseMiddle() {
if (middlePressing)
- WindowsMouse.releaseMiddle();
+ platformMouse.releaseMiddle();
middlePressing = false;
}
public void releaseRight() {
if (rightPressing)
- WindowsMouse.releaseRight();
+ platformMouse.releaseRight();
rightPressing = false;
}
@@ -606,11 +611,11 @@ public void stopWheelRight() {
}
public void showCursor() {
- WindowsMouse.showCursor();
+ platformMouse.showCursor();
}
public void hideCursor() {
- WindowsMouse.hideCursor();
+ platformMouse.hideCursor();
}
public void moveTo(int x, int y) {
@@ -623,8 +628,8 @@ public void moveTo(int x, int y) {
return;
// Move a single pixel. Skype's titlebar does not like being dragged too quick too far.
if (!mouse.smoothJumpEnabled()) {
- WindowsMouse.beginMove();
- WindowsMouse.synchronousMoveTo(x, y);
+ platformMouse.beginMove();
+ platformMouse.synchronousMoveTo(x, y);
return;
}
// If already jumping but one direction changes, then reset velocity.
@@ -638,7 +643,7 @@ public void moveTo(int x, int y) {
jumpX = mouseX;
jumpY = mouseY;
jumping = true;
- WindowsMouse.beginMove();
+ platformMouse.beginMove();
jumpEndX = x;
jumpEndY = y;
}
@@ -701,8 +706,8 @@ public void modeChanged(Mode newMode) {
if (jumping && !mouse.smoothJumpEnabled()) {
jumping = false;
jumpDuration = 0;
- WindowsMouse.beginMove();
- WindowsMouse.synchronousMoveTo(jumpEndX, jumpEndY);
+ platformMouse.beginMove();
+ platformMouse.synchronousMoveTo(jumpEndX, jumpEndY);
}
if (newMode.stopCommandsFromPreviousMode()) {
stopMoveDown();
diff --git a/src/main/java/mousemaster/Mousemaster.java b/src/main/java/mousemaster/Mousemaster.java
index 1bf4c73e..d2cf8218 100644
--- a/src/main/java/mousemaster/Mousemaster.java
+++ b/src/main/java/mousemaster/Mousemaster.java
@@ -31,7 +31,7 @@ public class Mousemaster {
public Mousemaster(Path configurationPath, Platform platform) throws IOException {
this.configurationPath = configurationPath;
this.platform = platform;
- this.activeKeyboardLayout = platform.activeKeyboardLayout();
+ this.activeKeyboardLayout = platform.keyboardLayoutProvider().activeKeyboardLayout();
QtManager.initialize();
loadConfiguration(true);
watchService = FileSystems.getDefault().newWatchService();
@@ -51,42 +51,42 @@ public void run() throws InterruptedException {
updateConfiguration();
long timeAfterOp = System.nanoTime();
long updateConfigurationDuration = (long) ((timeAfterOp - timeBeforeOp) / 1e6);
- platform.windowsMessagePump();
+ platform.pumpEvents();
timeBeforeOp = timeAfterOp;
updateActiveKeyboardLayout(delta);
timeAfterOp = System.nanoTime();
long updateActiveKeyboardLayoutDuration = (long) ((timeAfterOp - timeBeforeOp) / 1e6);
- platform.windowsMessagePump();
+ platform.pumpEvents();
timeBeforeOp = timeAfterOp;
QtManager.processEvents();
platform.update(delta);
timeAfterOp = System.nanoTime();
long platformDuration = (long) ((timeAfterOp - timeBeforeOp) / 1e6);
- platform.windowsMessagePump();
+ platform.pumpEvents();
timeBeforeOp = timeAfterOp;
modeController.update(delta);
timeAfterOp = System.nanoTime();
long modeControllerDuration = (long) ((timeAfterOp - timeBeforeOp) / 1e6);
- platform.windowsMessagePump();
+ platform.pumpEvents();
timeBeforeOp = timeAfterOp;
mouseController.update(delta);
timeAfterOp = System.nanoTime();
long mouseControllerDuration = (long) ((timeAfterOp - timeBeforeOp) / 1e6);
- platform.windowsMessagePump();
+ platform.pumpEvents();
timeBeforeOp = timeAfterOp;
keyboardManager.update(delta);
timeAfterOp = System.nanoTime();
long keyboardManagerDuration = (long) ((timeAfterOp - timeBeforeOp) / 1e6);
- platform.windowsMessagePump();
+ platform.pumpEvents();
timeBeforeOp = timeAfterOp;
indicatorManager.update(delta);
timeAfterOp = System.nanoTime();
long indicatorManagerDuration = (long) ((timeAfterOp - timeBeforeOp) / 1e6);
- platform.windowsMessagePump();
+ platform.pumpEvents();
timeBeforeOp = timeAfterOp;
zoomManager.update(delta);
timeAfterOp = System.nanoTime();
- platform.windowsMessagePump();
+ platform.pumpEvents();
timeBeforeOp = timeAfterOp;
macroPlayer.update(delta);
timeAfterOp = System.nanoTime();
@@ -112,7 +112,7 @@ public void run() throws InterruptedException {
private void updateActiveKeyboardLayout(double delta) {
if (forcedActiveKeyboardLayout != null)
return;
- KeyboardLayout newActiveKeyboardLayout = platform.activeKeyboardLayout();
+ KeyboardLayout newActiveKeyboardLayout = platform.keyboardLayoutProvider().activeKeyboardLayout();
if (!newActiveKeyboardLayout.equals(activeKeyboardLayout)) {
activeKeyboardLayout = newActiveKeyboardLayout;
tryLoadConfiguration(false);
@@ -172,18 +172,18 @@ private void loadConfiguration(boolean readFile) throws IOException {
else
MousemasterApplication.disableLogToFile();
if (configuration.hideConsole())
- MousemasterApplication.hideConsole();
+ platform.console().hide();
else
- MousemasterApplication.showConsole();
+ platform.console().show();
logger.info((reload ? "Reloaded" : "Loaded") + " configuration " +
(readFile ? "file " + configurationPath + " " : "") +
"with active keyboard layout " + activeKeyboardLayout);
- ScreenManager screenManager = new ScreenManager();
- mouseController = new MouseController(screenManager);
+ ScreenManager screenManager = new ScreenManager(platform.screens());
+ mouseController = new MouseController(screenManager, platform.mouse());
MouseState mouseState = new MouseState(mouseController);
- GridManager gridManager = new GridManager(screenManager, mouseController);
+ GridManager gridManager = new GridManager(screenManager, mouseController, platform.overlay());
HintManager hintManager = new HintManager(configuration.maxPositionHistorySize(),
- screenManager, mouseController);
+ screenManager, mouseController, platform.overlay(), platform.uiAutomation());
commandRunner = new CommandRunner(mouseController, gridManager, hintManager);
Set unpressedComboPreconditionKeys = new HashSet<>();
Set pressedComboPreconditionKeys = new HashSet<>();
@@ -201,18 +201,18 @@ private void loadConfiguration(boolean readFile) throws IOException {
}
}
ComboWatcher comboWatcher =
- new ComboWatcher(commandRunner, hintManager, new ActiveAppFinder(),
+ new ComboWatcher(commandRunner, hintManager, platform.activeAppFinder(),
platform.clock(),
unpressedComboPreconditionKeys,
pressedComboPreconditionKeys, configuration.logRedactKeys(),
configuration.modeMap());
keyboardManager = new KeyboardManager(comboWatcher, hintManager,
platform.keyRegurgitator());
- macroPlayer = new MacroPlayer(platform.clock(), comboWatcher, keyboardManager);
+ macroPlayer = new MacroPlayer(platform.clock(), comboWatcher, keyboardManager, platform.keyboard());
keyboardManager.setMacroPlayer(macroPlayer);
KeyboardState keyboardState = new KeyboardState(keyboardManager);
- indicatorManager = new IndicatorManager(mouseState, keyboardState);
- zoomManager = new ZoomManager(screenManager, hintManager);
+ indicatorManager = new IndicatorManager(platform.overlay(), mouseState, keyboardState);
+ zoomManager = new ZoomManager(screenManager, hintManager, platform.overlay());
// ComboWatcher is the sole broadcaster to ModeListeners: it broadcasts
// on mode switch (delegated from ModeController) and on mode mutation.
// ZoomManager must be notified after HintManager because it calls
diff --git a/src/main/java/mousemaster/MousemasterApplication.java b/src/main/java/mousemaster/MousemasterApplication.java
index fa557490..7c6cfeaa 100644
--- a/src/main/java/mousemaster/MousemasterApplication.java
+++ b/src/main/java/mousemaster/MousemasterApplication.java
@@ -7,23 +7,11 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;
-import com.sun.jna.Native;
-import com.sun.jna.platform.win32.Kernel32;
-import com.sun.jna.platform.win32.User32;
-import com.sun.jna.platform.win32.WinDef;
-import com.sun.jna.platform.win32.WinUser;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Properties;
import java.util.Scanner;
-import java.util.function.Predicate;
import java.util.logging.LogManager;
-import java.util.stream.Stream;
public class MousemasterApplication {
@@ -38,88 +26,7 @@ public class MousemasterApplication {
SLF4JBridgeHandler.install();
}
- public static void main(String[] args) throws InterruptedException, IOException {
- String tempDirectory = Stream.of(args)
- .filter(arg -> arg.startsWith("--temp-directory="))
- .map(arg -> arg.split("=")[1])
- .findFirst().orElse(null);
- setTempDirectory(tempDirectory);
- Stream.of(args)
- .filter(arg -> arg.startsWith("--log-level="))
- .map(arg -> arg.split("=")[1])
- .findFirst()
- .ifPresent(MousemasterApplication::setLogLevel);
- boolean logToFile = Stream.of(args)
- .filter(arg -> arg.startsWith("--log-to-file="))
- .map(arg -> arg.split("=")[1])
- .findFirst()
- .map(Boolean::parseBoolean)
- .orElse(false);
- if (logToFile)
- enableLogToFile();
- boolean pauseOnError = Stream.of(args)
- .filter(arg -> arg.startsWith("--pause-on-error="))
- .map(arg -> arg.split("=")[1])
- .findFirst()
- .map(Boolean::parseBoolean)
- .orElse(true);
- String version;
- String commitId;
- try (InputStream versionInputStream = MousemasterApplication.class.getClassLoader().getResourceAsStream("application.properties")) {
- Properties versionProp = new Properties();
- versionProp.load(versionInputStream);
- version = versionProp.getProperty("version");
- commitId = versionProp.getProperty("commitId");
- }
- if (Stream.of(args).anyMatch(Predicate.isEqual(("--version")))) {
- System.out.println("mousemaster v" + version + " (" + commitId + ")");
- return;
- }
- Path configurationPath = Stream.of(args)
- .filter(arg -> arg.startsWith(
- "--configuration-file="))
- .map(arg -> arg.split("=")[1])
- .findFirst()
- .map(Paths::get)
- .orElse(Paths.get("mousemaster.properties"));
- boolean multipleInstancesAllowed =
- Stream.of(args)
- .filter(arg -> arg.startsWith("--multiple-instances-allowed="))
- .map(arg -> arg.split("=")[1])
- .findFirst()
- .map(Boolean::parseBoolean)
- .orElse(false);
- boolean keyRegurgitationEnabled = // Feature flag.
- Stream.of(args)
- .filter(arg -> arg.startsWith("--key-regurgitation-enabled="))
- .map(arg -> arg.split("=")[1])
- .findFirst()
- .map(Boolean::parseBoolean)
- .orElse(true); // Remove this feature flag if confirmed working
- if (Stream.of(args).anyMatch(Predicate.isEqual(("--graalvm-agent-run")))) {
- logger.info("--graalvm-agent-run flag found, exiting in 20s");
- new Thread(() -> {
- try {
- Thread.sleep(20000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- System.exit(0);
- }).start();
- }
- WindowsPlatform platform = platform(multipleInstancesAllowed, keyRegurgitationEnabled, pauseOnError);
- logger.info("mousemaster v" + version + " (" + commitId + ")");
- if (platform == null)
- return;
- try {
- Native.setCallbackExceptionHandler((c, e) -> shutdownAfterException(e, platform, true, pauseOnError));
- new Mousemaster(configurationPath, platform).run();
- } catch (Throwable e) {
- shutdownAfterException(e, platform, false, pauseOnError);
- }
- }
-
- private static void setTempDirectory(String tempDirectory) {
+ public static void setTempDirectory(String tempDirectory) {
if (tempDirectory != null)
MousemasterApplication.tempDirectory = tempDirectory;
else {
@@ -134,24 +41,15 @@ private static void setTempDirectory(String tempDirectory) {
}
}
if (MousemasterApplication.tempDirectory == null)
- MousemasterApplication.tempDirectory = System.getProperty("java.io.tmpdir") + "mousemaster-" + System.getProperty("user.name").hashCode();
+ MousemasterApplication.tempDirectory =
+ System.getProperty("java.io.tmpdir") + "mousemaster-" +
+ System.getProperty("user.name").hashCode();
System.setProperty("jna.tmpdir", MousemasterApplication.tempDirectory + "/jna");
}
- private static WindowsPlatform platform(boolean multipleInstancesAllowed,
- boolean keyRegurgitationEnabled,
- boolean pauseOnError) {
- try {
- return new WindowsPlatform(multipleInstancesAllowed, keyRegurgitationEnabled);
- } catch (Exception e) {
- shutdownAfterException(e, null, false, pauseOnError);
- }
- return null;
- }
-
- private static void shutdownAfterException(Throwable e, Platform platform,
- boolean jnaCallback,
- boolean pauseOnError) {
+ public static void shutdownAfterException(Throwable e, Platform platform,
+ boolean jnaCallback,
+ boolean pauseOnError) {
if (platform != null)
platform.shutdown();
logger.error(jnaCallback ? "Error in JNA callback" : "", e);
@@ -197,15 +95,4 @@ public static void setLogLevel(String level) {
logger.setLevel(Level.valueOf(level));
}
- public static void showConsole() {
- // This should be moved to a Console interface (with one implementation per OS).
- WinDef.HWND hwnd = Kernel32.INSTANCE.GetConsoleWindow();
- User32.INSTANCE.ShowWindow(hwnd, WinUser.SW_SHOW);
- }
-
- public static void hideConsole() {
- WinDef.HWND hwnd = Kernel32.INSTANCE.GetConsoleWindow();
- User32.INSTANCE.ShowWindow(hwnd, WinUser.SW_HIDE);
- }
-
}
diff --git a/src/main/java/mousemaster/Platform.java b/src/main/java/mousemaster/Platform.java
index 34c00604..109e0533 100644
--- a/src/main/java/mousemaster/Platform.java
+++ b/src/main/java/mousemaster/Platform.java
@@ -1,12 +1,22 @@
package mousemaster;
+import mousemaster.platform.ActiveAppFinder;
+import mousemaster.platform.Console;
+import mousemaster.platform.Keyboard;
+import mousemaster.platform.KeyboardLayoutProvider;
+import mousemaster.platform.KeyRegurgitator;
+import mousemaster.platform.Overlay;
+import mousemaster.platform.PlatformMouse;
+import mousemaster.platform.Screens;
+import mousemaster.platform.UiAutomation;
+
import java.util.List;
public interface Platform extends ModeListener {
void update(double delta);
- void windowsMessagePump();
+ void pumpEvents();
void sleep() throws InterruptedException;
@@ -21,6 +31,20 @@ void reset(MouseController mouseController, KeyboardManager keyboardManager,
PlatformClock clock();
- KeyboardLayout activeKeyboardLayout();
+ KeyboardLayoutProvider keyboardLayoutProvider();
+
+ Keyboard keyboard();
+
+ PlatformMouse mouse();
+
+ Screens screens();
+
+ Overlay overlay();
+
+ UiAutomation uiAutomation();
+
+ ActiveAppFinder activeAppFinder();
+
+ Console console();
}
diff --git a/src/main/java/mousemaster/ScreenManager.java b/src/main/java/mousemaster/ScreenManager.java
index 01a8ae44..bd5801be 100644
--- a/src/main/java/mousemaster/ScreenManager.java
+++ b/src/main/java/mousemaster/ScreenManager.java
@@ -1,12 +1,19 @@
package mousemaster;
+import mousemaster.platform.Screens;
+
import java.util.Set;
public class ScreenManager implements MousePositionListener {
+ private final Screens screens;
private int mouseX;
private int mouseY;
+ public ScreenManager(Screens screens) {
+ this.screens = screens;
+ }
+
public Screen activeScreen() {
return nearestScreenContaining(mouseX, mouseY);
}
@@ -35,7 +42,7 @@ public Screen nearestScreenContaining(double pointX, double pointY) {
}
public Set screens() {
- return WindowsScreen.findScreens();
+ return screens.findScreens();
}
public Screen screenContaining(double x, double y) {
diff --git a/src/main/java/mousemaster/ZoomManager.java b/src/main/java/mousemaster/ZoomManager.java
index 03042e43..0dd966d6 100644
--- a/src/main/java/mousemaster/ZoomManager.java
+++ b/src/main/java/mousemaster/ZoomManager.java
@@ -1,5 +1,7 @@
package mousemaster;
+import mousemaster.platform.Overlay;
+
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -8,6 +10,7 @@ public class ZoomManager implements ModeListener, MousePositionListener {
private final ScreenManager screenManager;
private final HintManager hintManager;
+ private final Overlay overlay;
private Mode currentMode;
private int mouseX, mouseY;
@@ -27,9 +30,11 @@ public class ZoomManager implements ModeListener, MousePositionListener {
// The zoom center used to build endHintMesh.
private Point endHintZoomCenter;
- public ZoomManager(ScreenManager screenManager, HintManager hintManager) {
+ public ZoomManager(ScreenManager screenManager, HintManager hintManager,
+ Overlay overlay) {
this.screenManager = screenManager;
this.hintManager = hintManager;
+ this.overlay = overlay;
}
@Override
@@ -50,7 +55,7 @@ public void modeChanged(Mode newMode) {
currentPercent = endPercent;
if (endIsNoZoom) {
currentCenterPoint = null;
- WindowsOverlay.setZoom(null);
+ overlay.setZoom(null);
}
else {
Point centerPoint = newMode.zoom().center().centerPoint(
@@ -59,7 +64,7 @@ public void modeChanged(Mode newMode) {
currentCenterPoint = centerPoint;
Screen screen = screenManager.nearestScreenContaining(centerPoint.x(),
centerPoint.y());
- WindowsOverlay.setZoom(new Zoom(endPercent,
+ overlay.setZoom(new Zoom(endPercent,
centerPoint, screen.rectangle()));
}
}
@@ -98,7 +103,7 @@ public void modeChanged(Mode newMode) {
HintMesh interpolatedMesh = interpolateHintMesh(endHintMesh,
endHintZoomCenter, beginCenterPoint,
screen.rectangle().center(), endPercent, beginPercent);
- WindowsOverlay.setHintMesh(interpolatedMesh,
+ overlay.setHintMesh(interpolatedMesh,
new Zoom(beginPercent, beginCenterPoint, screen.rectangle()));
}
else {
@@ -109,7 +114,7 @@ public void modeChanged(Mode newMode) {
Screen screen = screenManager.nearestScreenContaining(
beginCenterPoint.x(), beginCenterPoint.y());
Zoom beginZoom = new Zoom(beginPercent, beginCenterPoint, screen.rectangle());
- WindowsOverlay.startScreenshotZoomAnimation(screen.rectangle(), beginZoom);
+ overlay.startScreenshotZoomAnimation(screen.rectangle(), beginZoom);
}
}
@@ -132,23 +137,23 @@ public void update(double delta) {
Screen screen = screenManager.nearestScreenContaining(centerPoint.x(),
centerPoint.y());
Zoom currentZoom = new Zoom(currentPercent, centerPoint, screen.rectangle());
- WindowsOverlay.updateScreenshotZoom(currentZoom);
+ overlay.updateScreenshotZoom(currentZoom);
if (endHintMesh != null) {
HintMesh interpolatedMesh = interpolateHintMesh(endHintMesh,
endHintZoomCenter, centerPoint,
screen.rectangle().center(), endPercent, currentPercent);
- WindowsOverlay.setHintMesh(interpolatedMesh, currentZoom);
+ overlay.setHintMesh(interpolatedMesh, currentZoom);
}
if (t >= 1.0) {
animating = false;
Zoom endZoom = endIsNoZoom ? null :
new Zoom(currentPercent, centerPoint, screen.rectangle());
- WindowsOverlay.endScreenshotZoomAnimation(endZoom);
+ overlay.endScreenshotZoomAnimation(endZoom);
if (endHintMesh != null) {
// Restore the final hint mesh.
if (endZoom == null)
endZoom = new Zoom(currentPercent, centerPoint, screen.rectangle());
- WindowsOverlay.setHintMesh(endHintMesh, endZoom);
+ overlay.setHintMesh(endHintMesh, endZoom);
endHintMesh = null;
endHintZoomCenter = null;
}
@@ -252,7 +257,7 @@ public void mouseMoved(int x, int y) {
currentCenterPoint = centerPoint;
Screen screen = screenManager.nearestScreenContaining(centerPoint.x(),
centerPoint.y());
- WindowsOverlay.setZoom(new Zoom(currentMode.zoom().percent(),
+ overlay.setZoom(new Zoom(currentMode.zoom().percent(),
centerPoint, screen.rectangle()));
}
}
diff --git a/src/main/java/mousemaster/platform/ActiveAppFinder.java b/src/main/java/mousemaster/platform/ActiveAppFinder.java
new file mode 100644
index 00000000..51798721
--- /dev/null
+++ b/src/main/java/mousemaster/platform/ActiveAppFinder.java
@@ -0,0 +1,8 @@
+package mousemaster.platform;
+
+import mousemaster.App;
+
+public interface ActiveAppFinder {
+
+ App activeApp();
+}
diff --git a/src/main/java/mousemaster/platform/Console.java b/src/main/java/mousemaster/platform/Console.java
new file mode 100644
index 00000000..76735045
--- /dev/null
+++ b/src/main/java/mousemaster/platform/Console.java
@@ -0,0 +1,8 @@
+package mousemaster.platform;
+
+public interface Console {
+
+ void show();
+
+ void hide();
+}
diff --git a/src/main/java/mousemaster/platform/KeyRegurgitator.java b/src/main/java/mousemaster/platform/KeyRegurgitator.java
new file mode 100644
index 00000000..f36aebbe
--- /dev/null
+++ b/src/main/java/mousemaster/platform/KeyRegurgitator.java
@@ -0,0 +1,8 @@
+package mousemaster.platform;
+
+import mousemaster.KeyboardManager;
+
+public interface KeyRegurgitator {
+
+ void regurgitate(KeyboardManager.Regurgitate regurgitate, boolean startRepeat);
+}
diff --git a/src/main/java/mousemaster/platform/Keyboard.java b/src/main/java/mousemaster/platform/Keyboard.java
new file mode 100644
index 00000000..a31811d2
--- /dev/null
+++ b/src/main/java/mousemaster/platform/Keyboard.java
@@ -0,0 +1,23 @@
+package mousemaster.platform;
+
+import mousemaster.Key;
+import mousemaster.ResolvedMacroMove;
+
+import java.util.List;
+
+public interface Keyboard {
+
+ void update(double delta);
+
+ void reset();
+
+ void sendInputMoves(List moves, boolean startRepeat);
+
+ void keyPressedNotEaten(Key key);
+
+ void keyReleasedNotEaten(Key key);
+
+ void recordEarlyReleaseForQueuedPress(Key key);
+
+ void clearEarlyReleaseForQueuedPress(Key key);
+}
diff --git a/src/main/java/mousemaster/platform/KeyboardLayoutProvider.java b/src/main/java/mousemaster/platform/KeyboardLayoutProvider.java
new file mode 100644
index 00000000..27286d43
--- /dev/null
+++ b/src/main/java/mousemaster/platform/KeyboardLayoutProvider.java
@@ -0,0 +1,8 @@
+package mousemaster.platform;
+
+import mousemaster.KeyboardLayout;
+
+public interface KeyboardLayoutProvider {
+
+ KeyboardLayout activeKeyboardLayout();
+}
diff --git a/src/main/java/mousemaster/platform/Overlay.java b/src/main/java/mousemaster/platform/Overlay.java
new file mode 100644
index 00000000..3337e4cf
--- /dev/null
+++ b/src/main/java/mousemaster/platform/Overlay.java
@@ -0,0 +1,60 @@
+package mousemaster.platform;
+
+import mousemaster.Grid;
+import mousemaster.Hint;
+import mousemaster.HintMesh;
+import mousemaster.HintMeshConfiguration;
+import mousemaster.Indicator;
+import mousemaster.Rectangle;
+import mousemaster.Zoom;
+
+import java.util.Set;
+import java.time.Duration;
+
+public interface Overlay {
+
+ void update(double delta);
+
+ void flushCache();
+
+ void setTopmost();
+
+ void setMessagePump(Runnable pump);
+
+ void preWarmFontStyles(Set configs);
+
+ void preWarmHintMeshWindows();
+
+ Rectangle activeWindowRectangle(double widthPct, double heightPct,
+ int topInset, int bottomInset,
+ int leftInset, int rightInset);
+
+ void setIndicator(Indicator indicator, boolean fadeAnimationEnabled,
+ Duration fadeAnimationDuration, boolean allowFade);
+
+ void hideIndicator(boolean allowFade);
+
+ void setGrid(Grid grid);
+
+ void hideGrid();
+
+ void setHintMesh(HintMesh hintMesh, Zoom zoom);
+
+ void setHintMesh(HintMesh hintMesh, Zoom zoom, boolean hintMatch);
+
+ void hideHintMesh();
+
+ void animateHintMatch(Hint hint);
+
+ void setZoom(Zoom zoom);
+
+ void startScreenshotZoomAnimation(Rectangle screenRect, Zoom beginZoom);
+
+ void updateScreenshotZoom(Zoom zoom);
+
+ void endScreenshotZoomAnimation(Zoom finalZoom);
+
+ boolean waitForZoomBeforeRepainting();
+
+ void setWaitForZoomBeforeRepainting(boolean value);
+}
diff --git a/src/main/java/mousemaster/platform/PlatformMouse.java b/src/main/java/mousemaster/platform/PlatformMouse.java
new file mode 100644
index 00000000..ad7b9928
--- /dev/null
+++ b/src/main/java/mousemaster/platform/PlatformMouse.java
@@ -0,0 +1,32 @@
+package mousemaster.platform;
+
+public interface PlatformMouse {
+
+ void beginMove();
+
+ void endMove();
+
+ void moveBy(boolean xForward, double dx, boolean yForward, double dy);
+
+ void synchronousMoveTo(int x, int y);
+
+ void pressLeft();
+
+ void pressMiddle();
+
+ void pressRight();
+
+ void releaseLeft();
+
+ void releaseMiddle();
+
+ void releaseRight();
+
+ void wheelHorizontallyBy(boolean forward, double delta);
+
+ void wheelVerticallyBy(boolean forward, double delta);
+
+ void showCursor();
+
+ void hideCursor();
+}
diff --git a/src/main/java/mousemaster/platform/Screens.java b/src/main/java/mousemaster/platform/Screens.java
new file mode 100644
index 00000000..25d12e07
--- /dev/null
+++ b/src/main/java/mousemaster/platform/Screens.java
@@ -0,0 +1,10 @@
+package mousemaster.platform;
+
+import mousemaster.Screen;
+
+import java.util.Set;
+
+public interface Screens {
+
+ Set findScreens();
+}
diff --git a/src/main/java/mousemaster/platform/UiAutomation.java b/src/main/java/mousemaster/platform/UiAutomation.java
new file mode 100644
index 00000000..de4959aa
--- /dev/null
+++ b/src/main/java/mousemaster/platform/UiAutomation.java
@@ -0,0 +1,12 @@
+package mousemaster.platform;
+
+import java.util.List;
+import java.util.concurrent.Future;
+
+public interface UiAutomation {
+
+ Future> startFindInteractiveUiElements();
+
+ record UiElement(double centerX, double centerY) {
+ }
+}
diff --git a/src/main/java/mousemaster/Dwmapi.java b/src/main/java/mousemaster/platform/windows/Dwmapi.java
similarity index 86%
rename from src/main/java/mousemaster/Dwmapi.java
rename to src/main/java/mousemaster/platform/windows/Dwmapi.java
index 55413c97..212cb38f 100644
--- a/src/main/java/mousemaster/Dwmapi.java
+++ b/src/main/java/mousemaster/platform/windows/Dwmapi.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef;
diff --git a/src/main/java/mousemaster/ExtendedAdvapi32.java b/src/main/java/mousemaster/platform/windows/ExtendedAdvapi32.java
similarity index 94%
rename from src/main/java/mousemaster/ExtendedAdvapi32.java
rename to src/main/java/mousemaster/platform/windows/ExtendedAdvapi32.java
index faba5a2c..ed498a58 100644
--- a/src/main/java/mousemaster/ExtendedAdvapi32.java
+++ b/src/main/java/mousemaster/platform/windows/ExtendedAdvapi32.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinNT;
diff --git a/src/main/java/mousemaster/ExtendedGDI32.java b/src/main/java/mousemaster/platform/windows/ExtendedGDI32.java
similarity index 98%
rename from src/main/java/mousemaster/ExtendedGDI32.java
rename to src/main/java/mousemaster/platform/windows/ExtendedGDI32.java
index 87ffaf03..c7a39d40 100644
--- a/src/main/java/mousemaster/ExtendedGDI32.java
+++ b/src/main/java/mousemaster/platform/windows/ExtendedGDI32.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.Structure;
diff --git a/src/main/java/mousemaster/ExtendedKernel32.java b/src/main/java/mousemaster/platform/windows/ExtendedKernel32.java
similarity index 87%
rename from src/main/java/mousemaster/ExtendedKernel32.java
rename to src/main/java/mousemaster/platform/windows/ExtendedKernel32.java
index 90f8d483..075955f8 100644
--- a/src/main/java/mousemaster/ExtendedKernel32.java
+++ b/src/main/java/mousemaster/platform/windows/ExtendedKernel32.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinBase;
diff --git a/src/main/java/mousemaster/ExtendedPsapi.java b/src/main/java/mousemaster/platform/windows/ExtendedPsapi.java
similarity index 86%
rename from src/main/java/mousemaster/ExtendedPsapi.java
rename to src/main/java/mousemaster/platform/windows/ExtendedPsapi.java
index f08b3472..603ad44c 100644
--- a/src/main/java/mousemaster/ExtendedPsapi.java
+++ b/src/main/java/mousemaster/platform/windows/ExtendedPsapi.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Psapi;
diff --git a/src/main/java/mousemaster/ExtendedUser32.java b/src/main/java/mousemaster/platform/windows/ExtendedUser32.java
similarity index 98%
rename from src/main/java/mousemaster/ExtendedUser32.java
rename to src/main/java/mousemaster/platform/windows/ExtendedUser32.java
index 29d364d5..5733037b 100644
--- a/src/main/java/mousemaster/ExtendedUser32.java
+++ b/src/main/java/mousemaster/platform/windows/ExtendedUser32.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
diff --git a/src/main/java/mousemaster/FadeAnimator.java b/src/main/java/mousemaster/platform/windows/FadeAnimator.java
similarity index 98%
rename from src/main/java/mousemaster/FadeAnimator.java
rename to src/main/java/mousemaster/platform/windows/FadeAnimator.java
index e5a5e52f..2daafcf6 100644
--- a/src/main/java/mousemaster/FadeAnimator.java
+++ b/src/main/java/mousemaster/platform/windows/FadeAnimator.java
@@ -1,8 +1,9 @@
-package mousemaster;
+package mousemaster.platform.windows;
import io.qt.core.QEasingCurve;
import io.qt.core.QMetaObject;
import io.qt.core.QVariantAnimation;
+import mousemaster.Easing;
import java.time.Duration;
import java.util.function.Consumer;
diff --git a/src/main/java/mousemaster/Gdiplus.java b/src/main/java/mousemaster/platform/windows/Gdiplus.java
similarity index 99%
rename from src/main/java/mousemaster/Gdiplus.java
rename to src/main/java/mousemaster/platform/windows/Gdiplus.java
index 1997dc1c..02a0c459 100644
--- a/src/main/java/mousemaster/Gdiplus.java
+++ b/src/main/java/mousemaster/platform/windows/Gdiplus.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
diff --git a/src/main/java/mousemaster/KbdlayoutinfoParser.java b/src/main/java/mousemaster/platform/windows/KbdlayoutinfoParser.java
similarity index 98%
rename from src/main/java/mousemaster/KbdlayoutinfoParser.java
rename to src/main/java/mousemaster/platform/windows/KbdlayoutinfoParser.java
index 027c7870..39af1831 100644
--- a/src/main/java/mousemaster/KbdlayoutinfoParser.java
+++ b/src/main/java/mousemaster/platform/windows/KbdlayoutinfoParser.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -299,7 +301,7 @@ else if (text != null)
key = Key.ofCharacter(text);
if (key != null)
keyboardLayout.keys()
- .add(new KeyboardLayout.KeyboardLayoutKey(sc, vk, key,
+ .add(new KeyboardLayout.KeyboardLayoutKey(sc, vk.name(), key,
text == null || text.isBlank() ? null : text,
name.isBlank() ? null : name));
}
@@ -353,7 +355,7 @@ private static void addCustomKeyboardLayouts(List keyboardLayout
.orElseThrow();
KeyboardLayout.KeyboardLayoutKey usHalmakKey = new KeyboardLayout.KeyboardLayoutKey(
usQwertyKey.scanCode(),
- sameCharacterUsQwertyKey.virtualKey(),
+ sameCharacterUsQwertyKey.virtualKeyName(),
sameCharacterUsQwertyKey.key(),
sameCharacterUsQwertyKey.text(),
sameCharacterUsQwertyKey.name()
@@ -374,7 +376,7 @@ private static void fixKoreanKeyboardLayout(List keyboardLayouts
.orElseThrow();
koreanKeyboardLayout.keys()
.add(new KeyboardLayout.KeyboardLayoutKey(57400,
- WindowsVirtualKey.VK_RMENU, Key.rightalt, null,
+ WindowsVirtualKey.VK_RMENU.name(), Key.rightalt, null,
"Right Alt"));
}
diff --git a/src/main/java/mousemaster/Magnification.java b/src/main/java/mousemaster/platform/windows/Magnification.java
similarity index 95%
rename from src/main/java/mousemaster/Magnification.java
rename to src/main/java/mousemaster/platform/windows/Magnification.java
index 0aff6f7e..5c8a903e 100644
--- a/src/main/java/mousemaster/Magnification.java
+++ b/src/main/java/mousemaster/platform/windows/Magnification.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Library;
import com.sun.jna.Native;
diff --git a/src/main/java/mousemaster/Shcore.java b/src/main/java/mousemaster/platform/windows/Shcore.java
similarity index 94%
rename from src/main/java/mousemaster/Shcore.java
rename to src/main/java/mousemaster/platform/windows/Shcore.java
index 3a90ec92..38b5fe79 100644
--- a/src/main/java/mousemaster/Shcore.java
+++ b/src/main/java/mousemaster/platform/windows/Shcore.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.IntegerType;
import com.sun.jna.Native;
diff --git a/src/main/java/mousemaster/ActiveAppFinder.java b/src/main/java/mousemaster/platform/windows/WindowsActiveAppFinder.java
similarity index 94%
rename from src/main/java/mousemaster/ActiveAppFinder.java
rename to src/main/java/mousemaster/platform/windows/WindowsActiveAppFinder.java
index d79ec5c8..9627cec0 100644
--- a/src/main/java/mousemaster/ActiveAppFinder.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsActiveAppFinder.java
@@ -1,16 +1,19 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.*;
import com.sun.jna.ptr.IntByReference;
+import mousemaster.platform.ActiveAppFinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects;
-public class ActiveAppFinder {
+public class WindowsActiveAppFinder implements ActiveAppFinder {
- private static final Logger logger = LoggerFactory.getLogger(ActiveAppFinder.class);
+ private static final Logger logger = LoggerFactory.getLogger(WindowsActiveAppFinder.class);
private IntByReference processId = new IntByReference();
private byte[] executableNameBytes = new byte[1024];
@@ -18,6 +21,7 @@ public class ActiveAppFinder {
private String lastIgnoredExecutableName;
private boolean lastAttemptFailed;
+ @Override
public App activeApp() {
WinDef.HWND hwnd = User32.INSTANCE.GetForegroundWindow();
User32.INSTANCE.GetWindowThreadProcessId(hwnd, processId);
diff --git a/src/main/java/mousemaster/WindowsClock.java b/src/main/java/mousemaster/platform/windows/WindowsClock.java
similarity index 94%
rename from src/main/java/mousemaster/WindowsClock.java
rename to src/main/java/mousemaster/platform/windows/WindowsClock.java
index 816bd3cb..9891bfbf 100644
--- a/src/main/java/mousemaster/WindowsClock.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsClock.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import java.time.Instant;
diff --git a/src/main/java/mousemaster/platform/windows/WindowsConsole.java b/src/main/java/mousemaster/platform/windows/WindowsConsole.java
new file mode 100644
index 00000000..fa9b38c7
--- /dev/null
+++ b/src/main/java/mousemaster/platform/windows/WindowsConsole.java
@@ -0,0 +1,25 @@
+package mousemaster.platform.windows;
+
+import mousemaster.*;
+
+import com.sun.jna.platform.win32.Kernel32;
+import com.sun.jna.platform.win32.User32;
+import com.sun.jna.platform.win32.WinDef;
+import com.sun.jna.platform.win32.WinUser;
+import mousemaster.platform.Console;
+
+public class WindowsConsole implements Console {
+
+ @Override
+ public void show() {
+ WinDef.HWND hwnd = Kernel32.INSTANCE.GetConsoleWindow();
+ User32.INSTANCE.ShowWindow(hwnd, WinUser.SW_SHOW);
+ }
+
+ @Override
+ public void hide() {
+ WinDef.HWND hwnd = Kernel32.INSTANCE.GetConsoleWindow();
+ User32.INSTANCE.ShowWindow(hwnd, WinUser.SW_HIDE);
+ }
+
+}
diff --git a/src/main/java/mousemaster/KeyRegurgitator.java b/src/main/java/mousemaster/platform/windows/WindowsKeyRegurgitator.java
similarity index 75%
rename from src/main/java/mousemaster/KeyRegurgitator.java
rename to src/main/java/mousemaster/platform/windows/WindowsKeyRegurgitator.java
index 6b61829d..9c943d76 100644
--- a/src/main/java/mousemaster/KeyRegurgitator.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsKeyRegurgitator.java
@@ -1,17 +1,24 @@
-package mousemaster;
+package mousemaster.platform.windows;
+import mousemaster.*;
+
+import mousemaster.platform.Keyboard;
+import mousemaster.platform.KeyRegurgitator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
-/**
- * This should be an interface with one implementation per platform.
- */
-public class KeyRegurgitator {
+public class WindowsKeyRegurgitator implements KeyRegurgitator {
+
+ private static final Logger logger = LoggerFactory.getLogger(WindowsKeyRegurgitator.class);
+ private final Keyboard keyboard;
- private static final Logger logger = LoggerFactory.getLogger(KeyRegurgitator.class);
+ public WindowsKeyRegurgitator(Keyboard keyboard) {
+ this.keyboard = keyboard;
+ }
+ @Override
public void regurgitate(KeyboardManager.Regurgitate regurgitate, boolean startRepeat) {
logger.debug(
"Regurgitating " + regurgitate.key() + ", startRepeat = " + startRepeat +
@@ -24,7 +31,7 @@ public void regurgitate(KeyboardManager.Regurgitate regurgitate, boolean startRe
// then just pressing g again would open the Windows Game popup, as if leftwin
// was still being pressed.
Key key = regurgitate.key();
- WindowsKeyboard.sendInputMoves(
+ keyboard.sendInputMoves(
!regurgitate.alsoRelease()
? List.of(new ResolvedKeyMacroMove(key, true, MacroMoveDestination.OS))
: List.of(new ResolvedKeyMacroMove(key, true, MacroMoveDestination.OS),
diff --git a/src/main/java/mousemaster/WindowsKeyboard.java b/src/main/java/mousemaster/platform/windows/WindowsKeyboard.java
similarity index 99%
rename from src/main/java/mousemaster/WindowsKeyboard.java
rename to src/main/java/mousemaster/platform/windows/WindowsKeyboard.java
index fa03abf8..3a912061 100644
--- a/src/main/java/mousemaster/WindowsKeyboard.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsKeyboard.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
diff --git a/src/main/java/mousemaster/platform/windows/WindowsKeyboardAdapter.java b/src/main/java/mousemaster/platform/windows/WindowsKeyboardAdapter.java
new file mode 100644
index 00000000..c97ea047
--- /dev/null
+++ b/src/main/java/mousemaster/platform/windows/WindowsKeyboardAdapter.java
@@ -0,0 +1,46 @@
+package mousemaster.platform.windows;
+
+import mousemaster.*;
+
+import mousemaster.platform.Keyboard;
+
+import java.util.List;
+
+public class WindowsKeyboardAdapter implements Keyboard {
+
+ @Override
+ public void update(double delta) {
+ WindowsKeyboard.update(delta);
+ }
+
+ @Override
+ public void reset() {
+ WindowsKeyboard.reset();
+ }
+
+ @Override
+ public void sendInputMoves(List moves, boolean startRepeat) {
+ WindowsKeyboard.sendInputMoves(moves, startRepeat);
+ }
+
+ @Override
+ public void keyPressedNotEaten(Key key) {
+ WindowsKeyboard.keyPressedNotEaten(key);
+ }
+
+ @Override
+ public void keyReleasedNotEaten(Key key) {
+ WindowsKeyboard.keyReleasedNotEaten(key);
+ }
+
+ @Override
+ public void recordEarlyReleaseForQueuedPress(Key key) {
+ WindowsKeyboard.recordEarlyReleaseForQueuedPress(key);
+ }
+
+ @Override
+ public void clearEarlyReleaseForQueuedPress(Key key) {
+ WindowsKeyboard.clearEarlyReleaseForQueuedPress(key);
+ }
+
+}
diff --git a/src/main/java/mousemaster/platform/windows/WindowsMain.java b/src/main/java/mousemaster/platform/windows/WindowsMain.java
new file mode 100644
index 00000000..d3b9ec23
--- /dev/null
+++ b/src/main/java/mousemaster/platform/windows/WindowsMain.java
@@ -0,0 +1,78 @@
+package mousemaster.platform.windows;
+
+import com.sun.jna.Native;
+import mousemaster.ApplicationOptions;
+import mousemaster.Mousemaster;
+import mousemaster.MousemasterApplication;
+import mousemaster.Platform;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+public class WindowsMain {
+
+ private static final Logger logger = LoggerFactory.getLogger(WindowsMain.class);
+
+ public static void main(String[] args) throws InterruptedException, IOException {
+ ApplicationOptions options = ApplicationOptions.parse(args);
+ MousemasterApplication.setTempDirectory(options.tempDirectory());
+ if (options.logLevel() != null)
+ MousemasterApplication.setLogLevel(options.logLevel());
+ if (options.logToFile())
+ MousemasterApplication.enableLogToFile();
+ String version;
+ String commitId;
+ try (InputStream versionInputStream = WindowsMain.class.getClassLoader()
+ .getResourceAsStream(
+ "application.properties")) {
+ Properties versionProp = new Properties();
+ versionProp.load(versionInputStream);
+ version = versionProp.getProperty("version");
+ commitId = versionProp.getProperty("commitId");
+ }
+ if (options.showVersion()) {
+ System.out.println("mousemaster v" + version + " (" + commitId + ")");
+ return;
+ }
+ if (options.graalvmAgentRun()) {
+ logger.info("--graalvm-agent-run flag found, exiting in 20s");
+ new Thread(() -> {
+ try {
+ Thread.sleep(20000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ System.exit(0);
+ }).start();
+ }
+ Platform platform = createPlatform(options.multipleInstancesAllowed(),
+ options.keyRegurgitationEnabled(), options.pauseOnError());
+ logger.info("mousemaster v" + version + " (" + commitId + ")");
+ if (platform == null)
+ return;
+ try {
+ Native.setCallbackExceptionHandler((c, e) ->
+ MousemasterApplication.shutdownAfterException(e, platform, true,
+ options.pauseOnError()));
+ new Mousemaster(options.configurationPath(), platform).run();
+ } catch (Throwable e) {
+ MousemasterApplication.shutdownAfterException(e, platform, false,
+ options.pauseOnError());
+ }
+ }
+
+ private static Platform createPlatform(boolean multipleInstancesAllowed,
+ boolean keyRegurgitationEnabled,
+ boolean pauseOnError) {
+ try {
+ return new WindowsPlatform(multipleInstancesAllowed, keyRegurgitationEnabled);
+ } catch (Exception e) {
+ MousemasterApplication.shutdownAfterException(e, null, false, pauseOnError);
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/mousemaster/WindowsMouse.java b/src/main/java/mousemaster/platform/windows/WindowsMouse.java
similarity index 99%
rename from src/main/java/mousemaster/WindowsMouse.java
rename to src/main/java/mousemaster/platform/windows/WindowsMouse.java
index 6152b925..38d345fc 100644
--- a/src/main/java/mousemaster/WindowsMouse.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsMouse.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
diff --git a/src/main/java/mousemaster/platform/windows/WindowsMouseAdapter.java b/src/main/java/mousemaster/platform/windows/WindowsMouseAdapter.java
new file mode 100644
index 00000000..32409fab
--- /dev/null
+++ b/src/main/java/mousemaster/platform/windows/WindowsMouseAdapter.java
@@ -0,0 +1,79 @@
+package mousemaster.platform.windows;
+
+import mousemaster.*;
+
+import mousemaster.platform.PlatformMouse;
+
+public class WindowsMouseAdapter implements PlatformMouse {
+
+ @Override
+ public void beginMove() {
+ WindowsMouse.beginMove();
+ }
+
+ @Override
+ public void endMove() {
+ WindowsMouse.endMove();
+ }
+
+ @Override
+ public void moveBy(boolean xForward, double dx, boolean yForward, double dy) {
+ WindowsMouse.moveBy(xForward, dx, yForward, dy);
+ }
+
+ @Override
+ public void synchronousMoveTo(int x, int y) {
+ WindowsMouse.synchronousMoveTo(x, y);
+ }
+
+ @Override
+ public void pressLeft() {
+ WindowsMouse.pressLeft();
+ }
+
+ @Override
+ public void pressMiddle() {
+ WindowsMouse.pressMiddle();
+ }
+
+ @Override
+ public void pressRight() {
+ WindowsMouse.pressRight();
+ }
+
+ @Override
+ public void releaseLeft() {
+ WindowsMouse.releaseLeft();
+ }
+
+ @Override
+ public void releaseMiddle() {
+ WindowsMouse.releaseMiddle();
+ }
+
+ @Override
+ public void releaseRight() {
+ WindowsMouse.releaseRight();
+ }
+
+ @Override
+ public void wheelHorizontallyBy(boolean forward, double delta) {
+ WindowsMouse.wheelHorizontallyBy(forward, delta);
+ }
+
+ @Override
+ public void wheelVerticallyBy(boolean forward, double delta) {
+ WindowsMouse.wheelVerticallyBy(forward, delta);
+ }
+
+ @Override
+ public void showCursor() {
+ WindowsMouse.showCursor();
+ }
+
+ @Override
+ public void hideCursor() {
+ WindowsMouse.hideCursor();
+ }
+
+}
diff --git a/src/main/java/mousemaster/WindowsOverlay.java b/src/main/java/mousemaster/platform/windows/WindowsOverlay.java
similarity index 99%
rename from src/main/java/mousemaster/WindowsOverlay.java
rename to src/main/java/mousemaster/platform/windows/WindowsOverlay.java
index 6befbd67..da9da24d 100644
--- a/src/main/java/mousemaster/WindowsOverlay.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsOverlay.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Memory;
import com.sun.jna.Native;
@@ -13,7 +15,7 @@
import io.qt.widgets.QGraphicsScene;
import io.qt.widgets.QLabel;
import io.qt.widgets.QWidget;
-import mousemaster.WindowsMouse.MouseSize;
+import mousemaster.platform.windows.WindowsMouse.MouseSize;
import mousemaster.qt.TransparentWindow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -4499,4 +4501,4 @@ static void mouseMoved(WinDef.POINT mousePosition) {
moveAndResizeIndicatorWindow(mousePosition);
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/mousemaster/platform/windows/WindowsOverlayAdapter.java b/src/main/java/mousemaster/platform/windows/WindowsOverlayAdapter.java
new file mode 100644
index 00000000..e5aec284
--- /dev/null
+++ b/src/main/java/mousemaster/platform/windows/WindowsOverlayAdapter.java
@@ -0,0 +1,122 @@
+package mousemaster.platform.windows;
+
+import mousemaster.*;
+
+import mousemaster.platform.Overlay;
+
+import java.time.Duration;
+import java.util.Set;
+
+public class WindowsOverlayAdapter implements Overlay {
+
+ @Override
+ public void update(double delta) {
+ WindowsOverlay.update(delta);
+ }
+
+ @Override
+ public void flushCache() {
+ WindowsOverlay.flushCache();
+ }
+
+ @Override
+ public void setTopmost() {
+ WindowsOverlay.setTopmost();
+ }
+
+ @Override
+ public void setMessagePump(Runnable pump) {
+ WindowsOverlay.setMessagePump(pump);
+ }
+
+ @Override
+ public void preWarmFontStyles(Set configs) {
+ WindowsOverlay.preWarmFontStyles(configs);
+ }
+
+ @Override
+ public void preWarmHintMeshWindows() {
+ WindowsOverlay.preWarmHintMeshWindows();
+ }
+
+ @Override
+ public Rectangle activeWindowRectangle(double widthPct, double heightPct,
+ int topInset, int bottomInset,
+ int leftInset, int rightInset) {
+ return WindowsOverlay.activeWindowRectangle(widthPct, heightPct,
+ topInset, bottomInset, leftInset, rightInset);
+ }
+
+ @Override
+ public void setIndicator(Indicator indicator, boolean fadeAnimationEnabled,
+ Duration fadeAnimationDuration, boolean allowFade) {
+ WindowsOverlay.setIndicator(indicator, fadeAnimationEnabled,
+ fadeAnimationDuration, allowFade);
+ }
+
+ @Override
+ public void hideIndicator(boolean allowFade) {
+ WindowsOverlay.hideIndicator(allowFade);
+ }
+
+ @Override
+ public void setGrid(Grid grid) {
+ WindowsOverlay.setGrid(grid);
+ }
+
+ @Override
+ public void hideGrid() {
+ WindowsOverlay.hideGrid();
+ }
+
+ @Override
+ public void setHintMesh(HintMesh hintMesh, Zoom zoom) {
+ WindowsOverlay.setHintMesh(hintMesh, zoom);
+ }
+
+ @Override
+ public void setHintMesh(HintMesh hintMesh, Zoom zoom, boolean hintMatch) {
+ WindowsOverlay.setHintMesh(hintMesh, zoom, hintMatch);
+ }
+
+ @Override
+ public void hideHintMesh() {
+ WindowsOverlay.hideHintMesh();
+ }
+
+ @Override
+ public void animateHintMatch(Hint hint) {
+ WindowsOverlay.animateHintMatch(hint);
+ }
+
+ @Override
+ public void setZoom(Zoom zoom) {
+ WindowsOverlay.setZoom(zoom);
+ }
+
+ @Override
+ public void startScreenshotZoomAnimation(Rectangle screenRect, Zoom beginZoom) {
+ WindowsOverlay.startScreenshotZoomAnimation(screenRect, beginZoom);
+ }
+
+ @Override
+ public void updateScreenshotZoom(Zoom zoom) {
+ WindowsOverlay.updateScreenshotZoom(zoom);
+ }
+
+ @Override
+ public void endScreenshotZoomAnimation(Zoom finalZoom) {
+ WindowsOverlay.endScreenshotZoomAnimation(finalZoom);
+ }
+
+ @Override
+ public boolean waitForZoomBeforeRepainting() {
+ return WindowsOverlay.waitForZoomBeforeRepainting;
+ }
+
+ @Override
+ public void setWaitForZoomBeforeRepainting(boolean value) {
+ WindowsOverlay.waitForZoomBeforeRepainting = value;
+ }
+
+}
diff --git a/src/main/java/mousemaster/WindowsPlatform.java b/src/main/java/mousemaster/platform/windows/WindowsPlatform.java
similarity index 94%
rename from src/main/java/mousemaster/WindowsPlatform.java
rename to src/main/java/mousemaster/platform/windows/WindowsPlatform.java
index 45e6f66f..b4a8e262 100644
--- a/src/main/java/mousemaster/WindowsPlatform.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsPlatform.java
@@ -1,8 +1,19 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.*;
+import mousemaster.platform.ActiveAppFinder;
+import mousemaster.platform.Console;
+import mousemaster.platform.Keyboard;
+import mousemaster.platform.KeyboardLayoutProvider;
+import mousemaster.platform.KeyRegurgitator;
+import mousemaster.platform.Overlay;
+import mousemaster.platform.PlatformMouse;
+import mousemaster.platform.Screens;
+import mousemaster.platform.UiAutomation;
import mousemaster.KeyEvent.PressKeyEvent;
import mousemaster.KeyEvent.ReleaseKeyEvent;
import org.slf4j.Logger;
@@ -18,7 +29,15 @@ public class WindowsPlatform implements Platform {
private static final Logger logger = LoggerFactory.getLogger(WindowsPlatform.class);
private final boolean keyRegurgitationEnabled;
- private final KeyRegurgitator keyRegurgitator = new KeyRegurgitator();
+ private final Keyboard keyboard = new WindowsKeyboardAdapter();
+ private final PlatformMouse mouse = new WindowsMouseAdapter();
+ private final Screens screens = new WindowsScreens();
+ private final Overlay overlay = new WindowsOverlayAdapter();
+ private final UiAutomation uiAutomation = new WindowsUiAutomationAdapter();
+ private final ActiveAppFinder activeAppFinder = new WindowsActiveAppFinder();
+ private final Console console = new WindowsConsole();
+ private final KeyboardLayoutProvider keyboardLayoutProvider = WindowsVirtualKey::activeKeyboardLayout;
+ private final KeyRegurgitator keyRegurgitator = new WindowsKeyRegurgitator(keyboard);
private final WindowsClock clock = new WindowsClock();
private MouseController mouseController;
@@ -83,7 +102,7 @@ public void update(double delta) {
}
@Override
- public void windowsMessagePump() {
+ public void pumpEvents() {
while (User32.INSTANCE.PeekMessage(msg, null, 0, 0, 1)) {
User32.INSTANCE.TranslateMessage(msg);
User32.INSTANCE.DispatchMessage(msg);
@@ -93,7 +112,7 @@ public void windowsMessagePump() {
@Override
public void sleep() throws InterruptedException {
- windowsMessagePump();
+ pumpEvents();
WinDef.POINT mousePosition = null;
for (WinDef.POINT p; (p = mousePositionQueue.poll()) != null;)
mousePosition = p;
@@ -115,14 +134,14 @@ else if (lastMousePosition != null) {
// Reposition the indicator with the updated cursor visual center.
WindowsOverlay.mouseMoved(lastMousePosition);
}
- windowsMessagePump();
+ pumpEvents();
long beforeTime = System.nanoTime();
while (true) {
long currentTime = System.nanoTime();
if ((currentTime - beforeTime) / 1e6 >= 10)
break;
Thread.sleep(1);
- windowsMessagePump();
+ pumpEvents();
}
}
@@ -166,7 +185,7 @@ public void reset(MouseController mouseController, KeyboardManager keyboardManag
mousePositionListeners.forEach(
mousePositionListener -> mousePositionListener.mouseMoved(mousePosition.x,
mousePosition.y));
- WindowsOverlay.setMessagePump(this::windowsMessagePump);
+ WindowsOverlay.setMessagePump(this::pumpEvents);
if (keyboardHookCallback == null)
installHooks();
}
@@ -257,10 +276,44 @@ public PlatformClock clock() {
}
@Override
- public KeyboardLayout activeKeyboardLayout() {
- return WindowsVirtualKey.activeKeyboardLayout();
+ public KeyboardLayoutProvider keyboardLayoutProvider() {
+ return keyboardLayoutProvider;
+ }
+
+ @Override
+ public Keyboard keyboard() {
+ return keyboard;
}
+ @Override
+ public PlatformMouse mouse() {
+ return mouse;
+ }
+
+ @Override
+ public Screens screens() {
+ return screens;
+ }
+
+ @Override
+ public Overlay overlay() {
+ return overlay;
+ }
+
+ @Override
+ public UiAutomation uiAutomation() {
+ return uiAutomation;
+ }
+
+ @Override
+ public ActiveAppFinder activeAppFinder() {
+ return activeAppFinder;
+ }
+
+ @Override
+ public Console console() {
+ return console;
+ }
/**
* On the Windows lock screen, hit space then enter the pin. Space press is recorded by the app but the
@@ -321,7 +374,7 @@ private void sanityCheckCurrentlyPressedKeys(double delta) {
boolean pressedAccordingToOs = (state & 0x8000) != 0;
if (!pressedAccordingToOs)
continue;
- Key key = layout.keyFromVirtualKey(virtualKey);
+ Key key = layout.keyFromVirtualKeyName(virtualKey.name());
if (key != null && keysPressedInHook.contains(key) &&
!currentlyPressedNotEatenKeys.containsKey(key)) {
if (key.equals(Key.leftctrl) &&
diff --git a/src/main/java/mousemaster/WindowsScreen.java b/src/main/java/mousemaster/platform/windows/WindowsScreen.java
similarity index 98%
rename from src/main/java/mousemaster/WindowsScreen.java
rename to src/main/java/mousemaster/platform/windows/WindowsScreen.java
index 199424c3..5f0fd36a 100644
--- a/src/main/java/mousemaster/WindowsScreen.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsScreen.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
diff --git a/src/main/java/mousemaster/platform/windows/WindowsScreens.java b/src/main/java/mousemaster/platform/windows/WindowsScreens.java
new file mode 100644
index 00000000..9dc9b697
--- /dev/null
+++ b/src/main/java/mousemaster/platform/windows/WindowsScreens.java
@@ -0,0 +1,16 @@
+package mousemaster.platform.windows;
+
+import mousemaster.*;
+
+import mousemaster.platform.Screens;
+
+import java.util.Set;
+
+public class WindowsScreens implements Screens {
+
+ @Override
+ public Set findScreens() {
+ return WindowsScreen.findScreens();
+ }
+
+}
diff --git a/src/main/java/mousemaster/WindowsUiAccess.java b/src/main/java/mousemaster/platform/windows/WindowsUiAccess.java
similarity index 99%
rename from src/main/java/mousemaster/WindowsUiAccess.java
rename to src/main/java/mousemaster/platform/windows/WindowsUiAccess.java
index 95485830..8db81298 100644
--- a/src/main/java/mousemaster/WindowsUiAccess.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsUiAccess.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.*;
diff --git a/src/main/java/mousemaster/WindowsUiAutomation.java b/src/main/java/mousemaster/platform/windows/WindowsUiAutomation.java
similarity index 99%
rename from src/main/java/mousemaster/WindowsUiAutomation.java
rename to src/main/java/mousemaster/platform/windows/WindowsUiAutomation.java
index 1cc910fb..b01e9dd2 100644
--- a/src/main/java/mousemaster/WindowsUiAutomation.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsUiAutomation.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.*;
import com.sun.jna.platform.win32.*;
@@ -6,6 +8,7 @@
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
+import mousemaster.platform.UiAutomation.UiElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,9 +61,6 @@ public class WindowsUiAutomation {
});
private static volatile boolean backgroundComInitialized;
- record UiElement(double centerX, double centerY) {
- }
-
private static Memory createBoolVariantTrue() {
Memory variant = new Memory(16);
variant.clear();
diff --git a/src/main/java/mousemaster/platform/windows/WindowsUiAutomationAdapter.java b/src/main/java/mousemaster/platform/windows/WindowsUiAutomationAdapter.java
new file mode 100644
index 00000000..51456f48
--- /dev/null
+++ b/src/main/java/mousemaster/platform/windows/WindowsUiAutomationAdapter.java
@@ -0,0 +1,17 @@
+package mousemaster.platform.windows;
+
+import mousemaster.*;
+
+import mousemaster.platform.UiAutomation;
+
+import java.util.List;
+import java.util.concurrent.Future;
+
+public class WindowsUiAutomationAdapter implements UiAutomation {
+
+ @Override
+ public Future> startFindInteractiveUiElements() {
+ return WindowsUiAutomation.startFindInteractiveUiElements();
+ }
+
+}
diff --git a/src/main/java/mousemaster/WindowsVirtualKey.java b/src/main/java/mousemaster/platform/windows/WindowsVirtualKey.java
similarity index 97%
rename from src/main/java/mousemaster/WindowsVirtualKey.java
rename to src/main/java/mousemaster/platform/windows/WindowsVirtualKey.java
index 65008a08..48cea009 100644
--- a/src/main/java/mousemaster/WindowsVirtualKey.java
+++ b/src/main/java/mousemaster/platform/windows/WindowsVirtualKey.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
@@ -472,7 +474,7 @@ private static KeyboardLayout startupKeyboardLayout() {
public static Key keyFromWindowsEvent(WindowsVirtualKey windowsVirtualKey, int scanCode, int flags) {
if (scanCode == 0) {
// Injected key event have scanCode 0.
- return WindowsVirtualKey.activeKeyboardLayout().keyFromVirtualKey(windowsVirtualKey);
+ return WindowsVirtualKey.activeKeyboardLayout().keyFromVirtualKeyName(windowsVirtualKey.name());
}
// When pressing rightctrl the scanCode should be E01D but is 1D (which is leftctrl's scanCode).
// rightctrl:
@@ -493,12 +495,18 @@ public static Key keyFromWindowsEvent(WindowsVirtualKey windowsVirtualKey, int s
public static WindowsVirtualKey windowsVirtualKeyFromKey(Key key,
KeyboardLayout keyboardLayout) {
- WindowsVirtualKey virtualKey = keyboardLayout.virtualKey(key);
- if (virtualKey == null) {
+ String virtualKeyName = keyboardLayout.virtualKeyName(key);
+ if (virtualKeyName == null) {
logger.debug("Unable to map key " + key + " to a Windows virtual key using " +
keyboardLayout);
+ return null;
+ }
+ try {
+ return WindowsVirtualKey.valueOf(virtualKeyName);
+ } catch (IllegalArgumentException e) {
+ logger.debug("Unknown virtual key name: " + virtualKeyName);
+ return null;
}
- return virtualKey;
}
}
diff --git a/src/main/java/mousemaster/Winmm.java b/src/main/java/mousemaster/platform/windows/Winmm.java
similarity index 78%
rename from src/main/java/mousemaster/Winmm.java
rename to src/main/java/mousemaster/platform/windows/Winmm.java
index cb912f29..5b5b7ced 100644
--- a/src/main/java/mousemaster/Winmm.java
+++ b/src/main/java/mousemaster/platform/windows/Winmm.java
@@ -1,4 +1,6 @@
-package mousemaster;
+package mousemaster.platform.windows;
+
+import mousemaster.*;
import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary;