From e859a14bee7a84a3aaed45770c89ef60c68b3e08 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Thu, 9 Mar 2023 16:30:02 +0800 Subject: [PATCH] Fix menu and popup race conditions on Android * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed): * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) (onMenuItemClick, run): * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog, onClick) (createDialog, onDismiss): Take menu event serial, and pass it along in context menu events. * java/org/gnu/emacs/EmacsNative.java (sendContextMenu): New argument. * src/android.c (sendContextMenu): Pass serial number in event. * src/androidgui.h (struct android_menu_event): New field `menu_event_serial'. * src/androidmenu.c (FIND_METHOD_STATIC) (android_init_emacs_context_menu): Adjust method declarations. (android_menu_show, android_dialog_show): * src/androidterm.c (handle_one_android_event): Expect serial in context menu events. * src/androidterm.h: Update prototypes. --- java/org/gnu/emacs/EmacsActivity.java | 8 +++++++- java/org/gnu/emacs/EmacsContextMenu.java | 14 ++++++++++---- java/org/gnu/emacs/EmacsDialog.java | 15 ++++++++++----- java/org/gnu/emacs/EmacsNative.java | 3 ++- src/android.c | 4 +++- src/androidgui.h | 3 +++ src/androidmenu.c | 24 +++++++++++++++++++----- src/androidterm.c | 7 ++++++- src/androidterm.h | 6 ++++++ 9 files changed, 66 insertions(+), 18 deletions(-) diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 692d8a14e22..735a464be8e 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -315,6 +315,8 @@ public class EmacsActivity extends Activity public final void onContextMenuClosed (Menu menu) { + int serial; + Log.d (TAG, "onContextMenuClosed: " + menu); /* See the comment inside onMenuItemClick. */ @@ -335,7 +337,11 @@ public class EmacsActivity extends Activity /* Send a context menu event given that no menu item has already been selected. */ if (!EmacsContextMenu.itemAlreadySelected) - EmacsNative.sendContextMenu ((short) 0, 0); + { + serial = EmacsContextMenu.lastMenuEventSerial; + EmacsNative.sendContextMenu ((short) 0, 0, + serial); + } super.onContextMenuClosed (menu); } diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index 0553ff9d4a3..abc1869ac6a 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -49,6 +49,9 @@ public final class EmacsContextMenu /* Whether or not a submenu was selected. */ public static boolean wasSubmenuSelected; + /* The serial ID of the last context menu to be displayed. */ + public static int lastMenuEventSerial; + private static class Item implements MenuItem.OnMenuItemClickListener { public int itemID; @@ -106,7 +109,8 @@ public final class EmacsContextMenu } /* Send a context menu event. */ - EmacsNative.sendContextMenu ((short) 0, itemID); + EmacsNative.sendContextMenu ((short) 0, itemID, + lastMenuEventSerial); /* Say that an item has already been selected. */ itemAlreadySelected = true; @@ -293,12 +297,13 @@ public final class EmacsContextMenu false); } - /* Display this context menu on WINDOW, at xPosition and - yPosition. */ + /* Display this context menu on WINDOW, at xPosition and yPosition. + SERIAL is a number that will be returned in any menu event + generated to identify this context menu. */ public boolean display (final EmacsWindow window, final int xPosition, - final int yPosition) + final int yPosition, final int serial) { Runnable runnable; final Holder rc; @@ -312,6 +317,7 @@ public final class EmacsContextMenu { synchronized (this) { + lastMenuEventSerial = serial; rc.thing = display1 (window, xPosition, yPosition); notify (); } diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index 80a5e5f7369..aed84de29e5 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -57,6 +57,9 @@ public final class EmacsDialog implements DialogInterface.OnDismissListener /* Dialog to dismiss after click. */ private AlertDialog dismissDialog; + /* The menu serial associated with this dialog box. */ + private int menuEventSerial; + private class EmacsButton implements View.OnClickListener, DialogInterface.OnClickListener { @@ -76,7 +79,7 @@ public final class EmacsDialog implements DialogInterface.OnDismissListener Log.d (TAG, "onClicked " + this); wasButtonClicked = true; - EmacsNative.sendContextMenu ((short) 0, id); + EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial); dismissDialog.dismiss (); } @@ -87,15 +90,16 @@ public final class EmacsDialog implements DialogInterface.OnDismissListener Log.d (TAG, "onClicked " + this); wasButtonClicked = true; - EmacsNative.sendContextMenu ((short) 0, id); + EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial); } }; /* Create a popup dialog with the title TITLE and the text TEXT. - TITLE may be NULL. */ + TITLE may be NULL. MENUEVENTSERIAL is a number which will + identify this popup dialog inside events it sends. */ public static EmacsDialog - createDialog (String title, String text) + createDialog (String title, String text, int menuEventSerial) { EmacsDialog dialog; @@ -103,6 +107,7 @@ public final class EmacsDialog implements DialogInterface.OnDismissListener dialog.buttons = new ArrayList (); dialog.title = title; dialog.text = text; + dialog.menuEventSerial = menuEventSerial; return dialog; } @@ -330,6 +335,6 @@ public final class EmacsDialog implements DialogInterface.OnDismissListener if (wasButtonClicked) return; - EmacsNative.sendContextMenu ((short) 0, 0); + EmacsNative.sendContextMenu ((short) 0, 0, menuEventSerial); } }; diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 8e626b9534b..11da5db8746 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -155,7 +155,8 @@ public final class EmacsNative public static native long sendDeiconified (short window); /* Send an ANDROID_CONTEXT_MENU event. */ - public static native long sendContextMenu (short window, int menuEventID); + public static native long sendContextMenu (short window, int menuEventID, + int menuEventSerial); /* Send an ANDROID_EXPOSE event. */ public static native long sendExpose (short window, int x, int y, diff --git a/src/android.c b/src/android.c index b14d2845544..e2ae77e30d0 100644 --- a/src/android.c +++ b/src/android.c @@ -2744,7 +2744,8 @@ NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object, JNIEXPORT jlong JNICALL NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, - jshort window, jint menu_event_id) + jshort window, jint menu_event_id, + jint menu_event_serial) { JNI_STACK_ALIGNMENT_PROLOGUE; @@ -2754,6 +2755,7 @@ NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, event.menu.serial = ++event_serial; event.menu.window = window; event.menu.menu_event_id = menu_event_id; + event.menu.menu_event_serial = menu_event_serial; android_write_event (&event); return event_serial; diff --git a/src/androidgui.h b/src/androidgui.h index afcaed98cae..5858a168080 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -418,6 +418,9 @@ struct android_menu_event /* Menu event ID. */ int menu_event_id; + + /* Menu event serial; this counter identifies the context menu. */ + int menu_event_serial; }; enum android_ime_operation diff --git a/src/androidmenu.c b/src/androidmenu.c index 540b25cf602..7d9c33e28b1 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c @@ -35,6 +35,11 @@ along with GNU Emacs. If not, see . */ static int popup_activated_flag; +/* Serial number used to identify which context menu events are + associated with the context menu currently being displayed. */ + +unsigned int current_menu_serial; + int popup_activated (void) { @@ -96,7 +101,8 @@ android_init_emacs_context_menu (void) eassert (menu_class.c_name); FIND_METHOD_STATIC (create_context_menu, "createContextMenu", - "(Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); + "(Ljava/lang/String;)" + "Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ" "Ljava/lang/String;)V"); @@ -105,7 +111,7 @@ android_init_emacs_context_menu (void) "Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V"); FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;"); - FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;II)Z"); + FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;III)Z"); FIND_METHOD (dismiss, "dismiss", "(Lorg/gnu/emacs/EmacsWindow;)V"); #undef FIND_METHOD @@ -254,8 +260,10 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, struct android_menu_subprefix *subprefix, *temp_subprefix; struct android_menu_subprefix *subprefix_1; bool checkmark; + unsigned int serial; count = SPECPDL_INDEX (); + serial = ++current_menu_serial; block_input (); @@ -458,7 +466,8 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, context_menu, menu_class.display, window, (jint) x, - (jint) y); + (jint) y, + (jint) serial); android_exception_check (); if (!rc) @@ -606,7 +615,7 @@ android_init_emacs_dialog (void) name, signature); \ FIND_METHOD_STATIC (create_dialog, "createDialog", "(Ljava/lang/String;" - "Ljava/lang/String;)Lorg/gnu/emacs/EmacsDialog;"); + "Ljava/lang/String;I)Lorg/gnu/emacs/EmacsDialog;"); FIND_METHOD (add_button, "addButton", "(Ljava/lang/String;IZ)V"); FIND_METHOD (display, "display", "()Z"); @@ -625,6 +634,10 @@ android_dialog_show (struct frame *f, Lisp_Object title, bool rc; int id; jmethodID method; + unsigned int serial; + + /* Generate a unique ID for events from this dialog box. */ + serial = ++current_menu_serial; if (menu_items_n_panes > 1) { @@ -651,7 +664,8 @@ android_dialog_show (struct frame *f, Lisp_Object title, dialog = (*android_java_env)->CallStaticObjectMethod (android_java_env, dialog_class.class, method, java_header, - java_title); + java_title, + (jint) serial); android_exception_check (); /* Delete now unused local references. */ diff --git a/src/androidterm.c b/src/androidterm.c index b502b1c6de5..2e2bf86706c 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -1457,7 +1457,12 @@ handle_one_android_event (struct android_display_info *dpyinfo, /* Context menu handling. */ case ANDROID_CONTEXT_MENU: - if (dpyinfo->menu_event_id == -1) + if (dpyinfo->menu_event_id == -1 + /* Previously displayed popup menus might generate events + after dismissal, which might interfere. + `current_menu_serial' is always set to an identifier + identifying the last context menu to be displayed. */ + && event->menu.menu_event_serial == current_menu_serial) dpyinfo->menu_event_id = event->menu.menu_event_id; goto OTHER; diff --git a/src/androidterm.h b/src/androidterm.h index 9bd11bb7853..9964eb54880 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -421,6 +421,12 @@ extern void android_finalize_font_entity (struct font_entity *); /* Defined in androidmenu.c. */ +#ifndef ANDROID_STUBIFY + +extern unsigned int current_menu_serial; + +#endif + extern Lisp_Object android_menu_show (struct frame *, int, int, int, Lisp_Object, const char **); extern Lisp_Object android_popup_dialog (struct frame *, Lisp_Object, -- 2.39.2