Cleanup the Android input logic implementation

This commit is contained in:
Fredia Huya-Kouadio 2022-08-14 23:07:43 -07:00
parent 337e4d185a
commit 1b3511ad49
10 changed files with 693 additions and 356 deletions

View File

@ -118,20 +118,31 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod
Input::get_singleton()->parse_input_event(ev); Input::get_singleton()->parse_input_event(ev);
} }
void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector<AndroidInputHandler::TouchPos> &p_points) { void AndroidInputHandler::_parse_all_touch(bool p_pressed) {
switch (p_event) {
case AMOTION_EVENT_ACTION_DOWN: { //gesture begin
if (touch.size()) { if (touch.size()) {
//end all if exist //end all if exist
for (int i = 0; i < touch.size(); i++) { for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev; Ref<InputEventScreenTouch> ev;
ev.instantiate(); ev.instantiate();
ev->set_index(touch[i].id); ev->set_index(touch[i].id);
ev->set_pressed(false); ev->set_pressed(p_pressed);
ev->set_position(touch[i].pos); ev->set_position(touch[i].pos);
Input::get_singleton()->parse_input_event(ev); Input::get_singleton()->parse_input_event(ev);
} }
} }
}
void AndroidInputHandler::_release_all_touch() {
_parse_all_touch(false);
touch.clear();
}
void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points) {
switch (p_event) {
case AMOTION_EVENT_ACTION_DOWN: { //gesture begin
// Release any remaining touches or mouse event
_release_mouse_event_info();
_release_all_touch();
touch.resize(p_points.size()); touch.resize(p_points.size());
for (int i = 0; i < p_points.size(); i++) { for (int i = 0; i < p_points.size(); i++) {
@ -140,18 +151,13 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
} }
//send touch //send touch
for (int i = 0; i < touch.size(); i++) { _parse_all_touch(true);
Ref<InputEventScreenTouch> ev;
ev.instantiate();
ev->set_index(touch[i].id);
ev->set_pressed(true);
ev->set_position(touch[i].pos);
Input::get_singleton()->parse_input_event(ev);
}
} break; } break;
case AMOTION_EVENT_ACTION_MOVE: { //motion case AMOTION_EVENT_ACTION_MOVE: { //motion
ERR_FAIL_COND(touch.size() != p_points.size()); if (touch.size() != p_points.size()) {
return;
}
for (int i = 0; i < touch.size(); i++) { for (int i = 0; i < touch.size(); i++) {
int idx = -1; int idx = -1;
@ -180,18 +186,7 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
} break; } break;
case AMOTION_EVENT_ACTION_CANCEL: case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_UP: { //release case AMOTION_EVENT_ACTION_UP: { //release
if (touch.size()) { _release_all_touch();
//end all if exist
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
ev.instantiate();
ev->set_index(touch[i].id);
ev->set_pressed(false);
ev->set_position(touch[i].pos);
Input::get_singleton()->parse_input_event(ev);
}
touch.clear();
}
} break; } break;
case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch
for (int i = 0; i < p_points.size(); i++) { for (int i = 0; i < p_points.size(); i++) {
@ -229,88 +224,118 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
} }
} }
void AndroidInputHandler::process_hover(int p_type, Point2 p_pos) { void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER if (!mouse_event_info.valid) {
switch (p_type) { return;
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
Ref<InputEventMouseMotion> ev;
ev.instantiate();
_set_key_modifier_state(ev);
ev->set_position(p_pos);
ev->set_global_position(p_pos);
ev->set_relative(p_pos - hover_prev_pos);
Input::get_singleton()->parse_input_event(ev);
hover_prev_pos = p_pos;
} break;
}
} }
void AndroidInputHandler::process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
MouseButton event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
switch (event_action) {
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
Ref<InputEventMouseButton> ev; Ref<InputEventMouseButton> ev;
ev.instantiate(); ev.instantiate();
_set_key_modifier_state(ev); _set_key_modifier_state(ev);
if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) { if (p_source_mouse_relative) {
ev->set_position(event_pos);
ev->set_global_position(event_pos);
} else {
ev->set_position(hover_prev_pos); ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos); ev->set_global_position(hover_prev_pos);
} else {
ev->set_position(mouse_event_info.pos);
ev->set_global_position(mouse_event_info.pos);
hover_prev_pos = mouse_event_info.pos;
} }
ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS); ev->set_pressed(p_pressed);
MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask); MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
buttons_state = event_buttons_mask; buttons_state = event_buttons_mask;
ev->set_button_index(_button_index_from_mask(changed_button_mask)); ev->set_button_index(_button_index_from_mask(changed_button_mask));
ev->set_button_mask(event_buttons_mask); ev->set_button_mask(event_buttons_mask);
ev->set_double_click(p_double_click);
Input::get_singleton()->parse_input_event(ev); Input::get_singleton()->parse_input_event(ev);
} break; }
case AMOTION_EVENT_ACTION_MOVE: { void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
_parse_mouse_event_info(MouseButton::NONE, false, false, p_source_mouse_relative);
mouse_event_info.valid = false;
}
void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative) {
MouseButton event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
switch (p_event_action) {
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
Ref<InputEventMouseMotion> ev; Ref<InputEventMouseMotion> ev;
ev.instantiate(); ev.instantiate();
_set_key_modifier_state(ev); _set_key_modifier_state(ev);
if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) { ev->set_position(p_event_pos);
ev->set_position(event_pos); ev->set_global_position(p_event_pos);
ev->set_global_position(event_pos); ev->set_relative(p_event_pos - hover_prev_pos);
ev->set_relative(event_pos - hover_prev_pos); Input::get_singleton()->parse_input_event(ev);
hover_prev_pos = event_pos; hover_prev_pos = p_event_pos;
} else { } break;
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_BUTTON_PRESS: {
// Release any remaining touches or mouse event
_release_mouse_event_info();
_release_all_touch();
mouse_event_info.valid = true;
mouse_event_info.pos = p_event_pos;
_parse_mouse_event_info(event_buttons_mask, true, p_double_click, p_source_mouse_relative);
} break;
case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
_release_mouse_event_info(p_source_mouse_relative);
} break;
case AMOTION_EVENT_ACTION_MOVE: {
if (!mouse_event_info.valid) {
return;
}
Ref<InputEventMouseMotion> ev;
ev.instantiate();
_set_key_modifier_state(ev);
if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos); ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos); ev->set_global_position(hover_prev_pos);
ev->set_relative(event_pos); ev->set_relative(p_event_pos);
} else {
ev->set_position(p_event_pos);
ev->set_global_position(p_event_pos);
ev->set_relative(p_event_pos - hover_prev_pos);
mouse_event_info.pos = p_event_pos;
hover_prev_pos = p_event_pos;
} }
ev->set_button_mask(event_buttons_mask); ev->set_button_mask(event_buttons_mask);
Input::get_singleton()->parse_input_event(ev); Input::get_singleton()->parse_input_event(ev);
} break; } break;
case AMOTION_EVENT_ACTION_SCROLL: { case AMOTION_EVENT_ACTION_SCROLL: {
Ref<InputEventMouseButton> ev; Ref<InputEventMouseButton> ev;
ev.instantiate(); ev.instantiate();
if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) { _set_key_modifier_state(ev);
ev->set_position(event_pos); if (p_source_mouse_relative) {
ev->set_global_position(event_pos);
} else {
ev->set_position(hover_prev_pos); ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos); ev->set_global_position(hover_prev_pos);
} else {
ev->set_position(p_event_pos);
ev->set_global_position(p_event_pos);
} }
ev->set_pressed(true); ev->set_pressed(true);
buttons_state = event_buttons_mask; buttons_state = event_buttons_mask;
if (event_vertical_factor > 0) { if (p_delta.y > 0) {
_wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, event_vertical_factor); _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, p_delta.y);
} else if (event_vertical_factor < 0) { } else if (p_delta.y < 0) {
_wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -event_vertical_factor); _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -p_delta.y);
} }
if (event_horizontal_factor > 0) { if (p_delta.x > 0) {
_wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, event_horizontal_factor); _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, p_delta.x);
} else if (event_horizontal_factor < 0) { } else if (p_delta.x < 0) {
_wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -event_horizontal_factor); _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -p_delta.x);
} }
} break; } break;
} }
@ -329,18 +354,22 @@ void AndroidInputHandler::_wheel_button_click(MouseButton event_buttons_mask, co
Input::get_singleton()->parse_input_event(evdd); Input::get_singleton()->parse_input_event(evdd);
} }
void AndroidInputHandler::process_double_tap(int event_android_button_mask, Point2 p_pos) { void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
MouseButton event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask); Ref<InputEventMagnifyGesture> magnify_event;
Ref<InputEventMouseButton> ev; magnify_event.instantiate();
ev.instantiate(); _set_key_modifier_state(magnify_event);
_set_key_modifier_state(ev); magnify_event->set_position(p_pos);
ev->set_position(p_pos); magnify_event->set_factor(p_factor);
ev->set_global_position(p_pos); Input::get_singleton()->parse_input_event(magnify_event);
ev->set_pressed(event_button_mask != MouseButton::NONE); }
ev->set_button_index(_button_index_from_mask(event_button_mask));
ev->set_button_mask(event_button_mask); void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
ev->set_double_click(true); Ref<InputEventPanGesture> pan_event;
Input::get_singleton()->parse_input_event(ev); pan_event.instantiate();
_set_key_modifier_state(pan_event);
pan_event->set_position(p_pos);
pan_event->set_delta(p_delta);
Input::get_singleton()->parse_input_event(pan_event);
} }
MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) { MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) {

View File

@ -44,6 +44,11 @@ public:
Point2 pos; Point2 pos;
}; };
struct MouseEventInfo {
bool valid = false;
Point2 pos;
};
enum { enum {
JOY_EVENT_BUTTON = 0, JOY_EVENT_BUTTON = 0,
JOY_EVENT_AXIS = 1, JOY_EVENT_AXIS = 1,
@ -68,6 +73,7 @@ private:
MouseButton buttons_state = MouseButton::NONE; MouseButton buttons_state = MouseButton::NONE;
Vector<TouchPos> touch; Vector<TouchPos> touch;
MouseEventInfo mouse_event_info;
Point2 hover_prev_pos; // needed to calculate the relative position on hover events Point2 hover_prev_pos; // needed to calculate the relative position on hover events
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev); void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
@ -77,11 +83,19 @@ private:
void _wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor); void _wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
void _parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative);
void _release_mouse_event_info(bool p_source_mouse_relative = false);
void _parse_all_touch(bool p_pressed);
void _release_all_touch();
public: public:
void process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points); void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative);
void process_hover(int p_type, Point2 p_pos); void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
void process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0); void process_magnify(Point2 p_pos, float p_factor);
void process_double_tap(int event_android_button_mask, Point2 p_pos); void process_pan(Point2 p_pos, Vector2 p_delta);
void process_joy_event(JoypadEvent p_event); void process_joy_event(JoypadEvent p_event);
void process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed); void process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed);
}; };

