diff --git a/Common/Input/InputState.h b/Common/Input/InputState.h index 2edb14210e9c..c5683b13f87c 100644 --- a/Common/Input/InputState.h +++ b/Common/Input/InputState.h @@ -138,7 +138,7 @@ enum { TOUCH_RELEASE_ALL = 1 << 6, // Useful for app focus switches when events may be lost. TOUCH_HOVER = 1 << 7, - // These are the Android getToolType() codes, shifted by 10. + // These are the Android getToolType() codes, shifted by 10. Unused currently. TOUCH_TOOL_MASK = 7 << 10, TOUCH_TOOL_UNKNOWN = 0 << 10, TOUCH_TOOL_FINGER = 1 << 10, diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index d95ad660c394..9c87411f370e 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -613,7 +613,7 @@ bool MainUI::event(QEvent *e) { case Qt::LeftButton: input.x = ((QMouseEvent*)e)->pos().x() * g_display.dpi_scale_x * xscale; input.y = ((QMouseEvent*)e)->pos().y() * g_display.dpi_scale_y * yscale; - input.flags = (e->type() == QEvent::MouseButtonPress) ? TOUCH_DOWN : TOUCH_UP; + input.flags = ((e->type() == QEvent::MouseButtonPress) ? TOUCH_DOWN : TOUCH_UP) | TOUCH_MOUSE; input.id = 0; NativeTouch(input); break; @@ -636,7 +636,7 @@ bool MainUI::event(QEvent *e) { case QEvent::MouseMove: input.x = ((QMouseEvent*)e)->pos().x() * g_display.dpi_scale_x * xscale; input.y = ((QMouseEvent*)e)->pos().y() * g_display.dpi_scale_y * yscale; - input.flags = TOUCH_MOVE; + input.flags = TOUCH_MOVE | TOUCH_MOUSE; input.id = 0; NativeTouch(input); break; diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 34ee89a07889..2484b808a074 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -16,6 +16,7 @@ + diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 7b91cfd5c271..c5b6318a130f 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1252,6 +1252,70 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_joystickAxis( env->ReleaseFloatArrayElements(values, valueBuffer, JNI_ABORT); // ABORT just means we don't want changes copied back! } +extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_mouse( + JNIEnv *env, jclass, jfloat x, jfloat y, int button, int action) { + if (!renderer_inited) + return false; + TouchInput input{}; + + static float last_x = 0.0f; + static float last_y = 0.0f; + + if (x == -1.0f) { + x = last_x; + } else { + last_x = x; + } + if (y == -1.0f) { + y = last_y; + } else { + last_y = y; + } + + x *= g_display.dpi_scale_x; + y *= g_display.dpi_scale_y; + + if (button == 0) { + // It's a pure mouse move. + input.flags = TOUCH_MOUSE | TOUCH_MOVE; + input.x = x; + input.y = y; + input.id = 0; + } else { + input.buttons = button; + input.x = x; + input.y = y; + switch (action) { + case 1: + input.flags = TOUCH_MOUSE | TOUCH_DOWN; + break; + case 2: + input.flags = TOUCH_MOUSE | TOUCH_UP; + break; + } + input.id = 0; + } + INFO_LOG(Log::System, "New-style mouse event: %f %f %d %d -> x: %f y: %f buttons: %d flags: %04x", x, y, button, action, input.x, input.y, input.buttons, input.flags); + NativeTouch(input); + + // Also send mouse button key events, for binding. + if (button) { + KeyInput input{}; + input.deviceId = DEVICE_ID_MOUSE; + switch (button) { + case 1: input.keyCode = NKCODE_EXT_MOUSEBUTTON_1; break; + case 2: input.keyCode = NKCODE_EXT_MOUSEBUTTON_2; break; + case 3: input.keyCode = NKCODE_EXT_MOUSEBUTTON_3; break; + default: WARN_LOG(Log::System, "Unexpected mouse button %d", button); + } + input.flags = action == 1 ? KEY_DOWN : KEY_UP; + if (input.keyCode != 0) { + NativeKey(input); + } + } + return true; +} + extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_mouseWheelEvent( JNIEnv *env, jclass, jfloat x, jfloat y) { if (!renderer_inited) diff --git a/android/src/org/ppsspp/ppsspp/MogaHack.java b/android/src/org/ppsspp/ppsspp/MogaHack.java index fdced81755d0..9b09d3a86ef4 100644 --- a/android/src/org/ppsspp/ppsspp/MogaHack.java +++ b/android/src/org/ppsspp/ppsspp/MogaHack.java @@ -68,7 +68,7 @@ public static void init(Controller controller, Context context) { // Convert implicit intent to explicit intent, see http://stackoverflow.com/a/26318757 Intent intent = new Intent(IControllerService.class.getName()); List resolveInfos = context.getPackageManager().queryIntentServices(intent, 0); - if (resolveInfos == null || resolveInfos.size() != 1) { + if (resolveInfos.size() != 1) { // What? this doesn't do anything. // Log.e("MogaHack", "Somebody is trying to intercept our intent. Disabling MOGA controller for security."); } diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index e5394565d349..ab4a11fc1982 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -275,7 +275,7 @@ private static ArrayList getSdCardPaths(final Context context) { for (String var : varNames) { Log.i(TAG, "getSdCardPaths: Checking env " + var); String secStore = System.getenv("SECONDARY_STORAGE"); - if (secStore != null && secStore.length() > 0) { + if (secStore != null && !secStore.isEmpty()) { list = new ArrayList(); list.add(secStore); break; @@ -573,10 +573,8 @@ private void updateSystemUiVisibility() { // Need API 11 to check for existence of a vibrator? Zany. @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void checkForVibrator() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - if (!vibrator.hasVibrator()) { - vibrator = null; - } + if (!vibrator.hasVibrator()) { + vibrator = null; } } @@ -786,7 +784,7 @@ protected void onDestroy() { do { try { Thread.sleep(10); - } catch (InterruptedException e) { + } catch (InterruptedException ignored) { } tries--; } while (nativeRenderer.isRenderingFrame() && tries > 0); @@ -979,7 +977,7 @@ protected String getInputDeviceDebugString() { for (InputDeviceState input : inputPlayers) { buffer += input.getDebugString(); } - if (buffer.length() == 0) { + if (buffer.isEmpty()) { buffer = "(no devices)"; } return buffer; @@ -997,6 +995,23 @@ public boolean IsXperiaPlay() { @Override public boolean dispatchKeyEvent(KeyEvent event) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && !isXperiaPlay) { + Log.i(TAG, "key event" + event.getSource()); + if (NativeSurfaceView.isFromSource(event, InputDevice.SOURCE_MOUSE)) { + Log.i(TAG, "Forwarding key event from mouse"); + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + // Probably a right click + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + NativeApp.mouse(-1, -1, 2, 1); + break; + case KeyEvent.ACTION_UP: + NativeApp.mouse(-1, -1, 2, 2); + break; + } + } + return true; + } + InputDeviceState state = getInputDeviceState(event); if (state == null) { return super.dispatchKeyEvent(event); @@ -1067,17 +1082,15 @@ void sendMouseDelta(float dx, float dy) { @Override @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) public boolean onGenericMotionEvent(MotionEvent event) { - // Log.d(TAG, "onGenericMotionEvent: " + event); + // Log.i(TAG, "NativeActivity onGenericMotionEvent: " + event); if (InputDeviceState.inputSourceIsJoystick(event.getSource())) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { - InputDeviceState state = getInputDeviceState(event); - if (state == null) { - Log.w(TAG, "Joystick event but failed to get input device state."); - return super.onGenericMotionEvent(event); - } - state.onJoystickMotion(event); - return true; + InputDeviceState state = getInputDeviceState(event); + if (state == null) { + Log.w(TAG, "Joystick event but failed to get input device state."); + return super.onGenericMotionEvent(event); } + state.onJoystickMotion(event); + return true; } if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { @@ -1092,12 +1105,30 @@ public boolean onGenericMotionEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_HOVER_MOVE: // process the mouse hover movement... + NativeApp.mouse(event.getX(), event.getY(), 0, 0); return true; case MotionEvent.ACTION_SCROLL: NativeApp.mouseWheelEvent(event.getAxisValue(MotionEvent.AXIS_HSCROLL), event.getAxisValue(MotionEvent.AXIS_VSCROLL)); return true; } } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_BUTTON_PRESS: { + int button = event.getActionButton(); + NativeApp.mouse(event.getX(), event.getY(), button, 1); + return true; + } + case MotionEvent.ACTION_BUTTON_RELEASE: { + int button = event.getActionButton(); + NativeApp.mouse(event.getX(), event.getY(), button, 2); + return true; + } + default: + break; + } + } return super.onGenericMotionEvent(event); } diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 3944fe73a358..a58a6a86e48a 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -50,6 +50,7 @@ public class NativeApp { public static native void accelerometer(float x, float y, float z); + public static native void mouse(float x, float y, int button, int action); public static native void mouseDelta(float x, float y); public static native void sendMessageFromJava(String msg, String arg); public static native void sendRequestResult(int seqID, boolean result, String value, int iValue); diff --git a/android/src/org/ppsspp/ppsspp/NativeGLView.java b/android/src/org/ppsspp/ppsspp/NativeGLView.java index 8686ca7593c0..6451bb822743 100644 --- a/android/src/org/ppsspp/ppsspp/NativeGLView.java +++ b/android/src/org/ppsspp/ppsspp/NativeGLView.java @@ -17,6 +17,7 @@ import android.os.Build; import android.os.Handler; import android.util.Log; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceControl; @@ -60,11 +61,48 @@ private int getToolType(final MotionEvent ev, int pointer) { return ev.getToolType(pointer); } + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) + private boolean onMouseEventModern(final MotionEvent ev) { + if (!NativeSurfaceView.isFromSource(ev, InputDevice.SOURCE_MOUSE)) { + return false; + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + // Log.i(TAG, "Action down. button state: " + ev.getButtonState()); + NativeApp.mouse(ev.getX(), ev.getY(), 1, 1); + break; + } + case MotionEvent.ACTION_UP: { + // Log.i(TAG, "Action up. button state: " + ev.getButtonState()); + NativeApp.mouse(ev.getX(), ev.getY(), 1, 2); + break; + } + case MotionEvent.ACTION_MOVE: { + // Log.i(TAG, "Action move. button state: " + ev.getButtonState()); + NativeApp.mouse(ev.getX(), ev.getY(), 0, 0); + break; + } + default: { + Log.i(TAG, "Unhandled modern mouse action: " + ev.getAction()); + break; + } + } + return true; + } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(final MotionEvent ev) { boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + // This is where workable mouse support arrived. + if (onMouseEventModern(ev)) { + return true; + } + } + for (int i = 0; i < ev.getPointerCount(); i++) { int pid = ev.getPointerId(i); int code = 0; diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index 3dee26703f2c..398c96c43776 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -16,6 +16,7 @@ import android.os.Handler; import android.util.Log; import android.view.InputDevice; +import android.view.InputEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceControl; @@ -66,15 +67,56 @@ private void processMouseDelta(final MotionEvent ev) { if ((ev.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { float dx = ev.getAxisValue(MotionEvent.AXIS_RELATIVE_X); float dy = ev.getAxisValue(MotionEvent.AXIS_RELATIVE_Y); + Log.i(TAG, "Mouse delta: " + dx + " " + dy); NativeApp.mouseDelta(dx, dy); } } + public static boolean isFromSource(final InputEvent ev, int source) { + return (ev.getSource() & source) == source; + } + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) + private boolean onMouseEventModern(final MotionEvent ev) { + if (!isFromSource(ev, InputDevice.SOURCE_MOUSE)) { + return false; + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + // Log.i(TAG, "Action down. button state: " + ev.getButtonState()); + NativeApp.mouse(ev.getX(), ev.getY(), 1, 1); + break; + } + case MotionEvent.ACTION_UP: { + // Log.i(TAG, "Action up. button state: " + ev.getButtonState()); + NativeApp.mouse(ev.getX(), ev.getY(), 1, 2); + break; + } + case MotionEvent.ACTION_MOVE: { + // Log.i(TAG, "Action move. button state: " + ev.getButtonState()); + NativeApp.mouse(ev.getX(), ev.getY(), 0, 0); + break; + } + default: { + Log.i(TAG, "Unhandled modern mouse action: " + ev.getAction()); + break; + } + } + return true; + } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(final MotionEvent ev) { boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + // This is where workable mouse support arrived. + if (onMouseEventModern(ev)) { + return true; + } + } + for (int i = 0; i < ev.getPointerCount(); i++) { int pid = ev.getPointerId(i); int code = 0; @@ -109,7 +151,6 @@ public boolean onTouchEvent(final MotionEvent ev) { int tool = getToolType(ev, i); code |= tool << 10; // We use the Android tool type codes } - // Can't use || due to short circuit evaluation NativeApp.touch(ev.getX(i), ev.getY(i), code, pid); } }