summaryrefslogtreecommitdiff
path: root/src/androidmenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/androidmenu.c')
-rw-r--r--src/androidmenu.c829
1 files changed, 829 insertions, 0 deletions
diff --git a/src/androidmenu.c b/src/androidmenu.c
new file mode 100644
index 00000000000..f74e7ca6d99
--- /dev/null
+++ b/src/androidmenu.c
@@ -0,0 +1,829 @@
+/* Communication module for Android terminals.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "lisp.h"
+#include "androidterm.h"
+#include "android.h"
+#include "blockinput.h"
+#include "keyboard.h"
+#include "menu.h"
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+/* Flag indicating whether or not a popup menu has been posted and not
+ yet popped down. */
+
+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)
+{
+ return popup_activated_flag;
+}
+
+
+
+/* Toolkit menu implementation. */
+
+/* Structure describing the EmacsContextMenu class. */
+
+struct android_emacs_context_menu
+{
+ jclass class;
+ jmethodID create_context_menu;
+ jmethodID add_item;
+ jmethodID add_submenu;
+ jmethodID add_pane;
+ jmethodID parent;
+ jmethodID display;
+ jmethodID dismiss;
+};
+
+/* Identifiers associated with the EmacsContextMenu class. */
+static struct android_emacs_context_menu menu_class;
+
+static void
+android_init_emacs_context_menu (void)
+{
+ jclass old;
+
+ menu_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsContextMenu");
+ eassert (menu_class.class);
+
+ old = menu_class.class;
+ menu_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!menu_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ menu_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ menu_class.class, \
+ name, signature); \
+ eassert (menu_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature) \
+ menu_class.c_name \
+ = (*android_java_env)->GetStaticMethodID (android_java_env, \
+ menu_class.class, \
+ name, signature); \
+ eassert (menu_class.c_name);
+
+ FIND_METHOD_STATIC (create_context_menu, "createContextMenu",
+ "(Ljava/lang/String;)"
+ "Lorg/gnu/emacs/EmacsContextMenu;");
+
+ FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ"
+ "Ljava/lang/String;Z)V");
+ FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;"
+ "Ljava/lang/String;Ljava/lang/String;)"
+ "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;III)Z");
+ FIND_METHOD (dismiss, "dismiss", "(Lorg/gnu/emacs/EmacsWindow;)V");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static void
+android_unwind_local_frame (void)
+{
+ (*android_java_env)->PopLocalFrame (android_java_env, NULL);
+}
+
+/* Push a local reference frame to the JVM stack and record it on the
+ specpdl. Release local references created within that frame when
+ the specpdl is unwound past where it is after returning. */
+
+static void
+android_push_local_frame (void)
+{
+ int rc;
+
+ rc = (*android_java_env)->PushLocalFrame (android_java_env, 30);
+
+ /* This means the JVM ran out of memory. */
+ if (rc < 1)
+ android_exception_check ();
+
+ record_unwind_protect_void (android_unwind_local_frame);
+}
+
+/* Data for android_dismiss_menu. */
+
+struct android_dismiss_menu_data
+{
+ /* The menu object. */
+ jobject menu;
+
+ /* The window object. */
+ jobject window;
+};
+
+/* Cancel the context menu passed in POINTER. Also, clear
+ popup_activated_flag. */
+
+static void
+android_dismiss_menu (void *pointer)
+{
+ struct android_dismiss_menu_data *data;
+
+ data = pointer;
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ data->menu,
+ menu_class.dismiss,
+ data->window);
+ popup_activated_flag = 0;
+}
+
+/* Recursively process events until a ANDROID_CONTEXT_MENU event
+ arrives. Then, return the item ID specified in the event in
+ *ID. */
+
+static void
+android_process_events_for_menu (int *id)
+{
+ int blocked;
+
+ /* Set menu_event_id to -1; handle_one_android_event will set it to
+ the event ID upon receiving a context menu event. This can cause
+ a non-local exit. */
+ x_display_list->menu_event_id = -1;
+
+ /* Unblock input completely. */
+ blocked = interrupt_input_blocked;
+ totally_unblock_input ();
+
+ /* Now wait for the menu event ID to change. */
+ while (x_display_list->menu_event_id == -1)
+ {
+ /* Wait for events to become available. */
+ android_wait_event ();
+
+ /* Process pending signals. */
+ process_pending_signals ();
+
+ /* Maybe quit. This is important because the framework (on
+ Android 4.0.3) can sometimes fail to deliver context menu
+ closed events if a submenu was opened, and the user still
+ needs to be able to quit. */
+ maybe_quit ();
+ }
+
+ /* Restore the input block. */
+ interrupt_input_blocked = blocked;
+
+ /* Return the ID. */
+ *id = x_display_list->menu_event_id;
+}
+
+/* Structure describing a ``subprefix'' in the menu. */
+
+struct android_menu_subprefix
+{
+ /* The subprefix above. */
+ struct android_menu_subprefix *last;
+
+ /* The subprefix itself. */
+ Lisp_Object subprefix;
+};
+
+/* Free the subprefixes starting from *DATA. */
+
+static void
+android_free_subprefixes (void *data)
+{
+ struct android_menu_subprefix **head, *subprefix;
+
+ head = data;
+
+ while (*head)
+ {
+ subprefix = *head;
+ *head = subprefix->last;
+
+ xfree (subprefix);
+ }
+}
+
+Lisp_Object
+android_menu_show (struct frame *f, int x, int y, int menuflags,
+ Lisp_Object title, const char **error_name)
+{
+ jobject context_menu, current_context_menu;
+ jobject title_string, help_string, temp;
+ size_t i;
+ Lisp_Object pane_name, prefix;
+ const char *pane_string;
+ specpdl_ref count, count1;
+ Lisp_Object item_name, enable, def, tem, entry, type, selected;
+ Lisp_Object help;
+ jmethodID method;
+ jobject store;
+ bool rc;
+ jobject window;
+ int id, item_id, submenu_depth;
+ struct android_dismiss_menu_data data;
+ 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 ();
+
+ /* Push the first local frame. */
+ android_push_local_frame ();
+
+ /* Push the first local frame for the context menu. */
+ title_string = (!NILP (title)
+ ? (jobject) android_build_string (title)
+ : NULL);
+ method = menu_class.create_context_menu;
+ current_context_menu = context_menu
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ menu_class.class,
+ method,
+ title_string);
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ /* Push the second local frame for temporaries. */
+ count1 = SPECPDL_INDEX ();
+ android_push_local_frame ();
+
+ /* Iterate over the menu. */
+ i = 0, submenu_depth = 0;
+
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ /* This is the start of a new submenu. However, it can be
+ ignored here. */
+ i += 1;
+ submenu_depth += 1;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ /* This is the end of a submenu. Go back to the previous
+ context menu. */
+ store = current_context_menu;
+ current_context_menu
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ current_context_menu,
+ menu_class.parent);
+ android_exception_check ();
+
+ if (store != context_menu)
+ ANDROID_DELETE_LOCAL_REF (store);
+ i += 1;
+ submenu_depth -= 1;
+
+ if (!current_context_menu || submenu_depth < 0)
+ {
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "unbalanced submenu pop in menu_items");
+ emacs_abort ();
+ }
+ }
+ else if (EQ (AREF (menu_items, i), Qt)
+ && submenu_depth != 0)
+ i += MENU_ITEMS_PANE_LENGTH;
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ /* This is a new pane. Switch back to the topmost context
+ menu. */
+ if (current_context_menu != context_menu)
+ ANDROID_DELETE_LOCAL_REF (current_context_menu);
+ current_context_menu = context_menu;
+
+ /* Now figure out the title of this pane. */
+ pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+ pane_string = (NILP (pane_name)
+ ? "" : SSDATA (pane_name));
+ if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
+ pane_string++;
+
+ /* Add the pane. */
+ temp = (*android_java_env)->NewStringUTF (android_java_env,
+ pane_string);
+ android_exception_check ();
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_pane,
+ temp);
+ android_exception_check ();
+ ANDROID_DELETE_LOCAL_REF (temp);
+
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ else
+ {
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+ def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+ type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+ selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+ help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+ /* This is an actual menu item (or submenu). Add it to the
+ menu. */
+
+ if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used
+ && NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
+ {
+ /* This is a submenu. Add it. */
+ title_string = (!NILP (item_name)
+ ? android_build_string (item_name)
+ : NULL);
+ help_string = NULL;
+
+ /* Menu items can have tool tips on Android 26 and
+ later. In this case, set it to the help string. */
+
+ if (android_get_current_api_level () >= 26
+ && STRINGP (help))
+ help_string = android_build_string (help);
+
+ store = current_context_menu;
+ current_context_menu
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_submenu,
+ title_string, NULL,
+ help_string);
+ android_exception_check ();
+
+ if (store != context_menu)
+ ANDROID_DELETE_LOCAL_REF (store);
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ if (help_string)
+ ANDROID_DELETE_LOCAL_REF (help_string);
+ }
+ else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
+ /* Ignore this separator item. */
+ ;
+ else
+ {
+ /* Compute the item ID. This is the index of value.
+ Make sure it doesn't overflow. */
+
+ if (!INT_ADD_OK (0, i + MENU_ITEMS_ITEM_VALUE, &item_id))
+ memory_full (i + MENU_ITEMS_ITEM_VALUE * sizeof (Lisp_Object));
+
+ /* Add this menu item with the appropriate state. */
+
+ title_string = (!NILP (item_name)
+ ? android_build_string (item_name)
+ : NULL);
+ help_string = NULL;
+
+ /* Menu items can have tool tips on Android 26 and
+ later. In this case, set it to the help string. */
+
+ if (android_get_current_api_level () >= 26
+ && STRINGP (help))
+ help_string = android_build_string (help);
+
+ /* Determine whether or not to display a check box. */
+
+ checkmark = (EQ (type, QCtoggle)
+ || EQ (type, QCradio));
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_item,
+ (jint) item_id,
+ title_string,
+ (jboolean) !NILP (enable),
+ (jboolean) checkmark,
+ (jboolean) !NILP (selected),
+ help_string,
+ (jboolean) (EQ (type,
+ QCradio)));
+ android_exception_check ();
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ if (help_string)
+ ANDROID_DELETE_LOCAL_REF (help_string);
+ }
+
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ /* The menu has now been built. Pop the second local frame. */
+ unbind_to (count1, Qnil);
+
+ /* Now, display the context menu. */
+ window = android_resolve_handle (FRAME_ANDROID_WINDOW (f),
+ ANDROID_HANDLE_WINDOW);
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ context_menu,
+ menu_class.display,
+ window, (jint) x,
+ (jint) y,
+ (jint) serial);
+ android_exception_check ();
+
+ if (!rc)
+ /* This means displaying the menu failed. */
+ goto finish;
+
+ /* Make sure the context menu is always dismissed. */
+ data.menu = context_menu;
+ data.window = window;
+ record_unwind_protect_ptr (android_dismiss_menu, &data);
+
+ /* Next, process events waiting for something to be selected. */
+ popup_activated_flag = 1;
+ android_process_events_for_menu (&id);
+
+ if (!id)
+ /* This means no menu item was selected. */
+ goto finish;
+
+ /* This means the id is invalid. */
+ if (id >= ASIZE (menu_items))
+ goto finish;
+
+ /* Now return the menu item at that location. */
+ tem = Qnil;
+ subprefix = NULL;
+ record_unwind_protect_ptr (android_free_subprefixes, &subprefix);
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+
+ prefix = entry = Qnil;
+ i = 0;
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ temp_subprefix = xmalloc (sizeof *temp_subprefix);
+ temp_subprefix->last = subprefix;
+ subprefix = temp_subprefix;
+ subprefix->subprefix = prefix;
+
+ prefix = entry;
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ prefix = subprefix->subprefix;
+ temp_subprefix = subprefix->last;
+ xfree (subprefix);
+ subprefix = temp_subprefix;
+
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ prefix
+ = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else
+ {
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+ if (i + MENU_ITEMS_ITEM_VALUE == id)
+ {
+ if (menuflags & MENU_KEYMAPS)
+ {
+ entry = list1 (entry);
+
+ if (!NILP (prefix))
+ entry = Fcons (prefix, entry);
+
+ for (subprefix_1 = subprefix; subprefix_1;
+ subprefix_1 = subprefix_1->last)
+ if (!NILP (subprefix_1->subprefix))
+ entry = Fcons (subprefix_1->subprefix, entry);
+ }
+
+ tem = entry;
+ }
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ unblock_input ();
+ return unbind_to (count, tem);
+
+ finish:
+ unblock_input ();
+ return unbind_to (count, Qnil);
+}
+
+
+
+/* Toolkit dialog implementation. */
+
+/* Structure describing the EmacsDialog class. */
+
+struct android_emacs_dialog
+{
+ jclass class;
+ jmethodID create_dialog;
+ jmethodID add_button;
+ jmethodID display;
+};
+
+/* Identifiers associated with the EmacsDialog class. */
+static struct android_emacs_dialog dialog_class;
+
+static void
+android_init_emacs_dialog (void)
+{
+ jclass old;
+
+ dialog_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsDialog");
+ eassert (dialog_class.class);
+
+ old = dialog_class.class;
+ dialog_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!dialog_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ dialog_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ dialog_class.class, \
+ name, signature); \
+ eassert (dialog_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature) \
+ dialog_class.c_name \
+ = (*android_java_env)->GetStaticMethodID (android_java_env, \
+ dialog_class.class, \
+ name, signature); \
+
+ FIND_METHOD_STATIC (create_dialog, "createDialog", "(Ljava/lang/String;"
+ "Ljava/lang/String;I)Lorg/gnu/emacs/EmacsDialog;");
+ FIND_METHOD (add_button, "addButton", "(Ljava/lang/String;IZ)V");
+ FIND_METHOD (display, "display", "()Z");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static Lisp_Object
+android_dialog_show (struct frame *f, Lisp_Object title,
+ Lisp_Object header, const char **error_name)
+{
+ specpdl_ref count;
+ jobject dialog, java_header, java_title, temp;
+ size_t i;
+ Lisp_Object item_name, enable, entry;
+ 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)
+ {
+ *error_name = "Multiple panes in dialog box";
+ return Qnil;
+ }
+
+ /* Do the initial setup. */
+ count = SPECPDL_INDEX ();
+ *error_name = NULL;
+
+ android_push_local_frame ();
+
+ /* Figure out what header to use. */
+ java_header = (!NILP (header)
+ ? android_build_jstring ("Information")
+ : android_build_jstring ("Question"));
+
+ /* And the title. */
+ java_title = android_build_string (title);
+
+ /* Now create the dialog. */
+ method = dialog_class.create_dialog;
+ dialog = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ dialog_class.class,
+ method, java_header,
+ java_title,
+ (jint) serial);
+ android_exception_check ();
+
+ /* Delete now unused local references. */
+ if (java_header)
+ ANDROID_DELETE_LOCAL_REF (java_header);
+ ANDROID_DELETE_LOCAL_REF (java_title);
+
+ /* Create the buttons. */
+ i = MENU_ITEMS_PANE_LENGTH;
+ while (i < menu_items_used)
+ {
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+
+ /* Verify that there is no submenu here. */
+
+ if (NILP (item_name))
+ {
+ *error_name = "Submenu in dialog items";
+ return unbind_to (count, Qnil);
+ }
+
+ /* Skip past boundaries between buttons on different sides. The
+ Android toolkit is too silly to understand this
+ distinction. */
+
+ if (EQ (item_name, Qquote))
+ ++i;
+ else
+ {
+ /* Make sure i is within bounds. */
+ if (i > TYPE_MAXIMUM (jint))
+ {
+ *error_name = "Dialog box too big";
+ return unbind_to (count, Qnil);
+ }
+
+ /* Add the button. */
+ temp = android_build_string (item_name);
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ dialog,
+ dialog_class.add_button,
+ temp, (jint) i,
+ (jboolean) NILP (enable));
+ android_exception_check ();
+ ANDROID_DELETE_LOCAL_REF (temp);
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ /* The dialog is now built. Run it. */
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ dialog,
+ dialog_class.display);
+ android_exception_check ();
+
+ if (!rc)
+ quit ();
+
+ /* Wait for the menu ID to arrive. */
+ android_process_events_for_menu (&id);
+
+ if (!id)
+ quit ();
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+ i = 0;
+ while (i < menu_items_used)
+ {
+ if (EQ (AREF (menu_items, i), Qt))
+ i += MENU_ITEMS_PANE_LENGTH;
+ else if (EQ (AREF (menu_items, i), Qquote))
+ /* This is the boundary between left-side elts and right-side
+ elts. */
+ ++i;
+ else
+ {
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+ if (id == i)
+ return entry;
+
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ return Qnil;
+}
+
+Lisp_Object
+android_popup_dialog (struct frame *f, Lisp_Object header,
+ Lisp_Object contents)
+{
+ Lisp_Object title;
+ const char *error_name;
+ Lisp_Object selection;
+ specpdl_ref specpdl_count = SPECPDL_INDEX ();
+
+ check_window_system (f);
+
+ /* Decode the dialog items from what was specified. */
+ title = Fcar (contents);
+ CHECK_STRING (title);
+ record_unwind_protect_void (unuse_menu_items);
+
+ if (NILP (Fcar (Fcdr (contents))))
+ /* No buttons specified, add an "Ok" button so users can pop down
+ the dialog. */
+ contents = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+ list_of_panes (list1 (contents));
+
+ /* Display them in a dialog box. */
+ block_input ();
+ selection = android_dialog_show (f, title, header, &error_name);
+ unblock_input ();
+
+ unbind_to (specpdl_count, Qnil);
+ discard_menu_items ();
+
+ if (error_name)
+ error ("%s", error_name);
+
+ return selection;
+}
+
+#else
+
+int
+popup_activated (void)
+{
+ return 0;
+}
+
+#endif
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p,
+ Smenu_or_popup_active_p, 0, 0, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (void)
+{
+ return (popup_activated ()) ? Qt : Qnil;
+}
+
+void
+init_androidmenu (void)
+{
+#ifndef ANDROID_STUBIFY
+ android_init_emacs_context_menu ();
+ android_init_emacs_dialog ();
+#endif
+}
+
+void
+syms_of_androidmenu (void)
+{
+ defsubr (&Smenu_or_popup_active_p);
+}