gnu.emacs/src/androidmenu.c
Po Lu 2b87ab7b27 Update Android port
* java/Makefile.in (clean): Fix distclean and bootstrap-clean rules.
* java/debug.sh (jdb_port):
(attach_existing):
(num_pids):
(line): Add new options to upload a gdbserver binary to the device.

* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make
focusedActivities public.
* java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
New class.
* java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Fix
bounds computation.
* java/org/gnu/emacs/EmacsGC.java (markDirty): Set stroke width
explicitly.
* java/org/gnu/emacs/EmacsService.java (EmacsService)
(getLocationOnScreen, nameKeysym): New functions.
* java/org/gnu/emacs/EmacsView.java (EmacsView): Disable focus
highlight.
(onCreateContextMenu, popupMenu, cancelPopupMenu): New
functions.
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): Implement a
kind of ``override redirect'' window for tooltips.
* src/android.c (struct android_emacs_service): New method
`name_keysym'.
(android_run_select_thread, android_init_events):
(android_select): Release select thread on semaphores instead of
signals to avoid one nasty race on SIGUSR2 delivery.
(android_init_emacs_service): Initialize new method.
(android_create_window): Handle CW_OVERRIDE_REDIRECT.
(android_move_resize_window, android_map_raised)
(android_translate_coordinates, android_get_keysym_name)
(android_build_string, android_exception_check): New functions.
* src/android.h: Update prototypes.

* src/androidfns.c (android_set_parent_frame, Fx_create_frame)
(unwind_create_tip_frame, android_create_tip_frame)
(android_hide_tip, compute_tip_xy, Fx_show_tip, Fx_hide_tip)
(syms_of_androidfns): Implement tooltips and iconification
reporting.

* src/androidgui.h (enum android_window_value_mask): Add
CWOverrideRedirect.
(struct android_set_window_attributes): Add `override_redirect'.
(ANDROID_IS_MODIFIER_KEY): Recognize Caps Lock.

* src/androidmenu.c (struct android_emacs_context_menu): New
struct.
(android_init_emacs_context_menu, android_unwind_local_frame)
(android_push_local_frame, android_menu_show, init_androidmenu):
New functions.

* src/androidterm.c (handle_one_android_event): Fix NULL pointer
dereference.
(android_fullscreen_hook): Handle fullscreen correctly.
(android_draw_box_rect): Fix top line.
(get_keysym_name): Implement function.
(android_create_terminal): Remove scroll bar stubs and add menu
hook.

* src/androidterm.h: Update prototypes.
* src/emacs.c (android_emacs_init): Initialize androidmenu.c.
* xcompile/Makefile.in: Fix clean rules.
2023-01-14 22:12:16 +08:00

332 lines
8.7 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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
/* Flag indicating whether or not a popup menu has been posted and not
yet popped down. */
static int popup_activated_flag;
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;
};
/* 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;Z)V");
FIND_METHOD (add_submenu, "addSubmenu", "(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;II)Z");
#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);
}
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, temp;
size_t i;
Lisp_Object pane_name, prefix;
const char *pane_string;
specpdl_ref count, count1;
Lisp_Object item_name, enable, def;
jmethodID method;
jobject store;
bool rc;
jobject window;
count = SPECPDL_INDEX ();
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;
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;
}
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;
eassert (current_context_menu);
}
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);
/* 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);
store = current_context_menu;
current_context_menu
= (*android_java_env)->CallObjectMethod (android_java_env,
current_context_menu,
menu_class.add_submenu,
title_string);
android_exception_check ();
if (store != context_menu)
ANDROID_DELETE_LOCAL_REF (store);
if (title_string)
ANDROID_DELETE_LOCAL_REF (title_string);
}
else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
/* Ignore this separator item. */
;
else
{
/* Add this menu item with the appropriate state. */
title_string = (!NILP (item_name)
? android_build_string (item_name)
: NULL);
(*android_java_env)->CallVoidMethod (android_java_env,
current_context_menu,
menu_class.add_item,
(jint) 1,
title_string,
(jboolean) !NILP (enable));
android_exception_check ();
if (title_string)
ANDROID_DELETE_LOCAL_REF (title_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,
window, (jint) x,
(jint) y);
android_exception_check ();
if (!rc)
/* This means displaying the menu failed. */
goto finish;
#if 0
record_unwind_protect_ptr (android_dismiss_menu, &context_menu);
/* Otherwise, loop waiting for the menu event to arrive. */
android_process_events_for_menu (&id);
if (!id)
/* This means no menu item was selected. */
goto finish;
#endif
finish:
unblock_input ();
return unbind_to (count, Qnil);
}
#endif
void
init_androidmenu (void)
{
#ifndef ANDROID_STUBIFY
android_init_emacs_context_menu ();
#endif
}