]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix menu and popup race conditions on Android
authorPo Lu <luangruo@yahoo.com>
Thu, 9 Mar 2023 08:30:02 +0000 (16:30 +0800)
committerPo Lu <luangruo@yahoo.com>
Thu, 9 Mar 2023 08:30:02 +0000 (16:30 +0800)
* 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
java/org/gnu/emacs/EmacsContextMenu.java
java/org/gnu/emacs/EmacsDialog.java
java/org/gnu/emacs/EmacsNative.java
src/android.c
src/androidgui.h
src/androidmenu.c
src/androidterm.c
src/androidterm.h

index 692d8a14e22e2f937fad705ca92de8b797603b77..735a464be8e7facb8e8e9c214dc417a023bc39d0 100644 (file)
@@ -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);
   }
index 0553ff9d4a3c34c88932afae0027f313e5f791cc..abc1869ac6ae1f381cc75366e42bc2452f9e8d39 100644 (file)
@@ -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<Boolean> rc;
@@ -312,6 +317,7 @@ public final class EmacsContextMenu
        {
          synchronized (this)
            {
+             lastMenuEventSerial = serial;
              rc.thing = display1 (window, xPosition, yPosition);
              notify ();
            }
index 80a5e5f736974f3273dbbd5e23dd22fc20a0ac7a..aed84de29e52584537890aa480fe17275d586a18 100644 (file)
@@ -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<EmacsButton> ();
     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);
   }
 };
index 8e626b9534b161e76c3eb6fd4a52cdca17657b94..11da5db8746ba6e4be40209b1289a6aa66911efd 100644 (file)
@@ -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,
index b14d28455446489c2dcac8dcdc9bfe7a5860ad21..e2ae77e30d053d3a5699bc348901c2bbcdce637f 100644 (file)
@@ -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;
index afcaed98cae3d65957507b67445dbc7c6f1f251d..5858a16808036643f4db8af61a296915ecf18bf0 100644 (file)
@@ -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
index 540b25cf6025c2473d9960e2b844b5aaf1eb92e7..7d9c33e28b14d46d755f5937b7800505084cf992 100644 (file)
@@ -35,6 +35,11 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
 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.  */
index b502b1c6de550dbcbdd4e26df9eb85a6202d02c4..2e2bf86706c85cd0ad4435c4188b5be3115468a3 100644 (file)
@@ -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;
index 9bd11bb7853c143437301ffbad0b014b4514dc0b..9964eb54880f008e6eb15516ce5cd6b542df336f 100644 (file)
@@ -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,