diff --git a/src/api-impl-jni/android_app_NativeActivity.c b/src/api-impl-jni/android_app_NativeActivity.c index 14a27c44..ed994680 100644 --- a/src/api-impl-jni/android_app_NativeActivity.c +++ b/src/api-impl-jni/android_app_NativeActivity.c @@ -544,20 +544,19 @@ Java_android_app_NativeActivity_onSurfaceDestroyedNative(JNIEnv* env, jobject cl } void -Java_android_app_NativeActivity_onInputQueueCreatedNative(JNIEnv* env, jobject clazz, jlong handle, jint queuePtr) +Java_android_app_NativeActivity_onInputQueueCreatedNative(JNIEnv* env, jobject clazz, jlong handle, jlong queue) { printf("STUB - onInputChannelCreated_native\n"); -/* if (handle != 0) { + if (handle != 0) { struct NativeCode* code = (struct NativeCode*)handle; if (code->callbacks.onInputQueueCreated != NULL) { - AInputQueue* queue = reinterpret_cast(queuePtr); code->callbacks.onInputQueueCreated(code, queue); } - }*/ + } } void -Java_android_app_NativeActivity_onInputQueueDestroyedNative(JNIEnv* env, jobject clazz, jlong handle, jint queuePtr) +Java_android_app_NativeActivity_onInputQueueDestroyedNative(JNIEnv* env, jobject clazz, jlong handle, jlong queuePtr) { printf("STUB - onInputChannelDestroyed_native\n"); /* if (handle != 0) { diff --git a/src/api-impl-jni/android_view_Window.c b/src/api-impl-jni/android_view_Window.c index f5a37755..7103522b 100644 --- a/src/api-impl-jni/android_view_Window.c +++ b/src/api-impl-jni/android_view_Window.c @@ -9,3 +9,18 @@ JNIEXPORT void JNICALL Java_android_view_Window_set_1widget_1as_1root(JNIEnv *en { gtk_window_set_child(GTK_WINDOW(_PTR(window)), gtk_widget_get_parent(GTK_WIDGET(_PTR(widget)))); } + +JNIEXPORT void JNICALL Java_android_view_Window_take_1input_1queue(JNIEnv *env, jobject this, jlong native_window, jobject callback, jobject queue) +{ + GtkWidget *window = _PTR(native_window); + printf("in Java_android_view_Window_take_1input_1queue\n"); + + GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_event_controller_legacy_new()); + gtk_widget_add_controller(window, controller); + + _SET_LONG_FIELD(queue, "native_ptr", _INTPTR(controller)); + + // we need to keep these for later, so they can be called after OnCreate finishes + g_object_set_data(G_OBJECT(window), "input_queue_callback", (gpointer)_REF(callback)); + g_object_set_data(G_OBJECT(window), "input_queue", (gpointer)_REF(queue)); +} diff --git a/src/api-impl-jni/generated_headers/android_app_NativeActivity.h b/src/api-impl-jni/generated_headers/android_app_NativeActivity.h index 8b2115e4..2ec18112 100644 --- a/src/api-impl-jni/generated_headers/android_app_NativeActivity.h +++ b/src/api-impl-jni/generated_headers/android_app_NativeActivity.h @@ -122,18 +122,18 @@ JNIEXPORT void JNICALL Java_android_app_NativeActivity_onSurfaceDestroyedNative /* * Class: android_app_NativeActivity * Method: onInputQueueCreatedNative - * Signature: (JI)V + * Signature: (JJ)V */ JNIEXPORT void JNICALL Java_android_app_NativeActivity_onInputQueueCreatedNative - (JNIEnv *, jobject, jlong, jint); + (JNIEnv *, jobject, jlong, jlong); /* * Class: android_app_NativeActivity * Method: onInputQueueDestroyedNative - * Signature: (JI)V + * Signature: (JJ)V */ JNIEXPORT void JNICALL Java_android_app_NativeActivity_onInputQueueDestroyedNative - (JNIEnv *, jobject, jlong, jint); + (JNIEnv *, jobject, jlong, jlong); /* * Class: android_app_NativeActivity diff --git a/src/api-impl-jni/generated_headers/android_view_Window.h b/src/api-impl-jni/generated_headers/android_view_Window.h index 0634d39b..4b468f65 100644 --- a/src/api-impl-jni/generated_headers/android_view_Window.h +++ b/src/api-impl-jni/generated_headers/android_view_Window.h @@ -15,6 +15,14 @@ extern "C" { JNIEXPORT void JNICALL Java_android_view_Window_set_1widget_1as_1root (JNIEnv *, jobject, jlong, jlong); +/* + * Class: android_view_Window + * Method: take_input_queue + * Signature: (JLandroid/view/InputQueue/Callback;Landroid/view/InputQueue;)V + */ +JNIEXPORT void JNICALL Java_android_view_Window_take_1input_1queue + (JNIEnv *, jobject, jlong, jobject, jobject); + #ifdef __cplusplus } #endif diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 7f6446a7..3d0cdcdc 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -68,6 +68,9 @@ void set_up_handle_cache(JNIEnv *env, char *apk_main_activity_class) handle_cache.audio_track_periodic_listener.class = _REF((*env)->FindClass(env, "android/media/AudioTrack$OnPlaybackPositionUpdateListener")); handle_cache.audio_track_periodic_listener.onPeriodicNotification = _METHOD(handle_cache.audio_track_periodic_listener.class, "onPeriodicNotification", "(Landroid/media/AudioTrack;)V"); + handle_cache.input_queue_callback.class = _REF((*env)->FindClass(env, "android/view/InputQueue$Callback")); + handle_cache.input_queue_callback.onInputQueueCreated = _METHOD(handle_cache.input_queue_callback.class, "onInputQueueCreated", "(Landroid/view/InputQueue;)V"); + handle_cache.view.class = _REF((*env)->FindClass(env, "android/view/View")); if((*env)->ExceptionCheck(env)) (*env)->ExceptionDescribe(env); diff --git a/src/api-impl-jni/util.h b/src/api-impl-jni/util.h index 936c9eb8..70dc412a 100644 --- a/src/api-impl-jni/util.h +++ b/src/api-impl-jni/util.h @@ -57,6 +57,10 @@ struct handle_cache { jclass class; jmethodID onPeriodicNotification; } audio_track_periodic_listener; + struct { + jclass class; + jmethodID onInputQueueCreated; + } input_queue_callback; struct { jclass class; jmethodID setLayoutParams; diff --git a/src/api-impl/android/app/NativeActivity.java b/src/api-impl/android/app/NativeActivity.java index f217ed1b..83599844 100644 --- a/src/api-impl/android/app/NativeActivity.java +++ b/src/api-impl/android/app/NativeActivity.java @@ -27,7 +27,7 @@ import android.os.Bundle; import android.os.Looper; import android.os.MessageQueue; import android.util.AttributeSet; -//import android.view.InputQueue; +import android.view.InputQueue; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; @@ -56,7 +56,7 @@ import java.io.File; * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all} */ public class NativeActivity extends Activity implements SurfaceHolder.Callback, - /*InputQueue.Callback, */OnGlobalLayoutListener { + InputQueue.Callback, OnGlobalLayoutListener { /** * Optional meta-that can be in the manifest for this component, specifying * the name of the native shared library to load. If not specified, @@ -79,7 +79,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, private long mNativeHandle; -// private InputQueue mCurInputQueue; + private InputQueue mCurInputQueue; private SurfaceHolder mCurSurfaceHolder; final int[] mLocation = new int[2]; @@ -110,8 +110,8 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, int format, int width, int height); private native void onSurfaceRedrawNeededNative(long handle, Surface surface); private native void onSurfaceDestroyedNative(long handle); - private native void onInputQueueCreatedNative(long handle, int queuePtr); - private native void onInputQueueDestroyedNative(long handle, int queuePtr); + private native void onInputQueueCreatedNative(long handle, long queuePtr); + private native void onInputQueueDestroyedNative(long handle, long queuePtr); private native void onContentRectChangedNative(long handle, int x, int y, int w, int h); static class NativeContentView extends SurfaceView { @@ -135,7 +135,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, // mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); // getWindow().takeSurface(this); -// getWindow().takeInputQueue(this); + getWindow().takeInputQueue(this); // getWindow().setFormat(PixelFormat.RGB_565); // getWindow().setSoftInputMode( // WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED @@ -198,10 +198,10 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, onSurfaceDestroyedNative(mNativeHandle); mCurSurfaceHolder = null; } -/* if (mCurInputQueue != null) { + if (mCurInputQueue != null) { onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr()); mCurInputQueue = null; - }*/ + } unloadNativeCode(mNativeHandle); super.onDestroy(); } @@ -291,19 +291,19 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, } } -/* public void onInputQueueCreated(InputQueue queue) { + public void onInputQueueCreated(InputQueue queue) { if (!mDestroyed) { mCurInputQueue = queue; onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); } - }*/ + } -/* public void onInputQueueDestroyed(InputQueue queue) { + public void onInputQueueDestroyed(InputQueue queue) { if (!mDestroyed) { onInputQueueDestroyedNative(mNativeHandle, queue.getNativePtr()); mCurInputQueue = null; } - }*/ + } public void onGlobalLayout() { /* mNativeContentView.getLocationInWindow(mLocation); diff --git a/src/api-impl/android/view/InputQueue.java b/src/api-impl/android/view/InputQueue.java new file mode 100644 index 00000000..1fa0929a --- /dev/null +++ b/src/api-impl/android/view/InputQueue.java @@ -0,0 +1,24 @@ +package android.view; + +public final class InputQueue { + // for now, we will put a GtkEventController for the window here + private long native_ptr = 0; + + public long getNativePtr() { + return native_ptr; // FIXME? + } + + public static interface Callback { + /** + * Called when the given InputQueue is now associated with the + * thread making this call, so it can start receiving events from it. + */ + void onInputQueueCreated(InputQueue queue); + + /** + * Called when the given InputQueue is no longer associated with + * the thread and thus not dispatching events. + */ + void onInputQueueDestroyed(InputQueue queue); + } +} diff --git a/src/api-impl/android/view/Window.java b/src/api-impl/android/view/Window.java index c3c4373a..bbb0f51f 100644 --- a/src/api-impl/android/view/Window.java +++ b/src/api-impl/android/view/Window.java @@ -33,4 +33,10 @@ public class Window { } private native void set_widget_as_root(long native_window, long widget); + + public native void take_input_queue(long native_window, InputQueue.Callback callback, InputQueue queue); + + public void takeInputQueue(InputQueue.Callback callback) { + take_input_queue(native_window, callback, new InputQueue()); + } } diff --git a/src/api-impl/meson.build b/src/api-impl/meson.build index 20ad92e5..7f8d0388 100644 --- a/src/api-impl/meson.build +++ b/src/api-impl/meson.build @@ -168,6 +168,7 @@ hax_jar = jar('hax', [ 'android/view/Gravity.java', 'android/view/InputDevice.java', 'android/view/InputEvent.java', + 'android/view/InputQueue.java', 'android/view/inputmethod/BaseInputConnection.java', 'android/view/inputmethod/InputConnection.java', 'android/view/LayoutInflater.java', diff --git a/src/libandroid/input.c b/src/libandroid/input.c index 25c9abe2..25597972 100644 --- a/src/libandroid/input.c +++ b/src/libandroid/input.c @@ -1,48 +1,234 @@ #include #include -struct AInputEvent; -struct AInputQueue; +#include + +enum { + AINPUT_EVENT_TYPE_KEY = 1, + AINPUT_EVENT_TYPE_MOTION = 2, + AINPUT_EVENT_TYPE_FOCUS = 3, + AINPUT_EVENT_TYPE_CAPTURE = 4, + AINPUT_EVENT_TYPE_DRAG = 5, + AINPUT_EVENT_TYPE_TOUCH_MODE = 6 +}; + +enum { + AINPUT_SOURCE_CLASS_MASK = 0x000000ff, + AINPUT_SOURCE_CLASS_NONE = 0x00000000, + AINPUT_SOURCE_CLASS_BUTTON = 0x00000001, + AINPUT_SOURCE_CLASS_POINTER = 0x00000002, + AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004, + AINPUT_SOURCE_CLASS_POSITION = 0x00000008, + AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010 +}; + +enum { + AINPUT_SOURCE_UNKNOWN = 0x00000000, + AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON, + AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON, + AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON, + AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER, + AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER, + AINPUT_SOURCE_STYLUS = 0x00004000 | AINPUT_SOURCE_CLASS_POINTER, + AINPUT_SOURCE_BLUETOOTH_STYLUS = 0x00008000 | AINPUT_SOURCE_STYLUS, + AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION, + AINPUT_SOURCE_MOUSE_RELATIVE = 0x00020000 | AINPUT_SOURCE_CLASS_NAVIGATION, + AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION, + AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE, + AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, + AINPUT_SOURCE_HDMI = 0x02000000 | AINPUT_SOURCE_CLASS_BUTTON, + AINPUT_SOURCE_SENSOR = 0x04000000 | AINPUT_SOURCE_CLASS_NONE, + AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE, + AINPUT_SOURCE_ANY = 0xffffff00 +}; + +enum { + AMOTION_EVENT_ACTION_MASK = 0xff, + AMOTION_EVENT_ACTION_POINTER_INDEX_MASK = 0xff00, + AMOTION_EVENT_ACTION_DOWN = 0, + AMOTION_EVENT_ACTION_UP = 1, + AMOTION_EVENT_ACTION_MOVE = 2, + AMOTION_EVENT_ACTION_CANCEL = 3, + AMOTION_EVENT_ACTION_OUTSIDE = 4, + AMOTION_EVENT_ACTION_POINTER_DOWN = 5, + AMOTION_EVENT_ACTION_POINTER_UP = 6, + AMOTION_EVENT_ACTION_HOVER_MOVE = 7, + AMOTION_EVENT_ACTION_SCROLL = 8, + AMOTION_EVENT_ACTION_HOVER_ENTER = 9, + AMOTION_EVENT_ACTION_HOVER_EXIT = 10, + AMOTION_EVENT_ACTION_BUTTON_PRESS = 11, + AMOTION_EVENT_ACTION_BUTTON_RELEASE = 12 +}; + +struct AInputEvent { + double x; + double y; + int32_t action; +}; + +typedef void AInputQueue; struct ALooper; -typedef void * ALooper_callbackFunc; +typedef int (*Looper_callbackFunc)(int fd, int events, void* data); + +float AMotionEvent_getAxisValue(const struct AInputEvent* motion_event, int32_t axis, size_t pointer_index) +{ + return -1; // no clue what to do here +} + +size_t AMotionEvent_getPointerCount(const struct AInputEvent* motion_event) +{ + return 1; // FIXME +} int32_t AInputEvent_getType(const struct AInputEvent* event) { - return -1; + if(event) { + return AINPUT_EVENT_TYPE_MOTION; // FIXME + } else { + return -1; + } +} + +int32_t AInputEvent_getSource(const struct AInputEvent* event) +{ + if(event) { + return AINPUT_SOURCE_TOUCHSCREEN; // FIXME + } else { + return -1; + } +} + +int32_t AMotionEvent_getAction(const struct AInputEvent* motion_event) +{ + if(motion_event) { + return motion_event->action; + } else { + return -1; + } +} + +int32_t AMotionEvent_getPointerId(const struct AInputEvent* motion_event, size_t pointer_index) +{ + if(motion_event) { + return 1; // FIXME + } else { + return -1; // 0? + } } float AMotionEvent_getX(const struct AInputEvent* motion_event, size_t pointer_index) { - return 0; + if(motion_event) { + return motion_event->x; + } else { + return -1; + } } float AMotionEvent_getY(const struct AInputEvent* motion_event, size_t pointer_index) { - return 0; + if(motion_event) { + return motion_event->y; + } else { + return -1; + } } -void AInputQueue_detachLooper(struct AInputQueue* queue) +void AInputQueue_detachLooper(AInputQueue* queue) { return; } -void AInputQueue_attachLooper(struct AInputQueue* queue, struct ALooper* looper, int ident, ALooper_callbackFunc callback, void* data) +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +// ugly; if this is < 0, there are no events +// we return this from AInputQueue_getEvent, +// and we don't want to have an actual queue +static int32_t fixme_ugly_are_there_events = -1; + +struct AInputEvent fixme_ugly_current_event; + +static gboolean on_event(GtkEventControllerLegacy* self, GdkEvent* event, struct android_poll_source *poll_source) { - return; + double x; + double y; + + // TODO: this doesn't work for multitouch + switch(gdk_event_get_event_type(event)) { + case GDK_BUTTON_PRESS: + gdk_event_get_position(event, &x, &y); + fixme_ugly_current_event.x = x; + fixme_ugly_current_event.y = y; + fixme_ugly_current_event.action = AMOTION_EVENT_ACTION_DOWN; + fixme_ugly_are_there_events = 0; // not < 0, so there are events + poll_source->process(poll_source->app, poll_source); + if(fixme_ugly_are_there_events != -1) + fprintf(stderr, "sus: poll_source->callback finished, but 'fixme_ugly_are_there_events' is not -1 (it's %d)\n" + "this should *probably* not happen?\n", fixme_ugly_are_there_events); + break; + case GDK_BUTTON_RELEASE: + gdk_event_get_position(event, &x, &y); + fixme_ugly_current_event.x = x; + fixme_ugly_current_event.y = y; + fixme_ugly_current_event.action = AMOTION_EVENT_ACTION_UP; + fixme_ugly_are_there_events = 0; // not < 0, so there are events + poll_source->process(poll_source->app, poll_source); + if(fixme_ugly_are_there_events != -1) + fprintf(stderr, "sus: poll_source->callback finished, but 'fixme_ugly_are_there_events' is not -1 (it's %d)\n" + "this should *probably* not happen?\n", fixme_ugly_are_there_events); + break; + case GDK_MOTION_NOTIFY: + gdk_event_get_position(event, &x, &y); + fixme_ugly_current_event.x = x; + fixme_ugly_current_event.y = y; + fixme_ugly_current_event.action = AMOTION_EVENT_ACTION_MOVE; + fixme_ugly_are_there_events = 0; // not < 0, so there are events + poll_source->process(poll_source->app, poll_source); + if(fixme_ugly_are_there_events != -1) + fprintf(stderr, "sus: poll_source->callback finished, but 'fixme_ugly_are_there_events' is not -1 (it's %d)\n" + "this should *probably* not happen?\n", fixme_ugly_are_there_events); + break; + } } -int32_t AInputQueue_getEvent(struct AInputQueue* queue, struct AInputEvent** outEvent) +void AInputQueue_attachLooper(AInputQueue* queue, struct ALooper* looper, int ident, Looper_callbackFunc callback, void* data) { - return -1; // no events or error + struct android_poll_source *poll_source = (struct android_poll_source *)data; + printf("AInputQueue_attachLooper called: queue: %p, looper: %p, ident: %d, callback %p, data: %p, process_func: %p\n", queue, looper, ident, callback, poll_source, poll_source->process); + + GtkEventController *controller = (GtkEventController *)queue; // TODO: is there a saner thing to pass here? + + g_signal_connect(controller, "event", G_CALLBACK(on_event), (gpointer)poll_source); } -int32_t AInputQueue_preDispatchEvent(struct AInputQueue* queue, struct AInputEvent* event) +int32_t AInputQueue_getEvent(AInputQueue* queue, struct AInputEvent** outEvent) { - return -1; // not sure what is best suited for a stub + if(fixme_ugly_are_there_events == 0) { + *outEvent = &fixme_ugly_current_event; + return fixme_ugly_are_there_events; + } else { + return -1; // no events or error + } } -void AInputQueue_finishEvent(struct AInputQueue* queue, struct AInputEvent* event, int handled) +int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, struct AInputEvent* event) { - return; + return 0; // we don't want to claim the event for ourselves, let the app process it +} + +void AInputQueue_finishEvent(AInputQueue* queue, struct AInputEvent* event, int handled) +{ + fixme_ugly_are_there_events = -1; } diff --git a/src/main-executable/main.c b/src/main-executable/main.c index 089791e8..64c63dbc 100644 --- a/src/main-executable/main.c +++ b/src/main-executable/main.c @@ -309,6 +309,15 @@ static void open(GtkApplication *app, GFile** files, gint nfiles, const gchar* h (*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onWindowFocusChanged, true); if((*env)->ExceptionCheck(env)) (*env)->ExceptionDescribe(env); + + jobject input_queue_callback = g_object_get_data(G_OBJECT(window), "input_queue_callback"); + if(input_queue_callback) { + jobject input_queue = g_object_get_data(G_OBJECT(window), "input_queue"); + + (*env)->CallVoidMethod(env, input_queue_callback, handle_cache.input_queue_callback.onInputQueueCreated, input_queue); + if((*env)->ExceptionCheck(env)) + (*env)->ExceptionDescribe(env); + } } static void activate(GtkApplication *app, struct jni_callback_data *d)