diff --git a/app/src/main/runtime/display/xserver/InputDeviceManager.java b/app/src/main/runtime/display/xserver/InputDeviceManager.java index 55db3439b..4645f0301 100644 --- a/app/src/main/runtime/display/xserver/InputDeviceManager.java +++ b/app/src/main/runtime/display/xserver/InputDeviceManager.java @@ -198,6 +198,8 @@ public void onPointerButtonPress(Pointer.Button button) { @Override public void onPointerButtonRelease(Pointer.Button button) { if (xServer.isRelativeMouseMovement()) { + // The wheel notch is delivered entirely by the press; a release has no Windows equivalent. + if (button == Pointer.Button.BUTTON_SCROLL_UP || button == Pointer.Button.BUTTON_SCROLL_DOWN) return; WinHandler winHandler = xServer.getWinHandler(); winHandler.mouseEvent(MouseEventFlags.getFlagFor(button, false), 0, 0, 0); } else { diff --git a/app/src/main/runtime/input/ui/InputControlsView.java b/app/src/main/runtime/input/ui/InputControlsView.java index 15f2c740a..0c01917c8 100644 --- a/app/src/main/runtime/input/ui/InputControlsView.java +++ b/app/src/main/runtime/input/ui/InputControlsView.java @@ -54,6 +54,8 @@ public class InputControlsView extends View { public static final float DEFAULT_OVERLAY_OPACITY = 0.4f; private static final byte MOUSE_WHEEL_DELTA = 120; + private static final long SCROLL_REPEAT_DELAY = 350; + private static final long SCROLL_REPEAT_INTERVAL = 90; private boolean editMode = false; private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Path path = new Path(); @@ -74,6 +76,9 @@ public class InputControlsView extends View { private Timer mouseMoveTimer; private volatile float mouseMoveOffsetX = 0f; private volatile float mouseMoveOffsetY = 0f; + private Timer scrollRepeatTimer; + private volatile boolean scrollRepeatUp = false; + private volatile boolean scrollRepeatDown = false; private boolean showTouchscreenControls = false; private VisualStyle visualStyle = VisualStyle.SLATE; private AccentTheme accentTheme = AccentTheme.CYAN; @@ -508,6 +513,7 @@ public void cancelContinuousMouseMove() { public synchronized void cancelActiveTouches() { releaseActiveTouchElements(); cancelContinuousMouseMove(); + cancelScrollRepeat(); } public int getMaxWidth() { @@ -517,6 +523,7 @@ public int getMaxWidth() { @Override protected void onDetachedFromWindow() { cancelContinuousMouseMove(); + cancelScrollRepeat(); if (mouseMoveTimer != null) { mouseMoveTimer.cancel(); mouseMoveTimer = null; @@ -556,6 +563,46 @@ public void run() { } } + // A wheel notch is a click, not a hold: press and release together. + private void injectScrollClick(Pointer.Button button) { + if (xServer == null) return; + xServer.injectPointerButtonPress(button); + xServer.injectPointerButtonRelease(button); + } + + private void startScrollRepeat(Pointer.Button button) { + if (button == Pointer.Button.BUTTON_SCROLL_UP) scrollRepeatUp = true; + else scrollRepeatDown = true; + if (scrollRepeatTimer != null) return; + scrollRepeatTimer = new Timer(); + scrollRepeatTimer.schedule( + new TimerTask() { + @Override + public void run() { + if (getContext() instanceof XServerDisplayActivity && ((XServerDisplayActivity) getContext()).isInputSuspended()) return; + if (scrollRepeatUp) injectScrollClick(Pointer.Button.BUTTON_SCROLL_UP); + if (scrollRepeatDown) injectScrollClick(Pointer.Button.BUTTON_SCROLL_DOWN); + } + }, + SCROLL_REPEAT_DELAY, + SCROLL_REPEAT_INTERVAL); + } + + private void stopScrollRepeat(Pointer.Button button) { + if (button == Pointer.Button.BUTTON_SCROLL_UP) scrollRepeatUp = false; + else scrollRepeatDown = false; + if (!scrollRepeatUp && !scrollRepeatDown) cancelScrollRepeat(); + } + + private void cancelScrollRepeat() { + scrollRepeatUp = false; + scrollRepeatDown = false; + if (scrollRepeatTimer != null) { + scrollRepeatTimer.cancel(); + scrollRepeatTimer = null; + } + } + // private void processJoystickInput(ExternalController controller) { // ExternalControllerBinding controllerBinding; // final int[] axes = {MotionEvent.AXIS_X, MotionEvent.AXIS_Y, MotionEvent.AXIS_Z, @@ -1083,12 +1130,20 @@ public void handleInputEvent( if (isActionDown) createMouseMoveTimer(); } else { Pointer.Button pointerButton = binding.getPointerButton(); + boolean isScroll = + pointerButton == Pointer.Button.BUTTON_SCROLL_UP + || pointerButton == Pointer.Button.BUTTON_SCROLL_DOWN; if (isActionDown) { - if (pointerButton != null) { + if (isScroll) { + injectScrollClick(pointerButton); + startScrollRepeat(pointerButton); + } else if (pointerButton != null) { xServer.injectPointerButtonPress(pointerButton); } else xServer.injectKeyPress(binding.keycode); } else { - if (pointerButton != null) { + if (isScroll) { + stopScrollRepeat(pointerButton); + } else if (pointerButton != null) { xServer.injectPointerButtonRelease(pointerButton); } else xServer.injectKeyRelease(binding.keycode); }