View File

@ -31,7 +31,6 @@
package org.godotengine.godot; package org.godotengine.godot;
import org.godotengine.godot.gl.GLSurfaceView; import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.gl.GodotRenderer; import org.godotengine.godot.gl.GodotRenderer;
import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler; import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.utils.GLUtils; import org.godotengine.godot.utils.GLUtils;
import org.godotengine.godot.xr.XRMode; import org.godotengine.godot.xr.XRMode;
@ -46,7 +45,6 @@ import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.os.Build; import android.os.Build;
import android.view.GestureDetector;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.PointerIcon; import android.view.PointerIcon;
@ -75,7 +73,6 @@ import androidx.annotation.Keep;
public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
private final Godot godot; private final Godot godot;
private final GodotInputHandler inputHandler; private final GodotInputHandler inputHandler;
private final GestureDetector detector;
private final GodotRenderer godotRenderer; private final GodotRenderer godotRenderer;
private PointerIcon pointerIcon; private PointerIcon pointerIcon;
@ -85,7 +82,6 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
this.godot = godot; this.godot = godot;
this.inputHandler = new GodotInputHandler(this); this.inputHandler = new GodotInputHandler(this);
this.detector = new GestureDetector(context, new GodotGestureHandler(this));
this.godotRenderer = new GodotRenderer(); this.godotRenderer = new GodotRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
@ -132,7 +128,6 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override @Override
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event); super.onTouchEvent(event);
this.detector.onTouchEvent(event);
return inputHandler.onTouchEvent(event); return inputHandler.onTouchEvent(event);
} }
@ -156,6 +151,24 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
return inputHandler.onGenericMotionEvent(event); return inputHandler.onGenericMotionEvent(event);
} }
@Override
public void onPointerCaptureChange(boolean hasCapture) {
super.onPointerCaptureChange(hasCapture);
inputHandler.onPointerCaptureChange(hasCapture);
}
@Override
public void requestPointerCapture() {
super.requestPointerCapture();
inputHandler.onPointerCaptureChange(true);
}
@Override
public void releasePointerCapture() {
super.releasePointerCapture();
inputHandler.onPointerCaptureChange(false);
}
/** /**
* called from JNI to change pointer icon * called from JNI to change pointer icon
*/ */

