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:
parent
745890de52
commit
e859a14bee
9 changed files with 66 additions and 18 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue