From 036b5510d3b3664610baad2d9f79254c06382d16 Mon Sep 17 00:00:00 2001 From: Julian Winkler Date: Sat, 30 Nov 2024 17:46:43 +0100 Subject: [PATCH] implement dispatchKeyEvent callback --- src/api-impl-jni/util.c | 1 + src/api-impl-jni/util.h | 1 + src/api-impl-jni/widgets/WrapperWidget.c | 65 +++++++++++++++++++ src/api-impl/android/view/KeyEvent.java | 5 +- src/api-impl/android/view/View.java | 2 + .../view/inputmethod/InputMethodManager.java | 4 ++ 6 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index d8728f15..26aa6718 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -142,6 +142,7 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.view.getId = _METHOD(handle_cache.view.class, "getId", "()I"); handle_cache.view.getIdName = _METHOD(handle_cache.view.class, "getIdName", "()Ljava/lang/String;"); handle_cache.view.getAllSuperClasses = _METHOD(handle_cache.view.class, "getAllSuperClasses", "()Ljava/lang/String;"); + handle_cache.view.dispatchKeyEvent = _METHOD(handle_cache.view.class, "dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z"); handle_cache.asset_manager.class = _REF((*env)->FindClass(env, "android/content/res/AssetManager")); handle_cache.asset_manager.extractFromAPK = _STATIC_METHOD(handle_cache.asset_manager.class, "extractFromAPK", "(Ljava/lang/String;Ljava/lang/String;)V"); diff --git a/src/api-impl-jni/util.h b/src/api-impl-jni/util.h index dff95a46..d34bd40c 100644 --- a/src/api-impl-jni/util.h +++ b/src/api-impl-jni/util.h @@ -88,6 +88,7 @@ struct handle_cache { jmethodID getId; jmethodID getIdName; jmethodID getAllSuperClasses; + jmethodID dispatchKeyEvent; } view; struct { jclass class; diff --git a/src/api-impl-jni/widgets/WrapperWidget.c b/src/api-impl-jni/widgets/WrapperWidget.c index e7581585..d1263f58 100644 --- a/src/api-impl-jni/widgets/WrapperWidget.c +++ b/src/api-impl-jni/widgets/WrapperWidget.c @@ -272,6 +272,62 @@ static bool on_click(GtkGestureClick *gesture, int n_press, double x, double y, return ret; } +#define KEYCODE_DPAD_UP 19 +#define KEYCODE_DPAD_DOWN 20 +#define KEYCODE_DPAD_LEFT 21 +#define KEYCODE_DPAD_RIGHT 22 +#define KEYCODE_ENTER 66 +#define KEYCODE_DEL 67 +#define KEYCODE_FORWARD_DEL 112 + +static int map_key_code(int key_code) { + switch (key_code) { + case GDK_KEY_Up: + return KEYCODE_DPAD_UP; + case GDK_KEY_Down: + return KEYCODE_DPAD_DOWN; + case GDK_KEY_Left: + return KEYCODE_DPAD_LEFT; + case GDK_KEY_Right: + return KEYCODE_DPAD_RIGHT; + case GDK_KEY_Return: + return KEYCODE_ENTER; + case GDK_KEY_BackSpace: + return KEYCODE_DEL; + case GDK_KEY_Delete: + return KEYCODE_FORWARD_DEL; + default: + return key_code; + } +} + +#define ACTION_DOWN 0 +#define ACTION_UP 1 + +static gboolean on_key_pressed(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, WrapperWidget *wrapper) +{ + JNIEnv *env = get_jni_env(); + + jobject key_event = (*env)->NewObject(env, handle_cache.key_event.class, handle_cache.key_event.constructor, ACTION_DOWN, map_key_code(keyval)); + _SET_INT_FIELD(key_event, "unicodeValue", gdk_keyval_to_unicode(keyval)); + gboolean ret = (*env)->CallBooleanMethod(env, wrapper->jobj, handle_cache.view.dispatchKeyEvent, key_event); + if((*env)->ExceptionCheck(env)) + (*env)->ExceptionDescribe(env); + return ret; +} + +static gboolean on_key_released(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, WrapperWidget *wrapper) +{ + JNIEnv *env = get_jni_env(); + + jobject key_event = (*env)->NewObject(env, handle_cache.key_event.class, handle_cache.key_event.constructor, ACTION_UP, map_key_code(keyval)); + _SET_INT_FIELD(key_event, "unicodeValue", gdk_keyval_to_unicode(keyval)); + gboolean ret = (*env)->CallBooleanMethod(env, wrapper->jobj, handle_cache.view.dispatchKeyEvent, key_event); + if((*env)->ExceptionCheck(env)) + (*env)->ExceptionDescribe(env); + return ret; +} + void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject jobj) { JavaVM *jvm; @@ -308,6 +364,15 @@ void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject job gtk_widget_add_controller(wrapper->child, controller); widget_set_needs_allocation(wrapper->child); } + + jmethodID dispatch_key_event_method = _METHOD(_CLASS(jobj), "dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z"); + if (dispatch_key_event_method != handle_cache.view.dispatchKeyEvent) { + GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_event_controller_key_new()); + g_signal_connect(controller, "key-pressed", G_CALLBACK(on_key_pressed), wrapper); + g_signal_connect(controller, "key-released", G_CALLBACK(on_key_released), wrapper); + gtk_widget_add_controller(GTK_WIDGET(wrapper), controller); + gtk_widget_set_focusable(GTK_WIDGET(wrapper), TRUE); + } } void wrapper_widget_set_layout_params(WrapperWidget *wrapper, int width, int height) diff --git a/src/api-impl/android/view/KeyEvent.java b/src/api-impl/android/view/KeyEvent.java index 5704214b..033ffb00 100644 --- a/src/api-impl/android/view/KeyEvent.java +++ b/src/api-impl/android/view/KeyEvent.java @@ -1737,6 +1737,8 @@ public class KeyEvent extends InputEvent { private long mEventTime; private String mCharacters; + int unicodeValue; // set from native code using gdk_keyval_to_unicode + public interface Callback { /** * Called when a key down event has occurred. If you return true, @@ -2984,7 +2986,8 @@ public class KeyEvent extends InputEvent { * @return The associated character or combining accent, or 0 if none. */ public int getUnicodeChar(int metaState) { - return getKeyCharacterMap().get(mKeyCode, metaState); + // return getKeyCharacterMap().get(mKeyCode, metaState); + return unicodeValue; } /** diff --git a/src/api-impl/android/view/View.java b/src/api-impl/android/view/View.java index 22b22a7c..e6b28c1f 100644 --- a/src/api-impl/android/view/View.java +++ b/src/api-impl/android/view/View.java @@ -2086,4 +2086,6 @@ public class View implements Drawable.Callback { public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) {return false;} public boolean requestRectangleOnScreen(Rect rectangle) {return false;} + + public boolean dispatchKeyEvent(KeyEvent event) {return false;} } diff --git a/src/api-impl/android/view/inputmethod/InputMethodManager.java b/src/api-impl/android/view/inputmethod/InputMethodManager.java index a9fcad86..3ba5ef0d 100644 --- a/src/api-impl/android/view/inputmethod/InputMethodManager.java +++ b/src/api-impl/android/view/inputmethod/InputMethodManager.java @@ -20,4 +20,8 @@ public class InputMethodManager { return Collections.emptyList(); } + public void restartInput(View view) {} + + public void updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd) {} + }