View File

@ -92,7 +92,7 @@ public class GodotLib {
public static native void newcontext(Surface p_surface); public static native void newcontext(Surface p_surface);
/** /**
* Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread. * Forward {@link Activity#onBackPressed()} event.
*/ */
public static native void back(); public static native void back();
@ -108,63 +108,60 @@ public class GodotLib {
public static native void ttsCallback(int event, int id, int pos); public static native void ttsCallback(int event, int id, int pos);
/** /**
* Forward touch events from the main thread to the GL thread. * Forward touch events.
*/ */
public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions); public static native void dispatchTouchEvent(int event, int pointer, int pointerCount, float[] positions);
public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask);
public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask, float verticalFactor, float horizontalFactor);
/** /**
* Forward hover events from the main thread to the GL thread. * Dispatch mouse events
*/ */
public static native void hover(int type, float x, float y); public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative);
public static native void magnify(float x, float y, float factor);
public static native void pan(float x, float y, float deltaX, float deltaY);
/** /**
* Forward double_tap events from the main thread to the GL thread. * Forward accelerometer sensor events.
*/
public static native void doubleTap(int buttonMask, int x, int y);
/**
* Forward accelerometer sensor events from the main thread to the GL thread.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent) * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/ */
public static native void accelerometer(float x, float y, float z); public static native void accelerometer(float x, float y, float z);
/** /**
* Forward gravity sensor events from the main thread to the GL thread. * Forward gravity sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent) * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/ */
public static native void gravity(float x, float y, float z); public static native void gravity(float x, float y, float z);
/** /**
* Forward magnetometer sensor events from the main thread to the GL thread. * Forward magnetometer sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent) * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/ */
public static native void magnetometer(float x, float y, float z); public static native void magnetometer(float x, float y, float z);
/** /**
* Forward gyroscope sensor events from the main thread to the GL thread. * Forward gyroscope sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent) * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/ */
public static native void gyroscope(float x, float y, float z); public static native void gyroscope(float x, float y, float z);
/** /**
* Forward regular key events from the main thread to the GL thread. * Forward regular key events.
*/ */
public static native void key(int p_keycode, int p_physical_keycode, int p_unicode, boolean p_pressed); public static native void key(int p_keycode, int p_physical_keycode, int p_unicode, boolean p_pressed);
/** /**
* Forward game device's key events from the main thread to the GL thread. * Forward game device's key events.
*/ */
public static native void joybutton(int p_device, int p_but, boolean p_pressed); public static native void joybutton(int p_device, int p_but, boolean p_pressed);
/** /**
* Forward joystick devices axis motion events from the main thread to the GL thread. * Forward joystick devices axis motion events.
*/ */
public static native void joyaxis(int p_device, int p_axis, float p_value); public static native void joyaxis(int p_device, int p_axis, float p_value);
/** /**
* Forward joystick devices hat motion events from the main thread to the GL thread. * Forward joystick devices hat motion events.
*/ */
public static native void joyhat(int p_device, int p_hat_x, int p_hat_y); public static native void joyhat(int p_device, int p_hat_x, int p_hat_y);

