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.
This commit is contained in:
Po Lu 2023-03-09 16:30:02 +08:00
parent 745890de52
commit e859a14bee
9 changed files with 66 additions and 18 deletions

View 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);
}

View 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 @@ private static class Item implements MenuItem.OnMenuItemClickListener
}
/* 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 @@ private static class Item implements MenuItem.OnMenuItemClickListener
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 @@ private static class Item implements MenuItem.OnMenuItemClickListener
{
synchronized (this)
{
lastMenuEventSerial = serial;
rc.thing = display1 (window, xPosition, yPosition);
notify ();
}

View 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 @@ private class EmacsButton implements View.OnClickListener,
Log.d (TAG, "onClicked " + this);
wasButtonClicked = true;
EmacsNative.sendContextMenu ((short) 0, id);
EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial);
dismissDialog.dismiss ();
}
@ -87,15 +90,16 @@ private class EmacsButton implements View.OnClickListener,
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 @@ private class EmacsButton implements View.OnClickListener,
dialog.buttons = new ArrayList<EmacsButton> ();
dialog.title = title;
dialog.text = text;
dialog.menuEventSerial = menuEventSerial;
return dialog;
}
@ -330,6 +335,6 @@ private class EmacsButton implements View.OnClickListener,
if (wasButtonClicked)
return;
EmacsNative.sendContextMenu ((short) 0, 0);
EmacsNative.sendContextMenu ((short) 0, 0, menuEventSerial);
}
};

View file

@ -155,7 +155,8 @@ public static native long sendWheel (short window, int x, int y,
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,

View 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;

View 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

View 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. */

View 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;

View 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,