From 193de847faabef924aee0e22b78d687927316010 Mon Sep 17 00:00:00 2001 From: Jesus Oliver Date: Fri, 6 Feb 2026 01:15:21 +0100 Subject: [PATCH 1/4] Added support for mouse devices on android Added setMouseGrab method to both MouseInput and InputManager. Android cannot change to grab/capture mode while dragging, all events after mouse down are ignored till mouse up is received --- .../input/android/AndroidInputHandler.java | 9 +- .../input/android/AndroidInputHandler14.java | 39 ++- .../input/android/AndroidInputHandler24.java | 44 +++ .../input/android/AndroidInputHandler26.java | 72 ++++ .../input/android/AndroidMouseInput14.java | 327 ++++++++++++++++++ .../input/android/AndroidMouseInput24.java | 133 +++++++ .../input/android/AndroidMouseInput26.java | 114 ++++++ .../com/jme3/system/android/OGLESContext.java | 14 +- .../com/jme3/app/ChaseCameraAppState.java | 4 +- .../main/java/com/jme3/input/ChaseCamera.java | 2 +- .../main/java/com/jme3/input/FlyByCamera.java | 11 +- .../java/com/jme3/input/InputManager.java | 13 + .../main/java/com/jme3/input/MouseInput.java | 7 + .../com/jme3/input/dummy/DummyMouseInput.java | 4 + .../java/com/jme3/input/AWTMouseInput.java | 6 +- .../com/jme3/input/awt/AwtMouseInput.java | 5 + .../com/jme3/input/lwjgl/LwjglMouseInput.java | 5 + .../com/jme3/input/lwjgl/GlfwMouseInput.java | 5 + 18 files changed, 800 insertions(+), 14 deletions(-) create mode 100644 jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java create mode 100644 jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java create mode 100644 jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java create mode 100644 jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java create mode 100644 jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java index 5c3d303ab5..fc05da90d6 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java @@ -40,7 +40,9 @@ import android.view.ScaleGestureDetector; import android.view.View; import com.jme3.input.JoyInput; +import com.jme3.input.MouseInput; import com.jme3.input.TouchInput; +import com.jme3.input.dummy.DummyMouseInput; import com.jme3.system.AppSettings; import java.util.logging.Logger; @@ -60,11 +62,12 @@ public class AndroidInputHandler implements View.OnTouchListener, protected GLSurfaceView view; protected AndroidTouchInput touchInput; protected AndroidJoyInput joyInput; - + protected MouseInput mouseInput; public AndroidInputHandler() { touchInput = new AndroidTouchInput(this); joyInput = new AndroidJoyInput(this); + mouseInput = new DummyMouseInput(); } public void setView(View view) { @@ -118,6 +121,10 @@ public JoyInput getJoyInput() { return joyInput; } + public MouseInput getMouseInput() { + return mouseInput; + } + /* * Android input events include the source from which the input came from. * We must look at the source of the input event to determine which type diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java index 5ee0f757b2..7274a857ad 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java @@ -37,6 +37,9 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; + +import com.jme3.system.AppSettings; + import java.util.logging.Logger; /** @@ -55,6 +58,7 @@ public class AndroidInputHandler14 extends AndroidInputHandler implements View.O public AndroidInputHandler14() { touchInput = new AndroidTouchInput14(this); joyInput = new AndroidJoyInput14(this); + mouseInput = new AndroidMouseInput14(this); } @Override @@ -71,6 +75,12 @@ protected void addListeners(GLSurfaceView view) { view.setOnGenericMotionListener(this); } + @Override + public void loadSettings(AppSettings settings) { + super.loadSettings(settings); + ((AndroidMouseInput14)mouseInput).loadSettings(settings); + } + @Override public boolean onHover(View view, MotionEvent event) { if (view != getView()) { @@ -91,6 +101,12 @@ public boolean onHover(View view, MotionEvent event) { consumed = ((AndroidTouchInput14)touchInput).onHover(event); } + boolean isMouse = ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE); + if (isMouse && mouseInput != null) { + // send the event to the mouse processor + consumed = ((AndroidMouseInput14)mouseInput).onHover(event); + } + return consumed; } @@ -116,6 +132,28 @@ public boolean onGenericMotion(View view, MotionEvent event) { consumed = consumed || ((AndroidJoyInput14)joyInput).onGenericMotion(event); } + if((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { + consumed = consumed || ((AndroidMouseInput14)mouseInput).onGenericMotion(event); + } + + return consumed; + } + + @Override + public boolean onTouch(View view, MotionEvent event) { + if (view != getView()) { + return false; + } + + boolean consumed = super.onTouch(view, event); + + // Mouse movement while button down is received as onTouch event instead + boolean isMouse = ((event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE); + if (isMouse && mouseInput != null) { + // send the event to the mouse processor + consumed |= ((AndroidMouseInput14)mouseInput).onGenericMotion(event); + } + return consumed; } @@ -154,7 +192,6 @@ public boolean onKey(View view, int keyCode, KeyEvent event) { } return consumed; - } } diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java new file mode 100644 index 0000000000..12b8263b70 --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009-2026 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.input.android; + +import android.view.View; + +public class AndroidInputHandler24 extends AndroidInputHandler14 { + + public AndroidInputHandler24() { + super(); + mouseInput = new AndroidMouseInput24(this); + } + +} diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java new file mode 100644 index 0000000000..27c9214c42 --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2009-2026 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.input.android; + +import android.opengl.GLSurfaceView; +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.View; + +public class AndroidInputHandler26 extends AndroidInputHandler24 implements View.OnCapturedPointerListener { + + public AndroidInputHandler26() { + super(); + mouseInput = new AndroidMouseInput26(this); + } + + protected void removeListeners(GLSurfaceView view) { + super.removeListeners(view); + view.setOnCapturedPointerListener(null); + } + + @Override + protected void addListeners(GLSurfaceView view) { + super.addListeners(view); + view.setOnCapturedPointerListener(this); + } + + @Override + public boolean onCapturedPointer(View view, MotionEvent event) { + if (view != getView()) { + return false; + } + + boolean consumed = false; + boolean isMouse = ((event.getSource() & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE); + if (isMouse && mouseInput != null) { + consumed = ((AndroidMouseInput26)mouseInput).onCapturedPointer(event); + } + + return consumed; + } +} diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java new file mode 100644 index 0000000000..b15056849e --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2009-2026 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.input.android; + +import android.view.MotionEvent; + +import com.jme3.cursors.plugins.JmeCursor; +import com.jme3.input.MouseInput; +import com.jme3.input.RawInputListener; +import com.jme3.input.event.InputEvent; +import com.jme3.input.event.MouseButtonEvent; +import com.jme3.input.event.MouseMotionEvent; +import com.jme3.system.AppSettings; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * AndroidMouseInput14 extends MouseInput to add mouse support for jME3 + * uses the onGenericMotion events that where added in Android rev 12 and MotionEvent.getButtonState + * from Android rev 14 so added "14" suffix to the class to specify the Android required rev and + * match other classes naming + * + * @author joliver82 + */ +public class AndroidMouseInput14 implements MouseInput { + private static final Logger logger = Logger.getLogger(AndroidMouseInput14.class.getName()); + + protected AndroidInputHandler inputHandler; + + private boolean initialized = false; + private RawInputListener listener = null; + private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue<>(); + private float scaleX = 1f; + private float scaleY = 1f; + + protected class MouseState { + int x, y, wheel; + boolean left, right, center; + + protected void setStartPosition(int startingX,int startingY) { + x = startingX; + y = startingY; + } + + protected int updateX(int newX) { + int deltaX=newX-x; + x=newX; + return deltaX; + } + + protected int incrementX(int deltaX) { + x+=deltaX; + return x; + } + + protected int updateY(int newY) { + int deltaY=newY-y; + y=newY; + return deltaY; + } + + protected int incrementY(int deltaY) { + y+=deltaY; + return y; + } + + protected int incrementWheel(int deltaWheel) { + wheel+=deltaWheel; + return wheel; + } + + protected boolean updateLeftButton(boolean left) { + if(this.left == left) { + return false; + } + this.left = left; + return true; + } + + protected boolean updateRightButton(boolean right) { + if(this.right == right) { + return false; + } + this.right = right; + return true; + } + + protected boolean updateCenterButton(boolean center) { + if(this.center == center) { + return false; + } + this.center = center; + return true; + } + } + + MouseState currentMouseState = new MouseState(); + + public AndroidMouseInput14(AndroidInputHandler inputHandler) { + this.inputHandler = inputHandler; + } + + protected int getJmeX(float origX) { + return (int) (origX * scaleX); + } + + protected int getJmeY(float origY) { + return (int) (origY * scaleY); + } + + public void loadSettings(AppSettings settings) { + // view width and height are 0 until the view is displayed on the screen + if (inputHandler.getView().getWidth() != 0 && inputHandler.getView().getHeight() != 0) { + scaleX = settings.getWidth() / (float)inputHandler.getView().getWidth(); + scaleY = settings.getHeight() / (float)inputHandler.getView().getHeight(); + currentMouseState.setStartPosition(inputHandler.getView().getWidth()/2, inputHandler.getView().getHeight()/2); + } + + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}", + new Object[]{scaleX, scaleY}); + } + + } + + protected void addMouseMotionEventFixedPositions(int x, int y, int deltaWheel) { + int deltaX=currentMouseState.updateX(x); + int deltaY=currentMouseState.updateY(y); + int wheel=currentMouseState.incrementWheel(deltaWheel); + + logger.log(Level.INFO, "Mouse motion event: " + x + "x" + y + " wheel: " + wheel); + + eventQueue.add(new MouseMotionEvent(x, y, deltaX, deltaY, wheel, deltaWheel)); + } + + protected void addMouseMotionEventRelativePositions(int deltaX, int deltaY, int deltaWheel) { + int x=currentMouseState.incrementX(deltaX); + int y=currentMouseState.incrementY(deltaY); + int wheel=currentMouseState.incrementWheel(deltaWheel); + + logger.log(Level.INFO, "Mouse motion event: " + x + "x" + y + " wheel: " + wheel); + + eventQueue.add(new MouseMotionEvent(x, y, deltaX, deltaY, wheel, deltaWheel)); + } + + protected boolean addMouseButtonEvent(boolean left, boolean right, boolean center, int x, int y) { + boolean eventAdded = false; + if(currentMouseState.updateLeftButton(left)) { + eventQueue.add(new MouseButtonEvent(MouseInput.BUTTON_LEFT, left, x, y)); + logger.log(Level.INFO, "Mouse button left: " + left); + eventAdded = true; + } + if(currentMouseState.updateRightButton(right)) { + eventQueue.add(new MouseButtonEvent(MouseInput.BUTTON_RIGHT, right, x, y)); + logger.log(Level.INFO, "Mouse button right: " + right); + eventAdded = true; + } + if(currentMouseState.updateCenterButton(center)) { + eventQueue.add(new MouseButtonEvent(MouseInput.BUTTON_MIDDLE, center, x, y)); + logger.log(Level.INFO, "Mouse button center: " + center); + eventAdded = true; + } + return eventAdded; + } + + public boolean onHover(MotionEvent event) { + boolean consumed = false; + + switch (event.getAction()) { + case MotionEvent.ACTION_SCROLL: // Should not be received here + case MotionEvent.ACTION_MOVE: // Should not be received here + case MotionEvent.ACTION_HOVER_MOVE: + case MotionEvent.ACTION_HOVER_EXIT: + case MotionEvent.ACTION_HOVER_ENTER: + addMouseMotionEventFixedPositions(getJmeX(event.getX()), getJmeY(event.getY()), (int) event.getAxisValue(MotionEvent.AXIS_VSCROLL)); + consumed = true; + break; + } + + return consumed; + } + + public boolean onGenericMotion(MotionEvent event) { + boolean consumed = false; + boolean btnEventReceived = false; + boolean leftPressed = false, rightPressed = false, centerPressed = false; + + int btnState = event.getButtonState(); + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + if((btnState & MotionEvent.BUTTON_PRIMARY) == MotionEvent.BUTTON_PRIMARY) { + leftPressed = true; + } + if((btnState & MotionEvent.BUTTON_SECONDARY) == MotionEvent.BUTTON_SECONDARY) { + rightPressed = true; + } + if((btnState & MotionEvent.BUTTON_TERTIARY) == MotionEvent.BUTTON_TERTIARY) { + centerPressed = true; + } + btnEventReceived = true; + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + if((btnState & MotionEvent.BUTTON_PRIMARY) == MotionEvent.BUTTON_PRIMARY) { + leftPressed = false; + } + if((btnState & MotionEvent.BUTTON_SECONDARY) == MotionEvent.BUTTON_SECONDARY) { + rightPressed = false; + } + if((btnState & MotionEvent.BUTTON_TERTIARY) == MotionEvent.BUTTON_TERTIARY) { + centerPressed = false; + } + btnEventReceived = true; + break; + + case MotionEvent.ACTION_HOVER_EXIT: + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_SCROLL: + case MotionEvent.ACTION_MOVE: + case MotionEvent.ACTION_HOVER_MOVE: + addMouseMotionEventFixedPositions(getJmeX(event.getX()), getJmeY(event.getY()), (int) event.getAxisValue(MotionEvent.AXIS_VSCROLL)); + consumed = true; + break; + } + + if (btnEventReceived) { + consumed = addMouseButtonEvent(leftPressed, rightPressed, centerPressed, getJmeX(event.getX()), getJmeY(event.getY())); + } + + return consumed; + } + + @Override + public void setCursorVisible(boolean visible) { + logger.log(Level.FINE, "Cannot hide mouse till API 24"); + } + + @Override + public void setMouseGrab(boolean grab) { + logger.log(Level.FINE, "Cannot grab mouse till API 26"); + } + + @Override + public int getButtonCount() { + return 3; // No way to get the number of buttons, defaulting to 3 buttons + } + + @Override + public void setNativeCursor(JmeCursor cursor) { + logger.log(Level.FINE, "Cannot change cursor till API 24"); + } + + @Override + public void initialize() { + initialized = true; + } + + @Override + public void update() { + if (listener != null) { + InputEvent inputEvent; + + while ((inputEvent = eventQueue.poll()) != null) { + if (inputEvent instanceof MouseMotionEvent) { + listener.onMouseMotionEvent((MouseMotionEvent)inputEvent); + } else if (inputEvent instanceof MouseButtonEvent) { + listener.onMouseButtonEvent((MouseButtonEvent)inputEvent); + } + } + } + + } + + @Override + public void destroy() { + initialized = false; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public void setInputListener(RawInputListener listener) { + this.listener = listener; + } + + @Override + public long getInputTimeNanos() { + return System.nanoTime(); + } +} diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java new file mode 100644 index 0000000000..95fa8de8b3 --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2009-2026 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.input.android; + +import android.graphics.Bitmap; +import android.view.MotionEvent; +import android.view.PointerIcon; + +import com.jme3.cursors.plugins.JmeCursor; + +import java.nio.IntBuffer; +import java.util.logging.Logger; + +public class AndroidMouseInput24 extends AndroidMouseInput14{ + private static final Logger logger = Logger.getLogger(AndroidMouseInput24.class.getName()); + + public AndroidMouseInput24(AndroidInputHandler inputHandler) { + super(inputHandler); + } + + @Override + public boolean onGenericMotion(MotionEvent event) { + + boolean consumed = super.onGenericMotion(event); + + if (!consumed) { + boolean leftPressed = false, rightPressed = false, centerPressed = false; + boolean btnEventReceived = false; + int btnAction = event.getActionButton(); + + switch (event.getAction()) { + case MotionEvent.ACTION_BUTTON_PRESS: + if(btnAction == MotionEvent.BUTTON_PRIMARY) { + leftPressed = true; + } + if(btnAction == MotionEvent.BUTTON_SECONDARY) { + rightPressed = true; + } + if(btnAction == MotionEvent.BUTTON_TERTIARY) { + centerPressed = true; + } + btnEventReceived = true; + break; + + case MotionEvent.ACTION_BUTTON_RELEASE: + case MotionEvent.ACTION_CANCEL: + if(btnAction == MotionEvent.BUTTON_PRIMARY) { + leftPressed = false; + } + if(btnAction == MotionEvent.BUTTON_SECONDARY) { + rightPressed = false; + } + if(btnAction == MotionEvent.BUTTON_TERTIARY) { + centerPressed = false; + } + btnEventReceived = true; + break; + } + + if (btnEventReceived) { + consumed = addMouseButtonEvent(leftPressed, rightPressed, centerPressed, getJmeX(event.getX()), getJmeY(event.getY())); + } + } + return consumed; + } + + @Override + public void setCursorVisible(boolean visible) { + if(inputHandler.getView()!=null) { + if(visible) { + inputHandler.getView().setPointerIcon(null); + } else { + inputHandler.getView().setPointerIcon(PointerIcon.getSystemIcon(inputHandler.getView().getContext(), PointerIcon.TYPE_NULL)); + } + } + } + + @Override + public void setNativeCursor(JmeCursor cursor) { + if(inputHandler.getView()!=null) { + if(cursor!=null) { + // Translate into Android Bitmap format ARGB888. Assuming input image as RGBA + int bufferSize = cursor.getHeight()*cursor.getWidth(); + int[] outputBitmap=new int[bufferSize]; + IntBuffer inputImage = cursor.getImagesData().asReadOnlyBuffer(); + inputImage.clear(); + int[] tmpPixel = new int[4]; + for(int i=0 ; i< bufferSize; ++i) { + inputImage.get(tmpPixel); + outputBitmap[i] = (tmpPixel[3] & 0xff) << 24 | (tmpPixel[0] & 0xff) << 16 | (tmpPixel[1] & 0xff) << 8 | (tmpPixel[2] & 0xff); + } + PointerIcon pointer = PointerIcon.create( + Bitmap.createBitmap(outputBitmap, cursor.getWidth(), cursor.getHeight(), Bitmap.Config.ARGB_8888), + cursor.getXHotSpot(), + cursor.getYHotSpot()); + inputHandler.getView().setPointerIcon(pointer); + } else { + inputHandler.getView().setPointerIcon(null); + } + } + } + +} diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java new file mode 100644 index 0000000000..6c61edd0fc --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2009-2026 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.input.android; + +import android.graphics.Bitmap; +import android.view.MotionEvent; +import android.view.PointerIcon; + +import com.jme3.cursors.plugins.JmeCursor; + +import java.nio.IntBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class AndroidMouseInput26 extends AndroidMouseInput24{ + private static final Logger logger = Logger.getLogger(AndroidMouseInput26.class.getName()); + + public AndroidMouseInput26(AndroidInputHandler inputHandler) { + super(inputHandler); + } + + public boolean onCapturedPointer(MotionEvent event) { + boolean consumed = false; + boolean btnEventReceived = false; + boolean leftPressed = false, rightPressed = false, centerPressed = false; + +// int btnState = event.getButtonState(); + int btnAction = event.getActionButton(); + + switch (event.getAction()) { + case MotionEvent.ACTION_BUTTON_PRESS: + if(btnAction == MotionEvent.BUTTON_PRIMARY) { + leftPressed = true; + } + if(btnAction == MotionEvent.BUTTON_SECONDARY) { + rightPressed = true; + } + if(btnAction == MotionEvent.BUTTON_TERTIARY) { + centerPressed = true; + } + btnEventReceived = true; + break; + + case MotionEvent.ACTION_BUTTON_RELEASE: + case MotionEvent.ACTION_CANCEL: + if(btnAction == MotionEvent.BUTTON_PRIMARY) { + leftPressed = false; + } + if(btnAction == MotionEvent.BUTTON_SECONDARY) { + rightPressed = false; + } + if(btnAction == MotionEvent.BUTTON_TERTIARY) { + centerPressed = false; + } + btnEventReceived = true; + break; + + case MotionEvent.ACTION_HOVER_EXIT: + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_SCROLL: + case MotionEvent.ACTION_MOVE: + case MotionEvent.ACTION_HOVER_MOVE: + addMouseMotionEventRelativePositions(getJmeX(event.getX()), getJmeY(event.getY()), (int) event.getAxisValue(MotionEvent.AXIS_VSCROLL)); + consumed = true; + break; + } + + if (btnEventReceived) { + consumed = addMouseButtonEvent(leftPressed, rightPressed, centerPressed, getJmeX(event.getX()), getJmeY(event.getY())); + } + + return consumed; + } + + @Override + public void setMouseGrab(boolean grab) { + if(grab) { + inputHandler.getView().requestPointerCapture(); + } else { + inputHandler.getView().releasePointerCapture(); + } + } + +} diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java index 70911d855a..6d8cf0962b 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java +++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java @@ -51,9 +51,10 @@ import com.jme3.input.*; import com.jme3.input.android.AndroidInputHandler; import com.jme3.input.android.AndroidInputHandler14; +import com.jme3.input.android.AndroidInputHandler24; +import com.jme3.input.android.AndroidInputHandler26; import com.jme3.input.controls.SoftTextDialogInputListener; import com.jme3.input.dummy.DummyKeyInput; -import com.jme3.input.dummy.DummyMouseInput; import com.jme3.renderer.android.AndroidGL; import com.jme3.renderer.opengl.*; import com.jme3.system.*; @@ -125,7 +126,11 @@ public GLSurfaceView createView(Context context) { GLSurfaceView view = new GLSurfaceView(context); logger.log(Level.INFO, "Android Build Version: {0}", Build.VERSION.SDK_INT); if (androidInput == null) { - if (Build.VERSION.SDK_INT >= 14) { + if (Build.VERSION.SDK_INT >= 26) { + androidInput = new AndroidInputHandler26(); + } else if (Build.VERSION.SDK_INT >= 24) { + androidInput = new AndroidInputHandler24(); + } else if (Build.VERSION.SDK_INT >= 14) { androidInput = new AndroidInputHandler14(); } else if (Build.VERSION.SDK_INT >= 9) { androidInput = new AndroidInputHandler(); @@ -141,6 +146,9 @@ public GLSurfaceView createView(Context context) { view.setFocusableInTouchMode(true); view.setFocusable(true); + view.setFocusedByDefault(true); + view.requestFocus(); + //view.setClickable(true); // setFormat must be set before AndroidConfigChooser is called by the surfaceview. // if setFormat is called after ConfigChooser is called, then execution @@ -310,7 +318,7 @@ public com.jme3.renderer.Renderer getRenderer() { @Override public MouseInput getMouseInput() { - return new DummyMouseInput(); + return androidInput.getMouseInput(); } @Override diff --git a/jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java b/jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java index 9e3740bec9..66f2048f54 100644 --- a/jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java +++ b/jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java @@ -127,7 +127,7 @@ public final void registerWithInput() { initToggleRotateInput(); inputManager.addListener(this, inputs); - inputManager.setCursorVisible(dragToRotate); + inputManager.setMouseGrab(!dragToRotate); } @Override @@ -441,7 +441,7 @@ public void setDragToRotate(boolean dragToRotate) { this.dragToRotate = dragToRotate; this.canRotate = !dragToRotate; if (inputManager != null) { - inputManager.setCursorVisible(dragToRotate); + inputManager.setMouseGrab(!dragToRotate); } } diff --git a/jme3-core/src/main/java/com/jme3/input/ChaseCamera.java b/jme3-core/src/main/java/com/jme3/input/ChaseCamera.java index fe494801b3..72cbc2dc2f 100644 --- a/jme3-core/src/main/java/com/jme3/input/ChaseCamera.java +++ b/jme3-core/src/main/java/com/jme3/input/ChaseCamera.java @@ -925,7 +925,7 @@ public boolean isDragToRotate() { public void setDragToRotate(boolean dragToRotate) { this.dragToRotate = dragToRotate; this.canRotate = !dragToRotate; - inputManager.setCursorVisible(dragToRotate); + inputManager.setMouseGrab(!dragToRotate); } /** diff --git a/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java b/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java index 1a39ee9820..b2a9919b2e 100644 --- a/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java +++ b/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java @@ -205,8 +205,9 @@ public float getZoomSpeed() { */ public void setEnabled(boolean enable) { if (enabled && !enable) { - if (inputManager != null && (!dragToRotate || (dragToRotate && canRotate))) { - inputManager.setCursorVisible(true); + if (inputManager != null) { + inputManager.setMouseGrab(!dragToRotate); + inputManager.setCursorVisible(!dragToRotate || (dragToRotate && canRotate)); } } enabled = enable; @@ -246,7 +247,7 @@ public boolean isDragToRotate() { public void setDragToRotate(boolean dragToRotate) { this.dragToRotate = dragToRotate; if (inputManager != null) { - inputManager.setCursorVisible(dragToRotate); + inputManager.setMouseGrab(!dragToRotate); } } @@ -287,7 +288,7 @@ public void registerWithInput(InputManager inputManager) { inputManager.addMapping(CameraInput.FLYCAM_LOWER, new KeyTrigger(KeyInput.KEY_Z)); inputManager.addListener(this, mappings); - inputManager.setCursorVisible(dragToRotate || !isEnabled()); + inputManager.setMouseGrab(!dragToRotate && isEnabled()); Joystick[] joysticks = inputManager.getJoysticks(); if (joysticks != null && joysticks.length > 0) { @@ -349,7 +350,7 @@ public void unregisterInput() { } inputManager.removeListener(this); - inputManager.setCursorVisible(!dragToRotate); + inputManager.setMouseGrab(false); // Joysticks cannot be "unassigned" in the same way, but mappings are removed with listener. // Joystick-specific mapping might persist but won't trigger this listener. diff --git a/jme3-core/src/main/java/com/jme3/input/InputManager.java b/jme3-core/src/main/java/com/jme3/input/InputManager.java index b45433d1d9..041c81df40 100644 --- a/jme3-core/src/main/java/com/jme3/input/InputManager.java +++ b/jme3-core/src/main/java/com/jme3/input/InputManager.java @@ -96,6 +96,7 @@ public class InputManager implements RawInputListener { private long frameDelta = 0; private boolean eventsPermitted = false; private boolean mouseVisible = true; + private boolean mouseGrab = false; private boolean safeMode = false; private float globalAxisDeadZone = 0.05f; private final Vector2f cursorPos = new Vector2f(); @@ -700,6 +701,18 @@ public void setCursorVisible(boolean visible) { } } + /** + * Set whether to grab the mouse or not. + * + * @param grab whether to grab the mouse or not. + */ + public void setMouseGrab(boolean grab) { + if (mouseGrab != grab) { + mouseGrab = grab; + mouse.setMouseGrab(mouseGrab); + } + } + /** * Returns the current cursor position. The position is relative to the * bottom-left of the screen and is in pixels. diff --git a/jme3-core/src/main/java/com/jme3/input/MouseInput.java b/jme3-core/src/main/java/com/jme3/input/MouseInput.java index d2c959c92e..29a9823abe 100644 --- a/jme3-core/src/main/java/com/jme3/input/MouseInput.java +++ b/jme3-core/src/main/java/com/jme3/input/MouseInput.java @@ -75,6 +75,13 @@ public interface MouseInput extends Input { */ public void setCursorVisible(boolean visible); + /** + * Set whether to grab the mouse or not. + * + * @param grab whether to grab the mouse or not. + */ + public void setMouseGrab(boolean grab); + /** * Returns the number of buttons the mouse has. Typically 3 for most mice. * diff --git a/jme3-core/src/main/java/com/jme3/input/dummy/DummyMouseInput.java b/jme3-core/src/main/java/com/jme3/input/dummy/DummyMouseInput.java index f8d18be2e1..0e2c3b04da 100644 --- a/jme3-core/src/main/java/com/jme3/input/dummy/DummyMouseInput.java +++ b/jme3-core/src/main/java/com/jme3/input/dummy/DummyMouseInput.java @@ -48,6 +48,10 @@ public void setCursorVisible(boolean visible) { throw new IllegalStateException("Input not initialized."); } + @Override + public void setMouseGrab(boolean grab) { + } + @Override public int getButtonCount() { return 0; diff --git a/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java b/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java index 4caef0ca12..e46ade061f 100644 --- a/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java +++ b/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java @@ -183,6 +183,10 @@ private int convertButton(int i) { public void setCursorVisible(final boolean visible) { } + @Override + public void setMouseGrab(boolean grab) { + } + @Override public int getButtonCount() { return 3; @@ -235,4 +239,4 @@ public void mouseExited(java.awt.event.MouseEvent e) { public void mouseWheelMoved(MouseWheelEvent e) { onWheelScroll(e.getWheelRotation() * WHEEL_SCALE, e.getWheelRotation() * WHEEL_SCALE); } -} \ No newline at end of file +} diff --git a/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java b/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java index d4076c87b8..57645a2d34 100644 --- a/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java +++ b/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java @@ -171,6 +171,11 @@ public void setCursorVisible(boolean visible) { } } + @Override + public void setMouseGrab(boolean grab) { + setCursorVisible(false); + } + @Override public void update() { if (cursorMoved) { diff --git a/jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java b/jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java index 275ddf6454..fa479c1582 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java +++ b/jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java @@ -166,6 +166,11 @@ public void setCursorVisible(boolean visible){ Mouse.setGrabbed(!visible); } + @Override + public void setMouseGrab(boolean grab) { + setCursorVisible(false); + } + @Override public void setInputListener(RawInputListener listener) { this.listener = listener; diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java index 1aba44d570..43a82b28e7 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java @@ -347,6 +347,11 @@ public void setCursorVisible(boolean visible) { } } + @Override + public void setMouseGrab(boolean grab) { + setCursorVisible(false); + } + @Override public void setInputListener(RawInputListener listener) { this.listener = listener; From 7b4e24904d649eccd50ed3a817bc3c9f6d0a8363 Mon Sep 17 00:00:00 2001 From: Jesus Oliver Date: Fri, 6 Feb 2026 01:24:58 +0100 Subject: [PATCH 2/4] Added documentation --- .../java/com/jme3/input/android/AndroidMouseInput14.java | 6 +++--- .../java/com/jme3/input/android/AndroidMouseInput24.java | 6 ++++++ .../java/com/jme3/input/android/AndroidMouseInput26.java | 7 ++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java index b15056849e..67c52c58c0 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java @@ -47,7 +47,7 @@ import java.util.logging.Logger; /** - * AndroidMouseInput14 extends MouseInput to add mouse support for jME3 + * AndroidMouseInput14 implements MouseInput to add mouse support for jME3 * uses the onGenericMotion events that where added in Android rev 12 and MotionEvent.getButtonState * from Android rev 14 so added "14" suffix to the class to specify the Android required rev and * match other classes naming @@ -199,8 +199,8 @@ public boolean onHover(MotionEvent event) { boolean consumed = false; switch (event.getAction()) { - case MotionEvent.ACTION_SCROLL: // Should not be received here - case MotionEvent.ACTION_MOVE: // Should not be received here + case MotionEvent.ACTION_SCROLL: + case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_HOVER_EXIT: case MotionEvent.ACTION_HOVER_ENTER: diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java index 95fa8de8b3..6a00d3d68d 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java @@ -41,6 +41,12 @@ import java.nio.IntBuffer; import java.util.logging.Logger; +/** + * AndroidMouseInput24 extends AndroidMouseInput14 to improve mouse support + * using new events defined in API rev 24 and adding support for cursor change and cursor visibility + * + * @author joliver82 + */ public class AndroidMouseInput24 extends AndroidMouseInput14{ private static final Logger logger = Logger.getLogger(AndroidMouseInput24.class.getName()); diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java index 6c61edd0fc..c1c83199b4 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java @@ -42,6 +42,12 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * AndroidMouseInput26 extends AndroidMouseInput24 to improve mouse support + * adding grab/capture support using onCapturedPointer events + * + * @author joliver82 + */ public class AndroidMouseInput26 extends AndroidMouseInput24{ private static final Logger logger = Logger.getLogger(AndroidMouseInput26.class.getName()); @@ -54,7 +60,6 @@ public boolean onCapturedPointer(MotionEvent event) { boolean btnEventReceived = false; boolean leftPressed = false, rightPressed = false, centerPressed = false; -// int btnState = event.getButtonState(); int btnAction = event.getActionButton(); switch (event.getAction()) { From caa8707ec759f65d1de5f3ac50c2e934aa7c3f5b Mon Sep 17 00:00:00 2001 From: Jesus Oliver Date: Fri, 6 Feb 2026 01:31:41 +0100 Subject: [PATCH 3/4] More documentation --- .../com/jme3/input/android/AndroidInputHandler24.java | 9 +++++++-- .../com/jme3/input/android/AndroidInputHandler26.java | 7 +++++++ .../java/com/jme3/input/android/AndroidMouseInput26.java | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java index 12b8263b70..ce405ec3f4 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler24.java @@ -32,8 +32,13 @@ package com.jme3.input.android; -import android.view.View; - +/** + * AndroidInputHandler24 extends AndroidInputHandler14 to + * use AndroidMouseInput24 which adds usage of newer events and also enables cursor visibility + * and cursor image change. + * + * @author joliver82 + */ public class AndroidInputHandler24 extends AndroidInputHandler14 { public AndroidInputHandler24() { diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java index 27c9214c42..f5b9c5e875 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler26.java @@ -37,6 +37,13 @@ import android.view.MotionEvent; import android.view.View; +/** + * AndroidInputHandler26 extends AndroidInputHandler24 to + * add the onCapturedPointer events that where added in Android rev 26.
+ * The onCapturedPointer events are received when mouse is grabbed/captured. + * + * @author joliver82 + */ public class AndroidInputHandler26 extends AndroidInputHandler24 implements View.OnCapturedPointerListener { public AndroidInputHandler26() { diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java index c1c83199b4..659cc077bc 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java @@ -44,7 +44,7 @@ /** * AndroidMouseInput26 extends AndroidMouseInput24 to improve mouse support - * adding grab/capture support using onCapturedPointer events + * adding grab/capture support using onCapturedPointer events. * * @author joliver82 */ From 8f33ef005a9916824644a90727587f95e8eb237e Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Sat, 7 Feb 2026 16:02:01 +0100 Subject: [PATCH 4/4] fix pointer grab by not handling action cancel --- .../com/jme3/input/android/AndroidMouseInput14.java | 5 ----- .../com/jme3/input/android/AndroidMouseInput24.java | 1 - .../com/jme3/input/android/AndroidMouseInput26.java | 5 ++--- .../main/java/com/jme3/app/ChaseCameraAppState.java | 4 ++-- .../src/main/java/com/jme3/input/ChaseCamera.java | 2 +- .../src/main/java/com/jme3/input/FlyByCamera.java | 11 +++++------ .../src/main/java/com/jme3/input/InputManager.java | 13 ------------- .../src/main/java/com/jme3/input/MouseInput.java | 7 ------- .../java/com/jme3/input/dummy/DummyMouseInput.java | 4 ---- .../src/main/java/com/jme3/input/AWTMouseInput.java | 6 +----- .../main/java/com/jme3/input/awt/AwtMouseInput.java | 5 ----- .../java/com/jme3/input/lwjgl/LwjglMouseInput.java | 5 ----- .../java/com/jme3/input/lwjgl/GlfwMouseInput.java | 5 ----- 13 files changed, 11 insertions(+), 62 deletions(-) diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java index 67c52c58c0..e93f69ac95 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput14.java @@ -234,7 +234,6 @@ public boolean onGenericMotion(MotionEvent event) { break; case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: if((btnState & MotionEvent.BUTTON_PRIMARY) == MotionEvent.BUTTON_PRIMARY) { leftPressed = false; } @@ -269,10 +268,6 @@ public void setCursorVisible(boolean visible) { logger.log(Level.FINE, "Cannot hide mouse till API 24"); } - @Override - public void setMouseGrab(boolean grab) { - logger.log(Level.FINE, "Cannot grab mouse till API 26"); - } @Override public int getButtonCount() { diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java index 6a00d3d68d..bdc1c5fef7 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput24.java @@ -79,7 +79,6 @@ public boolean onGenericMotion(MotionEvent event) { break; case MotionEvent.ACTION_BUTTON_RELEASE: - case MotionEvent.ACTION_CANCEL: if(btnAction == MotionEvent.BUTTON_PRIMARY) { leftPressed = false; } diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java index 659cc077bc..34240f0104 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidMouseInput26.java @@ -77,7 +77,6 @@ public boolean onCapturedPointer(MotionEvent event) { break; case MotionEvent.ACTION_BUTTON_RELEASE: - case MotionEvent.ACTION_CANCEL: if(btnAction == MotionEvent.BUTTON_PRIMARY) { leftPressed = false; } @@ -108,8 +107,8 @@ public boolean onCapturedPointer(MotionEvent event) { } @Override - public void setMouseGrab(boolean grab) { - if(grab) { + public void setCursorVisible(boolean visible) { + if(!visible) { inputHandler.getView().requestPointerCapture(); } else { inputHandler.getView().releasePointerCapture(); diff --git a/jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java b/jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java index 66f2048f54..9e3740bec9 100644 --- a/jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java +++ b/jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java @@ -127,7 +127,7 @@ public final void registerWithInput() { initToggleRotateInput(); inputManager.addListener(this, inputs); - inputManager.setMouseGrab(!dragToRotate); + inputManager.setCursorVisible(dragToRotate); } @Override @@ -441,7 +441,7 @@ public void setDragToRotate(boolean dragToRotate) { this.dragToRotate = dragToRotate; this.canRotate = !dragToRotate; if (inputManager != null) { - inputManager.setMouseGrab(!dragToRotate); + inputManager.setCursorVisible(dragToRotate); } } diff --git a/jme3-core/src/main/java/com/jme3/input/ChaseCamera.java b/jme3-core/src/main/java/com/jme3/input/ChaseCamera.java index 72cbc2dc2f..fe494801b3 100644 --- a/jme3-core/src/main/java/com/jme3/input/ChaseCamera.java +++ b/jme3-core/src/main/java/com/jme3/input/ChaseCamera.java @@ -925,7 +925,7 @@ public boolean isDragToRotate() { public void setDragToRotate(boolean dragToRotate) { this.dragToRotate = dragToRotate; this.canRotate = !dragToRotate; - inputManager.setMouseGrab(!dragToRotate); + inputManager.setCursorVisible(dragToRotate); } /** diff --git a/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java b/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java index b2a9919b2e..1a39ee9820 100644 --- a/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java +++ b/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java @@ -205,9 +205,8 @@ public float getZoomSpeed() { */ public void setEnabled(boolean enable) { if (enabled && !enable) { - if (inputManager != null) { - inputManager.setMouseGrab(!dragToRotate); - inputManager.setCursorVisible(!dragToRotate || (dragToRotate && canRotate)); + if (inputManager != null && (!dragToRotate || (dragToRotate && canRotate))) { + inputManager.setCursorVisible(true); } } enabled = enable; @@ -247,7 +246,7 @@ public boolean isDragToRotate() { public void setDragToRotate(boolean dragToRotate) { this.dragToRotate = dragToRotate; if (inputManager != null) { - inputManager.setMouseGrab(!dragToRotate); + inputManager.setCursorVisible(dragToRotate); } } @@ -288,7 +287,7 @@ public void registerWithInput(InputManager inputManager) { inputManager.addMapping(CameraInput.FLYCAM_LOWER, new KeyTrigger(KeyInput.KEY_Z)); inputManager.addListener(this, mappings); - inputManager.setMouseGrab(!dragToRotate && isEnabled()); + inputManager.setCursorVisible(dragToRotate || !isEnabled()); Joystick[] joysticks = inputManager.getJoysticks(); if (joysticks != null && joysticks.length > 0) { @@ -350,7 +349,7 @@ public void unregisterInput() { } inputManager.removeListener(this); - inputManager.setMouseGrab(false); + inputManager.setCursorVisible(!dragToRotate); // Joysticks cannot be "unassigned" in the same way, but mappings are removed with listener. // Joystick-specific mapping might persist but won't trigger this listener. diff --git a/jme3-core/src/main/java/com/jme3/input/InputManager.java b/jme3-core/src/main/java/com/jme3/input/InputManager.java index 041c81df40..b45433d1d9 100644 --- a/jme3-core/src/main/java/com/jme3/input/InputManager.java +++ b/jme3-core/src/main/java/com/jme3/input/InputManager.java @@ -96,7 +96,6 @@ public class InputManager implements RawInputListener { private long frameDelta = 0; private boolean eventsPermitted = false; private boolean mouseVisible = true; - private boolean mouseGrab = false; private boolean safeMode = false; private float globalAxisDeadZone = 0.05f; private final Vector2f cursorPos = new Vector2f(); @@ -701,18 +700,6 @@ public void setCursorVisible(boolean visible) { } } - /** - * Set whether to grab the mouse or not. - * - * @param grab whether to grab the mouse or not. - */ - public void setMouseGrab(boolean grab) { - if (mouseGrab != grab) { - mouseGrab = grab; - mouse.setMouseGrab(mouseGrab); - } - } - /** * Returns the current cursor position. The position is relative to the * bottom-left of the screen and is in pixels. diff --git a/jme3-core/src/main/java/com/jme3/input/MouseInput.java b/jme3-core/src/main/java/com/jme3/input/MouseInput.java index 29a9823abe..d2c959c92e 100644 --- a/jme3-core/src/main/java/com/jme3/input/MouseInput.java +++ b/jme3-core/src/main/java/com/jme3/input/MouseInput.java @@ -75,13 +75,6 @@ public interface MouseInput extends Input { */ public void setCursorVisible(boolean visible); - /** - * Set whether to grab the mouse or not. - * - * @param grab whether to grab the mouse or not. - */ - public void setMouseGrab(boolean grab); - /** * Returns the number of buttons the mouse has. Typically 3 for most mice. * diff --git a/jme3-core/src/main/java/com/jme3/input/dummy/DummyMouseInput.java b/jme3-core/src/main/java/com/jme3/input/dummy/DummyMouseInput.java index 0e2c3b04da..f8d18be2e1 100644 --- a/jme3-core/src/main/java/com/jme3/input/dummy/DummyMouseInput.java +++ b/jme3-core/src/main/java/com/jme3/input/dummy/DummyMouseInput.java @@ -48,10 +48,6 @@ public void setCursorVisible(boolean visible) { throw new IllegalStateException("Input not initialized."); } - @Override - public void setMouseGrab(boolean grab) { - } - @Override public int getButtonCount() { return 0; diff --git a/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java b/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java index e46ade061f..4caef0ca12 100644 --- a/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java +++ b/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java @@ -183,10 +183,6 @@ private int convertButton(int i) { public void setCursorVisible(final boolean visible) { } - @Override - public void setMouseGrab(boolean grab) { - } - @Override public int getButtonCount() { return 3; @@ -239,4 +235,4 @@ public void mouseExited(java.awt.event.MouseEvent e) { public void mouseWheelMoved(MouseWheelEvent e) { onWheelScroll(e.getWheelRotation() * WHEEL_SCALE, e.getWheelRotation() * WHEEL_SCALE); } -} +} \ No newline at end of file diff --git a/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java b/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java index 57645a2d34..d4076c87b8 100644 --- a/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java +++ b/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java @@ -171,11 +171,6 @@ public void setCursorVisible(boolean visible) { } } - @Override - public void setMouseGrab(boolean grab) { - setCursorVisible(false); - } - @Override public void update() { if (cursorMoved) { diff --git a/jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java b/jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java index fa479c1582..275ddf6454 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java +++ b/jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/LwjglMouseInput.java @@ -166,11 +166,6 @@ public void setCursorVisible(boolean visible){ Mouse.setGrabbed(!visible); } - @Override - public void setMouseGrab(boolean grab) { - setCursorVisible(false); - } - @Override public void setInputListener(RawInputListener listener) { this.listener = listener; diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java index 43a82b28e7..1aba44d570 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java @@ -347,11 +347,6 @@ public void setCursorVisible(boolean visible) { } } - @Override - public void setMouseGrab(boolean grab) { - setCursorVisible(false); - } - @Override public void setInputListener(RawInputListener listener) { this.listener = listener;