View File

@ -30,7 +30,6 @@
package org.godotengine.godot; package org.godotengine.godot;
import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler; import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.vulkan.VkRenderer; import org.godotengine.godot.vulkan.VkRenderer;
import org.godotengine.godot.vulkan.VkSurfaceView; import org.godotengine.godot.vulkan.VkSurfaceView;
@ -38,7 +37,6 @@ import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.view.GestureDetector;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.PointerIcon; import android.view.PointerIcon;
@ -49,7 +47,6 @@ import androidx.annotation.Keep;
public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
private final Godot godot; private final Godot godot;
private final GodotInputHandler mInputHandler; private final GodotInputHandler mInputHandler;
private final GestureDetector mGestureDetector;
private final VkRenderer mRenderer; private final VkRenderer mRenderer;
private PointerIcon pointerIcon; private PointerIcon pointerIcon;
@ -58,7 +55,6 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
this.godot = godot; this.godot = godot;
mInputHandler = new GodotInputHandler(this); mInputHandler = new GodotInputHandler(this);
mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
mRenderer = new VkRenderer(); mRenderer = new VkRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
@ -106,7 +102,6 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override @Override
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event); super.onTouchEvent(event);
mGestureDetector.onTouchEvent(event);
return mInputHandler.onTouchEvent(event); return mInputHandler.onTouchEvent(event);
} }
@ -130,6 +125,24 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
return mInputHandler.onGenericMotionEvent(event); return mInputHandler.onGenericMotionEvent(event);
} }
@Override
public void requestPointerCapture() {
super.requestPointerCapture();
mInputHandler.onPointerCaptureChange(true);
}
@Override
public void releasePointerCapture() {
super.releasePointerCapture();
mInputHandler.onPointerCaptureChange(false);
}
@Override
public void onPointerCaptureChange(boolean hasCapture) {
super.onPointerCaptureChange(hasCapture);
mInputHandler.onPointerCaptureChange(hasCapture);
}
/** /**
* called from JNI to change pointer icon * called from JNI to change pointer icon
*/ */

View File

@ -1,87 +0,0 @@
/*************************************************************************/
/* GodotGestureHandler.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
package org.godotengine.godot.input;
import org.godotengine.godot.GodotLib;
import org.godotengine.godot.GodotRenderView;
import android.view.GestureDetector;
import android.view.MotionEvent;
/**
* Handles gesture input related events for the {@link GodotRenderView} view.
* https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
*/
public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener {
private final GodotRenderView mRenderView;
public GodotGestureHandler(GodotRenderView godotView) {
mRenderView = godotView;
}
private void queueEvent(Runnable task) {
mRenderView.queueOnRenderThread(task);
}
@Override
public boolean onDown(MotionEvent event) {
super.onDown(event);
//Log.i("GodotGesture", "onDown");
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
super.onSingleTapConfirmed(event);
return true;
}
@Override
public void onLongPress(MotionEvent event) {
//Log.i("GodotGesture", "onLongPress");
}
@Override
public boolean onDoubleTap(MotionEvent event) {
//Log.i("GodotGesture", "onDoubleTap");
final int x = Math.round(event.getX());
final int y = Math.round(event.getY());
final int buttonMask = event.getButtonState();
GodotLib.doubleTap(buttonMask, x, y);
return true;
}
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
//Log.i("GodotGesture", "onFling");
return true;
}
}

View File

@ -0,0 +1,289 @@
/*************************************************************************/
/* GodotGestureHandler.kt */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
package org.godotengine.godot.input
import android.os.Build
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.InputDevice
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.ScaleGestureDetector.OnScaleGestureListener
import org.godotengine.godot.GodotLib
/**
* Handles regular and scale gesture input related events for the [GodotView] view.
*
* @See https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
* @See https://developer.android.com/reference/android/view/ScaleGestureDetector.OnScaleGestureListener
*/
internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureListener {
companion object {
private val TAG = GodotGestureHandler::class.java.simpleName
}
/**
* Enable pan and scale gestures
*/
var panningAndScalingEnabled = false
private var doubleTapInProgress = false
private var dragInProgress = false
private var scaleInProgress = false
private var contextClickInProgress = false
private var pointerCaptureInProgress = false
override fun onDown(event: MotionEvent): Boolean {
// Don't send / register a down event while we're in the middle of a double-tap
if (!doubleTapInProgress) {
// Send the down event
GodotInputHandler.handleMotionEvent(event)
}
return true
}
override fun onSingleTapUp(event: MotionEvent): Boolean {
GodotInputHandler.handleMotionEvent(event)
return true
}
override fun onLongPress(event: MotionEvent) {
contextClickRouter(event)
}
private fun contextClickRouter(event: MotionEvent) {
if (scaleInProgress) {
return
}
// Cancel the previous down event
GodotInputHandler.handleMotionEvent(
event.source,
MotionEvent.ACTION_CANCEL,
event.buttonState,
event.x,
event.y
)
// Turn a context click into a single tap right mouse button click.
GodotInputHandler.handleMouseEvent(
MotionEvent.ACTION_DOWN,
MotionEvent.BUTTON_SECONDARY,
event.x,
event.y
)
contextClickInProgress = true
}
fun onPointerCaptureChange(hasCapture: Boolean) {
if (pointerCaptureInProgress == hasCapture) {
return
}
if (!hasCapture) {
// Dispatch a mouse relative ACTION_UP event to signal the end of the capture
GodotInputHandler.handleMouseEvent(
MotionEvent.ACTION_UP,
0,
0f,
0f,
0f,
0f,
false,
true
)
}
pointerCaptureInProgress = hasCapture
}
fun onMotionEvent(event: MotionEvent): Boolean {
return when (event.actionMasked) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_BUTTON_RELEASE -> {
onActionUp(event)
}
MotionEvent.ACTION_MOVE -> {
onActionMove(event)
}
else -> false
}
}
private fun onActionUp(event: MotionEvent): Boolean {
val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
} else {
false
}
when {
pointerCaptureInProgress -> {
return if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
// Don't dispatch the ACTION_CANCEL while a capture is in progress
true
} else {
GodotInputHandler.handleMouseEvent(
MotionEvent.ACTION_UP,
event.buttonState,
event.x,
event.y,
0f,
0f,
false,
sourceMouseRelative
)
pointerCaptureInProgress = false
true
}
}
dragInProgress -> {
GodotInputHandler.handleMotionEvent(event)
dragInProgress = false
return true
}
contextClickInProgress -> {
GodotInputHandler.handleMouseEvent(
event.actionMasked,
0,
event.x,
event.y,
0f,
0f,
false,
sourceMouseRelative
)
contextClickInProgress = false
return true
}
else -> return false
}
}
private fun onActionMove(event: MotionEvent): Boolean {
if (contextClickInProgress) {
val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
} else {
false
}
GodotInputHandler.handleMouseEvent(
event.actionMasked,
MotionEvent.BUTTON_SECONDARY,
event.x,
event.y,
0f,
0f,
false,
sourceMouseRelative
)
return true
}
return false
}
override fun onDoubleTapEvent(event: MotionEvent): Boolean {
if (event.actionMasked == MotionEvent.ACTION_UP) {
doubleTapInProgress = false
}
return true
}
override fun onDoubleTap(event: MotionEvent): Boolean {
doubleTapInProgress = true
val x = event.x
val y = event.y
val buttonMask =
if (GodotInputHandler.isMouseEvent(event)) {
event.buttonState
} else {
MotionEvent.BUTTON_PRIMARY
}
GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_DOWN, buttonMask, x, y, true)
GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, 0, x, y, false)
return true
}
override fun onScroll(
originEvent: MotionEvent,
terminusEvent: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
if (scaleInProgress) {
if (dragInProgress) {
// Cancel the drag
GodotInputHandler.handleMotionEvent(
originEvent.source,
MotionEvent.ACTION_CANCEL,
originEvent.buttonState,
originEvent.x,
originEvent.y
)
dragInProgress = false
}
return true
}
dragInProgress = true
val x = terminusEvent.x
val y = terminusEvent.y
if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled) {
GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
} else {
GodotInputHandler.handleMotionEvent(terminusEvent)
}
return true
}
override fun onScale(detector: ScaleGestureDetector?): Boolean {
if (detector == null || !panningAndScalingEnabled) {
return false
}
GodotLib.magnify(
detector.focusX,
detector.focusY,
detector.scaleFactor
)
return true
}
override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
if (detector == null || !panningAndScalingEnabled) {
return false
}
scaleInProgress = true
return true
}
override fun onScaleEnd(detector: ScaleGestureDetector?) {
scaleInProgress = false
}
}

View File

@ -41,13 +41,13 @@ import android.os.Build;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.view.GestureDetector;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.InputDevice.MotionRange;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -55,21 +55,49 @@ import java.util.Set;
* Handles input related events for the {@link GodotRenderView} view. * Handles input related events for the {@link GodotRenderView} view.
*/ */
public class GodotInputHandler implements InputManager.InputDeviceListener { public class GodotInputHandler implements InputManager.InputDeviceListener {
private final GodotRenderView mRenderView; private static final String TAG = GodotInputHandler.class.getSimpleName();
private final InputManager mInputManager;
private final String tag = this.getClass().getSimpleName();
private final SparseIntArray mJoystickIds = new SparseIntArray(4); private final SparseIntArray mJoystickIds = new SparseIntArray(4);
private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4); private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);
private final GodotRenderView mRenderView;
private final InputManager mInputManager;
private final GestureDetector gestureDetector;
private final ScaleGestureDetector scaleGestureDetector;
private final GodotGestureHandler godotGestureHandler;
public GodotInputHandler(GodotRenderView godotView) { public GodotInputHandler(GodotRenderView godotView) {
final Context context = godotView.getView().getContext();
mRenderView = godotView; mRenderView = godotView;
mInputManager = (InputManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_SERVICE); mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
mInputManager.registerInputDeviceListener(this, null); mInputManager.registerInputDeviceListener(this, null);
this.godotGestureHandler = new GodotGestureHandler();
this.gestureDetector = new GestureDetector(context, godotGestureHandler);
this.gestureDetector.setIsLongpressEnabled(false);
this.scaleGestureDetector = new ScaleGestureDetector(context, godotGestureHandler);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
this.scaleGestureDetector.setStylusScaleEnabled(true);
}
} }
private boolean isKeyEvent_GameDevice(int source) { /**
* Enable long press events. This is false by default.
*/
public void enableLongPress(boolean enable) {
this.gestureDetector.setIsLongpressEnabled(enable);
}
/**
* Enable multi-fingers pan & scale gestures. This is false by default.
*
* Note: This may interfere with multi-touch handling / support.
*/
public void enablePanningAndScalingGestures(boolean enable) {
this.godotGestureHandler.setPanningAndScalingEnabled(enable);
}
private boolean isKeyEventGameDevice(int source) {
// Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD) // Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD)
if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD)) if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD))
return false; return false;
@ -77,6 +105,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD; return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
} }
public void onPointerCaptureChange(boolean hasCapture) {
godotGestureHandler.onPointerCaptureChange(hasCapture);
}
public boolean onKeyUp(final int keyCode, KeyEvent event) { public boolean onKeyUp(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) { if (keyCode == KeyEvent.KEYCODE_BACK) {
return true; return true;
@ -87,7 +119,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
int source = event.getSource(); int source = event.getSource();
if (isKeyEvent_GameDevice(source)) { if (isKeyEventGameDevice(source)) {
// Check if the device exists // Check if the device exists
final int deviceId = event.getDeviceId(); final int deviceId = event.getDeviceId();
if (mJoystickIds.indexOfKey(deviceId) >= 0) { if (mJoystickIds.indexOfKey(deviceId) >= 0) {
@ -121,11 +153,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
int source = event.getSource(); int source = event.getSource();
//Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
final int deviceId = event.getDeviceId(); final int deviceId = event.getDeviceId();
// Check if source is a game device and that the device is a registered gamepad // Check if source is a game device and that the device is a registered gamepad
if (isKeyEvent_GameDevice(source)) { if (isKeyEventGameDevice(source)) {
if (event.getRepeatCount() > 0) // ignore key echo if (event.getRepeatCount() > 0) // ignore key echo
return true; return true;
@ -145,47 +176,41 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
public boolean onTouchEvent(final MotionEvent event) { public boolean onTouchEvent(final MotionEvent event) {
// Mouse drag (mouse pressed and move) doesn't fire onGenericMotionEvent so this is needed this.scaleGestureDetector.onTouchEvent(event);
if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { if (this.gestureDetector.onTouchEvent(event)) {
if (event.getAction() != MotionEvent.ACTION_MOVE) { // The gesture detector has handled the event.
// we return true because every time a mouse event is fired, the event is already handled
// in onGenericMotionEvent, so by touch event we can say that the event is also handled
return true; return true;
} }
if (godotGestureHandler.onMotionEvent(event)) {
// The gesture handler has handled the event.
return true;
}
// Drag events are handled by the [GodotGestureHandler]
if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
return true;
}
if (isMouseEvent(event)) {
return handleMouseEvent(event); return handleMouseEvent(event);
} }
final int evcount = event.getPointerCount(); return handleTouchEvent(event);
if (evcount == 0)
return true;
if (mRenderView != null) {
final float[] arr = new float[event.getPointerCount() * 3]; // pointerId1, x1, y1, pointerId2, etc...
for (int i = 0; i < event.getPointerCount(); i++) {
arr[i * 3 + 0] = event.getPointerId(i);
arr[i * 3 + 1] = event.getX(i);
arr[i * 3 + 2] = event.getY(i);
}
final int action = event.getActionMasked();
final int pointer_idx = event.getPointerId(event.getActionIndex());
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN: {
GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr);
} break;
}
}
return true;
} }
public boolean onGenericMotionEvent(MotionEvent event) { public boolean onGenericMotionEvent(MotionEvent event) {
if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getAction() == MotionEvent.ACTION_MOVE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
// The gesture detector has handled the event.
return true;
}
if (godotGestureHandler.onMotionEvent(event)) {
// The gesture handler has handled the event.
return true;
}
if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
// Check if the device exists // Check if the device exists
final int deviceId = event.getDeviceId(); final int deviceId = event.getDeviceId();
if (mJoystickIds.indexOfKey(deviceId) >= 0) { if (mJoystickIds.indexOfKey(deviceId) >= 0) {
@ -198,15 +223,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
for (int i = 0; i < joystick.axes.size(); i++) { for (int i = 0; i < joystick.axes.size(); i++) {
final int axis = joystick.axes.get(i); final int axis = joystick.axes.get(i);
final float value = event.getAxisValue(axis); final float value = event.getAxisValue(axis);
/** /*
* As all axes are polled for each event, only fire an axis event if the value has actually changed. As all axes are polled for each event, only fire an axis event if the value has actually changed.
* Prevents flooding Godot with repeated events. Prevents flooding Godot with repeated events.
*/ */
if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) { if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) {
// save value to prevent repeats // save value to prevent repeats
joystick.axesValues.put(axis, value); joystick.axesValues.put(axis, value);
final int godotAxisIdx = i; GodotLib.joyaxis(godotJoyId, i, value);
GodotLib.joyaxis(godotJoyId, godotAxisIdx, value);
} }
} }
@ -221,18 +245,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
return true; return true;
} }
} else if (event.isFromSource(InputDevice.SOURCE_STYLUS)) { } else if (isMouseEvent(event)) {
final float x = event.getX();
final float y = event.getY();
final int type = event.getAction();
GodotLib.hover(type, x, y);
return true;
} else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return handleMouseEvent(event); return handleMouseEvent(event);
} }
}
return false; return false;
} }
@ -243,7 +258,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
for (int deviceId : deviceIds) { for (int deviceId : deviceIds) {
InputDevice device = mInputManager.getInputDevice(deviceId); InputDevice device = mInputManager.getInputDevice(deviceId);
if (DEBUG) { if (DEBUG) {
Log.v("GodotInputHandler", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); Log.v(TAG, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
} }
onInputDeviceAdded(deviceId); onInputDeviceAdded(deviceId);
} }
@ -288,13 +303,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
joystick.name = device.getName(); joystick.name = device.getName();
//Helps with creating new joypad mappings. //Helps with creating new joypad mappings.
Log.i(tag, "=== New Input Device: " + joystick.name); Log.i(TAG, "=== New Input Device: " + joystick.name);
Set<Integer> already = new HashSet<>(); Set<Integer> already = new HashSet<>();
for (InputDevice.MotionRange range : device.getMotionRanges()) { for (InputDevice.MotionRange range : device.getMotionRanges()) {
boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK); boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);
boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD); boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD);
//Log.i(tag, "axis: "+range.getAxis()+ ", isJoystick: "+isJoystick+", isGamepad: "+isGamepad);
if (!isJoystick && !isGamepad) { if (!isJoystick && !isGamepad) {
continue; continue;
} }
@ -306,14 +320,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
already.add(axis); already.add(axis);
joystick.axes.add(axis); joystick.axes.add(axis);
} else { } else {
Log.w(tag, " - DUPLICATE AXIS VALUE IN LIST: " + axis); Log.w(TAG, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
} }
} }
} }
Collections.sort(joystick.axes); Collections.sort(joystick.axes);
for (int idx = 0; idx < joystick.axes.size(); idx++) { for (int idx = 0; idx < joystick.axes.size(); idx++) {
//Helps with creating new joypad mappings. //Helps with creating new joypad mappings.
Log.i(tag, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx); Log.i(TAG, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx);
} }
mJoysticksDevices.put(deviceId, joystick); mJoysticksDevices.put(deviceId, joystick);
@ -338,13 +352,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
onInputDeviceAdded(deviceId); onInputDeviceAdded(deviceId);
} }
private static class RangeComparator implements Comparator<MotionRange> {
@Override
public int compare(MotionRange arg0, MotionRange arg1) {
return arg0.getAxis() - arg1.getAxis();
}
}
public static int getGodotButton(int keyCode) { public static int getGodotButton(int keyCode) {
int button; int button;
switch (keyCode) { switch (keyCode) {
@ -410,39 +417,113 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return button; return button;
} }
private boolean handleMouseEvent(final MotionEvent event) { static boolean isMouseEvent(MotionEvent event) {
switch (event.getActionMasked()) { return isMouseEvent(event.getSource());
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_HOVER_EXIT: {
final float x = event.getX();
final float y = event.getY();
final int type = event.getAction();
GodotLib.hover(type, x, y);
return true;
} }
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE: private static boolean isMouseEvent(int eventSource) {
case MotionEvent.ACTION_MOVE: { boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE);
}
return mouseSource;
}
static boolean handleMotionEvent(final MotionEvent event) {
if (isMouseEvent(event)) {
return handleMouseEvent(event);
}
return handleTouchEvent(event);
}
static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y) {
return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0);
}
static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY) {
if (isMouseEvent(eventSource)) {
return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, false, false);
}
return handleTouchEvent(eventAction, x, y);
}
static boolean handleMouseEvent(final MotionEvent event) {
final int eventAction = event.getActionMasked();
final float x = event.getX(); final float x = event.getX();
final float y = event.getY(); final float y = event.getY();
final int buttonsMask = event.getButtonState(); final int buttonsMask = event.getButtonState();
final int action = event.getAction();
GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask);
return true;
}
case MotionEvent.ACTION_SCROLL: {
final float x = event.getX();
final float y = event.getY();
final int buttonsMask = event.getButtonState();
final int action = event.getAction();
final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL); final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL); final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor); boolean sourceMouseRelative = false;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE);
} }
return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative);
}
static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) {
return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false);
}
static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, boolean doubleClick) {
return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, doubleClick, false);
}
static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) {
switch (eventAction) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// Zero-up the button state
buttonsMask = 0;
// FALL THROUGH
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP: { case MotionEvent.ACTION_HOVER_ENTER:
// we can safely ignore these cases because they are always come beside ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE case MotionEvent.ACTION_HOVER_EXIT:
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_SCROLL: {
GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative);
return true;
}
}
return false;
}
static boolean handleTouchEvent(final MotionEvent event) {
final int pointerCount = event.getPointerCount();
if (pointerCount == 0) {
return true;
}
final float[] positions = new float[pointerCount * 3]; // pointerId1, x1, y1, pointerId2, etc...
for (int i = 0; i < pointerCount; i++) {
positions[i * 3 + 0] = event.getPointerId(i);
positions[i * 3 + 1] = event.getX(i);
positions[i * 3 + 2] = event.getY(i);
}
final int action = event.getActionMasked();
final int actionPointerId = event.getPointerId(event.getActionIndex());
return handleTouchEvent(action, actionPointerId, pointerCount, positions);
}
static boolean handleTouchEvent(int eventAction, float x, float y) {
return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y });
}
static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions) {
switch (eventAction) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN: {
GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions);
return true; return true;
} }
} }

View File

@ -254,7 +254,17 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
return should_swap_buffers; return should_swap_buffers;
} }
void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) { // Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative) {
if (step.get() <= 0) {
return;
}
input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click, p_source_mouse_relative);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
if (step.get() <= 0) { if (step.get() <= 0) {
return; return;
} }
@ -262,50 +272,30 @@ void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev,
Vector<AndroidInputHandler::TouchPos> points; Vector<AndroidInputHandler::TouchPos> points;
for (int i = 0; i < pointer_count; i++) { for (int i = 0; i < pointer_count; i++) {
jfloat p[3]; jfloat p[3];
env->GetFloatArrayRegion(positions, i * 3, 3, p); env->GetFloatArrayRegion(position, i * 3, 3, p);
AndroidInputHandler::TouchPos tp; AndroidInputHandler::TouchPos tp;
tp.pos = Point2(p[1], p[2]); tp.pos = Point2(p[1], p[2]);
tp.id = (int)p[0]; tp.id = (int)p[0];
points.push_back(tp); points.push_back(tp);
} }
if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE || (input_device & AINPUT_SOURCE_MOUSE_RELATIVE) == AINPUT_SOURCE_MOUSE_RELATIVE) {
input_handler->process_mouse_event(input_device, ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor); input_handler->process_touch_event(ev, pointer, points);
} else {
input_handler->process_touch(ev, pointer, points);
}
} }
// Called on the UI thread // Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position) { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor) {
touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask) {
touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask, vertical_factor, horizontal_factor);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y) {
if (step.get() <= 0) { if (step.get() <= 0) {
return; return;
} }
input_handler->process_magnify(Point2(p_x, p_y), p_factor);
input_handler->process_hover(p_type, Point2(p_x, p_y));
} }
// Called on the UI thread // Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y) { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y) {
if (step.get() <= 0) { if (step.get() <= 0) {
return; return;
} }
input_handler->process_pan(Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y));
input_handler->process_double_tap(p_button_mask, Point2(p_x, p_y));
} }
// Called on the UI thread // Called on the UI thread

View File

@ -45,12 +45,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz); JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);